comparison third_party/sqlite3/autosetup/jimsh0.c @ 167:589bab390fb4

[ThirdParty] Added sqlite3 to the third_party.
author MrJuneJune <me@mrjunejune.com>
date Mon, 19 Jan 2026 16:28:45 -0800
parents
children
comparison
equal deleted inserted replaced
166:78ea8d5ccc87 167:589bab390fb4
1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
2 #define JIM_COMPAT
3 #define JIM_ANSIC
4 #define JIM_REGEXP
5 #define HAVE_NO_AUTOCONF
6 #define JIM_TINY
7 #define _JIMAUTOCONF_H
8 #define TCL_LIBRARY "."
9 #define jim_ext_bootstrap
10 #define jim_ext_aio
11 #define jim_ext_readdir
12 #define jim_ext_regexp
13 #define jim_ext_file
14 #define jim_ext_glob
15 #define jim_ext_exec
16 #define jim_ext_clock
17 #define jim_ext_array
18 #define jim_ext_stdlib
19 #define jim_ext_tclcompat
20 #if defined(_MSC_VER)
21 #define TCL_PLATFORM_OS "windows"
22 #define TCL_PLATFORM_PLATFORM "windows"
23 #define TCL_PLATFORM_PATH_SEPARATOR ";"
24 #define HAVE_MKDIR_ONE_ARG
25 #define HAVE_SYSTEM
26 #elif defined(__MINGW32__)
27 #define TCL_PLATFORM_OS "mingw"
28 #define TCL_PLATFORM_PLATFORM "windows"
29 #define TCL_PLATFORM_PATH_SEPARATOR ";"
30 #define HAVE_MKDIR_ONE_ARG
31 #define HAVE_SYSTEM
32 #define HAVE_SYS_TIME_H
33 #define HAVE_DIRENT_H
34 #define HAVE_UNISTD_H
35 #define HAVE_UMASK
36 #include <sys/stat.h>
37 #ifndef S_IRWXG
38 #define S_IRWXG 0
39 #endif
40 #ifndef S_IRWXO
41 #define S_IRWXO 0
42 #endif
43 #else
44 #define TCL_PLATFORM_OS "unknown"
45 #define TCL_PLATFORM_PLATFORM "unix"
46 #define TCL_PLATFORM_PATH_SEPARATOR ":"
47 #ifdef _MINIX
48 #define vfork fork
49 #define _POSIX_SOURCE
50 #else
51 #define _GNU_SOURCE
52 #endif
53 #define HAVE_FORK
54 #define HAVE_WAITPID
55 #define HAVE_ISATTY
56 #define HAVE_MKSTEMP
57 #define HAVE_LINK
58 #define HAVE_SYS_TIME_H
59 #define HAVE_DIRENT_H
60 #define HAVE_UNISTD_H
61 #define HAVE_UMASK
62 #define HAVE_PIPE
63 #define _FILE_OFFSET_BITS 64
64 #endif
65 #define JIM_VERSION 84
66 #ifndef JIM_WIN32COMPAT_H
67 #define JIM_WIN32COMPAT_H
68
69
70
71 #ifdef __cplusplus
72 extern "C" {
73 #endif
74
75
76 #if defined(_WIN32) || defined(WIN32)
77
78 #define HAVE_DLOPEN
79 void *dlopen(const char *path, int mode);
80 int dlclose(void *handle);
81 void *dlsym(void *handle, const char *symbol);
82 char *dlerror(void);
83
84
85 #if defined(__MINGW32__)
86 #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
87 #endif
88
89 #ifdef _MSC_VER
90
91
92 #if _MSC_VER >= 1000
93 #pragma warning(disable:4146)
94 #endif
95
96 #include <limits.h>
97 #define jim_wide _int64
98 #ifndef HAVE_LONG_LONG
99 #define HAVE_LONG_LONG
100 #endif
101 #ifndef LLONG_MAX
102 #define LLONG_MAX 9223372036854775807I64
103 #endif
104 #ifndef LLONG_MIN
105 #define LLONG_MIN (-LLONG_MAX - 1I64)
106 #endif
107 #define JIM_WIDE_MIN LLONG_MIN
108 #define JIM_WIDE_MAX LLONG_MAX
109 #define JIM_WIDE_MODIFIER "I64d"
110 #define strcasecmp _stricmp
111 #define strtoull _strtoui64
112
113 #include <io.h>
114
115 #include <winsock.h>
116 int gettimeofday(struct timeval *tv, void *unused);
117
118 #define HAVE_OPENDIR
119 struct dirent {
120 char *d_name;
121 };
122
123 typedef struct DIR {
124 long handle;
125 struct _finddata_t info;
126 struct dirent result;
127 char *name;
128 } DIR;
129
130 DIR *opendir(const char *name);
131 int closedir(DIR *dir);
132 struct dirent *readdir(DIR *dir);
133
134 #endif
135
136 #endif
137
138 #ifdef __cplusplus
139 }
140 #endif
141
142 #endif
143 #ifndef UTF8_UTIL_H
144 #define UTF8_UTIL_H
145
146 #ifdef __cplusplus
147 extern "C" {
148 #endif
149
150
151
152 #define MAX_UTF8_LEN 4
153
154 int utf8_fromunicode(char *p, unsigned uc);
155
156 #ifndef JIM_UTF8
157 #include <ctype.h>
158
159
160 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
161 #define utf8_strwidth(S, B) utf8_strlen((S), (B))
162 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
163 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
164 #define utf8_upper(C) toupper(C)
165 #define utf8_title(C) toupper(C)
166 #define utf8_lower(C) tolower(C)
167 #define utf8_index(C, I) (I)
168 #define utf8_charlen(C) 1
169 #define utf8_prev_len(S, L) 1
170 #define utf8_width(C) 1
171
172 #else
173
174 #endif
175
176 #ifdef __cplusplus
177 }
178 #endif
179
180 #endif
181
182 #ifndef __JIM__H
183 #define __JIM__H
184
185 #ifdef __cplusplus
186 extern "C" {
187 #endif
188
189 #include <time.h>
190 #include <limits.h>
191 #include <stdlib.h>
192 #include <stdarg.h>
193
194
195 #ifndef HAVE_NO_AUTOCONF
196 #endif
197
198
199
200 #ifndef jim_wide
201 # ifdef HAVE_LONG_LONG
202 # define jim_wide long long
203 # ifndef LLONG_MAX
204 # define LLONG_MAX 9223372036854775807LL
205 # endif
206 # ifndef LLONG_MIN
207 # define LLONG_MIN (-LLONG_MAX - 1LL)
208 # endif
209 # define JIM_WIDE_MIN LLONG_MIN
210 # define JIM_WIDE_MAX LLONG_MAX
211 # else
212 # define jim_wide long
213 # define JIM_WIDE_MIN LONG_MIN
214 # define JIM_WIDE_MAX LONG_MAX
215 # endif
216
217
218 # ifdef HAVE_LONG_LONG
219 # define JIM_WIDE_MODIFIER "lld"
220 # else
221 # define JIM_WIDE_MODIFIER "ld"
222 # define strtoull strtoul
223 # endif
224 #endif
225
226 #define UCHAR(c) ((unsigned char)(c))
227
228
229
230 #define JIM_ABI_VERSION 101
231
232 #define JIM_OK 0
233 #define JIM_ERR 1
234 #define JIM_RETURN 2
235 #define JIM_BREAK 3
236 #define JIM_CONTINUE 4
237 #define JIM_SIGNAL 5
238 #define JIM_EXIT 6
239
240 #define JIM_EVAL 7
241
242 #define JIM_MAX_CALLFRAME_DEPTH 1000
243 #define JIM_MAX_EVAL_DEPTH 2000
244
245
246 #define JIM_PRIV_FLAG_SHIFT 20
247
248 #define JIM_NONE 0
249 #define JIM_ERRMSG 1
250 #define JIM_ENUM_ABBREV 2
251 #define JIM_UNSHARED 4
252 #define JIM_MUSTEXIST 8
253 #define JIM_NORESULT 16
254
255
256 #define JIM_SUBST_NOVAR 1
257 #define JIM_SUBST_NOCMD 2
258 #define JIM_SUBST_NOESC 4
259 #define JIM_SUBST_FLAG 128
260
261
262 #define JIM_CASESENS 0
263 #define JIM_NOCASE 1
264 #define JIM_OPT_END 2
265
266
267 #define JIM_PATH_LEN 1024
268
269
270 #define JIM_NOTUSED(V) ((void) V)
271
272 #define JIM_LIBPATH "auto_path"
273 #define JIM_INTERACTIVE "tcl_interactive"
274
275
276 typedef struct Jim_Stack {
277 int len;
278 int maxlen;
279 void **vector;
280 } Jim_Stack;
281
282
283 typedef struct Jim_HashEntry {
284 void *key;
285 union {
286 void *val;
287 int intval;
288 } u;
289 struct Jim_HashEntry *next;
290 } Jim_HashEntry;
291
292 typedef struct Jim_HashTableType {
293 unsigned int (*hashFunction)(const void *key);
294 void *(*keyDup)(void *privdata, const void *key);
295 void *(*valDup)(void *privdata, const void *obj);
296 int (*keyCompare)(void *privdata, const void *key1, const void *key2);
297 void (*keyDestructor)(void *privdata, void *key);
298 void (*valDestructor)(void *privdata, void *obj);
299 } Jim_HashTableType;
300
301 typedef struct Jim_HashTable {
302 Jim_HashEntry **table;
303 const Jim_HashTableType *type;
304 void *privdata;
305 unsigned int size;
306 unsigned int sizemask;
307 unsigned int used;
308 unsigned int collisions;
309 unsigned int uniq;
310 } Jim_HashTable;
311
312 typedef struct Jim_HashTableIterator {
313 Jim_HashTable *ht;
314 Jim_HashEntry *entry, *nextEntry;
315 int index;
316 } Jim_HashTableIterator;
317
318
319 #define JIM_HT_INITIAL_SIZE 16
320
321
322 #define Jim_FreeEntryVal(ht, entry) \
323 if ((ht)->type->valDestructor) \
324 (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
325
326 #define Jim_SetHashVal(ht, entry, _val_) do { \
327 if ((ht)->type->valDup) \
328 (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
329 else \
330 (entry)->u.val = (_val_); \
331 } while(0)
332
333 #define Jim_SetHashIntVal(ht, entry, _val_) (entry)->u.intval = (_val_)
334
335 #define Jim_FreeEntryKey(ht, entry) \
336 if ((ht)->type->keyDestructor) \
337 (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
338
339 #define Jim_SetHashKey(ht, entry, _key_) do { \
340 if ((ht)->type->keyDup) \
341 (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
342 else \
343 (entry)->key = (void *)(_key_); \
344 } while(0)
345
346 #define Jim_CompareHashKeys(ht, key1, key2) \
347 (((ht)->type->keyCompare) ? \
348 (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
349 (key1) == (key2))
350
351 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
352
353 #define Jim_GetHashEntryKey(he) ((he)->key)
354 #define Jim_GetHashEntryVal(he) ((he)->u.val)
355 #define Jim_GetHashEntryIntVal(he) ((he)->u.intval)
356 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
357 #define Jim_GetHashTableSize(ht) ((ht)->size)
358 #define Jim_GetHashTableUsed(ht) ((ht)->used)
359
360
361 typedef struct Jim_Obj {
362 char *bytes;
363 const struct Jim_ObjType *typePtr;
364 int refCount;
365 int length;
366
367 union {
368
369 jim_wide wideValue;
370
371 int intValue;
372
373 double doubleValue;
374
375 void *ptr;
376
377 struct {
378 void *ptr1;
379 void *ptr2;
380 } twoPtrValue;
381
382 struct {
383 void *ptr;
384 int int1;
385 int int2;
386 } ptrIntValue;
387
388 struct {
389 struct Jim_VarVal *vv;
390 unsigned long callFrameId;
391 int global;
392 } varValue;
393
394 struct {
395 struct Jim_Obj *nsObj;
396 struct Jim_Cmd *cmdPtr;
397 unsigned long procEpoch;
398 } cmdValue;
399
400 struct {
401 struct Jim_Obj **ele;
402 int len;
403 int maxLen;
404 } listValue;
405
406 struct Jim_Dict *dictValue;
407
408 struct {
409 int maxLength;
410 int charLength;
411 } strValue;
412
413 struct {
414 unsigned long id;
415 struct Jim_Reference *refPtr;
416 } refValue;
417
418 struct {
419 struct Jim_Obj *fileNameObj;
420 int lineNumber;
421 } sourceValue;
422
423 struct {
424 struct Jim_Obj *varNameObjPtr;
425 struct Jim_Obj *indexObjPtr;
426 } dictSubstValue;
427 struct {
428 int line;
429 int argc;
430 } scriptLineValue;
431 } internalRep;
432 struct Jim_Obj *prevObjPtr;
433 struct Jim_Obj *nextObjPtr;
434 } Jim_Obj;
435
436
437 #define Jim_IncrRefCount(objPtr) \
438 ++(objPtr)->refCount
439 #define Jim_DecrRefCount(interp, objPtr) \
440 if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
441 #define Jim_IsShared(objPtr) \
442 ((objPtr)->refCount > 1)
443
444 #define Jim_FreeNewObj Jim_FreeObj
445
446
447 #define Jim_FreeIntRep(i,o) \
448 if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
449 (o)->typePtr->freeIntRepProc(i, o)
450
451
452 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
453
454
455 #define Jim_SetIntRepPtr(o, p) \
456 (o)->internalRep.ptr = (p)
457
458
459 struct Jim_Interp;
460
461 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
462 struct Jim_Obj *objPtr);
463 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
464 struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
465 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
466
467 typedef struct Jim_ObjType {
468 const char *name;
469 Jim_FreeInternalRepProc *freeIntRepProc;
470 Jim_DupInternalRepProc *dupIntRepProc;
471 Jim_UpdateStringProc *updateStringProc;
472 int flags;
473 } Jim_ObjType;
474
475
476 #define JIM_TYPE_NONE 0
477 #define JIM_TYPE_REFERENCES 1
478
479
480
481 typedef struct Jim_CallFrame {
482 unsigned long id;
483 int level;
484 struct Jim_HashTable vars;
485 struct Jim_HashTable *staticVars;
486 struct Jim_CallFrame *parent;
487 Jim_Obj *const *argv;
488 int argc;
489 Jim_Obj *procArgsObjPtr;
490 Jim_Obj *procBodyObjPtr;
491 struct Jim_CallFrame *next;
492 Jim_Obj *nsObj;
493 Jim_Obj *unused_fileNameObj;
494 int unused_line;
495 Jim_Stack *localCommands;
496 struct Jim_Obj *tailcallObj;
497 struct Jim_Cmd *tailcallCmd;
498 } Jim_CallFrame;
499
500
501 typedef struct Jim_EvalFrame {
502 Jim_CallFrame *framePtr;
503 int level;
504 int procLevel;
505 struct Jim_Cmd *cmd;
506 struct Jim_EvalFrame *parent;
507 Jim_Obj *const *argv;
508 int argc;
509 Jim_Obj *scriptObj;
510 } Jim_EvalFrame;
511
512 typedef struct Jim_VarVal {
513 Jim_Obj *objPtr;
514 struct Jim_CallFrame *linkFramePtr;
515 int refCount;
516 } Jim_VarVal;
517
518
519 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
520 Jim_Obj *const *argv);
521 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
522
523 typedef struct Jim_Dict {
524 struct JimDictHashEntry {
525 int offset;
526 unsigned hash;
527 } *ht;
528 unsigned int size;
529 unsigned int sizemask;
530 unsigned int uniq;
531 Jim_Obj **table;
532 int len;
533 int maxLen;
534 unsigned int dummy;
535 } Jim_Dict;
536
537 typedef struct Jim_Cmd {
538 int inUse;
539 int isproc;
540 struct Jim_Cmd *prevCmd;
541 Jim_Obj *cmdNameObj;
542 union {
543 struct {
544
545 Jim_CmdProc *cmdProc;
546 Jim_DelCmdProc *delProc;
547 void *privData;
548 } native;
549 struct {
550
551 Jim_Obj *argListObjPtr;
552 Jim_Obj *bodyObjPtr;
553 Jim_HashTable *staticVars;
554 int argListLen;
555 int reqArity;
556 int optArity;
557 int argsPos;
558 int upcall;
559 struct Jim_ProcArg {
560 Jim_Obj *nameObjPtr;
561 Jim_Obj *defaultObjPtr;
562 } *arglist;
563 Jim_Obj *nsObj;
564 } proc;
565 } u;
566 } Jim_Cmd;
567
568
569 typedef struct Jim_PrngState {
570 unsigned char sbox[256];
571 unsigned int i, j;
572 } Jim_PrngState;
573
574 typedef struct Jim_Interp {
575 Jim_Obj *result;
576 int unused_errorLine;
577 Jim_Obj *currentFilenameObj;
578 int break_level;
579 int maxCallFrameDepth;
580 int maxEvalDepth;
581 int evalDepth;
582 int returnCode;
583 int returnLevel;
584 int exitCode;
585 long id;
586 int signal_level;
587 jim_wide sigmask;
588 int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
589 Jim_CallFrame *framePtr;
590 Jim_CallFrame *topFramePtr;
591 struct Jim_HashTable commands;
592 unsigned long procEpoch; /* Incremented every time the result
593 of procedures names lookup caching
594 may no longer be valid. */
595 unsigned long callFrameEpoch; /* Incremented every time a new
596 callframe is created. This id is used for the
597 'ID' field contained in the Jim_CallFrame
598 structure. */
599 int local;
600 int quitting;
601 int safeexpr;
602 Jim_Obj *liveList;
603 Jim_Obj *freeList;
604 Jim_Obj *unused_currentScriptObj;
605 Jim_EvalFrame topEvalFrame;
606 Jim_EvalFrame *evalFrame;
607 int procLevel;
608 Jim_Obj * const *unused_argv;
609 Jim_Obj *nullScriptObj;
610 Jim_Obj *emptyObj;
611 Jim_Obj *trueObj;
612 Jim_Obj *falseObj;
613 unsigned long referenceNextId;
614 struct Jim_HashTable references;
615 unsigned long lastCollectId; /* reference max Id of the last GC
616 execution. It's set to ~0 while the collection
617 is running as sentinel to avoid to recursive
618 calls via the [collect] command inside
619 finalizers. */
620 jim_wide lastCollectTime;
621 Jim_Obj *stackTrace;
622 Jim_Obj *errorProc;
623 Jim_Obj *unknown;
624 Jim_Obj *defer;
625 Jim_Obj *traceCmdObj;
626 int unknown_called;
627 int errorFlag;
628 void *cmdPrivData; /* Used to pass the private data pointer to
629 a command. It is set to what the user specified
630 via Jim_CreateCommand(). */
631
632 Jim_Cmd *oldCmdCache;
633 int oldCmdCacheSize;
634 struct Jim_CallFrame *freeFramesList;
635 struct Jim_HashTable assocData;
636 Jim_PrngState *prngState;
637 struct Jim_HashTable packages;
638 Jim_Stack *loadHandles;
639 } Jim_Interp;
640
641 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
642 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
643
644 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
645 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
646 #define Jim_GetResult(i) ((i)->result)
647 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
648
649 #define Jim_SetResult(i,o) do { \
650 Jim_Obj *_resultObjPtr_ = (o); \
651 Jim_IncrRefCount(_resultObjPtr_); \
652 Jim_DecrRefCount(i,(i)->result); \
653 (i)->result = _resultObjPtr_; \
654 } while(0)
655
656
657 #define Jim_GetId(i) (++(i)->id)
658
659
660 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
661 string representation must be fixed length. */
662 typedef struct Jim_Reference {
663 Jim_Obj *objPtr;
664 Jim_Obj *finalizerCmdNamePtr;
665 char tag[JIM_REFERENCE_TAGLEN+1];
666 } Jim_Reference;
667
668
669 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
670 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
671
672 #define JIM_EXPORT extern
673
674
675
676 JIM_EXPORT void *(*Jim_Allocator)(void *ptr, size_t size);
677
678 #define Jim_Free(P) Jim_Allocator((P), 0)
679 #define Jim_Realloc(P, S) Jim_Allocator((P), (S))
680 #define Jim_Alloc(S) Jim_Allocator(NULL, (S))
681 JIM_EXPORT char * Jim_StrDup (const char *s);
682 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
683
684
685 JIM_EXPORT char **Jim_GetEnviron(void);
686 JIM_EXPORT void Jim_SetEnviron(char **env);
687 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
688 #ifndef CLOCK_REALTIME
689 # define CLOCK_REALTIME 0
690 #endif
691 #ifndef CLOCK_MONOTONIC
692 # define CLOCK_MONOTONIC 1
693 #endif
694 #ifndef CLOCK_MONOTONIC_RAW
695 # define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
696 #endif
697 JIM_EXPORT jim_wide Jim_GetTimeUsec(unsigned type);
698
699
700 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
701
702
703 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
704
705 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
706
707 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
708 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
709 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
710 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
711 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
712 Jim_Obj *const *objv);
713 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
714 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
715 int objc, Jim_Obj *const *objv);
716 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
717 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
718 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
719 Jim_Obj **resObjPtrPtr, int flags);
720
721
722 JIM_EXPORT Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
723 int *lineptr);
724
725 JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
726 Jim_Obj *fileNameObj, int lineNumber);
727
728
729
730 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
731 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
732 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
733 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
734 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
735 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
736 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
737
738
739 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
740 const Jim_HashTableType *type, void *privdata);
741 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
742 unsigned int size);
743 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
744 void *val);
745 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
746 const void *key, void *val);
747 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
748 const void *key);
749 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
750 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
751 const void *key);
752 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
753 (Jim_HashTable *ht);
754 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
755 (Jim_HashTableIterator *iter);
756
757
758 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
759 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
760 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
761 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
762 Jim_Obj *objPtr);
763 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
764 int *lenPtr);
765 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
766 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
767
768
769 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
770 const char *s, int len);
771 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
772 const char *s, int charlen);
773 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
774 char *s, int len);
775 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
776 const char *str, int len);
777 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
778 Jim_Obj *appendObjPtr);
779 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
780 Jim_Obj *objPtr, ...);
781 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
782 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
783 Jim_Obj *objPtr, int nocase);
784 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
785 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
786 Jim_Obj *lastObjPtr);
787 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
788 Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
789 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
790 Jim_Obj *fmtObjPtr, int flags);
791 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
792 Jim_Obj *objPtr, const char *str);
793 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
794 Jim_Obj *secondObjPtr, int nocase);
795 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
796
797
798 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
799 Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
800 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
801 Jim_Obj *objPtr);
802 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
803 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
804
805
806 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
807 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
808 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
809 JIM_EXPORT const char *Jim_ReturnCode(int code);
810 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
811
812
813 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
814 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
815 const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
816 Jim_DelCmdProc *delProc);
817 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
818 Jim_Obj *cmdNameObj);
819 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
820 Jim_Obj *oldNameObj, Jim_Obj *newNameObj);
821 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
822 Jim_Obj *objPtr, int flags);
823 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
824 Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
825 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
826 const char *name, Jim_Obj *objPtr);
827 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
828 const char *name, Jim_Obj *objPtr);
829 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
830 const char *name, const char *val);
831 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
832 Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
833 Jim_CallFrame *targetCallFrame);
834 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
835 Jim_Obj *nameObjPtr);
836 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
837 Jim_Obj *nameObjPtr, int flags);
838 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
839 Jim_Obj *nameObjPtr, int flags);
840 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
841 const char *name, int flags);
842 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
843 const char *name, int flags);
844 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
845 Jim_Obj *nameObjPtr, int flags);
846
847
848 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
849 Jim_Obj *levelObjPtr);
850
851
852 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
853 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
854
855
856 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
857 int *indexPtr);
858
859
860 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
861 Jim_Obj *const *elements, int len);
862 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
863 Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
864 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
865 Jim_Obj *listPtr, Jim_Obj *objPtr);
866 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
867 Jim_Obj *listPtr, Jim_Obj *appendListPtr);
868 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
869 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
870 int listindex, Jim_Obj **objPtrPtr, int seterr);
871 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
872 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
873 Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
874 Jim_Obj *newObjPtr);
875 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
876 Jim_Obj *const *objv);
877 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
878 Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
879
880
881 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
882 Jim_Obj *const *elements, int len);
883 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
884 Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
885 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
886 Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
887 Jim_Obj **objPtrPtr, int flags);
888 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
889 Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
890 Jim_Obj *newObjPtr, int flags);
891 JIM_EXPORT Jim_Obj **Jim_DictPairs(Jim_Interp *interp,
892 Jim_Obj *dictPtr, int *len);
893 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
894 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
895
896 #define JIM_DICTMATCH_KEYS 0x0001
897 #define JIM_DICTMATCH_VALUES 0x002
898
899 JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
900 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
901 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
902 JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
903
904
905 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
906 int *intPtr);
907
908
909 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
910 Jim_Obj *exprObjPtr);
911 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
912 Jim_Obj *exprObjPtr, int *boolPtr);
913
914
915 JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
916 int *booleanPtr);
917
918
919 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
920 jim_wide *widePtr);
921 JIM_EXPORT int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr,
922 jim_wide *widePtr);
923 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
924 long *longPtr);
925 #define Jim_NewWideObj Jim_NewIntObj
926 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
927 jim_wide wideValue);
928
929
930 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
931 double *doublePtr);
932 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
933 double doubleValue);
934 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
935
936
937 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
938 Jim_Obj *const *argv, const char *msg);
939 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
940 const char * const *tablePtr, int *indexPtr, const char *name, int flags);
941 JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
942 const char *const *tablePtr);
943 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
944 Jim_Obj *scriptObj, char *stateCharPtr);
945
946 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
947
948
949 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
950 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
951 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
952 Jim_InterpDeleteProc *delProc, void *data);
953 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
954 JIM_EXPORT int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version);
955
956
957
958
959 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
960 const char *name, const char *ver, int flags);
961 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
962 const char *name, int flags);
963 #define Jim_PackageProvideCheck(INTERP, NAME) \
964 if (Jim_CheckAbiVersion(INTERP, JIM_ABI_VERSION) == JIM_ERR || Jim_PackageProvide(INTERP, NAME, "1.0", JIM_ERRMSG)) \
965 return JIM_ERR
966
967
968 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
969
970
971 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
972 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
973 JIM_EXPORT void Jim_HistorySave(const char *filename);
974 JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
975 JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj);
976 JIM_EXPORT void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj);
977 JIM_EXPORT void Jim_HistoryAdd(const char *line);
978 JIM_EXPORT void Jim_HistoryShow(void);
979 JIM_EXPORT void Jim_HistorySetMaxLen(int length);
980 JIM_EXPORT int Jim_HistoryGetMaxLen(void);
981
982
983 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
984 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
985 JIM_EXPORT int Jim_IsBigEndian(void);
986
987 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
988 JIM_EXPORT void Jim_SignalSetIgnored(jim_wide mask);
989
990
991 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
992 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
993
994
995 JIM_EXPORT int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
996
997
998 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
999 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
1000
1001 #ifdef __cplusplus
1002 }
1003 #endif
1004
1005 #endif
1006
1007 #ifndef JIM_SUBCMD_H
1008 #define JIM_SUBCMD_H
1009
1010
1011 #ifdef __cplusplus
1012 extern "C" {
1013 #endif
1014
1015
1016 #define JIM_MODFLAG_HIDDEN 0x0001
1017 #define JIM_MODFLAG_FULLARGV 0x0002
1018
1019
1020
1021 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1022
1023 typedef struct {
1024 const char *cmd;
1025 const char *args;
1026 jim_subcmd_function *function;
1027 short minargs;
1028 short maxargs;
1029 unsigned short flags;
1030 } jim_subcmd_type;
1031
1032 #define JIM_DEF_SUBCMD(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs }
1033 #define JIM_DEF_SUBCMD_HIDDEN(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs, JIM_MODFLAG_HIDDEN }
1034
1035 const jim_subcmd_type *
1036 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
1037
1038 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1039
1040 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
1041
1042 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type *ct, Jim_Obj *subcmd);
1043
1044 #ifdef __cplusplus
1045 }
1046 #endif
1047
1048 #endif
1049 #ifndef JIMREGEXP_H
1050 #define JIMREGEXP_H
1051
1052
1053 #ifdef __cplusplus
1054 extern "C" {
1055 #endif
1056
1057 #include <stdlib.h>
1058
1059 typedef struct {
1060 int rm_so;
1061 int rm_eo;
1062 } regmatch_t;
1063
1064
1065 typedef struct regexp {
1066
1067 int re_nsub;
1068
1069
1070 int cflags;
1071 int err;
1072 int regstart;
1073 int reganch;
1074 int regmust;
1075 int regmlen;
1076 int *program;
1077
1078
1079 const char *regparse;
1080 int p;
1081 int proglen;
1082
1083
1084 int eflags;
1085 const char *start;
1086 const char *reginput;
1087 const char *regbol;
1088
1089
1090 regmatch_t *pmatch;
1091 int nmatch;
1092 } regexp;
1093
1094 typedef regexp regex_t;
1095
1096 #define REG_EXTENDED 0
1097 #define REG_NEWLINE 1
1098 #define REG_ICASE 2
1099
1100 #define REG_NOTBOL 16
1101
1102 enum {
1103 REG_NOERROR,
1104 REG_NOMATCH,
1105 REG_BADPAT,
1106 REG_ERR_NULL_ARGUMENT,
1107 REG_ERR_UNKNOWN,
1108 REG_ERR_TOO_BIG,
1109 REG_ERR_NOMEM,
1110 REG_ERR_TOO_MANY_PAREN,
1111 REG_ERR_UNMATCHED_PAREN,
1112 REG_ERR_UNMATCHED_BRACES,
1113 REG_ERR_BAD_COUNT,
1114 REG_ERR_JUNK_ON_END,
1115 REG_ERR_OPERAND_COULD_BE_EMPTY,
1116 REG_ERR_NESTED_COUNT,
1117 REG_ERR_INTERNAL,
1118 REG_ERR_COUNT_FOLLOWS_NOTHING,
1119 REG_ERR_INVALID_ESCAPE,
1120 REG_ERR_CORRUPTED,
1121 REG_ERR_NULL_CHAR,
1122 REG_ERR_UNMATCHED_BRACKET,
1123 REG_ERR_NUM
1124 };
1125
1126 int jim_regcomp(regex_t *preg, const char *regex, int cflags);
1127 int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1128 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
1129 void jim_regfree(regex_t *preg);
1130
1131 #ifdef __cplusplus
1132 }
1133 #endif
1134
1135 #endif
1136 #ifndef JIM_SIGNAL_H
1137 #define JIM_SIGNAL_H
1138
1139 #ifdef __cplusplus
1140 extern "C" {
1141 #endif
1142
1143 const char *Jim_SignalId(int sig);
1144
1145 #ifdef __cplusplus
1146 }
1147 #endif
1148
1149 #endif
1150 #ifndef JIMIOCOMPAT_H
1151 #define JIMIOCOMPAT_H
1152
1153
1154 #include <stdio.h>
1155 #include <errno.h>
1156 #include <sys/stat.h>
1157
1158
1159 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
1160
1161 int Jim_OpenForWrite(const char *filename, int append);
1162
1163 int Jim_OpenForRead(const char *filename);
1164
1165 #if defined(__MINGW32__) || defined(_WIN32)
1166 #ifndef STRICT
1167 #define STRICT
1168 #endif
1169 #define WIN32_LEAN_AND_MEAN
1170 #include <windows.h>
1171 #include <fcntl.h>
1172 #include <io.h>
1173 #include <process.h>
1174
1175 typedef HANDLE phandle_t;
1176 #define JIM_BAD_PHANDLE INVALID_HANDLE_VALUE
1177
1178
1179 #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
1180 #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
1181 #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
1182 #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
1183 #define WNOHANG 1
1184
1185 int Jim_Errno(void);
1186
1187 long waitpid(phandle_t phandle, int *status, int nohang);
1188
1189 phandle_t JimWaitPid(long processid, int *status, int nohang);
1190
1191 long JimProcessPid(phandle_t phandle);
1192
1193 #define HAVE_PIPE
1194 #define pipe(P) _pipe((P), 0, O_NOINHERIT)
1195
1196 typedef struct __stat64 jim_stat_t;
1197 #define Jim_Stat _stat64
1198 #define Jim_FileStat _fstat64
1199 #define Jim_Lseek _lseeki64
1200 #define O_TEXT _O_TEXT
1201 #define O_BINARY _O_BINARY
1202 #define Jim_SetMode _setmode
1203 #ifndef STDIN_FILENO
1204 #define STDIN_FILENO 0
1205 #endif
1206
1207 #else
1208 #if defined(HAVE_STAT64)
1209 typedef struct stat64 jim_stat_t;
1210 #define Jim_Stat stat64
1211 #if defined(HAVE_FSTAT64)
1212 #define Jim_FileStat fstat64
1213 #endif
1214 #if defined(HAVE_LSTAT64)
1215 #define Jim_LinkStat lstat64
1216 #endif
1217 #else
1218 typedef struct stat jim_stat_t;
1219 #define Jim_Stat stat
1220 #if defined(HAVE_FSTAT)
1221 #define Jim_FileStat fstat
1222 #endif
1223 #if defined(HAVE_LSTAT)
1224 #define Jim_LinkStat lstat
1225 #endif
1226 #endif
1227 #if defined(HAVE_LSEEK64)
1228 #define Jim_Lseek lseek64
1229 #else
1230 #define Jim_Lseek lseek
1231 #endif
1232
1233 #if defined(HAVE_UNISTD_H)
1234 #include <unistd.h>
1235 #include <fcntl.h>
1236 #include <sys/wait.h>
1237
1238 typedef int phandle_t;
1239 #define Jim_Errno() errno
1240 #define JIM_BAD_PHANDLE -1
1241 #define JimProcessPid(PIDTYPE) (PIDTYPE)
1242 #define JimWaitPid waitpid
1243
1244 #ifndef HAVE_EXECVPE
1245 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
1246 #endif
1247 #endif
1248
1249 #ifndef O_TEXT
1250 #define O_TEXT 0
1251 #endif
1252
1253 #endif
1254
1255 # ifndef MAXPATHLEN
1256 # ifdef PATH_MAX
1257 # define MAXPATHLEN PATH_MAX
1258 # else
1259 # define MAXPATHLEN JIM_PATH_LEN
1260 # endif
1261 # endif
1262
1263
1264 int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb);
1265
1266 #endif
1267 int Jim_bootstrapInit(Jim_Interp *interp)
1268 {
1269 if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1270 return JIM_ERR;
1271
1272 return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1273 "\n"
1274 "proc package {cmd args} {\n"
1275 " if {$cmd eq \"require\"} {\n"
1276 " foreach path $::auto_path {\n"
1277 " lassign $args pkg\n"
1278 " set pkgpath $path/$pkg.tcl\n"
1279 " if {$path eq \".\"} {\n"
1280 " set pkgpath $pkg.tcl\n"
1281 " }\n"
1282 " if {[file exists $pkgpath]} {\n"
1283 " tailcall uplevel #0 [list source $pkgpath]\n"
1284 " }\n"
1285 " }\n"
1286 " }\n"
1287 "}\n"
1288 "set tcl_platform(bootstrap) 1\n"
1289 );
1290 }
1291 int Jim_initjimshInit(Jim_Interp *interp)
1292 {
1293 if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1294 return JIM_ERR;
1295
1296 return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1297 "\n"
1298 "\n"
1299 "\n"
1300 "proc _jimsh_init {} {\n"
1301 " rename _jimsh_init {}\n"
1302 " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1303 "\n"
1304 "\n"
1305 " if {[exists jim::argv0]} {\n"
1306 " if {[string match \"*/*\" $jim::argv0]} {\n"
1307 " set jim::exe [file join [pwd] $jim::argv0]\n"
1308 " } else {\n"
1309 " set jim::argv0 [file tail $jim::argv0]\n"
1310 " set path [split [env PATH \"\"] $tcl_platform(pathSeparator)]\n"
1311 " if {$tcl_platform(platform) eq \"windows\"} {\n"
1312 "\n"
1313 " set path [lmap p [list \"\" {*}$path] { string map {\\\\ /} $p }]\n"
1314 " }\n"
1315 " foreach p $path {\n"
1316 " set exec [file join [pwd] $p $jim::argv0]\n"
1317 " if {[file executable $exec]} {\n"
1318 " set jim::exe $exec\n"
1319 " break\n"
1320 " }\n"
1321 " }\n"
1322 " }\n"
1323 " }\n"
1324 "\n"
1325 "\n"
1326 " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1327 " if {[exists jim::exe]} {\n"
1328 " lappend p [file dirname $jim::exe]\n"
1329 " }\n"
1330 " lappend p {*}$auto_path\n"
1331 " set auto_path $p\n"
1332 "\n"
1333 " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1334 " foreach src {.jimrc jimrc.tcl} {\n"
1335 " if {[file exists [env HOME]/$src]} {\n"
1336 " uplevel #0 source [env HOME]/$src\n"
1337 " break\n"
1338 " }\n"
1339 " }\n"
1340 " }\n"
1341 " return \"\"\n"
1342 "}\n"
1343 "\n"
1344 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1345 " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1346 "}\n"
1347 "\n"
1348 "\n"
1349 "set tcl::autocomplete_commands {array clock debug dict file history info namespace package signal socket string tcl::prefix zlib}\n"
1350 "\n"
1351 "\n"
1352 "\n"
1353 "proc tcl::autocomplete {prefix} {\n"
1354 " if {[set space [string first \" \" $prefix]] != -1} {\n"
1355 " set cmd [string range $prefix 0 $space-1]\n"
1356 " if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
1357 " set arg [string range $prefix $space+1 end]\n"
1358 "\n"
1359 " return [lmap p [$cmd -commands] {\n"
1360 " if {![string match \"${arg}*\" $p]} continue\n"
1361 " function \"$cmd $p\"\n"
1362 " }]\n"
1363 " }\n"
1364 " }\n"
1365 "\n"
1366 " if {[string match \"source *\" $prefix]} {\n"
1367 " set path [string range $prefix 7 end]\n"
1368 " return [lmap p [glob -nocomplain \"${path}*\"] {\n"
1369 " function \"source $p\"\n"
1370 " }]\n"
1371 " }\n"
1372 "\n"
1373 " return [lmap p [lsort [info commands $prefix*]] {\n"
1374 " if {[string match \"* *\" $p]} {\n"
1375 " continue\n"
1376 " }\n"
1377 " function $p\n"
1378 " }]\n"
1379 "}\n"
1380 "\n"
1381 "\n"
1382 "set tcl::stdhint_commands {array clock debug dict file history info namespace package signal string zlib}\n"
1383 "\n"
1384 "set tcl::stdhint_cols {\n"
1385 " none {0}\n"
1386 " black {30}\n"
1387 " red {31}\n"
1388 " green {32}\n"
1389 " yellow {33}\n"
1390 " blue {34}\n"
1391 " purple {35}\n"
1392 " cyan {36}\n"
1393 " normal {37}\n"
1394 " grey {30 1}\n"
1395 " gray {30 1}\n"
1396 " lred {31 1}\n"
1397 " lgreen {32 1}\n"
1398 " lyellow {33 1}\n"
1399 " lblue {34 1}\n"
1400 " lpurple {35 1}\n"
1401 " lcyan {36 1}\n"
1402 " white {37 1}\n"
1403 "}\n"
1404 "\n"
1405 "\n"
1406 "set tcl::stdhint_col $tcl::stdhint_cols(lcyan)\n"
1407 "\n"
1408 "\n"
1409 "proc tcl::stdhint {string} {\n"
1410 " set result \"\"\n"
1411 " if {[llength $string] >= 2} {\n"
1412 " lassign $string cmd arg\n"
1413 " if {$cmd in $::tcl::stdhint_commands || [info channel $cmd] ne \"\"} {\n"
1414 " catch {\n"
1415 " set help [$cmd -help $arg]\n"
1416 " if {[string match \"Usage: $cmd *\" $help]} {\n"
1417 " set n [llength $string]\n"
1418 " set subcmd [lindex $help $n]\n"
1419 " incr n\n"
1420 " set hint [join [lrange $help $n end]]\n"
1421 " set prefix \"\"\n"
1422 " if {![string match \"* \" $string]} {\n"
1423 " if {$n == 3 && $subcmd ne $arg} {\n"
1424 "\n"
1425 " set prefix \"[string range $subcmd [string length $arg] end] \"\n"
1426 " } else {\n"
1427 " set prefix \" \"\n"
1428 " }\n"
1429 " }\n"
1430 " set result [list $prefix$hint {*}$::tcl::stdhint_col]\n"
1431 " }\n"
1432 " }\n"
1433 " }\n"
1434 " }\n"
1435 " return $result\n"
1436 "}\n"
1437 "\n"
1438 "_jimsh_init\n"
1439 );
1440 }
1441 int Jim_globInit(Jim_Interp *interp)
1442 {
1443 if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1444 return JIM_ERR;
1445
1446 return Jim_EvalSource(interp, "glob.tcl", 1,
1447 "\n"
1448 "\n"
1449 "\n"
1450 "\n"
1451 "\n"
1452 "\n"
1453 "\n"
1454 "package require readdir\n"
1455 "\n"
1456 "\n"
1457 "proc glob.globdir {dir pattern} {\n"
1458 " if {[file exists $dir/$pattern]} {\n"
1459 "\n"
1460 " return [list $pattern]\n"
1461 " }\n"
1462 "\n"
1463 " set result {}\n"
1464 " set files [readdir $dir]\n"
1465 " lappend files . ..\n"
1466 "\n"
1467 " foreach name $files {\n"
1468 " if {[string match $pattern $name]} {\n"
1469 "\n"
1470 " if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1471 " continue\n"
1472 " }\n"
1473 " lappend result $name\n"
1474 " }\n"
1475 " }\n"
1476 "\n"
1477 " return $result\n"
1478 "}\n"
1479 "\n"
1480 "\n"
1481 "\n"
1482 "\n"
1483 "proc glob.explode {pattern} {\n"
1484 " set oldexp {}\n"
1485 " set newexp {\"\"}\n"
1486 "\n"
1487 " while 1 {\n"
1488 " set oldexp $newexp\n"
1489 " set newexp {}\n"
1490 " set ob [string first \\{ $pattern]\n"
1491 " set cb [string first \\} $pattern]\n"
1492 "\n"
1493 " if {$ob < $cb && $ob != -1} {\n"
1494 " set mid [string range $pattern 0 $ob-1]\n"
1495 " set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1496 " if {$pattern eq \"\"} {\n"
1497 " error \"unmatched open brace in glob pattern\"\n"
1498 " }\n"
1499 " set pattern [string range $pattern 1 end]\n"
1500 "\n"
1501 " foreach subs $subexp {\n"
1502 " foreach sub [split $subs ,] {\n"
1503 " foreach old $oldexp {\n"
1504 " lappend newexp $old$mid$sub\n"
1505 " }\n"
1506 " }\n"
1507 " }\n"
1508 " } elseif {$cb != -1} {\n"
1509 " set suf [string range $pattern 0 $cb-1]\n"
1510 " set rest [string range $pattern $cb end]\n"
1511 " break\n"
1512 " } else {\n"
1513 " set suf $pattern\n"
1514 " set rest \"\"\n"
1515 " break\n"
1516 " }\n"
1517 " }\n"
1518 "\n"
1519 " foreach old $oldexp {\n"
1520 " lappend newexp $old$suf\n"
1521 " }\n"
1522 " list $rest {*}$newexp\n"
1523 "}\n"
1524 "\n"
1525 "\n"
1526 "\n"
1527 "proc glob.glob {base pattern} {\n"
1528 " set dir [file dirname $pattern]\n"
1529 " if {$pattern eq $dir || $pattern eq \"\"} {\n"
1530 " return [list [file join $base $dir] $pattern]\n"
1531 " } elseif {$pattern eq [file tail $pattern]} {\n"
1532 " set dir \"\"\n"
1533 " }\n"
1534 "\n"
1535 "\n"
1536 " set dirlist [glob.glob $base $dir]\n"
1537 " set pattern [file tail $pattern]\n"
1538 "\n"
1539 "\n"
1540 " set result {}\n"
1541 " foreach {realdir dir} $dirlist {\n"
1542 " if {![file isdir $realdir]} {\n"
1543 " continue\n"
1544 " }\n"
1545 " if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1546 " append dir /\n"
1547 " }\n"
1548 " foreach name [glob.globdir $realdir $pattern] {\n"
1549 " lappend result [file join $realdir $name] $dir$name\n"
1550 " }\n"
1551 " }\n"
1552 " return $result\n"
1553 "}\n"
1554 "\n"
1555 "\n"
1556 "\n"
1557 "\n"
1558 "\n"
1559 "\n"
1560 "\n"
1561 "\n"
1562 "\n"
1563 "\n"
1564 "\n"
1565 "\n"
1566 "proc glob {args} {\n"
1567 " set nocomplain 0\n"
1568 " set base \"\"\n"
1569 " set tails 0\n"
1570 "\n"
1571 " set n 0\n"
1572 " foreach arg $args {\n"
1573 " if {[info exists param]} {\n"
1574 " set $param $arg\n"
1575 " unset param\n"
1576 " incr n\n"
1577 " continue\n"
1578 " }\n"
1579 " switch -glob -- $arg {\n"
1580 " -d* {\n"
1581 " set switch $arg\n"
1582 " set param base\n"
1583 " }\n"
1584 " -n* {\n"
1585 " set nocomplain 1\n"
1586 " }\n"
1587 " -ta* {\n"
1588 " set tails 1\n"
1589 " }\n"
1590 " -- {\n"
1591 " incr n\n"
1592 " break\n"
1593 " }\n"
1594 " -* {\n"
1595 " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1596 " }\n"
1597 " * {\n"
1598 " break\n"
1599 " }\n"
1600 " }\n"
1601 " incr n\n"
1602 " }\n"
1603 " if {[info exists param]} {\n"
1604 " return -code error \"missing argument to \\\"$switch\\\"\"\n"
1605 " }\n"
1606 " if {[llength $args] <= $n} {\n"
1607 " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1608 " }\n"
1609 "\n"
1610 " set args [lrange $args $n end]\n"
1611 "\n"
1612 " set result {}\n"
1613 " foreach pattern $args {\n"
1614 " set escpattern [string map {\n"
1615 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1616 " } $pattern]\n"
1617 " set patexps [lassign [glob.explode $escpattern] rest]\n"
1618 " if {$rest ne \"\"} {\n"
1619 " return -code error \"unmatched close brace in glob pattern\"\n"
1620 " }\n"
1621 " foreach patexp $patexps {\n"
1622 " set patexp [string map {\n"
1623 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1624 " } $patexp]\n"
1625 " foreach {realname name} [glob.glob $base $patexp] {\n"
1626 " incr n\n"
1627 " if {$tails} {\n"
1628 " lappend result $name\n"
1629 " } else {\n"
1630 " lappend result [file join $base $name]\n"
1631 " }\n"
1632 " }\n"
1633 " }\n"
1634 " }\n"
1635 "\n"
1636 " if {!$nocomplain && [llength $result] == 0} {\n"
1637 " set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1638 " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1639 " }\n"
1640 "\n"
1641 " return $result\n"
1642 "}\n"
1643 );
1644 }
1645 int Jim_stdlibInit(Jim_Interp *interp)
1646 {
1647 if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1648 return JIM_ERR;
1649
1650 return Jim_EvalSource(interp, "stdlib.tcl", 1,
1651 "\n"
1652 "\n"
1653 "if {![exists -command ref]} {\n"
1654 "\n"
1655 " proc ref {args} {{count 0}} {\n"
1656 " format %08x [incr count]\n"
1657 " }\n"
1658 "}\n"
1659 "\n"
1660 "\n"
1661 "proc lambda {arglist args} {\n"
1662 " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1663 "}\n"
1664 "\n"
1665 "proc lambda.finalizer {name val} {\n"
1666 " rename $name {}\n"
1667 "}\n"
1668 "\n"
1669 "\n"
1670 "proc curry {args} {\n"
1671 " alias [ref {} function lambda.finalizer] {*}$args\n"
1672 "}\n"
1673 "\n"
1674 "\n"
1675 "\n"
1676 "\n"
1677 "\n"
1678 "\n"
1679 "\n"
1680 "\n"
1681 "\n"
1682 "proc function {value} {\n"
1683 " return $value\n"
1684 "}\n"
1685 "\n"
1686 "\n"
1687 "proc stackdump {stacktrace} {\n"
1688 " set lines {}\n"
1689 " lappend lines \"Traceback (most recent call last):\"\n"
1690 " foreach {cmd l f p} [lreverse $stacktrace] {\n"
1691 " set line {}\n"
1692 " if {$f ne \"\"} {\n"
1693 " append line \" File \\\"$f\\\", line $l\"\n"
1694 " }\n"
1695 " if {$p ne \"\"} {\n"
1696 " append line \", in $p\"\n"
1697 " }\n"
1698 " if {$line ne \"\"} {\n"
1699 " lappend lines $line\n"
1700 " if {$cmd ne \"\"} {\n"
1701 " set nl [string first \\n $cmd 1]\n"
1702 " if {$nl >= 0} {\n"
1703 " set cmd [string range $cmd 0 $nl-1]...\n"
1704 " }\n"
1705 " lappend lines \" $cmd\"\n"
1706 " }\n"
1707 " }\n"
1708 " }\n"
1709 " if {[llength $lines] > 1} {\n"
1710 " return [join $lines \\n]\n"
1711 " }\n"
1712 "}\n"
1713 "\n"
1714 "\n"
1715 "\n"
1716 "proc defer {script} {\n"
1717 " upvar jim::defer v\n"
1718 " lappend v $script\n"
1719 "}\n"
1720 "\n"
1721 "\n"
1722 "\n"
1723 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1724 " if {$stacktrace eq \"\"} {\n"
1725 "\n"
1726 " set stacktrace [info stacktrace]\n"
1727 " }\n"
1728 " lassign $stacktrace p f l cmd\n"
1729 " if {$f ne \"\"} {\n"
1730 " set result \"$f:$l: Error: \"\n"
1731 " }\n"
1732 " append result \"$msg\\n\"\n"
1733 " append result [stackdump $stacktrace]\n"
1734 "\n"
1735 "\n"
1736 " string trim $result\n"
1737 "}\n"
1738 "\n"
1739 "\n"
1740 "\n"
1741 "proc {info nameofexecutable} {} {\n"
1742 " if {[exists ::jim::exe]} {\n"
1743 " return $::jim::exe\n"
1744 " }\n"
1745 "}\n"
1746 "\n"
1747 "\n"
1748 "proc {dict update} {&varName args script} {\n"
1749 " set keys {}\n"
1750 " foreach {n v} $args {\n"
1751 " upvar $v var_$v\n"
1752 " if {[dict exists $varName $n]} {\n"
1753 " set var_$v [dict get $varName $n]\n"
1754 " }\n"
1755 " }\n"
1756 " catch {uplevel 1 $script} msg opts\n"
1757 " if {[info exists varName]} {\n"
1758 " foreach {n v} $args {\n"
1759 " if {[info exists var_$v]} {\n"
1760 " dict set varName $n [set var_$v]\n"
1761 " } else {\n"
1762 " dict unset varName $n\n"
1763 " }\n"
1764 " }\n"
1765 " }\n"
1766 " return {*}$opts $msg\n"
1767 "}\n"
1768 "\n"
1769 "proc {dict replace} {dictionary {args {key value}}} {\n"
1770 " if {[llength ${key value}] % 2} {\n"
1771 " tailcall {dict replace}\n"
1772 " }\n"
1773 " tailcall dict merge $dictionary ${key value}\n"
1774 "}\n"
1775 "\n"
1776 "\n"
1777 "proc {dict lappend} {varName key {args value}} {\n"
1778 " upvar $varName dict\n"
1779 " if {[exists dict] && [dict exists $dict $key]} {\n"
1780 " set list [dict get $dict $key]\n"
1781 " }\n"
1782 " lappend list {*}$value\n"
1783 " dict set dict $key $list\n"
1784 "}\n"
1785 "\n"
1786 "\n"
1787 "proc {dict append} {varName key {args value}} {\n"
1788 " upvar $varName dict\n"
1789 " if {[exists dict] && [dict exists $dict $key]} {\n"
1790 " set str [dict get $dict $key]\n"
1791 " }\n"
1792 " append str {*}$value\n"
1793 " dict set dict $key $str\n"
1794 "}\n"
1795 "\n"
1796 "\n"
1797 "proc {dict incr} {varName key {increment 1}} {\n"
1798 " upvar $varName dict\n"
1799 " if {[exists dict] && [dict exists $dict $key]} {\n"
1800 " set value [dict get $dict $key]\n"
1801 " }\n"
1802 " incr value $increment\n"
1803 " dict set dict $key $value\n"
1804 "}\n"
1805 "\n"
1806 "\n"
1807 "proc {dict remove} {dictionary {args key}} {\n"
1808 " foreach k $key {\n"
1809 " dict unset dictionary $k\n"
1810 " }\n"
1811 " return $dictionary\n"
1812 "}\n"
1813 "\n"
1814 "\n"
1815 "proc {dict for} {vars dictionary script} {\n"
1816 " if {[llength $vars] != 2} {\n"
1817 " return -code error \"must have exactly two variable names\"\n"
1818 " }\n"
1819 " dict size $dictionary\n"
1820 " tailcall foreach $vars $dictionary $script\n"
1821 "}\n"
1822 );
1823 }
1824 int Jim_tclcompatInit(Jim_Interp *interp)
1825 {
1826 if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1827 return JIM_ERR;
1828
1829 return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1830 "\n"
1831 "\n"
1832 "\n"
1833 "\n"
1834 "\n"
1835 "\n"
1836 "\n"
1837 "\n"
1838 "set env [env]\n"
1839 "\n"
1840 "\n"
1841 "if {[exists -command stdout]} {\n"
1842 "\n"
1843 " foreach p {gets flush close eof seek tell} {\n"
1844 " proc $p {chan args} {p} {\n"
1845 " tailcall $chan $p {*}$args\n"
1846 " }\n"
1847 " }\n"
1848 " unset p\n"
1849 "\n"
1850 "\n"
1851 "\n"
1852 " proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1853 " if {${-nonewline} ni {-nonewline {}}} {\n"
1854 " tailcall ${-nonewline} puts $msg\n"
1855 " }\n"
1856 " tailcall $chan puts {*}${-nonewline} $msg\n"
1857 " }\n"
1858 "\n"
1859 "\n"
1860 "\n"
1861 "\n"
1862 "\n"
1863 " proc read {{-nonewline {}} chan} {\n"
1864 " if {${-nonewline} ni {-nonewline {}}} {\n"
1865 " tailcall ${-nonewline} read {*}${chan}\n"
1866 " }\n"
1867 " tailcall $chan read {*}${-nonewline}\n"
1868 " }\n"
1869 "\n"
1870 " proc fconfigure {f args} {\n"
1871 " foreach {n v} $args {\n"
1872 " switch -glob -- $n {\n"
1873 " -bl* {\n"
1874 " $f ndelay $(!$v)\n"
1875 " }\n"
1876 " -bu* {\n"
1877 " $f buffering $v\n"
1878 " }\n"
1879 " -tr* {\n"
1880 " $f translation $v\n"
1881 " }\n"
1882 " default {\n"
1883 " return -code error \"fconfigure: unknown option $n\"\n"
1884 " }\n"
1885 " }\n"
1886 " }\n"
1887 " }\n"
1888 "}\n"
1889 "\n"
1890 "\n"
1891 "proc fileevent {args} {\n"
1892 " tailcall {*}$args\n"
1893 "}\n"
1894 "\n"
1895 "\n"
1896 "\n"
1897 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1898 " upvar $arrayname a\n"
1899 "\n"
1900 " set max 0\n"
1901 " foreach name [array names a $pattern]] {\n"
1902 " if {[string length $name] > $max} {\n"
1903 " set max [string length $name]\n"
1904 " }\n"
1905 " }\n"
1906 " incr max [string length $arrayname]\n"
1907 " incr max 2\n"
1908 " foreach name [lsort [array names a $pattern]] {\n"
1909 " $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1910 " }\n"
1911 "}\n"
1912 "\n"
1913 "\n"
1914 "proc {file copy} {{force {}} source target} {\n"
1915 " try {\n"
1916 " if {$force ni {{} -force}} {\n"
1917 " error \"bad option \\\"$force\\\": should be -force\"\n"
1918 " }\n"
1919 "\n"
1920 " set in [open $source rb]\n"
1921 "\n"
1922 " if {[file exists $target]} {\n"
1923 " if {$force eq \"\"} {\n"
1924 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1925 " }\n"
1926 "\n"
1927 " if {$source eq $target} {\n"
1928 " return\n"
1929 " }\n"
1930 "\n"
1931 "\n"
1932 " file stat $source ss\n"
1933 " file stat $target ts\n"
1934 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1935 " return\n"
1936 " }\n"
1937 " }\n"
1938 " set out [open $target wb]\n"
1939 " $in copyto $out\n"
1940 " $out close\n"
1941 " } on error {msg opts} {\n"
1942 " incr opts(-level)\n"
1943 " return {*}$opts $msg\n"
1944 " } finally {\n"
1945 " catch {$in close}\n"
1946 " }\n"
1947 "}\n"
1948 "\n"
1949 "\n"
1950 "\n"
1951 "proc popen {cmd {mode r}} {\n"
1952 " lassign [pipe] r w\n"
1953 " try {\n"
1954 " if {[string match \"w*\" $mode]} {\n"
1955 " lappend cmd <@$r &\n"
1956 " set pids [exec {*}$cmd]\n"
1957 " $r close\n"
1958 " set f $w\n"
1959 " } else {\n"
1960 " lappend cmd >@$w &\n"
1961 " set pids [exec {*}$cmd]\n"
1962 " $w close\n"
1963 " set f $r\n"
1964 " }\n"
1965 " lambda {cmd args} {f pids} {\n"
1966 " if {$cmd eq \"pid\"} {\n"
1967 " return $pids\n"
1968 " }\n"
1969 " if {$cmd eq \"close\"} {\n"
1970 " $f close\n"
1971 "\n"
1972 " set retopts {}\n"
1973 " foreach p $pids {\n"
1974 " lassign [wait $p] status - rc\n"
1975 " if {$status eq \"CHILDSTATUS\"} {\n"
1976 " if {$rc == 0} {\n"
1977 " continue\n"
1978 " }\n"
1979 " set msg \"child process exited abnormally\"\n"
1980 " } else {\n"
1981 " set msg \"child killed: received signal\"\n"
1982 " }\n"
1983 " set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n"
1984 " }\n"
1985 " return {*}$retopts\n"
1986 " }\n"
1987 " tailcall $f $cmd {*}$args\n"
1988 " }\n"
1989 " } on error {error opts} {\n"
1990 " $r close\n"
1991 " $w close\n"
1992 " error $error\n"
1993 " }\n"
1994 "}\n"
1995 "\n"
1996 "\n"
1997 "local proc pid {{channelId {}}} {\n"
1998 " if {$channelId eq \"\"} {\n"
1999 " tailcall upcall pid\n"
2000 " }\n"
2001 " if {[catch {$channelId tell}]} {\n"
2002 " return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
2003 " }\n"
2004 " if {[catch {$channelId pid} pids]} {\n"
2005 " return \"\"\n"
2006 " }\n"
2007 " return $pids\n"
2008 "}\n"
2009 "\n"
2010 "\n"
2011 "\n"
2012 "proc throw {code {msg \"\"}} {\n"
2013 " return -code $code $msg\n"
2014 "}\n"
2015 "\n"
2016 "\n"
2017 "proc {file delete force} {path} {\n"
2018 " foreach e [readdir $path] {\n"
2019 " file delete -force $path/$e\n"
2020 " }\n"
2021 " file delete $path\n"
2022 "}\n"
2023 );
2024 }
2025
2026
2027 #include <stdio.h>
2028 #include <string.h>
2029 #include <errno.h>
2030 #include <fcntl.h>
2031 #include <assert.h>
2032 #ifdef HAVE_UNISTD_H
2033 #include <unistd.h>
2034 #include <sys/stat.h>
2035 #endif
2036 #ifdef HAVE_UTIL_H
2037 #include <util.h>
2038 #endif
2039 #ifdef HAVE_PTY_H
2040 #include <pty.h>
2041 #endif
2042
2043
2044 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
2045 #include <sys/socket.h>
2046 #include <netinet/in.h>
2047 #include <netinet/tcp.h>
2048 #include <arpa/inet.h>
2049 #include <netdb.h>
2050 #ifdef HAVE_SYS_UN_H
2051 #include <sys/un.h>
2052 #endif
2053 #define HAVE_SOCKETS
2054 #elif defined (__MINGW32__)
2055
2056 #endif
2057
2058 #if defined(JIM_SSL)
2059 #include <openssl/ssl.h>
2060 #include <openssl/err.h>
2061 #endif
2062
2063 #ifdef HAVE_TERMIOS_H
2064 #endif
2065
2066
2067 #define AIO_CMD_LEN 32
2068 #define AIO_DEFAULT_RBUF_LEN 256
2069 #define AIO_DEFAULT_WBUF_LIMIT (64 * 1024)
2070
2071 #define AIO_KEEPOPEN 1
2072 #define AIO_NODELETE 2
2073 #define AIO_EOF 4
2074 #define AIO_WBUF_NONE 8
2075 #define AIO_NONBLOCK 16
2076
2077 #define AIO_ONEREAD 32
2078
2079 enum wbuftype {
2080 WBUF_OPT_NONE,
2081 WBUF_OPT_LINE,
2082 WBUF_OPT_FULL,
2083 };
2084
2085 #if defined(JIM_IPV6)
2086 #define IPV6 1
2087 #else
2088 #define IPV6 0
2089 #ifndef PF_INET6
2090 #define PF_INET6 0
2091 #endif
2092 #endif
2093 #if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
2094 #define UNIX_SOCKETS 1
2095 #else
2096 #define UNIX_SOCKETS 0
2097 #endif
2098
2099
2100
2101
2102 static int JimReadableTimeout(int fd, long ms)
2103 {
2104 #ifdef HAVE_SELECT
2105 int retval;
2106 struct timeval tv;
2107 fd_set rfds;
2108
2109 FD_ZERO(&rfds);
2110
2111 FD_SET(fd, &rfds);
2112 tv.tv_sec = ms / 1000;
2113 tv.tv_usec = (ms % 1000) * 1000;
2114
2115 retval = select(fd + 1, &rfds, NULL, NULL, ms == 0 ? NULL : &tv);
2116
2117 if (retval > 0) {
2118 return JIM_OK;
2119 }
2120 return JIM_ERR;
2121 #else
2122 return JIM_OK;
2123 #endif
2124 }
2125
2126
2127 struct AioFile;
2128
2129 typedef struct {
2130 int (*writer)(struct AioFile *af, const char *buf, int len);
2131 int (*reader)(struct AioFile *af, char *buf, int len, int pending);
2132 int (*error)(const struct AioFile *af);
2133 const char *(*strerror)(struct AioFile *af);
2134 int (*verify)(struct AioFile *af);
2135 } JimAioFopsType;
2136
2137 typedef struct AioFile
2138 {
2139 Jim_Obj *filename;
2140 int wbuft;
2141 int flags;
2142 long timeout;
2143 int fd;
2144 int addr_family;
2145 void *ssl;
2146 const JimAioFopsType *fops;
2147 Jim_Obj *readbuf;
2148 Jim_Obj *writebuf;
2149 char *rbuf;
2150 size_t rbuf_len;
2151 size_t wbuf_limit;
2152 } AioFile;
2153
2154 static void aio_consume(Jim_Obj *objPtr, int n);
2155
2156 static int stdio_writer(struct AioFile *af, const char *buf, int len)
2157 {
2158 int ret = write(af->fd, buf, len);
2159 if (ret < 0 && errno == EPIPE) {
2160 aio_consume(af->writebuf, Jim_Length(af->writebuf));
2161 }
2162 return ret;
2163 }
2164
2165 static int stdio_reader(struct AioFile *af, char *buf, int len, int nb)
2166 {
2167 if (nb || af->timeout == 0 || JimReadableTimeout(af->fd, af->timeout) == JIM_OK) {
2168
2169 int ret;
2170
2171 errno = 0;
2172 ret = read(af->fd, buf, len);
2173 if (ret <= 0 && errno != EAGAIN && errno != EINTR) {
2174 af->flags |= AIO_EOF;
2175 }
2176 return ret;
2177 }
2178 errno = ETIMEDOUT;
2179 return -1;
2180 }
2181
2182 static int stdio_error(const AioFile *af)
2183 {
2184 if (af->flags & AIO_EOF) {
2185 return JIM_OK;
2186 }
2187
2188 switch (errno) {
2189 case EAGAIN:
2190 case EINTR:
2191 case ETIMEDOUT:
2192 #ifdef ECONNRESET
2193 case ECONNRESET:
2194 #endif
2195 #ifdef ECONNABORTED
2196 case ECONNABORTED:
2197 #endif
2198 return JIM_OK;
2199 default:
2200 return JIM_ERR;
2201 }
2202 }
2203
2204 static const char *stdio_strerror(struct AioFile *af)
2205 {
2206 return strerror(errno);
2207 }
2208
2209 static const JimAioFopsType stdio_fops = {
2210 stdio_writer,
2211 stdio_reader,
2212 stdio_error,
2213 stdio_strerror,
2214 NULL,
2215 };
2216
2217
2218 static void aio_set_nonblocking(AioFile *af, int nb)
2219 {
2220 #ifdef O_NDELAY
2221 int old = !!(af->flags & AIO_NONBLOCK);
2222 if (old != nb) {
2223 int fmode = fcntl(af->fd, F_GETFL);
2224 if (nb) {
2225 fmode |= O_NDELAY;
2226 af->flags |= AIO_NONBLOCK;
2227 }
2228 else {
2229 fmode &= ~O_NDELAY;
2230 af->flags &= ~AIO_NONBLOCK;
2231 }
2232 (void)fcntl(af->fd, F_SETFL, fmode);
2233 }
2234 #endif
2235 }
2236
2237 static int aio_start_nonblocking(AioFile *af)
2238 {
2239 int old = !!(af->flags & AIO_NONBLOCK);
2240 if (af->timeout) {
2241 aio_set_nonblocking(af, 1);
2242 }
2243 return old;
2244 }
2245
2246 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
2247 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
2248 const char *hdlfmt, int family, int flags);
2249
2250
2251 static const char *JimAioErrorString(AioFile *af)
2252 {
2253 if (af && af->fops)
2254 return af->fops->strerror(af);
2255
2256 return strerror(errno);
2257 }
2258
2259 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
2260 {
2261 AioFile *af = Jim_CmdPrivData(interp);
2262
2263 if (name) {
2264 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
2265 }
2266 else {
2267 Jim_SetResultString(interp, JimAioErrorString(af), -1);
2268 }
2269 }
2270
2271 static int aio_eof(AioFile *af)
2272 {
2273 return af->flags & AIO_EOF;
2274 }
2275
2276 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
2277 {
2278 int ret = 0;
2279 if (!aio_eof(af)) {
2280 ret = af->fops->error(af);
2281 if (ret) {
2282 JimAioSetError(interp, af->filename);
2283 }
2284 }
2285 return ret;
2286 }
2287
2288 static void aio_consume(Jim_Obj *objPtr, int n)
2289 {
2290 assert(objPtr->bytes);
2291 assert(n <= objPtr->length);
2292
2293
2294 memmove(objPtr->bytes, objPtr->bytes + n, objPtr->length - n + 1);
2295 objPtr->length -= n;
2296 }
2297
2298
2299 static int aio_flush(Jim_Interp *interp, AioFile *af);
2300
2301 #ifdef jim_ext_eventloop
2302 static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask)
2303 {
2304 AioFile *af = clientData;
2305
2306 aio_flush(interp, af);
2307 if (Jim_Length(af->writebuf) == 0) {
2308
2309 return -1;
2310 }
2311 return 0;
2312 }
2313 #endif
2314
2315
2316 static int aio_flush(Jim_Interp *interp, AioFile *af)
2317 {
2318 int len;
2319 const char *pt = Jim_GetString(af->writebuf, &len);
2320 if (len) {
2321 int ret = af->fops->writer(af, pt, len);
2322 if (ret > 0) {
2323
2324 aio_consume(af->writebuf, ret);
2325 }
2326 if (ret < 0) {
2327 return JimCheckStreamError(interp, af);
2328 }
2329 if (Jim_Length(af->writebuf)) {
2330 #ifdef jim_ext_eventloop
2331 void *handler = Jim_FindFileHandler(interp, af->fd, JIM_EVENT_WRITABLE);
2332 if (handler == NULL) {
2333 Jim_CreateFileHandler(interp, af->fd, JIM_EVENT_WRITABLE, aio_autoflush, af, NULL);
2334 return JIM_OK;
2335 }
2336 else if (handler == af) {
2337
2338 return JIM_OK;
2339 }
2340 #endif
2341
2342 Jim_SetResultString(interp, "send buffer is full", -1);
2343 return JIM_ERR;
2344 }
2345 }
2346 return JIM_OK;
2347 }
2348
2349 static int aio_read_len(Jim_Interp *interp, AioFile *af, unsigned flags, int neededLen)
2350 {
2351 if (!af->readbuf) {
2352 af->readbuf = Jim_NewStringObj(interp, NULL, 0);
2353 }
2354
2355 if (neededLen >= 0) {
2356 neededLen -= Jim_Length(af->readbuf);
2357 if (neededLen <= 0) {
2358 return JIM_OK;
2359 }
2360 }
2361
2362 while (neededLen && !aio_eof(af)) {
2363 int retval;
2364 int readlen;
2365
2366 if (neededLen == -1) {
2367 readlen = af->rbuf_len;
2368 }
2369 else {
2370 readlen = (neededLen > af->rbuf_len ? af->rbuf_len : neededLen);
2371 }
2372
2373 if (!af->rbuf) {
2374 af->rbuf = Jim_Alloc(af->rbuf_len);
2375 }
2376 retval = af->fops->reader(af, af->rbuf, readlen, flags & AIO_NONBLOCK);
2377 if (retval > 0) {
2378 if (retval) {
2379 Jim_AppendString(interp, af->readbuf, af->rbuf, retval);
2380 }
2381 if (neededLen != -1) {
2382 neededLen -= retval;
2383 }
2384 if (flags & AIO_ONEREAD) {
2385 return JIM_OK;
2386 }
2387 continue;
2388 }
2389 if ((flags & AIO_ONEREAD) || JimCheckStreamError(interp, af)) {
2390 return JIM_ERR;
2391 }
2392 break;
2393 }
2394
2395 return JIM_OK;
2396 }
2397
2398 static Jim_Obj *aio_read_consume(Jim_Interp *interp, AioFile *af, int neededLen)
2399 {
2400 Jim_Obj *objPtr = NULL;
2401
2402 if (neededLen < 0 || af->readbuf == NULL || Jim_Length(af->readbuf) <= neededLen) {
2403 objPtr = af->readbuf;
2404 af->readbuf = NULL;
2405 }
2406 else if (af->readbuf) {
2407
2408 int len;
2409 const char *pt = Jim_GetString(af->readbuf, &len);
2410
2411 objPtr = Jim_NewStringObj(interp, pt, neededLen);
2412 aio_consume(af->readbuf, neededLen);
2413 }
2414
2415 return objPtr;
2416 }
2417
2418 static void JimAioDelProc(Jim_Interp *interp, void *privData)
2419 {
2420 AioFile *af = privData;
2421
2422 JIM_NOTUSED(interp);
2423
2424
2425 aio_flush(interp, af);
2426 Jim_DecrRefCount(interp, af->writebuf);
2427
2428 #if UNIX_SOCKETS
2429 if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) {
2430
2431 Jim_Obj *filenameObj = aio_sockname(interp, af->fd);
2432 if (filenameObj) {
2433 if (Jim_Length(filenameObj)) {
2434 remove(Jim_String(filenameObj));
2435 }
2436 Jim_FreeNewObj(interp, filenameObj);
2437 }
2438 }
2439 #endif
2440
2441 Jim_DecrRefCount(interp, af->filename);
2442
2443 #ifdef jim_ext_eventloop
2444
2445 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
2446 #endif
2447
2448 #if defined(JIM_SSL)
2449 if (af->ssl != NULL) {
2450 SSL_free(af->ssl);
2451 }
2452 #endif
2453 if (!(af->flags & AIO_KEEPOPEN)) {
2454 close(af->fd);
2455 }
2456 if (af->readbuf) {
2457 Jim_FreeNewObj(interp, af->readbuf);
2458 }
2459
2460 Jim_Free(af->rbuf);
2461 Jim_Free(af);
2462 }
2463
2464 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2465 {
2466 AioFile *af = Jim_CmdPrivData(interp);
2467 int nonewline = 0;
2468 jim_wide neededLen = -1;
2469 static const char * const options[] = { "-pending", "-nonewline", NULL };
2470 enum { OPT_PENDING, OPT_NONEWLINE };
2471 int option;
2472 int nb;
2473 Jim_Obj *objPtr;
2474
2475 if (argc) {
2476 if (*Jim_String(argv[0]) == '-') {
2477 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2478 return JIM_ERR;
2479 }
2480 switch (option) {
2481 case OPT_PENDING:
2482
2483 break;
2484 case OPT_NONEWLINE:
2485 nonewline++;
2486 break;
2487 }
2488 }
2489 else {
2490 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
2491 return JIM_ERR;
2492 if (neededLen < 0) {
2493 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
2494 return JIM_ERR;
2495 }
2496 }
2497 argc--;
2498 argv++;
2499 }
2500 if (argc) {
2501 return -1;
2502 }
2503
2504
2505 nb = aio_start_nonblocking(af);
2506
2507 if (aio_read_len(interp, af, nb ? AIO_NONBLOCK : 0, neededLen) != JIM_OK) {
2508 aio_set_nonblocking(af, nb);
2509 return JIM_ERR;
2510 }
2511 objPtr = aio_read_consume(interp, af, neededLen);
2512
2513 aio_set_nonblocking(af, nb);
2514
2515 if (objPtr) {
2516 if (nonewline) {
2517 int len;
2518 const char *s = Jim_GetString(objPtr, &len);
2519
2520 if (len > 0 && s[len - 1] == '\n') {
2521 objPtr->length--;
2522 objPtr->bytes[objPtr->length] = '\0';
2523 }
2524 }
2525 Jim_SetResult(interp, objPtr);
2526 }
2527 else {
2528 Jim_SetEmptyResult(interp);
2529 }
2530 return JIM_OK;
2531 }
2532
2533 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2534 {
2535 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2536
2537
2538 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2539 return ((AioFile *) cmdPtr->u.native.privData)->fd;
2540 }
2541 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2542 return -1;
2543 }
2544
2545 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2546 {
2547 AioFile *af = Jim_CmdPrivData(interp);
2548
2549
2550 aio_flush(interp, af);
2551
2552 Jim_SetResultInt(interp, af->fd);
2553
2554 return JIM_OK;
2555 }
2556
2557 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2558 {
2559 AioFile *af = Jim_CmdPrivData(interp);
2560 jim_wide count = 0;
2561 jim_wide maxlen = JIM_WIDE_MAX;
2562 int ok = 1;
2563 Jim_Obj *objv[4];
2564
2565 if (argc == 2) {
2566 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
2567 return JIM_ERR;
2568 }
2569 }
2570
2571 objv[0] = argv[0];
2572 objv[1] = Jim_NewStringObj(interp, "flush", -1);
2573 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
2574 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", argv[0]);
2575 return JIM_ERR;
2576 }
2577
2578
2579 objv[0] = argv[0];
2580 objv[1] = Jim_NewStringObj(interp, "puts", -1);
2581 objv[2] = Jim_NewStringObj(interp, "-nonewline", -1);
2582 Jim_IncrRefCount(objv[1]);
2583 Jim_IncrRefCount(objv[2]);
2584
2585 while (count < maxlen) {
2586 jim_wide len = maxlen - count;
2587 if (len > af->rbuf_len) {
2588 len = af->rbuf_len;
2589 }
2590 if (aio_read_len(interp, af, 0, len) != JIM_OK) {
2591 ok = 0;
2592 break;
2593 }
2594 objv[3] = aio_read_consume(interp, af, len);
2595 count += Jim_Length(objv[3]);
2596 if (Jim_EvalObjVector(interp, 4, objv) != JIM_OK) {
2597 ok = 0;
2598 break;
2599 }
2600 if (aio_eof(af)) {
2601 break;
2602 }
2603 if (count >= 16384 && af->rbuf_len < 65536) {
2604
2605 af->rbuf_len = 65536;
2606 af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len);
2607 }
2608 }
2609
2610 Jim_DecrRefCount(interp, objv[1]);
2611 Jim_DecrRefCount(interp, objv[2]);
2612
2613 if (!ok) {
2614 return JIM_ERR;
2615 }
2616
2617 Jim_SetResultInt(interp, count);
2618
2619 return JIM_OK;
2620 }
2621
2622 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2623 {
2624 AioFile *af = Jim_CmdPrivData(interp);
2625 Jim_Obj *objPtr = NULL;
2626 int len;
2627 int nb;
2628 unsigned flags = AIO_ONEREAD;
2629 char *nl = NULL;
2630 int offset = 0;
2631
2632 errno = 0;
2633
2634
2635 nb = aio_start_nonblocking(af);
2636 if (nb) {
2637 flags |= AIO_NONBLOCK;
2638 }
2639
2640 while (!aio_eof(af)) {
2641 if (af->readbuf) {
2642 const char *pt = Jim_GetString(af->readbuf, &len);
2643 nl = memchr(pt + offset, '\n', len - offset);
2644 if (nl) {
2645
2646 objPtr = Jim_NewStringObj(interp, pt, nl - pt);
2647
2648 aio_consume(af->readbuf, nl - pt + 1);
2649 break;
2650 }
2651 offset = len;
2652 }
2653
2654
2655 if (aio_read_len(interp, af, flags, -1) != JIM_OK) {
2656 break;
2657 }
2658 }
2659
2660 aio_set_nonblocking(af, nb);
2661
2662 if (!nl && aio_eof(af) && af->readbuf) {
2663
2664 objPtr = af->readbuf;
2665 af->readbuf = NULL;
2666 }
2667 else if (!objPtr) {
2668 objPtr = Jim_NewStringObj(interp, NULL, 0);
2669 }
2670
2671 if (argc) {
2672 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2673 Jim_FreeNewObj(interp, objPtr);
2674 return JIM_ERR;
2675 }
2676
2677 len = Jim_Length(objPtr);
2678
2679 if (!nl && len == 0) {
2680
2681 len = -1;
2682 }
2683 Jim_SetResultInt(interp, len);
2684 }
2685 else {
2686 Jim_SetResult(interp, objPtr);
2687 }
2688 return JIM_OK;
2689 }
2690
2691 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2692 {
2693 AioFile *af = Jim_CmdPrivData(interp);
2694 int wlen;
2695 const char *wdata;
2696 Jim_Obj *strObj;
2697 int wnow = 0;
2698 int nl = 1;
2699
2700 if (argc == 2) {
2701 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2702 return -1;
2703 }
2704 strObj = argv[1];
2705 nl = 0;
2706 }
2707 else {
2708 strObj = argv[0];
2709 }
2710
2711 #ifdef JIM_MAINTAINER
2712 if (Jim_IsShared(af->writebuf)) {
2713 Jim_DecrRefCount(interp, af->writebuf);
2714 af->writebuf = Jim_DuplicateObj(interp, af->writebuf);
2715 Jim_IncrRefCount(af->writebuf);
2716 }
2717 #endif
2718 Jim_AppendObj(interp, af->writebuf, strObj);
2719 if (nl) {
2720 Jim_AppendString(interp, af->writebuf, "\n", 1);
2721 }
2722
2723
2724 wdata = Jim_GetString(af->writebuf, &wlen);
2725 switch (af->wbuft) {
2726 case WBUF_OPT_NONE:
2727
2728 wnow = 1;
2729 break;
2730
2731 case WBUF_OPT_LINE:
2732
2733 if (nl || memchr(wdata, '\n', wlen) != NULL) {
2734 wnow = 1;
2735 }
2736 break;
2737
2738 case WBUF_OPT_FULL:
2739 if (wlen >= af->wbuf_limit) {
2740 wnow = 1;
2741 }
2742 break;
2743 }
2744
2745 if (wnow) {
2746 return aio_flush(interp, af);
2747 }
2748 return JIM_OK;
2749 }
2750
2751 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2752 {
2753 #ifdef HAVE_ISATTY
2754 AioFile *af = Jim_CmdPrivData(interp);
2755 Jim_SetResultInt(interp, isatty(af->fd));
2756 #else
2757 Jim_SetResultInt(interp, 0);
2758 #endif
2759
2760 return JIM_OK;
2761 }
2762
2763
2764 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2765 {
2766 AioFile *af = Jim_CmdPrivData(interp);
2767 return aio_flush(interp, af);
2768 }
2769
2770 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2771 {
2772 AioFile *af = Jim_CmdPrivData(interp);
2773
2774 Jim_SetResultInt(interp, !!aio_eof(af));
2775 return JIM_OK;
2776 }
2777
2778 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2779 {
2780 AioFile *af = Jim_CmdPrivData(interp);
2781 if (argc == 3) {
2782 int option = -1;
2783 #if defined(HAVE_SOCKETS)
2784 static const char * const options[] = { "r", "w", "-nodelete", NULL };
2785 enum { OPT_R, OPT_W, OPT_NODELETE };
2786
2787 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2788 return JIM_ERR;
2789 }
2790 #endif
2791 switch (option) {
2792 #if defined(HAVE_SHUTDOWN)
2793 case OPT_R:
2794 case OPT_W:
2795 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2796 return JIM_OK;
2797 }
2798 JimAioSetError(interp, NULL);
2799 return JIM_ERR;
2800 #endif
2801 #if UNIX_SOCKETS
2802 case OPT_NODELETE:
2803 if (af->addr_family == PF_UNIX) {
2804 af->flags |= AIO_NODELETE;
2805 break;
2806 }
2807
2808 #endif
2809 default:
2810 Jim_SetResultString(interp, "not supported", -1);
2811 return JIM_ERR;
2812 }
2813 }
2814
2815
2816 af->flags &= ~AIO_KEEPOPEN;
2817
2818 return Jim_DeleteCommand(interp, argv[0]);
2819 }
2820
2821 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2822 {
2823 AioFile *af = Jim_CmdPrivData(interp);
2824 int orig = SEEK_SET;
2825 jim_wide offset;
2826
2827 if (argc == 2) {
2828 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2829 orig = SEEK_SET;
2830 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2831 orig = SEEK_CUR;
2832 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2833 orig = SEEK_END;
2834 else {
2835 return -1;
2836 }
2837 }
2838 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2839 return JIM_ERR;
2840 }
2841 if (orig != SEEK_CUR || offset != 0) {
2842
2843 aio_flush(interp, af);
2844 }
2845 if (Jim_Lseek(af->fd, offset, orig) == -1) {
2846 JimAioSetError(interp, af->filename);
2847 return JIM_ERR;
2848 }
2849 if (af->readbuf) {
2850 Jim_FreeNewObj(interp, af->readbuf);
2851 af->readbuf = NULL;
2852 }
2853 af->flags &= ~AIO_EOF;
2854 return JIM_OK;
2855 }
2856
2857 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2858 {
2859 AioFile *af = Jim_CmdPrivData(interp);
2860
2861 Jim_SetResultInt(interp, Jim_Lseek(af->fd, 0, SEEK_CUR));
2862 return JIM_OK;
2863 }
2864
2865 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2866 {
2867 AioFile *af = Jim_CmdPrivData(interp);
2868
2869 Jim_SetResult(interp, af->filename);
2870 return JIM_OK;
2871 }
2872
2873 #ifdef O_NDELAY
2874 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2875 {
2876 AioFile *af = Jim_CmdPrivData(interp);
2877
2878 if (argc) {
2879 long nb;
2880
2881 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2882 return JIM_ERR;
2883 }
2884 aio_set_nonblocking(af, nb);
2885 }
2886 Jim_SetResultInt(interp, (af->flags & AIO_NONBLOCK) ? 1 : 0);
2887 return JIM_OK;
2888 }
2889 #endif
2890
2891
2892 #ifdef HAVE_FSYNC
2893 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2894 {
2895 AioFile *af = Jim_CmdPrivData(interp);
2896
2897 if (aio_flush(interp, af) != JIM_OK) {
2898 return JIM_ERR;
2899 }
2900 fsync(af->fd);
2901 return JIM_OK;
2902 }
2903 #endif
2904
2905 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2906 {
2907 AioFile *af = Jim_CmdPrivData(interp);
2908 Jim_Obj *resultObj;
2909
2910 static const char * const options[] = {
2911 "none",
2912 "line",
2913 "full",
2914 NULL
2915 };
2916
2917 if (argc) {
2918 if (Jim_GetEnum(interp, argv[0], options, &af->wbuft, NULL, JIM_ERRMSG) != JIM_OK) {
2919 return JIM_ERR;
2920 }
2921
2922 if (af->wbuft == WBUF_OPT_FULL && argc == 2) {
2923 long l;
2924 if (Jim_GetLong(interp, argv[1], &l) != JIM_OK || l <= 0) {
2925 return JIM_ERR;
2926 }
2927 af->wbuf_limit = l;
2928 }
2929
2930 if (af->wbuft == WBUF_OPT_NONE) {
2931 if (aio_flush(interp, af) != JIM_OK) {
2932 return JIM_ERR;
2933 }
2934 }
2935
2936 }
2937
2938 resultObj = Jim_NewListObj(interp, NULL, 0);
2939 Jim_ListAppendElement(interp, resultObj, Jim_NewStringObj(interp, options[af->wbuft], -1));
2940 if (af->wbuft == WBUF_OPT_FULL) {
2941 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, af->wbuf_limit));
2942 }
2943 Jim_SetResult(interp, resultObj);
2944
2945 return JIM_OK;
2946 }
2947
2948 static int aio_cmd_translation(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2949 {
2950 enum {OPT_BINARY, OPT_TEXT};
2951 static const char * const options[] = {
2952 "binary",
2953 "text",
2954 NULL
2955 };
2956 int opt;
2957
2958 if (Jim_GetEnum(interp, argv[0], options, &opt, NULL, JIM_ERRMSG) != JIM_OK) {
2959 return JIM_ERR;
2960 }
2961 #if defined(Jim_SetMode)
2962 else {
2963 AioFile *af = Jim_CmdPrivData(interp);
2964 Jim_SetMode(af->fd, opt == OPT_BINARY ? O_BINARY : O_TEXT);
2965 }
2966 #endif
2967 return JIM_OK;
2968 }
2969
2970 static int aio_cmd_readsize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2971 {
2972 AioFile *af = Jim_CmdPrivData(interp);
2973
2974 if (argc) {
2975 long l;
2976 if (Jim_GetLong(interp, argv[0], &l) != JIM_OK || l <= 0) {
2977 return JIM_ERR;
2978 }
2979 af->rbuf_len = l;
2980 if (af->rbuf) {
2981 af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len);
2982 }
2983 }
2984 Jim_SetResultInt(interp, af->rbuf_len);
2985
2986 return JIM_OK;
2987 }
2988
2989 #ifdef jim_ext_eventloop
2990 static int aio_cmd_timeout(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2991 {
2992 #ifdef HAVE_SELECT
2993 AioFile *af = Jim_CmdPrivData(interp);
2994 if (argc == 1) {
2995 if (Jim_GetLong(interp, argv[0], &af->timeout) != JIM_OK) {
2996 return JIM_ERR;
2997 }
2998 }
2999 Jim_SetResultInt(interp, af->timeout);
3000 return JIM_OK;
3001 #else
3002 Jim_SetResultString(interp, "timeout not supported", -1);
3003 return JIM_ERR;
3004 #endif
3005 }
3006
3007 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask,
3008 int argc, Jim_Obj * const *argv)
3009 {
3010 if (argc == 0) {
3011
3012 Jim_Obj *objPtr = Jim_FindFileHandler(interp, af->fd, mask);
3013 if (objPtr) {
3014 Jim_SetResult(interp, objPtr);
3015 }
3016 return JIM_OK;
3017 }
3018
3019
3020 Jim_DeleteFileHandler(interp, af->fd, mask);
3021
3022
3023 if (Jim_Length(argv[0])) {
3024 Jim_CreateScriptFileHandler(interp, af->fd, mask, argv[0]);
3025 }
3026
3027 return JIM_OK;
3028 }
3029
3030 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3031 {
3032 AioFile *af = Jim_CmdPrivData(interp);
3033
3034 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, argc, argv);
3035 }
3036
3037 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3038 {
3039 AioFile *af = Jim_CmdPrivData(interp);
3040
3041 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, argc, argv);
3042 }
3043
3044 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3045 {
3046 AioFile *af = Jim_CmdPrivData(interp);
3047
3048 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, argc, argv);
3049 }
3050 #endif
3051
3052 #if defined(jim_ext_file) && defined(Jim_FileStat)
3053 static int aio_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3054 {
3055 jim_stat_t sb;
3056 AioFile *af = Jim_CmdPrivData(interp);
3057
3058 if (Jim_FileStat(af->fd, &sb) == -1) {
3059 JimAioSetError(interp, NULL);
3060 return JIM_ERR;
3061 }
3062 return Jim_FileStoreStatData(interp, argc == 0 ? NULL : argv[0], &sb);
3063 }
3064 #endif
3065
3066
3067
3068
3069 static const jim_subcmd_type aio_command_table[] = {
3070 { "read",
3071 "?-nonewline|len?",
3072 aio_cmd_read,
3073 0,
3074 2,
3075
3076 },
3077 { "copyto",
3078 "handle ?size?",
3079 aio_cmd_copy,
3080 1,
3081 2,
3082
3083 },
3084 { "getfd",
3085 NULL,
3086 aio_cmd_getfd,
3087 0,
3088 0,
3089
3090 },
3091 { "gets",
3092 "?var?",
3093 aio_cmd_gets,
3094 0,
3095 1,
3096
3097 },
3098 { "puts",
3099 "?-nonewline? str",
3100 aio_cmd_puts,
3101 1,
3102 2,
3103
3104 },
3105 { "isatty",
3106 NULL,
3107 aio_cmd_isatty,
3108 0,
3109 0,
3110
3111 },
3112 { "flush",
3113 NULL,
3114 aio_cmd_flush,
3115 0,
3116 0,
3117
3118 },
3119 { "eof",
3120 NULL,
3121 aio_cmd_eof,
3122 0,
3123 0,
3124
3125 },
3126 { "close",
3127 "?r(ead)|w(rite)?",
3128 aio_cmd_close,
3129 0,
3130 1,
3131 JIM_MODFLAG_FULLARGV,
3132
3133 },
3134 { "seek",
3135 "offset ?start|current|end",
3136 aio_cmd_seek,
3137 1,
3138 2,
3139
3140 },
3141 { "tell",
3142 NULL,
3143 aio_cmd_tell,
3144 0,
3145 0,
3146
3147 },
3148 { "filename",
3149 NULL,
3150 aio_cmd_filename,
3151 0,
3152 0,
3153
3154 },
3155 #ifdef O_NDELAY
3156 { "ndelay",
3157 "?0|1?",
3158 aio_cmd_ndelay,
3159 0,
3160 1,
3161
3162 },
3163 #endif
3164 #ifdef HAVE_FSYNC
3165 { "sync",
3166 NULL,
3167 aio_cmd_sync,
3168 0,
3169 0,
3170
3171 },
3172 #endif
3173 { "buffering",
3174 "?none|line|full? ?size?",
3175 aio_cmd_buffering,
3176 0,
3177 2,
3178
3179 },
3180 { "translation",
3181 "binary|text",
3182 aio_cmd_translation,
3183 1,
3184 1,
3185
3186 },
3187 { "readsize",
3188 "?size?",
3189 aio_cmd_readsize,
3190 0,
3191 1,
3192
3193 },
3194 #if defined(jim_ext_file) && defined(Jim_FileStat)
3195 { "stat",
3196 "?var?",
3197 aio_cmd_stat,
3198 0,
3199 1,
3200
3201 },
3202 #endif
3203 #ifdef jim_ext_eventloop
3204 { "readable",
3205 "?readable-script?",
3206 aio_cmd_readable,
3207 0,
3208 1,
3209
3210 },
3211 { "writable",
3212 "?writable-script?",
3213 aio_cmd_writable,
3214 0,
3215 1,
3216
3217 },
3218 { "onexception",
3219 "?exception-script?",
3220 aio_cmd_onexception,
3221 0,
3222 1,
3223
3224 },
3225 { "timeout",
3226 "?ms?",
3227 aio_cmd_timeout,
3228 0,
3229 1,
3230
3231 },
3232 #endif
3233 { NULL }
3234 };
3235
3236 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3237 {
3238 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
3239 }
3240
3241 static int parse_posix_open_mode(Jim_Interp *interp, Jim_Obj *modeObj)
3242 {
3243 int i;
3244 int flags = 0;
3245 #ifndef O_NOCTTY
3246
3247 #define O_NOCTTY 0
3248 #endif
3249 static const char * const modetypes[] = {
3250 "RDONLY", "WRONLY", "RDWR", "APPEND", "BINARY", "CREAT", "EXCL", "NOCTTY", "TRUNC", NULL
3251 };
3252 static const int modeflags[] = {
3253 O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, 0, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC,
3254 };
3255
3256 for (i = 0; i < Jim_ListLength(interp, modeObj); i++) {
3257 int opt;
3258 Jim_Obj *objPtr = Jim_ListGetIndex(interp, modeObj, i);
3259 if (Jim_GetEnum(interp, objPtr, modetypes, &opt, "access mode", JIM_ERRMSG) != JIM_OK) {
3260 return -1;
3261 }
3262 flags |= modeflags[opt];
3263 }
3264 return flags;
3265 }
3266
3267 static int parse_open_mode(Jim_Interp *interp, Jim_Obj *filenameObj, Jim_Obj *modeObj)
3268 {
3269
3270 int flags;
3271 const char *mode = Jim_String(modeObj);
3272 if (*mode == 'R' || *mode == 'W') {
3273 return parse_posix_open_mode(interp, modeObj);
3274 }
3275 if (*mode == 'r') {
3276 flags = O_RDONLY;
3277 }
3278 else if (*mode == 'w') {
3279 flags = O_WRONLY | O_CREAT | O_TRUNC;
3280 }
3281 else if (*mode == 'a') {
3282 flags = O_WRONLY | O_CREAT | O_APPEND;
3283 }
3284 else {
3285 Jim_SetResultFormatted(interp, "%s: invalid open mode '%s'", Jim_String(filenameObj), mode);
3286 return -1;
3287 }
3288 mode++;
3289
3290 if (*mode == 'b') {
3291 #ifdef O_BINARY
3292 flags |= O_BINARY;
3293 #endif
3294 mode++;
3295 }
3296
3297 if (*mode == 't') {
3298 #ifdef O_TEXT
3299 flags |= O_TEXT;
3300 #endif
3301 mode++;
3302 }
3303
3304 if (*mode == '+') {
3305 mode++;
3306
3307 flags &= ~(O_RDONLY | O_WRONLY);
3308 flags |= O_RDWR;
3309 }
3310
3311 if (*mode == 'x') {
3312 mode++;
3313 #ifdef O_EXCL
3314 flags |= O_EXCL;
3315 #endif
3316 }
3317
3318 if (*mode == 'F') {
3319 mode++;
3320 #ifdef O_LARGEFILE
3321 flags |= O_LARGEFILE;
3322 #endif
3323 }
3324
3325 if (*mode == 'e') {
3326
3327 mode++;
3328 }
3329 return flags;
3330 }
3331
3332 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
3333 Jim_Obj *const *argv)
3334 {
3335 int openflags;
3336 const char *filename;
3337 int fd = -1;
3338 int n = 0;
3339 int flags = 0;
3340
3341 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-noclose")) {
3342 flags = AIO_KEEPOPEN;
3343 n++;
3344 }
3345 if (argc < 2 || argc > 3 + n) {
3346 Jim_WrongNumArgs(interp, 1, argv, "filename ?-noclose? ?mode?");
3347 return JIM_ERR;
3348 }
3349
3350 filename = Jim_String(argv[1]);
3351
3352 #ifdef jim_ext_tclcompat
3353 {
3354
3355
3356 if (*filename == '|') {
3357 Jim_Obj *evalObj[3];
3358 int i = 0;
3359
3360 evalObj[i++] = Jim_NewStringObj(interp, "::popen", -1);
3361 evalObj[i++] = Jim_NewStringObj(interp, filename + 1, -1);
3362 if (argc == 3 + n) {
3363 evalObj[i++] = argv[2 + n];
3364 }
3365
3366 return Jim_EvalObjVector(interp, i, evalObj);
3367 }
3368 }
3369 #endif
3370 if (argc == 3 + n) {
3371 openflags = parse_open_mode(interp, argv[1], argv[2 + n]);
3372 if (openflags == -1) {
3373 return JIM_ERR;
3374 }
3375 }
3376 else {
3377 openflags = O_RDONLY;
3378 }
3379 fd = open(filename, openflags, 0666);
3380 if (fd < 0) {
3381 JimAioSetError(interp, argv[1]);
3382 return JIM_ERR;
3383 }
3384
3385 return JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags) ? JIM_OK : JIM_ERR;
3386 }
3387
3388
3389 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
3390 const char *hdlfmt, int family, int flags)
3391 {
3392 AioFile *af;
3393 char buf[AIO_CMD_LEN];
3394 Jim_Obj *cmdname;
3395
3396 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
3397 cmdname = Jim_NewStringObj(interp, buf, -1);
3398 if (!filename) {
3399 filename = cmdname;
3400 }
3401 Jim_IncrRefCount(filename);
3402
3403
3404 af = Jim_Alloc(sizeof(*af));
3405 memset(af, 0, sizeof(*af));
3406 af->filename = filename;
3407 af->fd = fd;
3408 af->addr_family = family;
3409 af->fops = &stdio_fops;
3410 af->ssl = NULL;
3411 if (flags & AIO_WBUF_NONE) {
3412 af->wbuft = WBUF_OPT_NONE;
3413 }
3414 else {
3415 #ifdef HAVE_ISATTY
3416 af->wbuft = isatty(af->fd) ? WBUF_OPT_LINE : WBUF_OPT_FULL;
3417 #else
3418 af->wbuft = WBUF_OPT_FULL;
3419 #endif
3420 }
3421
3422 #ifdef FD_CLOEXEC
3423 if ((flags & AIO_KEEPOPEN) == 0) {
3424 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
3425 }
3426 #endif
3427 aio_set_nonblocking(af, !!(flags & AIO_NONBLOCK));
3428
3429 af->flags |= flags;
3430
3431 af->writebuf = Jim_NewStringObj(interp, NULL, 0);
3432 Jim_IncrRefCount(af->writebuf);
3433 af->wbuf_limit = AIO_DEFAULT_WBUF_LIMIT;
3434 af->rbuf_len = AIO_DEFAULT_RBUF_LEN;
3435
3436
3437 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
3438
3439 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, cmdname));
3440
3441 return af;
3442 }
3443
3444 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
3445 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
3446 const char *hdlfmt, int family, int flags)
3447 {
3448 if (JimMakeChannel(interp, p[0], filename, hdlfmt, family, flags)) {
3449 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
3450 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
3451 if (JimMakeChannel(interp, p[1], filename, hdlfmt, family, flags)) {
3452 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
3453 Jim_SetResult(interp, objPtr);
3454 return JIM_OK;
3455 }
3456 }
3457
3458
3459 close(p[0]);
3460 close(p[1]);
3461 JimAioSetError(interp, NULL);
3462 return JIM_ERR;
3463 }
3464 #endif
3465
3466 #ifdef HAVE_PIPE
3467 static int JimCreatePipe(Jim_Interp *interp, Jim_Obj *filenameObj, int flags)
3468 {
3469 int p[2];
3470
3471 if (pipe(p) != 0) {
3472 JimAioSetError(interp, NULL);
3473 return JIM_ERR;
3474 }
3475
3476 return JimMakeChannelPair(interp, p, filenameObj, "aio.pipe%ld", 0, flags);
3477 }
3478
3479
3480 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3481 {
3482 if (argc != 1) {
3483 Jim_WrongNumArgs(interp, 1, argv, "");
3484 return JIM_ERR;
3485 }
3486 return JimCreatePipe(interp, argv[0], 0);
3487 }
3488 #endif
3489
3490 #ifdef HAVE_OPENPTY
3491 static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3492 {
3493 int p[2];
3494 char path[MAXPATHLEN];
3495
3496 if (argc != 1) {
3497 Jim_WrongNumArgs(interp, 1, argv, "");
3498 return JIM_ERR;
3499 }
3500
3501 if (openpty(&p[0], &p[1], path, NULL, NULL) != 0) {
3502 JimAioSetError(interp, NULL);
3503 return JIM_ERR;
3504 }
3505
3506
3507 return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
3508 return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
3509 }
3510 #endif
3511
3512
3513
3514 int Jim_aioInit(Jim_Interp *interp)
3515 {
3516 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
3517 return JIM_ERR;
3518
3519 #if defined(JIM_SSL)
3520 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
3521 #endif
3522
3523 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
3524 #ifdef HAVE_SOCKETS
3525 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
3526 #endif
3527 #ifdef HAVE_PIPE
3528 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
3529 #endif
3530
3531
3532 JimMakeChannel(interp, fileno(stdin), NULL, "stdin", 0, AIO_KEEPOPEN);
3533 JimMakeChannel(interp, fileno(stdout), NULL, "stdout", 0, AIO_KEEPOPEN);
3534 JimMakeChannel(interp, fileno(stderr), NULL, "stderr", 0, AIO_KEEPOPEN | AIO_WBUF_NONE);
3535
3536 return JIM_OK;
3537 }
3538
3539 #include <errno.h>
3540 #include <stdio.h>
3541 #include <string.h>
3542
3543
3544 #ifdef HAVE_DIRENT_H
3545 #include <dirent.h>
3546 #endif
3547
3548 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3549 {
3550 const char *dirPath;
3551 DIR *dirPtr;
3552 struct dirent *entryPtr;
3553 int nocomplain = 0;
3554
3555 if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
3556 nocomplain = 1;
3557 }
3558 if (argc != 2 && !nocomplain) {
3559 Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
3560 return JIM_ERR;
3561 }
3562
3563 dirPath = Jim_String(argv[1 + nocomplain]);
3564
3565 dirPtr = opendir(dirPath);
3566 if (dirPtr == NULL) {
3567 if (nocomplain) {
3568 return JIM_OK;
3569 }
3570 Jim_SetResultString(interp, strerror(errno), -1);
3571 return JIM_ERR;
3572 }
3573 else {
3574 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3575
3576 while ((entryPtr = readdir(dirPtr)) != NULL) {
3577 if (entryPtr->d_name[0] == '.') {
3578 if (entryPtr->d_name[1] == '\0') {
3579 continue;
3580 }
3581 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
3582 continue;
3583 }
3584 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
3585 }
3586 closedir(dirPtr);
3587
3588 Jim_SetResult(interp, listObj);
3589
3590 return JIM_OK;
3591 }
3592 }
3593
3594 int Jim_readdirInit(Jim_Interp *interp)
3595 {
3596 Jim_PackageProvideCheck(interp, "readdir");
3597 Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
3598 return JIM_OK;
3599 }
3600
3601 #include <stdlib.h>
3602 #include <string.h>
3603
3604 #if defined(JIM_REGEXP)
3605 #else
3606 #include <regex.h>
3607 #define jim_regcomp regcomp
3608 #define jim_regexec regexec
3609 #define jim_regerror regerror
3610 #define jim_regfree regfree
3611 #endif
3612
3613 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3614 {
3615 jim_regfree(objPtr->internalRep.ptrIntValue.ptr);
3616 Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
3617 }
3618
3619 static const Jim_ObjType regexpObjType = {
3620 "regexp",
3621 FreeRegexpInternalRep,
3622 NULL,
3623 NULL,
3624 JIM_TYPE_NONE
3625 };
3626
3627 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
3628 {
3629 regex_t *compre;
3630 const char *pattern;
3631 int ret;
3632
3633
3634 if (objPtr->typePtr == &regexpObjType &&
3635 objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) {
3636
3637 return objPtr->internalRep.ptrIntValue.ptr;
3638 }
3639
3640
3641
3642
3643 pattern = Jim_String(objPtr);
3644 compre = Jim_Alloc(sizeof(regex_t));
3645
3646 if ((ret = jim_regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
3647 char buf[100];
3648
3649 jim_regerror(ret, compre, buf, sizeof(buf));
3650 Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
3651 jim_regfree(compre);
3652 Jim_Free(compre);
3653 return NULL;
3654 }
3655
3656 Jim_FreeIntRep(interp, objPtr);
3657
3658 objPtr->typePtr = &regexpObjType;
3659 objPtr->internalRep.ptrIntValue.int1 = flags;
3660 objPtr->internalRep.ptrIntValue.ptr = compre;
3661
3662 return compre;
3663 }
3664
3665 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3666 {
3667 int opt_indices = 0;
3668 int opt_all = 0;
3669 int opt_inline = 0;
3670 regex_t *regex;
3671 int match, i, j;
3672 int offset = 0;
3673 regmatch_t *pmatch = NULL;
3674 int source_len;
3675 int result = JIM_OK;
3676 const char *pattern;
3677 const char *source_str;
3678 int num_matches = 0;
3679 int num_vars;
3680 Jim_Obj *resultListObj = NULL;
3681 int regcomp_flags = 0;
3682 int eflags = 0;
3683 int option;
3684 enum {
3685 OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
3686 };
3687 static const char * const options[] = {
3688 "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
3689 };
3690
3691 if (argc < 3) {
3692 wrongNumArgs:
3693 Jim_WrongNumArgs(interp, 1, argv,
3694 "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
3695 return JIM_ERR;
3696 }
3697
3698 for (i = 1; i < argc; i++) {
3699 const char *opt = Jim_String(argv[i]);
3700
3701 if (*opt != '-') {
3702 break;
3703 }
3704 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3705 return JIM_ERR;
3706 }
3707 if (option == OPT_END) {
3708 i++;
3709 break;
3710 }
3711 switch (option) {
3712 case OPT_INDICES:
3713 opt_indices = 1;
3714 break;
3715
3716 case OPT_NOCASE:
3717 regcomp_flags |= REG_ICASE;
3718 break;
3719
3720 case OPT_LINE:
3721 regcomp_flags |= REG_NEWLINE;
3722 break;
3723
3724 case OPT_ALL:
3725 opt_all = 1;
3726 break;
3727
3728 case OPT_INLINE:
3729 opt_inline = 1;
3730 break;
3731
3732 case OPT_START:
3733 if (++i == argc) {
3734 goto wrongNumArgs;
3735 }
3736 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3737 return JIM_ERR;
3738 }
3739 break;
3740 }
3741 }
3742 if (argc - i < 2) {
3743 goto wrongNumArgs;
3744 }
3745
3746 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3747 if (!regex) {
3748 return JIM_ERR;
3749 }
3750
3751 pattern = Jim_String(argv[i]);
3752 source_str = Jim_GetString(argv[i + 1], &source_len);
3753
3754 num_vars = argc - i - 2;
3755
3756 if (opt_inline) {
3757 if (num_vars) {
3758 Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
3759 -1);
3760 result = JIM_ERR;
3761 goto done;
3762 }
3763 num_vars = regex->re_nsub + 1;
3764 }
3765
3766 pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
3767
3768 if (offset) {
3769 if (offset < 0) {
3770 offset += source_len + 1;
3771 }
3772 if (offset > source_len) {
3773 source_str += source_len;
3774 }
3775 else if (offset > 0) {
3776 source_str += utf8_index(source_str, offset);
3777 }
3778 eflags |= REG_NOTBOL;
3779 }
3780
3781 if (opt_inline) {
3782 resultListObj = Jim_NewListObj(interp, NULL, 0);
3783 }
3784
3785 next_match:
3786 match = jim_regexec(regex, source_str, num_vars + 1, pmatch, eflags);
3787 if (match >= REG_BADPAT) {
3788 char buf[100];
3789
3790 jim_regerror(match, regex, buf, sizeof(buf));
3791 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3792 result = JIM_ERR;
3793 goto done;
3794 }
3795
3796 if (match == REG_NOMATCH) {
3797 goto done;
3798 }
3799
3800 num_matches++;
3801
3802 if (opt_all && !opt_inline) {
3803
3804 goto try_next_match;
3805 }
3806
3807
3808 j = 0;
3809 for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
3810 Jim_Obj *resultObj;
3811
3812 if (opt_indices) {
3813 resultObj = Jim_NewListObj(interp, NULL, 0);
3814 }
3815 else {
3816 resultObj = Jim_NewStringObj(interp, "", 0);
3817 }
3818
3819 if (pmatch[j].rm_so == -1) {
3820 if (opt_indices) {
3821 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3822 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3823 }
3824 }
3825 else {
3826 if (opt_indices) {
3827
3828 int so = utf8_strlen(source_str, pmatch[j].rm_so);
3829 int eo = utf8_strlen(source_str, pmatch[j].rm_eo);
3830 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + so));
3831 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + eo - 1));
3832 }
3833 else {
3834 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
3835 }
3836 }
3837
3838 if (opt_inline) {
3839 Jim_ListAppendElement(interp, resultListObj, resultObj);
3840 }
3841 else {
3842
3843 result = Jim_SetVariable(interp, argv[i], resultObj);
3844
3845 if (result != JIM_OK) {
3846 Jim_FreeObj(interp, resultObj);
3847 break;
3848 }
3849 }
3850 }
3851
3852 try_next_match:
3853 if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
3854 if (pmatch[0].rm_eo) {
3855 offset += utf8_strlen(source_str, pmatch[0].rm_eo);
3856 source_str += pmatch[0].rm_eo;
3857 }
3858 else {
3859 source_str++;
3860 offset++;
3861 }
3862 if (*source_str) {
3863 eflags = REG_NOTBOL;
3864 goto next_match;
3865 }
3866 }
3867
3868 done:
3869 if (result == JIM_OK) {
3870 if (opt_inline) {
3871 Jim_SetResult(interp, resultListObj);
3872 }
3873 else {
3874 Jim_SetResultInt(interp, num_matches);
3875 }
3876 }
3877
3878 Jim_Free(pmatch);
3879 return result;
3880 }
3881
3882 #define MAX_SUB_MATCHES 50
3883
3884 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3885 {
3886 int regcomp_flags = 0;
3887 int regexec_flags = 0;
3888 int opt_all = 0;
3889 int opt_command = 0;
3890 int offset = 0;
3891 regex_t *regex;
3892 const char *p;
3893 int result = JIM_OK;
3894 regmatch_t pmatch[MAX_SUB_MATCHES + 1];
3895 int num_matches = 0;
3896
3897 int i, j, n;
3898 Jim_Obj *varname;
3899 Jim_Obj *resultObj;
3900 Jim_Obj *cmd_prefix = NULL;
3901 Jim_Obj *regcomp_obj = NULL;
3902 const char *source_str;
3903 int source_len;
3904 const char *replace_str = NULL;
3905 int replace_len;
3906 const char *pattern;
3907 int option;
3908 enum {
3909 OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END
3910 };
3911 static const char * const options[] = {
3912 "-nocase", "-line", "-all", "-start", "-command", "--", NULL
3913 };
3914
3915 if (argc < 4) {
3916 wrongNumArgs:
3917 Jim_WrongNumArgs(interp, 1, argv,
3918 "?-switch ...? exp string subSpec ?varName?");
3919 return JIM_ERR;
3920 }
3921
3922 for (i = 1; i < argc; i++) {
3923 const char *opt = Jim_String(argv[i]);
3924
3925 if (*opt != '-') {
3926 break;
3927 }
3928 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3929 return JIM_ERR;
3930 }
3931 if (option == OPT_END) {
3932 i++;
3933 break;
3934 }
3935 switch (option) {
3936 case OPT_NOCASE:
3937 regcomp_flags |= REG_ICASE;
3938 break;
3939
3940 case OPT_LINE:
3941 regcomp_flags |= REG_NEWLINE;
3942 break;
3943
3944 case OPT_ALL:
3945 opt_all = 1;
3946 break;
3947
3948 case OPT_START:
3949 if (++i == argc) {
3950 goto wrongNumArgs;
3951 }
3952 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3953 return JIM_ERR;
3954 }
3955 break;
3956
3957 case OPT_COMMAND:
3958 opt_command = 1;
3959 break;
3960 }
3961 }
3962 if (argc - i != 3 && argc - i != 4) {
3963 goto wrongNumArgs;
3964 }
3965
3966
3967 regcomp_obj = Jim_DuplicateObj(interp, argv[i]);
3968 Jim_IncrRefCount(regcomp_obj);
3969 regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags);
3970 if (!regex) {
3971 Jim_DecrRefCount(interp, regcomp_obj);
3972 return JIM_ERR;
3973 }
3974 pattern = Jim_String(argv[i]);
3975
3976 source_str = Jim_GetString(argv[i + 1], &source_len);
3977 if (opt_command) {
3978 cmd_prefix = argv[i + 2];
3979 if (Jim_ListLength(interp, cmd_prefix) == 0) {
3980 Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1);
3981 Jim_DecrRefCount(interp, regcomp_obj);
3982 return JIM_ERR;
3983 }
3984 Jim_IncrRefCount(cmd_prefix);
3985 }
3986 else {
3987 replace_str = Jim_GetString(argv[i + 2], &replace_len);
3988 }
3989 varname = argv[i + 3];
3990
3991
3992 resultObj = Jim_NewStringObj(interp, "", 0);
3993
3994 if (offset) {
3995 if (offset < 0) {
3996 offset += source_len + 1;
3997 }
3998 if (offset > source_len) {
3999 offset = source_len;
4000 }
4001 else if (offset < 0) {
4002 offset = 0;
4003 }
4004 }
4005
4006 offset = utf8_index(source_str, offset);
4007
4008
4009 Jim_AppendString(interp, resultObj, source_str, offset);
4010
4011
4012 n = source_len - offset;
4013 p = source_str + offset;
4014 do {
4015 int match = jim_regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
4016
4017 if (match >= REG_BADPAT) {
4018 char buf[100];
4019
4020 jim_regerror(match, regex, buf, sizeof(buf));
4021 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
4022 return JIM_ERR;
4023 }
4024 if (match == REG_NOMATCH) {
4025 break;
4026 }
4027
4028 num_matches++;
4029
4030 Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
4031
4032 if (opt_command) {
4033
4034 Jim_Obj *cmdListObj = Jim_DuplicateObj(interp, cmd_prefix);
4035 for (j = 0; j < MAX_SUB_MATCHES; j++) {
4036 if (pmatch[j].rm_so == -1) {
4037 break;
4038 }
4039 else {
4040 Jim_Obj *srcObj = Jim_NewStringObj(interp, p + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
4041 Jim_ListAppendElement(interp, cmdListObj, srcObj);
4042 }
4043 }
4044 Jim_IncrRefCount(cmdListObj);
4045
4046 result = Jim_EvalObj(interp, cmdListObj);
4047 Jim_DecrRefCount(interp, cmdListObj);
4048 if (result != JIM_OK) {
4049 goto cmd_error;
4050 }
4051 Jim_AppendString(interp, resultObj, Jim_String(Jim_GetResult(interp)), -1);
4052 }
4053 else {
4054
4055 for (j = 0; j < replace_len; j++) {
4056 int idx;
4057 int c = replace_str[j];
4058
4059 if (c == '&') {
4060 idx = 0;
4061 }
4062 else if (c == '\\' && j < replace_len) {
4063 c = replace_str[++j];
4064 if ((c >= '0') && (c <= '9')) {
4065 idx = c - '0';
4066 }
4067 else if ((c == '\\') || (c == '&')) {
4068 Jim_AppendString(interp, resultObj, replace_str + j, 1);
4069 continue;
4070 }
4071 else {
4072 Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
4073 continue;
4074 }
4075 }
4076 else {
4077 Jim_AppendString(interp, resultObj, replace_str + j, 1);
4078 continue;
4079 }
4080 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
4081 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
4082 pmatch[idx].rm_eo - pmatch[idx].rm_so);
4083 }
4084 }
4085 }
4086
4087 p += pmatch[0].rm_eo;
4088 n -= pmatch[0].rm_eo;
4089
4090
4091 if (!opt_all || n == 0) {
4092 break;
4093 }
4094
4095
4096 if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
4097 break;
4098 }
4099
4100
4101 if (pattern[0] == '\0' && n) {
4102
4103 Jim_AppendString(interp, resultObj, p, 1);
4104 p++;
4105 n--;
4106 }
4107
4108 if (pmatch[0].rm_eo == pmatch[0].rm_so) {
4109
4110 regexec_flags = REG_NOTBOL;
4111 }
4112 else {
4113 regexec_flags = 0;
4114 }
4115
4116 } while (n);
4117
4118 Jim_AppendString(interp, resultObj, p, -1);
4119
4120 cmd_error:
4121 if (result == JIM_OK) {
4122
4123 if (argc - i == 4) {
4124 result = Jim_SetVariable(interp, varname, resultObj);
4125
4126 if (result == JIM_OK) {
4127 Jim_SetResultInt(interp, num_matches);
4128 }
4129 else {
4130 Jim_FreeObj(interp, resultObj);
4131 }
4132 }
4133 else {
4134 Jim_SetResult(interp, resultObj);
4135 result = JIM_OK;
4136 }
4137 }
4138 else {
4139 Jim_FreeObj(interp, resultObj);
4140 }
4141
4142 if (opt_command) {
4143 Jim_DecrRefCount(interp, cmd_prefix);
4144 }
4145
4146 Jim_DecrRefCount(interp, regcomp_obj);
4147
4148 return result;
4149 }
4150
4151 int Jim_regexpInit(Jim_Interp *interp)
4152 {
4153 Jim_PackageProvideCheck(interp, "regexp");
4154 Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
4155 Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
4156 return JIM_OK;
4157 }
4158
4159 #include <limits.h>
4160 #include <stdlib.h>
4161 #include <string.h>
4162 #include <stdio.h>
4163 #include <errno.h>
4164
4165
4166 #ifdef HAVE_UTIMES
4167 #include <sys/time.h>
4168 #endif
4169 #ifdef HAVE_UNISTD_H
4170 #include <unistd.h>
4171 #elif defined(_MSC_VER)
4172 #include <direct.h>
4173 #define F_OK 0
4174 #define W_OK 2
4175 #define R_OK 4
4176 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
4177 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
4178 #endif
4179
4180 #if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
4181 #define ISWINDOWS 1
4182
4183 #undef HAVE_SYMLINK
4184 #else
4185 #define ISWINDOWS 0
4186 #endif
4187
4188
4189 #if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
4190 #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
4191 #elif defined(HAVE_STRUCT_STAT_ST_MTIM)
4192 #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
4193 #endif
4194
4195
4196 static void JimFixPath(char *path)
4197 {
4198 if (ISWINDOWS) {
4199
4200 char *p = path;
4201 while ((p = strchr(p, '\\')) != NULL) {
4202 *p++ = '/';
4203 }
4204 }
4205 }
4206
4207
4208 static const char *JimGetFileType(int mode)
4209 {
4210 if (S_ISREG(mode)) {
4211 return "file";
4212 }
4213 else if (S_ISDIR(mode)) {
4214 return "directory";
4215 }
4216 #ifdef S_ISCHR
4217 else if (S_ISCHR(mode)) {
4218 return "characterSpecial";
4219 }
4220 #endif
4221 #ifdef S_ISBLK
4222 else if (S_ISBLK(mode)) {
4223 return "blockSpecial";
4224 }
4225 #endif
4226 #ifdef S_ISFIFO
4227 else if (S_ISFIFO(mode)) {
4228 return "fifo";
4229 }
4230 #endif
4231 #ifdef S_ISLNK
4232 else if (S_ISLNK(mode)) {
4233 return "link";
4234 }
4235 #endif
4236 #ifdef S_ISSOCK
4237 else if (S_ISSOCK(mode)) {
4238 return "socket";
4239 }
4240 #endif
4241 return "unknown";
4242 }
4243
4244 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
4245 {
4246 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
4247 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
4248 }
4249
4250 int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb)
4251 {
4252
4253 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
4254
4255 AppendStatElement(interp, listObj, "dev", sb->st_dev);
4256 AppendStatElement(interp, listObj, "ino", sb->st_ino);
4257 AppendStatElement(interp, listObj, "mode", sb->st_mode);
4258 AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
4259 AppendStatElement(interp, listObj, "uid", sb->st_uid);
4260 AppendStatElement(interp, listObj, "gid", sb->st_gid);
4261 AppendStatElement(interp, listObj, "size", sb->st_size);
4262 AppendStatElement(interp, listObj, "atime", sb->st_atime);
4263 AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
4264 AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
4265 #ifdef STAT_MTIME_US
4266 AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb));
4267 #endif
4268 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
4269 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
4270
4271
4272 if (varName) {
4273 Jim_Obj *objPtr;
4274 objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
4275
4276 if (objPtr) {
4277 Jim_Obj *objv[2];
4278
4279 objv[0] = objPtr;
4280 objv[1] = listObj;
4281
4282 objPtr = Jim_DictMerge(interp, 2, objv);
4283 if (objPtr == NULL) {
4284
4285 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
4286 Jim_FreeNewObj(interp, listObj);
4287 return JIM_ERR;
4288 }
4289
4290 Jim_InvalidateStringRep(objPtr);
4291
4292 Jim_FreeNewObj(interp, listObj);
4293 listObj = objPtr;
4294 }
4295 Jim_SetVariable(interp, varName, listObj);
4296 }
4297
4298
4299 Jim_SetResult(interp, listObj);
4300
4301 return JIM_OK;
4302 }
4303
4304 static int JimPathLenNoTrailingSlashes(const char *path, int len)
4305 {
4306 int i;
4307 for (i = len; i > 1 && path[i - 1] == '/'; i--) {
4308
4309 if (ISWINDOWS && path[i - 2] == ':') {
4310
4311 break;
4312 }
4313 }
4314 return i;
4315 }
4316
4317 static Jim_Obj *JimStripTrailingSlashes(Jim_Interp *interp, Jim_Obj *objPtr)
4318 {
4319 int len = Jim_Length(objPtr);
4320 const char *path = Jim_String(objPtr);
4321 int i = JimPathLenNoTrailingSlashes(path, len);
4322 if (i != len) {
4323 objPtr = Jim_NewStringObj(interp, path, i);
4324 }
4325 Jim_IncrRefCount(objPtr);
4326 return objPtr;
4327 }
4328
4329 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4330 {
4331 Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
4332 const char *path = Jim_String(objPtr);
4333 const char *p = strrchr(path, '/');
4334
4335 if (!p) {
4336 Jim_SetResultString(interp, ".", -1);
4337 }
4338 else if (p[1] == 0) {
4339
4340 Jim_SetResult(interp, objPtr);
4341 }
4342 else if (p == path) {
4343 Jim_SetResultString(interp, "/", -1);
4344 }
4345 else if (ISWINDOWS && p[-1] == ':') {
4346
4347 Jim_SetResultString(interp, path, p - path + 1);
4348 }
4349 else {
4350
4351 int len = JimPathLenNoTrailingSlashes(path, p - path);
4352 Jim_SetResultString(interp, path, len);
4353 }
4354 Jim_DecrRefCount(interp, objPtr);
4355 return JIM_OK;
4356 }
4357
4358 static int file_cmd_split(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4359 {
4360 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
4361 const char *path = Jim_String(argv[0]);
4362
4363 if (*path == '/') {
4364 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "/", 1));
4365 }
4366
4367 while (1) {
4368
4369 while (*path == '/') {
4370 path++;
4371 }
4372 if (*path) {
4373 const char *pt = strchr(path, '/');
4374 if (pt) {
4375 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, pt - path));
4376 path = pt;
4377 continue;
4378 }
4379 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, -1));
4380 }
4381 break;
4382 }
4383 Jim_SetResult(interp, listObj);
4384 return JIM_OK;
4385 }
4386
4387 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4388 {
4389 const char *path = Jim_String(argv[0]);
4390 const char *lastSlash = strrchr(path, '/');
4391 const char *p = strrchr(path, '.');
4392
4393 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
4394 Jim_SetResult(interp, argv[0]);
4395 }
4396 else {
4397 Jim_SetResultString(interp, path, p - path);
4398 }
4399 return JIM_OK;
4400 }
4401
4402 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4403 {
4404 Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
4405 const char *path = Jim_String(objPtr);
4406 const char *lastSlash = strrchr(path, '/');
4407 const char *p = strrchr(path, '.');
4408
4409 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
4410 p = "";
4411 }
4412 Jim_SetResultString(interp, p, -1);
4413 Jim_DecrRefCount(interp, objPtr);
4414 return JIM_OK;
4415 }
4416
4417 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4418 {
4419 Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
4420 const char *path = Jim_String(objPtr);
4421 const char *lastSlash = strrchr(path, '/');
4422
4423 if (lastSlash) {
4424 Jim_SetResultString(interp, lastSlash + 1, -1);
4425 }
4426 else {
4427 Jim_SetResult(interp, objPtr);
4428 }
4429 Jim_DecrRefCount(interp, objPtr);
4430 return JIM_OK;
4431 }
4432
4433 #ifndef HAVE_RESTRICT
4434 #define restrict
4435 #endif
4436
4437 static char *JimRealPath(const char *restrict path, char *restrict resolved_path, size_t len)
4438 {
4439 #if defined(HAVE__FULLPATH)
4440 return _fullpath(resolved_path, path, len);
4441 #elif defined(HAVE_REALPATH)
4442 return realpath(path, resolved_path);
4443 #else
4444 return NULL;
4445 #endif
4446 }
4447
4448 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4449 {
4450 const char *path = Jim_String(argv[0]);
4451 char *newname = Jim_Alloc(MAXPATHLEN);
4452
4453 if (JimRealPath(path, newname, MAXPATHLEN)) {
4454 JimFixPath(newname);
4455 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
4456 return JIM_OK;
4457 }
4458 Jim_Free(newname);
4459 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
4460 return JIM_ERR;
4461 }
4462
4463 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4464 {
4465 int i;
4466 char *newname = Jim_Alloc(MAXPATHLEN + 1);
4467 char *last = newname;
4468
4469 *newname = 0;
4470
4471
4472 for (i = 0; i < argc; i++) {
4473 int len;
4474 const char *part = Jim_GetString(argv[i], &len);
4475
4476 if (*part == '/') {
4477
4478 last = newname;
4479 }
4480 else if (ISWINDOWS && strchr(part, ':')) {
4481
4482 last = newname;
4483 }
4484 else if (part[0] == '.') {
4485 if (part[1] == '/') {
4486 part += 2;
4487 len -= 2;
4488 }
4489 else if (part[1] == 0 && last != newname) {
4490
4491 continue;
4492 }
4493 }
4494
4495
4496 if (last != newname && last[-1] != '/') {
4497 *last++ = '/';
4498 }
4499
4500 if (len) {
4501 if (last + len - newname >= MAXPATHLEN) {
4502 Jim_Free(newname);
4503 Jim_SetResultString(interp, "Path too long", -1);
4504 return JIM_ERR;
4505 }
4506 memcpy(last, part, len);
4507 last += len;
4508 }
4509
4510
4511 if (last > newname + 1 && last[-1] == '/') {
4512
4513 if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
4514 *--last = 0;
4515 }
4516 }
4517 }
4518
4519 *last = 0;
4520
4521
4522
4523 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
4524
4525 return JIM_OK;
4526 }
4527
4528 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
4529 {
4530 Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
4531
4532 return JIM_OK;
4533 }
4534
4535 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4536 {
4537 return file_access(interp, argv[0], R_OK);
4538 }
4539
4540 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4541 {
4542 return file_access(interp, argv[0], W_OK);
4543 }
4544
4545 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4546 {
4547 #ifdef X_OK
4548 return file_access(interp, argv[0], X_OK);
4549 #else
4550
4551 Jim_SetResultBool(interp, 1);
4552 return JIM_OK;
4553 #endif
4554 }
4555
4556 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4557 {
4558 return file_access(interp, argv[0], F_OK);
4559 }
4560
4561 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4562 {
4563 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
4564
4565 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
4566 argc--;
4567 argv++;
4568 }
4569
4570 while (argc--) {
4571 const char *path = Jim_String(argv[0]);
4572
4573 if (unlink(path) == -1 && errno != ENOENT) {
4574 if (rmdir(path) == -1) {
4575
4576 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
4577 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
4578 strerror(errno));
4579 return JIM_ERR;
4580 }
4581 }
4582 }
4583 argv++;
4584 }
4585 return JIM_OK;
4586 }
4587
4588 #ifdef HAVE_MKDIR_ONE_ARG
4589 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
4590 #else
4591 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
4592 #endif
4593
4594 static int mkdir_all(char *path)
4595 {
4596 int ok = 1;
4597
4598
4599 goto first;
4600
4601 while (ok--) {
4602
4603 {
4604 char *slash = strrchr(path, '/');
4605
4606 if (slash && slash != path) {
4607 *slash = 0;
4608 if (mkdir_all(path) != 0) {
4609 return -1;
4610 }
4611 *slash = '/';
4612 }
4613 }
4614 first:
4615 if (MKDIR_DEFAULT(path) == 0) {
4616 return 0;
4617 }
4618 if (errno == ENOENT) {
4619
4620 continue;
4621 }
4622
4623 if (errno == EEXIST) {
4624 jim_stat_t sb;
4625
4626 if (Jim_Stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
4627 return 0;
4628 }
4629
4630 errno = EEXIST;
4631 }
4632
4633 break;
4634 }
4635 return -1;
4636 }
4637
4638 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4639 {
4640 while (argc--) {
4641 char *path = Jim_StrDup(Jim_String(argv[0]));
4642 int rc = mkdir_all(path);
4643
4644 Jim_Free(path);
4645 if (rc != 0) {
4646 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
4647 strerror(errno));
4648 return JIM_ERR;
4649 }
4650 argv++;
4651 }
4652 return JIM_OK;
4653 }
4654
4655 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4656 {
4657 int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);
4658
4659 if (fd < 0) {
4660 return JIM_ERR;
4661 }
4662 close(fd);
4663
4664 return JIM_OK;
4665 }
4666
4667 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4668 {
4669 const char *source;
4670 const char *dest;
4671 int force = 0;
4672
4673 if (argc == 3) {
4674 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
4675 return -1;
4676 }
4677 force++;
4678 argv++;
4679 argc--;
4680 }
4681
4682 source = Jim_String(argv[0]);
4683 dest = Jim_String(argv[1]);
4684
4685 if (!force && access(dest, F_OK) == 0) {
4686 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
4687 argv[1]);
4688 return JIM_ERR;
4689 }
4690 #if ISWINDOWS
4691 if (access(dest, F_OK) == 0) {
4692
4693 remove(dest);
4694 }
4695 #endif
4696 if (rename(source, dest) != 0) {
4697 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
4698 strerror(errno));
4699 return JIM_ERR;
4700 }
4701
4702 return JIM_OK;
4703 }
4704
4705 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4706 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4707 {
4708 int ret;
4709 const char *source;
4710 const char *dest;
4711 static const char * const options[] = { "-hard", "-symbolic", NULL };
4712 enum { OPT_HARD, OPT_SYMBOLIC, };
4713 int option = OPT_HARD;
4714
4715 if (argc == 3) {
4716 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
4717 return JIM_ERR;
4718 }
4719 argv++;
4720 argc--;
4721 }
4722
4723 dest = Jim_String(argv[0]);
4724 source = Jim_String(argv[1]);
4725
4726 if (option == OPT_HARD) {
4727 ret = link(source, dest);
4728 }
4729 else {
4730 ret = symlink(source, dest);
4731 }
4732
4733 if (ret != 0) {
4734 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
4735 strerror(errno));
4736 return JIM_ERR;
4737 }
4738
4739 return JIM_OK;
4740 }
4741 #endif
4742
4743 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
4744 {
4745 const char *path = Jim_String(filename);
4746
4747 if (Jim_Stat(path, sb) == -1) {
4748 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
4749 return JIM_ERR;
4750 }
4751 return JIM_OK;
4752 }
4753
4754 #ifdef Jim_LinkStat
4755 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
4756 {
4757 const char *path = Jim_String(filename);
4758
4759 if (Jim_LinkStat(path, sb) == -1) {
4760 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
4761 return JIM_ERR;
4762 }
4763 return JIM_OK;
4764 }
4765 #else
4766 #define file_lstat file_stat
4767 #endif
4768
4769 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4770 {
4771 jim_stat_t sb;
4772
4773 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4774 return JIM_ERR;
4775 }
4776 Jim_SetResultInt(interp, sb.st_atime);
4777 return JIM_OK;
4778 }
4779
4780 static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us)
4781 {
4782 #ifdef HAVE_UTIMES
4783 struct timeval times[2];
4784
4785 times[1].tv_sec = times[0].tv_sec = us / 1000000;
4786 times[1].tv_usec = times[0].tv_usec = us % 1000000;
4787
4788 if (utimes(filename, times) != 0) {
4789 Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno));
4790 return JIM_ERR;
4791 }
4792 return JIM_OK;
4793 #else
4794 Jim_SetResultString(interp, "Not implemented", -1);
4795 return JIM_ERR;
4796 #endif
4797 }
4798
4799 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4800 {
4801 jim_stat_t sb;
4802
4803 if (argc == 2) {
4804 jim_wide secs;
4805 if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
4806 return JIM_ERR;
4807 }
4808 return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
4809 }
4810 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4811 return JIM_ERR;
4812 }
4813 Jim_SetResultInt(interp, sb.st_mtime);
4814 return JIM_OK;
4815 }
4816
4817 #ifdef STAT_MTIME_US
4818 static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4819 {
4820 jim_stat_t sb;
4821
4822 if (argc == 2) {
4823 jim_wide us;
4824 if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
4825 return JIM_ERR;
4826 }
4827 return JimSetFileTimes(interp, Jim_String(argv[0]), us);
4828 }
4829 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4830 return JIM_ERR;
4831 }
4832 Jim_SetResultInt(interp, STAT_MTIME_US(sb));
4833 return JIM_OK;
4834 }
4835 #endif
4836
4837 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4838 {
4839 return Jim_EvalPrefix(interp, "file copy", argc, argv);
4840 }
4841
4842 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4843 {
4844 jim_stat_t sb;
4845
4846 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4847 return JIM_ERR;
4848 }
4849 Jim_SetResultInt(interp, sb.st_size);
4850 return JIM_OK;
4851 }
4852
4853 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4854 {
4855 jim_stat_t sb;
4856 int ret = 0;
4857
4858 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4859 ret = S_ISDIR(sb.st_mode);
4860 }
4861 Jim_SetResultInt(interp, ret);
4862 return JIM_OK;
4863 }
4864
4865 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4866 {
4867 jim_stat_t sb;
4868 int ret = 0;
4869
4870 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4871 ret = S_ISREG(sb.st_mode);
4872 }
4873 Jim_SetResultInt(interp, ret);
4874 return JIM_OK;
4875 }
4876
4877 #ifdef HAVE_GETEUID
4878 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4879 {
4880 jim_stat_t sb;
4881 int ret = 0;
4882
4883 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4884 ret = (geteuid() == sb.st_uid);
4885 }
4886 Jim_SetResultInt(interp, ret);
4887 return JIM_OK;
4888 }
4889 #endif
4890
4891 #if defined(HAVE_READLINK)
4892 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4893 {
4894 const char *path = Jim_String(argv[0]);
4895 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
4896
4897 int linkLength = readlink(path, linkValue, MAXPATHLEN);
4898
4899 if (linkLength == -1) {
4900 Jim_Free(linkValue);
4901 Jim_SetResultFormatted(interp, "could not read link \"%#s\": %s", argv[0], strerror(errno));
4902 return JIM_ERR;
4903 }
4904 linkValue[linkLength] = 0;
4905 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
4906 return JIM_OK;
4907 }
4908 #endif
4909
4910 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4911 {
4912 jim_stat_t sb;
4913
4914 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4915 return JIM_ERR;
4916 }
4917 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
4918 return JIM_OK;
4919 }
4920
4921 #ifdef Jim_LinkStat
4922 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4923 {
4924 jim_stat_t sb;
4925
4926 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4927 return JIM_ERR;
4928 }
4929 return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4930 }
4931 #else
4932 #define file_cmd_lstat file_cmd_stat
4933 #endif
4934
4935 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4936 {
4937 jim_stat_t sb;
4938
4939 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4940 return JIM_ERR;
4941 }
4942 return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4943 }
4944
4945 static const jim_subcmd_type file_command_table[] = {
4946 { "atime",
4947 "name",
4948 file_cmd_atime,
4949 1,
4950 1,
4951
4952 },
4953 { "mtime",
4954 "name ?time?",
4955 file_cmd_mtime,
4956 1,
4957 2,
4958
4959 },
4960 #ifdef STAT_MTIME_US
4961 { "mtimeus",
4962 "name ?time?",
4963 file_cmd_mtimeus,
4964 1,
4965 2,
4966
4967 },
4968 #endif
4969 { "copy",
4970 "?-force? source dest",
4971 file_cmd_copy,
4972 2,
4973 3,
4974
4975 },
4976 { "dirname",
4977 "name",
4978 file_cmd_dirname,
4979 1,
4980 1,
4981
4982 },
4983 { "rootname",
4984 "name",
4985 file_cmd_rootname,
4986 1,
4987 1,
4988
4989 },
4990 { "extension",
4991 "name",
4992 file_cmd_extension,
4993 1,
4994 1,
4995
4996 },
4997 { "tail",
4998 "name",
4999 file_cmd_tail,
5000 1,
5001 1,
5002
5003 },
5004 { "split",
5005 "name",
5006 file_cmd_split,
5007 1,
5008 1,
5009
5010 },
5011 { "normalize",
5012 "name",
5013 file_cmd_normalize,
5014 1,
5015 1,
5016
5017 },
5018 { "join",
5019 "name ?name ...?",
5020 file_cmd_join,
5021 1,
5022 -1,
5023
5024 },
5025 { "readable",
5026 "name",
5027 file_cmd_readable,
5028 1,
5029 1,
5030
5031 },
5032 { "writable",
5033 "name",
5034 file_cmd_writable,
5035 1,
5036 1,
5037
5038 },
5039 { "executable",
5040 "name",
5041 file_cmd_executable,
5042 1,
5043 1,
5044
5045 },
5046 { "exists",
5047 "name",
5048 file_cmd_exists,
5049 1,
5050 1,
5051
5052 },
5053 { "delete",
5054 "?-force|--? name ...",
5055 file_cmd_delete,
5056 1,
5057 -1,
5058
5059 },
5060 { "mkdir",
5061 "dir ...",
5062 file_cmd_mkdir,
5063 1,
5064 -1,
5065
5066 },
5067 { "tempfile",
5068 "?template?",
5069 file_cmd_tempfile,
5070 0,
5071 1,
5072
5073 },
5074 { "rename",
5075 "?-force? source dest",
5076 file_cmd_rename,
5077 2,
5078 3,
5079
5080 },
5081 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
5082 { "link",
5083 "?-symbolic|-hard? newname target",
5084 file_cmd_link,
5085 2,
5086 3,
5087
5088 },
5089 #endif
5090 #if defined(HAVE_READLINK)
5091 { "readlink",
5092 "name",
5093 file_cmd_readlink,
5094 1,
5095 1,
5096
5097 },
5098 #endif
5099 { "size",
5100 "name",
5101 file_cmd_size,
5102 1,
5103 1,
5104
5105 },
5106 { "stat",
5107 "name ?var?",
5108 file_cmd_stat,
5109 1,
5110 2,
5111
5112 },
5113 { "lstat",
5114 "name ?var?",
5115 file_cmd_lstat,
5116 1,
5117 2,
5118
5119 },
5120 { "type",
5121 "name",
5122 file_cmd_type,
5123 1,
5124 1,
5125
5126 },
5127 #ifdef HAVE_GETEUID
5128 { "owned",
5129 "name",
5130 file_cmd_owned,
5131 1,
5132 1,
5133
5134 },
5135 #endif
5136 { "isdirectory",
5137 "name",
5138 file_cmd_isdirectory,
5139 1,
5140 1,
5141
5142 },
5143 { "isfile",
5144 "name",
5145 file_cmd_isfile,
5146 1,
5147 1,
5148
5149 },
5150 {
5151 NULL
5152 }
5153 };
5154
5155 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5156 {
5157 const char *path;
5158
5159 if (argc != 2) {
5160 Jim_WrongNumArgs(interp, 1, argv, "dirname");
5161 return JIM_ERR;
5162 }
5163
5164 path = Jim_String(argv[1]);
5165
5166 if (chdir(path) != 0) {
5167 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
5168 strerror(errno));
5169 return JIM_ERR;
5170 }
5171 return JIM_OK;
5172 }
5173
5174 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5175 {
5176 char *cwd = Jim_Alloc(MAXPATHLEN);
5177
5178 if (getcwd(cwd, MAXPATHLEN) == NULL) {
5179 Jim_SetResultString(interp, "Failed to get pwd", -1);
5180 Jim_Free(cwd);
5181 return JIM_ERR;
5182 }
5183 JimFixPath(cwd);
5184 Jim_SetResultString(interp, cwd, -1);
5185
5186 Jim_Free(cwd);
5187 return JIM_OK;
5188 }
5189
5190 int Jim_fileInit(Jim_Interp *interp)
5191 {
5192 Jim_PackageProvideCheck(interp, "file");
5193 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
5194 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
5195 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
5196 return JIM_OK;
5197 }
5198
5199 #include <string.h>
5200 #include <ctype.h>
5201
5202
5203 #if (!(defined(HAVE_VFORK) || defined(HAVE_FORK)) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
5204 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5205 {
5206 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
5207 int i, j;
5208 int rc;
5209
5210
5211 for (i = 1; i < argc; i++) {
5212 int len;
5213 const char *arg = Jim_GetString(argv[i], &len);
5214
5215 if (i > 1) {
5216 Jim_AppendString(interp, cmdlineObj, " ", 1);
5217 }
5218 if (strpbrk(arg, "\\\" ") == NULL) {
5219
5220 Jim_AppendString(interp, cmdlineObj, arg, len);
5221 continue;
5222 }
5223
5224 Jim_AppendString(interp, cmdlineObj, "\"", 1);
5225 for (j = 0; j < len; j++) {
5226 if (arg[j] == '\\' || arg[j] == '"') {
5227 Jim_AppendString(interp, cmdlineObj, "\\", 1);
5228 }
5229 Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
5230 }
5231 Jim_AppendString(interp, cmdlineObj, "\"", 1);
5232 }
5233 rc = system(Jim_String(cmdlineObj));
5234
5235 Jim_FreeNewObj(interp, cmdlineObj);
5236
5237 if (rc) {
5238 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
5239 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
5240 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
5241 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
5242 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
5243 return JIM_ERR;
5244 }
5245
5246 return JIM_OK;
5247 }
5248
5249 int Jim_execInit(Jim_Interp *interp)
5250 {
5251 Jim_PackageProvideCheck(interp, "exec");
5252 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
5253 return JIM_OK;
5254 }
5255 #else
5256
5257
5258 #include <errno.h>
5259 #include <signal.h>
5260 #include <sys/stat.h>
5261
5262 struct WaitInfoTable;
5263
5264 static char **JimOriginalEnviron(void);
5265 static char **JimSaveEnv(char **env);
5266 static void JimRestoreEnv(char **env);
5267 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
5268 phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
5269 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr);
5270 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj);
5271 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
5272
5273 #if defined(__MINGW32__)
5274 static phandle_t JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
5275 #endif
5276
5277 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
5278 {
5279 int len;
5280 const char *s = Jim_GetString(objPtr, &len);
5281
5282 if (len > 0 && s[len - 1] == '\n') {
5283 objPtr->length--;
5284 objPtr->bytes[objPtr->length] = '\0';
5285 }
5286 }
5287
5288 static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
5289 {
5290 char buf[256];
5291 int ret = 0;
5292
5293 while (1) {
5294 int retval = read(fd, buf, sizeof(buf));
5295 if (retval > 0) {
5296 ret = 1;
5297 Jim_AppendString(interp, strObj, buf, retval);
5298 }
5299 if (retval <= 0) {
5300 break;
5301 }
5302 }
5303 close(fd);
5304 return ret;
5305 }
5306
5307 static char **JimBuildEnv(Jim_Interp *interp)
5308 {
5309 int i;
5310 int size;
5311 int num;
5312 int n;
5313 char **envptr;
5314 char *envdata;
5315
5316 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
5317
5318 if (!objPtr) {
5319 return JimOriginalEnviron();
5320 }
5321
5322
5323
5324 num = Jim_ListLength(interp, objPtr);
5325 if (num % 2) {
5326
5327 num--;
5328 }
5329 size = Jim_Length(objPtr) + 2;
5330
5331 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
5332 envdata = (char *)&envptr[num / 2 + 1];
5333
5334 n = 0;
5335 for (i = 0; i < num; i += 2) {
5336 const char *s1, *s2;
5337 Jim_Obj *elemObj;
5338
5339 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
5340 s1 = Jim_String(elemObj);
5341 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
5342 s2 = Jim_String(elemObj);
5343
5344 envptr[n] = envdata;
5345 envdata += sprintf(envdata, "%s=%s", s1, s2);
5346 envdata++;
5347 n++;
5348 }
5349 envptr[n] = NULL;
5350 *envdata = 0;
5351
5352 return envptr;
5353 }
5354
5355 static void JimFreeEnv(char **env, char **original_environ)
5356 {
5357 if (env != original_environ) {
5358 Jim_Free(env);
5359 }
5360 }
5361
5362 static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
5363 {
5364 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
5365
5366 if (pid <= 0) {
5367 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
5368 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
5369 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
5370 }
5371 else if (WIFEXITED(waitStatus)) {
5372 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
5373 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
5374 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
5375 }
5376 else {
5377 const char *type;
5378 const char *action;
5379 const char *signame;
5380
5381 if (WIFSIGNALED(waitStatus)) {
5382 type = "CHILDKILLED";
5383 action = "killed";
5384 signame = Jim_SignalId(WTERMSIG(waitStatus));
5385 }
5386 else {
5387 type = "CHILDSUSP";
5388 action = "suspended";
5389 signame = "none";
5390 }
5391
5392 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
5393
5394 if (errStrObj) {
5395 Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
5396 }
5397
5398 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
5399 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
5400 }
5401 return errorCode;
5402 }
5403
5404 static int JimCheckWaitStatus(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
5405 {
5406 if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
5407 return JIM_OK;
5408 }
5409 Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
5410
5411 return JIM_ERR;
5412 }
5413
5414
5415 struct WaitInfo
5416 {
5417 phandle_t phandle;
5418 int status;
5419 int flags;
5420 };
5421
5422
5423 struct WaitInfoTable {
5424 struct WaitInfo *info;
5425 int size;
5426 int used;
5427 int refcount;
5428 };
5429
5430
5431 #define WI_DETACHED 2
5432
5433 #define WAIT_TABLE_GROW_BY 4
5434
5435 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
5436 {
5437 struct WaitInfoTable *table = privData;
5438
5439 if (--table->refcount == 0) {
5440 Jim_Free(table->info);
5441 Jim_Free(table);
5442 }
5443 }
5444
5445 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
5446 {
5447 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
5448 table->info = NULL;
5449 table->size = table->used = 0;
5450 table->refcount = 1;
5451
5452 return table;
5453 }
5454
5455 static int JimWaitRemove(struct WaitInfoTable *table, phandle_t phandle)
5456 {
5457 int i;
5458
5459
5460 for (i = 0; i < table->used; i++) {
5461 if (phandle == table->info[i].phandle) {
5462 if (i != table->used - 1) {
5463 table->info[i] = table->info[table->used - 1];
5464 }
5465 table->used--;
5466 return 0;
5467 }
5468 }
5469 return -1;
5470 }
5471
5472 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5473 {
5474 int outputId;
5475 int errorId;
5476 phandle_t *pidPtr;
5477 int numPids, result;
5478 int child_siginfo = 1;
5479 Jim_Obj *childErrObj;
5480 Jim_Obj *errStrObj;
5481 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5482
5483 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
5484 Jim_Obj *listObj;
5485 int i;
5486
5487 argc--;
5488 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
5489 if (numPids < 0) {
5490 return JIM_ERR;
5491 }
5492
5493 listObj = Jim_NewListObj(interp, NULL, 0);
5494 for (i = 0; i < numPids; i++) {
5495 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, JimProcessPid(pidPtr[i])));
5496 }
5497 Jim_SetResult(interp, listObj);
5498 JimDetachPids(table, numPids, pidPtr);
5499 Jim_Free(pidPtr);
5500 return JIM_OK;
5501 }
5502
5503 numPids =
5504 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
5505
5506 if (numPids < 0) {
5507 return JIM_ERR;
5508 }
5509
5510 result = JIM_OK;
5511
5512 errStrObj = Jim_NewStringObj(interp, "", 0);
5513
5514
5515 if (outputId != -1) {
5516 if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
5517 result = JIM_ERR;
5518 Jim_SetResultErrno(interp, "error reading from output pipe");
5519 }
5520 }
5521
5522
5523 childErrObj = Jim_NewStringObj(interp, "", 0);
5524 Jim_IncrRefCount(childErrObj);
5525
5526 if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
5527 result = JIM_ERR;
5528 }
5529
5530 if (errorId != -1) {
5531 int ret;
5532 Jim_Lseek(errorId, 0, SEEK_SET);
5533 ret = JimAppendStreamToString(interp, errorId, errStrObj);
5534 if (ret < 0) {
5535 Jim_SetResultErrno(interp, "error reading from error pipe");
5536 result = JIM_ERR;
5537 }
5538 else if (ret > 0) {
5539
5540 child_siginfo = 0;
5541 }
5542 }
5543
5544 if (child_siginfo) {
5545
5546 Jim_AppendObj(interp, errStrObj, childErrObj);
5547 }
5548 Jim_DecrRefCount(interp, childErrObj);
5549
5550
5551 Jim_RemoveTrailingNewline(errStrObj);
5552
5553
5554 Jim_SetResult(interp, errStrObj);
5555
5556 return result;
5557 }
5558
5559 static long JimWaitForProcess(struct WaitInfoTable *table, phandle_t phandle, int *statusPtr)
5560 {
5561 if (JimWaitRemove(table, phandle) == 0) {
5562
5563 return waitpid(phandle, statusPtr, 0);
5564 }
5565
5566
5567 return -1;
5568 }
5569
5570 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr)
5571 {
5572 int j;
5573
5574 for (j = 0; j < numPids; j++) {
5575
5576 int i;
5577 for (i = 0; i < table->used; i++) {
5578 if (pidPtr[j] == table->info[i].phandle) {
5579 table->info[i].flags |= WI_DETACHED;
5580 break;
5581 }
5582 }
5583 }
5584 }
5585
5586 static int JimGetChannelFd(Jim_Interp *interp, const char *name)
5587 {
5588 Jim_Obj *objv[2];
5589
5590 objv[0] = Jim_NewStringObj(interp, name, -1);
5591 objv[1] = Jim_NewStringObj(interp, "getfd", -1);
5592
5593 if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
5594 jim_wide fd;
5595 if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
5596 return fd;
5597 }
5598 }
5599 return -1;
5600 }
5601
5602 static void JimReapDetachedPids(struct WaitInfoTable *table)
5603 {
5604 struct WaitInfo *waitPtr;
5605 int count;
5606 int dest;
5607
5608 if (!table) {
5609 return;
5610 }
5611
5612 waitPtr = table->info;
5613 dest = 0;
5614 for (count = table->used; count > 0; waitPtr++, count--) {
5615 if (waitPtr->flags & WI_DETACHED) {
5616 int status;
5617 long pid = waitpid(waitPtr->phandle, &status, WNOHANG);
5618 if (pid > 0) {
5619
5620 table->used--;
5621 continue;
5622 }
5623 }
5624 if (waitPtr != &table->info[dest]) {
5625 table->info[dest] = *waitPtr;
5626 }
5627 dest++;
5628 }
5629 }
5630
5631 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5632 {
5633 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5634 int nohang = 0;
5635 long pid;
5636 phandle_t phandle;
5637 int status;
5638 Jim_Obj *errCodeObj;
5639
5640
5641 if (argc == 1) {
5642 JimReapDetachedPids(table);
5643 return JIM_OK;
5644 }
5645
5646 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
5647 nohang = 1;
5648 }
5649 if (argc != nohang + 2) {
5650 Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
5651 return JIM_ERR;
5652 }
5653 if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) {
5654 return JIM_ERR;
5655 }
5656
5657
5658 phandle = JimWaitPid(pid, &status, nohang ? WNOHANG : 0);
5659 if (phandle == JIM_BAD_PHANDLE) {
5660 pid = -1;
5661 }
5662 #ifndef __MINGW32__
5663 else if (pid < 0) {
5664 pid = phandle;
5665 }
5666 #endif
5667
5668 errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
5669
5670 if (phandle != JIM_BAD_PHANDLE && (WIFEXITED(status) || WIFSIGNALED(status))) {
5671
5672 JimWaitRemove(table, phandle);
5673 }
5674 Jim_SetResult(interp, errCodeObj);
5675 return JIM_OK;
5676 }
5677
5678 static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5679 {
5680 if (argc != 1) {
5681 Jim_WrongNumArgs(interp, 1, argv, "");
5682 return JIM_ERR;
5683 }
5684
5685 Jim_SetResultInt(interp, (jim_wide)getpid());
5686 return JIM_OK;
5687 }
5688
5689 static int
5690 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
5691 int *inPipePtr, int *outPipePtr, int *errFilePtr)
5692 {
5693 phandle_t *pidPtr = NULL; /* Points to alloc-ed array holding all
5694 * the pids of child processes. */
5695 int numPids = 0; /* Actual number of processes that exist
5696 * at *pidPtr right now. */
5697 int cmdCount; /* Count of number of distinct commands
5698 * found in argc/argv. */
5699 const char *input = NULL; /* Describes input for pipeline, depending
5700 * on "inputFile". NULL means take input
5701 * from stdin/pipe. */
5702 int input_len = 0;
5703
5704 #define FILE_NAME 0
5705 #define FILE_APPEND 1
5706 #define FILE_HANDLE 2
5707 #define FILE_TEXT 3
5708
5709 int inputFile = FILE_NAME; /* 1 means input is name of input file.
5710 * 2 means input is filehandle name.
5711 * 0 means input holds actual
5712 * text to be input to command. */
5713
5714 int outputFile = FILE_NAME; /* 0 means output is the name of output file.
5715 * 1 means output is the name of output file, and append.
5716 * 2 means output is filehandle name.
5717 * All this is ignored if output is NULL
5718 */
5719 int errorFile = FILE_NAME; /* 0 means error is the name of error file.
5720 * 1 means error is the name of error file, and append.
5721 * 2 means error is filehandle name.
5722 * All this is ignored if error is NULL
5723 */
5724 const char *output = NULL; /* Holds name of output file to pipe to,
5725 * or NULL if output goes to stdout/pipe. */
5726 const char *error = NULL; /* Holds name of stderr file to pipe to,
5727 * or NULL if stderr goes to stderr/pipe. */
5728 int inputId = -1;
5729 int outputId = -1;
5730 int errorId = -1;
5731 int lastOutputId = -1;
5732 int pipeIds[2];
5733 int firstArg, lastArg; /* Indexes of first and last arguments in
5734 * current command. */
5735 int lastBar;
5736 int i;
5737 phandle_t phandle;
5738 char **save_environ;
5739 #if defined(HAVE_EXECVPE) && !defined(__MINGW32__)
5740 char **child_environ;
5741 #endif
5742 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5743
5744
5745 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
5746 int arg_count = 0;
5747
5748 if (inPipePtr != NULL) {
5749 *inPipePtr = -1;
5750 }
5751 if (outPipePtr != NULL) {
5752 *outPipePtr = -1;
5753 }
5754 if (errFilePtr != NULL) {
5755 *errFilePtr = -1;
5756 }
5757 pipeIds[0] = pipeIds[1] = -1;
5758
5759 cmdCount = 1;
5760 lastBar = -1;
5761 for (i = 0; i < argc; i++) {
5762 const char *arg = Jim_String(argv[i]);
5763
5764 if (arg[0] == '<') {
5765 inputFile = FILE_NAME;
5766 input = arg + 1;
5767 if (*input == '<') {
5768 inputFile = FILE_TEXT;
5769 input_len = Jim_Length(argv[i]) - 2;
5770 input++;
5771 }
5772 else if (*input == '@') {
5773 inputFile = FILE_HANDLE;
5774 input++;
5775 }
5776
5777 if (!*input && ++i < argc) {
5778 input = Jim_GetString(argv[i], &input_len);
5779 }
5780 }
5781 else if (arg[0] == '>') {
5782 int dup_error = 0;
5783
5784 outputFile = FILE_NAME;
5785
5786 output = arg + 1;
5787 if (*output == '>') {
5788 outputFile = FILE_APPEND;
5789 output++;
5790 }
5791 if (*output == '&') {
5792
5793 output++;
5794 dup_error = 1;
5795 }
5796 if (*output == '@') {
5797 outputFile = FILE_HANDLE;
5798 output++;
5799 }
5800 if (!*output && ++i < argc) {
5801 output = Jim_String(argv[i]);
5802 }
5803 if (dup_error) {
5804 errorFile = outputFile;
5805 error = output;
5806 }
5807 }
5808 else if (arg[0] == '2' && arg[1] == '>') {
5809 error = arg + 2;
5810 errorFile = FILE_NAME;
5811
5812 if (*error == '@') {
5813 errorFile = FILE_HANDLE;
5814 error++;
5815 }
5816 else if (*error == '>') {
5817 errorFile = FILE_APPEND;
5818 error++;
5819 }
5820 if (!*error && ++i < argc) {
5821 error = Jim_String(argv[i]);
5822 }
5823 }
5824 else {
5825 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
5826 if (i == lastBar + 1 || i == argc - 1) {
5827 Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
5828 goto badargs;
5829 }
5830 lastBar = i;
5831 cmdCount++;
5832 }
5833
5834 arg_array[arg_count++] = (char *)arg;
5835 continue;
5836 }
5837
5838 if (i >= argc) {
5839 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
5840 goto badargs;
5841 }
5842 }
5843
5844 if (arg_count == 0) {
5845 Jim_SetResultString(interp, "didn't specify command to execute", -1);
5846 badargs:
5847 Jim_Free(arg_array);
5848 return -1;
5849 }
5850
5851
5852 save_environ = JimSaveEnv(JimBuildEnv(interp));
5853
5854 if (input != NULL) {
5855 if (inputFile == FILE_TEXT) {
5856 inputId = Jim_MakeTempFile(interp, NULL, 1);
5857 if (inputId == -1) {
5858 goto error;
5859 }
5860 if (write(inputId, input, input_len) != input_len) {
5861 Jim_SetResultErrno(interp, "couldn't write temp file");
5862 close(inputId);
5863 goto error;
5864 }
5865 Jim_Lseek(inputId, 0L, SEEK_SET);
5866 }
5867 else if (inputFile == FILE_HANDLE) {
5868 int fd = JimGetChannelFd(interp, input);
5869
5870 if (fd < 0) {
5871 goto error;
5872 }
5873 inputId = dup(fd);
5874 }
5875 else {
5876 inputId = Jim_OpenForRead(input);
5877 if (inputId == -1) {
5878 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
5879 goto error;
5880 }
5881 }
5882 }
5883 else if (inPipePtr != NULL) {
5884 if (pipe(pipeIds) != 0) {
5885 Jim_SetResultErrno(interp, "couldn't create input pipe for command");
5886 goto error;
5887 }
5888 inputId = pipeIds[0];
5889 *inPipePtr = pipeIds[1];
5890 pipeIds[0] = pipeIds[1] = -1;
5891 }
5892
5893 if (output != NULL) {
5894 if (outputFile == FILE_HANDLE) {
5895 int fd = JimGetChannelFd(interp, output);
5896 if (fd < 0) {
5897 goto error;
5898 }
5899 lastOutputId = dup(fd);
5900 }
5901 else {
5902 lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
5903 if (lastOutputId == -1) {
5904 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
5905 goto error;
5906 }
5907 }
5908 }
5909 else if (outPipePtr != NULL) {
5910 if (pipe(pipeIds) != 0) {
5911 Jim_SetResultErrno(interp, "couldn't create output pipe");
5912 goto error;
5913 }
5914 lastOutputId = pipeIds[1];
5915 *outPipePtr = pipeIds[0];
5916 pipeIds[0] = pipeIds[1] = -1;
5917 }
5918
5919 if (error != NULL) {
5920 if (errorFile == FILE_HANDLE) {
5921 if (strcmp(error, "1") == 0) {
5922
5923 if (lastOutputId != -1) {
5924 errorId = dup(lastOutputId);
5925 }
5926 else {
5927
5928 error = "stdout";
5929 }
5930 }
5931 if (errorId == -1) {
5932 int fd = JimGetChannelFd(interp, error);
5933 if (fd < 0) {
5934 goto error;
5935 }
5936 errorId = dup(fd);
5937 }
5938 }
5939 else {
5940 errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
5941 if (errorId == -1) {
5942 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
5943 goto error;
5944 }
5945 }
5946 }
5947 else if (errFilePtr != NULL) {
5948 errorId = Jim_MakeTempFile(interp, NULL, 1);
5949 if (errorId == -1) {
5950 goto error;
5951 }
5952 *errFilePtr = dup(errorId);
5953 }
5954
5955
5956 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
5957 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
5958 int pipe_dup_err = 0;
5959 int origErrorId = errorId;
5960
5961 for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
5962 if (strcmp(arg_array[lastArg], "|") == 0) {
5963 break;
5964 }
5965 if (strcmp(arg_array[lastArg], "|&") == 0) {
5966 pipe_dup_err = 1;
5967 break;
5968 }
5969 }
5970
5971 if (lastArg == firstArg) {
5972 Jim_SetResultString(interp, "missing command to exec", -1);
5973 goto error;
5974 }
5975
5976
5977 arg_array[lastArg] = NULL;
5978 if (lastArg == arg_count) {
5979 outputId = lastOutputId;
5980 lastOutputId = -1;
5981 }
5982 else {
5983 if (pipe(pipeIds) != 0) {
5984 Jim_SetResultErrno(interp, "couldn't create pipe");
5985 goto error;
5986 }
5987 outputId = pipeIds[1];
5988 }
5989
5990
5991 if (pipe_dup_err) {
5992 errorId = outputId;
5993 }
5994
5995
5996
5997 #ifdef __MINGW32__
5998 phandle = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
5999 if (phandle == JIM_BAD_PHANDLE) {
6000 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
6001 goto error;
6002 }
6003 #else
6004 i = strlen(arg_array[firstArg]);
6005
6006 #ifdef HAVE_EXECVPE
6007 child_environ = Jim_GetEnviron();
6008 #endif
6009 #ifdef HAVE_VFORK
6010 phandle = vfork();
6011 #else
6012 phandle = fork();
6013 #endif
6014 if (phandle < 0) {
6015 Jim_SetResultErrno(interp, "couldn't fork child process");
6016 goto error;
6017 }
6018 if (phandle == 0) {
6019
6020
6021 if (inputId != -1 && inputId != fileno(stdin)) {
6022 dup2(inputId, fileno(stdin));
6023 close(inputId);
6024 }
6025 if (outputId != -1 && outputId != fileno(stdout)) {
6026 dup2(outputId, fileno(stdout));
6027 if (outputId != errorId) {
6028 close(outputId);
6029 }
6030 }
6031 if (errorId != -1 && errorId != fileno(stderr)) {
6032 dup2(errorId, fileno(stderr));
6033 close(errorId);
6034 }
6035
6036 if (outPipePtr && *outPipePtr != -1) {
6037 close(*outPipePtr);
6038 }
6039 if (errFilePtr && *errFilePtr != -1) {
6040 close(*errFilePtr);
6041 }
6042 if (pipeIds[0] != -1) {
6043 close(pipeIds[0]);
6044 }
6045 if (lastOutputId != -1) {
6046 close(lastOutputId);
6047 }
6048
6049 execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
6050
6051 if (write(fileno(stderr), "couldn't exec \"", 15) &&
6052 write(fileno(stderr), arg_array[firstArg], i) &&
6053 write(fileno(stderr), "\"\n", 2)) {
6054
6055 }
6056 #ifdef JIM_MAINTAINER
6057 {
6058
6059 static char *const false_argv[2] = {"false", NULL};
6060 execvp(false_argv[0],false_argv);
6061 }
6062 #endif
6063 _exit(127);
6064 }
6065 #endif
6066
6067
6068
6069 if (table->used == table->size) {
6070 table->size += WAIT_TABLE_GROW_BY;
6071 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
6072 }
6073
6074 table->info[table->used].phandle = phandle;
6075 table->info[table->used].flags = 0;
6076 table->used++;
6077
6078 pidPtr[numPids] = phandle;
6079
6080
6081 errorId = origErrorId;
6082
6083
6084 if (inputId != -1) {
6085 close(inputId);
6086 }
6087 if (outputId != -1) {
6088 close(outputId);
6089 }
6090 inputId = pipeIds[0];
6091 pipeIds[0] = pipeIds[1] = -1;
6092 }
6093 *pidArrayPtr = pidPtr;
6094
6095
6096 cleanup:
6097 if (inputId != -1) {
6098 close(inputId);
6099 }
6100 if (lastOutputId != -1) {
6101 close(lastOutputId);
6102 }
6103 if (errorId != -1) {
6104 close(errorId);
6105 }
6106 Jim_Free(arg_array);
6107
6108 JimRestoreEnv(save_environ);
6109
6110 return numPids;
6111
6112
6113 error:
6114 if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
6115 close(*inPipePtr);
6116 *inPipePtr = -1;
6117 }
6118 if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
6119 close(*outPipePtr);
6120 *outPipePtr = -1;
6121 }
6122 if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
6123 close(*errFilePtr);
6124 *errFilePtr = -1;
6125 }
6126 if (pipeIds[0] != -1) {
6127 close(pipeIds[0]);
6128 }
6129 if (pipeIds[1] != -1) {
6130 close(pipeIds[1]);
6131 }
6132 if (pidPtr != NULL) {
6133 for (i = 0; i < numPids; i++) {
6134 if (pidPtr[i] != JIM_BAD_PHANDLE) {
6135 JimDetachPids(table, 1, &pidPtr[i]);
6136 }
6137 }
6138 Jim_Free(pidPtr);
6139 }
6140 numPids = -1;
6141 goto cleanup;
6142 }
6143
6144
6145 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj)
6146 {
6147 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
6148 int result = JIM_OK;
6149 int i;
6150
6151
6152 for (i = 0; i < numPids; i++) {
6153 int waitStatus = 0;
6154 long pid = JimWaitForProcess(table, pidPtr[i], &waitStatus);
6155 if (pid > 0) {
6156 if (JimCheckWaitStatus(interp, pid, waitStatus, errStrObj) != JIM_OK) {
6157 result = JIM_ERR;
6158 }
6159 }
6160 }
6161 Jim_Free(pidPtr);
6162
6163 return result;
6164 }
6165
6166 int Jim_execInit(Jim_Interp *interp)
6167 {
6168 struct WaitInfoTable *waitinfo;
6169
6170 Jim_PackageProvideCheck(interp, "exec");
6171
6172 waitinfo = JimAllocWaitInfoTable();
6173 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
6174 waitinfo->refcount++;
6175 Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
6176 Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
6177
6178 return JIM_OK;
6179 }
6180
6181 #if defined(__MINGW32__)
6182
6183
6184 static int
6185 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
6186 {
6187 int i;
6188 static char extensions[][5] = {".exe", "", ".bat"};
6189
6190 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
6191 snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
6192
6193 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
6194 continue;
6195 }
6196 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
6197 continue;
6198 }
6199 return 0;
6200 }
6201
6202 return -1;
6203 }
6204
6205 static char **JimSaveEnv(char **env)
6206 {
6207 return env;
6208 }
6209
6210 static void JimRestoreEnv(char **env)
6211 {
6212 JimFreeEnv(env, Jim_GetEnviron());
6213 }
6214
6215 static char **JimOriginalEnviron(void)
6216 {
6217 return NULL;
6218 }
6219
6220 static Jim_Obj *
6221 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
6222 {
6223 char *start, *special;
6224 int quote, i;
6225
6226 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
6227
6228 for (i = 0; argv[i]; i++) {
6229 if (i > 0) {
6230 Jim_AppendString(interp, strObj, " ", 1);
6231 }
6232
6233 if (argv[i][0] == '\0') {
6234 quote = 1;
6235 }
6236 else {
6237 quote = 0;
6238 for (start = argv[i]; *start != '\0'; start++) {
6239 if (isspace(UCHAR(*start))) {
6240 quote = 1;
6241 break;
6242 }
6243 }
6244 }
6245 if (quote) {
6246 Jim_AppendString(interp, strObj, "\"" , 1);
6247 }
6248
6249 start = argv[i];
6250 for (special = argv[i]; ; ) {
6251 if ((*special == '\\') && (special[1] == '\\' ||
6252 special[1] == '"' || (quote && special[1] == '\0'))) {
6253 Jim_AppendString(interp, strObj, start, special - start);
6254 start = special;
6255 while (1) {
6256 special++;
6257 if (*special == '"' || (quote && *special == '\0')) {
6258
6259 Jim_AppendString(interp, strObj, start, special - start);
6260 break;
6261 }
6262 if (*special != '\\') {
6263 break;
6264 }
6265 }
6266 Jim_AppendString(interp, strObj, start, special - start);
6267 start = special;
6268 }
6269 if (*special == '"') {
6270 if (special == start) {
6271 Jim_AppendString(interp, strObj, "\"", 1);
6272 }
6273 else {
6274 Jim_AppendString(interp, strObj, start, special - start);
6275 }
6276 Jim_AppendString(interp, strObj, "\\\"", 2);
6277 start = special + 1;
6278 }
6279 if (*special == '\0') {
6280 break;
6281 }
6282 special++;
6283 }
6284 Jim_AppendString(interp, strObj, start, special - start);
6285 if (quote) {
6286 Jim_AppendString(interp, strObj, "\"", 1);
6287 }
6288 }
6289 return strObj;
6290 }
6291
6292 static phandle_t
6293 JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
6294 {
6295 STARTUPINFO startInfo;
6296 PROCESS_INFORMATION procInfo;
6297 HANDLE hProcess;
6298 char execPath[MAX_PATH];
6299 phandle_t phandle = INVALID_HANDLE_VALUE;
6300 Jim_Obj *cmdLineObj;
6301 char *winenv;
6302
6303 if (JimWinFindExecutable(argv[0], execPath) < 0) {
6304 return phandle;
6305 }
6306 argv[0] = execPath;
6307
6308 hProcess = GetCurrentProcess();
6309 cmdLineObj = JimWinBuildCommandLine(interp, argv);
6310
6311
6312 ZeroMemory(&startInfo, sizeof(startInfo));
6313 startInfo.cb = sizeof(startInfo);
6314 startInfo.dwFlags = STARTF_USESTDHANDLES;
6315 startInfo.hStdInput = INVALID_HANDLE_VALUE;
6316 startInfo.hStdOutput= INVALID_HANDLE_VALUE;
6317 startInfo.hStdError = INVALID_HANDLE_VALUE;
6318
6319 if (inputId == -1) {
6320 inputId = _fileno(stdin);
6321 }
6322 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
6323 0, TRUE, DUPLICATE_SAME_ACCESS);
6324 if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
6325 goto end;
6326 }
6327
6328 if (outputId == -1) {
6329 outputId = _fileno(stdout);
6330 }
6331 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
6332 0, TRUE, DUPLICATE_SAME_ACCESS);
6333 if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
6334 goto end;
6335 }
6336
6337
6338 if (errorId == -1) {
6339 errorId = _fileno(stderr);
6340 }
6341 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
6342 0, TRUE, DUPLICATE_SAME_ACCESS);
6343 if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
6344 goto end;
6345 }
6346
6347 if (env == NULL) {
6348
6349 winenv = NULL;
6350 }
6351 else if (env[0] == NULL) {
6352 winenv = (char *)"\0";
6353 }
6354 else {
6355 winenv = env[0];
6356 }
6357
6358 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
6359 0, winenv, NULL, &startInfo, &procInfo)) {
6360 goto end;
6361 }
6362
6363
6364 WaitForInputIdle(procInfo.hProcess, 5000);
6365 CloseHandle(procInfo.hThread);
6366
6367 phandle = procInfo.hProcess;
6368
6369 end:
6370 Jim_FreeNewObj(interp, cmdLineObj);
6371 if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
6372 CloseHandle(startInfo.hStdInput);
6373 }
6374 if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
6375 CloseHandle(startInfo.hStdOutput);
6376 }
6377 if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
6378 CloseHandle(startInfo.hStdError);
6379 }
6380 return phandle;
6381 }
6382
6383 #else
6384
6385 static char **JimOriginalEnviron(void)
6386 {
6387 return Jim_GetEnviron();
6388 }
6389
6390 static char **JimSaveEnv(char **env)
6391 {
6392 char **saveenv = Jim_GetEnviron();
6393 Jim_SetEnviron(env);
6394 return saveenv;
6395 }
6396
6397 static void JimRestoreEnv(char **env)
6398 {
6399 JimFreeEnv(Jim_GetEnviron(), env);
6400 Jim_SetEnviron(env);
6401 }
6402 #endif
6403 #endif
6404
6405
6406 #include <stdlib.h>
6407 #include <string.h>
6408 #include <stdio.h>
6409 #include <time.h>
6410
6411
6412 #ifdef HAVE_SYS_TIME_H
6413 #include <sys/time.h>
6414 #endif
6415
6416 struct clock_options {
6417 int gmt;
6418 const char *format;
6419 };
6420
6421 static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
6422 {
6423 static const char * const options[] = { "-gmt", "-format", NULL };
6424 enum { OPT_GMT, OPT_FORMAT, };
6425 int i;
6426
6427 for (i = 0; i < argc; i += 2) {
6428 int option;
6429 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
6430 return JIM_ERR;
6431 }
6432 switch (option) {
6433 case OPT_GMT:
6434 if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
6435 return JIM_ERR;
6436 }
6437 break;
6438 case OPT_FORMAT:
6439 opts->format = Jim_String(argv[i + 1]);
6440 break;
6441 }
6442 }
6443 return JIM_OK;
6444 }
6445
6446 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6447 {
6448
6449 char buf[100];
6450 time_t t;
6451 jim_wide seconds;
6452 struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
6453 struct tm *tm;
6454
6455 if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
6456 return JIM_ERR;
6457 }
6458 if (argc % 2 == 0) {
6459 return -1;
6460 }
6461 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
6462 return JIM_ERR;
6463 }
6464
6465 t = seconds;
6466 tm = options.gmt ? gmtime(&t) : localtime(&t);
6467
6468 if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
6469 Jim_SetResultString(interp, "format string too long or invalid time", -1);
6470 return JIM_ERR;
6471 }
6472
6473 Jim_SetResultString(interp, buf, -1);
6474
6475 return JIM_OK;
6476 }
6477
6478 #ifdef HAVE_STRPTIME
6479 static time_t jim_timegm(const struct tm *tm)
6480 {
6481 int m = tm->tm_mon + 1;
6482 int y = 1900 + tm->tm_year - (m <= 2);
6483 int era = (y >= 0 ? y : y - 399) / 400;
6484 unsigned yoe = (unsigned)(y - era * 400);
6485 unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1;
6486 unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
6487 long days = (era * 146097 + (int)doe - 719468);
6488 int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
6489
6490 return days * 24 * 60 * 60 + secs;
6491 }
6492
6493 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6494 {
6495 char *pt;
6496 struct tm tm;
6497 time_t now = time(NULL);
6498
6499 struct clock_options options = { 0, NULL };
6500
6501 if (argc % 2 == 0) {
6502 return -1;
6503 }
6504
6505 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
6506 return JIM_ERR;
6507 }
6508 if (options.format == NULL) {
6509 return -1;
6510 }
6511
6512 localtime_r(&now, &tm);
6513
6514 pt = strptime(Jim_String(argv[0]), options.format, &tm);
6515 if (pt == 0 || *pt != 0) {
6516 Jim_SetResultString(interp, "Failed to parse time according to format", -1);
6517 return JIM_ERR;
6518 }
6519
6520
6521 tm.tm_isdst = options.gmt ? 0 : -1;
6522 Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
6523
6524 return JIM_OK;
6525 }
6526 #endif
6527
6528 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6529 {
6530 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000000);
6531 return JIM_OK;
6532 }
6533
6534 static int clock_cmd_clicks(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6535 {
6536 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW));
6537 return JIM_OK;
6538 }
6539
6540 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6541 {
6542 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME));
6543 return JIM_OK;
6544 }
6545
6546 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6547 {
6548 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000);
6549 return JIM_OK;
6550 }
6551
6552 static const jim_subcmd_type clock_command_table[] = {
6553 { "clicks",
6554 NULL,
6555 clock_cmd_clicks,
6556 0,
6557 0,
6558
6559 },
6560 { "format",
6561 "seconds ?-format string? ?-gmt boolean?",
6562 clock_cmd_format,
6563 1,
6564 5,
6565
6566 },
6567 { "microseconds",
6568 NULL,
6569 clock_cmd_micros,
6570 0,
6571 0,
6572
6573 },
6574 { "milliseconds",
6575 NULL,
6576 clock_cmd_millis,
6577 0,
6578 0,
6579
6580 },
6581 #ifdef HAVE_STRPTIME
6582 { "scan",
6583 "str -format format ?-gmt boolean?",
6584 clock_cmd_scan,
6585 3,
6586 5,
6587
6588 },
6589 #endif
6590 { "seconds",
6591 NULL,
6592 clock_cmd_seconds,
6593 0,
6594 0,
6595
6596 },
6597 { NULL }
6598 };
6599
6600 int Jim_clockInit(Jim_Interp *interp)
6601 {
6602 Jim_PackageProvideCheck(interp, "clock");
6603 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
6604 return JIM_OK;
6605 }
6606
6607 #include <limits.h>
6608 #include <stdlib.h>
6609 #include <string.h>
6610 #include <stdio.h>
6611 #include <errno.h>
6612
6613
6614 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6615 {
6616
6617 Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
6618 Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
6619 return JIM_OK;
6620 }
6621
6622 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6623 {
6624 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6625 Jim_Obj *patternObj;
6626
6627 if (!objPtr) {
6628 return JIM_OK;
6629 }
6630
6631 patternObj = (argc == 1) ? NULL : argv[1];
6632
6633
6634 if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
6635 if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
6636
6637 Jim_SetResult(interp, objPtr);
6638 return JIM_OK;
6639 }
6640 }
6641
6642 return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
6643 }
6644
6645 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6646 {
6647 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6648
6649 if (!objPtr) {
6650 return JIM_OK;
6651 }
6652
6653 return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
6654 }
6655
6656 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6657 {
6658 int i;
6659 int len;
6660 Jim_Obj *resultObj;
6661 Jim_Obj *objPtr;
6662 Jim_Obj **dictValuesObj;
6663
6664 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
6665
6666 Jim_UnsetVariable(interp, argv[0], JIM_NONE);
6667 return JIM_OK;
6668 }
6669
6670 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6671
6672 if (objPtr == NULL) {
6673
6674 return JIM_OK;
6675 }
6676
6677 dictValuesObj = Jim_DictPairs(interp, objPtr, &len);
6678 if (dictValuesObj == NULL) {
6679
6680 Jim_SetResultString(interp, "", -1);
6681 return JIM_OK;
6682 }
6683
6684
6685 resultObj = Jim_NewDictObj(interp, NULL, 0);
6686
6687 for (i = 0; i < len; i += 2) {
6688 if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
6689 Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
6690 }
6691 }
6692
6693 Jim_SetVariable(interp, argv[0], resultObj);
6694 return JIM_OK;
6695 }
6696
6697 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6698 {
6699 Jim_Obj *objPtr;
6700 int len = 0;
6701
6702
6703 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6704 if (objPtr) {
6705 len = Jim_DictSize(interp, objPtr);
6706 if (len < 0) {
6707
6708 Jim_SetResultInt(interp, 0);
6709 return JIM_OK;
6710 }
6711 }
6712
6713 Jim_SetResultInt(interp, len);
6714
6715 return JIM_OK;
6716 }
6717
6718 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6719 {
6720 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6721 if (objPtr) {
6722 return Jim_DictInfo(interp, objPtr);
6723 }
6724 Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
6725 return JIM_ERR;
6726 }
6727
6728 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6729 {
6730 int i;
6731 int len;
6732 Jim_Obj *listObj = argv[1];
6733 Jim_Obj *dictObj;
6734
6735 len = Jim_ListLength(interp, listObj);
6736 if (len % 2) {
6737 Jim_SetResultString(interp, "list must have an even number of elements", -1);
6738 return JIM_ERR;
6739 }
6740
6741 dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
6742 if (!dictObj) {
6743
6744 return Jim_SetVariable(interp, argv[0], listObj);
6745 }
6746 else if (Jim_DictSize(interp, dictObj) < 0) {
6747 return JIM_ERR;
6748 }
6749
6750 if (Jim_IsShared(dictObj)) {
6751 dictObj = Jim_DuplicateObj(interp, dictObj);
6752 }
6753
6754 for (i = 0; i < len; i += 2) {
6755 Jim_Obj *nameObj;
6756 Jim_Obj *valueObj;
6757
6758 Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
6759 Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
6760
6761 Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
6762 }
6763 return Jim_SetVariable(interp, argv[0], dictObj);
6764 }
6765
6766 static const jim_subcmd_type array_command_table[] = {
6767 { "exists",
6768 "arrayName",
6769 array_cmd_exists,
6770 1,
6771 1,
6772
6773 },
6774 { "get",
6775 "arrayName ?pattern?",
6776 array_cmd_get,
6777 1,
6778 2,
6779
6780 },
6781 { "names",
6782 "arrayName ?pattern?",
6783 array_cmd_names,
6784 1,
6785 2,
6786
6787 },
6788 { "set",
6789 "arrayName list",
6790 array_cmd_set,
6791 2,
6792 2,
6793
6794 },
6795 { "size",
6796 "arrayName",
6797 array_cmd_size,
6798 1,
6799 1,
6800
6801 },
6802 { "stat",
6803 "arrayName",
6804 array_cmd_stat,
6805 1,
6806 1,
6807
6808 },
6809 { "unset",
6810 "arrayName ?pattern?",
6811 array_cmd_unset,
6812 1,
6813 2,
6814
6815 },
6816 { NULL
6817 }
6818 };
6819
6820 int Jim_arrayInit(Jim_Interp *interp)
6821 {
6822 Jim_PackageProvideCheck(interp, "array");
6823 Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
6824 return JIM_OK;
6825 }
6826 int Jim_InitStaticExtensions(Jim_Interp *interp)
6827 {
6828 extern int Jim_bootstrapInit(Jim_Interp *);
6829 extern int Jim_aioInit(Jim_Interp *);
6830 extern int Jim_readdirInit(Jim_Interp *);
6831 extern int Jim_regexpInit(Jim_Interp *);
6832 extern int Jim_fileInit(Jim_Interp *);
6833 extern int Jim_globInit(Jim_Interp *);
6834 extern int Jim_execInit(Jim_Interp *);
6835 extern int Jim_clockInit(Jim_Interp *);
6836 extern int Jim_arrayInit(Jim_Interp *);
6837 extern int Jim_stdlibInit(Jim_Interp *);
6838 extern int Jim_tclcompatInit(Jim_Interp *);
6839 Jim_bootstrapInit(interp);
6840 Jim_aioInit(interp);
6841 Jim_readdirInit(interp);
6842 Jim_regexpInit(interp);
6843 Jim_fileInit(interp);
6844 Jim_globInit(interp);
6845 Jim_execInit(interp);
6846 Jim_clockInit(interp);
6847 Jim_arrayInit(interp);
6848 Jim_stdlibInit(interp);
6849 Jim_tclcompatInit(interp);
6850 return JIM_OK;
6851 }
6852 #ifndef JIM_TINY
6853 #define JIM_OPTIMIZATION
6854 #endif
6855
6856 #include <stdio.h>
6857 #include <stdlib.h>
6858
6859 #include <string.h>
6860 #include <stdarg.h>
6861 #include <ctype.h>
6862 #include <limits.h>
6863 #include <assert.h>
6864 #include <errno.h>
6865 #include <time.h>
6866 #include <setjmp.h>
6867
6868
6869 #ifdef HAVE_SYS_TIME_H
6870 #include <sys/time.h>
6871 #endif
6872 #ifdef HAVE_EXECINFO_H
6873 #include <execinfo.h>
6874 #endif
6875 #ifdef HAVE_CRT_EXTERNS_H
6876 #include <crt_externs.h>
6877 #endif
6878
6879
6880 #include <math.h>
6881
6882
6883
6884
6885
6886 #ifndef TCL_LIBRARY
6887 #define TCL_LIBRARY "."
6888 #endif
6889 #ifndef TCL_PLATFORM_OS
6890 #define TCL_PLATFORM_OS "unknown"
6891 #endif
6892 #ifndef TCL_PLATFORM_PLATFORM
6893 #define TCL_PLATFORM_PLATFORM "unknown"
6894 #endif
6895 #ifndef TCL_PLATFORM_PATH_SEPARATOR
6896 #define TCL_PLATFORM_PATH_SEPARATOR ":"
6897 #endif
6898
6899
6900
6901
6902
6903
6904
6905 #ifdef JIM_MAINTAINER
6906 #define JIM_DEBUG_COMMAND
6907 #define JIM_DEBUG_PANIC
6908 #endif
6909
6910
6911
6912 #define JIM_INTEGER_SPACE 24
6913
6914 #if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST)
6915 static const char *jim_tt_name(int type);
6916 #endif
6917
6918 #ifdef JIM_DEBUG_PANIC
6919 static void JimPanicDump(int fail_condition, const char *fmt, ...);
6920 #define JimPanic(X) JimPanicDump X
6921 #else
6922 #define JimPanic(X)
6923 #endif
6924
6925 #ifdef JIM_OPTIMIZATION
6926 static int JimIsWide(Jim_Obj *objPtr);
6927 #define JIM_IF_OPTIM(X) X
6928 #else
6929 #define JIM_IF_OPTIM(X)
6930 #endif
6931
6932
6933 static char JimEmptyStringRep[] = "";
6934
6935 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
6936 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
6937 int flags);
6938 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *const *indexv, int indexc,
6939 Jim_Obj **resultObj, int flags);
6940 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
6941 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
6942 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6943 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
6944 const char *prefix, const char *const *tablePtr, const char *name);
6945 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
6946 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
6947 static int JimSign(jim_wide w);
6948 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
6949 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
6950 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv);
6951 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr);
6952 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6953
6954 #define JIM_DICT_SUGAR 100
6955
6956
6957
6958
6959 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
6960
6961 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
6962
6963 static int utf8_tounicode_case(const char *s, int *uc, int upper)
6964 {
6965 int l = utf8_tounicode(s, uc);
6966 if (upper) {
6967 *uc = utf8_upper(*uc);
6968 }
6969 return l;
6970 }
6971
6972 static Jim_Obj *JimPushInterpObjImpl(Jim_Obj **iop, Jim_Obj *no)
6973 {
6974 Jim_Obj *io = *iop;
6975 Jim_IncrRefCount(no);
6976 *iop = no;
6977 return io;
6978 }
6979
6980 #define JimPushInterpObj(IO, NO) JimPushInterpObjImpl(&(IO), NO)
6981 #define JimPopInterpObj(I, IO, SO) do { Jim_DecrRefCount(I, IO); IO = SO; } while (0)
6982
6983
6984 #define JIM_CHARSET_SCAN 2
6985 #define JIM_CHARSET_GLOB 0
6986
6987 static const char *JimCharsetMatch(const char *pattern, int plen, int c, int flags)
6988 {
6989 int not = 0;
6990 int pchar;
6991 int match = 0;
6992 int nocase = 0;
6993 int n;
6994
6995 if (flags & JIM_NOCASE) {
6996 nocase++;
6997 c = utf8_upper(c);
6998 }
6999
7000 if (flags & JIM_CHARSET_SCAN) {
7001 if (*pattern == '^') {
7002 not++;
7003 pattern++;
7004 plen--;
7005 }
7006
7007
7008 if (*pattern == ']') {
7009 goto first;
7010 }
7011 }
7012
7013 while (plen && *pattern != ']') {
7014
7015 if (pattern[0] == '\\') {
7016 first:
7017 n = utf8_tounicode_case(pattern, &pchar, nocase);
7018 pattern += n;
7019 plen -= n;
7020 }
7021 else {
7022
7023 int start;
7024 int end;
7025
7026 n = utf8_tounicode_case(pattern, &start, nocase);
7027 pattern += n;
7028 plen -= n;
7029 if (pattern[0] == '-' && plen > 1) {
7030
7031 n = 1 + utf8_tounicode_case(pattern + 1, &end, nocase);
7032 pattern += n;
7033 plen -= n;
7034
7035
7036 if ((c >= start && c <= end) || (c >= end && c <= start)) {
7037 match = 1;
7038 }
7039 continue;
7040 }
7041 pchar = start;
7042 }
7043
7044 if (pchar == c) {
7045 match = 1;
7046 }
7047 }
7048 if (not) {
7049 match = !match;
7050 }
7051
7052 return match ? pattern : NULL;
7053 }
7054
7055
7056
7057 static int JimGlobMatch(const char *pattern, int plen, const char *string, int slen, int nocase)
7058 {
7059 int c;
7060 int pchar;
7061 int n;
7062 const char *p;
7063 while (plen) {
7064 switch (pattern[0]) {
7065 case '*':
7066 while (pattern[1] == '*' && plen) {
7067 pattern++;
7068 plen--;
7069 }
7070 pattern++;
7071 plen--;
7072 if (!plen) {
7073 return 1;
7074 }
7075 while (slen) {
7076
7077 if (JimGlobMatch(pattern, plen, string, slen, nocase))
7078 return 1;
7079 n = utf8_tounicode(string, &c);
7080 string += n;
7081 slen -= n;
7082 }
7083 return 0;
7084
7085 case '?':
7086 n = utf8_tounicode(string, &c);
7087 string += n;
7088 slen -= n;
7089 break;
7090
7091 case '[': {
7092 n = utf8_tounicode(string, &c);
7093 string += n;
7094 slen -= n;
7095 p = JimCharsetMatch(pattern + 1, plen - 1, c, nocase ? JIM_NOCASE : 0);
7096 if (!p) {
7097 return 0;
7098 }
7099 plen -= p - pattern;
7100 pattern = p;
7101
7102 if (!plen) {
7103
7104 continue;
7105 }
7106 break;
7107 }
7108 case '\\':
7109 if (pattern[1]) {
7110 pattern++;
7111 plen--;
7112 }
7113
7114 default:
7115 n = utf8_tounicode_case(string, &c, nocase);
7116 string += n;
7117 slen -= n;
7118 utf8_tounicode_case(pattern, &pchar, nocase);
7119 if (pchar != c) {
7120 return 0;
7121 }
7122 break;
7123 }
7124 n = utf8_tounicode_case(pattern, &pchar, nocase);
7125 pattern += n;
7126 plen -= n;
7127 if (!slen) {
7128 while (*pattern == '*' && plen) {
7129 pattern++;
7130 plen--;
7131 }
7132 break;
7133 }
7134 }
7135 if (!plen && !slen) {
7136 return 1;
7137 }
7138 return 0;
7139 }
7140
7141 static int JimStringCompareUtf8(const char *s1, int l1, const char *s2, int l2, int nocase)
7142 {
7143 int minlen = l1;
7144 if (l2 < l1) {
7145 minlen = l2;
7146 }
7147 while (minlen) {
7148 int c1, c2;
7149 s1 += utf8_tounicode_case(s1, &c1, nocase);
7150 s2 += utf8_tounicode_case(s2, &c2, nocase);
7151 if (c1 != c2) {
7152 return JimSign(c1 - c2);
7153 }
7154 minlen--;
7155 }
7156
7157 if (l1 < l2) {
7158 return -1;
7159 }
7160 if (l1 > l2) {
7161 return 1;
7162 }
7163 return 0;
7164 }
7165
7166 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
7167 {
7168 int i;
7169 int l1bytelen;
7170
7171 if (!l1 || !l2 || l1 > l2) {
7172 return -1;
7173 }
7174 if (idx < 0)
7175 idx = 0;
7176 s2 += utf8_index(s2, idx);
7177
7178 l1bytelen = utf8_index(s1, l1);
7179
7180 for (i = idx; i <= l2 - l1; i++) {
7181 int c;
7182 if (memcmp(s2, s1, l1bytelen) == 0) {
7183 return i;
7184 }
7185 s2 += utf8_tounicode(s2, &c);
7186 }
7187 return -1;
7188 }
7189
7190 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
7191 {
7192 const char *p;
7193
7194 if (!l1 || !l2 || l1 > l2)
7195 return -1;
7196
7197
7198 for (p = s2 + l2 - 1; p != s2 - 1; p--) {
7199 if (*p == *s1 && memcmp(s1, p, l1) == 0) {
7200 return p - s2;
7201 }
7202 }
7203 return -1;
7204 }
7205
7206 #ifdef JIM_UTF8
7207 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
7208 {
7209 int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
7210 if (n > 0) {
7211 n = utf8_strlen(s2, n);
7212 }
7213 return n;
7214 }
7215 #endif
7216
7217 static int JimCheckConversion(const char *str, const char *endptr)
7218 {
7219 if (str[0] == '\0' || str == endptr) {
7220 return JIM_ERR;
7221 }
7222
7223 if (endptr[0] != '\0') {
7224 while (*endptr) {
7225 if (!isspace(UCHAR(*endptr))) {
7226 return JIM_ERR;
7227 }
7228 endptr++;
7229 }
7230 }
7231 return JIM_OK;
7232 }
7233
7234 static int JimNumberBase(const char *str, int *base, int *sign)
7235 {
7236 int i = 0;
7237
7238 *base = 0;
7239
7240 while (isspace(UCHAR(str[i]))) {
7241 i++;
7242 }
7243
7244 if (str[i] == '-') {
7245 *sign = -1;
7246 i++;
7247 }
7248 else {
7249 if (str[i] == '+') {
7250 i++;
7251 }
7252 *sign = 1;
7253 }
7254
7255 if (str[i] != '0') {
7256
7257 return 0;
7258 }
7259
7260
7261 switch (str[i + 1]) {
7262 case 'x': case 'X': *base = 16; break;
7263 case 'o': case 'O': *base = 8; break;
7264 case 'b': case 'B': *base = 2; break;
7265 case 'd': case 'D': *base = 10; break;
7266 default: return 0;
7267 }
7268 i += 2;
7269
7270 if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
7271
7272 return i;
7273 }
7274
7275 *base = 0;
7276 return 0;
7277 }
7278
7279 static long jim_strtol(const char *str, char **endptr)
7280 {
7281 int sign;
7282 int base;
7283 int i = JimNumberBase(str, &base, &sign);
7284
7285 if (base != 0) {
7286 long value = strtol(str + i, endptr, base);
7287 if (endptr == NULL || *endptr != str + i) {
7288 return value * sign;
7289 }
7290 }
7291
7292
7293 return strtol(str, endptr, 10);
7294 }
7295
7296
7297 static jim_wide jim_strtoull(const char *str, char **endptr)
7298 {
7299 #ifdef HAVE_LONG_LONG
7300 int sign;
7301 int base;
7302 int i = JimNumberBase(str, &base, &sign);
7303
7304 if (base != 0) {
7305 jim_wide value = strtoull(str + i, endptr, base);
7306 if (endptr == NULL || *endptr != str + i) {
7307 return value * sign;
7308 }
7309 }
7310
7311
7312 return strtoull(str, endptr, 10);
7313 #else
7314 return (unsigned long)jim_strtol(str, endptr);
7315 #endif
7316 }
7317
7318 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
7319 {
7320 char *endptr;
7321
7322 if (base) {
7323 *widePtr = strtoull(str, &endptr, base);
7324 }
7325 else {
7326 *widePtr = jim_strtoull(str, &endptr);
7327 }
7328
7329 return JimCheckConversion(str, endptr);
7330 }
7331
7332 int Jim_StringToDouble(const char *str, double *doublePtr)
7333 {
7334 char *endptr;
7335
7336
7337 errno = 0;
7338
7339 *doublePtr = strtod(str, &endptr);
7340
7341 return JimCheckConversion(str, endptr);
7342 }
7343
7344 static jim_wide JimPowWide(jim_wide b, jim_wide e)
7345 {
7346 jim_wide res = 1;
7347
7348
7349 if (b == 1) {
7350
7351 return 1;
7352 }
7353 if (e < 0) {
7354 if (b != -1) {
7355 return 0;
7356 }
7357 e = -e;
7358 }
7359 while (e)
7360 {
7361 if (e & 1) {
7362 res *= b;
7363 }
7364 e >>= 1;
7365 b *= b;
7366 }
7367 return res;
7368 }
7369
7370 #ifdef JIM_DEBUG_PANIC
7371 static void JimPanicDump(int condition, const char *fmt, ...)
7372 {
7373 va_list ap;
7374
7375 if (!condition) {
7376 return;
7377 }
7378
7379 va_start(ap, fmt);
7380
7381 fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
7382 vfprintf(stderr, fmt, ap);
7383 fprintf(stderr, "\n\n");
7384 va_end(ap);
7385
7386 #if defined(HAVE_BACKTRACE)
7387 {
7388 void *array[40];
7389 int size, i;
7390 char **strings;
7391
7392 size = backtrace(array, 40);
7393 strings = backtrace_symbols(array, size);
7394 for (i = 0; i < size; i++)
7395 fprintf(stderr, "[backtrace] %s\n", strings[i]);
7396 fprintf(stderr, "[backtrace] Include the above lines and the output\n");
7397 fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
7398 }
7399 #endif
7400
7401 exit(1);
7402 }
7403 #endif
7404
7405
7406 void *JimDefaultAllocator(void *ptr, size_t size)
7407 {
7408 if (size == 0) {
7409 free(ptr);
7410 return NULL;
7411 }
7412 else if (ptr) {
7413 return realloc(ptr, size);
7414 }
7415 else {
7416 return malloc(size);
7417 }
7418 }
7419
7420 void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator;
7421
7422 char *Jim_StrDup(const char *s)
7423 {
7424 return Jim_StrDupLen(s, strlen(s));
7425 }
7426
7427 char *Jim_StrDupLen(const char *s, int l)
7428 {
7429 char *copy = Jim_Alloc(l + 1);
7430
7431 memcpy(copy, s, l);
7432 copy[l] = 0;
7433 return copy;
7434 }
7435
7436
7437 jim_wide Jim_GetTimeUsec(unsigned type)
7438 {
7439 long long now;
7440 struct timeval tv;
7441
7442 #if defined(HAVE_CLOCK_GETTIME)
7443 struct timespec ts;
7444
7445 if (clock_gettime(type, &ts) == 0) {
7446 now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
7447 }
7448 else
7449 #endif
7450 {
7451 gettimeofday(&tv, NULL);
7452
7453 now = tv.tv_sec * 1000000LL + tv.tv_usec;
7454 }
7455
7456 return now;
7457 }
7458
7459
7460
7461
7462
7463 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
7464 static unsigned int JimHashTableNextPower(unsigned int size);
7465 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
7466
7467
7468
7469
7470 unsigned int Jim_IntHashFunction(unsigned int key)
7471 {
7472 key += ~(key << 15);
7473 key ^= (key >> 10);
7474 key += (key << 3);
7475 key ^= (key >> 6);
7476 key += ~(key << 11);
7477 key ^= (key >> 16);
7478 return key;
7479 }
7480
7481
7482 unsigned int Jim_GenHashFunction(const unsigned char *string, int length)
7483 {
7484 unsigned result = 0;
7485 string += length;
7486 while (length--) {
7487 result += (result << 3) + (unsigned char)(*--string);
7488 }
7489 return result;
7490 }
7491
7492
7493
7494 static void JimResetHashTable(Jim_HashTable *ht)
7495 {
7496 ht->table = NULL;
7497 ht->size = 0;
7498 ht->sizemask = 0;
7499 ht->used = 0;
7500 ht->collisions = 0;
7501 #ifdef JIM_RANDOMISE_HASH
7502 ht->uniq = (rand() ^ time(NULL) ^ clock());
7503 #else
7504 ht->uniq = 0;
7505 #endif
7506 }
7507
7508 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
7509 {
7510 iter->ht = ht;
7511 iter->index = -1;
7512 iter->entry = NULL;
7513 iter->nextEntry = NULL;
7514 }
7515
7516
7517 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
7518 {
7519 JimResetHashTable(ht);
7520 ht->type = type;
7521 ht->privdata = privDataPtr;
7522 return JIM_OK;
7523 }
7524
7525
7526 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
7527 {
7528 Jim_HashTable n;
7529 unsigned int realsize = JimHashTableNextPower(size), i;
7530
7531 if (size <= ht->used)
7532 return;
7533
7534 Jim_InitHashTable(&n, ht->type, ht->privdata);
7535 n.size = realsize;
7536 n.sizemask = realsize - 1;
7537 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
7538
7539 n.uniq = ht->uniq;
7540
7541
7542 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
7543
7544 n.used = ht->used;
7545 for (i = 0; ht->used > 0; i++) {
7546 Jim_HashEntry *he, *nextHe;
7547
7548 if (ht->table[i] == NULL)
7549 continue;
7550
7551
7552 he = ht->table[i];
7553 while (he) {
7554 unsigned int h;
7555
7556 nextHe = he->next;
7557
7558 h = Jim_HashKey(ht, he->key) & n.sizemask;
7559 he->next = n.table[h];
7560 n.table[h] = he;
7561 ht->used--;
7562
7563 he = nextHe;
7564 }
7565 }
7566 assert(ht->used == 0);
7567 Jim_Free(ht->table);
7568
7569
7570 *ht = n;
7571 }
7572
7573 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
7574 {
7575 Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);;
7576 if (entry == NULL)
7577 return JIM_ERR;
7578
7579
7580 Jim_SetHashKey(ht, entry, key);
7581 Jim_SetHashVal(ht, entry, val);
7582 return JIM_OK;
7583 }
7584
7585
7586 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
7587 {
7588 int existed;
7589 Jim_HashEntry *entry;
7590
7591 entry = JimInsertHashEntry(ht, key, 1);
7592 if (entry->key) {
7593 if (ht->type->valDestructor && ht->type->valDup) {
7594 void *newval = ht->type->valDup(ht->privdata, val);
7595 ht->type->valDestructor(ht->privdata, entry->u.val);
7596 entry->u.val = newval;
7597 }
7598 else {
7599 Jim_FreeEntryVal(ht, entry);
7600 Jim_SetHashVal(ht, entry, val);
7601 }
7602 existed = 1;
7603 }
7604 else {
7605
7606 Jim_SetHashKey(ht, entry, key);
7607 Jim_SetHashVal(ht, entry, val);
7608 existed = 0;
7609 }
7610
7611 return existed;
7612 }
7613
7614 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
7615 {
7616 if (ht->used) {
7617 unsigned int h = Jim_HashKey(ht, key) & ht->sizemask;
7618 Jim_HashEntry *prevHe = NULL;
7619 Jim_HashEntry *he = ht->table[h];
7620
7621 while (he) {
7622 if (Jim_CompareHashKeys(ht, key, he->key)) {
7623
7624 if (prevHe)
7625 prevHe->next = he->next;
7626 else
7627 ht->table[h] = he->next;
7628 ht->used--;
7629 Jim_FreeEntryKey(ht, he);
7630 Jim_FreeEntryVal(ht, he);
7631 Jim_Free(he);
7632 return JIM_OK;
7633 }
7634 prevHe = he;
7635 he = he->next;
7636 }
7637 }
7638
7639 return JIM_ERR;
7640 }
7641
7642 void Jim_ClearHashTable(Jim_HashTable *ht)
7643 {
7644 unsigned int i;
7645
7646
7647 for (i = 0; ht->used > 0; i++) {
7648 Jim_HashEntry *he, *nextHe;
7649
7650 he = ht->table[i];
7651 while (he) {
7652 nextHe = he->next;
7653 Jim_FreeEntryKey(ht, he);
7654 Jim_FreeEntryVal(ht, he);
7655 Jim_Free(he);
7656 ht->used--;
7657 he = nextHe;
7658 }
7659 ht->table[i] = NULL;
7660 }
7661 }
7662
7663 int Jim_FreeHashTable(Jim_HashTable *ht)
7664 {
7665 Jim_ClearHashTable(ht);
7666
7667 Jim_Free(ht->table);
7668
7669 JimResetHashTable(ht);
7670 return JIM_OK;
7671 }
7672
7673 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
7674 {
7675 Jim_HashEntry *he;
7676 unsigned int h;
7677
7678 if (ht->used == 0)
7679 return NULL;
7680 h = Jim_HashKey(ht, key) & ht->sizemask;
7681 he = ht->table[h];
7682 while (he) {
7683 if (Jim_CompareHashKeys(ht, key, he->key))
7684 return he;
7685 he = he->next;
7686 }
7687 return NULL;
7688 }
7689
7690 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
7691 {
7692 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
7693 JimInitHashTableIterator(ht, iter);
7694 return iter;
7695 }
7696
7697 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
7698 {
7699 while (1) {
7700 if (iter->entry == NULL) {
7701 iter->index++;
7702 if (iter->index >= (signed)iter->ht->size)
7703 break;
7704 iter->entry = iter->ht->table[iter->index];
7705 }
7706 else {
7707 iter->entry = iter->nextEntry;
7708 }
7709 if (iter->entry) {
7710 iter->nextEntry = iter->entry->next;
7711 return iter->entry;
7712 }
7713 }
7714 return NULL;
7715 }
7716
7717
7718
7719
7720 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
7721 {
7722 if (ht->size == 0)
7723 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
7724 if (ht->size == ht->used)
7725 Jim_ExpandHashTable(ht, ht->size * 2);
7726 }
7727
7728
7729 static unsigned int JimHashTableNextPower(unsigned int size)
7730 {
7731 unsigned int i = JIM_HT_INITIAL_SIZE;
7732
7733 if (size >= 2147483648U)
7734 return 2147483648U;
7735 while (1) {
7736 if (i >= size)
7737 return i;
7738 i *= 2;
7739 }
7740 }
7741
7742 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
7743 {
7744 unsigned int h;
7745 Jim_HashEntry *he;
7746
7747
7748 JimExpandHashTableIfNeeded(ht);
7749
7750
7751 h = Jim_HashKey(ht, key) & ht->sizemask;
7752
7753 he = ht->table[h];
7754 while (he) {
7755 if (Jim_CompareHashKeys(ht, key, he->key))
7756 return replace ? he : NULL;
7757 he = he->next;
7758 }
7759
7760
7761 he = Jim_Alloc(sizeof(*he));
7762 he->next = ht->table[h];
7763 ht->table[h] = he;
7764 ht->used++;
7765 he->key = NULL;
7766
7767 return he;
7768 }
7769
7770
7771
7772 static unsigned int JimStringCopyHTHashFunction(const void *key)
7773 {
7774 return Jim_GenHashFunction(key, strlen(key));
7775 }
7776
7777 static void *JimStringCopyHTDup(void *privdata, const void *key)
7778 {
7779 return Jim_StrDup(key);
7780 }
7781
7782 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
7783 {
7784 return strcmp(key1, key2) == 0;
7785 }
7786
7787 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
7788 {
7789 Jim_Free(key);
7790 }
7791
7792 static const Jim_HashTableType JimPackageHashTableType = {
7793 JimStringCopyHTHashFunction,
7794 JimStringCopyHTDup,
7795 NULL,
7796 JimStringCopyHTKeyCompare,
7797 JimStringCopyHTKeyDestructor,
7798 NULL
7799 };
7800
7801 typedef struct AssocDataValue
7802 {
7803 Jim_InterpDeleteProc *delProc;
7804 void *data;
7805 } AssocDataValue;
7806
7807 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
7808 {
7809 AssocDataValue *assocPtr = (AssocDataValue *) data;
7810
7811 if (assocPtr->delProc != NULL)
7812 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
7813 Jim_Free(data);
7814 }
7815
7816 static const Jim_HashTableType JimAssocDataHashTableType = {
7817 JimStringCopyHTHashFunction,
7818 JimStringCopyHTDup,
7819 NULL,
7820 JimStringCopyHTKeyCompare,
7821 JimStringCopyHTKeyDestructor,
7822 JimAssocDataHashTableValueDestructor
7823 };
7824
7825 void Jim_InitStack(Jim_Stack *stack)
7826 {
7827 stack->len = 0;
7828 stack->maxlen = 0;
7829 stack->vector = NULL;
7830 }
7831
7832 void Jim_FreeStack(Jim_Stack *stack)
7833 {
7834 Jim_Free(stack->vector);
7835 }
7836
7837 int Jim_StackLen(Jim_Stack *stack)
7838 {
7839 return stack->len;
7840 }
7841
7842 void Jim_StackPush(Jim_Stack *stack, void *element)
7843 {
7844 int neededLen = stack->len + 1;
7845
7846 if (neededLen > stack->maxlen) {
7847 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
7848 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
7849 }
7850 stack->vector[stack->len] = element;
7851 stack->len++;
7852 }
7853
7854 void *Jim_StackPop(Jim_Stack *stack)
7855 {
7856 if (stack->len == 0)
7857 return NULL;
7858 stack->len--;
7859 return stack->vector[stack->len];
7860 }
7861
7862 void *Jim_StackPeek(Jim_Stack *stack)
7863 {
7864 if (stack->len == 0)
7865 return NULL;
7866 return stack->vector[stack->len - 1];
7867 }
7868
7869 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
7870 {
7871 int i;
7872
7873 for (i = 0; i < stack->len; i++)
7874 freeFunc(stack->vector[i]);
7875 }
7876
7877
7878
7879 #define JIM_TT_NONE 0
7880 #define JIM_TT_STR 1
7881 #define JIM_TT_ESC 2
7882 #define JIM_TT_VAR 3
7883 #define JIM_TT_DICTSUGAR 4
7884 #define JIM_TT_CMD 5
7885
7886 #define JIM_TT_SEP 6
7887 #define JIM_TT_EOL 7
7888 #define JIM_TT_EOF 8
7889
7890 #define JIM_TT_LINE 9
7891 #define JIM_TT_WORD 10
7892
7893
7894 #define JIM_TT_SUBEXPR_START 11
7895 #define JIM_TT_SUBEXPR_END 12
7896 #define JIM_TT_SUBEXPR_COMMA 13
7897 #define JIM_TT_EXPR_INT 14
7898 #define JIM_TT_EXPR_DOUBLE 15
7899 #define JIM_TT_EXPR_BOOLEAN 16
7900
7901 #define JIM_TT_EXPRSUGAR 17
7902
7903
7904 #define JIM_TT_EXPR_OP 20
7905
7906 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
7907
7908 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
7909
7910 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
7911
7912 struct JimParseMissing {
7913 int ch;
7914 int line;
7915 };
7916
7917 struct JimParserCtx
7918 {
7919 const char *p;
7920 int len;
7921 int linenr;
7922 const char *tstart;
7923 const char *tend;
7924 int tline;
7925 int tt;
7926 int eof;
7927 int inquote;
7928 int comment;
7929 struct JimParseMissing missing;
7930 const char *errmsg;
7931 };
7932
7933 static int JimParseScript(struct JimParserCtx *pc);
7934 static int JimParseSep(struct JimParserCtx *pc);
7935 static int JimParseEol(struct JimParserCtx *pc);
7936 static int JimParseCmd(struct JimParserCtx *pc);
7937 static int JimParseQuote(struct JimParserCtx *pc);
7938 static int JimParseVar(struct JimParserCtx *pc);
7939 static int JimParseBrace(struct JimParserCtx *pc);
7940 static int JimParseStr(struct JimParserCtx *pc);
7941 static int JimParseComment(struct JimParserCtx *pc);
7942 static void JimParseSubCmd(struct JimParserCtx *pc);
7943 static int JimParseSubQuote(struct JimParserCtx *pc);
7944 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7945
7946 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7947 {
7948 pc->p = prg;
7949 pc->len = len;
7950 pc->tstart = NULL;
7951 pc->tend = NULL;
7952 pc->tline = 0;
7953 pc->tt = JIM_TT_NONE;
7954 pc->eof = 0;
7955 pc->inquote = 0;
7956 pc->linenr = linenr;
7957 pc->comment = 1;
7958 pc->missing.ch = ' ';
7959 pc->missing.line = linenr;
7960 }
7961
7962 static int JimParseScript(struct JimParserCtx *pc)
7963 {
7964 while (1) {
7965 if (!pc->len) {
7966 pc->tstart = pc->p;
7967 pc->tend = pc->p - 1;
7968 pc->tline = pc->linenr;
7969 pc->tt = JIM_TT_EOL;
7970 if (pc->inquote) {
7971 pc->missing.ch = '"';
7972 }
7973 pc->eof = 1;
7974 return JIM_OK;
7975 }
7976 switch (*(pc->p)) {
7977 case '\\':
7978 if (*(pc->p + 1) == '\n' && !pc->inquote) {
7979 return JimParseSep(pc);
7980 }
7981 pc->comment = 0;
7982 return JimParseStr(pc);
7983 case ' ':
7984 case '\t':
7985 case '\r':
7986 case '\f':
7987 if (!pc->inquote)
7988 return JimParseSep(pc);
7989 pc->comment = 0;
7990 return JimParseStr(pc);
7991 case '\n':
7992 case ';':
7993 pc->comment = 1;
7994 if (!pc->inquote)
7995 return JimParseEol(pc);
7996 return JimParseStr(pc);
7997 case '[':
7998 pc->comment = 0;
7999 return JimParseCmd(pc);
8000 case '$':
8001 pc->comment = 0;
8002 if (JimParseVar(pc) == JIM_ERR) {
8003
8004 pc->tstart = pc->tend = pc->p++;
8005 pc->len--;
8006 pc->tt = JIM_TT_ESC;
8007 }
8008 return JIM_OK;
8009 case '#':
8010 if (pc->comment) {
8011 JimParseComment(pc);
8012 continue;
8013 }
8014 return JimParseStr(pc);
8015 default:
8016 pc->comment = 0;
8017 return JimParseStr(pc);
8018 }
8019 return JIM_OK;
8020 }
8021 }
8022
8023 static int JimParseSep(struct JimParserCtx *pc)
8024 {
8025 pc->tstart = pc->p;
8026 pc->tline = pc->linenr;
8027 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
8028 if (*pc->p == '\n') {
8029 break;
8030 }
8031 if (*pc->p == '\\') {
8032 pc->p++;
8033 pc->len--;
8034 pc->linenr++;
8035 }
8036 pc->p++;
8037 pc->len--;
8038 }
8039 pc->tend = pc->p - 1;
8040 pc->tt = JIM_TT_SEP;
8041 return JIM_OK;
8042 }
8043
8044 static int JimParseEol(struct JimParserCtx *pc)
8045 {
8046 pc->tstart = pc->p;
8047 pc->tline = pc->linenr;
8048 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
8049 if (*pc->p == '\n')
8050 pc->linenr++;
8051 pc->p++;
8052 pc->len--;
8053 }
8054 pc->tend = pc->p - 1;
8055 pc->tt = JIM_TT_EOL;
8056 return JIM_OK;
8057 }
8058
8059
8060 static void JimParseSubBrace(struct JimParserCtx *pc)
8061 {
8062 int level = 1;
8063
8064
8065 pc->p++;
8066 pc->len--;
8067 while (pc->len) {
8068 switch (*pc->p) {
8069 case '\\':
8070 if (pc->len > 1) {
8071 if (*++pc->p == '\n') {
8072 pc->linenr++;
8073 }
8074 pc->len--;
8075 }
8076 break;
8077
8078 case '{':
8079 level++;
8080 break;
8081
8082 case '}':
8083 if (--level == 0) {
8084 pc->tend = pc->p - 1;
8085 pc->p++;
8086 pc->len--;
8087 return;
8088 }
8089 break;
8090
8091 case '\n':
8092 pc->linenr++;
8093 break;
8094 }
8095 pc->p++;
8096 pc->len--;
8097 }
8098 pc->missing.ch = '{';
8099 pc->missing.line = pc->tline;
8100 pc->tend = pc->p - 1;
8101 }
8102
8103 static int JimParseSubQuote(struct JimParserCtx *pc)
8104 {
8105 int tt = JIM_TT_STR;
8106 int line = pc->tline;
8107
8108
8109 pc->p++;
8110 pc->len--;
8111 while (pc->len) {
8112 switch (*pc->p) {
8113 case '\\':
8114 if (pc->len > 1) {
8115 if (*++pc->p == '\n') {
8116 pc->linenr++;
8117 }
8118 pc->len--;
8119 tt = JIM_TT_ESC;
8120 }
8121 break;
8122
8123 case '"':
8124 pc->tend = pc->p - 1;
8125 pc->p++;
8126 pc->len--;
8127 return tt;
8128
8129 case '[':
8130 JimParseSubCmd(pc);
8131 tt = JIM_TT_ESC;
8132 continue;
8133
8134 case '\n':
8135 pc->linenr++;
8136 break;
8137
8138 case '$':
8139 tt = JIM_TT_ESC;
8140 break;
8141 }
8142 pc->p++;
8143 pc->len--;
8144 }
8145 pc->missing.ch = '"';
8146 pc->missing.line = line;
8147 pc->tend = pc->p - 1;
8148 return tt;
8149 }
8150
8151 static void JimParseSubCmd(struct JimParserCtx *pc)
8152 {
8153 int level = 1;
8154 int startofword = 1;
8155 int line = pc->tline;
8156
8157
8158 pc->p++;
8159 pc->len--;
8160 while (pc->len) {
8161 switch (*pc->p) {
8162 case '\\':
8163 if (pc->len > 1) {
8164 if (*++pc->p == '\n') {
8165 pc->linenr++;
8166 }
8167 pc->len--;
8168 }
8169 break;
8170
8171 case '[':
8172 level++;
8173 break;
8174
8175 case ']':
8176 if (--level == 0) {
8177 pc->tend = pc->p - 1;
8178 pc->p++;
8179 pc->len--;
8180 return;
8181 }
8182 break;
8183
8184 case '"':
8185 if (startofword) {
8186 JimParseSubQuote(pc);
8187 if (pc->missing.ch == '"') {
8188 return;
8189 }
8190 continue;
8191 }
8192 break;
8193
8194 case '{':
8195 JimParseSubBrace(pc);
8196 startofword = 0;
8197 continue;
8198
8199 case '\n':
8200 pc->linenr++;
8201 break;
8202 }
8203 startofword = isspace(UCHAR(*pc->p));
8204 pc->p++;
8205 pc->len--;
8206 }
8207 pc->missing.ch = '[';
8208 pc->missing.line = line;
8209 pc->tend = pc->p - 1;
8210 }
8211
8212 static int JimParseBrace(struct JimParserCtx *pc)
8213 {
8214 pc->tstart = pc->p + 1;
8215 pc->tline = pc->linenr;
8216 pc->tt = JIM_TT_STR;
8217 JimParseSubBrace(pc);
8218 return JIM_OK;
8219 }
8220
8221 static int JimParseCmd(struct JimParserCtx *pc)
8222 {
8223 pc->tstart = pc->p + 1;
8224 pc->tline = pc->linenr;
8225 pc->tt = JIM_TT_CMD;
8226 JimParseSubCmd(pc);
8227 return JIM_OK;
8228 }
8229
8230 static int JimParseQuote(struct JimParserCtx *pc)
8231 {
8232 pc->tstart = pc->p + 1;
8233 pc->tline = pc->linenr;
8234 pc->tt = JimParseSubQuote(pc);
8235 return JIM_OK;
8236 }
8237
8238 static int JimParseVar(struct JimParserCtx *pc)
8239 {
8240
8241 pc->p++;
8242 pc->len--;
8243
8244 #ifdef EXPRSUGAR_BRACKET
8245 if (*pc->p == '[') {
8246
8247 JimParseCmd(pc);
8248 pc->tt = JIM_TT_EXPRSUGAR;
8249 return JIM_OK;
8250 }
8251 #endif
8252
8253 pc->tstart = pc->p;
8254 pc->tt = JIM_TT_VAR;
8255 pc->tline = pc->linenr;
8256
8257 if (*pc->p == '{') {
8258 pc->tstart = ++pc->p;
8259 pc->len--;
8260
8261 while (pc->len && *pc->p != '}') {
8262 if (*pc->p == '\n') {
8263 pc->linenr++;
8264 }
8265 pc->p++;
8266 pc->len--;
8267 }
8268 pc->tend = pc->p - 1;
8269 if (pc->len) {
8270 pc->p++;
8271 pc->len--;
8272 }
8273 }
8274 else {
8275 while (1) {
8276
8277 if (pc->p[0] == ':' && pc->p[1] == ':') {
8278 while (*pc->p == ':') {
8279 pc->p++;
8280 pc->len--;
8281 }
8282 continue;
8283 }
8284 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
8285 pc->p++;
8286 pc->len--;
8287 continue;
8288 }
8289 break;
8290 }
8291
8292 if (*pc->p == '(') {
8293 int count = 1;
8294 const char *paren = NULL;
8295
8296 pc->tt = JIM_TT_DICTSUGAR;
8297
8298 while (count && pc->len) {
8299 pc->p++;
8300 pc->len--;
8301 if (*pc->p == '\\' && pc->len >= 1) {
8302 pc->p++;
8303 pc->len--;
8304 }
8305 else if (*pc->p == '(') {
8306 count++;
8307 }
8308 else if (*pc->p == ')') {
8309 paren = pc->p;
8310 count--;
8311 }
8312 }
8313 if (count == 0) {
8314 pc->p++;
8315 pc->len--;
8316 }
8317 else if (paren) {
8318
8319 paren++;
8320 pc->len += (pc->p - paren);
8321 pc->p = paren;
8322 }
8323 #ifndef EXPRSUGAR_BRACKET
8324 if (*pc->tstart == '(') {
8325 pc->tt = JIM_TT_EXPRSUGAR;
8326 }
8327 #endif
8328 }
8329 pc->tend = pc->p - 1;
8330 }
8331 if (pc->tstart == pc->p) {
8332 pc->p--;
8333 pc->len++;
8334 return JIM_ERR;
8335 }
8336 return JIM_OK;
8337 }
8338
8339 static int JimParseStr(struct JimParserCtx *pc)
8340 {
8341 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
8342 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
8343
8344 if (*pc->p == '{') {
8345 return JimParseBrace(pc);
8346 }
8347 if (*pc->p == '"') {
8348 pc->inquote = 1;
8349 pc->p++;
8350 pc->len--;
8351
8352 pc->missing.line = pc->tline;
8353 }
8354 }
8355 pc->tstart = pc->p;
8356 pc->tline = pc->linenr;
8357 while (1) {
8358 if (pc->len == 0) {
8359 if (pc->inquote) {
8360 pc->missing.ch = '"';
8361 }
8362 pc->tend = pc->p - 1;
8363 pc->tt = JIM_TT_ESC;
8364 return JIM_OK;
8365 }
8366 switch (*pc->p) {
8367 case '\\':
8368 if (!pc->inquote && *(pc->p + 1) == '\n') {
8369 pc->tend = pc->p - 1;
8370 pc->tt = JIM_TT_ESC;
8371 return JIM_OK;
8372 }
8373 if (pc->len >= 2) {
8374 if (*(pc->p + 1) == '\n') {
8375 pc->linenr++;
8376 }
8377 pc->p++;
8378 pc->len--;
8379 }
8380 else if (pc->len == 1) {
8381
8382 pc->missing.ch = '\\';
8383 }
8384 break;
8385 case '(':
8386
8387 if (pc->len > 1 && pc->p[1] != '$') {
8388 break;
8389 }
8390
8391 case ')':
8392
8393 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
8394 if (pc->p == pc->tstart) {
8395
8396 pc->p++;
8397 pc->len--;
8398 }
8399 pc->tend = pc->p - 1;
8400 pc->tt = JIM_TT_ESC;
8401 return JIM_OK;
8402 }
8403 break;
8404
8405 case '$':
8406 case '[':
8407 pc->tend = pc->p - 1;
8408 pc->tt = JIM_TT_ESC;
8409 return JIM_OK;
8410 case ' ':
8411 case '\t':
8412 case '\n':
8413 case '\r':
8414 case '\f':
8415 case ';':
8416 if (!pc->inquote) {
8417 pc->tend = pc->p - 1;
8418 pc->tt = JIM_TT_ESC;
8419 return JIM_OK;
8420 }
8421 else if (*pc->p == '\n') {
8422 pc->linenr++;
8423 }
8424 break;
8425 case '"':
8426 if (pc->inquote) {
8427 pc->tend = pc->p - 1;
8428 pc->tt = JIM_TT_ESC;
8429 pc->p++;
8430 pc->len--;
8431 pc->inquote = 0;
8432 return JIM_OK;
8433 }
8434 break;
8435 }
8436 pc->p++;
8437 pc->len--;
8438 }
8439 return JIM_OK;
8440 }
8441
8442 static int JimParseComment(struct JimParserCtx *pc)
8443 {
8444 while (*pc->p) {
8445 if (*pc->p == '\\') {
8446 pc->p++;
8447 pc->len--;
8448 if (pc->len == 0) {
8449 pc->missing.ch = '\\';
8450 return JIM_OK;
8451 }
8452 if (*pc->p == '\n') {
8453 pc->linenr++;
8454 }
8455 }
8456 else if (*pc->p == '\n') {
8457 pc->p++;
8458 pc->len--;
8459 pc->linenr++;
8460 break;
8461 }
8462 pc->p++;
8463 pc->len--;
8464 }
8465 return JIM_OK;
8466 }
8467
8468
8469 static int xdigitval(int c)
8470 {
8471 if (c >= '0' && c <= '9')
8472 return c - '0';
8473 if (c >= 'a' && c <= 'f')
8474 return c - 'a' + 10;
8475 if (c >= 'A' && c <= 'F')
8476 return c - 'A' + 10;
8477 return -1;
8478 }
8479
8480 static int odigitval(int c)
8481 {
8482 if (c >= '0' && c <= '7')
8483 return c - '0';
8484 return -1;
8485 }
8486
8487 static int JimEscape(char *dest, const char *s, int slen)
8488 {
8489 char *p = dest;
8490 int i, len;
8491
8492 for (i = 0; i < slen; i++) {
8493 switch (s[i]) {
8494 case '\\':
8495 switch (s[i + 1]) {
8496 case 'a':
8497 *p++ = 0x7;
8498 i++;
8499 break;
8500 case 'b':
8501 *p++ = 0x8;
8502 i++;
8503 break;
8504 case 'f':
8505 *p++ = 0xc;
8506 i++;
8507 break;
8508 case 'n':
8509 *p++ = 0xa;
8510 i++;
8511 break;
8512 case 'r':
8513 *p++ = 0xd;
8514 i++;
8515 break;
8516 case 't':
8517 *p++ = 0x9;
8518 i++;
8519 break;
8520 case 'u':
8521 case 'U':
8522 case 'x':
8523 {
8524 unsigned val = 0;
8525 int k;
8526 int maxchars = 2;
8527
8528 i++;
8529
8530 if (s[i] == 'U') {
8531 maxchars = 8;
8532 }
8533 else if (s[i] == 'u') {
8534 if (s[i + 1] == '{') {
8535 maxchars = 6;
8536 i++;
8537 }
8538 else {
8539 maxchars = 4;
8540 }
8541 }
8542
8543 for (k = 0; k < maxchars; k++) {
8544 int c = xdigitval(s[i + k + 1]);
8545 if (c == -1) {
8546 break;
8547 }
8548 val = (val << 4) | c;
8549 }
8550
8551 if (s[i] == '{') {
8552 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
8553
8554 i--;
8555 k = 0;
8556 }
8557 else {
8558
8559 k++;
8560 }
8561 }
8562 if (k) {
8563
8564 if (s[i] == 'x') {
8565 *p++ = val;
8566 }
8567 else {
8568 p += utf8_fromunicode(p, val);
8569 }
8570 i += k;
8571 break;
8572 }
8573
8574 *p++ = s[i];
8575 }
8576 break;
8577 case 'v':
8578 *p++ = 0xb;
8579 i++;
8580 break;
8581 case '\0':
8582 *p++ = '\\';
8583 i++;
8584 break;
8585 case '\n':
8586
8587 *p++ = ' ';
8588 do {
8589 i++;
8590 } while (s[i + 1] == ' ' || s[i + 1] == '\t');
8591 break;
8592 case '0':
8593 case '1':
8594 case '2':
8595 case '3':
8596 case '4':
8597 case '5':
8598 case '6':
8599 case '7':
8600
8601 {
8602 int val = 0;
8603 int c = odigitval(s[i + 1]);
8604
8605 val = c;
8606 c = odigitval(s[i + 2]);
8607 if (c == -1) {
8608 *p++ = val;
8609 i++;
8610 break;
8611 }
8612 val = (val * 8) + c;
8613 c = odigitval(s[i + 3]);
8614 if (c == -1) {
8615 *p++ = val;
8616 i += 2;
8617 break;
8618 }
8619 val = (val * 8) + c;
8620 *p++ = val;
8621 i += 3;
8622 }
8623 break;
8624 default:
8625 *p++ = s[i + 1];
8626 i++;
8627 break;
8628 }
8629 break;
8630 default:
8631 *p++ = s[i];
8632 break;
8633 }
8634 }
8635 len = p - dest;
8636 *p = '\0';
8637 return len;
8638 }
8639
8640 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
8641 {
8642 const char *start, *end;
8643 char *token;
8644 int len;
8645
8646 start = pc->tstart;
8647 end = pc->tend;
8648 len = (end - start) + 1;
8649 if (len < 0) {
8650 len = 0;
8651 }
8652 token = Jim_Alloc(len + 1);
8653 if (pc->tt != JIM_TT_ESC) {
8654
8655 memcpy(token, start, len);
8656 token[len] = '\0';
8657 }
8658 else {
8659
8660 len = JimEscape(token, start, len);
8661 }
8662
8663 return Jim_NewStringObjNoAlloc(interp, token, len);
8664 }
8665
8666 static int JimParseListSep(struct JimParserCtx *pc);
8667 static int JimParseListStr(struct JimParserCtx *pc);
8668 static int JimParseListQuote(struct JimParserCtx *pc);
8669
8670 static int JimParseList(struct JimParserCtx *pc)
8671 {
8672 if (isspace(UCHAR(*pc->p))) {
8673 return JimParseListSep(pc);
8674 }
8675 switch (*pc->p) {
8676 case '"':
8677 return JimParseListQuote(pc);
8678
8679 case '{':
8680 return JimParseBrace(pc);
8681
8682 default:
8683 if (pc->len) {
8684 return JimParseListStr(pc);
8685 }
8686 break;
8687 }
8688
8689 pc->tstart = pc->tend = pc->p;
8690 pc->tline = pc->linenr;
8691 pc->tt = JIM_TT_EOL;
8692 pc->eof = 1;
8693 return JIM_OK;
8694 }
8695
8696 static int JimParseListSep(struct JimParserCtx *pc)
8697 {
8698 pc->tstart = pc->p;
8699 pc->tline = pc->linenr;
8700 while (isspace(UCHAR(*pc->p))) {
8701 if (*pc->p == '\n') {
8702 pc->linenr++;
8703 }
8704 pc->p++;
8705 pc->len--;
8706 }
8707 pc->tend = pc->p - 1;
8708 pc->tt = JIM_TT_SEP;
8709 return JIM_OK;
8710 }
8711
8712 static int JimParseListQuote(struct JimParserCtx *pc)
8713 {
8714 pc->p++;
8715 pc->len--;
8716
8717 pc->tstart = pc->p;
8718 pc->tline = pc->linenr;
8719 pc->tt = JIM_TT_STR;
8720
8721 while (pc->len) {
8722 switch (*pc->p) {
8723 case '\\':
8724 pc->tt = JIM_TT_ESC;
8725 if (--pc->len == 0) {
8726
8727 pc->tend = pc->p;
8728 return JIM_OK;
8729 }
8730 pc->p++;
8731 break;
8732 case '\n':
8733 pc->linenr++;
8734 break;
8735 case '"':
8736 pc->tend = pc->p - 1;
8737 pc->p++;
8738 pc->len--;
8739 return JIM_OK;
8740 }
8741 pc->p++;
8742 pc->len--;
8743 }
8744
8745 pc->tend = pc->p - 1;
8746 return JIM_OK;
8747 }
8748
8749 static int JimParseListStr(struct JimParserCtx *pc)
8750 {
8751 pc->tstart = pc->p;
8752 pc->tline = pc->linenr;
8753 pc->tt = JIM_TT_STR;
8754
8755 while (pc->len) {
8756 if (isspace(UCHAR(*pc->p))) {
8757 pc->tend = pc->p - 1;
8758 return JIM_OK;
8759 }
8760 if (*pc->p == '\\') {
8761 if (--pc->len == 0) {
8762
8763 pc->tend = pc->p;
8764 return JIM_OK;
8765 }
8766 pc->tt = JIM_TT_ESC;
8767 pc->p++;
8768 }
8769 pc->p++;
8770 pc->len--;
8771 }
8772 pc->tend = pc->p - 1;
8773 return JIM_OK;
8774 }
8775
8776
8777
8778 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
8779 {
8780 Jim_Obj *objPtr;
8781
8782
8783 if (interp->freeList != NULL) {
8784
8785 objPtr = interp->freeList;
8786 interp->freeList = objPtr->nextObjPtr;
8787 }
8788 else {
8789
8790 objPtr = Jim_Alloc(sizeof(*objPtr));
8791 }
8792
8793 objPtr->refCount = 0;
8794
8795
8796 objPtr->prevObjPtr = NULL;
8797 objPtr->nextObjPtr = interp->liveList;
8798 if (interp->liveList)
8799 interp->liveList->prevObjPtr = objPtr;
8800 interp->liveList = objPtr;
8801
8802 return objPtr;
8803 }
8804
8805 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
8806 {
8807
8808 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
8809 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
8810
8811
8812 Jim_FreeIntRep(interp, objPtr);
8813
8814 if (objPtr->bytes != NULL) {
8815 if (objPtr->bytes != JimEmptyStringRep)
8816 Jim_Free(objPtr->bytes);
8817 }
8818
8819 if (objPtr->prevObjPtr)
8820 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
8821 if (objPtr->nextObjPtr)
8822 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
8823 if (interp->liveList == objPtr)
8824 interp->liveList = objPtr->nextObjPtr;
8825 #ifdef JIM_DISABLE_OBJECT_POOL
8826 Jim_Free(objPtr);
8827 #else
8828
8829 objPtr->prevObjPtr = NULL;
8830 objPtr->nextObjPtr = interp->freeList;
8831 if (interp->freeList)
8832 interp->freeList->prevObjPtr = objPtr;
8833 interp->freeList = objPtr;
8834 objPtr->refCount = -1;
8835 #endif
8836 }
8837
8838
8839 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
8840 {
8841 if (objPtr->bytes != NULL) {
8842 if (objPtr->bytes != JimEmptyStringRep)
8843 Jim_Free(objPtr->bytes);
8844 }
8845 objPtr->bytes = NULL;
8846 }
8847
8848
8849 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
8850 {
8851 Jim_Obj *dupPtr;
8852
8853 dupPtr = Jim_NewObj(interp);
8854 if (objPtr->bytes == NULL) {
8855
8856 dupPtr->bytes = NULL;
8857 }
8858 else if (objPtr->length == 0) {
8859 dupPtr->bytes = JimEmptyStringRep;
8860 dupPtr->length = 0;
8861 dupPtr->typePtr = NULL;
8862 return dupPtr;
8863 }
8864 else {
8865 dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
8866 dupPtr->length = objPtr->length;
8867
8868 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
8869 }
8870
8871
8872 dupPtr->typePtr = objPtr->typePtr;
8873 if (objPtr->typePtr != NULL) {
8874 if (objPtr->typePtr->dupIntRepProc == NULL) {
8875 dupPtr->internalRep = objPtr->internalRep;
8876 }
8877 else {
8878
8879 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
8880 }
8881 }
8882 return dupPtr;
8883 }
8884
8885 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
8886 {
8887 if (objPtr->bytes == NULL) {
8888
8889 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8890 objPtr->typePtr->updateStringProc(objPtr);
8891 }
8892 if (lenPtr)
8893 *lenPtr = objPtr->length;
8894 return objPtr->bytes;
8895 }
8896
8897
8898 int Jim_Length(Jim_Obj *objPtr)
8899 {
8900 if (objPtr->bytes == NULL) {
8901
8902 Jim_GetString(objPtr, NULL);
8903 }
8904 return objPtr->length;
8905 }
8906
8907
8908 const char *Jim_String(Jim_Obj *objPtr)
8909 {
8910 if (objPtr->bytes == NULL) {
8911
8912 Jim_GetString(objPtr, NULL);
8913 }
8914 return objPtr->bytes;
8915 }
8916
8917 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
8918 {
8919 objPtr->bytes = Jim_StrDup(str);
8920 objPtr->length = strlen(str);
8921 }
8922
8923 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8924 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8925
8926 static const Jim_ObjType dictSubstObjType = {
8927 "dict-substitution",
8928 FreeDictSubstInternalRep,
8929 DupDictSubstInternalRep,
8930 NULL,
8931 JIM_TYPE_NONE,
8932 };
8933
8934 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8935 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8936
8937 static const Jim_ObjType interpolatedObjType = {
8938 "interpolated",
8939 FreeInterpolatedInternalRep,
8940 DupInterpolatedInternalRep,
8941 NULL,
8942 JIM_TYPE_NONE,
8943 };
8944
8945 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8946 {
8947 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8948 }
8949
8950 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8951 {
8952
8953 dupPtr->internalRep = srcPtr->internalRep;
8954
8955 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
8956 }
8957
8958 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8959 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8960
8961 static const Jim_ObjType stringObjType = {
8962 "string",
8963 NULL,
8964 DupStringInternalRep,
8965 NULL,
8966 JIM_TYPE_REFERENCES,
8967 };
8968
8969 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8970 {
8971 JIM_NOTUSED(interp);
8972
8973 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8974 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8975 }
8976
8977 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8978 {
8979 if (objPtr->typePtr != &stringObjType) {
8980
8981 if (objPtr->bytes == NULL) {
8982
8983 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8984 objPtr->typePtr->updateStringProc(objPtr);
8985 }
8986
8987 Jim_FreeIntRep(interp, objPtr);
8988
8989 objPtr->typePtr = &stringObjType;
8990 objPtr->internalRep.strValue.maxLength = objPtr->length;
8991
8992 objPtr->internalRep.strValue.charLength = -1;
8993 }
8994 return JIM_OK;
8995 }
8996
8997 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8998 {
8999 #ifdef JIM_UTF8
9000 SetStringFromAny(interp, objPtr);
9001
9002 if (objPtr->internalRep.strValue.charLength < 0) {
9003 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
9004 }
9005 return objPtr->internalRep.strValue.charLength;
9006 #else
9007 return Jim_Length(objPtr);
9008 #endif
9009 }
9010
9011
9012 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
9013 {
9014 Jim_Obj *objPtr = Jim_NewObj(interp);
9015
9016
9017 if (len == -1)
9018 len = strlen(s);
9019
9020 if (len == 0) {
9021 objPtr->bytes = JimEmptyStringRep;
9022 }
9023 else {
9024 objPtr->bytes = Jim_StrDupLen(s, len);
9025 }
9026 objPtr->length = len;
9027
9028
9029 objPtr->typePtr = NULL;
9030 return objPtr;
9031 }
9032
9033
9034 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
9035 {
9036 #ifdef JIM_UTF8
9037
9038 int bytelen = utf8_index(s, charlen);
9039
9040 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
9041
9042
9043 objPtr->typePtr = &stringObjType;
9044 objPtr->internalRep.strValue.maxLength = bytelen;
9045 objPtr->internalRep.strValue.charLength = charlen;
9046
9047 return objPtr;
9048 #else
9049 return Jim_NewStringObj(interp, s, charlen);
9050 #endif
9051 }
9052
9053 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
9054 {
9055 Jim_Obj *objPtr = Jim_NewObj(interp);
9056
9057 objPtr->bytes = s;
9058 objPtr->length = (len == -1) ? strlen(s) : len;
9059 objPtr->typePtr = NULL;
9060 return objPtr;
9061 }
9062
9063 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
9064 {
9065 int needlen;
9066
9067 if (len == -1)
9068 len = strlen(str);
9069 needlen = objPtr->length + len;
9070 if (objPtr->internalRep.strValue.maxLength < needlen ||
9071 objPtr->internalRep.strValue.maxLength == 0) {
9072 needlen *= 2;
9073
9074 if (needlen < 7) {
9075 needlen = 7;
9076 }
9077 if (objPtr->bytes == JimEmptyStringRep) {
9078 objPtr->bytes = Jim_Alloc(needlen + 1);
9079 }
9080 else {
9081 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
9082 }
9083 objPtr->internalRep.strValue.maxLength = needlen;
9084 }
9085 memcpy(objPtr->bytes + objPtr->length, str, len);
9086 objPtr->bytes[objPtr->length + len] = '\0';
9087
9088 if (objPtr->internalRep.strValue.charLength >= 0) {
9089
9090 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
9091 }
9092 objPtr->length += len;
9093 }
9094
9095 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
9096 {
9097 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
9098 SetStringFromAny(interp, objPtr);
9099 StringAppendString(objPtr, str, len);
9100 }
9101
9102 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
9103 {
9104 int len;
9105 const char *str = Jim_GetString(appendObjPtr, &len);
9106 Jim_AppendString(interp, objPtr, str, len);
9107 }
9108
9109 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
9110 {
9111 va_list ap;
9112
9113 SetStringFromAny(interp, objPtr);
9114 va_start(ap, objPtr);
9115 while (1) {
9116 const char *s = va_arg(ap, const char *);
9117
9118 if (s == NULL)
9119 break;
9120 Jim_AppendString(interp, objPtr, s, -1);
9121 }
9122 va_end(ap);
9123 }
9124
9125 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
9126 {
9127 if (aObjPtr == bObjPtr) {
9128 return 1;
9129 }
9130 else {
9131 int Alen, Blen;
9132 const char *sA = Jim_GetString(aObjPtr, &Alen);
9133 const char *sB = Jim_GetString(bObjPtr, &Blen);
9134
9135 return Alen == Blen && memcmp(sA, sB, Alen) == 0;
9136 }
9137 }
9138
9139 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
9140 {
9141 int plen, slen;
9142 const char *pattern = Jim_GetString(patternObjPtr, &plen);
9143 const char *string = Jim_GetString(objPtr, &slen);
9144 return JimGlobMatch(pattern, plen, string, slen, nocase);
9145 }
9146
9147 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
9148 {
9149 const char *s1 = Jim_String(firstObjPtr);
9150 int l1 = Jim_Utf8Length(interp, firstObjPtr);
9151 const char *s2 = Jim_String(secondObjPtr);
9152 int l2 = Jim_Utf8Length(interp, secondObjPtr);
9153 return JimStringCompareUtf8(s1, l1, s2, l2, nocase);
9154 }
9155
9156 static int JimRelToAbsIndex(int len, int idx)
9157 {
9158 if (idx < 0 && idx > -INT_MAX)
9159 return len + idx;
9160 return idx;
9161 }
9162
9163 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
9164 {
9165 int rangeLen;
9166
9167 if (*firstPtr > *lastPtr) {
9168 rangeLen = 0;
9169 }
9170 else {
9171 rangeLen = *lastPtr - *firstPtr + 1;
9172 if (rangeLen) {
9173 if (*firstPtr < 0) {
9174 rangeLen += *firstPtr;
9175 *firstPtr = 0;
9176 }
9177 if (*lastPtr >= len) {
9178 rangeLen -= (*lastPtr - (len - 1));
9179 *lastPtr = len - 1;
9180 }
9181 }
9182 }
9183 if (rangeLen < 0)
9184 rangeLen = 0;
9185
9186 *rangeLenPtr = rangeLen;
9187 }
9188
9189 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
9190 int len, int *first, int *last, int *range)
9191 {
9192 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
9193 return JIM_ERR;
9194 }
9195 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
9196 return JIM_ERR;
9197 }
9198 *first = JimRelToAbsIndex(len, *first);
9199 *last = JimRelToAbsIndex(len, *last);
9200 JimRelToAbsRange(len, first, last, range);
9201 return JIM_OK;
9202 }
9203
9204 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
9205 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
9206 {
9207 int first, last;
9208 const char *str;
9209 int rangeLen;
9210 int bytelen;
9211
9212 str = Jim_GetString(strObjPtr, &bytelen);
9213
9214 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
9215 return NULL;
9216 }
9217
9218 if (first == 0 && rangeLen == bytelen) {
9219 return strObjPtr;
9220 }
9221 return Jim_NewStringObj(interp, str + first, rangeLen);
9222 }
9223
9224 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
9225 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
9226 {
9227 #ifdef JIM_UTF8
9228 int first, last;
9229 const char *str;
9230 int len, rangeLen;
9231 int bytelen;
9232
9233 str = Jim_GetString(strObjPtr, &bytelen);
9234 len = Jim_Utf8Length(interp, strObjPtr);
9235
9236 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
9237 return NULL;
9238 }
9239
9240 if (first == 0 && rangeLen == len) {
9241 return strObjPtr;
9242 }
9243 if (len == bytelen) {
9244
9245 return Jim_NewStringObj(interp, str + first, rangeLen);
9246 }
9247 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
9248 #else
9249 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
9250 #endif
9251 }
9252
9253 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
9254 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
9255 {
9256 int first, last;
9257 const char *str;
9258 int len, rangeLen;
9259 Jim_Obj *objPtr;
9260
9261 len = Jim_Utf8Length(interp, strObjPtr);
9262
9263 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
9264 return NULL;
9265 }
9266
9267 if (last < first) {
9268 return strObjPtr;
9269 }
9270
9271 str = Jim_String(strObjPtr);
9272
9273
9274 objPtr = Jim_NewStringObjUtf8(interp, str, first);
9275
9276
9277 if (newStrObj) {
9278 Jim_AppendObj(interp, objPtr, newStrObj);
9279 }
9280
9281
9282 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
9283
9284 return objPtr;
9285 }
9286
9287 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
9288 {
9289 while (*str) {
9290 int c;
9291 str += utf8_tounicode(str, &c);
9292 dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
9293 }
9294 *dest = 0;
9295 }
9296
9297 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
9298 {
9299 char *buf;
9300 int len;
9301 const char *str;
9302
9303 str = Jim_GetString(strObjPtr, &len);
9304
9305 #ifdef JIM_UTF8
9306 len *= 2;
9307 #endif
9308 buf = Jim_Alloc(len + 1);
9309 JimStrCopyUpperLower(buf, str, 0);
9310 return Jim_NewStringObjNoAlloc(interp, buf, -1);
9311 }
9312
9313 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
9314 {
9315 char *buf;
9316 const char *str;
9317 int len;
9318
9319 str = Jim_GetString(strObjPtr, &len);
9320
9321 #ifdef JIM_UTF8
9322 len *= 2;
9323 #endif
9324 buf = Jim_Alloc(len + 1);
9325 JimStrCopyUpperLower(buf, str, 1);
9326 return Jim_NewStringObjNoAlloc(interp, buf, -1);
9327 }
9328
9329 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
9330 {
9331 char *buf, *p;
9332 int len;
9333 int c;
9334 const char *str;
9335
9336 str = Jim_GetString(strObjPtr, &len);
9337
9338 #ifdef JIM_UTF8
9339 len *= 2;
9340 #endif
9341 buf = p = Jim_Alloc(len + 1);
9342
9343 str += utf8_tounicode(str, &c);
9344 p += utf8_getchars(p, utf8_title(c));
9345
9346 JimStrCopyUpperLower(p, str, 0);
9347
9348 return Jim_NewStringObjNoAlloc(interp, buf, -1);
9349 }
9350
9351 static const char *utf8_memchr(const char *str, int len, int c)
9352 {
9353 #ifdef JIM_UTF8
9354 while (len) {
9355 int sc;
9356 int n = utf8_tounicode(str, &sc);
9357 if (sc == c) {
9358 return str;
9359 }
9360 str += n;
9361 len -= n;
9362 }
9363 return NULL;
9364 #else
9365 return memchr(str, c, len);
9366 #endif
9367 }
9368
9369 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
9370 {
9371 while (len) {
9372 int c;
9373 int n = utf8_tounicode(str, &c);
9374
9375 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
9376
9377 break;
9378 }
9379 str += n;
9380 len -= n;
9381 }
9382 return str;
9383 }
9384
9385 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
9386 {
9387 str += len;
9388
9389 while (len) {
9390 int c;
9391 int n = utf8_prev_len(str, len);
9392
9393 len -= n;
9394 str -= n;
9395
9396 n = utf8_tounicode(str, &c);
9397
9398 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
9399 return str + n;
9400 }
9401 }
9402
9403 return NULL;
9404 }
9405
9406 static const char default_trim_chars[] = " \t\n\r";
9407
9408 static int default_trim_chars_len = sizeof(default_trim_chars);
9409
9410 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9411 {
9412 int len;
9413 const char *str = Jim_GetString(strObjPtr, &len);
9414 const char *trimchars = default_trim_chars;
9415 int trimcharslen = default_trim_chars_len;
9416 const char *newstr;
9417
9418 if (trimcharsObjPtr) {
9419 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
9420 }
9421
9422 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
9423 if (newstr == str) {
9424 return strObjPtr;
9425 }
9426
9427 return Jim_NewStringObj(interp, newstr, len - (newstr - str));
9428 }
9429
9430 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9431 {
9432 int len;
9433 const char *trimchars = default_trim_chars;
9434 int trimcharslen = default_trim_chars_len;
9435 const char *nontrim;
9436
9437 if (trimcharsObjPtr) {
9438 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
9439 }
9440
9441 SetStringFromAny(interp, strObjPtr);
9442
9443 len = Jim_Length(strObjPtr);
9444 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
9445
9446 if (nontrim == NULL) {
9447
9448 return Jim_NewEmptyStringObj(interp);
9449 }
9450 if (nontrim == strObjPtr->bytes + len) {
9451
9452 return strObjPtr;
9453 }
9454
9455 if (Jim_IsShared(strObjPtr)) {
9456 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
9457 }
9458 else {
9459
9460 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
9461 strObjPtr->length = (nontrim - strObjPtr->bytes);
9462 }
9463
9464 return strObjPtr;
9465 }
9466
9467 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9468 {
9469
9470 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
9471
9472
9473 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
9474
9475
9476 if (objPtr != strObjPtr && objPtr->refCount == 0) {
9477
9478 Jim_FreeNewObj(interp, objPtr);
9479 }
9480
9481 return strObjPtr;
9482 }
9483
9484
9485 #ifdef HAVE_ISASCII
9486 #define jim_isascii isascii
9487 #else
9488 static int jim_isascii(int c)
9489 {
9490 return !(c & ~0x7f);
9491 }
9492 #endif
9493
9494 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
9495 {
9496 static const char * const strclassnames[] = {
9497 "integer", "alpha", "alnum", "ascii", "digit",
9498 "double", "lower", "upper", "space", "xdigit",
9499 "control", "print", "graph", "punct", "boolean",
9500 NULL
9501 };
9502 enum {
9503 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
9504 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
9505 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
9506 };
9507 int strclass;
9508 int len;
9509 int i;
9510 const char *str;
9511 int (*isclassfunc)(int c) = NULL;
9512
9513 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
9514 return JIM_ERR;
9515 }
9516
9517 str = Jim_GetString(strObjPtr, &len);
9518 if (len == 0) {
9519 Jim_SetResultBool(interp, !strict);
9520 return JIM_OK;
9521 }
9522
9523 switch (strclass) {
9524 case STR_IS_INTEGER:
9525 {
9526 jim_wide w;
9527 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
9528 return JIM_OK;
9529 }
9530
9531 case STR_IS_DOUBLE:
9532 {
9533 double d;
9534 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
9535 return JIM_OK;
9536 }
9537
9538 case STR_IS_BOOLEAN:
9539 {
9540 int b;
9541 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
9542 return JIM_OK;
9543 }
9544
9545 case STR_IS_ALPHA: isclassfunc = isalpha; break;
9546 case STR_IS_ALNUM: isclassfunc = isalnum; break;
9547 case STR_IS_ASCII: isclassfunc = jim_isascii; break;
9548 case STR_IS_DIGIT: isclassfunc = isdigit; break;
9549 case STR_IS_LOWER: isclassfunc = islower; break;
9550 case STR_IS_UPPER: isclassfunc = isupper; break;
9551 case STR_IS_SPACE: isclassfunc = isspace; break;
9552 case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
9553 case STR_IS_CONTROL: isclassfunc = iscntrl; break;
9554 case STR_IS_PRINT: isclassfunc = isprint; break;
9555 case STR_IS_GRAPH: isclassfunc = isgraph; break;
9556 case STR_IS_PUNCT: isclassfunc = ispunct; break;
9557 default:
9558 return JIM_ERR;
9559 }
9560
9561 for (i = 0; i < len; i++) {
9562 if (!isclassfunc(UCHAR(str[i]))) {
9563 Jim_SetResultBool(interp, 0);
9564 return JIM_OK;
9565 }
9566 }
9567 Jim_SetResultBool(interp, 1);
9568 return JIM_OK;
9569 }
9570
9571
9572
9573 static const Jim_ObjType comparedStringObjType = {
9574 "compared-string",
9575 NULL,
9576 NULL,
9577 NULL,
9578 JIM_TYPE_REFERENCES,
9579 };
9580
9581 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
9582 {
9583 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
9584 return 1;
9585 }
9586 else {
9587 if (strcmp(str, Jim_String(objPtr)) != 0)
9588 return 0;
9589
9590 if (objPtr->typePtr != &comparedStringObjType) {
9591 Jim_FreeIntRep(interp, objPtr);
9592 objPtr->typePtr = &comparedStringObjType;
9593 }
9594 objPtr->internalRep.ptr = (char *)str;
9595 return 1;
9596 }
9597 }
9598
9599 static int qsortCompareStringPointers(const void *a, const void *b)
9600 {
9601 char *const *sa = (char *const *)a;
9602 char *const *sb = (char *const *)b;
9603
9604 return strcmp(*sa, *sb);
9605 }
9606
9607
9608
9609 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
9610 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
9611
9612 static const Jim_ObjType sourceObjType = {
9613 "source",
9614 FreeSourceInternalRep,
9615 DupSourceInternalRep,
9616 NULL,
9617 JIM_TYPE_REFERENCES,
9618 };
9619
9620 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9621 {
9622 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
9623 }
9624
9625 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9626 {
9627 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
9628 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
9629 }
9630
9631 static const Jim_ObjType scriptLineObjType = {
9632 "scriptline",
9633 NULL,
9634 NULL,
9635 NULL,
9636 JIM_NONE,
9637 };
9638
9639 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
9640 {
9641 Jim_Obj *objPtr;
9642
9643 #ifdef DEBUG_SHOW_SCRIPT
9644 char buf[100];
9645 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
9646 objPtr = Jim_NewStringObj(interp, buf, -1);
9647 #else
9648 objPtr = Jim_NewEmptyStringObj(interp);
9649 #endif
9650 objPtr->typePtr = &scriptLineObjType;
9651 objPtr->internalRep.scriptLineValue.argc = argc;
9652 objPtr->internalRep.scriptLineValue.line = line;
9653
9654 return objPtr;
9655 }
9656
9657 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
9658 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
9659
9660 static const Jim_ObjType scriptObjType = {
9661 "script",
9662 FreeScriptInternalRep,
9663 DupScriptInternalRep,
9664 NULL,
9665 JIM_TYPE_NONE,
9666 };
9667
9668 typedef struct ScriptToken
9669 {
9670 Jim_Obj *objPtr;
9671 int type;
9672 } ScriptToken;
9673
9674 typedef struct ScriptObj
9675 {
9676 ScriptToken *token;
9677 Jim_Obj *fileNameObj;
9678 int len;
9679 int substFlags;
9680 int inUse; /* Used to share a ScriptObj. Currently
9681 only used by Jim_EvalObj() as protection against
9682 shimmering of the currently evaluated object. */
9683 int firstline;
9684 int linenr;
9685 int missing;
9686 } ScriptObj;
9687
9688 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9689 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
9690 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
9691 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script);
9692
9693 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9694 {
9695 int i;
9696 struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
9697
9698 if (--script->inUse != 0)
9699 return;
9700 for (i = 0; i < script->len; i++) {
9701 Jim_DecrRefCount(interp, script->token[i].objPtr);
9702 }
9703 Jim_Free(script->token);
9704 Jim_DecrRefCount(interp, script->fileNameObj);
9705 Jim_Free(script);
9706 }
9707
9708 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9709 {
9710 JIM_NOTUSED(interp);
9711 JIM_NOTUSED(srcPtr);
9712
9713 dupPtr->typePtr = NULL;
9714 }
9715
9716 typedef struct
9717 {
9718 const char *token;
9719 int len;
9720 int type;
9721 int line;
9722 } ParseToken;
9723
9724 typedef struct
9725 {
9726
9727 ParseToken *list;
9728 int size;
9729 int count;
9730 ParseToken static_list[20];
9731 } ParseTokenList;
9732
9733 static void ScriptTokenListInit(ParseTokenList *tokenlist)
9734 {
9735 tokenlist->list = tokenlist->static_list;
9736 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
9737 tokenlist->count = 0;
9738 }
9739
9740 static void ScriptTokenListFree(ParseTokenList *tokenlist)
9741 {
9742 if (tokenlist->list != tokenlist->static_list) {
9743 Jim_Free(tokenlist->list);
9744 }
9745 }
9746
9747 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
9748 int line)
9749 {
9750 ParseToken *t;
9751
9752 if (tokenlist->count == tokenlist->size) {
9753
9754 tokenlist->size *= 2;
9755 if (tokenlist->list != tokenlist->static_list) {
9756 tokenlist->list =
9757 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
9758 }
9759 else {
9760
9761 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
9762 memcpy(tokenlist->list, tokenlist->static_list,
9763 tokenlist->count * sizeof(*tokenlist->list));
9764 }
9765 }
9766 t = &tokenlist->list[tokenlist->count++];
9767 t->token = token;
9768 t->len = len;
9769 t->type = type;
9770 t->line = line;
9771 }
9772
9773 static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
9774 {
9775 int expand = 1;
9776 int count = 0;
9777
9778
9779 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
9780 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
9781
9782 expand = -1;
9783 t++;
9784 }
9785 else {
9786 if (script->missing == ' ') {
9787
9788 script->missing = '}';
9789 script->linenr = t[1].line;
9790 }
9791 }
9792 }
9793
9794
9795 while (!TOKEN_IS_SEP(t->type)) {
9796 t++;
9797 count++;
9798 }
9799
9800 return count * expand;
9801 }
9802
9803 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
9804 {
9805 Jim_Obj *objPtr;
9806
9807 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
9808
9809 int len = t->len;
9810 char *str = Jim_Alloc(len + 1);
9811 len = JimEscape(str, t->token, len);
9812 objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
9813 }
9814 else {
9815 objPtr = Jim_NewStringObj(interp, t->token, t->len);
9816 }
9817 return objPtr;
9818 }
9819
9820 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9821 ParseTokenList *tokenlist)
9822 {
9823 int i;
9824 struct ScriptToken *token;
9825
9826 int lineargs = 0;
9827
9828 ScriptToken *linefirst;
9829 int count;
9830 int linenr;
9831
9832 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
9833 printf("==== Tokens ====\n");
9834 for (i = 0; i < tokenlist->count; i++) {
9835 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
9836 tokenlist->list[i].len, tokenlist->list[i].token);
9837 }
9838 #endif
9839
9840
9841 count = tokenlist->count;
9842 for (i = 0; i < tokenlist->count; i++) {
9843 if (tokenlist->list[i].type == JIM_TT_EOL) {
9844 count++;
9845 }
9846 }
9847 linenr = script->firstline = tokenlist->list[0].line;
9848
9849 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
9850
9851
9852 linefirst = token++;
9853
9854 for (i = 0; i < tokenlist->count; ) {
9855
9856 int wordtokens;
9857
9858
9859 while (tokenlist->list[i].type == JIM_TT_SEP) {
9860 i++;
9861 }
9862
9863 wordtokens = JimCountWordTokens(script, tokenlist->list + i);
9864
9865 if (wordtokens == 0) {
9866
9867 if (lineargs) {
9868 linefirst->type = JIM_TT_LINE;
9869 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
9870 Jim_IncrRefCount(linefirst->objPtr);
9871
9872
9873 lineargs = 0;
9874 linefirst = token++;
9875 }
9876 i++;
9877 continue;
9878 }
9879 else if (wordtokens != 1) {
9880
9881 token->type = JIM_TT_WORD;
9882 token->objPtr = Jim_NewIntObj(interp, wordtokens);
9883 Jim_IncrRefCount(token->objPtr);
9884 token++;
9885 if (wordtokens < 0) {
9886
9887 i++;
9888 wordtokens = -wordtokens - 1;
9889 lineargs--;
9890 }
9891 }
9892
9893 if (lineargs == 0) {
9894
9895 linenr = tokenlist->list[i].line;
9896 }
9897 lineargs++;
9898
9899
9900 while (wordtokens--) {
9901 const ParseToken *t = &tokenlist->list[i++];
9902
9903 token->type = t->type;
9904 token->objPtr = JimMakeScriptObj(interp, t);
9905 Jim_IncrRefCount(token->objPtr);
9906
9907 Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
9908 token++;
9909 }
9910 }
9911
9912 if (lineargs == 0) {
9913 token--;
9914 }
9915
9916 script->len = token - script->token;
9917
9918 JimPanic((script->len >= count, "allocated script array is too short"));
9919
9920 #ifdef DEBUG_SHOW_SCRIPT
9921 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9922 for (i = 0; i < script->len; i++) {
9923 const ScriptToken *t = &script->token[i];
9924 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9925 }
9926 #endif
9927
9928 }
9929
9930 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9931 {
9932 ScriptObj *script = JimGetScript(interp, scriptObj);
9933 if (stateCharPtr) {
9934 *stateCharPtr = script->missing;
9935 }
9936 return script->missing == ' ' || script->missing == '}';
9937 }
9938
9939 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9940 {
9941 const char *msg;
9942
9943 switch (ch) {
9944 case '\\':
9945 case ' ':
9946 return JIM_OK;
9947
9948 case '[':
9949 msg = "unmatched \"[\"";
9950 break;
9951 case '{':
9952 msg = "missing close-brace";
9953 break;
9954 case '}':
9955 msg = "extra characters after close-brace";
9956 break;
9957 case '"':
9958 default:
9959 msg = "missing quote";
9960 break;
9961 }
9962
9963 Jim_SetResultString(interp, msg, -1);
9964 return JIM_ERR;
9965 }
9966
9967 Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr)
9968 {
9969 int line;
9970 Jim_Obj *fileNameObj;
9971
9972 if (objPtr->typePtr == &sourceObjType) {
9973 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9974 line = objPtr->internalRep.sourceValue.lineNumber;
9975 }
9976 else if (objPtr->typePtr == &scriptObjType) {
9977 ScriptObj *script = JimGetScript(interp, objPtr);
9978 fileNameObj = script->fileNameObj;
9979 line = script->firstline;
9980 }
9981 else {
9982 fileNameObj = interp->emptyObj;
9983 line = 1;
9984 }
9985 *lineptr = line;
9986 return fileNameObj;
9987 }
9988
9989 void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
9990 Jim_Obj *fileNameObj, int lineNumber)
9991 {
9992 JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object"));
9993 Jim_FreeIntRep(interp, objPtr);
9994 Jim_IncrRefCount(fileNameObj);
9995 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
9996 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
9997 objPtr->typePtr = &sourceObjType;
9998 }
9999
10000 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
10001 ParseTokenList *tokenlist)
10002 {
10003 int i;
10004 struct ScriptToken *token;
10005
10006 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
10007
10008 for (i = 0; i < tokenlist->count; i++) {
10009 const ParseToken *t = &tokenlist->list[i];
10010
10011
10012 token->type = t->type;
10013 token->objPtr = JimMakeScriptObj(interp, t);
10014 Jim_IncrRefCount(token->objPtr);
10015 token++;
10016 }
10017
10018 script->len = i;
10019 }
10020
10021 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
10022 {
10023 int scriptTextLen;
10024 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
10025 struct JimParserCtx parser;
10026 struct ScriptObj *script;
10027 ParseTokenList tokenlist;
10028 Jim_Obj *fileNameObj;
10029 int line;
10030
10031
10032 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
10033
10034
10035 ScriptTokenListInit(&tokenlist);
10036
10037 JimParserInit(&parser, scriptText, scriptTextLen, line);
10038 while (!parser.eof) {
10039 JimParseScript(&parser);
10040 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
10041 parser.tline);
10042 }
10043
10044
10045 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
10046
10047
10048 script = Jim_Alloc(sizeof(*script));
10049 memset(script, 0, sizeof(*script));
10050 script->inUse = 1;
10051 script->fileNameObj = fileNameObj;
10052 Jim_IncrRefCount(script->fileNameObj);
10053 script->missing = parser.missing.ch;
10054 script->linenr = parser.missing.line;
10055
10056 ScriptObjAddTokens(interp, script, &tokenlist);
10057
10058
10059 ScriptTokenListFree(&tokenlist);
10060
10061
10062 Jim_FreeIntRep(interp, objPtr);
10063 Jim_SetIntRepPtr(objPtr, script);
10064 objPtr->typePtr = &scriptObjType;
10065 }
10066
10067 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
10068 {
10069 if (objPtr == interp->emptyObj) {
10070
10071 objPtr = interp->nullScriptObj;
10072 }
10073
10074 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
10075 JimSetScriptFromAny(interp, objPtr);
10076 }
10077
10078 return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
10079 }
10080
10081 void Jim_InterpIncrProcEpoch(Jim_Interp *interp)
10082 {
10083 interp->procEpoch++;
10084
10085
10086 while (interp->oldCmdCache) {
10087 Jim_Cmd *next = interp->oldCmdCache->prevCmd;
10088 Jim_Free(interp->oldCmdCache);
10089 interp->oldCmdCache = next;
10090 }
10091 interp->oldCmdCacheSize = 0;
10092 }
10093
10094 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
10095 {
10096 cmdPtr->inUse++;
10097 }
10098
10099 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
10100 {
10101 if (--cmdPtr->inUse == 0) {
10102 if (cmdPtr->isproc) {
10103 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
10104 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
10105 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
10106 if (cmdPtr->u.proc.staticVars) {
10107 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
10108 Jim_Free(cmdPtr->u.proc.staticVars);
10109 }
10110 }
10111 else {
10112
10113 if (cmdPtr->u.native.delProc) {
10114 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
10115 }
10116 }
10117 if (cmdPtr->prevCmd) {
10118
10119 JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
10120 }
10121
10122 cmdPtr->prevCmd = interp->oldCmdCache;
10123 interp->oldCmdCache = cmdPtr;
10124 if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) {
10125 Jim_InterpIncrProcEpoch(interp);
10126 }
10127 }
10128 }
10129
10130 static void JimIncrVarRef(Jim_VarVal *vv)
10131 {
10132 vv->refCount++;
10133 }
10134
10135 static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv)
10136 {
10137 assert(vv->refCount > 0);
10138 if (--vv->refCount == 0) {
10139 if (vv->objPtr) {
10140 Jim_DecrRefCount(interp, vv->objPtr);
10141 }
10142 Jim_Free(vv);
10143 }
10144 }
10145
10146 static void JimVariablesHTValDestructor(void *interp, void *val)
10147 {
10148 JimDecrVarRef(interp, val);
10149 }
10150
10151 static unsigned int JimObjectHTHashFunction(const void *key)
10152 {
10153 Jim_Obj *keyObj = (Jim_Obj *)key;
10154 int length;
10155 const char *string;
10156
10157 #ifdef JIM_OPTIMIZATION
10158 if (JimIsWide(keyObj) && keyObj->bytes == NULL) {
10159
10160 jim_wide objValue = JimWideValue(keyObj);
10161 if (objValue > INT_MIN && objValue < INT_MAX) {
10162 unsigned result = 0;
10163 unsigned value = (unsigned)objValue;
10164
10165 if (objValue < 0) {
10166 value = (unsigned)-objValue;
10167 }
10168
10169
10170 do {
10171 result += (result << 3) + (value % 10 + '0');
10172 value /= 10;
10173 } while (value);
10174
10175 if (objValue < 0) {
10176 result += (result << 3) + '-';
10177 }
10178 return result;
10179 }
10180 }
10181 #endif
10182 string = Jim_GetString(keyObj, &length);
10183 return Jim_GenHashFunction((const unsigned char *)string, length);
10184 }
10185
10186 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
10187 {
10188 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
10189 }
10190
10191 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
10192 {
10193 Jim_IncrRefCount((Jim_Obj *)val);
10194 return (void *)val;
10195 }
10196
10197 static void JimObjectHTKeyValDestructor(void *interp, void *val)
10198 {
10199 Jim_DecrRefCount(interp, (Jim_Obj *)val);
10200 }
10201
10202
10203 static void *JimVariablesHTValDup(void *privdata, const void *val)
10204 {
10205 JimIncrVarRef((Jim_VarVal *)val);
10206 return (void *)val;
10207 }
10208
10209 static const Jim_HashTableType JimVariablesHashTableType = {
10210 JimObjectHTHashFunction,
10211 JimObjectHTKeyValDup,
10212 JimVariablesHTValDup,
10213 JimObjectHTKeyCompare,
10214 JimObjectHTKeyValDestructor,
10215 JimVariablesHTValDestructor
10216 };
10217
10218
10219 static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length)
10220 {
10221 int len;
10222 const char *str = Jim_GetString(objPtr, &len);
10223 if (len >= 2 && str[0] == ':' && str[1] == ':') {
10224 while (len && *str == ':') {
10225 len--;
10226 str++;
10227 }
10228 }
10229 *length = len;
10230 return str;
10231 }
10232
10233 static unsigned int JimCommandsHT_HashFunction(const void *key)
10234 {
10235 int len;
10236 const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len);
10237 return Jim_GenHashFunction((const unsigned char *)str, len);
10238 }
10239
10240 static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2)
10241 {
10242 int len1, len2;
10243 const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
10244 const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
10245 return len1 == len2 && memcmp(str1, str2, len1) == 0;
10246 }
10247
10248 static void JimCommandsHT_ValDestructor(void *interp, void *val)
10249 {
10250 JimDecrCmdRefCount(interp, val);
10251 }
10252
10253 static const Jim_HashTableType JimCommandsHashTableType = {
10254 JimCommandsHT_HashFunction,
10255 JimObjectHTKeyValDup,
10256 NULL,
10257 JimCommandsHT_KeyCompare,
10258 JimObjectHTKeyValDestructor,
10259 JimCommandsHT_ValDestructor
10260 };
10261
10262
10263
10264 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
10265 {
10266 #ifdef jim_ext_namespace
10267 Jim_Obj *resultObj;
10268
10269 const char *name = Jim_String(nameObjPtr);
10270 if (name[0] == ':' && name[1] == ':') {
10271 return nameObjPtr;
10272 }
10273 Jim_IncrRefCount(nameObjPtr);
10274 resultObj = Jim_NewStringObj(interp, "::", -1);
10275 Jim_AppendObj(interp, resultObj, nameObjPtr);
10276 Jim_DecrRefCount(interp, nameObjPtr);
10277
10278 return resultObj;
10279 #else
10280 return nameObjPtr;
10281 #endif
10282 }
10283
10284 static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr)
10285 {
10286 #ifdef jim_ext_namespace
10287 if (Jim_Length(interp->framePtr->nsObj)) {
10288 int len;
10289 const char *name = Jim_GetString(objPtr, &len);
10290 if (len < 2 || name[0] != ':' || name[1] != ':') {
10291
10292 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
10293 Jim_AppendStrings(interp, objPtr, "::", name, NULL);
10294 }
10295 }
10296 #endif
10297 Jim_IncrRefCount(objPtr);
10298 return objPtr;
10299 }
10300
10301 static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd)
10302 {
10303 JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name"));
10304
10305 if (interp->local) {
10306 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr);
10307 if (he) {
10308
10309 cmd->prevCmd = Jim_GetHashEntryVal(he);
10310 Jim_SetHashVal(&interp->commands, he, cmd);
10311
10312 Jim_InterpIncrProcEpoch(interp);
10313 return;
10314 }
10315 }
10316
10317
10318
10319 Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd);
10320 }
10321
10322 int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
10323 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
10324 {
10325 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
10326
10327
10328 memset(cmdPtr, 0, sizeof(*cmdPtr));
10329 cmdPtr->inUse = 1;
10330 cmdPtr->u.native.delProc = delProc;
10331 cmdPtr->u.native.cmdProc = cmdProc;
10332 cmdPtr->u.native.privData = privData;
10333
10334 Jim_IncrRefCount(cmdNameObj);
10335 JimCreateCommand(interp, cmdNameObj, cmdPtr);
10336 Jim_DecrRefCount(interp, cmdNameObj);
10337
10338 return JIM_OK;
10339 }
10340
10341
10342 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
10343 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
10344 {
10345 return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc);
10346 }
10347
10348 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
10349 {
10350 int len, i;
10351
10352 len = Jim_ListLength(interp, staticsListObjPtr);
10353 if (len == 0) {
10354 return JIM_OK;
10355 }
10356
10357 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
10358 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
10359 for (i = 0; i < len; i++) {
10360 Jim_Obj *initObjPtr = NULL;
10361 Jim_Obj *nameObjPtr;
10362 Jim_VarVal *vv = NULL;
10363 Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
10364 int subLen = Jim_ListLength(interp, objPtr);
10365 int byref = 0;
10366
10367
10368 if (subLen != 1 && subLen != 2) {
10369 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
10370 objPtr);
10371 return JIM_ERR;
10372 }
10373
10374 nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
10375
10376
10377 if (subLen == 1) {
10378 int len;
10379 const char *pt = Jim_GetString(nameObjPtr, &len);
10380 if (*pt == '&') {
10381
10382 nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1);
10383 byref = 1;
10384 }
10385 }
10386 Jim_IncrRefCount(nameObjPtr);
10387
10388 if (subLen == 1) {
10389 switch (SetVariableFromAny(interp, nameObjPtr)) {
10390 case JIM_DICT_SUGAR:
10391
10392 if (byref) {
10393 Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr);
10394 }
10395 else {
10396 Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr);
10397 }
10398 Jim_DecrRefCount(interp, nameObjPtr);
10399 return JIM_ERR;
10400
10401 case JIM_OK:
10402 if (byref) {
10403 vv = nameObjPtr->internalRep.varValue.vv;
10404 }
10405 else {
10406 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
10407 }
10408 break;
10409
10410 case JIM_ERR:
10411
10412 Jim_SetResultFormatted(interp,
10413 "variable for initialization of static \"%#s\" not found in the local context",
10414 nameObjPtr);
10415 Jim_DecrRefCount(interp, nameObjPtr);
10416 return JIM_ERR;
10417 }
10418 }
10419 else {
10420 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
10421 }
10422
10423 if (vv == NULL) {
10424 vv = Jim_Alloc(sizeof(*vv));
10425 vv->objPtr = initObjPtr;
10426 Jim_IncrRefCount(vv->objPtr);
10427 vv->linkFramePtr = NULL;
10428 vv->refCount = 0;
10429 }
10430
10431 if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) {
10432 Jim_SetResultFormatted(interp,
10433 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
10434 JimIncrVarRef(vv);
10435 JimDecrVarRef(interp, vv);
10436 Jim_DecrRefCount(interp, nameObjPtr);
10437 return JIM_ERR;
10438 }
10439
10440 Jim_DecrRefCount(interp, nameObjPtr);
10441 }
10442 return JIM_OK;
10443 }
10444
10445
10446 #ifdef jim_ext_namespace
10447 static const char *Jim_memrchr(const char *p, int c, int len)
10448 {
10449 int i;
10450 for (i = len; i > 0; i--) {
10451 if (p[i] == c) {
10452 return p + i;
10453 }
10454 }
10455 return NULL;
10456 }
10457 #endif
10458
10459 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr)
10460 {
10461 #ifdef jim_ext_namespace
10462 if (cmdPtr->isproc) {
10463 int len;
10464 const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len);
10465
10466 const char *pt = Jim_memrchr(cmdname, ':', len);
10467 if (pt && pt != cmdname && pt[-1] == ':') {
10468 pt++;
10469 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
10470 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2);
10471 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
10472
10473 Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname));
10474 if (Jim_FindHashEntry(&interp->commands, tempObj)) {
10475
10476 Jim_InterpIncrProcEpoch(interp);
10477 }
10478 Jim_FreeNewObj(interp, tempObj);
10479 }
10480 }
10481 #endif
10482 }
10483
10484 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
10485 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
10486 {
10487 Jim_Cmd *cmdPtr;
10488 int argListLen;
10489 int i;
10490
10491 argListLen = Jim_ListLength(interp, argListObjPtr);
10492
10493
10494 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
10495 assert(cmdPtr);
10496 memset(cmdPtr, 0, sizeof(*cmdPtr));
10497 cmdPtr->inUse = 1;
10498 cmdPtr->isproc = 1;
10499 cmdPtr->u.proc.argListObjPtr = argListObjPtr;
10500 cmdPtr->u.proc.argListLen = argListLen;
10501 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
10502 cmdPtr->u.proc.argsPos = -1;
10503 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
10504 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
10505 Jim_IncrRefCount(argListObjPtr);
10506 Jim_IncrRefCount(bodyObjPtr);
10507 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
10508
10509
10510 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
10511 goto err;
10512 }
10513
10514
10515
10516 for (i = 0; i < argListLen; i++) {
10517 Jim_Obj *argPtr;
10518 Jim_Obj *nameObjPtr;
10519 Jim_Obj *defaultObjPtr;
10520 int len;
10521
10522
10523 argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
10524 len = Jim_ListLength(interp, argPtr);
10525 if (len == 0) {
10526 Jim_SetResultString(interp, "argument with no name", -1);
10527 err:
10528 JimDecrCmdRefCount(interp, cmdPtr);
10529 return NULL;
10530 }
10531 if (len > 2) {
10532 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
10533 goto err;
10534 }
10535
10536 if (len == 2) {
10537
10538 nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
10539 defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
10540 }
10541 else {
10542
10543 nameObjPtr = argPtr;
10544 defaultObjPtr = NULL;
10545 }
10546
10547
10548 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
10549 if (cmdPtr->u.proc.argsPos >= 0) {
10550 Jim_SetResultString(interp, "'args' specified more than once", -1);
10551 goto err;
10552 }
10553 cmdPtr->u.proc.argsPos = i;
10554 }
10555 else {
10556 if (len == 2) {
10557 cmdPtr->u.proc.optArity++;
10558 }
10559 else {
10560 cmdPtr->u.proc.reqArity++;
10561 }
10562 }
10563
10564 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
10565 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
10566 }
10567
10568 return cmdPtr;
10569 }
10570
10571 int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj)
10572 {
10573 int ret = JIM_OK;
10574
10575 nameObj = JimQualifyName(interp, nameObj);
10576
10577 if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) {
10578 Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj);
10579 ret = JIM_ERR;
10580 }
10581 Jim_DecrRefCount(interp, nameObj);
10582
10583 return ret;
10584 }
10585
10586 int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj)
10587 {
10588 int ret = JIM_ERR;
10589 Jim_HashEntry *he;
10590 Jim_Cmd *cmdPtr;
10591
10592 if (Jim_Length(newNameObj) == 0) {
10593 return Jim_DeleteCommand(interp, oldNameObj);
10594 }
10595
10596
10597
10598 oldNameObj = JimQualifyName(interp, oldNameObj);
10599 newNameObj = JimQualifyName(interp, newNameObj);
10600
10601
10602 he = Jim_FindHashEntry(&interp->commands, oldNameObj);
10603 if (he == NULL) {
10604 Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj);
10605 }
10606 else if (Jim_FindHashEntry(&interp->commands, newNameObj)) {
10607 Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj);
10608 }
10609 else {
10610 cmdPtr = Jim_GetHashEntryVal(he);
10611 if (cmdPtr->prevCmd) {
10612 Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj);
10613 }
10614 else {
10615
10616 JimIncrCmdRefCount(cmdPtr);
10617 JimUpdateProcNamespace(interp, cmdPtr, newNameObj);
10618 Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr);
10619
10620
10621 Jim_DeleteHashEntry(&interp->commands, oldNameObj);
10622
10623
10624 Jim_InterpIncrProcEpoch(interp);
10625
10626 ret = JIM_OK;
10627 }
10628 }
10629
10630 Jim_DecrRefCount(interp, oldNameObj);
10631 Jim_DecrRefCount(interp, newNameObj);
10632
10633 return ret;
10634 }
10635
10636
10637 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10638 {
10639 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
10640 }
10641
10642 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10643 {
10644 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
10645 dupPtr->typePtr = srcPtr->typePtr;
10646 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
10647 }
10648
10649 static const Jim_ObjType commandObjType = {
10650 "command",
10651 FreeCommandInternalRep,
10652 DupCommandInternalRep,
10653 NULL,
10654 JIM_TYPE_REFERENCES,
10655 };
10656
10657 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10658 {
10659 Jim_Cmd *cmd;
10660
10661 if (objPtr->typePtr == &commandObjType
10662 && objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch
10663 #ifdef jim_ext_namespace
10664 && Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
10665 #endif
10666 && objPtr->internalRep.cmdValue.cmdPtr->inUse) {
10667
10668 cmd = objPtr->internalRep.cmdValue.cmdPtr;
10669 }
10670 else {
10671 Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr);
10672 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj);
10673 #ifdef jim_ext_namespace
10674 if (he == NULL && Jim_Length(interp->framePtr->nsObj)) {
10675 he = Jim_FindHashEntry(&interp->commands, objPtr);
10676 }
10677 #endif
10678 if (he == NULL) {
10679 if (flags & JIM_ERRMSG) {
10680 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
10681 }
10682 Jim_DecrRefCount(interp, qualifiedNameObj);
10683 return NULL;
10684 }
10685 cmd = Jim_GetHashEntryVal(he);
10686
10687 cmd->cmdNameObj = Jim_GetHashEntryKey(he);
10688
10689
10690 Jim_FreeIntRep(interp, objPtr);
10691 objPtr->typePtr = &commandObjType;
10692 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
10693 objPtr->internalRep.cmdValue.cmdPtr = cmd;
10694 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
10695 Jim_IncrRefCount(interp->framePtr->nsObj);
10696 Jim_DecrRefCount(interp, qualifiedNameObj);
10697 }
10698 while (cmd->u.proc.upcall) {
10699 cmd = cmd->prevCmd;
10700 }
10701 return cmd;
10702 }
10703
10704
10705
10706 static const Jim_ObjType variableObjType = {
10707 "variable",
10708 NULL,
10709 NULL,
10710 NULL,
10711 JIM_TYPE_REFERENCES,
10712 };
10713
10714 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
10715 {
10716 const char *varName;
10717 Jim_CallFrame *framePtr;
10718 int global;
10719 int len;
10720 Jim_VarVal *vv;
10721
10722
10723 if (objPtr->typePtr == &variableObjType) {
10724 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
10725 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
10726
10727 return JIM_OK;
10728 }
10729
10730 }
10731 else if (objPtr->typePtr == &dictSubstObjType) {
10732 return JIM_DICT_SUGAR;
10733 }
10734
10735 varName = Jim_GetString(objPtr, &len);
10736
10737
10738 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
10739 return JIM_DICT_SUGAR;
10740 }
10741
10742 if (varName[0] == ':' && varName[1] == ':') {
10743 while (*varName == ':') {
10744 varName++;
10745 len--;
10746 }
10747 global = 1;
10748 framePtr = interp->topFramePtr;
10749
10750 Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len);
10751 vv = JimFindVariable(&framePtr->vars, tempObj);
10752 Jim_FreeNewObj(interp, tempObj);
10753 }
10754 else {
10755 global = 0;
10756 framePtr = interp->framePtr;
10757
10758 vv = JimFindVariable(&framePtr->vars, objPtr);
10759 if (vv == NULL && framePtr->staticVars) {
10760
10761 vv = JimFindVariable(framePtr->staticVars, objPtr);
10762 }
10763 }
10764
10765 if (vv == NULL) {
10766 return JIM_ERR;
10767 }
10768
10769
10770 Jim_FreeIntRep(interp, objPtr);
10771 objPtr->typePtr = &variableObjType;
10772 objPtr->internalRep.varValue.callFrameId = framePtr->id;
10773 objPtr->internalRep.varValue.vv = vv;
10774 objPtr->internalRep.varValue.global = global;
10775 return JIM_OK;
10776 }
10777
10778
10779 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
10780 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
10781
10782 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv)
10783 {
10784 return Jim_AddHashEntry(ht, nameObjPtr, vv);
10785 }
10786
10787 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
10788 {
10789 Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr);
10790 if (he) {
10791 return (Jim_VarVal *)Jim_GetHashEntryVal(he);
10792 }
10793 return NULL;
10794 }
10795
10796 static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
10797 {
10798 return Jim_DeleteHashEntry(ht, nameObjPtr);
10799 }
10800
10801 static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
10802 {
10803 const char *name;
10804 Jim_CallFrame *framePtr;
10805 int global;
10806 int len;
10807
10808
10809 Jim_VarVal *vv = Jim_Alloc(sizeof(*vv));
10810
10811 vv->objPtr = valObjPtr;
10812 Jim_IncrRefCount(valObjPtr);
10813 vv->linkFramePtr = NULL;
10814 vv->refCount = 0;
10815
10816 name = Jim_GetString(nameObjPtr, &len);
10817 if (name[0] == ':' && name[1] == ':') {
10818 while (*name == ':') {
10819 name++;
10820 len--;
10821 }
10822 framePtr = interp->topFramePtr;
10823 global = 1;
10824 JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv);
10825 }
10826 else {
10827 framePtr = interp->framePtr;
10828 global = 0;
10829 JimSetNewVariable(&framePtr->vars, nameObjPtr, vv);
10830 }
10831
10832
10833 Jim_FreeIntRep(interp, nameObjPtr);
10834 nameObjPtr->typePtr = &variableObjType;
10835 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
10836 nameObjPtr->internalRep.varValue.vv = vv;
10837 nameObjPtr->internalRep.varValue.global = global;
10838
10839 return vv;
10840 }
10841
10842 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
10843 {
10844 int err;
10845 Jim_VarVal *vv;
10846
10847 switch (SetVariableFromAny(interp, nameObjPtr)) {
10848 case JIM_DICT_SUGAR:
10849 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
10850
10851 case JIM_ERR:
10852 JimCreateVariable(interp, nameObjPtr, valObjPtr);
10853 break;
10854
10855 case JIM_OK:
10856 vv = nameObjPtr->internalRep.varValue.vv;
10857 if (vv->linkFramePtr == NULL) {
10858 Jim_IncrRefCount(valObjPtr);
10859 Jim_DecrRefCount(interp, vv->objPtr);
10860 vv->objPtr = valObjPtr;
10861 }
10862 else {
10863 Jim_CallFrame *savedCallFrame;
10864
10865 savedCallFrame = interp->framePtr;
10866 interp->framePtr = vv->linkFramePtr;
10867 err = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
10868 interp->framePtr = savedCallFrame;
10869 if (err != JIM_OK)
10870 return err;
10871 }
10872 }
10873 return JIM_OK;
10874 }
10875
10876 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
10877 {
10878 Jim_Obj *nameObjPtr;
10879 int result;
10880
10881 nameObjPtr = Jim_NewStringObj(interp, name, -1);
10882 Jim_IncrRefCount(nameObjPtr);
10883 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
10884 Jim_DecrRefCount(interp, nameObjPtr);
10885 return result;
10886 }
10887
10888 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
10889 {
10890 Jim_CallFrame *savedFramePtr;
10891 int result;
10892
10893 savedFramePtr = interp->framePtr;
10894 interp->framePtr = interp->topFramePtr;
10895 result = Jim_SetVariableStr(interp, name, objPtr);
10896 interp->framePtr = savedFramePtr;
10897 return result;
10898 }
10899
10900 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
10901 {
10902 Jim_Obj *valObjPtr;
10903 int result;
10904
10905 valObjPtr = Jim_NewStringObj(interp, val, -1);
10906 Jim_IncrRefCount(valObjPtr);
10907 result = Jim_SetVariableStr(interp, name, valObjPtr);
10908 Jim_DecrRefCount(interp, valObjPtr);
10909 return result;
10910 }
10911
10912 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
10913 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
10914 {
10915 const char *varName;
10916 const char *targetName;
10917 Jim_CallFrame *framePtr;
10918 Jim_VarVal *vv;
10919 int len;
10920 int varnamelen;
10921
10922
10923 switch (SetVariableFromAny(interp, nameObjPtr)) {
10924 case JIM_DICT_SUGAR:
10925
10926 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
10927 return JIM_ERR;
10928
10929 case JIM_OK:
10930 vv = nameObjPtr->internalRep.varValue.vv;
10931
10932 if (vv->linkFramePtr == NULL) {
10933 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
10934 return JIM_ERR;
10935 }
10936
10937
10938 vv->linkFramePtr = NULL;
10939 break;
10940 }
10941
10942
10943
10944 varName = Jim_GetString(nameObjPtr, &varnamelen);
10945
10946 if (varName[0] == ':' && varName[1] == ':') {
10947 while (*varName == ':') {
10948 varName++;
10949 varnamelen--;
10950 }
10951
10952 framePtr = interp->topFramePtr;
10953 }
10954 else {
10955 framePtr = interp->framePtr;
10956 }
10957
10958 targetName = Jim_GetString(targetNameObjPtr, &len);
10959 if (targetName[0] == ':' && targetName[1] == ':') {
10960 while (*targetName == ':') {
10961 targetName++;
10962 len--;
10963 }
10964 targetNameObjPtr = Jim_NewStringObj(interp, targetName, len);
10965 targetCallFrame = interp->topFramePtr;
10966 }
10967 Jim_IncrRefCount(targetNameObjPtr);
10968
10969 if (framePtr->level < targetCallFrame->level) {
10970 Jim_SetResultFormatted(interp,
10971 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
10972 nameObjPtr);
10973 Jim_DecrRefCount(interp, targetNameObjPtr);
10974 return JIM_ERR;
10975 }
10976
10977
10978 if (framePtr == targetCallFrame) {
10979 Jim_Obj *objPtr = targetNameObjPtr;
10980
10981
10982 while (1) {
10983 if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) {
10984 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
10985 Jim_DecrRefCount(interp, targetNameObjPtr);
10986 return JIM_ERR;
10987 }
10988 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
10989 break;
10990 vv = objPtr->internalRep.varValue.vv;
10991 if (vv->linkFramePtr != targetCallFrame)
10992 break;
10993 objPtr = vv->objPtr;
10994 }
10995 }
10996
10997
10998 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
10999
11000 nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame;
11001 Jim_DecrRefCount(interp, targetNameObjPtr);
11002 return JIM_OK;
11003 }
11004
11005 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11006 {
11007 if (interp->safeexpr) {
11008 return nameObjPtr;
11009 }
11010 switch (SetVariableFromAny(interp, nameObjPtr)) {
11011 case JIM_OK:{
11012 Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv;
11013
11014 if (vv->linkFramePtr == NULL) {
11015 return vv->objPtr;
11016 }
11017 else {
11018 Jim_Obj *objPtr;
11019
11020
11021 Jim_CallFrame *savedCallFrame = interp->framePtr;
11022
11023 interp->framePtr = vv->linkFramePtr;
11024 objPtr = Jim_GetVariable(interp, vv->objPtr, flags);
11025 interp->framePtr = savedCallFrame;
11026 if (objPtr) {
11027 return objPtr;
11028 }
11029
11030 }
11031 }
11032 break;
11033
11034 case JIM_DICT_SUGAR:
11035
11036 return JimDictSugarGet(interp, nameObjPtr, flags);
11037 }
11038 if (flags & JIM_ERRMSG) {
11039 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
11040 }
11041 return NULL;
11042 }
11043
11044 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11045 {
11046 Jim_CallFrame *savedFramePtr;
11047 Jim_Obj *objPtr;
11048
11049 savedFramePtr = interp->framePtr;
11050 interp->framePtr = interp->topFramePtr;
11051 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
11052 interp->framePtr = savedFramePtr;
11053
11054 return objPtr;
11055 }
11056
11057 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
11058 {
11059 Jim_Obj *nameObjPtr, *varObjPtr;
11060
11061 nameObjPtr = Jim_NewStringObj(interp, name, -1);
11062 Jim_IncrRefCount(nameObjPtr);
11063 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
11064 Jim_DecrRefCount(interp, nameObjPtr);
11065 return varObjPtr;
11066 }
11067
11068 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
11069 {
11070 Jim_CallFrame *savedFramePtr;
11071 Jim_Obj *objPtr;
11072
11073 savedFramePtr = interp->framePtr;
11074 interp->framePtr = interp->topFramePtr;
11075 objPtr = Jim_GetVariableStr(interp, name, flags);
11076 interp->framePtr = savedFramePtr;
11077
11078 return objPtr;
11079 }
11080
11081 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11082 {
11083 Jim_VarVal *vv;
11084 int retval;
11085 Jim_CallFrame *framePtr;
11086
11087 retval = SetVariableFromAny(interp, nameObjPtr);
11088 if (retval == JIM_DICT_SUGAR) {
11089
11090 return JimDictSugarSet(interp, nameObjPtr, NULL);
11091 }
11092 else if (retval == JIM_OK) {
11093 vv = nameObjPtr->internalRep.varValue.vv;
11094
11095
11096 if (vv->linkFramePtr) {
11097 framePtr = interp->framePtr;
11098 interp->framePtr = vv->linkFramePtr;
11099 retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE);
11100 interp->framePtr = framePtr;
11101 }
11102 else {
11103 if (nameObjPtr->internalRep.varValue.global) {
11104 int len;
11105 const char *name = Jim_GetString(nameObjPtr, &len);
11106 while (*name == ':') {
11107 name++;
11108 len--;
11109 }
11110 framePtr = interp->topFramePtr;
11111 Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len);
11112 retval = JimUnsetVariable(&framePtr->vars, tempObj);
11113 Jim_FreeNewObj(interp, tempObj);
11114 }
11115 else {
11116 framePtr = interp->framePtr;
11117 retval = JimUnsetVariable(&framePtr->vars, nameObjPtr);
11118 }
11119
11120 if (retval == JIM_OK) {
11121
11122 framePtr->id = interp->callFrameEpoch++;
11123 }
11124 }
11125 }
11126 if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
11127 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
11128 }
11129 return retval;
11130 }
11131
11132
11133
11134 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
11135 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
11136 {
11137 const char *str, *p;
11138 int len, keyLen;
11139 Jim_Obj *varObjPtr, *keyObjPtr;
11140
11141 str = Jim_GetString(objPtr, &len);
11142
11143 p = strchr(str, '(');
11144 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
11145
11146 varObjPtr = Jim_NewStringObj(interp, str, p - str);
11147
11148 p++;
11149 keyLen = (str + len) - p;
11150 if (str[len - 1] == ')') {
11151 keyLen--;
11152 }
11153
11154
11155 keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
11156
11157 Jim_IncrRefCount(varObjPtr);
11158 Jim_IncrRefCount(keyObjPtr);
11159 *varPtrPtr = varObjPtr;
11160 *keyPtrPtr = keyObjPtr;
11161 }
11162
11163 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
11164 {
11165 int err;
11166
11167 SetDictSubstFromAny(interp, objPtr);
11168
11169 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
11170 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
11171
11172 if (err == JIM_OK) {
11173
11174 Jim_SetEmptyResult(interp);
11175 }
11176 else {
11177 if (!valObjPtr) {
11178
11179 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
11180 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
11181 objPtr);
11182 return err;
11183 }
11184 }
11185
11186 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
11187 (valObjPtr ? "set" : "unset"), objPtr);
11188 }
11189 return err;
11190 }
11191
11192 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
11193 Jim_Obj *keyObjPtr, int flags)
11194 {
11195 Jim_Obj *dictObjPtr;
11196 Jim_Obj *resObjPtr = NULL;
11197 int ret;
11198
11199 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
11200 if (!dictObjPtr) {
11201 return NULL;
11202 }
11203
11204 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
11205 if (ret != JIM_OK) {
11206 Jim_SetResultFormatted(interp,
11207 "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
11208 ret < 0 ? "variable isn't" : "no such element in");
11209 }
11210 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
11211
11212 Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
11213 }
11214
11215 return resObjPtr;
11216 }
11217
11218
11219 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11220 {
11221 SetDictSubstFromAny(interp, objPtr);
11222
11223 return JimDictExpandArrayVariable(interp,
11224 objPtr->internalRep.dictSubstValue.varNameObjPtr,
11225 objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
11226 }
11227
11228
11229
11230 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11231 {
11232 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
11233 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
11234 }
11235
11236 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11237 {
11238
11239 dupPtr->internalRep = srcPtr->internalRep;
11240
11241 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
11242 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
11243 }
11244
11245
11246 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11247 {
11248 if (objPtr->typePtr != &dictSubstObjType) {
11249 Jim_Obj *varObjPtr, *keyObjPtr;
11250
11251 if (objPtr->typePtr == &interpolatedObjType) {
11252
11253
11254 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
11255 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
11256
11257 Jim_IncrRefCount(varObjPtr);
11258 Jim_IncrRefCount(keyObjPtr);
11259 }
11260 else {
11261 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
11262 }
11263
11264 Jim_FreeIntRep(interp, objPtr);
11265 objPtr->typePtr = &dictSubstObjType;
11266 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
11267 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
11268 }
11269 }
11270
11271 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
11272 {
11273 Jim_Obj *resObjPtr = NULL;
11274 Jim_Obj *substKeyObjPtr = NULL;
11275
11276 if (interp->safeexpr) {
11277 return objPtr;
11278 }
11279
11280 SetDictSubstFromAny(interp, objPtr);
11281
11282 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
11283 &substKeyObjPtr, JIM_NONE)
11284 != JIM_OK) {
11285 return NULL;
11286 }
11287 Jim_IncrRefCount(substKeyObjPtr);
11288 resObjPtr =
11289 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
11290 substKeyObjPtr, 0);
11291 Jim_DecrRefCount(interp, substKeyObjPtr);
11292
11293 return resObjPtr;
11294 }
11295
11296
11297 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
11298 {
11299 Jim_CallFrame *cf;
11300
11301 if (interp->freeFramesList) {
11302 cf = interp->freeFramesList;
11303 interp->freeFramesList = cf->next;
11304
11305 cf->argv = NULL;
11306 cf->argc = 0;
11307 cf->procArgsObjPtr = NULL;
11308 cf->procBodyObjPtr = NULL;
11309 cf->next = NULL;
11310 cf->staticVars = NULL;
11311 cf->localCommands = NULL;
11312 cf->tailcallObj = NULL;
11313 cf->tailcallCmd = NULL;
11314 }
11315 else {
11316 cf = Jim_Alloc(sizeof(*cf));
11317 memset(cf, 0, sizeof(*cf));
11318
11319 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
11320 }
11321
11322 cf->id = interp->callFrameEpoch++;
11323 cf->parent = parent;
11324 cf->level = parent ? parent->level + 1 : 0;
11325 cf->nsObj = nsObj;
11326 Jim_IncrRefCount(nsObj);
11327
11328 return cf;
11329 }
11330
11331 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
11332 {
11333
11334 if (localCommands) {
11335 Jim_Obj *cmdNameObj;
11336
11337 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
11338 Jim_HashTable *ht = &interp->commands;
11339 Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj);
11340 if (he) {
11341 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
11342 if (cmd->prevCmd) {
11343 Jim_Cmd *prevCmd = cmd->prevCmd;
11344 cmd->prevCmd = NULL;
11345
11346
11347 JimDecrCmdRefCount(interp, cmd);
11348
11349
11350 Jim_SetHashVal(ht, he, prevCmd);
11351 }
11352 else {
11353 Jim_DeleteHashEntry(ht, cmdNameObj);
11354 }
11355 }
11356 Jim_DecrRefCount(interp, cmdNameObj);
11357 }
11358 Jim_FreeStack(localCommands);
11359 Jim_Free(localCommands);
11360 }
11361 return JIM_OK;
11362 }
11363
11364 static int JimInvokeDefer(Jim_Interp *interp, int retcode)
11365 {
11366 Jim_Obj *objPtr;
11367
11368
11369 if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) {
11370 return retcode;
11371 }
11372 objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE);
11373
11374 if (objPtr) {
11375 int ret = JIM_OK;
11376 int i;
11377 int listLen = Jim_ListLength(interp, objPtr);
11378 Jim_Obj *resultObjPtr;
11379
11380 Jim_IncrRefCount(objPtr);
11381
11382 resultObjPtr = Jim_GetResult(interp);
11383 Jim_IncrRefCount(resultObjPtr);
11384 Jim_SetEmptyResult(interp);
11385
11386
11387 for (i = listLen; i > 0; i--) {
11388
11389 Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
11390 ret = Jim_EvalObj(interp, scriptObjPtr);
11391 if (ret != JIM_OK) {
11392 break;
11393 }
11394 }
11395
11396 if (ret == JIM_OK || retcode == JIM_ERR) {
11397
11398 Jim_SetResult(interp, resultObjPtr);
11399 }
11400 else {
11401 retcode = ret;
11402 }
11403
11404 Jim_DecrRefCount(interp, resultObjPtr);
11405 Jim_DecrRefCount(interp, objPtr);
11406 }
11407 return retcode;
11408 }
11409
11410 #define JIM_FCF_FULL 0
11411 #define JIM_FCF_REUSE 1
11412 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
11413 {
11414 JimDeleteLocalProcs(interp, cf->localCommands);
11415
11416 if (cf->procArgsObjPtr)
11417 Jim_DecrRefCount(interp, cf->procArgsObjPtr);
11418 if (cf->procBodyObjPtr)
11419 Jim_DecrRefCount(interp, cf->procBodyObjPtr);
11420 Jim_DecrRefCount(interp, cf->nsObj);
11421 if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
11422 Jim_FreeHashTable(&cf->vars);
11423 else {
11424 Jim_ClearHashTable(&cf->vars);
11425 }
11426 cf->next = interp->freeFramesList;
11427 interp->freeFramesList = cf;
11428 }
11429
11430
11431
11432 int Jim_IsBigEndian(void)
11433 {
11434 union {
11435 unsigned short s;
11436 unsigned char c[2];
11437 } uval = {0x0102};
11438
11439 return uval.c[0] == 1;
11440 }
11441
11442
11443 Jim_Interp *Jim_CreateInterp(void)
11444 {
11445 Jim_Interp *i = Jim_Alloc(sizeof(*i));
11446
11447 memset(i, 0, sizeof(*i));
11448
11449 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
11450 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
11451 i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
11452
11453 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
11454 #ifdef JIM_REFERENCES
11455 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
11456 #endif
11457 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
11458 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
11459 i->emptyObj = Jim_NewEmptyStringObj(i);
11460 i->trueObj = Jim_NewIntObj(i, 1);
11461 i->falseObj = Jim_NewIntObj(i, 0);
11462 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
11463 i->result = i->emptyObj;
11464 i->stackTrace = Jim_NewListObj(i, NULL, 0);
11465 i->unknown = Jim_NewStringObj(i, "unknown", -1);
11466 i->defer = Jim_NewStringObj(i, "jim::defer", -1);
11467 i->errorProc = i->emptyObj;
11468 i->nullScriptObj = Jim_NewEmptyStringObj(i);
11469 i->evalFrame = &i->topEvalFrame;
11470 i->currentFilenameObj = Jim_NewEmptyStringObj(i);
11471 Jim_IncrRefCount(i->emptyObj);
11472 Jim_IncrRefCount(i->result);
11473 Jim_IncrRefCount(i->stackTrace);
11474 Jim_IncrRefCount(i->unknown);
11475 Jim_IncrRefCount(i->defer);
11476 Jim_IncrRefCount(i->nullScriptObj);
11477 Jim_IncrRefCount(i->errorProc);
11478 Jim_IncrRefCount(i->trueObj);
11479 Jim_IncrRefCount(i->falseObj);
11480 Jim_IncrRefCount(i->currentFilenameObj);
11481
11482
11483 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
11484 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
11485
11486 Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
11487 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
11488 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
11489 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
11490 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
11491 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
11492 Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0");
11493 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
11494 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
11495 Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4));
11496
11497 return i;
11498 }
11499
11500 void Jim_FreeInterp(Jim_Interp *i)
11501 {
11502 Jim_CallFrame *cf, *cfx;
11503
11504 Jim_Obj *objPtr, *nextObjPtr;
11505
11506 i->quitting = 1;
11507
11508
11509 for (cf = i->framePtr; cf; cf = cfx) {
11510
11511 JimInvokeDefer(i, JIM_OK);
11512 cfx = cf->parent;
11513 JimFreeCallFrame(i, cf, JIM_FCF_FULL);
11514 }
11515
11516
11517 Jim_FreeHashTable(&i->commands);
11518
11519 Jim_DecrRefCount(i, i->emptyObj);
11520 Jim_DecrRefCount(i, i->trueObj);
11521 Jim_DecrRefCount(i, i->falseObj);
11522 Jim_DecrRefCount(i, i->result);
11523 Jim_DecrRefCount(i, i->stackTrace);
11524 Jim_DecrRefCount(i, i->errorProc);
11525 Jim_DecrRefCount(i, i->unknown);
11526 Jim_DecrRefCount(i, i->defer);
11527 Jim_DecrRefCount(i, i->nullScriptObj);
11528 Jim_DecrRefCount(i, i->currentFilenameObj);
11529
11530
11531 Jim_InterpIncrProcEpoch(i);
11532
11533 #ifdef JIM_REFERENCES
11534 Jim_FreeHashTable(&i->references);
11535 #endif
11536 Jim_FreeHashTable(&i->packages);
11537 Jim_Free(i->prngState);
11538 Jim_FreeHashTable(&i->assocData);
11539 if (i->traceCmdObj) {
11540 Jim_DecrRefCount(i, i->traceCmdObj);
11541 }
11542
11543 #ifdef JIM_MAINTAINER
11544 if (i->liveList != NULL) {
11545 objPtr = i->liveList;
11546
11547 printf("\n-------------------------------------\n");
11548 printf("Objects still in the free list:\n");
11549 while (objPtr) {
11550 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
11551 Jim_String(objPtr);
11552
11553 if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
11554 printf("%p (%d) %-10s: '%.20s...'\n",
11555 (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
11556 }
11557 else {
11558 printf("%p (%d) %-10s: '%s'\n",
11559 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
11560 }
11561 if (objPtr->typePtr == &sourceObjType) {
11562 printf("FILE %s LINE %d\n",
11563 Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
11564 objPtr->internalRep.sourceValue.lineNumber);
11565 }
11566 objPtr = objPtr->nextObjPtr;
11567 }
11568 printf("-------------------------------------\n\n");
11569 JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
11570 }
11571 #endif
11572
11573
11574 objPtr = i->freeList;
11575 while (objPtr) {
11576 nextObjPtr = objPtr->nextObjPtr;
11577 Jim_Free(objPtr);
11578 objPtr = nextObjPtr;
11579 }
11580
11581
11582 for (cf = i->freeFramesList; cf; cf = cfx) {
11583 cfx = cf->next;
11584 if (cf->vars.table)
11585 Jim_FreeHashTable(&cf->vars);
11586 Jim_Free(cf);
11587 }
11588
11589
11590 Jim_Free(i);
11591 }
11592
11593 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
11594 {
11595 long level;
11596 const char *str;
11597 Jim_CallFrame *framePtr;
11598
11599 if (levelObjPtr) {
11600 str = Jim_String(levelObjPtr);
11601 if (str[0] == '#') {
11602 char *endptr;
11603
11604 level = jim_strtol(str + 1, &endptr);
11605 if (str[1] == '\0' || endptr[0] != '\0') {
11606 level = -1;
11607 }
11608 }
11609 else {
11610 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
11611 level = -1;
11612 }
11613 else {
11614
11615 level = interp->framePtr->level - level;
11616 }
11617 }
11618 }
11619 else {
11620 str = "1";
11621 level = interp->framePtr->level - 1;
11622 }
11623
11624 if (level == 0) {
11625 return interp->topFramePtr;
11626 }
11627 if (level > 0) {
11628
11629 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
11630 if (framePtr->level == level) {
11631 return framePtr;
11632 }
11633 }
11634 }
11635
11636 Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
11637 return NULL;
11638 }
11639
11640 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level)
11641 {
11642 Jim_CallFrame *framePtr;
11643
11644 if (level == 0) {
11645 return interp->framePtr;
11646 }
11647
11648 if (level < 0) {
11649
11650 level = interp->framePtr->level + level;
11651 }
11652
11653 if (level > 0) {
11654
11655 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
11656 if (framePtr->level == level) {
11657 return framePtr;
11658 }
11659 }
11660 }
11661 return NULL;
11662 }
11663
11664 static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel)
11665 {
11666 Jim_EvalFrame *evalFrame;
11667
11668 if (proclevel == 0) {
11669 return interp->evalFrame;
11670 }
11671
11672 if (proclevel < 0) {
11673
11674 proclevel = interp->procLevel + proclevel;
11675 }
11676
11677 if (proclevel >= 0) {
11678
11679 for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) {
11680 if (evalFrame->procLevel == proclevel) {
11681 return evalFrame;
11682 }
11683 }
11684 }
11685 return NULL;
11686 }
11687
11688 static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame)
11689 {
11690 if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) {
11691 Jim_EvalFrame *e;
11692 for (e = frame->parent; e; e = e->parent) {
11693 if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) {
11694 break;
11695 }
11696 }
11697 if (e && e->cmd && e->cmd->cmdNameObj) {
11698 return e->cmd->cmdNameObj;
11699 }
11700 }
11701 return NULL;
11702 }
11703
11704 static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj)
11705 {
11706 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
11707 Jim_Obj *fileNameObj = interp->emptyObj;
11708 int linenr = 1;
11709
11710 if (frame->scriptObj) {
11711 ScriptObj *script = JimGetScript(interp, frame->scriptObj);
11712 fileNameObj = script->fileNameObj;
11713 linenr = script->linenr;
11714 }
11715
11716 Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj);
11717 Jim_ListAppendElement(interp, listObj, fileNameObj);
11718 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr));
11719 Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc));
11720 }
11721
11722 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
11723 {
11724
11725 Jim_IncrRefCount(stackTraceObj);
11726 Jim_DecrRefCount(interp, interp->stackTrace);
11727 interp->stackTrace = stackTraceObj;
11728 interp->errorFlag = 1;
11729 }
11730
11731 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script)
11732 {
11733 if (!interp->errorFlag) {
11734 int i;
11735 Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0);
11736
11737 if (interp->procLevel == 0 && script) {
11738 Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
11739 Jim_ListAppendElement(interp, stackTrace, script->fileNameObj);
11740 Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr));
11741 Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
11742 }
11743 else {
11744 for (i = 0; i <= interp->procLevel; i++) {
11745 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
11746 if (frame) {
11747 JimAddStackFrame(interp, frame, stackTrace);
11748 }
11749 }
11750 }
11751 JimSetStackTrace(interp, stackTrace);
11752 }
11753 }
11754
11755 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
11756 void *data)
11757 {
11758 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
11759
11760 assocEntryPtr->delProc = delProc;
11761 assocEntryPtr->data = data;
11762 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
11763 }
11764
11765 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
11766 {
11767 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
11768
11769 if (entryPtr != NULL) {
11770 AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
11771 return assocEntryPtr->data;
11772 }
11773 return NULL;
11774 }
11775
11776 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
11777 {
11778 return Jim_DeleteHashEntry(&interp->assocData, key);
11779 }
11780
11781 int Jim_GetExitCode(Jim_Interp *interp)
11782 {
11783 return interp->exitCode;
11784 }
11785
11786 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
11787 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
11788
11789 static const Jim_ObjType intObjType = {
11790 "int",
11791 NULL,
11792 NULL,
11793 UpdateStringOfInt,
11794 JIM_TYPE_NONE,
11795 };
11796
11797 static const Jim_ObjType coercedDoubleObjType = {
11798 "coerced-double",
11799 NULL,
11800 NULL,
11801 UpdateStringOfInt,
11802 JIM_TYPE_NONE,
11803 };
11804
11805
11806 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
11807 {
11808 char buf[JIM_INTEGER_SPACE + 1];
11809 jim_wide wideValue = JimWideValue(objPtr);
11810 int pos = 0;
11811
11812 if (wideValue == 0) {
11813 buf[pos++] = '0';
11814 }
11815 else {
11816 char tmp[JIM_INTEGER_SPACE];
11817 int num = 0;
11818 int i;
11819
11820 if (wideValue < 0) {
11821 buf[pos++] = '-';
11822 i = wideValue % 10;
11823 tmp[num++] = (i > 0) ? (10 - i) : -i;
11824 wideValue /= -10;
11825 }
11826
11827 while (wideValue) {
11828 tmp[num++] = wideValue % 10;
11829 wideValue /= 10;
11830 }
11831
11832 for (i = 0; i < num; i++) {
11833 buf[pos++] = '0' + tmp[num - i - 1];
11834 }
11835 }
11836 buf[pos] = 0;
11837
11838 JimSetStringBytes(objPtr, buf);
11839 }
11840
11841 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11842 {
11843 jim_wide wideValue;
11844 const char *str;
11845
11846 if (objPtr->typePtr == &coercedDoubleObjType) {
11847
11848 objPtr->typePtr = &intObjType;
11849 return JIM_OK;
11850 }
11851
11852
11853 str = Jim_String(objPtr);
11854
11855 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
11856 if (flags & JIM_ERRMSG) {
11857 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
11858 }
11859 return JIM_ERR;
11860 }
11861 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
11862 Jim_SetResultString(interp, "Integer value too big to be represented", -1);
11863 return JIM_ERR;
11864 }
11865
11866 Jim_FreeIntRep(interp, objPtr);
11867 objPtr->typePtr = &intObjType;
11868 objPtr->internalRep.wideValue = wideValue;
11869 return JIM_OK;
11870 }
11871
11872 #ifdef JIM_OPTIMIZATION
11873 static int JimIsWide(Jim_Obj *objPtr)
11874 {
11875 return objPtr->typePtr == &intObjType;
11876 }
11877 #endif
11878
11879 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11880 {
11881 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
11882 return JIM_ERR;
11883 *widePtr = JimWideValue(objPtr);
11884 return JIM_OK;
11885 }
11886
11887 int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11888 {
11889 int ret = JIM_OK;
11890
11891 if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) {
11892 SetIntFromAny(interp, objPtr, 0);
11893 }
11894 if (objPtr->typePtr == &intObjType) {
11895 *widePtr = JimWideValue(objPtr);
11896 }
11897 else {
11898 JimPanic((interp->safeexpr, "interp->safeexpr is set"));
11899 interp->safeexpr++;
11900 ret = Jim_EvalExpression(interp, objPtr);
11901 interp->safeexpr--;
11902
11903 if (ret == JIM_OK) {
11904 ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr);
11905 }
11906 if (ret != JIM_OK) {
11907 Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr);
11908 }
11909 }
11910 return ret;
11911 }
11912
11913
11914 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11915 {
11916 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
11917 return JIM_ERR;
11918 *widePtr = JimWideValue(objPtr);
11919 return JIM_OK;
11920 }
11921
11922 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
11923 {
11924 jim_wide wideValue;
11925 int retval;
11926
11927 retval = Jim_GetWide(interp, objPtr, &wideValue);
11928 if (retval == JIM_OK) {
11929 *longPtr = (long)wideValue;
11930 return JIM_OK;
11931 }
11932 return JIM_ERR;
11933 }
11934
11935 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
11936 {
11937 Jim_Obj *objPtr;
11938
11939 objPtr = Jim_NewObj(interp);
11940 objPtr->typePtr = &intObjType;
11941 objPtr->bytes = NULL;
11942 objPtr->internalRep.wideValue = wideValue;
11943 return objPtr;
11944 }
11945
11946 #define JIM_DOUBLE_SPACE 30
11947
11948 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
11949 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
11950
11951 static const Jim_ObjType doubleObjType = {
11952 "double",
11953 NULL,
11954 NULL,
11955 UpdateStringOfDouble,
11956 JIM_TYPE_NONE,
11957 };
11958
11959 #if !HAVE_DECL_ISNAN
11960 #undef isnan
11961 #define isnan(X) ((X) != (X))
11962 #endif
11963 #if !HAVE_DECL_ISINF
11964 #undef isinf
11965 #define isinf(X) (1.0 / (X) == 0.0)
11966 #endif
11967
11968 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
11969 {
11970 double value = objPtr->internalRep.doubleValue;
11971
11972 if (isnan(value)) {
11973 JimSetStringBytes(objPtr, "NaN");
11974 return;
11975 }
11976 if (isinf(value)) {
11977 if (value < 0) {
11978 JimSetStringBytes(objPtr, "-Inf");
11979 }
11980 else {
11981 JimSetStringBytes(objPtr, "Inf");
11982 }
11983 return;
11984 }
11985 {
11986 char buf[JIM_DOUBLE_SPACE + 1];
11987 int i;
11988 int len = sprintf(buf, "%.12g", value);
11989
11990
11991 for (i = 0; i < len; i++) {
11992 if (buf[i] == '.' || buf[i] == 'e') {
11993 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
11994 char *e = strchr(buf, 'e');
11995 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
11996
11997 e += 2;
11998 memmove(e, e + 1, len - (e - buf));
11999 }
12000 #endif
12001 break;
12002 }
12003 }
12004 if (buf[i] == '\0') {
12005 buf[i++] = '.';
12006 buf[i++] = '0';
12007 buf[i] = '\0';
12008 }
12009 JimSetStringBytes(objPtr, buf);
12010 }
12011 }
12012
12013 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12014 {
12015 double doubleValue;
12016 jim_wide wideValue;
12017 const char *str;
12018
12019 #ifdef HAVE_LONG_LONG
12020
12021 #define MIN_INT_IN_DOUBLE -(1LL << 53)
12022 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
12023
12024 if (objPtr->typePtr == &intObjType
12025 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
12026 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
12027
12028
12029 objPtr->typePtr = &coercedDoubleObjType;
12030 return JIM_OK;
12031 }
12032 #endif
12033 str = Jim_String(objPtr);
12034
12035 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
12036
12037 Jim_FreeIntRep(interp, objPtr);
12038 objPtr->typePtr = &coercedDoubleObjType;
12039 objPtr->internalRep.wideValue = wideValue;
12040 return JIM_OK;
12041 }
12042 else {
12043
12044 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
12045 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
12046 return JIM_ERR;
12047 }
12048
12049 Jim_FreeIntRep(interp, objPtr);
12050 }
12051 objPtr->typePtr = &doubleObjType;
12052 objPtr->internalRep.doubleValue = doubleValue;
12053 return JIM_OK;
12054 }
12055
12056 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
12057 {
12058 if (objPtr->typePtr == &coercedDoubleObjType) {
12059 *doublePtr = JimWideValue(objPtr);
12060 return JIM_OK;
12061 }
12062 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
12063 return JIM_ERR;
12064
12065 if (objPtr->typePtr == &coercedDoubleObjType) {
12066 *doublePtr = JimWideValue(objPtr);
12067 }
12068 else {
12069 *doublePtr = objPtr->internalRep.doubleValue;
12070 }
12071 return JIM_OK;
12072 }
12073
12074 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
12075 {
12076 Jim_Obj *objPtr;
12077
12078 objPtr = Jim_NewObj(interp);
12079 objPtr->typePtr = &doubleObjType;
12080 objPtr->bytes = NULL;
12081 objPtr->internalRep.doubleValue = doubleValue;
12082 return objPtr;
12083 }
12084
12085 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
12086
12087 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
12088 {
12089 if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
12090 return JIM_ERR;
12091 *booleanPtr = (int) JimWideValue(objPtr);
12092 return JIM_OK;
12093 }
12094
12095 static const char * const jim_true_false_strings[8] = {
12096 "1", "true", "yes", "on",
12097 "0", "false", "no", "off"
12098 };
12099
12100 static const int jim_true_false_lens[8] = {
12101 1, 4, 3, 2,
12102 1, 5, 2, 3,
12103 };
12104
12105 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
12106 {
12107 int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings,
12108 sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings));
12109 if (index < 0) {
12110 if (flags & JIM_ERRMSG) {
12111 Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
12112 }
12113 return JIM_ERR;
12114 }
12115
12116
12117 Jim_FreeIntRep(interp, objPtr);
12118 objPtr->typePtr = &intObjType;
12119
12120 objPtr->internalRep.wideValue = index < 4 ? 1 : 0;
12121 return JIM_OK;
12122 }
12123
12124 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
12125 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
12126 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
12127 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
12128 static void UpdateStringOfList(struct Jim_Obj *objPtr);
12129 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12130
12131 static const Jim_ObjType listObjType = {
12132 "list",
12133 FreeListInternalRep,
12134 DupListInternalRep,
12135 UpdateStringOfList,
12136 JIM_TYPE_NONE,
12137 };
12138
12139 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
12140 {
12141 int i;
12142
12143 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
12144 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
12145 }
12146 Jim_Free(objPtr->internalRep.listValue.ele);
12147 }
12148
12149 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
12150 {
12151 int i;
12152
12153 JIM_NOTUSED(interp);
12154
12155 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
12156 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
12157 dupPtr->internalRep.listValue.ele =
12158 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
12159 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
12160 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
12161 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
12162 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
12163 }
12164 dupPtr->typePtr = &listObjType;
12165 }
12166
12167 #define JIM_ELESTR_SIMPLE 0
12168 #define JIM_ELESTR_BRACE 1
12169 #define JIM_ELESTR_QUOTE 2
12170 static unsigned char ListElementQuotingType(const char *s, int len)
12171 {
12172 int i, level, blevel, trySimple = 1;
12173
12174
12175 if (len == 0)
12176 return JIM_ELESTR_BRACE;
12177 if (s[0] == '"' || s[0] == '{') {
12178 trySimple = 0;
12179 goto testbrace;
12180 }
12181 for (i = 0; i < len; i++) {
12182 switch (s[i]) {
12183 case ' ':
12184 case '$':
12185 case '"':
12186 case '[':
12187 case ']':
12188 case ';':
12189 case '\\':
12190 case '\r':
12191 case '\n':
12192 case '\t':
12193 case '\f':
12194 case '\v':
12195 trySimple = 0;
12196
12197 case '{':
12198 case '}':
12199 goto testbrace;
12200 }
12201 }
12202 return JIM_ELESTR_SIMPLE;
12203
12204 testbrace:
12205
12206 if (s[len - 1] == '\\')
12207 return JIM_ELESTR_QUOTE;
12208 level = 0;
12209 blevel = 0;
12210 for (i = 0; i < len; i++) {
12211 switch (s[i]) {
12212 case '{':
12213 level++;
12214 break;
12215 case '}':
12216 level--;
12217 if (level < 0)
12218 return JIM_ELESTR_QUOTE;
12219 break;
12220 case '[':
12221 blevel++;
12222 break;
12223 case ']':
12224 blevel--;
12225 break;
12226 case '\\':
12227 if (s[i + 1] == '\n')
12228 return JIM_ELESTR_QUOTE;
12229 else if (s[i + 1] != '\0')
12230 i++;
12231 break;
12232 }
12233 }
12234 if (blevel < 0) {
12235 return JIM_ELESTR_QUOTE;
12236 }
12237
12238 if (level == 0) {
12239 if (!trySimple)
12240 return JIM_ELESTR_BRACE;
12241 for (i = 0; i < len; i++) {
12242 switch (s[i]) {
12243 case ' ':
12244 case '$':
12245 case '"':
12246 case '[':
12247 case ']':
12248 case ';':
12249 case '\\':
12250 case '\r':
12251 case '\n':
12252 case '\t':
12253 case '\f':
12254 case '\v':
12255 return JIM_ELESTR_BRACE;
12256 break;
12257 }
12258 }
12259 return JIM_ELESTR_SIMPLE;
12260 }
12261 return JIM_ELESTR_QUOTE;
12262 }
12263
12264 static int BackslashQuoteString(const char *s, int len, char *q)
12265 {
12266 char *p = q;
12267
12268 while (len--) {
12269 switch (*s) {
12270 case ' ':
12271 case '$':
12272 case '"':
12273 case '[':
12274 case ']':
12275 case '{':
12276 case '}':
12277 case ';':
12278 case '\\':
12279 *p++ = '\\';
12280 *p++ = *s++;
12281 break;
12282 case '\n':
12283 *p++ = '\\';
12284 *p++ = 'n';
12285 s++;
12286 break;
12287 case '\r':
12288 *p++ = '\\';
12289 *p++ = 'r';
12290 s++;
12291 break;
12292 case '\t':
12293 *p++ = '\\';
12294 *p++ = 't';
12295 s++;
12296 break;
12297 case '\f':
12298 *p++ = '\\';
12299 *p++ = 'f';
12300 s++;
12301 break;
12302 case '\v':
12303 *p++ = '\\';
12304 *p++ = 'v';
12305 s++;
12306 break;
12307 default:
12308 *p++ = *s++;
12309 break;
12310 }
12311 }
12312 *p = '\0';
12313
12314 return p - q;
12315 }
12316
12317 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
12318 {
12319 #define STATIC_QUOTING_LEN 32
12320 int i, bufLen, realLength;
12321 const char *strRep;
12322 char *p;
12323 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
12324
12325
12326 if (objc > STATIC_QUOTING_LEN) {
12327 quotingType = Jim_Alloc(objc);
12328 }
12329 else {
12330 quotingType = staticQuoting;
12331 }
12332 bufLen = 0;
12333 for (i = 0; i < objc; i++) {
12334 int len;
12335
12336 strRep = Jim_GetString(objv[i], &len);
12337 quotingType[i] = ListElementQuotingType(strRep, len);
12338 switch (quotingType[i]) {
12339 case JIM_ELESTR_SIMPLE:
12340 if (i != 0 || strRep[0] != '#') {
12341 bufLen += len;
12342 break;
12343 }
12344
12345 quotingType[i] = JIM_ELESTR_BRACE;
12346
12347 case JIM_ELESTR_BRACE:
12348 bufLen += len + 2;
12349 break;
12350 case JIM_ELESTR_QUOTE:
12351 bufLen += len * 2;
12352 break;
12353 }
12354 bufLen++;
12355 }
12356 bufLen++;
12357
12358
12359 p = objPtr->bytes = Jim_Alloc(bufLen + 1);
12360 realLength = 0;
12361 for (i = 0; i < objc; i++) {
12362 int len, qlen;
12363
12364 strRep = Jim_GetString(objv[i], &len);
12365
12366 switch (quotingType[i]) {
12367 case JIM_ELESTR_SIMPLE:
12368 memcpy(p, strRep, len);
12369 p += len;
12370 realLength += len;
12371 break;
12372 case JIM_ELESTR_BRACE:
12373 *p++ = '{';
12374 memcpy(p, strRep, len);
12375 p += len;
12376 *p++ = '}';
12377 realLength += len + 2;
12378 break;
12379 case JIM_ELESTR_QUOTE:
12380 if (i == 0 && strRep[0] == '#') {
12381 *p++ = '\\';
12382 realLength++;
12383 }
12384 qlen = BackslashQuoteString(strRep, len, p);
12385 p += qlen;
12386 realLength += qlen;
12387 break;
12388 }
12389
12390 if (i + 1 != objc) {
12391 *p++ = ' ';
12392 realLength++;
12393 }
12394 }
12395 *p = '\0';
12396 objPtr->length = realLength;
12397
12398 if (quotingType != staticQuoting) {
12399 Jim_Free(quotingType);
12400 }
12401 }
12402
12403 static void UpdateStringOfList(struct Jim_Obj *objPtr)
12404 {
12405 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
12406 }
12407
12408 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
12409 {
12410 struct JimParserCtx parser;
12411 const char *str;
12412 int strLen;
12413 Jim_Obj *fileNameObj;
12414 int linenr;
12415
12416 if (objPtr->typePtr == &listObjType) {
12417 return JIM_OK;
12418 }
12419
12420
12421 if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
12422 Jim_Dict *dict = objPtr->internalRep.dictValue;
12423
12424
12425 objPtr->typePtr = &listObjType;
12426 objPtr->internalRep.listValue.len = dict->len;
12427 objPtr->internalRep.listValue.maxLen = dict->maxLen;
12428 objPtr->internalRep.listValue.ele = dict->table;
12429
12430
12431 Jim_Free(dict->ht);
12432
12433
12434 Jim_Free(dict);
12435 return JIM_OK;
12436 }
12437
12438
12439 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr);
12440 Jim_IncrRefCount(fileNameObj);
12441
12442
12443 str = Jim_GetString(objPtr, &strLen);
12444
12445 Jim_FreeIntRep(interp, objPtr);
12446 objPtr->typePtr = &listObjType;
12447 objPtr->internalRep.listValue.len = 0;
12448 objPtr->internalRep.listValue.maxLen = 0;
12449 objPtr->internalRep.listValue.ele = NULL;
12450
12451
12452 if (strLen) {
12453 JimParserInit(&parser, str, strLen, linenr);
12454 while (!parser.eof) {
12455 Jim_Obj *elementPtr;
12456
12457 JimParseList(&parser);
12458 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
12459 continue;
12460 elementPtr = JimParserGetTokenObj(interp, &parser);
12461 Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
12462 ListAppendElement(objPtr, elementPtr);
12463 }
12464 }
12465 Jim_DecrRefCount(interp, fileNameObj);
12466 return JIM_OK;
12467 }
12468
12469 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12470 {
12471 Jim_Obj *objPtr;
12472
12473 objPtr = Jim_NewObj(interp);
12474 objPtr->typePtr = &listObjType;
12475 objPtr->bytes = NULL;
12476 objPtr->internalRep.listValue.ele = NULL;
12477 objPtr->internalRep.listValue.len = 0;
12478 objPtr->internalRep.listValue.maxLen = 0;
12479
12480 if (len) {
12481 ListInsertElements(objPtr, 0, len, elements);
12482 }
12483
12484 return objPtr;
12485 }
12486
12487 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
12488 Jim_Obj ***listVec)
12489 {
12490 *listLen = Jim_ListLength(interp, listObj);
12491 *listVec = listObj->internalRep.listValue.ele;
12492 }
12493
12494
12495 static int JimSign(jim_wide w)
12496 {
12497 if (w == 0) {
12498 return 0;
12499 }
12500 else if (w < 0) {
12501 return -1;
12502 }
12503 return 1;
12504 }
12505
12506
12507 struct lsort_info {
12508 jmp_buf jmpbuf;
12509 Jim_Obj *command;
12510 Jim_Interp *interp;
12511 enum {
12512 JIM_LSORT_ASCII,
12513 JIM_LSORT_NOCASE,
12514 JIM_LSORT_INTEGER,
12515 JIM_LSORT_REAL,
12516 JIM_LSORT_COMMAND,
12517 JIM_LSORT_DICT
12518 } type;
12519 int order;
12520 Jim_Obj **indexv;
12521 int indexc;
12522 int unique;
12523 int (*subfn)(Jim_Obj **, Jim_Obj **);
12524 };
12525
12526 static struct lsort_info *sort_info;
12527
12528 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12529 {
12530 Jim_Obj *lObj, *rObj;
12531
12532 if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK ||
12533 Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) {
12534 longjmp(sort_info->jmpbuf, JIM_ERR);
12535 }
12536 return sort_info->subfn(&lObj, &rObj);
12537 }
12538
12539
12540 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12541 {
12542 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
12543 }
12544
12545 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12546 {
12547 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
12548 }
12549
12550 static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12551 {
12552
12553 const char *left = Jim_String(*lhsObj);
12554 const char *right = Jim_String(*rhsObj);
12555
12556 while (1) {
12557 if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) {
12558
12559 jim_wide lint, rint;
12560 char *lend, *rend;
12561 lint = jim_strtoull(left, &lend);
12562 rint = jim_strtoull(right, &rend);
12563 if (lint != rint) {
12564 return JimSign(lint - rint) * sort_info->order;
12565 }
12566 if (lend -left != rend - right) {
12567 return JimSign((lend - left) - (rend - right)) * sort_info->order;
12568 }
12569 left = lend;
12570 right = rend;
12571 }
12572 else {
12573 int cl, cr;
12574 left += utf8_tounicode_case(left, &cl, 1);
12575 right += utf8_tounicode_case(right, &cr, 1);
12576 if (cl != cr) {
12577 return JimSign(cl - cr) * sort_info->order;
12578 }
12579 if (cl == 0) {
12580
12581 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
12582 }
12583 }
12584 }
12585 }
12586
12587 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12588 {
12589 jim_wide lhs = 0, rhs = 0;
12590
12591 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
12592 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
12593 longjmp(sort_info->jmpbuf, JIM_ERR);
12594 }
12595
12596 return JimSign(lhs - rhs) * sort_info->order;
12597 }
12598
12599 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12600 {
12601 double lhs = 0, rhs = 0;
12602
12603 if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
12604 Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
12605 longjmp(sort_info->jmpbuf, JIM_ERR);
12606 }
12607 if (lhs == rhs) {
12608 return 0;
12609 }
12610 if (lhs > rhs) {
12611 return sort_info->order;
12612 }
12613 return -sort_info->order;
12614 }
12615
12616 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12617 {
12618 Jim_Obj *compare_script;
12619 int rc;
12620
12621 jim_wide ret = 0;
12622
12623
12624 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
12625 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
12626 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
12627
12628 rc = Jim_EvalObj(sort_info->interp, compare_script);
12629
12630 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
12631 longjmp(sort_info->jmpbuf, rc);
12632 }
12633
12634 return JimSign(ret) * sort_info->order;
12635 }
12636
12637 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
12638 {
12639 int src;
12640 int dst = 0;
12641 Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
12642
12643 for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
12644 if (comp(&ele[dst], &ele[src]) == 0) {
12645
12646 Jim_DecrRefCount(sort_info->interp, ele[dst]);
12647 }
12648 else {
12649
12650 dst++;
12651 }
12652 ele[dst] = ele[src];
12653 }
12654
12655
12656 dst++;
12657 if (dst < listObjPtr->internalRep.listValue.len) {
12658 ele[dst] = ele[src];
12659 }
12660
12661
12662 listObjPtr->internalRep.listValue.len = dst;
12663 }
12664
12665
12666 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
12667 {
12668 struct lsort_info *prev_info;
12669
12670 typedef int (qsort_comparator) (const void *, const void *);
12671 int (*fn) (Jim_Obj **, Jim_Obj **);
12672 Jim_Obj **vector;
12673 int len;
12674 int rc;
12675
12676 JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
12677 SetListFromAny(interp, listObjPtr);
12678
12679
12680 prev_info = sort_info;
12681 sort_info = info;
12682
12683 vector = listObjPtr->internalRep.listValue.ele;
12684 len = listObjPtr->internalRep.listValue.len;
12685 switch (info->type) {
12686 case JIM_LSORT_ASCII:
12687 fn = ListSortString;
12688 break;
12689 case JIM_LSORT_NOCASE:
12690 fn = ListSortStringNoCase;
12691 break;
12692 case JIM_LSORT_INTEGER:
12693 fn = ListSortInteger;
12694 break;
12695 case JIM_LSORT_REAL:
12696 fn = ListSortReal;
12697 break;
12698 case JIM_LSORT_COMMAND:
12699 fn = ListSortCommand;
12700 break;
12701 case JIM_LSORT_DICT:
12702 fn = ListSortDict;
12703 break;
12704 default:
12705 fn = NULL;
12706 JimPanic((1, "ListSort called with invalid sort type"));
12707 return -1;
12708 }
12709
12710 if (info->indexc) {
12711
12712 info->subfn = fn;
12713 fn = ListSortIndexHelper;
12714 }
12715
12716 if ((rc = setjmp(info->jmpbuf)) == 0) {
12717 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
12718
12719 if (info->unique && len > 1) {
12720 ListRemoveDuplicates(listObjPtr, fn);
12721 }
12722
12723 Jim_InvalidateStringRep(listObjPtr);
12724 }
12725 sort_info = prev_info;
12726
12727 return rc;
12728 }
12729
12730
12731 static void ListEnsureLength(Jim_Obj *listPtr, int idx)
12732 {
12733 assert(idx >= 0);
12734 if (idx >= listPtr->internalRep.listValue.maxLen) {
12735 if (idx < 4) {
12736
12737 idx = 4;
12738 }
12739 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
12740 sizeof(Jim_Obj *) * idx);
12741
12742 listPtr->internalRep.listValue.maxLen = idx;
12743 }
12744 }
12745
12746 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
12747 {
12748 int currentLen = listPtr->internalRep.listValue.len;
12749 int requiredLen = currentLen + elemc;
12750 int i;
12751 Jim_Obj **point;
12752
12753 if (elemc == 0) {
12754
12755 return;
12756 }
12757
12758 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
12759 if (currentLen) {
12760
12761 requiredLen *= 2;
12762 }
12763 ListEnsureLength(listPtr, requiredLen);
12764 }
12765 if (idx < 0) {
12766 idx = currentLen;
12767 }
12768 point = listPtr->internalRep.listValue.ele + idx;
12769 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
12770 for (i = 0; i < elemc; ++i) {
12771 point[i] = elemVec[i];
12772 Jim_IncrRefCount(point[i]);
12773 }
12774 listPtr->internalRep.listValue.len += elemc;
12775 }
12776
12777 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
12778 {
12779 ListInsertElements(listPtr, -1, 1, &objPtr);
12780 }
12781
12782 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
12783 {
12784 ListInsertElements(listPtr, -1,
12785 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
12786 }
12787
12788 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
12789 {
12790 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
12791 SetListFromAny(interp, listPtr);
12792 Jim_InvalidateStringRep(listPtr);
12793 ListAppendElement(listPtr, objPtr);
12794 }
12795
12796 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
12797 {
12798 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
12799 SetListFromAny(interp, listPtr);
12800 SetListFromAny(interp, appendListPtr);
12801 Jim_InvalidateStringRep(listPtr);
12802 ListAppendList(listPtr, appendListPtr);
12803 }
12804
12805 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
12806 {
12807 SetListFromAny(interp, objPtr);
12808 return objPtr->internalRep.listValue.len;
12809 }
12810
12811 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
12812 int objc, Jim_Obj *const *objVec)
12813 {
12814 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
12815 SetListFromAny(interp, listPtr);
12816 if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
12817 idx = listPtr->internalRep.listValue.len;
12818 else if (idx < 0)
12819 idx = 0;
12820 Jim_InvalidateStringRep(listPtr);
12821 ListInsertElements(listPtr, idx, objc, objVec);
12822 }
12823
12824 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
12825 {
12826 SetListFromAny(interp, listPtr);
12827 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
12828 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
12829 return NULL;
12830 }
12831 if (idx < 0)
12832 idx = listPtr->internalRep.listValue.len + idx;
12833 return listPtr->internalRep.listValue.ele[idx];
12834 }
12835
12836 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
12837 {
12838 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
12839 if (*objPtrPtr == NULL) {
12840 if (flags & JIM_ERRMSG) {
12841 Jim_SetResultString(interp, "list index out of range", -1);
12842 }
12843 return JIM_ERR;
12844 }
12845 return JIM_OK;
12846 }
12847
12848 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr,
12849 Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags)
12850 {
12851 int i;
12852 int static_idxes[5];
12853 int *idxes = static_idxes;
12854 int ret = JIM_OK;
12855
12856 if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) {
12857 idxes = Jim_Alloc(indexc * sizeof(*idxes));
12858 }
12859
12860 for (i = 0; i < indexc; i++) {
12861 ret = Jim_GetIndex(interp, indexv[i], &idxes[i]);
12862 if (ret != JIM_OK) {
12863 goto err;
12864 }
12865 }
12866
12867 for (i = 0; i < indexc; i++) {
12868 Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]);
12869 if (!objPtr) {
12870 if (flags & JIM_ERRMSG) {
12871 if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) {
12872 Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
12873 }
12874 else {
12875 Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr);
12876 }
12877 }
12878 return -1;
12879 }
12880 listPtr = objPtr;
12881 }
12882 *resultObj = listPtr;
12883 err:
12884 if (idxes != static_idxes)
12885 Jim_Free(idxes);
12886 return ret;
12887 }
12888
12889 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
12890 Jim_Obj *newObjPtr, int flags)
12891 {
12892 SetListFromAny(interp, listPtr);
12893 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
12894 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
12895 if (flags & JIM_ERRMSG) {
12896 Jim_SetResultString(interp, "list index out of range", -1);
12897 }
12898 return JIM_ERR;
12899 }
12900 if (idx < 0)
12901 idx = listPtr->internalRep.listValue.len + idx;
12902 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
12903 listPtr->internalRep.listValue.ele[idx] = newObjPtr;
12904 Jim_IncrRefCount(newObjPtr);
12905 return JIM_OK;
12906 }
12907
12908 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
12909 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
12910 {
12911 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
12912 int shared, i, idx;
12913
12914 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
12915 if (objPtr == NULL)
12916 return JIM_ERR;
12917 if ((shared = Jim_IsShared(objPtr)))
12918 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12919 for (i = 0; i < indexc - 1; i++) {
12920 listObjPtr = objPtr;
12921 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
12922 goto err;
12923
12924 objPtr = Jim_ListGetIndex(interp, listObjPtr, idx);
12925 if (objPtr == NULL) {
12926 Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
12927 goto err;
12928 }
12929 if (Jim_IsShared(objPtr)) {
12930 objPtr = Jim_DuplicateObj(interp, objPtr);
12931 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
12932 }
12933 Jim_InvalidateStringRep(listObjPtr);
12934 }
12935 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
12936 goto err;
12937 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
12938 goto err;
12939 Jim_InvalidateStringRep(objPtr);
12940 Jim_InvalidateStringRep(varObjPtr);
12941 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
12942 goto err;
12943 Jim_SetResult(interp, varObjPtr);
12944 return JIM_OK;
12945 err:
12946 if (shared) {
12947 Jim_FreeNewObj(interp, varObjPtr);
12948 }
12949 return JIM_ERR;
12950 }
12951
12952 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
12953 {
12954 int i;
12955 int listLen = Jim_ListLength(interp, listObjPtr);
12956 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
12957
12958 for (i = 0; i < listLen; ) {
12959 Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
12960 if (++i != listLen) {
12961 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
12962 }
12963 }
12964 return resObjPtr;
12965 }
12966
12967 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
12968 {
12969 int i;
12970
12971 for (i = 0; i < objc; i++) {
12972 if (!Jim_IsList(objv[i]))
12973 break;
12974 }
12975 if (i == objc) {
12976 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
12977
12978 for (i = 0; i < objc; i++)
12979 ListAppendList(objPtr, objv[i]);
12980 return objPtr;
12981 }
12982 else {
12983
12984 int len = 0, objLen;
12985 char *bytes, *p;
12986
12987
12988 for (i = 0; i < objc; i++) {
12989 len += Jim_Length(objv[i]);
12990 }
12991 if (objc)
12992 len += objc - 1;
12993
12994 p = bytes = Jim_Alloc(len + 1);
12995 for (i = 0; i < objc; i++) {
12996 const char *s = Jim_GetString(objv[i], &objLen);
12997
12998
12999 while (objLen && isspace(UCHAR(*s))) {
13000 s++;
13001 objLen--;
13002 len--;
13003 }
13004
13005 while (objLen && isspace(UCHAR(s[objLen - 1]))) {
13006
13007 if (objLen > 1 && s[objLen - 2] == '\\') {
13008 break;
13009 }
13010 objLen--;
13011 len--;
13012 }
13013 memcpy(p, s, objLen);
13014 p += objLen;
13015 if (i + 1 != objc) {
13016 if (objLen)
13017 *p++ = ' ';
13018 else {
13019 len--;
13020 }
13021 }
13022 }
13023 *p = '\0';
13024 return Jim_NewStringObjNoAlloc(interp, bytes, len);
13025 }
13026 }
13027
13028 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
13029 Jim_Obj *lastObjPtr)
13030 {
13031 int first, last;
13032 int len, rangeLen;
13033
13034 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
13035 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
13036 return NULL;
13037 len = Jim_ListLength(interp, listObjPtr);
13038 first = JimRelToAbsIndex(len, first);
13039 last = JimRelToAbsIndex(len, last);
13040 JimRelToAbsRange(len, &first, &last, &rangeLen);
13041 if (first == 0 && last == len) {
13042 return listObjPtr;
13043 }
13044 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
13045 }
13046
13047 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13048 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13049 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
13050 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13051
13052
13053 static const Jim_ObjType dictObjType = {
13054 "dict",
13055 FreeDictInternalRep,
13056 DupDictInternalRep,
13057 UpdateStringOfDict,
13058 JIM_TYPE_NONE,
13059 };
13060
13061 static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict)
13062 {
13063 int i;
13064 for (i = 0; i < dict->len; i++) {
13065 Jim_DecrRefCount(interp, dict->table[i]);
13066 }
13067 Jim_Free(dict->table);
13068 Jim_Free(dict->ht);
13069 Jim_Free(dict);
13070 }
13071
13072 enum {
13073 DICT_HASH_FIND = -1,
13074 DICT_HASH_REMOVE = -2,
13075 DICT_HASH_ADD = -3,
13076 };
13077
13078 static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset)
13079 {
13080 unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq);
13081 unsigned idx = h & dict->sizemask;
13082 int tvoffset = 0;
13083 unsigned peturb = h;
13084 unsigned first_removed = ~0;
13085
13086 if (dict->len) {
13087 while ((tvoffset = dict->ht[idx].offset)) {
13088 if (tvoffset == -1) {
13089 if (first_removed == ~0) {
13090 first_removed = idx;
13091 }
13092 }
13093 else if (dict->ht[idx].hash == h) {
13094 if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) {
13095 break;
13096 }
13097 }
13098
13099 peturb >>= 5;
13100 idx = (5 * idx + 1 + peturb) & dict->sizemask;
13101 }
13102 }
13103
13104 switch (op_tvoffset) {
13105 case DICT_HASH_FIND:
13106
13107 break;
13108 case DICT_HASH_REMOVE:
13109 if (tvoffset) {
13110
13111 dict->ht[idx].offset = -1;
13112 dict->dummy++;
13113 }
13114
13115 break;
13116 case DICT_HASH_ADD:
13117 if (tvoffset == 0) {
13118
13119 if (first_removed != ~0) {
13120 idx = first_removed;
13121 dict->dummy--;
13122 }
13123 dict->ht[idx].offset = dict->len + 1;
13124 dict->ht[idx].hash = h;
13125 }
13126
13127 break;
13128 default:
13129 assert(tvoffset);
13130
13131 dict->ht[idx].offset = op_tvoffset;
13132 break;
13133 }
13134
13135 return tvoffset;
13136 }
13137
13138 static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size)
13139 {
13140 int i;
13141 struct JimDictHashEntry *prevht = dict->ht;
13142 int prevsize = dict->size;
13143
13144 dict->size = JimHashTableNextPower(size);
13145 dict->sizemask = dict->size - 1;
13146
13147
13148 dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht));
13149 memset(dict->ht, 0, dict->size * sizeof(*dict->ht));
13150
13151
13152 for (i = 0; i < prevsize; i++) {
13153 if (prevht[i].offset > 0) {
13154
13155 unsigned h = prevht[i].hash;
13156 unsigned idx = h & dict->sizemask;
13157 unsigned peturb = h;
13158
13159 while (dict->ht[idx].offset) {
13160 peturb >>= 5;
13161 idx = (5 * idx + 1 + peturb) & dict->sizemask;
13162 }
13163 dict->ht[idx].offset = prevht[i].offset;
13164 dict->ht[idx].hash = h;
13165 }
13166 }
13167 Jim_Free(prevht);
13168 }
13169
13170 static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr)
13171 {
13172 if (dict->size <= dict->len + dict->dummy) {
13173 JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8);
13174 }
13175 return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD);
13176 }
13177
13178 static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size)
13179 {
13180 Jim_Dict *dict = Jim_Alloc(sizeof(*dict));
13181 memset(dict, 0, sizeof(*dict));
13182
13183 if (ht_size) {
13184 JimDictExpandHashTable(dict, ht_size);
13185 }
13186 if (table_size) {
13187 dict->table = Jim_Alloc(table_size * sizeof(*dict->table));
13188 dict->maxLen = table_size;
13189 }
13190 #ifdef JIM_RANDOMISE_HASH
13191 dict->uniq = (rand() ^ time(NULL) ^ clock());
13192 #endif
13193 return dict;
13194 }
13195
13196 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13197 {
13198 JimFreeDict(interp, objPtr->internalRep.dictValue);
13199 }
13200
13201 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13202 {
13203 Jim_Dict *oldDict = srcPtr->internalRep.dictValue;
13204 int i;
13205
13206
13207 Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size);
13208
13209
13210 for (i = 0; i < oldDict->len; i++) {
13211 newDict->table[i] = oldDict->table[i];
13212 Jim_IncrRefCount(newDict->table[i]);
13213 }
13214 newDict->len = oldDict->len;
13215
13216
13217 newDict->uniq = oldDict->uniq;
13218
13219
13220 memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size);
13221
13222 dupPtr->internalRep.dictValue = newDict;
13223 dupPtr->typePtr = &dictObjType;
13224 }
13225
13226 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
13227 {
13228 JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len);
13229 }
13230
13231 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13232 {
13233 int listlen;
13234
13235 if (objPtr->typePtr == &dictObjType) {
13236 return JIM_OK;
13237 }
13238
13239 if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
13240 Jim_String(objPtr);
13241 }
13242
13243 listlen = Jim_ListLength(interp, objPtr);
13244 if (listlen % 2) {
13245 Jim_SetResultString(interp, "missing value to go with key", -1);
13246 return JIM_ERR;
13247 }
13248 else {
13249
13250 Jim_Dict *dict = JimDictNew(interp, 0, listlen);
13251 int i;
13252
13253
13254 dict->table = objPtr->internalRep.listValue.ele;
13255 dict->maxLen = objPtr->internalRep.listValue.maxLen;
13256
13257
13258 for (i = 0; i < listlen; i += 2) {
13259 int tvoffset = JimDictAdd(dict, dict->table[i]);
13260 if (tvoffset) {
13261
13262
13263 Jim_DecrRefCount(interp, dict->table[tvoffset]);
13264
13265 dict->table[tvoffset] = dict->table[i + 1];
13266
13267 Jim_DecrRefCount(interp, dict->table[i]);
13268 }
13269 else {
13270 if (dict->len != i) {
13271 dict->table[dict->len++] = dict->table[i];
13272 dict->table[dict->len++] = dict->table[i + 1];
13273 }
13274 else {
13275 dict->len += 2;
13276 }
13277 }
13278 }
13279
13280 objPtr->typePtr = &dictObjType;
13281 objPtr->internalRep.dictValue = dict;
13282
13283 return JIM_OK;
13284 }
13285 }
13286
13287
13288
13289 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
13290 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
13291 {
13292 Jim_Dict *dict = objPtr->internalRep.dictValue;
13293 if (valueObjPtr == NULL) {
13294
13295 int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE);
13296 if (tvoffset) {
13297
13298 Jim_DecrRefCount(interp, dict->table[tvoffset - 1]);
13299 Jim_DecrRefCount(interp, dict->table[tvoffset]);
13300 dict->len -= 2;
13301 if (tvoffset != dict->len + 1) {
13302
13303 dict->table[tvoffset - 1] = dict->table[dict->len];
13304 dict->table[tvoffset] = dict->table[dict->len + 1];
13305
13306
13307 JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset);
13308 }
13309 return JIM_OK;
13310 }
13311 return JIM_ERR;
13312 }
13313 else {
13314
13315 int tvoffset = JimDictAdd(dict, keyObjPtr);
13316 if (tvoffset) {
13317
13318 Jim_IncrRefCount(valueObjPtr);
13319 Jim_DecrRefCount(interp, dict->table[tvoffset]);
13320 dict->table[tvoffset] = valueObjPtr;
13321 }
13322 else {
13323 if (dict->maxLen == dict->len) {
13324
13325 if (dict->maxLen < 4) {
13326 dict->maxLen = 4;
13327 }
13328 else {
13329 dict->maxLen *= 2;
13330 }
13331 dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table));
13332 }
13333 Jim_IncrRefCount(keyObjPtr);
13334 Jim_IncrRefCount(valueObjPtr);
13335
13336 dict->table[dict->len++] = keyObjPtr;
13337 dict->table[dict->len++] = valueObjPtr;
13338
13339 }
13340 return JIM_OK;
13341 }
13342 }
13343
13344 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
13345 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
13346 {
13347 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
13348 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
13349 return JIM_ERR;
13350 }
13351 Jim_InvalidateStringRep(objPtr);
13352 return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
13353 }
13354
13355 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
13356 {
13357 Jim_Obj *objPtr;
13358 int i;
13359
13360 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
13361
13362 objPtr = Jim_NewObj(interp);
13363 objPtr->typePtr = &dictObjType;
13364 objPtr->bytes = NULL;
13365
13366 objPtr->internalRep.dictValue = JimDictNew(interp, len, len);
13367 for (i = 0; i < len; i += 2)
13368 DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
13369 return objPtr;
13370 }
13371
13372 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
13373 Jim_Obj **objPtrPtr, int flags)
13374 {
13375 int tvoffset;
13376 Jim_Dict *dict;
13377
13378 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
13379 return -1;
13380 }
13381 dict = dictPtr->internalRep.dictValue;
13382 tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND);
13383 if (tvoffset == 0) {
13384 if (flags & JIM_ERRMSG) {
13385 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
13386 }
13387 return JIM_ERR;
13388 }
13389 *objPtrPtr = dict->table[tvoffset];
13390 return JIM_OK;
13391 }
13392
13393 Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len)
13394 {
13395
13396 if (Jim_IsList(dictPtr)) {
13397 Jim_Obj **table;
13398 JimListGetElements(interp, dictPtr, len, &table);
13399 if (*len % 2 == 0) {
13400 return table;
13401 }
13402
13403 }
13404 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
13405
13406 *len = 1;
13407 return NULL;
13408 }
13409 *len = dictPtr->internalRep.dictValue->len;
13410 return dictPtr->internalRep.dictValue->table;
13411 }
13412
13413
13414 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
13415 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
13416 {
13417 int i;
13418
13419 if (keyc == 0) {
13420 *objPtrPtr = dictPtr;
13421 return JIM_OK;
13422 }
13423
13424 for (i = 0; i < keyc; i++) {
13425 Jim_Obj *objPtr;
13426
13427 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
13428 if (rc != JIM_OK) {
13429 return rc;
13430 }
13431 dictPtr = objPtr;
13432 }
13433 *objPtrPtr = dictPtr;
13434 return JIM_OK;
13435 }
13436
13437 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
13438 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
13439 {
13440 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
13441 int shared, i;
13442
13443 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
13444 if (objPtr == NULL) {
13445 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
13446
13447 return JIM_ERR;
13448 }
13449 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
13450 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
13451 Jim_FreeNewObj(interp, varObjPtr);
13452 return JIM_ERR;
13453 }
13454 }
13455 if ((shared = Jim_IsShared(objPtr)))
13456 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
13457 for (i = 0; i < keyc; i++) {
13458 dictObjPtr = objPtr;
13459
13460
13461 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
13462 goto err;
13463 }
13464
13465 if (i == keyc - 1) {
13466
13467 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
13468 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
13469 goto err;
13470 }
13471 }
13472 break;
13473 }
13474
13475
13476 Jim_InvalidateStringRep(dictObjPtr);
13477 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
13478 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
13479 if (Jim_IsShared(objPtr)) {
13480 objPtr = Jim_DuplicateObj(interp, objPtr);
13481 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
13482 }
13483 }
13484 else {
13485 if (newObjPtr == NULL) {
13486 goto err;
13487 }
13488 objPtr = Jim_NewDictObj(interp, NULL, 0);
13489 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
13490 }
13491 }
13492
13493 Jim_InvalidateStringRep(objPtr);
13494 Jim_InvalidateStringRep(varObjPtr);
13495 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
13496 goto err;
13497 }
13498
13499 if (!(flags & JIM_NORESULT)) {
13500 Jim_SetResult(interp, varObjPtr);
13501 }
13502 return JIM_OK;
13503 err:
13504 if (shared) {
13505 Jim_FreeNewObj(interp, varObjPtr);
13506 }
13507 return JIM_ERR;
13508 }
13509
13510 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
13511 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13512
13513 static const Jim_ObjType indexObjType = {
13514 "index",
13515 NULL,
13516 NULL,
13517 UpdateStringOfIndex,
13518 JIM_TYPE_NONE,
13519 };
13520
13521 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
13522 {
13523 if (objPtr->internalRep.intValue == -1) {
13524 JimSetStringBytes(objPtr, "end");
13525 }
13526 else {
13527 char buf[JIM_INTEGER_SPACE + 1];
13528 if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) {
13529 sprintf(buf, "%d", objPtr->internalRep.intValue);
13530 }
13531 else {
13532
13533 sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
13534 }
13535 JimSetStringBytes(objPtr, buf);
13536 }
13537 }
13538
13539 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
13540 {
13541 jim_wide idx;
13542 int end = 0;
13543 const char *str;
13544 Jim_Obj *exprObj = objPtr;
13545
13546 JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object"));
13547
13548
13549 str = Jim_String(objPtr);
13550
13551
13552 if (strncmp(str, "end", 3) == 0) {
13553 end = 1;
13554 str += 3;
13555 idx = 0;
13556 switch (*str) {
13557 case '\0':
13558 exprObj = NULL;
13559 break;
13560
13561 case '-':
13562 case '+':
13563 exprObj = Jim_NewStringObj(interp, str, -1);
13564 break;
13565
13566 default:
13567 goto badindex;
13568 }
13569 }
13570 if (exprObj) {
13571 int ret;
13572 Jim_IncrRefCount(exprObj);
13573 ret = Jim_GetWideExpr(interp, exprObj, &idx);
13574 Jim_DecrRefCount(interp, exprObj);
13575 if (ret != JIM_OK) {
13576 goto badindex;
13577 }
13578 }
13579
13580 if (end) {
13581 if (idx > 0) {
13582 idx = INT_MAX;
13583 }
13584 else {
13585
13586 idx--;
13587 }
13588 }
13589 else if (idx < 0) {
13590 idx = -INT_MAX;
13591 }
13592
13593
13594 Jim_FreeIntRep(interp, objPtr);
13595 objPtr->typePtr = &indexObjType;
13596 objPtr->internalRep.intValue = idx;
13597 return JIM_OK;
13598
13599 badindex:
13600 Jim_SetResultFormatted(interp,
13601 "bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr);
13602 return JIM_ERR;
13603 }
13604
13605 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
13606 {
13607
13608 if (objPtr->typePtr == &intObjType) {
13609 jim_wide val = JimWideValue(objPtr);
13610
13611 if (val < 0)
13612 *indexPtr = -INT_MAX;
13613 else if (val > INT_MAX)
13614 *indexPtr = INT_MAX;
13615 else
13616 *indexPtr = (int)val;
13617 return JIM_OK;
13618 }
13619 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
13620 return JIM_ERR;
13621 *indexPtr = objPtr->internalRep.intValue;
13622 return JIM_OK;
13623 }
13624
13625
13626
13627 static const char * const jimReturnCodes[] = {
13628 "ok",
13629 "error",
13630 "return",
13631 "break",
13632 "continue",
13633 "signal",
13634 "exit",
13635 "eval",
13636 NULL
13637 };
13638
13639 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
13640
13641 static const Jim_ObjType returnCodeObjType = {
13642 "return-code",
13643 NULL,
13644 NULL,
13645 NULL,
13646 JIM_TYPE_NONE,
13647 };
13648
13649 const char *Jim_ReturnCode(int code)
13650 {
13651 if (code < 0 || code >= (int)jimReturnCodesSize) {
13652 return "?";
13653 }
13654 else {
13655 return jimReturnCodes[code];
13656 }
13657 }
13658
13659 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
13660 {
13661 int returnCode;
13662 jim_wide wideValue;
13663
13664
13665 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
13666 returnCode = (int)wideValue;
13667 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
13668 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
13669 return JIM_ERR;
13670 }
13671
13672 Jim_FreeIntRep(interp, objPtr);
13673 objPtr->typePtr = &returnCodeObjType;
13674 objPtr->internalRep.intValue = returnCode;
13675 return JIM_OK;
13676 }
13677
13678 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
13679 {
13680 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
13681 return JIM_ERR;
13682 *intPtr = objPtr->internalRep.intValue;
13683 return JIM_OK;
13684 }
13685
13686 static int JimParseExprOperator(struct JimParserCtx *pc);
13687 static int JimParseExprNumber(struct JimParserCtx *pc);
13688 static int JimParseExprIrrational(struct JimParserCtx *pc);
13689 static int JimParseExprBoolean(struct JimParserCtx *pc);
13690
13691
13692 enum
13693 {
13694
13695
13696
13697 JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
13698 JIM_EXPROP_DIV,
13699 JIM_EXPROP_MOD,
13700 JIM_EXPROP_SUB,
13701 JIM_EXPROP_ADD,
13702 JIM_EXPROP_LSHIFT,
13703 JIM_EXPROP_RSHIFT,
13704 JIM_EXPROP_ROTL,
13705 JIM_EXPROP_ROTR,
13706 JIM_EXPROP_LT,
13707 JIM_EXPROP_GT,
13708 JIM_EXPROP_LTE,
13709 JIM_EXPROP_GTE,
13710 JIM_EXPROP_NUMEQ,
13711 JIM_EXPROP_NUMNE,
13712 JIM_EXPROP_BITAND,
13713 JIM_EXPROP_BITXOR,
13714 JIM_EXPROP_BITOR,
13715 JIM_EXPROP_LOGICAND,
13716 JIM_EXPROP_LOGICOR,
13717 JIM_EXPROP_TERNARY,
13718 JIM_EXPROP_COLON,
13719 JIM_EXPROP_POW,
13720
13721
13722 JIM_EXPROP_STREQ,
13723 JIM_EXPROP_STRNE,
13724 JIM_EXPROP_STRIN,
13725 JIM_EXPROP_STRNI,
13726 JIM_EXPROP_STRLT,
13727 JIM_EXPROP_STRGT,
13728 JIM_EXPROP_STRLE,
13729 JIM_EXPROP_STRGE,
13730
13731
13732 JIM_EXPROP_NOT,
13733 JIM_EXPROP_BITNOT,
13734 JIM_EXPROP_UNARYMINUS,
13735 JIM_EXPROP_UNARYPLUS,
13736
13737
13738 JIM_EXPROP_FUNC_INT,
13739 JIM_EXPROP_FUNC_WIDE,
13740 JIM_EXPROP_FUNC_ABS,
13741 JIM_EXPROP_FUNC_DOUBLE,
13742 JIM_EXPROP_FUNC_ROUND,
13743 JIM_EXPROP_FUNC_RAND,
13744 JIM_EXPROP_FUNC_SRAND,
13745
13746
13747 JIM_EXPROP_FUNC_SIN,
13748 JIM_EXPROP_FUNC_COS,
13749 JIM_EXPROP_FUNC_TAN,
13750 JIM_EXPROP_FUNC_ASIN,
13751 JIM_EXPROP_FUNC_ACOS,
13752 JIM_EXPROP_FUNC_ATAN,
13753 JIM_EXPROP_FUNC_ATAN2,
13754 JIM_EXPROP_FUNC_SINH,
13755 JIM_EXPROP_FUNC_COSH,
13756 JIM_EXPROP_FUNC_TANH,
13757 JIM_EXPROP_FUNC_CEIL,
13758 JIM_EXPROP_FUNC_FLOOR,
13759 JIM_EXPROP_FUNC_EXP,
13760 JIM_EXPROP_FUNC_LOG,
13761 JIM_EXPROP_FUNC_LOG10,
13762 JIM_EXPROP_FUNC_SQRT,
13763 JIM_EXPROP_FUNC_POW,
13764 JIM_EXPROP_FUNC_HYPOT,
13765 JIM_EXPROP_FUNC_FMOD,
13766 };
13767
13768 struct JimExprNode {
13769 int type;
13770 struct Jim_Obj *objPtr;
13771
13772 struct JimExprNode *left;
13773 struct JimExprNode *right;
13774 struct JimExprNode *ternary;
13775 };
13776
13777
13778 typedef struct Jim_ExprOperator
13779 {
13780 const char *name;
13781 int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
13782 unsigned char precedence;
13783 unsigned char arity;
13784 unsigned char attr;
13785 unsigned char namelen;
13786 } Jim_ExprOperator;
13787
13788 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
13789 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
13790 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
13791
13792 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
13793 {
13794 int intresult = 1;
13795 int rc, bA = 0;
13796 double dA, dC = 0;
13797 jim_wide wA, wC = 0;
13798 Jim_Obj *A;
13799
13800 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13801 return rc;
13802 }
13803
13804 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
13805 switch (node->type) {
13806 case JIM_EXPROP_FUNC_INT:
13807 case JIM_EXPROP_FUNC_WIDE:
13808 case JIM_EXPROP_FUNC_ROUND:
13809 case JIM_EXPROP_UNARYPLUS:
13810 wC = wA;
13811 break;
13812 case JIM_EXPROP_FUNC_DOUBLE:
13813 dC = wA;
13814 intresult = 0;
13815 break;
13816 case JIM_EXPROP_FUNC_ABS:
13817 wC = wA >= 0 ? wA : -wA;
13818 break;
13819 case JIM_EXPROP_UNARYMINUS:
13820 wC = -wA;
13821 break;
13822 case JIM_EXPROP_NOT:
13823 wC = !wA;
13824 break;
13825 default:
13826 abort();
13827 }
13828 }
13829 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
13830 switch (node->type) {
13831 case JIM_EXPROP_FUNC_INT:
13832 case JIM_EXPROP_FUNC_WIDE:
13833 wC = dA;
13834 break;
13835 case JIM_EXPROP_FUNC_ROUND:
13836 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
13837 break;
13838 case JIM_EXPROP_FUNC_DOUBLE:
13839 case JIM_EXPROP_UNARYPLUS:
13840 dC = dA;
13841 intresult = 0;
13842 break;
13843 case JIM_EXPROP_FUNC_ABS:
13844 #ifdef JIM_MATH_FUNCTIONS
13845 dC = fabs(dA);
13846 #else
13847 dC = dA >= 0 ? dA : -dA;
13848 #endif
13849 intresult = 0;
13850 break;
13851 case JIM_EXPROP_UNARYMINUS:
13852 dC = -dA;
13853 intresult = 0;
13854 break;
13855 case JIM_EXPROP_NOT:
13856 wC = !dA;
13857 break;
13858 default:
13859 abort();
13860 }
13861 }
13862 else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) {
13863 switch (node->type) {
13864 case JIM_EXPROP_NOT:
13865 wC = !bA;
13866 break;
13867 default:
13868 abort();
13869 }
13870 }
13871
13872 if (rc == JIM_OK) {
13873 if (intresult) {
13874 Jim_SetResultInt(interp, wC);
13875 }
13876 else {
13877 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
13878 }
13879 }
13880
13881 Jim_DecrRefCount(interp, A);
13882
13883 return rc;
13884 }
13885
13886 static double JimRandDouble(Jim_Interp *interp)
13887 {
13888 unsigned long x;
13889 JimRandomBytes(interp, &x, sizeof(x));
13890
13891 return (double)x / (double)~0UL;
13892 }
13893
13894 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
13895 {
13896 jim_wide wA;
13897 Jim_Obj *A;
13898 int rc;
13899
13900 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13901 return rc;
13902 }
13903
13904 rc = Jim_GetWide(interp, A, &wA);
13905 if (rc == JIM_OK) {
13906 switch (node->type) {
13907 case JIM_EXPROP_BITNOT:
13908 Jim_SetResultInt(interp, ~wA);
13909 break;
13910 case JIM_EXPROP_FUNC_SRAND:
13911 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
13912 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
13913 break;
13914 default:
13915 abort();
13916 }
13917 }
13918
13919 Jim_DecrRefCount(interp, A);
13920
13921 return rc;
13922 }
13923
13924 static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
13925 {
13926 JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
13927
13928 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
13929
13930 return JIM_OK;
13931 }
13932
13933 #ifdef JIM_MATH_FUNCTIONS
13934 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
13935 {
13936 int rc;
13937 double dA, dC;
13938 Jim_Obj *A;
13939
13940 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13941 return rc;
13942 }
13943
13944 rc = Jim_GetDouble(interp, A, &dA);
13945 if (rc == JIM_OK) {
13946 switch (node->type) {
13947 case JIM_EXPROP_FUNC_SIN:
13948 dC = sin(dA);
13949 break;
13950 case JIM_EXPROP_FUNC_COS:
13951 dC = cos(dA);
13952 break;
13953 case JIM_EXPROP_FUNC_TAN:
13954 dC = tan(dA);
13955 break;
13956 case JIM_EXPROP_FUNC_ASIN:
13957 dC = asin(dA);
13958 break;
13959 case JIM_EXPROP_FUNC_ACOS:
13960 dC = acos(dA);
13961 break;
13962 case JIM_EXPROP_FUNC_ATAN:
13963 dC = atan(dA);
13964 break;
13965 case JIM_EXPROP_FUNC_SINH:
13966 dC = sinh(dA);
13967 break;
13968 case JIM_EXPROP_FUNC_COSH:
13969 dC = cosh(dA);
13970 break;
13971 case JIM_EXPROP_FUNC_TANH:
13972 dC = tanh(dA);
13973 break;
13974 case JIM_EXPROP_FUNC_CEIL:
13975 dC = ceil(dA);
13976 break;
13977 case JIM_EXPROP_FUNC_FLOOR:
13978 dC = floor(dA);
13979 break;
13980 case JIM_EXPROP_FUNC_EXP:
13981 dC = exp(dA);
13982 break;
13983 case JIM_EXPROP_FUNC_LOG:
13984 dC = log(dA);
13985 break;
13986 case JIM_EXPROP_FUNC_LOG10:
13987 dC = log10(dA);
13988 break;
13989 case JIM_EXPROP_FUNC_SQRT:
13990 dC = sqrt(dA);
13991 break;
13992 default:
13993 abort();
13994 }
13995 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
13996 }
13997
13998 Jim_DecrRefCount(interp, A);
13999
14000 return rc;
14001 }
14002 #endif
14003
14004
14005 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
14006 {
14007 jim_wide wA, wB;
14008 int rc;
14009 Jim_Obj *A, *B;
14010
14011 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14012 return rc;
14013 }
14014 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14015 Jim_DecrRefCount(interp, A);
14016 return rc;
14017 }
14018
14019 rc = JIM_ERR;
14020
14021 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
14022 jim_wide wC;
14023
14024 rc = JIM_OK;
14025
14026 switch (node->type) {
14027 case JIM_EXPROP_LSHIFT:
14028 wC = wA << wB;
14029 break;
14030 case JIM_EXPROP_RSHIFT:
14031 wC = wA >> wB;
14032 break;
14033 case JIM_EXPROP_BITAND:
14034 wC = wA & wB;
14035 break;
14036 case JIM_EXPROP_BITXOR:
14037 wC = wA ^ wB;
14038 break;
14039 case JIM_EXPROP_BITOR:
14040 wC = wA | wB;
14041 break;
14042 case JIM_EXPROP_MOD:
14043 if (wB == 0) {
14044 wC = 0;
14045 Jim_SetResultString(interp, "Division by zero", -1);
14046 rc = JIM_ERR;
14047 }
14048 else {
14049 int negative = 0;
14050
14051 if (wB < 0) {
14052 wB = -wB;
14053 wA = -wA;
14054 negative = 1;
14055 }
14056 wC = wA % wB;
14057 if (wC < 0) {
14058 wC += wB;
14059 }
14060 if (negative) {
14061 wC = -wC;
14062 }
14063 }
14064 break;
14065 case JIM_EXPROP_ROTL:
14066 case JIM_EXPROP_ROTR:{
14067
14068 unsigned long uA = (unsigned long)wA;
14069 unsigned long uB = (unsigned long)wB;
14070 const unsigned int S = sizeof(unsigned long) * 8;
14071
14072
14073 uB %= S;
14074
14075 if (node->type == JIM_EXPROP_ROTR) {
14076 uB = S - uB;
14077 }
14078 wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
14079 break;
14080 }
14081 default:
14082 abort();
14083 }
14084 Jim_SetResultInt(interp, wC);
14085 }
14086
14087 Jim_DecrRefCount(interp, A);
14088 Jim_DecrRefCount(interp, B);
14089
14090 return rc;
14091 }
14092
14093
14094
14095 static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
14096 {
14097 int rc = JIM_OK;
14098 double dA, dB, dC = 0;
14099 jim_wide wA, wB, wC = 0;
14100 Jim_Obj *A, *B;
14101
14102 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14103 return rc;
14104 }
14105 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14106 Jim_DecrRefCount(interp, A);
14107 return rc;
14108 }
14109
14110 if ((A->typePtr != &doubleObjType || A->bytes) &&
14111 (B->typePtr != &doubleObjType || B->bytes) &&
14112 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
14113
14114
14115
14116 switch (node->type) {
14117 case JIM_EXPROP_POW:
14118 case JIM_EXPROP_FUNC_POW:
14119 if (wA == 0 && wB < 0) {
14120 Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
14121 rc = JIM_ERR;
14122 goto done;
14123 }
14124 wC = JimPowWide(wA, wB);
14125 goto intresult;
14126 case JIM_EXPROP_ADD:
14127 wC = wA + wB;
14128 goto intresult;
14129 case JIM_EXPROP_SUB:
14130 wC = wA - wB;
14131 goto intresult;
14132 case JIM_EXPROP_MUL:
14133 wC = wA * wB;
14134 goto intresult;
14135 case JIM_EXPROP_DIV:
14136 if (wB == 0) {
14137 Jim_SetResultString(interp, "Division by zero", -1);
14138 rc = JIM_ERR;
14139 goto done;
14140 }
14141 else {
14142 if (wB < 0) {
14143 wB = -wB;
14144 wA = -wA;
14145 }
14146 wC = wA / wB;
14147 if (wA % wB < 0) {
14148 wC--;
14149 }
14150 goto intresult;
14151 }
14152 case JIM_EXPROP_LT:
14153 wC = wA < wB;
14154 goto intresult;
14155 case JIM_EXPROP_GT:
14156 wC = wA > wB;
14157 goto intresult;
14158 case JIM_EXPROP_LTE:
14159 wC = wA <= wB;
14160 goto intresult;
14161 case JIM_EXPROP_GTE:
14162 wC = wA >= wB;
14163 goto intresult;
14164 case JIM_EXPROP_NUMEQ:
14165 wC = wA == wB;
14166 goto intresult;
14167 case JIM_EXPROP_NUMNE:
14168 wC = wA != wB;
14169 goto intresult;
14170 }
14171 }
14172 if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
14173 switch (node->type) {
14174 #ifndef JIM_MATH_FUNCTIONS
14175 case JIM_EXPROP_POW:
14176 case JIM_EXPROP_FUNC_POW:
14177 case JIM_EXPROP_FUNC_ATAN2:
14178 case JIM_EXPROP_FUNC_HYPOT:
14179 case JIM_EXPROP_FUNC_FMOD:
14180 Jim_SetResultString(interp, "unsupported", -1);
14181 rc = JIM_ERR;
14182 goto done;
14183 #else
14184 case JIM_EXPROP_POW:
14185 case JIM_EXPROP_FUNC_POW:
14186 dC = pow(dA, dB);
14187 goto doubleresult;
14188 case JIM_EXPROP_FUNC_ATAN2:
14189 dC = atan2(dA, dB);
14190 goto doubleresult;
14191 case JIM_EXPROP_FUNC_HYPOT:
14192 dC = hypot(dA, dB);
14193 goto doubleresult;
14194 case JIM_EXPROP_FUNC_FMOD:
14195 dC = fmod(dA, dB);
14196 goto doubleresult;
14197 #endif
14198 case JIM_EXPROP_ADD:
14199 dC = dA + dB;
14200 goto doubleresult;
14201 case JIM_EXPROP_SUB:
14202 dC = dA - dB;
14203 goto doubleresult;
14204 case JIM_EXPROP_MUL:
14205 dC = dA * dB;
14206 goto doubleresult;
14207 case JIM_EXPROP_DIV:
14208 if (dB == 0) {
14209 #ifdef INFINITY
14210 dC = dA < 0 ? -INFINITY : INFINITY;
14211 #else
14212 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
14213 #endif
14214 }
14215 else {
14216 dC = dA / dB;
14217 }
14218 goto doubleresult;
14219 case JIM_EXPROP_LT:
14220 wC = dA < dB;
14221 goto intresult;
14222 case JIM_EXPROP_GT:
14223 wC = dA > dB;
14224 goto intresult;
14225 case JIM_EXPROP_LTE:
14226 wC = dA <= dB;
14227 goto intresult;
14228 case JIM_EXPROP_GTE:
14229 wC = dA >= dB;
14230 goto intresult;
14231 case JIM_EXPROP_NUMEQ:
14232 wC = dA == dB;
14233 goto intresult;
14234 case JIM_EXPROP_NUMNE:
14235 wC = dA != dB;
14236 goto intresult;
14237 }
14238 }
14239 else {
14240
14241
14242
14243 int i = Jim_StringCompareObj(interp, A, B, 0);
14244
14245 switch (node->type) {
14246 case JIM_EXPROP_LT:
14247 wC = i < 0;
14248 goto intresult;
14249 case JIM_EXPROP_GT:
14250 wC = i > 0;
14251 goto intresult;
14252 case JIM_EXPROP_LTE:
14253 wC = i <= 0;
14254 goto intresult;
14255 case JIM_EXPROP_GTE:
14256 wC = i >= 0;
14257 goto intresult;
14258 case JIM_EXPROP_NUMEQ:
14259 wC = i == 0;
14260 goto intresult;
14261 case JIM_EXPROP_NUMNE:
14262 wC = i != 0;
14263 goto intresult;
14264 }
14265 }
14266
14267 rc = JIM_ERR;
14268 done:
14269 Jim_DecrRefCount(interp, A);
14270 Jim_DecrRefCount(interp, B);
14271 return rc;
14272 intresult:
14273 Jim_SetResultInt(interp, wC);
14274 goto done;
14275 doubleresult:
14276 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
14277 goto done;
14278 }
14279
14280 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
14281 {
14282 int listlen;
14283 int i;
14284
14285 listlen = Jim_ListLength(interp, listObjPtr);
14286 for (i = 0; i < listlen; i++) {
14287 if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
14288 return 1;
14289 }
14290 }
14291 return 0;
14292 }
14293
14294
14295
14296 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
14297 {
14298 Jim_Obj *A, *B;
14299 jim_wide wC;
14300 int comp, rc;
14301
14302 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14303 return rc;
14304 }
14305 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14306 Jim_DecrRefCount(interp, A);
14307 return rc;
14308 }
14309
14310 switch (node->type) {
14311 case JIM_EXPROP_STREQ:
14312 case JIM_EXPROP_STRNE:
14313 wC = Jim_StringEqObj(A, B);
14314 if (node->type == JIM_EXPROP_STRNE) {
14315 wC = !wC;
14316 }
14317 break;
14318 case JIM_EXPROP_STRLT:
14319 case JIM_EXPROP_STRGT:
14320 case JIM_EXPROP_STRLE:
14321 case JIM_EXPROP_STRGE:
14322 comp = Jim_StringCompareObj(interp, A, B, 0);
14323 if (node->type == JIM_EXPROP_STRLT) {
14324 wC = comp == -1;
14325 } else if (node->type == JIM_EXPROP_STRGT) {
14326 wC = comp == 1;
14327 } else if (node->type == JIM_EXPROP_STRLE) {
14328 wC = comp == -1 || comp == 0;
14329 } else {
14330 wC = comp == 0 || comp == 1;
14331 }
14332 break;
14333 case JIM_EXPROP_STRIN:
14334 wC = JimSearchList(interp, B, A);
14335 break;
14336 case JIM_EXPROP_STRNI:
14337 wC = !JimSearchList(interp, B, A);
14338 break;
14339 default:
14340 abort();
14341 }
14342 Jim_SetResultInt(interp, wC);
14343
14344 Jim_DecrRefCount(interp, A);
14345 Jim_DecrRefCount(interp, B);
14346
14347 return rc;
14348 }
14349
14350 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
14351 {
14352 long l;
14353 double d;
14354 int b;
14355 int ret = -1;
14356
14357
14358 Jim_IncrRefCount(obj);
14359
14360 if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
14361 ret = (l != 0);
14362 }
14363 else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
14364 ret = (d != 0);
14365 }
14366 else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
14367 ret = (b != 0);
14368 }
14369
14370 Jim_DecrRefCount(interp, obj);
14371 return ret;
14372 }
14373
14374 static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
14375 {
14376
14377 int result = JimExprGetTermBoolean(interp, node->left);
14378
14379 if (result == 1) {
14380
14381 result = JimExprGetTermBoolean(interp, node->right);
14382 }
14383 if (result == -1) {
14384 return JIM_ERR;
14385 }
14386 Jim_SetResultInt(interp, result);
14387 return JIM_OK;
14388 }
14389
14390 static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
14391 {
14392
14393 int result = JimExprGetTermBoolean(interp, node->left);
14394
14395 if (result == 0) {
14396
14397 result = JimExprGetTermBoolean(interp, node->right);
14398 }
14399 if (result == -1) {
14400 return JIM_ERR;
14401 }
14402 Jim_SetResultInt(interp, result);
14403 return JIM_OK;
14404 }
14405
14406 static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
14407 {
14408
14409 int result = JimExprGetTermBoolean(interp, node->left);
14410
14411 if (result == 1) {
14412
14413 return JimExprEvalTermNode(interp, node->right);
14414 }
14415 else if (result == 0) {
14416
14417 return JimExprEvalTermNode(interp, node->ternary);
14418 }
14419
14420 return JIM_ERR;
14421 }
14422
14423 enum
14424 {
14425 OP_FUNC = 0x0001,
14426 OP_RIGHT_ASSOC = 0x0002,
14427 };
14428
14429 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
14430 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
14431
14432 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
14433 OPRINIT("*", 110, 2, JimExprOpBin),
14434 OPRINIT("/", 110, 2, JimExprOpBin),
14435 OPRINIT("%", 110, 2, JimExprOpIntBin),
14436
14437 OPRINIT("-", 100, 2, JimExprOpBin),
14438 OPRINIT("+", 100, 2, JimExprOpBin),
14439
14440 OPRINIT("<<", 90, 2, JimExprOpIntBin),
14441 OPRINIT(">>", 90, 2, JimExprOpIntBin),
14442
14443 OPRINIT("<<<", 90, 2, JimExprOpIntBin),
14444 OPRINIT(">>>", 90, 2, JimExprOpIntBin),
14445
14446 OPRINIT("<", 80, 2, JimExprOpBin),
14447 OPRINIT(">", 80, 2, JimExprOpBin),
14448 OPRINIT("<=", 80, 2, JimExprOpBin),
14449 OPRINIT(">=", 80, 2, JimExprOpBin),
14450
14451 OPRINIT("==", 70, 2, JimExprOpBin),
14452 OPRINIT("!=", 70, 2, JimExprOpBin),
14453
14454 OPRINIT("&", 50, 2, JimExprOpIntBin),
14455 OPRINIT("^", 49, 2, JimExprOpIntBin),
14456 OPRINIT("|", 48, 2, JimExprOpIntBin),
14457
14458 OPRINIT("&&", 10, 2, JimExprOpAnd),
14459 OPRINIT("||", 9, 2, JimExprOpOr),
14460 OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
14461 OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
14462
14463
14464 OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
14465
14466 OPRINIT("eq", 60, 2, JimExprOpStrBin),
14467 OPRINIT("ne", 60, 2, JimExprOpStrBin),
14468
14469 OPRINIT("in", 55, 2, JimExprOpStrBin),
14470 OPRINIT("ni", 55, 2, JimExprOpStrBin),
14471
14472 OPRINIT("lt", 75, 2, JimExprOpStrBin),
14473 OPRINIT("gt", 75, 2, JimExprOpStrBin),
14474 OPRINIT("le", 75, 2, JimExprOpStrBin),
14475 OPRINIT("ge", 75, 2, JimExprOpStrBin),
14476
14477 OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14478 OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
14479 OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14480 OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14481
14482
14483
14484 OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
14485 OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
14486 OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
14487 OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
14488 OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
14489 OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
14490 OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
14491
14492 #ifdef JIM_MATH_FUNCTIONS
14493 OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14494 OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14495 OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14496 OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14497 OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14498 OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14499 OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
14500 OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14501 OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14502 OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14503 OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14504 OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14505 OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14506 OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14507 OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14508 OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14509 OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
14510 OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
14511 OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
14512 #endif
14513 };
14514 #undef OPRINIT
14515 #undef OPRINIT_ATTR
14516
14517 #define JIM_EXPR_OPERATORS_NUM \
14518 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
14519
14520 static int JimParseExpression(struct JimParserCtx *pc)
14521 {
14522 pc->errmsg = NULL;
14523
14524 while (1) {
14525
14526 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
14527 if (*pc->p == '\n') {
14528 pc->linenr++;
14529 }
14530 pc->p++;
14531 pc->len--;
14532 }
14533
14534 if (*pc->p == '#') {
14535 JimParseComment(pc);
14536
14537 continue;
14538 }
14539 break;
14540 }
14541
14542
14543 pc->tline = pc->linenr;
14544 pc->tstart = pc->p;
14545
14546 if (pc->len == 0) {
14547 pc->tend = pc->p;
14548 pc->tt = JIM_TT_EOL;
14549 pc->eof = 1;
14550 return JIM_OK;
14551 }
14552 switch (*(pc->p)) {
14553 case '(':
14554 pc->tt = JIM_TT_SUBEXPR_START;
14555 goto singlechar;
14556 case ')':
14557 pc->tt = JIM_TT_SUBEXPR_END;
14558 goto singlechar;
14559 case ',':
14560 pc->tt = JIM_TT_SUBEXPR_COMMA;
14561 singlechar:
14562 pc->tend = pc->p;
14563 pc->p++;
14564 pc->len--;
14565 break;
14566 case '[':
14567 return JimParseCmd(pc);
14568 case '$':
14569 if (JimParseVar(pc) == JIM_ERR)
14570 return JimParseExprOperator(pc);
14571 else {
14572
14573 if (pc->tt == JIM_TT_EXPRSUGAR) {
14574 pc->errmsg = "nesting expr in expr is not allowed";
14575 return JIM_ERR;
14576 }
14577 return JIM_OK;
14578 }
14579 break;
14580 case '0':
14581 case '1':
14582 case '2':
14583 case '3':
14584 case '4':
14585 case '5':
14586 case '6':
14587 case '7':
14588 case '8':
14589 case '9':
14590 case '.':
14591 return JimParseExprNumber(pc);
14592 case '"':
14593 return JimParseQuote(pc);
14594 case '{':
14595 return JimParseBrace(pc);
14596
14597 case 'N':
14598 case 'I':
14599 case 'n':
14600 case 'i':
14601 if (JimParseExprIrrational(pc) == JIM_ERR)
14602 if (JimParseExprBoolean(pc) == JIM_ERR)
14603 return JimParseExprOperator(pc);
14604 break;
14605 case 't':
14606 case 'f':
14607 case 'o':
14608 case 'y':
14609 if (JimParseExprBoolean(pc) == JIM_ERR)
14610 return JimParseExprOperator(pc);
14611 break;
14612 default:
14613 return JimParseExprOperator(pc);
14614 break;
14615 }
14616 return JIM_OK;
14617 }
14618
14619 static int JimParseExprNumber(struct JimParserCtx *pc)
14620 {
14621 char *end;
14622
14623
14624 pc->tt = JIM_TT_EXPR_INT;
14625
14626 jim_strtoull(pc->p, (char **)&pc->p);
14627
14628 if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
14629 if (strtod(pc->tstart, &end)) { }
14630 if (end == pc->tstart)
14631 return JIM_ERR;
14632 if (end > pc->p) {
14633
14634 pc->tt = JIM_TT_EXPR_DOUBLE;
14635 pc->p = end;
14636 }
14637 }
14638 pc->tend = pc->p - 1;
14639 pc->len -= (pc->p - pc->tstart);
14640 return JIM_OK;
14641 }
14642
14643 static int JimParseExprIrrational(struct JimParserCtx *pc)
14644 {
14645 const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
14646 int i;
14647
14648 for (i = 0; irrationals[i]; i++) {
14649 const char *irr = irrationals[i];
14650
14651 if (strncmp(irr, pc->p, 3) == 0) {
14652 pc->p += 3;
14653 pc->len -= 3;
14654 pc->tend = pc->p - 1;
14655 pc->tt = JIM_TT_EXPR_DOUBLE;
14656 return JIM_OK;
14657 }
14658 }
14659 return JIM_ERR;
14660 }
14661
14662 static int JimParseExprBoolean(struct JimParserCtx *pc)
14663 {
14664 int i;
14665 for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) {
14666 if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) {
14667 pc->p += jim_true_false_lens[i];
14668 pc->len -= jim_true_false_lens[i];
14669 pc->tend = pc->p - 1;
14670 pc->tt = JIM_TT_EXPR_BOOLEAN;
14671 return JIM_OK;
14672 }
14673 }
14674 return JIM_ERR;
14675 }
14676
14677 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
14678 {
14679 static Jim_ExprOperator dummy_op;
14680 if (opcode < JIM_TT_EXPR_OP) {
14681 return &dummy_op;
14682 }
14683 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
14684 }
14685
14686 static int JimParseExprOperator(struct JimParserCtx *pc)
14687 {
14688 int i;
14689 const struct Jim_ExprOperator *bestOp = NULL;
14690 int bestLen = 0;
14691
14692
14693 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
14694 const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
14695
14696 if (op->name[0] != pc->p[0]) {
14697 continue;
14698 }
14699
14700 if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
14701 bestOp = op;
14702 bestLen = op->namelen;
14703 }
14704 }
14705 if (bestOp == NULL) {
14706 return JIM_ERR;
14707 }
14708
14709
14710 if (bestOp->attr & OP_FUNC) {
14711 const char *p = pc->p + bestLen;
14712 int len = pc->len - bestLen;
14713
14714 while (len && isspace(UCHAR(*p))) {
14715 len--;
14716 p++;
14717 }
14718 if (*p != '(') {
14719 pc->errmsg = "function requires parentheses";
14720 return JIM_ERR;
14721 }
14722 }
14723 pc->tend = pc->p + bestLen - 1;
14724 pc->p += bestLen;
14725 pc->len -= bestLen;
14726
14727 pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
14728 return JIM_OK;
14729 }
14730
14731
14732 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14733 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14734 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
14735
14736 static const Jim_ObjType exprObjType = {
14737 "expression",
14738 FreeExprInternalRep,
14739 DupExprInternalRep,
14740 NULL,
14741 JIM_TYPE_NONE,
14742 };
14743
14744
14745 struct ExprTree
14746 {
14747 struct JimExprNode *expr;
14748 struct JimExprNode *nodes;
14749 int len;
14750 int inUse;
14751 };
14752
14753 static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
14754 {
14755 int i;
14756 for (i = 0; i < num; i++) {
14757 if (nodes[i].objPtr) {
14758 Jim_DecrRefCount(interp, nodes[i].objPtr);
14759 }
14760 }
14761 Jim_Free(nodes);
14762 }
14763
14764 static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
14765 {
14766 ExprTreeFreeNodes(interp, expr->nodes, expr->len);
14767 Jim_Free(expr);
14768 }
14769
14770 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14771 {
14772 struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
14773
14774 if (expr) {
14775 if (--expr->inUse != 0) {
14776 return;
14777 }
14778
14779 ExprTreeFree(interp, expr);
14780 }
14781 }
14782
14783 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14784 {
14785 JIM_NOTUSED(interp);
14786 JIM_NOTUSED(srcPtr);
14787
14788
14789 dupPtr->typePtr = NULL;
14790 }
14791
14792 struct ExprBuilder {
14793 int parencount;
14794 int level;
14795 ParseToken *token;
14796 ParseToken *first_token;
14797 Jim_Stack stack;
14798 Jim_Obj *exprObjPtr;
14799 Jim_Obj *fileNameObj;
14800 struct JimExprNode *nodes;
14801 struct JimExprNode *next;
14802 };
14803
14804 #ifdef DEBUG_SHOW_EXPR
14805 static void JimShowExprNode(struct JimExprNode *node, int level)
14806 {
14807 int i;
14808 for (i = 0; i < level; i++) {
14809 printf(" ");
14810 }
14811 if (TOKEN_IS_EXPR_OP(node->type)) {
14812 printf("%s\n", jim_tt_name(node->type));
14813 if (node->left) {
14814 JimShowExprNode(node->left, level + 1);
14815 }
14816 if (node->right) {
14817 JimShowExprNode(node->right, level + 1);
14818 }
14819 if (node->ternary) {
14820 JimShowExprNode(node->ternary, level + 1);
14821 }
14822 }
14823 else {
14824 printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
14825 }
14826 }
14827 #endif
14828
14829 #define EXPR_UNTIL_CLOSE 0x0001
14830 #define EXPR_FUNC_ARGS 0x0002
14831 #define EXPR_TERNARY 0x0004
14832
14833 static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) {
14834 int rc;
14835 struct JimExprNode *node;
14836
14837 int exp_stacklen = builder->stack.len + exp_numterms;
14838
14839 if (builder->level++ > 200) {
14840 Jim_SetResultString(interp, "Expression too complex", -1);
14841 return JIM_ERR;
14842 }
14843
14844 while (builder->token->type != JIM_TT_EOL) {
14845 ParseToken *t = builder->token++;
14846 int prevtt;
14847
14848 if (t == builder->first_token) {
14849 prevtt = JIM_TT_NONE;
14850 }
14851 else {
14852 prevtt = t[-1].type;
14853 }
14854
14855 if (t->type == JIM_TT_SUBEXPR_START) {
14856 if (builder->stack.len == exp_stacklen) {
14857 Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
14858 return JIM_ERR;
14859 }
14860 builder->parencount++;
14861 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
14862 if (rc != JIM_OK) {
14863 return rc;
14864 }
14865
14866 }
14867 else if (t->type == JIM_TT_SUBEXPR_END) {
14868 if (!(flags & EXPR_UNTIL_CLOSE)) {
14869 if (builder->stack.len == exp_stacklen && builder->level > 1) {
14870 builder->token--;
14871 builder->level--;
14872 return JIM_OK;
14873 }
14874 Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
14875 return JIM_ERR;
14876 }
14877 builder->parencount--;
14878 if (builder->stack.len == exp_stacklen) {
14879
14880 break;
14881 }
14882 }
14883 else if (t->type == JIM_TT_SUBEXPR_COMMA) {
14884 if (!(flags & EXPR_FUNC_ARGS)) {
14885 if (builder->stack.len == exp_stacklen) {
14886
14887 builder->token--;
14888 builder->level--;
14889 return JIM_OK;
14890 }
14891 Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
14892 return JIM_ERR;
14893 }
14894 else {
14895
14896 if (builder->stack.len > exp_stacklen) {
14897 Jim_SetResultFormatted(interp, "too many arguments to math function");
14898 return JIM_ERR;
14899 }
14900 }
14901
14902 }
14903 else if (t->type == JIM_EXPROP_COLON) {
14904 if (!(flags & EXPR_TERNARY)) {
14905 if (builder->level != 1) {
14906
14907 builder->token--;
14908 builder->level--;
14909 return JIM_OK;
14910 }
14911 Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
14912 return JIM_ERR;
14913 }
14914 if (builder->stack.len == exp_stacklen) {
14915
14916 builder->token--;
14917 builder->level--;
14918 return JIM_OK;
14919 }
14920
14921 }
14922 else if (TOKEN_IS_EXPR_OP(t->type)) {
14923 const struct Jim_ExprOperator *op;
14924
14925
14926 if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
14927 if (t->type == JIM_EXPROP_SUB) {
14928 t->type = JIM_EXPROP_UNARYMINUS;
14929 }
14930 else if (t->type == JIM_EXPROP_ADD) {
14931 t->type = JIM_EXPROP_UNARYPLUS;
14932 }
14933 }
14934
14935 op = JimExprOperatorInfoByOpcode(t->type);
14936
14937 if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
14938
14939 builder->token--;
14940 break;
14941 }
14942
14943 if (op->attr & OP_FUNC) {
14944 if (builder->token->type != JIM_TT_SUBEXPR_START) {
14945 Jim_SetResultString(interp, "missing arguments for math function", -1);
14946 return JIM_ERR;
14947 }
14948 builder->token++;
14949 if (op->arity == 0) {
14950 if (builder->token->type != JIM_TT_SUBEXPR_END) {
14951 Jim_SetResultString(interp, "too many arguments for math function", -1);
14952 return JIM_ERR;
14953 }
14954 builder->token++;
14955 goto noargs;
14956 }
14957 builder->parencount++;
14958
14959
14960 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
14961 }
14962 else if (t->type == JIM_EXPROP_TERNARY) {
14963
14964 rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
14965 }
14966 else {
14967 rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
14968 }
14969
14970 if (rc != JIM_OK) {
14971 return rc;
14972 }
14973
14974 noargs:
14975 node = builder->next++;
14976 node->type = t->type;
14977
14978 if (op->arity >= 3) {
14979 node->ternary = Jim_StackPop(&builder->stack);
14980 if (node->ternary == NULL) {
14981 goto missingoperand;
14982 }
14983 }
14984 if (op->arity >= 2) {
14985 node->right = Jim_StackPop(&builder->stack);
14986 if (node->right == NULL) {
14987 goto missingoperand;
14988 }
14989 }
14990 if (op->arity >= 1) {
14991 node->left = Jim_StackPop(&builder->stack);
14992 if (node->left == NULL) {
14993 missingoperand:
14994 Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
14995 builder->next--;
14996 return JIM_ERR;
14997
14998 }
14999 }
15000
15001
15002 Jim_StackPush(&builder->stack, node);
15003 }
15004 else {
15005 Jim_Obj *objPtr = NULL;
15006
15007
15008
15009
15010 if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
15011 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
15012 return JIM_ERR;
15013 }
15014
15015
15016 if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
15017 char *endptr;
15018 if (t->type == JIM_TT_EXPR_INT) {
15019 objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
15020 }
15021 else {
15022 objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
15023 }
15024 if (endptr != t->token + t->len) {
15025
15026 Jim_FreeNewObj(interp, objPtr);
15027 objPtr = NULL;
15028 }
15029 }
15030
15031 if (!objPtr) {
15032
15033 objPtr = Jim_NewStringObj(interp, t->token, t->len);
15034 if (t->type == JIM_TT_CMD) {
15035
15036 Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
15037 }
15038 }
15039
15040
15041 node = builder->next++;
15042 node->objPtr = objPtr;
15043 Jim_IncrRefCount(node->objPtr);
15044 node->type = t->type;
15045 Jim_StackPush(&builder->stack, node);
15046 }
15047 }
15048
15049 if (builder->stack.len == exp_stacklen) {
15050 builder->level--;
15051 return JIM_OK;
15052 }
15053
15054 if ((flags & EXPR_FUNC_ARGS)) {
15055 Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
15056 }
15057 else {
15058 if (builder->stack.len < exp_stacklen) {
15059 if (builder->level == 0) {
15060 Jim_SetResultFormatted(interp, "empty expression");
15061 }
15062 else {
15063 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
15064 }
15065 }
15066 else {
15067 Jim_SetResultFormatted(interp, "extra terms after expression");
15068 }
15069 }
15070
15071 return JIM_ERR;
15072 }
15073
15074 static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
15075 {
15076 struct ExprTree *expr;
15077 struct ExprBuilder builder;
15078 int rc;
15079 struct JimExprNode *top = NULL;
15080
15081 builder.parencount = 0;
15082 builder.level = 0;
15083 builder.token = builder.first_token = tokenlist->list;
15084 builder.exprObjPtr = exprObjPtr;
15085 builder.fileNameObj = fileNameObj;
15086
15087 builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
15088 memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
15089 builder.next = builder.nodes;
15090 Jim_InitStack(&builder.stack);
15091
15092 rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
15093
15094 if (rc == JIM_OK) {
15095 top = Jim_StackPop(&builder.stack);
15096
15097 if (builder.parencount) {
15098 Jim_SetResultString(interp, "missing close parenthesis", -1);
15099 rc = JIM_ERR;
15100 }
15101 }
15102
15103
15104 Jim_FreeStack(&builder.stack);
15105
15106 if (rc != JIM_OK) {
15107 ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
15108 return NULL;
15109 }
15110
15111 expr = Jim_Alloc(sizeof(*expr));
15112 expr->inUse = 1;
15113 expr->expr = top;
15114 expr->nodes = builder.nodes;
15115 expr->len = builder.next - builder.nodes;
15116
15117 assert(expr->len <= tokenlist->count - 1);
15118
15119 return expr;
15120 }
15121
15122 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
15123 {
15124 int exprTextLen;
15125 const char *exprText;
15126 struct JimParserCtx parser;
15127 struct ExprTree *expr;
15128 ParseTokenList tokenlist;
15129 int line;
15130 Jim_Obj *fileNameObj;
15131 int rc = JIM_ERR;
15132
15133
15134 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
15135 Jim_IncrRefCount(fileNameObj);
15136
15137 exprText = Jim_GetString(objPtr, &exprTextLen);
15138
15139
15140 ScriptTokenListInit(&tokenlist);
15141
15142 JimParserInit(&parser, exprText, exprTextLen, line);
15143 while (!parser.eof) {
15144 if (JimParseExpression(&parser) != JIM_OK) {
15145 ScriptTokenListFree(&tokenlist);
15146 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
15147 if (parser.errmsg) {
15148 Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL);
15149 }
15150 expr = NULL;
15151 goto err;
15152 }
15153
15154 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15155 parser.tline);
15156 }
15157
15158 #ifdef DEBUG_SHOW_EXPR_TOKENS
15159 {
15160 int i;
15161 printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
15162 for (i = 0; i < tokenlist.count; i++) {
15163 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
15164 tokenlist.list[i].len, tokenlist.list[i].token);
15165 }
15166 }
15167 #endif
15168
15169 if (tokenlist.count <= 1) {
15170 Jim_SetResultString(interp, "empty expression", -1);
15171 rc = JIM_ERR;
15172 }
15173 else {
15174 rc = JimParseCheckMissing(interp, parser.missing.ch);
15175 }
15176 if (rc != JIM_OK) {
15177 ScriptTokenListFree(&tokenlist);
15178 Jim_DecrRefCount(interp, fileNameObj);
15179 return rc;
15180 }
15181
15182
15183 expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
15184
15185
15186 ScriptTokenListFree(&tokenlist);
15187
15188 if (!expr) {
15189 goto err;
15190 }
15191
15192 #ifdef DEBUG_SHOW_EXPR
15193 printf("==== Expr ====\n");
15194 JimShowExprNode(expr->expr, 0);
15195 #endif
15196
15197 rc = JIM_OK;
15198
15199 err:
15200
15201 Jim_DecrRefCount(interp, fileNameObj);
15202 Jim_FreeIntRep(interp, objPtr);
15203 Jim_SetIntRepPtr(objPtr, expr);
15204 objPtr->typePtr = &exprObjType;
15205 return rc;
15206 }
15207
15208 static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
15209 {
15210 if (objPtr->typePtr != &exprObjType) {
15211 if (SetExprFromAny(interp, objPtr) != JIM_OK) {
15212 return NULL;
15213 }
15214 }
15215 return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
15216 }
15217
15218 #ifdef JIM_OPTIMIZATION
15219 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
15220 {
15221 if (node->type == JIM_TT_EXPR_INT)
15222 return node->objPtr;
15223 else if (node->type == JIM_TT_VAR)
15224 return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
15225 else if (node->type == JIM_TT_DICTSUGAR)
15226 return JimExpandDictSugar(interp, node->objPtr);
15227 else
15228 return NULL;
15229 }
15230 #endif
15231
15232
15233 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
15234 {
15235 if (TOKEN_IS_EXPR_OP(node->type)) {
15236 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
15237 return op->funcop(interp, node);
15238 }
15239 else {
15240 Jim_Obj *objPtr;
15241
15242
15243 switch (node->type) {
15244 case JIM_TT_EXPR_INT:
15245 case JIM_TT_EXPR_DOUBLE:
15246 case JIM_TT_EXPR_BOOLEAN:
15247 case JIM_TT_STR:
15248 Jim_SetResult(interp, node->objPtr);
15249 return JIM_OK;
15250
15251 case JIM_TT_VAR:
15252 objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
15253 if (objPtr) {
15254 Jim_SetResult(interp, objPtr);
15255 return JIM_OK;
15256 }
15257 return JIM_ERR;
15258
15259 case JIM_TT_DICTSUGAR:
15260 objPtr = JimExpandDictSugar(interp, node->objPtr);
15261 if (objPtr) {
15262 Jim_SetResult(interp, objPtr);
15263 return JIM_OK;
15264 }
15265 return JIM_ERR;
15266
15267 case JIM_TT_ESC:
15268 if (interp->safeexpr) {
15269 return JIM_ERR;
15270 }
15271 if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
15272 Jim_SetResult(interp, objPtr);
15273 return JIM_OK;
15274 }
15275 return JIM_ERR;
15276
15277 case JIM_TT_CMD:
15278 if (interp->safeexpr) {
15279 return JIM_ERR;
15280 }
15281 return Jim_EvalObj(interp, node->objPtr);
15282
15283 default:
15284
15285 return JIM_ERR;
15286 }
15287 }
15288 }
15289
15290 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
15291 {
15292 int rc = JimExprEvalTermNode(interp, node);
15293 if (rc == JIM_OK) {
15294 *objPtrPtr = Jim_GetResult(interp);
15295 Jim_IncrRefCount(*objPtrPtr);
15296 }
15297 return rc;
15298 }
15299
15300 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
15301 {
15302 if (JimExprEvalTermNode(interp, node) == JIM_OK) {
15303 return ExprBool(interp, Jim_GetResult(interp));
15304 }
15305 return -1;
15306 }
15307
15308 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
15309 {
15310 struct ExprTree *expr;
15311 int retcode = JIM_OK;
15312
15313 Jim_IncrRefCount(exprObjPtr);
15314 expr = JimGetExpression(interp, exprObjPtr);
15315 if (!expr) {
15316 retcode = JIM_ERR;
15317 goto done;
15318 }
15319
15320 #ifdef JIM_OPTIMIZATION
15321 if (!interp->safeexpr) {
15322 Jim_Obj *objPtr;
15323
15324
15325 switch (expr->len) {
15326 case 1:
15327 objPtr = JimExprIntValOrVar(interp, expr->expr);
15328 if (objPtr) {
15329 Jim_SetResult(interp, objPtr);
15330 goto done;
15331 }
15332 break;
15333
15334 case 2:
15335 if (expr->expr->type == JIM_EXPROP_NOT) {
15336 objPtr = JimExprIntValOrVar(interp, expr->expr->left);
15337
15338 if (objPtr && JimIsWide(objPtr)) {
15339 Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
15340 goto done;
15341 }
15342 }
15343 break;
15344
15345 case 3:
15346 objPtr = JimExprIntValOrVar(interp, expr->expr->left);
15347 if (objPtr && JimIsWide(objPtr)) {
15348 Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
15349 if (objPtr2 && JimIsWide(objPtr2)) {
15350 jim_wide wideValueA = JimWideValue(objPtr);
15351 jim_wide wideValueB = JimWideValue(objPtr2);
15352 int cmpRes;
15353 switch (expr->expr->type) {
15354 case JIM_EXPROP_LT:
15355 cmpRes = wideValueA < wideValueB;
15356 break;
15357 case JIM_EXPROP_LTE:
15358 cmpRes = wideValueA <= wideValueB;
15359 break;
15360 case JIM_EXPROP_GT:
15361 cmpRes = wideValueA > wideValueB;
15362 break;
15363 case JIM_EXPROP_GTE:
15364 cmpRes = wideValueA >= wideValueB;
15365 break;
15366 case JIM_EXPROP_NUMEQ:
15367 cmpRes = wideValueA == wideValueB;
15368 break;
15369 case JIM_EXPROP_NUMNE:
15370 cmpRes = wideValueA != wideValueB;
15371 break;
15372 default:
15373 goto noopt;
15374 }
15375 Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
15376 goto done;
15377 }
15378 }
15379 break;
15380 }
15381 }
15382 noopt:
15383 #endif
15384
15385 expr->inUse++;
15386
15387
15388 retcode = JimExprEvalTermNode(interp, expr->expr);
15389
15390
15391 Jim_FreeIntRep(interp, exprObjPtr);
15392 exprObjPtr->typePtr = &exprObjType;
15393 Jim_SetIntRepPtr(exprObjPtr, expr);
15394
15395 done:
15396 Jim_DecrRefCount(interp, exprObjPtr);
15397
15398 return retcode;
15399 }
15400
15401 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
15402 {
15403 int retcode = Jim_EvalExpression(interp, exprObjPtr);
15404
15405 if (retcode == JIM_OK) {
15406 switch (ExprBool(interp, Jim_GetResult(interp))) {
15407 case 0:
15408 *boolPtr = 0;
15409 break;
15410
15411 case 1:
15412 *boolPtr = 1;
15413 break;
15414
15415 case -1:
15416 retcode = JIM_ERR;
15417 break;
15418 }
15419 }
15420 return retcode;
15421 }
15422
15423
15424
15425
15426 typedef struct ScanFmtPartDescr
15427 {
15428 const char *arg;
15429 const char *prefix;
15430 size_t width;
15431 int pos;
15432 char type;
15433 char modifier;
15434 } ScanFmtPartDescr;
15435
15436
15437 typedef struct ScanFmtStringObj
15438 {
15439 jim_wide size;
15440 char *stringRep;
15441 size_t count;
15442 size_t convCount;
15443 size_t maxPos;
15444 const char *error;
15445 char *scratch;
15446 ScanFmtPartDescr descr[1];
15447 } ScanFmtStringObj;
15448
15449
15450 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
15451 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
15452 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
15453
15454 static const Jim_ObjType scanFmtStringObjType = {
15455 "scanformatstring",
15456 FreeScanFmtInternalRep,
15457 DupScanFmtInternalRep,
15458 UpdateStringOfScanFmt,
15459 JIM_TYPE_NONE,
15460 };
15461
15462 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
15463 {
15464 JIM_NOTUSED(interp);
15465 Jim_Free((char *)objPtr->internalRep.ptr);
15466 objPtr->internalRep.ptr = 0;
15467 }
15468
15469 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
15470 {
15471 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
15472 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
15473
15474 JIM_NOTUSED(interp);
15475 memcpy(newVec, srcPtr->internalRep.ptr, size);
15476 dupPtr->internalRep.ptr = newVec;
15477 dupPtr->typePtr = &scanFmtStringObjType;
15478 }
15479
15480 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
15481 {
15482 JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
15483 }
15484
15485
15486 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
15487 {
15488 ScanFmtStringObj *fmtObj;
15489 char *buffer;
15490 int maxCount, i, approxSize, lastPos = -1;
15491 const char *fmt = Jim_String(objPtr);
15492 int maxFmtLen = Jim_Length(objPtr);
15493 const char *fmtEnd = fmt + maxFmtLen;
15494 int curr;
15495
15496 Jim_FreeIntRep(interp, objPtr);
15497
15498 for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
15499 if (fmt[i] == '%')
15500 ++maxCount;
15501
15502 approxSize = sizeof(ScanFmtStringObj)
15503 +(maxCount + 1) * sizeof(ScanFmtPartDescr)
15504 +maxFmtLen * sizeof(char) + 3 + 1
15505 + maxFmtLen * sizeof(char) + 1
15506 + maxFmtLen * sizeof(char)
15507 +(maxCount + 1) * sizeof(char)
15508 +1;
15509 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
15510 memset(fmtObj, 0, approxSize);
15511 fmtObj->size = approxSize;
15512 fmtObj->maxPos = 0;
15513 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
15514 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
15515 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
15516 buffer = fmtObj->stringRep + maxFmtLen + 1;
15517 objPtr->internalRep.ptr = fmtObj;
15518 objPtr->typePtr = &scanFmtStringObjType;
15519 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
15520 int width = 0, skip;
15521 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
15522
15523 fmtObj->count++;
15524 descr->width = 0;
15525
15526 if (*fmt != '%' || fmt[1] == '%') {
15527 descr->type = 0;
15528 descr->prefix = &buffer[i];
15529 for (; fmt < fmtEnd; ++fmt) {
15530 if (*fmt == '%') {
15531 if (fmt[1] != '%')
15532 break;
15533 ++fmt;
15534 }
15535 buffer[i++] = *fmt;
15536 }
15537 buffer[i++] = 0;
15538 }
15539
15540 ++fmt;
15541
15542 if (fmt >= fmtEnd)
15543 goto done;
15544 descr->pos = 0;
15545 if (*fmt == '*') {
15546 descr->pos = -1;
15547 ++fmt;
15548 }
15549 else
15550 fmtObj->convCount++;
15551
15552 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
15553 fmt += skip;
15554
15555 if (descr->pos != -1 && *fmt == '$') {
15556 int prev;
15557
15558 ++fmt;
15559 descr->pos = width;
15560 width = 0;
15561
15562 if ((lastPos == 0 && descr->pos > 0)
15563 || (lastPos > 0 && descr->pos == 0)) {
15564 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
15565 return JIM_ERR;
15566 }
15567
15568 for (prev = 0; prev < curr; ++prev) {
15569 if (fmtObj->descr[prev].pos == -1)
15570 continue;
15571 if (fmtObj->descr[prev].pos == descr->pos) {
15572 fmtObj->error =
15573 "variable is assigned by multiple \"%n$\" conversion specifiers";
15574 return JIM_ERR;
15575 }
15576 }
15577 if (descr->pos < 0) {
15578 fmtObj->error =
15579 "\"%n$\" conversion specifier is negative";
15580 return JIM_ERR;
15581 }
15582
15583 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
15584 descr->width = width;
15585 fmt += skip;
15586 }
15587 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
15588 fmtObj->maxPos = descr->pos;
15589 }
15590 else {
15591
15592 descr->width = width;
15593 }
15594 }
15595
15596 if (lastPos == -1)
15597 lastPos = descr->pos;
15598
15599 if (*fmt == '[') {
15600 int swapped = 1, beg = i, end, j;
15601
15602 descr->type = '[';
15603 descr->arg = &buffer[i];
15604 ++fmt;
15605 if (*fmt == '^')
15606 buffer[i++] = *fmt++;
15607 if (*fmt == ']')
15608 buffer[i++] = *fmt++;
15609 while (*fmt && *fmt != ']')
15610 buffer[i++] = *fmt++;
15611 if (*fmt != ']') {
15612 fmtObj->error = "unmatched [ in format string";
15613 return JIM_ERR;
15614 }
15615 end = i;
15616 buffer[i++] = 0;
15617
15618 while (swapped) {
15619 swapped = 0;
15620 for (j = beg + 1; j < end - 1; ++j) {
15621 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
15622 char tmp = buffer[j - 1];
15623
15624 buffer[j - 1] = buffer[j + 1];
15625 buffer[j + 1] = tmp;
15626 swapped = 1;
15627 }
15628 }
15629 }
15630 }
15631 else {
15632
15633 if (fmt < fmtEnd && strchr("hlL", *fmt))
15634 descr->modifier = tolower((int)*fmt++);
15635
15636 if (fmt >= fmtEnd) {
15637 fmtObj->error = "missing scan conversion character";
15638 return JIM_ERR;
15639 }
15640
15641 descr->type = *fmt;
15642 if (strchr("efgcsndoxui", *fmt) == 0) {
15643 fmtObj->error = "bad scan conversion character";
15644 return JIM_ERR;
15645 }
15646 else if (*fmt == 'c' && descr->width != 0) {
15647 fmtObj->error = "field width may not be specified in %c " "conversion";
15648 return JIM_ERR;
15649 }
15650 else if (*fmt == 'u' && descr->modifier == 'l') {
15651 fmtObj->error = "unsigned wide not supported";
15652 return JIM_ERR;
15653 }
15654 }
15655 curr++;
15656 }
15657 done:
15658 return JIM_OK;
15659 }
15660
15661
15662
15663 #define FormatGetCnvCount(_fo_) \
15664 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
15665 #define FormatGetMaxPos(_fo_) \
15666 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
15667 #define FormatGetError(_fo_) \
15668 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
15669
15670 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
15671 {
15672 char *buffer = Jim_StrDup(str);
15673 char *p = buffer;
15674
15675 while (*str) {
15676 int c;
15677 int n;
15678
15679 if (!sdescr && isspace(UCHAR(*str)))
15680 break;
15681
15682 n = utf8_tounicode(str, &c);
15683 if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN))
15684 break;
15685 while (n--)
15686 *p++ = *str++;
15687 }
15688 *p = 0;
15689 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
15690 }
15691
15692
15693 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen,
15694 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
15695 {
15696 const char *tok;
15697 const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
15698 size_t scanned = 0;
15699 size_t anchor = pos;
15700 int i;
15701 Jim_Obj *tmpObj = NULL;
15702
15703
15704 *valObjPtr = 0;
15705 if (descr->prefix) {
15706 for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) {
15707
15708 if (isspace(UCHAR(descr->prefix[i])))
15709 while (pos < str_bytelen && isspace(UCHAR(str[pos])))
15710 ++pos;
15711 else if (descr->prefix[i] != str[pos])
15712 break;
15713 else
15714 ++pos;
15715 }
15716 if (pos >= str_bytelen) {
15717 return -1;
15718 }
15719 else if (descr->prefix[i] != 0)
15720 return 0;
15721 }
15722
15723 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
15724 while (isspace(UCHAR(str[pos])))
15725 ++pos;
15726
15727
15728 scanned = pos - anchor;
15729
15730
15731 if (descr->type == 'n') {
15732
15733 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
15734 }
15735 else if (pos >= str_bytelen) {
15736
15737 return -1;
15738 }
15739 else if (descr->type == 'c') {
15740 int c;
15741 scanned += utf8_tounicode(&str[pos], &c);
15742 *valObjPtr = Jim_NewIntObj(interp, c);
15743 return scanned;
15744 }
15745 else {
15746
15747 if (descr->width > 0) {
15748 size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos);
15749 size_t tLen = descr->width > sLen ? sLen : descr->width;
15750
15751 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
15752 tok = tmpObj->bytes;
15753 }
15754 else {
15755
15756 tok = &str[pos];
15757 }
15758 switch (descr->type) {
15759 case 'd':
15760 case 'o':
15761 case 'x':
15762 case 'u':
15763 case 'i':{
15764 char *endp;
15765 jim_wide w;
15766
15767 int base = descr->type == 'o' ? 8
15768 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
15769
15770
15771 if (base == 0) {
15772 w = jim_strtoull(tok, &endp);
15773 }
15774 else {
15775 w = strtoull(tok, &endp, base);
15776 }
15777
15778 if (endp != tok) {
15779
15780 *valObjPtr = Jim_NewIntObj(interp, w);
15781
15782
15783 scanned += endp - tok;
15784 }
15785 else {
15786 scanned = *tok ? 0 : -1;
15787 }
15788 break;
15789 }
15790 case 's':
15791 case '[':{
15792 *valObjPtr = JimScanAString(interp, descr->arg, tok);
15793 scanned += Jim_Length(*valObjPtr);
15794 break;
15795 }
15796 case 'e':
15797 case 'f':
15798 case 'g':{
15799 char *endp;
15800 double value = strtod(tok, &endp);
15801
15802 if (endp != tok) {
15803
15804 *valObjPtr = Jim_NewDoubleObj(interp, value);
15805
15806 scanned += endp - tok;
15807 }
15808 else {
15809 scanned = *tok ? 0 : -1;
15810 }
15811 break;
15812 }
15813 }
15814 if (tmpObj) {
15815 Jim_FreeNewObj(interp, tmpObj);
15816 }
15817 }
15818 return scanned;
15819 }
15820
15821
15822 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
15823 {
15824 size_t i, pos;
15825 int scanned = 1;
15826 const char *str = Jim_String(strObjPtr);
15827 int str_bytelen = Jim_Length(strObjPtr);
15828 Jim_Obj *resultList = 0;
15829 Jim_Obj **resultVec = 0;
15830 int resultc;
15831 Jim_Obj *emptyStr = 0;
15832 ScanFmtStringObj *fmtObj;
15833
15834
15835 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
15836
15837 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
15838
15839 if (fmtObj->error != 0) {
15840 if (flags & JIM_ERRMSG)
15841 Jim_SetResultString(interp, fmtObj->error, -1);
15842 return 0;
15843 }
15844
15845 emptyStr = Jim_NewEmptyStringObj(interp);
15846 Jim_IncrRefCount(emptyStr);
15847
15848 resultList = Jim_NewListObj(interp, NULL, 0);
15849 if (fmtObj->maxPos > 0) {
15850 for (i = 0; i < fmtObj->maxPos; ++i)
15851 Jim_ListAppendElement(interp, resultList, emptyStr);
15852 JimListGetElements(interp, resultList, &resultc, &resultVec);
15853 }
15854
15855 for (i = 0, pos = 0; i < fmtObj->count; ++i) {
15856 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
15857 Jim_Obj *value = 0;
15858
15859
15860 if (descr->type == 0)
15861 continue;
15862
15863 if (scanned > 0)
15864 scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value);
15865
15866 if (scanned == -1 && i == 0)
15867 goto eof;
15868
15869 pos += scanned;
15870
15871
15872 if (value == 0)
15873 value = Jim_NewEmptyStringObj(interp);
15874
15875 if (descr->pos == -1) {
15876 Jim_FreeNewObj(interp, value);
15877 }
15878 else if (descr->pos == 0)
15879
15880 Jim_ListAppendElement(interp, resultList, value);
15881 else if (resultVec[descr->pos - 1] == emptyStr) {
15882
15883 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
15884 Jim_IncrRefCount(value);
15885 resultVec[descr->pos - 1] = value;
15886 }
15887 else {
15888
15889 Jim_FreeNewObj(interp, value);
15890 goto err;
15891 }
15892 }
15893 Jim_DecrRefCount(interp, emptyStr);
15894 return resultList;
15895 eof:
15896 Jim_DecrRefCount(interp, emptyStr);
15897 Jim_FreeNewObj(interp, resultList);
15898 return (Jim_Obj *)EOF;
15899 err:
15900 Jim_DecrRefCount(interp, emptyStr);
15901 Jim_FreeNewObj(interp, resultList);
15902 return 0;
15903 }
15904
15905
15906 static void JimPrngInit(Jim_Interp *interp)
15907 {
15908 #define PRNG_SEED_SIZE 256
15909 int i;
15910 unsigned int *seed;
15911 time_t t = time(NULL);
15912
15913 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
15914
15915 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
15916 for (i = 0; i < PRNG_SEED_SIZE; i++) {
15917 seed[i] = (rand() ^ t ^ clock());
15918 }
15919 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
15920 Jim_Free(seed);
15921 }
15922
15923
15924 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
15925 {
15926 Jim_PrngState *prng;
15927 unsigned char *destByte = (unsigned char *)dest;
15928 unsigned int si, sj, x;
15929
15930
15931 if (interp->prngState == NULL)
15932 JimPrngInit(interp);
15933 prng = interp->prngState;
15934
15935 for (x = 0; x < len; x++) {
15936 prng->i = (prng->i + 1) & 0xff;
15937 si = prng->sbox[prng->i];
15938 prng->j = (prng->j + si) & 0xff;
15939 sj = prng->sbox[prng->j];
15940 prng->sbox[prng->i] = sj;
15941 prng->sbox[prng->j] = si;
15942 *destByte++ = prng->sbox[(si + sj) & 0xff];
15943 }
15944 }
15945
15946
15947 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
15948 {
15949 int i;
15950 Jim_PrngState *prng;
15951
15952
15953 if (interp->prngState == NULL)
15954 JimPrngInit(interp);
15955 prng = interp->prngState;
15956
15957
15958 for (i = 0; i < 256; i++)
15959 prng->sbox[i] = i;
15960
15961 for (i = 0; i < seedLen; i++) {
15962 unsigned char t;
15963
15964 t = prng->sbox[i & 0xFF];
15965 prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
15966 prng->sbox[seed[i]] = t;
15967 }
15968 prng->i = prng->j = 0;
15969
15970 for (i = 0; i < 256; i += seedLen) {
15971 JimRandomBytes(interp, seed, seedLen);
15972 }
15973 }
15974
15975
15976 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15977 {
15978 jim_wide wideValue, increment = 1;
15979 Jim_Obj *intObjPtr;
15980
15981 if (argc != 2 && argc != 3) {
15982 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
15983 return JIM_ERR;
15984 }
15985 if (argc == 3) {
15986 if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK)
15987 return JIM_ERR;
15988 }
15989 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
15990 if (!intObjPtr) {
15991
15992 wideValue = 0;
15993 }
15994 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
15995 return JIM_ERR;
15996 }
15997 if (!intObjPtr || Jim_IsShared(intObjPtr)) {
15998 intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
15999 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
16000 Jim_FreeNewObj(interp, intObjPtr);
16001 return JIM_ERR;
16002 }
16003 }
16004 else {
16005
16006 Jim_InvalidateStringRep(intObjPtr);
16007 JimWideValue(intObjPtr) = wideValue + increment;
16008
16009 if (argv[1]->typePtr != &variableObjType) {
16010
16011 Jim_SetVariable(interp, argv[1], intObjPtr);
16012 }
16013 }
16014 Jim_SetResult(interp, intObjPtr);
16015 return JIM_OK;
16016 }
16017
16018
16019 #define JIM_EVAL_SARGV_LEN 8
16020 #define JIM_EVAL_SINTV_LEN 8
16021
16022 static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv)
16023 {
16024 JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object"));
16025
16026 int ret;
16027 Jim_Obj *nargv[7];
16028 Jim_Obj *traceCmdObj = interp->traceCmdObj;
16029 Jim_Obj *resultObj = Jim_GetResult(interp);
16030 ScriptObj *script = NULL;
16031
16032
16033
16034 if (interp->evalFrame->scriptObj) {
16035 script = JimGetScript(interp, interp->evalFrame->scriptObj);
16036 }
16037
16038 nargv[0] = traceCmdObj;
16039 nargv[1] = Jim_NewStringObj(interp, type, -1);
16040 nargv[2] = script ? script->fileNameObj : interp->emptyObj;
16041 nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1);
16042 nargv[4] = resultObj;
16043 nargv[5] = argv[0];
16044 nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1);
16045
16046
16047 interp->traceCmdObj = NULL;
16048
16049 Jim_IncrRefCount(resultObj);
16050 ret = Jim_EvalObjVector(interp, 7, nargv);
16051 Jim_DecrRefCount(interp, resultObj);
16052
16053 if (ret == JIM_OK || ret == JIM_RETURN) {
16054
16055 interp->traceCmdObj = traceCmdObj;
16056 Jim_SetEmptyResult(interp);
16057 ret = JIM_OK;
16058 }
16059 else {
16060
16061 Jim_DecrRefCount(interp, traceCmdObj);
16062 }
16063 return ret;
16064 }
16065
16066
16067 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16068 {
16069 int retcode;
16070
16071 if (interp->unknown_called > 50) {
16072 return JIM_ERR;
16073 }
16074
16075
16076
16077 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
16078 return JIM_ERR;
16079
16080 interp->unknown_called++;
16081
16082 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
16083 interp->unknown_called--;
16084
16085 return retcode;
16086 }
16087
16088 static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj)
16089 {
16090 memset(frame, 0, sizeof(*frame));
16091 frame->parent = interp->evalFrame;
16092 frame->level = frame->parent->level + 1;
16093 frame->procLevel = interp->procLevel;
16094 frame->framePtr = interp->framePtr;
16095 if (scriptObj) {
16096 frame->scriptObj = scriptObj;
16097 }
16098 else {
16099 frame->scriptObj = frame->parent->scriptObj;
16100 }
16101 interp->evalFrame = frame;
16102 #if 0
16103 if (frame->scriptObj) {
16104 printf("script: %.*s\n", 20, Jim_String(frame->scriptObj));
16105 }
16106 #endif
16107 }
16108
16109 static void JimPopEvalFrame(Jim_Interp *interp)
16110 {
16111 interp->evalFrame = interp->evalFrame->parent;
16112 }
16113
16114
16115 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
16116 {
16117 int retcode;
16118 Jim_Cmd *cmdPtr;
16119 void *prevPrivData;
16120 Jim_Obj *tailcallObj = NULL;
16121
16122 #if 0
16123 printf("invoke");
16124 int j;
16125 for (j = 0; j < objc; j++) {
16126 printf(" '%s'", Jim_String(objv[j]));
16127 }
16128 printf("\n");
16129 #endif
16130
16131 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
16132 if (cmdPtr == NULL) {
16133 return JimUnknown(interp, objc, objv);
16134 }
16135 JimIncrCmdRefCount(cmdPtr);
16136
16137 if (interp->evalDepth == interp->maxEvalDepth) {
16138 Jim_SetResultString(interp, "Infinite eval recursion", -1);
16139 retcode = JIM_ERR;
16140 goto out;
16141 }
16142 interp->evalDepth++;
16143 prevPrivData = interp->cmdPrivData;
16144
16145 tailcall:
16146
16147 interp->evalFrame->argc = objc;
16148 interp->evalFrame->argv = objv;
16149 interp->evalFrame->cmd = cmdPtr;
16150
16151 if (!interp->traceCmdObj ||
16152 (retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) {
16153
16154 Jim_SetEmptyResult(interp);
16155 if (cmdPtr->isproc) {
16156 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
16157 }
16158 else {
16159 interp->cmdPrivData = cmdPtr->u.native.privData;
16160 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
16161 }
16162 if (retcode == JIM_ERR) {
16163 JimSetErrorStack(interp, NULL);
16164 }
16165 }
16166
16167 if (tailcallObj) {
16168
16169 Jim_DecrRefCount(interp, tailcallObj);
16170 tailcallObj = NULL;
16171 }
16172
16173
16174 interp->evalFrame->argc = 0;
16175 interp->evalFrame->argv = NULL;
16176
16177
16178 if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) {
16179 JimDecrCmdRefCount(interp, cmdPtr);
16180
16181
16182 cmdPtr = interp->framePtr->tailcallCmd;
16183 interp->framePtr->tailcallCmd = NULL;
16184 tailcallObj = interp->framePtr->tailcallObj;
16185 interp->framePtr->tailcallObj = NULL;
16186 objc = tailcallObj->internalRep.listValue.len;
16187 objv = tailcallObj->internalRep.listValue.ele;
16188 goto tailcall;
16189 }
16190
16191 interp->cmdPrivData = prevPrivData;
16192 interp->evalDepth--;
16193
16194 out:
16195 JimDecrCmdRefCount(interp, cmdPtr);
16196
16197 if (retcode == JIM_ERR) {
16198 JimSetErrorStack(interp, NULL);
16199 }
16200
16201 if (interp->framePtr->tailcallObj) {
16202 JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
16203 Jim_DecrRefCount(interp, interp->framePtr->tailcallObj);
16204 interp->framePtr->tailcallCmd = NULL;
16205 interp->framePtr->tailcallObj = NULL;
16206 }
16207
16208 return retcode;
16209 }
16210
16211 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
16212 {
16213 int i, retcode;
16214 Jim_EvalFrame frame;
16215
16216
16217 for (i = 0; i < objc; i++)
16218 Jim_IncrRefCount(objv[i]);
16219
16220
16221 JimPushEvalFrame(interp, &frame, NULL);
16222
16223 retcode = JimInvokeCommand(interp, objc, objv);
16224
16225 JimPopEvalFrame(interp);
16226
16227
16228 for (i = 0; i < objc; i++)
16229 Jim_DecrRefCount(interp, objv[i]);
16230
16231 return retcode;
16232 }
16233
16234 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
16235 {
16236 int ret;
16237 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
16238
16239 nargv[0] = prefix;
16240 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
16241 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
16242 Jim_Free(nargv);
16243 return ret;
16244 }
16245
16246 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
16247 {
16248 Jim_Obj *objPtr;
16249 int ret = JIM_ERR;
16250
16251 switch (token->type) {
16252 case JIM_TT_STR:
16253 case JIM_TT_ESC:
16254 objPtr = token->objPtr;
16255 break;
16256 case JIM_TT_VAR:
16257 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
16258 break;
16259 case JIM_TT_DICTSUGAR:
16260 objPtr = JimExpandDictSugar(interp, token->objPtr);
16261 break;
16262 case JIM_TT_EXPRSUGAR:
16263 ret = Jim_EvalExpression(interp, token->objPtr);
16264 if (ret == JIM_OK) {
16265 objPtr = Jim_GetResult(interp);
16266 }
16267 else {
16268 objPtr = NULL;
16269 }
16270 break;
16271 case JIM_TT_CMD:
16272 ret = Jim_EvalObj(interp, token->objPtr);
16273 if (ret == JIM_OK || ret == JIM_RETURN) {
16274 objPtr = interp->result;
16275 } else {
16276
16277 objPtr = NULL;
16278 }
16279 break;
16280 default:
16281 JimPanic((1,
16282 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
16283 objPtr = NULL;
16284 break;
16285 }
16286 if (objPtr) {
16287 *objPtrPtr = objPtr;
16288 return JIM_OK;
16289 }
16290 return ret;
16291 }
16292
16293 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
16294 {
16295 int totlen = 0, i;
16296 Jim_Obj **intv;
16297 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
16298 Jim_Obj *objPtr;
16299 char *s;
16300
16301 if (tokens <= JIM_EVAL_SINTV_LEN)
16302 intv = sintv;
16303 else
16304 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
16305
16306 for (i = 0; i < tokens; i++) {
16307 switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
16308 case JIM_OK:
16309 case JIM_RETURN:
16310 break;
16311 case JIM_BREAK:
16312 if (flags & JIM_SUBST_FLAG) {
16313
16314 tokens = i;
16315 continue;
16316 }
16317
16318
16319 case JIM_CONTINUE:
16320 if (flags & JIM_SUBST_FLAG) {
16321 intv[i] = NULL;
16322 continue;
16323 }
16324
16325
16326 default:
16327 while (i--) {
16328 Jim_DecrRefCount(interp, intv[i]);
16329 }
16330 if (intv != sintv) {
16331 Jim_Free(intv);
16332 }
16333 return NULL;
16334 }
16335 Jim_IncrRefCount(intv[i]);
16336 Jim_String(intv[i]);
16337 totlen += intv[i]->length;
16338 }
16339
16340
16341 if (tokens == 1 && intv[0] && intv == sintv) {
16342
16343 intv[0]->refCount--;
16344 return intv[0];
16345 }
16346
16347 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
16348
16349 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
16350 && token[2].type == JIM_TT_VAR) {
16351
16352 objPtr->typePtr = &interpolatedObjType;
16353 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
16354 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
16355 Jim_IncrRefCount(intv[2]);
16356 }
16357 else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
16358
16359 int line;
16360 Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line);
16361 Jim_SetSourceInfo(interp, objPtr, fileNameObj, line);
16362 }
16363
16364
16365 s = objPtr->bytes = Jim_Alloc(totlen + 1);
16366 objPtr->length = totlen;
16367 for (i = 0; i < tokens; i++) {
16368 if (intv[i]) {
16369 memcpy(s, intv[i]->bytes, intv[i]->length);
16370 s += intv[i]->length;
16371 Jim_DecrRefCount(interp, intv[i]);
16372 }
16373 }
16374 objPtr->bytes[totlen] = '\0';
16375
16376 if (intv != sintv) {
16377 Jim_Free(intv);
16378 }
16379
16380 return objPtr;
16381 }
16382
16383
16384 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
16385 {
16386 int retcode = JIM_OK;
16387 Jim_EvalFrame frame;
16388
16389 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
16390
16391 JimPushEvalFrame(interp, &frame, NULL);
16392
16393 if (listPtr->internalRep.listValue.len) {
16394 Jim_IncrRefCount(listPtr);
16395 retcode = JimInvokeCommand(interp,
16396 listPtr->internalRep.listValue.len,
16397 listPtr->internalRep.listValue.ele);
16398 Jim_DecrRefCount(interp, listPtr);
16399 }
16400
16401 JimPopEvalFrame(interp);
16402
16403 return retcode;
16404 }
16405
16406 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
16407 {
16408 SetListFromAny(interp, listPtr);
16409 return JimEvalObjList(interp, listPtr);
16410 }
16411
16412 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
16413 {
16414 int i;
16415 ScriptObj *script;
16416 ScriptToken *token;
16417 int retcode = JIM_OK;
16418 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
16419 Jim_EvalFrame frame;
16420
16421 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
16422 return JimEvalObjList(interp, scriptObjPtr);
16423 }
16424
16425 Jim_IncrRefCount(scriptObjPtr);
16426 script = JimGetScript(interp, scriptObjPtr);
16427 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
16428 JimSetErrorStack(interp, script);
16429 Jim_DecrRefCount(interp, scriptObjPtr);
16430 return JIM_ERR;
16431 }
16432
16433 Jim_SetEmptyResult(interp);
16434
16435 token = script->token;
16436
16437 #ifdef JIM_OPTIMIZATION
16438 if (script->len == 0) {
16439 Jim_DecrRefCount(interp, scriptObjPtr);
16440 return JIM_OK;
16441 }
16442 if (script->len == 3
16443 && token[1].objPtr->typePtr == &commandObjType
16444 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
16445 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
16446 && token[2].objPtr->typePtr == &variableObjType) {
16447
16448 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
16449
16450 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16451 JimWideValue(objPtr)++;
16452 Jim_InvalidateStringRep(objPtr);
16453 Jim_DecrRefCount(interp, scriptObjPtr);
16454 Jim_SetResult(interp, objPtr);
16455 return JIM_OK;
16456 }
16457 }
16458 #endif
16459
16460 script->inUse++;
16461
16462 JimPushEvalFrame(interp, &frame, scriptObjPtr);
16463
16464
16465 interp->errorFlag = 0;
16466 argv = sargv;
16467
16468 for (i = 0; i < script->len && retcode == JIM_OK; ) {
16469 int argc;
16470 int j;
16471
16472
16473 argc = token[i].objPtr->internalRep.scriptLineValue.argc;
16474 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
16475
16476
16477 if (argc > JIM_EVAL_SARGV_LEN)
16478 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
16479
16480
16481 i++;
16482
16483 for (j = 0; j < argc; j++) {
16484 long wordtokens = 1;
16485 int expand = 0;
16486 Jim_Obj *wordObjPtr = NULL;
16487
16488 if (token[i].type == JIM_TT_WORD) {
16489 wordtokens = JimWideValue(token[i++].objPtr);
16490 if (wordtokens < 0) {
16491 expand = 1;
16492 wordtokens = -wordtokens;
16493 }
16494 }
16495
16496 if (wordtokens == 1) {
16497
16498 switch (token[i].type) {
16499 case JIM_TT_ESC:
16500 case JIM_TT_STR:
16501 wordObjPtr = token[i].objPtr;
16502 break;
16503 case JIM_TT_VAR:
16504 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
16505 break;
16506 case JIM_TT_EXPRSUGAR:
16507 retcode = Jim_EvalExpression(interp, token[i].objPtr);
16508 if (retcode == JIM_OK) {
16509 wordObjPtr = Jim_GetResult(interp);
16510 }
16511 else {
16512 wordObjPtr = NULL;
16513 }
16514 break;
16515 case JIM_TT_DICTSUGAR:
16516 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
16517 break;
16518 case JIM_TT_CMD:
16519 retcode = Jim_EvalObj(interp, token[i].objPtr);
16520 if (retcode == JIM_OK) {
16521 wordObjPtr = Jim_GetResult(interp);
16522 }
16523 break;
16524 default:
16525 JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
16526 }
16527 }
16528 else {
16529 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
16530 }
16531
16532 if (!wordObjPtr) {
16533 if (retcode == JIM_OK) {
16534 retcode = JIM_ERR;
16535 }
16536 break;
16537 }
16538
16539 Jim_IncrRefCount(wordObjPtr);
16540 i += wordtokens;
16541
16542 if (!expand) {
16543 argv[j] = wordObjPtr;
16544 }
16545 else {
16546
16547 int len = Jim_ListLength(interp, wordObjPtr);
16548 int newargc = argc + len - 1;
16549 int k;
16550
16551 if (len > 1) {
16552 if (argv == sargv) {
16553 if (newargc > JIM_EVAL_SARGV_LEN) {
16554 argv = Jim_Alloc(sizeof(*argv) * newargc);
16555 memcpy(argv, sargv, sizeof(*argv) * j);
16556 }
16557 }
16558 else {
16559
16560 argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
16561 }
16562 }
16563
16564
16565 for (k = 0; k < len; k++) {
16566 argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
16567 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
16568 }
16569
16570 Jim_DecrRefCount(interp, wordObjPtr);
16571
16572
16573 j--;
16574 argc += len - 1;
16575 }
16576 }
16577
16578 if (retcode == JIM_OK && argc) {
16579
16580 retcode = JimInvokeCommand(interp, argc, argv);
16581
16582 if (Jim_CheckSignal(interp)) {
16583 retcode = JIM_SIGNAL;
16584 }
16585 }
16586
16587
16588 while (j-- > 0) {
16589 Jim_DecrRefCount(interp, argv[j]);
16590 }
16591
16592 if (argv != sargv) {
16593 Jim_Free(argv);
16594 argv = sargv;
16595 }
16596 }
16597
16598
16599 if (retcode == JIM_ERR) {
16600 JimSetErrorStack(interp, NULL);
16601 }
16602
16603 JimPopEvalFrame(interp);
16604
16605 Jim_FreeIntRep(interp, scriptObjPtr);
16606 scriptObjPtr->typePtr = &scriptObjType;
16607 Jim_SetIntRepPtr(scriptObjPtr, script);
16608 Jim_DecrRefCount(interp, scriptObjPtr);
16609
16610 return retcode;
16611 }
16612
16613 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
16614 {
16615 int retcode;
16616
16617 const char *varname = Jim_String(argNameObj);
16618 if (*varname == '&') {
16619
16620 Jim_Obj *objPtr;
16621 Jim_CallFrame *savedCallFrame = interp->framePtr;
16622
16623 interp->framePtr = interp->framePtr->parent;
16624 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
16625 interp->framePtr = savedCallFrame;
16626 if (!objPtr) {
16627 return JIM_ERR;
16628 }
16629
16630
16631 objPtr = Jim_NewStringObj(interp, varname + 1, -1);
16632 Jim_IncrRefCount(objPtr);
16633 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
16634 Jim_DecrRefCount(interp, objPtr);
16635 }
16636 else {
16637 retcode = Jim_SetVariable(interp, argNameObj, argValObj);
16638 }
16639 return retcode;
16640 }
16641
16642 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
16643 {
16644
16645 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
16646 int i;
16647
16648 for (i = 0; i < cmd->u.proc.argListLen; i++) {
16649 Jim_AppendString(interp, argmsg, " ", 1);
16650
16651 if (i == cmd->u.proc.argsPos) {
16652 if (cmd->u.proc.arglist[i].defaultObjPtr) {
16653
16654 Jim_AppendString(interp, argmsg, "?", 1);
16655 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
16656 Jim_AppendString(interp, argmsg, " ...?", -1);
16657 }
16658 else {
16659
16660 Jim_AppendString(interp, argmsg, "?arg ...?", -1);
16661 }
16662 }
16663 else {
16664 if (cmd->u.proc.arglist[i].defaultObjPtr) {
16665 Jim_AppendString(interp, argmsg, "?", 1);
16666 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
16667 Jim_AppendString(interp, argmsg, "?", 1);
16668 }
16669 else {
16670 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
16671 if (*arg == '&') {
16672 arg++;
16673 }
16674 Jim_AppendString(interp, argmsg, arg, -1);
16675 }
16676 }
16677 }
16678 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
16679 }
16680
16681 #ifdef jim_ext_namespace
16682 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
16683 {
16684 Jim_CallFrame *callFramePtr;
16685 int retcode;
16686
16687
16688 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
16689 callFramePtr->argv = interp->evalFrame->argv;
16690 callFramePtr->argc = interp->evalFrame->argc;
16691 callFramePtr->procArgsObjPtr = NULL;
16692 callFramePtr->procBodyObjPtr = scriptObj;
16693 callFramePtr->staticVars = NULL;
16694 Jim_IncrRefCount(scriptObj);
16695 interp->framePtr = callFramePtr;
16696
16697
16698 if (interp->framePtr->level == interp->maxCallFrameDepth) {
16699 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
16700 retcode = JIM_ERR;
16701 }
16702 else {
16703
16704 retcode = Jim_EvalObj(interp, scriptObj);
16705 }
16706
16707
16708 interp->framePtr = interp->framePtr->parent;
16709 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
16710
16711 return retcode;
16712 }
16713 #endif
16714
16715 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
16716 {
16717 Jim_CallFrame *callFramePtr;
16718 int i, d, retcode, optargs;
16719
16720
16721 if (argc - 1 < cmd->u.proc.reqArity ||
16722 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
16723 JimSetProcWrongArgs(interp, argv[0], cmd);
16724 return JIM_ERR;
16725 }
16726
16727 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
16728
16729 return JIM_OK;
16730 }
16731
16732
16733 if (interp->framePtr->level == interp->maxCallFrameDepth) {
16734 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
16735 return JIM_ERR;
16736 }
16737
16738
16739 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
16740 callFramePtr->argv = argv;
16741 callFramePtr->argc = argc;
16742 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
16743 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
16744 callFramePtr->staticVars = cmd->u.proc.staticVars;
16745
16746 interp->procLevel++;
16747
16748 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
16749 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
16750 interp->framePtr = callFramePtr;
16751
16752
16753 optargs = (argc - 1 - cmd->u.proc.reqArity);
16754
16755
16756 i = 1;
16757 for (d = 0; d < cmd->u.proc.argListLen; d++) {
16758 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
16759 if (d == cmd->u.proc.argsPos) {
16760
16761 Jim_Obj *listObjPtr;
16762 int argsLen = 0;
16763 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
16764 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
16765 }
16766 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
16767
16768
16769 if (cmd->u.proc.arglist[d].defaultObjPtr) {
16770 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
16771 }
16772 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
16773 if (retcode != JIM_OK) {
16774 goto badargset;
16775 }
16776
16777 i += argsLen;
16778 continue;
16779 }
16780
16781
16782 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
16783 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
16784 }
16785 else {
16786
16787 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
16788 }
16789 if (retcode != JIM_OK) {
16790 goto badargset;
16791 }
16792 }
16793
16794 if (interp->traceCmdObj == NULL ||
16795 (retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) {
16796
16797 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
16798 }
16799
16800 badargset:
16801
16802
16803 retcode = JimInvokeDefer(interp, retcode);
16804 interp->framePtr = interp->framePtr->parent;
16805 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
16806
16807
16808 if (retcode == JIM_RETURN) {
16809 if (--interp->returnLevel <= 0) {
16810 retcode = interp->returnCode;
16811 interp->returnCode = JIM_OK;
16812 interp->returnLevel = 0;
16813 }
16814 }
16815 interp->procLevel--;
16816
16817 return retcode;
16818 }
16819
16820 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
16821 {
16822 int retval;
16823 Jim_Obj *scriptObjPtr;
16824
16825 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
16826 Jim_IncrRefCount(scriptObjPtr);
16827 if (filename) {
16828 Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
16829 }
16830 retval = Jim_EvalObj(interp, scriptObjPtr);
16831 Jim_DecrRefCount(interp, scriptObjPtr);
16832 return retval;
16833 }
16834
16835 int Jim_Eval(Jim_Interp *interp, const char *script)
16836 {
16837 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
16838 }
16839
16840
16841 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
16842 {
16843 int retval;
16844 Jim_CallFrame *savedFramePtr = interp->framePtr;
16845
16846 interp->framePtr = interp->topFramePtr;
16847 retval = Jim_Eval(interp, script);
16848 interp->framePtr = savedFramePtr;
16849
16850 return retval;
16851 }
16852
16853 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
16854 {
16855 int retval;
16856 Jim_CallFrame *savedFramePtr = interp->framePtr;
16857
16858 interp->framePtr = interp->topFramePtr;
16859 retval = Jim_EvalFile(interp, filename);
16860 interp->framePtr = savedFramePtr;
16861
16862 return retval;
16863 }
16864
16865 #include <sys/stat.h>
16866
16867 static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename)
16868 {
16869 jim_stat_t sb;
16870 int fd;
16871 char *buf;
16872 int readlen;
16873
16874 if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) {
16875 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
16876 return NULL;
16877 }
16878 buf = Jim_Alloc(sb.st_size + 1);
16879 readlen = read(fd, buf, sb.st_size);
16880 close(fd);
16881 if (readlen < 0) {
16882 Jim_Free(buf);
16883 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
16884 return NULL;
16885 }
16886 else {
16887 Jim_Obj *objPtr;
16888 buf[readlen] = 0;
16889
16890 objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
16891
16892 return objPtr;
16893 }
16894 }
16895
16896
16897 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
16898 {
16899 Jim_Obj *filenameObj;
16900 Jim_Obj *oldFilenameObj;
16901 Jim_Obj *scriptObjPtr;
16902 int retcode;
16903
16904 scriptObjPtr = JimReadTextFile(interp, filename);
16905 if (!scriptObjPtr) {
16906 return JIM_ERR;
16907 }
16908
16909 filenameObj = Jim_NewStringObj(interp, filename, -1);
16910 Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1);
16911
16912 oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj);
16913
16914 retcode = Jim_EvalObj(interp, scriptObjPtr);
16915
16916 JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj);
16917
16918
16919 if (retcode == JIM_RETURN) {
16920 if (--interp->returnLevel <= 0) {
16921 retcode = interp->returnCode;
16922 interp->returnCode = JIM_OK;
16923 interp->returnLevel = 0;
16924 }
16925 }
16926
16927 return retcode;
16928 }
16929
16930 static void JimParseSubst(struct JimParserCtx *pc, int flags)
16931 {
16932 pc->tstart = pc->p;
16933 pc->tline = pc->linenr;
16934
16935 if (pc->len == 0) {
16936 pc->tend = pc->p;
16937 pc->tt = JIM_TT_EOL;
16938 pc->eof = 1;
16939 return;
16940 }
16941 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
16942 JimParseCmd(pc);
16943 return;
16944 }
16945 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
16946 if (JimParseVar(pc) == JIM_OK) {
16947 return;
16948 }
16949
16950 pc->tstart = pc->p;
16951
16952 pc->p++;
16953 pc->len--;
16954 }
16955 while (pc->len) {
16956 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
16957 break;
16958 }
16959 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
16960 break;
16961 }
16962 if (*pc->p == '\\' && pc->len > 1) {
16963 pc->p++;
16964 pc->len--;
16965 }
16966 pc->p++;
16967 pc->len--;
16968 }
16969 pc->tend = pc->p - 1;
16970 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
16971 }
16972
16973
16974 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
16975 {
16976 int scriptTextLen;
16977 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
16978 struct JimParserCtx parser;
16979 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
16980 ParseTokenList tokenlist;
16981
16982
16983 ScriptTokenListInit(&tokenlist);
16984
16985 JimParserInit(&parser, scriptText, scriptTextLen, 1);
16986 while (1) {
16987 JimParseSubst(&parser, flags);
16988 if (parser.eof) {
16989
16990 break;
16991 }
16992 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
16993 parser.tline);
16994 }
16995
16996
16997 script->inUse = 1;
16998 script->substFlags = flags;
16999 script->fileNameObj = interp->emptyObj;
17000 Jim_IncrRefCount(script->fileNameObj);
17001 SubstObjAddTokens(interp, script, &tokenlist);
17002
17003
17004 ScriptTokenListFree(&tokenlist);
17005
17006 #ifdef DEBUG_SHOW_SUBST
17007 {
17008 int i;
17009
17010 printf("==== Subst ====\n");
17011 for (i = 0; i < script->len; i++) {
17012 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
17013 Jim_String(script->token[i].objPtr));
17014 }
17015 }
17016 #endif
17017
17018
17019 Jim_FreeIntRep(interp, objPtr);
17020 Jim_SetIntRepPtr(objPtr, script);
17021 objPtr->typePtr = &scriptObjType;
17022 return JIM_OK;
17023 }
17024
17025 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
17026 {
17027 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
17028 SetSubstFromAny(interp, objPtr, flags);
17029 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
17030 }
17031
17032 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
17033 {
17034 ScriptObj *script;
17035
17036 JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object"));
17037
17038 script = Jim_GetSubst(interp, substObjPtr, flags);
17039
17040 Jim_IncrRefCount(substObjPtr);
17041 script->inUse++;
17042
17043 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
17044
17045 script->inUse--;
17046 Jim_DecrRefCount(interp, substObjPtr);
17047 if (*resObjPtrPtr == NULL) {
17048 return JIM_ERR;
17049 }
17050 return JIM_OK;
17051 }
17052
17053 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
17054 {
17055 Jim_Obj *objPtr;
17056 Jim_Obj *listObjPtr;
17057
17058 JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
17059
17060 listObjPtr = Jim_NewListObj(interp, argv, argc);
17061
17062 if (msg && *msg) {
17063 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
17064 }
17065 Jim_IncrRefCount(listObjPtr);
17066 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
17067 Jim_DecrRefCount(interp, listObjPtr);
17068
17069 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
17070 }
17071
17072 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
17073 Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type);
17074
17075 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
17076
17077 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
17078 JimHashtableIteratorCallbackType *callback, int type)
17079 {
17080 Jim_HashEntry *he;
17081 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
17082
17083
17084 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
17085 he = Jim_FindHashEntry(ht, patternObjPtr);
17086 if (he) {
17087 callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
17088 patternObjPtr, type);
17089 }
17090 }
17091 else {
17092 Jim_HashTableIterator htiter;
17093 JimInitHashTableIterator(ht, &htiter);
17094 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
17095 callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
17096 patternObjPtr, type);
17097 }
17098 }
17099 return listObjPtr;
17100 }
17101
17102
17103 #define JIM_CMDLIST_COMMANDS 0
17104 #define JIM_CMDLIST_PROCS 1
17105 #define JIM_CMDLIST_CHANNELS 2
17106
17107 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
17108 Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
17109 {
17110 Jim_Cmd *cmdPtr = (Jim_Cmd *)value;
17111
17112 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
17113
17114 return;
17115 }
17116
17117 Jim_IncrRefCount(keyObj);
17118
17119 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) {
17120 int match = 1;
17121 if (patternObj) {
17122 int plen, slen;
17123 const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
17124 const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
17125 #ifdef JIM_NO_INTROSPECTION
17126
17127 match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
17128 #else
17129 match = JimGlobMatch(pattern, plen, str, slen, 0);
17130 #endif
17131 }
17132 if (match) {
17133 Jim_ListAppendElement(interp, listObjPtr, keyObj);
17134 }
17135 }
17136 Jim_DecrRefCount(interp, keyObj);
17137 }
17138
17139 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
17140 {
17141 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
17142 }
17143
17144
17145 #define JIM_VARLIST_GLOBALS 0
17146 #define JIM_VARLIST_LOCALS 1
17147 #define JIM_VARLIST_VARS 2
17148 #define JIM_VARLIST_MASK 0x000f
17149
17150 #define JIM_VARLIST_VALUES 0x1000
17151
17152 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
17153 Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
17154 {
17155 Jim_VarVal *vv = (Jim_VarVal *)value;
17156
17157 if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) {
17158 if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) {
17159 Jim_ListAppendElement(interp, listObjPtr, keyObj);
17160 if (type & JIM_VARLIST_VALUES) {
17161 Jim_ListAppendElement(interp, listObjPtr, vv->objPtr);
17162 }
17163 }
17164 }
17165 }
17166
17167
17168 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
17169 {
17170 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
17171 return interp->emptyObj;
17172 }
17173 else {
17174 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
17175 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch,
17176 mode);
17177 }
17178 }
17179
17180 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
17181 {
17182 long level;
17183
17184 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
17185 Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level);
17186 if (targetCallFrame && targetCallFrame != interp->topFramePtr) {
17187 #ifdef JIM_NO_INTROSPECTION
17188
17189 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1);
17190 #else
17191 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
17192 #endif
17193 return JIM_OK;
17194 }
17195 }
17196 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
17197 return JIM_ERR;
17198 }
17199
17200 static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
17201 {
17202 long level;
17203
17204 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
17205 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level);
17206 if (frame) {
17207 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
17208
17209 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
17210 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1));
17211 if (frame->scriptObj) {
17212 ScriptObj *script = JimGetScript(interp, frame->scriptObj);
17213 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1));
17214 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr));
17215 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1));
17216 Jim_ListAppendElement(interp, listObj, script->fileNameObj);
17217 }
17218 #ifndef JIM_NO_INTROSPECTION
17219 {
17220 Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc);
17221
17222 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1));
17223 Jim_ListAppendElement(interp, listObj, cmdObj);
17224 }
17225 #endif
17226 {
17227 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
17228 if (procNameObj) {
17229 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1));
17230 Jim_ListAppendElement(interp, listObj, procNameObj);
17231 }
17232 }
17233 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1));
17234 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level));
17235
17236 *objPtrPtr = listObj;
17237 return JIM_OK;
17238 }
17239 }
17240 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
17241 return JIM_ERR;
17242 }
17243
17244
17245 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17246 {
17247 if (argc != 2 && argc != 3) {
17248 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
17249 return JIM_ERR;
17250 }
17251 if (argc == 3) {
17252 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
17253 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
17254 return JIM_ERR;
17255 }
17256 else {
17257 fputs(Jim_String(argv[2]), stdout);
17258 }
17259 }
17260 else {
17261 puts(Jim_String(argv[1]));
17262 }
17263 return JIM_OK;
17264 }
17265
17266
17267 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
17268 {
17269 jim_wide wideValue, res;
17270 double doubleValue, doubleRes;
17271 int i;
17272
17273 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
17274
17275 for (i = 1; i < argc; i++) {
17276 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
17277 goto trydouble;
17278 if (op == JIM_EXPROP_ADD)
17279 res += wideValue;
17280 else
17281 res *= wideValue;
17282 }
17283 Jim_SetResultInt(interp, res);
17284 return JIM_OK;
17285 trydouble:
17286 doubleRes = (double)res;
17287 for (; i < argc; i++) {
17288 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
17289 return JIM_ERR;
17290 if (op == JIM_EXPROP_ADD)
17291 doubleRes += doubleValue;
17292 else
17293 doubleRes *= doubleValue;
17294 }
17295 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17296 return JIM_OK;
17297 }
17298
17299
17300 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
17301 {
17302 jim_wide wideValue, res = 0;
17303 double doubleValue, doubleRes = 0;
17304 int i = 2;
17305
17306 if (argc < 2) {
17307 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
17308 return JIM_ERR;
17309 }
17310 else if (argc == 2) {
17311 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
17312 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
17313 return JIM_ERR;
17314 }
17315 else {
17316 if (op == JIM_EXPROP_SUB)
17317 doubleRes = -doubleValue;
17318 else
17319 doubleRes = 1.0 / doubleValue;
17320 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17321 return JIM_OK;
17322 }
17323 }
17324 if (op == JIM_EXPROP_SUB) {
17325 res = -wideValue;
17326 Jim_SetResultInt(interp, res);
17327 }
17328 else {
17329 doubleRes = 1.0 / wideValue;
17330 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17331 }
17332 return JIM_OK;
17333 }
17334 else {
17335 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
17336 if (Jim_GetDouble(interp, argv[1], &doubleRes)
17337 != JIM_OK) {
17338 return JIM_ERR;
17339 }
17340 else {
17341 goto trydouble;
17342 }
17343 }
17344 }
17345 for (i = 2; i < argc; i++) {
17346 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
17347 doubleRes = (double)res;
17348 goto trydouble;
17349 }
17350 if (op == JIM_EXPROP_SUB)
17351 res -= wideValue;
17352 else {
17353 if (wideValue == 0) {
17354 Jim_SetResultString(interp, "Division by zero", -1);
17355 return JIM_ERR;
17356 }
17357 res /= wideValue;
17358 }
17359 }
17360 Jim_SetResultInt(interp, res);
17361 return JIM_OK;
17362 trydouble:
17363 for (; i < argc; i++) {
17364 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
17365 return JIM_ERR;
17366 if (op == JIM_EXPROP_SUB)
17367 doubleRes -= doubleValue;
17368 else
17369 doubleRes /= doubleValue;
17370 }
17371 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17372 return JIM_OK;
17373 }
17374
17375
17376
17377 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17378 {
17379 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
17380 }
17381
17382
17383 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17384 {
17385 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
17386 }
17387
17388
17389 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17390 {
17391 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
17392 }
17393
17394
17395 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17396 {
17397 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
17398 }
17399
17400
17401 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17402 {
17403 if (argc != 2 && argc != 3) {
17404 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
17405 return JIM_ERR;
17406 }
17407 if (argc == 2) {
17408 Jim_Obj *objPtr;
17409
17410 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17411 if (!objPtr)
17412 return JIM_ERR;
17413 Jim_SetResult(interp, objPtr);
17414 return JIM_OK;
17415 }
17416
17417 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17418 return JIM_ERR;
17419 Jim_SetResult(interp, argv[2]);
17420 return JIM_OK;
17421 }
17422
17423 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17424 {
17425 int i = 1;
17426 int complain = 1;
17427
17428 while (i < argc) {
17429 if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
17430 i++;
17431 break;
17432 }
17433 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
17434 complain = 0;
17435 i++;
17436 continue;
17437 }
17438 break;
17439 }
17440
17441 while (i < argc) {
17442 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
17443 && complain) {
17444 return JIM_ERR;
17445 }
17446 i++;
17447 }
17448
17449 Jim_SetEmptyResult(interp);
17450 return JIM_OK;
17451 }
17452
17453 static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
17454 {
17455 if (retval == JIM_BREAK || retval == JIM_CONTINUE) {
17456 if (--interp->break_level > 0) {
17457 return 1;
17458 }
17459 }
17460 return 0;
17461 }
17462
17463
17464 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17465 {
17466 if (argc != 3) {
17467 Jim_WrongNumArgs(interp, 1, argv, "condition body");
17468 return JIM_ERR;
17469 }
17470
17471
17472 while (1) {
17473 int boolean = 0, retval;
17474
17475 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
17476 return retval;
17477 if (!boolean)
17478 break;
17479
17480 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
17481 if (JimCheckLoopRetcode(interp, retval)) {
17482 return retval;
17483 }
17484 switch (retval) {
17485 case JIM_BREAK:
17486 goto out;
17487 case JIM_CONTINUE:
17488 continue;
17489 default:
17490 return retval;
17491 }
17492 }
17493 }
17494 out:
17495 Jim_SetEmptyResult(interp);
17496 return JIM_OK;
17497 }
17498
17499
17500 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17501 {
17502 int retval;
17503 int boolean = 1;
17504 int immediate = 0;
17505 Jim_Obj *varNamePtr = NULL;
17506 Jim_Obj *stopVarNamePtr = NULL;
17507
17508 if (argc != 5) {
17509 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
17510 return JIM_ERR;
17511 }
17512
17513
17514 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
17515 return retval;
17516 }
17517
17518 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
17519
17520
17521 #ifdef JIM_OPTIMIZATION
17522 if (retval == JIM_OK && boolean) {
17523 ScriptObj *incrScript;
17524 struct ExprTree *expr;
17525 jim_wide stop, currentVal;
17526 Jim_Obj *objPtr;
17527 int cmpOffset;
17528
17529
17530 expr = JimGetExpression(interp, argv[2]);
17531 incrScript = JimGetScript(interp, argv[3]);
17532
17533
17534 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
17535 goto evalstart;
17536 }
17537
17538 if (incrScript->token[1].type != JIM_TT_ESC) {
17539 goto evalstart;
17540 }
17541
17542 if (expr->expr->type == JIM_EXPROP_LT) {
17543 cmpOffset = 0;
17544 }
17545 else if (expr->expr->type == JIM_EXPROP_LTE) {
17546 cmpOffset = 1;
17547 }
17548 else {
17549 goto evalstart;
17550 }
17551
17552 if (expr->expr->left->type != JIM_TT_VAR) {
17553 goto evalstart;
17554 }
17555
17556 if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
17557 goto evalstart;
17558 }
17559
17560
17561 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
17562 goto evalstart;
17563 }
17564
17565
17566 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
17567 goto evalstart;
17568 }
17569
17570
17571 if (expr->expr->right->type == JIM_TT_EXPR_INT) {
17572 if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
17573 goto evalstart;
17574 }
17575 }
17576 else {
17577 stopVarNamePtr = expr->expr->right->objPtr;
17578 Jim_IncrRefCount(stopVarNamePtr);
17579
17580 stop = 0;
17581 }
17582
17583
17584 varNamePtr = expr->expr->left->objPtr;
17585 Jim_IncrRefCount(varNamePtr);
17586
17587 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
17588 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
17589 goto testcond;
17590 }
17591
17592
17593 while (retval == JIM_OK) {
17594
17595
17596
17597
17598 if (stopVarNamePtr) {
17599 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
17600 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
17601 goto testcond;
17602 }
17603 }
17604
17605 if (currentVal >= stop + cmpOffset) {
17606 break;
17607 }
17608
17609
17610 retval = Jim_EvalObj(interp, argv[4]);
17611 if (JimCheckLoopRetcode(interp, retval)) {
17612 immediate++;
17613 goto out;
17614 }
17615 if (retval == JIM_OK || retval == JIM_CONTINUE) {
17616 retval = JIM_OK;
17617
17618 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
17619
17620
17621 if (objPtr == NULL) {
17622 retval = JIM_ERR;
17623 goto out;
17624 }
17625 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
17626 currentVal = ++JimWideValue(objPtr);
17627 Jim_InvalidateStringRep(objPtr);
17628 }
17629 else {
17630 if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
17631 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
17632 ++currentVal)) != JIM_OK) {
17633 goto evalnext;
17634 }
17635 }
17636 }
17637 }
17638 goto out;
17639 }
17640 evalstart:
17641 #endif
17642
17643 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
17644
17645 retval = Jim_EvalObj(interp, argv[4]);
17646 if (JimCheckLoopRetcode(interp, retval)) {
17647 immediate++;
17648 break;
17649 }
17650 if (retval == JIM_OK || retval == JIM_CONTINUE) {
17651
17652 JIM_IF_OPTIM(evalnext:)
17653 retval = Jim_EvalObj(interp, argv[3]);
17654 if (retval == JIM_OK || retval == JIM_CONTINUE) {
17655
17656 JIM_IF_OPTIM(testcond:)
17657 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
17658 }
17659 }
17660 }
17661 JIM_IF_OPTIM(out:)
17662 if (stopVarNamePtr) {
17663 Jim_DecrRefCount(interp, stopVarNamePtr);
17664 }
17665 if (varNamePtr) {
17666 Jim_DecrRefCount(interp, varNamePtr);
17667 }
17668
17669 if (!immediate) {
17670 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
17671 Jim_SetEmptyResult(interp);
17672 return JIM_OK;
17673 }
17674 }
17675
17676 return retval;
17677 }
17678
17679
17680 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17681 {
17682 int retval;
17683 jim_wide i;
17684 jim_wide limit = 0;
17685 jim_wide incr = 1;
17686 Jim_Obj *bodyObjPtr;
17687
17688 if (argc < 4 || argc > 6) {
17689 Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body");
17690 return JIM_ERR;
17691 }
17692
17693 retval = Jim_GetWideExpr(interp, argv[2], &i);
17694 if (argc > 4 && retval == JIM_OK) {
17695 retval = Jim_GetWideExpr(interp, argv[3], &limit);
17696 }
17697 if (argc > 5 && retval == JIM_OK) {
17698 Jim_GetWideExpr(interp, argv[4], &incr);
17699 }
17700 if (retval != JIM_OK) {
17701 return retval;
17702 }
17703 if (argc == 4) {
17704 limit = i;
17705 i = 0;
17706 }
17707 bodyObjPtr = argv[argc - 1];
17708
17709 retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i));
17710
17711 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
17712 retval = Jim_EvalObj(interp, bodyObjPtr);
17713 if (JimCheckLoopRetcode(interp, retval)) {
17714 return retval;
17715 }
17716 if (retval == JIM_OK || retval == JIM_CONTINUE) {
17717 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17718
17719 retval = JIM_OK;
17720
17721
17722 i += incr;
17723
17724 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
17725 if (argv[1]->typePtr != &variableObjType) {
17726 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
17727 return JIM_ERR;
17728 }
17729 }
17730 JimWideValue(objPtr) = i;
17731 Jim_InvalidateStringRep(objPtr);
17732
17733 if (argv[1]->typePtr != &variableObjType) {
17734 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
17735 retval = JIM_ERR;
17736 break;
17737 }
17738 }
17739 }
17740 else {
17741 objPtr = Jim_NewIntObj(interp, i);
17742 retval = Jim_SetVariable(interp, argv[1], objPtr);
17743 if (retval != JIM_OK) {
17744 Jim_FreeNewObj(interp, objPtr);
17745 }
17746 }
17747 }
17748 }
17749
17750 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
17751 Jim_SetEmptyResult(interp);
17752 return JIM_OK;
17753 }
17754 return retval;
17755 }
17756
17757 typedef struct {
17758 Jim_Obj *objPtr;
17759 int idx;
17760 } Jim_ListIter;
17761
17762 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
17763 {
17764 iter->objPtr = objPtr;
17765 iter->idx = 0;
17766 }
17767
17768 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
17769 {
17770 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
17771 return NULL;
17772 }
17773 return iter->objPtr->internalRep.listValue.ele[iter->idx++];
17774 }
17775
17776 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
17777 {
17778 return iter->idx >= Jim_ListLength(interp, iter->objPtr);
17779 }
17780
17781
17782 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
17783 {
17784 int result = JIM_OK;
17785 int i, numargs;
17786 Jim_ListIter twoiters[2];
17787 Jim_ListIter *iters;
17788 Jim_Obj *script;
17789 Jim_Obj *resultObj;
17790
17791 if (argc < 4 || argc % 2 != 0) {
17792 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
17793 return JIM_ERR;
17794 }
17795 script = argv[argc - 1];
17796 numargs = (argc - 1 - 1);
17797
17798 if (numargs == 2) {
17799 iters = twoiters;
17800 }
17801 else {
17802 iters = Jim_Alloc(numargs * sizeof(*iters));
17803 }
17804 for (i = 0; i < numargs; i++) {
17805 JimListIterInit(&iters[i], argv[i + 1]);
17806 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
17807 result = JIM_ERR;
17808 }
17809 }
17810 if (result != JIM_OK) {
17811 Jim_SetResultString(interp, "foreach varlist is empty", -1);
17812 goto empty_varlist;
17813 }
17814
17815 if (doMap) {
17816 resultObj = Jim_NewListObj(interp, NULL, 0);
17817 }
17818 else {
17819 resultObj = interp->emptyObj;
17820 }
17821 Jim_IncrRefCount(resultObj);
17822
17823 while (1) {
17824
17825 for (i = 0; i < numargs; i += 2) {
17826 if (!JimListIterDone(interp, &iters[i + 1])) {
17827 break;
17828 }
17829 }
17830 if (i == numargs) {
17831
17832 break;
17833 }
17834
17835
17836 for (i = 0; i < numargs; i += 2) {
17837 Jim_Obj *varName;
17838
17839
17840 JimListIterInit(&iters[i], argv[i + 1]);
17841 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
17842 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
17843 if (!valObj) {
17844
17845 valObj = interp->emptyObj;
17846 }
17847
17848 Jim_IncrRefCount(valObj);
17849 result = Jim_SetVariable(interp, varName, valObj);
17850 Jim_DecrRefCount(interp, valObj);
17851 if (result != JIM_OK) {
17852 goto err;
17853 }
17854 }
17855 }
17856 result = Jim_EvalObj(interp, script);
17857 if (JimCheckLoopRetcode(interp, result)) {
17858 goto err;
17859 }
17860 switch (result) {
17861 case JIM_OK:
17862 if (doMap) {
17863 Jim_ListAppendElement(interp, resultObj, interp->result);
17864 }
17865 break;
17866 case JIM_CONTINUE:
17867 break;
17868 case JIM_BREAK:
17869 goto out;
17870 default:
17871 goto err;
17872 }
17873 }
17874 out:
17875 result = JIM_OK;
17876 Jim_SetResult(interp, resultObj);
17877 err:
17878 Jim_DecrRefCount(interp, resultObj);
17879 empty_varlist:
17880 if (numargs > 2) {
17881 Jim_Free(iters);
17882 }
17883 return result;
17884 }
17885
17886
17887 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17888 {
17889 return JimForeachMapHelper(interp, argc, argv, 0);
17890 }
17891
17892
17893 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17894 {
17895 return JimForeachMapHelper(interp, argc, argv, 1);
17896 }
17897
17898
17899 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17900 {
17901 int result = JIM_ERR;
17902 int i;
17903 Jim_ListIter iter;
17904 Jim_Obj *resultObj;
17905
17906 if (argc < 2) {
17907 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
17908 return JIM_ERR;
17909 }
17910
17911 JimListIterInit(&iter, argv[1]);
17912
17913 for (i = 2; i < argc; i++) {
17914 Jim_Obj *valObj = JimListIterNext(interp, &iter);
17915 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
17916 if (result != JIM_OK) {
17917 return result;
17918 }
17919 }
17920
17921 resultObj = Jim_NewListObj(interp, NULL, 0);
17922 while (!JimListIterDone(interp, &iter)) {
17923 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
17924 }
17925
17926 Jim_SetResult(interp, resultObj);
17927
17928 return JIM_OK;
17929 }
17930
17931
17932 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17933 {
17934 int boolean, retval, current = 1, falsebody = 0;
17935
17936 if (argc >= 3) {
17937 while (1) {
17938
17939 if (current >= argc)
17940 goto err;
17941 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
17942 != JIM_OK)
17943 return retval;
17944
17945 if (current >= argc)
17946 goto err;
17947 if (Jim_CompareStringImmediate(interp, argv[current], "then"))
17948 current++;
17949
17950 if (current >= argc)
17951 goto err;
17952 if (boolean)
17953 return Jim_EvalObj(interp, argv[current]);
17954
17955 if (++current >= argc) {
17956 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
17957 return JIM_OK;
17958 }
17959 falsebody = current++;
17960 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
17961
17962 if (current != argc - 1)
17963 goto err;
17964 return Jim_EvalObj(interp, argv[current]);
17965 }
17966 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
17967 continue;
17968
17969 else if (falsebody != argc - 1)
17970 goto err;
17971 return Jim_EvalObj(interp, argv[falsebody]);
17972 }
17973 return JIM_OK;
17974 }
17975 err:
17976 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
17977 return JIM_ERR;
17978 }
17979
17980
17981 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
17982 Jim_Obj *stringObj, int flags)
17983 {
17984 Jim_Obj *parms[5];
17985 int argc = 0;
17986 long eq;
17987 int rc;
17988
17989 parms[argc++] = commandObj;
17990 if (flags & JIM_NOCASE) {
17991 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
17992 }
17993 if (flags & JIM_OPT_END) {
17994 parms[argc++] = Jim_NewStringObj(interp, "--", -1);
17995 }
17996 parms[argc++] = patternObj;
17997 parms[argc++] = stringObj;
17998
17999 rc = Jim_EvalObjVector(interp, argc, parms);
18000
18001 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
18002 eq = -rc;
18003 }
18004
18005 return eq;
18006 }
18007
18008
18009 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18010 {
18011 enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
18012 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
18013 int match_flags = 0;
18014 Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
18015 Jim_Obj **caseList;
18016
18017 if (argc < 3) {
18018 wrongnumargs:
18019 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
18020 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
18021 return JIM_ERR;
18022 }
18023 for (opt = 1; opt < argc; ++opt) {
18024 const char *option = Jim_String(argv[opt]);
18025
18026 if (*option != '-')
18027 break;
18028 else if (strncmp(option, "--", 2) == 0) {
18029 ++opt;
18030 break;
18031 }
18032 else if (strncmp(option, "-exact", 2) == 0)
18033 matchOpt = SWITCH_EXACT;
18034 else if (strncmp(option, "-glob", 2) == 0)
18035 matchOpt = SWITCH_GLOB;
18036 else if (strncmp(option, "-regexp", 2) == 0) {
18037 matchOpt = SWITCH_RE;
18038 match_flags |= JIM_OPT_END;
18039 }
18040 else if (strncmp(option, "-command", 2) == 0) {
18041 matchOpt = SWITCH_CMD;
18042 if ((argc - opt) < 2)
18043 goto wrongnumargs;
18044 command = argv[++opt];
18045 }
18046 else {
18047 Jim_SetResultFormatted(interp,
18048 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
18049 argv[opt]);
18050 return JIM_ERR;
18051 }
18052 if ((argc - opt) < 2)
18053 goto wrongnumargs;
18054 }
18055 strObj = argv[opt++];
18056 patCount = argc - opt;
18057 if (patCount == 1) {
18058 JimListGetElements(interp, argv[opt], &patCount, &caseList);
18059 }
18060 else
18061 caseList = (Jim_Obj **)&argv[opt];
18062 if (patCount == 0 || patCount % 2 != 0)
18063 goto wrongnumargs;
18064 for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
18065 Jim_Obj *patObj = caseList[i];
18066
18067 if (!Jim_CompareStringImmediate(interp, patObj, "default")
18068 || i < (patCount - 2)) {
18069 switch (matchOpt) {
18070 case SWITCH_EXACT:
18071 if (Jim_StringEqObj(strObj, patObj))
18072 scriptObj = caseList[i + 1];
18073 break;
18074 case SWITCH_GLOB:
18075 if (Jim_StringMatchObj(interp, patObj, strObj, 0))
18076 scriptObj = caseList[i + 1];
18077 break;
18078 case SWITCH_RE:
18079 command = Jim_NewStringObj(interp, "regexp", -1);
18080
18081 case SWITCH_CMD:{
18082 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags);
18083
18084 if (argc - opt == 1) {
18085 JimListGetElements(interp, argv[opt], &patCount, &caseList);
18086 }
18087
18088 if (rc < 0) {
18089 return -rc;
18090 }
18091 if (rc)
18092 scriptObj = caseList[i + 1];
18093 break;
18094 }
18095 }
18096 }
18097 else {
18098 scriptObj = caseList[i + 1];
18099 }
18100 }
18101 for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
18102 scriptObj = caseList[i + 1];
18103 if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
18104 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
18105 return JIM_ERR;
18106 }
18107 Jim_SetEmptyResult(interp);
18108 if (scriptObj) {
18109 return Jim_EvalObj(interp, scriptObj);
18110 }
18111 return JIM_OK;
18112 }
18113
18114
18115 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18116 {
18117 Jim_Obj *listObjPtr;
18118
18119 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
18120 Jim_SetResult(interp, listObjPtr);
18121 return JIM_OK;
18122 }
18123
18124
18125 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18126 {
18127 Jim_Obj *objPtr;
18128 int ret;
18129
18130 if (argc < 2) {
18131 Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
18132 return JIM_ERR;
18133 }
18134 ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
18135 if (ret < 0) {
18136 ret = JIM_OK;
18137 Jim_SetEmptyResult(interp);
18138 }
18139 else if (ret == JIM_OK) {
18140 Jim_SetResult(interp, objPtr);
18141 }
18142 return ret;
18143 }
18144
18145
18146 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18147 {
18148 if (argc != 2) {
18149 Jim_WrongNumArgs(interp, 1, argv, "list");
18150 return JIM_ERR;
18151 }
18152 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
18153 return JIM_OK;
18154 }
18155
18156
18157 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18158 {
18159 static const char * const options[] = {
18160 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
18161 "-stride", "-index", NULL
18162 };
18163 enum
18164 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
18165 OPT_COMMAND, OPT_STRIDE, OPT_INDEX };
18166 int i;
18167 int opt_bool = 0;
18168 int opt_not = 0;
18169 int opt_all = 0;
18170 int opt_inline = 0;
18171 int opt_match = OPT_EXACT;
18172 int listlen;
18173 int rc = JIM_OK;
18174 Jim_Obj *listObjPtr = NULL;
18175 Jim_Obj *commandObj = NULL;
18176 Jim_Obj *indexObj = NULL;
18177 int match_flags = 0;
18178 long stride = 1;
18179
18180 if (argc < 3) {
18181 wrongargs:
18182 Jim_WrongNumArgs(interp, 1, argv,
18183 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
18184 return JIM_ERR;
18185 }
18186
18187 for (i = 1; i < argc - 2; i++) {
18188 int option;
18189
18190 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
18191 return JIM_ERR;
18192 }
18193 switch (option) {
18194 case OPT_BOOL:
18195 opt_bool = 1;
18196 opt_inline = 0;
18197 break;
18198 case OPT_NOT:
18199 opt_not = 1;
18200 break;
18201 case OPT_NOCASE:
18202 match_flags |= JIM_NOCASE;
18203 break;
18204 case OPT_INLINE:
18205 opt_inline = 1;
18206 opt_bool = 0;
18207 break;
18208 case OPT_ALL:
18209 opt_all = 1;
18210 break;
18211 case OPT_REGEXP:
18212 opt_match = option;
18213 match_flags |= JIM_OPT_END;
18214 break;
18215 case OPT_COMMAND:
18216 if (i >= argc - 2) {
18217 goto wrongargs;
18218 }
18219 commandObj = argv[++i];
18220
18221 case OPT_EXACT:
18222 case OPT_GLOB:
18223 opt_match = option;
18224 break;
18225 case OPT_INDEX:
18226 if (i >= argc - 2) {
18227 goto wrongargs;
18228 }
18229 indexObj = argv[++i];
18230 break;
18231 case OPT_STRIDE:
18232 if (i >= argc - 2) {
18233 goto wrongargs;
18234 }
18235 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
18236 return JIM_ERR;
18237 }
18238 if (stride < 1) {
18239 Jim_SetResultString(interp, "stride length must be at least 1", -1);
18240 return JIM_ERR;
18241 }
18242 break;
18243 }
18244 }
18245
18246 argc -= i;
18247 if (argc < 2) {
18248 goto wrongargs;
18249 }
18250 argv += i;
18251
18252 listlen = Jim_ListLength(interp, argv[0]);
18253 if (listlen % stride) {
18254 Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
18255 return JIM_ERR;
18256 }
18257
18258 if (opt_all) {
18259 listObjPtr = Jim_NewListObj(interp, NULL, 0);
18260 }
18261 if (opt_match == OPT_REGEXP) {
18262 commandObj = Jim_NewStringObj(interp, "regexp", -1);
18263 }
18264 if (commandObj) {
18265 Jim_IncrRefCount(commandObj);
18266 }
18267
18268 for (i = 0; i < listlen; i += stride) {
18269 int eq = 0;
18270 Jim_Obj *searchListObj;
18271 Jim_Obj *objPtr;
18272 int offset;
18273
18274 if (indexObj) {
18275 int indexlen = Jim_ListLength(interp, indexObj);
18276 if (stride == 1) {
18277 searchListObj = Jim_ListGetIndex(interp, argv[0], i);
18278 }
18279 else {
18280 searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride);
18281 }
18282 Jim_IncrRefCount(searchListObj);
18283 rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG);
18284 if (rc != JIM_OK) {
18285 Jim_DecrRefCount(interp, searchListObj);
18286 rc = JIM_ERR;
18287 goto done;
18288 }
18289
18290 offset = 0;
18291 }
18292 else {
18293
18294 searchListObj = argv[0];
18295 offset = i;
18296 objPtr = Jim_ListGetIndex(interp, searchListObj, i);
18297 Jim_IncrRefCount(searchListObj);
18298 }
18299
18300 switch (opt_match) {
18301 case OPT_EXACT:
18302 eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0;
18303 break;
18304
18305 case OPT_GLOB:
18306 eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags);
18307 break;
18308
18309 case OPT_REGEXP:
18310 case OPT_COMMAND:
18311 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags);
18312 if (eq < 0) {
18313 Jim_DecrRefCount(interp, searchListObj);
18314 rc = JIM_ERR;
18315 goto done;
18316 }
18317 break;
18318 }
18319
18320
18321 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
18322 Jim_Obj *resultObj;
18323
18324 if (opt_bool) {
18325 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
18326 }
18327 else if (!opt_inline) {
18328 resultObj = Jim_NewIntObj(interp, i);
18329 }
18330 else if (stride == 1) {
18331 resultObj = objPtr;
18332 }
18333 else if (opt_all) {
18334
18335 ListInsertElements(listObjPtr, -1, stride,
18336 searchListObj->internalRep.listValue.ele + offset);
18337
18338 resultObj = NULL;
18339 }
18340 else {
18341 resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride);
18342 }
18343
18344 if (opt_all) {
18345
18346 if (stride == 1) {
18347 Jim_ListAppendElement(interp, listObjPtr, resultObj);
18348 }
18349 }
18350 else {
18351 Jim_SetResult(interp, resultObj);
18352 Jim_DecrRefCount(interp, searchListObj);
18353 goto done;
18354 }
18355 }
18356 Jim_DecrRefCount(interp, searchListObj);
18357 }
18358
18359 if (opt_all) {
18360 Jim_SetResult(interp, listObjPtr);
18361 listObjPtr = NULL;
18362 }
18363 else {
18364
18365 if (opt_bool) {
18366 Jim_SetResultBool(interp, opt_not);
18367 }
18368 else if (!opt_inline) {
18369 Jim_SetResultInt(interp, -1);
18370 }
18371 }
18372
18373 done:
18374 if (listObjPtr) {
18375 Jim_FreeNewObj(interp, listObjPtr);
18376 }
18377 if (commandObj) {
18378 Jim_DecrRefCount(interp, commandObj);
18379 }
18380 return rc;
18381 }
18382
18383
18384 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18385 {
18386 Jim_Obj *listObjPtr;
18387 int new_obj = 0;
18388 int i;
18389
18390 if (argc < 2) {
18391 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
18392 return JIM_ERR;
18393 }
18394 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
18395 if (!listObjPtr) {
18396
18397 listObjPtr = Jim_NewListObj(interp, NULL, 0);
18398 new_obj = 1;
18399 }
18400 else if (Jim_IsShared(listObjPtr)) {
18401 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
18402 new_obj = 1;
18403 }
18404 for (i = 2; i < argc; i++)
18405 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
18406 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
18407 if (new_obj)
18408 Jim_FreeNewObj(interp, listObjPtr);
18409 return JIM_ERR;
18410 }
18411 Jim_SetResult(interp, listObjPtr);
18412 return JIM_OK;
18413 }
18414
18415
18416 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18417 {
18418 int idx, len;
18419 Jim_Obj *listPtr;
18420
18421 if (argc < 3) {
18422 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
18423 return JIM_ERR;
18424 }
18425 listPtr = argv[1];
18426 if (Jim_IsShared(listPtr))
18427 listPtr = Jim_DuplicateObj(interp, listPtr);
18428 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
18429 goto err;
18430 len = Jim_ListLength(interp, listPtr);
18431 if (idx >= len)
18432 idx = len;
18433 else if (idx < 0)
18434 idx = len + idx + 1;
18435 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
18436 Jim_SetResult(interp, listPtr);
18437 return JIM_OK;
18438 err:
18439 if (listPtr != argv[1]) {
18440 Jim_FreeNewObj(interp, listPtr);
18441 }
18442 return JIM_ERR;
18443 }
18444
18445
18446 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18447 {
18448 int first, last, len, rangeLen;
18449 Jim_Obj *listObj;
18450 Jim_Obj *newListObj;
18451
18452 if (argc < 4) {
18453 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
18454 return JIM_ERR;
18455 }
18456 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
18457 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
18458 return JIM_ERR;
18459 }
18460
18461 listObj = argv[1];
18462 len = Jim_ListLength(interp, listObj);
18463
18464 first = JimRelToAbsIndex(len, first);
18465 last = JimRelToAbsIndex(len, last);
18466 JimRelToAbsRange(len, &first, &last, &rangeLen);
18467
18468
18469 if (first > len) {
18470 first = len;
18471 }
18472
18473
18474 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
18475
18476
18477 ListInsertElements(newListObj, -1, argc - 4, argv + 4);
18478
18479
18480 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
18481
18482 Jim_SetResult(interp, newListObj);
18483 return JIM_OK;
18484 }
18485
18486
18487 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18488 {
18489 if (argc < 3) {
18490 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value");
18491 return JIM_ERR;
18492 }
18493 else if (argc == 3) {
18494
18495 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
18496 return JIM_ERR;
18497 Jim_SetResult(interp, argv[2]);
18498 return JIM_OK;
18499 }
18500 return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
18501 }
18502
18503
18504 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
18505 {
18506 static const char * const options[] = {
18507 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique",
18508 "-stride", "-dictionary", NULL
18509 };
18510 enum {
18511 OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE,
18512 OPT_STRIDE, OPT_DICT
18513 };
18514 Jim_Obj *resObj;
18515 int i;
18516 int retCode;
18517 int shared;
18518 long stride = 1;
18519 Jim_Obj **elements;
18520 int listlen;
18521
18522 struct lsort_info info;
18523
18524 if (argc < 2) {
18525 wrongargs:
18526 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
18527 return JIM_ERR;
18528 }
18529
18530 info.type = JIM_LSORT_ASCII;
18531 info.order = 1;
18532 info.indexc = 0;
18533 info.unique = 0;
18534 info.command = NULL;
18535 info.interp = interp;
18536
18537 for (i = 1; i < (argc - 1); i++) {
18538 int option;
18539
18540 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
18541 != JIM_OK)
18542 return JIM_ERR;
18543 switch (option) {
18544 case OPT_ASCII:
18545 info.type = JIM_LSORT_ASCII;
18546 break;
18547 case OPT_DICT:
18548 info.type = JIM_LSORT_DICT;
18549 break;
18550 case OPT_NOCASE:
18551 info.type = JIM_LSORT_NOCASE;
18552 break;
18553 case OPT_INTEGER:
18554 info.type = JIM_LSORT_INTEGER;
18555 break;
18556 case OPT_REAL:
18557 info.type = JIM_LSORT_REAL;
18558 break;
18559 case OPT_INCREASING:
18560 info.order = 1;
18561 break;
18562 case OPT_DECREASING:
18563 info.order = -1;
18564 break;
18565 case OPT_UNIQUE:
18566 info.unique = 1;
18567 break;
18568 case OPT_COMMAND:
18569 if (i >= (argc - 2)) {
18570 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
18571 return JIM_ERR;
18572 }
18573 info.type = JIM_LSORT_COMMAND;
18574 info.command = argv[i + 1];
18575 i++;
18576 break;
18577 case OPT_STRIDE:
18578 if (i >= argc - 2) {
18579 goto wrongargs;
18580 }
18581 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
18582 return JIM_ERR;
18583 }
18584 if (stride < 2) {
18585 Jim_SetResultString(interp, "stride length must be at least 2", -1);
18586 return JIM_ERR;
18587 }
18588 break;
18589 case OPT_INDEX:
18590 if (i >= (argc - 2)) {
18591 badindex:
18592 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
18593 return JIM_ERR;
18594 }
18595 JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv);
18596 if (info.indexc == 0) {
18597 goto badindex;
18598 }
18599 i++;
18600 break;
18601 }
18602 }
18603 resObj = argv[argc - 1];
18604 JimListGetElements(interp, resObj, &listlen, &elements);
18605 if (listlen <= 1) {
18606
18607 Jim_SetResult(interp, resObj);
18608 return JIM_OK;
18609 }
18610
18611 if (stride > 1) {
18612 Jim_Obj *tmpListObj;
18613 int i;
18614
18615 if (listlen % stride) {
18616 Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
18617 return JIM_ERR;
18618 }
18619
18620 tmpListObj = Jim_NewListObj(interp, NULL, 0);
18621 Jim_IncrRefCount(tmpListObj);
18622 for (i = 0; i < listlen; i += stride) {
18623 Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride));
18624 }
18625 retCode = ListSortElements(interp, tmpListObj, &info);
18626 if (retCode == JIM_OK) {
18627 resObj = Jim_NewListObj(interp, NULL, 0);
18628
18629 for (i = 0; i < listlen; i += stride) {
18630 Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride));
18631 }
18632 Jim_SetResult(interp, resObj);
18633 }
18634 Jim_DecrRefCount(interp, tmpListObj);
18635 }
18636 else {
18637 if ((shared = Jim_IsShared(resObj))) {
18638 resObj = Jim_DuplicateObj(interp, resObj);
18639 }
18640 retCode = ListSortElements(interp, resObj, &info);
18641 if (retCode == JIM_OK) {
18642 Jim_SetResult(interp, resObj);
18643 }
18644 else if (shared) {
18645 Jim_FreeNewObj(interp, resObj);
18646 }
18647 }
18648 return retCode;
18649 }
18650
18651
18652 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18653 {
18654 Jim_Obj *stringObjPtr;
18655 int i;
18656
18657 if (argc < 2) {
18658 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
18659 return JIM_ERR;
18660 }
18661 if (argc == 2) {
18662 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
18663 if (!stringObjPtr)
18664 return JIM_ERR;
18665 }
18666 else {
18667 int new_obj = 0;
18668 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
18669 if (!stringObjPtr) {
18670
18671 stringObjPtr = Jim_NewEmptyStringObj(interp);
18672 new_obj = 1;
18673 }
18674 else if (Jim_IsShared(stringObjPtr)) {
18675 new_obj = 1;
18676 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
18677 }
18678 for (i = 2; i < argc; i++) {
18679 Jim_AppendObj(interp, stringObjPtr, argv[i]);
18680 }
18681 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
18682 if (new_obj) {
18683 Jim_FreeNewObj(interp, stringObjPtr);
18684 }
18685 return JIM_ERR;
18686 }
18687 }
18688 Jim_SetResult(interp, stringObjPtr);
18689 return JIM_OK;
18690 }
18691
18692
18693
18694
18695
18696 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18697 {
18698 int rc;
18699
18700 if (argc < 2) {
18701 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
18702 return JIM_ERR;
18703 }
18704
18705 if (argc == 2) {
18706 rc = Jim_EvalObj(interp, argv[1]);
18707 }
18708 else {
18709 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
18710 }
18711
18712 return rc;
18713 }
18714
18715
18716 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18717 {
18718 if (argc >= 2) {
18719 int retcode;
18720 Jim_CallFrame *savedCallFrame, *targetCallFrame;
18721 const char *str;
18722
18723
18724 savedCallFrame = interp->framePtr;
18725
18726
18727 str = Jim_String(argv[1]);
18728 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
18729 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
18730 argc--;
18731 argv++;
18732 }
18733 else {
18734 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
18735 }
18736 if (targetCallFrame == NULL) {
18737 return JIM_ERR;
18738 }
18739 if (argc < 2) {
18740 Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
18741 return JIM_ERR;
18742 }
18743
18744 interp->framePtr = targetCallFrame;
18745 if (argc == 2) {
18746 retcode = Jim_EvalObj(interp, argv[1]);
18747 }
18748 else {
18749 retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
18750 }
18751 interp->framePtr = savedCallFrame;
18752 return retcode;
18753 }
18754 else {
18755 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
18756 return JIM_ERR;
18757 }
18758 }
18759
18760
18761 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18762 {
18763 int retcode;
18764
18765 if (argc == 2) {
18766 retcode = Jim_EvalExpression(interp, argv[1]);
18767 }
18768 #ifndef JIM_COMPAT
18769 else {
18770 Jim_WrongNumArgs(interp, 1, argv, "expression");
18771 retcode = JIM_ERR;
18772 }
18773 #else
18774 else if (argc > 2) {
18775 Jim_Obj *objPtr;
18776
18777 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
18778 Jim_IncrRefCount(objPtr);
18779 retcode = Jim_EvalExpression(interp, objPtr);
18780 Jim_DecrRefCount(interp, objPtr);
18781 }
18782 else {
18783 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
18784 return JIM_ERR;
18785 }
18786 #endif
18787 return retcode;
18788 }
18789
18790 static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
18791 {
18792 if (argc != 1 && argc != 2) {
18793 Jim_WrongNumArgs(interp, 1, argv, "?level?");
18794 return JIM_ERR;
18795 }
18796 if (argc == 2) {
18797 long level;
18798 int ret = Jim_GetLong(interp, argv[1], &level);
18799 if (ret != JIM_OK) {
18800 return ret;
18801 }
18802 interp->break_level = level;
18803 }
18804 return retcode;
18805 }
18806
18807
18808 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18809 {
18810 return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK);
18811 }
18812
18813
18814 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18815 {
18816 return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE);
18817 }
18818
18819
18820 static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18821 {
18822 Jim_Obj *listObj;
18823 int i;
18824 jim_wide skip = 0;
18825 jim_wide last = 0;
18826
18827 if (argc > 1) {
18828 if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) {
18829 return JIM_ERR;
18830 }
18831 }
18832 if (argc > 2) {
18833 if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) {
18834 return JIM_ERR;
18835 }
18836 }
18837
18838 listObj = Jim_NewListObj(interp, NULL, 0);
18839 for (i = skip; i <= interp->procLevel; i++) {
18840 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
18841 if (frame->procLevel < last) {
18842 break;
18843 }
18844 JimAddStackFrame(interp, frame, listObj);
18845 }
18846 Jim_SetResult(interp, listObj);
18847 return JIM_OK;
18848 }
18849
18850
18851 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18852 {
18853 int i;
18854 Jim_Obj *stackTraceObj = NULL;
18855 Jim_Obj *errorCodeObj = NULL;
18856 int returnCode = JIM_OK;
18857 long level = 1;
18858
18859 for (i = 1; i < argc - 1; i += 2) {
18860 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
18861 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
18862 return JIM_ERR;
18863 }
18864 }
18865 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
18866 stackTraceObj = argv[i + 1];
18867 }
18868 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
18869 errorCodeObj = argv[i + 1];
18870 }
18871 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
18872 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
18873 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
18874 return JIM_ERR;
18875 }
18876 }
18877 else {
18878 break;
18879 }
18880 }
18881
18882 if (i != argc - 1 && i != argc) {
18883 Jim_WrongNumArgs(interp, 1, argv,
18884 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
18885 }
18886
18887
18888 if (stackTraceObj && returnCode == JIM_ERR) {
18889 JimSetStackTrace(interp, stackTraceObj);
18890 }
18891
18892 if (errorCodeObj && returnCode == JIM_ERR) {
18893 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
18894 }
18895 interp->returnCode = returnCode;
18896 interp->returnLevel = level;
18897
18898 if (i == argc - 1) {
18899 Jim_SetResult(interp, argv[i]);
18900 }
18901 return level == 0 ? returnCode : JIM_RETURN;
18902 }
18903
18904
18905 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18906 {
18907 if (interp->framePtr->level == 0) {
18908 Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
18909 return JIM_ERR;
18910 }
18911 else if (argc >= 2) {
18912
18913 Jim_CallFrame *cf = interp->framePtr->parent;
18914
18915 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
18916 if (cmdPtr == NULL) {
18917 return JIM_ERR;
18918 }
18919
18920 JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
18921
18922
18923 JimIncrCmdRefCount(cmdPtr);
18924 cf->tailcallCmd = cmdPtr;
18925
18926
18927 JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
18928
18929 cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
18930 Jim_IncrRefCount(cf->tailcallObj);
18931
18932
18933 return JIM_EVAL;
18934 }
18935 return JIM_OK;
18936 }
18937
18938 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18939 {
18940 Jim_Obj *cmdList;
18941 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
18942
18943
18944 cmdList = Jim_DuplicateObj(interp, prefixListObj);
18945 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
18946
18947 return JimEvalObjList(interp, cmdList);
18948 }
18949
18950 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
18951 {
18952 Jim_Obj *prefixListObj = privData;
18953 Jim_DecrRefCount(interp, prefixListObj);
18954 }
18955
18956 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18957 {
18958 Jim_Obj *prefixListObj;
18959
18960 if (argc < 3) {
18961 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
18962 return JIM_ERR;
18963 }
18964
18965 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
18966 Jim_IncrRefCount(prefixListObj);
18967 Jim_SetResult(interp, argv[1]);
18968
18969 return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete);
18970 }
18971
18972
18973 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18974 {
18975 Jim_Cmd *cmd;
18976
18977 if (argc != 4 && argc != 5) {
18978 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
18979 return JIM_ERR;
18980 }
18981
18982 if (argc == 4) {
18983 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
18984 }
18985 else {
18986 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
18987 }
18988
18989 if (cmd) {
18990
18991 Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]);
18992 JimCreateCommand(interp, nameObjPtr, cmd);
18993
18994
18995 JimUpdateProcNamespace(interp, cmd, nameObjPtr);
18996 Jim_DecrRefCount(interp, nameObjPtr);
18997
18998
18999 Jim_SetResult(interp, argv[1]);
19000 return JIM_OK;
19001 }
19002 return JIM_ERR;
19003 }
19004
19005
19006 static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19007 {
19008 if (argc != 2) {
19009 Jim_WrongNumArgs(interp, 1, argv, "callback");
19010 return JIM_ERR;
19011 }
19012
19013 if (interp->traceCmdObj) {
19014 Jim_DecrRefCount(interp, interp->traceCmdObj);
19015 interp->traceCmdObj = NULL;
19016 }
19017
19018 if (Jim_Length(argv[1])) {
19019
19020 interp->traceCmdObj = argv[1];
19021 Jim_IncrRefCount(interp->traceCmdObj);
19022 }
19023 return JIM_OK;
19024 }
19025
19026
19027 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19028 {
19029 int retcode;
19030
19031 if (argc < 2) {
19032 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
19033 return JIM_ERR;
19034 }
19035
19036
19037 interp->local++;
19038 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
19039 interp->local--;
19040
19041
19042
19043 if (retcode == 0) {
19044 Jim_Obj *cmdNameObj = Jim_GetResult(interp);
19045
19046 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
19047 return JIM_ERR;
19048 }
19049 if (interp->framePtr->localCommands == NULL) {
19050 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
19051 Jim_InitStack(interp->framePtr->localCommands);
19052 }
19053 Jim_IncrRefCount(cmdNameObj);
19054 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
19055 }
19056
19057 return retcode;
19058 }
19059
19060
19061 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19062 {
19063 if (argc < 2) {
19064 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
19065 return JIM_ERR;
19066 }
19067 else {
19068 int retcode;
19069
19070 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
19071 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
19072 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
19073 return JIM_ERR;
19074 }
19075
19076 cmdPtr->u.proc.upcall++;
19077 JimIncrCmdRefCount(cmdPtr);
19078
19079
19080 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
19081
19082
19083 cmdPtr->u.proc.upcall--;
19084 JimDecrCmdRefCount(interp, cmdPtr);
19085
19086 return retcode;
19087 }
19088 }
19089
19090
19091 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19092 {
19093 if (argc < 2) {
19094 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
19095 return JIM_ERR;
19096 }
19097 else {
19098 int ret;
19099 Jim_Cmd *cmd;
19100 Jim_Obj *argListObjPtr;
19101 Jim_Obj *bodyObjPtr;
19102 Jim_Obj *nsObj = NULL;
19103 Jim_Obj **nargv;
19104
19105 int len = Jim_ListLength(interp, argv[1]);
19106 if (len != 2 && len != 3) {
19107 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
19108 return JIM_ERR;
19109 }
19110
19111 if (len == 3) {
19112 #ifdef jim_ext_namespace
19113
19114 nsObj = Jim_ListGetIndex(interp, argv[1], 2);
19115 #else
19116 Jim_SetResultString(interp, "namespaces not enabled", -1);
19117 return JIM_ERR;
19118 #endif
19119 }
19120 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
19121 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
19122
19123 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
19124
19125 if (cmd) {
19126
19127 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
19128 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
19129 Jim_IncrRefCount(nargv[0]);
19130 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
19131 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
19132 Jim_DecrRefCount(interp, nargv[0]);
19133 Jim_Free(nargv);
19134
19135 JimDecrCmdRefCount(interp, cmd);
19136 return ret;
19137 }
19138 return JIM_ERR;
19139 }
19140 }
19141
19142
19143
19144 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19145 {
19146 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
19147 return JIM_OK;
19148 }
19149
19150
19151 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19152 {
19153 int i;
19154 Jim_CallFrame *targetCallFrame;
19155
19156
19157 if (argc > 3 && (argc % 2 == 0)) {
19158 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
19159 argc--;
19160 argv++;
19161 }
19162 else {
19163 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
19164 }
19165 if (targetCallFrame == NULL) {
19166 return JIM_ERR;
19167 }
19168
19169
19170 if (argc < 3) {
19171 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
19172 return JIM_ERR;
19173 }
19174
19175
19176 for (i = 1; i < argc; i += 2) {
19177 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
19178 return JIM_ERR;
19179 }
19180 return JIM_OK;
19181 }
19182
19183
19184 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19185 {
19186 int i;
19187
19188 if (argc < 2) {
19189 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
19190 return JIM_ERR;
19191 }
19192
19193 if (interp->framePtr->level == 0)
19194 return JIM_OK;
19195 for (i = 1; i < argc; i++) {
19196
19197 const char *name = Jim_String(argv[i]);
19198 if (name[0] != ':' || name[1] != ':') {
19199 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
19200 return JIM_ERR;
19201 }
19202 }
19203 return JIM_OK;
19204 }
19205
19206 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
19207 Jim_Obj *objPtr, int nocase)
19208 {
19209 int numMaps;
19210 const char *str, *noMatchStart = NULL;
19211 int strLen, i;
19212 Jim_Obj *resultObjPtr;
19213
19214 numMaps = Jim_ListLength(interp, mapListObjPtr);
19215 if (numMaps % 2) {
19216 Jim_SetResultString(interp, "list must contain an even number of elements", -1);
19217 return NULL;
19218 }
19219
19220 str = Jim_String(objPtr);
19221 strLen = Jim_Utf8Length(interp, objPtr);
19222
19223
19224 resultObjPtr = Jim_NewStringObj(interp, "", 0);
19225 while (strLen) {
19226 for (i = 0; i < numMaps; i += 2) {
19227 Jim_Obj *eachObjPtr;
19228 const char *k;
19229 int kl;
19230
19231 eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
19232 k = Jim_String(eachObjPtr);
19233 kl = Jim_Utf8Length(interp, eachObjPtr);
19234
19235 if (strLen >= kl && kl) {
19236 int rc;
19237 rc = JimStringCompareUtf8(str, kl, k, kl, nocase);
19238 if (rc == 0) {
19239 if (noMatchStart) {
19240 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
19241 noMatchStart = NULL;
19242 }
19243 Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
19244 str += utf8_index(str, kl);
19245 strLen -= kl;
19246 break;
19247 }
19248 }
19249 }
19250 if (i == numMaps) {
19251 int c;
19252 if (noMatchStart == NULL)
19253 noMatchStart = str;
19254 str += utf8_tounicode(str, &c);
19255 strLen--;
19256 }
19257 }
19258 if (noMatchStart) {
19259 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
19260 }
19261 return resultObjPtr;
19262 }
19263
19264
19265 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19266 {
19267 int len;
19268 int opt_case = 1;
19269 int option;
19270 static const char * const nocase_options[] = {
19271 "-nocase", NULL
19272 };
19273 static const char * const nocase_length_options[] = {
19274 "-nocase", "-length", NULL
19275 };
19276
19277 enum {
19278 OPT_BYTELENGTH,
19279 OPT_BYTERANGE,
19280 OPT_CAT,
19281 OPT_COMPARE,
19282 OPT_EQUAL,
19283 OPT_FIRST,
19284 OPT_INDEX,
19285 OPT_IS,
19286 OPT_LAST,
19287 OPT_LENGTH,
19288 OPT_MAP,
19289 OPT_MATCH,
19290 OPT_RANGE,
19291 OPT_REPEAT,
19292 OPT_REPLACE,
19293 OPT_REVERSE,
19294 OPT_TOLOWER,
19295 OPT_TOTITLE,
19296 OPT_TOUPPER,
19297 OPT_TRIM,
19298 OPT_TRIMLEFT,
19299 OPT_TRIMRIGHT,
19300 OPT_COUNT
19301 };
19302 static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
19303 JIM_DEF_SUBCMD("bytelength", "string", 1, 1),
19304 JIM_DEF_SUBCMD("byterange", "string first last", 3, 3),
19305 JIM_DEF_SUBCMD("cat", "?...?", 0, -1),
19306 JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5),
19307 JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5),
19308 JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3),
19309 JIM_DEF_SUBCMD("index", "string index", 2, 2),
19310 JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3),
19311 JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3),
19312 JIM_DEF_SUBCMD("length","string", 1, 1),
19313 JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3),
19314 JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3),
19315 JIM_DEF_SUBCMD("range", "string first last", 3, 3),
19316 JIM_DEF_SUBCMD("repeat", "string count", 2, 2),
19317 JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4),
19318 JIM_DEF_SUBCMD("reverse", "string", 1, 1),
19319 JIM_DEF_SUBCMD("tolower", "string", 1, 1),
19320 JIM_DEF_SUBCMD("totitle", "string", 1, 1),
19321 JIM_DEF_SUBCMD("toupper", "string", 1, 1),
19322 JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2),
19323 JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2),
19324 JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2),
19325 { NULL }
19326 };
19327 const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
19328 if (!ct) {
19329 return JIM_ERR;
19330 }
19331 if (ct->function) {
19332
19333 return ct->function(interp, argc, argv);
19334 }
19335
19336 option = ct - cmds;
19337
19338 switch (option) {
19339 case OPT_LENGTH:
19340 Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2]));
19341 return JIM_OK;
19342
19343 case OPT_BYTELENGTH:
19344 Jim_SetResultInt(interp, Jim_Length(argv[2]));
19345 return JIM_OK;
19346
19347 case OPT_CAT:{
19348 Jim_Obj *objPtr;
19349 if (argc == 3) {
19350
19351 objPtr = argv[2];
19352 }
19353 else {
19354 int i;
19355
19356 objPtr = Jim_NewStringObj(interp, "", 0);
19357
19358 for (i = 2; i < argc; i++) {
19359 Jim_AppendObj(interp, objPtr, argv[i]);
19360 }
19361 }
19362 Jim_SetResult(interp, objPtr);
19363 return JIM_OK;
19364 }
19365
19366 case OPT_COMPARE:
19367 case OPT_EQUAL:
19368 {
19369
19370 long opt_length = -1;
19371 int n = argc - 4;
19372 int i = 2;
19373 while (n > 0) {
19374 int subopt;
19375 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
19376 JIM_ENUM_ABBREV) != JIM_OK) {
19377 badcompareargs:
19378 Jim_SubCmdArgError(interp, ct, argv[0]);
19379 return JIM_ERR;
19380 }
19381 if (subopt == 0) {
19382
19383 opt_case = 0;
19384 n--;
19385 }
19386 else {
19387
19388 if (n < 2) {
19389 goto badcompareargs;
19390 }
19391 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
19392 return JIM_ERR;
19393 }
19394 n -= 2;
19395 }
19396 }
19397 if (n) {
19398 goto badcompareargs;
19399 }
19400 argv += argc - 2;
19401 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
19402
19403 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
19404 }
19405 else {
19406 const char *s1 = Jim_String(argv[0]);
19407 int l1 = Jim_Utf8Length(interp, argv[0]);
19408 const char *s2 = Jim_String(argv[1]);
19409 int l2 = Jim_Utf8Length(interp, argv[1]);
19410 if (opt_length >= 0) {
19411 if (l1 > opt_length) {
19412 l1 = opt_length;
19413 }
19414 if (l2 > opt_length) {
19415 l2 = opt_length;
19416 }
19417 }
19418 n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case);
19419 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
19420 }
19421 return JIM_OK;
19422 }
19423
19424 case OPT_MATCH:
19425 if (argc != 4 &&
19426 (argc != 5 ||
19427 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
19428 JIM_ENUM_ABBREV) != JIM_OK)) {
19429 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
19430 return JIM_ERR;
19431 }
19432 if (opt_case == 0) {
19433 argv++;
19434 }
19435 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
19436 return JIM_OK;
19437
19438 case OPT_MAP:{
19439 Jim_Obj *objPtr;
19440
19441 if (argc != 4 &&
19442 (argc != 5 ||
19443 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
19444 JIM_ENUM_ABBREV) != JIM_OK)) {
19445 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
19446 return JIM_ERR;
19447 }
19448
19449 if (opt_case == 0) {
19450 argv++;
19451 }
19452 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
19453 if (objPtr == NULL) {
19454 return JIM_ERR;
19455 }
19456 Jim_SetResult(interp, objPtr);
19457 return JIM_OK;
19458 }
19459
19460 case OPT_RANGE:{
19461 Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
19462 if (objPtr == NULL) {
19463 return JIM_ERR;
19464 }
19465 Jim_SetResult(interp, objPtr);
19466 return JIM_OK;
19467 }
19468
19469 case OPT_BYTERANGE:{
19470 Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
19471 if (objPtr == NULL) {
19472 return JIM_ERR;
19473 }
19474 Jim_SetResult(interp, objPtr);
19475 return JIM_OK;
19476 }
19477
19478 case OPT_REPLACE:{
19479 Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
19480 if (objPtr == NULL) {
19481 return JIM_ERR;
19482 }
19483 Jim_SetResult(interp, objPtr);
19484 return JIM_OK;
19485 }
19486
19487
19488 case OPT_REPEAT:{
19489 Jim_Obj *objPtr;
19490 jim_wide count;
19491
19492 if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) {
19493 return JIM_ERR;
19494 }
19495 objPtr = Jim_NewStringObj(interp, "", 0);
19496 if (count > 0) {
19497 while (count--) {
19498 Jim_AppendObj(interp, objPtr, argv[2]);
19499 }
19500 }
19501 Jim_SetResult(interp, objPtr);
19502 return JIM_OK;
19503 }
19504
19505 case OPT_REVERSE:{
19506 char *buf, *p;
19507 const char *str;
19508 int i;
19509
19510 str = Jim_GetString(argv[2], &len);
19511 buf = Jim_Alloc(len + 1);
19512 assert(buf);
19513 p = buf + len;
19514 *p = 0;
19515 for (i = 0; i < len; ) {
19516 int c;
19517 int l = utf8_tounicode(str, &c);
19518 memcpy(p - l, str, l);
19519 p -= l;
19520 i += l;
19521 str += l;
19522 }
19523 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19524 return JIM_OK;
19525 }
19526
19527 case OPT_INDEX:{
19528 int idx;
19529 const char *str;
19530
19531 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
19532 return JIM_ERR;
19533 }
19534 str = Jim_String(argv[2]);
19535 len = Jim_Utf8Length(interp, argv[2]);
19536 idx = JimRelToAbsIndex(len, idx);
19537 if (idx < 0 || idx >= len || str == NULL) {
19538 Jim_SetResultString(interp, "", 0);
19539 }
19540 else if (len == Jim_Length(argv[2])) {
19541
19542 Jim_SetResultString(interp, str + idx, 1);
19543 }
19544 else {
19545 int c;
19546 int i = utf8_index(str, idx);
19547 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
19548 }
19549 return JIM_OK;
19550 }
19551
19552 case OPT_FIRST:
19553 case OPT_LAST:{
19554 int idx = 0, l1, l2;
19555 const char *s1, *s2;
19556
19557 s1 = Jim_String(argv[2]);
19558 s2 = Jim_String(argv[3]);
19559 l1 = Jim_Utf8Length(interp, argv[2]);
19560 l2 = Jim_Utf8Length(interp, argv[3]);
19561 if (argc == 5) {
19562 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
19563 return JIM_ERR;
19564 }
19565 idx = JimRelToAbsIndex(l2, idx);
19566 if (idx < 0) {
19567 idx = 0;
19568 }
19569 }
19570 else if (option == OPT_LAST) {
19571 idx = l2;
19572 }
19573 if (option == OPT_FIRST) {
19574 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
19575 }
19576 else {
19577 #ifdef JIM_UTF8
19578 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
19579 #else
19580 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
19581 #endif
19582 }
19583 return JIM_OK;
19584 }
19585
19586 case OPT_TRIM:
19587 Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL));
19588 return JIM_OK;
19589 case OPT_TRIMLEFT:
19590 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL));
19591 return JIM_OK;
19592 case OPT_TRIMRIGHT:{
19593 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL));
19594 return JIM_OK;
19595 }
19596
19597 case OPT_TOLOWER:
19598 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
19599 return JIM_OK;
19600 case OPT_TOUPPER:
19601 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
19602 return JIM_OK;
19603 case OPT_TOTITLE:
19604 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
19605 return JIM_OK;
19606
19607 case OPT_IS:
19608 if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) {
19609 Jim_SubCmdArgError(interp, ct, argv[0]);
19610 return JIM_ERR;
19611 }
19612 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
19613 }
19614 return JIM_OK;
19615 }
19616
19617
19618 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19619 {
19620 long i, count = 1;
19621 jim_wide start, elapsed;
19622
19623 if (argc < 2) {
19624 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
19625 return JIM_ERR;
19626 }
19627 if (argc == 3) {
19628 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
19629 return JIM_ERR;
19630 }
19631 if (count < 0)
19632 return JIM_OK;
19633 i = count;
19634 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19635 while (i-- > 0) {
19636 int retval;
19637
19638 retval = Jim_EvalObj(interp, argv[1]);
19639 if (retval != JIM_OK) {
19640 return retval;
19641 }
19642 }
19643 elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19644 if (elapsed < count * 10) {
19645 Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count));
19646 }
19647 else {
19648 Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count);
19649 }
19650 Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1);
19651 return JIM_OK;
19652 }
19653
19654
19655 static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19656 {
19657 long us = 0;
19658 jim_wide start, delta, overhead;
19659 Jim_Obj *objPtr;
19660 double us_per_iter;
19661 int count;
19662 int n;
19663
19664 if (argc < 2) {
19665 Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?");
19666 return JIM_ERR;
19667 }
19668 if (argc == 3) {
19669 if (Jim_GetLong(interp, argv[2], &us) != JIM_OK)
19670 return JIM_ERR;
19671 us *= 1000;
19672 }
19673 if (us < 1) {
19674
19675 us = 1000 * 1000;
19676 }
19677
19678
19679 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19680 count = 0;
19681 do {
19682 int retval = Jim_EvalObj(interp, argv[1]);
19683 delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19684 if (retval != JIM_OK) {
19685 return retval;
19686 }
19687 count++;
19688 } while (delta < us);
19689
19690
19691 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19692 n = 0;
19693 do {
19694 int retval = Jim_EvalObj(interp, interp->nullScriptObj);
19695 overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19696 if (retval != JIM_OK) {
19697 return retval;
19698 }
19699 n++;
19700 } while (n < count);
19701
19702 delta -= overhead;
19703
19704 us_per_iter = (double)delta / count;
19705 objPtr = Jim_NewListObj(interp, NULL, 0);
19706
19707 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1));
19708 Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter));
19709 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1));
19710 Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter));
19711 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1));
19712 Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count));
19713 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1));
19714 Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta));
19715 Jim_SetResult(interp, objPtr);
19716 return JIM_OK;
19717 }
19718
19719
19720 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19721 {
19722 long exitCode = 0;
19723
19724 if (argc > 2) {
19725 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
19726 return JIM_ERR;
19727 }
19728 if (argc == 2) {
19729 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
19730 return JIM_ERR;
19731 Jim_SetResult(interp, argv[1]);
19732 }
19733 interp->exitCode = exitCode;
19734 return JIM_EXIT;
19735 }
19736
19737 static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc)
19738 {
19739 int len = Jim_ListLength(interp, retcodeListObj);
19740 int i;
19741 for (i = 0; i < len; i++) {
19742 int returncode;
19743 if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) {
19744 return JIM_ERR;
19745 }
19746 if (rc == returncode) {
19747 return JIM_OK;
19748 }
19749 }
19750 return -1;
19751 }
19752
19753
19754 static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv)
19755 {
19756 static const char * const wrongargs_catchtry[2] = {
19757 "?-?no?code ... --? script ?resultVarName? ?optionVarName?",
19758 "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
19759 };
19760 int exitCode = 0;
19761 int i;
19762 int sig = 0;
19763 int ok;
19764 Jim_Obj *finallyScriptObj = NULL;
19765 Jim_Obj *msgVarObj = NULL;
19766 Jim_Obj *optsVarObj = NULL;
19767 Jim_Obj *handlerScriptObj = NULL;
19768 Jim_Obj *errorCodeObj;
19769 int idx;
19770
19771
19772 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
19773 static const int max_ignore_code = sizeof(ignore_mask) * 8;
19774
19775 JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper"));
19776
19777 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
19778
19779 for (i = 1; i < argc - 1; i++) {
19780 const char *arg = Jim_String(argv[i]);
19781 jim_wide option;
19782 int ignore;
19783
19784
19785 if (strcmp(arg, "--") == 0) {
19786 i++;
19787 break;
19788 }
19789 if (*arg != '-') {
19790 break;
19791 }
19792
19793 if (strncmp(arg, "-no", 3) == 0) {
19794 arg += 3;
19795 ignore = 1;
19796 }
19797 else {
19798 arg++;
19799 ignore = 0;
19800 }
19801
19802 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
19803 option = -1;
19804 }
19805 if (option < 0) {
19806 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
19807 }
19808 if (option < 0) {
19809 goto wrongargs;
19810 }
19811
19812 if (ignore) {
19813 ignore_mask |= ((jim_wide)1 << option);
19814 }
19815 else {
19816 ignore_mask &= (~((jim_wide)1 << option));
19817 }
19818 }
19819
19820 idx = i;
19821
19822 if (argc - idx < 1) {
19823 wrongargs:
19824 Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]);
19825 return JIM_ERR;
19826 }
19827
19828 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
19829 sig++;
19830 }
19831
19832 interp->signal_level += sig;
19833 if (Jim_CheckSignal(interp)) {
19834
19835 exitCode = JIM_SIGNAL;
19836 }
19837 else {
19838 exitCode = Jim_EvalObj(interp, argv[idx]);
19839
19840 interp->errorFlag = 0;
19841 }
19842 interp->signal_level -= sig;
19843
19844 errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
19845
19846 idx++;
19847 if (istry) {
19848 while (idx < argc) {
19849 int option;
19850 int ret;
19851 static const char * const try_options[] = { "on", "trap", "finally", NULL };
19852 enum { TRY_ON, TRY_TRAP, TRY_FINALLY, };
19853
19854 if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) {
19855 return JIM_ERR;
19856 }
19857 switch (option) {
19858 case TRY_ON:
19859 case TRY_TRAP:
19860 if (idx + 4 > argc) {
19861 goto wrongargs;
19862 }
19863 if (option == TRY_ON) {
19864 ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
19865 if (ret > JIM_OK) {
19866 goto wrongargs;
19867 }
19868 }
19869 else if (errorCodeObj) {
19870 int len = Jim_ListLength(interp, argv[idx + 1]);
19871 int i;
19872
19873 ret = JIM_OK;
19874
19875 for (i = 0; i < len; i++) {
19876 Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
19877 Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
19878 if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
19879 ret = -1;
19880 break;
19881 }
19882 }
19883 }
19884 else {
19885
19886 ret = -1;
19887 }
19888
19889 if (ret == JIM_OK && handlerScriptObj == NULL) {
19890 msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
19891 optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
19892 handlerScriptObj = argv[idx + 3];
19893 }
19894 idx += 4;
19895 break;
19896 case TRY_FINALLY:
19897 if (idx + 2 != argc) {
19898 goto wrongargs;
19899 }
19900 finallyScriptObj = argv[idx + 1];
19901 idx += 2;
19902 break;
19903 }
19904 }
19905 }
19906 else {
19907 if (argc - idx >= 1) {
19908 msgVarObj = argv[idx];
19909 idx++;
19910 if (argc - idx >= 1) {
19911 optsVarObj = argv[idx];
19912 idx++;
19913 }
19914 }
19915 }
19916
19917
19918 if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
19919
19920 if (finallyScriptObj) {
19921 Jim_EvalObj(interp, finallyScriptObj);
19922 }
19923 return exitCode;
19924 }
19925
19926 if (sig && exitCode == JIM_SIGNAL) {
19927
19928 if (interp->signal_set_result) {
19929 interp->signal_set_result(interp, interp->sigmask);
19930 }
19931 else if (!istry) {
19932 Jim_SetResultInt(interp, interp->sigmask);
19933 }
19934 interp->sigmask = 0;
19935 }
19936
19937 ok = 1;
19938 if (msgVarObj && Jim_Length(msgVarObj)) {
19939 if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) {
19940 ok = 0;
19941 }
19942 }
19943 if (ok && optsVarObj && Jim_Length(optsVarObj)) {
19944 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
19945
19946 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
19947 Jim_ListAppendElement(interp, optListObj,
19948 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
19949 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
19950 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
19951 if (exitCode == JIM_ERR) {
19952 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
19953 -1));
19954 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
19955
19956 if (errorCodeObj) {
19957 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
19958 Jim_ListAppendElement(interp, optListObj, errorCodeObj);
19959 }
19960 }
19961 if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) {
19962 ok = 0;
19963 }
19964 }
19965 if (ok && handlerScriptObj) {
19966
19967 exitCode = Jim_EvalObj(interp, handlerScriptObj);
19968 }
19969
19970 if (finallyScriptObj) {
19971
19972 Jim_Obj *prevResultObj = Jim_GetResult(interp);
19973 Jim_IncrRefCount(prevResultObj);
19974 int ret = Jim_EvalObj(interp, finallyScriptObj);
19975 if (ret == JIM_OK) {
19976 Jim_SetResult(interp, prevResultObj);
19977 }
19978 else {
19979 exitCode = ret;
19980 }
19981 Jim_DecrRefCount(interp, prevResultObj);
19982 }
19983 if (!istry) {
19984 Jim_SetResultInt(interp, exitCode);
19985 exitCode = JIM_OK;
19986 }
19987 return exitCode;
19988 }
19989
19990
19991 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19992 {
19993 return JimCatchTryHelper(interp, 0, argc, argv);
19994 }
19995
19996
19997 static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19998 {
19999 return JimCatchTryHelper(interp, 1, argc, argv);
20000 }
20001
20002
20003
20004 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20005 {
20006 if (argc != 3) {
20007 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
20008 return JIM_ERR;
20009 }
20010
20011 return Jim_RenameCommand(interp, argv[1], argv[2]);
20012 }
20013
20014 #define JIM_DICTMATCH_KEYS 0x0001
20015 #define JIM_DICTMATCH_VALUES 0x002
20016
20017 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
20018 {
20019 Jim_Obj *listObjPtr;
20020 Jim_Dict *dict;
20021 int i;
20022
20023 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20024 return JIM_ERR;
20025 }
20026 dict = objPtr->internalRep.dictValue;
20027
20028 listObjPtr = Jim_NewListObj(interp, NULL, 0);
20029
20030 for (i = 0; i < dict->len; i += 2 ) {
20031 Jim_Obj *keyObj = dict->table[i];
20032 Jim_Obj *valObj = dict->table[i + 1];
20033 if (patternObj) {
20034 Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj;
20035 if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) {
20036
20037 continue;
20038 }
20039 }
20040 if (return_types & JIM_DICTMATCH_KEYS) {
20041 Jim_ListAppendElement(interp, listObjPtr, keyObj);
20042 }
20043 if (return_types & JIM_DICTMATCH_VALUES) {
20044 Jim_ListAppendElement(interp, listObjPtr, valObj);
20045 }
20046 }
20047
20048 Jim_SetResult(interp, listObjPtr);
20049 return JIM_OK;
20050 }
20051
20052 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
20053 {
20054 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20055 return -1;
20056 }
20057 return objPtr->internalRep.dictValue->len / 2;
20058 }
20059
20060 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
20061 {
20062 Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
20063 int i;
20064
20065 JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
20066
20067
20068
20069 for (i = 0; i < objc; i++) {
20070 Jim_Obj **table;
20071 int tablelen;
20072 int j;
20073
20074 table = Jim_DictPairs(interp, objv[i], &tablelen);
20075 if (tablelen && !table) {
20076 Jim_FreeNewObj(interp, objPtr);
20077 return NULL;
20078 }
20079 for (j = 0; j < tablelen; j += 2) {
20080 DictAddElement(interp, objPtr, table[j], table[j + 1]);
20081 }
20082 }
20083 return objPtr;
20084 }
20085
20086 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
20087 {
20088 char buffer[100];
20089 Jim_Obj *output;
20090 Jim_Dict *dict;
20091
20092 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20093 return JIM_ERR;
20094 }
20095
20096 dict = objPtr->internalRep.dictValue;
20097
20098
20099 snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size);
20100 output = Jim_NewStringObj(interp, buffer, -1);
20101 Jim_SetResult(interp, output);
20102 return JIM_OK;
20103 }
20104
20105 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
20106 {
20107 Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
20108
20109 Jim_AppendString(interp, prefixObj, " ", 1);
20110 Jim_AppendString(interp, prefixObj, subcmd, -1);
20111
20112 return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
20113 }
20114
20115 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
20116 {
20117 int i;
20118 Jim_Obj *objPtr;
20119 Jim_Obj *dictObj;
20120 Jim_Obj **dictValues;
20121 int len;
20122 int ret = JIM_OK;
20123
20124
20125 dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
20126 if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
20127 return JIM_ERR;
20128 }
20129
20130 dictValues = Jim_DictPairs(interp, objPtr, &len);
20131 if (len && dictValues == NULL) {
20132 return JIM_ERR;
20133 }
20134 for (i = 0; i < len; i += 2) {
20135 if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
20136 return JIM_ERR;
20137 }
20138 }
20139
20140
20141 if (Jim_Length(scriptObj)) {
20142 ret = Jim_EvalObj(interp, scriptObj);
20143
20144
20145 if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
20146
20147 Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
20148 for (i = 0; i < keyc; i++) {
20149 newkeyv[i] = keyv[i];
20150 }
20151
20152 for (i = 0; i < len; i += 2) {
20153
20154 if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) {
20155
20156 objPtr = Jim_GetVariable(interp, dictValues[i], 0);
20157 newkeyv[keyc] = dictValues[i];
20158 Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT);
20159 }
20160 }
20161 Jim_Free(newkeyv);
20162 }
20163 }
20164
20165 return ret;
20166 }
20167
20168
20169 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20170 {
20171 Jim_Obj *objPtr;
20172 int types = JIM_DICTMATCH_KEYS;
20173
20174 enum {
20175 OPT_CREATE,
20176 OPT_GET,
20177 OPT_GETDEF,
20178 OPT_GETWITHDEFAULT,
20179 OPT_SET,
20180 OPT_UNSET,
20181 OPT_EXISTS,
20182 OPT_KEYS,
20183 OPT_SIZE,
20184 OPT_INFO,
20185 OPT_MERGE,
20186 OPT_WITH,
20187 OPT_APPEND,
20188 OPT_LAPPEND,
20189 OPT_INCR,
20190 OPT_REMOVE,
20191 OPT_VALUES,
20192 OPT_FOR,
20193 OPT_REPLACE,
20194 OPT_UPDATE,
20195 OPT_COUNT
20196 };
20197 static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
20198 JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2),
20199 JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1),
20200 JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1),
20201 JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1),
20202 JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1),
20203 JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1),
20204 JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1),
20205 JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2),
20206 JIM_DEF_SUBCMD("size", "dictionary", 1, 1),
20207 JIM_DEF_SUBCMD("info", "dictionary", 1, 1),
20208 JIM_DEF_SUBCMD("merge", "?...?", 0, -1),
20209 JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1),
20210 JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1),
20211 JIM_DEF_SUBCMD("lappend", "varName key ?value ...?", 2, -1),
20212 JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3),
20213 JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1),
20214 JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2),
20215 JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3),
20216 JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1),
20217 JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1),
20218 { NULL }
20219 };
20220 const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
20221 if (!ct) {
20222 return JIM_ERR;
20223 }
20224 if (ct->function) {
20225
20226 return ct->function(interp, argc, argv);
20227 }
20228
20229
20230 switch (ct - cmds) {
20231 case OPT_GET:
20232 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
20233 JIM_ERRMSG) != JIM_OK) {
20234 return JIM_ERR;
20235 }
20236 Jim_SetResult(interp, objPtr);
20237 return JIM_OK;
20238
20239 case OPT_GETDEF:
20240 case OPT_GETWITHDEFAULT:{
20241 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG);
20242 if (rc == -1) {
20243
20244 return JIM_ERR;
20245 }
20246 if (rc == JIM_ERR) {
20247 Jim_SetResult(interp, argv[argc - 1]);
20248 }
20249 else {
20250 Jim_SetResult(interp, objPtr);
20251 }
20252 return JIM_OK;
20253 }
20254
20255 case OPT_SET:
20256 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
20257
20258 case OPT_EXISTS:{
20259 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
20260 if (rc < 0) {
20261 return JIM_ERR;
20262 }
20263 Jim_SetResultBool(interp, rc == JIM_OK);
20264 return JIM_OK;
20265 }
20266
20267 case OPT_UNSET:
20268 if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
20269 return JIM_ERR;
20270 }
20271 return JIM_OK;
20272
20273 case OPT_VALUES:
20274 types = JIM_DICTMATCH_VALUES;
20275
20276 case OPT_KEYS:
20277 return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
20278
20279 case OPT_SIZE:
20280 if (Jim_DictSize(interp, argv[2]) < 0) {
20281 return JIM_ERR;
20282 }
20283 Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
20284 return JIM_OK;
20285
20286 case OPT_MERGE:
20287 if (argc == 2) {
20288 return JIM_OK;
20289 }
20290 objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
20291 if (objPtr == NULL) {
20292 return JIM_ERR;
20293 }
20294 Jim_SetResult(interp, objPtr);
20295 return JIM_OK;
20296
20297 case OPT_CREATE:
20298 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
20299 Jim_SetResult(interp, objPtr);
20300 return JIM_OK;
20301
20302 case OPT_INFO:
20303 return Jim_DictInfo(interp, argv[2]);
20304
20305 case OPT_WITH:
20306 return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
20307
20308 case OPT_UPDATE:
20309 if (argc < 6 || argc % 2) {
20310
20311 argc = 2;
20312 }
20313
20314 default:
20315 return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2);
20316 }
20317 }
20318
20319
20320 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20321 {
20322 static const char * const options[] = {
20323 "-nobackslashes", "-nocommands", "-novariables", NULL
20324 };
20325 enum
20326 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
20327 int i;
20328 int flags = JIM_SUBST_FLAG;
20329 Jim_Obj *objPtr;
20330
20331 if (argc < 2) {
20332 Jim_WrongNumArgs(interp, 1, argv, "?options? string");
20333 return JIM_ERR;
20334 }
20335 for (i = 1; i < (argc - 1); i++) {
20336 int option;
20337
20338 if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
20339 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
20340 return JIM_ERR;
20341 }
20342 switch (option) {
20343 case OPT_NOBACKSLASHES:
20344 flags |= JIM_SUBST_NOESC;
20345 break;
20346 case OPT_NOCOMMANDS:
20347 flags |= JIM_SUBST_NOCMD;
20348 break;
20349 case OPT_NOVARIABLES:
20350 flags |= JIM_SUBST_NOVAR;
20351 break;
20352 }
20353 }
20354 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
20355 return JIM_ERR;
20356 }
20357 Jim_SetResult(interp, objPtr);
20358 return JIM_OK;
20359 }
20360
20361 #ifdef jim_ext_namespace
20362 static int JimIsGlobalNamespace(Jim_Obj *objPtr)
20363 {
20364 int len;
20365 const char *str = Jim_GetString(objPtr, &len);
20366 return len >= 2 && str[0] == ':' && str[1] == ':';
20367 }
20368 #endif
20369
20370
20371 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20372 {
20373 Jim_Obj *objPtr;
20374 int mode = 0;
20375
20376
20377 enum {
20378 INFO_ALIAS,
20379 INFO_ARGS,
20380 INFO_BODY,
20381 INFO_CHANNELS,
20382 INFO_COMMANDS,
20383 INFO_COMPLETE,
20384 INFO_EXISTS,
20385 INFO_FRAME,
20386 INFO_GLOBALS,
20387 INFO_HOSTNAME,
20388 INFO_LEVEL,
20389 INFO_LOCALS,
20390 INFO_NAMEOFEXECUTABLE,
20391 INFO_PATCHLEVEL,
20392 INFO_PROCS,
20393 INFO_REFERENCES,
20394 INFO_RETURNCODES,
20395 INFO_SCRIPT,
20396 INFO_SOURCE,
20397 INFO_STACKTRACE,
20398 INFO_STATICS,
20399 INFO_VARS,
20400 INFO_VERSION,
20401 INFO_COUNT
20402 };
20403 static const jim_subcmd_type cmds[INFO_COUNT + 1] = {
20404 JIM_DEF_SUBCMD("alias", "command", 1, 1),
20405 JIM_DEF_SUBCMD("args", "procname", 1, 1),
20406 JIM_DEF_SUBCMD("body", "procname", 1, 1),
20407 JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1),
20408 JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1),
20409 JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2),
20410 JIM_DEF_SUBCMD("exists", "varName", 1, 1),
20411 JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1),
20412 JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1),
20413 JIM_DEF_SUBCMD("hostname", NULL, 0, 0),
20414 JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1),
20415 JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1),
20416 JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0),
20417 JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0),
20418 JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1),
20419 JIM_DEF_SUBCMD("references", NULL, 0, 0),
20420 JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1),
20421 JIM_DEF_SUBCMD("script", "?filename?", 0, 1),
20422 JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3),
20423 JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0),
20424 JIM_DEF_SUBCMD("statics", "procname", 1, 1),
20425 JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1),
20426 JIM_DEF_SUBCMD("version", NULL, 0, 0),
20427 { NULL }
20428 };
20429 const jim_subcmd_type *ct;
20430 #ifdef jim_ext_namespace
20431 int nons = 0;
20432
20433 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
20434
20435 argc--;
20436 argv++;
20437 nons = 1;
20438 }
20439 #endif
20440 ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
20441 if (!ct) {
20442 return JIM_ERR;
20443 }
20444 if (ct->function) {
20445
20446 return ct->function(interp, argc, argv);
20447 }
20448
20449 int option = ct - cmds;
20450
20451 switch (option) {
20452 case INFO_EXISTS:
20453 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
20454 return JIM_OK;
20455
20456 case INFO_ALIAS:{
20457 Jim_Cmd *cmdPtr;
20458
20459 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
20460 return JIM_ERR;
20461 }
20462 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
20463 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
20464 return JIM_ERR;
20465 }
20466 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
20467 return JIM_OK;
20468 }
20469
20470 case INFO_CHANNELS:
20471 mode++;
20472 #ifndef jim_ext_aio
20473 Jim_SetResultString(interp, "aio not enabled", -1);
20474 return JIM_ERR;
20475 #endif
20476
20477 case INFO_PROCS:
20478 mode++;
20479
20480 case INFO_COMMANDS:
20481
20482 #ifdef jim_ext_namespace
20483 if (!nons) {
20484 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
20485 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
20486 }
20487 }
20488 #endif
20489 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
20490 return JIM_OK;
20491
20492 case INFO_VARS:
20493 mode++;
20494
20495 case INFO_LOCALS:
20496 mode++;
20497
20498 case INFO_GLOBALS:
20499
20500 #ifdef jim_ext_namespace
20501 if (!nons) {
20502 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
20503 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
20504 }
20505 }
20506 #endif
20507 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
20508 return JIM_OK;
20509
20510 case INFO_SCRIPT:
20511 if (argc == 3) {
20512 Jim_IncrRefCount(argv[2]);
20513 Jim_DecrRefCount(interp, interp->currentFilenameObj);
20514 interp->currentFilenameObj = argv[2];
20515 }
20516 Jim_SetResult(interp, interp->currentFilenameObj);
20517 return JIM_OK;
20518
20519 case INFO_SOURCE:{
20520 Jim_Obj *resObjPtr;
20521 Jim_Obj *fileNameObj;
20522
20523 if (argc == 4) {
20524 Jim_SubCmdArgError(interp, ct, argv[0]);
20525 return JIM_ERR;
20526 }
20527 if (argc == 5) {
20528 jim_wide line;
20529 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
20530 return JIM_ERR;
20531 }
20532 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
20533 Jim_SetSourceInfo(interp, resObjPtr, argv[3], line);
20534 }
20535 else {
20536 int line;
20537 fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line);
20538 resObjPtr = Jim_NewListObj(interp, NULL, 0);
20539 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
20540 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
20541 }
20542 Jim_SetResult(interp, resObjPtr);
20543 return JIM_OK;
20544 }
20545
20546 case INFO_STACKTRACE:
20547 Jim_SetResult(interp, interp->stackTrace);
20548 return JIM_OK;
20549
20550 case INFO_LEVEL:
20551 if (argc == 2) {
20552 Jim_SetResultInt(interp, interp->framePtr->level);
20553 }
20554 else {
20555 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) {
20556 return JIM_ERR;
20557 }
20558 Jim_SetResult(interp, objPtr);
20559 }
20560 return JIM_OK;
20561
20562 case INFO_FRAME:
20563 if (argc == 2) {
20564 Jim_SetResultInt(interp, interp->procLevel + 1);
20565 }
20566 else {
20567 if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) {
20568 return JIM_ERR;
20569 }
20570 Jim_SetResult(interp, objPtr);
20571 }
20572 return JIM_OK;
20573
20574 case INFO_BODY:
20575 case INFO_STATICS:
20576 case INFO_ARGS:{
20577 Jim_Cmd *cmdPtr;
20578
20579 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
20580 return JIM_ERR;
20581 }
20582 if (!cmdPtr->isproc) {
20583 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
20584 return JIM_ERR;
20585 }
20586 switch (option) {
20587 #ifdef JIM_NO_INTROSPECTION
20588 default:
20589 Jim_SetResultString(interp, "unsupported", -1);
20590 return JIM_ERR;
20591 #else
20592 case INFO_BODY:
20593 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
20594 break;
20595 case INFO_ARGS:
20596 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
20597 break;
20598 #endif
20599 case INFO_STATICS:
20600 if (cmdPtr->u.proc.staticVars) {
20601 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
20602 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
20603 }
20604 break;
20605 }
20606 return JIM_OK;
20607 }
20608
20609 case INFO_VERSION:
20610 case INFO_PATCHLEVEL:{
20611 char buf[(JIM_INTEGER_SPACE * 2) + 1];
20612
20613 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
20614 Jim_SetResultString(interp, buf, -1);
20615 return JIM_OK;
20616 }
20617
20618 case INFO_COMPLETE: {
20619 char missing;
20620
20621 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
20622 if (missing != ' ' && argc == 4) {
20623 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
20624 }
20625 return JIM_OK;
20626 }
20627
20628 case INFO_HOSTNAME:
20629
20630 return Jim_Eval(interp, "os.gethostname");
20631
20632 case INFO_NAMEOFEXECUTABLE:
20633
20634 return Jim_Eval(interp, "{info nameofexecutable}");
20635
20636 case INFO_RETURNCODES:
20637 if (argc == 2) {
20638 int i;
20639 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
20640
20641 for (i = 0; jimReturnCodes[i]; i++) {
20642 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
20643 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
20644 jimReturnCodes[i], -1));
20645 }
20646
20647 Jim_SetResult(interp, listObjPtr);
20648 }
20649 else if (argc == 3) {
20650 long code;
20651 const char *name;
20652
20653 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
20654 return JIM_ERR;
20655 }
20656 name = Jim_ReturnCode(code);
20657 if (*name == '?') {
20658 Jim_SetResultInt(interp, code);
20659 }
20660 else {
20661 Jim_SetResultString(interp, name, -1);
20662 }
20663 }
20664 return JIM_OK;
20665 case INFO_REFERENCES:
20666 #ifdef JIM_REFERENCES
20667 return JimInfoReferences(interp, argc, argv);
20668 #else
20669 Jim_SetResultString(interp, "not supported", -1);
20670 return JIM_ERR;
20671 #endif
20672 default:
20673 abort();
20674 }
20675 }
20676
20677
20678 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20679 {
20680 Jim_Obj *objPtr;
20681 int result = 0;
20682
20683 static const char * const options[] = {
20684 "-command", "-proc", "-alias", "-var", NULL
20685 };
20686 enum
20687 {
20688 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
20689 };
20690 int option;
20691
20692 if (argc == 2) {
20693 option = OPT_VAR;
20694 objPtr = argv[1];
20695 }
20696 else if (argc == 3) {
20697 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
20698 return JIM_ERR;
20699 }
20700 objPtr = argv[2];
20701 }
20702 else {
20703 Jim_WrongNumArgs(interp, 1, argv, "?option? name");
20704 return JIM_ERR;
20705 }
20706
20707 if (option == OPT_VAR) {
20708 result = Jim_GetVariable(interp, objPtr, 0) != NULL;
20709 }
20710 else {
20711
20712 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
20713
20714 if (cmd) {
20715 switch (option) {
20716 case OPT_COMMAND:
20717 result = 1;
20718 break;
20719
20720 case OPT_ALIAS:
20721 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
20722 break;
20723
20724 case OPT_PROC:
20725 result = cmd->isproc;
20726 break;
20727 }
20728 }
20729 }
20730 Jim_SetResultBool(interp, result);
20731 return JIM_OK;
20732 }
20733
20734
20735 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20736 {
20737 const char *str, *splitChars, *noMatchStart;
20738 int splitLen, strLen;
20739 Jim_Obj *resObjPtr;
20740 int c;
20741 int len;
20742
20743 if (argc != 2 && argc != 3) {
20744 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
20745 return JIM_ERR;
20746 }
20747
20748 str = Jim_GetString(argv[1], &len);
20749 if (len == 0) {
20750 return JIM_OK;
20751 }
20752 strLen = Jim_Utf8Length(interp, argv[1]);
20753
20754
20755 if (argc == 2) {
20756 splitChars = " \n\t\r";
20757 splitLen = 4;
20758 }
20759 else {
20760 splitChars = Jim_String(argv[2]);
20761 splitLen = Jim_Utf8Length(interp, argv[2]);
20762 }
20763
20764 noMatchStart = str;
20765 resObjPtr = Jim_NewListObj(interp, NULL, 0);
20766
20767
20768 if (splitLen) {
20769 Jim_Obj *objPtr;
20770 while (strLen--) {
20771 const char *sc = splitChars;
20772 int scLen = splitLen;
20773 int sl = utf8_tounicode(str, &c);
20774 while (scLen--) {
20775 int pc;
20776 sc += utf8_tounicode(sc, &pc);
20777 if (c == pc) {
20778 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
20779 Jim_ListAppendElement(interp, resObjPtr, objPtr);
20780 noMatchStart = str + sl;
20781 break;
20782 }
20783 }
20784 str += sl;
20785 }
20786 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
20787 Jim_ListAppendElement(interp, resObjPtr, objPtr);
20788 }
20789 else {
20790 Jim_Obj **commonObj = NULL;
20791 #define NUM_COMMON (128 - 9)
20792 while (strLen--) {
20793 int n = utf8_tounicode(str, &c);
20794 #ifdef JIM_OPTIMIZATION
20795 if (c >= 9 && c < 128) {
20796
20797 c -= 9;
20798 if (!commonObj) {
20799 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
20800 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
20801 }
20802 if (!commonObj[c]) {
20803 commonObj[c] = Jim_NewStringObj(interp, str, 1);
20804 }
20805 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
20806 str++;
20807 continue;
20808 }
20809 #endif
20810 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
20811 str += n;
20812 }
20813 Jim_Free(commonObj);
20814 }
20815
20816 Jim_SetResult(interp, resObjPtr);
20817 return JIM_OK;
20818 }
20819
20820
20821 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20822 {
20823 const char *joinStr;
20824 int joinStrLen;
20825
20826 if (argc != 2 && argc != 3) {
20827 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
20828 return JIM_ERR;
20829 }
20830
20831 if (argc == 2) {
20832 joinStr = " ";
20833 joinStrLen = 1;
20834 }
20835 else {
20836 joinStr = Jim_GetString(argv[2], &joinStrLen);
20837 }
20838 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
20839 return JIM_OK;
20840 }
20841
20842
20843 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20844 {
20845 Jim_Obj *objPtr;
20846
20847 if (argc < 2) {
20848 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
20849 return JIM_ERR;
20850 }
20851 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
20852 if (objPtr == NULL)
20853 return JIM_ERR;
20854 Jim_SetResult(interp, objPtr);
20855 return JIM_OK;
20856 }
20857
20858
20859 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20860 {
20861 Jim_Obj *listPtr, **outVec;
20862 int outc, i;
20863
20864 if (argc < 3) {
20865 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
20866 return JIM_ERR;
20867 }
20868 if (argv[2]->typePtr != &scanFmtStringObjType)
20869 SetScanFmtFromAny(interp, argv[2]);
20870 if (FormatGetError(argv[2]) != 0) {
20871 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
20872 return JIM_ERR;
20873 }
20874 if (argc > 3) {
20875 int maxPos = FormatGetMaxPos(argv[2]);
20876 int count = FormatGetCnvCount(argv[2]);
20877
20878 if (maxPos > argc - 3) {
20879 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
20880 return JIM_ERR;
20881 }
20882 else if (count > argc - 3) {
20883 Jim_SetResultString(interp, "different numbers of variable names and "
20884 "field specifiers", -1);
20885 return JIM_ERR;
20886 }
20887 else if (count < argc - 3) {
20888 Jim_SetResultString(interp, "variable is not assigned by any "
20889 "conversion specifiers", -1);
20890 return JIM_ERR;
20891 }
20892 }
20893 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
20894 if (listPtr == 0)
20895 return JIM_ERR;
20896 if (argc > 3) {
20897 int rc = JIM_OK;
20898 int count = 0;
20899
20900 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
20901 int len = Jim_ListLength(interp, listPtr);
20902
20903 if (len != 0) {
20904 JimListGetElements(interp, listPtr, &outc, &outVec);
20905 for (i = 0; i < outc; ++i) {
20906 if (Jim_Length(outVec[i]) > 0) {
20907 ++count;
20908 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
20909 rc = JIM_ERR;
20910 }
20911 }
20912 }
20913 }
20914 Jim_FreeNewObj(interp, listPtr);
20915 }
20916 else {
20917 count = -1;
20918 }
20919 if (rc == JIM_OK) {
20920 Jim_SetResultInt(interp, count);
20921 }
20922 return rc;
20923 }
20924 else {
20925 if (listPtr == (Jim_Obj *)EOF) {
20926 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
20927 return JIM_OK;
20928 }
20929 Jim_SetResult(interp, listPtr);
20930 }
20931 return JIM_OK;
20932 }
20933
20934
20935 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20936 {
20937 if (argc != 2 && argc != 3) {
20938 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
20939 return JIM_ERR;
20940 }
20941 Jim_SetResult(interp, argv[1]);
20942 if (argc == 3) {
20943 JimSetStackTrace(interp, argv[2]);
20944 return JIM_ERR;
20945 }
20946 return JIM_ERR;
20947 }
20948
20949
20950 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20951 {
20952 Jim_Obj *objPtr;
20953
20954 if (argc != 4) {
20955 Jim_WrongNumArgs(interp, 1, argv, "list first last");
20956 return JIM_ERR;
20957 }
20958 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
20959 return JIM_ERR;
20960 Jim_SetResult(interp, objPtr);
20961 return JIM_OK;
20962 }
20963
20964
20965 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20966 {
20967 Jim_Obj *objPtr;
20968 jim_wide count;
20969
20970 if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) {
20971 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
20972 return JIM_ERR;
20973 }
20974 if (count == 0 || argc == 2) {
20975 Jim_SetEmptyResult(interp);
20976 return JIM_OK;
20977 }
20978
20979 argc -= 2;
20980 argv += 2;
20981
20982 objPtr = Jim_NewListObj(interp, NULL, 0);
20983 ListEnsureLength(objPtr, argc * count);
20984 while (count--) {
20985 ListInsertElements(objPtr, -1, argc, argv);
20986 }
20987
20988 Jim_SetResult(interp, objPtr);
20989 return JIM_OK;
20990 }
20991
20992 char **Jim_GetEnviron(void)
20993 {
20994 #if defined(HAVE__NSGETENVIRON)
20995 return *_NSGetEnviron();
20996 #elif defined(_environ)
20997 return _environ;
20998 #else
20999 #if !defined(NO_ENVIRON_EXTERN)
21000 extern char **environ;
21001 #endif
21002 return environ;
21003 #endif
21004 }
21005
21006 void Jim_SetEnviron(char **env)
21007 {
21008 #if defined(HAVE__NSGETENVIRON)
21009 *_NSGetEnviron() = env;
21010 #elif defined(_environ)
21011 _environ = env;
21012 #else
21013 #if !defined(NO_ENVIRON_EXTERN)
21014 extern char **environ;
21015 #endif
21016
21017 environ = env;
21018 #endif
21019 }
21020
21021
21022 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21023 {
21024 const char *key;
21025 const char *val;
21026
21027 if (argc == 1) {
21028 char **e = Jim_GetEnviron();
21029
21030 int i;
21031 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
21032
21033 for (i = 0; e[i]; i++) {
21034 const char *equals = strchr(e[i], '=');
21035
21036 if (equals) {
21037 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
21038 equals - e[i]));
21039 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
21040 }
21041 }
21042
21043 Jim_SetResult(interp, listObjPtr);
21044 return JIM_OK;
21045 }
21046
21047 if (argc > 3) {
21048 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
21049 return JIM_ERR;
21050 }
21051 key = Jim_String(argv[1]);
21052 val = getenv(key);
21053 if (val == NULL) {
21054 if (argc < 3) {
21055 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
21056 return JIM_ERR;
21057 }
21058 val = Jim_String(argv[2]);
21059 }
21060 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
21061 return JIM_OK;
21062 }
21063
21064
21065 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21066 {
21067 int retval;
21068
21069 if (argc != 2) {
21070 Jim_WrongNumArgs(interp, 1, argv, "fileName");
21071 return JIM_ERR;
21072 }
21073 retval = Jim_EvalFile(interp, Jim_String(argv[1]));
21074 if (retval == JIM_RETURN)
21075 return JIM_OK;
21076 return retval;
21077 }
21078
21079
21080 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21081 {
21082 Jim_Obj *revObjPtr, **ele;
21083 int len;
21084
21085 if (argc != 2) {
21086 Jim_WrongNumArgs(interp, 1, argv, "list");
21087 return JIM_ERR;
21088 }
21089 JimListGetElements(interp, argv[1], &len, &ele);
21090 revObjPtr = Jim_NewListObj(interp, NULL, 0);
21091 ListEnsureLength(revObjPtr, len);
21092 len--;
21093 while (len >= 0)
21094 ListAppendElement(revObjPtr, ele[len--]);
21095 Jim_SetResult(interp, revObjPtr);
21096 return JIM_OK;
21097 }
21098
21099 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
21100 {
21101 jim_wide len;
21102
21103 if (step == 0)
21104 return -1;
21105 if (start == end)
21106 return 0;
21107 else if (step > 0 && start > end)
21108 return -1;
21109 else if (step < 0 && end > start)
21110 return -1;
21111 len = end - start;
21112 if (len < 0)
21113 len = -len;
21114 if (step < 0)
21115 step = -step;
21116 len = 1 + ((len - 1) / step);
21117 if (len > INT_MAX)
21118 len = INT_MAX;
21119 return (int)((len < 0) ? -1 : len);
21120 }
21121
21122
21123 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21124 {
21125 jim_wide start = 0, end, step = 1;
21126 int len, i;
21127 Jim_Obj *objPtr;
21128
21129 if (argc < 2 || argc > 4) {
21130 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
21131 return JIM_ERR;
21132 }
21133 if (argc == 2) {
21134 if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK)
21135 return JIM_ERR;
21136 }
21137 else {
21138 if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK ||
21139 Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK)
21140 return JIM_ERR;
21141 if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK)
21142 return JIM_ERR;
21143 }
21144 if ((len = JimRangeLen(start, end, step)) == -1) {
21145 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
21146 return JIM_ERR;
21147 }
21148 objPtr = Jim_NewListObj(interp, NULL, 0);
21149 ListEnsureLength(objPtr, len);
21150 for (i = 0; i < len; i++)
21151 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
21152 Jim_SetResult(interp, objPtr);
21153 return JIM_OK;
21154 }
21155
21156
21157 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21158 {
21159 jim_wide min = 0, max = 0, len, maxMul;
21160
21161 if (argc < 1 || argc > 3) {
21162 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
21163 return JIM_ERR;
21164 }
21165 if (argc == 1) {
21166 max = JIM_WIDE_MAX;
21167 } else if (argc == 2) {
21168 if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK)
21169 return JIM_ERR;
21170 } else if (argc == 3) {
21171 if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK ||
21172 Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK)
21173 return JIM_ERR;
21174 }
21175 len = max-min;
21176 if (len < 0) {
21177 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
21178 return JIM_ERR;
21179 }
21180 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
21181 while (1) {
21182 jim_wide r;
21183
21184 JimRandomBytes(interp, &r, sizeof(jim_wide));
21185 if (r < 0 || r >= maxMul) continue;
21186 r = (len == 0) ? 0 : r%len;
21187 Jim_SetResultInt(interp, min+r);
21188 return JIM_OK;
21189 }
21190 }
21191
21192 static const struct {
21193 const char *name;
21194 Jim_CmdProc *cmdProc;
21195 } Jim_CoreCommandsTable[] = {
21196 {"alias", Jim_AliasCoreCommand},
21197 {"set", Jim_SetCoreCommand},
21198 {"unset", Jim_UnsetCoreCommand},
21199 {"puts", Jim_PutsCoreCommand},
21200 {"+", Jim_AddCoreCommand},
21201 {"*", Jim_MulCoreCommand},
21202 {"-", Jim_SubCoreCommand},
21203 {"/", Jim_DivCoreCommand},
21204 {"incr", Jim_IncrCoreCommand},
21205 {"while", Jim_WhileCoreCommand},
21206 {"loop", Jim_LoopCoreCommand},
21207 {"for", Jim_ForCoreCommand},
21208 {"foreach", Jim_ForeachCoreCommand},
21209 {"lmap", Jim_LmapCoreCommand},
21210 {"lassign", Jim_LassignCoreCommand},
21211 {"if", Jim_IfCoreCommand},
21212 {"switch", Jim_SwitchCoreCommand},
21213 {"list", Jim_ListCoreCommand},
21214 {"lindex", Jim_LindexCoreCommand},
21215 {"lset", Jim_LsetCoreCommand},
21216 {"lsearch", Jim_LsearchCoreCommand},
21217 {"llength", Jim_LlengthCoreCommand},
21218 {"lappend", Jim_LappendCoreCommand},
21219 {"linsert", Jim_LinsertCoreCommand},
21220 {"lreplace", Jim_LreplaceCoreCommand},
21221 {"lsort", Jim_LsortCoreCommand},
21222 {"append", Jim_AppendCoreCommand},
21223 {"eval", Jim_EvalCoreCommand},
21224 {"uplevel", Jim_UplevelCoreCommand},
21225 {"expr", Jim_ExprCoreCommand},
21226 {"break", Jim_BreakCoreCommand},
21227 {"continue", Jim_ContinueCoreCommand},
21228 {"proc", Jim_ProcCoreCommand},
21229 {"xtrace", Jim_XtraceCoreCommand},
21230 {"concat", Jim_ConcatCoreCommand},
21231 {"return", Jim_ReturnCoreCommand},
21232 {"upvar", Jim_UpvarCoreCommand},
21233 {"global", Jim_GlobalCoreCommand},
21234 {"string", Jim_StringCoreCommand},
21235 {"time", Jim_TimeCoreCommand},
21236 {"timerate", Jim_TimeRateCoreCommand},
21237 {"exit", Jim_ExitCoreCommand},
21238 {"catch", Jim_CatchCoreCommand},
21239 {"try", Jim_TryCoreCommand},
21240 #ifdef JIM_REFERENCES
21241 {"ref", Jim_RefCoreCommand},
21242 {"getref", Jim_GetrefCoreCommand},
21243 {"setref", Jim_SetrefCoreCommand},
21244 {"finalize", Jim_FinalizeCoreCommand},
21245 {"collect", Jim_CollectCoreCommand},
21246 #endif
21247 {"rename", Jim_RenameCoreCommand},
21248 {"dict", Jim_DictCoreCommand},
21249 {"subst", Jim_SubstCoreCommand},
21250 {"info", Jim_InfoCoreCommand},
21251 {"exists", Jim_ExistsCoreCommand},
21252 {"split", Jim_SplitCoreCommand},
21253 {"join", Jim_JoinCoreCommand},
21254 {"format", Jim_FormatCoreCommand},
21255 {"scan", Jim_ScanCoreCommand},
21256 {"error", Jim_ErrorCoreCommand},
21257 {"lrange", Jim_LrangeCoreCommand},
21258 {"lrepeat", Jim_LrepeatCoreCommand},
21259 {"env", Jim_EnvCoreCommand},
21260 {"source", Jim_SourceCoreCommand},
21261 {"lreverse", Jim_LreverseCoreCommand},
21262 {"range", Jim_RangeCoreCommand},
21263 {"rand", Jim_RandCoreCommand},
21264 {"tailcall", Jim_TailcallCoreCommand},
21265 {"local", Jim_LocalCoreCommand},
21266 {"upcall", Jim_UpcallCoreCommand},
21267 {"apply", Jim_ApplyCoreCommand},
21268 {"stacktrace", Jim_StacktraceCoreCommand},
21269 {NULL, NULL},
21270 };
21271
21272 void Jim_RegisterCoreCommands(Jim_Interp *interp)
21273 {
21274 int i = 0;
21275
21276 while (Jim_CoreCommandsTable[i].name != NULL) {
21277 Jim_CreateCommand(interp,
21278 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
21279 i++;
21280 }
21281 }
21282
21283 void Jim_MakeErrorMessage(Jim_Interp *interp)
21284 {
21285 Jim_Obj *argv[2];
21286
21287 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
21288 argv[1] = interp->result;
21289
21290 Jim_EvalObjVector(interp, 2, argv);
21291 }
21292
21293 static char **JimSortStringTable(const char *const *tablePtr)
21294 {
21295 int count;
21296 char **tablePtrSorted;
21297
21298
21299 for (count = 0; tablePtr[count]; count++) {
21300 }
21301
21302
21303 tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
21304 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
21305 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
21306 tablePtrSorted[count] = NULL;
21307
21308 return tablePtrSorted;
21309 }
21310
21311 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
21312 const char *prefix, const char *const *tablePtr, const char *name)
21313 {
21314 char **tablePtrSorted;
21315 int i;
21316
21317 if (name == NULL) {
21318 name = "option";
21319 }
21320
21321 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
21322 tablePtrSorted = JimSortStringTable(tablePtr);
21323 for (i = 0; tablePtrSorted[i]; i++) {
21324 if (tablePtrSorted[i + 1] == NULL && i > 0) {
21325 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
21326 }
21327 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
21328 if (tablePtrSorted[i + 1]) {
21329 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
21330 }
21331 }
21332 Jim_Free(tablePtrSorted);
21333 }
21334
21335
21336 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
21337 {
21338 if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
21339 int i;
21340 char **tablePtrSorted = JimSortStringTable(tablePtr);
21341 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
21342 for (i = 0; tablePtrSorted[i]; i++) {
21343 Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
21344 }
21345 Jim_Free(tablePtrSorted);
21346 return JIM_OK;
21347 }
21348 return JIM_ERR;
21349 }
21350
21351 static const Jim_ObjType getEnumObjType = {
21352 "get-enum",
21353 NULL,
21354 NULL,
21355 NULL,
21356 JIM_TYPE_REFERENCES
21357 };
21358
21359 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
21360 const char *const *tablePtr, int *indexPtr, const char *name, int flags)
21361 {
21362 const char *bad = "bad ";
21363 const char *const *entryPtr = NULL;
21364 int i;
21365 int match = -1;
21366 int arglen;
21367 const char *arg;
21368
21369 if (objPtr->typePtr == &getEnumObjType) {
21370 if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
21371 *indexPtr = objPtr->internalRep.ptrIntValue.int2;
21372 return JIM_OK;
21373 }
21374 }
21375
21376 arg = Jim_GetString(objPtr, &arglen);
21377
21378 *indexPtr = -1;
21379
21380 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
21381 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
21382
21383 match = i;
21384 goto found;
21385 }
21386 if (flags & JIM_ENUM_ABBREV) {
21387 if (strncmp(arg, *entryPtr, arglen) == 0) {
21388 if (*arg == '-' && arglen == 1) {
21389 break;
21390 }
21391 if (match >= 0) {
21392 bad = "ambiguous ";
21393 goto ambiguous;
21394 }
21395 match = i;
21396 }
21397 }
21398 }
21399
21400
21401 if (match >= 0) {
21402 found:
21403
21404 Jim_FreeIntRep(interp, objPtr);
21405 objPtr->typePtr = &getEnumObjType;
21406 objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
21407 objPtr->internalRep.ptrIntValue.int1 = flags;
21408 objPtr->internalRep.ptrIntValue.int2 = match;
21409
21410 *indexPtr = match;
21411 return JIM_OK;
21412 }
21413
21414 ambiguous:
21415 if (flags & JIM_ERRMSG) {
21416 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
21417 }
21418 return JIM_ERR;
21419 }
21420
21421 int Jim_FindByName(const char *name, const char * const array[], size_t len)
21422 {
21423 int i;
21424
21425 for (i = 0; i < (int)len; i++) {
21426 if (array[i] && strcmp(array[i], name) == 0) {
21427 return i;
21428 }
21429 }
21430 return -1;
21431 }
21432
21433 int Jim_IsDict(Jim_Obj *objPtr)
21434 {
21435 return objPtr->typePtr == &dictObjType;
21436 }
21437
21438 int Jim_IsList(Jim_Obj *objPtr)
21439 {
21440 return objPtr->typePtr == &listObjType;
21441 }
21442
21443 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
21444 {
21445
21446 int len = strlen(format);
21447 int extra = 0;
21448 int n = 0;
21449 const char *params[5];
21450 int nobjparam = 0;
21451 Jim_Obj *objparam[5];
21452 char *buf;
21453 va_list args;
21454 int i;
21455
21456 va_start(args, format);
21457
21458 for (i = 0; i < len && n < 5; i++) {
21459 int l;
21460
21461 if (strncmp(format + i, "%s", 2) == 0) {
21462 params[n] = va_arg(args, char *);
21463
21464 l = strlen(params[n]);
21465 }
21466 else if (strncmp(format + i, "%#s", 3) == 0) {
21467 Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
21468
21469 params[n] = Jim_GetString(objPtr, &l);
21470 objparam[nobjparam++] = objPtr;
21471 Jim_IncrRefCount(objPtr);
21472 }
21473 else {
21474 if (format[i] == '%') {
21475 i++;
21476 }
21477 continue;
21478 }
21479 n++;
21480 extra += l;
21481 }
21482
21483 len += extra;
21484 buf = Jim_Alloc(len + 1);
21485 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
21486
21487 va_end(args);
21488
21489 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
21490
21491 for (i = 0; i < nobjparam; i++) {
21492 Jim_DecrRefCount(interp, objparam[i]);
21493 }
21494 }
21495
21496 int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version)
21497 {
21498 if (abi_version != JIM_ABI_VERSION) {
21499 Jim_SetResultString(interp, "ABI version mismatch", -1);
21500 return JIM_ERR;
21501 }
21502 return JIM_OK;
21503 }
21504
21505
21506 #ifndef jim_ext_package
21507 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
21508 {
21509 return JIM_OK;
21510 }
21511 #endif
21512 #ifndef jim_ext_aio
21513 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
21514 {
21515 return -1;
21516 }
21517 #endif
21518
21519
21520 #include <stdio.h>
21521 #include <string.h>
21522
21523
21524 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21525 {
21526
21527 return JIM_OK;
21528 }
21529
21530 static const jim_subcmd_type dummy_subcmd = {
21531 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
21532 };
21533
21534 static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
21535 {
21536
21537 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
21538 Jim_Obj *sortCmd[2];
21539
21540 for (; ct->cmd; ct++) {
21541 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
21542 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1));
21543 }
21544 }
21545
21546
21547 sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1);
21548 sortCmd[1] = listObj;
21549
21550 if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) {
21551 return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep));
21552 }
21553
21554 return Jim_GetResult(interp);
21555 }
21556
21557 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
21558 Jim_Obj *cmd, Jim_Obj *subcmd)
21559 {
21560 Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type,
21561 subcmd, subcmd_cmd_list(interp, command_table, ", "));
21562 }
21563
21564 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
21565 Jim_Obj *const *argv)
21566 {
21567 Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s",
21568 argv[0], subcmd_cmd_list(interp, command_table, ", "));
21569 }
21570
21571 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
21572 {
21573 if (cmd) {
21574 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
21575 }
21576 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
21577 if (ct->args && *ct->args) {
21578 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
21579 }
21580 }
21581
21582 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd)
21583 {
21584 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
21585 add_cmd_usage(interp, ct, subcmd);
21586 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
21587 }
21588
21589 static const Jim_ObjType subcmdLookupObjType = {
21590 "subcmd-lookup",
21591 NULL,
21592 NULL,
21593 NULL,
21594 JIM_TYPE_REFERENCES
21595 };
21596
21597 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
21598 int argc, Jim_Obj *const *argv)
21599 {
21600 const jim_subcmd_type *ct;
21601 const jim_subcmd_type *partial = 0;
21602 int cmdlen;
21603 Jim_Obj *cmd;
21604 const char *cmdstr;
21605 int help = 0;
21606 int argsok = 1;
21607
21608 if (argc < 2) {
21609 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
21610 "Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
21611 return 0;
21612 }
21613
21614 cmd = argv[1];
21615
21616
21617 if (cmd->typePtr == &subcmdLookupObjType) {
21618 if (cmd->internalRep.ptrIntValue.ptr == command_table) {
21619 ct = command_table + cmd->internalRep.ptrIntValue.int1;
21620 goto found;
21621 }
21622 }
21623
21624
21625 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
21626 if (argc == 2) {
21627
21628 show_cmd_usage(interp, command_table, argc, argv);
21629 return &dummy_subcmd;
21630 }
21631 help = 1;
21632
21633
21634 cmd = argv[2];
21635 }
21636
21637
21638 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
21639 Jim_SetResult(interp, subcmd_cmd_list(interp, command_table, " "));
21640 return &dummy_subcmd;
21641 }
21642
21643 cmdstr = Jim_GetString(cmd, &cmdlen);
21644
21645 for (ct = command_table; ct->cmd; ct++) {
21646 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
21647
21648 break;
21649 }
21650 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
21651 if (partial) {
21652
21653 if (help) {
21654
21655 show_cmd_usage(interp, command_table, argc, argv);
21656 return &dummy_subcmd;
21657 }
21658 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
21659 return 0;
21660 }
21661 partial = ct;
21662 }
21663 continue;
21664 }
21665
21666
21667 if (partial && !ct->cmd) {
21668 ct = partial;
21669 }
21670
21671 if (!ct->cmd) {
21672
21673 if (help) {
21674
21675 show_cmd_usage(interp, command_table, argc, argv);
21676 return &dummy_subcmd;
21677 }
21678 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
21679 return 0;
21680 }
21681
21682 if (help) {
21683 Jim_SetResultString(interp, "Usage: ", -1);
21684
21685 add_cmd_usage(interp, ct, argv[0]);
21686 return &dummy_subcmd;
21687 }
21688
21689
21690 Jim_FreeIntRep(interp, cmd);
21691 cmd->typePtr = &subcmdLookupObjType;
21692 cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
21693 cmd->internalRep.ptrIntValue.int1 = ct - command_table;
21694
21695 found:
21696
21697
21698 if (argc - 2 < ct->minargs) {
21699 argsok = 0;
21700 }
21701 else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) {
21702 argsok = 0;
21703 }
21704 else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) {
21705
21706 argsok = 0;
21707 }
21708 if (!argsok) {
21709 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
21710
21711 add_cmd_usage(interp, ct, argv[0]);
21712 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
21713
21714 return 0;
21715 }
21716
21717
21718 return ct;
21719 }
21720
21721 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
21722 {
21723 int ret = JIM_ERR;
21724
21725 if (ct) {
21726 if (ct->flags & JIM_MODFLAG_FULLARGV) {
21727 ret = ct->function(interp, argc, argv);
21728 }
21729 else {
21730 ret = ct->function(interp, argc - 2, argv + 2);
21731 }
21732 if (ret < 0) {
21733 Jim_SubCmdArgError(interp, ct, argv[0]);
21734 ret = JIM_ERR;
21735 }
21736 }
21737 return ret;
21738 }
21739
21740 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21741 {
21742 const jim_subcmd_type *ct =
21743 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
21744
21745 return Jim_CallSubCmd(interp, ct, argc, argv);
21746 }
21747
21748 #include <ctype.h>
21749 #include <stdlib.h>
21750 #include <string.h>
21751 #include <stdio.h>
21752 #include <assert.h>
21753
21754
21755 int utf8_fromunicode(char *p, unsigned uc)
21756 {
21757 if (uc <= 0x7f) {
21758 *p = uc;
21759 return 1;
21760 }
21761 else if (uc <= 0x7ff) {
21762 *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
21763 *p = 0x80 | (uc & 0x3f);
21764 return 2;
21765 }
21766 else if (uc <= 0xffff) {
21767 *p++ = 0xe0 | ((uc & 0xf000) >> 12);
21768 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
21769 *p = 0x80 | (uc & 0x3f);
21770 return 3;
21771 }
21772
21773 else {
21774 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
21775 *p++ = 0x80 | ((uc & 0x3f000) >> 12);
21776 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
21777 *p = 0x80 | (uc & 0x3f);
21778 return 4;
21779 }
21780 }
21781
21782 #include <ctype.h>
21783 #include <string.h>
21784 #include <stdio.h>
21785
21786
21787 #define JIM_INTEGER_SPACE 24
21788 #define MAX_FLOAT_WIDTH 320
21789
21790 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
21791 {
21792 const char *span, *format, *formatEnd, *msg;
21793 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
21794 static const char * const mixedXPG =
21795 "cannot mix \"%\" and \"%n$\" conversion specifiers";
21796 static const char * const badIndex[2] = {
21797 "not enough arguments for all format specifiers",
21798 "\"%n$\" argument index out of range"
21799 };
21800 int formatLen;
21801 Jim_Obj *resultPtr;
21802
21803 char *num_buffer = NULL;
21804 int num_buffer_size = 0;
21805
21806 span = format = Jim_GetString(fmtObjPtr, &formatLen);
21807 formatEnd = format + formatLen;
21808 resultPtr = Jim_NewEmptyStringObj(interp);
21809
21810 while (format != formatEnd) {
21811 char *end;
21812 int gotMinus, sawFlag;
21813 int gotPrecision, useShort;
21814 long width, precision;
21815 int newXpg;
21816 int ch;
21817 int step;
21818 int doubleType;
21819 char pad = ' ';
21820 char spec[2*JIM_INTEGER_SPACE + 12];
21821 char *p;
21822
21823 int formatted_chars;
21824 int formatted_bytes;
21825 const char *formatted_buf;
21826
21827 step = utf8_tounicode(format, &ch);
21828 format += step;
21829 if (ch != '%') {
21830 numBytes += step;
21831 continue;
21832 }
21833 if (numBytes) {
21834 Jim_AppendString(interp, resultPtr, span, numBytes);
21835 numBytes = 0;
21836 }
21837
21838
21839 step = utf8_tounicode(format, &ch);
21840 if (ch == '%') {
21841 span = format;
21842 numBytes = step;
21843 format += step;
21844 continue;
21845 }
21846
21847
21848 newXpg = 0;
21849 if (isdigit(ch)) {
21850 int position = strtoul(format, &end, 10);
21851 if (*end == '$') {
21852 newXpg = 1;
21853 objIndex = position - 1;
21854 format = end + 1;
21855 step = utf8_tounicode(format, &ch);
21856 }
21857 }
21858 if (newXpg) {
21859 if (gotSequential) {
21860 msg = mixedXPG;
21861 goto errorMsg;
21862 }
21863 gotXpg = 1;
21864 } else {
21865 if (gotXpg) {
21866 msg = mixedXPG;
21867 goto errorMsg;
21868 }
21869 gotSequential = 1;
21870 }
21871 if ((objIndex < 0) || (objIndex >= objc)) {
21872 msg = badIndex[gotXpg];
21873 goto errorMsg;
21874 }
21875
21876 p = spec;
21877 *p++ = '%';
21878
21879 gotMinus = 0;
21880 sawFlag = 1;
21881 do {
21882 switch (ch) {
21883 case '-':
21884 gotMinus = 1;
21885 break;
21886 case '0':
21887 pad = ch;
21888 break;
21889 case ' ':
21890 case '+':
21891 case '#':
21892 break;
21893 default:
21894 sawFlag = 0;
21895 continue;
21896 }
21897 *p++ = ch;
21898 format += step;
21899 step = utf8_tounicode(format, &ch);
21900
21901 } while (sawFlag && (p - spec <= 5));
21902
21903
21904 width = 0;
21905 if (isdigit(ch)) {
21906 width = strtoul(format, &end, 10);
21907 format = end;
21908 step = utf8_tounicode(format, &ch);
21909 } else if (ch == '*') {
21910 if (objIndex >= objc - 1) {
21911 msg = badIndex[gotXpg];
21912 goto errorMsg;
21913 }
21914 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
21915 goto error;
21916 }
21917 if (width < 0) {
21918 width = -width;
21919 if (!gotMinus) {
21920 *p++ = '-';
21921 gotMinus = 1;
21922 }
21923 }
21924 objIndex++;
21925 format += step;
21926 step = utf8_tounicode(format, &ch);
21927 }
21928
21929
21930 gotPrecision = precision = 0;
21931 if (ch == '.') {
21932 gotPrecision = 1;
21933 format += step;
21934 step = utf8_tounicode(format, &ch);
21935 }
21936 if (isdigit(ch)) {
21937 precision = strtoul(format, &end, 10);
21938 format = end;
21939 step = utf8_tounicode(format, &ch);
21940 } else if (ch == '*') {
21941 if (objIndex >= objc - 1) {
21942 msg = badIndex[gotXpg];
21943 goto errorMsg;
21944 }
21945 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
21946 goto error;
21947 }
21948
21949
21950 if (precision < 0) {
21951 precision = 0;
21952 }
21953 objIndex++;
21954 format += step;
21955 step = utf8_tounicode(format, &ch);
21956 }
21957
21958
21959 useShort = 0;
21960 if (ch == 'h') {
21961 useShort = 1;
21962 format += step;
21963 step = utf8_tounicode(format, &ch);
21964 } else if (ch == 'l') {
21965
21966 format += step;
21967 step = utf8_tounicode(format, &ch);
21968 if (ch == 'l') {
21969 format += step;
21970 step = utf8_tounicode(format, &ch);
21971 }
21972 }
21973
21974 format += step;
21975 span = format;
21976
21977
21978 if (ch == 'i') {
21979 ch = 'd';
21980 }
21981
21982 doubleType = 0;
21983
21984 switch (ch) {
21985 case '\0':
21986 msg = "format string ended in middle of field specifier";
21987 goto errorMsg;
21988 case 's': {
21989 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
21990 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
21991 if (gotPrecision && (precision < formatted_chars)) {
21992
21993 formatted_chars = precision;
21994 formatted_bytes = utf8_index(formatted_buf, precision);
21995 }
21996 break;
21997 }
21998 case 'c': {
21999 jim_wide code;
22000
22001 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
22002 goto error;
22003 }
22004
22005 formatted_bytes = utf8_getchars(spec, code);
22006 formatted_buf = spec;
22007 formatted_chars = 1;
22008 break;
22009 }
22010 case 'b': {
22011 unsigned jim_wide w;
22012 int length;
22013 int i;
22014 int j;
22015
22016 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
22017 goto error;
22018 }
22019 length = sizeof(w) * 8;
22020
22021
22022
22023 if (num_buffer_size < length + 1) {
22024 num_buffer_size = length + 1;
22025 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
22026 }
22027
22028 j = 0;
22029 for (i = length; i > 0; ) {
22030 i--;
22031 if (w & ((unsigned jim_wide)1 << i)) {
22032 num_buffer[j++] = '1';
22033 }
22034 else if (j || i == 0) {
22035 num_buffer[j++] = '0';
22036 }
22037 }
22038 num_buffer[j] = 0;
22039 formatted_chars = formatted_bytes = j;
22040 formatted_buf = num_buffer;
22041 break;
22042 }
22043
22044 case 'e':
22045 case 'E':
22046 case 'f':
22047 case 'g':
22048 case 'G':
22049 doubleType = 1;
22050
22051 case 'd':
22052 case 'u':
22053 case 'o':
22054 case 'x':
22055 case 'X': {
22056 jim_wide w;
22057 double d;
22058 int length;
22059
22060
22061 if (width) {
22062 p += sprintf(p, "%ld", width);
22063 }
22064 if (gotPrecision) {
22065 p += sprintf(p, ".%ld", precision);
22066 }
22067
22068
22069 if (doubleType) {
22070 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
22071 goto error;
22072 }
22073 length = MAX_FLOAT_WIDTH;
22074 }
22075 else {
22076 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
22077 goto error;
22078 }
22079 length = JIM_INTEGER_SPACE;
22080 if (useShort) {
22081 if (ch == 'd') {
22082 w = (short)w;
22083 }
22084 else {
22085 w = (unsigned short)w;
22086 }
22087 }
22088 *p++ = 'l';
22089 #ifdef HAVE_LONG_LONG
22090 if (sizeof(long long) == sizeof(jim_wide)) {
22091 *p++ = 'l';
22092 }
22093 #endif
22094 }
22095
22096 *p++ = (char) ch;
22097 *p = '\0';
22098
22099
22100 if (width > 10000 || length > 10000 || precision > 10000) {
22101 Jim_SetResultString(interp, "format too long", -1);
22102 goto error;
22103 }
22104
22105
22106
22107 if (width > length) {
22108 length = width;
22109 }
22110 if (gotPrecision) {
22111 length += precision;
22112 }
22113
22114
22115 if (num_buffer_size < length + 1) {
22116 num_buffer_size = length + 1;
22117 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
22118 }
22119
22120 if (doubleType) {
22121 snprintf(num_buffer, length + 1, spec, d);
22122 }
22123 else {
22124 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
22125 }
22126 formatted_chars = formatted_bytes = strlen(num_buffer);
22127 formatted_buf = num_buffer;
22128 break;
22129 }
22130
22131 default: {
22132
22133 spec[0] = ch;
22134 spec[1] = '\0';
22135 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
22136 goto error;
22137 }
22138 }
22139
22140 if (!gotMinus) {
22141 while (formatted_chars < width) {
22142 Jim_AppendString(interp, resultPtr, &pad, 1);
22143 formatted_chars++;
22144 }
22145 }
22146
22147 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
22148
22149 while (formatted_chars < width) {
22150 Jim_AppendString(interp, resultPtr, &pad, 1);
22151 formatted_chars++;
22152 }
22153
22154 objIndex += gotSequential;
22155 }
22156 if (numBytes) {
22157 Jim_AppendString(interp, resultPtr, span, numBytes);
22158 }
22159
22160 Jim_Free(num_buffer);
22161 return resultPtr;
22162
22163 errorMsg:
22164 Jim_SetResultString(interp, msg, -1);
22165 error:
22166 Jim_FreeNewObj(interp, resultPtr);
22167 Jim_Free(num_buffer);
22168 return NULL;
22169 }
22170
22171
22172 #if defined(JIM_REGEXP)
22173 #include <stdio.h>
22174 #include <ctype.h>
22175 #include <stdlib.h>
22176 #include <string.h>
22177
22178
22179
22180 #define REG_MAX_PAREN 100
22181
22182
22183
22184 #define END 0
22185 #define BOL 1
22186 #define EOL 2
22187 #define ANY 3
22188 #define ANYOF 4
22189 #define ANYBUT 5
22190 #define BRANCH 6
22191 #define BACK 7
22192 #define EXACTLY 8
22193 #define NOTHING 9
22194 #define REP 10
22195 #define REPMIN 11
22196 #define REPX 12
22197 #define REPXMIN 13
22198 #define BOLX 14
22199 #define EOLX 15
22200 #define WORDA 16
22201 #define WORDZ 17
22202
22203 #define OPENNC 1000
22204 #define OPEN 1001
22205
22206
22207
22208
22209 #define CLOSENC 2000
22210 #define CLOSE 2001
22211 #define CLOSE_END (CLOSE+REG_MAX_PAREN)
22212
22213 #define REG_MAGIC 0xFADED00D
22214
22215
22216 #define OP(preg, p) (preg->program[p])
22217 #define NEXT(preg, p) (preg->program[p + 1])
22218 #define OPERAND(p) ((p) + 2)
22219
22220
22221
22222
22223 #define FAIL(R,M) { (R)->err = (M); return (M); }
22224 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
22225 #define META "^$.[()|?{+*"
22226
22227 #define HASWIDTH 1
22228 #define SIMPLE 2
22229 #define SPSTART 4
22230 #define WORST 0
22231
22232 #define MAX_REP_COUNT 1000000
22233
22234 static int reg(regex_t *preg, int paren, int *flagp );
22235 static int regpiece(regex_t *preg, int *flagp );
22236 static int regbranch(regex_t *preg, int *flagp );
22237 static int regatom(regex_t *preg, int *flagp );
22238 static int regnode(regex_t *preg, int op );
22239 static int regnext(regex_t *preg, int p );
22240 static void regc(regex_t *preg, int b );
22241 static int reginsert(regex_t *preg, int op, int size, int opnd );
22242 static void regtail(regex_t *preg, int p, int val);
22243 static void regoptail(regex_t *preg, int p, int val );
22244 static int regopsize(regex_t *preg, int p );
22245
22246 static int reg_range_find(const int *string, int c);
22247 static const char *str_find(const char *string, int c, int nocase);
22248 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
22249
22250
22251 #ifdef DEBUG
22252 static int regnarrate = 0;
22253 static void regdump(regex_t *preg);
22254 static const char *regprop( int op );
22255 #endif
22256
22257
22258 static int str_int_len(const int *seq)
22259 {
22260 int n = 0;
22261 while (*seq++) {
22262 n++;
22263 }
22264 return n;
22265 }
22266
22267 int jim_regcomp(regex_t *preg, const char *exp, int cflags)
22268 {
22269 int scan;
22270 int longest;
22271 unsigned len;
22272 int flags;
22273
22274 #ifdef DEBUG
22275 fprintf(stderr, "Compiling: '%s'\n", exp);
22276 #endif
22277 memset(preg, 0, sizeof(*preg));
22278
22279 if (exp == NULL)
22280 FAIL(preg, REG_ERR_NULL_ARGUMENT);
22281
22282
22283 preg->cflags = cflags;
22284 preg->regparse = exp;
22285
22286
22287 preg->proglen = (strlen(exp) + 1) * 5;
22288 preg->program = malloc(preg->proglen * sizeof(int));
22289 if (preg->program == NULL)
22290 FAIL(preg, REG_ERR_NOMEM);
22291
22292 regc(preg, REG_MAGIC);
22293 if (reg(preg, 0, &flags) == 0) {
22294 return preg->err;
22295 }
22296
22297
22298 if (preg->re_nsub >= REG_MAX_PAREN)
22299 FAIL(preg,REG_ERR_TOO_BIG);
22300
22301
22302 preg->regstart = 0;
22303 preg->reganch = 0;
22304 preg->regmust = 0;
22305 preg->regmlen = 0;
22306 scan = 1;
22307 if (OP(preg, regnext(preg, scan)) == END) {
22308 scan = OPERAND(scan);
22309
22310
22311 if (OP(preg, scan) == EXACTLY) {
22312 preg->regstart = preg->program[OPERAND(scan)];
22313 }
22314 else if (OP(preg, scan) == BOL)
22315 preg->reganch++;
22316
22317 if (flags&SPSTART) {
22318 longest = 0;
22319 len = 0;
22320 for (; scan != 0; scan = regnext(preg, scan)) {
22321 if (OP(preg, scan) == EXACTLY) {
22322 int plen = str_int_len(preg->program + OPERAND(scan));
22323 if (plen >= len) {
22324 longest = OPERAND(scan);
22325 len = plen;
22326 }
22327 }
22328 }
22329 preg->regmust = longest;
22330 preg->regmlen = len;
22331 }
22332 }
22333
22334 #ifdef DEBUG
22335 regdump(preg);
22336 #endif
22337
22338 return 0;
22339 }
22340
22341 static int reg(regex_t *preg, int paren, int *flagp )
22342 {
22343 int ret;
22344 int br;
22345 int ender;
22346 int parno = 0;
22347 int flags;
22348
22349 *flagp = HASWIDTH;
22350
22351
22352 if (paren) {
22353 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
22354
22355 preg->regparse += 2;
22356 parno = -1;
22357 }
22358 else {
22359 parno = ++preg->re_nsub;
22360 }
22361 ret = regnode(preg, OPEN+parno);
22362 } else
22363 ret = 0;
22364
22365
22366 br = regbranch(preg, &flags);
22367 if (br == 0)
22368 return 0;
22369 if (ret != 0)
22370 regtail(preg, ret, br);
22371 else
22372 ret = br;
22373 if (!(flags&HASWIDTH))
22374 *flagp &= ~HASWIDTH;
22375 *flagp |= flags&SPSTART;
22376 while (*preg->regparse == '|') {
22377 preg->regparse++;
22378 br = regbranch(preg, &flags);
22379 if (br == 0)
22380 return 0;
22381 regtail(preg, ret, br);
22382 if (!(flags&HASWIDTH))
22383 *flagp &= ~HASWIDTH;
22384 *flagp |= flags&SPSTART;
22385 }
22386
22387
22388 ender = regnode(preg, (paren) ? CLOSE+parno : END);
22389 regtail(preg, ret, ender);
22390
22391
22392 for (br = ret; br != 0; br = regnext(preg, br))
22393 regoptail(preg, br, ender);
22394
22395
22396 if (paren && *preg->regparse++ != ')') {
22397 preg->err = REG_ERR_UNMATCHED_PAREN;
22398 return 0;
22399 } else if (!paren && *preg->regparse != '\0') {
22400 if (*preg->regparse == ')') {
22401 preg->err = REG_ERR_UNMATCHED_PAREN;
22402 return 0;
22403 } else {
22404 preg->err = REG_ERR_JUNK_ON_END;
22405 return 0;
22406 }
22407 }
22408
22409 return(ret);
22410 }
22411
22412 static int regbranch(regex_t *preg, int *flagp )
22413 {
22414 int ret;
22415 int chain;
22416 int latest;
22417 int flags;
22418
22419 *flagp = WORST;
22420
22421 ret = regnode(preg, BRANCH);
22422 chain = 0;
22423 while (*preg->regparse != '\0' && *preg->regparse != ')' &&
22424 *preg->regparse != '|') {
22425 latest = regpiece(preg, &flags);
22426 if (latest == 0)
22427 return 0;
22428 *flagp |= flags&HASWIDTH;
22429 if (chain == 0) {
22430 *flagp |= flags&SPSTART;
22431 }
22432 else {
22433 regtail(preg, chain, latest);
22434 }
22435 chain = latest;
22436 }
22437 if (chain == 0)
22438 (void) regnode(preg, NOTHING);
22439
22440 return(ret);
22441 }
22442
22443 static int regpiece(regex_t *preg, int *flagp)
22444 {
22445 int ret;
22446 char op;
22447 int next;
22448 int flags;
22449 int min;
22450 int max;
22451
22452 ret = regatom(preg, &flags);
22453 if (ret == 0)
22454 return 0;
22455
22456 op = *preg->regparse;
22457 if (!ISMULT(op)) {
22458 *flagp = flags;
22459 return(ret);
22460 }
22461
22462 if (!(flags&HASWIDTH) && op != '?') {
22463 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
22464 return 0;
22465 }
22466
22467
22468 if (op == '{') {
22469 char *end;
22470
22471 min = strtoul(preg->regparse + 1, &end, 10);
22472 if (end == preg->regparse + 1) {
22473 preg->err = REG_ERR_BAD_COUNT;
22474 return 0;
22475 }
22476 if (*end == '}') {
22477 max = min;
22478 }
22479 else if (*end == '\0') {
22480 preg->err = REG_ERR_UNMATCHED_BRACES;
22481 return 0;
22482 }
22483 else {
22484 preg->regparse = end;
22485 max = strtoul(preg->regparse + 1, &end, 10);
22486 if (*end != '}') {
22487 preg->err = REG_ERR_UNMATCHED_BRACES;
22488 return 0;
22489 }
22490 }
22491 if (end == preg->regparse + 1) {
22492 max = MAX_REP_COUNT;
22493 }
22494 else if (max < min || max >= 100) {
22495 preg->err = REG_ERR_BAD_COUNT;
22496 return 0;
22497 }
22498 if (min >= 100) {
22499 preg->err = REG_ERR_BAD_COUNT;
22500 return 0;
22501 }
22502
22503 preg->regparse = strchr(preg->regparse, '}');
22504 }
22505 else {
22506 min = (op == '+');
22507 max = (op == '?' ? 1 : MAX_REP_COUNT);
22508 }
22509
22510 if (preg->regparse[1] == '?') {
22511 preg->regparse++;
22512 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
22513 }
22514 else {
22515 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
22516 }
22517 preg->program[ret + 2] = max;
22518 preg->program[ret + 3] = min;
22519 preg->program[ret + 4] = 0;
22520
22521 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
22522
22523 if (!(flags & SIMPLE)) {
22524 int back = regnode(preg, BACK);
22525 regtail(preg, back, ret);
22526 regtail(preg, next, back);
22527 }
22528
22529 preg->regparse++;
22530 if (ISMULT(*preg->regparse)) {
22531 preg->err = REG_ERR_NESTED_COUNT;
22532 return 0;
22533 }
22534
22535 return ret;
22536 }
22537
22538 static void reg_addrange(regex_t *preg, int lower, int upper)
22539 {
22540 if (lower > upper) {
22541 reg_addrange(preg, upper, lower);
22542 }
22543
22544 regc(preg, upper - lower + 1);
22545 regc(preg, lower);
22546 }
22547
22548 static void reg_addrange_str(regex_t *preg, const char *str)
22549 {
22550 while (*str) {
22551 reg_addrange(preg, *str, *str);
22552 str++;
22553 }
22554 }
22555
22556 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
22557 {
22558 int l = utf8_tounicode(s, uc);
22559 if (upper) {
22560 *uc = utf8_upper(*uc);
22561 }
22562 return l;
22563 }
22564
22565 static int hexdigitval(int c)
22566 {
22567 if (c >= '0' && c <= '9')
22568 return c - '0';
22569 if (c >= 'a' && c <= 'f')
22570 return c - 'a' + 10;
22571 if (c >= 'A' && c <= 'F')
22572 return c - 'A' + 10;
22573 return -1;
22574 }
22575
22576 static int parse_hex(const char *s, int n, int *uc)
22577 {
22578 int val = 0;
22579 int k;
22580
22581 for (k = 0; k < n; k++) {
22582 int c = hexdigitval(*s++);
22583 if (c == -1) {
22584 break;
22585 }
22586 val = (val << 4) | c;
22587 }
22588 if (k) {
22589 *uc = val;
22590 }
22591 return k;
22592 }
22593
22594 static int reg_decode_escape(const char *s, int *ch)
22595 {
22596 int n;
22597 const char *s0 = s;
22598
22599 *ch = *s++;
22600
22601 switch (*ch) {
22602 case 'b': *ch = '\b'; break;
22603 case 'e': *ch = 27; break;
22604 case 'f': *ch = '\f'; break;
22605 case 'n': *ch = '\n'; break;
22606 case 'r': *ch = '\r'; break;
22607 case 't': *ch = '\t'; break;
22608 case 'v': *ch = '\v'; break;
22609 case 'u':
22610 if (*s == '{') {
22611
22612 n = parse_hex(s + 1, 6, ch);
22613 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
22614 s += n + 2;
22615 }
22616 else {
22617
22618 *ch = 'u';
22619 }
22620 }
22621 else if ((n = parse_hex(s, 4, ch)) > 0) {
22622 s += n;
22623 }
22624 break;
22625 case 'U':
22626 if ((n = parse_hex(s, 8, ch)) > 0) {
22627 s += n;
22628 }
22629 break;
22630 case 'x':
22631 if ((n = parse_hex(s, 2, ch)) > 0) {
22632 s += n;
22633 }
22634 break;
22635 case '\0':
22636 s--;
22637 *ch = '\\';
22638 break;
22639 }
22640 return s - s0;
22641 }
22642
22643 static int regatom(regex_t *preg, int *flagp)
22644 {
22645 int ret;
22646 int flags;
22647 int nocase = (preg->cflags & REG_ICASE);
22648
22649 int ch;
22650 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
22651
22652 *flagp = WORST;
22653
22654 preg->regparse += n;
22655 switch (ch) {
22656
22657 case '^':
22658 ret = regnode(preg, BOL);
22659 break;
22660 case '$':
22661 ret = regnode(preg, EOL);
22662 break;
22663 case '.':
22664 ret = regnode(preg, ANY);
22665 *flagp |= HASWIDTH|SIMPLE;
22666 break;
22667 case '[': {
22668 const char *pattern = preg->regparse;
22669
22670 if (*pattern == '^') {
22671 ret = regnode(preg, ANYBUT);
22672 pattern++;
22673 } else
22674 ret = regnode(preg, ANYOF);
22675
22676
22677 if (*pattern == ']' || *pattern == '-') {
22678 reg_addrange(preg, *pattern, *pattern);
22679 pattern++;
22680 }
22681
22682 while (*pattern != ']') {
22683
22684 int start;
22685 int end;
22686
22687 enum {
22688 CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
22689 CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
22690 CC_NUM
22691 };
22692 int cc;
22693
22694 if (!*pattern) {
22695 preg->err = REG_ERR_UNMATCHED_BRACKET;
22696 return 0;
22697 }
22698
22699 pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
22700 if (start == '\\') {
22701
22702 switch (*pattern) {
22703 case 's':
22704 pattern++;
22705 cc = CC_SPACE;
22706 goto cc_switch;
22707 case 'd':
22708 pattern++;
22709 cc = CC_DIGIT;
22710 goto cc_switch;
22711 case 'w':
22712 pattern++;
22713 reg_addrange(preg, '_', '_');
22714 cc = CC_ALNUM;
22715 goto cc_switch;
22716 }
22717 pattern += reg_decode_escape(pattern, &start);
22718 if (start == 0) {
22719 preg->err = REG_ERR_NULL_CHAR;
22720 return 0;
22721 }
22722 if (start == '\\' && *pattern == 0) {
22723 preg->err = REG_ERR_INVALID_ESCAPE;
22724 return 0;
22725 }
22726 }
22727 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
22728
22729 pattern += utf8_tounicode(pattern, &end);
22730 pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
22731 if (end == '\\') {
22732 pattern += reg_decode_escape(pattern, &end);
22733 if (end == 0) {
22734 preg->err = REG_ERR_NULL_CHAR;
22735 return 0;
22736 }
22737 if (end == '\\' && *pattern == 0) {
22738 preg->err = REG_ERR_INVALID_ESCAPE;
22739 return 0;
22740 }
22741 }
22742
22743 reg_addrange(preg, start, end);
22744 continue;
22745 }
22746 if (start == '[' && pattern[0] == ':') {
22747 static const char *character_class[] = {
22748 ":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
22749 ":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
22750 };
22751
22752 for (cc = 0; cc < CC_NUM; cc++) {
22753 n = strlen(character_class[cc]);
22754 if (strncmp(pattern, character_class[cc], n) == 0) {
22755 if (pattern[n] != ']') {
22756 preg->err = REG_ERR_UNMATCHED_BRACKET;
22757 return 0;
22758 }
22759
22760 pattern += n + 1;
22761 break;
22762 }
22763 }
22764 if (cc != CC_NUM) {
22765 cc_switch:
22766 switch (cc) {
22767 case CC_ALNUM:
22768 reg_addrange(preg, '0', '9');
22769
22770 case CC_ALPHA:
22771 if ((preg->cflags & REG_ICASE) == 0) {
22772 reg_addrange(preg, 'a', 'z');
22773 }
22774 reg_addrange(preg, 'A', 'Z');
22775 break;
22776 case CC_SPACE:
22777 reg_addrange_str(preg, " \t\r\n\f\v");
22778 break;
22779 case CC_BLANK:
22780 reg_addrange_str(preg, " \t");
22781 break;
22782 case CC_UPPER:
22783 reg_addrange(preg, 'A', 'Z');
22784 break;
22785 case CC_LOWER:
22786 reg_addrange(preg, 'a', 'z');
22787 break;
22788 case CC_XDIGIT:
22789 reg_addrange(preg, 'a', 'f');
22790 reg_addrange(preg, 'A', 'F');
22791
22792 case CC_DIGIT:
22793 reg_addrange(preg, '0', '9');
22794 break;
22795 case CC_CNTRL:
22796 reg_addrange(preg, 0, 31);
22797 reg_addrange(preg, 127, 127);
22798 break;
22799 case CC_PRINT:
22800 reg_addrange(preg, ' ', '~');
22801 break;
22802 case CC_GRAPH:
22803 reg_addrange(preg, '!', '~');
22804 break;
22805 case CC_PUNCT:
22806 reg_addrange(preg, '!', '/');
22807 reg_addrange(preg, ':', '@');
22808 reg_addrange(preg, '[', '`');
22809 reg_addrange(preg, '{', '~');
22810 break;
22811 }
22812 continue;
22813 }
22814 }
22815
22816 reg_addrange(preg, start, start);
22817 }
22818 regc(preg, '\0');
22819
22820 if (*pattern) {
22821 pattern++;
22822 }
22823 preg->regparse = pattern;
22824
22825 *flagp |= HASWIDTH|SIMPLE;
22826 }
22827 break;
22828 case '(':
22829 ret = reg(preg, 1, &flags);
22830 if (ret == 0)
22831 return 0;
22832 *flagp |= flags&(HASWIDTH|SPSTART);
22833 break;
22834 case '\0':
22835 case '|':
22836 case ')':
22837 preg->err = REG_ERR_INTERNAL;
22838 return 0;
22839 case '?':
22840 case '+':
22841 case '*':
22842 case '{':
22843 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
22844 return 0;
22845 case '\\':
22846 ch = *preg->regparse++;
22847 switch (ch) {
22848 case '\0':
22849 preg->err = REG_ERR_INVALID_ESCAPE;
22850 return 0;
22851 case 'A':
22852 ret = regnode(preg, BOLX);
22853 break;
22854 case 'Z':
22855 ret = regnode(preg, EOLX);
22856 break;
22857 case '<':
22858 case 'm':
22859 ret = regnode(preg, WORDA);
22860 break;
22861 case '>':
22862 case 'M':
22863 ret = regnode(preg, WORDZ);
22864 break;
22865 case 'd':
22866 case 'D':
22867 ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
22868 reg_addrange(preg, '0', '9');
22869 regc(preg, '\0');
22870 *flagp |= HASWIDTH|SIMPLE;
22871 break;
22872 case 'w':
22873 case 'W':
22874 ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
22875 if ((preg->cflags & REG_ICASE) == 0) {
22876 reg_addrange(preg, 'a', 'z');
22877 }
22878 reg_addrange(preg, 'A', 'Z');
22879 reg_addrange(preg, '0', '9');
22880 reg_addrange(preg, '_', '_');
22881 regc(preg, '\0');
22882 *flagp |= HASWIDTH|SIMPLE;
22883 break;
22884 case 's':
22885 case 'S':
22886 ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
22887 reg_addrange_str(preg," \t\r\n\f\v");
22888 regc(preg, '\0');
22889 *flagp |= HASWIDTH|SIMPLE;
22890 break;
22891
22892 default:
22893
22894
22895 preg->regparse--;
22896 goto de_fault;
22897 }
22898 break;
22899 de_fault:
22900 default: {
22901 int added = 0;
22902
22903
22904 preg->regparse -= n;
22905
22906 ret = regnode(preg, EXACTLY);
22907
22908
22909
22910 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
22911 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
22912 if (ch == '\\' && preg->regparse[n]) {
22913 if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
22914
22915 break;
22916 }
22917 n += reg_decode_escape(preg->regparse + n, &ch);
22918 if (ch == 0) {
22919 preg->err = REG_ERR_NULL_CHAR;
22920 return 0;
22921 }
22922 }
22923
22924
22925 if (ISMULT(preg->regparse[n])) {
22926
22927 if (added) {
22928
22929 break;
22930 }
22931
22932 regc(preg, ch);
22933 added++;
22934 preg->regparse += n;
22935 break;
22936 }
22937
22938
22939 regc(preg, ch);
22940 added++;
22941 preg->regparse += n;
22942 }
22943 regc(preg, '\0');
22944
22945 *flagp |= HASWIDTH;
22946 if (added == 1)
22947 *flagp |= SIMPLE;
22948 break;
22949 }
22950 break;
22951 }
22952
22953 return(ret);
22954 }
22955
22956 static void reg_grow(regex_t *preg, int n)
22957 {
22958 if (preg->p + n >= preg->proglen) {
22959 preg->proglen = (preg->p + n) * 2;
22960 preg->program = realloc(preg->program, preg->proglen * sizeof(int));
22961 }
22962 }
22963
22964
22965 static int regnode(regex_t *preg, int op)
22966 {
22967 reg_grow(preg, 2);
22968
22969
22970 preg->program[preg->p++] = op;
22971 preg->program[preg->p++] = 0;
22972
22973
22974 return preg->p - 2;
22975 }
22976
22977 static void regc(regex_t *preg, int b )
22978 {
22979 reg_grow(preg, 1);
22980 preg->program[preg->p++] = b;
22981 }
22982
22983 static int reginsert(regex_t *preg, int op, int size, int opnd )
22984 {
22985 reg_grow(preg, size);
22986
22987
22988 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
22989
22990 memset(preg->program + opnd, 0, sizeof(int) * size);
22991
22992 preg->program[opnd] = op;
22993
22994 preg->p += size;
22995
22996 return opnd + size;
22997 }
22998
22999 static void regtail(regex_t *preg, int p, int val)
23000 {
23001 int scan;
23002 int temp;
23003 int offset;
23004
23005
23006 scan = p;
23007 for (;;) {
23008 temp = regnext(preg, scan);
23009 if (temp == 0)
23010 break;
23011 scan = temp;
23012 }
23013
23014 if (OP(preg, scan) == BACK)
23015 offset = scan - val;
23016 else
23017 offset = val - scan;
23018
23019 preg->program[scan + 1] = offset;
23020 }
23021
23022
23023 static void regoptail(regex_t *preg, int p, int val )
23024 {
23025
23026 if (p != 0 && OP(preg, p) == BRANCH) {
23027 regtail(preg, OPERAND(p), val);
23028 }
23029 }
23030
23031
23032 static int regtry(regex_t *preg, const char *string );
23033 static int regmatch(regex_t *preg, int prog);
23034 static int regrepeat(regex_t *preg, int p, int max);
23035
23036 int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
23037 {
23038 const char *s;
23039 int scan;
23040
23041
23042 if (preg == NULL || preg->program == NULL || string == NULL) {
23043 return REG_ERR_NULL_ARGUMENT;
23044 }
23045
23046
23047 if (*preg->program != REG_MAGIC) {
23048 return REG_ERR_CORRUPTED;
23049 }
23050
23051 #ifdef DEBUG
23052 fprintf(stderr, "regexec: %s\n", string);
23053 regdump(preg);
23054 #endif
23055
23056 preg->eflags = eflags;
23057 preg->pmatch = pmatch;
23058 preg->nmatch = nmatch;
23059 preg->start = string;
23060
23061
23062 for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
23063 int op = OP(preg, scan);
23064 if (op == END)
23065 break;
23066 if (op == REPX || op == REPXMIN)
23067 preg->program[scan + 4] = 0;
23068 }
23069
23070
23071 if (preg->regmust != 0) {
23072 s = string;
23073 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
23074 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
23075 break;
23076 }
23077 s++;
23078 }
23079 if (s == NULL)
23080 return REG_NOMATCH;
23081 }
23082
23083
23084 preg->regbol = string;
23085
23086
23087 if (preg->reganch) {
23088 if (eflags & REG_NOTBOL) {
23089
23090 goto nextline;
23091 }
23092 while (1) {
23093 if (regtry(preg, string)) {
23094 return REG_NOERROR;
23095 }
23096 if (*string) {
23097 nextline:
23098 if (preg->cflags & REG_NEWLINE) {
23099
23100 string = strchr(string, '\n');
23101 if (string) {
23102 preg->regbol = ++string;
23103 continue;
23104 }
23105 }
23106 }
23107 return REG_NOMATCH;
23108 }
23109 }
23110
23111
23112 s = string;
23113 if (preg->regstart != '\0') {
23114
23115 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
23116 if (regtry(preg, s))
23117 return REG_NOERROR;
23118 s++;
23119 }
23120 }
23121 else
23122
23123 while (1) {
23124 if (regtry(preg, s))
23125 return REG_NOERROR;
23126 if (*s == '\0') {
23127 break;
23128 }
23129 else {
23130 int c;
23131 s += utf8_tounicode(s, &c);
23132 }
23133 }
23134
23135
23136 return REG_NOMATCH;
23137 }
23138
23139
23140 static int regtry( regex_t *preg, const char *string )
23141 {
23142 int i;
23143
23144 preg->reginput = string;
23145
23146 for (i = 0; i < preg->nmatch; i++) {
23147 preg->pmatch[i].rm_so = -1;
23148 preg->pmatch[i].rm_eo = -1;
23149 }
23150 if (regmatch(preg, 1)) {
23151 preg->pmatch[0].rm_so = string - preg->start;
23152 preg->pmatch[0].rm_eo = preg->reginput - preg->start;
23153 return(1);
23154 } else
23155 return(0);
23156 }
23157
23158 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
23159 {
23160 const char *s = string;
23161 while (proglen && *s) {
23162 int ch;
23163 int n = reg_utf8_tounicode_case(s, &ch, nocase);
23164 if (ch != *prog) {
23165 return -1;
23166 }
23167 prog++;
23168 s += n;
23169 proglen--;
23170 }
23171 if (proglen == 0) {
23172 return s - string;
23173 }
23174 return -1;
23175 }
23176
23177 static int reg_range_find(const int *range, int c)
23178 {
23179 while (*range) {
23180
23181 if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
23182 return 1;
23183 }
23184 range += 2;
23185 }
23186 return 0;
23187 }
23188
23189 static const char *str_find(const char *string, int c, int nocase)
23190 {
23191 if (nocase) {
23192
23193 c = utf8_upper(c);
23194 }
23195 while (*string) {
23196 int ch;
23197 int n = reg_utf8_tounicode_case(string, &ch, nocase);
23198 if (c == ch) {
23199 return string;
23200 }
23201 string += n;
23202 }
23203 return NULL;
23204 }
23205
23206 static int reg_iseol(regex_t *preg, int ch)
23207 {
23208 if (preg->cflags & REG_NEWLINE) {
23209 return ch == '\0' || ch == '\n';
23210 }
23211 else {
23212 return ch == '\0';
23213 }
23214 }
23215
23216 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
23217 {
23218 int nextch = '\0';
23219 const char *save;
23220 int no;
23221 int c;
23222
23223 int max = preg->program[scan + 2];
23224 int min = preg->program[scan + 3];
23225 int next = regnext(preg, scan);
23226
23227 if (OP(preg, next) == EXACTLY) {
23228 nextch = preg->program[OPERAND(next)];
23229 }
23230 save = preg->reginput;
23231 no = regrepeat(preg, scan + 5, max);
23232 if (no < min) {
23233 return 0;
23234 }
23235 if (matchmin) {
23236
23237 max = no;
23238 no = min;
23239 }
23240
23241 while (1) {
23242 if (matchmin) {
23243 if (no > max) {
23244 break;
23245 }
23246 }
23247 else {
23248 if (no < min) {
23249 break;
23250 }
23251 }
23252 preg->reginput = save + utf8_index(save, no);
23253 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
23254
23255 if (reg_iseol(preg, nextch) || c == nextch) {
23256 if (regmatch(preg, next)) {
23257 return(1);
23258 }
23259 }
23260 if (matchmin) {
23261
23262 no++;
23263 }
23264 else {
23265
23266 no--;
23267 }
23268 }
23269 return(0);
23270 }
23271
23272 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
23273 {
23274 int *scanpt = preg->program + scan;
23275
23276 int max = scanpt[2];
23277 int min = scanpt[3];
23278
23279
23280 if (scanpt[4] < min) {
23281
23282 scanpt[4]++;
23283 if (regmatch(preg, scan + 5)) {
23284 return 1;
23285 }
23286 scanpt[4]--;
23287 return 0;
23288 }
23289 if (scanpt[4] > max) {
23290 return 0;
23291 }
23292
23293 if (matchmin) {
23294
23295 if (regmatch(preg, regnext(preg, scan))) {
23296 return 1;
23297 }
23298
23299 scanpt[4]++;
23300 if (regmatch(preg, scan + 5)) {
23301 return 1;
23302 }
23303 scanpt[4]--;
23304 return 0;
23305 }
23306
23307 if (scanpt[4] < max) {
23308 scanpt[4]++;
23309 if (regmatch(preg, scan + 5)) {
23310 return 1;
23311 }
23312 scanpt[4]--;
23313 }
23314
23315 return regmatch(preg, regnext(preg, scan));
23316 }
23317
23318
23319 static int regmatch(regex_t *preg, int prog)
23320 {
23321 int scan;
23322 int next;
23323 const char *save;
23324
23325 scan = prog;
23326
23327 #ifdef DEBUG
23328 if (scan != 0 && regnarrate)
23329 fprintf(stderr, "%s(\n", regprop(scan));
23330 #endif
23331 while (scan != 0) {
23332 int n;
23333 int c;
23334 #ifdef DEBUG
23335 if (regnarrate) {
23336 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
23337 }
23338 #endif
23339 next = regnext(preg, scan);
23340 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
23341
23342 switch (OP(preg, scan)) {
23343 case BOLX:
23344 if ((preg->eflags & REG_NOTBOL)) {
23345 return(0);
23346 }
23347
23348 case BOL:
23349 if (preg->reginput != preg->regbol) {
23350 return(0);
23351 }
23352 break;
23353 case EOLX:
23354 if (c != 0) {
23355
23356 return 0;
23357 }
23358 break;
23359 case EOL:
23360 if (!reg_iseol(preg, c)) {
23361 return(0);
23362 }
23363 break;
23364 case WORDA:
23365
23366 if ((!isalnum(UCHAR(c))) && c != '_')
23367 return(0);
23368
23369 if (preg->reginput > preg->regbol &&
23370 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
23371 return(0);
23372 break;
23373 case WORDZ:
23374
23375 if (preg->reginput > preg->regbol) {
23376
23377 if (reg_iseol(preg, c) || !(isalnum(UCHAR(c)) || c == '_')) {
23378 c = preg->reginput[-1];
23379
23380 if (isalnum(UCHAR(c)) || c == '_') {
23381 break;
23382 }
23383 }
23384 }
23385
23386 return(0);
23387
23388 case ANY:
23389 if (reg_iseol(preg, c))
23390 return 0;
23391 preg->reginput += n;
23392 break;
23393 case EXACTLY: {
23394 int opnd;
23395 int len;
23396 int slen;
23397
23398 opnd = OPERAND(scan);
23399 len = str_int_len(preg->program + opnd);
23400
23401 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
23402 if (slen < 0) {
23403 return(0);
23404 }
23405 preg->reginput += slen;
23406 }
23407 break;
23408 case ANYOF:
23409 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
23410 return(0);
23411 }
23412 preg->reginput += n;
23413 break;
23414 case ANYBUT:
23415 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
23416 return(0);
23417 }
23418 preg->reginput += n;
23419 break;
23420 case NOTHING:
23421 break;
23422 case BACK:
23423 break;
23424 case BRANCH:
23425 if (OP(preg, next) != BRANCH)
23426 next = OPERAND(scan);
23427 else {
23428 do {
23429 save = preg->reginput;
23430 if (regmatch(preg, OPERAND(scan))) {
23431 return(1);
23432 }
23433 preg->reginput = save;
23434 scan = regnext(preg, scan);
23435 } while (scan != 0 && OP(preg, scan) == BRANCH);
23436 return(0);
23437
23438 }
23439 break;
23440 case REP:
23441 case REPMIN:
23442 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
23443
23444 case REPX:
23445 case REPXMIN:
23446 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
23447
23448 case END:
23449 return 1;
23450
23451 case OPENNC:
23452 case CLOSENC:
23453 return regmatch(preg, next);
23454
23455 default:
23456 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
23457 save = preg->reginput;
23458 if (regmatch(preg, next)) {
23459 if (OP(preg, scan) < CLOSE) {
23460 int no = OP(preg, scan) - OPEN;
23461 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
23462 preg->pmatch[no].rm_so = save - preg->start;
23463 }
23464 }
23465 else {
23466 int no = OP(preg, scan) - CLOSE;
23467 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
23468 preg->pmatch[no].rm_eo = save - preg->start;
23469 }
23470 }
23471 return(1);
23472 }
23473
23474 preg->reginput = save;
23475 return(0);
23476 }
23477 return REG_ERR_INTERNAL;
23478 }
23479
23480 scan = next;
23481 }
23482
23483 return REG_ERR_INTERNAL;
23484 }
23485
23486 static int regrepeat(regex_t *preg, int p, int max)
23487 {
23488 int count = 0;
23489 const char *scan;
23490 int opnd;
23491 int ch;
23492 int n;
23493
23494 scan = preg->reginput;
23495 opnd = OPERAND(p);
23496 switch (OP(preg, p)) {
23497 case ANY:
23498 while (!reg_iseol(preg, *scan) && count < max) {
23499 count++;
23500 scan += utf8_charlen(*scan);
23501 }
23502 break;
23503 case EXACTLY:
23504 while (count < max) {
23505 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23506 if (preg->program[opnd] != ch) {
23507 break;
23508 }
23509 count++;
23510 scan += n;
23511 }
23512 break;
23513 case ANYOF:
23514 while (count < max) {
23515 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23516 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
23517 break;
23518 }
23519 count++;
23520 scan += n;
23521 }
23522 break;
23523 case ANYBUT:
23524 while (count < max) {
23525 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23526 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
23527 break;
23528 }
23529 count++;
23530 scan += n;
23531 }
23532 break;
23533 default:
23534 preg->err = REG_ERR_INTERNAL;
23535 count = 0;
23536 break;
23537 }
23538 preg->reginput = scan;
23539
23540 return(count);
23541 }
23542
23543 static int regnext(regex_t *preg, int p )
23544 {
23545 int offset;
23546
23547 offset = NEXT(preg, p);
23548
23549 if (offset == 0)
23550 return 0;
23551
23552 if (OP(preg, p) == BACK)
23553 return(p-offset);
23554 else
23555 return(p+offset);
23556 }
23557
23558 static int regopsize(regex_t *preg, int p )
23559 {
23560
23561 switch (OP(preg, p)) {
23562 case REP:
23563 case REPMIN:
23564 case REPX:
23565 case REPXMIN:
23566 return 5;
23567
23568 case ANYOF:
23569 case ANYBUT:
23570 case EXACTLY: {
23571 int s = p + 2;
23572 while (preg->program[s++]) {
23573 }
23574 return s - p;
23575 }
23576 }
23577 return 2;
23578 }
23579
23580
23581 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
23582 {
23583 static const char *error_strings[] = {
23584 "success",
23585 "no match",
23586 "bad pattern",
23587 "null argument",
23588 "unknown error",
23589 "too big",
23590 "out of memory",
23591 "too many ()",
23592 "parentheses () not balanced",
23593 "braces {} not balanced",
23594 "invalid repetition count(s)",
23595 "extra characters",
23596 "*+ of empty atom",
23597 "nested count",
23598 "internal error",
23599 "count follows nothing",
23600 "invalid escape \\ sequence",
23601 "corrupted program",
23602 "contains null char",
23603 "brackets [] not balanced",
23604 };
23605 const char *err;
23606
23607 if (errcode < 0 || errcode >= REG_ERR_NUM) {
23608 err = "Bad error code";
23609 }
23610 else {
23611 err = error_strings[errcode];
23612 }
23613
23614 return snprintf(errbuf, errbuf_size, "%s", err);
23615 }
23616
23617 void jim_regfree(regex_t *preg)
23618 {
23619 free(preg->program);
23620 }
23621
23622 #endif
23623 #include <string.h>
23624
23625 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
23626 {
23627 Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
23628 }
23629
23630 #if defined(_WIN32) || defined(WIN32)
23631 #include <sys/stat.h>
23632
23633 int Jim_Errno(void)
23634 {
23635 switch (GetLastError()) {
23636 case ERROR_FILE_NOT_FOUND: return ENOENT;
23637 case ERROR_PATH_NOT_FOUND: return ENOENT;
23638 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
23639 case ERROR_ACCESS_DENIED: return EACCES;
23640 case ERROR_INVALID_HANDLE: return EBADF;
23641 case ERROR_BAD_ENVIRONMENT: return E2BIG;
23642 case ERROR_BAD_FORMAT: return ENOEXEC;
23643 case ERROR_INVALID_ACCESS: return EACCES;
23644 case ERROR_INVALID_DRIVE: return ENOENT;
23645 case ERROR_CURRENT_DIRECTORY: return EACCES;
23646 case ERROR_NOT_SAME_DEVICE: return EXDEV;
23647 case ERROR_NO_MORE_FILES: return ENOENT;
23648 case ERROR_WRITE_PROTECT: return EROFS;
23649 case ERROR_BAD_UNIT: return ENXIO;
23650 case ERROR_NOT_READY: return EBUSY;
23651 case ERROR_BAD_COMMAND: return EIO;
23652 case ERROR_CRC: return EIO;
23653 case ERROR_BAD_LENGTH: return EIO;
23654 case ERROR_SEEK: return EIO;
23655 case ERROR_WRITE_FAULT: return EIO;
23656 case ERROR_READ_FAULT: return EIO;
23657 case ERROR_GEN_FAILURE: return EIO;
23658 case ERROR_SHARING_VIOLATION: return EACCES;
23659 case ERROR_LOCK_VIOLATION: return EACCES;
23660 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
23661 case ERROR_HANDLE_DISK_FULL: return ENOSPC;
23662 case ERROR_NOT_SUPPORTED: return ENODEV;
23663 case ERROR_REM_NOT_LIST: return EBUSY;
23664 case ERROR_DUP_NAME: return EEXIST;
23665 case ERROR_BAD_NETPATH: return ENOENT;
23666 case ERROR_NETWORK_BUSY: return EBUSY;
23667 case ERROR_DEV_NOT_EXIST: return ENODEV;
23668 case ERROR_TOO_MANY_CMDS: return EAGAIN;
23669 case ERROR_ADAP_HDW_ERR: return EIO;
23670 case ERROR_BAD_NET_RESP: return EIO;
23671 case ERROR_UNEXP_NET_ERR: return EIO;
23672 case ERROR_NETNAME_DELETED: return ENOENT;
23673 case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
23674 case ERROR_BAD_DEV_TYPE: return ENODEV;
23675 case ERROR_BAD_NET_NAME: return ENOENT;
23676 case ERROR_TOO_MANY_NAMES: return ENFILE;
23677 case ERROR_TOO_MANY_SESS: return EIO;
23678 case ERROR_SHARING_PAUSED: return EAGAIN;
23679 case ERROR_REDIR_PAUSED: return EAGAIN;
23680 case ERROR_FILE_EXISTS: return EEXIST;
23681 case ERROR_CANNOT_MAKE: return ENOSPC;
23682 case ERROR_OUT_OF_STRUCTURES: return ENFILE;
23683 case ERROR_ALREADY_ASSIGNED: return EEXIST;
23684 case ERROR_INVALID_PASSWORD: return EPERM;
23685 case ERROR_NET_WRITE_FAULT: return EIO;
23686 case ERROR_NO_PROC_SLOTS: return EAGAIN;
23687 case ERROR_DISK_CHANGE: return EXDEV;
23688 case ERROR_BROKEN_PIPE: return EPIPE;
23689 case ERROR_OPEN_FAILED: return ENOENT;
23690 case ERROR_DISK_FULL: return ENOSPC;
23691 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
23692 case ERROR_INVALID_TARGET_HANDLE: return EBADF;
23693 case ERROR_INVALID_NAME: return ENOENT;
23694 case ERROR_PROC_NOT_FOUND: return ESRCH;
23695 case ERROR_WAIT_NO_CHILDREN: return ECHILD;
23696 case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
23697 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
23698 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
23699 case ERROR_BUSY_DRIVE: return EAGAIN;
23700 case ERROR_DIR_NOT_EMPTY: return EEXIST;
23701 case ERROR_NOT_LOCKED: return EACCES;
23702 case ERROR_BAD_PATHNAME: return ENOENT;
23703 case ERROR_LOCK_FAILED: return EACCES;
23704 case ERROR_ALREADY_EXISTS: return EEXIST;
23705 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
23706 case ERROR_BAD_PIPE: return EPIPE;
23707 case ERROR_PIPE_BUSY: return EAGAIN;
23708 case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
23709 case ERROR_DIRECTORY: return ENOTDIR;
23710 }
23711 return EINVAL;
23712 }
23713
23714 long JimProcessPid(phandle_t pid)
23715 {
23716 if (pid == INVALID_HANDLE_VALUE) {
23717 return -1;
23718 }
23719 return GetProcessId(pid);
23720 }
23721
23722 phandle_t JimWaitPid(long pid, int *status, int nohang)
23723 {
23724 if (pid > 0) {
23725 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
23726 if (h) {
23727 long pid = waitpid(h, status, nohang);
23728 CloseHandle(h);
23729 if (pid > 0) {
23730 return h;
23731 }
23732 }
23733 }
23734 return JIM_BAD_PHANDLE;
23735 }
23736
23737 long waitpid(phandle_t phandle, int *status, int nohang)
23738 {
23739 long pid;
23740 DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE);
23741 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
23742
23743 return -1;
23744 }
23745 GetExitCodeProcess(phandle, &ret);
23746 *status = ret;
23747
23748 pid = GetProcessId(phandle);
23749 CloseHandle(phandle);
23750 return pid;
23751 }
23752
23753 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
23754 {
23755 char name[MAX_PATH];
23756 HANDLE handle;
23757
23758 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
23759 return -1;
23760 }
23761
23762 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
23763 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
23764 NULL);
23765
23766 if (handle == INVALID_HANDLE_VALUE) {
23767 goto error;
23768 }
23769
23770 Jim_SetResultString(interp, name, -1);
23771 return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT);
23772
23773 error:
23774 Jim_SetResultErrno(interp, name);
23775 DeleteFile(name);
23776 return -1;
23777 }
23778
23779 int Jim_OpenForWrite(const char *filename, int append)
23780 {
23781 if (strcmp(filename, "/dev/null") == 0) {
23782 filename = "nul:";
23783 }
23784 int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
23785 if (fd >= 0 && append) {
23786
23787 _lseek(fd, 0L, SEEK_END);
23788 }
23789 return fd;
23790 }
23791
23792 int Jim_OpenForRead(const char *filename)
23793 {
23794 if (strcmp(filename, "/dev/null") == 0) {
23795 filename = "nul:";
23796 }
23797 return _open(filename, _O_RDONLY | _O_TEXT, 0);
23798 }
23799
23800 #elif defined(HAVE_UNISTD_H)
23801
23802
23803
23804 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
23805 {
23806 int fd;
23807 mode_t mask;
23808 Jim_Obj *filenameObj;
23809
23810 if (filename_template == NULL) {
23811 const char *tmpdir = getenv("TMPDIR");
23812 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
23813 tmpdir = "/tmp/";
23814 }
23815 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
23816 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
23817 Jim_AppendString(interp, filenameObj, "/", 1);
23818 }
23819 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
23820 }
23821 else {
23822 filenameObj = Jim_NewStringObj(interp, filename_template, -1);
23823 }
23824
23825
23826 #ifdef HAVE_UMASK
23827 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
23828 #endif
23829 #ifdef HAVE_MKSTEMP
23830 fd = mkstemp(filenameObj->bytes);
23831 #else
23832 if (mktemp(filenameObj->bytes) == NULL) {
23833 fd = -1;
23834 }
23835 else {
23836 fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
23837 }
23838 #endif
23839 #ifdef HAVE_UMASK
23840 umask(mask);
23841 #endif
23842 if (fd < 0) {
23843 Jim_SetResultErrno(interp, Jim_String(filenameObj));
23844 Jim_FreeNewObj(interp, filenameObj);
23845 return -1;
23846 }
23847 if (unlink_file) {
23848 remove(Jim_String(filenameObj));
23849 }
23850
23851 Jim_SetResult(interp, filenameObj);
23852 return fd;
23853 }
23854
23855 int Jim_OpenForWrite(const char *filename, int append)
23856 {
23857 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
23858 }
23859
23860 int Jim_OpenForRead(const char *filename)
23861 {
23862 return open(filename, O_RDONLY, 0);
23863 }
23864
23865 #endif
23866
23867 #if defined(_WIN32) || defined(WIN32)
23868 #ifndef STRICT
23869 #define STRICT
23870 #endif
23871 #define WIN32_LEAN_AND_MEAN
23872 #include <windows.h>
23873
23874 #if defined(HAVE_DLOPEN_COMPAT)
23875 void *dlopen(const char *path, int mode)
23876 {
23877 JIM_NOTUSED(mode);
23878
23879 return (void *)LoadLibraryA(path);
23880 }
23881
23882 int dlclose(void *handle)
23883 {
23884 FreeLibrary((HANDLE)handle);
23885 return 0;
23886 }
23887
23888 void *dlsym(void *handle, const char *symbol)
23889 {
23890 return GetProcAddress((HMODULE)handle, symbol);
23891 }
23892
23893 char *dlerror(void)
23894 {
23895 static char msg[121];
23896 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
23897 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
23898 return msg;
23899 }
23900 #endif
23901
23902 #ifdef _MSC_VER
23903
23904 #include <sys/timeb.h>
23905
23906
23907 int gettimeofday(struct timeval *tv, void *unused)
23908 {
23909 struct _timeb tb;
23910
23911 _ftime(&tb);
23912 tv->tv_sec = tb.time;
23913 tv->tv_usec = tb.millitm * 1000;
23914
23915 return 0;
23916 }
23917
23918
23919 DIR *opendir(const char *name)
23920 {
23921 DIR *dir = 0;
23922
23923 if (name && name[0]) {
23924 size_t base_length = strlen(name);
23925 const char *all =
23926 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
23927
23928 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
23929 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
23930 strcat(strcpy(dir->name, name), all);
23931
23932 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
23933 dir->result.d_name = 0;
23934 else {
23935 Jim_Free(dir->name);
23936 Jim_Free(dir);
23937 dir = 0;
23938 }
23939 }
23940 else {
23941 Jim_Free(dir);
23942 dir = 0;
23943 errno = ENOMEM;
23944 }
23945 }
23946 else {
23947 errno = EINVAL;
23948 }
23949 return dir;
23950 }
23951
23952 int closedir(DIR * dir)
23953 {
23954 int result = -1;
23955
23956 if (dir) {
23957 if (dir->handle != -1)
23958 result = _findclose(dir->handle);
23959 Jim_Free(dir->name);
23960 Jim_Free(dir);
23961 }
23962 if (result == -1)
23963 errno = EBADF;
23964 return result;
23965 }
23966
23967 struct dirent *readdir(DIR * dir)
23968 {
23969 struct dirent *result = 0;
23970
23971 if (dir && dir->handle != -1) {
23972 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
23973 result = &dir->result;
23974 result->d_name = dir->info.name;
23975 }
23976 }
23977 else {
23978 errno = EBADF;
23979 }
23980 return result;
23981 }
23982 #endif
23983 #endif
23984 #include <stdio.h>
23985 #include <signal.h>
23986
23987
23988
23989
23990
23991
23992 #ifndef SIGPIPE
23993 #define SIGPIPE 13
23994 #endif
23995 #ifndef SIGINT
23996 #define SIGINT 2
23997 #endif
23998
23999 const char *Jim_SignalId(int sig)
24000 {
24001 static char buf[10];
24002 switch (sig) {
24003 case SIGINT: return "SIGINT";
24004 case SIGPIPE: return "SIGPIPE";
24005
24006 }
24007 snprintf(buf, sizeof(buf), "%d", sig);
24008 return buf;
24009 }
24010 #ifndef JIM_BOOTSTRAP_LIB_ONLY
24011 #include <errno.h>
24012 #include <string.h>
24013 #include <stdio.h>
24014
24015
24016 #ifdef USE_LINENOISE
24017 #ifdef HAVE_UNISTD_H
24018 #include <unistd.h>
24019 #endif
24020 #ifdef HAVE_SYS_STAT_H
24021 #include <sys/stat.h>
24022 #endif
24023 #include "linenoise.h"
24024 #else
24025 #define MAX_LINE_LEN 512
24026 #endif
24027
24028 #ifdef USE_LINENOISE
24029 struct JimCompletionInfo {
24030 Jim_Interp *interp;
24031 Jim_Obj *completion_command;
24032 Jim_Obj *hints_command;
24033
24034 };
24035
24036 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp);
24037 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
24038 static const char completion_callback_assoc_key[] = "interactive-completion";
24039 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata);
24040 static void JimFreeHintsCallback(void *hint, void *userdata);
24041 #endif
24042
24043 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
24044 {
24045 #ifdef USE_LINENOISE
24046 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24047 char *result;
24048 Jim_Obj *objPtr;
24049 long mlmode = 0;
24050 if (compinfo->completion_command) {
24051 linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
24052 }
24053 if (compinfo->hints_command) {
24054 linenoiseSetHintsCallback(JimHintsCallback, compinfo);
24055 linenoiseSetFreeHintsCallback(JimFreeHintsCallback);
24056 }
24057 objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
24058 if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
24059 linenoiseSetMultiLine(mlmode);
24060 }
24061
24062 result = linenoise(prompt);
24063
24064 linenoiseSetCompletionCallback(NULL, NULL);
24065 linenoiseSetHintsCallback(NULL, NULL);
24066 linenoiseSetFreeHintsCallback(NULL);
24067 return result;
24068 #else
24069 int len;
24070 char *line = Jim_Alloc(MAX_LINE_LEN);
24071
24072 fputs(prompt, stdout);
24073 fflush(stdout);
24074
24075 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
24076 Jim_Free(line);
24077 return NULL;
24078 }
24079 len = strlen(line);
24080 if (len && line[len - 1] == '\n') {
24081 line[len - 1] = '\0';
24082 }
24083 return line;
24084 #endif
24085 }
24086
24087 void Jim_HistoryLoad(const char *filename)
24088 {
24089 #ifdef USE_LINENOISE
24090 linenoiseHistoryLoad(filename);
24091 #endif
24092 }
24093
24094 void Jim_HistoryAdd(const char *line)
24095 {
24096 #ifdef USE_LINENOISE
24097 linenoiseHistoryAdd(line);
24098 #endif
24099 }
24100
24101 void Jim_HistorySave(const char *filename)
24102 {
24103 #ifdef USE_LINENOISE
24104 #ifdef HAVE_UMASK
24105 mode_t mask;
24106
24107 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
24108 #endif
24109 linenoiseHistorySave(filename);
24110 #ifdef HAVE_UMASK
24111 umask(mask);
24112 #endif
24113 #endif
24114 }
24115
24116 void Jim_HistoryShow(void)
24117 {
24118 #ifdef USE_LINENOISE
24119
24120 int i;
24121 int len;
24122 char **history = linenoiseHistory(&len);
24123 for (i = 0; i < len; i++) {
24124 printf("%4d %s\n", i + 1, history[i]);
24125 }
24126 #endif
24127 }
24128
24129 void Jim_HistorySetMaxLen(int length)
24130 {
24131 #ifdef USE_LINENOISE
24132 linenoiseHistorySetMaxLen(length);
24133 #endif
24134 }
24135
24136 int Jim_HistoryGetMaxLen(void)
24137 {
24138 #ifdef USE_LINENOISE
24139 return linenoiseHistoryGetMaxLen();
24140 #endif
24141 return 0;
24142 }
24143
24144 #ifdef USE_LINENOISE
24145 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
24146 {
24147 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
24148 Jim_Obj *objv[2];
24149 int ret;
24150
24151 objv[0] = info->completion_command;
24152 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
24153
24154 ret = Jim_EvalObjVector(info->interp, 2, objv);
24155
24156
24157 if (ret == JIM_OK) {
24158 int i;
24159 Jim_Obj *listObj = Jim_GetResult(info->interp);
24160 int len = Jim_ListLength(info->interp, listObj);
24161 for (i = 0; i < len; i++) {
24162 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
24163 }
24164 }
24165 }
24166
24167 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata)
24168 {
24169 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
24170 Jim_Obj *objv[2];
24171 int ret;
24172 char *result = NULL;
24173
24174 objv[0] = info->hints_command;
24175 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
24176
24177 ret = Jim_EvalObjVector(info->interp, 2, objv);
24178
24179
24180 if (ret == JIM_OK) {
24181 Jim_Obj *listObj = Jim_GetResult(info->interp);
24182 Jim_IncrRefCount(listObj);
24183
24184 int len = Jim_ListLength(info->interp, listObj);
24185 if (len >= 1) {
24186 long x;
24187 result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0)));
24188 if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) {
24189 *color = x;
24190 }
24191 if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) {
24192 *bold = x;
24193 }
24194 }
24195 Jim_DecrRefCount(info->interp, listObj);
24196 }
24197 return result;
24198 }
24199
24200 static void JimFreeHintsCallback(void *hint, void *userdata)
24201 {
24202 Jim_Free(hint);
24203 }
24204
24205 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
24206 {
24207 struct JimCompletionInfo *compinfo = data;
24208
24209 if (compinfo->completion_command) {
24210 Jim_DecrRefCount(interp, compinfo->completion_command);
24211 }
24212 if (compinfo->hints_command) {
24213 Jim_DecrRefCount(interp, compinfo->hints_command);
24214 }
24215
24216 Jim_Free(compinfo);
24217 }
24218
24219 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp)
24220 {
24221 struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
24222 if (compinfo == NULL) {
24223 compinfo = Jim_Alloc(sizeof(*compinfo));
24224 compinfo->interp = interp;
24225 compinfo->completion_command = NULL;
24226 compinfo->hints_command = NULL;
24227 Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
24228 }
24229 return compinfo;
24230 }
24231 #endif
24232
24233 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj)
24234 {
24235 #ifdef USE_LINENOISE
24236 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24237
24238 if (completionCommandObj) {
24239
24240 Jim_IncrRefCount(completionCommandObj);
24241 }
24242 if (compinfo->completion_command) {
24243 Jim_DecrRefCount(interp, compinfo->completion_command);
24244 }
24245 compinfo->completion_command = completionCommandObj;
24246 #endif
24247 }
24248
24249 void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj)
24250 {
24251 #ifdef USE_LINENOISE
24252 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24253
24254 if (hintsCommandObj) {
24255
24256 Jim_IncrRefCount(hintsCommandObj);
24257 }
24258 if (compinfo->hints_command) {
24259 Jim_DecrRefCount(interp, compinfo->hints_command);
24260 }
24261 compinfo->hints_command = hintsCommandObj;
24262 #endif
24263 }
24264
24265 int Jim_InteractivePrompt(Jim_Interp *interp)
24266 {
24267 int retcode = JIM_OK;
24268 char *history_file = NULL;
24269 #ifdef USE_LINENOISE
24270 const char *home;
24271
24272 home = getenv("HOME");
24273 if (home && isatty(STDIN_FILENO)) {
24274 int history_len = strlen(home) + sizeof("/.jim_history");
24275 history_file = Jim_Alloc(history_len);
24276 snprintf(history_file, history_len, "%s/.jim_history", home);
24277 Jim_HistoryLoad(history_file);
24278 }
24279
24280 Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
24281 Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1));
24282 #endif
24283
24284 printf("Welcome to Jim version %d.%d\n",
24285 JIM_VERSION / 100, JIM_VERSION % 100);
24286 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
24287
24288 while (1) {
24289 Jim_Obj *scriptObjPtr;
24290 const char *result;
24291 int reslen;
24292 char prompt[20];
24293
24294 if (retcode != JIM_OK) {
24295 const char *retcodestr = Jim_ReturnCode(retcode);
24296
24297 if (*retcodestr == '?') {
24298 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
24299 }
24300 else {
24301 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
24302 }
24303 }
24304 else {
24305 strcpy(prompt, ". ");
24306 }
24307
24308 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
24309 Jim_IncrRefCount(scriptObjPtr);
24310 while (1) {
24311 char state;
24312 char *line;
24313
24314 line = Jim_HistoryGetline(interp, prompt);
24315 if (line == NULL) {
24316 if (errno == EINTR) {
24317 continue;
24318 }
24319 Jim_DecrRefCount(interp, scriptObjPtr);
24320 retcode = JIM_OK;
24321 goto out;
24322 }
24323 if (Jim_Length(scriptObjPtr) != 0) {
24324
24325 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
24326 }
24327 Jim_AppendString(interp, scriptObjPtr, line, -1);
24328 Jim_Free(line);
24329 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
24330 break;
24331
24332 snprintf(prompt, sizeof(prompt), "%c> ", state);
24333 }
24334 #ifdef USE_LINENOISE
24335 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
24336
24337 Jim_HistoryShow();
24338 Jim_DecrRefCount(interp, scriptObjPtr);
24339 continue;
24340 }
24341
24342 Jim_HistoryAdd(Jim_String(scriptObjPtr));
24343 if (history_file) {
24344 Jim_HistorySave(history_file);
24345 }
24346 #endif
24347 retcode = Jim_EvalObj(interp, scriptObjPtr);
24348 Jim_DecrRefCount(interp, scriptObjPtr);
24349
24350 if (retcode == JIM_EXIT) {
24351 break;
24352 }
24353 if (retcode == JIM_ERR) {
24354 Jim_MakeErrorMessage(interp);
24355 }
24356 result = Jim_GetString(Jim_GetResult(interp), &reslen);
24357 if (reslen) {
24358 if (fwrite(result, reslen, 1, stdout) == 0) {
24359
24360 }
24361 putchar('\n');
24362 }
24363 }
24364 out:
24365 Jim_Free(history_file);
24366
24367 return retcode;
24368 }
24369
24370 #include <stdio.h>
24371 #include <stdlib.h>
24372 #include <string.h>
24373
24374
24375
24376 extern int Jim_initjimshInit(Jim_Interp *interp);
24377
24378 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
24379 {
24380 int n;
24381 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
24382
24383
24384 for (n = 0; n < argc; n++) {
24385 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
24386
24387 Jim_ListAppendElement(interp, listObj, obj);
24388 }
24389
24390 Jim_SetVariableStr(interp, "argv", listObj);
24391 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
24392 }
24393
24394 static void JimPrintErrorMessage(Jim_Interp *interp)
24395 {
24396 Jim_MakeErrorMessage(interp);
24397 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
24398 }
24399
24400 void usage(const char* executable_name)
24401 {
24402 printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
24403 printf("Usage: %s\n", executable_name);
24404 printf("or : %s [options] [filename]\n", executable_name);
24405 printf("\n");
24406 printf("Without options: Interactive mode\n");
24407 printf("\n");
24408 printf("Options:\n");
24409 printf(" --version : prints the version string\n");
24410 printf(" --help : prints this text\n");
24411 printf(" -e CMD : executes command CMD\n");
24412 printf(" NOTE: all subsequent options will be passed as arguments to the command\n");
24413 printf(" [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
24414 printf(" NOTE: all subsequent options will be passed to the script\n\n");
24415 }
24416
24417 int main(int argc, char *const argv[])
24418 {
24419 int retcode;
24420 Jim_Interp *interp;
24421 char *const orig_argv0 = argv[0];
24422
24423
24424 if (argc > 1 && strcmp(argv[1], "--version") == 0) {
24425 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
24426 return 0;
24427 }
24428 else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
24429 usage(argv[0]);
24430 return 0;
24431 }
24432
24433
24434 interp = Jim_CreateInterp();
24435 Jim_RegisterCoreCommands(interp);
24436
24437
24438 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
24439 JimPrintErrorMessage(interp);
24440 }
24441
24442 Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
24443 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
24444 #ifdef USE_LINENOISE
24445 Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1");
24446 #else
24447 Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0");
24448 #endif
24449 retcode = Jim_initjimshInit(interp);
24450
24451 if (argc == 1) {
24452
24453 if (retcode == JIM_ERR) {
24454 JimPrintErrorMessage(interp);
24455 }
24456 if (retcode != JIM_EXIT) {
24457 JimSetArgv(interp, 0, NULL);
24458 if (!isatty(STDIN_FILENO)) {
24459
24460 goto eval_stdin;
24461 }
24462 retcode = Jim_InteractivePrompt(interp);
24463 }
24464 }
24465 else {
24466
24467 if (argc > 2 && strcmp(argv[1], "-e") == 0) {
24468
24469 JimSetArgv(interp, argc - 3, argv + 3);
24470 retcode = Jim_Eval(interp, argv[2]);
24471 if (retcode != JIM_ERR) {
24472 int len;
24473 const char *msg = Jim_GetString(Jim_GetResult(interp), &len);
24474 if (fwrite(msg, len, 1, stdout) == 0) {
24475
24476 }
24477 putchar('\n');
24478 }
24479 }
24480 else {
24481 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
24482 JimSetArgv(interp, argc - 2, argv + 2);
24483 if (strcmp(argv[1], "-") == 0) {
24484 eval_stdin:
24485 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
24486 } else {
24487 retcode = Jim_EvalFile(interp, argv[1]);
24488 }
24489 }
24490 if (retcode == JIM_ERR) {
24491 JimPrintErrorMessage(interp);
24492 }
24493 }
24494 if (retcode == JIM_EXIT) {
24495 retcode = Jim_GetExitCode(interp);
24496 }
24497 else if (retcode == JIM_ERR) {
24498 retcode = 1;
24499 }
24500 else {
24501 retcode = 0;
24502 }
24503 Jim_FreeInterp(interp);
24504 return retcode;
24505 }
24506 #endif