jimregexp: remove dead code
[jimtcl.git] / autosetup / jimsh0.c
blobc9380ac2ffced1c0a52a988394b3d917ad9e1aae
1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.berlios.de/ */
2 #define _GNU_SOURCE
3 #define JIM_TCL_COMPAT
4 #define JIM_REFERENCES
5 #define JIM_ANSIC
6 #define JIM_REGEXP
7 #define HAVE_NO_AUTOCONF
8 #define _JIMAUTOCONF_H
9 #define TCL_LIBRARY "."
10 #define jim_ext_bootstrap
11 #define jim_ext_aio
12 #define jim_ext_readdir
13 #define jim_ext_glob
14 #define jim_ext_regexp
15 #define jim_ext_file
16 #define jim_ext_exec
17 #define jim_ext_clock
18 #define jim_ext_array
19 #define jim_ext_stdlib
20 #define jim_ext_tclcompat
21 #if defined(_MSC_VER)
22 #define TCL_PLATFORM_OS "windows"
23 #define TCL_PLATFORM_PLATFORM "windows"
24 #define TCL_PLATFORM_PATH_SEPARATOR ";"
25 #define HAVE_MKDIR_ONE_ARG
26 #define HAVE_SYSTEM
27 #elif defined(__MINGW32__)
28 #define TCL_PLATFORM_OS "mingw"
29 #define TCL_PLATFORM_PLATFORM "windows"
30 #define TCL_PLATFORM_PATH_SEPARATOR ";"
31 #define HAVE_MKDIR_ONE_ARG
32 #define HAVE_SYSTEM
33 #define HAVE_SYS_TIME_H
34 #define HAVE_DIRENT_H
35 #define HAVE_UNISTD_H
36 #else
37 #define TCL_PLATFORM_OS "unknown"
38 #define TCL_PLATFORM_PLATFORM "unix"
39 #define TCL_PLATFORM_PATH_SEPARATOR ":"
40 #define HAVE_VFORK
41 #define HAVE_WAITPID
42 #define HAVE_ISATTY
43 #define HAVE_SYS_TIME_H
44 #define HAVE_DIRENT_H
45 #define HAVE_UNISTD_H
46 #endif
47 #define JIM_VERSION 75
48 #ifndef JIM_WIN32COMPAT_H
49 #define JIM_WIN32COMPAT_H
54 #if defined(_WIN32) || defined(WIN32)
56 #define HAVE_DLOPEN
57 void *dlopen(const char *path, int mode);
58 int dlclose(void *handle);
59 void *dlsym(void *handle, const char *symbol);
60 char *dlerror(void);
63 #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
65 #ifdef _MSC_VER
68 #if _MSC_VER >= 1000
69 #pragma warning(disable:4146)
70 #endif
72 #include <limits.h>
73 #define jim_wide _int64
74 #ifndef LLONG_MAX
75 #define LLONG_MAX 9223372036854775807I64
76 #endif
77 #ifndef LLONG_MIN
78 #define LLONG_MIN (-LLONG_MAX - 1I64)
79 #endif
80 #define JIM_WIDE_MIN LLONG_MIN
81 #define JIM_WIDE_MAX LLONG_MAX
82 #define JIM_WIDE_MODIFIER "I64d"
83 #define strcasecmp _stricmp
84 #define strtoull _strtoui64
85 #define snprintf _snprintf
87 #include <io.h>
89 struct timeval {
90 long tv_sec;
91 long tv_usec;
94 int gettimeofday(struct timeval *tv, void *unused);
96 #define HAVE_OPENDIR
97 struct dirent {
98 char *d_name;
101 typedef struct DIR {
102 long handle;
103 struct _finddata_t info;
104 struct dirent result;
105 char *name;
106 } DIR;
108 DIR *opendir(const char *name);
109 int closedir(DIR *dir);
110 struct dirent *readdir(DIR *dir);
111 #endif
113 #endif
115 #endif
116 #ifndef UTF8_UTIL_H
117 #define UTF8_UTIL_H
120 #define MAX_UTF8_LEN 4
122 int utf8_fromunicode(char *p, unsigned uc);
124 #ifndef JIM_UTF8
125 #include <ctype.h>
128 #define utf8_strlen(S, B) ((B) < 0 ? strlen(S) : (B))
129 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
130 #define utf8_upper(C) toupper(C)
131 #define utf8_title(C) toupper(C)
132 #define utf8_lower(C) tolower(C)
133 #define utf8_index(C, I) (I)
134 #define utf8_charlen(C) 1
135 #define utf8_prev_len(S, L) 1
137 #else
139 #endif
141 #endif
143 #ifndef __JIM__H
144 #define __JIM__H
146 #ifdef __cplusplus
147 extern "C" {
148 #endif
150 #include <time.h>
151 #include <limits.h>
152 #include <stdio.h>
153 #include <stdlib.h>
154 #include <stdarg.h>
157 #ifndef HAVE_NO_AUTOCONF
158 #endif
162 #ifndef jim_wide
163 # ifdef HAVE_LONG_LONG
164 # define jim_wide long long
165 # ifndef LLONG_MAX
166 # define LLONG_MAX 9223372036854775807LL
167 # endif
168 # ifndef LLONG_MIN
169 # define LLONG_MIN (-LLONG_MAX - 1LL)
170 # endif
171 # define JIM_WIDE_MIN LLONG_MIN
172 # define JIM_WIDE_MAX LLONG_MAX
173 # else
174 # define jim_wide long
175 # define JIM_WIDE_MIN LONG_MIN
176 # define JIM_WIDE_MAX LONG_MAX
177 # endif
180 # ifdef HAVE_LONG_LONG
181 # define JIM_WIDE_MODIFIER "lld"
182 # else
183 # define JIM_WIDE_MODIFIER "ld"
184 # define strtoull strtoul
185 # endif
186 #endif
188 #define UCHAR(c) ((unsigned char)(c))
191 #define JIM_OK 0
192 #define JIM_ERR 1
193 #define JIM_RETURN 2
194 #define JIM_BREAK 3
195 #define JIM_CONTINUE 4
196 #define JIM_SIGNAL 5
197 #define JIM_EXIT 6
199 #define JIM_EVAL 7
201 #define JIM_MAX_CALLFRAME_DEPTH 1000
202 #define JIM_MAX_EVAL_DEPTH 2000
204 #define JIM_NONE 0
205 #define JIM_ERRMSG 1
207 #define JIM_UNSHARED 4
208 #define JIM_MUSTEXIST 8
211 #define JIM_GLOBAL_ONLY 0x100
214 #define JIM_SUBST_NOVAR 1
215 #define JIM_SUBST_NOCMD 2
216 #define JIM_SUBST_NOESC 4
217 #define JIM_SUBST_FLAG 128
220 #define JIM_NOTUSED(V) ((void) V)
223 #define JIM_ENUM_ABBREV 2
226 #define JIM_CASESENS 0
227 #define JIM_NOCASE 1
230 #define JIM_PATH_LEN 1024
233 #ifdef JIM_CRLF
234 #define JIM_NL "\r\n"
235 #else
236 #define JIM_NL "\n"
237 #endif
239 #define JIM_LIBPATH "auto_path"
240 #define JIM_INTERACTIVE "tcl_interactive"
243 typedef struct Jim_Stack {
244 int len;
245 int maxlen;
246 void **vector;
247 } Jim_Stack;
250 typedef struct Jim_HashEntry {
251 void *key;
252 union {
253 void *val;
254 int intval;
255 } u;
256 struct Jim_HashEntry *next;
257 } Jim_HashEntry;
259 typedef struct Jim_HashTableType {
260 unsigned int (*hashFunction)(const void *key);
261 void *(*keyDup)(void *privdata, const void *key);
262 void *(*valDup)(void *privdata, const void *obj);
263 int (*keyCompare)(void *privdata, const void *key1, const void *key2);
264 void (*keyDestructor)(void *privdata, void *key);
265 void (*valDestructor)(void *privdata, void *obj);
266 } Jim_HashTableType;
268 typedef struct Jim_HashTable {
269 Jim_HashEntry **table;
270 const Jim_HashTableType *type;
271 unsigned int size;
272 unsigned int sizemask;
273 unsigned int used;
274 unsigned int collisions;
275 void *privdata;
276 } Jim_HashTable;
278 typedef struct Jim_HashTableIterator {
279 Jim_HashTable *ht;
280 int index;
281 Jim_HashEntry *entry, *nextEntry;
282 } Jim_HashTableIterator;
285 #define JIM_HT_INITIAL_SIZE 16
288 #define Jim_FreeEntryVal(ht, entry) \
289 if ((ht)->type->valDestructor) \
290 (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
292 #define Jim_SetHashVal(ht, entry, _val_) do { \
293 if ((ht)->type->valDup) \
294 entry->u.val = (ht)->type->valDup((ht)->privdata, _val_); \
295 else \
296 entry->u.val = (_val_); \
297 } while(0)
299 #define Jim_FreeEntryKey(ht, entry) \
300 if ((ht)->type->keyDestructor) \
301 (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
303 #define Jim_SetHashKey(ht, entry, _key_) do { \
304 if ((ht)->type->keyDup) \
305 entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \
306 else \
307 entry->key = (void *)(_key_); \
308 } while(0)
310 #define Jim_CompareHashKeys(ht, key1, key2) \
311 (((ht)->type->keyCompare) ? \
312 (ht)->type->keyCompare((ht)->privdata, key1, key2) : \
313 (key1) == (key2))
315 #define Jim_HashKey(ht, key) (ht)->type->hashFunction(key)
317 #define Jim_GetHashEntryKey(he) ((he)->key)
318 #define Jim_GetHashEntryVal(he) ((he)->val)
319 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
320 #define Jim_GetHashTableSize(ht) ((ht)->size)
321 #define Jim_GetHashTableUsed(ht) ((ht)->used)
324 typedef struct Jim_Obj {
325 char *bytes;
326 const struct Jim_ObjType *typePtr;
327 int refCount;
328 int length;
330 union {
332 jim_wide wideValue;
334 int intValue;
336 double doubleValue;
338 void *ptr;
340 struct {
341 void *ptr1;
342 void *ptr2;
343 } twoPtrValue;
345 struct {
346 unsigned long callFrameId;
347 struct Jim_Var *varPtr;
348 int global;
349 } varValue;
351 struct {
352 unsigned long procEpoch;
353 struct Jim_Obj *nsObj;
354 struct Jim_Cmd *cmdPtr;
355 } cmdValue;
357 struct {
358 struct Jim_Obj **ele;
359 int len;
360 int maxLen;
361 } listValue;
363 struct {
364 int maxLength;
365 int charLength;
366 } strValue;
368 struct {
369 unsigned long id;
370 struct Jim_Reference *refPtr;
371 } refValue;
373 struct {
374 struct Jim_Obj *fileNameObj;
375 int lineNumber;
376 } sourceValue;
378 struct {
379 struct Jim_Obj *varNameObjPtr;
380 struct Jim_Obj *indexObjPtr;
381 } dictSubstValue;
383 struct {
384 unsigned flags;
385 void *compre;
386 } regexpValue;
387 struct {
388 int line;
389 int argc;
390 } scriptLineValue;
391 } internalRep;
392 struct Jim_Obj *prevObjPtr;
393 struct Jim_Obj *nextObjPtr;
394 } Jim_Obj;
397 #define Jim_IncrRefCount(objPtr) \
398 ++(objPtr)->refCount
399 #define Jim_DecrRefCount(interp, objPtr) \
400 if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
401 #define Jim_IsShared(objPtr) \
402 ((objPtr)->refCount > 1)
404 #define Jim_FreeNewObj Jim_FreeObj
407 #define Jim_FreeIntRep(i,o) \
408 if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
409 (o)->typePtr->freeIntRepProc(i, o)
412 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
415 #define Jim_SetIntRepPtr(o, p) \
416 (o)->internalRep.ptr = (p)
419 struct Jim_Interp;
421 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
422 struct Jim_Obj *objPtr);
423 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
424 struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
425 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
427 typedef struct Jim_ObjType {
428 const char *name;
429 Jim_FreeInternalRepProc *freeIntRepProc;
430 Jim_DupInternalRepProc *dupIntRepProc;
431 Jim_UpdateStringProc *updateStringProc;
432 int flags;
433 } Jim_ObjType;
436 #define JIM_TYPE_NONE 0
437 #define JIM_TYPE_REFERENCES 1
439 #define JIM_PRIV_FLAG_SHIFT 20
443 typedef struct Jim_CallFrame {
444 unsigned long id;
445 int level;
446 struct Jim_HashTable vars;
447 struct Jim_HashTable *staticVars;
448 struct Jim_CallFrame *parent;
449 Jim_Obj *const *argv;
450 int argc;
451 Jim_Obj *procArgsObjPtr;
452 Jim_Obj *procBodyObjPtr;
453 struct Jim_CallFrame *next;
454 Jim_Obj *nsObj;
455 Jim_Obj *fileNameObj;
456 int line;
457 Jim_Stack *localCommands;
458 } Jim_CallFrame;
460 typedef struct Jim_Var {
461 Jim_Obj *objPtr;
462 struct Jim_CallFrame *linkFramePtr;
463 } Jim_Var;
466 typedef int (*Jim_CmdProc)(struct Jim_Interp *interp, int argc,
467 Jim_Obj *const *argv);
468 typedef void (*Jim_DelCmdProc)(struct Jim_Interp *interp, void *privData);
472 typedef struct Jim_Cmd {
473 int inUse;
474 int isproc;
475 struct Jim_Cmd *prevCmd;
476 union {
477 struct {
479 Jim_CmdProc cmdProc;
480 Jim_DelCmdProc delProc;
481 void *privData;
482 } native;
483 struct {
485 Jim_Obj *argListObjPtr;
486 Jim_Obj *bodyObjPtr;
487 Jim_HashTable *staticVars;
488 int argListLen;
489 int reqArity;
490 int optArity;
491 int argsPos;
492 int upcall;
493 struct Jim_ProcArg {
494 Jim_Obj *nameObjPtr;
495 Jim_Obj *defaultObjPtr;
496 } *arglist;
497 Jim_Obj *nsObj;
498 } proc;
499 } u;
500 } Jim_Cmd;
503 typedef struct Jim_PrngState {
504 unsigned char sbox[256];
505 unsigned int i, j;
506 } Jim_PrngState;
508 typedef struct Jim_Interp {
509 Jim_Obj *result;
510 int errorLine;
511 Jim_Obj *errorFileNameObj;
512 int addStackTrace;
513 int maxCallFrameDepth;
514 int maxEvalDepth;
515 int evalDepth;
516 int returnCode;
517 int returnLevel;
518 int exitCode;
519 long id;
520 int signal_level;
521 jim_wide sigmask;
522 int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
523 Jim_CallFrame *framePtr;
524 Jim_CallFrame *topFramePtr;
525 struct Jim_HashTable commands;
526 unsigned long procEpoch; /* Incremented every time the result
527 of procedures names lookup caching
528 may no longer be valid. */
529 unsigned long callFrameEpoch; /* Incremented every time a new
530 callframe is created. This id is used for the
531 'ID' field contained in the Jim_CallFrame
532 structure. */
533 int local;
534 Jim_Obj *liveList;
535 Jim_Obj *freeList;
536 Jim_Obj *currentScriptObj;
537 Jim_Obj *nullScriptObj;
538 Jim_Obj *emptyObj;
539 Jim_Obj *trueObj;
540 Jim_Obj *falseObj;
541 unsigned long referenceNextId;
542 struct Jim_HashTable references;
543 unsigned long lastCollectId; /* reference max Id of the last GC
544 execution. It's set to -1 while the collection
545 is running as sentinel to avoid to recursive
546 calls via the [collect] command inside
547 finalizers. */
548 time_t lastCollectTime;
549 Jim_Obj *stackTrace;
550 Jim_Obj *errorProc;
551 Jim_Obj *unknown;
552 int unknown_called;
553 int errorFlag;
554 void *cmdPrivData; /* Used to pass the private data pointer to
555 a command. It is set to what the user specified
556 via Jim_CreateCommand(). */
558 struct Jim_CallFrame *freeFramesList;
559 struct Jim_HashTable assocData;
560 Jim_PrngState *prngState;
561 struct Jim_HashTable packages;
562 Jim_Stack *loadHandles;
563 } Jim_Interp;
565 #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
566 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
567 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
569 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
570 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
571 #define Jim_GetResult(i) ((i)->result)
572 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
574 #define Jim_SetResult(i,o) do { \
575 Jim_Obj *_resultObjPtr_ = (o); \
576 Jim_IncrRefCount(_resultObjPtr_); \
577 Jim_DecrRefCount(i,(i)->result); \
578 (i)->result = _resultObjPtr_; \
579 } while(0)
582 #define Jim_GetId(i) (++(i)->id)
585 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
586 string representation must be fixed length. */
587 typedef struct Jim_Reference {
588 Jim_Obj *objPtr;
589 Jim_Obj *finalizerCmdNamePtr;
590 char tag[JIM_REFERENCE_TAGLEN+1];
591 } Jim_Reference;
595 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
599 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
601 #define JIM_EXPORT
604 JIM_EXPORT void *Jim_Alloc (int size);
605 JIM_EXPORT void *Jim_Realloc(void *ptr, int size);
606 JIM_EXPORT void Jim_Free (void *ptr);
607 JIM_EXPORT char * Jim_StrDup (const char *s);
608 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
611 JIM_EXPORT char **Jim_GetEnviron(void);
612 JIM_EXPORT void Jim_SetEnviron(char **env);
615 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
618 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
620 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
622 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
623 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
624 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
625 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
626 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
627 Jim_Obj *const *objv);
628 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
629 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
630 int objc, Jim_Obj *const *objv);
631 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
632 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
633 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
634 Jim_Obj **resObjPtrPtr, int flags);
637 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
638 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
639 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
640 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
641 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
642 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
643 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
646 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
647 const Jim_HashTableType *type, void *privdata);
648 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
649 unsigned int size);
650 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
651 void *val);
652 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
653 const void *key, void *val);
654 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
655 const void *key);
656 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
657 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
658 const void *key);
659 JIM_EXPORT void Jim_ResizeHashTable (Jim_HashTable *ht);
660 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
661 (Jim_HashTable *ht);
662 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
663 (Jim_HashTableIterator *iter);
666 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
667 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
668 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
669 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
670 Jim_Obj *objPtr);
671 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
672 int *lenPtr);
673 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
674 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
677 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
678 const char *s, int len);
679 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
680 const char *s, int charlen);
681 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
682 char *s, int len);
683 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
684 const char *str, int len);
685 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
686 Jim_Obj *appendObjPtr);
687 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
688 Jim_Obj *objPtr, ...);
689 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
690 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
691 Jim_Obj *objPtr, int nocase);
692 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
693 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
694 Jim_Obj *lastObjPtr);
695 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
696 Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
697 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
698 Jim_Obj *fmtObjPtr, int flags);
699 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
700 Jim_Obj *objPtr, const char *str);
701 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
702 Jim_Obj *secondObjPtr, int nocase);
703 JIM_EXPORT int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
704 Jim_Obj *secondObjPtr, int nocase);
705 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
708 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
709 Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
710 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
711 Jim_Obj *objPtr);
712 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
713 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
716 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
717 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
718 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
719 JIM_EXPORT const char *Jim_ReturnCode(int code);
720 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
723 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
724 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
725 const char *cmdName, Jim_CmdProc cmdProc, void *privData,
726 Jim_DelCmdProc delProc);
727 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
728 const char *cmdName);
729 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
730 const char *oldName, const char *newName);
731 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
732 Jim_Obj *objPtr, int flags);
733 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
734 Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
735 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
736 const char *name, Jim_Obj *objPtr);
737 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
738 const char *name, Jim_Obj *objPtr);
739 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
740 const char *name, const char *val);
741 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
742 Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
743 Jim_CallFrame *targetCallFrame);
744 JIM_EXPORT int Jim_CreateNamespaceVariable(Jim_Interp *interp,
745 Jim_Obj *varNameObj, Jim_Obj *targetNameObj);
746 JIM_EXPORT int Jim_DiscardNamespaceVars(Jim_Interp *interp);
747 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
748 Jim_Obj *nameObjPtr, int flags);
749 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
750 Jim_Obj *nameObjPtr, int flags);
751 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
752 const char *name, int flags);
753 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
754 const char *name, int flags);
755 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
756 Jim_Obj *nameObjPtr, int flags);
759 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
760 Jim_Obj *levelObjPtr);
763 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
764 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
767 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
768 int *indexPtr);
771 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
772 Jim_Obj *const *elements, int len);
773 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
774 Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
775 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
776 Jim_Obj *listPtr, Jim_Obj *objPtr);
777 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
778 Jim_Obj *listPtr, Jim_Obj *appendListPtr);
779 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
780 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
781 int listindex, Jim_Obj **objPtrPtr, int seterr);
782 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
783 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
784 Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
785 Jim_Obj *newObjPtr);
786 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
787 Jim_Obj *const *objv);
788 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
789 Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
792 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
793 Jim_Obj *const *elements, int len);
794 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
795 Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
796 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
797 Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
798 Jim_Obj **objPtrPtr, int flags);
799 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
800 Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
801 Jim_Obj *newObjPtr, int flags);
802 JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
803 Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
804 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
805 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
806 JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj);
807 JIM_EXPORT int Jim_DictValues(Jim_Interp *interp, Jim_Obj *dictObjPtr, Jim_Obj *patternObjPtr);
808 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
811 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
812 int *intPtr);
815 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
816 Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr);
817 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
818 Jim_Obj *exprObjPtr, int *boolPtr);
821 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
822 jim_wide *widePtr);
823 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
824 long *longPtr);
825 #define Jim_NewWideObj Jim_NewIntObj
826 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
827 jim_wide wideValue);
830 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
831 double *doublePtr);
832 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
833 double doubleValue);
834 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
837 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
838 Jim_Obj *const *argv, const char *msg);
839 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
840 const char * const *tablePtr, int *indexPtr, const char *name, int flags);
841 JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len,
842 char *stateCharPtr);
843 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
846 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
847 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
848 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
849 Jim_InterpDeleteProc *delProc, void *data);
850 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
854 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
855 const char *name, const char *ver, int flags);
856 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
857 const char *name, int flags);
860 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
863 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
864 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
865 JIM_EXPORT void Jim_HistorySave(const char *filename);
866 JIM_EXPORT char *Jim_HistoryGetline(const char *prompt);
867 JIM_EXPORT void Jim_HistoryAdd(const char *line);
868 JIM_EXPORT void Jim_HistoryShow(void);
871 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
872 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
873 JIM_EXPORT int Jim_IsBigEndian(void);
874 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
877 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
878 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
881 JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
885 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
886 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
888 #ifdef __cplusplus
890 #endif
892 #endif
894 #ifndef JIM_SUBCMD_H
895 #define JIM_SUBCMD_H
898 #ifdef __cplusplus
899 extern "C" {
900 #endif
903 #define JIM_MODFLAG_HIDDEN 0x0001
904 #define JIM_MODFLAG_FULLARGV 0x0002
908 typedef int tclmod_cmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
910 typedef struct {
911 const char *cmd;
912 const char *args;
913 tclmod_cmd_function *function;
914 short minargs;
915 short maxargs;
916 unsigned short flags;
917 } jim_subcmd_type;
919 const jim_subcmd_type *
920 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
922 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
924 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
926 #ifdef __cplusplus
928 #endif
930 #endif
931 #ifndef JIMREGEXP_H
932 #define JIMREGEXP_H
934 #ifndef _JIMAUTOCONF_H
935 #error Need jimautoconf.h
936 #endif
938 #if defined(HAVE_REGCOMP) && !defined(JIM_REGEXP)
940 #include <regex.h>
942 #else
944 #include <stdlib.h>
947 typedef struct {
948 int rm_so;
949 int rm_eo;
950 } regmatch_t;
953 typedef struct regexp {
955 int re_nsub;
958 int cflags;
959 int err;
960 int regstart;
961 int reganch;
962 int regmust;
963 int regmlen;
964 int *program;
967 const char *regparse;
968 int p;
969 int proglen;
972 int eflags;
973 const char *start;
974 const char *reginput;
975 const char *regbol;
978 regmatch_t *pmatch;
979 int nmatch;
980 } regexp;
982 typedef regexp regex_t;
984 #define REG_EXTENDED 0
985 #define REG_NEWLINE 1
986 #define REG_ICASE 2
988 #define REG_NOTBOL 16
990 enum {
991 REG_NOERROR,
992 REG_NOMATCH,
993 REG_BADPAT,
994 REG_ERR_NULL_ARGUMENT,
995 REG_ERR_UNKNOWN,
996 REG_ERR_TOO_BIG,
997 REG_ERR_NOMEM,
998 REG_ERR_TOO_MANY_PAREN,
999 REG_ERR_UNMATCHED_PAREN,
1000 REG_ERR_UNMATCHED_BRACES,
1001 REG_ERR_BAD_COUNT,
1002 REG_ERR_JUNK_ON_END,
1003 REG_ERR_OPERAND_COULD_BE_EMPTY,
1004 REG_ERR_NESTED_COUNT,
1005 REG_ERR_INTERNAL,
1006 REG_ERR_COUNT_FOLLOWS_NOTHING,
1007 REG_ERR_TRAILING_BACKSLASH,
1008 REG_ERR_CORRUPTED,
1009 REG_ERR_NULL_CHAR,
1010 REG_ERR_NUM
1013 int regcomp(regex_t *preg, const char *regex, int cflags);
1014 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1015 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
1016 void regfree(regex_t *preg);
1018 #endif
1020 #endif
1021 int Jim_bootstrapInit(Jim_Interp *interp)
1023 if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1024 return JIM_ERR;
1026 return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1027 "\n"
1028 "\n"
1029 "proc package {args} {}\n"
1032 int Jim_initjimshInit(Jim_Interp *interp)
1034 if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1035 return JIM_ERR;
1037 return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1038 "\n"
1039 "\n"
1040 "\n"
1041 "proc _jimsh_init {} {\n"
1042 " rename _jimsh_init {}\n"
1043 "\n"
1044 "\n"
1045 " lappend p {*}[split [env JIMLIB {}] $::tcl_platform(pathSeparator)]\n"
1046 " lappend p {*}$::auto_path\n"
1047 " lappend p [file dirname [info nameofexecutable]]\n"
1048 " set ::auto_path $p\n"
1049 "\n"
1050 " if {$::tcl_interactive && [env HOME {}] ne \"\"} {\n"
1051 " foreach src {.jimrc jimrc.tcl} {\n"
1052 " if {[file exists [env HOME]/$src]} {\n"
1053 " uplevel #0 source [env HOME]/$src\n"
1054 " break\n"
1055 " }\n"
1056 " }\n"
1057 " }\n"
1058 "}\n"
1059 "\n"
1060 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1061 " set jim_argv0 [string map {\\\\ /} $jim_argv0]\n"
1062 "}\n"
1063 "\n"
1064 "_jimsh_init\n"
1067 int Jim_globInit(Jim_Interp *interp)
1069 if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1070 return JIM_ERR;
1072 return Jim_EvalSource(interp, "glob.tcl", 1,
1073 "\n"
1074 "\n"
1075 "\n"
1076 "\n"
1077 "\n"
1078 "\n"
1079 "\n"
1080 "package require readdir\n"
1081 "\n"
1082 "\n"
1083 "proc glob.globdir {dir pattern} {\n"
1084 " set result {}\n"
1085 " set files [readdir $dir]\n"
1086 " lappend files . ..\n"
1087 "\n"
1088 " foreach name $files {\n"
1089 " if {[string match $pattern $name]} {\n"
1090 "\n"
1091 " if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1092 " continue\n"
1093 " }\n"
1094 " lappend result $name\n"
1095 " }\n"
1096 " }\n"
1097 "\n"
1098 " return $result\n"
1099 "}\n"
1100 "\n"
1101 "\n"
1102 "\n"
1103 "\n"
1104 "proc glob.explode {pattern} {\n"
1105 " set oldexp {}\n"
1106 " set newexp {\"\"}\n"
1107 "\n"
1108 " while 1 {\n"
1109 " set oldexp $newexp\n"
1110 " set newexp {}\n"
1111 " set ob [string first \\{ $pattern]\n"
1112 " set cb [string first \\} $pattern]\n"
1113 "\n"
1114 " if {$ob < $cb && $ob != -1} {\n"
1115 " set mid [string range $pattern 0 $ob-1]\n"
1116 " set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1117 " if {$pattern eq \"\"} {\n"
1118 " error \"unmatched open brace in glob pattern\"\n"
1119 " }\n"
1120 " set pattern [string range $pattern 1 end]\n"
1121 "\n"
1122 " foreach subs $subexp {\n"
1123 " foreach sub [split $subs ,] {\n"
1124 " foreach old $oldexp {\n"
1125 " lappend newexp $old$mid$sub\n"
1126 " }\n"
1127 " }\n"
1128 " }\n"
1129 " } elseif {$cb != -1} {\n"
1130 " set suf [string range $pattern 0 $cb-1]\n"
1131 " set rest [string range $pattern $cb end]\n"
1132 " break\n"
1133 " } else {\n"
1134 " set suf $pattern\n"
1135 " set rest \"\"\n"
1136 " break\n"
1137 " }\n"
1138 " }\n"
1139 "\n"
1140 " foreach old $oldexp {\n"
1141 " lappend newexp $old$suf\n"
1142 " }\n"
1143 " linsert $newexp 0 $rest\n"
1144 "}\n"
1145 "\n"
1146 "\n"
1147 "\n"
1148 "proc glob.glob {base pattern} {\n"
1149 " set dir [file dirname $pattern]\n"
1150 " if {$pattern eq $dir || $pattern eq \"\"} {\n"
1151 " return [list [file join $base $dir] $pattern]\n"
1152 " } elseif {$pattern eq [file tail $pattern]} {\n"
1153 " set dir \"\"\n"
1154 " }\n"
1155 "\n"
1156 "\n"
1157 " set dirlist [glob.glob $base $dir]\n"
1158 " set pattern [file tail $pattern]\n"
1159 "\n"
1160 "\n"
1161 " set result {}\n"
1162 " foreach {realdir dir} $dirlist {\n"
1163 " if {![file isdir $realdir]} {\n"
1164 " continue\n"
1165 " }\n"
1166 " if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1167 " append dir /\n"
1168 " }\n"
1169 " foreach name [glob.globdir $realdir $pattern] {\n"
1170 " lappend result [file join $realdir $name] $dir$name\n"
1171 " }\n"
1172 " }\n"
1173 " return $result\n"
1174 "}\n"
1175 "\n"
1176 "\n"
1177 "\n"
1178 "\n"
1179 "\n"
1180 "\n"
1181 "\n"
1182 "\n"
1183 "\n"
1184 "\n"
1185 "\n"
1186 "\n"
1187 "proc glob {args} {\n"
1188 " set nocomplain 0\n"
1189 " set base \"\"\n"
1190 "\n"
1191 " set n 0\n"
1192 " foreach arg $args {\n"
1193 " if {[info exists param]} {\n"
1194 " set $param $arg\n"
1195 " unset param\n"
1196 " incr n\n"
1197 " continue\n"
1198 " }\n"
1199 " switch -glob -- $arg {\n"
1200 " -d* {\n"
1201 " set switch $arg\n"
1202 " set param base\n"
1203 " }\n"
1204 " -n* {\n"
1205 " set nocomplain 1\n"
1206 " }\n"
1207 " -t* {\n"
1208 "\n"
1209 " }\n"
1210 "\n"
1211 " -* {\n"
1212 " return -code error \"bad option \\\"$switch\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1213 " }\n"
1214 " -- {\n"
1215 " incr n\n"
1216 " break\n"
1217 " }\n"
1218 " * {\n"
1219 " break\n"
1220 " }\n"
1221 " }\n"
1222 " incr n\n"
1223 " }\n"
1224 " if {[info exists param]} {\n"
1225 " return -code error \"missing argument to \\\"$switch\\\"\"\n"
1226 " }\n"
1227 " if {[llength $args] <= $n} {\n"
1228 " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1229 " }\n"
1230 "\n"
1231 " set args [lrange $args $n end]\n"
1232 "\n"
1233 " set result {}\n"
1234 " foreach pattern $args {\n"
1235 " set pattern [string map {\n"
1236 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1237 " } $pattern]\n"
1238 " set patexps [lassign [glob.explode $pattern] rest]\n"
1239 " if {$rest ne \"\"} {\n"
1240 " return -code error \"unmatched close brace in glob pattern\"\n"
1241 " }\n"
1242 " foreach patexp $patexps {\n"
1243 " set patexp [string map {\n"
1244 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1245 " } $patexp]\n"
1246 " foreach {realname name} [glob.glob $base $patexp] {\n"
1247 " lappend result $name\n"
1248 " }\n"
1249 " }\n"
1250 " }\n"
1251 "\n"
1252 " if {!$nocomplain && [llength $result] == 0} {\n"
1253 " return -code error \"no files matched glob patterns\"\n"
1254 " }\n"
1255 "\n"
1256 " return $result\n"
1257 "}\n"
1260 int Jim_stdlibInit(Jim_Interp *interp)
1262 if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1263 return JIM_ERR;
1265 return Jim_EvalSource(interp, "stdlib.tcl", 1,
1266 "\n"
1267 "proc lambda {arglist args} {\n"
1268 " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1269 "}\n"
1270 "\n"
1271 "proc lambda.finalizer {name val} {\n"
1272 " rename $name {}\n"
1273 "}\n"
1274 "\n"
1275 "\n"
1276 "proc curry {args} {\n"
1277 " alias [ref {} function lambda.finalizer] {*}$args\n"
1278 "}\n"
1279 "\n"
1280 "\n"
1281 "\n"
1282 "\n"
1283 "\n"
1284 "\n"
1285 "\n"
1286 "\n"
1287 "\n"
1288 "proc function {value} {\n"
1289 " return $value\n"
1290 "}\n"
1291 "\n"
1292 "\n"
1293 "\n"
1294 "\n"
1295 "proc stacktrace {} {\n"
1296 " set trace {}\n"
1297 " foreach level [range 1 [info level]] {\n"
1298 " lassign [info frame -$level] p f l\n"
1299 " lappend trace $p $f $l\n"
1300 " }\n"
1301 " return $trace\n"
1302 "}\n"
1303 "\n"
1304 "\n"
1305 "proc stackdump {stacktrace} {\n"
1306 " set result {}\n"
1307 " set count 0\n"
1308 " foreach {l f p} [lreverse $stacktrace] {\n"
1309 " if {$count} {\n"
1310 " append result \\n\n"
1311 " }\n"
1312 " incr count\n"
1313 " if {$p ne \"\"} {\n"
1314 " append result \"in procedure '$p' \"\n"
1315 " if {$f ne \"\"} {\n"
1316 " append result \"called \"\n"
1317 " }\n"
1318 " }\n"
1319 " if {$f ne \"\"} {\n"
1320 " append result \"at file \\\"$f\\\", line $l\"\n"
1321 " }\n"
1322 " }\n"
1323 " return $result\n"
1324 "}\n"
1325 "\n"
1326 "\n"
1327 "\n"
1328 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1329 " if {$stacktrace eq \"\"} {\n"
1330 " set stacktrace [info stacktrace]\n"
1331 " }\n"
1332 " lassign $stacktrace p f l\n"
1333 " if {$f ne \"\"} {\n"
1334 " set result \"Runtime Error: $f:$l: \"\n"
1335 " }\n"
1336 " append result \"$msg\\n\"\n"
1337 " append result [stackdump $stacktrace]\n"
1338 "\n"
1339 "\n"
1340 " string trim $result\n"
1341 "}\n"
1342 "\n"
1343 "\n"
1344 "\n"
1345 "proc {info nameofexecutable} {} {\n"
1346 " if {[info exists ::jim_argv0]} {\n"
1347 " if {[string match \"*/*\" $::jim_argv0]} {\n"
1348 " return [file join [pwd] $::jim_argv0]\n"
1349 " }\n"
1350 " foreach path [split [env PATH \"\"] $::tcl_platform(pathSeparator)] {\n"
1351 " set exec [file join [pwd] [string map {\\\\ /} $path] $::jim_argv0]\n"
1352 " if {[file executable $exec]} {\n"
1353 " return $exec\n"
1354 " }\n"
1355 " }\n"
1356 " }\n"
1357 " return \"\"\n"
1358 "}\n"
1359 "\n"
1360 "\n"
1361 "proc {dict with} {dictVar args script} {\n"
1362 " upvar $dictVar dict\n"
1363 " set keys {}\n"
1364 " foreach {n v} [dict get $dict {*}$args] {\n"
1365 " upvar $n var_$n\n"
1366 " set var_$n $v\n"
1367 " lappend keys $n\n"
1368 " }\n"
1369 " catch {uplevel 1 $script} msg opts\n"
1370 " if {[info exists dict] && [dict exists $dict {*}$args]} {\n"
1371 " foreach n $keys {\n"
1372 " if {[info exists var_$n]} {\n"
1373 " dict set dict {*}$args $n [set var_$n]\n"
1374 " } else {\n"
1375 " dict unset dict {*}$args $n\n"
1376 " }\n"
1377 " }\n"
1378 " }\n"
1379 " return {*}$opts $msg\n"
1380 "}\n"
1381 "\n"
1382 "\n"
1383 "\n"
1384 "proc {dict merge} {dict args} {\n"
1385 " foreach d $args {\n"
1386 "\n"
1387 " dict size $d\n"
1388 " foreach {k v} $d {\n"
1389 " dict set dict $k $v\n"
1390 " }\n"
1391 " }\n"
1392 " return $dict\n"
1393 "}\n"
1396 int Jim_tclcompatInit(Jim_Interp *interp)
1398 if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1399 return JIM_ERR;
1401 return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1402 "\n"
1403 "\n"
1404 "\n"
1405 "\n"
1406 "\n"
1407 "\n"
1408 "\n"
1409 "set env [env]\n"
1410 "\n"
1411 "if {[info commands stdout] ne \"\"} {\n"
1412 "\n"
1413 " foreach p {gets flush close eof seek tell} {\n"
1414 " proc $p {chan args} {p} {\n"
1415 " tailcall $chan $p {*}$args\n"
1416 " }\n"
1417 " }\n"
1418 " unset p\n"
1419 "\n"
1420 "\n"
1421 "\n"
1422 " proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1423 " if {${-nonewline} ni {-nonewline {}}} {\n"
1424 " tailcall ${-nonewline} puts $msg\n"
1425 " }\n"
1426 " tailcall $chan puts {*}${-nonewline} $msg\n"
1427 " }\n"
1428 "\n"
1429 "\n"
1430 "\n"
1431 "\n"
1432 "\n"
1433 " proc read {{-nonewline {}} chan} {\n"
1434 " if {${-nonewline} ni {-nonewline {}}} {\n"
1435 " tailcall ${-nonewline} read {*}${chan}\n"
1436 " }\n"
1437 " tailcall $chan read {*}${-nonewline}\n"
1438 " }\n"
1439 "\n"
1440 " proc fconfigure {f args} {\n"
1441 " foreach {n v} $args {\n"
1442 " switch -glob -- $n {\n"
1443 " -bl* {\n"
1444 " $f ndelay $(!$v)\n"
1445 " }\n"
1446 " -bu* {\n"
1447 " $f buffering $v\n"
1448 " }\n"
1449 " -tr* {\n"
1450 "\n"
1451 " }\n"
1452 " default {\n"
1453 " return -code error \"fconfigure: unknown option $n\"\n"
1454 " }\n"
1455 " }\n"
1456 " }\n"
1457 " }\n"
1458 "}\n"
1459 "\n"
1460 "\n"
1461 "proc case {var args} {\n"
1462 "\n"
1463 " if {[lindex $args 0] eq \"in\"} {\n"
1464 " set args [lrange $args 1 end]\n"
1465 " }\n"
1466 "\n"
1467 "\n"
1468 " if {[llength $args] == 1} {\n"
1469 " set args [lindex $args 0]\n"
1470 " }\n"
1471 "\n"
1472 "\n"
1473 " if {[llength $args] % 2 != 0} {\n"
1474 " return -code error \"extra case pattern with no body\"\n"
1475 " }\n"
1476 "\n"
1477 "\n"
1478 " local proc case.checker {value pattern} {\n"
1479 " string match $pattern $value\n"
1480 " }\n"
1481 "\n"
1482 " foreach {value action} $args {\n"
1483 " if {$value eq \"default\"} {\n"
1484 " set do_action $action\n"
1485 " continue\n"
1486 " } elseif {[lsearch -bool -command case.checker $value $var]} {\n"
1487 " set do_action $action\n"
1488 " break\n"
1489 " }\n"
1490 " }\n"
1491 "\n"
1492 " if {[info exists do_action]} {\n"
1493 " set rc [catch [list uplevel 1 $do_action] result opts]\n"
1494 " if {$rc} {\n"
1495 " incr opts(-level)\n"
1496 " }\n"
1497 " return {*}$opts $result\n"
1498 " }\n"
1499 "}\n"
1500 "\n"
1501 "\n"
1502 "proc fileevent {args} {\n"
1503 " tailcall {*}$args\n"
1504 "}\n"
1505 "\n"
1506 "\n"
1507 "\n"
1508 "\n"
1509 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1510 " upvar $arrayname a\n"
1511 "\n"
1512 " set max 0\n"
1513 " foreach name [array names a $pattern]] {\n"
1514 " if {[string length $name] > $max} {\n"
1515 " set max [string length $name]\n"
1516 " }\n"
1517 " }\n"
1518 " incr max [string length $arrayname]\n"
1519 " incr max 2\n"
1520 " foreach name [lsort [array names a $pattern]] {\n"
1521 " $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1522 " }\n"
1523 "}\n"
1524 "\n"
1525 "\n"
1526 "proc {file copy} {{force {}} source target} {\n"
1527 " try {\n"
1528 " if {$force ni {{} -force}} {\n"
1529 " error \"bad option \\\"$force\\\": should be -force\"\n"
1530 " }\n"
1531 "\n"
1532 " set in [open $source]\n"
1533 "\n"
1534 " if {$force eq \"\" && [file exists $target]} {\n"
1535 " $in close\n"
1536 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1537 " }\n"
1538 " set out [open $target w]\n"
1539 " $in copyto $out\n"
1540 " $out close\n"
1541 " } on error {msg opts} {\n"
1542 " incr opts(-level)\n"
1543 " return {*}$opts $msg\n"
1544 " } finally {\n"
1545 " catch {$in close}\n"
1546 " }\n"
1547 "}\n"
1548 "\n"
1549 "\n"
1550 "\n"
1551 "proc popen {cmd {mode r}} {\n"
1552 " lassign [socket pipe] r w\n"
1553 " try {\n"
1554 " if {[string match \"w*\" $mode]} {\n"
1555 " lappend cmd <@$r &\n"
1556 " set pids [exec {*}$cmd]\n"
1557 " $r close\n"
1558 " set f $w\n"
1559 " } else {\n"
1560 " lappend cmd >@$w &\n"
1561 " set pids [exec {*}$cmd]\n"
1562 " $w close\n"
1563 " set f $r\n"
1564 " }\n"
1565 " lambda {cmd args} {f pids} {\n"
1566 " if {$cmd eq \"pid\"} {\n"
1567 " return $pids\n"
1568 " }\n"
1569 " if {$cmd eq \"close\"} {\n"
1570 " $f close\n"
1571 "\n"
1572 " foreach p $pids { os.wait $p }\n"
1573 " return\n"
1574 " }\n"
1575 " tailcall $f $cmd {*}$args\n"
1576 " }\n"
1577 " } on error {error opts} {\n"
1578 " $r close\n"
1579 " $w close\n"
1580 " error $error\n"
1581 " }\n"
1582 "}\n"
1583 "\n"
1584 "\n"
1585 "local proc pid {{chan {}}} {\n"
1586 " if {$chan eq \"\"} {\n"
1587 " tailcall upcall pid\n"
1588 " }\n"
1589 " if {[catch {$chan tell}]} {\n"
1590 " return -code error \"can not find channel named \\\"$chan\\\"\"\n"
1591 " }\n"
1592 " if {[catch {$chan pid} pids]} {\n"
1593 " return \"\"\n"
1594 " }\n"
1595 " return $pids\n"
1596 "}\n"
1597 "\n"
1598 "\n"
1599 "\n"
1600 "\n"
1601 "\n"
1602 "\n"
1603 "\n"
1604 "\n"
1605 "\n"
1606 "\n"
1607 "\n"
1608 "\n"
1609 "\n"
1610 "\n"
1611 "proc try {args} {\n"
1612 " set catchopts {}\n"
1613 " while {[string match -* [lindex $args 0]]} {\n"
1614 " set args [lassign $args opt]\n"
1615 " if {$opt eq \"--\"} {\n"
1616 " break\n"
1617 " }\n"
1618 " lappend catchopts $opt\n"
1619 " }\n"
1620 " if {[llength $args] == 0} {\n"
1621 " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1622 " }\n"
1623 " set args [lassign $args script]\n"
1624 " set code [catch -eval {*}$catchopts [list uplevel 1 $script] msg opts]\n"
1625 "\n"
1626 " set handled 0\n"
1627 "\n"
1628 " foreach {on codes vars script} $args {\n"
1629 " switch -- $on \\\n"
1630 " on {\n"
1631 " if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n"
1632 " lassign $vars msgvar optsvar\n"
1633 " if {$msgvar ne \"\"} {\n"
1634 " upvar $msgvar hmsg\n"
1635 " set hmsg $msg\n"
1636 " }\n"
1637 " if {$optsvar ne \"\"} {\n"
1638 " upvar $optsvar hopts\n"
1639 " set hopts $opts\n"
1640 " }\n"
1641 "\n"
1642 " set code [catch [list uplevel 1 $script] msg opts]\n"
1643 " incr handled\n"
1644 " }\n"
1645 " } \\\n"
1646 " finally {\n"
1647 " set finalcode [catch [list uplevel 1 $codes] finalmsg finalopts]\n"
1648 " if {$finalcode} {\n"
1649 "\n"
1650 " set code $finalcode\n"
1651 " set msg $finalmsg\n"
1652 " set opts $finalopts\n"
1653 " }\n"
1654 " break\n"
1655 " } \\\n"
1656 " default {\n"
1657 " return -code error \"try: expected 'on' or 'finally', got '$on'\"\n"
1658 " }\n"
1659 " }\n"
1660 "\n"
1661 " if {$code} {\n"
1662 " incr opts(-level)\n"
1663 " return {*}$opts $msg\n"
1664 " }\n"
1665 " return $msg\n"
1666 "}\n"
1667 "\n"
1668 "\n"
1669 "\n"
1670 "proc throw {code {msg \"\"}} {\n"
1671 " return -code $code $msg\n"
1672 "}\n"
1673 "\n"
1674 "\n"
1675 "proc {file delete force} {path} {\n"
1676 " foreach e [readdir $path] {\n"
1677 " file delete -force $path/$e\n"
1678 " }\n"
1679 " file delete $path\n"
1680 "}\n"
1686 #include <stdio.h>
1687 #include <string.h>
1688 #include <errno.h>
1689 #include <fcntl.h>
1692 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
1693 #include <sys/socket.h>
1694 #include <netinet/in.h>
1695 #include <arpa/inet.h>
1696 #include <netdb.h>
1697 #include <unistd.h>
1698 #ifdef HAVE_SYS_UN_H
1699 #include <sys/un.h>
1700 #endif
1701 #else
1702 #define JIM_ANSIC
1703 #endif
1706 #define AIO_CMD_LEN 32
1707 #define AIO_BUF_LEN 256
1709 #ifndef HAVE_FTELLO
1710 #define ftello ftell
1711 #endif
1712 #ifndef HAVE_FSEEKO
1713 #define fseeko fseek
1714 #endif
1716 #define AIO_KEEPOPEN 1
1718 #if defined(JIM_IPV6)
1719 #define IPV6 1
1720 #else
1721 #define IPV6 0
1722 #ifndef PF_INET6
1723 #define PF_INET6 0
1724 #endif
1725 #endif
1728 typedef struct AioFile
1730 FILE *fp;
1731 Jim_Obj *filename;
1732 int type;
1733 int OpenFlags;
1734 int fd;
1735 #ifdef O_NDELAY
1736 int flags;
1737 #endif
1738 Jim_Obj *rEvent;
1739 Jim_Obj *wEvent;
1740 Jim_Obj *eEvent;
1741 int addr_family;
1742 } AioFile;
1744 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1745 static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1746 const char *hdlfmt, int family, const char *mode);
1749 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
1751 if (name) {
1752 Jim_SetResultFormatted(interp, "%#s: %s", name, strerror(errno));
1754 else {
1755 Jim_SetResultString(interp, strerror(errno), -1);
1759 static void JimAioDelProc(Jim_Interp *interp, void *privData)
1761 AioFile *af = privData;
1763 JIM_NOTUSED(interp);
1765 if (!(af->OpenFlags & AIO_KEEPOPEN)) {
1766 fclose(af->fp);
1769 Jim_DecrRefCount(interp, af->filename);
1771 #ifdef jim_ext_eventloop
1773 Jim_DeleteFileHandler(interp, af->fp, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
1774 #endif
1775 Jim_Free(af);
1778 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
1780 if (!ferror(af->fp)) {
1781 return JIM_OK;
1783 clearerr(af->fp);
1785 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
1786 return JIM_OK;
1788 #ifdef ECONNRESET
1789 if (errno == ECONNRESET) {
1790 return JIM_OK;
1792 #endif
1793 #ifdef ECONNABORTED
1794 if (errno != ECONNABORTED) {
1795 return JIM_OK;
1797 #endif
1798 JimAioSetError(interp, af->filename);
1799 return JIM_ERR;
1802 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1804 AioFile *af = Jim_CmdPrivData(interp);
1805 char buf[AIO_BUF_LEN];
1806 Jim_Obj *objPtr;
1807 int nonewline = 0;
1808 jim_wide neededLen = -1;
1810 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
1811 nonewline = 1;
1812 argv++;
1813 argc--;
1815 if (argc == 1) {
1816 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
1817 return JIM_ERR;
1818 if (neededLen < 0) {
1819 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
1820 return JIM_ERR;
1823 else if (argc) {
1824 return -1;
1826 objPtr = Jim_NewStringObj(interp, NULL, 0);
1827 while (neededLen != 0) {
1828 int retval;
1829 int readlen;
1831 if (neededLen == -1) {
1832 readlen = AIO_BUF_LEN;
1834 else {
1835 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
1837 retval = fread(buf, 1, readlen, af->fp);
1838 if (retval > 0) {
1839 Jim_AppendString(interp, objPtr, buf, retval);
1840 if (neededLen != -1) {
1841 neededLen -= retval;
1844 if (retval != readlen)
1845 break;
1848 if (JimCheckStreamError(interp, af)) {
1849 Jim_FreeNewObj(interp, objPtr);
1850 return JIM_ERR;
1852 if (nonewline) {
1853 int len;
1854 const char *s = Jim_GetString(objPtr, &len);
1856 if (len > 0 && s[len - 1] == '\n') {
1857 objPtr->length--;
1858 objPtr->bytes[objPtr->length] = '\0';
1861 Jim_SetResult(interp, objPtr);
1862 return JIM_OK;
1865 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1867 AioFile *af = Jim_CmdPrivData(interp);
1868 jim_wide count = 0;
1869 jim_wide maxlen = JIM_WIDE_MAX;
1870 FILE *outfh = Jim_AioFilehandle(interp, argv[0]);
1872 if (outfh == NULL) {
1873 return JIM_ERR;
1876 if (argc == 2) {
1877 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
1878 return JIM_ERR;
1882 while (count < maxlen) {
1883 int ch = fgetc(af->fp);
1885 if (ch == EOF || fputc(ch, outfh) == EOF) {
1886 break;
1888 count++;
1891 if (ferror(af->fp)) {
1892 Jim_SetResultFormatted(interp, "error while reading: %s", strerror(errno));
1893 clearerr(af->fp);
1894 return JIM_ERR;
1897 if (ferror(outfh)) {
1898 Jim_SetResultFormatted(interp, "error while writing: %s", strerror(errno));
1899 clearerr(outfh);
1900 return JIM_ERR;
1903 Jim_SetResultInt(interp, count);
1905 return JIM_OK;
1908 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1910 AioFile *af = Jim_CmdPrivData(interp);
1911 char buf[AIO_BUF_LEN];
1912 Jim_Obj *objPtr;
1913 int len;
1915 errno = 0;
1917 objPtr = Jim_NewStringObj(interp, NULL, 0);
1918 while (1) {
1919 buf[AIO_BUF_LEN - 1] = '_';
1920 if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL)
1921 break;
1923 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
1924 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
1926 else {
1927 len = strlen(buf);
1929 if (len && (buf[len - 1] == '\n')) {
1931 len--;
1934 Jim_AppendString(interp, objPtr, buf, len);
1935 break;
1938 if (JimCheckStreamError(interp, af)) {
1940 Jim_FreeNewObj(interp, objPtr);
1941 return JIM_ERR;
1944 if (argc) {
1945 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
1946 Jim_FreeNewObj(interp, objPtr);
1947 return JIM_ERR;
1950 len = Jim_Length(objPtr);
1952 if (len == 0 && feof(af->fp)) {
1954 len = -1;
1956 Jim_SetResultInt(interp, len);
1958 else {
1959 Jim_SetResult(interp, objPtr);
1961 return JIM_OK;
1964 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1966 AioFile *af = Jim_CmdPrivData(interp);
1967 int wlen;
1968 const char *wdata;
1969 Jim_Obj *strObj;
1971 if (argc == 2) {
1972 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
1973 return -1;
1975 strObj = argv[1];
1977 else {
1978 strObj = argv[0];
1981 wdata = Jim_GetString(strObj, &wlen);
1982 if (fwrite(wdata, 1, wlen, af->fp) == (unsigned)wlen) {
1983 if (argc == 2 || putc('\n', af->fp) != EOF) {
1984 return JIM_OK;
1987 JimAioSetError(interp, af->filename);
1988 return JIM_ERR;
1991 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1993 #ifdef HAVE_ISATTY
1994 AioFile *af = Jim_CmdPrivData(interp);
1995 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
1996 #else
1997 Jim_SetResultInt(interp, 0);
1998 #endif
2000 return JIM_OK;
2004 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2006 AioFile *af = Jim_CmdPrivData(interp);
2008 if (fflush(af->fp) == EOF) {
2009 JimAioSetError(interp, af->filename);
2010 return JIM_ERR;
2012 return JIM_OK;
2015 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2017 AioFile *af = Jim_CmdPrivData(interp);
2019 Jim_SetResultInt(interp, feof(af->fp));
2020 return JIM_OK;
2023 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2025 Jim_DeleteCommand(interp, Jim_String(argv[0]));
2026 return JIM_OK;
2029 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2031 AioFile *af = Jim_CmdPrivData(interp);
2032 int orig = SEEK_SET;
2033 jim_wide offset;
2035 if (argc == 2) {
2036 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2037 orig = SEEK_SET;
2038 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2039 orig = SEEK_CUR;
2040 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2041 orig = SEEK_END;
2042 else {
2043 return -1;
2046 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2047 return JIM_ERR;
2049 if (fseeko(af->fp, offset, orig) == -1) {
2050 JimAioSetError(interp, af->filename);
2051 return JIM_ERR;
2053 return JIM_OK;
2056 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2058 AioFile *af = Jim_CmdPrivData(interp);
2060 Jim_SetResultInt(interp, ftello(af->fp));
2061 return JIM_OK;
2064 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2066 AioFile *af = Jim_CmdPrivData(interp);
2068 Jim_SetResult(interp, af->filename);
2069 return JIM_OK;
2072 #ifdef O_NDELAY
2073 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2075 AioFile *af = Jim_CmdPrivData(interp);
2077 int fmode = af->flags;
2079 if (argc) {
2080 long nb;
2082 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2083 return JIM_ERR;
2085 if (nb) {
2086 fmode |= O_NDELAY;
2088 else {
2089 fmode &= ~O_NDELAY;
2091 fcntl(af->fd, F_SETFL, fmode);
2092 af->flags = fmode;
2094 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
2095 return JIM_OK;
2097 #endif
2099 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2101 AioFile *af = Jim_CmdPrivData(interp);
2103 static const char * const options[] = {
2104 "none",
2105 "line",
2106 "full",
2107 NULL
2109 enum
2111 OPT_NONE,
2112 OPT_LINE,
2113 OPT_FULL,
2115 int option;
2117 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2118 return JIM_ERR;
2120 switch (option) {
2121 case OPT_NONE:
2122 setvbuf(af->fp, NULL, _IONBF, 0);
2123 break;
2124 case OPT_LINE:
2125 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
2126 break;
2127 case OPT_FULL:
2128 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
2129 break;
2131 return JIM_OK;
2134 #ifdef jim_ext_eventloop
2135 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
2137 Jim_Obj **objPtrPtr = clientData;
2139 Jim_DecrRefCount(interp, *objPtrPtr);
2140 *objPtrPtr = NULL;
2143 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
2145 Jim_Obj **objPtrPtr = clientData;
2147 return Jim_EvalObjBackground(interp, *objPtrPtr);
2150 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
2151 int argc, Jim_Obj * const *argv)
2153 if (argc == 0) {
2155 if (*scriptHandlerObj) {
2156 Jim_SetResult(interp, *scriptHandlerObj);
2158 return JIM_OK;
2161 if (*scriptHandlerObj) {
2163 Jim_DeleteFileHandler(interp, af->fp, mask);
2167 if (Jim_Length(argv[0]) == 0) {
2169 return JIM_OK;
2173 Jim_IncrRefCount(argv[0]);
2174 *scriptHandlerObj = argv[0];
2176 Jim_CreateFileHandler(interp, af->fp, mask,
2177 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
2179 return JIM_OK;
2182 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2184 AioFile *af = Jim_CmdPrivData(interp);
2186 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
2189 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2191 AioFile *af = Jim_CmdPrivData(interp);
2193 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
2196 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2198 AioFile *af = Jim_CmdPrivData(interp);
2200 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->wEvent, argc, argv);
2202 #endif
2204 static const jim_subcmd_type aio_command_table[] = {
2205 { "read",
2206 "?-nonewline? ?len?",
2207 aio_cmd_read,
2212 { "copyto",
2213 "handle ?size?",
2214 aio_cmd_copy,
2219 { "gets",
2220 "?var?",
2221 aio_cmd_gets,
2226 { "puts",
2227 "?-nonewline? str",
2228 aio_cmd_puts,
2233 { "isatty",
2234 NULL,
2235 aio_cmd_isatty,
2240 { "flush",
2241 NULL,
2242 aio_cmd_flush,
2247 { "eof",
2248 NULL,
2249 aio_cmd_eof,
2254 { "close",
2255 NULL,
2256 aio_cmd_close,
2259 JIM_MODFLAG_FULLARGV,
2262 { "seek",
2263 "offset ?start|current|end",
2264 aio_cmd_seek,
2269 { "tell",
2270 NULL,
2271 aio_cmd_tell,
2276 { "filename",
2277 NULL,
2278 aio_cmd_filename,
2283 #ifdef O_NDELAY
2284 { "ndelay",
2285 "?0|1?",
2286 aio_cmd_ndelay,
2291 #endif
2292 { "buffering",
2293 "none|line|full",
2294 aio_cmd_buffering,
2299 #ifdef jim_ext_eventloop
2300 { "readable",
2301 "?readable-script?",
2302 aio_cmd_readable,
2307 { "writable",
2308 "?writable-script?",
2309 aio_cmd_writable,
2314 { "onexception",
2315 "?exception-script?",
2316 aio_cmd_onexception,
2321 #endif
2322 { NULL }
2325 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2327 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
2330 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
2331 Jim_Obj *const *argv)
2333 const char *mode;
2334 const char *filename;
2336 if (argc != 2 && argc != 3) {
2337 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
2338 return JIM_ERR;
2341 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
2342 filename = Jim_String(argv[1]);
2344 #ifdef jim_ext_tclcompat
2346 if (*filename == '|') {
2347 Jim_Obj *evalObj[3];
2349 evalObj[0] = Jim_NewStringObj(interp, "popen", -1);
2350 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
2351 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
2353 return Jim_EvalObjVector(interp, 3, evalObj);
2355 #endif
2356 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode);
2359 static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2360 const char *hdlfmt, int family, const char *mode)
2362 AioFile *af;
2363 char buf[AIO_CMD_LEN];
2364 int OpenFlags = 0;
2366 if (filename == NULL) {
2367 filename = Jim_NewStringObj(interp, hdlfmt, -1);
2370 Jim_IncrRefCount(filename);
2372 if (fh == NULL) {
2373 if (fd < 0) {
2374 fh = fopen(Jim_String(filename), mode);
2376 else {
2377 fh = fdopen(fd, mode);
2380 else {
2381 OpenFlags = AIO_KEEPOPEN;
2384 if (fh == NULL) {
2385 JimAioSetError(interp, filename);
2386 #if !defined(JIM_ANSIC)
2387 if (fd >= 0) {
2388 close(fd);
2390 #endif
2391 Jim_DecrRefCount(interp, filename);
2392 return JIM_ERR;
2396 af = Jim_Alloc(sizeof(*af));
2397 memset(af, 0, sizeof(*af));
2398 af->fp = fh;
2399 af->fd = fileno(fh);
2400 af->filename = filename;
2401 #ifdef FD_CLOEXEC
2402 if ((OpenFlags & AIO_KEEPOPEN) == 0) {
2403 fcntl(af->fd, F_SETFD, FD_CLOEXEC);
2405 #endif
2406 af->OpenFlags = OpenFlags;
2407 #ifdef O_NDELAY
2408 af->flags = fcntl(af->fd, F_GETFL);
2409 #endif
2410 af->addr_family = family;
2411 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2412 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2414 Jim_SetResultString(interp, buf, -1);
2416 return JIM_OK;
2420 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2422 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2424 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2425 return ((AioFile *) cmdPtr->u.native.privData)->fp;
2427 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2428 return NULL;
2431 int Jim_aioInit(Jim_Interp *interp)
2433 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2434 return JIM_ERR;
2436 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2437 #ifndef JIM_ANSIC
2438 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2439 #endif
2442 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2443 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2444 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2446 return JIM_OK;
2450 #include <errno.h>
2451 #include <stdio.h>
2452 #include <string.h>
2455 #ifdef HAVE_DIRENT_H
2456 #include <dirent.h>
2457 #endif
2459 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2461 const char *dirPath;
2462 DIR *dirPtr;
2463 struct dirent *entryPtr;
2464 int nocomplain = 0;
2466 if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
2467 nocomplain = 1;
2469 if (argc != 2 && !nocomplain) {
2470 Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
2471 return JIM_ERR;
2474 dirPath = Jim_String(argv[1 + nocomplain]);
2476 dirPtr = opendir(dirPath);
2477 if (dirPtr == NULL) {
2478 if (nocomplain) {
2479 return JIM_OK;
2481 Jim_SetResultString(interp, strerror(errno), -1);
2482 return JIM_ERR;
2484 Jim_SetResultString(interp, strerror(errno), -1);
2486 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
2488 while ((entryPtr = readdir(dirPtr)) != NULL) {
2489 if (entryPtr->d_name[0] == '.') {
2490 if (entryPtr->d_name[1] == '\0') {
2491 continue;
2493 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
2494 continue;
2496 Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp,
2497 entryPtr->d_name, -1));
2499 closedir(dirPtr);
2501 return JIM_OK;
2504 int Jim_readdirInit(Jim_Interp *interp)
2506 if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG))
2507 return JIM_ERR;
2509 Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
2510 return JIM_OK;
2513 #include <stdlib.h>
2514 #include <string.h>
2517 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2519 regfree(objPtr->internalRep.regexpValue.compre);
2520 Jim_Free(objPtr->internalRep.regexpValue.compre);
2523 static const Jim_ObjType regexpObjType = {
2524 "regexp",
2525 FreeRegexpInternalRep,
2526 NULL,
2527 NULL,
2528 JIM_TYPE_NONE
2531 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
2533 regex_t *compre;
2534 const char *pattern;
2535 int ret;
2538 if (objPtr->typePtr == &regexpObjType &&
2539 objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) {
2541 return objPtr->internalRep.regexpValue.compre;
2547 pattern = Jim_String(objPtr);
2548 compre = Jim_Alloc(sizeof(regex_t));
2550 if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
2551 char buf[100];
2553 regerror(ret, compre, buf, sizeof(buf));
2554 Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
2555 regfree(compre);
2556 Jim_Free(compre);
2557 return NULL;
2560 Jim_FreeIntRep(interp, objPtr);
2562 objPtr->typePtr = &regexpObjType;
2563 objPtr->internalRep.regexpValue.flags = flags;
2564 objPtr->internalRep.regexpValue.compre = compre;
2566 return compre;
2569 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2571 int opt_indices = 0;
2572 int opt_all = 0;
2573 int opt_inline = 0;
2574 regex_t *regex;
2575 int match, i, j;
2576 int offset = 0;
2577 regmatch_t *pmatch = NULL;
2578 int source_len;
2579 int result = JIM_OK;
2580 const char *pattern;
2581 const char *source_str;
2582 int num_matches = 0;
2583 int num_vars;
2584 Jim_Obj *resultListObj = NULL;
2585 int regcomp_flags = 0;
2586 int eflags = 0;
2587 int option;
2588 enum {
2589 OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
2591 static const char * const options[] = {
2592 "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
2595 if (argc < 3) {
2596 wrongNumArgs:
2597 Jim_WrongNumArgs(interp, 1, argv,
2598 "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
2599 return JIM_ERR;
2602 for (i = 1; i < argc; i++) {
2603 const char *opt = Jim_String(argv[i]);
2605 if (*opt != '-') {
2606 break;
2608 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
2609 return JIM_ERR;
2611 if (option == OPT_END) {
2612 i++;
2613 break;
2615 switch (option) {
2616 case OPT_INDICES:
2617 opt_indices = 1;
2618 break;
2620 case OPT_NOCASE:
2621 regcomp_flags |= REG_ICASE;
2622 break;
2624 case OPT_LINE:
2625 regcomp_flags |= REG_NEWLINE;
2626 break;
2628 case OPT_ALL:
2629 opt_all = 1;
2630 break;
2632 case OPT_INLINE:
2633 opt_inline = 1;
2634 break;
2636 case OPT_START:
2637 if (++i == argc) {
2638 goto wrongNumArgs;
2640 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
2641 return JIM_ERR;
2643 break;
2646 if (argc - i < 2) {
2647 goto wrongNumArgs;
2650 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
2651 if (!regex) {
2652 return JIM_ERR;
2655 pattern = Jim_String(argv[i]);
2656 source_str = Jim_GetString(argv[i + 1], &source_len);
2658 num_vars = argc - i - 2;
2660 if (opt_inline) {
2661 if (num_vars) {
2662 Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
2663 -1);
2664 result = JIM_ERR;
2665 goto done;
2667 num_vars = regex->re_nsub + 1;
2670 pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
2672 if (offset) {
2673 if (offset < 0) {
2674 offset += source_len + 1;
2676 if (offset > source_len) {
2677 source_str += source_len;
2679 else if (offset > 0) {
2680 source_str += offset;
2682 eflags |= REG_NOTBOL;
2685 if (opt_inline) {
2686 resultListObj = Jim_NewListObj(interp, NULL, 0);
2689 next_match:
2690 match = regexec(regex, source_str, num_vars + 1, pmatch, eflags);
2691 if (match >= REG_BADPAT) {
2692 char buf[100];
2694 regerror(match, regex, buf, sizeof(buf));
2695 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
2696 result = JIM_ERR;
2697 goto done;
2700 if (match == REG_NOMATCH) {
2701 goto done;
2704 num_matches++;
2706 if (opt_all && !opt_inline) {
2708 goto try_next_match;
2712 j = 0;
2713 for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
2714 Jim_Obj *resultObj;
2716 if (opt_indices) {
2717 resultObj = Jim_NewListObj(interp, NULL, 0);
2719 else {
2720 resultObj = Jim_NewStringObj(interp, "", 0);
2723 if (pmatch[j].rm_so == -1) {
2724 if (opt_indices) {
2725 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
2726 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
2729 else {
2730 int len = pmatch[j].rm_eo - pmatch[j].rm_so;
2732 if (opt_indices) {
2733 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
2734 offset + pmatch[j].rm_so));
2735 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
2736 offset + pmatch[j].rm_so + len - 1));
2738 else {
2739 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len);
2743 if (opt_inline) {
2744 Jim_ListAppendElement(interp, resultListObj, resultObj);
2746 else {
2748 result = Jim_SetVariable(interp, argv[i], resultObj);
2750 if (result != JIM_OK) {
2751 Jim_FreeObj(interp, resultObj);
2752 break;
2757 try_next_match:
2758 if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
2759 if (pmatch[0].rm_eo) {
2760 offset += pmatch[0].rm_eo;
2761 source_str += pmatch[0].rm_eo;
2763 else {
2764 source_str++;
2765 offset++;
2767 if (*source_str) {
2768 eflags = REG_NOTBOL;
2769 goto next_match;
2773 done:
2774 if (result == JIM_OK) {
2775 if (opt_inline) {
2776 Jim_SetResult(interp, resultListObj);
2778 else {
2779 Jim_SetResultInt(interp, num_matches);
2783 Jim_Free(pmatch);
2784 return result;
2787 #define MAX_SUB_MATCHES 50
2789 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2791 int regcomp_flags = 0;
2792 int regexec_flags = 0;
2793 int opt_all = 0;
2794 int offset = 0;
2795 regex_t *regex;
2796 const char *p;
2797 int result;
2798 regmatch_t pmatch[MAX_SUB_MATCHES + 1];
2799 int num_matches = 0;
2801 int i, j, n;
2802 Jim_Obj *varname;
2803 Jim_Obj *resultObj;
2804 const char *source_str;
2805 int source_len;
2806 const char *replace_str;
2807 int replace_len;
2808 const char *pattern;
2809 int option;
2810 enum {
2811 OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END
2813 static const char * const options[] = {
2814 "-nocase", "-line", "-all", "-start", "--", NULL
2817 if (argc < 4) {
2818 wrongNumArgs:
2819 Jim_WrongNumArgs(interp, 1, argv,
2820 "?switches? exp string subSpec ?varName?");
2821 return JIM_ERR;
2824 for (i = 1; i < argc; i++) {
2825 const char *opt = Jim_String(argv[i]);
2827 if (*opt != '-') {
2828 break;
2830 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
2831 return JIM_ERR;
2833 if (option == OPT_END) {
2834 i++;
2835 break;
2837 switch (option) {
2838 case OPT_NOCASE:
2839 regcomp_flags |= REG_ICASE;
2840 break;
2842 case OPT_LINE:
2843 regcomp_flags |= REG_NEWLINE;
2844 break;
2846 case OPT_ALL:
2847 opt_all = 1;
2848 break;
2850 case OPT_START:
2851 if (++i == argc) {
2852 goto wrongNumArgs;
2854 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
2855 return JIM_ERR;
2857 break;
2860 if (argc - i != 3 && argc - i != 4) {
2861 goto wrongNumArgs;
2864 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
2865 if (!regex) {
2866 return JIM_ERR;
2868 pattern = Jim_String(argv[i]);
2870 source_str = Jim_GetString(argv[i + 1], &source_len);
2871 replace_str = Jim_GetString(argv[i + 2], &replace_len);
2872 varname = argv[i + 3];
2875 resultObj = Jim_NewStringObj(interp, "", 0);
2877 if (offset) {
2878 if (offset < 0) {
2879 offset += source_len + 1;
2881 if (offset > source_len) {
2882 offset = source_len;
2884 else if (offset < 0) {
2885 offset = 0;
2890 Jim_AppendString(interp, resultObj, source_str, offset);
2893 n = source_len - offset;
2894 p = source_str + offset;
2895 do {
2896 int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
2898 if (match >= REG_BADPAT) {
2899 char buf[100];
2901 regerror(match, regex, buf, sizeof(buf));
2902 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
2903 return JIM_ERR;
2905 if (match == REG_NOMATCH) {
2906 break;
2909 num_matches++;
2911 Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
2914 for (j = 0; j < replace_len; j++) {
2915 int idx;
2916 int c = replace_str[j];
2918 if (c == '&') {
2919 idx = 0;
2921 else if (c == '\\' && j < replace_len) {
2922 c = replace_str[++j];
2923 if ((c >= '0') && (c <= '9')) {
2924 idx = c - '0';
2926 else if ((c == '\\') || (c == '&')) {
2927 Jim_AppendString(interp, resultObj, replace_str + j, 1);
2928 continue;
2930 else {
2931 Jim_AppendString(interp, resultObj, replace_str + j - 1, 2);
2932 continue;
2935 else {
2936 Jim_AppendString(interp, resultObj, replace_str + j, 1);
2937 continue;
2939 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
2940 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
2941 pmatch[idx].rm_eo - pmatch[idx].rm_so);
2945 p += pmatch[0].rm_eo;
2946 n -= pmatch[0].rm_eo;
2949 if (!opt_all || n == 0) {
2950 break;
2954 if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
2955 break;
2959 if (pattern[0] == '\0' && n) {
2961 Jim_AppendString(interp, resultObj, p, 1);
2962 p++;
2963 n--;
2966 regexec_flags |= REG_NOTBOL;
2967 } while (n);
2969 Jim_AppendString(interp, resultObj, p, -1);
2972 if (argc - i == 4) {
2973 result = Jim_SetVariable(interp, varname, resultObj);
2975 if (result == JIM_OK) {
2976 Jim_SetResultInt(interp, num_matches);
2978 else {
2979 Jim_FreeObj(interp, resultObj);
2982 else {
2983 Jim_SetResult(interp, resultObj);
2984 result = JIM_OK;
2987 return result;
2990 int Jim_regexpInit(Jim_Interp *interp)
2992 if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG))
2993 return JIM_ERR;
2995 Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
2996 Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
2997 return JIM_OK;
3000 #include <limits.h>
3001 #include <stdlib.h>
3002 #include <string.h>
3003 #include <stdio.h>
3004 #include <errno.h>
3005 #include <sys/stat.h>
3008 #ifdef HAVE_UTIMES
3009 #include <sys/time.h>
3010 #endif
3011 #ifdef HAVE_UNISTD_H
3012 #include <unistd.h>
3013 #elif defined(_MSC_VER)
3014 #include <direct.h>
3015 #define F_OK 0
3016 #define W_OK 2
3017 #define R_OK 4
3018 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
3019 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3020 #endif
3022 # ifndef MAXPATHLEN
3023 # define MAXPATHLEN JIM_PATH_LEN
3024 # endif
3027 static const char *JimGetFileType(int mode)
3029 if (S_ISREG(mode)) {
3030 return "file";
3032 else if (S_ISDIR(mode)) {
3033 return "directory";
3035 #ifdef S_ISCHR
3036 else if (S_ISCHR(mode)) {
3037 return "characterSpecial";
3039 #endif
3040 #ifdef S_ISBLK
3041 else if (S_ISBLK(mode)) {
3042 return "blockSpecial";
3044 #endif
3045 #ifdef S_ISFIFO
3046 else if (S_ISFIFO(mode)) {
3047 return "fifo";
3049 #endif
3050 #ifdef S_ISLNK
3051 else if (S_ISLNK(mode)) {
3052 return "link";
3054 #endif
3055 #ifdef S_ISSOCK
3056 else if (S_ISSOCK(mode)) {
3057 return "socket";
3059 #endif
3060 return "unknown";
3064 static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
3065 jim_wide value)
3067 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
3068 Jim_Obj *valobj = Jim_NewWideObj(interp, value);
3070 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
3071 Jim_FreeObj(interp, nameobj);
3072 Jim_FreeObj(interp, valobj);
3073 return JIM_ERR;
3075 return JIM_OK;
3078 static int set_array_string_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
3079 const char *value)
3081 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
3082 Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1);
3084 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
3085 Jim_FreeObj(interp, nameobj);
3086 Jim_FreeObj(interp, valobj);
3087 return JIM_ERR;
3089 return JIM_OK;
3092 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
3094 if (set_array_int_value(interp, varName, "dev", sb->st_dev) != JIM_OK) {
3095 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
3096 return JIM_ERR;
3098 set_array_int_value(interp, varName, "ino", sb->st_ino);
3099 set_array_int_value(interp, varName, "mode", sb->st_mode);
3100 set_array_int_value(interp, varName, "nlink", sb->st_nlink);
3101 set_array_int_value(interp, varName, "uid", sb->st_uid);
3102 set_array_int_value(interp, varName, "gid", sb->st_gid);
3103 set_array_int_value(interp, varName, "size", sb->st_size);
3104 set_array_int_value(interp, varName, "atime", sb->st_atime);
3105 set_array_int_value(interp, varName, "mtime", sb->st_mtime);
3106 set_array_int_value(interp, varName, "ctime", sb->st_ctime);
3107 set_array_string_value(interp, varName, "type", JimGetFileType((int)sb->st_mode));
3110 Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0));
3112 return JIM_OK;
3115 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3117 const char *path = Jim_String(argv[0]);
3118 const char *p = strrchr(path, '/');
3120 if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
3121 Jim_SetResultString(interp, "..", -1);
3122 } else if (!p) {
3123 Jim_SetResultString(interp, ".", -1);
3125 else if (p == path) {
3126 Jim_SetResultString(interp, "/", -1);
3128 #if defined(__MINGW32__) || defined(_MSC_VER)
3129 else if (p[-1] == ':') {
3131 Jim_SetResultString(interp, path, p - path + 1);
3133 #endif
3134 else {
3135 Jim_SetResultString(interp, path, p - path);
3137 return JIM_OK;
3140 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3142 const char *path = Jim_String(argv[0]);
3143 const char *lastSlash = strrchr(path, '/');
3144 const char *p = strrchr(path, '.');
3146 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
3147 Jim_SetResult(interp, argv[0]);
3149 else {
3150 Jim_SetResultString(interp, path, p - path);
3152 return JIM_OK;
3155 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3157 const char *path = Jim_String(argv[0]);
3158 const char *lastSlash = strrchr(path, '/');
3159 const char *p = strrchr(path, '.');
3161 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
3162 p = "";
3164 Jim_SetResultString(interp, p, -1);
3165 return JIM_OK;
3168 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3170 const char *path = Jim_String(argv[0]);
3171 const char *lastSlash = strrchr(path, '/');
3173 if (lastSlash) {
3174 Jim_SetResultString(interp, lastSlash + 1, -1);
3176 else {
3177 Jim_SetResult(interp, argv[0]);
3179 return JIM_OK;
3182 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3184 #ifdef HAVE_REALPATH
3185 const char *path = Jim_String(argv[0]);
3186 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3188 if (realpath(path, newname)) {
3189 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
3190 return JIM_OK;
3192 else {
3193 Jim_Free(newname);
3194 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
3195 return JIM_ERR;
3197 #else
3198 Jim_SetResultString(interp, "Not implemented", -1);
3199 return JIM_ERR;
3200 #endif
3203 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3205 int i;
3206 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3207 char *last = newname;
3209 *newname = 0;
3212 for (i = 0; i < argc; i++) {
3213 int len;
3214 const char *part = Jim_GetString(argv[i], &len);
3216 if (*part == '/') {
3218 last = newname;
3220 #if defined(__MINGW32__) || defined(_MSC_VER)
3221 else if (strchr(part, ':')) {
3223 last = newname;
3225 #endif
3226 else if (part[0] == '.') {
3227 if (part[1] == '/') {
3228 part += 2;
3229 len -= 2;
3231 else if (part[1] == 0 && last != newname) {
3233 continue;
3238 if (last != newname && last[-1] != '/') {
3239 *last++ = '/';
3242 if (len) {
3243 if (last + len - newname >= MAXPATHLEN) {
3244 Jim_Free(newname);
3245 Jim_SetResultString(interp, "Path too long", -1);
3246 return JIM_ERR;
3248 memcpy(last, part, len);
3249 last += len;
3253 if (last > newname + 1 && last[-1] == '/') {
3254 *--last = 0;
3258 *last = 0;
3262 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
3264 return JIM_OK;
3267 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3269 const char *path = Jim_String(filename);
3270 int rc = access(path, mode);
3272 Jim_SetResultBool(interp, rc != -1);
3274 return JIM_OK;
3277 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3279 return file_access(interp, argv[0], R_OK);
3282 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3284 return file_access(interp, argv[0], W_OK);
3287 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3289 #ifdef X_OK
3290 return file_access(interp, argv[0], X_OK);
3291 #else
3292 Jim_SetResultBool(interp, 1);
3293 return JIM_OK;
3294 #endif
3297 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3299 return file_access(interp, argv[0], F_OK);
3302 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3304 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
3306 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
3307 argc++;
3308 argv--;
3311 while (argc--) {
3312 const char *path = Jim_String(argv[0]);
3314 if (unlink(path) == -1 && errno != ENOENT) {
3315 if (rmdir(path) == -1) {
3317 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
3318 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
3319 strerror(errno));
3320 return JIM_ERR;
3324 argv++;
3326 return JIM_OK;
3329 #ifdef HAVE_MKDIR_ONE_ARG
3330 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
3331 #else
3332 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
3333 #endif
3335 static int mkdir_all(char *path)
3337 int ok = 1;
3340 goto first;
3342 while (ok--) {
3345 char *slash = strrchr(path, '/');
3347 if (slash && slash != path) {
3348 *slash = 0;
3349 if (mkdir_all(path) != 0) {
3350 return -1;
3352 *slash = '/';
3355 first:
3356 if (MKDIR_DEFAULT(path) == 0) {
3357 return 0;
3359 if (errno == ENOENT) {
3361 continue;
3364 if (errno == EEXIST) {
3365 struct stat sb;
3367 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
3368 return 0;
3371 errno = EEXIST;
3374 break;
3376 return -1;
3379 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3381 while (argc--) {
3382 char *path = Jim_StrDup(Jim_String(argv[0]));
3383 int rc = mkdir_all(path);
3385 Jim_Free(path);
3386 if (rc != 0) {
3387 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
3388 strerror(errno));
3389 return JIM_ERR;
3391 argv++;
3393 return JIM_OK;
3396 #ifdef HAVE_MKSTEMP
3397 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3399 int fd;
3400 char *filename;
3401 const char *template = "/tmp/tcl.tmp.XXXXXX";
3403 if (argc >= 1) {
3404 template = Jim_String(argv[0]);
3406 filename = Jim_StrDup(template);
3408 fd = mkstemp(filename);
3409 if (fd < 0) {
3410 Jim_SetResultString(interp, "Failed to create tempfile", -1);
3411 return JIM_ERR;
3413 close(fd);
3415 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, filename, -1));
3416 return JIM_OK;
3418 #endif
3420 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3422 const char *source;
3423 const char *dest;
3424 int force = 0;
3426 if (argc == 3) {
3427 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
3428 return -1;
3430 force++;
3431 argv++;
3432 argc--;
3435 source = Jim_String(argv[0]);
3436 dest = Jim_String(argv[1]);
3438 if (!force && access(dest, F_OK) == 0) {
3439 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
3440 argv[1]);
3441 return JIM_ERR;
3444 if (rename(source, dest) != 0) {
3445 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3446 strerror(errno));
3447 return JIM_ERR;
3450 return JIM_OK;
3453 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3455 const char *path = Jim_String(filename);
3457 if (stat(path, sb) == -1) {
3458 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3459 return JIM_ERR;
3461 return JIM_OK;
3464 #ifndef HAVE_LSTAT
3465 #define lstat stat
3466 #endif
3468 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3470 const char *path = Jim_String(filename);
3472 if (lstat(path, sb) == -1) {
3473 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3474 return JIM_ERR;
3476 return JIM_OK;
3479 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3481 struct stat sb;
3483 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3484 return JIM_ERR;
3486 Jim_SetResultInt(interp, sb.st_atime);
3487 return JIM_OK;
3490 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3492 struct stat sb;
3494 if (argc == 2) {
3495 #ifdef HAVE_UTIMES
3496 jim_wide newtime;
3497 struct timeval times[2];
3499 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
3500 return JIM_ERR;
3503 times[1].tv_sec = times[0].tv_sec = newtime;
3504 times[1].tv_usec = times[0].tv_usec = 0;
3506 if (utimes(Jim_String(argv[0]), times) != 0) {
3507 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
3508 return JIM_ERR;
3510 #else
3511 Jim_SetResultString(interp, "Not implemented", -1);
3512 return JIM_ERR;
3513 #endif
3515 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3516 return JIM_ERR;
3518 Jim_SetResultInt(interp, sb.st_mtime);
3519 return JIM_OK;
3522 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3524 return Jim_EvalPrefix(interp, "file copy", argc, argv);
3527 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3529 struct stat sb;
3531 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3532 return JIM_ERR;
3534 Jim_SetResultInt(interp, sb.st_size);
3535 return JIM_OK;
3538 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3540 struct stat sb;
3541 int ret = 0;
3543 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3544 ret = S_ISDIR(sb.st_mode);
3546 Jim_SetResultInt(interp, ret);
3547 return JIM_OK;
3550 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3552 struct stat sb;
3553 int ret = 0;
3555 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3556 ret = S_ISREG(sb.st_mode);
3558 Jim_SetResultInt(interp, ret);
3559 return JIM_OK;
3562 #ifdef HAVE_GETEUID
3563 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3565 struct stat sb;
3566 int ret = 0;
3568 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3569 ret = (geteuid() == sb.st_uid);
3571 Jim_SetResultInt(interp, ret);
3572 return JIM_OK;
3574 #endif
3576 #if defined(HAVE_READLINK)
3577 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3579 const char *path = Jim_String(argv[0]);
3580 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
3582 int linkLength = readlink(path, linkValue, MAXPATHLEN);
3584 if (linkLength == -1) {
3585 Jim_Free(linkValue);
3586 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
3587 return JIM_ERR;
3589 linkValue[linkLength] = 0;
3590 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
3591 return JIM_OK;
3593 #endif
3595 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3597 struct stat sb;
3599 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3600 return JIM_ERR;
3602 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
3603 return JIM_OK;
3606 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3608 struct stat sb;
3610 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3611 return JIM_ERR;
3613 return StoreStatData(interp, argv[1], &sb);
3616 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3618 struct stat sb;
3620 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3621 return JIM_ERR;
3623 return StoreStatData(interp, argv[1], &sb);
3626 static const jim_subcmd_type file_command_table[] = {
3627 { "atime",
3628 "name",
3629 file_cmd_atime,
3634 { "mtime",
3635 "name ?time?",
3636 file_cmd_mtime,
3641 { "copy",
3642 "?-force? source dest",
3643 file_cmd_copy,
3648 { "dirname",
3649 "name",
3650 file_cmd_dirname,
3655 { "rootname",
3656 "name",
3657 file_cmd_rootname,
3662 { "extension",
3663 "name",
3664 file_cmd_extension,
3669 { "tail",
3670 "name",
3671 file_cmd_tail,
3676 { "normalize",
3677 "name",
3678 file_cmd_normalize,
3683 { "join",
3684 "name ?name ...?",
3685 file_cmd_join,
3690 { "readable",
3691 "name",
3692 file_cmd_readable,
3697 { "writable",
3698 "name",
3699 file_cmd_writable,
3704 { "executable",
3705 "name",
3706 file_cmd_executable,
3711 { "exists",
3712 "name",
3713 file_cmd_exists,
3718 { "delete",
3719 "?-force|--? name ...",
3720 file_cmd_delete,
3725 { "mkdir",
3726 "dir ...",
3727 file_cmd_mkdir,
3732 #ifdef HAVE_MKSTEMP
3733 { "tempfile",
3734 "?template?",
3735 file_cmd_tempfile,
3740 #endif
3741 { "rename",
3742 "?-force? source dest",
3743 file_cmd_rename,
3748 #if defined(HAVE_READLINK)
3749 { "readlink",
3750 "name",
3751 file_cmd_readlink,
3756 #endif
3757 { "size",
3758 "name",
3759 file_cmd_size,
3764 { "stat",
3765 "name var",
3766 file_cmd_stat,
3771 { "lstat",
3772 "name var",
3773 file_cmd_lstat,
3778 { "type",
3779 "name",
3780 file_cmd_type,
3785 #ifdef HAVE_GETEUID
3786 { "owned",
3787 "name",
3788 file_cmd_owned,
3793 #endif
3794 { "isdirectory",
3795 "name",
3796 file_cmd_isdirectory,
3801 { "isfile",
3802 "name",
3803 file_cmd_isfile,
3809 NULL
3813 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3815 const char *path;
3817 if (argc != 2) {
3818 Jim_WrongNumArgs(interp, 1, argv, "dirname");
3819 return JIM_ERR;
3822 path = Jim_String(argv[1]);
3824 if (chdir(path) != 0) {
3825 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
3826 strerror(errno));
3827 return JIM_ERR;
3829 return JIM_OK;
3832 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3834 const int cwd_len = 2048;
3835 char *cwd = malloc(cwd_len);
3837 if (getcwd(cwd, cwd_len) == NULL) {
3838 Jim_SetResultString(interp, "Failed to get pwd", -1);
3839 return JIM_ERR;
3841 #if defined(__MINGW32__) || defined(_MSC_VER)
3844 char *p = cwd;
3845 while ((p = strchr(p, '\\')) != NULL) {
3846 *p++ = '/';
3849 #endif
3851 Jim_SetResultString(interp, cwd, -1);
3853 free(cwd);
3854 return JIM_OK;
3857 int Jim_fileInit(Jim_Interp *interp)
3859 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
3860 return JIM_ERR;
3862 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
3863 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
3864 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
3865 return JIM_OK;
3868 #include <string.h>
3869 #include <ctype.h>
3872 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
3873 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3875 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
3876 int i, j;
3877 int rc;
3880 for (i = 1; i < argc; i++) {
3881 int len;
3882 const char *arg = Jim_GetString(argv[i], &len);
3884 if (i > 1) {
3885 Jim_AppendString(interp, cmdlineObj, " ", 1);
3887 if (strpbrk(arg, "\\\" ") == NULL) {
3889 Jim_AppendString(interp, cmdlineObj, arg, len);
3890 continue;
3893 Jim_AppendString(interp, cmdlineObj, "\"", 1);
3894 for (j = 0; j < len; j++) {
3895 if (arg[j] == '\\' || arg[j] == '"') {
3896 Jim_AppendString(interp, cmdlineObj, "\\", 1);
3898 Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
3900 Jim_AppendString(interp, cmdlineObj, "\"", 1);
3902 rc = system(Jim_String(cmdlineObj));
3904 Jim_FreeNewObj(interp, cmdlineObj);
3906 if (rc) {
3907 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
3908 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
3909 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
3910 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
3911 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
3912 return JIM_ERR;
3915 return JIM_OK;
3918 int Jim_execInit(Jim_Interp *interp)
3920 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
3921 return JIM_ERR;
3922 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
3923 return JIM_OK;
3925 #else
3928 #include <errno.h>
3929 #include <signal.h>
3931 #if defined(__MINGW32__)
3933 #ifndef STRICT
3934 #define STRICT
3935 #endif
3936 #define WIN32_LEAN_AND_MEAN
3937 #include <windows.h>
3938 #include <fcntl.h>
3940 typedef HANDLE fdtype;
3941 typedef HANDLE pidtype;
3942 #define JIM_BAD_FD INVALID_HANDLE_VALUE
3943 #define JIM_BAD_PID INVALID_HANDLE_VALUE
3944 #define JimCloseFd CloseHandle
3946 #define WIFEXITED(STATUS) 1
3947 #define WEXITSTATUS(STATUS) (STATUS)
3948 #define WIFSIGNALED(STATUS) 0
3949 #define WTERMSIG(STATUS) 0
3950 #define WNOHANG 1
3952 static fdtype JimFileno(FILE *fh);
3953 static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
3954 static fdtype JimDupFd(fdtype infd);
3955 static fdtype JimOpenForRead(const char *filename);
3956 static FILE *JimFdOpenForRead(fdtype fd);
3957 static int JimPipe(fdtype pipefd[2]);
3958 static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env,
3959 fdtype inputId, fdtype outputId, fdtype errorId);
3960 static int JimErrno(void);
3961 #else
3962 #include <unistd.h>
3963 #include <fcntl.h>
3964 #include <sys/wait.h>
3966 typedef int fdtype;
3967 typedef int pidtype;
3968 #define JimPipe pipe
3969 #define JimErrno() errno
3970 #define JIM_BAD_FD -1
3971 #define JIM_BAD_PID -1
3972 #define JimFileno fileno
3973 #define JimReadFd read
3974 #define JimCloseFd close
3975 #define JimWaitPid waitpid
3976 #define JimDupFd dup
3977 #define JimFdOpenForRead(FD) fdopen((FD), "r")
3978 #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
3980 #ifndef HAVE_EXECVPE
3981 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
3982 #endif
3983 #endif
3985 static const char *JimStrError(void);
3986 static char **JimSaveEnv(char **env);
3987 static void JimRestoreEnv(char **env);
3988 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
3989 pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
3990 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
3991 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId);
3992 static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len);
3993 static fdtype JimOpenForWrite(const char *filename, int append);
3994 static int JimRewindFd(fdtype fd);
3996 static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
3998 Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
4001 static const char *JimStrError(void)
4003 return strerror(JimErrno());
4006 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
4008 int len;
4009 const char *s = Jim_GetString(objPtr, &len);
4011 if (len > 0 && s[len - 1] == '\n') {
4012 objPtr->length--;
4013 objPtr->bytes[objPtr->length] = '\0';
4017 static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
4019 char buf[256];
4020 FILE *fh = JimFdOpenForRead(fd);
4021 if (fh == NULL) {
4022 return JIM_ERR;
4025 while (1) {
4026 int retval = fread(buf, 1, sizeof(buf), fh);
4027 if (retval > 0) {
4028 Jim_AppendString(interp, strObj, buf, retval);
4030 if (retval != sizeof(buf)) {
4031 break;
4034 Jim_RemoveTrailingNewline(strObj);
4035 fclose(fh);
4036 return JIM_OK;
4039 static void JimTrimTrailingNewline(Jim_Interp *interp)
4041 int len;
4042 const char *p = Jim_GetString(Jim_GetResult(interp), &len);
4044 if (len > 0 && p[len - 1] == '\n') {
4045 Jim_SetResultString(interp, p, len - 1);
4049 static char **JimBuildEnv(Jim_Interp *interp)
4051 #if defined(jim_ext_tclcompat)
4052 int i;
4053 int size;
4054 int num;
4055 int n;
4056 char **envptr;
4057 char *envdata;
4059 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
4061 if (!objPtr) {
4062 return Jim_GetEnviron();
4067 num = Jim_ListLength(interp, objPtr);
4068 if (num % 2) {
4069 num--;
4071 size = Jim_Length(objPtr) + 2;
4073 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
4074 envdata = (char *)&envptr[num / 2 + 1];
4076 n = 0;
4077 for (i = 0; i < num; i += 2) {
4078 const char *s1, *s2;
4079 Jim_Obj *elemObj;
4081 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
4082 s1 = Jim_String(elemObj);
4083 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
4084 s2 = Jim_String(elemObj);
4086 envptr[n] = envdata;
4087 envdata += sprintf(envdata, "%s=%s", s1, s2);
4088 envdata++;
4089 n++;
4091 envptr[n] = NULL;
4092 *envdata = 0;
4094 return envptr;
4095 #else
4096 return Jim_GetEnviron();
4097 #endif
4100 static void JimFreeEnv(char **env, char **original_environ)
4102 #ifdef jim_ext_tclcompat
4103 if (env != original_environ) {
4104 Jim_Free(env);
4106 #endif
4109 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus)
4111 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4112 int rc = JIM_ERR;
4114 if (WIFEXITED(waitStatus)) {
4115 if (WEXITSTATUS(waitStatus) == 0) {
4116 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
4117 rc = JIM_OK;
4119 else {
4120 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4121 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4122 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
4125 else {
4126 const char *type;
4127 const char *action;
4129 if (WIFSIGNALED(waitStatus)) {
4130 type = "CHILDKILLED";
4131 action = "killed";
4133 else {
4134 type = "CHILDSUSP";
4135 action = "suspended";
4138 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
4140 #ifdef jim_ext_signal
4141 Jim_SetResultFormatted(interp, "child %s by signal %s", action, Jim_SignalId(WTERMSIG(waitStatus)));
4142 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
4143 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
4144 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
4145 #else
4146 Jim_SetResultFormatted(interp, "child %s by signal %d", action, WTERMSIG(waitStatus));
4147 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
4148 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4149 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
4150 #endif
4152 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4153 return rc;
4157 struct WaitInfo
4159 pidtype pid;
4160 int status;
4161 int flags;
4164 struct WaitInfoTable {
4165 struct WaitInfo *info;
4166 int size;
4167 int used;
4171 #define WI_DETACHED 2
4173 #define WAIT_TABLE_GROW_BY 4
4175 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
4177 struct WaitInfoTable *table = privData;
4179 Jim_Free(table->info);
4180 Jim_Free(table);
4183 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
4185 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
4186 table->info = NULL;
4187 table->size = table->used = 0;
4189 return table;
4192 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4194 fdtype outputId; /* File id for output pipe. -1
4195 * means command overrode. */
4196 fdtype errorId; /* File id for temporary file
4197 * containing error output. */
4198 pidtype *pidPtr;
4199 int numPids, result;
4201 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
4202 Jim_Obj *listObj;
4203 int i;
4205 argc--;
4206 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
4207 if (numPids < 0) {
4208 return JIM_ERR;
4211 listObj = Jim_NewListObj(interp, NULL, 0);
4212 for (i = 0; i < numPids; i++) {
4213 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
4215 Jim_SetResult(interp, listObj);
4216 JimDetachPids(interp, numPids, pidPtr);
4217 Jim_Free(pidPtr);
4218 return JIM_OK;
4221 numPids =
4222 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
4224 if (numPids < 0) {
4225 return JIM_ERR;
4228 Jim_SetResultString(interp, "", 0);
4230 result = JIM_OK;
4231 if (outputId != JIM_BAD_FD) {
4232 result = JimAppendStreamToString(interp, outputId, Jim_GetResult(interp));
4233 if (result < 0) {
4234 Jim_SetResultErrno(interp, "error reading from output pipe");
4238 if (JimCleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) {
4239 result = JIM_ERR;
4241 return result;
4244 static void JimReapDetachedPids(struct WaitInfoTable *table)
4246 struct WaitInfo *waitPtr;
4247 int count;
4249 if (!table) {
4250 return;
4253 for (waitPtr = table->info, count = table->used; count > 0; waitPtr++, count--) {
4254 if (waitPtr->flags & WI_DETACHED) {
4255 int status;
4256 pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
4257 if (pid != JIM_BAD_PID) {
4258 if (waitPtr != &table->info[table->used - 1]) {
4259 *waitPtr = table->info[table->used - 1];
4261 table->used--;
4267 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
4269 int i;
4272 for (i = 0; i < table->used; i++) {
4273 if (pid == table->info[i].pid) {
4275 JimWaitPid(pid, statusPtr, 0);
4278 if (i != table->used - 1) {
4279 table->info[i] = table->info[table->used - 1];
4281 table->used--;
4282 return pid;
4287 return JIM_BAD_PID;
4291 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
4293 int j;
4294 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4296 for (j = 0; j < numPids; j++) {
4298 int i;
4299 for (i = 0; i < table->used; i++) {
4300 if (pidPtr[j] == table->info[i].pid) {
4301 table->info[i].flags |= WI_DETACHED;
4302 break;
4308 static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
4310 FILE *fh;
4311 Jim_Obj *fhObj;
4313 fhObj = Jim_NewStringObj(interp, name, -1);
4314 Jim_IncrRefCount(fhObj);
4315 fh = Jim_AioFilehandle(interp, fhObj);
4316 Jim_DecrRefCount(interp, fhObj);
4318 return fh;
4321 static int
4322 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
4323 fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
4325 pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
4326 * the pids of child processes. */
4327 int numPids = 0; /* Actual number of processes that exist
4328 * at *pidPtr right now. */
4329 int cmdCount; /* Count of number of distinct commands
4330 * found in argc/argv. */
4331 const char *input = NULL; /* Describes input for pipeline, depending
4332 * on "inputFile". NULL means take input
4333 * from stdin/pipe. */
4334 int input_len = 0;
4336 #define FILE_NAME 0
4337 #define FILE_APPEND 1
4338 #define FILE_HANDLE 2
4339 #define FILE_TEXT 3
4341 int inputFile = FILE_NAME; /* 1 means input is name of input file.
4342 * 2 means input is filehandle name.
4343 * 0 means input holds actual
4344 * text to be input to command. */
4346 int outputFile = FILE_NAME; /* 0 means output is the name of output file.
4347 * 1 means output is the name of output file, and append.
4348 * 2 means output is filehandle name.
4349 * All this is ignored if output is NULL
4351 int errorFile = FILE_NAME; /* 0 means error is the name of error file.
4352 * 1 means error is the name of error file, and append.
4353 * 2 means error is filehandle name.
4354 * All this is ignored if error is NULL
4356 const char *output = NULL; /* Holds name of output file to pipe to,
4357 * or NULL if output goes to stdout/pipe. */
4358 const char *error = NULL; /* Holds name of stderr file to pipe to,
4359 * or NULL if stderr goes to stderr/pipe. */
4360 fdtype inputId = JIM_BAD_FD;
4361 fdtype outputId = JIM_BAD_FD;
4362 fdtype errorId = JIM_BAD_FD;
4363 fdtype lastOutputId = JIM_BAD_FD;
4364 fdtype pipeIds[2];
4365 int firstArg, lastArg; /* Indexes of first and last arguments in
4366 * current command. */
4367 int lastBar;
4368 int i;
4369 pidtype pid;
4370 char **save_environ;
4371 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4374 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
4375 int arg_count = 0;
4377 JimReapDetachedPids(table);
4379 if (inPipePtr != NULL) {
4380 *inPipePtr = JIM_BAD_FD;
4382 if (outPipePtr != NULL) {
4383 *outPipePtr = JIM_BAD_FD;
4385 if (errFilePtr != NULL) {
4386 *errFilePtr = JIM_BAD_FD;
4388 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4390 cmdCount = 1;
4391 lastBar = -1;
4392 for (i = 0; i < argc; i++) {
4393 const char *arg = Jim_String(argv[i]);
4395 if (arg[0] == '<') {
4396 inputFile = FILE_NAME;
4397 input = arg + 1;
4398 if (*input == '<') {
4399 inputFile = FILE_TEXT;
4400 input_len = Jim_Length(argv[i]) - 2;
4401 input++;
4403 else if (*input == '@') {
4404 inputFile = FILE_HANDLE;
4405 input++;
4408 if (!*input && ++i < argc) {
4409 input = Jim_GetString(argv[i], &input_len);
4412 else if (arg[0] == '>') {
4413 int dup_error = 0;
4415 outputFile = FILE_NAME;
4417 output = arg + 1;
4418 if (*output == '>') {
4419 outputFile = FILE_APPEND;
4420 output++;
4422 if (*output == '&') {
4424 output++;
4425 dup_error = 1;
4427 if (*output == '@') {
4428 outputFile = FILE_HANDLE;
4429 output++;
4431 if (!*output && ++i < argc) {
4432 output = Jim_String(argv[i]);
4434 if (dup_error) {
4435 errorFile = outputFile;
4436 error = output;
4439 else if (arg[0] == '2' && arg[1] == '>') {
4440 error = arg + 2;
4441 errorFile = FILE_NAME;
4443 if (*error == '@') {
4444 errorFile = FILE_HANDLE;
4445 error++;
4447 else if (*error == '>') {
4448 errorFile = FILE_APPEND;
4449 error++;
4451 if (!*error && ++i < argc) {
4452 error = Jim_String(argv[i]);
4455 else {
4456 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
4457 if (i == lastBar + 1 || i == argc - 1) {
4458 Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
4459 goto badargs;
4461 lastBar = i;
4462 cmdCount++;
4465 arg_array[arg_count++] = (char *)arg;
4466 continue;
4469 if (i >= argc) {
4470 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
4471 goto badargs;
4475 if (arg_count == 0) {
4476 Jim_SetResultString(interp, "didn't specify command to execute", -1);
4477 badargs:
4478 Jim_Free(arg_array);
4479 return -1;
4483 save_environ = JimSaveEnv(JimBuildEnv(interp));
4485 if (input != NULL) {
4486 if (inputFile == FILE_TEXT) {
4487 inputId = JimCreateTemp(interp, input, input_len);
4488 if (inputId == JIM_BAD_FD) {
4489 goto error;
4492 else if (inputFile == FILE_HANDLE) {
4494 FILE *fh = JimGetAioFilehandle(interp, input);
4496 if (fh == NULL) {
4497 goto error;
4499 inputId = JimDupFd(JimFileno(fh));
4501 else {
4502 inputId = JimOpenForRead(input);
4503 if (inputId == JIM_BAD_FD) {
4504 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
4505 goto error;
4509 else if (inPipePtr != NULL) {
4510 if (JimPipe(pipeIds) != 0) {
4511 Jim_SetResultErrno(interp, "couldn't create input pipe for command");
4512 goto error;
4514 inputId = pipeIds[0];
4515 *inPipePtr = pipeIds[1];
4516 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4519 if (output != NULL) {
4520 if (outputFile == FILE_HANDLE) {
4521 FILE *fh = JimGetAioFilehandle(interp, output);
4522 if (fh == NULL) {
4523 goto error;
4525 fflush(fh);
4526 lastOutputId = JimDupFd(JimFileno(fh));
4528 else {
4529 lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
4530 if (lastOutputId == JIM_BAD_FD) {
4531 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
4532 goto error;
4536 else if (outPipePtr != NULL) {
4537 if (JimPipe(pipeIds) != 0) {
4538 Jim_SetResultErrno(interp, "couldn't create output pipe");
4539 goto error;
4541 lastOutputId = pipeIds[1];
4542 *outPipePtr = pipeIds[0];
4543 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4546 if (error != NULL) {
4547 if (errorFile == FILE_HANDLE) {
4548 if (strcmp(error, "1") == 0) {
4550 if (lastOutputId != JIM_BAD_FD) {
4551 errorId = JimDupFd(lastOutputId);
4553 else {
4555 error = "stdout";
4558 if (errorId == JIM_BAD_FD) {
4559 FILE *fh = JimGetAioFilehandle(interp, error);
4560 if (fh == NULL) {
4561 goto error;
4563 fflush(fh);
4564 errorId = JimDupFd(JimFileno(fh));
4567 else {
4568 errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
4569 if (errorId == JIM_BAD_FD) {
4570 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
4571 goto error;
4575 else if (errFilePtr != NULL) {
4576 errorId = JimCreateTemp(interp, NULL, 0);
4577 if (errorId == JIM_BAD_FD) {
4578 goto error;
4580 *errFilePtr = JimDupFd(errorId);
4584 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
4585 for (i = 0; i < numPids; i++) {
4586 pidPtr[i] = JIM_BAD_PID;
4588 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
4589 int pipe_dup_err = 0;
4590 fdtype origErrorId = errorId;
4592 for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
4593 if (arg_array[lastArg][0] == '|') {
4594 if (arg_array[lastArg][1] == '&') {
4595 pipe_dup_err = 1;
4597 break;
4601 arg_array[lastArg] = NULL;
4602 if (lastArg == arg_count) {
4603 outputId = lastOutputId;
4605 else {
4606 if (JimPipe(pipeIds) != 0) {
4607 Jim_SetResultErrno(interp, "couldn't create pipe");
4608 goto error;
4610 outputId = pipeIds[1];
4615 #ifdef __MINGW32__
4616 pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId);
4617 if (pid == JIM_BAD_PID) {
4618 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
4619 goto error;
4621 #else
4622 if (table->info == NULL) {
4623 (void)signal(SIGPIPE, SIG_IGN);
4627 if (pipe_dup_err) {
4628 errorId = outputId;
4631 pid = vfork();
4632 if (pid < 0) {
4633 Jim_SetResultErrno(interp, "couldn't fork child process");
4634 goto error;
4636 if (pid == 0) {
4639 if (inputId != -1) dup2(inputId, 0);
4640 if (outputId != -1) dup2(outputId, 1);
4641 if (errorId != -1) dup2(errorId, 2);
4643 for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
4644 close(i);
4647 execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron());
4650 fprintf(stderr, "couldn't exec \"%s\"", arg_array[firstArg]);
4651 _exit(127);
4653 #endif
4657 if (table->used == table->size) {
4658 table->size += WAIT_TABLE_GROW_BY;
4659 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
4662 table->info[table->used].pid = pid;
4663 table->info[table->used].flags = 0;
4664 table->used++;
4666 pidPtr[numPids] = pid;
4669 errorId = origErrorId;
4672 if (inputId != JIM_BAD_FD) {
4673 JimCloseFd(inputId);
4675 if (outputId != JIM_BAD_FD) {
4676 JimCloseFd(outputId);
4678 inputId = pipeIds[0];
4679 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4681 *pidArrayPtr = pidPtr;
4684 cleanup:
4685 if (inputId != JIM_BAD_FD) {
4686 JimCloseFd(inputId);
4688 if (lastOutputId != JIM_BAD_FD) {
4689 JimCloseFd(lastOutputId);
4691 if (errorId != JIM_BAD_FD) {
4692 JimCloseFd(errorId);
4694 Jim_Free(arg_array);
4696 JimRestoreEnv(save_environ);
4698 return numPids;
4701 error:
4702 if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
4703 JimCloseFd(*inPipePtr);
4704 *inPipePtr = JIM_BAD_FD;
4706 if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
4707 JimCloseFd(*outPipePtr);
4708 *outPipePtr = JIM_BAD_FD;
4710 if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
4711 JimCloseFd(*errFilePtr);
4712 *errFilePtr = JIM_BAD_FD;
4714 if (pipeIds[0] != JIM_BAD_FD) {
4715 JimCloseFd(pipeIds[0]);
4717 if (pipeIds[1] != JIM_BAD_FD) {
4718 JimCloseFd(pipeIds[1]);
4720 if (pidPtr != NULL) {
4721 for (i = 0; i < numPids; i++) {
4722 if (pidPtr[i] != JIM_BAD_PID) {
4723 JimDetachPids(interp, 1, &pidPtr[i]);
4726 Jim_Free(pidPtr);
4728 numPids = -1;
4729 goto cleanup;
4733 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId)
4735 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4736 int result = JIM_OK;
4737 int i;
4739 for (i = 0; i < numPids; i++) {
4740 int waitStatus = 0;
4741 if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
4742 if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus) != JIM_OK) {
4743 result = JIM_ERR;
4747 Jim_Free(pidPtr);
4749 if (errorId != JIM_BAD_FD) {
4750 JimRewindFd(errorId);
4751 if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) {
4752 result = JIM_ERR;
4756 JimTrimTrailingNewline(interp);
4758 return result;
4761 int Jim_execInit(Jim_Interp *interp)
4763 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4764 return JIM_ERR;
4765 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
4766 return JIM_OK;
4769 #if defined(__MINGW32__)
4772 static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
4774 static SECURITY_ATTRIBUTES secAtts;
4776 secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
4777 secAtts.lpSecurityDescriptor = NULL;
4778 secAtts.bInheritHandle = TRUE;
4779 return &secAtts;
4782 static int JimErrno(void)
4784 switch (GetLastError()) {
4785 case ERROR_FILE_NOT_FOUND: return ENOENT;
4786 case ERROR_PATH_NOT_FOUND: return ENOENT;
4787 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
4788 case ERROR_ACCESS_DENIED: return EACCES;
4789 case ERROR_INVALID_HANDLE: return EBADF;
4790 case ERROR_BAD_ENVIRONMENT: return E2BIG;
4791 case ERROR_BAD_FORMAT: return ENOEXEC;
4792 case ERROR_INVALID_ACCESS: return EACCES;
4793 case ERROR_INVALID_DRIVE: return ENOENT;
4794 case ERROR_CURRENT_DIRECTORY: return EACCES;
4795 case ERROR_NOT_SAME_DEVICE: return EXDEV;
4796 case ERROR_NO_MORE_FILES: return ENOENT;
4797 case ERROR_WRITE_PROTECT: return EROFS;
4798 case ERROR_BAD_UNIT: return ENXIO;
4799 case ERROR_NOT_READY: return EBUSY;
4800 case ERROR_BAD_COMMAND: return EIO;
4801 case ERROR_CRC: return EIO;
4802 case ERROR_BAD_LENGTH: return EIO;
4803 case ERROR_SEEK: return EIO;
4804 case ERROR_WRITE_FAULT: return EIO;
4805 case ERROR_READ_FAULT: return EIO;
4806 case ERROR_GEN_FAILURE: return EIO;
4807 case ERROR_SHARING_VIOLATION: return EACCES;
4808 case ERROR_LOCK_VIOLATION: return EACCES;
4809 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
4810 case ERROR_HANDLE_DISK_FULL: return ENOSPC;
4811 case ERROR_NOT_SUPPORTED: return ENODEV;
4812 case ERROR_REM_NOT_LIST: return EBUSY;
4813 case ERROR_DUP_NAME: return EEXIST;
4814 case ERROR_BAD_NETPATH: return ENOENT;
4815 case ERROR_NETWORK_BUSY: return EBUSY;
4816 case ERROR_DEV_NOT_EXIST: return ENODEV;
4817 case ERROR_TOO_MANY_CMDS: return EAGAIN;
4818 case ERROR_ADAP_HDW_ERR: return EIO;
4819 case ERROR_BAD_NET_RESP: return EIO;
4820 case ERROR_UNEXP_NET_ERR: return EIO;
4821 case ERROR_NETNAME_DELETED: return ENOENT;
4822 case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
4823 case ERROR_BAD_DEV_TYPE: return ENODEV;
4824 case ERROR_BAD_NET_NAME: return ENOENT;
4825 case ERROR_TOO_MANY_NAMES: return ENFILE;
4826 case ERROR_TOO_MANY_SESS: return EIO;
4827 case ERROR_SHARING_PAUSED: return EAGAIN;
4828 case ERROR_REDIR_PAUSED: return EAGAIN;
4829 case ERROR_FILE_EXISTS: return EEXIST;
4830 case ERROR_CANNOT_MAKE: return ENOSPC;
4831 case ERROR_OUT_OF_STRUCTURES: return ENFILE;
4832 case ERROR_ALREADY_ASSIGNED: return EEXIST;
4833 case ERROR_INVALID_PASSWORD: return EPERM;
4834 case ERROR_NET_WRITE_FAULT: return EIO;
4835 case ERROR_NO_PROC_SLOTS: return EAGAIN;
4836 case ERROR_DISK_CHANGE: return EXDEV;
4837 case ERROR_BROKEN_PIPE: return EPIPE;
4838 case ERROR_OPEN_FAILED: return ENOENT;
4839 case ERROR_DISK_FULL: return ENOSPC;
4840 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
4841 case ERROR_INVALID_TARGET_HANDLE: return EBADF;
4842 case ERROR_INVALID_NAME: return ENOENT;
4843 case ERROR_PROC_NOT_FOUND: return ESRCH;
4844 case ERROR_WAIT_NO_CHILDREN: return ECHILD;
4845 case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
4846 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
4847 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
4848 case ERROR_BUSY_DRIVE: return EAGAIN;
4849 case ERROR_DIR_NOT_EMPTY: return EEXIST;
4850 case ERROR_NOT_LOCKED: return EACCES;
4851 case ERROR_BAD_PATHNAME: return ENOENT;
4852 case ERROR_LOCK_FAILED: return EACCES;
4853 case ERROR_ALREADY_EXISTS: return EEXIST;
4854 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
4855 case ERROR_BAD_PIPE: return EPIPE;
4856 case ERROR_PIPE_BUSY: return EAGAIN;
4857 case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
4858 case ERROR_DIRECTORY: return ENOTDIR;
4860 return EINVAL;
4863 static int JimPipe(fdtype pipefd[2])
4865 if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
4866 return 0;
4868 return -1;
4871 static fdtype JimDupFd(fdtype infd)
4873 fdtype dupfd;
4874 pidtype pid = GetCurrentProcess();
4876 if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
4877 return dupfd;
4879 return JIM_BAD_FD;
4882 static int JimRewindFd(fdtype fd)
4884 return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
4887 #if 0
4888 static int JimReadFd(fdtype fd, char *buffer, size_t len)
4890 DWORD num;
4892 if (ReadFile(fd, buffer, len, &num, NULL)) {
4893 return num;
4895 if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
4896 return 0;
4898 return -1;
4900 #endif
4902 static FILE *JimFdOpenForRead(fdtype fd)
4904 return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
4907 static fdtype JimFileno(FILE *fh)
4909 return (fdtype)_get_osfhandle(_fileno(fh));
4912 static fdtype JimOpenForRead(const char *filename)
4914 return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
4915 JimStdSecAttrs(), OPEN_EXISTING, 0, NULL);
4918 static fdtype JimOpenForWrite(const char *filename, int append)
4920 return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
4921 JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL);
4924 static FILE *JimFdOpenForWrite(fdtype fd)
4926 return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
4929 static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
4931 DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
4932 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
4934 return JIM_BAD_PID;
4936 GetExitCodeProcess(pid, &ret);
4937 *status = ret;
4938 CloseHandle(pid);
4939 return pid;
4942 static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
4944 char name[MAX_PATH];
4945 HANDLE handle;
4947 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
4948 return JIM_BAD_FD;
4951 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
4952 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
4953 NULL);
4955 if (handle == INVALID_HANDLE_VALUE) {
4956 goto error;
4959 if (contents != NULL) {
4961 FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
4962 if (fh == NULL) {
4963 goto error;
4966 if (fwrite(contents, len, 1, fh) != 1) {
4967 fclose(fh);
4968 goto error;
4970 fseek(fh, 0, SEEK_SET);
4971 fclose(fh);
4973 return handle;
4975 error:
4976 Jim_SetResultErrno(interp, "failed to create temp file");
4977 CloseHandle(handle);
4978 DeleteFile(name);
4979 return JIM_BAD_FD;
4982 static int
4983 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
4985 int i;
4986 static char extensions[][5] = {".exe", "", ".bat"};
4988 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
4989 lstrcpyn(fullPath, originalName, MAX_PATH - 5);
4990 lstrcat(fullPath, extensions[i]);
4992 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
4993 continue;
4995 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
4996 continue;
4998 return 0;
5001 return -1;
5004 static char **JimSaveEnv(char **env)
5006 return env;
5009 static void JimRestoreEnv(char **env)
5011 JimFreeEnv(env, Jim_GetEnviron());
5014 static Jim_Obj *
5015 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
5017 char *start, *special;
5018 int quote, i;
5020 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
5022 for (i = 0; argv[i]; i++) {
5023 if (i > 0) {
5024 Jim_AppendString(interp, strObj, " ", 1);
5027 if (argv[i][0] == '\0') {
5028 quote = 1;
5030 else {
5031 quote = 0;
5032 for (start = argv[i]; *start != '\0'; start++) {
5033 if (isspace(UCHAR(*start))) {
5034 quote = 1;
5035 break;
5039 if (quote) {
5040 Jim_AppendString(interp, strObj, "\"" , 1);
5043 start = argv[i];
5044 for (special = argv[i]; ; ) {
5045 if ((*special == '\\') && (special[1] == '\\' ||
5046 special[1] == '"' || (quote && special[1] == '\0'))) {
5047 Jim_AppendString(interp, strObj, start, special - start);
5048 start = special;
5049 while (1) {
5050 special++;
5051 if (*special == '"' || (quote && *special == '\0')) {
5053 Jim_AppendString(interp, strObj, start, special - start);
5054 break;
5056 if (*special != '\\') {
5057 break;
5060 Jim_AppendString(interp, strObj, start, special - start);
5061 start = special;
5063 if (*special == '"') {
5064 if (special == start) {
5065 Jim_AppendString(interp, strObj, "\"", 1);
5067 else {
5068 Jim_AppendString(interp, strObj, start, special - start);
5070 Jim_AppendString(interp, strObj, "\\\"", 2);
5071 start = special + 1;
5073 if (*special == '\0') {
5074 break;
5076 special++;
5078 Jim_AppendString(interp, strObj, start, special - start);
5079 if (quote) {
5080 Jim_AppendString(interp, strObj, "\"", 1);
5083 return strObj;
5086 static pidtype
5087 JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId)
5089 STARTUPINFO startInfo;
5090 PROCESS_INFORMATION procInfo;
5091 HANDLE hProcess, h;
5092 char execPath[MAX_PATH];
5093 char *originalName;
5094 pidtype pid = JIM_BAD_PID;
5095 Jim_Obj *cmdLineObj;
5097 if (JimWinFindExecutable(argv[0], execPath) < 0) {
5098 return JIM_BAD_PID;
5100 originalName = argv[0];
5101 argv[0] = execPath;
5103 hProcess = GetCurrentProcess();
5104 cmdLineObj = JimWinBuildCommandLine(interp, argv);
5107 ZeroMemory(&startInfo, sizeof(startInfo));
5108 startInfo.cb = sizeof(startInfo);
5109 startInfo.dwFlags = STARTF_USESTDHANDLES;
5110 startInfo.hStdInput = INVALID_HANDLE_VALUE;
5111 startInfo.hStdOutput= INVALID_HANDLE_VALUE;
5112 startInfo.hStdError = INVALID_HANDLE_VALUE;
5114 if (inputId == JIM_BAD_FD) {
5115 if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
5116 CloseHandle(h);
5118 } else {
5119 DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
5120 0, TRUE, DUPLICATE_SAME_ACCESS);
5122 if (startInfo.hStdInput == JIM_BAD_FD) {
5123 goto end;
5126 if (outputId == JIM_BAD_FD) {
5127 startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
5128 JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5129 } else {
5130 DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
5131 0, TRUE, DUPLICATE_SAME_ACCESS);
5133 if (startInfo.hStdOutput == JIM_BAD_FD) {
5134 goto end;
5137 if (errorId == JIM_BAD_FD) {
5139 startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
5140 JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
5141 } else {
5142 DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
5143 0, TRUE, DUPLICATE_SAME_ACCESS);
5145 if (startInfo.hStdError == JIM_BAD_FD) {
5146 goto end;
5149 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
5150 0, env, NULL, &startInfo, &procInfo)) {
5151 goto end;
5155 WaitForInputIdle(procInfo.hProcess, 5000);
5156 CloseHandle(procInfo.hThread);
5158 pid = procInfo.hProcess;
5160 end:
5161 Jim_FreeNewObj(interp, cmdLineObj);
5162 if (startInfo.hStdInput != JIM_BAD_FD) {
5163 CloseHandle(startInfo.hStdInput);
5165 if (startInfo.hStdOutput != JIM_BAD_FD) {
5166 CloseHandle(startInfo.hStdOutput);
5168 if (startInfo.hStdError != JIM_BAD_FD) {
5169 CloseHandle(startInfo.hStdError);
5171 return pid;
5173 #else
5175 static int JimOpenForWrite(const char *filename, int append)
5177 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
5180 static int JimRewindFd(int fd)
5182 return lseek(fd, 0L, SEEK_SET);
5185 static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5187 char inName[] = "/tmp/tcl.tmp.XXXXXX";
5189 int fd = mkstemp(inName);
5190 if (fd == JIM_BAD_FD) {
5191 Jim_SetResultErrno(interp, "couldn't create temp file");
5192 return -1;
5194 unlink(inName);
5195 if (contents) {
5196 if (write(fd, contents, len) != len) {
5197 Jim_SetResultErrno(interp, "couldn't write temp file");
5198 close(fd);
5199 return -1;
5201 lseek(fd, 0L, SEEK_SET);
5203 return fd;
5206 static char **JimSaveEnv(char **env)
5208 char **saveenv = Jim_GetEnviron();
5209 Jim_SetEnviron(env);
5210 return saveenv;
5213 static void JimRestoreEnv(char **env)
5215 JimFreeEnv(Jim_GetEnviron(), env);
5216 Jim_SetEnviron(env);
5218 #endif
5219 #endif
5223 #ifndef _XOPEN_SOURCE
5224 #define _XOPEN_SOURCE 500
5225 #endif
5227 #include <stdlib.h>
5228 #include <string.h>
5229 #include <stdio.h>
5230 #include <time.h>
5233 #ifdef HAVE_SYS_TIME_H
5234 #include <sys/time.h>
5235 #endif
5237 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5240 char buf[100];
5241 time_t t;
5242 long seconds;
5244 const char *format = "%a %b %d %H:%M:%S %Z %Y";
5246 if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) {
5247 return -1;
5250 if (argc == 3) {
5251 format = Jim_String(argv[2]);
5254 if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) {
5255 return JIM_ERR;
5257 t = seconds;
5259 strftime(buf, sizeof(buf), format, localtime(&t));
5261 Jim_SetResultString(interp, buf, -1);
5263 return JIM_OK;
5266 #ifdef HAVE_STRPTIME
5267 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5269 char *pt;
5270 struct tm tm;
5271 time_t now = time(0);
5273 if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) {
5274 return -1;
5278 localtime_r(&now, &tm);
5280 pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm);
5281 if (pt == 0 || *pt != 0) {
5282 Jim_SetResultString(interp, "Failed to parse time according to format", -1);
5283 return JIM_ERR;
5287 Jim_SetResultInt(interp, mktime(&tm));
5289 return JIM_OK;
5291 #endif
5293 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5295 Jim_SetResultInt(interp, time(NULL));
5297 return JIM_OK;
5300 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5302 struct timeval tv;
5304 gettimeofday(&tv, NULL);
5306 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
5308 return JIM_OK;
5311 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5313 struct timeval tv;
5315 gettimeofday(&tv, NULL);
5317 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
5319 return JIM_OK;
5322 static const jim_subcmd_type clock_command_table[] = {
5323 { "seconds",
5324 NULL,
5325 clock_cmd_seconds,
5330 { "clicks",
5331 NULL,
5332 clock_cmd_micros,
5337 { "microseconds",
5338 NULL,
5339 clock_cmd_micros,
5344 { "milliseconds",
5345 NULL,
5346 clock_cmd_millis,
5351 { "format",
5352 "seconds ?-format format?",
5353 clock_cmd_format,
5358 #ifdef HAVE_STRPTIME
5359 { "scan",
5360 "str -format format",
5361 clock_cmd_scan,
5366 #endif
5367 { NULL }
5370 int Jim_clockInit(Jim_Interp *interp)
5372 if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
5373 return JIM_ERR;
5375 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
5376 return JIM_OK;
5380 #include <limits.h>
5381 #include <stdlib.h>
5382 #include <string.h>
5383 #include <stdio.h>
5384 #include <errno.h>
5387 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5390 Jim_SetResultInt(interp, Jim_GetVariable(interp, argv[0], 0) != 0);
5391 return JIM_OK;
5394 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5396 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5398 if (!objPtr) {
5399 return JIM_OK;
5402 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5404 if (Jim_IsList(objPtr)) {
5405 if (Jim_ListLength(interp, objPtr) % 2 != 0) {
5407 return JIM_ERR;
5410 else if (Jim_DictSize(interp, objPtr) < 0) {
5412 return JIM_ERR;
5414 Jim_SetResult(interp, objPtr);
5415 return JIM_OK;
5419 return Jim_DictValues(interp, objPtr, argv[1]);
5422 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5424 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5426 if (!objPtr) {
5427 return JIM_OK;
5430 return Jim_DictKeys(interp, objPtr, argc == 1 ? NULL : argv[1]);
5433 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5435 int i;
5436 int len;
5437 Jim_Obj *resultObj;
5438 Jim_Obj *objPtr;
5439 Jim_Obj **dictValuesObj;
5441 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5443 Jim_UnsetVariable(interp, argv[0], JIM_NONE);
5444 return JIM_OK;
5447 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5449 if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {
5450 return JIM_ERR;
5454 resultObj = Jim_NewDictObj(interp, NULL, 0);
5456 for (i = 0; i < len; i += 2) {
5457 if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
5458 Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
5461 Jim_Free(dictValuesObj);
5463 Jim_SetVariable(interp, argv[0], resultObj);
5464 return JIM_OK;
5467 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5469 Jim_Obj *objPtr;
5470 int len = 0;
5473 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5474 if (objPtr) {
5475 len = Jim_DictSize(interp, objPtr);
5476 if (len < 0) {
5477 return JIM_ERR;
5481 Jim_SetResultInt(interp, len);
5483 return JIM_OK;
5486 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5488 int i;
5489 int len;
5490 Jim_Obj *listObj = argv[1];
5491 Jim_Obj *dictObj;
5493 len = Jim_ListLength(interp, listObj);
5494 if (len % 2) {
5495 Jim_SetResultString(interp, "list must have an even number of elements", -1);
5496 return JIM_ERR;
5499 dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5500 if (!dictObj) {
5502 return Jim_SetVariable(interp, argv[0], listObj);
5505 if (Jim_IsShared(dictObj)) {
5506 dictObj = Jim_DuplicateObj(interp, dictObj);
5509 for (i = 0; i < len; i += 2) {
5510 Jim_Obj *nameObj;
5511 Jim_Obj *valueObj;
5513 Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
5514 Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
5516 Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
5518 return Jim_SetVariable(interp, argv[0], dictObj);
5521 static const jim_subcmd_type array_command_table[] = {
5522 { "exists",
5523 "arrayName",
5524 array_cmd_exists,
5529 { "get",
5530 "arrayName ?pattern?",
5531 array_cmd_get,
5536 { "names",
5537 "arrayName ?pattern?",
5538 array_cmd_names,
5543 { "set",
5544 "arrayName list",
5545 array_cmd_set,
5550 { "size",
5551 "arrayName",
5552 array_cmd_size,
5557 { "unset",
5558 "arrayName ?pattern?",
5559 array_cmd_unset,
5564 { NULL
5568 int Jim_arrayInit(Jim_Interp *interp)
5570 if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG))
5571 return JIM_ERR;
5573 Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
5574 return JIM_OK;
5576 int Jim_InitStaticExtensions(Jim_Interp *interp)
5578 extern int Jim_bootstrapInit(Jim_Interp *);
5579 extern int Jim_aioInit(Jim_Interp *);
5580 extern int Jim_readdirInit(Jim_Interp *);
5581 extern int Jim_globInit(Jim_Interp *);
5582 extern int Jim_regexpInit(Jim_Interp *);
5583 extern int Jim_fileInit(Jim_Interp *);
5584 extern int Jim_execInit(Jim_Interp *);
5585 extern int Jim_clockInit(Jim_Interp *);
5586 extern int Jim_arrayInit(Jim_Interp *);
5587 extern int Jim_stdlibInit(Jim_Interp *);
5588 extern int Jim_tclcompatInit(Jim_Interp *);
5589 Jim_bootstrapInit(interp);
5590 Jim_aioInit(interp);
5591 Jim_readdirInit(interp);
5592 Jim_globInit(interp);
5593 Jim_regexpInit(interp);
5594 Jim_fileInit(interp);
5595 Jim_execInit(interp);
5596 Jim_clockInit(interp);
5597 Jim_arrayInit(interp);
5598 Jim_stdlibInit(interp);
5599 Jim_tclcompatInit(interp);
5600 return JIM_OK;
5603 #define JIM_OPTIMIZATION
5605 #include <stdio.h>
5606 #include <stdlib.h>
5608 #include <string.h>
5609 #include <stdarg.h>
5610 #include <ctype.h>
5611 #include <limits.h>
5612 #include <assert.h>
5613 #include <errno.h>
5614 #include <time.h>
5615 #include <setjmp.h>
5618 #ifdef HAVE_SYS_TIME_H
5619 #include <sys/time.h>
5620 #endif
5621 #ifdef HAVE_BACKTRACE
5622 #include <execinfo.h>
5623 #endif
5624 #ifdef HAVE_CRT_EXTERNS_H
5625 #include <crt_externs.h>
5626 #endif
5629 #include <math.h>
5635 #ifndef TCL_LIBRARY
5636 #define TCL_LIBRARY "."
5637 #endif
5638 #ifndef TCL_PLATFORM_OS
5639 #define TCL_PLATFORM_OS "unknown"
5640 #endif
5641 #ifndef TCL_PLATFORM_PLATFORM
5642 #define TCL_PLATFORM_PLATFORM "unknown"
5643 #endif
5644 #ifndef TCL_PLATFORM_PATH_SEPARATOR
5645 #define TCL_PLATFORM_PATH_SEPARATOR ":"
5646 #endif
5654 #ifdef JIM_MAINTAINER
5655 #define JIM_DEBUG_COMMAND
5656 #define JIM_DEBUG_PANIC
5657 #endif
5661 #define JIM_INTEGER_SPACE 24
5663 const char *jim_tt_name(int type);
5665 #ifdef JIM_DEBUG_PANIC
5666 static void JimPanicDump(int panic_condition, const char *fmt, ...);
5667 #define JimPanic(X) JimPanicDump X
5668 #else
5669 #define JimPanic(X)
5670 #endif
5673 static char JimEmptyStringRep[] = "";
5675 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
5676 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
5677 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
5678 int flags);
5679 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
5680 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
5681 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5682 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
5683 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
5684 const char *prefix, const char *const *tablePtr, const char *name);
5685 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
5686 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
5687 static int JimSign(jim_wide w);
5688 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr);
5689 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
5690 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
5694 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
5696 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
5698 static int utf8_tounicode_case(const char *s, int *uc, int upper)
5700 int l = utf8_tounicode(s, uc);
5701 if (upper) {
5702 *uc = utf8_upper(*uc);
5704 return l;
5708 #define JIM_CHARSET_SCAN 2
5709 #define JIM_CHARSET_GLOB 0
5711 static const char *JimCharsetMatch(const char *pattern, int c, int flags)
5713 int not = 0;
5714 int pchar;
5715 int match = 0;
5716 int nocase = 0;
5718 if (flags & JIM_NOCASE) {
5719 nocase++;
5720 c = utf8_upper(c);
5723 if (flags & JIM_CHARSET_SCAN) {
5724 if (*pattern == '^') {
5725 not++;
5726 pattern++;
5730 if (*pattern == ']') {
5731 goto first;
5735 while (*pattern && *pattern != ']') {
5737 if (pattern[0] == '\\') {
5738 first:
5739 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
5741 else {
5743 int start;
5744 int end;
5746 pattern += utf8_tounicode_case(pattern, &start, nocase);
5747 if (pattern[0] == '-' && pattern[1]) {
5749 pattern += utf8_tounicode(pattern, &pchar);
5750 pattern += utf8_tounicode_case(pattern, &end, nocase);
5753 if ((c >= start && c <= end) || (c >= end && c <= start)) {
5754 match = 1;
5756 continue;
5758 pchar = start;
5761 if (pchar == c) {
5762 match = 1;
5765 if (not) {
5766 match = !match;
5769 return match ? pattern : NULL;
5774 static int JimGlobMatch(const char *pattern, const char *string, int nocase)
5776 int c;
5777 int pchar;
5778 while (*pattern) {
5779 switch (pattern[0]) {
5780 case '*':
5781 while (pattern[1] == '*') {
5782 pattern++;
5784 pattern++;
5785 if (!pattern[0]) {
5786 return 1;
5788 while (*string) {
5790 if (JimGlobMatch(pattern, string, nocase))
5791 return 1;
5792 string += utf8_tounicode(string, &c);
5794 return 0;
5796 case '?':
5797 string += utf8_tounicode(string, &c);
5798 break;
5800 case '[': {
5801 string += utf8_tounicode(string, &c);
5802 pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
5803 if (!pattern) {
5804 return 0;
5806 if (!*pattern) {
5808 continue;
5810 break;
5812 case '\\':
5813 if (pattern[1]) {
5814 pattern++;
5817 default:
5818 string += utf8_tounicode_case(string, &c, nocase);
5819 utf8_tounicode_case(pattern, &pchar, nocase);
5820 if (pchar != c) {
5821 return 0;
5823 break;
5825 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
5826 if (!*string) {
5827 while (*pattern == '*') {
5828 pattern++;
5830 break;
5833 if (!*pattern && !*string) {
5834 return 1;
5836 return 0;
5839 static int JimStringCompare(const char *s1, int l1, const char *s2, int l2)
5841 if (l1 < l2) {
5842 return memcmp(s1, s2, l1) <= 0 ? -1 : 1;
5844 else if (l2 < l1) {
5845 return memcmp(s1, s2, l2) >= 0 ? 1 : -1;
5847 else {
5848 return JimSign(memcmp(s1, s2, l1));
5852 static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int nocase)
5854 while (*s1 && *s2 && maxchars) {
5855 int c1, c2;
5856 s1 += utf8_tounicode_case(s1, &c1, nocase);
5857 s2 += utf8_tounicode_case(s2, &c2, nocase);
5858 if (c1 != c2) {
5859 return JimSign(c1 - c2);
5861 maxchars--;
5863 if (!maxchars) {
5864 return 0;
5867 if (*s1) {
5868 return 1;
5870 if (*s2) {
5871 return -1;
5873 return 0;
5876 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
5878 int i;
5879 int l1bytelen;
5881 if (!l1 || !l2 || l1 > l2) {
5882 return -1;
5884 if (idx < 0)
5885 idx = 0;
5886 s2 += utf8_index(s2, idx);
5888 l1bytelen = utf8_index(s1, l1);
5890 for (i = idx; i <= l2 - l1; i++) {
5891 int c;
5892 if (memcmp(s2, s1, l1bytelen) == 0) {
5893 return i;
5895 s2 += utf8_tounicode(s2, &c);
5897 return -1;
5900 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
5902 const char *p;
5904 if (!l1 || !l2 || l1 > l2)
5905 return -1;
5908 for (p = s2 + l2 - 1; p != s2 - 1; p--) {
5909 if (*p == *s1 && memcmp(s1, p, l1) == 0) {
5910 return p - s2;
5913 return -1;
5916 #ifdef JIM_UTF8
5917 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
5919 int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
5920 if (n > 0) {
5921 n = utf8_strlen(s2, n);
5923 return n;
5925 #endif
5927 static int JimWideToString(char *buf, jim_wide wideValue)
5929 int pos = 0;
5931 if (wideValue == 0) {
5932 buf[pos++] = '0';
5934 else {
5935 char tmp[JIM_INTEGER_SPACE];
5936 int num = 0;
5937 int i;
5939 if (wideValue < 0) {
5940 buf[pos++] = '-';
5942 i = wideValue % 10;
5943 tmp[num++] = (i > 0) ? (10 - i) : -i;
5944 wideValue /= -10;
5947 while (wideValue) {
5948 tmp[num++] = wideValue % 10;
5949 wideValue /= 10;
5952 for (i = 0; i < num; i++) {
5953 buf[pos++] = '0' + tmp[num - i - 1];
5956 buf[pos] = 0;
5958 return pos;
5961 static int JimCheckConversion(const char *str, const char *endptr)
5963 if (str[0] == '\0' || str == endptr) {
5964 return JIM_ERR;
5967 if (endptr[0] != '\0') {
5968 while (*endptr) {
5969 if (!isspace(UCHAR(*endptr))) {
5970 return JIM_ERR;
5972 endptr++;
5975 return JIM_OK;
5978 static int JimNumberBase(const char *str, int *base, int *sign)
5980 int i = 0;
5982 *base = 10;
5984 while (isspace(UCHAR(str[i]))) {
5985 i++;
5988 if (str[i] == '-') {
5989 *sign = -1;
5990 i++;
5992 else {
5993 if (str[i] == '+') {
5994 i++;
5996 *sign = 1;
5999 if (str[i] != '0') {
6001 return 0;
6005 switch (str[i + 1]) {
6006 case 'x': case 'X': *base = 16; break;
6007 case 'o': case 'O': *base = 8; break;
6008 case 'b': case 'B': *base = 2; break;
6009 default: return 0;
6011 i += 2;
6013 if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
6015 return i;
6018 *base = 10;
6019 return 0;
6022 static long jim_strtol(const char *str, char **endptr)
6024 int sign;
6025 int base;
6026 int i = JimNumberBase(str, &base, &sign);
6028 if (base != 10) {
6029 long value = strtol(str + i, endptr, base);
6030 if (endptr == NULL || *endptr != str + i) {
6031 return value * sign;
6036 return strtol(str, endptr, 10);
6040 static jim_wide jim_strtoull(const char *str, char **endptr)
6042 #ifdef HAVE_LONG_LONG
6043 int sign;
6044 int base;
6045 int i = JimNumberBase(str, &base, &sign);
6047 if (base != 10) {
6048 jim_wide value = strtoull(str + i, endptr, base);
6049 if (endptr == NULL || *endptr != str + i) {
6050 return value * sign;
6055 return strtoull(str, endptr, 10);
6056 #else
6057 return (unsigned long)jim_strtol(str, endptr);
6058 #endif
6061 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
6063 char *endptr;
6065 if (base) {
6066 *widePtr = strtoull(str, &endptr, base);
6068 else {
6069 *widePtr = jim_strtoull(str, &endptr);
6072 return JimCheckConversion(str, endptr);
6075 int Jim_DoubleToString(char *buf, double doubleValue)
6077 int len;
6078 int i;
6080 len = sprintf(buf, "%.12g", doubleValue);
6083 for (i = 0; i < len; i++) {
6084 if (buf[i] == '.' || buf[i] == 'e') {
6085 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
6086 char *e = strchr(buf, 'e');
6087 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
6089 e += 2;
6090 memmove(e, e + 1, len - (e - buf));
6091 return len - 1;
6093 #endif
6094 return len;
6097 if (buf[i] == 'i' || buf[i] == 'I' || buf[i] == 'n' || buf[i] == 'N') {
6098 buf[i] = toupper(UCHAR(buf[i]));
6099 if (buf[i] == 'n' || buf[i] == 'N')
6100 buf[i+2] = toupper(UCHAR(buf[i+2]));
6101 buf[i + 3] = 0;
6102 return i + 3;
6106 buf[i++] = '.';
6107 buf[i++] = '0';
6108 buf[i] = '\0';
6110 return i;
6113 int Jim_StringToDouble(const char *str, double *doublePtr)
6115 char *endptr;
6118 errno = 0;
6120 *doublePtr = strtod(str, &endptr);
6122 return JimCheckConversion(str, endptr);
6125 static jim_wide JimPowWide(jim_wide b, jim_wide e)
6127 jim_wide i, res = 1;
6129 if ((b == 0 && e != 0) || (e < 0))
6130 return 0;
6131 for (i = 0; i < e; i++) {
6132 res *= b;
6134 return res;
6137 #ifdef JIM_DEBUG_PANIC
6138 void JimPanicDump(int condition, const char *fmt, ...)
6140 va_list ap;
6142 if (!condition) {
6143 return;
6146 va_start(ap, fmt);
6148 fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
6149 vfprintf(stderr, fmt, ap);
6150 fprintf(stderr, JIM_NL JIM_NL);
6151 va_end(ap);
6153 #ifdef HAVE_BACKTRACE
6155 void *array[40];
6156 int size, i;
6157 char **strings;
6159 size = backtrace(array, 40);
6160 strings = backtrace_symbols(array, size);
6161 for (i = 0; i < size; i++)
6162 fprintf(stderr, "[backtrace] %s" JIM_NL, strings[i]);
6163 fprintf(stderr, "[backtrace] Include the above lines and the output" JIM_NL);
6164 fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
6166 #endif
6168 exit(1);
6170 #endif
6173 void *Jim_Alloc(int size)
6175 return size ? malloc(size) : NULL;
6178 void Jim_Free(void *ptr)
6180 free(ptr);
6183 void *Jim_Realloc(void *ptr, int size)
6185 return realloc(ptr, size);
6188 char *Jim_StrDup(const char *s)
6190 return strdup(s);
6193 char *Jim_StrDupLen(const char *s, int l)
6195 char *copy = Jim_Alloc(l + 1);
6197 memcpy(copy, s, l + 1);
6198 copy[l] = 0;
6199 return copy;
6204 static jim_wide JimClock(void)
6206 struct timeval tv;
6208 gettimeofday(&tv, NULL);
6209 return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec;
6214 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
6215 static unsigned int JimHashTableNextPower(unsigned int size);
6216 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
6221 unsigned int Jim_IntHashFunction(unsigned int key)
6223 key += ~(key << 15);
6224 key ^= (key >> 10);
6225 key += (key << 3);
6226 key ^= (key >> 6);
6227 key += ~(key << 11);
6228 key ^= (key >> 16);
6229 return key;
6232 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
6234 unsigned int h = 0;
6236 while (len--)
6237 h += (h << 3) + *buf++;
6238 return h;
6243 static void JimResetHashTable(Jim_HashTable *ht)
6245 ht->table = NULL;
6246 ht->size = 0;
6247 ht->sizemask = 0;
6248 ht->used = 0;
6249 ht->collisions = 0;
6252 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
6254 iter->ht = ht;
6255 iter->index = -1;
6256 iter->entry = NULL;
6257 iter->nextEntry = NULL;
6261 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
6263 JimResetHashTable(ht);
6264 ht->type = type;
6265 ht->privdata = privDataPtr;
6266 return JIM_OK;
6269 void Jim_ResizeHashTable(Jim_HashTable *ht)
6271 int minimal = ht->used;
6273 if (minimal < JIM_HT_INITIAL_SIZE)
6274 minimal = JIM_HT_INITIAL_SIZE;
6275 Jim_ExpandHashTable(ht, minimal);
6279 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
6281 Jim_HashTable n;
6282 unsigned int realsize = JimHashTableNextPower(size), i;
6284 if (size <= ht->used)
6285 return;
6287 Jim_InitHashTable(&n, ht->type, ht->privdata);
6288 n.size = realsize;
6289 n.sizemask = realsize - 1;
6290 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
6293 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
6295 n.used = ht->used;
6296 for (i = 0; ht->used > 0; i++) {
6297 Jim_HashEntry *he, *nextHe;
6299 if (ht->table[i] == NULL)
6300 continue;
6303 he = ht->table[i];
6304 while (he) {
6305 unsigned int h;
6307 nextHe = he->next;
6309 h = Jim_HashKey(ht, he->key) & n.sizemask;
6310 he->next = n.table[h];
6311 n.table[h] = he;
6312 ht->used--;
6314 he = nextHe;
6317 assert(ht->used == 0);
6318 Jim_Free(ht->table);
6321 *ht = n;
6325 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
6327 Jim_HashEntry *entry;
6329 entry = JimInsertHashEntry(ht, key, 0);
6330 if (entry == NULL)
6331 return JIM_ERR;
6334 Jim_SetHashKey(ht, entry, key);
6335 Jim_SetHashVal(ht, entry, val);
6336 return JIM_OK;
6340 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6342 int existed;
6343 Jim_HashEntry *entry;
6345 entry = JimInsertHashEntry(ht, key, 1);
6346 if (entry->key) {
6348 Jim_FreeEntryVal(ht, entry);
6349 existed = 1;
6351 else {
6353 Jim_SetHashKey(ht, entry, key);
6354 existed = 0;
6356 Jim_SetHashVal(ht, entry, val);
6358 return existed;
6362 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
6364 unsigned int h;
6365 Jim_HashEntry *he, *prevHe;
6367 if (ht->used == 0)
6368 return JIM_ERR;
6369 h = Jim_HashKey(ht, key) & ht->sizemask;
6370 he = ht->table[h];
6372 prevHe = NULL;
6373 while (he) {
6374 if (Jim_CompareHashKeys(ht, key, he->key)) {
6376 if (prevHe)
6377 prevHe->next = he->next;
6378 else
6379 ht->table[h] = he->next;
6380 Jim_FreeEntryKey(ht, he);
6381 Jim_FreeEntryVal(ht, he);
6382 Jim_Free(he);
6383 ht->used--;
6384 return JIM_OK;
6386 prevHe = he;
6387 he = he->next;
6389 return JIM_ERR;
6393 int Jim_FreeHashTable(Jim_HashTable *ht)
6395 unsigned int i;
6398 for (i = 0; ht->used > 0; i++) {
6399 Jim_HashEntry *he, *nextHe;
6401 if ((he = ht->table[i]) == NULL)
6402 continue;
6403 while (he) {
6404 nextHe = he->next;
6405 Jim_FreeEntryKey(ht, he);
6406 Jim_FreeEntryVal(ht, he);
6407 Jim_Free(he);
6408 ht->used--;
6409 he = nextHe;
6413 Jim_Free(ht->table);
6415 JimResetHashTable(ht);
6416 return JIM_OK;
6419 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
6421 Jim_HashEntry *he;
6422 unsigned int h;
6424 if (ht->used == 0)
6425 return NULL;
6426 h = Jim_HashKey(ht, key) & ht->sizemask;
6427 he = ht->table[h];
6428 while (he) {
6429 if (Jim_CompareHashKeys(ht, key, he->key))
6430 return he;
6431 he = he->next;
6433 return NULL;
6436 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
6438 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
6439 JimInitHashTableIterator(ht, iter);
6440 return iter;
6443 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
6445 while (1) {
6446 if (iter->entry == NULL) {
6447 iter->index++;
6448 if (iter->index >= (signed)iter->ht->size)
6449 break;
6450 iter->entry = iter->ht->table[iter->index];
6452 else {
6453 iter->entry = iter->nextEntry;
6455 if (iter->entry) {
6456 iter->nextEntry = iter->entry->next;
6457 return iter->entry;
6460 return NULL;
6466 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
6468 if (ht->size == 0)
6469 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
6470 if (ht->size == ht->used)
6471 Jim_ExpandHashTable(ht, ht->size * 2);
6475 static unsigned int JimHashTableNextPower(unsigned int size)
6477 unsigned int i = JIM_HT_INITIAL_SIZE;
6479 if (size >= 2147483648U)
6480 return 2147483648U;
6481 while (1) {
6482 if (i >= size)
6483 return i;
6484 i *= 2;
6488 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
6490 unsigned int h;
6491 Jim_HashEntry *he;
6494 JimExpandHashTableIfNeeded(ht);
6497 h = Jim_HashKey(ht, key) & ht->sizemask;
6499 he = ht->table[h];
6500 while (he) {
6501 if (Jim_CompareHashKeys(ht, key, he->key))
6502 return replace ? he : NULL;
6503 he = he->next;
6507 he = Jim_Alloc(sizeof(*he));
6508 he->next = ht->table[h];
6509 ht->table[h] = he;
6510 ht->used++;
6511 he->key = NULL;
6513 return he;
6518 static unsigned int JimStringCopyHTHashFunction(const void *key)
6520 return Jim_GenHashFunction(key, strlen(key));
6523 static void *JimStringCopyHTDup(void *privdata, const void *key)
6525 return strdup(key);
6528 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
6530 return strcmp(key1, key2) == 0;
6533 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
6535 Jim_Free(key);
6538 static const Jim_HashTableType JimPackageHashTableType = {
6539 JimStringCopyHTHashFunction,
6540 JimStringCopyHTDup,
6541 NULL,
6542 JimStringCopyHTKeyCompare,
6543 JimStringCopyHTKeyDestructor,
6544 NULL
6547 typedef struct AssocDataValue
6549 Jim_InterpDeleteProc *delProc;
6550 void *data;
6551 } AssocDataValue;
6553 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
6555 AssocDataValue *assocPtr = (AssocDataValue *) data;
6557 if (assocPtr->delProc != NULL)
6558 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
6559 Jim_Free(data);
6562 static const Jim_HashTableType JimAssocDataHashTableType = {
6563 JimStringCopyHTHashFunction,
6564 JimStringCopyHTDup,
6565 NULL,
6566 JimStringCopyHTKeyCompare,
6567 JimStringCopyHTKeyDestructor,
6568 JimAssocDataHashTableValueDestructor
6571 void Jim_InitStack(Jim_Stack *stack)
6573 stack->len = 0;
6574 stack->maxlen = 0;
6575 stack->vector = NULL;
6578 void Jim_FreeStack(Jim_Stack *stack)
6580 Jim_Free(stack->vector);
6583 int Jim_StackLen(Jim_Stack *stack)
6585 return stack->len;
6588 void Jim_StackPush(Jim_Stack *stack, void *element)
6590 int neededLen = stack->len + 1;
6592 if (neededLen > stack->maxlen) {
6593 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
6594 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
6596 stack->vector[stack->len] = element;
6597 stack->len++;
6600 void *Jim_StackPop(Jim_Stack *stack)
6602 if (stack->len == 0)
6603 return NULL;
6604 stack->len--;
6605 return stack->vector[stack->len];
6608 void *Jim_StackPeek(Jim_Stack *stack)
6610 if (stack->len == 0)
6611 return NULL;
6612 return stack->vector[stack->len - 1];
6615 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
6617 int i;
6619 for (i = 0; i < stack->len; i++)
6620 freeFunc(stack->vector[i]);
6625 #define JIM_TT_NONE 0
6626 #define JIM_TT_STR 1
6627 #define JIM_TT_ESC 2
6628 #define JIM_TT_VAR 3
6629 #define JIM_TT_DICTSUGAR 4
6630 #define JIM_TT_CMD 5
6632 #define JIM_TT_SEP 6
6633 #define JIM_TT_EOL 7
6634 #define JIM_TT_EOF 8
6636 #define JIM_TT_LINE 9
6637 #define JIM_TT_WORD 10
6640 #define JIM_TT_SUBEXPR_START 11
6641 #define JIM_TT_SUBEXPR_END 12
6642 #define JIM_TT_SUBEXPR_COMMA 13
6643 #define JIM_TT_EXPR_INT 14
6644 #define JIM_TT_EXPR_DOUBLE 15
6646 #define JIM_TT_EXPRSUGAR 16
6649 #define JIM_TT_EXPR_OP 20
6651 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
6654 #define JIM_PS_DEF 0
6655 #define JIM_PS_QUOTE 1
6656 #define JIM_PS_DICTSUGAR 2
6658 struct JimParserCtx
6660 const char *p;
6661 int len;
6662 int linenr;
6663 const char *tstart;
6664 const char *tend;
6665 int tline;
6666 int tt;
6667 int eof;
6668 int state;
6669 int comment;
6670 char missing;
6671 int missingline;
6674 struct JimParseResult {
6675 char missing;
6676 int line;
6679 static int JimParseScript(struct JimParserCtx *pc);
6680 static int JimParseSep(struct JimParserCtx *pc);
6681 static int JimParseEol(struct JimParserCtx *pc);
6682 static int JimParseCmd(struct JimParserCtx *pc);
6683 static int JimParseQuote(struct JimParserCtx *pc);
6684 static int JimParseVar(struct JimParserCtx *pc);
6685 static int JimParseBrace(struct JimParserCtx *pc);
6686 static int JimParseStr(struct JimParserCtx *pc);
6687 static int JimParseComment(struct JimParserCtx *pc);
6688 static void JimParseSubCmd(struct JimParserCtx *pc);
6689 static int JimParseSubQuote(struct JimParserCtx *pc);
6690 static void JimParseSubCmd(struct JimParserCtx *pc);
6691 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
6693 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
6695 pc->p = prg;
6696 pc->len = len;
6697 pc->tstart = NULL;
6698 pc->tend = NULL;
6699 pc->tline = 0;
6700 pc->tt = JIM_TT_NONE;
6701 pc->eof = 0;
6702 pc->state = JIM_PS_DEF;
6703 pc->linenr = linenr;
6704 pc->comment = 1;
6705 pc->missing = ' ';
6706 pc->missingline = linenr;
6709 static int JimParseScript(struct JimParserCtx *pc)
6711 while (1) {
6712 if (!pc->len) {
6713 pc->tstart = pc->p;
6714 pc->tend = pc->p - 1;
6715 pc->tline = pc->linenr;
6716 pc->tt = JIM_TT_EOL;
6717 pc->eof = 1;
6718 return JIM_OK;
6720 switch (*(pc->p)) {
6721 case '\\':
6722 if (*(pc->p + 1) == '\n' && pc->state == JIM_PS_DEF) {
6723 return JimParseSep(pc);
6725 pc->comment = 0;
6726 return JimParseStr(pc);
6727 case ' ':
6728 case '\t':
6729 case '\r':
6730 case '\f':
6731 if (pc->state == JIM_PS_DEF)
6732 return JimParseSep(pc);
6733 pc->comment = 0;
6734 return JimParseStr(pc);
6735 case '\n':
6736 case ';':
6737 pc->comment = 1;
6738 if (pc->state == JIM_PS_DEF)
6739 return JimParseEol(pc);
6740 return JimParseStr(pc);
6741 case '[':
6742 pc->comment = 0;
6743 return JimParseCmd(pc);
6744 case '$':
6745 pc->comment = 0;
6746 if (JimParseVar(pc) == JIM_ERR) {
6748 pc->tstart = pc->tend = pc->p++;
6749 pc->len--;
6750 pc->tt = JIM_TT_ESC;
6752 return JIM_OK;
6753 case '#':
6754 if (pc->comment) {
6755 JimParseComment(pc);
6756 continue;
6758 return JimParseStr(pc);
6759 default:
6760 pc->comment = 0;
6761 return JimParseStr(pc);
6763 return JIM_OK;
6767 static int JimParseSep(struct JimParserCtx *pc)
6769 pc->tstart = pc->p;
6770 pc->tline = pc->linenr;
6771 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
6772 if (*pc->p == '\n') {
6773 break;
6775 if (*pc->p == '\\') {
6776 pc->p++;
6777 pc->len--;
6778 pc->linenr++;
6780 pc->p++;
6781 pc->len--;
6783 pc->tend = pc->p - 1;
6784 pc->tt = JIM_TT_SEP;
6785 return JIM_OK;
6788 static int JimParseEol(struct JimParserCtx *pc)
6790 pc->tstart = pc->p;
6791 pc->tline = pc->linenr;
6792 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
6793 if (*pc->p == '\n')
6794 pc->linenr++;
6795 pc->p++;
6796 pc->len--;
6798 pc->tend = pc->p - 1;
6799 pc->tt = JIM_TT_EOL;
6800 return JIM_OK;
6804 static void JimParseSubBrace(struct JimParserCtx *pc)
6806 int level = 1;
6809 pc->p++;
6810 pc->len--;
6811 while (pc->len) {
6812 switch (*pc->p) {
6813 case '\\':
6814 if (pc->len > 1) {
6815 if (*++pc->p == '\n') {
6816 pc->linenr++;
6818 pc->len--;
6820 break;
6822 case '{':
6823 level++;
6824 break;
6826 case '}':
6827 if (--level == 0) {
6828 pc->tend = pc->p - 1;
6829 pc->p++;
6830 pc->len--;
6831 return;
6833 break;
6835 case '\n':
6836 pc->linenr++;
6837 break;
6839 pc->p++;
6840 pc->len--;
6842 pc->missing = '{';
6843 pc->missingline = pc->tline;
6844 pc->tend = pc->p - 1;
6847 static int JimParseSubQuote(struct JimParserCtx *pc)
6849 int tt = JIM_TT_STR;
6850 int line = pc->tline;
6853 pc->p++;
6854 pc->len--;
6855 while (pc->len) {
6856 switch (*pc->p) {
6857 case '\\':
6858 if (pc->len > 1) {
6859 if (*++pc->p == '\n') {
6860 pc->linenr++;
6862 pc->len--;
6863 tt = JIM_TT_ESC;
6865 break;
6867 case '"':
6868 pc->tend = pc->p - 1;
6869 pc->p++;
6870 pc->len--;
6871 return tt;
6873 case '[':
6874 JimParseSubCmd(pc);
6875 tt = JIM_TT_ESC;
6876 continue;
6878 case '\n':
6879 pc->linenr++;
6880 break;
6882 case '$':
6883 tt = JIM_TT_ESC;
6884 break;
6886 pc->p++;
6887 pc->len--;
6889 pc->missing = '"';
6890 pc->missingline = line;
6891 pc->tend = pc->p - 1;
6892 return tt;
6895 static void JimParseSubCmd(struct JimParserCtx *pc)
6897 int level = 1;
6898 int startofword = 1;
6899 int line = pc->tline;
6902 pc->p++;
6903 pc->len--;
6904 while (pc->len) {
6905 switch (*pc->p) {
6906 case '\\':
6907 if (pc->len > 1) {
6908 if (*++pc->p == '\n') {
6909 pc->linenr++;
6911 pc->len--;
6913 break;
6915 case '[':
6916 level++;
6917 break;
6919 case ']':
6920 if (--level == 0) {
6921 pc->tend = pc->p - 1;
6922 pc->p++;
6923 pc->len--;
6924 return;
6926 break;
6928 case '"':
6929 if (startofword) {
6930 JimParseSubQuote(pc);
6931 continue;
6933 break;
6935 case '{':
6936 JimParseSubBrace(pc);
6937 startofword = 0;
6938 continue;
6940 case '\n':
6941 pc->linenr++;
6942 break;
6944 startofword = isspace(UCHAR(*pc->p));
6945 pc->p++;
6946 pc->len--;
6948 pc->missing = '[';
6949 pc->missingline = line;
6950 pc->tend = pc->p - 1;
6953 static int JimParseBrace(struct JimParserCtx *pc)
6955 pc->tstart = pc->p + 1;
6956 pc->tline = pc->linenr;
6957 pc->tt = JIM_TT_STR;
6958 JimParseSubBrace(pc);
6959 return JIM_OK;
6962 static int JimParseCmd(struct JimParserCtx *pc)
6964 pc->tstart = pc->p + 1;
6965 pc->tline = pc->linenr;
6966 pc->tt = JIM_TT_CMD;
6967 JimParseSubCmd(pc);
6968 return JIM_OK;
6971 static int JimParseQuote(struct JimParserCtx *pc)
6973 pc->tstart = pc->p + 1;
6974 pc->tline = pc->linenr;
6975 pc->tt = JimParseSubQuote(pc);
6976 return JIM_OK;
6979 static int JimParseVar(struct JimParserCtx *pc)
6982 pc->p++;
6983 pc->len--;
6985 #ifdef EXPRSUGAR_BRACKET
6986 if (*pc->p == '[') {
6988 JimParseCmd(pc);
6989 pc->tt = JIM_TT_EXPRSUGAR;
6990 return JIM_OK;
6992 #endif
6994 pc->tstart = pc->p;
6995 pc->tt = JIM_TT_VAR;
6996 pc->tline = pc->linenr;
6998 if (*pc->p == '{') {
6999 pc->tstart = ++pc->p;
7000 pc->len--;
7002 while (pc->len && *pc->p != '}') {
7003 if (*pc->p == '\n') {
7004 pc->linenr++;
7006 pc->p++;
7007 pc->len--;
7009 pc->tend = pc->p - 1;
7010 if (pc->len) {
7011 pc->p++;
7012 pc->len--;
7015 else {
7016 while (1) {
7018 if (pc->p[0] == ':' && pc->p[1] == ':') {
7019 while (*pc->p == ':') {
7020 pc->p++;
7021 pc->len--;
7023 continue;
7025 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
7026 pc->p++;
7027 pc->len--;
7028 continue;
7030 break;
7033 if (*pc->p == '(') {
7034 int count = 1;
7035 const char *paren = NULL;
7037 pc->tt = JIM_TT_DICTSUGAR;
7039 while (count && pc->len) {
7040 pc->p++;
7041 pc->len--;
7042 if (*pc->p == '\\' && pc->len >= 1) {
7043 pc->p++;
7044 pc->len--;
7046 else if (*pc->p == '(') {
7047 count++;
7049 else if (*pc->p == ')') {
7050 paren = pc->p;
7051 count--;
7054 if (count == 0) {
7055 pc->p++;
7056 pc->len--;
7058 else if (paren) {
7060 paren++;
7061 pc->len += (pc->p - paren);
7062 pc->p = paren;
7064 #ifndef EXPRSUGAR_BRACKET
7065 if (*pc->tstart == '(') {
7066 pc->tt = JIM_TT_EXPRSUGAR;
7068 #endif
7070 pc->tend = pc->p - 1;
7072 if (pc->tstart == pc->p) {
7073 pc->p--;
7074 pc->len++;
7075 return JIM_ERR;
7077 return JIM_OK;
7080 static int JimParseStr(struct JimParserCtx *pc)
7082 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
7083 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
7085 if (*pc->p == '{') {
7086 return JimParseBrace(pc);
7088 if (*pc->p == '"') {
7089 pc->state = JIM_PS_QUOTE;
7090 pc->p++;
7091 pc->len--;
7093 pc->missingline = pc->tline;
7096 pc->tstart = pc->p;
7097 pc->tline = pc->linenr;
7098 while (1) {
7099 if (pc->len == 0) {
7100 if (pc->state == JIM_PS_QUOTE) {
7101 pc->missing = '"';
7103 pc->tend = pc->p - 1;
7104 pc->tt = JIM_TT_ESC;
7105 return JIM_OK;
7107 switch (*pc->p) {
7108 case '\\':
7109 if (pc->state == JIM_PS_DEF && *(pc->p + 1) == '\n') {
7110 pc->tend = pc->p - 1;
7111 pc->tt = JIM_TT_ESC;
7112 return JIM_OK;
7114 if (pc->len >= 2) {
7115 if (*(pc->p + 1) == '\n') {
7116 pc->linenr++;
7118 pc->p++;
7119 pc->len--;
7121 break;
7122 case '(':
7124 if (pc->len > 1 && pc->p[1] != '$') {
7125 break;
7127 case ')':
7129 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
7130 if (pc->p == pc->tstart) {
7132 pc->p++;
7133 pc->len--;
7135 pc->tend = pc->p - 1;
7136 pc->tt = JIM_TT_ESC;
7137 return JIM_OK;
7139 break;
7141 case '$':
7142 case '[':
7143 pc->tend = pc->p - 1;
7144 pc->tt = JIM_TT_ESC;
7145 return JIM_OK;
7146 case ' ':
7147 case '\t':
7148 case '\n':
7149 case '\r':
7150 case '\f':
7151 case ';':
7152 if (pc->state == JIM_PS_DEF) {
7153 pc->tend = pc->p - 1;
7154 pc->tt = JIM_TT_ESC;
7155 return JIM_OK;
7157 else if (*pc->p == '\n') {
7158 pc->linenr++;
7160 break;
7161 case '"':
7162 if (pc->state == JIM_PS_QUOTE) {
7163 pc->tend = pc->p - 1;
7164 pc->tt = JIM_TT_ESC;
7165 pc->p++;
7166 pc->len--;
7167 pc->state = JIM_PS_DEF;
7168 return JIM_OK;
7170 break;
7172 pc->p++;
7173 pc->len--;
7175 return JIM_OK;
7178 static int JimParseComment(struct JimParserCtx *pc)
7180 while (*pc->p) {
7181 if (*pc->p == '\n') {
7182 pc->linenr++;
7183 if (*(pc->p - 1) != '\\') {
7184 pc->p++;
7185 pc->len--;
7186 return JIM_OK;
7189 pc->p++;
7190 pc->len--;
7192 return JIM_OK;
7196 static int xdigitval(int c)
7198 if (c >= '0' && c <= '9')
7199 return c - '0';
7200 if (c >= 'a' && c <= 'f')
7201 return c - 'a' + 10;
7202 if (c >= 'A' && c <= 'F')
7203 return c - 'A' + 10;
7204 return -1;
7207 static int odigitval(int c)
7209 if (c >= '0' && c <= '7')
7210 return c - '0';
7211 return -1;
7214 static int JimEscape(char *dest, const char *s, int slen)
7216 char *p = dest;
7217 int i, len;
7219 if (slen == -1)
7220 slen = strlen(s);
7222 for (i = 0; i < slen; i++) {
7223 switch (s[i]) {
7224 case '\\':
7225 switch (s[i + 1]) {
7226 case 'a':
7227 *p++ = 0x7;
7228 i++;
7229 break;
7230 case 'b':
7231 *p++ = 0x8;
7232 i++;
7233 break;
7234 case 'f':
7235 *p++ = 0xc;
7236 i++;
7237 break;
7238 case 'n':
7239 *p++ = 0xa;
7240 i++;
7241 break;
7242 case 'r':
7243 *p++ = 0xd;
7244 i++;
7245 break;
7246 case 't':
7247 *p++ = 0x9;
7248 i++;
7249 break;
7250 case 'u':
7251 case 'U':
7252 case 'x':
7254 unsigned val = 0;
7255 int k;
7256 int maxchars = 2;
7258 i++;
7260 if (s[i] == 'U') {
7261 maxchars = 8;
7263 else if (s[i] == 'u') {
7264 if (s[i + 1] == '{') {
7265 maxchars = 6;
7266 i++;
7268 else {
7269 maxchars = 4;
7273 for (k = 0; k < maxchars; k++) {
7274 int c = xdigitval(s[i + k + 1]);
7275 if (c == -1) {
7276 break;
7278 val = (val << 4) | c;
7281 if (s[i] == '{') {
7282 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
7284 i--;
7285 k = 0;
7287 else {
7289 k++;
7292 if (k) {
7294 if (s[i] == 'x') {
7295 *p++ = val;
7297 else {
7298 p += utf8_fromunicode(p, val);
7300 i += k;
7301 break;
7304 *p++ = s[i];
7306 break;
7307 case 'v':
7308 *p++ = 0xb;
7309 i++;
7310 break;
7311 case '\0':
7312 *p++ = '\\';
7313 i++;
7314 break;
7315 case '\n':
7317 *p++ = ' ';
7318 do {
7319 i++;
7320 } while (s[i + 1] == ' ' || s[i + 1] == '\t');
7321 break;
7322 case '0':
7323 case '1':
7324 case '2':
7325 case '3':
7326 case '4':
7327 case '5':
7328 case '6':
7329 case '7':
7332 int val = 0;
7333 int c = odigitval(s[i + 1]);
7335 val = c;
7336 c = odigitval(s[i + 2]);
7337 if (c == -1) {
7338 *p++ = val;
7339 i++;
7340 break;
7342 val = (val * 8) + c;
7343 c = odigitval(s[i + 3]);
7344 if (c == -1) {
7345 *p++ = val;
7346 i += 2;
7347 break;
7349 val = (val * 8) + c;
7350 *p++ = val;
7351 i += 3;
7353 break;
7354 default:
7355 *p++ = s[i + 1];
7356 i++;
7357 break;
7359 break;
7360 default:
7361 *p++ = s[i];
7362 break;
7365 len = p - dest;
7366 *p = '\0';
7367 return len;
7370 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
7372 const char *start, *end;
7373 char *token;
7374 int len;
7376 start = pc->tstart;
7377 end = pc->tend;
7378 if (start > end) {
7379 len = 0;
7380 token = Jim_Alloc(1);
7381 token[0] = '\0';
7383 else {
7384 len = (end - start) + 1;
7385 token = Jim_Alloc(len + 1);
7386 if (pc->tt != JIM_TT_ESC) {
7388 memcpy(token, start, len);
7389 token[len] = '\0';
7391 else {
7393 len = JimEscape(token, start, len);
7397 return Jim_NewStringObjNoAlloc(interp, token, len);
7400 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
7402 struct JimParserCtx parser;
7404 JimParserInit(&parser, s, len, 1);
7405 while (!parser.eof) {
7406 JimParseScript(&parser);
7408 if (stateCharPtr) {
7409 *stateCharPtr = parser.missing;
7411 return parser.missing == ' ';
7414 static int JimParseListSep(struct JimParserCtx *pc);
7415 static int JimParseListStr(struct JimParserCtx *pc);
7416 static int JimParseListQuote(struct JimParserCtx *pc);
7418 static int JimParseList(struct JimParserCtx *pc)
7420 if (isspace(UCHAR(*pc->p))) {
7421 return JimParseListSep(pc);
7423 switch (*pc->p) {
7424 case '"':
7425 return JimParseListQuote(pc);
7427 case '{':
7428 return JimParseBrace(pc);
7430 default:
7431 if (pc->len) {
7432 return JimParseListStr(pc);
7434 break;
7437 pc->tstart = pc->tend = pc->p;
7438 pc->tline = pc->linenr;
7439 pc->tt = JIM_TT_EOL;
7440 pc->eof = 1;
7441 return JIM_OK;
7444 static int JimParseListSep(struct JimParserCtx *pc)
7446 pc->tstart = pc->p;
7447 pc->tline = pc->linenr;
7448 while (isspace(UCHAR(*pc->p))) {
7449 if (*pc->p == '\n') {
7450 pc->linenr++;
7452 pc->p++;
7453 pc->len--;
7455 pc->tend = pc->p - 1;
7456 pc->tt = JIM_TT_SEP;
7457 return JIM_OK;
7460 static int JimParseListQuote(struct JimParserCtx *pc)
7462 pc->p++;
7463 pc->len--;
7465 pc->tstart = pc->p;
7466 pc->tline = pc->linenr;
7467 pc->tt = JIM_TT_STR;
7469 while (pc->len) {
7470 switch (*pc->p) {
7471 case '\\':
7472 pc->tt = JIM_TT_ESC;
7473 if (--pc->len == 0) {
7475 pc->tend = pc->p;
7476 return JIM_OK;
7478 pc->p++;
7479 break;
7480 case '\n':
7481 pc->linenr++;
7482 break;
7483 case '"':
7484 pc->tend = pc->p - 1;
7485 pc->p++;
7486 pc->len--;
7487 return JIM_OK;
7489 pc->p++;
7490 pc->len--;
7493 pc->tend = pc->p - 1;
7494 return JIM_OK;
7497 static int JimParseListStr(struct JimParserCtx *pc)
7499 pc->tstart = pc->p;
7500 pc->tline = pc->linenr;
7501 pc->tt = JIM_TT_STR;
7503 while (pc->len) {
7504 if (isspace(UCHAR(*pc->p))) {
7505 pc->tend = pc->p - 1;
7506 return JIM_OK;
7508 if (*pc->p == '\\') {
7509 if (--pc->len == 0) {
7511 pc->tend = pc->p;
7512 return JIM_OK;
7514 pc->tt = JIM_TT_ESC;
7515 pc->p++;
7517 pc->p++;
7518 pc->len--;
7520 pc->tend = pc->p - 1;
7521 return JIM_OK;
7526 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
7528 Jim_Obj *objPtr;
7531 if (interp->freeList != NULL) {
7533 objPtr = interp->freeList;
7534 interp->freeList = objPtr->nextObjPtr;
7536 else {
7538 objPtr = Jim_Alloc(sizeof(*objPtr));
7541 objPtr->refCount = 0;
7544 objPtr->prevObjPtr = NULL;
7545 objPtr->nextObjPtr = interp->liveList;
7546 if (interp->liveList)
7547 interp->liveList->prevObjPtr = objPtr;
7548 interp->liveList = objPtr;
7550 return objPtr;
7553 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
7556 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
7557 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
7560 Jim_FreeIntRep(interp, objPtr);
7562 if (objPtr->bytes != NULL) {
7563 if (objPtr->bytes != JimEmptyStringRep)
7564 Jim_Free(objPtr->bytes);
7567 if (objPtr->prevObjPtr)
7568 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
7569 if (objPtr->nextObjPtr)
7570 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
7571 if (interp->liveList == objPtr)
7572 interp->liveList = objPtr->nextObjPtr;
7573 #ifdef JIM_DISABLE_OBJECT_POOL
7574 Jim_Free(objPtr);
7575 #else
7577 objPtr->prevObjPtr = NULL;
7578 objPtr->nextObjPtr = interp->freeList;
7579 if (interp->freeList)
7580 interp->freeList->prevObjPtr = objPtr;
7581 interp->freeList = objPtr;
7582 objPtr->refCount = -1;
7583 #endif
7587 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
7589 if (objPtr->bytes != NULL) {
7590 if (objPtr->bytes != JimEmptyStringRep)
7591 Jim_Free(objPtr->bytes);
7593 objPtr->bytes = NULL;
7597 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
7599 Jim_Obj *dupPtr;
7601 dupPtr = Jim_NewObj(interp);
7602 if (objPtr->bytes == NULL) {
7604 dupPtr->bytes = NULL;
7606 else if (objPtr->length == 0) {
7608 dupPtr->bytes = JimEmptyStringRep;
7609 dupPtr->length = 0;
7610 dupPtr->typePtr = NULL;
7611 return dupPtr;
7613 else {
7614 dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
7615 dupPtr->length = objPtr->length;
7617 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
7621 dupPtr->typePtr = objPtr->typePtr;
7622 if (objPtr->typePtr != NULL) {
7623 if (objPtr->typePtr->dupIntRepProc == NULL) {
7624 dupPtr->internalRep = objPtr->internalRep;
7626 else {
7628 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
7631 return dupPtr;
7634 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
7636 if (objPtr->bytes == NULL) {
7638 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7639 objPtr->typePtr->updateStringProc(objPtr);
7641 if (lenPtr)
7642 *lenPtr = objPtr->length;
7643 return objPtr->bytes;
7647 int Jim_Length(Jim_Obj *objPtr)
7649 if (objPtr->bytes == NULL) {
7651 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7652 objPtr->typePtr->updateStringProc(objPtr);
7654 return objPtr->length;
7658 const char *Jim_String(Jim_Obj *objPtr)
7660 if (objPtr->bytes == NULL) {
7662 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7663 objPtr->typePtr->updateStringProc(objPtr);
7665 return objPtr->bytes;
7668 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7669 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7671 static const Jim_ObjType dictSubstObjType = {
7672 "dict-substitution",
7673 FreeDictSubstInternalRep,
7674 DupDictSubstInternalRep,
7675 NULL,
7676 JIM_TYPE_NONE,
7679 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7681 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
7684 static const Jim_ObjType interpolatedObjType = {
7685 "interpolated",
7686 FreeInterpolatedInternalRep,
7687 NULL,
7688 NULL,
7689 JIM_TYPE_NONE,
7692 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7693 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
7695 static const Jim_ObjType stringObjType = {
7696 "string",
7697 NULL,
7698 DupStringInternalRep,
7699 NULL,
7700 JIM_TYPE_REFERENCES,
7703 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7705 JIM_NOTUSED(interp);
7707 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
7709 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
7712 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7714 if (objPtr->typePtr != &stringObjType) {
7716 if (objPtr->bytes == NULL) {
7718 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7719 objPtr->typePtr->updateStringProc(objPtr);
7722 Jim_FreeIntRep(interp, objPtr);
7724 objPtr->typePtr = &stringObjType;
7725 objPtr->internalRep.strValue.maxLength = objPtr->length;
7727 objPtr->internalRep.strValue.charLength = -1;
7729 return JIM_OK;
7732 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
7734 #ifdef JIM_UTF8
7735 SetStringFromAny(interp, objPtr);
7737 if (objPtr->internalRep.strValue.charLength < 0) {
7738 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
7740 return objPtr->internalRep.strValue.charLength;
7741 #else
7742 return Jim_Length(objPtr);
7743 #endif
7747 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
7749 Jim_Obj *objPtr = Jim_NewObj(interp);
7752 if (len == -1)
7753 len = strlen(s);
7755 if (len == 0) {
7756 objPtr->bytes = JimEmptyStringRep;
7757 objPtr->length = 0;
7759 else {
7760 objPtr->bytes = Jim_Alloc(len + 1);
7761 objPtr->length = len;
7762 memcpy(objPtr->bytes, s, len);
7763 objPtr->bytes[len] = '\0';
7767 objPtr->typePtr = NULL;
7768 return objPtr;
7772 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
7774 #ifdef JIM_UTF8
7776 int bytelen = utf8_index(s, charlen);
7778 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
7781 objPtr->typePtr = &stringObjType;
7782 objPtr->internalRep.strValue.maxLength = bytelen;
7783 objPtr->internalRep.strValue.charLength = charlen;
7785 return objPtr;
7786 #else
7787 return Jim_NewStringObj(interp, s, charlen);
7788 #endif
7791 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
7793 Jim_Obj *objPtr = Jim_NewObj(interp);
7795 objPtr->bytes = s;
7796 objPtr->length = len == -1 ? strlen(s) : len;
7797 objPtr->typePtr = NULL;
7798 return objPtr;
7801 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
7803 int needlen;
7805 if (len == -1)
7806 len = strlen(str);
7807 needlen = objPtr->length + len;
7808 if (objPtr->internalRep.strValue.maxLength < needlen ||
7809 objPtr->internalRep.strValue.maxLength == 0) {
7810 needlen *= 2;
7812 if (needlen < 7) {
7813 needlen = 7;
7815 if (objPtr->bytes == JimEmptyStringRep) {
7816 objPtr->bytes = Jim_Alloc(needlen + 1);
7818 else {
7819 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
7821 objPtr->internalRep.strValue.maxLength = needlen;
7823 memcpy(objPtr->bytes + objPtr->length, str, len);
7824 objPtr->bytes[objPtr->length + len] = '\0';
7825 if (objPtr->internalRep.strValue.charLength >= 0) {
7827 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
7829 objPtr->length += len;
7833 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
7835 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
7836 SetStringFromAny(interp, objPtr);
7837 StringAppendString(objPtr, str, len);
7840 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
7842 int len;
7843 const char *str;
7845 str = Jim_GetString(appendObjPtr, &len);
7846 Jim_AppendString(interp, objPtr, str, len);
7849 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
7851 va_list ap;
7853 SetStringFromAny(interp, objPtr);
7854 va_start(ap, objPtr);
7855 while (1) {
7856 char *s = va_arg(ap, char *);
7858 if (s == NULL)
7859 break;
7860 Jim_AppendString(interp, objPtr, s, -1);
7862 va_end(ap);
7865 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
7867 const char *aStr, *bStr;
7868 int aLen, bLen;
7870 if (aObjPtr == bObjPtr)
7871 return 1;
7872 aStr = Jim_GetString(aObjPtr, &aLen);
7873 bStr = Jim_GetString(bObjPtr, &bLen);
7874 if (aLen != bLen)
7875 return 0;
7876 return JimStringCompare(aStr, aLen, bStr, bLen) == 0;
7879 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
7881 return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase);
7884 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
7886 int l1, l2;
7887 const char *s1 = Jim_GetString(firstObjPtr, &l1);
7888 const char *s2 = Jim_GetString(secondObjPtr, &l2);
7890 if (nocase) {
7892 return JimStringCompareLen(s1, s2, -1, nocase);
7894 return JimStringCompare(s1, l1, s2, l2);
7897 int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
7899 const char *s1 = Jim_String(firstObjPtr);
7900 const char *s2 = Jim_String(secondObjPtr);
7902 return JimStringCompareLen(s1, s2, Jim_Utf8Length(interp, firstObjPtr), nocase);
7905 static int JimRelToAbsIndex(int len, int idx)
7907 if (idx < 0)
7908 return len + idx;
7909 return idx;
7912 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
7914 int rangeLen;
7916 if (*firstPtr > *lastPtr) {
7917 rangeLen = 0;
7919 else {
7920 rangeLen = *lastPtr - *firstPtr + 1;
7921 if (rangeLen) {
7922 if (*firstPtr < 0) {
7923 rangeLen += *firstPtr;
7924 *firstPtr = 0;
7926 if (*lastPtr >= len) {
7927 rangeLen -= (*lastPtr - (len - 1));
7928 *lastPtr = len - 1;
7932 if (rangeLen < 0)
7933 rangeLen = 0;
7935 *rangeLenPtr = rangeLen;
7938 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
7939 int len, int *first, int *last, int *range)
7941 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
7942 return JIM_ERR;
7944 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
7945 return JIM_ERR;
7947 *first = JimRelToAbsIndex(len, *first);
7948 *last = JimRelToAbsIndex(len, *last);
7949 JimRelToAbsRange(len, first, last, range);
7950 return JIM_OK;
7953 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
7954 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
7956 int first, last;
7957 const char *str;
7958 int rangeLen;
7959 int bytelen;
7961 str = Jim_GetString(strObjPtr, &bytelen);
7963 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
7964 return NULL;
7967 if (first == 0 && rangeLen == bytelen) {
7968 return strObjPtr;
7970 return Jim_NewStringObj(interp, str + first, rangeLen);
7973 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
7974 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
7976 #ifdef JIM_UTF8
7977 int first, last;
7978 const char *str;
7979 int len, rangeLen;
7980 int bytelen;
7982 str = Jim_GetString(strObjPtr, &bytelen);
7983 len = Jim_Utf8Length(interp, strObjPtr);
7985 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
7986 return NULL;
7989 if (first == 0 && rangeLen == len) {
7990 return strObjPtr;
7992 if (len == bytelen) {
7994 return Jim_NewStringObj(interp, str + first, rangeLen);
7996 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
7997 #else
7998 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
7999 #endif
8002 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
8003 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
8005 int first, last;
8006 const char *str;
8007 int len, rangeLen;
8008 Jim_Obj *objPtr;
8010 len = Jim_Utf8Length(interp, strObjPtr);
8012 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8013 return NULL;
8016 if (last < first) {
8017 return strObjPtr;
8020 str = Jim_String(strObjPtr);
8023 objPtr = Jim_NewStringObjUtf8(interp, str, first);
8026 if (newStrObj) {
8027 Jim_AppendObj(interp, objPtr, newStrObj);
8031 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
8033 return objPtr;
8036 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
8038 while (*str) {
8039 int c;
8040 str += utf8_tounicode(str, &c);
8041 dest += utf8_fromunicode(dest, uc ? utf8_upper(c) : utf8_lower(c));
8043 *dest = 0;
8046 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
8048 char *buf;
8049 int len;
8050 const char *str;
8052 SetStringFromAny(interp, strObjPtr);
8054 str = Jim_GetString(strObjPtr, &len);
8056 #ifdef JIM_UTF8
8057 len *= 2;
8058 #endif
8059 buf = Jim_Alloc(len + 1);
8060 JimStrCopyUpperLower(buf, str, 0);
8061 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8064 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
8066 char *buf;
8067 const char *str;
8068 int len;
8070 if (strObjPtr->typePtr != &stringObjType) {
8071 SetStringFromAny(interp, strObjPtr);
8074 str = Jim_GetString(strObjPtr, &len);
8076 #ifdef JIM_UTF8
8077 len *= 2;
8078 #endif
8079 buf = Jim_Alloc(len + 1);
8080 JimStrCopyUpperLower(buf, str, 1);
8081 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8084 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
8086 char *buf, *p;
8087 int len;
8088 int c;
8089 const char *str;
8091 str = Jim_GetString(strObjPtr, &len);
8092 if (len == 0) {
8093 return strObjPtr;
8095 #ifdef JIM_UTF8
8096 len *= 2;
8097 #endif
8098 buf = p = Jim_Alloc(len + 1);
8100 str += utf8_tounicode(str, &c);
8101 p += utf8_fromunicode(p, utf8_title(c));
8103 JimStrCopyUpperLower(p, str, 0);
8105 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8108 static const char *utf8_memchr(const char *str, int len, int c)
8110 #ifdef JIM_UTF8
8111 while (len) {
8112 int sc;
8113 int n = utf8_tounicode(str, &sc);
8114 if (sc == c) {
8115 return str;
8117 str += n;
8118 len -= n;
8120 return NULL;
8121 #else
8122 return memchr(str, c, len);
8123 #endif
8126 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
8128 while (len) {
8129 int c;
8130 int n = utf8_tounicode(str, &c);
8132 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8134 break;
8136 str += n;
8137 len -= n;
8139 return str;
8142 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
8144 str += len;
8146 while (len) {
8147 int c;
8148 int n = utf8_prev_len(str, len);
8150 len -= n;
8151 str -= n;
8153 n = utf8_tounicode(str, &c);
8155 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8156 return str + n;
8160 return NULL;
8163 static const char default_trim_chars[] = " \t\n\r";
8165 static int default_trim_chars_len = sizeof(default_trim_chars);
8167 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8169 int len;
8170 const char *str = Jim_GetString(strObjPtr, &len);
8171 const char *trimchars = default_trim_chars;
8172 int trimcharslen = default_trim_chars_len;
8173 const char *newstr;
8175 if (trimcharsObjPtr) {
8176 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8179 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
8180 if (newstr == str) {
8181 return strObjPtr;
8184 return Jim_NewStringObj(interp, newstr, len - (newstr - str));
8187 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8189 int len;
8190 const char *trimchars = default_trim_chars;
8191 int trimcharslen = default_trim_chars_len;
8192 const char *nontrim;
8194 if (trimcharsObjPtr) {
8195 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8198 SetStringFromAny(interp, strObjPtr);
8200 len = Jim_Length(strObjPtr);
8201 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
8203 if (nontrim == NULL) {
8205 return Jim_NewEmptyStringObj(interp);
8207 if (nontrim == strObjPtr->bytes + len) {
8208 return strObjPtr;
8211 if (Jim_IsShared(strObjPtr)) {
8212 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
8214 else {
8216 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
8217 strObjPtr->length = (nontrim - strObjPtr->bytes);
8220 return strObjPtr;
8223 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8226 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
8229 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
8231 if (objPtr != strObjPtr) {
8233 Jim_IncrRefCount(objPtr);
8234 Jim_DecrRefCount(interp, objPtr);
8237 return strObjPtr;
8241 #ifdef HAVE_ISASCII
8242 #define jim_isascii isascii
8243 #else
8244 static int jim_isascii(int c)
8246 return !(c & ~0x7f);
8248 #endif
8250 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
8252 static const char * const strclassnames[] = {
8253 "integer", "alpha", "alnum", "ascii", "digit",
8254 "double", "lower", "upper", "space", "xdigit",
8255 "control", "print", "graph", "punct",
8256 NULL
8258 enum {
8259 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
8260 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
8261 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT
8263 int strclass;
8264 int len;
8265 int i;
8266 const char *str;
8267 int (*isclassfunc)(int c) = NULL;
8269 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
8270 return JIM_ERR;
8273 str = Jim_GetString(strObjPtr, &len);
8274 if (len == 0) {
8275 Jim_SetResultInt(interp, !strict);
8276 return JIM_OK;
8279 switch (strclass) {
8280 case STR_IS_INTEGER:
8282 jim_wide w;
8283 Jim_SetResultInt(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
8284 return JIM_OK;
8287 case STR_IS_DOUBLE:
8289 double d;
8290 Jim_SetResultInt(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
8291 return JIM_OK;
8294 case STR_IS_ALPHA: isclassfunc = isalpha; break;
8295 case STR_IS_ALNUM: isclassfunc = isalnum; break;
8296 case STR_IS_ASCII: isclassfunc = jim_isascii; break;
8297 case STR_IS_DIGIT: isclassfunc = isdigit; break;
8298 case STR_IS_LOWER: isclassfunc = islower; break;
8299 case STR_IS_UPPER: isclassfunc = isupper; break;
8300 case STR_IS_SPACE: isclassfunc = isspace; break;
8301 case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
8302 case STR_IS_CONTROL: isclassfunc = iscntrl; break;
8303 case STR_IS_PRINT: isclassfunc = isprint; break;
8304 case STR_IS_GRAPH: isclassfunc = isgraph; break;
8305 case STR_IS_PUNCT: isclassfunc = ispunct; break;
8306 default:
8307 return JIM_ERR;
8310 for (i = 0; i < len; i++) {
8311 if (!isclassfunc(str[i])) {
8312 Jim_SetResultInt(interp, 0);
8313 return JIM_OK;
8316 Jim_SetResultInt(interp, 1);
8317 return JIM_OK;
8322 static const Jim_ObjType comparedStringObjType = {
8323 "compared-string",
8324 NULL,
8325 NULL,
8326 NULL,
8327 JIM_TYPE_REFERENCES,
8330 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
8332 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str)
8333 return 1;
8334 else {
8335 const char *objStr = Jim_String(objPtr);
8337 if (strcmp(str, objStr) != 0)
8338 return 0;
8339 if (objPtr->typePtr != &comparedStringObjType) {
8340 Jim_FreeIntRep(interp, objPtr);
8341 objPtr->typePtr = &comparedStringObjType;
8343 objPtr->internalRep.ptr = (char *)str;
8344 return 1;
8348 static int qsortCompareStringPointers(const void *a, const void *b)
8350 char *const *sa = (char *const *)a;
8351 char *const *sb = (char *const *)b;
8353 return strcmp(*sa, *sb);
8358 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8359 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8361 static const Jim_ObjType sourceObjType = {
8362 "source",
8363 FreeSourceInternalRep,
8364 DupSourceInternalRep,
8365 NULL,
8366 JIM_TYPE_REFERENCES,
8369 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8371 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
8374 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8376 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
8377 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
8380 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
8381 Jim_Obj *fileNameObj, int lineNumber)
8383 JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object"));
8384 JimPanic((objPtr->typePtr == &sourceObjType, "JimSetSourceInfo called with non-source object"));
8385 Jim_IncrRefCount(fileNameObj);
8386 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
8387 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
8388 objPtr->typePtr = &sourceObjType;
8392 static const Jim_ObjType scriptLineObjType = {
8393 "scriptline",
8394 NULL,
8395 NULL,
8396 NULL,
8400 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
8402 Jim_Obj *objPtr;
8404 #ifdef DEBUG_SHOW_SCRIPT
8405 char buf[100];
8406 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
8407 objPtr = Jim_NewStringObj(interp, buf, -1);
8408 #else
8409 objPtr = Jim_NewEmptyStringObj(interp);
8410 #endif
8411 objPtr->typePtr = &scriptLineObjType;
8412 objPtr->internalRep.scriptLineValue.argc = argc;
8413 objPtr->internalRep.scriptLineValue.line = line;
8415 return objPtr;
8418 #define JIM_CMDSTRUCT_EXPAND -1
8420 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8421 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8422 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, struct JimParseResult *result);
8424 static const Jim_ObjType scriptObjType = {
8425 "script",
8426 FreeScriptInternalRep,
8427 DupScriptInternalRep,
8428 NULL,
8429 JIM_TYPE_REFERENCES,
8432 typedef struct ScriptToken
8434 int type;
8435 Jim_Obj *objPtr;
8436 } ScriptToken;
8438 typedef struct ScriptObj
8440 int len;
8441 ScriptToken *token;
8442 int substFlags;
8443 int inUse; /* Used to share a ScriptObj. Currently
8444 only used by Jim_EvalObj() as protection against
8445 shimmering of the currently evaluated object. */
8446 Jim_Obj *fileNameObj;
8447 int firstline;
8448 int linenr;
8449 } ScriptObj;
8451 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8453 int i;
8454 struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
8456 script->inUse--;
8457 if (script->inUse != 0)
8458 return;
8459 for (i = 0; i < script->len; i++) {
8460 Jim_DecrRefCount(interp, script->token[i].objPtr);
8462 Jim_Free(script->token);
8463 Jim_DecrRefCount(interp, script->fileNameObj);
8464 Jim_Free(script);
8467 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8469 JIM_NOTUSED(interp);
8470 JIM_NOTUSED(srcPtr);
8473 dupPtr->typePtr = NULL;
8476 typedef struct
8478 const char *token;
8479 int len;
8480 int type;
8481 int line;
8482 } ParseToken;
8484 typedef struct
8487 ParseToken *list;
8488 int size;
8489 int count;
8490 ParseToken static_list[20];
8491 } ParseTokenList;
8493 static void ScriptTokenListInit(ParseTokenList *tokenlist)
8495 tokenlist->list = tokenlist->static_list;
8496 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
8497 tokenlist->count = 0;
8500 static void ScriptTokenListFree(ParseTokenList *tokenlist)
8502 if (tokenlist->list != tokenlist->static_list) {
8503 Jim_Free(tokenlist->list);
8507 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
8508 int line)
8510 ParseToken *t;
8512 if (tokenlist->count == tokenlist->size) {
8514 tokenlist->size *= 2;
8515 if (tokenlist->list != tokenlist->static_list) {
8516 tokenlist->list =
8517 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
8519 else {
8521 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
8522 memcpy(tokenlist->list, tokenlist->static_list,
8523 tokenlist->count * sizeof(*tokenlist->list));
8526 t = &tokenlist->list[tokenlist->count++];
8527 t->token = token;
8528 t->len = len;
8529 t->type = type;
8530 t->line = line;
8533 static int JimCountWordTokens(ParseToken *t)
8535 int expand = 1;
8536 int count = 0;
8539 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
8540 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
8542 expand = -1;
8543 t++;
8548 while (!TOKEN_IS_SEP(t->type)) {
8549 t++;
8550 count++;
8553 return count * expand;
8556 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
8558 Jim_Obj *objPtr;
8560 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
8562 int len = t->len;
8563 char *str = Jim_Alloc(len + 1);
8564 len = JimEscape(str, t->token, len);
8565 objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
8567 else {
8568 objPtr = Jim_NewStringObj(interp, t->token, t->len);
8570 return objPtr;
8573 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
8574 ParseTokenList *tokenlist)
8576 int i;
8577 struct ScriptToken *token;
8579 int lineargs = 0;
8581 ScriptToken *linefirst;
8582 int count;
8583 int linenr;
8585 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
8586 printf("==== Tokens ====\n");
8587 for (i = 0; i < tokenlist->count; i++) {
8588 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
8589 tokenlist->list[i].len, tokenlist->list[i].token);
8591 #endif
8594 count = tokenlist->count;
8595 for (i = 0; i < tokenlist->count; i++) {
8596 if (tokenlist->list[i].type == JIM_TT_EOL) {
8597 count++;
8600 linenr = script->firstline = tokenlist->list[0].line;
8602 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
8605 linefirst = token++;
8607 for (i = 0; i < tokenlist->count; ) {
8609 int wordtokens;
8612 while (tokenlist->list[i].type == JIM_TT_SEP) {
8613 i++;
8616 wordtokens = JimCountWordTokens(tokenlist->list + i);
8618 if (wordtokens == 0) {
8620 if (lineargs) {
8621 linefirst->type = JIM_TT_LINE;
8622 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
8623 Jim_IncrRefCount(linefirst->objPtr);
8626 lineargs = 0;
8627 linefirst = token++;
8629 i++;
8630 continue;
8632 else if (wordtokens != 1) {
8634 token->type = JIM_TT_WORD;
8635 token->objPtr = Jim_NewIntObj(interp, wordtokens);
8636 Jim_IncrRefCount(token->objPtr);
8637 token++;
8638 if (wordtokens < 0) {
8640 i++;
8641 wordtokens = -wordtokens - 1;
8642 lineargs--;
8646 if (lineargs == 0) {
8648 linenr = tokenlist->list[i].line;
8650 lineargs++;
8653 while (wordtokens--) {
8654 const ParseToken *t = &tokenlist->list[i++];
8656 token->type = t->type;
8657 token->objPtr = JimMakeScriptObj(interp, t);
8658 Jim_IncrRefCount(token->objPtr);
8660 JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
8661 token++;
8665 if (lineargs == 0) {
8666 token--;
8669 script->len = token - script->token;
8671 assert(script->len < count);
8673 #ifdef DEBUG_SHOW_SCRIPT
8674 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
8675 for (i = 0; i < script->len; i++) {
8676 const ScriptToken *t = &script->token[i];
8677 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
8679 #endif
8683 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
8684 ParseTokenList *tokenlist)
8686 int i;
8687 struct ScriptToken *token;
8689 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
8691 for (i = 0; i < tokenlist->count; i++) {
8692 const ParseToken *t = &tokenlist->list[i];
8695 token->type = t->type;
8696 token->objPtr = JimMakeScriptObj(interp, t);
8697 Jim_IncrRefCount(token->objPtr);
8698 token++;
8701 script->len = i;
8704 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, struct JimParseResult *result)
8706 int scriptTextLen;
8707 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8708 struct JimParserCtx parser;
8709 struct ScriptObj *script;
8710 ParseTokenList tokenlist;
8711 int line = 1;
8714 if (objPtr->typePtr == &sourceObjType) {
8715 line = objPtr->internalRep.sourceValue.lineNumber;
8719 ScriptTokenListInit(&tokenlist);
8721 JimParserInit(&parser, scriptText, scriptTextLen, line);
8722 while (!parser.eof) {
8723 JimParseScript(&parser);
8724 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
8725 parser.tline);
8727 if (result && parser.missing != ' ') {
8728 ScriptTokenListFree(&tokenlist);
8729 result->missing = parser.missing;
8730 result->line = parser.missingline;
8731 return JIM_ERR;
8735 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
8738 script = Jim_Alloc(sizeof(*script));
8739 memset(script, 0, sizeof(*script));
8740 script->inUse = 1;
8741 if (objPtr->typePtr == &sourceObjType) {
8742 script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
8744 else {
8745 script->fileNameObj = interp->emptyObj;
8747 Jim_IncrRefCount(script->fileNameObj);
8749 ScriptObjAddTokens(interp, script, &tokenlist);
8752 ScriptTokenListFree(&tokenlist);
8755 Jim_FreeIntRep(interp, objPtr);
8756 Jim_SetIntRepPtr(objPtr, script);
8757 objPtr->typePtr = &scriptObjType;
8759 return JIM_OK;
8762 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
8764 if (objPtr == interp->emptyObj) {
8766 objPtr = interp->nullScriptObj;
8769 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
8770 SetScriptFromAny(interp, objPtr, NULL);
8772 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
8775 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
8777 cmdPtr->inUse++;
8780 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
8782 if (--cmdPtr->inUse == 0) {
8783 if (cmdPtr->isproc) {
8784 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
8785 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
8786 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
8787 if (cmdPtr->u.proc.staticVars) {
8788 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
8789 Jim_Free(cmdPtr->u.proc.staticVars);
8792 else {
8794 if (cmdPtr->u.native.delProc) {
8795 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
8798 if (cmdPtr->prevCmd) {
8800 JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
8802 Jim_Free(cmdPtr);
8807 static void JimVariablesHTValDestructor(void *interp, void *val)
8809 Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
8810 Jim_Free(val);
8813 static const Jim_HashTableType JimVariablesHashTableType = {
8814 JimStringCopyHTHashFunction,
8815 JimStringCopyHTDup,
8816 NULL,
8817 JimStringCopyHTKeyCompare,
8818 JimStringCopyHTKeyDestructor,
8819 JimVariablesHTValDestructor
8822 static void JimCommandsHT_ValDestructor(void *interp, void *val)
8824 JimDecrCmdRefCount(interp, val);
8827 static const Jim_HashTableType JimCommandsHashTableType = {
8828 JimStringCopyHTHashFunction,
8829 JimStringCopyHTDup,
8830 NULL,
8831 JimStringCopyHTKeyCompare,
8832 JimStringCopyHTKeyDestructor,
8833 JimCommandsHT_ValDestructor
8838 #ifdef jim_ext_namespace
8839 static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
8841 const char *name = Jim_String(nsObj);
8842 if (name[0] == ':' && name[1] == ':') {
8844 while (*++name == ':') {
8846 nsObj = Jim_NewStringObj(interp, name, -1);
8848 else if (Jim_Length(interp->framePtr->nsObj)) {
8850 nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
8851 Jim_AppendStrings(interp, nsObj, "::", name, NULL);
8853 return nsObj;
8856 static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
8858 Jim_Obj *objPtr = interp->emptyObj;
8860 if (name[0] == ':' && name[1] == ':') {
8862 while (*++name == ':') {
8865 else if (Jim_Length(interp->framePtr->nsObj)) {
8867 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
8868 Jim_AppendStrings(interp, objPtr, "::", name, NULL);
8869 name = Jim_String(objPtr);
8871 Jim_IncrRefCount(objPtr);
8872 *objPtrPtr = objPtr;
8873 return name;
8876 #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))
8878 #else
8880 #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
8881 #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)
8882 #endif
8884 static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
8886 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
8887 if (he) {
8889 Jim_InterpIncrProcEpoch(interp);
8892 if (he && interp->local) {
8894 cmd->prevCmd = he->u.val;
8895 he->u.val = cmd;
8897 else {
8898 if (he) {
8900 Jim_DeleteHashEntry(&interp->commands, name);
8903 Jim_AddHashEntry(&interp->commands, name, cmd);
8905 return JIM_OK;
8909 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
8910 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
8912 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
8915 memset(cmdPtr, 0, sizeof(*cmdPtr));
8916 cmdPtr->inUse = 1;
8917 cmdPtr->u.native.delProc = delProc;
8918 cmdPtr->u.native.cmdProc = cmdProc;
8919 cmdPtr->u.native.privData = privData;
8921 JimCreateCommand(interp, cmdNameStr, cmdPtr);
8923 return JIM_OK;
8926 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
8928 int len, i;
8930 len = Jim_ListLength(interp, staticsListObjPtr);
8931 if (len == 0) {
8932 return JIM_OK;
8935 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
8936 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
8937 for (i = 0; i < len; i++) {
8938 Jim_Obj *objPtr = NULL, *initObjPtr = NULL, *nameObjPtr = NULL;
8939 Jim_Var *varPtr;
8940 int subLen;
8942 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
8944 subLen = Jim_ListLength(interp, objPtr);
8945 if (subLen == 1 || subLen == 2) {
8946 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
8947 if (subLen == 1) {
8948 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
8949 if (initObjPtr == NULL) {
8950 Jim_SetResultFormatted(interp,
8951 "variable for initialization of static \"%#s\" not found in the local context",
8952 nameObjPtr);
8953 return JIM_ERR;
8956 else {
8957 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
8959 if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) {
8960 return JIM_ERR;
8963 varPtr = Jim_Alloc(sizeof(*varPtr));
8964 varPtr->objPtr = initObjPtr;
8965 Jim_IncrRefCount(initObjPtr);
8966 varPtr->linkFramePtr = NULL;
8967 if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars,
8968 Jim_String(nameObjPtr), varPtr) != JIM_OK) {
8969 Jim_SetResultFormatted(interp,
8970 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
8971 Jim_DecrRefCount(interp, initObjPtr);
8972 Jim_Free(varPtr);
8973 return JIM_ERR;
8976 else {
8977 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
8978 objPtr);
8979 return JIM_ERR;
8982 return JIM_OK;
8985 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
8987 #ifdef jim_ext_namespace
8988 if (cmdPtr->isproc) {
8990 const char *pt = strrchr(cmdname, ':');
8991 if (pt && pt != cmdname && pt[-1] == ':') {
8992 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
8993 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
8994 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
8996 if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
8998 Jim_InterpIncrProcEpoch(interp);
9002 #endif
9005 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
9006 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
9008 Jim_Cmd *cmdPtr;
9009 int argListLen;
9010 int i;
9012 argListLen = Jim_ListLength(interp, argListObjPtr);
9015 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
9016 memset(cmdPtr, 0, sizeof(*cmdPtr));
9017 cmdPtr->inUse = 1;
9018 cmdPtr->isproc = 1;
9019 cmdPtr->u.proc.argListObjPtr = argListObjPtr;
9020 cmdPtr->u.proc.argListLen = argListLen;
9021 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
9022 cmdPtr->u.proc.argsPos = -1;
9023 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
9024 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
9025 Jim_IncrRefCount(argListObjPtr);
9026 Jim_IncrRefCount(bodyObjPtr);
9027 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9030 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
9031 goto err;
9036 for (i = 0; i < argListLen; i++) {
9037 Jim_Obj *argPtr;
9038 Jim_Obj *nameObjPtr;
9039 Jim_Obj *defaultObjPtr;
9040 int len;
9043 Jim_ListIndex(interp, argListObjPtr, i, &argPtr, JIM_NONE);
9044 len = Jim_ListLength(interp, argPtr);
9045 if (len == 0) {
9046 Jim_SetResultString(interp, "argument with no name", -1);
9047 err:
9048 JimDecrCmdRefCount(interp, cmdPtr);
9049 return NULL;
9051 if (len > 2) {
9052 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
9053 goto err;
9056 if (len == 2) {
9058 Jim_ListIndex(interp, argPtr, 0, &nameObjPtr, JIM_NONE);
9059 Jim_ListIndex(interp, argPtr, 1, &defaultObjPtr, JIM_NONE);
9061 else {
9063 nameObjPtr = argPtr;
9064 defaultObjPtr = NULL;
9068 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
9069 if (cmdPtr->u.proc.argsPos >= 0) {
9070 Jim_SetResultString(interp, "'args' specified more than once", -1);
9071 goto err;
9073 cmdPtr->u.proc.argsPos = i;
9075 else {
9076 if (len == 2) {
9077 cmdPtr->u.proc.optArity++;
9079 else {
9080 cmdPtr->u.proc.reqArity++;
9084 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
9085 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
9088 return cmdPtr;
9091 int Jim_DeleteCommand(Jim_Interp *interp, const char *name)
9093 int ret = JIM_OK;
9094 Jim_Obj *qualifiedNameObj;
9095 const char *qualname = JimQualifyName(interp, name, &qualifiedNameObj);
9097 if (Jim_DeleteHashEntry(&interp->commands, qualname) == JIM_ERR) {
9098 Jim_SetResultFormatted(interp, "can't delete \"%s\": command doesn't exist", name);
9099 ret = JIM_ERR;
9101 else {
9102 Jim_InterpIncrProcEpoch(interp);
9105 JimFreeQualifiedName(interp, qualifiedNameObj);
9107 return ret;
9110 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName)
9112 int ret = JIM_ERR;
9113 Jim_HashEntry *he;
9114 Jim_Cmd *cmdPtr;
9115 Jim_Obj *qualifiedOldNameObj;
9116 Jim_Obj *qualifiedNewNameObj;
9117 const char *fqold;
9118 const char *fqnew;
9120 if (newName[0] == 0) {
9121 return Jim_DeleteCommand(interp, oldName);
9124 fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
9125 fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);
9128 he = Jim_FindHashEntry(&interp->commands, fqold);
9129 if (he == NULL) {
9130 Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
9132 else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
9133 Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
9135 else {
9137 cmdPtr = he->u.val;
9138 JimIncrCmdRefCount(cmdPtr);
9139 JimUpdateProcNamespace(interp, cmdPtr, fqnew);
9140 Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);
9143 Jim_DeleteHashEntry(&interp->commands, fqold);
9146 Jim_InterpIncrProcEpoch(interp);
9148 ret = JIM_OK;
9151 JimFreeQualifiedName(interp, qualifiedOldNameObj);
9152 JimFreeQualifiedName(interp, qualifiedNewNameObj);
9154 return ret;
9158 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9160 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
9163 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9165 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
9166 dupPtr->typePtr = srcPtr->typePtr;
9167 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
9170 static const Jim_ObjType commandObjType = {
9171 "command",
9172 FreeCommandInternalRep,
9173 DupCommandInternalRep,
9174 NULL,
9175 JIM_TYPE_REFERENCES,
9178 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9180 Jim_Cmd *cmd;
9182 if (objPtr->typePtr != &commandObjType ||
9183 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
9184 #ifdef jim_ext_namespace
9185 || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
9186 #endif
9191 const char *name = Jim_String(objPtr);
9192 Jim_HashEntry *he;
9194 if (name[0] == ':' && name[1] == ':') {
9195 while (*++name == ':') {
9198 #ifdef jim_ext_namespace
9199 else if (Jim_Length(interp->framePtr->nsObj)) {
9201 Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9202 Jim_AppendStrings(interp, nameObj, "::", name, NULL);
9203 he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
9204 Jim_FreeNewObj(interp, nameObj);
9205 if (he) {
9206 goto found;
9209 #endif
9212 he = Jim_FindHashEntry(&interp->commands, name);
9213 if (he == NULL) {
9214 if (flags & JIM_ERRMSG) {
9215 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
9217 return NULL;
9219 #ifdef jim_ext_namespace
9220 found:
9221 #endif
9222 cmd = (Jim_Cmd *)he->u.val;
9225 Jim_FreeIntRep(interp, objPtr);
9226 objPtr->typePtr = &commandObjType;
9227 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
9228 objPtr->internalRep.cmdValue.cmdPtr = cmd;
9229 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
9230 Jim_IncrRefCount(interp->framePtr->nsObj);
9232 else {
9233 cmd = objPtr->internalRep.cmdValue.cmdPtr;
9235 while (cmd->u.proc.upcall) {
9236 cmd = cmd->prevCmd;
9238 return cmd;
9243 #define JIM_DICT_SUGAR 100
9245 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9247 static const Jim_ObjType variableObjType = {
9248 "variable",
9249 NULL,
9250 NULL,
9251 NULL,
9252 JIM_TYPE_REFERENCES,
9255 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
9258 if (nameObjPtr->typePtr != &variableObjType) {
9259 int len;
9260 const char *str = Jim_GetString(nameObjPtr, &len);
9261 if (memchr(str, '\0', len)) {
9262 Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
9263 return JIM_ERR;
9266 return JIM_OK;
9269 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9271 const char *varName;
9272 Jim_CallFrame *framePtr;
9273 Jim_HashEntry *he;
9274 int global;
9275 int len;
9278 if (objPtr->typePtr == &variableObjType) {
9279 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
9280 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
9282 return JIM_OK;
9286 else if (objPtr->typePtr == &dictSubstObjType) {
9287 return JIM_DICT_SUGAR;
9289 else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
9290 return JIM_ERR;
9294 varName = Jim_GetString(objPtr, &len);
9297 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
9298 return JIM_DICT_SUGAR;
9301 if (varName[0] == ':' && varName[1] == ':') {
9302 while (*++varName == ':') {
9304 global = 1;
9305 framePtr = interp->topFramePtr;
9307 else {
9308 global = 0;
9309 framePtr = interp->framePtr;
9313 he = Jim_FindHashEntry(&framePtr->vars, varName);
9314 if (he == NULL) {
9315 if (!global && framePtr->staticVars) {
9317 he = Jim_FindHashEntry(framePtr->staticVars, varName);
9319 if (he == NULL) {
9320 return JIM_ERR;
9325 Jim_FreeIntRep(interp, objPtr);
9326 objPtr->typePtr = &variableObjType;
9327 objPtr->internalRep.varValue.callFrameId = framePtr->id;
9328 objPtr->internalRep.varValue.varPtr = he->u.val;
9329 objPtr->internalRep.varValue.global = global;
9330 return JIM_OK;
9334 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
9335 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
9337 static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9339 const char *name;
9340 Jim_CallFrame *framePtr;
9341 int global;
9344 Jim_Var *var = Jim_Alloc(sizeof(*var));
9346 var->objPtr = valObjPtr;
9347 Jim_IncrRefCount(valObjPtr);
9348 var->linkFramePtr = NULL;
9350 name = Jim_String(nameObjPtr);
9351 if (name[0] == ':' && name[1] == ':') {
9352 while (*++name == ':') {
9354 framePtr = interp->topFramePtr;
9355 global = 1;
9357 else {
9358 framePtr = interp->framePtr;
9359 global = 0;
9363 Jim_AddHashEntry(&framePtr->vars, name, var);
9366 Jim_FreeIntRep(interp, nameObjPtr);
9367 nameObjPtr->typePtr = &variableObjType;
9368 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
9369 nameObjPtr->internalRep.varValue.varPtr = var;
9370 nameObjPtr->internalRep.varValue.global = global;
9372 return var;
9376 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9378 int err;
9379 Jim_Var *var;
9381 switch (SetVariableFromAny(interp, nameObjPtr)) {
9382 case JIM_DICT_SUGAR:
9383 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
9385 case JIM_ERR:
9386 if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) {
9387 return JIM_ERR;
9389 JimCreateVariable(interp, nameObjPtr, valObjPtr);
9390 break;
9392 case JIM_OK:
9393 var = nameObjPtr->internalRep.varValue.varPtr;
9394 if (var->linkFramePtr == NULL) {
9395 Jim_IncrRefCount(valObjPtr);
9396 Jim_DecrRefCount(interp, var->objPtr);
9397 var->objPtr = valObjPtr;
9399 else {
9400 Jim_CallFrame *savedCallFrame;
9402 savedCallFrame = interp->framePtr;
9403 interp->framePtr = var->linkFramePtr;
9404 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
9405 interp->framePtr = savedCallFrame;
9406 if (err != JIM_OK)
9407 return err;
9410 return JIM_OK;
9413 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9415 Jim_Obj *nameObjPtr;
9416 int result;
9418 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9419 Jim_IncrRefCount(nameObjPtr);
9420 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
9421 Jim_DecrRefCount(interp, nameObjPtr);
9422 return result;
9425 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9427 Jim_CallFrame *savedFramePtr;
9428 int result;
9430 savedFramePtr = interp->framePtr;
9431 interp->framePtr = interp->topFramePtr;
9432 result = Jim_SetVariableStr(interp, name, objPtr);
9433 interp->framePtr = savedFramePtr;
9434 return result;
9437 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
9439 Jim_Obj *nameObjPtr, *valObjPtr;
9440 int result;
9442 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9443 valObjPtr = Jim_NewStringObj(interp, val, -1);
9444 Jim_IncrRefCount(nameObjPtr);
9445 Jim_IncrRefCount(valObjPtr);
9446 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
9447 Jim_DecrRefCount(interp, nameObjPtr);
9448 Jim_DecrRefCount(interp, valObjPtr);
9449 return result;
9452 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
9453 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
9455 const char *varName;
9456 const char *targetName;
9457 Jim_CallFrame *framePtr;
9458 Jim_Var *varPtr;
9461 switch (SetVariableFromAny(interp, nameObjPtr)) {
9462 case JIM_DICT_SUGAR:
9464 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
9465 return JIM_ERR;
9467 case JIM_OK:
9468 varPtr = nameObjPtr->internalRep.varValue.varPtr;
9470 if (varPtr->linkFramePtr == NULL) {
9471 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
9472 return JIM_ERR;
9476 varPtr->linkFramePtr = NULL;
9477 break;
9482 varName = Jim_String(nameObjPtr);
9484 if (varName[0] == ':' && varName[1] == ':') {
9485 while (*++varName == ':') {
9488 framePtr = interp->topFramePtr;
9490 else {
9491 framePtr = interp->framePtr;
9494 targetName = Jim_String(targetNameObjPtr);
9495 if (targetName[0] == ':' && targetName[1] == ':') {
9496 while (*++targetName == ':') {
9498 targetNameObjPtr = Jim_NewStringObj(interp, targetName, -1);
9499 targetCallFrame = interp->topFramePtr;
9501 Jim_IncrRefCount(targetNameObjPtr);
9503 if (framePtr->level < targetCallFrame->level) {
9504 Jim_SetResultFormatted(interp,
9505 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
9506 nameObjPtr);
9507 Jim_DecrRefCount(interp, targetNameObjPtr);
9508 return JIM_ERR;
9512 if (framePtr == targetCallFrame) {
9513 Jim_Obj *objPtr = targetNameObjPtr;
9516 while (1) {
9517 if (strcmp(Jim_String(objPtr), varName) == 0) {
9518 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
9519 Jim_DecrRefCount(interp, targetNameObjPtr);
9520 return JIM_ERR;
9522 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
9523 break;
9524 varPtr = objPtr->internalRep.varValue.varPtr;
9525 if (varPtr->linkFramePtr != targetCallFrame)
9526 break;
9527 objPtr = varPtr->objPtr;
9532 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
9534 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
9535 Jim_DecrRefCount(interp, targetNameObjPtr);
9536 return JIM_OK;
9539 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9541 switch (SetVariableFromAny(interp, nameObjPtr)) {
9542 case JIM_OK:{
9543 Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;
9545 if (varPtr->linkFramePtr == NULL) {
9546 return varPtr->objPtr;
9548 else {
9549 Jim_Obj *objPtr;
9552 Jim_CallFrame *savedCallFrame = interp->framePtr;
9554 interp->framePtr = varPtr->linkFramePtr;
9555 objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
9556 interp->framePtr = savedCallFrame;
9557 if (objPtr) {
9558 return objPtr;
9563 break;
9565 case JIM_DICT_SUGAR:
9567 return JimDictSugarGet(interp, nameObjPtr, flags);
9569 if (flags & JIM_ERRMSG) {
9570 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
9572 return NULL;
9575 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9577 Jim_CallFrame *savedFramePtr;
9578 Jim_Obj *objPtr;
9580 savedFramePtr = interp->framePtr;
9581 interp->framePtr = interp->topFramePtr;
9582 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
9583 interp->framePtr = savedFramePtr;
9585 return objPtr;
9588 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
9590 Jim_Obj *nameObjPtr, *varObjPtr;
9592 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9593 Jim_IncrRefCount(nameObjPtr);
9594 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
9595 Jim_DecrRefCount(interp, nameObjPtr);
9596 return varObjPtr;
9599 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
9601 Jim_CallFrame *savedFramePtr;
9602 Jim_Obj *objPtr;
9604 savedFramePtr = interp->framePtr;
9605 interp->framePtr = interp->topFramePtr;
9606 objPtr = Jim_GetVariableStr(interp, name, flags);
9607 interp->framePtr = savedFramePtr;
9609 return objPtr;
9612 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9614 Jim_Var *varPtr;
9615 int retval;
9616 Jim_CallFrame *framePtr;
9618 retval = SetVariableFromAny(interp, nameObjPtr);
9619 if (retval == JIM_DICT_SUGAR) {
9621 return JimDictSugarSet(interp, nameObjPtr, NULL);
9623 else if (retval == JIM_OK) {
9624 varPtr = nameObjPtr->internalRep.varValue.varPtr;
9627 if (varPtr->linkFramePtr) {
9628 framePtr = interp->framePtr;
9629 interp->framePtr = varPtr->linkFramePtr;
9630 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
9631 interp->framePtr = framePtr;
9633 else {
9634 const char *name = Jim_String(nameObjPtr);
9635 if (nameObjPtr->internalRep.varValue.global) {
9636 name += 2;
9637 framePtr = interp->topFramePtr;
9639 else {
9640 framePtr = interp->framePtr;
9643 retval = Jim_DeleteHashEntry(&framePtr->vars, name);
9644 if (retval == JIM_OK) {
9646 JimChangeCallFrameId(interp, framePtr);
9650 if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
9651 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
9653 return retval;
9658 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
9659 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
9661 const char *str, *p;
9662 int len, keyLen;
9663 Jim_Obj *varObjPtr, *keyObjPtr;
9665 str = Jim_GetString(objPtr, &len);
9667 p = strchr(str, '(');
9668 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
9670 varObjPtr = Jim_NewStringObj(interp, str, p - str);
9672 p++;
9673 keyLen = (str + len) - p;
9674 if (str[len - 1] == ')') {
9675 keyLen--;
9679 keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
9681 Jim_IncrRefCount(varObjPtr);
9682 Jim_IncrRefCount(keyObjPtr);
9683 *varPtrPtr = varObjPtr;
9684 *keyPtrPtr = keyObjPtr;
9687 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
9689 int err;
9691 SetDictSubstFromAny(interp, objPtr);
9693 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
9694 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
9696 if (err == JIM_OK) {
9698 Jim_SetEmptyResult(interp);
9700 else {
9701 if (!valObjPtr) {
9703 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
9704 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
9705 objPtr);
9706 return err;
9710 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
9711 (valObjPtr ? "set" : "unset"), objPtr);
9713 return err;
9716 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
9717 Jim_Obj *keyObjPtr, int flags)
9719 Jim_Obj *dictObjPtr;
9720 Jim_Obj *resObjPtr = NULL;
9721 int ret;
9723 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
9724 if (!dictObjPtr) {
9725 return NULL;
9728 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
9729 if (ret != JIM_OK) {
9730 resObjPtr = NULL;
9731 if (ret < 0) {
9732 Jim_SetResultFormatted(interp,
9733 "can't read \"%#s(%#s)\": variable isn't array", varObjPtr, keyObjPtr);
9735 else {
9736 Jim_SetResultFormatted(interp,
9737 "can't read \"%#s(%#s)\": no such element in array", varObjPtr, keyObjPtr);
9740 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
9741 dictObjPtr = Jim_DuplicateObj(interp, dictObjPtr);
9742 if (Jim_SetVariable(interp, varObjPtr, dictObjPtr) != JIM_OK) {
9744 JimPanic((1, "SetVariable failed for JIM_UNSHARED"));
9747 Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
9750 return resObjPtr;
9754 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9756 SetDictSubstFromAny(interp, objPtr);
9758 return JimDictExpandArrayVariable(interp,
9759 objPtr->internalRep.dictSubstValue.varNameObjPtr,
9760 objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
9765 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9767 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
9768 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
9771 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9773 JIM_NOTUSED(interp);
9775 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
9776 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
9777 dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr;
9778 dupPtr->typePtr = &dictSubstObjType;
9782 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
9784 if (objPtr->typePtr != &dictSubstObjType) {
9785 Jim_Obj *varObjPtr, *keyObjPtr;
9787 if (objPtr->typePtr == &interpolatedObjType) {
9790 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
9791 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
9793 Jim_IncrRefCount(varObjPtr);
9794 Jim_IncrRefCount(keyObjPtr);
9796 else {
9797 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
9800 Jim_FreeIntRep(interp, objPtr);
9801 objPtr->typePtr = &dictSubstObjType;
9802 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
9803 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
9807 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
9809 Jim_Obj *resObjPtr = NULL;
9810 Jim_Obj *substKeyObjPtr = NULL;
9812 SetDictSubstFromAny(interp, objPtr);
9814 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
9815 &substKeyObjPtr, JIM_NONE)
9816 != JIM_OK) {
9817 return NULL;
9819 Jim_IncrRefCount(substKeyObjPtr);
9820 resObjPtr =
9821 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
9822 substKeyObjPtr, 0);
9823 Jim_DecrRefCount(interp, substKeyObjPtr);
9825 return resObjPtr;
9828 static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
9830 Jim_Obj *resultObjPtr;
9832 if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
9834 resultObjPtr->refCount--;
9835 return resultObjPtr;
9837 return NULL;
9841 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
9843 Jim_CallFrame *cf;
9845 if (interp->freeFramesList) {
9846 cf = interp->freeFramesList;
9847 interp->freeFramesList = cf->next;
9849 else {
9850 cf = Jim_Alloc(sizeof(*cf));
9851 cf->vars.table = NULL;
9854 cf->id = interp->callFrameEpoch++;
9855 cf->parent = parent;
9856 cf->level = parent ? parent->level + 1 : 0;
9857 cf->argv = NULL;
9858 cf->argc = 0;
9859 cf->procArgsObjPtr = NULL;
9860 cf->procBodyObjPtr = NULL;
9861 cf->next = NULL;
9862 cf->staticVars = NULL;
9863 cf->localCommands = NULL;
9865 cf->nsObj = nsObj;
9866 Jim_IncrRefCount(nsObj);
9867 if (cf->vars.table == NULL)
9868 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
9869 return cf;
9873 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
9875 cf->id = interp->callFrameEpoch++;
9878 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
9881 if (localCommands) {
9882 Jim_Obj *cmdNameObj;
9884 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
9885 Jim_HashEntry *he;
9886 Jim_Obj *fqObjName;
9888 const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);
9890 he = Jim_FindHashEntry(&interp->commands, fqname);
9892 if (he) {
9893 Jim_Cmd *cmd = he->u.val;
9894 if (cmd->prevCmd) {
9895 Jim_Cmd *prevCmd = cmd->prevCmd;
9896 cmd->prevCmd = NULL;
9899 JimDecrCmdRefCount(interp, cmd);
9902 he->u.val = prevCmd;
9904 else {
9905 Jim_DeleteHashEntry(&interp->commands, fqname);
9906 Jim_InterpIncrProcEpoch(interp);
9909 Jim_DecrRefCount(interp, cmdNameObj);
9910 JimFreeQualifiedName(interp, fqObjName);
9912 Jim_FreeStack(localCommands);
9913 Jim_Free(localCommands);
9915 return JIM_OK;
9919 #define JIM_FCF_NONE 0
9920 #define JIM_FCF_NOHT 1
9921 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags)
9923 if (cf->procArgsObjPtr)
9924 Jim_DecrRefCount(interp, cf->procArgsObjPtr);
9925 if (cf->procBodyObjPtr)
9926 Jim_DecrRefCount(interp, cf->procBodyObjPtr);
9927 Jim_DecrRefCount(interp, cf->nsObj);
9928 if (!(flags & JIM_FCF_NOHT))
9929 Jim_FreeHashTable(&cf->vars);
9930 else {
9931 int i;
9932 Jim_HashEntry **table = cf->vars.table, *he;
9934 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
9935 he = table[i];
9936 while (he != NULL) {
9937 Jim_HashEntry *nextEntry = he->next;
9938 Jim_Var *varPtr = (void *)he->u.val;
9940 Jim_DecrRefCount(interp, varPtr->objPtr);
9941 Jim_Free(he->u.val);
9942 Jim_Free((void *)he->key);
9943 Jim_Free(he);
9944 table[i] = NULL;
9945 he = nextEntry;
9948 cf->vars.used = 0;
9951 JimDeleteLocalProcs(interp, cf->localCommands);
9953 cf->next = interp->freeFramesList;
9954 interp->freeFramesList = cf;
9959 #ifdef JIM_REFERENCES
9961 static void JimReferencesHTValDestructor(void *interp, void *val)
9963 Jim_Reference *refPtr = (void *)val;
9965 Jim_DecrRefCount(interp, refPtr->objPtr);
9966 if (refPtr->finalizerCmdNamePtr != NULL) {
9967 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
9969 Jim_Free(val);
9972 static unsigned int JimReferencesHTHashFunction(const void *key)
9975 const unsigned long *widePtr = key;
9976 unsigned int intValue = (unsigned int)*widePtr;
9978 return Jim_IntHashFunction(intValue);
9981 static void *JimReferencesHTKeyDup(void *privdata, const void *key)
9983 void *copy = Jim_Alloc(sizeof(unsigned long));
9985 JIM_NOTUSED(privdata);
9987 memcpy(copy, key, sizeof(unsigned long));
9988 return copy;
9991 static int JimReferencesHTKeyCompare(void *privdata, const void *key1, const void *key2)
9993 JIM_NOTUSED(privdata);
9995 return memcmp(key1, key2, sizeof(unsigned long)) == 0;
9998 static void JimReferencesHTKeyDestructor(void *privdata, void *key)
10000 JIM_NOTUSED(privdata);
10002 Jim_Free(key);
10005 static const Jim_HashTableType JimReferencesHashTableType = {
10006 JimReferencesHTHashFunction,
10007 JimReferencesHTKeyDup,
10008 NULL,
10009 JimReferencesHTKeyCompare,
10010 JimReferencesHTKeyDestructor,
10011 JimReferencesHTValDestructor
10016 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
10018 static int JimFormatReference(char *buf, Jim_Reference *refPtr, unsigned long id)
10020 const char *fmt = "<reference.<%s>.%020lu>";
10022 sprintf(buf, fmt, refPtr->tag, id);
10023 return JIM_REFERENCE_SPACE;
10026 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
10028 static const Jim_ObjType referenceObjType = {
10029 "reference",
10030 NULL,
10031 NULL,
10032 UpdateStringOfReference,
10033 JIM_TYPE_REFERENCES,
10036 void UpdateStringOfReference(struct Jim_Obj *objPtr)
10038 int len;
10039 char buf[JIM_REFERENCE_SPACE + 1];
10040 Jim_Reference *refPtr;
10042 refPtr = objPtr->internalRep.refValue.refPtr;
10043 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
10044 objPtr->bytes = Jim_Alloc(len + 1);
10045 memcpy(objPtr->bytes, buf, len + 1);
10046 objPtr->length = len;
10049 static int isrefchar(int c)
10051 return (c == '_' || isalnum(c));
10054 static int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10056 unsigned long value;
10057 int i, len;
10058 const char *str, *start, *end;
10059 char refId[21];
10060 Jim_Reference *refPtr;
10061 Jim_HashEntry *he;
10062 char *endptr;
10065 str = Jim_GetString(objPtr, &len);
10067 if (len < JIM_REFERENCE_SPACE)
10068 goto badformat;
10070 start = str;
10071 end = str + len - 1;
10072 while (*start == ' ')
10073 start++;
10074 while (*end == ' ' && end > start)
10075 end--;
10076 if (end - start + 1 != JIM_REFERENCE_SPACE)
10077 goto badformat;
10079 if (memcmp(start, "<reference.<", 12) != 0)
10080 goto badformat;
10081 if (start[12 + JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>')
10082 goto badformat;
10084 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
10085 if (!isrefchar(start[12 + i]))
10086 goto badformat;
10089 memcpy(refId, start + 14 + JIM_REFERENCE_TAGLEN, 20);
10090 refId[20] = '\0';
10092 value = strtoul(refId, &endptr, 10);
10093 if (JimCheckConversion(refId, endptr) != JIM_OK)
10094 goto badformat;
10096 he = Jim_FindHashEntry(&interp->references, &value);
10097 if (he == NULL) {
10098 Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr);
10099 return JIM_ERR;
10101 refPtr = he->u.val;
10103 Jim_FreeIntRep(interp, objPtr);
10104 objPtr->typePtr = &referenceObjType;
10105 objPtr->internalRep.refValue.id = value;
10106 objPtr->internalRep.refValue.refPtr = refPtr;
10107 return JIM_OK;
10109 badformat:
10110 Jim_SetResultFormatted(interp, "expected reference but got \"%#s\"", objPtr);
10111 return JIM_ERR;
10114 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr)
10116 struct Jim_Reference *refPtr;
10117 unsigned long id;
10118 Jim_Obj *refObjPtr;
10119 const char *tag;
10120 int tagLen, i;
10123 Jim_CollectIfNeeded(interp);
10125 refPtr = Jim_Alloc(sizeof(*refPtr));
10126 refPtr->objPtr = objPtr;
10127 Jim_IncrRefCount(objPtr);
10128 refPtr->finalizerCmdNamePtr = cmdNamePtr;
10129 if (cmdNamePtr)
10130 Jim_IncrRefCount(cmdNamePtr);
10131 id = interp->referenceNextId++;
10132 Jim_AddHashEntry(&interp->references, &id, refPtr);
10133 refObjPtr = Jim_NewObj(interp);
10134 refObjPtr->typePtr = &referenceObjType;
10135 refObjPtr->bytes = NULL;
10136 refObjPtr->internalRep.refValue.id = id;
10137 refObjPtr->internalRep.refValue.refPtr = refPtr;
10138 interp->referenceNextId++;
10139 tag = Jim_GetString(tagPtr, &tagLen);
10140 if (tagLen > JIM_REFERENCE_TAGLEN)
10141 tagLen = JIM_REFERENCE_TAGLEN;
10142 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
10143 if (i < tagLen && isrefchar(tag[i]))
10144 refPtr->tag[i] = tag[i];
10145 else
10146 refPtr->tag[i] = '_';
10148 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
10149 return refObjPtr;
10152 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
10154 if (objPtr->typePtr != &referenceObjType && SetReferenceFromAny(interp, objPtr) == JIM_ERR)
10155 return NULL;
10156 return objPtr->internalRep.refValue.refPtr;
10159 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
10161 Jim_Reference *refPtr;
10163 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
10164 return JIM_ERR;
10165 Jim_IncrRefCount(cmdNamePtr);
10166 if (refPtr->finalizerCmdNamePtr)
10167 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
10168 refPtr->finalizerCmdNamePtr = cmdNamePtr;
10169 return JIM_OK;
10172 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
10174 Jim_Reference *refPtr;
10176 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
10177 return JIM_ERR;
10178 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
10179 return JIM_OK;
10184 static const Jim_HashTableType JimRefMarkHashTableType = {
10185 JimReferencesHTHashFunction,
10186 JimReferencesHTKeyDup,
10187 NULL,
10188 JimReferencesHTKeyCompare,
10189 JimReferencesHTKeyDestructor,
10190 NULL
10194 int Jim_Collect(Jim_Interp *interp)
10196 int collected = 0;
10197 return collected;
10200 #define JIM_COLLECT_ID_PERIOD 5000
10201 #define JIM_COLLECT_TIME_PERIOD 300
10203 void Jim_CollectIfNeeded(Jim_Interp *interp)
10205 unsigned long elapsedId;
10206 int elapsedTime;
10208 elapsedId = interp->referenceNextId - interp->lastCollectId;
10209 elapsedTime = time(NULL) - interp->lastCollectTime;
10212 if (elapsedId > JIM_COLLECT_ID_PERIOD || elapsedTime > JIM_COLLECT_TIME_PERIOD) {
10213 Jim_Collect(interp);
10216 #endif
10218 int Jim_IsBigEndian(void)
10220 union {
10221 unsigned short s;
10222 unsigned char c[2];
10223 } uval = {0x0102};
10225 return uval.c[0] == 1;
10229 Jim_Interp *Jim_CreateInterp(void)
10231 Jim_Interp *i = Jim_Alloc(sizeof(*i));
10233 memset(i, 0, sizeof(*i));
10235 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
10236 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
10237 i->lastCollectTime = time(NULL);
10239 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
10240 #ifdef JIM_REFERENCES
10241 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
10242 #endif
10243 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
10244 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
10245 i->emptyObj = Jim_NewEmptyStringObj(i);
10246 i->trueObj = Jim_NewIntObj(i, 1);
10247 i->falseObj = Jim_NewIntObj(i, 0);
10248 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
10249 i->errorFileNameObj = i->emptyObj;
10250 i->result = i->emptyObj;
10251 i->stackTrace = Jim_NewListObj(i, NULL, 0);
10252 i->unknown = Jim_NewStringObj(i, "unknown", -1);
10253 i->errorProc = i->emptyObj;
10254 i->currentScriptObj = Jim_NewEmptyStringObj(i);
10255 i->nullScriptObj = Jim_NewEmptyStringObj(i);
10256 Jim_IncrRefCount(i->emptyObj);
10257 Jim_IncrRefCount(i->errorFileNameObj);
10258 Jim_IncrRefCount(i->result);
10259 Jim_IncrRefCount(i->stackTrace);
10260 Jim_IncrRefCount(i->unknown);
10261 Jim_IncrRefCount(i->currentScriptObj);
10262 Jim_IncrRefCount(i->nullScriptObj);
10263 Jim_IncrRefCount(i->errorProc);
10264 Jim_IncrRefCount(i->trueObj);
10265 Jim_IncrRefCount(i->falseObj);
10268 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
10269 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
10271 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
10272 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
10273 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10274 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
10275 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
10276 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
10277 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
10279 return i;
10282 void Jim_FreeInterp(Jim_Interp *i)
10284 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
10285 Jim_Obj *objPtr, *nextObjPtr;
10287 Jim_DecrRefCount(i, i->emptyObj);
10288 Jim_DecrRefCount(i, i->trueObj);
10289 Jim_DecrRefCount(i, i->falseObj);
10290 Jim_DecrRefCount(i, i->result);
10291 Jim_DecrRefCount(i, i->stackTrace);
10292 Jim_DecrRefCount(i, i->errorProc);
10293 Jim_DecrRefCount(i, i->unknown);
10294 Jim_DecrRefCount(i, i->errorFileNameObj);
10295 Jim_DecrRefCount(i, i->currentScriptObj);
10296 Jim_DecrRefCount(i, i->nullScriptObj);
10297 Jim_FreeHashTable(&i->commands);
10298 #ifdef JIM_REFERENCES
10299 Jim_FreeHashTable(&i->references);
10300 #endif
10301 Jim_FreeHashTable(&i->packages);
10302 Jim_Free(i->prngState);
10303 Jim_FreeHashTable(&i->assocData);
10306 while (cf) {
10307 prevcf = cf->parent;
10308 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
10309 cf = prevcf;
10311 if (i->liveList != NULL) {
10312 objPtr = i->liveList;
10314 printf(JIM_NL "-------------------------------------" JIM_NL);
10315 printf("Objects still in the free list:" JIM_NL);
10316 while (objPtr) {
10317 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
10319 if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
10320 printf("%p (%d) %-10s: '%.20s...'" JIM_NL,
10321 (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
10323 else {
10324 printf("%p (%d) %-10s: '%s'" JIM_NL,
10325 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
10327 if (objPtr->typePtr == &sourceObjType) {
10328 printf("FILE %s LINE %d" JIM_NL,
10329 Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
10330 objPtr->internalRep.sourceValue.lineNumber);
10332 objPtr = objPtr->nextObjPtr;
10334 printf("-------------------------------------" JIM_NL JIM_NL);
10335 JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
10338 objPtr = i->freeList;
10339 while (objPtr) {
10340 nextObjPtr = objPtr->nextObjPtr;
10341 Jim_Free(objPtr);
10342 objPtr = nextObjPtr;
10345 cf = i->freeFramesList;
10346 while (cf) {
10347 nextcf = cf->next;
10348 if (cf->vars.table != NULL)
10349 Jim_Free(cf->vars.table);
10350 Jim_Free(cf);
10351 cf = nextcf;
10353 #ifdef jim_ext_load
10354 Jim_FreeLoadHandles(i);
10355 #endif
10358 Jim_Free(i);
10361 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10363 long level;
10364 const char *str;
10365 Jim_CallFrame *framePtr;
10367 if (levelObjPtr) {
10368 str = Jim_String(levelObjPtr);
10369 if (str[0] == '#') {
10370 char *endptr;
10372 level = jim_strtol(str + 1, &endptr);
10373 if (str[1] == '\0' || endptr[0] != '\0') {
10374 level = -1;
10377 else {
10378 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
10379 level = -1;
10381 else {
10383 level = interp->framePtr->level - level;
10387 else {
10388 str = "1";
10389 level = interp->framePtr->level - 1;
10392 if (level == 0) {
10393 return interp->topFramePtr;
10395 if (level > 0) {
10397 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10398 if (framePtr->level == level) {
10399 return framePtr;
10404 Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
10405 return NULL;
10408 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10410 long level;
10411 Jim_CallFrame *framePtr;
10413 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
10414 if (level <= 0) {
10416 level = interp->framePtr->level + level;
10419 if (level == 0) {
10420 return interp->topFramePtr;
10424 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10425 if (framePtr->level == level) {
10426 return framePtr;
10431 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
10432 return NULL;
10435 static void JimResetStackTrace(Jim_Interp *interp)
10437 Jim_DecrRefCount(interp, interp->stackTrace);
10438 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
10439 Jim_IncrRefCount(interp->stackTrace);
10442 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
10444 int len;
10447 Jim_IncrRefCount(stackTraceObj);
10448 Jim_DecrRefCount(interp, interp->stackTrace);
10449 interp->stackTrace = stackTraceObj;
10450 interp->errorFlag = 1;
10452 len = Jim_ListLength(interp, interp->stackTrace);
10453 if (len >= 3) {
10454 if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
10455 interp->addStackTrace = 1;
10461 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
10462 Jim_Obj *fileNameObj, int linenr)
10464 if (strcmp(procname, "unknown") == 0) {
10465 procname = "";
10467 if (!*procname && !Jim_Length(fileNameObj)) {
10469 return;
10472 if (Jim_IsShared(interp->stackTrace)) {
10473 Jim_DecrRefCount(interp, interp->stackTrace);
10474 interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
10475 Jim_IncrRefCount(interp->stackTrace);
10479 if (!*procname && Jim_Length(fileNameObj)) {
10481 int len = Jim_ListLength(interp, interp->stackTrace);
10483 if (len >= 3) {
10484 Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
10485 if (Jim_Length(objPtr)) {
10487 objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
10488 if (Jim_Length(objPtr) == 0) {
10490 ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
10491 ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
10492 return;
10498 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1));
10499 Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj);
10500 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr));
10503 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
10504 void *data)
10506 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
10508 assocEntryPtr->delProc = delProc;
10509 assocEntryPtr->data = data;
10510 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
10513 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
10515 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
10517 if (entryPtr != NULL) {
10518 AssocDataValue *assocEntryPtr = (AssocDataValue *) entryPtr->u.val;
10520 return assocEntryPtr->data;
10522 return NULL;
10525 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
10527 return Jim_DeleteHashEntry(&interp->assocData, key);
10530 int Jim_GetExitCode(Jim_Interp *interp)
10532 return interp->exitCode;
10535 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
10536 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
10538 static const Jim_ObjType intObjType = {
10539 "int",
10540 NULL,
10541 NULL,
10542 UpdateStringOfInt,
10543 JIM_TYPE_NONE,
10546 static const Jim_ObjType coercedDoubleObjType = {
10547 "coerced-double",
10548 NULL,
10549 NULL,
10550 UpdateStringOfInt,
10551 JIM_TYPE_NONE,
10555 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
10557 int len;
10558 char buf[JIM_INTEGER_SPACE + 1];
10560 len = JimWideToString(buf, JimWideValue(objPtr));
10561 objPtr->bytes = Jim_Alloc(len + 1);
10562 memcpy(objPtr->bytes, buf, len + 1);
10563 objPtr->length = len;
10566 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10568 jim_wide wideValue;
10569 const char *str;
10571 if (objPtr->typePtr == &coercedDoubleObjType) {
10573 objPtr->typePtr = &intObjType;
10574 return JIM_OK;
10578 str = Jim_String(objPtr);
10580 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
10581 if (flags & JIM_ERRMSG) {
10582 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
10584 return JIM_ERR;
10586 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
10587 Jim_SetResultString(interp, "Integer value too big to be represented", -1);
10588 return JIM_ERR;
10591 Jim_FreeIntRep(interp, objPtr);
10592 objPtr->typePtr = &intObjType;
10593 objPtr->internalRep.wideValue = wideValue;
10594 return JIM_OK;
10597 #ifdef JIM_OPTIMIZATION
10598 static int JimIsWide(Jim_Obj *objPtr)
10600 return objPtr->typePtr == &intObjType;
10602 #endif
10604 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10606 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
10607 return JIM_ERR;
10608 *widePtr = JimWideValue(objPtr);
10609 return JIM_OK;
10613 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10615 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
10616 return JIM_ERR;
10617 *widePtr = JimWideValue(objPtr);
10618 return JIM_OK;
10621 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
10623 jim_wide wideValue;
10624 int retval;
10626 retval = Jim_GetWide(interp, objPtr, &wideValue);
10627 if (retval == JIM_OK) {
10628 *longPtr = (long)wideValue;
10629 return JIM_OK;
10631 return JIM_ERR;
10634 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
10636 Jim_Obj *objPtr;
10638 objPtr = Jim_NewObj(interp);
10639 objPtr->typePtr = &intObjType;
10640 objPtr->bytes = NULL;
10641 objPtr->internalRep.wideValue = wideValue;
10642 return objPtr;
10645 #define JIM_DOUBLE_SPACE 30
10647 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
10648 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
10650 static const Jim_ObjType doubleObjType = {
10651 "double",
10652 NULL,
10653 NULL,
10654 UpdateStringOfDouble,
10655 JIM_TYPE_NONE,
10658 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
10660 int len;
10661 char buf[JIM_DOUBLE_SPACE + 1];
10663 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
10664 objPtr->bytes = Jim_Alloc(len + 1);
10665 memcpy(objPtr->bytes, buf, len + 1);
10666 objPtr->length = len;
10669 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10671 double doubleValue;
10672 jim_wide wideValue;
10673 const char *str;
10675 str = Jim_String(objPtr);
10677 #ifdef HAVE_LONG_LONG
10679 #define MIN_INT_IN_DOUBLE -(1LL << 53)
10680 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
10682 if (objPtr->typePtr == &intObjType
10683 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
10684 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
10687 objPtr->typePtr = &coercedDoubleObjType;
10688 return JIM_OK;
10690 else
10691 #endif
10692 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
10694 Jim_FreeIntRep(interp, objPtr);
10695 objPtr->typePtr = &coercedDoubleObjType;
10696 objPtr->internalRep.wideValue = wideValue;
10697 return JIM_OK;
10699 else {
10701 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
10702 Jim_SetResultFormatted(interp, "expected number but got \"%#s\"", objPtr);
10703 return JIM_ERR;
10706 Jim_FreeIntRep(interp, objPtr);
10708 objPtr->typePtr = &doubleObjType;
10709 objPtr->internalRep.doubleValue = doubleValue;
10710 return JIM_OK;
10713 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
10715 if (objPtr->typePtr == &coercedDoubleObjType) {
10716 *doublePtr = JimWideValue(objPtr);
10717 return JIM_OK;
10719 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
10720 return JIM_ERR;
10722 if (objPtr->typePtr == &coercedDoubleObjType) {
10723 *doublePtr = JimWideValue(objPtr);
10725 else {
10726 *doublePtr = objPtr->internalRep.doubleValue;
10728 return JIM_OK;
10731 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
10733 Jim_Obj *objPtr;
10735 objPtr = Jim_NewObj(interp);
10736 objPtr->typePtr = &doubleObjType;
10737 objPtr->bytes = NULL;
10738 objPtr->internalRep.doubleValue = doubleValue;
10739 return objPtr;
10742 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
10743 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
10744 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
10745 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
10746 static void UpdateStringOfList(struct Jim_Obj *objPtr);
10747 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
10749 static const Jim_ObjType listObjType = {
10750 "list",
10751 FreeListInternalRep,
10752 DupListInternalRep,
10753 UpdateStringOfList,
10754 JIM_TYPE_NONE,
10757 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10759 int i;
10761 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
10762 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
10764 Jim_Free(objPtr->internalRep.listValue.ele);
10767 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10769 int i;
10771 JIM_NOTUSED(interp);
10773 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
10774 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
10775 dupPtr->internalRep.listValue.ele =
10776 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
10777 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
10778 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
10779 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
10780 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
10782 dupPtr->typePtr = &listObjType;
10785 #define JIM_ELESTR_SIMPLE 0
10786 #define JIM_ELESTR_BRACE 1
10787 #define JIM_ELESTR_QUOTE 2
10788 static unsigned char ListElementQuotingType(const char *s, int len)
10790 int i, level, blevel, trySimple = 1;
10793 if (len == 0)
10794 return JIM_ELESTR_BRACE;
10795 if (s[0] == '"' || s[0] == '{') {
10796 trySimple = 0;
10797 goto testbrace;
10799 for (i = 0; i < len; i++) {
10800 switch (s[i]) {
10801 case ' ':
10802 case '$':
10803 case '"':
10804 case '[':
10805 case ']':
10806 case ';':
10807 case '\\':
10808 case '\r':
10809 case '\n':
10810 case '\t':
10811 case '\f':
10812 case '\v':
10813 trySimple = 0;
10814 case '{':
10815 case '}':
10816 goto testbrace;
10819 return JIM_ELESTR_SIMPLE;
10821 testbrace:
10823 if (s[len - 1] == '\\')
10824 return JIM_ELESTR_QUOTE;
10825 level = 0;
10826 blevel = 0;
10827 for (i = 0; i < len; i++) {
10828 switch (s[i]) {
10829 case '{':
10830 level++;
10831 break;
10832 case '}':
10833 level--;
10834 if (level < 0)
10835 return JIM_ELESTR_QUOTE;
10836 break;
10837 case '[':
10838 blevel++;
10839 break;
10840 case ']':
10841 blevel--;
10842 break;
10843 case '\\':
10844 if (s[i + 1] == '\n')
10845 return JIM_ELESTR_QUOTE;
10846 else if (s[i + 1] != '\0')
10847 i++;
10848 break;
10851 if (blevel < 0) {
10852 return JIM_ELESTR_QUOTE;
10855 if (level == 0) {
10856 if (!trySimple)
10857 return JIM_ELESTR_BRACE;
10858 for (i = 0; i < len; i++) {
10859 switch (s[i]) {
10860 case ' ':
10861 case '$':
10862 case '"':
10863 case '[':
10864 case ']':
10865 case ';':
10866 case '\\':
10867 case '\r':
10868 case '\n':
10869 case '\t':
10870 case '\f':
10871 case '\v':
10872 return JIM_ELESTR_BRACE;
10873 break;
10876 return JIM_ELESTR_SIMPLE;
10878 return JIM_ELESTR_QUOTE;
10881 static int BackslashQuoteString(const char *s, char *q)
10883 char *p = q;
10885 while (*s) {
10886 switch (*s) {
10887 case ' ':
10888 case '$':
10889 case '"':
10890 case '[':
10891 case ']':
10892 case '{':
10893 case '}':
10894 case ';':
10895 case '\\':
10896 *p++ = '\\';
10897 *p++ = *s++;
10898 break;
10899 case '\n':
10900 *p++ = '\\';
10901 *p++ = 'n';
10902 s++;
10903 break;
10904 case '\r':
10905 *p++ = '\\';
10906 *p++ = 'r';
10907 s++;
10908 break;
10909 case '\t':
10910 *p++ = '\\';
10911 *p++ = 't';
10912 s++;
10913 break;
10914 case '\f':
10915 *p++ = '\\';
10916 *p++ = 'f';
10917 s++;
10918 break;
10919 case '\v':
10920 *p++ = '\\';
10921 *p++ = 'v';
10922 s++;
10923 break;
10924 default:
10925 *p++ = *s++;
10926 break;
10929 *p = '\0';
10931 return p - q;
10934 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
10936 #define STATIC_QUOTING_LEN 32
10937 int i, bufLen, realLength;
10938 const char *strRep;
10939 char *p;
10940 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
10943 if (objc > STATIC_QUOTING_LEN) {
10944 quotingType = Jim_Alloc(objc);
10946 else {
10947 quotingType = staticQuoting;
10949 bufLen = 0;
10950 for (i = 0; i < objc; i++) {
10951 int len;
10953 strRep = Jim_GetString(objv[i], &len);
10954 quotingType[i] = ListElementQuotingType(strRep, len);
10955 switch (quotingType[i]) {
10956 case JIM_ELESTR_SIMPLE:
10957 if (i != 0 || strRep[0] != '#') {
10958 bufLen += len;
10959 break;
10962 quotingType[i] = JIM_ELESTR_BRACE;
10964 case JIM_ELESTR_BRACE:
10965 bufLen += len + 2;
10966 break;
10967 case JIM_ELESTR_QUOTE:
10968 bufLen += len * 2;
10969 break;
10971 bufLen++;
10973 bufLen++;
10976 p = objPtr->bytes = Jim_Alloc(bufLen + 1);
10977 realLength = 0;
10978 for (i = 0; i < objc; i++) {
10979 int len, qlen;
10981 strRep = Jim_GetString(objv[i], &len);
10983 switch (quotingType[i]) {
10984 case JIM_ELESTR_SIMPLE:
10985 memcpy(p, strRep, len);
10986 p += len;
10987 realLength += len;
10988 break;
10989 case JIM_ELESTR_BRACE:
10990 *p++ = '{';
10991 memcpy(p, strRep, len);
10992 p += len;
10993 *p++ = '}';
10994 realLength += len + 2;
10995 break;
10996 case JIM_ELESTR_QUOTE:
10997 if (i == 0 && strRep[0] == '#') {
10998 *p++ = '\\';
10999 realLength++;
11001 qlen = BackslashQuoteString(strRep, p);
11002 p += qlen;
11003 realLength += qlen;
11004 break;
11007 if (i + 1 != objc) {
11008 *p++ = ' ';
11009 realLength++;
11012 *p = '\0';
11013 objPtr->length = realLength;
11015 if (quotingType != staticQuoting) {
11016 Jim_Free(quotingType);
11020 static void UpdateStringOfList(struct Jim_Obj *objPtr)
11022 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
11025 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11027 struct JimParserCtx parser;
11028 const char *str;
11029 int strLen;
11030 Jim_Obj *fileNameObj;
11031 int linenr;
11033 if (objPtr->typePtr == &listObjType) {
11034 return JIM_OK;
11037 if (Jim_IsDict(objPtr) && !Jim_IsShared(objPtr)) {
11038 Jim_Obj **listObjPtrPtr;
11039 int len;
11040 int i;
11042 listObjPtrPtr = JimDictPairs(objPtr, &len);
11043 for (i = 0; i < len; i++) {
11044 Jim_IncrRefCount(listObjPtrPtr[i]);
11048 Jim_FreeIntRep(interp, objPtr);
11049 objPtr->typePtr = &listObjType;
11050 objPtr->internalRep.listValue.len = len;
11051 objPtr->internalRep.listValue.maxLen = len;
11052 objPtr->internalRep.listValue.ele = listObjPtrPtr;
11054 return JIM_OK;
11058 if (objPtr->typePtr == &sourceObjType) {
11059 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
11060 linenr = objPtr->internalRep.sourceValue.lineNumber;
11062 else {
11063 fileNameObj = interp->emptyObj;
11064 linenr = 1;
11066 Jim_IncrRefCount(fileNameObj);
11069 str = Jim_GetString(objPtr, &strLen);
11071 Jim_FreeIntRep(interp, objPtr);
11072 objPtr->typePtr = &listObjType;
11073 objPtr->internalRep.listValue.len = 0;
11074 objPtr->internalRep.listValue.maxLen = 0;
11075 objPtr->internalRep.listValue.ele = NULL;
11078 if (strLen) {
11079 JimParserInit(&parser, str, strLen, linenr);
11080 while (!parser.eof) {
11081 Jim_Obj *elementPtr;
11083 JimParseList(&parser);
11084 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11085 continue;
11086 elementPtr = JimParserGetTokenObj(interp, &parser);
11087 JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
11088 ListAppendElement(objPtr, elementPtr);
11091 Jim_DecrRefCount(interp, fileNameObj);
11092 return JIM_OK;
11095 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11097 Jim_Obj *objPtr;
11099 objPtr = Jim_NewObj(interp);
11100 objPtr->typePtr = &listObjType;
11101 objPtr->bytes = NULL;
11102 objPtr->internalRep.listValue.ele = NULL;
11103 objPtr->internalRep.listValue.len = 0;
11104 objPtr->internalRep.listValue.maxLen = 0;
11106 if (len) {
11107 ListInsertElements(objPtr, 0, len, elements);
11110 return objPtr;
11113 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
11114 Jim_Obj ***listVec)
11116 *listLen = Jim_ListLength(interp, listObj);
11117 *listVec = listObj->internalRep.listValue.ele;
11121 static int JimSign(jim_wide w)
11123 if (w == 0) {
11124 return 0;
11126 else if (w < 0) {
11127 return -1;
11129 return 1;
11133 struct lsort_info {
11134 jmp_buf jmpbuf;
11135 Jim_Obj *command;
11136 Jim_Interp *interp;
11137 enum {
11138 JIM_LSORT_ASCII,
11139 JIM_LSORT_NOCASE,
11140 JIM_LSORT_INTEGER,
11141 JIM_LSORT_COMMAND
11142 } type;
11143 int order;
11144 int index;
11145 int indexed;
11146 int (*subfn)(Jim_Obj **, Jim_Obj **);
11149 static struct lsort_info *sort_info;
11151 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11153 Jim_Obj *lObj, *rObj;
11155 if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
11156 Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
11157 longjmp(sort_info->jmpbuf, JIM_ERR);
11159 return sort_info->subfn(&lObj, &rObj);
11163 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11165 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
11168 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11170 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
11173 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11175 jim_wide lhs = 0, rhs = 0;
11177 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11178 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11179 longjmp(sort_info->jmpbuf, JIM_ERR);
11182 return JimSign(lhs - rhs) * sort_info->order;
11185 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11187 Jim_Obj *compare_script;
11188 int rc;
11190 jim_wide ret = 0;
11193 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
11194 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
11195 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
11197 rc = Jim_EvalObj(sort_info->interp, compare_script);
11199 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11200 longjmp(sort_info->jmpbuf, rc);
11203 return JimSign(ret) * sort_info->order;
11207 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
11209 struct lsort_info *prev_info;
11211 typedef int (qsort_comparator) (const void *, const void *);
11212 int (*fn) (Jim_Obj **, Jim_Obj **);
11213 Jim_Obj **vector;
11214 int len;
11215 int rc;
11217 JimPanic((Jim_IsShared(listObjPtr), "Jim_ListSortElements called with shared object"));
11218 SetListFromAny(interp, listObjPtr);
11221 prev_info = sort_info;
11222 sort_info = info;
11224 vector = listObjPtr->internalRep.listValue.ele;
11225 len = listObjPtr->internalRep.listValue.len;
11226 switch (info->type) {
11227 case JIM_LSORT_ASCII:
11228 fn = ListSortString;
11229 break;
11230 case JIM_LSORT_NOCASE:
11231 fn = ListSortStringNoCase;
11232 break;
11233 case JIM_LSORT_INTEGER:
11234 fn = ListSortInteger;
11235 break;
11236 case JIM_LSORT_COMMAND:
11237 fn = ListSortCommand;
11238 break;
11239 default:
11240 fn = NULL;
11241 JimPanic((1, "ListSort called with invalid sort type"));
11244 if (info->indexed) {
11246 info->subfn = fn;
11247 fn = ListSortIndexHelper;
11250 if ((rc = setjmp(info->jmpbuf)) == 0) {
11251 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
11253 Jim_InvalidateStringRep(listObjPtr);
11254 sort_info = prev_info;
11256 return rc;
11259 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
11261 int currentLen = listPtr->internalRep.listValue.len;
11262 int requiredLen = currentLen + elemc;
11263 int i;
11264 Jim_Obj **point;
11266 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
11267 if (requiredLen < 2) {
11269 requiredLen = 4;
11271 else {
11272 requiredLen *= 2;
11275 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11276 sizeof(Jim_Obj *) * requiredLen);
11278 listPtr->internalRep.listValue.maxLen = requiredLen;
11280 if (idx < 0) {
11281 idx = currentLen;
11283 point = listPtr->internalRep.listValue.ele + idx;
11284 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
11285 for (i = 0; i < elemc; ++i) {
11286 point[i] = elemVec[i];
11287 Jim_IncrRefCount(point[i]);
11289 listPtr->internalRep.listValue.len += elemc;
11292 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
11294 ListInsertElements(listPtr, -1, 1, &objPtr);
11297 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11299 ListInsertElements(listPtr, -1,
11300 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
11303 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
11305 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
11306 SetListFromAny(interp, listPtr);
11307 Jim_InvalidateStringRep(listPtr);
11308 ListAppendElement(listPtr, objPtr);
11311 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11313 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
11314 SetListFromAny(interp, listPtr);
11315 SetListFromAny(interp, appendListPtr);
11316 Jim_InvalidateStringRep(listPtr);
11317 ListAppendList(listPtr, appendListPtr);
11320 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
11322 SetListFromAny(interp, objPtr);
11323 return objPtr->internalRep.listValue.len;
11326 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11327 int objc, Jim_Obj *const *objVec)
11329 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
11330 SetListFromAny(interp, listPtr);
11331 if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
11332 idx = listPtr->internalRep.listValue.len;
11333 else if (idx < 0)
11334 idx = 0;
11335 Jim_InvalidateStringRep(listPtr);
11336 ListInsertElements(listPtr, idx, objc, objVec);
11339 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
11341 SetListFromAny(interp, listPtr);
11342 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11343 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11344 return NULL;
11346 if (idx < 0)
11347 idx = listPtr->internalRep.listValue.len + idx;
11348 return listPtr->internalRep.listValue.ele[idx];
11351 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
11353 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
11354 if (*objPtrPtr == NULL) {
11355 if (flags & JIM_ERRMSG) {
11356 Jim_SetResultString(interp, "list index out of range", -1);
11358 return JIM_ERR;
11360 return JIM_OK;
11363 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11364 Jim_Obj *newObjPtr, int flags)
11366 SetListFromAny(interp, listPtr);
11367 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11368 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11369 if (flags & JIM_ERRMSG) {
11370 Jim_SetResultString(interp, "list index out of range", -1);
11372 return JIM_ERR;
11374 if (idx < 0)
11375 idx = listPtr->internalRep.listValue.len + idx;
11376 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
11377 listPtr->internalRep.listValue.ele[idx] = newObjPtr;
11378 Jim_IncrRefCount(newObjPtr);
11379 return JIM_OK;
11382 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
11383 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
11385 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
11386 int shared, i, idx;
11388 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
11389 if (objPtr == NULL)
11390 return JIM_ERR;
11391 if ((shared = Jim_IsShared(objPtr)))
11392 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11393 for (i = 0; i < indexc - 1; i++) {
11394 listObjPtr = objPtr;
11395 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
11396 goto err;
11397 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
11398 goto err;
11400 if (Jim_IsShared(objPtr)) {
11401 objPtr = Jim_DuplicateObj(interp, objPtr);
11402 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
11404 Jim_InvalidateStringRep(listObjPtr);
11406 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
11407 goto err;
11408 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
11409 goto err;
11410 Jim_InvalidateStringRep(objPtr);
11411 Jim_InvalidateStringRep(varObjPtr);
11412 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
11413 goto err;
11414 Jim_SetResult(interp, varObjPtr);
11415 return JIM_OK;
11416 err:
11417 if (shared) {
11418 Jim_FreeNewObj(interp, varObjPtr);
11420 return JIM_ERR;
11423 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
11425 int i;
11426 int listLen = Jim_ListLength(interp, listObjPtr);
11427 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
11429 for (i = 0; i < listLen; ) {
11430 Jim_Obj *objPtr;
11432 Jim_ListIndex(interp, listObjPtr, i, &objPtr, JIM_NONE);
11433 Jim_AppendObj(interp, resObjPtr, objPtr);
11434 if (++i != listLen) {
11435 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11438 return resObjPtr;
11441 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
11443 int i;
11445 for (i = 0; i < objc; i++) {
11446 if (!Jim_IsList(objv[i]))
11447 break;
11449 if (i == objc) {
11450 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
11452 for (i = 0; i < objc; i++)
11453 ListAppendList(objPtr, objv[i]);
11454 return objPtr;
11456 else {
11458 int len = 0, objLen;
11459 char *bytes, *p;
11462 for (i = 0; i < objc; i++) {
11463 Jim_GetString(objv[i], &objLen);
11464 len += objLen;
11466 if (objc)
11467 len += objc - 1;
11469 p = bytes = Jim_Alloc(len + 1);
11470 for (i = 0; i < objc; i++) {
11471 const char *s = Jim_GetString(objv[i], &objLen);
11474 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n')) {
11475 s++;
11476 objLen--;
11477 len--;
11480 while (objLen && (s[objLen - 1] == ' ' ||
11481 s[objLen - 1] == '\n' || s[objLen - 1] == '\t')) {
11483 if (objLen > 1 && s[objLen - 2] == '\\') {
11484 break;
11486 objLen--;
11487 len--;
11489 memcpy(p, s, objLen);
11490 p += objLen;
11491 if (objLen && i + 1 != objc) {
11492 *p++ = ' ';
11494 else if (i + 1 != objc) {
11495 len--;
11498 *p = '\0';
11499 return Jim_NewStringObjNoAlloc(interp, bytes, len);
11503 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
11504 Jim_Obj *lastObjPtr)
11506 int first, last;
11507 int len, rangeLen;
11509 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
11510 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
11511 return NULL;
11512 len = Jim_ListLength(interp, listObjPtr);
11513 first = JimRelToAbsIndex(len, first);
11514 last = JimRelToAbsIndex(len, last);
11515 JimRelToAbsRange(len, &first, &last, &rangeLen);
11516 if (first == 0 && last == len) {
11517 return listObjPtr;
11519 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
11522 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11523 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11524 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
11525 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11528 static unsigned int JimObjectHTHashFunction(const void *key)
11530 int len;
11531 const char *str = Jim_GetString((Jim_Obj *)key, &len);
11532 return Jim_GenHashFunction((const unsigned char *)str, len);
11535 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
11537 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
11540 static void JimObjectHTKeyValDestructor(void *interp, void *val)
11542 Jim_DecrRefCount(interp, (Jim_Obj *)val);
11545 static const Jim_HashTableType JimDictHashTableType = {
11546 JimObjectHTHashFunction,
11547 NULL,
11548 NULL,
11549 JimObjectHTKeyCompare,
11550 JimObjectHTKeyValDestructor,
11551 JimObjectHTKeyValDestructor
11554 static const Jim_ObjType dictObjType = {
11555 "dict",
11556 FreeDictInternalRep,
11557 DupDictInternalRep,
11558 UpdateStringOfDict,
11559 JIM_TYPE_NONE,
11562 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11564 JIM_NOTUSED(interp);
11566 Jim_FreeHashTable(objPtr->internalRep.ptr);
11567 Jim_Free(objPtr->internalRep.ptr);
11570 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11572 Jim_HashTable *ht, *dupHt;
11573 Jim_HashTableIterator htiter;
11574 Jim_HashEntry *he;
11577 ht = srcPtr->internalRep.ptr;
11578 dupHt = Jim_Alloc(sizeof(*dupHt));
11579 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
11580 if (ht->size != 0)
11581 Jim_ExpandHashTable(dupHt, ht->size);
11583 JimInitHashTableIterator(ht, &htiter);
11584 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11585 const Jim_Obj *keyObjPtr = he->key;
11586 Jim_Obj *valObjPtr = he->u.val;
11588 Jim_IncrRefCount((Jim_Obj *)keyObjPtr);
11589 Jim_IncrRefCount(valObjPtr);
11590 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
11593 dupPtr->internalRep.ptr = dupHt;
11594 dupPtr->typePtr = &dictObjType;
11597 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
11599 Jim_HashTable *ht;
11600 Jim_HashTableIterator htiter;
11601 Jim_HashEntry *he;
11602 Jim_Obj **objv;
11603 int i;
11605 ht = dictPtr->internalRep.ptr;
11608 objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
11609 JimInitHashTableIterator(ht, &htiter);
11610 i = 0;
11611 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11612 objv[i++] = (Jim_Obj *)he->key;
11613 objv[i++] = he->u.val;
11615 *len = i;
11616 return objv;
11619 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
11622 int len;
11623 Jim_Obj **objv = JimDictPairs(objPtr, &len);
11625 JimMakeListStringRep(objPtr, objv, len);
11627 Jim_Free(objv);
11630 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11632 int listlen;
11634 if (objPtr->typePtr == &dictObjType) {
11635 return JIM_OK;
11638 Jim_String(objPtr);
11641 listlen = Jim_ListLength(interp, objPtr);
11642 if (listlen % 2) {
11643 Jim_SetResultString(interp, "missing value to go with key", -1);
11644 return JIM_ERR;
11646 else {
11648 Jim_HashTable *ht;
11649 int i;
11651 ht = Jim_Alloc(sizeof(*ht));
11652 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
11654 for (i = 0; i < listlen; i += 2) {
11655 Jim_Obj *keyObjPtr;
11656 Jim_Obj *valObjPtr;
11658 Jim_ListIndex(interp, objPtr, i, &keyObjPtr, JIM_NONE);
11659 Jim_ListIndex(interp, objPtr, i + 1, &valObjPtr, JIM_NONE);
11661 Jim_IncrRefCount(keyObjPtr);
11662 Jim_IncrRefCount(valObjPtr);
11664 if (Jim_AddHashEntry(ht, keyObjPtr, valObjPtr) != JIM_OK) {
11665 Jim_HashEntry *he;
11667 he = Jim_FindHashEntry(ht, keyObjPtr);
11668 Jim_DecrRefCount(interp, keyObjPtr);
11670 Jim_DecrRefCount(interp, (Jim_Obj *)he->u.val);
11671 he->u.val = valObjPtr;
11675 Jim_FreeIntRep(interp, objPtr);
11676 objPtr->typePtr = &dictObjType;
11677 objPtr->internalRep.ptr = ht;
11679 return JIM_OK;
11685 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
11686 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
11688 Jim_HashTable *ht = objPtr->internalRep.ptr;
11690 if (valueObjPtr == NULL) {
11691 return Jim_DeleteHashEntry(ht, keyObjPtr);
11693 Jim_IncrRefCount(keyObjPtr);
11694 Jim_IncrRefCount(valueObjPtr);
11695 if (Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr)) {
11697 Jim_DecrRefCount(interp, keyObjPtr);
11699 return JIM_OK;
11702 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
11703 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
11705 int retcode;
11707 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
11708 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
11709 return JIM_ERR;
11711 retcode = DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
11712 Jim_InvalidateStringRep(objPtr);
11713 return retcode;
11716 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11718 Jim_Obj *objPtr;
11719 int i;
11721 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
11723 objPtr = Jim_NewObj(interp);
11724 objPtr->typePtr = &dictObjType;
11725 objPtr->bytes = NULL;
11726 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
11727 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
11728 for (i = 0; i < len; i += 2)
11729 DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
11730 return objPtr;
11733 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
11734 Jim_Obj **objPtrPtr, int flags)
11736 Jim_HashEntry *he;
11737 Jim_HashTable *ht;
11739 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
11740 return -1;
11742 ht = dictPtr->internalRep.ptr;
11743 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
11744 if (flags & JIM_ERRMSG) {
11745 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
11747 return JIM_ERR;
11749 *objPtrPtr = he->u.val;
11750 return JIM_OK;
11754 int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
11756 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
11757 return JIM_ERR;
11759 *objPtrPtr = JimDictPairs(dictPtr, len);
11761 return JIM_OK;
11766 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
11767 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
11769 int i;
11771 if (keyc == 0) {
11772 *objPtrPtr = dictPtr;
11773 return JIM_OK;
11776 for (i = 0; i < keyc; i++) {
11777 Jim_Obj *objPtr;
11779 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
11780 if (rc != JIM_OK) {
11781 return rc;
11783 dictPtr = objPtr;
11785 *objPtrPtr = dictPtr;
11786 return JIM_OK;
11789 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
11790 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
11792 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
11793 int shared, i;
11795 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
11796 if (objPtr == NULL) {
11797 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
11799 return JIM_ERR;
11801 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
11802 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
11803 Jim_FreeNewObj(interp, varObjPtr);
11804 return JIM_ERR;
11807 if ((shared = Jim_IsShared(objPtr)))
11808 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11809 for (i = 0; i < keyc; i++) {
11810 dictObjPtr = objPtr;
11813 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
11814 goto err;
11817 if (i == keyc - 1) {
11819 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
11820 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
11821 goto err;
11824 break;
11828 Jim_InvalidateStringRep(dictObjPtr);
11829 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
11830 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
11831 if (Jim_IsShared(objPtr)) {
11832 objPtr = Jim_DuplicateObj(interp, objPtr);
11833 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
11836 else {
11837 if (newObjPtr == NULL) {
11838 goto err;
11840 objPtr = Jim_NewDictObj(interp, NULL, 0);
11841 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
11844 Jim_InvalidateStringRep(objPtr);
11845 Jim_InvalidateStringRep(varObjPtr);
11846 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
11847 goto err;
11849 Jim_SetResult(interp, varObjPtr);
11850 return JIM_OK;
11851 err:
11852 if (shared) {
11853 Jim_FreeNewObj(interp, varObjPtr);
11855 return JIM_ERR;
11858 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
11859 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11861 static const Jim_ObjType indexObjType = {
11862 "index",
11863 NULL,
11864 NULL,
11865 UpdateStringOfIndex,
11866 JIM_TYPE_NONE,
11869 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
11871 int len;
11872 char buf[JIM_INTEGER_SPACE + 1];
11874 if (objPtr->internalRep.intValue >= 0)
11875 len = sprintf(buf, "%d", objPtr->internalRep.intValue);
11876 else if (objPtr->internalRep.intValue == -1)
11877 len = sprintf(buf, "end");
11878 else {
11879 len = sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
11881 objPtr->bytes = Jim_Alloc(len + 1);
11882 memcpy(objPtr->bytes, buf, len + 1);
11883 objPtr->length = len;
11886 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11888 int idx, end = 0;
11889 const char *str;
11890 char *endptr;
11893 str = Jim_String(objPtr);
11896 if (strncmp(str, "end", 3) == 0) {
11897 end = 1;
11898 str += 3;
11899 idx = 0;
11901 else {
11902 idx = jim_strtol(str, &endptr);
11904 if (endptr == str) {
11905 goto badindex;
11907 str = endptr;
11911 if (*str == '+' || *str == '-') {
11912 int sign = (*str == '+' ? 1 : -1);
11914 idx += sign * jim_strtol(++str, &endptr);
11915 if (str == endptr || *endptr) {
11916 goto badindex;
11918 str = endptr;
11921 while (isspace(UCHAR(*str))) {
11922 str++;
11924 if (*str) {
11925 goto badindex;
11927 if (end) {
11928 if (idx > 0) {
11929 idx = INT_MAX;
11931 else {
11933 idx--;
11936 else if (idx < 0) {
11937 idx = -INT_MAX;
11941 Jim_FreeIntRep(interp, objPtr);
11942 objPtr->typePtr = &indexObjType;
11943 objPtr->internalRep.intValue = idx;
11944 return JIM_OK;
11946 badindex:
11947 Jim_SetResultFormatted(interp,
11948 "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
11949 return JIM_ERR;
11952 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
11955 if (objPtr->typePtr == &intObjType) {
11956 jim_wide val = JimWideValue(objPtr);
11958 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
11959 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
11960 return JIM_OK;
11963 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
11964 return JIM_ERR;
11965 *indexPtr = objPtr->internalRep.intValue;
11966 return JIM_OK;
11971 static const char * const jimReturnCodes[] = {
11972 "ok",
11973 "error",
11974 "return",
11975 "break",
11976 "continue",
11977 "signal",
11978 "exit",
11979 "eval",
11980 NULL
11983 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
11985 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
11987 static const Jim_ObjType returnCodeObjType = {
11988 "return-code",
11989 NULL,
11990 NULL,
11991 NULL,
11992 JIM_TYPE_NONE,
11995 const char *Jim_ReturnCode(int code)
11997 if (code < 0 || code >= (int)jimReturnCodesSize) {
11998 return "?";
12000 else {
12001 return jimReturnCodes[code];
12005 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12007 int returnCode;
12008 jim_wide wideValue;
12011 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
12012 returnCode = (int)wideValue;
12013 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
12014 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
12015 return JIM_ERR;
12018 Jim_FreeIntRep(interp, objPtr);
12019 objPtr->typePtr = &returnCodeObjType;
12020 objPtr->internalRep.intValue = returnCode;
12021 return JIM_OK;
12024 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
12026 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
12027 return JIM_ERR;
12028 *intPtr = objPtr->internalRep.intValue;
12029 return JIM_OK;
12032 static int JimParseExprOperator(struct JimParserCtx *pc);
12033 static int JimParseExprNumber(struct JimParserCtx *pc);
12034 static int JimParseExprIrrational(struct JimParserCtx *pc);
12039 enum
12043 JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
12044 JIM_EXPROP_DIV,
12045 JIM_EXPROP_MOD,
12046 JIM_EXPROP_SUB,
12047 JIM_EXPROP_ADD,
12048 JIM_EXPROP_LSHIFT,
12049 JIM_EXPROP_RSHIFT,
12050 JIM_EXPROP_ROTL,
12051 JIM_EXPROP_ROTR,
12052 JIM_EXPROP_LT,
12053 JIM_EXPROP_GT,
12054 JIM_EXPROP_LTE,
12055 JIM_EXPROP_GTE,
12056 JIM_EXPROP_NUMEQ,
12057 JIM_EXPROP_NUMNE,
12058 JIM_EXPROP_BITAND,
12059 JIM_EXPROP_BITXOR,
12060 JIM_EXPROP_BITOR,
12063 JIM_EXPROP_LOGICAND,
12064 JIM_EXPROP_LOGICAND_LEFT,
12065 JIM_EXPROP_LOGICAND_RIGHT,
12068 JIM_EXPROP_LOGICOR,
12069 JIM_EXPROP_LOGICOR_LEFT,
12070 JIM_EXPROP_LOGICOR_RIGHT,
12074 JIM_EXPROP_TERNARY,
12075 JIM_EXPROP_TERNARY_LEFT,
12076 JIM_EXPROP_TERNARY_RIGHT,
12079 JIM_EXPROP_COLON,
12080 JIM_EXPROP_COLON_LEFT,
12081 JIM_EXPROP_COLON_RIGHT,
12083 JIM_EXPROP_POW,
12086 JIM_EXPROP_STREQ,
12087 JIM_EXPROP_STRNE,
12088 JIM_EXPROP_STRIN,
12089 JIM_EXPROP_STRNI,
12092 JIM_EXPROP_NOT,
12093 JIM_EXPROP_BITNOT,
12094 JIM_EXPROP_UNARYMINUS,
12095 JIM_EXPROP_UNARYPLUS,
12098 JIM_EXPROP_FUNC_FIRST,
12099 JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
12100 JIM_EXPROP_FUNC_ABS,
12101 JIM_EXPROP_FUNC_DOUBLE,
12102 JIM_EXPROP_FUNC_ROUND,
12103 JIM_EXPROP_FUNC_RAND,
12104 JIM_EXPROP_FUNC_SRAND,
12107 JIM_EXPROP_FUNC_SIN,
12108 JIM_EXPROP_FUNC_COS,
12109 JIM_EXPROP_FUNC_TAN,
12110 JIM_EXPROP_FUNC_ASIN,
12111 JIM_EXPROP_FUNC_ACOS,
12112 JIM_EXPROP_FUNC_ATAN,
12113 JIM_EXPROP_FUNC_SINH,
12114 JIM_EXPROP_FUNC_COSH,
12115 JIM_EXPROP_FUNC_TANH,
12116 JIM_EXPROP_FUNC_CEIL,
12117 JIM_EXPROP_FUNC_FLOOR,
12118 JIM_EXPROP_FUNC_EXP,
12119 JIM_EXPROP_FUNC_LOG,
12120 JIM_EXPROP_FUNC_LOG10,
12121 JIM_EXPROP_FUNC_SQRT,
12122 JIM_EXPROP_FUNC_POW,
12125 struct JimExprState
12127 Jim_Obj **stack;
12128 int stacklen;
12129 int opcode;
12130 int skip;
12134 typedef struct Jim_ExprOperator
12136 const char *name;
12137 int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
12138 unsigned char precedence;
12139 unsigned char arity;
12140 unsigned char lazy;
12141 unsigned char namelen;
12142 } Jim_ExprOperator;
12144 static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
12146 Jim_IncrRefCount(obj);
12147 e->stack[e->stacklen++] = obj;
12150 static Jim_Obj *ExprPop(struct JimExprState *e)
12152 return e->stack[--e->stacklen];
12155 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
12157 int intresult = 0;
12158 int rc = JIM_OK;
12159 Jim_Obj *A = ExprPop(e);
12160 double dA, dC = 0;
12161 jim_wide wA, wC = 0;
12163 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12164 intresult = 1;
12166 switch (e->opcode) {
12167 case JIM_EXPROP_FUNC_INT:
12168 wC = wA;
12169 break;
12170 case JIM_EXPROP_FUNC_ROUND:
12171 wC = wA;
12172 break;
12173 case JIM_EXPROP_FUNC_DOUBLE:
12174 dC = wA;
12175 intresult = 0;
12176 break;
12177 case JIM_EXPROP_FUNC_ABS:
12178 wC = wA >= 0 ? wA : -wA;
12179 break;
12180 case JIM_EXPROP_UNARYMINUS:
12181 wC = -wA;
12182 break;
12183 case JIM_EXPROP_UNARYPLUS:
12184 wC = wA;
12185 break;
12186 case JIM_EXPROP_NOT:
12187 wC = !wA;
12188 break;
12189 default:
12190 abort();
12193 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12194 switch (e->opcode) {
12195 case JIM_EXPROP_FUNC_INT:
12196 wC = dA;
12197 intresult = 1;
12198 break;
12199 case JIM_EXPROP_FUNC_ROUND:
12200 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12201 intresult = 1;
12202 break;
12203 case JIM_EXPROP_FUNC_DOUBLE:
12204 dC = dA;
12205 break;
12206 case JIM_EXPROP_FUNC_ABS:
12207 dC = dA >= 0 ? dA : -dA;
12208 break;
12209 case JIM_EXPROP_UNARYMINUS:
12210 dC = -dA;
12211 break;
12212 case JIM_EXPROP_UNARYPLUS:
12213 dC = dA;
12214 break;
12215 case JIM_EXPROP_NOT:
12216 wC = !dA;
12217 intresult = 1;
12218 break;
12219 default:
12220 abort();
12224 if (rc == JIM_OK) {
12225 if (intresult) {
12226 ExprPush(e, Jim_NewIntObj(interp, wC));
12228 else {
12229 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12233 Jim_DecrRefCount(interp, A);
12235 return rc;
12238 static double JimRandDouble(Jim_Interp *interp)
12240 unsigned long x;
12241 JimRandomBytes(interp, &x, sizeof(x));
12243 return (double)x / (unsigned long)~0;
12246 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
12248 Jim_Obj *A = ExprPop(e);
12249 jim_wide wA;
12251 int rc = Jim_GetWide(interp, A, &wA);
12252 if (rc == JIM_OK) {
12253 switch (e->opcode) {
12254 case JIM_EXPROP_BITNOT:
12255 ExprPush(e, Jim_NewIntObj(interp, ~wA));
12256 break;
12257 case JIM_EXPROP_FUNC_SRAND:
12258 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
12259 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12260 break;
12261 default:
12262 abort();
12266 Jim_DecrRefCount(interp, A);
12268 return rc;
12271 static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
12273 JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
12275 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12277 return JIM_OK;
12280 #ifdef JIM_MATH_FUNCTIONS
12281 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
12283 int rc;
12284 Jim_Obj *A = ExprPop(e);
12285 double dA, dC;
12287 rc = Jim_GetDouble(interp, A, &dA);
12288 if (rc == JIM_OK) {
12289 switch (e->opcode) {
12290 case JIM_EXPROP_FUNC_SIN:
12291 dC = sin(dA);
12292 break;
12293 case JIM_EXPROP_FUNC_COS:
12294 dC = cos(dA);
12295 break;
12296 case JIM_EXPROP_FUNC_TAN:
12297 dC = tan(dA);
12298 break;
12299 case JIM_EXPROP_FUNC_ASIN:
12300 dC = asin(dA);
12301 break;
12302 case JIM_EXPROP_FUNC_ACOS:
12303 dC = acos(dA);
12304 break;
12305 case JIM_EXPROP_FUNC_ATAN:
12306 dC = atan(dA);
12307 break;
12308 case JIM_EXPROP_FUNC_SINH:
12309 dC = sinh(dA);
12310 break;
12311 case JIM_EXPROP_FUNC_COSH:
12312 dC = cosh(dA);
12313 break;
12314 case JIM_EXPROP_FUNC_TANH:
12315 dC = tanh(dA);
12316 break;
12317 case JIM_EXPROP_FUNC_CEIL:
12318 dC = ceil(dA);
12319 break;
12320 case JIM_EXPROP_FUNC_FLOOR:
12321 dC = floor(dA);
12322 break;
12323 case JIM_EXPROP_FUNC_EXP:
12324 dC = exp(dA);
12325 break;
12326 case JIM_EXPROP_FUNC_LOG:
12327 dC = log(dA);
12328 break;
12329 case JIM_EXPROP_FUNC_LOG10:
12330 dC = log10(dA);
12331 break;
12332 case JIM_EXPROP_FUNC_SQRT:
12333 dC = sqrt(dA);
12334 break;
12335 default:
12336 abort();
12338 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12341 Jim_DecrRefCount(interp, A);
12343 return rc;
12345 #endif
12348 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
12350 Jim_Obj *B = ExprPop(e);
12351 Jim_Obj *A = ExprPop(e);
12352 jim_wide wA, wB;
12353 int rc = JIM_ERR;
12355 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
12356 jim_wide wC;
12358 rc = JIM_OK;
12360 switch (e->opcode) {
12361 case JIM_EXPROP_LSHIFT:
12362 wC = wA << wB;
12363 break;
12364 case JIM_EXPROP_RSHIFT:
12365 wC = wA >> wB;
12366 break;
12367 case JIM_EXPROP_BITAND:
12368 wC = wA & wB;
12369 break;
12370 case JIM_EXPROP_BITXOR:
12371 wC = wA ^ wB;
12372 break;
12373 case JIM_EXPROP_BITOR:
12374 wC = wA | wB;
12375 break;
12376 case JIM_EXPROP_MOD:
12377 if (wB == 0) {
12378 wC = 0;
12379 Jim_SetResultString(interp, "Division by zero", -1);
12380 rc = JIM_ERR;
12382 else {
12383 int negative = 0;
12385 if (wB < 0) {
12386 wB = -wB;
12387 wA = -wA;
12388 negative = 1;
12390 wC = wA % wB;
12391 if (wC < 0) {
12392 wC += wB;
12394 if (negative) {
12395 wC = -wC;
12398 break;
12399 case JIM_EXPROP_ROTL:
12400 case JIM_EXPROP_ROTR:{
12402 unsigned long uA = (unsigned long)wA;
12403 unsigned long uB = (unsigned long)wB;
12404 const unsigned int S = sizeof(unsigned long) * 8;
12407 uB %= S;
12409 if (e->opcode == JIM_EXPROP_ROTR) {
12410 uB = S - uB;
12412 wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
12413 break;
12415 default:
12416 abort();
12418 ExprPush(e, Jim_NewIntObj(interp, wC));
12422 Jim_DecrRefCount(interp, A);
12423 Jim_DecrRefCount(interp, B);
12425 return rc;
12430 static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
12432 int intresult = 0;
12433 int rc = JIM_OK;
12434 double dA, dB, dC = 0;
12435 jim_wide wA, wB, wC = 0;
12437 Jim_Obj *B = ExprPop(e);
12438 Jim_Obj *A = ExprPop(e);
12440 if ((A->typePtr != &doubleObjType || A->bytes) &&
12441 (B->typePtr != &doubleObjType || B->bytes) &&
12442 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
12446 intresult = 1;
12448 switch (e->opcode) {
12449 case JIM_EXPROP_POW:
12450 case JIM_EXPROP_FUNC_POW:
12451 wC = JimPowWide(wA, wB);
12452 break;
12453 case JIM_EXPROP_ADD:
12454 wC = wA + wB;
12455 break;
12456 case JIM_EXPROP_SUB:
12457 wC = wA - wB;
12458 break;
12459 case JIM_EXPROP_MUL:
12460 wC = wA * wB;
12461 break;
12462 case JIM_EXPROP_DIV:
12463 if (wB == 0) {
12464 Jim_SetResultString(interp, "Division by zero", -1);
12465 rc = JIM_ERR;
12467 else {
12468 if (wB < 0) {
12469 wB = -wB;
12470 wA = -wA;
12472 wC = wA / wB;
12473 if (wA % wB < 0) {
12474 wC--;
12477 break;
12478 case JIM_EXPROP_LT:
12479 wC = wA < wB;
12480 break;
12481 case JIM_EXPROP_GT:
12482 wC = wA > wB;
12483 break;
12484 case JIM_EXPROP_LTE:
12485 wC = wA <= wB;
12486 break;
12487 case JIM_EXPROP_GTE:
12488 wC = wA >= wB;
12489 break;
12490 case JIM_EXPROP_NUMEQ:
12491 wC = wA == wB;
12492 break;
12493 case JIM_EXPROP_NUMNE:
12494 wC = wA != wB;
12495 break;
12496 default:
12497 abort();
12500 else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
12501 switch (e->opcode) {
12502 case JIM_EXPROP_POW:
12503 case JIM_EXPROP_FUNC_POW:
12504 #ifdef JIM_MATH_FUNCTIONS
12505 dC = pow(dA, dB);
12506 #else
12507 Jim_SetResultString(interp, "unsupported", -1);
12508 rc = JIM_ERR;
12509 #endif
12510 break;
12511 case JIM_EXPROP_ADD:
12512 dC = dA + dB;
12513 break;
12514 case JIM_EXPROP_SUB:
12515 dC = dA - dB;
12516 break;
12517 case JIM_EXPROP_MUL:
12518 dC = dA * dB;
12519 break;
12520 case JIM_EXPROP_DIV:
12521 if (dB == 0) {
12522 #ifdef INFINITY
12523 dC = dA < 0 ? -INFINITY : INFINITY;
12524 #else
12525 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
12526 #endif
12528 else {
12529 dC = dA / dB;
12531 break;
12532 case JIM_EXPROP_LT:
12533 wC = dA < dB;
12534 intresult = 1;
12535 break;
12536 case JIM_EXPROP_GT:
12537 wC = dA > dB;
12538 intresult = 1;
12539 break;
12540 case JIM_EXPROP_LTE:
12541 wC = dA <= dB;
12542 intresult = 1;
12543 break;
12544 case JIM_EXPROP_GTE:
12545 wC = dA >= dB;
12546 intresult = 1;
12547 break;
12548 case JIM_EXPROP_NUMEQ:
12549 wC = dA == dB;
12550 intresult = 1;
12551 break;
12552 case JIM_EXPROP_NUMNE:
12553 wC = dA != dB;
12554 intresult = 1;
12555 break;
12556 default:
12557 abort();
12560 else {
12564 int i = Jim_StringCompareObj(interp, A, B, 0);
12566 intresult = 1;
12568 switch (e->opcode) {
12569 case JIM_EXPROP_LT:
12570 wC = i < 0;
12571 break;
12572 case JIM_EXPROP_GT:
12573 wC = i > 0;
12574 break;
12575 case JIM_EXPROP_LTE:
12576 wC = i <= 0;
12577 break;
12578 case JIM_EXPROP_GTE:
12579 wC = i >= 0;
12580 break;
12581 case JIM_EXPROP_NUMEQ:
12582 wC = i == 0;
12583 break;
12584 case JIM_EXPROP_NUMNE:
12585 wC = i != 0;
12586 break;
12587 default:
12588 rc = JIM_ERR;
12589 break;
12593 if (rc == JIM_OK) {
12594 if (intresult) {
12595 ExprPush(e, Jim_NewIntObj(interp, wC));
12597 else {
12598 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12602 Jim_DecrRefCount(interp, A);
12603 Jim_DecrRefCount(interp, B);
12605 return rc;
12608 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
12610 int listlen;
12611 int i;
12613 listlen = Jim_ListLength(interp, listObjPtr);
12614 for (i = 0; i < listlen; i++) {
12615 Jim_Obj *objPtr;
12617 Jim_ListIndex(interp, listObjPtr, i, &objPtr, JIM_NONE);
12619 if (Jim_StringEqObj(objPtr, valObj)) {
12620 return 1;
12623 return 0;
12626 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
12628 Jim_Obj *B = ExprPop(e);
12629 Jim_Obj *A = ExprPop(e);
12631 jim_wide wC;
12633 switch (e->opcode) {
12634 case JIM_EXPROP_STREQ:
12635 case JIM_EXPROP_STRNE: {
12636 int Alen, Blen;
12637 const char *sA = Jim_GetString(A, &Alen);
12638 const char *sB = Jim_GetString(B, &Blen);
12640 if (e->opcode == JIM_EXPROP_STREQ) {
12641 wC = (Alen == Blen && memcmp(sA, sB, Alen) == 0);
12643 else {
12644 wC = (Alen != Blen || memcmp(sA, sB, Alen) != 0);
12646 break;
12648 case JIM_EXPROP_STRIN:
12649 wC = JimSearchList(interp, B, A);
12650 break;
12651 case JIM_EXPROP_STRNI:
12652 wC = !JimSearchList(interp, B, A);
12653 break;
12654 default:
12655 abort();
12657 ExprPush(e, Jim_NewIntObj(interp, wC));
12659 Jim_DecrRefCount(interp, A);
12660 Jim_DecrRefCount(interp, B);
12662 return JIM_OK;
12665 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
12667 long l;
12668 double d;
12670 if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
12671 return l != 0;
12673 if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
12674 return d != 0;
12676 return -1;
12679 static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
12681 Jim_Obj *skip = ExprPop(e);
12682 Jim_Obj *A = ExprPop(e);
12683 int rc = JIM_OK;
12685 switch (ExprBool(interp, A)) {
12686 case 0:
12688 e->skip = JimWideValue(skip);
12689 ExprPush(e, Jim_NewIntObj(interp, 0));
12690 break;
12692 case 1:
12694 break;
12696 case -1:
12698 rc = JIM_ERR;
12700 Jim_DecrRefCount(interp, A);
12701 Jim_DecrRefCount(interp, skip);
12703 return rc;
12706 static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
12708 Jim_Obj *skip = ExprPop(e);
12709 Jim_Obj *A = ExprPop(e);
12710 int rc = JIM_OK;
12712 switch (ExprBool(interp, A)) {
12713 case 0:
12715 break;
12717 case 1:
12719 e->skip = JimWideValue(skip);
12720 ExprPush(e, Jim_NewIntObj(interp, 1));
12721 break;
12723 case -1:
12725 rc = JIM_ERR;
12726 break;
12728 Jim_DecrRefCount(interp, A);
12729 Jim_DecrRefCount(interp, skip);
12731 return rc;
12734 static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
12736 Jim_Obj *A = ExprPop(e);
12737 int rc = JIM_OK;
12739 switch (ExprBool(interp, A)) {
12740 case 0:
12741 ExprPush(e, Jim_NewIntObj(interp, 0));
12742 break;
12744 case 1:
12745 ExprPush(e, Jim_NewIntObj(interp, 1));
12746 break;
12748 case -1:
12750 rc = JIM_ERR;
12751 break;
12753 Jim_DecrRefCount(interp, A);
12755 return rc;
12758 static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
12760 Jim_Obj *skip = ExprPop(e);
12761 Jim_Obj *A = ExprPop(e);
12762 int rc = JIM_OK;
12765 ExprPush(e, A);
12767 switch (ExprBool(interp, A)) {
12768 case 0:
12770 e->skip = JimWideValue(skip);
12772 ExprPush(e, Jim_NewIntObj(interp, 0));
12773 break;
12775 case 1:
12777 break;
12779 case -1:
12781 rc = JIM_ERR;
12782 break;
12784 Jim_DecrRefCount(interp, A);
12785 Jim_DecrRefCount(interp, skip);
12787 return rc;
12790 static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
12792 Jim_Obj *skip = ExprPop(e);
12793 Jim_Obj *B = ExprPop(e);
12794 Jim_Obj *A = ExprPop(e);
12797 if (ExprBool(interp, A)) {
12799 e->skip = JimWideValue(skip);
12801 ExprPush(e, B);
12804 Jim_DecrRefCount(interp, skip);
12805 Jim_DecrRefCount(interp, A);
12806 Jim_DecrRefCount(interp, B);
12807 return JIM_OK;
12810 static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
12812 return JIM_OK;
12815 enum
12817 LAZY_NONE,
12818 LAZY_OP,
12819 LAZY_LEFT,
12820 LAZY_RIGHT
12823 #define OPRINIT(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1}
12825 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
12826 OPRINIT("*", 110, 2, JimExprOpBin, LAZY_NONE),
12827 OPRINIT("/", 110, 2, JimExprOpBin, LAZY_NONE),
12828 OPRINIT("%", 110, 2, JimExprOpIntBin, LAZY_NONE),
12830 OPRINIT("-", 100, 2, JimExprOpBin, LAZY_NONE),
12831 OPRINIT("+", 100, 2, JimExprOpBin, LAZY_NONE),
12833 OPRINIT("<<", 90, 2, JimExprOpIntBin, LAZY_NONE),
12834 OPRINIT(">>", 90, 2, JimExprOpIntBin, LAZY_NONE),
12836 OPRINIT("<<<", 90, 2, JimExprOpIntBin, LAZY_NONE),
12837 OPRINIT(">>>", 90, 2, JimExprOpIntBin, LAZY_NONE),
12839 OPRINIT("<", 80, 2, JimExprOpBin, LAZY_NONE),
12840 OPRINIT(">", 80, 2, JimExprOpBin, LAZY_NONE),
12841 OPRINIT("<=", 80, 2, JimExprOpBin, LAZY_NONE),
12842 OPRINIT(">=", 80, 2, JimExprOpBin, LAZY_NONE),
12844 OPRINIT("==", 70, 2, JimExprOpBin, LAZY_NONE),
12845 OPRINIT("!=", 70, 2, JimExprOpBin, LAZY_NONE),
12847 OPRINIT("&", 50, 2, JimExprOpIntBin, LAZY_NONE),
12848 OPRINIT("^", 49, 2, JimExprOpIntBin, LAZY_NONE),
12849 OPRINIT("|", 48, 2, JimExprOpIntBin, LAZY_NONE),
12851 OPRINIT("&&", 10, 2, NULL, LAZY_OP),
12852 OPRINIT(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
12853 OPRINIT(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),
12855 OPRINIT("||", 9, 2, NULL, LAZY_OP),
12856 OPRINIT(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
12857 OPRINIT(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),
12859 OPRINIT("?", 5, 2, JimExprOpNull, LAZY_OP),
12860 OPRINIT(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
12861 OPRINIT(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
12863 OPRINIT(":", 5, 2, JimExprOpNull, LAZY_OP),
12864 OPRINIT(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
12865 OPRINIT(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
12867 OPRINIT("**", 250, 2, JimExprOpBin, LAZY_NONE),
12869 OPRINIT("eq", 60, 2, JimExprOpStrBin, LAZY_NONE),
12870 OPRINIT("ne", 60, 2, JimExprOpStrBin, LAZY_NONE),
12872 OPRINIT("in", 55, 2, JimExprOpStrBin, LAZY_NONE),
12873 OPRINIT("ni", 55, 2, JimExprOpStrBin, LAZY_NONE),
12875 OPRINIT("!", 150, 1, JimExprOpNumUnary, LAZY_NONE),
12876 OPRINIT("~", 150, 1, JimExprOpIntUnary, LAZY_NONE),
12877 OPRINIT(NULL, 150, 1, JimExprOpNumUnary, LAZY_NONE),
12878 OPRINIT(NULL, 150, 1, JimExprOpNumUnary, LAZY_NONE),
12882 OPRINIT("int", 200, 1, JimExprOpNumUnary, LAZY_NONE),
12883 OPRINIT("abs", 200, 1, JimExprOpNumUnary, LAZY_NONE),
12884 OPRINIT("double", 200, 1, JimExprOpNumUnary, LAZY_NONE),
12885 OPRINIT("round", 200, 1, JimExprOpNumUnary, LAZY_NONE),
12886 OPRINIT("rand", 200, 0, JimExprOpNone, LAZY_NONE),
12887 OPRINIT("srand", 200, 1, JimExprOpIntUnary, LAZY_NONE),
12889 #ifdef JIM_MATH_FUNCTIONS
12890 OPRINIT("sin", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12891 OPRINIT("cos", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12892 OPRINIT("tan", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12893 OPRINIT("asin", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12894 OPRINIT("acos", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12895 OPRINIT("atan", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12896 OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12897 OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12898 OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12899 OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12900 OPRINIT("floor", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12901 OPRINIT("exp", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12902 OPRINIT("log", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12903 OPRINIT("log10", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12904 OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary, LAZY_NONE),
12905 OPRINIT("pow", 200, 2, JimExprOpBin, LAZY_NONE),
12906 #endif
12908 #undef OPRINIT
12910 #define JIM_EXPR_OPERATORS_NUM \
12911 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
12913 static int JimParseExpression(struct JimParserCtx *pc)
12916 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
12917 if (*pc->p == '\n') {
12918 pc->linenr++;
12920 pc->p++;
12921 pc->len--;
12924 if (pc->len == 0) {
12925 pc->tstart = pc->tend = pc->p;
12926 pc->tline = pc->linenr;
12927 pc->tt = JIM_TT_EOL;
12928 pc->eof = 1;
12929 return JIM_OK;
12931 switch (*(pc->p)) {
12932 case '(':
12933 pc->tt = JIM_TT_SUBEXPR_START;
12934 goto singlechar;
12935 case ')':
12936 pc->tt = JIM_TT_SUBEXPR_END;
12937 goto singlechar;
12938 case ',':
12939 pc->tt = JIM_TT_SUBEXPR_COMMA;
12940 singlechar:
12941 pc->tstart = pc->tend = pc->p;
12942 pc->tline = pc->linenr;
12943 pc->p++;
12944 pc->len--;
12945 break;
12946 case '[':
12947 return JimParseCmd(pc);
12948 case '$':
12949 if (JimParseVar(pc) == JIM_ERR)
12950 return JimParseExprOperator(pc);
12951 else {
12953 if (pc->tt == JIM_TT_EXPRSUGAR) {
12954 return JIM_ERR;
12956 return JIM_OK;
12958 break;
12959 case '0':
12960 case '1':
12961 case '2':
12962 case '3':
12963 case '4':
12964 case '5':
12965 case '6':
12966 case '7':
12967 case '8':
12968 case '9':
12969 case '.':
12970 return JimParseExprNumber(pc);
12971 case '"':
12972 return JimParseQuote(pc);
12973 case '{':
12974 return JimParseBrace(pc);
12976 case 'N':
12977 case 'I':
12978 case 'n':
12979 case 'i':
12980 if (JimParseExprIrrational(pc) == JIM_ERR)
12981 return JimParseExprOperator(pc);
12982 break;
12983 default:
12984 return JimParseExprOperator(pc);
12985 break;
12987 return JIM_OK;
12990 static int JimParseExprNumber(struct JimParserCtx *pc)
12992 int allowdot = 1;
12993 int base = 10;
12996 pc->tt = JIM_TT_EXPR_INT;
12997 pc->tstart = pc->p;
12998 pc->tline = pc->linenr;
13001 if (pc->p[0] == '0') {
13002 switch (pc->p[1]) {
13003 case 'x':
13004 case 'X':
13005 base = 16;
13006 allowdot = 0;
13007 pc->p += 2;
13008 pc->len -= 2;
13009 break;
13010 case 'o':
13011 case 'O':
13012 base = 8;
13013 allowdot = 0;
13014 pc->p += 2;
13015 pc->len -= 2;
13016 break;
13017 case 'b':
13018 case 'B':
13019 base = 2;
13020 allowdot = 0;
13021 pc->p += 2;
13022 pc->len -= 2;
13023 break;
13027 while (isdigit(UCHAR(*pc->p))
13028 || (base == 16 && isxdigit(UCHAR(*pc->p)))
13029 || (base == 8 && *pc->p >= '0' && *pc->p <= '7')
13030 || (base == 2 && (*pc->p == '0' || *pc->p == '1'))
13031 || (allowdot && *pc->p == '.')
13033 if (*pc->p == '.') {
13034 allowdot = 0;
13035 pc->tt = JIM_TT_EXPR_DOUBLE;
13037 pc->p++;
13038 pc->len--;
13039 if (base == 10 && (*pc->p == 'e' || *pc->p == 'E') && (pc->p[1] == '-' || pc->p[1] == '+'
13040 || isdigit(UCHAR(pc->p[1])))) {
13041 pc->p += 2;
13042 pc->len -= 2;
13043 pc->tt = JIM_TT_EXPR_DOUBLE;
13046 pc->tend = pc->p - 1;
13047 return JIM_OK;
13050 static int JimParseExprIrrational(struct JimParserCtx *pc)
13052 const char *Tokens[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
13053 const char **token;
13055 for (token = Tokens; *token != NULL; token++) {
13056 int len = strlen(*token);
13058 if (strncmp(*token, pc->p, len) == 0) {
13059 pc->tstart = pc->p;
13060 pc->tend = pc->p + len - 1;
13061 pc->p += len;
13062 pc->len -= len;
13063 pc->tline = pc->linenr;
13064 pc->tt = JIM_TT_EXPR_DOUBLE;
13065 return JIM_OK;
13068 return JIM_ERR;
13071 static int JimParseExprOperator(struct JimParserCtx *pc)
13073 int i;
13074 int bestIdx = -1, bestLen = 0;
13077 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
13078 const char * const opname = Jim_ExprOperators[i].name;
13079 const int oplen = Jim_ExprOperators[i].namelen;
13081 if (opname == NULL || opname[0] != pc->p[0]) {
13082 continue;
13085 if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
13086 bestIdx = i + JIM_TT_EXPR_OP;
13087 bestLen = oplen;
13090 if (bestIdx == -1) {
13091 return JIM_ERR;
13095 if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {
13096 const char *p = pc->p + bestLen;
13097 int len = pc->len - bestLen;
13099 while (len && isspace(UCHAR(*p))) {
13100 len--;
13101 p++;
13103 if (*p != '(') {
13104 return JIM_ERR;
13107 pc->tstart = pc->p;
13108 pc->tend = pc->p + bestLen - 1;
13109 pc->p += bestLen;
13110 pc->len -= bestLen;
13111 pc->tline = pc->linenr;
13113 pc->tt = bestIdx;
13114 return JIM_OK;
13117 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
13119 static Jim_ExprOperator dummy_op;
13120 if (opcode < JIM_TT_EXPR_OP) {
13121 return &dummy_op;
13123 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
13126 const char *jim_tt_name(int type)
13128 static const char * const tt_names[JIM_TT_EXPR_OP] =
13129 { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
13130 "DBL", "$()" };
13131 if (type < JIM_TT_EXPR_OP) {
13132 return tt_names[type];
13134 else {
13135 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
13136 static char buf[20];
13138 if (op->name) {
13139 return op->name;
13141 sprintf(buf, "(%d)", type);
13142 return buf;
13146 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13147 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13148 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13150 static const Jim_ObjType exprObjType = {
13151 "expression",
13152 FreeExprInternalRep,
13153 DupExprInternalRep,
13154 NULL,
13155 JIM_TYPE_REFERENCES,
13159 typedef struct ExprByteCode
13161 ScriptToken *token;
13162 int len;
13163 int inUse;
13164 } ExprByteCode;
13166 static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
13168 int i;
13170 for (i = 0; i < expr->len; i++) {
13171 Jim_DecrRefCount(interp, expr->token[i].objPtr);
13173 Jim_Free(expr->token);
13174 Jim_Free(expr);
13177 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13179 ExprByteCode *expr = (void *)objPtr->internalRep.ptr;
13181 if (expr) {
13182 if (--expr->inUse != 0) {
13183 return;
13186 ExprFreeByteCode(interp, expr);
13190 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13192 JIM_NOTUSED(interp);
13193 JIM_NOTUSED(srcPtr);
13196 dupPtr->typePtr = NULL;
13200 static int ExprCheckCorrectness(ExprByteCode * expr)
13202 int i;
13203 int stacklen = 0;
13204 int ternary = 0;
13206 for (i = 0; i < expr->len; i++) {
13207 ScriptToken *t = &expr->token[i];
13208 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13210 stacklen -= op->arity;
13211 if (stacklen < 0) {
13212 break;
13214 if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
13215 ternary++;
13217 else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
13218 ternary--;
13222 stacklen++;
13224 if (stacklen != 1 || ternary != 0) {
13225 return JIM_ERR;
13227 return JIM_OK;
13230 static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13232 int i;
13234 int leftindex, arity, offset;
13237 leftindex = expr->len - 1;
13239 arity = 1;
13240 while (arity) {
13241 ScriptToken *tt = &expr->token[leftindex];
13243 if (tt->type >= JIM_TT_EXPR_OP) {
13244 arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
13246 arity--;
13247 if (--leftindex < 0) {
13248 return JIM_ERR;
13251 leftindex++;
13254 memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
13255 sizeof(*expr->token) * (expr->len - leftindex));
13256 expr->len += 2;
13257 offset = (expr->len - leftindex) - 1;
13259 expr->token[leftindex + 1].type = t->type + 1;
13260 expr->token[leftindex + 1].objPtr = interp->emptyObj;
13262 expr->token[leftindex].type = JIM_TT_EXPR_INT;
13263 expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);
13266 expr->token[expr->len].objPtr = interp->emptyObj;
13267 expr->token[expr->len].type = t->type + 2;
13268 expr->len++;
13271 for (i = leftindex - 1; i > 0; i--) {
13272 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
13273 if (op->lazy == LAZY_LEFT) {
13274 if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
13275 JimWideValue(expr->token[i - 1].objPtr) += 2;
13279 return JIM_OK;
13282 static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13284 struct ScriptToken *token = &expr->token[expr->len];
13285 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13287 if (op->lazy == LAZY_OP) {
13288 if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
13289 Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
13290 return JIM_ERR;
13293 else {
13294 token->objPtr = interp->emptyObj;
13295 token->type = t->type;
13296 expr->len++;
13298 return JIM_OK;
13301 static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
13303 int ternary_count = 1;
13305 right_index--;
13307 while (right_index > 1) {
13308 if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
13309 ternary_count--;
13311 else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
13312 ternary_count++;
13314 else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
13315 return right_index;
13317 right_index--;
13321 return -1;
13324 static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
13326 int i = right_index - 1;
13327 int ternary_count = 1;
13329 while (i > 1) {
13330 if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
13331 if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
13332 *prev_right_index = i - 2;
13333 *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
13334 return 1;
13337 else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
13338 if (ternary_count == 0) {
13339 return 0;
13341 ternary_count++;
13343 i--;
13345 return 0;
13348 static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
13350 int i;
13352 for (i = expr->len - 1; i > 1; i--) {
13353 int prev_right_index;
13354 int prev_left_index;
13355 int j;
13356 ScriptToken tmp;
13358 if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
13359 continue;
13363 if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
13364 continue;
13367 tmp = expr->token[prev_right_index];
13368 for (j = prev_right_index; j < i; j++) {
13369 expr->token[j] = expr->token[j + 1];
13371 expr->token[i] = tmp;
13373 JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);
13376 i++;
13380 static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *fileNameObj)
13382 Jim_Stack stack;
13383 ExprByteCode *expr;
13384 int ok = 1;
13385 int i;
13386 int prevtt = JIM_TT_NONE;
13387 int have_ternary = 0;
13390 int count = tokenlist->count - 1;
13392 expr = Jim_Alloc(sizeof(*expr));
13393 expr->inUse = 1;
13394 expr->len = 0;
13396 Jim_InitStack(&stack);
13398 for (i = 0; i < tokenlist->count; i++) {
13399 ParseToken *t = &tokenlist->list[i];
13400 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13402 if (op->lazy == LAZY_OP) {
13403 count += 2;
13405 if (t->type == JIM_EXPROP_TERNARY) {
13406 have_ternary = 1;
13411 expr->token = Jim_Alloc(sizeof(ScriptToken) * count);
13413 for (i = 0; i < tokenlist->count && ok; i++) {
13414 ParseToken *t = &tokenlist->list[i];
13417 struct ScriptToken *token = &expr->token[expr->len];
13419 if (t->type == JIM_TT_EOL) {
13420 break;
13423 switch (t->type) {
13424 case JIM_TT_STR:
13425 case JIM_TT_ESC:
13426 case JIM_TT_VAR:
13427 case JIM_TT_DICTSUGAR:
13428 case JIM_TT_EXPRSUGAR:
13429 case JIM_TT_CMD:
13430 token->type = t->type;
13431 strexpr:
13432 token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
13433 if (t->type == JIM_TT_CMD) {
13435 JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
13437 expr->len++;
13438 break;
13440 case JIM_TT_EXPR_INT:
13441 case JIM_TT_EXPR_DOUBLE:
13443 char *endptr;
13444 if (t->type == JIM_TT_EXPR_INT) {
13445 token->objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
13447 else {
13448 token->objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
13450 if (endptr != t->token + t->len) {
13452 Jim_FreeNewObj(interp, token->objPtr);
13453 token->type = JIM_TT_STR;
13454 goto strexpr;
13456 token->type = t->type;
13457 expr->len++;
13459 break;
13461 case JIM_TT_SUBEXPR_START:
13462 Jim_StackPush(&stack, t);
13463 prevtt = JIM_TT_NONE;
13464 continue;
13466 case JIM_TT_SUBEXPR_COMMA:
13468 continue;
13470 case JIM_TT_SUBEXPR_END:
13471 ok = 0;
13472 while (Jim_StackLen(&stack)) {
13473 ParseToken *tt = Jim_StackPop(&stack);
13475 if (tt->type == JIM_TT_SUBEXPR_START) {
13476 ok = 1;
13477 break;
13480 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13481 goto err;
13484 if (!ok) {
13485 Jim_SetResultString(interp, "Unexpected close parenthesis", -1);
13486 goto err;
13488 break;
13491 default:{
13493 const struct Jim_ExprOperator *op;
13494 ParseToken *tt;
13497 if (prevtt == JIM_TT_NONE || prevtt >= JIM_TT_EXPR_OP) {
13498 if (t->type == JIM_EXPROP_SUB) {
13499 t->type = JIM_EXPROP_UNARYMINUS;
13501 else if (t->type == JIM_EXPROP_ADD) {
13502 t->type = JIM_EXPROP_UNARYPLUS;
13506 op = JimExprOperatorInfoByOpcode(t->type);
13509 while ((tt = Jim_StackPeek(&stack)) != NULL) {
13510 const struct Jim_ExprOperator *tt_op =
13511 JimExprOperatorInfoByOpcode(tt->type);
13515 if (op->arity != 1 && tt_op->precedence >= op->precedence) {
13516 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13517 ok = 0;
13518 goto err;
13520 Jim_StackPop(&stack);
13522 else {
13523 break;
13526 Jim_StackPush(&stack, t);
13527 break;
13530 prevtt = t->type;
13534 while (Jim_StackLen(&stack)) {
13535 ParseToken *tt = Jim_StackPop(&stack);
13537 if (tt->type == JIM_TT_SUBEXPR_START) {
13538 ok = 0;
13539 Jim_SetResultString(interp, "Missing close parenthesis", -1);
13540 goto err;
13542 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13543 ok = 0;
13544 goto err;
13548 if (have_ternary) {
13549 ExprTernaryReorderExpression(interp, expr);
13552 err:
13554 Jim_FreeStack(&stack);
13556 for (i = 0; i < expr->len; i++) {
13557 Jim_IncrRefCount(expr->token[i].objPtr);
13560 if (!ok) {
13561 ExprFreeByteCode(interp, expr);
13562 return NULL;
13565 return expr;
13569 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13571 int exprTextLen;
13572 const char *exprText;
13573 struct JimParserCtx parser;
13574 struct ExprByteCode *expr;
13575 ParseTokenList tokenlist;
13576 int line;
13577 Jim_Obj *fileNameObj;
13578 int rc = JIM_ERR;
13581 if (objPtr->typePtr == &sourceObjType) {
13582 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
13583 line = objPtr->internalRep.sourceValue.lineNumber;
13585 else {
13586 fileNameObj = interp->emptyObj;
13587 line = 1;
13589 Jim_IncrRefCount(fileNameObj);
13591 exprText = Jim_GetString(objPtr, &exprTextLen);
13594 ScriptTokenListInit(&tokenlist);
13596 JimParserInit(&parser, exprText, exprTextLen, line);
13597 while (!parser.eof) {
13598 if (JimParseExpression(&parser) != JIM_OK) {
13599 ScriptTokenListFree(&tokenlist);
13600 invalidexpr:
13601 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
13602 expr = NULL;
13603 goto err;
13606 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
13607 parser.tline);
13610 #ifdef DEBUG_SHOW_EXPR_TOKENS
13612 int i;
13613 printf("==== Expr Tokens ====\n");
13614 for (i = 0; i < tokenlist.count; i++) {
13615 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
13616 tokenlist.list[i].len, tokenlist.list[i].token);
13619 #endif
13622 expr = ExprCreateByteCode(interp, &tokenlist, fileNameObj);
13625 ScriptTokenListFree(&tokenlist);
13627 if (!expr) {
13628 goto err;
13631 #ifdef DEBUG_SHOW_EXPR
13633 int i;
13635 printf("==== Expr ====\n");
13636 for (i = 0; i < expr->len; i++) {
13637 ScriptToken *t = &expr->token[i];
13639 printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
13642 #endif
13645 if (ExprCheckCorrectness(expr) != JIM_OK) {
13646 ExprFreeByteCode(interp, expr);
13647 goto invalidexpr;
13650 rc = JIM_OK;
13652 err:
13654 Jim_DecrRefCount(interp, fileNameObj);
13655 Jim_FreeIntRep(interp, objPtr);
13656 Jim_SetIntRepPtr(objPtr, expr);
13657 objPtr->typePtr = &exprObjType;
13658 return rc;
13661 static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
13663 if (objPtr->typePtr != &exprObjType) {
13664 if (SetExprFromAny(interp, objPtr) != JIM_OK) {
13665 return NULL;
13668 return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
13671 #define JIM_EE_STATICSTACK_LEN 10
13673 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
13675 ExprByteCode *expr;
13676 Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
13677 int i;
13678 int retcode = JIM_OK;
13679 struct JimExprState e;
13681 expr = JimGetExpression(interp, exprObjPtr);
13682 if (!expr) {
13683 return JIM_ERR;
13686 #ifdef JIM_OPTIMIZATION
13688 Jim_Obj *objPtr;
13691 switch (expr->len) {
13692 case 1:
13693 if (expr->token[0].type == JIM_TT_EXPR_INT) {
13694 *exprResultPtrPtr = expr->token[0].objPtr;
13695 Jim_IncrRefCount(*exprResultPtrPtr);
13696 return JIM_OK;
13698 if (expr->token[0].type == JIM_TT_VAR) {
13699 objPtr = Jim_GetVariable(interp, expr->token[0].objPtr, JIM_ERRMSG);
13700 if (objPtr) {
13701 *exprResultPtrPtr = objPtr;
13702 Jim_IncrRefCount(*exprResultPtrPtr);
13703 return JIM_OK;
13706 break;
13708 case 2:
13709 if (expr->token[1].type == JIM_EXPROP_NOT && expr->token[0].type == JIM_TT_VAR) {
13710 jim_wide wideValue;
13712 objPtr = Jim_GetVariable(interp, expr->token[0].objPtr, JIM_NONE);
13713 if (objPtr && JimIsWide(objPtr)
13714 && Jim_GetWide(interp, objPtr, &wideValue) == JIM_OK) {
13715 *exprResultPtrPtr = wideValue ? interp->falseObj : interp->trueObj;
13716 Jim_IncrRefCount(*exprResultPtrPtr);
13717 return JIM_OK;
13720 break;
13722 case 3:
13723 if (expr->token[0].type == JIM_TT_VAR && (expr->token[1].type == JIM_TT_EXPR_INT
13724 || expr->token[1].type == JIM_TT_VAR)) {
13725 switch (expr->token[2].type) {
13726 case JIM_EXPROP_LT:
13727 case JIM_EXPROP_LTE:
13728 case JIM_EXPROP_GT:
13729 case JIM_EXPROP_GTE:
13730 case JIM_EXPROP_NUMEQ:
13731 case JIM_EXPROP_NUMNE:{
13733 jim_wide wideValueA;
13734 jim_wide wideValueB;
13736 objPtr = Jim_GetVariable(interp, expr->token[0].objPtr, JIM_NONE);
13737 if (objPtr && JimIsWide(objPtr)
13738 && Jim_GetWide(interp, objPtr, &wideValueA) == JIM_OK) {
13739 if (expr->token[1].type == JIM_TT_VAR) {
13740 objPtr =
13741 Jim_GetVariable(interp, expr->token[1].objPtr,
13742 JIM_NONE);
13744 else {
13745 objPtr = expr->token[1].objPtr;
13747 if (objPtr && JimIsWide(objPtr)
13748 && Jim_GetWide(interp, objPtr, &wideValueB) == JIM_OK) {
13749 int cmpRes;
13751 switch (expr->token[2].type) {
13752 case JIM_EXPROP_LT:
13753 cmpRes = wideValueA < wideValueB;
13754 break;
13755 case JIM_EXPROP_LTE:
13756 cmpRes = wideValueA <= wideValueB;
13757 break;
13758 case JIM_EXPROP_GT:
13759 cmpRes = wideValueA > wideValueB;
13760 break;
13761 case JIM_EXPROP_GTE:
13762 cmpRes = wideValueA >= wideValueB;
13763 break;
13764 case JIM_EXPROP_NUMEQ:
13765 cmpRes = wideValueA == wideValueB;
13766 break;
13767 case JIM_EXPROP_NUMNE:
13768 cmpRes = wideValueA != wideValueB;
13769 break;
13770 default:
13771 cmpRes = 0;
13773 *exprResultPtrPtr =
13774 cmpRes ? interp->trueObj : interp->falseObj;
13775 Jim_IncrRefCount(*exprResultPtrPtr);
13776 return JIM_OK;
13782 break;
13785 #endif
13787 expr->inUse++;
13791 if (expr->len > JIM_EE_STATICSTACK_LEN)
13792 e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
13793 else
13794 e.stack = staticStack;
13796 e.stacklen = 0;
13799 for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
13800 Jim_Obj *objPtr;
13802 switch (expr->token[i].type) {
13803 case JIM_TT_EXPR_INT:
13804 case JIM_TT_EXPR_DOUBLE:
13805 case JIM_TT_STR:
13806 ExprPush(&e, expr->token[i].objPtr);
13807 break;
13809 case JIM_TT_VAR:
13810 objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
13811 if (objPtr) {
13812 ExprPush(&e, objPtr);
13814 else {
13815 retcode = JIM_ERR;
13817 break;
13819 case JIM_TT_DICTSUGAR:
13820 objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
13821 if (objPtr) {
13822 ExprPush(&e, objPtr);
13824 else {
13825 retcode = JIM_ERR;
13827 break;
13829 case JIM_TT_ESC:
13830 retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
13831 if (retcode == JIM_OK) {
13832 ExprPush(&e, objPtr);
13834 break;
13836 case JIM_TT_CMD:
13837 retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
13838 if (retcode == JIM_OK) {
13839 ExprPush(&e, Jim_GetResult(interp));
13841 break;
13843 default:{
13845 e.skip = 0;
13846 e.opcode = expr->token[i].type;
13848 retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
13850 i += e.skip;
13851 continue;
13856 expr->inUse--;
13858 if (retcode == JIM_OK) {
13859 *exprResultPtrPtr = ExprPop(&e);
13861 else {
13862 for (i = 0; i < e.stacklen; i++) {
13863 Jim_DecrRefCount(interp, e.stack[i]);
13866 if (e.stack != staticStack) {
13867 Jim_Free(e.stack);
13869 return retcode;
13872 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
13874 int retcode;
13875 jim_wide wideValue;
13876 double doubleValue;
13877 Jim_Obj *exprResultPtr;
13879 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
13880 if (retcode != JIM_OK)
13881 return retcode;
13883 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
13884 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) {
13885 Jim_DecrRefCount(interp, exprResultPtr);
13886 return JIM_ERR;
13888 else {
13889 Jim_DecrRefCount(interp, exprResultPtr);
13890 *boolPtr = doubleValue != 0;
13891 return JIM_OK;
13894 *boolPtr = wideValue != 0;
13896 Jim_DecrRefCount(interp, exprResultPtr);
13897 return JIM_OK;
13903 typedef struct ScanFmtPartDescr
13905 char *arg;
13906 char *prefix;
13907 size_t width;
13908 int pos;
13909 char type;
13910 char modifier;
13911 } ScanFmtPartDescr;
13914 typedef struct ScanFmtStringObj
13916 jim_wide size;
13917 char *stringRep;
13918 size_t count;
13919 size_t convCount;
13920 size_t maxPos;
13921 const char *error;
13922 char *scratch;
13923 ScanFmtPartDescr descr[1];
13924 } ScanFmtStringObj;
13927 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13928 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13929 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
13931 static const Jim_ObjType scanFmtStringObjType = {
13932 "scanformatstring",
13933 FreeScanFmtInternalRep,
13934 DupScanFmtInternalRep,
13935 UpdateStringOfScanFmt,
13936 JIM_TYPE_NONE,
13939 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13941 JIM_NOTUSED(interp);
13942 Jim_Free((char *)objPtr->internalRep.ptr);
13943 objPtr->internalRep.ptr = 0;
13946 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13948 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
13949 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
13951 JIM_NOTUSED(interp);
13952 memcpy(newVec, srcPtr->internalRep.ptr, size);
13953 dupPtr->internalRep.ptr = newVec;
13954 dupPtr->typePtr = &scanFmtStringObjType;
13957 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
13959 char *bytes = ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep;
13961 objPtr->bytes = Jim_StrDup(bytes);
13962 objPtr->length = strlen(bytes);
13966 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
13968 ScanFmtStringObj *fmtObj;
13969 char *buffer;
13970 int maxCount, i, approxSize, lastPos = -1;
13971 const char *fmt = objPtr->bytes;
13972 int maxFmtLen = objPtr->length;
13973 const char *fmtEnd = fmt + maxFmtLen;
13974 int curr;
13976 Jim_FreeIntRep(interp, objPtr);
13978 for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
13979 if (fmt[i] == '%')
13980 ++maxCount;
13982 approxSize = sizeof(ScanFmtStringObj)
13983 +(maxCount + 1) * sizeof(ScanFmtPartDescr)
13984 +maxFmtLen * sizeof(char) + 3 + 1
13985 + maxFmtLen * sizeof(char) + 1
13986 + maxFmtLen * sizeof(char)
13987 +(maxCount + 1) * sizeof(char)
13988 +1;
13989 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
13990 memset(fmtObj, 0, approxSize);
13991 fmtObj->size = approxSize;
13992 fmtObj->maxPos = 0;
13993 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
13994 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
13995 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
13996 buffer = fmtObj->stringRep + maxFmtLen + 1;
13997 objPtr->internalRep.ptr = fmtObj;
13998 objPtr->typePtr = &scanFmtStringObjType;
13999 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
14000 int width = 0, skip;
14001 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
14003 fmtObj->count++;
14004 descr->width = 0;
14006 if (*fmt != '%' || fmt[1] == '%') {
14007 descr->type = 0;
14008 descr->prefix = &buffer[i];
14009 for (; fmt < fmtEnd; ++fmt) {
14010 if (*fmt == '%') {
14011 if (fmt[1] != '%')
14012 break;
14013 ++fmt;
14015 buffer[i++] = *fmt;
14017 buffer[i++] = 0;
14020 ++fmt;
14022 if (fmt >= fmtEnd)
14023 goto done;
14024 descr->pos = 0;
14025 if (*fmt == '*') {
14026 descr->pos = -1;
14027 ++fmt;
14029 else
14030 fmtObj->convCount++;
14032 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14033 fmt += skip;
14035 if (descr->pos != -1 && *fmt == '$') {
14036 int prev;
14038 ++fmt;
14039 descr->pos = width;
14040 width = 0;
14042 if ((lastPos == 0 && descr->pos > 0)
14043 || (lastPos > 0 && descr->pos == 0)) {
14044 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
14045 return JIM_ERR;
14048 for (prev = 0; prev < curr; ++prev) {
14049 if (fmtObj->descr[prev].pos == -1)
14050 continue;
14051 if (fmtObj->descr[prev].pos == descr->pos) {
14052 fmtObj->error =
14053 "variable is assigned by multiple \"%n$\" conversion specifiers";
14054 return JIM_ERR;
14058 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14059 descr->width = width;
14060 fmt += skip;
14062 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
14063 fmtObj->maxPos = descr->pos;
14065 else {
14067 descr->width = width;
14071 if (lastPos == -1)
14072 lastPos = descr->pos;
14074 if (*fmt == '[') {
14075 int swapped = 1, beg = i, end, j;
14077 descr->type = '[';
14078 descr->arg = &buffer[i];
14079 ++fmt;
14080 if (*fmt == '^')
14081 buffer[i++] = *fmt++;
14082 if (*fmt == ']')
14083 buffer[i++] = *fmt++;
14084 while (*fmt && *fmt != ']')
14085 buffer[i++] = *fmt++;
14086 if (*fmt != ']') {
14087 fmtObj->error = "unmatched [ in format string";
14088 return JIM_ERR;
14090 end = i;
14091 buffer[i++] = 0;
14093 while (swapped) {
14094 swapped = 0;
14095 for (j = beg + 1; j < end - 1; ++j) {
14096 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
14097 char tmp = buffer[j - 1];
14099 buffer[j - 1] = buffer[j + 1];
14100 buffer[j + 1] = tmp;
14101 swapped = 1;
14106 else {
14108 if (strchr("hlL", *fmt) != 0)
14109 descr->modifier = tolower((int)*fmt++);
14111 descr->type = *fmt;
14112 if (strchr("efgcsndoxui", *fmt) == 0) {
14113 fmtObj->error = "bad scan conversion character";
14114 return JIM_ERR;
14116 else if (*fmt == 'c' && descr->width != 0) {
14117 fmtObj->error = "field width may not be specified in %c " "conversion";
14118 return JIM_ERR;
14120 else if (*fmt == 'u' && descr->modifier == 'l') {
14121 fmtObj->error = "unsigned wide not supported";
14122 return JIM_ERR;
14125 curr++;
14127 done:
14128 return JIM_OK;
14133 #define FormatGetCnvCount(_fo_) \
14134 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
14135 #define FormatGetMaxPos(_fo_) \
14136 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
14137 #define FormatGetError(_fo_) \
14138 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
14140 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
14142 char *buffer = Jim_StrDup(str);
14143 char *p = buffer;
14145 while (*str) {
14146 int c;
14147 int n;
14149 if (!sdescr && isspace(UCHAR(*str)))
14150 break;
14152 n = utf8_tounicode(str, &c);
14153 if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
14154 break;
14155 while (n--)
14156 *p++ = *str++;
14158 *p = 0;
14159 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
14163 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen,
14164 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
14166 const char *tok;
14167 const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
14168 size_t scanned = 0;
14169 size_t anchor = pos;
14170 int i;
14171 Jim_Obj *tmpObj = NULL;
14174 *valObjPtr = 0;
14175 if (descr->prefix) {
14177 for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
14179 if (isspace(UCHAR(descr->prefix[i])))
14180 while (pos < strLen && isspace(UCHAR(str[pos])))
14181 ++pos;
14182 else if (descr->prefix[i] != str[pos])
14183 break;
14184 else
14185 ++pos;
14187 if (pos >= strLen) {
14188 return -1;
14190 else if (descr->prefix[i] != 0)
14191 return 0;
14194 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
14195 while (isspace(UCHAR(str[pos])))
14196 ++pos;
14198 scanned = pos - anchor;
14201 if (descr->type == 'n') {
14203 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
14205 else if (pos >= strLen) {
14207 return -1;
14209 else if (descr->type == 'c') {
14210 int c;
14211 scanned += utf8_tounicode(&str[pos], &c);
14212 *valObjPtr = Jim_NewIntObj(interp, c);
14213 return scanned;
14215 else {
14217 if (descr->width > 0) {
14218 size_t sLen = utf8_strlen(&str[pos], strLen - pos);
14219 size_t tLen = descr->width > sLen ? sLen : descr->width;
14221 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
14222 tok = tmpObj->bytes;
14224 else {
14226 tok = &str[pos];
14228 switch (descr->type) {
14229 case 'd':
14230 case 'o':
14231 case 'x':
14232 case 'u':
14233 case 'i':{
14234 char *endp;
14235 jim_wide w;
14237 int base = descr->type == 'o' ? 8
14238 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
14241 if (base == 0) {
14242 w = jim_strtoull(tok, &endp);
14244 else {
14245 w = strtoull(tok, &endp, base);
14248 if (endp != tok) {
14250 *valObjPtr = Jim_NewIntObj(interp, w);
14253 scanned += endp - tok;
14255 else {
14256 scanned = *tok ? 0 : -1;
14258 break;
14260 case 's':
14261 case '[':{
14262 *valObjPtr = JimScanAString(interp, descr->arg, tok);
14263 scanned += Jim_Length(*valObjPtr);
14264 break;
14266 case 'e':
14267 case 'f':
14268 case 'g':{
14269 char *endp;
14270 double value = strtod(tok, &endp);
14272 if (endp != tok) {
14274 *valObjPtr = Jim_NewDoubleObj(interp, value);
14276 scanned += endp - tok;
14278 else {
14279 scanned = *tok ? 0 : -1;
14281 break;
14284 if (tmpObj) {
14285 Jim_FreeNewObj(interp, tmpObj);
14288 return scanned;
14292 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
14294 size_t i, pos;
14295 int scanned = 1;
14296 const char *str = Jim_String(strObjPtr);
14297 int strLen = Jim_Utf8Length(interp, strObjPtr);
14298 Jim_Obj *resultList = 0;
14299 Jim_Obj **resultVec = 0;
14300 int resultc;
14301 Jim_Obj *emptyStr = 0;
14302 ScanFmtStringObj *fmtObj;
14305 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
14307 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
14309 if (fmtObj->error != 0) {
14310 if (flags & JIM_ERRMSG)
14311 Jim_SetResultString(interp, fmtObj->error, -1);
14312 return 0;
14315 emptyStr = Jim_NewEmptyStringObj(interp);
14316 Jim_IncrRefCount(emptyStr);
14318 resultList = Jim_NewListObj(interp, NULL, 0);
14319 if (fmtObj->maxPos > 0) {
14320 for (i = 0; i < fmtObj->maxPos; ++i)
14321 Jim_ListAppendElement(interp, resultList, emptyStr);
14322 JimListGetElements(interp, resultList, &resultc, &resultVec);
14325 for (i = 0, pos = 0; i < fmtObj->count; ++i) {
14326 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
14327 Jim_Obj *value = 0;
14330 if (descr->type == 0)
14331 continue;
14333 if (scanned > 0)
14334 scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
14336 if (scanned == -1 && i == 0)
14337 goto eof;
14339 pos += scanned;
14342 if (value == 0)
14343 value = Jim_NewEmptyStringObj(interp);
14345 if (descr->pos == -1) {
14346 Jim_FreeNewObj(interp, value);
14348 else if (descr->pos == 0)
14350 Jim_ListAppendElement(interp, resultList, value);
14351 else if (resultVec[descr->pos - 1] == emptyStr) {
14353 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
14354 Jim_IncrRefCount(value);
14355 resultVec[descr->pos - 1] = value;
14357 else {
14359 Jim_FreeNewObj(interp, value);
14360 goto err;
14363 Jim_DecrRefCount(interp, emptyStr);
14364 return resultList;
14365 eof:
14366 Jim_DecrRefCount(interp, emptyStr);
14367 Jim_FreeNewObj(interp, resultList);
14368 return (Jim_Obj *)EOF;
14369 err:
14370 Jim_DecrRefCount(interp, emptyStr);
14371 Jim_FreeNewObj(interp, resultList);
14372 return 0;
14376 static void JimPrngInit(Jim_Interp *interp)
14378 #define PRNG_SEED_SIZE 256
14379 int i;
14380 unsigned int *seed;
14381 time_t t = time(NULL);
14383 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
14385 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
14386 for (i = 0; i < PRNG_SEED_SIZE; i++) {
14387 seed[i] = (rand() ^ t ^ clock());
14389 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
14390 Jim_Free(seed);
14394 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
14396 Jim_PrngState *prng;
14397 unsigned char *destByte = (unsigned char *)dest;
14398 unsigned int si, sj, x;
14401 if (interp->prngState == NULL)
14402 JimPrngInit(interp);
14403 prng = interp->prngState;
14405 for (x = 0; x < len; x++) {
14406 prng->i = (prng->i + 1) & 0xff;
14407 si = prng->sbox[prng->i];
14408 prng->j = (prng->j + si) & 0xff;
14409 sj = prng->sbox[prng->j];
14410 prng->sbox[prng->i] = sj;
14411 prng->sbox[prng->j] = si;
14412 *destByte++ = prng->sbox[(si + sj) & 0xff];
14417 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
14419 int i;
14420 Jim_PrngState *prng;
14423 if (interp->prngState == NULL)
14424 JimPrngInit(interp);
14425 prng = interp->prngState;
14428 for (i = 0; i < 256; i++)
14429 prng->sbox[i] = i;
14431 for (i = 0; i < seedLen; i++) {
14432 unsigned char t;
14434 t = prng->sbox[i & 0xFF];
14435 prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
14436 prng->sbox[seed[i]] = t;
14438 prng->i = prng->j = 0;
14440 for (i = 0; i < 256; i += seedLen) {
14441 JimRandomBytes(interp, seed, seedLen);
14446 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14448 jim_wide wideValue, increment = 1;
14449 Jim_Obj *intObjPtr;
14451 if (argc != 2 && argc != 3) {
14452 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
14453 return JIM_ERR;
14455 if (argc == 3) {
14456 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
14457 return JIM_ERR;
14459 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
14460 if (!intObjPtr) {
14462 wideValue = 0;
14464 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
14465 return JIM_ERR;
14467 if (!intObjPtr || Jim_IsShared(intObjPtr)) {
14468 intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
14469 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
14470 Jim_FreeNewObj(interp, intObjPtr);
14471 return JIM_ERR;
14474 else {
14476 Jim_InvalidateStringRep(intObjPtr);
14477 JimWideValue(intObjPtr) = wideValue + increment;
14479 if (argv[1]->typePtr != &variableObjType) {
14481 Jim_SetVariable(interp, argv[1], intObjPtr);
14484 Jim_SetResult(interp, intObjPtr);
14485 return JIM_OK;
14489 #define JIM_EVAL_SARGV_LEN 8
14490 #define JIM_EVAL_SINTV_LEN 8
14493 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14495 int retcode;
14497 if (interp->unknown_called > 50) {
14498 return JIM_ERR;
14503 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
14504 return JIM_ERR;
14506 interp->unknown_called++;
14508 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
14509 interp->unknown_called--;
14511 return retcode;
14514 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14516 int retcode;
14517 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
14519 if (cmdPtr == NULL) {
14520 return JimUnknown(interp, objc, objv);
14522 if (interp->evalDepth == interp->maxEvalDepth) {
14523 Jim_SetResultString(interp, "Infinite eval recursion", -1);
14524 return JIM_ERR;
14526 interp->evalDepth++;
14529 JimIncrCmdRefCount(cmdPtr);
14530 Jim_SetEmptyResult(interp);
14531 if (cmdPtr->isproc) {
14532 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
14534 else {
14535 interp->cmdPrivData = cmdPtr->u.native.privData;
14536 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
14538 JimDecrCmdRefCount(interp, cmdPtr);
14539 interp->evalDepth--;
14541 return retcode;
14544 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14546 int i, retcode;
14549 for (i = 0; i < objc; i++)
14550 Jim_IncrRefCount(objv[i]);
14552 retcode = JimInvokeCommand(interp, objc, objv);
14555 for (i = 0; i < objc; i++)
14556 Jim_DecrRefCount(interp, objv[i]);
14558 return retcode;
14561 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14563 int ret;
14564 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
14566 nargv[0] = prefix;
14567 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
14568 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14569 Jim_Free(nargv);
14570 return ret;
14573 static void JimAddErrorToStack(Jim_Interp *interp, int retcode, ScriptObj *script)
14575 int rc = retcode;
14577 if (rc == JIM_ERR && !interp->errorFlag) {
14579 interp->errorFlag = 1;
14580 Jim_IncrRefCount(script->fileNameObj);
14581 Jim_DecrRefCount(interp, interp->errorFileNameObj);
14582 interp->errorFileNameObj = script->fileNameObj;
14583 interp->errorLine = script->linenr;
14585 JimResetStackTrace(interp);
14587 interp->addStackTrace++;
14591 if (rc == JIM_ERR && interp->addStackTrace > 0) {
14594 JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14596 if (Jim_Length(script->fileNameObj)) {
14597 interp->addStackTrace = 0;
14600 Jim_DecrRefCount(interp, interp->errorProc);
14601 interp->errorProc = interp->emptyObj;
14602 Jim_IncrRefCount(interp->errorProc);
14604 else if (rc == JIM_RETURN && interp->returnCode == JIM_ERR) {
14607 else {
14608 interp->addStackTrace = 0;
14612 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14614 Jim_Obj *objPtr;
14616 switch (token->type) {
14617 case JIM_TT_STR:
14618 case JIM_TT_ESC:
14619 objPtr = token->objPtr;
14620 break;
14621 case JIM_TT_VAR:
14622 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
14623 break;
14624 case JIM_TT_DICTSUGAR:
14625 objPtr = JimExpandDictSugar(interp, token->objPtr);
14626 break;
14627 case JIM_TT_EXPRSUGAR:
14628 objPtr = JimExpandExprSugar(interp, token->objPtr);
14629 break;
14630 case JIM_TT_CMD:
14631 switch (Jim_EvalObj(interp, token->objPtr)) {
14632 case JIM_OK:
14633 case JIM_RETURN:
14634 objPtr = interp->result;
14635 break;
14636 case JIM_BREAK:
14638 return JIM_BREAK;
14639 case JIM_CONTINUE:
14641 return JIM_CONTINUE;
14642 default:
14643 return JIM_ERR;
14645 break;
14646 default:
14647 JimPanic((1,
14648 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
14649 objPtr = NULL;
14650 break;
14652 if (objPtr) {
14653 *objPtrPtr = objPtr;
14654 return JIM_OK;
14656 return JIM_ERR;
14659 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
14661 int totlen = 0, i;
14662 Jim_Obj **intv;
14663 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
14664 Jim_Obj *objPtr;
14665 char *s;
14667 if (tokens <= JIM_EVAL_SINTV_LEN)
14668 intv = sintv;
14669 else
14670 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
14672 for (i = 0; i < tokens; i++) {
14673 switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
14674 case JIM_OK:
14675 case JIM_RETURN:
14676 break;
14677 case JIM_BREAK:
14678 if (flags & JIM_SUBST_FLAG) {
14680 tokens = i;
14681 continue;
14685 case JIM_CONTINUE:
14686 if (flags & JIM_SUBST_FLAG) {
14687 intv[i] = NULL;
14688 continue;
14692 default:
14693 while (i--) {
14694 Jim_DecrRefCount(interp, intv[i]);
14696 if (intv != sintv) {
14697 Jim_Free(intv);
14699 return NULL;
14701 Jim_IncrRefCount(intv[i]);
14702 Jim_String(intv[i]);
14703 totlen += intv[i]->length;
14707 if (tokens == 1 && intv[0] && intv == sintv) {
14708 Jim_DecrRefCount(interp, intv[0]);
14709 return intv[0];
14712 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
14714 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
14715 && token[2].type == JIM_TT_VAR) {
14717 objPtr->typePtr = &interpolatedObjType;
14718 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
14719 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
14720 Jim_IncrRefCount(intv[2]);
14723 s = objPtr->bytes = Jim_Alloc(totlen + 1);
14724 objPtr->length = totlen;
14725 for (i = 0; i < tokens; i++) {
14726 if (intv[i]) {
14727 memcpy(s, intv[i]->bytes, intv[i]->length);
14728 s += intv[i]->length;
14729 Jim_DecrRefCount(interp, intv[i]);
14732 objPtr->bytes[totlen] = '\0';
14734 if (intv != sintv) {
14735 Jim_Free(intv);
14738 return objPtr;
14742 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
14744 int retcode = JIM_OK;
14746 if (listPtr->internalRep.listValue.len) {
14747 Jim_IncrRefCount(listPtr);
14748 retcode = JimInvokeCommand(interp,
14749 listPtr->internalRep.listValue.len,
14750 listPtr->internalRep.listValue.ele);
14751 Jim_DecrRefCount(interp, listPtr);
14753 return retcode;
14756 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
14758 SetListFromAny(interp, listPtr);
14759 return JimEvalObjList(interp, listPtr);
14762 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
14764 int i;
14765 ScriptObj *script;
14766 ScriptToken *token;
14767 int retcode = JIM_OK;
14768 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
14769 Jim_Obj *prevScriptObj;
14771 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
14772 return JimEvalObjList(interp, scriptObjPtr);
14775 Jim_IncrRefCount(scriptObjPtr);
14776 script = Jim_GetScript(interp, scriptObjPtr);
14778 Jim_SetEmptyResult(interp);
14780 token = script->token;
14782 #ifdef JIM_OPTIMIZATION
14783 if (script->len == 0) {
14784 Jim_DecrRefCount(interp, scriptObjPtr);
14785 return JIM_OK;
14787 if (script->len == 3
14788 && token[1].objPtr->typePtr == &commandObjType
14789 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
14790 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
14791 && token[2].objPtr->typePtr == &variableObjType) {
14793 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
14795 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
14796 JimWideValue(objPtr)++;
14797 Jim_InvalidateStringRep(objPtr);
14798 Jim_DecrRefCount(interp, scriptObjPtr);
14799 Jim_SetResult(interp, objPtr);
14800 return JIM_OK;
14803 #endif
14805 script->inUse++;
14808 prevScriptObj = interp->currentScriptObj;
14809 interp->currentScriptObj = scriptObjPtr;
14811 interp->errorFlag = 0;
14812 argv = sargv;
14814 for (i = 0; i < script->len && retcode == JIM_OK; ) {
14815 int argc;
14816 int j;
14819 argc = token[i].objPtr->internalRep.scriptLineValue.argc;
14820 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
14823 if (argc > JIM_EVAL_SARGV_LEN)
14824 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
14827 i++;
14829 for (j = 0; j < argc; j++) {
14830 long wordtokens = 1;
14831 int expand = 0;
14832 Jim_Obj *wordObjPtr = NULL;
14834 if (token[i].type == JIM_TT_WORD) {
14835 wordtokens = JimWideValue(token[i++].objPtr);
14836 if (wordtokens < 0) {
14837 expand = 1;
14838 wordtokens = -wordtokens;
14842 if (wordtokens == 1) {
14844 switch (token[i].type) {
14845 case JIM_TT_ESC:
14846 case JIM_TT_STR:
14847 wordObjPtr = token[i].objPtr;
14848 break;
14849 case JIM_TT_VAR:
14850 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
14851 break;
14852 case JIM_TT_EXPRSUGAR:
14853 wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
14854 break;
14855 case JIM_TT_DICTSUGAR:
14856 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
14857 break;
14858 case JIM_TT_CMD:
14859 retcode = Jim_EvalObj(interp, token[i].objPtr);
14860 if (retcode == JIM_OK) {
14861 wordObjPtr = Jim_GetResult(interp);
14863 break;
14864 default:
14865 JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
14868 else {
14869 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
14872 if (!wordObjPtr) {
14873 if (retcode == JIM_OK) {
14874 retcode = JIM_ERR;
14876 break;
14879 Jim_IncrRefCount(wordObjPtr);
14880 i += wordtokens;
14882 if (!expand) {
14883 argv[j] = wordObjPtr;
14885 else {
14887 int len = Jim_ListLength(interp, wordObjPtr);
14888 int newargc = argc + len - 1;
14889 int k;
14891 if (len > 1) {
14892 if (argv == sargv) {
14893 if (newargc > JIM_EVAL_SARGV_LEN) {
14894 argv = Jim_Alloc(sizeof(*argv) * newargc);
14895 memcpy(argv, sargv, sizeof(*argv) * j);
14898 else {
14900 argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
14905 for (k = 0; k < len; k++) {
14906 argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
14907 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
14910 Jim_DecrRefCount(interp, wordObjPtr);
14913 j--;
14914 argc += len - 1;
14918 if (retcode == JIM_OK && argc) {
14920 retcode = JimInvokeCommand(interp, argc, argv);
14922 if (Jim_CheckSignal(interp)) {
14923 retcode = JIM_SIGNAL;
14928 while (j-- > 0) {
14929 Jim_DecrRefCount(interp, argv[j]);
14932 if (argv != sargv) {
14933 Jim_Free(argv);
14934 argv = sargv;
14939 JimAddErrorToStack(interp, retcode, script);
14942 interp->currentScriptObj = prevScriptObj;
14944 Jim_FreeIntRep(interp, scriptObjPtr);
14945 scriptObjPtr->typePtr = &scriptObjType;
14946 Jim_SetIntRepPtr(scriptObjPtr, script);
14947 Jim_DecrRefCount(interp, scriptObjPtr);
14949 return retcode;
14952 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
14954 int retcode;
14956 const char *varname = Jim_String(argNameObj);
14957 if (*varname == '&') {
14959 Jim_Obj *objPtr;
14960 Jim_CallFrame *savedCallFrame = interp->framePtr;
14962 interp->framePtr = interp->framePtr->parent;
14963 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
14964 interp->framePtr = savedCallFrame;
14965 if (!objPtr) {
14966 return JIM_ERR;
14970 objPtr = Jim_NewStringObj(interp, varname + 1, -1);
14971 Jim_IncrRefCount(objPtr);
14972 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
14973 Jim_DecrRefCount(interp, objPtr);
14975 else {
14976 retcode = Jim_SetVariable(interp, argNameObj, argValObj);
14978 return retcode;
14981 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
14984 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
14985 int i;
14987 for (i = 0; i < cmd->u.proc.argListLen; i++) {
14988 Jim_AppendString(interp, argmsg, " ", 1);
14990 if (i == cmd->u.proc.argsPos) {
14991 if (cmd->u.proc.arglist[i].defaultObjPtr) {
14993 Jim_AppendString(interp, argmsg, "?", 1);
14994 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
14995 Jim_AppendString(interp, argmsg, " ...?", -1);
14997 else {
14999 Jim_AppendString(interp, argmsg, "?arg...?", -1);
15002 else {
15003 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15004 Jim_AppendString(interp, argmsg, "?", 1);
15005 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
15006 Jim_AppendString(interp, argmsg, "?", 1);
15008 else {
15009 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
15010 if (*arg == '&') {
15011 arg++;
15013 Jim_AppendString(interp, argmsg, arg, -1);
15017 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
15018 Jim_FreeNewObj(interp, argmsg);
15021 #ifdef jim_ext_namespace
15022 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
15024 Jim_CallFrame *callFramePtr;
15025 int retcode;
15028 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
15029 callFramePtr->argv = &interp->emptyObj;
15030 callFramePtr->argc = 0;
15031 callFramePtr->procArgsObjPtr = NULL;
15032 callFramePtr->procBodyObjPtr = scriptObj;
15033 callFramePtr->staticVars = NULL;
15034 callFramePtr->fileNameObj = interp->emptyObj;
15035 callFramePtr->line = 0;
15036 Jim_IncrRefCount(scriptObj);
15037 interp->framePtr = callFramePtr;
15040 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15041 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15042 retcode = JIM_ERR;
15044 else {
15046 retcode = Jim_EvalObj(interp, scriptObj);
15050 interp->framePtr = interp->framePtr->parent;
15051 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
15052 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
15054 else {
15055 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
15058 return retcode;
15060 #endif
15062 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
15064 Jim_CallFrame *callFramePtr;
15065 int i, d, retcode, optargs;
15066 Jim_Stack *localCommands;
15067 ScriptObj *script;
15070 if (argc - 1 < cmd->u.proc.reqArity ||
15071 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
15072 JimSetProcWrongArgs(interp, argv[0], cmd);
15073 return JIM_ERR;
15076 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
15078 return JIM_OK;
15082 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15083 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15084 return JIM_ERR;
15088 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
15089 callFramePtr->argv = argv;
15090 callFramePtr->argc = argc;
15091 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15092 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15093 callFramePtr->staticVars = cmd->u.proc.staticVars;
15096 script = Jim_GetScript(interp, interp->currentScriptObj);
15097 callFramePtr->fileNameObj = script->fileNameObj;
15098 callFramePtr->line = script->linenr;
15100 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15101 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
15102 interp->framePtr = callFramePtr;
15105 optargs = (argc - 1 - cmd->u.proc.reqArity);
15108 i = 1;
15109 for (d = 0; d < cmd->u.proc.argListLen; d++) {
15110 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
15111 if (d == cmd->u.proc.argsPos) {
15113 Jim_Obj *listObjPtr;
15114 int argsLen = 0;
15115 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
15116 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
15118 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
15121 if (cmd->u.proc.arglist[d].defaultObjPtr) {
15122 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
15124 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
15125 if (retcode != JIM_OK) {
15126 goto badargset;
15129 i += argsLen;
15130 continue;
15134 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
15135 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
15137 else {
15139 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
15141 if (retcode != JIM_OK) {
15142 goto badargset;
15147 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
15149 badargset:
15152 localCommands = callFramePtr->localCommands;
15153 callFramePtr->localCommands = NULL;
15155 interp->framePtr = interp->framePtr->parent;
15156 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
15157 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
15159 else {
15160 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
15164 while (retcode == JIM_EVAL) {
15165 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
15167 Jim_IncrRefCount(resultScriptObjPtr);
15169 JimPanic((!Jim_IsList(resultScriptObjPtr), "tailcall (JIM_EVAL) returned non-list"));
15171 retcode = JimEvalObjList(interp, resultScriptObjPtr);
15172 if (retcode == JIM_RETURN) {
15173 interp->returnLevel++;
15175 Jim_DecrRefCount(interp, resultScriptObjPtr);
15178 if (retcode == JIM_RETURN) {
15179 if (--interp->returnLevel <= 0) {
15180 retcode = interp->returnCode;
15181 interp->returnCode = JIM_OK;
15182 interp->returnLevel = 0;
15185 else if (retcode == JIM_ERR) {
15186 interp->addStackTrace++;
15187 Jim_DecrRefCount(interp, interp->errorProc);
15188 interp->errorProc = argv[0];
15189 Jim_IncrRefCount(interp->errorProc);
15193 JimDeleteLocalProcs(interp, localCommands);
15195 return retcode;
15198 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
15200 int retval;
15201 Jim_Obj *scriptObjPtr;
15203 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
15204 Jim_IncrRefCount(scriptObjPtr);
15206 if (filename) {
15207 Jim_Obj *prevScriptObj;
15209 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
15211 prevScriptObj = interp->currentScriptObj;
15212 interp->currentScriptObj = scriptObjPtr;
15214 retval = Jim_EvalObj(interp, scriptObjPtr);
15216 interp->currentScriptObj = prevScriptObj;
15218 else {
15219 retval = Jim_EvalObj(interp, scriptObjPtr);
15221 Jim_DecrRefCount(interp, scriptObjPtr);
15222 return retval;
15225 int Jim_Eval(Jim_Interp *interp, const char *script)
15227 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
15231 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
15233 int retval;
15234 Jim_CallFrame *savedFramePtr = interp->framePtr;
15236 interp->framePtr = interp->topFramePtr;
15237 retval = Jim_Eval(interp, script);
15238 interp->framePtr = savedFramePtr;
15240 return retval;
15243 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
15245 int retval;
15246 Jim_CallFrame *savedFramePtr = interp->framePtr;
15248 interp->framePtr = interp->topFramePtr;
15249 retval = Jim_EvalFile(interp, filename);
15250 interp->framePtr = savedFramePtr;
15252 return retval;
15255 #include <sys/stat.h>
15257 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
15259 FILE *fp;
15260 char *buf;
15261 Jim_Obj *scriptObjPtr;
15262 Jim_Obj *prevScriptObj;
15263 struct stat sb;
15264 int retcode;
15265 int readlen;
15266 struct JimParseResult result;
15268 if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) {
15269 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
15270 return JIM_ERR;
15272 if (sb.st_size == 0) {
15273 fclose(fp);
15274 return JIM_OK;
15277 buf = Jim_Alloc(sb.st_size + 1);
15278 readlen = fread(buf, 1, sb.st_size, fp);
15279 if (ferror(fp)) {
15280 fclose(fp);
15281 Jim_Free(buf);
15282 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
15283 return JIM_ERR;
15285 fclose(fp);
15286 buf[readlen] = 0;
15288 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15289 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15290 Jim_IncrRefCount(scriptObjPtr);
15293 if (SetScriptFromAny(interp, scriptObjPtr, &result) == JIM_ERR) {
15294 const char *msg;
15295 char linebuf[20];
15297 switch (result.missing) {
15298 case '[':
15299 msg = "unmatched \"[\"";
15300 break;
15301 case '{':
15302 msg = "missing close-brace";
15303 break;
15304 case '"':
15305 default:
15306 msg = "missing quote";
15307 break;
15310 snprintf(linebuf, sizeof(linebuf), "%d", result.line);
15312 Jim_SetResultFormatted(interp, "%s in \"%s\" at line %s",
15313 msg, filename, linebuf);
15314 Jim_DecrRefCount(interp, scriptObjPtr);
15315 return JIM_ERR;
15318 prevScriptObj = interp->currentScriptObj;
15319 interp->currentScriptObj = scriptObjPtr;
15321 retcode = Jim_EvalObj(interp, scriptObjPtr);
15324 if (retcode == JIM_RETURN) {
15325 if (--interp->returnLevel <= 0) {
15326 retcode = interp->returnCode;
15327 interp->returnCode = JIM_OK;
15328 interp->returnLevel = 0;
15331 if (retcode == JIM_ERR) {
15333 interp->addStackTrace++;
15336 interp->currentScriptObj = prevScriptObj;
15338 Jim_DecrRefCount(interp, scriptObjPtr);
15340 return retcode;
15343 static void JimParseSubst(struct JimParserCtx *pc, int flags)
15345 pc->tstart = pc->p;
15346 pc->tline = pc->linenr;
15348 if (pc->len == 0) {
15349 pc->tend = pc->p;
15350 pc->tt = JIM_TT_EOL;
15351 pc->eof = 1;
15352 return;
15354 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15355 JimParseCmd(pc);
15356 return;
15358 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15359 if (JimParseVar(pc) == JIM_OK) {
15360 return;
15363 pc->tstart = pc->p;
15364 flags |= JIM_SUBST_NOVAR;
15366 while (pc->len) {
15367 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15368 break;
15370 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15371 break;
15373 if (*pc->p == '\\' && pc->len > 1) {
15374 pc->p++;
15375 pc->len--;
15377 pc->p++;
15378 pc->len--;
15380 pc->tend = pc->p - 1;
15381 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
15385 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
15387 int scriptTextLen;
15388 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
15389 struct JimParserCtx parser;
15390 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
15391 ParseTokenList tokenlist;
15394 ScriptTokenListInit(&tokenlist);
15396 JimParserInit(&parser, scriptText, scriptTextLen, 1);
15397 while (1) {
15398 JimParseSubst(&parser, flags);
15399 if (parser.eof) {
15401 break;
15403 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15404 parser.tline);
15408 script->inUse = 1;
15409 script->substFlags = flags;
15410 script->fileNameObj = interp->emptyObj;
15411 Jim_IncrRefCount(script->fileNameObj);
15412 SubstObjAddTokens(interp, script, &tokenlist);
15415 ScriptTokenListFree(&tokenlist);
15417 #ifdef DEBUG_SHOW_SUBST
15419 int i;
15421 printf("==== Subst ====\n");
15422 for (i = 0; i < script->len; i++) {
15423 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
15424 Jim_String(script->token[i].objPtr));
15427 #endif
15430 Jim_FreeIntRep(interp, objPtr);
15431 Jim_SetIntRepPtr(objPtr, script);
15432 objPtr->typePtr = &scriptObjType;
15433 return JIM_OK;
15436 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
15438 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
15439 SetSubstFromAny(interp, objPtr, flags);
15440 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
15443 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
15445 ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
15447 Jim_IncrRefCount(substObjPtr);
15448 script->inUse++;
15450 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
15452 script->inUse--;
15453 Jim_DecrRefCount(interp, substObjPtr);
15454 if (*resObjPtrPtr == NULL) {
15455 return JIM_ERR;
15457 return JIM_OK;
15460 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
15462 Jim_Obj *objPtr;
15463 Jim_Obj *listObjPtr = Jim_NewListObj(interp, argv, argc);
15465 if (*msg) {
15466 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
15468 Jim_IncrRefCount(listObjPtr);
15469 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
15470 Jim_DecrRefCount(interp, listObjPtr);
15472 Jim_IncrRefCount(objPtr);
15473 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
15474 Jim_DecrRefCount(interp, objPtr);
15477 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
15478 Jim_HashEntry *he, int type);
15480 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
15482 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
15483 JimHashtableIteratorCallbackType *callback, int type)
15485 Jim_HashEntry *he;
15486 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
15489 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
15490 he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
15491 if (he) {
15492 callback(interp, listObjPtr, he, type);
15495 else {
15496 Jim_HashTableIterator htiter;
15497 JimInitHashTableIterator(ht, &htiter);
15498 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
15499 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
15500 callback(interp, listObjPtr, he, type);
15504 return listObjPtr;
15508 #define JIM_CMDLIST_COMMANDS 0
15509 #define JIM_CMDLIST_PROCS 1
15510 #define JIM_CMDLIST_CHANNELS 2
15512 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15513 Jim_HashEntry *he, int type)
15515 Jim_Cmd *cmdPtr = (Jim_Cmd *)he->u.val;
15516 Jim_Obj *objPtr;
15518 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
15520 return;
15523 objPtr = Jim_NewStringObj(interp, he->key, -1);
15524 Jim_IncrRefCount(objPtr);
15526 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15527 Jim_ListAppendElement(interp, listObjPtr, objPtr);
15529 Jim_DecrRefCount(interp, objPtr);
15533 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
15535 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
15539 #define JIM_VARLIST_GLOBALS 0
15540 #define JIM_VARLIST_LOCALS 1
15541 #define JIM_VARLIST_VARS 2
15543 #define JIM_VARLIST_VALUES 0x1000
15545 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15546 Jim_HashEntry *he, int type)
15548 Jim_Var *varPtr = (Jim_Var *)he->u.val;
15550 if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) {
15551 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
15552 if (type & JIM_VARLIST_VALUES) {
15553 Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr);
15559 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
15561 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
15562 return interp->emptyObj;
15564 else {
15565 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
15566 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, mode);
15570 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
15571 Jim_Obj **objPtrPtr, int info_level_cmd)
15573 Jim_CallFrame *targetCallFrame;
15575 targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
15576 if (targetCallFrame == NULL) {
15577 return JIM_ERR;
15580 if (targetCallFrame == interp->topFramePtr) {
15581 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
15582 return JIM_ERR;
15584 if (info_level_cmd) {
15585 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
15587 else {
15588 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
15590 Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]);
15591 Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj);
15592 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line));
15593 *objPtrPtr = listObj;
15595 return JIM_OK;
15600 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15602 if (argc != 2 && argc != 3) {
15603 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
15604 return JIM_ERR;
15606 if (argc == 3) {
15607 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
15608 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
15609 return JIM_ERR;
15611 else {
15612 fputs(Jim_String(argv[2]), stdout);
15615 else {
15616 puts(Jim_String(argv[1]));
15618 return JIM_OK;
15622 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15624 jim_wide wideValue, res;
15625 double doubleValue, doubleRes;
15626 int i;
15628 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
15630 for (i = 1; i < argc; i++) {
15631 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
15632 goto trydouble;
15633 if (op == JIM_EXPROP_ADD)
15634 res += wideValue;
15635 else
15636 res *= wideValue;
15638 Jim_SetResultInt(interp, res);
15639 return JIM_OK;
15640 trydouble:
15641 doubleRes = (double)res;
15642 for (; i < argc; i++) {
15643 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15644 return JIM_ERR;
15645 if (op == JIM_EXPROP_ADD)
15646 doubleRes += doubleValue;
15647 else
15648 doubleRes *= doubleValue;
15650 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15651 return JIM_OK;
15655 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15657 jim_wide wideValue, res = 0;
15658 double doubleValue, doubleRes = 0;
15659 int i = 2;
15661 if (argc < 2) {
15662 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
15663 return JIM_ERR;
15665 else if (argc == 2) {
15666 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
15667 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
15668 return JIM_ERR;
15670 else {
15671 if (op == JIM_EXPROP_SUB)
15672 doubleRes = -doubleValue;
15673 else
15674 doubleRes = 1.0 / doubleValue;
15675 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15676 return JIM_OK;
15679 if (op == JIM_EXPROP_SUB) {
15680 res = -wideValue;
15681 Jim_SetResultInt(interp, res);
15683 else {
15684 doubleRes = 1.0 / wideValue;
15685 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15687 return JIM_OK;
15689 else {
15690 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
15691 if (Jim_GetDouble(interp, argv[1], &doubleRes)
15692 != JIM_OK) {
15693 return JIM_ERR;
15695 else {
15696 goto trydouble;
15700 for (i = 2; i < argc; i++) {
15701 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
15702 doubleRes = (double)res;
15703 goto trydouble;
15705 if (op == JIM_EXPROP_SUB)
15706 res -= wideValue;
15707 else
15708 res /= wideValue;
15710 Jim_SetResultInt(interp, res);
15711 return JIM_OK;
15712 trydouble:
15713 for (; i < argc; i++) {
15714 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15715 return JIM_ERR;
15716 if (op == JIM_EXPROP_SUB)
15717 doubleRes -= doubleValue;
15718 else
15719 doubleRes /= doubleValue;
15721 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15722 return JIM_OK;
15727 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15729 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
15733 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15735 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
15739 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15741 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
15745 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15747 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
15751 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15753 if (argc != 2 && argc != 3) {
15754 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
15755 return JIM_ERR;
15757 if (argc == 2) {
15758 Jim_Obj *objPtr;
15760 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
15761 if (!objPtr)
15762 return JIM_ERR;
15763 Jim_SetResult(interp, objPtr);
15764 return JIM_OK;
15767 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
15768 return JIM_ERR;
15769 Jim_SetResult(interp, argv[2]);
15770 return JIM_OK;
15773 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15775 int i = 1;
15776 int complain = 1;
15778 while (i < argc) {
15779 if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
15780 i++;
15781 break;
15783 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
15784 complain = 0;
15785 i++;
15786 continue;
15788 break;
15791 while (i < argc) {
15792 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
15793 && complain) {
15794 return JIM_ERR;
15796 i++;
15798 return JIM_OK;
15802 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15804 if (argc != 3) {
15805 Jim_WrongNumArgs(interp, 1, argv, "condition body");
15806 return JIM_ERR;
15810 while (1) {
15811 int boolean, retval;
15813 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
15814 return retval;
15815 if (!boolean)
15816 break;
15818 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
15819 switch (retval) {
15820 case JIM_BREAK:
15821 goto out;
15822 break;
15823 case JIM_CONTINUE:
15824 continue;
15825 break;
15826 default:
15827 return retval;
15831 out:
15832 Jim_SetEmptyResult(interp);
15833 return JIM_OK;
15837 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15839 int retval;
15840 int boolean = 1;
15841 Jim_Obj *varNamePtr = NULL;
15842 Jim_Obj *stopVarNamePtr = NULL;
15844 if (argc != 5) {
15845 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
15846 return JIM_ERR;
15850 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
15851 return retval;
15854 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
15857 #ifdef JIM_OPTIMIZATION
15858 if (retval == JIM_OK && boolean) {
15859 ScriptObj *incrScript;
15860 ExprByteCode *expr;
15861 jim_wide stop, currentVal;
15862 Jim_Obj *objPtr;
15863 int cmpOffset;
15866 expr = JimGetExpression(interp, argv[2]);
15867 incrScript = Jim_GetScript(interp, argv[3]);
15870 if (incrScript->len != 3 || !expr || expr->len != 3) {
15871 goto evalstart;
15874 if (incrScript->token[1].type != JIM_TT_ESC ||
15875 expr->token[0].type != JIM_TT_VAR ||
15876 (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
15877 goto evalstart;
15880 if (expr->token[2].type == JIM_EXPROP_LT) {
15881 cmpOffset = 0;
15883 else if (expr->token[2].type == JIM_EXPROP_LTE) {
15884 cmpOffset = 1;
15886 else {
15887 goto evalstart;
15891 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
15892 goto evalstart;
15896 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {
15897 goto evalstart;
15901 if (expr->token[1].type == JIM_TT_EXPR_INT) {
15902 if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {
15903 goto evalstart;
15906 else {
15907 stopVarNamePtr = expr->token[1].objPtr;
15908 Jim_IncrRefCount(stopVarNamePtr);
15910 stop = 0;
15914 varNamePtr = expr->token[0].objPtr;
15915 Jim_IncrRefCount(varNamePtr);
15917 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
15918 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
15919 goto testcond;
15923 while (retval == JIM_OK) {
15928 if (stopVarNamePtr) {
15929 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
15930 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
15931 goto testcond;
15935 if (currentVal >= stop + cmpOffset) {
15936 break;
15940 retval = Jim_EvalObj(interp, argv[4]);
15941 if (retval == JIM_OK || retval == JIM_CONTINUE) {
15942 retval = JIM_OK;
15944 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
15947 if (objPtr == NULL) {
15948 retval = JIM_ERR;
15949 goto out;
15951 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
15952 currentVal = ++JimWideValue(objPtr);
15953 Jim_InvalidateStringRep(objPtr);
15955 else {
15956 if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
15957 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
15958 ++currentVal)) != JIM_OK) {
15959 goto evalnext;
15964 goto out;
15966 evalstart:
15967 #endif
15969 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
15971 retval = Jim_EvalObj(interp, argv[4]);
15973 if (retval == JIM_OK || retval == JIM_CONTINUE) {
15975 evalnext:
15976 retval = Jim_EvalObj(interp, argv[3]);
15977 if (retval == JIM_OK || retval == JIM_CONTINUE) {
15979 testcond:
15980 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
15984 out:
15985 if (stopVarNamePtr) {
15986 Jim_DecrRefCount(interp, stopVarNamePtr);
15988 if (varNamePtr) {
15989 Jim_DecrRefCount(interp, varNamePtr);
15992 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
15993 Jim_SetEmptyResult(interp);
15994 return JIM_OK;
15997 return retval;
16001 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16003 int retval;
16004 jim_wide i;
16005 jim_wide limit;
16006 jim_wide incr = 1;
16007 Jim_Obj *bodyObjPtr;
16009 if (argc != 5 && argc != 6) {
16010 Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body");
16011 return JIM_ERR;
16014 if (Jim_GetWide(interp, argv[2], &i) != JIM_OK ||
16015 Jim_GetWide(interp, argv[3], &limit) != JIM_OK ||
16016 (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) {
16017 return JIM_ERR;
16019 bodyObjPtr = (argc == 5) ? argv[4] : argv[5];
16021 retval = Jim_SetVariable(interp, argv[1], argv[2]);
16023 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
16024 retval = Jim_EvalObj(interp, bodyObjPtr);
16025 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16026 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16028 retval = JIM_OK;
16031 i += incr;
16033 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16034 if (argv[1]->typePtr != &variableObjType) {
16035 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16036 return JIM_ERR;
16039 JimWideValue(objPtr) = i;
16040 Jim_InvalidateStringRep(objPtr);
16042 if (argv[1]->typePtr != &variableObjType) {
16043 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16044 retval = JIM_ERR;
16045 break;
16049 else {
16050 objPtr = Jim_NewIntObj(interp, i);
16051 retval = Jim_SetVariable(interp, argv[1], objPtr);
16052 if (retval != JIM_OK) {
16053 Jim_FreeNewObj(interp, objPtr);
16059 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
16060 Jim_SetEmptyResult(interp);
16061 return JIM_OK;
16063 return retval;
16066 typedef struct {
16067 Jim_Obj *objPtr;
16068 int idx;
16069 } Jim_ListIter;
16071 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
16073 iter->objPtr = objPtr;
16074 iter->idx = 0;
16077 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
16079 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
16080 return NULL;
16082 return iter->objPtr->internalRep.listValue.ele[iter->idx++];
16085 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
16087 return iter->idx >= Jim_ListLength(interp, iter->objPtr);
16091 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
16093 int result = JIM_ERR;
16094 int i, numargs;
16095 Jim_ListIter twoiters[2];
16096 Jim_ListIter *iters;
16097 Jim_Obj *script;
16098 Jim_Obj *resultObj;
16100 if (argc < 4 || argc % 2 != 0) {
16101 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
16102 return JIM_ERR;
16104 script = argv[argc - 1];
16105 numargs = (argc - 1 - 1);
16107 if (numargs == 2) {
16108 iters = twoiters;
16110 else {
16111 iters = Jim_Alloc(numargs * sizeof(*iters));
16113 for (i = 0; i < numargs; i++) {
16114 JimListIterInit(&iters[i], argv[i + 1]);
16115 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
16116 Jim_SetResultString(interp, "foreach varlist is empty", -1);
16117 return JIM_ERR;
16121 if (doMap) {
16122 resultObj = Jim_NewListObj(interp, NULL, 0);
16124 else {
16125 resultObj = interp->emptyObj;
16127 Jim_IncrRefCount(resultObj);
16129 while (1) {
16131 for (i = 0; i < numargs; i += 2) {
16132 if (!JimListIterDone(interp, &iters[i + 1])) {
16133 break;
16136 if (i == numargs) {
16138 break;
16142 for (i = 0; i < numargs; i += 2) {
16143 Jim_Obj *varName;
16146 JimListIterInit(&iters[i], argv[i + 1]);
16147 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
16148 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
16149 if (!valObj) {
16151 valObj = interp->emptyObj;
16154 Jim_IncrRefCount(valObj);
16155 result = Jim_SetVariable(interp, varName, valObj);
16156 Jim_DecrRefCount(interp, valObj);
16157 if (result != JIM_OK) {
16158 goto err;
16162 switch (result = Jim_EvalObj(interp, script)) {
16163 case JIM_OK:
16164 if (doMap) {
16165 Jim_ListAppendElement(interp, resultObj, interp->result);
16167 break;
16168 case JIM_CONTINUE:
16169 break;
16170 case JIM_BREAK:
16171 goto out;
16172 default:
16173 goto err;
16176 out:
16177 result = JIM_OK;
16178 Jim_SetResult(interp, resultObj);
16179 err:
16180 Jim_DecrRefCount(interp, resultObj);
16181 if (numargs > 2) {
16182 Jim_Free(iters);
16184 return result;
16188 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16190 return JimForeachMapHelper(interp, argc, argv, 0);
16194 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16196 return JimForeachMapHelper(interp, argc, argv, 1);
16200 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16202 int result = JIM_ERR;
16203 int i;
16204 Jim_ListIter iter;
16205 Jim_Obj *resultObj;
16207 if (argc < 2) {
16208 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
16209 return JIM_ERR;
16212 JimListIterInit(&iter, argv[1]);
16214 for (i = 2; i < argc; i++) {
16215 Jim_Obj *valObj = JimListIterNext(interp, &iter);
16216 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
16217 if (result != JIM_OK) {
16218 return result;
16222 resultObj = Jim_NewListObj(interp, NULL, 0);
16223 while (!JimListIterDone(interp, &iter)) {
16224 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
16227 Jim_SetResult(interp, resultObj);
16229 return JIM_OK;
16233 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16235 int boolean, retval, current = 1, falsebody = 0;
16237 if (argc >= 3) {
16238 while (1) {
16240 if (current >= argc)
16241 goto err;
16242 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
16243 != JIM_OK)
16244 return retval;
16246 if (current >= argc)
16247 goto err;
16248 if (Jim_CompareStringImmediate(interp, argv[current], "then"))
16249 current++;
16251 if (current >= argc)
16252 goto err;
16253 if (boolean)
16254 return Jim_EvalObj(interp, argv[current]);
16256 if (++current >= argc) {
16257 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
16258 return JIM_OK;
16260 falsebody = current++;
16261 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
16263 if (current != argc - 1)
16264 goto err;
16265 return Jim_EvalObj(interp, argv[current]);
16267 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
16268 continue;
16270 else if (falsebody != argc - 1)
16271 goto err;
16272 return Jim_EvalObj(interp, argv[falsebody]);
16274 return JIM_OK;
16276 err:
16277 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
16278 return JIM_ERR;
16283 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
16284 Jim_Obj *stringObj, int nocase)
16286 Jim_Obj *parms[4];
16287 int argc = 0;
16288 long eq;
16289 int rc;
16291 parms[argc++] = commandObj;
16292 if (nocase) {
16293 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
16295 parms[argc++] = patternObj;
16296 parms[argc++] = stringObj;
16298 rc = Jim_EvalObjVector(interp, argc, parms);
16300 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
16301 eq = -rc;
16304 return eq;
16307 enum
16308 { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
16311 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16313 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
16314 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
16315 Jim_Obj *script = 0;
16317 if (argc < 3) {
16318 wrongnumargs:
16319 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
16320 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
16321 return JIM_ERR;
16323 for (opt = 1; opt < argc; ++opt) {
16324 const char *option = Jim_String(argv[opt]);
16326 if (*option != '-')
16327 break;
16328 else if (strncmp(option, "--", 2) == 0) {
16329 ++opt;
16330 break;
16332 else if (strncmp(option, "-exact", 2) == 0)
16333 matchOpt = SWITCH_EXACT;
16334 else if (strncmp(option, "-glob", 2) == 0)
16335 matchOpt = SWITCH_GLOB;
16336 else if (strncmp(option, "-regexp", 2) == 0)
16337 matchOpt = SWITCH_RE;
16338 else if (strncmp(option, "-command", 2) == 0) {
16339 matchOpt = SWITCH_CMD;
16340 if ((argc - opt) < 2)
16341 goto wrongnumargs;
16342 command = argv[++opt];
16344 else {
16345 Jim_SetResultFormatted(interp,
16346 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
16347 argv[opt]);
16348 return JIM_ERR;
16350 if ((argc - opt) < 2)
16351 goto wrongnumargs;
16353 strObj = argv[opt++];
16354 patCount = argc - opt;
16355 if (patCount == 1) {
16356 Jim_Obj **vector;
16358 JimListGetElements(interp, argv[opt], &patCount, &vector);
16359 caseList = vector;
16361 else
16362 caseList = &argv[opt];
16363 if (patCount == 0 || patCount % 2 != 0)
16364 goto wrongnumargs;
16365 for (i = 0; script == 0 && i < patCount; i += 2) {
16366 Jim_Obj *patObj = caseList[i];
16368 if (!Jim_CompareStringImmediate(interp, patObj, "default")
16369 || i < (patCount - 2)) {
16370 switch (matchOpt) {
16371 case SWITCH_EXACT:
16372 if (Jim_StringEqObj(strObj, patObj))
16373 script = caseList[i + 1];
16374 break;
16375 case SWITCH_GLOB:
16376 if (Jim_StringMatchObj(interp, patObj, strObj, 0))
16377 script = caseList[i + 1];
16378 break;
16379 case SWITCH_RE:
16380 command = Jim_NewStringObj(interp, "regexp", -1);
16382 case SWITCH_CMD:{
16383 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);
16385 if (argc - opt == 1) {
16386 Jim_Obj **vector;
16388 JimListGetElements(interp, argv[opt], &patCount, &vector);
16389 caseList = vector;
16392 if (rc < 0) {
16393 return -rc;
16395 if (rc)
16396 script = caseList[i + 1];
16397 break;
16401 else {
16402 script = caseList[i + 1];
16405 for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2)
16406 script = caseList[i + 1];
16407 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
16408 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
16409 return JIM_ERR;
16411 Jim_SetEmptyResult(interp);
16412 if (script) {
16413 return Jim_EvalObj(interp, script);
16415 return JIM_OK;
16419 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16421 Jim_Obj *listObjPtr;
16423 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
16424 Jim_SetResult(interp, listObjPtr);
16425 return JIM_OK;
16429 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16431 Jim_Obj *objPtr, *listObjPtr;
16432 int i;
16433 int idx;
16435 if (argc < 3) {
16436 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
16437 return JIM_ERR;
16439 objPtr = argv[1];
16440 Jim_IncrRefCount(objPtr);
16441 for (i = 2; i < argc; i++) {
16442 listObjPtr = objPtr;
16443 if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
16444 Jim_DecrRefCount(interp, listObjPtr);
16445 return JIM_ERR;
16447 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
16448 Jim_DecrRefCount(interp, listObjPtr);
16449 Jim_SetEmptyResult(interp);
16450 return JIM_OK;
16452 Jim_IncrRefCount(objPtr);
16453 Jim_DecrRefCount(interp, listObjPtr);
16455 Jim_SetResult(interp, objPtr);
16456 Jim_DecrRefCount(interp, objPtr);
16457 return JIM_OK;
16461 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16463 if (argc != 2) {
16464 Jim_WrongNumArgs(interp, 1, argv, "list");
16465 return JIM_ERR;
16467 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
16468 return JIM_OK;
16472 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16474 static const char * const options[] = {
16475 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
16476 NULL
16478 enum
16479 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
16480 OPT_COMMAND };
16481 int i;
16482 int opt_bool = 0;
16483 int opt_not = 0;
16484 int opt_nocase = 0;
16485 int opt_all = 0;
16486 int opt_inline = 0;
16487 int opt_match = OPT_EXACT;
16488 int listlen;
16489 int rc = JIM_OK;
16490 Jim_Obj *listObjPtr = NULL;
16491 Jim_Obj *commandObj = NULL;
16493 if (argc < 3) {
16494 wrongargs:
16495 Jim_WrongNumArgs(interp, 1, argv,
16496 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
16497 return JIM_ERR;
16500 for (i = 1; i < argc - 2; i++) {
16501 int option;
16503 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
16504 return JIM_ERR;
16506 switch (option) {
16507 case OPT_BOOL:
16508 opt_bool = 1;
16509 opt_inline = 0;
16510 break;
16511 case OPT_NOT:
16512 opt_not = 1;
16513 break;
16514 case OPT_NOCASE:
16515 opt_nocase = 1;
16516 break;
16517 case OPT_INLINE:
16518 opt_inline = 1;
16519 opt_bool = 0;
16520 break;
16521 case OPT_ALL:
16522 opt_all = 1;
16523 break;
16524 case OPT_COMMAND:
16525 if (i >= argc - 2) {
16526 goto wrongargs;
16528 commandObj = argv[++i];
16530 case OPT_EXACT:
16531 case OPT_GLOB:
16532 case OPT_REGEXP:
16533 opt_match = option;
16534 break;
16538 argv += i;
16540 if (opt_all) {
16541 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16543 if (opt_match == OPT_REGEXP) {
16544 commandObj = Jim_NewStringObj(interp, "regexp", -1);
16546 if (commandObj) {
16547 Jim_IncrRefCount(commandObj);
16550 listlen = Jim_ListLength(interp, argv[0]);
16551 for (i = 0; i < listlen; i++) {
16552 Jim_Obj *objPtr;
16553 int eq = 0;
16555 Jim_ListIndex(interp, argv[0], i, &objPtr, JIM_NONE);
16556 switch (opt_match) {
16557 case OPT_EXACT:
16558 eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0;
16559 break;
16561 case OPT_GLOB:
16562 eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase);
16563 break;
16565 case OPT_REGEXP:
16566 case OPT_COMMAND:
16567 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase);
16568 if (eq < 0) {
16569 if (listObjPtr) {
16570 Jim_FreeNewObj(interp, listObjPtr);
16572 rc = JIM_ERR;
16573 goto done;
16575 break;
16579 if (!eq && opt_bool && opt_not && !opt_all) {
16580 continue;
16583 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
16585 Jim_Obj *resultObj;
16587 if (opt_bool) {
16588 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
16590 else if (!opt_inline) {
16591 resultObj = Jim_NewIntObj(interp, i);
16593 else {
16594 resultObj = objPtr;
16597 if (opt_all) {
16598 Jim_ListAppendElement(interp, listObjPtr, resultObj);
16600 else {
16601 Jim_SetResult(interp, resultObj);
16602 goto done;
16607 if (opt_all) {
16608 Jim_SetResult(interp, listObjPtr);
16610 else {
16612 if (opt_bool) {
16613 Jim_SetResultBool(interp, opt_not);
16615 else if (!opt_inline) {
16616 Jim_SetResultInt(interp, -1);
16620 done:
16621 if (commandObj) {
16622 Jim_DecrRefCount(interp, commandObj);
16624 return rc;
16628 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16630 Jim_Obj *listObjPtr;
16631 int shared, i;
16633 if (argc < 2) {
16634 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
16635 return JIM_ERR;
16637 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
16638 if (!listObjPtr) {
16640 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16641 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
16642 Jim_FreeNewObj(interp, listObjPtr);
16643 return JIM_ERR;
16646 shared = Jim_IsShared(listObjPtr);
16647 if (shared)
16648 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
16649 for (i = 2; i < argc; i++)
16650 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
16651 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
16652 if (shared)
16653 Jim_FreeNewObj(interp, listObjPtr);
16654 return JIM_ERR;
16656 Jim_SetResult(interp, listObjPtr);
16657 return JIM_OK;
16661 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16663 int idx, len;
16664 Jim_Obj *listPtr;
16666 if (argc < 3) {
16667 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
16668 return JIM_ERR;
16670 listPtr = argv[1];
16671 if (Jim_IsShared(listPtr))
16672 listPtr = Jim_DuplicateObj(interp, listPtr);
16673 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
16674 goto err;
16675 len = Jim_ListLength(interp, listPtr);
16676 if (idx >= len)
16677 idx = len;
16678 else if (idx < 0)
16679 idx = len + idx + 1;
16680 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
16681 Jim_SetResult(interp, listPtr);
16682 return JIM_OK;
16683 err:
16684 if (listPtr != argv[1]) {
16685 Jim_FreeNewObj(interp, listPtr);
16687 return JIM_ERR;
16691 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16693 int first, last, len, rangeLen;
16694 Jim_Obj *listObj;
16695 Jim_Obj *newListObj;
16697 if (argc < 4) {
16698 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
16699 return JIM_ERR;
16701 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
16702 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
16703 return JIM_ERR;
16706 listObj = argv[1];
16707 len = Jim_ListLength(interp, listObj);
16709 first = JimRelToAbsIndex(len, first);
16710 last = JimRelToAbsIndex(len, last);
16711 JimRelToAbsRange(len, &first, &last, &rangeLen);
16715 if (first < len) {
16718 else if (len == 0) {
16720 first = 0;
16722 else {
16723 Jim_SetResultString(interp, "list doesn't contain element ", -1);
16724 Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]);
16725 return JIM_ERR;
16729 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
16732 ListInsertElements(newListObj, -1, argc - 4, argv + 4);
16735 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
16737 Jim_SetResult(interp, newListObj);
16738 return JIM_OK;
16742 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16744 if (argc < 3) {
16745 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
16746 return JIM_ERR;
16748 else if (argc == 3) {
16749 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16750 return JIM_ERR;
16751 Jim_SetResult(interp, argv[2]);
16752 return JIM_OK;
16754 if (Jim_SetListIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1])
16755 == JIM_ERR)
16756 return JIM_ERR;
16757 return JIM_OK;
16761 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
16763 static const char * const options[] = {
16764 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-index", NULL
16766 enum
16767 { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_INDEX };
16768 Jim_Obj *resObj;
16769 int i;
16770 int retCode;
16772 struct lsort_info info;
16774 if (argc < 2) {
16775 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
16776 return JIM_ERR;
16779 info.type = JIM_LSORT_ASCII;
16780 info.order = 1;
16781 info.indexed = 0;
16782 info.command = NULL;
16783 info.interp = interp;
16785 for (i = 1; i < (argc - 1); i++) {
16786 int option;
16788 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG)
16789 != JIM_OK)
16790 return JIM_ERR;
16791 switch (option) {
16792 case OPT_ASCII:
16793 info.type = JIM_LSORT_ASCII;
16794 break;
16795 case OPT_NOCASE:
16796 info.type = JIM_LSORT_NOCASE;
16797 break;
16798 case OPT_INTEGER:
16799 info.type = JIM_LSORT_INTEGER;
16800 break;
16801 case OPT_INCREASING:
16802 info.order = 1;
16803 break;
16804 case OPT_DECREASING:
16805 info.order = -1;
16806 break;
16807 case OPT_COMMAND:
16808 if (i >= (argc - 2)) {
16809 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
16810 return JIM_ERR;
16812 info.type = JIM_LSORT_COMMAND;
16813 info.command = argv[i + 1];
16814 i++;
16815 break;
16816 case OPT_INDEX:
16817 if (i >= (argc - 2)) {
16818 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
16819 return JIM_ERR;
16821 if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
16822 return JIM_ERR;
16824 info.indexed = 1;
16825 i++;
16826 break;
16829 resObj = Jim_DuplicateObj(interp, argv[argc - 1]);
16830 retCode = ListSortElements(interp, resObj, &info);
16831 if (retCode == JIM_OK) {
16832 Jim_SetResult(interp, resObj);
16834 else {
16835 Jim_FreeNewObj(interp, resObj);
16837 return retCode;
16841 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16843 Jim_Obj *stringObjPtr;
16844 int i;
16846 if (argc < 2) {
16847 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
16848 return JIM_ERR;
16850 if (argc == 2) {
16851 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16852 if (!stringObjPtr)
16853 return JIM_ERR;
16855 else {
16856 int freeobj = 0;
16857 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
16858 if (!stringObjPtr) {
16860 stringObjPtr = Jim_NewEmptyStringObj(interp);
16861 freeobj = 1;
16863 else if (Jim_IsShared(stringObjPtr)) {
16864 freeobj = 1;
16865 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
16867 for (i = 2; i < argc; i++) {
16868 Jim_AppendObj(interp, stringObjPtr, argv[i]);
16870 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
16871 if (freeobj) {
16872 Jim_FreeNewObj(interp, stringObjPtr);
16874 return JIM_ERR;
16877 Jim_SetResult(interp, stringObjPtr);
16878 return JIM_OK;
16882 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16884 #if !defined(JIM_DEBUG_COMMAND)
16885 Jim_SetResultString(interp, "unsupported", -1);
16886 return JIM_ERR;
16887 #endif
16891 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16893 int rc;
16895 if (argc < 2) {
16896 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
16897 return JIM_ERR;
16900 if (argc == 2) {
16901 rc = Jim_EvalObj(interp, argv[1]);
16903 else {
16904 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
16907 if (rc == JIM_ERR) {
16909 interp->addStackTrace++;
16911 return rc;
16915 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16917 if (argc >= 2) {
16918 int retcode;
16919 Jim_CallFrame *savedCallFrame, *targetCallFrame;
16920 Jim_Obj *objPtr;
16921 const char *str;
16924 savedCallFrame = interp->framePtr;
16927 str = Jim_String(argv[1]);
16928 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
16929 targetCallFrame =Jim_GetCallFrameByLevel(interp, argv[1]);
16930 argc--;
16931 argv++;
16933 else {
16934 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
16936 if (targetCallFrame == NULL) {
16937 return JIM_ERR;
16939 if (argc < 2) {
16940 argv--;
16941 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
16942 return JIM_ERR;
16945 interp->framePtr = targetCallFrame;
16946 if (argc == 2) {
16947 retcode = Jim_EvalObj(interp, argv[1]);
16949 else {
16950 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
16951 Jim_IncrRefCount(objPtr);
16952 retcode = Jim_EvalObj(interp, objPtr);
16953 Jim_DecrRefCount(interp, objPtr);
16955 interp->framePtr = savedCallFrame;
16956 return retcode;
16958 else {
16959 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
16960 return JIM_ERR;
16965 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16967 Jim_Obj *exprResultPtr;
16968 int retcode;
16970 if (argc == 2) {
16971 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
16973 else if (argc > 2) {
16974 Jim_Obj *objPtr;
16976 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
16977 Jim_IncrRefCount(objPtr);
16978 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
16979 Jim_DecrRefCount(interp, objPtr);
16981 else {
16982 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
16983 return JIM_ERR;
16985 if (retcode != JIM_OK)
16986 return retcode;
16987 Jim_SetResult(interp, exprResultPtr);
16988 Jim_DecrRefCount(interp, exprResultPtr);
16989 return JIM_OK;
16993 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16995 if (argc != 1) {
16996 Jim_WrongNumArgs(interp, 1, argv, "");
16997 return JIM_ERR;
16999 return JIM_BREAK;
17003 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17005 if (argc != 1) {
17006 Jim_WrongNumArgs(interp, 1, argv, "");
17007 return JIM_ERR;
17009 return JIM_CONTINUE;
17013 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17015 int i;
17016 Jim_Obj *stackTraceObj = NULL;
17017 Jim_Obj *errorCodeObj = NULL;
17018 int returnCode = JIM_OK;
17019 long level = 1;
17021 for (i = 1; i < argc - 1; i += 2) {
17022 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
17023 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
17024 return JIM_ERR;
17027 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
17028 stackTraceObj = argv[i + 1];
17030 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
17031 errorCodeObj = argv[i + 1];
17033 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
17034 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
17035 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
17036 return JIM_ERR;
17039 else {
17040 break;
17044 if (i != argc - 1 && i != argc) {
17045 Jim_WrongNumArgs(interp, 1, argv,
17046 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
17050 if (stackTraceObj && returnCode == JIM_ERR) {
17051 JimSetStackTrace(interp, stackTraceObj);
17054 if (errorCodeObj && returnCode == JIM_ERR) {
17055 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
17057 interp->returnCode = returnCode;
17058 interp->returnLevel = level;
17060 if (i == argc - 1) {
17061 Jim_SetResult(interp, argv[i]);
17063 return JIM_RETURN;
17067 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17069 Jim_SetResult(interp, Jim_NewListObj(interp, argv + 1, argc - 1));
17070 return JIM_EVAL;
17073 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17075 Jim_Obj *cmdList;
17076 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17079 cmdList = Jim_DuplicateObj(interp, prefixListObj);
17080 ListInsertElements(cmdList, -1, argc - 1, argv + 1);
17082 return JimEvalObjList(interp, cmdList);
17085 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17087 Jim_Obj *prefixListObj = privData;
17088 Jim_DecrRefCount(interp, prefixListObj);
17091 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17093 Jim_Obj *prefixListObj;
17094 const char *newname;
17096 if (argc < 3) {
17097 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
17098 return JIM_ERR;
17101 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
17102 Jim_IncrRefCount(prefixListObj);
17103 newname = Jim_String(argv[1]);
17104 if (newname[0] == ':' && newname[1] == ':') {
17105 while (*++newname == ':') {
17109 Jim_SetResult(interp, argv[1]);
17111 return Jim_CreateCommand(interp, newname, JimAliasCmd, prefixListObj, JimAliasCmdDelete);
17115 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17117 Jim_Cmd *cmd;
17119 if (argc != 4 && argc != 5) {
17120 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
17121 return JIM_ERR;
17124 if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
17125 return JIM_ERR;
17128 if (argc == 4) {
17129 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
17131 else {
17132 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
17135 if (cmd) {
17137 Jim_Obj *qualifiedCmdNameObj;
17138 const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);
17140 JimCreateCommand(interp, cmdname, cmd);
17143 JimUpdateProcNamespace(interp, cmd, cmdname);
17145 JimFreeQualifiedName(interp, qualifiedCmdNameObj);
17148 Jim_SetResult(interp, argv[1]);
17149 return JIM_OK;
17151 return JIM_ERR;
17155 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17157 int retcode;
17159 if (argc < 2) {
17160 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17161 return JIM_ERR;
17165 interp->local++;
17166 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17167 interp->local--;
17171 if (retcode == 0) {
17172 Jim_Obj *cmdNameObj = Jim_GetResult(interp);
17174 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
17175 return JIM_ERR;
17177 if (interp->framePtr->localCommands == NULL) {
17178 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
17179 Jim_InitStack(interp->framePtr->localCommands);
17181 Jim_IncrRefCount(cmdNameObj);
17182 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
17185 return retcode;
17189 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17191 if (argc < 2) {
17192 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17193 return JIM_ERR;
17195 else {
17196 int retcode;
17198 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17199 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
17200 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
17201 return JIM_ERR;
17204 cmdPtr->u.proc.upcall++;
17205 JimIncrCmdRefCount(cmdPtr);
17208 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17211 cmdPtr->u.proc.upcall--;
17212 JimDecrCmdRefCount(interp, cmdPtr);
17214 return retcode;
17219 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17221 if (argc < 2) {
17222 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
17223 return JIM_ERR;
17225 else {
17226 int ret;
17227 Jim_Cmd *cmd;
17228 Jim_Obj *argListObjPtr;
17229 Jim_Obj *bodyObjPtr;
17230 Jim_Obj *nsObj = NULL;
17231 Jim_Obj **nargv;
17233 int len = Jim_ListLength(interp, argv[1]);
17234 if (len != 2 && len != 3) {
17235 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
17236 return JIM_ERR;
17239 if (len == 3) {
17240 #ifdef jim_ext_namespace
17242 nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
17243 #else
17244 Jim_SetResultString(interp, "namespaces not enabled", -1);
17245 return JIM_ERR;
17246 #endif
17248 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
17249 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
17251 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
17253 if (cmd) {
17255 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
17256 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
17257 Jim_IncrRefCount(nargv[0]);
17258 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
17259 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
17260 Jim_DecrRefCount(interp, nargv[0]);
17261 Jim_Free(nargv);
17263 JimDecrCmdRefCount(interp, cmd);
17264 return ret;
17266 return JIM_ERR;
17272 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17274 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17275 return JIM_OK;
17279 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17281 int i;
17282 Jim_CallFrame *targetCallFrame;
17285 if (argc > 3 && (argc % 2 == 0)) {
17286 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17287 argc--;
17288 argv++;
17290 else {
17291 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17293 if (targetCallFrame == NULL) {
17294 return JIM_ERR;
17298 if (argc < 3) {
17299 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
17300 return JIM_ERR;
17304 for (i = 1; i < argc; i += 2) {
17305 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
17306 return JIM_ERR;
17308 return JIM_OK;
17312 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17314 int i;
17316 if (argc < 2) {
17317 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
17318 return JIM_ERR;
17321 if (interp->framePtr->level == 0)
17322 return JIM_OK;
17323 for (i = 1; i < argc; i++) {
17325 const char *name = Jim_String(argv[i]);
17326 if (name[0] != ':' || name[1] != ':') {
17327 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
17328 return JIM_ERR;
17331 return JIM_OK;
17334 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
17335 Jim_Obj *objPtr, int nocase)
17337 int numMaps;
17338 const char *str, *noMatchStart = NULL;
17339 int strLen, i;
17340 Jim_Obj *resultObjPtr;
17342 numMaps = Jim_ListLength(interp, mapListObjPtr);
17343 if (numMaps % 2) {
17344 Jim_SetResultString(interp, "list must contain an even number of elements", -1);
17345 return NULL;
17348 str = Jim_String(objPtr);
17349 strLen = Jim_Utf8Length(interp, objPtr);
17352 resultObjPtr = Jim_NewStringObj(interp, "", 0);
17353 while (strLen) {
17354 for (i = 0; i < numMaps; i += 2) {
17355 Jim_Obj *objPtr;
17356 const char *k;
17357 int kl;
17359 Jim_ListIndex(interp, mapListObjPtr, i, &objPtr, JIM_NONE);
17360 k = Jim_String(objPtr);
17361 kl = Jim_Utf8Length(interp, objPtr);
17363 if (strLen >= kl && kl) {
17364 int rc;
17365 rc = JimStringCompareLen(str, k, kl, nocase);
17366 if (rc == 0) {
17367 if (noMatchStart) {
17368 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17369 noMatchStart = NULL;
17371 Jim_ListIndex(interp, mapListObjPtr, i + 1, &objPtr, JIM_NONE);
17372 Jim_AppendObj(interp, resultObjPtr, objPtr);
17373 str += utf8_index(str, kl);
17374 strLen -= kl;
17375 break;
17379 if (i == numMaps) {
17380 int c;
17381 if (noMatchStart == NULL)
17382 noMatchStart = str;
17383 str += utf8_tounicode(str, &c);
17384 strLen--;
17387 if (noMatchStart) {
17388 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17390 return resultObjPtr;
17394 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17396 int len;
17397 int opt_case = 1;
17398 int option;
17399 static const char * const options[] = {
17400 "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17401 "map", "repeat", "reverse", "index", "first", "last",
17402 "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17404 enum
17406 OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17407 OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST,
17408 OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17410 static const char * const nocase_options[] = {
17411 "-nocase", NULL
17413 static const char * const nocase_length_options[] = {
17414 "-nocase", "-length", NULL
17417 if (argc < 2) {
17418 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
17419 return JIM_ERR;
17421 if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
17422 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
17423 return JIM_ERR;
17425 switch (option) {
17426 case OPT_LENGTH:
17427 case OPT_BYTELENGTH:
17428 if (argc != 3) {
17429 Jim_WrongNumArgs(interp, 2, argv, "string");
17430 return JIM_ERR;
17432 if (option == OPT_LENGTH) {
17433 len = Jim_Utf8Length(interp, argv[2]);
17435 else {
17436 len = Jim_Length(argv[2]);
17438 Jim_SetResultInt(interp, len);
17439 return JIM_OK;
17441 case OPT_COMPARE:
17442 case OPT_EQUAL:
17445 long opt_length = -1;
17446 int n = argc - 4;
17447 int i = 2;
17448 while (n > 0) {
17449 int subopt;
17450 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
17451 JIM_ENUM_ABBREV) != JIM_OK) {
17452 badcompareargs:
17453 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
17454 return JIM_ERR;
17456 if (subopt == 0) {
17458 opt_case = 0;
17459 n--;
17461 else {
17463 if (n < 2) {
17464 goto badcompareargs;
17466 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
17467 return JIM_ERR;
17469 n -= 2;
17472 if (n) {
17473 goto badcompareargs;
17475 argv += argc - 2;
17476 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
17478 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
17480 else {
17481 if (opt_length >= 0) {
17482 n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
17484 else {
17485 n = Jim_StringCompareObj(interp, argv[0], argv[1], !opt_case);
17487 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
17489 return JIM_OK;
17492 case OPT_MATCH:
17493 if (argc != 4 &&
17494 (argc != 5 ||
17495 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17496 JIM_ENUM_ABBREV) != JIM_OK)) {
17497 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
17498 return JIM_ERR;
17500 if (opt_case == 0) {
17501 argv++;
17503 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
17504 return JIM_OK;
17506 case OPT_MAP:{
17507 Jim_Obj *objPtr;
17509 if (argc != 4 &&
17510 (argc != 5 ||
17511 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17512 JIM_ENUM_ABBREV) != JIM_OK)) {
17513 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
17514 return JIM_ERR;
17517 if (opt_case == 0) {
17518 argv++;
17520 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
17521 if (objPtr == NULL) {
17522 return JIM_ERR;
17524 Jim_SetResult(interp, objPtr);
17525 return JIM_OK;
17528 case OPT_RANGE:
17529 case OPT_BYTERANGE:{
17530 Jim_Obj *objPtr;
17532 if (argc != 5) {
17533 Jim_WrongNumArgs(interp, 2, argv, "string first last");
17534 return JIM_ERR;
17536 if (option == OPT_RANGE) {
17537 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
17539 else
17541 objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
17544 if (objPtr == NULL) {
17545 return JIM_ERR;
17547 Jim_SetResult(interp, objPtr);
17548 return JIM_OK;
17551 case OPT_REPLACE:{
17552 Jim_Obj *objPtr;
17554 if (argc != 5 && argc != 6) {
17555 Jim_WrongNumArgs(interp, 2, argv, "string first last ?string?");
17556 return JIM_ERR;
17558 objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
17559 if (objPtr == NULL) {
17560 return JIM_ERR;
17562 Jim_SetResult(interp, objPtr);
17563 return JIM_OK;
17567 case OPT_REPEAT:{
17568 Jim_Obj *objPtr;
17569 jim_wide count;
17571 if (argc != 4) {
17572 Jim_WrongNumArgs(interp, 2, argv, "string count");
17573 return JIM_ERR;
17575 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) {
17576 return JIM_ERR;
17578 objPtr = Jim_NewStringObj(interp, "", 0);
17579 if (count > 0) {
17580 while (count--) {
17581 Jim_AppendObj(interp, objPtr, argv[2]);
17584 Jim_SetResult(interp, objPtr);
17585 return JIM_OK;
17588 case OPT_REVERSE:{
17589 char *buf, *p;
17590 const char *str;
17591 int len;
17592 int i;
17594 if (argc != 3) {
17595 Jim_WrongNumArgs(interp, 2, argv, "string");
17596 return JIM_ERR;
17599 str = Jim_GetString(argv[2], &len);
17600 buf = Jim_Alloc(len + 1);
17601 p = buf + len;
17602 *p = 0;
17603 for (i = 0; i < len; ) {
17604 int c;
17605 int l = utf8_tounicode(str, &c);
17606 memcpy(p - l, str, l);
17607 p -= l;
17608 i += l;
17609 str += l;
17611 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
17612 return JIM_OK;
17615 case OPT_INDEX:{
17616 int idx;
17617 const char *str;
17619 if (argc != 4) {
17620 Jim_WrongNumArgs(interp, 2, argv, "string index");
17621 return JIM_ERR;
17623 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
17624 return JIM_ERR;
17626 str = Jim_String(argv[2]);
17627 len = Jim_Utf8Length(interp, argv[2]);
17628 if (idx != INT_MIN && idx != INT_MAX) {
17629 idx = JimRelToAbsIndex(len, idx);
17631 if (idx < 0 || idx >= len || str == NULL) {
17632 Jim_SetResultString(interp, "", 0);
17634 else if (len == Jim_Length(argv[2])) {
17636 Jim_SetResultString(interp, str + idx, 1);
17638 else {
17639 int c;
17640 int i = utf8_index(str, idx);
17641 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
17643 return JIM_OK;
17646 case OPT_FIRST:
17647 case OPT_LAST:{
17648 int idx = 0, l1, l2;
17649 const char *s1, *s2;
17651 if (argc != 4 && argc != 5) {
17652 Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?");
17653 return JIM_ERR;
17655 s1 = Jim_String(argv[2]);
17656 s2 = Jim_String(argv[3]);
17657 l1 = Jim_Utf8Length(interp, argv[2]);
17658 l2 = Jim_Utf8Length(interp, argv[3]);
17659 if (argc == 5) {
17660 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
17661 return JIM_ERR;
17663 idx = JimRelToAbsIndex(l2, idx);
17665 else if (option == OPT_LAST) {
17666 idx = l2;
17668 if (option == OPT_FIRST) {
17669 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
17671 else {
17672 #ifdef JIM_UTF8
17673 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
17674 #else
17675 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
17676 #endif
17678 return JIM_OK;
17681 case OPT_TRIM:
17682 case OPT_TRIMLEFT:
17683 case OPT_TRIMRIGHT:{
17684 Jim_Obj *trimchars;
17686 if (argc != 3 && argc != 4) {
17687 Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?");
17688 return JIM_ERR;
17690 trimchars = (argc == 4 ? argv[3] : NULL);
17691 if (option == OPT_TRIM) {
17692 Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars));
17694 else if (option == OPT_TRIMLEFT) {
17695 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars));
17697 else if (option == OPT_TRIMRIGHT) {
17698 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars));
17700 return JIM_OK;
17703 case OPT_TOLOWER:
17704 case OPT_TOUPPER:
17705 case OPT_TOTITLE:
17706 if (argc != 3) {
17707 Jim_WrongNumArgs(interp, 2, argv, "string");
17708 return JIM_ERR;
17710 if (option == OPT_TOLOWER) {
17711 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
17713 else if (option == OPT_TOUPPER) {
17714 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
17716 else {
17717 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
17719 return JIM_OK;
17721 case OPT_IS:
17722 if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) {
17723 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
17725 Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str");
17726 return JIM_ERR;
17728 return JIM_OK;
17732 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17734 long i, count = 1;
17735 jim_wide start, elapsed;
17736 char buf[60];
17737 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
17739 if (argc < 2) {
17740 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
17741 return JIM_ERR;
17743 if (argc == 3) {
17744 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
17745 return JIM_ERR;
17747 if (count < 0)
17748 return JIM_OK;
17749 i = count;
17750 start = JimClock();
17751 while (i-- > 0) {
17752 int retval;
17754 retval = Jim_EvalObj(interp, argv[1]);
17755 if (retval != JIM_OK) {
17756 return retval;
17759 elapsed = JimClock() - start;
17760 sprintf(buf, fmt, count == 0 ? 0 : elapsed / count);
17761 Jim_SetResultString(interp, buf, -1);
17762 return JIM_OK;
17766 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17768 long exitCode = 0;
17770 if (argc > 2) {
17771 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
17772 return JIM_ERR;
17774 if (argc == 2) {
17775 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
17776 return JIM_ERR;
17778 interp->exitCode = exitCode;
17779 return JIM_EXIT;
17783 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17785 int exitCode = 0;
17786 int i;
17787 int sig = 0;
17790 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
17791 static const int max_ignore_code = sizeof(ignore_mask) * 8;
17793 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
17795 for (i = 1; i < argc - 1; i++) {
17796 const char *arg = Jim_String(argv[i]);
17797 jim_wide option;
17798 int ignore;
17801 if (strcmp(arg, "--") == 0) {
17802 i++;
17803 break;
17805 if (*arg != '-') {
17806 break;
17809 if (strncmp(arg, "-no", 3) == 0) {
17810 arg += 3;
17811 ignore = 1;
17813 else {
17814 arg++;
17815 ignore = 0;
17818 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
17819 option = -1;
17821 if (option < 0) {
17822 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
17824 if (option < 0) {
17825 goto wrongargs;
17828 if (ignore) {
17829 ignore_mask |= (1 << option);
17831 else {
17832 ignore_mask &= ~(1 << option);
17836 argc -= i;
17837 if (argc < 1 || argc > 3) {
17838 wrongargs:
17839 Jim_WrongNumArgs(interp, 1, argv,
17840 "?-?no?code ... --? script ?resultVarName? ?optionVarName?");
17841 return JIM_ERR;
17843 argv += i;
17845 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
17846 sig++;
17849 interp->signal_level += sig;
17850 if (Jim_CheckSignal(interp)) {
17852 exitCode = JIM_SIGNAL;
17854 else {
17855 exitCode = Jim_EvalObj(interp, argv[0]);
17857 interp->signal_level -= sig;
17860 if (exitCode >= 0 && exitCode < max_ignore_code && ((1 << exitCode) & ignore_mask)) {
17862 return exitCode;
17865 if (sig && exitCode == JIM_SIGNAL) {
17867 if (interp->signal_set_result) {
17868 interp->signal_set_result(interp, interp->sigmask);
17870 else {
17871 Jim_SetResultInt(interp, interp->sigmask);
17873 interp->sigmask = 0;
17876 if (argc >= 2) {
17877 if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) {
17878 return JIM_ERR;
17880 if (argc == 3) {
17881 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
17883 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
17884 Jim_ListAppendElement(interp, optListObj,
17885 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
17886 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
17887 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
17888 if (exitCode == JIM_ERR) {
17889 Jim_Obj *errorCode;
17890 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
17891 -1));
17892 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
17894 errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
17895 if (errorCode) {
17896 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
17897 Jim_ListAppendElement(interp, optListObj, errorCode);
17900 if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
17901 return JIM_ERR;
17905 Jim_SetResultInt(interp, exitCode);
17906 return JIM_OK;
17909 #ifdef JIM_REFERENCES
17912 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17914 if (argc != 3 && argc != 4) {
17915 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
17916 return JIM_ERR;
17918 if (argc == 3) {
17919 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
17921 else {
17922 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], argv[3]));
17924 return JIM_OK;
17928 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17930 Jim_Reference *refPtr;
17932 if (argc != 2) {
17933 Jim_WrongNumArgs(interp, 1, argv, "reference");
17934 return JIM_ERR;
17936 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
17937 return JIM_ERR;
17938 Jim_SetResult(interp, refPtr->objPtr);
17939 return JIM_OK;
17943 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17945 Jim_Reference *refPtr;
17947 if (argc != 3) {
17948 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
17949 return JIM_ERR;
17951 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
17952 return JIM_ERR;
17953 Jim_IncrRefCount(argv[2]);
17954 Jim_DecrRefCount(interp, refPtr->objPtr);
17955 refPtr->objPtr = argv[2];
17956 Jim_SetResult(interp, argv[2]);
17957 return JIM_OK;
17961 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17963 if (argc != 1) {
17964 Jim_WrongNumArgs(interp, 1, argv, "");
17965 return JIM_ERR;
17967 Jim_SetResultInt(interp, Jim_Collect(interp));
17970 while (interp->freeList) {
17971 Jim_Obj *nextObjPtr = interp->freeList->nextObjPtr;
17972 Jim_Free(interp->freeList);
17973 interp->freeList = nextObjPtr;
17976 return JIM_OK;
17980 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17982 if (argc != 2 && argc != 3) {
17983 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
17984 return JIM_ERR;
17986 if (argc == 2) {
17987 Jim_Obj *cmdNamePtr;
17989 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
17990 return JIM_ERR;
17991 if (cmdNamePtr != NULL)
17992 Jim_SetResult(interp, cmdNamePtr);
17994 else {
17995 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
17996 return JIM_ERR;
17997 Jim_SetResult(interp, argv[2]);
17999 return JIM_OK;
18003 static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18005 Jim_Obj *listObjPtr;
18006 Jim_HashTableIterator htiter;
18007 Jim_HashEntry *he;
18009 listObjPtr = Jim_NewListObj(interp, NULL, 0);
18011 JimInitHashTableIterator(&interp->references, &htiter);
18012 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18013 char buf[JIM_REFERENCE_SPACE + 1];
18014 Jim_Reference *refPtr = he->u.val;
18015 const unsigned long *refId = he->key;
18017 JimFormatReference(buf, refPtr, *refId);
18018 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1));
18020 Jim_SetResult(interp, listObjPtr);
18021 return JIM_OK;
18023 #endif
18026 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18028 if (argc != 3) {
18029 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
18030 return JIM_ERR;
18033 if (JimValidName(interp, "new procedure", argv[2])) {
18034 return JIM_ERR;
18037 return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
18040 #define JIM_DICTMATCH_VALUES 0x0001
18042 typedef void JimDictMatchCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type);
18044 static void JimDictMatchKeys(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type)
18046 Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
18047 if (type & JIM_DICTMATCH_VALUES) {
18048 Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->u.val);
18052 static Jim_Obj *JimDictPatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
18053 JimDictMatchCallbackType *callback, int type)
18055 Jim_HashEntry *he;
18056 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18059 Jim_HashTableIterator htiter;
18060 JimInitHashTableIterator(ht, &htiter);
18061 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18062 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) {
18063 callback(interp, listObjPtr, he, type);
18067 return listObjPtr;
18071 int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
18073 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18074 return JIM_ERR;
18076 Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, 0));
18077 return JIM_OK;
18080 int Jim_DictValues(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
18082 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18083 return JIM_ERR;
18085 Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, JIM_DICTMATCH_VALUES));
18086 return JIM_OK;
18089 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
18091 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18092 return -1;
18094 return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
18098 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18100 Jim_Obj *objPtr;
18101 int option;
18102 static const char * const options[] = {
18103 "create", "get", "set", "unset", "exists", "keys", "merge", "size", "with", NULL
18105 enum
18107 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST, OPT_KEYS, OPT_MERGE, OPT_SIZE, OPT_WITH,
18110 if (argc < 2) {
18111 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
18112 return JIM_ERR;
18115 if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
18116 return JIM_ERR;
18119 switch (option) {
18120 case OPT_GET:
18121 if (argc < 3) {
18122 Jim_WrongNumArgs(interp, 2, argv, "varName ?key ...?");
18123 return JIM_ERR;
18125 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
18126 JIM_ERRMSG) != JIM_OK) {
18127 return JIM_ERR;
18129 Jim_SetResult(interp, objPtr);
18130 return JIM_OK;
18132 case OPT_SET:
18133 if (argc < 5) {
18134 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
18135 return JIM_ERR;
18137 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
18139 case OPT_EXIST:
18140 if (argc < 3) {
18141 Jim_WrongNumArgs(interp, 2, argv, "varName ?key ...?");
18142 return JIM_ERR;
18144 Jim_SetResultBool(interp, Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3,
18145 &objPtr, JIM_ERRMSG) == JIM_OK);
18146 return JIM_OK;
18148 case OPT_UNSET:
18149 if (argc < 4) {
18150 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
18151 return JIM_ERR;
18153 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE);
18155 case OPT_KEYS:
18156 if (argc != 3 && argc != 4) {
18157 Jim_WrongNumArgs(interp, 2, argv, "dictVar ?pattern?");
18158 return JIM_ERR;
18160 return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL);
18162 case OPT_SIZE: {
18163 int size;
18165 if (argc != 3) {
18166 Jim_WrongNumArgs(interp, 2, argv, "dictVar");
18167 return JIM_ERR;
18170 size = Jim_DictSize(interp, argv[2]);
18171 if (size < 0) {
18172 return JIM_ERR;
18174 Jim_SetResultInt(interp, size);
18175 return JIM_OK;
18178 case OPT_MERGE:
18179 if (argc == 2) {
18180 return JIM_OK;
18182 else if (SetDictFromAny(interp, argv[2]) != JIM_OK) {
18183 return JIM_ERR;
18185 else {
18186 return Jim_EvalPrefix(interp, "dict merge", argc - 2, argv + 2);
18189 case OPT_WITH:
18190 if (argc < 4) {
18191 Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script");
18192 return JIM_ERR;
18194 else if (Jim_GetVariable(interp, argv[2], JIM_ERRMSG) == NULL) {
18195 return JIM_ERR;
18197 else {
18198 return Jim_EvalPrefix(interp, "dict with", argc - 2, argv + 2);
18201 case OPT_CREATE:
18202 if (argc % 2) {
18203 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
18204 return JIM_ERR;
18206 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
18207 Jim_SetResult(interp, objPtr);
18208 return JIM_OK;
18210 return JIM_ERR;
18214 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18216 static const char * const options[] = {
18217 "-nobackslashes", "-nocommands", "-novariables", NULL
18219 enum
18220 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
18221 int i;
18222 int flags = JIM_SUBST_FLAG;
18223 Jim_Obj *objPtr;
18225 if (argc < 2) {
18226 Jim_WrongNumArgs(interp, 1, argv, "?options? string");
18227 return JIM_ERR;
18229 for (i = 1; i < (argc - 1); i++) {
18230 int option;
18232 if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
18233 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18234 return JIM_ERR;
18236 switch (option) {
18237 case OPT_NOBACKSLASHES:
18238 flags |= JIM_SUBST_NOESC;
18239 break;
18240 case OPT_NOCOMMANDS:
18241 flags |= JIM_SUBST_NOCMD;
18242 break;
18243 case OPT_NOVARIABLES:
18244 flags |= JIM_SUBST_NOVAR;
18245 break;
18248 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
18249 return JIM_ERR;
18251 Jim_SetResult(interp, objPtr);
18252 return JIM_OK;
18256 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18258 int cmd;
18259 Jim_Obj *objPtr;
18260 int mode = 0;
18262 static const char * const commands[] = {
18263 "body", "statics", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals",
18264 "vars", "version", "patchlevel", "complete", "args", "hostname",
18265 "script", "source", "stacktrace", "nameofexecutable", "returncodes",
18266 "references", "alias", NULL
18268 enum
18269 { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
18270 INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
18271 INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
18272 INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS
18275 #ifdef jim_ext_namespace
18276 int nons = 0;
18278 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
18280 argc--;
18281 argv++;
18282 nons = 1;
18284 #endif
18286 if (argc < 2) {
18287 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
18288 return JIM_ERR;
18290 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV)
18291 != JIM_OK) {
18292 return JIM_ERR;
18296 switch (cmd) {
18297 case INFO_EXISTS:
18298 if (argc != 3) {
18299 Jim_WrongNumArgs(interp, 2, argv, "varName");
18300 return JIM_ERR;
18302 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18303 break;
18305 case INFO_ALIAS:{
18306 Jim_Cmd *cmdPtr;
18308 if (argc != 3) {
18309 Jim_WrongNumArgs(interp, 2, argv, "command");
18310 return JIM_ERR;
18312 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18313 return JIM_ERR;
18315 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
18316 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
18317 return JIM_ERR;
18319 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
18320 return JIM_OK;
18323 case INFO_CHANNELS:
18324 mode++;
18325 #ifndef jim_ext_aio
18326 Jim_SetResultString(interp, "aio not enabled", -1);
18327 return JIM_ERR;
18328 #endif
18329 case INFO_PROCS:
18330 mode++;
18331 case INFO_COMMANDS:
18333 if (argc != 2 && argc != 3) {
18334 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18335 return JIM_ERR;
18337 #ifdef jim_ext_namespace
18338 if (!nons) {
18339 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18340 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18343 #endif
18344 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
18345 break;
18347 case INFO_VARS:
18348 mode++;
18349 case INFO_LOCALS:
18350 mode++;
18351 case INFO_GLOBALS:
18353 if (argc != 2 && argc != 3) {
18354 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18355 return JIM_ERR;
18357 #ifdef jim_ext_namespace
18358 if (!nons) {
18359 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18360 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18363 #endif
18364 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
18365 break;
18367 case INFO_SCRIPT:
18368 if (argc != 2) {
18369 Jim_WrongNumArgs(interp, 2, argv, "");
18370 return JIM_ERR;
18372 Jim_SetResult(interp, Jim_GetScript(interp, interp->currentScriptObj)->fileNameObj);
18373 break;
18375 case INFO_SOURCE:{
18376 int line;
18377 Jim_Obj *resObjPtr;
18378 Jim_Obj *fileNameObj;
18380 if (argc != 3) {
18381 Jim_WrongNumArgs(interp, 2, argv, "source");
18382 return JIM_ERR;
18384 if (argv[2]->typePtr == &sourceObjType) {
18385 fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18386 line = argv[2]->internalRep.sourceValue.lineNumber;
18388 else if (argv[2]->typePtr == &scriptObjType) {
18389 ScriptObj *script = Jim_GetScript(interp, argv[2]);
18390 fileNameObj = script->fileNameObj;
18391 line = script->firstline;
18393 else {
18394 fileNameObj = interp->emptyObj;
18395 line = 1;
18397 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18398 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18399 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18400 Jim_SetResult(interp, resObjPtr);
18401 break;
18404 case INFO_STACKTRACE:
18405 Jim_SetResult(interp, interp->stackTrace);
18406 break;
18408 case INFO_LEVEL:
18409 case INFO_FRAME:
18410 switch (argc) {
18411 case 2:
18412 Jim_SetResultInt(interp, interp->framePtr->level);
18413 break;
18415 case 3:
18416 if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) {
18417 return JIM_ERR;
18419 Jim_SetResult(interp, objPtr);
18420 break;
18422 default:
18423 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
18424 return JIM_ERR;
18426 break;
18428 case INFO_BODY:
18429 case INFO_STATICS:
18430 case INFO_ARGS:{
18431 Jim_Cmd *cmdPtr;
18433 if (argc != 3) {
18434 Jim_WrongNumArgs(interp, 2, argv, "procname");
18435 return JIM_ERR;
18437 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18438 return JIM_ERR;
18440 if (!cmdPtr->isproc) {
18441 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
18442 return JIM_ERR;
18444 switch (cmd) {
18445 case INFO_BODY:
18446 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
18447 break;
18448 case INFO_ARGS:
18449 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
18450 break;
18451 case INFO_STATICS:
18452 if (cmdPtr->u.proc.staticVars) {
18453 int mode = JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES;
18454 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
18455 NULL, JimVariablesMatch, mode));
18457 break;
18459 break;
18462 case INFO_VERSION:
18463 case INFO_PATCHLEVEL:{
18464 char buf[(JIM_INTEGER_SPACE * 2) + 1];
18466 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
18467 Jim_SetResultString(interp, buf, -1);
18468 break;
18471 case INFO_COMPLETE:
18472 if (argc != 3 && argc != 4) {
18473 Jim_WrongNumArgs(interp, 2, argv, "script ?missing?");
18474 return JIM_ERR;
18476 else {
18477 int len;
18478 const char *s = Jim_GetString(argv[2], &len);
18479 char missing;
18481 Jim_SetResultBool(interp, Jim_ScriptIsComplete(s, len, &missing));
18482 if (missing != ' ' && argc == 4) {
18483 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
18486 break;
18488 case INFO_HOSTNAME:
18490 return Jim_Eval(interp, "os.gethostname");
18492 case INFO_NAMEOFEXECUTABLE:
18494 return Jim_Eval(interp, "{info nameofexecutable}");
18496 case INFO_RETURNCODES:
18497 if (argc == 2) {
18498 int i;
18499 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18501 for (i = 0; jimReturnCodes[i]; i++) {
18502 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
18503 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
18504 jimReturnCodes[i], -1));
18507 Jim_SetResult(interp, listObjPtr);
18509 else if (argc == 3) {
18510 long code;
18511 const char *name;
18513 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
18514 return JIM_ERR;
18516 name = Jim_ReturnCode(code);
18517 if (*name == '?') {
18518 Jim_SetResultInt(interp, code);
18520 else {
18521 Jim_SetResultString(interp, name, -1);
18524 else {
18525 Jim_WrongNumArgs(interp, 2, argv, "?code?");
18526 return JIM_ERR;
18528 break;
18529 case INFO_REFERENCES:
18530 #ifdef JIM_REFERENCES
18531 return JimInfoReferences(interp, argc, argv);
18532 #else
18533 Jim_SetResultString(interp, "not supported", -1);
18534 return JIM_ERR;
18535 #endif
18537 return JIM_OK;
18541 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18543 Jim_Obj *objPtr;
18544 int result = 0;
18546 static const char * const options[] = {
18547 "-command", "-proc", "-alias", "-var", NULL
18549 enum
18551 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
18553 int option;
18555 if (argc == 2) {
18556 option = OPT_VAR;
18557 objPtr = argv[1];
18559 else if (argc == 3) {
18560 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18561 return JIM_ERR;
18563 objPtr = argv[2];
18565 else {
18566 Jim_WrongNumArgs(interp, 1, argv, "?option? name");
18567 return JIM_ERR;
18570 if (option == OPT_VAR) {
18571 result = Jim_GetVariable(interp, objPtr, 0) != NULL;
18573 else {
18575 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
18577 if (cmd) {
18578 switch (option) {
18579 case OPT_COMMAND:
18580 result = 1;
18581 break;
18583 case OPT_ALIAS:
18584 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
18585 break;
18587 case OPT_PROC:
18588 result = cmd->isproc;
18589 break;
18593 Jim_SetResultBool(interp, result);
18594 return JIM_OK;
18598 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18600 const char *str, *splitChars, *noMatchStart;
18601 int splitLen, strLen;
18602 Jim_Obj *resObjPtr;
18603 int c;
18604 int len;
18606 if (argc != 2 && argc != 3) {
18607 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
18608 return JIM_ERR;
18611 str = Jim_GetString(argv[1], &len);
18612 if (len == 0) {
18613 return JIM_OK;
18615 strLen = Jim_Utf8Length(interp, argv[1]);
18618 if (argc == 2) {
18619 splitChars = " \n\t\r";
18620 splitLen = 4;
18622 else {
18623 splitChars = Jim_String(argv[2]);
18624 splitLen = Jim_Utf8Length(interp, argv[2]);
18627 noMatchStart = str;
18628 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18631 if (splitLen) {
18632 Jim_Obj *objPtr;
18633 while (strLen--) {
18634 const char *sc = splitChars;
18635 int scLen = splitLen;
18636 int sl = utf8_tounicode(str, &c);
18637 while (scLen--) {
18638 int pc;
18639 sc += utf8_tounicode(sc, &pc);
18640 if (c == pc) {
18641 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
18642 Jim_ListAppendElement(interp, resObjPtr, objPtr);
18643 noMatchStart = str + sl;
18644 break;
18647 str += sl;
18649 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
18650 Jim_ListAppendElement(interp, resObjPtr, objPtr);
18652 else {
18653 Jim_Obj **commonObj = NULL;
18654 #define NUM_COMMON (128 - 9)
18655 while (strLen--) {
18656 int n = utf8_tounicode(str, &c);
18657 #ifdef JIM_OPTIMIZATION
18658 if (c >= 9 && c < 128) {
18660 c -= 9;
18661 if (!commonObj) {
18662 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
18663 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
18665 if (!commonObj[c]) {
18666 commonObj[c] = Jim_NewStringObj(interp, str, 1);
18668 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
18669 str++;
18670 continue;
18672 #endif
18673 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
18674 str += n;
18676 Jim_Free(commonObj);
18679 Jim_SetResult(interp, resObjPtr);
18680 return JIM_OK;
18684 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18686 const char *joinStr;
18687 int joinStrLen;
18689 if (argc != 2 && argc != 3) {
18690 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
18691 return JIM_ERR;
18694 if (argc == 2) {
18695 joinStr = " ";
18696 joinStrLen = 1;
18698 else {
18699 joinStr = Jim_GetString(argv[2], &joinStrLen);
18701 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
18702 return JIM_OK;
18706 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18708 Jim_Obj *objPtr;
18710 if (argc < 2) {
18711 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
18712 return JIM_ERR;
18714 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
18715 if (objPtr == NULL)
18716 return JIM_ERR;
18717 Jim_SetResult(interp, objPtr);
18718 return JIM_OK;
18722 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18724 Jim_Obj *listPtr, **outVec;
18725 int outc, i;
18727 if (argc < 3) {
18728 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
18729 return JIM_ERR;
18731 if (argv[2]->typePtr != &scanFmtStringObjType)
18732 SetScanFmtFromAny(interp, argv[2]);
18733 if (FormatGetError(argv[2]) != 0) {
18734 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
18735 return JIM_ERR;
18737 if (argc > 3) {
18738 int maxPos = FormatGetMaxPos(argv[2]);
18739 int count = FormatGetCnvCount(argv[2]);
18741 if (maxPos > argc - 3) {
18742 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
18743 return JIM_ERR;
18745 else if (count > argc - 3) {
18746 Jim_SetResultString(interp, "different numbers of variable names and "
18747 "field specifiers", -1);
18748 return JIM_ERR;
18750 else if (count < argc - 3) {
18751 Jim_SetResultString(interp, "variable is not assigned by any "
18752 "conversion specifiers", -1);
18753 return JIM_ERR;
18756 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
18757 if (listPtr == 0)
18758 return JIM_ERR;
18759 if (argc > 3) {
18760 int rc = JIM_OK;
18761 int count = 0;
18763 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
18764 int len = Jim_ListLength(interp, listPtr);
18766 if (len != 0) {
18767 JimListGetElements(interp, listPtr, &outc, &outVec);
18768 for (i = 0; i < outc; ++i) {
18769 if (Jim_Length(outVec[i]) > 0) {
18770 ++count;
18771 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
18772 rc = JIM_ERR;
18777 Jim_FreeNewObj(interp, listPtr);
18779 else {
18780 count = -1;
18782 if (rc == JIM_OK) {
18783 Jim_SetResultInt(interp, count);
18785 return rc;
18787 else {
18788 if (listPtr == (Jim_Obj *)EOF) {
18789 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
18790 return JIM_OK;
18792 Jim_SetResult(interp, listPtr);
18794 return JIM_OK;
18798 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18800 if (argc != 2 && argc != 3) {
18801 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
18802 return JIM_ERR;
18804 Jim_SetResult(interp, argv[1]);
18805 if (argc == 3) {
18806 JimSetStackTrace(interp, argv[2]);
18807 return JIM_ERR;
18809 interp->addStackTrace++;
18810 return JIM_ERR;
18814 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18816 Jim_Obj *objPtr;
18818 if (argc != 4) {
18819 Jim_WrongNumArgs(interp, 1, argv, "list first last");
18820 return JIM_ERR;
18822 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
18823 return JIM_ERR;
18824 Jim_SetResult(interp, objPtr);
18825 return JIM_OK;
18829 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18831 Jim_Obj *objPtr;
18832 long count;
18834 if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) {
18835 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
18836 return JIM_ERR;
18839 if (count == 0 || argc == 2) {
18840 return JIM_OK;
18843 argc -= 2;
18844 argv += 2;
18846 objPtr = Jim_NewListObj(interp, argv, argc);
18847 while (--count) {
18848 ListInsertElements(objPtr, -1, argc, argv);
18851 Jim_SetResult(interp, objPtr);
18852 return JIM_OK;
18855 char **Jim_GetEnviron(void)
18857 #if defined(HAVE__NSGETENVIRON)
18858 return *_NSGetEnviron();
18859 #else
18860 #if !defined(NO_ENVIRON_EXTERN)
18861 extern char **environ;
18862 #endif
18864 return environ;
18865 #endif
18868 void Jim_SetEnviron(char **env)
18870 #if defined(HAVE__NSGETENVIRON)
18871 *_NSGetEnviron() = env;
18872 #else
18873 #if !defined(NO_ENVIRON_EXTERN)
18874 extern char **environ;
18875 #endif
18877 environ = env;
18878 #endif
18882 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18884 const char *key;
18885 const char *val;
18887 if (argc == 1) {
18888 char **e = Jim_GetEnviron();
18890 int i;
18891 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18893 for (i = 0; e[i]; i++) {
18894 const char *equals = strchr(e[i], '=');
18896 if (equals) {
18897 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
18898 equals - e[i]));
18899 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
18903 Jim_SetResult(interp, listObjPtr);
18904 return JIM_OK;
18907 if (argc < 2) {
18908 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
18909 return JIM_ERR;
18911 key = Jim_String(argv[1]);
18912 val = getenv(key);
18913 if (val == NULL) {
18914 if (argc < 3) {
18915 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
18916 return JIM_ERR;
18918 val = Jim_String(argv[2]);
18920 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
18921 return JIM_OK;
18925 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18927 int retval;
18929 if (argc != 2) {
18930 Jim_WrongNumArgs(interp, 1, argv, "fileName");
18931 return JIM_ERR;
18933 retval = Jim_EvalFile(interp, Jim_String(argv[1]));
18934 if (retval == JIM_RETURN)
18935 return JIM_OK;
18936 return retval;
18940 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18942 Jim_Obj *revObjPtr, **ele;
18943 int len;
18945 if (argc != 2) {
18946 Jim_WrongNumArgs(interp, 1, argv, "list");
18947 return JIM_ERR;
18949 JimListGetElements(interp, argv[1], &len, &ele);
18950 len--;
18951 revObjPtr = Jim_NewListObj(interp, NULL, 0);
18952 while (len >= 0)
18953 ListAppendElement(revObjPtr, ele[len--]);
18954 Jim_SetResult(interp, revObjPtr);
18955 return JIM_OK;
18958 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
18960 jim_wide len;
18962 if (step == 0)
18963 return -1;
18964 if (start == end)
18965 return 0;
18966 else if (step > 0 && start > end)
18967 return -1;
18968 else if (step < 0 && end > start)
18969 return -1;
18970 len = end - start;
18971 if (len < 0)
18972 len = -len;
18973 if (step < 0)
18974 step = -step;
18975 len = 1 + ((len - 1) / step);
18976 if (len > INT_MAX)
18977 len = INT_MAX;
18978 return (int)((len < 0) ? -1 : len);
18982 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18984 jim_wide start = 0, end, step = 1;
18985 int len, i;
18986 Jim_Obj *objPtr;
18988 if (argc < 2 || argc > 4) {
18989 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
18990 return JIM_ERR;
18992 if (argc == 2) {
18993 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
18994 return JIM_ERR;
18996 else {
18997 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
18998 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
18999 return JIM_ERR;
19000 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
19001 return JIM_ERR;
19003 if ((len = JimRangeLen(start, end, step)) == -1) {
19004 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
19005 return JIM_ERR;
19007 objPtr = Jim_NewListObj(interp, NULL, 0);
19008 for (i = 0; i < len; i++)
19009 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
19010 Jim_SetResult(interp, objPtr);
19011 return JIM_OK;
19015 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19017 jim_wide min = 0, max = 0, len, maxMul;
19019 if (argc < 1 || argc > 3) {
19020 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
19021 return JIM_ERR;
19023 if (argc == 1) {
19024 max = JIM_WIDE_MAX;
19025 } else if (argc == 2) {
19026 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
19027 return JIM_ERR;
19028 } else if (argc == 3) {
19029 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
19030 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
19031 return JIM_ERR;
19033 len = max-min;
19034 if (len < 0) {
19035 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
19036 return JIM_ERR;
19038 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
19039 while (1) {
19040 jim_wide r;
19042 JimRandomBytes(interp, &r, sizeof(jim_wide));
19043 if (r < 0 || r >= maxMul) continue;
19044 r = (len == 0) ? 0 : r%len;
19045 Jim_SetResultInt(interp, min+r);
19046 return JIM_OK;
19050 static const struct {
19051 const char *name;
19052 Jim_CmdProc cmdProc;
19053 } Jim_CoreCommandsTable[] = {
19054 {"alias", Jim_AliasCoreCommand},
19055 {"set", Jim_SetCoreCommand},
19056 {"unset", Jim_UnsetCoreCommand},
19057 {"puts", Jim_PutsCoreCommand},
19058 {"+", Jim_AddCoreCommand},
19059 {"*", Jim_MulCoreCommand},
19060 {"-", Jim_SubCoreCommand},
19061 {"/", Jim_DivCoreCommand},
19062 {"incr", Jim_IncrCoreCommand},
19063 {"while", Jim_WhileCoreCommand},
19064 {"loop", Jim_LoopCoreCommand},
19065 {"for", Jim_ForCoreCommand},
19066 {"foreach", Jim_ForeachCoreCommand},
19067 {"lmap", Jim_LmapCoreCommand},
19068 {"lassign", Jim_LassignCoreCommand},
19069 {"if", Jim_IfCoreCommand},
19070 {"switch", Jim_SwitchCoreCommand},
19071 {"list", Jim_ListCoreCommand},
19072 {"lindex", Jim_LindexCoreCommand},
19073 {"lset", Jim_LsetCoreCommand},
19074 {"lsearch", Jim_LsearchCoreCommand},
19075 {"llength", Jim_LlengthCoreCommand},
19076 {"lappend", Jim_LappendCoreCommand},
19077 {"linsert", Jim_LinsertCoreCommand},
19078 {"lreplace", Jim_LreplaceCoreCommand},
19079 {"lsort", Jim_LsortCoreCommand},
19080 {"append", Jim_AppendCoreCommand},
19081 {"debug", Jim_DebugCoreCommand},
19082 {"eval", Jim_EvalCoreCommand},
19083 {"uplevel", Jim_UplevelCoreCommand},
19084 {"expr", Jim_ExprCoreCommand},
19085 {"break", Jim_BreakCoreCommand},
19086 {"continue", Jim_ContinueCoreCommand},
19087 {"proc", Jim_ProcCoreCommand},
19088 {"concat", Jim_ConcatCoreCommand},
19089 {"return", Jim_ReturnCoreCommand},
19090 {"upvar", Jim_UpvarCoreCommand},
19091 {"global", Jim_GlobalCoreCommand},
19092 {"string", Jim_StringCoreCommand},
19093 {"time", Jim_TimeCoreCommand},
19094 {"exit", Jim_ExitCoreCommand},
19095 {"catch", Jim_CatchCoreCommand},
19096 #ifdef JIM_REFERENCES
19097 {"ref", Jim_RefCoreCommand},
19098 {"getref", Jim_GetrefCoreCommand},
19099 {"setref", Jim_SetrefCoreCommand},
19100 {"finalize", Jim_FinalizeCoreCommand},
19101 {"collect", Jim_CollectCoreCommand},
19102 #endif
19103 {"rename", Jim_RenameCoreCommand},
19104 {"dict", Jim_DictCoreCommand},
19105 {"subst", Jim_SubstCoreCommand},
19106 {"info", Jim_InfoCoreCommand},
19107 {"exists", Jim_ExistsCoreCommand},
19108 {"split", Jim_SplitCoreCommand},
19109 {"join", Jim_JoinCoreCommand},
19110 {"format", Jim_FormatCoreCommand},
19111 {"scan", Jim_ScanCoreCommand},
19112 {"error", Jim_ErrorCoreCommand},
19113 {"lrange", Jim_LrangeCoreCommand},
19114 {"lrepeat", Jim_LrepeatCoreCommand},
19115 {"env", Jim_EnvCoreCommand},
19116 {"source", Jim_SourceCoreCommand},
19117 {"lreverse", Jim_LreverseCoreCommand},
19118 {"range", Jim_RangeCoreCommand},
19119 {"rand", Jim_RandCoreCommand},
19120 {"tailcall", Jim_TailcallCoreCommand},
19121 {"local", Jim_LocalCoreCommand},
19122 {"upcall", Jim_UpcallCoreCommand},
19123 {"apply", Jim_ApplyCoreCommand},
19124 {NULL, NULL},
19127 void Jim_RegisterCoreCommands(Jim_Interp *interp)
19129 int i = 0;
19131 while (Jim_CoreCommandsTable[i].name != NULL) {
19132 Jim_CreateCommand(interp,
19133 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
19134 i++;
19138 void Jim_MakeErrorMessage(Jim_Interp *interp)
19140 Jim_Obj *argv[2];
19142 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
19143 argv[1] = interp->result;
19145 Jim_EvalObjVector(interp, 2, argv);
19148 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
19149 const char *prefix, const char *const *tablePtr, const char *name)
19151 int count;
19152 char **tablePtrSorted;
19153 int i;
19155 for (count = 0; tablePtr[count]; count++) {
19158 if (name == NULL) {
19159 name = "option";
19162 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
19163 tablePtrSorted = Jim_Alloc(sizeof(char *) * count);
19164 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
19165 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
19166 for (i = 0; i < count; i++) {
19167 if (i + 1 == count && count > 1) {
19168 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
19170 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
19171 if (i + 1 != count) {
19172 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
19175 Jim_Free(tablePtrSorted);
19178 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
19179 const char *const *tablePtr, int *indexPtr, const char *name, int flags)
19181 const char *bad = "bad ";
19182 const char *const *entryPtr = NULL;
19183 int i;
19184 int match = -1;
19185 int arglen;
19186 const char *arg = Jim_GetString(objPtr, &arglen);
19188 *indexPtr = -1;
19190 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
19191 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
19193 *indexPtr = i;
19194 return JIM_OK;
19196 if (flags & JIM_ENUM_ABBREV) {
19197 if (strncmp(arg, *entryPtr, arglen) == 0) {
19198 if (*arg == '-' && arglen == 1) {
19199 break;
19201 if (match >= 0) {
19202 bad = "ambiguous ";
19203 goto ambiguous;
19205 match = i;
19211 if (match >= 0) {
19212 *indexPtr = match;
19213 return JIM_OK;
19216 ambiguous:
19217 if (flags & JIM_ERRMSG) {
19218 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19220 return JIM_ERR;
19223 int Jim_FindByName(const char *name, const char * const array[], size_t len)
19225 int i;
19227 for (i = 0; i < (int)len; i++) {
19228 if (array[i] && strcmp(array[i], name) == 0) {
19229 return i;
19232 return -1;
19235 int Jim_IsDict(Jim_Obj *objPtr)
19237 return objPtr->typePtr == &dictObjType;
19240 int Jim_IsList(Jim_Obj *objPtr)
19242 return objPtr->typePtr == &listObjType;
19245 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
19248 int len = strlen(format);
19249 int extra = 0;
19250 int n = 0;
19251 const char *params[5];
19252 char *buf;
19253 va_list args;
19254 int i;
19256 va_start(args, format);
19258 for (i = 0; i < len && n < 5; i++) {
19259 int l;
19261 if (strncmp(format + i, "%s", 2) == 0) {
19262 params[n] = va_arg(args, char *);
19264 l = strlen(params[n]);
19266 else if (strncmp(format + i, "%#s", 3) == 0) {
19267 Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
19269 params[n] = Jim_GetString(objPtr, &l);
19271 else {
19272 if (format[i] == '%') {
19273 i++;
19275 continue;
19277 n++;
19278 extra += l;
19281 len += extra;
19282 buf = Jim_Alloc(len + 1);
19283 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
19285 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19289 #ifndef jim_ext_package
19290 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
19292 return JIM_OK;
19294 #endif
19295 #ifndef jim_ext_aio
19296 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
19298 Jim_SetResultString(interp, "aio not enabled", -1);
19299 return NULL;
19301 #endif
19304 #include <stdio.h>
19305 #include <string.h>
19308 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19311 return JIM_OK;
19314 static const jim_subcmd_type dummy_subcmd = {
19315 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
19318 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
19320 const char *s = "";
19322 for (; ct->cmd; ct++) {
19323 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
19324 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
19325 s = sep;
19330 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
19331 Jim_Obj *cmd, Jim_Obj *subcmd)
19333 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19334 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
19335 " command \"", Jim_String(subcmd), "\": should be ", NULL);
19336 add_commands(interp, command_table, ", ");
19339 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
19340 Jim_Obj *const *argv)
19342 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19343 Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
19344 " command ... \", where command is one of: ", NULL);
19345 add_commands(interp, command_table, ", ");
19348 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
19350 if (cmd) {
19351 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
19353 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
19354 if (ct->args && *ct->args) {
19355 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
19359 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
19361 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19362 add_cmd_usage(interp, command_table, subcmd);
19363 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19366 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
19367 int argc, Jim_Obj *const *argv)
19369 const jim_subcmd_type *ct;
19370 const jim_subcmd_type *partial = 0;
19371 int cmdlen;
19372 Jim_Obj *cmd;
19373 const char *cmdstr;
19374 const char *cmdname;
19375 int help = 0;
19377 cmdname = Jim_String(argv[0]);
19379 if (argc < 2) {
19380 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19381 Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
19382 " command ...\"\n", NULL);
19383 Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
19384 return 0;
19387 cmd = argv[1];
19390 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
19391 if (argc == 2) {
19393 show_cmd_usage(interp, command_table, argc, argv);
19394 return &dummy_subcmd;
19396 help = 1;
19399 cmd = argv[2];
19403 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
19405 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19406 add_commands(interp, command_table, " ");
19407 return &dummy_subcmd;
19410 cmdstr = Jim_GetString(cmd, &cmdlen);
19412 for (ct = command_table; ct->cmd; ct++) {
19413 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
19415 break;
19417 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
19418 if (partial) {
19420 if (help) {
19422 show_cmd_usage(interp, command_table, argc, argv);
19423 return &dummy_subcmd;
19425 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
19426 return 0;
19428 partial = ct;
19430 continue;
19434 if (partial && !ct->cmd) {
19435 ct = partial;
19438 if (!ct->cmd) {
19440 if (help) {
19442 show_cmd_usage(interp, command_table, argc, argv);
19443 return &dummy_subcmd;
19445 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
19446 return 0;
19449 if (help) {
19450 Jim_SetResultString(interp, "Usage: ", -1);
19452 add_cmd_usage(interp, ct, argv[0]);
19453 return &dummy_subcmd;
19457 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
19458 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19460 add_cmd_usage(interp, ct, argv[0]);
19461 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19463 return 0;
19467 return ct;
19470 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
19472 int ret = JIM_ERR;
19474 if (ct) {
19475 if (ct->flags & JIM_MODFLAG_FULLARGV) {
19476 ret = ct->function(interp, argc, argv);
19478 else {
19479 ret = ct->function(interp, argc - 2, argv + 2);
19481 if (ret < 0) {
19482 set_wrong_args(interp, ct, argv[0]);
19483 ret = JIM_ERR;
19486 return ret;
19489 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19491 const jim_subcmd_type *ct =
19492 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
19494 return Jim_CallSubCmd(interp, ct, argc, argv);
19497 #include <ctype.h>
19498 #include <stdlib.h>
19499 #include <string.h>
19500 #include <stdio.h>
19501 #include <assert.h>
19504 int utf8_fromunicode(char *p, unsigned uc)
19506 if (uc <= 0x7f) {
19507 *p = uc;
19508 return 1;
19510 else if (uc <= 0x7ff) {
19511 *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
19512 *p = 0x80 | (uc & 0x3f);
19513 return 2;
19515 else if (uc <= 0xffff) {
19516 *p++ = 0xe0 | ((uc & 0xf000) >> 12);
19517 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19518 *p = 0x80 | (uc & 0x3f);
19519 return 3;
19522 else {
19523 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
19524 *p++ = 0x80 | ((uc & 0x3f000) >> 12);
19525 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19526 *p = 0x80 | (uc & 0x3f);
19527 return 4;
19531 #include <ctype.h>
19532 #include <string.h>
19535 #define JIM_UTF_MAX 3
19536 #define JIM_INTEGER_SPACE 24
19537 #define MAX_FLOAT_WIDTH 320
19539 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
19541 const char *span, *format, *formatEnd, *msg;
19542 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
19543 static const char * const mixedXPG =
19544 "cannot mix \"%\" and \"%n$\" conversion specifiers";
19545 static const char * const badIndex[2] = {
19546 "not enough arguments for all format specifiers",
19547 "\"%n$\" argument index out of range"
19549 int formatLen;
19550 Jim_Obj *resultPtr;
19552 char *num_buffer = NULL;
19553 int num_buffer_size = 0;
19555 span = format = Jim_GetString(fmtObjPtr, &formatLen);
19556 formatEnd = format + formatLen;
19557 resultPtr = Jim_NewEmptyStringObj(interp);
19559 while (format != formatEnd) {
19560 char *end;
19561 int gotMinus, sawFlag;
19562 int gotPrecision, useShort;
19563 long width, precision;
19564 int newXpg;
19565 int ch;
19566 int step;
19567 int doubleType;
19568 char pad = ' ';
19569 char spec[2*JIM_INTEGER_SPACE + 12];
19570 char *p;
19572 int formatted_chars;
19573 int formatted_bytes;
19574 const char *formatted_buf;
19576 step = utf8_tounicode(format, &ch);
19577 format += step;
19578 if (ch != '%') {
19579 numBytes += step;
19580 continue;
19582 if (numBytes) {
19583 Jim_AppendString(interp, resultPtr, span, numBytes);
19584 numBytes = 0;
19588 step = utf8_tounicode(format, &ch);
19589 if (ch == '%') {
19590 span = format;
19591 numBytes = step;
19592 format += step;
19593 continue;
19597 newXpg = 0;
19598 if (isdigit(ch)) {
19599 int position = strtoul(format, &end, 10);
19600 if (*end == '$') {
19601 newXpg = 1;
19602 objIndex = position - 1;
19603 format = end + 1;
19604 step = utf8_tounicode(format, &ch);
19607 if (newXpg) {
19608 if (gotSequential) {
19609 msg = mixedXPG;
19610 goto errorMsg;
19612 gotXpg = 1;
19613 } else {
19614 if (gotXpg) {
19615 msg = mixedXPG;
19616 goto errorMsg;
19618 gotSequential = 1;
19620 if ((objIndex < 0) || (objIndex >= objc)) {
19621 msg = badIndex[gotXpg];
19622 goto errorMsg;
19625 p = spec;
19626 *p++ = '%';
19628 gotMinus = 0;
19629 sawFlag = 1;
19630 do {
19631 switch (ch) {
19632 case '-':
19633 gotMinus = 1;
19634 break;
19635 case '0':
19636 pad = ch;
19637 break;
19638 case ' ':
19639 case '+':
19640 case '#':
19641 break;
19642 default:
19643 sawFlag = 0;
19644 continue;
19646 *p++ = ch;
19647 format += step;
19648 step = utf8_tounicode(format, &ch);
19649 } while (sawFlag);
19652 width = 0;
19653 if (isdigit(ch)) {
19654 width = strtoul(format, &end, 10);
19655 format = end;
19656 step = utf8_tounicode(format, &ch);
19657 } else if (ch == '*') {
19658 if (objIndex >= objc - 1) {
19659 msg = badIndex[gotXpg];
19660 goto errorMsg;
19662 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
19663 goto error;
19665 if (width < 0) {
19666 width = -width;
19667 if (!gotMinus) {
19668 *p++ = '-';
19669 gotMinus = 1;
19672 objIndex++;
19673 format += step;
19674 step = utf8_tounicode(format, &ch);
19678 gotPrecision = precision = 0;
19679 if (ch == '.') {
19680 gotPrecision = 1;
19681 format += step;
19682 step = utf8_tounicode(format, &ch);
19684 if (isdigit(ch)) {
19685 precision = strtoul(format, &end, 10);
19686 format = end;
19687 step = utf8_tounicode(format, &ch);
19688 } else if (ch == '*') {
19689 if (objIndex >= objc - 1) {
19690 msg = badIndex[gotXpg];
19691 goto errorMsg;
19693 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
19694 goto error;
19698 if (precision < 0) {
19699 precision = 0;
19701 objIndex++;
19702 format += step;
19703 step = utf8_tounicode(format, &ch);
19707 useShort = 0;
19708 if (ch == 'h') {
19709 useShort = 1;
19710 format += step;
19711 step = utf8_tounicode(format, &ch);
19712 } else if (ch == 'l') {
19714 format += step;
19715 step = utf8_tounicode(format, &ch);
19716 if (ch == 'l') {
19717 format += step;
19718 step = utf8_tounicode(format, &ch);
19722 format += step;
19723 span = format;
19726 if (ch == 'i') {
19727 ch = 'd';
19730 doubleType = 0;
19732 switch (ch) {
19733 case '\0':
19734 msg = "format string ended in middle of field specifier";
19735 goto errorMsg;
19736 case 's': {
19737 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
19738 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
19739 if (gotPrecision && (precision < formatted_chars)) {
19741 formatted_chars = precision;
19742 formatted_bytes = utf8_index(formatted_buf, precision);
19744 break;
19746 case 'c': {
19747 jim_wide code;
19749 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
19750 goto error;
19753 formatted_bytes = utf8_fromunicode(spec, code);
19754 formatted_buf = spec;
19755 formatted_chars = 1;
19756 break;
19759 case 'e':
19760 case 'E':
19761 case 'f':
19762 case 'g':
19763 case 'G':
19764 doubleType = 1;
19766 case 'd':
19767 case 'u':
19768 case 'o':
19769 case 'x':
19770 case 'X': {
19771 jim_wide w;
19772 double d;
19773 int length;
19776 if (width) {
19777 p += sprintf(p, "%ld", width);
19779 if (gotPrecision) {
19780 p += sprintf(p, ".%ld", precision);
19784 if (doubleType) {
19785 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
19786 goto error;
19788 length = MAX_FLOAT_WIDTH;
19790 else {
19791 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
19792 goto error;
19794 length = JIM_INTEGER_SPACE;
19795 if (useShort) {
19796 *p++ = 'h';
19797 if (ch == 'd') {
19798 w = (short)w;
19800 else {
19801 w = (unsigned short)w;
19804 else {
19805 *p++ = 'l';
19806 #ifdef HAVE_LONG_LONG
19807 if (sizeof(long long) == sizeof(jim_wide)) {
19808 *p++ = 'l';
19810 #endif
19814 *p++ = (char) ch;
19815 *p = '\0';
19818 if (width > length) {
19819 length = width;
19821 if (gotPrecision) {
19822 length += precision;
19826 if (num_buffer_size < length + 1) {
19827 num_buffer_size = length + 1;
19828 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
19831 if (doubleType) {
19832 snprintf(num_buffer, length + 1, spec, d);
19834 else {
19835 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
19837 formatted_chars = formatted_bytes = strlen(num_buffer);
19838 formatted_buf = num_buffer;
19839 break;
19842 default: {
19844 spec[0] = ch;
19845 spec[1] = '\0';
19846 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
19847 goto error;
19851 if (!gotMinus) {
19852 while (formatted_chars < width) {
19853 Jim_AppendString(interp, resultPtr, &pad, 1);
19854 formatted_chars++;
19858 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
19860 while (formatted_chars < width) {
19861 Jim_AppendString(interp, resultPtr, &pad, 1);
19862 formatted_chars++;
19865 objIndex += gotSequential;
19867 if (numBytes) {
19868 Jim_AppendString(interp, resultPtr, span, numBytes);
19871 Jim_Free(num_buffer);
19872 return resultPtr;
19874 errorMsg:
19875 Jim_SetResultString(interp, msg, -1);
19876 error:
19877 Jim_FreeNewObj(interp, resultPtr);
19878 Jim_Free(num_buffer);
19879 return NULL;
19881 #include <stdio.h>
19882 #include <ctype.h>
19883 #include <stdlib.h>
19884 #include <string.h>
19887 #if !defined(HAVE_REGCOMP) || defined(JIM_REGEXP)
19891 #define REG_MAX_PAREN 100
19894 #define END 0
19895 #define BOL 1
19896 #define EOL 2
19897 #define ANY 3
19898 #define ANYOF 4
19899 #define ANYBUT 5
19900 #define BRANCH 6
19901 #define BACK 7
19902 #define EXACTLY 8
19903 #define NOTHING 9
19904 #define REP 10
19905 #define REPMIN 11
19906 #define REPX 12
19907 #define REPXMIN 13
19909 #define WORDA 15
19910 #define WORDZ 16
19911 #define OPENNC 19
19912 #define OPEN 20
19914 #define CLOSE (OPEN+REG_MAX_PAREN+1)
19915 #define CLOSE_END (CLOSE+REG_MAX_PAREN)
19916 #define CLOSENC (CLOSE-1)
19918 #define REG_MAGIC 0xFADED00D
19921 #define OP(preg, p) (preg->program[p])
19922 #define NEXT(preg, p) (preg->program[p + 1])
19923 #define OPERAND(p) ((p) + 2)
19928 #define FAIL(R,M) { (R)->err = (M); return (M); }
19929 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
19930 #define META "^$.[()|?{+*"
19932 #define HASWIDTH 01
19933 #define SIMPLE 02
19934 #define SPSTART 04
19935 #define WORST 0
19937 #define MAX_REP_COUNT 1000000
19939 static int reg(regex_t *preg, int paren , int *flagp );
19940 static int regpiece(regex_t *preg, int *flagp );
19941 static int regbranch(regex_t *preg, int *flagp );
19942 static int regatom(regex_t *preg, int *flagp );
19943 static int regnode(regex_t *preg, int op );
19944 static int regnext(regex_t *preg, int p );
19945 static void regc(regex_t *preg, int b );
19946 static int reginsert(regex_t *preg, int op, int size, int opnd );
19947 static void regtail_(regex_t *preg, int p, int val, int line );
19948 static void regoptail(regex_t *preg, int p, int val );
19949 #define regtail(PREG, P, VAL) regtail_(PREG, P, VAL, __LINE__)
19951 static int reg_range_find(const int *string, int c);
19952 static const char *str_find(const char *string, int c, int nocase);
19953 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
19956 #ifdef DEBUG
19957 static int regnarrate = 0;
19958 static void regdump(regex_t *preg);
19959 static const char *regprop( int op );
19960 #endif
19963 static int str_int_len(const int *seq)
19965 int n = 0;
19966 while (*seq++) {
19967 n++;
19969 return n;
19972 int regcomp(regex_t *preg, const char *exp, int cflags)
19974 int scan;
19975 int longest;
19976 unsigned len;
19977 int flags;
19979 #ifdef DEBUG
19980 fprintf(stderr, "Compiling: '%s'\n", exp);
19981 #endif
19982 memset(preg, 0, sizeof(*preg));
19984 if (exp == NULL)
19985 FAIL(preg, REG_ERR_NULL_ARGUMENT);
19988 preg->cflags = cflags;
19989 preg->regparse = exp;
19991 preg->program = NULL;
19992 preg->proglen = 0;
19995 preg->proglen = (strlen(exp) + 1) * 5;
19996 preg->program = malloc(preg->proglen * sizeof(int));
19997 if (preg->program == NULL)
19998 FAIL(preg, REG_ERR_NOMEM);
20000 regc(preg, REG_MAGIC);
20001 if (reg(preg, 0, &flags) == 0) {
20002 return preg->err;
20006 if (preg->re_nsub >= REG_MAX_PAREN)
20007 FAIL(preg,REG_ERR_TOO_BIG);
20010 preg->regstart = 0;
20011 preg->reganch = 0;
20012 preg->regmust = 0;
20013 preg->regmlen = 0;
20014 scan = 1;
20015 if (OP(preg, regnext(preg, scan)) == END) {
20016 scan = OPERAND(scan);
20019 if (OP(preg, scan) == EXACTLY) {
20020 preg->regstart = preg->program[OPERAND(scan)];
20022 else if (OP(preg, scan) == BOL)
20023 preg->reganch++;
20025 if (flags&SPSTART) {
20026 longest = 0;
20027 len = 0;
20028 for (; scan != 0; scan = regnext(preg, scan)) {
20029 if (OP(preg, scan) == EXACTLY) {
20030 int plen = str_int_len(preg->program + OPERAND(scan));
20031 if (plen >= len) {
20032 longest = OPERAND(scan);
20033 len = plen;
20037 preg->regmust = longest;
20038 preg->regmlen = len;
20042 #ifdef DEBUG
20043 regdump(preg);
20044 #endif
20046 return 0;
20049 static int reg(regex_t *preg, int paren , int *flagp )
20051 int ret;
20052 int br;
20053 int ender;
20054 int parno = 0;
20055 int flags;
20057 *flagp = HASWIDTH;
20060 if (paren) {
20061 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
20063 preg->regparse += 2;
20064 parno = -1;
20066 else {
20067 parno = ++preg->re_nsub;
20069 ret = regnode(preg, OPEN+parno);
20070 } else
20071 ret = 0;
20074 br = regbranch(preg, &flags);
20075 if (br == 0)
20076 return 0;
20077 if (ret != 0)
20078 regtail(preg, ret, br);
20079 else
20080 ret = br;
20081 if (!(flags&HASWIDTH))
20082 *flagp &= ~HASWIDTH;
20083 *flagp |= flags&SPSTART;
20084 while (*preg->regparse == '|') {
20085 preg->regparse++;
20086 br = regbranch(preg, &flags);
20087 if (br == 0)
20088 return 0;
20089 regtail(preg, ret, br);
20090 if (!(flags&HASWIDTH))
20091 *flagp &= ~HASWIDTH;
20092 *flagp |= flags&SPSTART;
20096 ender = regnode(preg, (paren) ? CLOSE+parno : END);
20097 regtail(preg, ret, ender);
20100 for (br = ret; br != 0; br = regnext(preg, br))
20101 regoptail(preg, br, ender);
20104 if (paren && *preg->regparse++ != ')') {
20105 preg->err = REG_ERR_UNMATCHED_PAREN;
20106 return 0;
20107 } else if (!paren && *preg->regparse != '\0') {
20108 if (*preg->regparse == ')') {
20109 preg->err = REG_ERR_UNMATCHED_PAREN;
20110 return 0;
20111 } else {
20112 preg->err = REG_ERR_JUNK_ON_END;
20113 return 0;
20117 return(ret);
20120 static int regbranch(regex_t *preg, int *flagp )
20122 int ret;
20123 int chain;
20124 int latest;
20125 int flags;
20127 *flagp = WORST;
20129 ret = regnode(preg, BRANCH);
20130 chain = 0;
20131 while (*preg->regparse != '\0' && *preg->regparse != ')' &&
20132 *preg->regparse != '|') {
20133 latest = regpiece(preg, &flags);
20134 if (latest == 0)
20135 return 0;
20136 *flagp |= flags&HASWIDTH;
20137 if (chain == 0) {
20138 *flagp |= flags&SPSTART;
20140 else {
20141 regtail(preg, chain, latest);
20143 chain = latest;
20145 if (chain == 0)
20146 (void) regnode(preg, NOTHING);
20148 return(ret);
20151 static int regpiece(regex_t *preg, int *flagp)
20153 int ret;
20154 char op;
20155 int next;
20156 int flags;
20157 int chain = 0;
20158 int min;
20159 int max;
20161 ret = regatom(preg, &flags);
20162 if (ret == 0)
20163 return 0;
20165 op = *preg->regparse;
20166 if (!ISMULT(op)) {
20167 *flagp = flags;
20168 return(ret);
20171 if (!(flags&HASWIDTH) && op != '?') {
20172 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
20173 return 0;
20177 if (op == '{') {
20178 char *end;
20180 min = strtoul(preg->regparse + 1, &end, 10);
20181 if (end == preg->regparse + 1) {
20182 preg->err = REG_ERR_BAD_COUNT;
20183 return 0;
20185 if (*end == '}') {
20186 max = min;
20188 else {
20189 preg->regparse = end;
20190 max = strtoul(preg->regparse + 1, &end, 10);
20191 if (*end != '}') {
20192 preg->err = REG_ERR_UNMATCHED_BRACES;
20193 return 0;
20196 if (end == preg->regparse + 1) {
20197 max = MAX_REP_COUNT;
20199 else if (max < min || max >= 100) {
20200 preg->err = REG_ERR_BAD_COUNT;
20201 return 0;
20203 if (min >= 100) {
20204 preg->err = REG_ERR_BAD_COUNT;
20205 return 0;
20208 preg->regparse = strchr(preg->regparse, '}');
20210 else {
20211 min = (op == '+');
20212 max = (op == '?' ? 1 : MAX_REP_COUNT);
20215 if (preg->regparse[1] == '?') {
20216 preg->regparse++;
20217 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
20219 else {
20220 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
20222 preg->program[ret + 2] = max;
20223 preg->program[ret + 3] = min;
20224 preg->program[ret + 4] = 0;
20226 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
20228 if (!(flags & SIMPLE)) {
20229 int back = regnode(preg, BACK);
20230 regtail(preg, back, ret);
20231 regtail(preg, next, back);
20234 preg->regparse++;
20235 if (ISMULT(*preg->regparse)) {
20236 preg->err = REG_ERR_NESTED_COUNT;
20237 return 0;
20240 return chain ? chain : ret;
20243 static void reg_addrange(regex_t *preg, int lower, int upper)
20245 if (lower > upper) {
20246 reg_addrange(preg, upper, lower);
20249 regc(preg, upper - lower + 1);
20250 regc(preg, lower);
20253 static void reg_addrange_str(regex_t *preg, const char *str)
20255 while (*str) {
20256 reg_addrange(preg, *str, *str);
20257 str++;
20261 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
20263 int l = utf8_tounicode(s, uc);
20264 if (upper) {
20265 *uc = utf8_upper(*uc);
20267 return l;
20270 static int hexdigitval(int c)
20272 if (c >= '0' && c <= '9')
20273 return c - '0';
20274 if (c >= 'a' && c <= 'f')
20275 return c - 'a' + 10;
20276 if (c >= 'A' && c <= 'F')
20277 return c - 'A' + 10;
20278 return -1;
20281 static int parse_hex(const char *s, int n, int *uc)
20283 int val = 0;
20284 int k;
20286 for (k = 0; k < n; k++) {
20287 int c = hexdigitval(*s++);
20288 if (c == -1) {
20289 break;
20291 val = (val << 4) | c;
20293 if (k) {
20294 *uc = val;
20296 return k;
20299 static int reg_decode_escape(const char *s, int *ch)
20301 int n;
20302 const char *s0 = s;
20304 *ch = *s++;
20306 switch (*ch) {
20307 case 'b': *ch = '\b'; break;
20308 case 'e': *ch = 27; break;
20309 case 'f': *ch = '\f'; break;
20310 case 'n': *ch = '\n'; break;
20311 case 'r': *ch = '\r'; break;
20312 case 't': *ch = '\t'; break;
20313 case 'v': *ch = '\v'; break;
20314 case 'u':
20315 if (*s == '{') {
20317 n = parse_hex(s + 1, 6, ch);
20318 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
20319 s += n + 2;
20321 else {
20323 *ch = 'u';
20326 else if ((n = parse_hex(s, 4, ch)) > 0) {
20327 s += n;
20329 break;
20330 case 'U':
20331 if ((n = parse_hex(s, 8, ch)) > 0) {
20332 s += n;
20334 case 'x':
20335 if ((n = parse_hex(s, 2, ch)) > 0) {
20336 s += n;
20338 break;
20339 case '\0':
20340 s--;
20341 *ch = '\\';
20342 break;
20344 return s - s0;
20347 static int regatom(regex_t *preg, int *flagp)
20349 int ret;
20350 int flags;
20351 int nocase = (preg->cflags & REG_ICASE);
20353 int ch;
20354 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
20356 *flagp = WORST;
20358 preg->regparse += n;
20359 switch (ch) {
20361 case '^':
20362 ret = regnode(preg, BOL);
20363 break;
20364 case '$':
20365 ret = regnode(preg, EOL);
20366 break;
20367 case '.':
20368 ret = regnode(preg, ANY);
20369 *flagp |= HASWIDTH|SIMPLE;
20370 break;
20371 case '[': {
20372 const char *pattern = preg->regparse;
20374 if (*pattern == '^') {
20375 ret = regnode(preg, ANYBUT);
20376 pattern++;
20377 } else
20378 ret = regnode(preg, ANYOF);
20381 if (*pattern == ']' || *pattern == '-') {
20382 reg_addrange(preg, *pattern, *pattern);
20383 pattern++;
20386 while (*pattern && *pattern != ']') {
20388 int start;
20389 int end;
20391 pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
20392 if (start == '\\') {
20393 pattern += reg_decode_escape(pattern, &start);
20394 if (start == 0) {
20395 preg->err = REG_ERR_NULL_CHAR;
20396 return 0;
20399 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
20401 pattern += utf8_tounicode(pattern, &end);
20402 pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
20403 if (end == '\\') {
20404 pattern += reg_decode_escape(pattern, &end);
20405 if (end == 0) {
20406 preg->err = REG_ERR_NULL_CHAR;
20407 return 0;
20411 reg_addrange(preg, start, end);
20412 continue;
20414 if (start == '[') {
20415 if (strncmp(pattern, ":alpha:]", 8) == 0) {
20416 if ((preg->cflags & REG_ICASE) == 0) {
20417 reg_addrange(preg, 'a', 'z');
20419 reg_addrange(preg, 'A', 'Z');
20420 pattern += 8;
20421 continue;
20423 if (strncmp(pattern, ":alnum:]", 8) == 0) {
20424 if ((preg->cflags & REG_ICASE) == 0) {
20425 reg_addrange(preg, 'a', 'z');
20427 reg_addrange(preg, 'A', 'Z');
20428 reg_addrange(preg, '0', '9');
20429 pattern += 8;
20430 continue;
20432 if (strncmp(pattern, ":space:]", 8) == 0) {
20433 reg_addrange_str(preg, " \t\r\n\f\v");
20434 pattern += 8;
20435 continue;
20439 reg_addrange(preg, start, start);
20441 regc(preg, '\0');
20443 if (*pattern) {
20444 pattern++;
20446 preg->regparse = pattern;
20448 *flagp |= HASWIDTH|SIMPLE;
20450 break;
20451 case '(':
20452 ret = reg(preg, 1, &flags);
20453 if (ret == 0)
20454 return 0;
20455 *flagp |= flags&(HASWIDTH|SPSTART);
20456 break;
20457 case '\0':
20458 case '|':
20459 case ')':
20460 preg->err = REG_ERR_INTERNAL;
20461 return 0;
20462 case '?':
20463 case '+':
20464 case '*':
20465 case '{':
20466 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
20467 return 0;
20468 case '\\':
20469 switch (*preg->regparse++) {
20470 case '\0':
20471 preg->err = REG_ERR_TRAILING_BACKSLASH;
20472 return 0;
20473 case '<':
20474 case 'm':
20475 ret = regnode(preg, WORDA);
20476 break;
20477 case '>':
20478 case 'M':
20479 ret = regnode(preg, WORDZ);
20480 break;
20481 case 'd':
20482 ret = regnode(preg, ANYOF);
20483 reg_addrange(preg, '0', '9');
20484 regc(preg, '\0');
20485 *flagp |= HASWIDTH|SIMPLE;
20486 break;
20487 case 'w':
20488 ret = regnode(preg, ANYOF);
20489 if ((preg->cflags & REG_ICASE) == 0) {
20490 reg_addrange(preg, 'a', 'z');
20492 reg_addrange(preg, 'A', 'Z');
20493 reg_addrange(preg, '0', '9');
20494 reg_addrange(preg, '_', '_');
20495 regc(preg, '\0');
20496 *flagp |= HASWIDTH|SIMPLE;
20497 break;
20498 case 's':
20499 ret = regnode(preg, ANYOF);
20500 reg_addrange_str(preg," \t\r\n\f\v");
20501 regc(preg, '\0');
20502 *flagp |= HASWIDTH|SIMPLE;
20503 break;
20505 default:
20508 preg->regparse--;
20509 goto de_fault;
20511 break;
20512 de_fault:
20513 default: {
20514 int added = 0;
20517 preg->regparse -= n;
20519 ret = regnode(preg, EXACTLY);
20523 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
20524 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
20525 if (ch == '\\' && preg->regparse[n]) {
20526 if (strchr("<>mMwds", preg->regparse[n])) {
20528 break;
20530 n += reg_decode_escape(preg->regparse + n, &ch);
20531 if (ch == 0) {
20532 preg->err = REG_ERR_NULL_CHAR;
20533 return 0;
20538 if (ISMULT(preg->regparse[n])) {
20540 if (added) {
20542 break;
20545 regc(preg, ch);
20546 added++;
20547 preg->regparse += n;
20548 break;
20552 regc(preg, ch);
20553 added++;
20554 preg->regparse += n;
20556 regc(preg, '\0');
20558 *flagp |= HASWIDTH;
20559 if (added == 1)
20560 *flagp |= SIMPLE;
20561 break;
20563 break;
20566 return(ret);
20569 static void reg_grow(regex_t *preg, int n)
20571 if (preg->p + n >= preg->proglen) {
20572 preg->proglen = (preg->p + n) * 2;
20573 preg->program = realloc(preg->program, preg->proglen * sizeof(int));
20578 static int regnode(regex_t *preg, int op)
20580 reg_grow(preg, 2);
20582 preg->program[preg->p++] = op;
20583 preg->program[preg->p++] = 0;
20586 return preg->p - 2;
20589 static void regc(regex_t *preg, int b )
20591 reg_grow(preg, 1);
20592 preg->program[preg->p++] = b;
20595 static int reginsert(regex_t *preg, int op, int size, int opnd )
20597 reg_grow(preg, size);
20600 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
20602 memset(preg->program + opnd, 0, sizeof(int) * size);
20604 preg->program[opnd] = op;
20606 preg->p += size;
20608 return opnd + size;
20611 static void regtail_(regex_t *preg, int p, int val, int line )
20613 int scan;
20614 int temp;
20615 int offset;
20618 scan = p;
20619 for (;;) {
20620 temp = regnext(preg, scan);
20621 if (temp == 0)
20622 break;
20623 scan = temp;
20626 if (OP(preg, scan) == BACK)
20627 offset = scan - val;
20628 else
20629 offset = val - scan;
20631 preg->program[scan + 1] = offset;
20635 static void regoptail(regex_t *preg, int p, int val )
20638 if (p != 0 && OP(preg, p) == BRANCH) {
20639 regtail(preg, OPERAND(p), val);
20644 static int regtry(regex_t *preg, const char *string );
20645 static int regmatch(regex_t *preg, int prog);
20646 static int regrepeat(regex_t *preg, int p, int max);
20648 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
20650 const char *s;
20651 int scan;
20654 if (preg == NULL || preg->program == NULL || string == NULL) {
20655 return REG_ERR_NULL_ARGUMENT;
20659 if (*preg->program != REG_MAGIC) {
20660 return REG_ERR_CORRUPTED;
20663 #ifdef DEBUG
20664 fprintf(stderr, "regexec: %s\n", string);
20665 regdump(preg);
20666 #endif
20668 preg->eflags = eflags;
20669 preg->pmatch = pmatch;
20670 preg->nmatch = nmatch;
20671 preg->start = string;
20674 for (scan = OPERAND(1); scan != 0; ) {
20675 switch (OP(preg, scan)) {
20676 case REP:
20677 case REPMIN:
20678 case REPX:
20679 case REPXMIN:
20680 preg->program[scan + 4] = 0;
20681 scan += 5;
20682 break;
20684 case ANYOF:
20685 case ANYBUT:
20686 case EXACTLY:
20687 scan += 2;
20688 while (preg->program[scan++]) {
20690 break;
20692 case END:
20693 scan = 0;
20694 break;
20696 default:
20697 scan += 2;
20698 break;
20703 if (preg->regmust != 0) {
20704 s = string;
20705 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
20706 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
20707 break;
20709 s++;
20711 if (s == NULL)
20712 return REG_NOMATCH;
20716 preg->regbol = string;
20719 if (preg->reganch) {
20720 if (eflags & REG_NOTBOL) {
20722 goto nextline;
20724 while (1) {
20725 if (regtry(preg, string)) {
20726 return REG_NOERROR;
20728 if (*string) {
20729 nextline:
20730 if (preg->cflags & REG_NEWLINE) {
20732 string = strchr(string, '\n');
20733 if (string) {
20734 preg->regbol = ++string;
20735 continue;
20739 return REG_NOMATCH;
20744 s = string;
20745 if (preg->regstart != '\0') {
20747 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
20748 if (regtry(preg, s))
20749 return REG_NOERROR;
20750 s++;
20753 else
20755 while (1) {
20756 if (regtry(preg, s))
20757 return REG_NOERROR;
20758 if (*s == '\0') {
20759 break;
20761 else {
20762 int c;
20763 s += utf8_tounicode(s, &c);
20768 return REG_NOMATCH;
20772 static int regtry( regex_t *preg, const char *string )
20774 int i;
20776 preg->reginput = string;
20778 for (i = 0; i < preg->nmatch; i++) {
20779 preg->pmatch[i].rm_so = -1;
20780 preg->pmatch[i].rm_eo = -1;
20782 if (regmatch(preg, 1)) {
20783 preg->pmatch[0].rm_so = string - preg->start;
20784 preg->pmatch[0].rm_eo = preg->reginput - preg->start;
20785 return(1);
20786 } else
20787 return(0);
20790 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
20792 const char *s = string;
20793 while (proglen && *s) {
20794 int ch;
20795 int n = reg_utf8_tounicode_case(s, &ch, nocase);
20796 if (ch != *prog) {
20797 return -1;
20799 prog++;
20800 s += n;
20801 proglen--;
20803 if (proglen == 0) {
20804 return s - string;
20806 return -1;
20809 static int reg_range_find(const int *range, int c)
20811 while (*range) {
20813 if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
20814 return 1;
20816 range += 2;
20818 return 0;
20821 static const char *str_find(const char *string, int c, int nocase)
20823 if (nocase) {
20825 c = utf8_upper(c);
20827 while (*string) {
20828 int ch;
20829 int n = reg_utf8_tounicode_case(string, &ch, nocase);
20830 if (c == ch) {
20831 return string;
20833 string += n;
20835 return NULL;
20838 static int reg_iseol(regex_t *preg, int ch)
20840 if (preg->cflags & REG_NEWLINE) {
20841 return ch == '\0' || ch == '\n';
20843 else {
20844 return ch == '\0';
20848 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
20850 int nextch = '\0';
20851 const char *save;
20852 int no;
20853 int c;
20855 int max = preg->program[scan + 2];
20856 int min = preg->program[scan + 3];
20857 int next = regnext(preg, scan);
20859 if (OP(preg, next) == EXACTLY) {
20860 nextch = preg->program[OPERAND(next)];
20862 save = preg->reginput;
20863 no = regrepeat(preg, scan + 5, max);
20864 if (no < min) {
20865 return 0;
20867 if (matchmin) {
20869 max = no;
20870 no = min;
20873 while (1) {
20874 if (matchmin) {
20875 if (no > max) {
20876 break;
20879 else {
20880 if (no < min) {
20881 break;
20884 preg->reginput = save + utf8_index(save, no);
20885 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
20887 if (reg_iseol(preg, nextch) || c == nextch) {
20888 if (regmatch(preg, next)) {
20889 return(1);
20892 if (matchmin) {
20894 no++;
20896 else {
20898 no--;
20901 return(0);
20904 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
20906 int *scanpt = preg->program + scan;
20908 int max = scanpt[2];
20909 int min = scanpt[3];
20912 if (scanpt[4] < min) {
20914 scanpt[4]++;
20915 if (regmatch(preg, scan + 5)) {
20916 return 1;
20918 scanpt[4]--;
20919 return 0;
20921 if (scanpt[4] > max) {
20922 return 0;
20925 if (matchmin) {
20927 if (regmatch(preg, regnext(preg, scan))) {
20928 return 1;
20931 scanpt[4]++;
20932 if (regmatch(preg, scan + 5)) {
20933 return 1;
20935 scanpt[4]--;
20936 return 0;
20939 if (scanpt[4] < max) {
20940 scanpt[4]++;
20941 if (regmatch(preg, scan + 5)) {
20942 return 1;
20944 scanpt[4]--;
20947 return regmatch(preg, regnext(preg, scan));
20951 static int regmatch(regex_t *preg, int prog)
20953 int scan;
20954 int next;
20956 scan = prog;
20958 #ifdef DEBUG
20959 if (scan != 0 && regnarrate)
20960 fprintf(stderr, "%s(\n", regprop(scan));
20961 #endif
20962 while (scan != 0) {
20963 int n;
20964 int c;
20965 #ifdef DEBUG
20966 if (regnarrate) {
20967 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
20969 #endif
20970 next = regnext(preg, scan);
20971 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
20973 switch (OP(preg, scan)) {
20974 case BOL:
20975 if (preg->reginput != preg->regbol)
20976 return(0);
20977 break;
20978 case EOL:
20979 if (!reg_iseol(preg, c)) {
20980 return(0);
20982 break;
20983 case WORDA:
20985 if ((!isalnum(UCHAR(c))) && c != '_')
20986 return(0);
20988 if (preg->reginput > preg->regbol &&
20989 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
20990 return(0);
20991 break;
20992 case WORDZ:
20994 if (preg->reginput > preg->regbol) {
20996 if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
20997 c = preg->reginput[-1];
20999 if (isalnum(UCHAR(c)) || c == '_') {
21000 break;
21005 return(0);
21007 case ANY:
21008 if (reg_iseol(preg, c))
21009 return 0;
21010 preg->reginput += n;
21011 break;
21012 case EXACTLY: {
21013 int opnd;
21014 int len;
21015 int slen;
21017 opnd = OPERAND(scan);
21018 len = str_int_len(preg->program + opnd);
21020 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
21021 if (slen < 0) {
21022 return(0);
21024 preg->reginput += slen;
21026 break;
21027 case ANYOF:
21028 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
21029 return(0);
21031 preg->reginput += n;
21032 break;
21033 case ANYBUT:
21034 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
21035 return(0);
21037 preg->reginput += n;
21038 break;
21039 case NOTHING:
21040 break;
21041 case BACK:
21042 break;
21043 case BRANCH: {
21044 const char *save;
21046 if (OP(preg, next) != BRANCH)
21047 next = OPERAND(scan);
21048 else {
21049 do {
21050 save = preg->reginput;
21051 if (regmatch(preg, OPERAND(scan))) {
21052 return(1);
21054 preg->reginput = save;
21055 scan = regnext(preg, scan);
21056 } while (scan != 0 && OP(preg, scan) == BRANCH);
21057 return(0);
21061 break;
21062 case REP:
21063 case REPMIN:
21064 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
21066 case REPX:
21067 case REPXMIN:
21068 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
21070 case END:
21071 return(1);
21072 break;
21074 case OPENNC:
21075 case CLOSENC:
21076 if (regmatch(preg, next)) {
21077 return 1;
21079 return 0;
21081 default:
21082 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21083 const char *save;
21085 save = preg->reginput;
21087 if (regmatch(preg, next)) {
21088 int no;
21089 if (OP(preg, scan) < CLOSE) {
21090 no = OP(preg, scan) - OPEN;
21091 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
21092 preg->pmatch[no].rm_so = save - preg->start;
21095 else {
21096 no = OP(preg, scan) - CLOSE;
21097 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
21098 preg->pmatch[no].rm_eo = save - preg->start;
21101 return(1);
21102 } else
21103 return(0);
21105 return REG_ERR_INTERNAL;
21108 scan = next;
21111 return REG_ERR_INTERNAL;
21114 static int regrepeat(regex_t *preg, int p, int max)
21116 int count = 0;
21117 const char *scan;
21118 int opnd;
21119 int ch;
21120 int n;
21122 scan = preg->reginput;
21123 opnd = OPERAND(p);
21124 switch (OP(preg, p)) {
21125 case ANY:
21127 while (!reg_iseol(preg, *scan) && count < max) {
21128 count++;
21129 scan++;
21131 break;
21132 case EXACTLY:
21133 while (count < max) {
21134 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21135 if (preg->program[opnd] != ch) {
21136 break;
21138 count++;
21139 scan += n;
21141 break;
21142 case ANYOF:
21143 while (count < max) {
21144 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21145 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
21146 break;
21148 count++;
21149 scan += n;
21151 break;
21152 case ANYBUT:
21153 while (count < max) {
21154 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21155 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
21156 break;
21158 count++;
21159 scan += n;
21161 break;
21162 default:
21163 preg->err = REG_ERR_INTERNAL;
21164 count = 0;
21165 break;
21167 preg->reginput = scan;
21169 return(count);
21172 static int regnext(regex_t *preg, int p )
21174 int offset;
21176 offset = NEXT(preg, p);
21178 if (offset == 0)
21179 return 0;
21181 if (OP(preg, p) == BACK)
21182 return(p-offset);
21183 else
21184 return(p+offset);
21188 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
21190 static const char *error_strings[] = {
21191 "success",
21192 "no match",
21193 "bad pattern",
21194 "null argument",
21195 "unknown error",
21196 "too big",
21197 "out of memory",
21198 "too many ()",
21199 "parentheses () not balanced",
21200 "braces {} not balanced",
21201 "invalid repetition count(s)",
21202 "extra characters",
21203 "*+ of empty atom",
21204 "nested count",
21205 "internal error",
21206 "count follows nothing",
21207 "trailing backslash",
21208 "corrupted program",
21209 "contains null char",
21211 const char *err;
21213 if (errcode < 0 || errcode >= REG_ERR_NUM) {
21214 err = "Bad error code";
21216 else {
21217 err = error_strings[errcode];
21220 return snprintf(errbuf, errbuf_size, "%s", err);
21223 void regfree(regex_t *preg)
21225 free(preg->program);
21228 #endif
21230 #if defined(_WIN32) || defined(WIN32)
21231 #ifndef STRICT
21232 #define STRICT
21233 #endif
21234 #define WIN32_LEAN_AND_MEAN
21235 #include <windows.h>
21237 #if defined(HAVE_DLOPEN_COMPAT)
21238 void *dlopen(const char *path, int mode)
21240 JIM_NOTUSED(mode);
21242 return (void *)LoadLibraryA(path);
21245 int dlclose(void *handle)
21247 FreeLibrary((HANDLE)handle);
21248 return 0;
21251 void *dlsym(void *handle, const char *symbol)
21253 return GetProcAddress((HMODULE)handle, symbol);
21256 char *dlerror(void)
21258 static char msg[121];
21259 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
21260 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
21261 return msg;
21263 #endif
21265 #ifdef _MSC_VER
21267 #include <sys/timeb.h>
21270 int gettimeofday(struct timeval *tv, void *unused)
21272 struct _timeb tb;
21274 _ftime(&tb);
21275 tv->tv_sec = tb.time;
21276 tv->tv_usec = tb.millitm * 1000;
21278 return 0;
21282 DIR *opendir(const char *name)
21284 DIR *dir = 0;
21286 if (name && name[0]) {
21287 size_t base_length = strlen(name);
21288 const char *all =
21289 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
21291 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
21292 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
21293 strcat(strcpy(dir->name, name), all);
21295 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
21296 dir->result.d_name = 0;
21297 else {
21298 Jim_Free(dir->name);
21299 Jim_Free(dir);
21300 dir = 0;
21303 else {
21304 Jim_Free(dir);
21305 dir = 0;
21306 errno = ENOMEM;
21309 else {
21310 errno = EINVAL;
21312 return dir;
21315 int closedir(DIR * dir)
21317 int result = -1;
21319 if (dir) {
21320 if (dir->handle != -1)
21321 result = _findclose(dir->handle);
21322 Jim_Free(dir->name);
21323 Jim_Free(dir);
21325 if (result == -1)
21326 errno = EBADF;
21327 return result;
21330 struct dirent *readdir(DIR * dir)
21332 struct dirent *result = 0;
21334 if (dir && dir->handle != -1) {
21335 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
21336 result = &dir->result;
21337 result->d_name = dir->info.name;
21340 else {
21341 errno = EBADF;
21343 return result;
21345 #endif
21346 #endif
21347 #ifndef JIM_BOOTSTRAP_LIB_ONLY
21348 #include <errno.h>
21349 #include <string.h>
21352 #ifdef USE_LINENOISE
21353 #include <unistd.h>
21354 #include "linenoise.h"
21355 #else
21356 #define MAX_LINE_LEN 512
21357 #endif
21359 char *Jim_HistoryGetline(const char *prompt)
21361 #ifdef USE_LINENOISE
21362 return linenoise(prompt);
21363 #else
21364 char *line = malloc(MAX_LINE_LEN);
21366 fputs(prompt, stdout);
21367 fflush(stdout);
21369 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
21370 free(line);
21371 return NULL;
21373 return line;
21374 #endif
21377 void Jim_HistoryLoad(const char *filename)
21379 #ifdef USE_LINENOISE
21380 linenoiseHistoryLoad(filename);
21381 #endif
21384 void Jim_HistoryAdd(const char *line)
21386 #ifdef USE_LINENOISE
21387 linenoiseHistoryAdd(line);
21388 #endif
21391 void Jim_HistorySave(const char *filename)
21393 #ifdef USE_LINENOISE
21394 linenoiseHistorySave(filename);
21395 #endif
21398 void Jim_HistoryShow(void)
21400 #ifdef USE_LINENOISE
21402 int i;
21403 int len;
21404 char **history = linenoiseHistory(&len);
21405 for (i = 0; i < len; i++) {
21406 printf("%4d %s\n", i + 1, history[i]);
21408 #endif
21411 int Jim_InteractivePrompt(Jim_Interp *interp)
21413 int retcode = JIM_OK;
21414 char *history_file = NULL;
21415 #ifdef USE_LINENOISE
21416 const char *home;
21418 home = getenv("HOME");
21419 if (home && isatty(STDIN_FILENO)) {
21420 int history_len = strlen(home) + sizeof("/.jim_history");
21421 history_file = Jim_Alloc(history_len);
21422 snprintf(history_file, history_len, "%s/.jim_history", home);
21423 Jim_HistoryLoad(history_file);
21425 #endif
21427 printf("Welcome to Jim version %d.%d" JIM_NL,
21428 JIM_VERSION / 100, JIM_VERSION % 100);
21429 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
21431 while (1) {
21432 Jim_Obj *scriptObjPtr;
21433 const char *result;
21434 int reslen;
21435 char prompt[20];
21436 const char *str;
21438 if (retcode != 0) {
21439 const char *retcodestr = Jim_ReturnCode(retcode);
21441 if (*retcodestr == '?') {
21442 snprintf(prompt, sizeof(prompt) - 3, "[%d] ", retcode);
21444 else {
21445 snprintf(prompt, sizeof(prompt) - 3, "[%s] ", retcodestr);
21448 else {
21449 prompt[0] = '\0';
21451 strcat(prompt, ". ");
21453 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
21454 Jim_IncrRefCount(scriptObjPtr);
21455 while (1) {
21456 char state;
21457 int len;
21458 char *line;
21460 line = Jim_HistoryGetline(prompt);
21461 if (line == NULL) {
21462 if (errno == EINTR) {
21463 continue;
21465 Jim_DecrRefCount(interp, scriptObjPtr);
21466 retcode = JIM_OK;
21467 goto out;
21469 if (Jim_Length(scriptObjPtr) != 0) {
21470 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
21472 Jim_AppendString(interp, scriptObjPtr, line, -1);
21473 free(line);
21474 str = Jim_GetString(scriptObjPtr, &len);
21475 if (len == 0) {
21476 continue;
21478 if (Jim_ScriptIsComplete(str, len, &state))
21479 break;
21481 snprintf(prompt, sizeof(prompt), "%c> ", state);
21483 #ifdef USE_LINENOISE
21484 if (strcmp(str, "h") == 0) {
21486 Jim_HistoryShow();
21487 Jim_DecrRefCount(interp, scriptObjPtr);
21488 continue;
21491 Jim_HistoryAdd(Jim_String(scriptObjPtr));
21492 if (history_file) {
21493 Jim_HistorySave(history_file);
21495 #endif
21496 retcode = Jim_EvalObj(interp, scriptObjPtr);
21497 Jim_DecrRefCount(interp, scriptObjPtr);
21499 if (retcode == JIM_EXIT) {
21500 retcode = JIM_EXIT;
21501 break;
21503 if (retcode == JIM_ERR) {
21504 Jim_MakeErrorMessage(interp);
21506 result = Jim_GetString(Jim_GetResult(interp), &reslen);
21507 if (reslen) {
21508 printf("%s\n", result);
21511 out:
21512 Jim_Free(history_file);
21513 return retcode;
21516 #include <stdio.h>
21517 #include <stdlib.h>
21518 #include <string.h>
21522 extern int Jim_initjimshInit(Jim_Interp *interp);
21524 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
21526 int n;
21527 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
21530 for (n = 0; n < argc; n++) {
21531 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
21533 Jim_ListAppendElement(interp, listObj, obj);
21536 Jim_SetVariableStr(interp, "argv", listObj);
21537 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
21540 int main(int argc, char *const argv[])
21542 int retcode;
21543 Jim_Interp *interp;
21545 if (argc > 1 && strcmp(argv[1], "--version") == 0) {
21546 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
21547 return 0;
21551 interp = Jim_CreateInterp();
21552 Jim_RegisterCoreCommands(interp);
21555 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
21556 Jim_MakeErrorMessage(interp);
21557 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
21560 Jim_SetVariableStrWithStr(interp, "jim_argv0", argv[0]);
21561 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
21562 retcode = Jim_initjimshInit(interp);
21564 if (argc == 1) {
21565 if (retcode == JIM_ERR) {
21566 Jim_MakeErrorMessage(interp);
21567 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
21569 if (retcode != JIM_EXIT) {
21570 JimSetArgv(interp, 0, NULL);
21571 retcode = Jim_InteractivePrompt(interp);
21574 else {
21575 if (argc > 2 && strcmp(argv[1], "-e") == 0) {
21576 JimSetArgv(interp, argc - 3, argv + 3);
21577 retcode = Jim_Eval(interp, argv[2]);
21578 if (retcode != JIM_ERR) {
21579 printf("%s\n", Jim_String(Jim_GetResult(interp)));
21582 else {
21583 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
21584 JimSetArgv(interp, argc - 2, argv + 2);
21585 retcode = Jim_EvalFile(interp, argv[1]);
21587 if (retcode == JIM_ERR) {
21588 Jim_MakeErrorMessage(interp);
21589 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
21592 if (retcode == JIM_EXIT) {
21593 retcode = Jim_GetExitCode(interp);
21595 else if (retcode == JIM_ERR) {
21596 retcode = 1;
21598 else {
21599 retcode = 0;
21601 Jim_FreeInterp(interp);
21602 return retcode;
21604 #endif