Fossil SCM

fossil-scm / autosetup / jimsh0.c
Blame History Raw 24457 lines
1
/* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
2
#define JIM_TCL_COMPAT
3
#define JIM_ANSIC
4
#define JIM_REGEXP
5
#define HAVE_NO_AUTOCONF
6
#define 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_posix
17
#define jim_ext_clock
18
#define jim_ext_array
19
#define jim_ext_stdlib
20
#define jim_ext_tclcompat
21
#if defined(_MSC_VER)
22
#define TCL_PLATFORM_OS "windows"
23
#define TCL_PLATFORM_PLATFORM "windows"
24
#define TCL_PLATFORM_PATH_SEPARATOR ";"
25
#define HAVE_MKDIR_ONE_ARG
26
#define HAVE_SYSTEM
27
#elif defined(__MINGW32__)
28
#define TCL_PLATFORM_OS "mingw"
29
#define TCL_PLATFORM_PLATFORM "windows"
30
#define TCL_PLATFORM_PATH_SEPARATOR ";"
31
#define HAVE_MKDIR_ONE_ARG
32
#define HAVE_SYSTEM
33
#define HAVE_SYS_TIME_H
34
#define HAVE_DIRENT_H
35
#define HAVE_UNISTD_H
36
#define HAVE_UMASK
37
#include <sys/stat.h>
38
#ifndef S_IRWXG
39
#define S_IRWXG 0
40
#endif
41
#ifndef S_IRWXO
42
#define S_IRWXO 0
43
#endif
44
#else
45
#define TCL_PLATFORM_OS "unknown"
46
#define TCL_PLATFORM_PLATFORM "unix"
47
#define TCL_PLATFORM_PATH_SEPARATOR ":"
48
#ifdef _MINIX
49
#define vfork fork
50
#define _POSIX_SOURCE
51
#else
52
#define _GNU_SOURCE
53
#endif
54
#ifndef __ixemul__
55
#define HAVE_FORK
56
#else
57
#define HAVE_VFORK
58
#endif
59
#define HAVE_WAITPID
60
#define HAVE_ISATTY
61
#define HAVE_MKSTEMP
62
#define HAVE_LINK
63
#define HAVE_SYS_TIME_H
64
#define HAVE_DIRENT_H
65
#define HAVE_UNISTD_H
66
#define HAVE_UMASK
67
#define HAVE_PIPE
68
#define _FILE_OFFSET_BITS 64
69
#endif
70
#define JIM_VERSION 83
71
#ifndef JIM_WIN32COMPAT_H
72
#define JIM_WIN32COMPAT_H
73
74
75
76
#ifdef __cplusplus
77
extern "C" {
78
#endif
79
80
81
#if defined(_WIN32) || defined(WIN32)
82
83
#define HAVE_DLOPEN
84
void *dlopen(const char *path, int mode);
85
int dlclose(void *handle);
86
void *dlsym(void *handle, const char *symbol);
87
char *dlerror(void);
88
89
90
#if defined(__MINGW32__)
91
#define JIM_SPRINTF_DOUBLE_NEEDS_FIX
92
#endif
93
94
#ifdef _MSC_VER
95
96
97
#if _MSC_VER >= 1000
98
#pragma warning(disable:4146)
99
#endif
100
101
#include <limits.h>
102
#define jim_wide _int64
103
#ifndef LLONG_MAX
104
#define LLONG_MAX 9223372036854775807I64
105
#endif
106
#ifndef LLONG_MIN
107
#define LLONG_MIN (-LLONG_MAX - 1I64)
108
#endif
109
#define JIM_WIDE_MIN LLONG_MIN
110
#define JIM_WIDE_MAX LLONG_MAX
111
#define JIM_WIDE_MODIFIER "I64d"
112
#define strcasecmp _stricmp
113
#define strtoull _strtoui64
114
115
#include <io.h>
116
117
struct timeval {
118
long tv_sec;
119
long tv_usec;
120
};
121
122
int gettimeofday(struct timeval *tv, void *unused);
123
124
#define HAVE_OPENDIR
125
struct dirent {
126
char *d_name;
127
};
128
129
typedef struct DIR {
130
long handle;
131
struct _finddata_t info;
132
struct dirent result;
133
char *name;
134
} DIR;
135
136
DIR *opendir(const char *name);
137
int closedir(DIR *dir);
138
struct dirent *readdir(DIR *dir);
139
140
#endif
141
142
#endif
143
144
#ifdef __cplusplus
145
}
146
#endif
147
148
#endif
149
#ifndef UTF8_UTIL_H
150
#define UTF8_UTIL_H
151
152
#ifdef __cplusplus
153
extern "C" {
154
#endif
155
156
157
158
#define MAX_UTF8_LEN 4
159
160
int utf8_fromunicode(char *p, unsigned uc);
161
162
#ifndef JIM_UTF8
163
#include <ctype.h>
164
165
166
#define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
167
#define utf8_strwidth(S, B) utf8_strlen((S), (B))
168
#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
169
#define utf8_getchars(CP, C) (*(CP) = (C), 1)
170
#define utf8_upper(C) toupper(C)
171
#define utf8_title(C) toupper(C)
172
#define utf8_lower(C) tolower(C)
173
#define utf8_index(C, I) (I)
174
#define utf8_charlen(C) 1
175
#define utf8_prev_len(S, L) 1
176
#define utf8_width(C) 1
177
178
#else
179
180
#endif
181
182
#ifdef __cplusplus
183
}
184
#endif
185
186
#endif
187
188
#ifndef __JIM__H
189
#define __JIM__H
190
191
#ifdef __cplusplus
192
extern "C" {
193
#endif
194
195
#include <time.h>
196
#include <limits.h>
197
#include <stdlib.h>
198
#include <stdarg.h>
199
200
201
#ifndef HAVE_NO_AUTOCONF
202
#endif
203
204
205
206
#ifndef jim_wide
207
# ifdef HAVE_LONG_LONG
208
# define jim_wide long long
209
# ifndef LLONG_MAX
210
# define LLONG_MAX 9223372036854775807LL
211
# endif
212
# ifndef LLONG_MIN
213
# define LLONG_MIN (-LLONG_MAX - 1LL)
214
# endif
215
# define JIM_WIDE_MIN LLONG_MIN
216
# define JIM_WIDE_MAX LLONG_MAX
217
# else
218
# define jim_wide long
219
# define JIM_WIDE_MIN LONG_MIN
220
# define JIM_WIDE_MAX LONG_MAX
221
# endif
222
223
224
# ifdef HAVE_LONG_LONG
225
# define JIM_WIDE_MODIFIER "lld"
226
# else
227
# define JIM_WIDE_MODIFIER "ld"
228
# define strtoull strtoul
229
# endif
230
#endif
231
232
#define UCHAR(c) ((unsigned char)(c))
233
234
235
236
#define JIM_ABI_VERSION 101
237
238
#define JIM_OK 0
239
#define JIM_ERR 1
240
#define JIM_RETURN 2
241
#define JIM_BREAK 3
242
#define JIM_CONTINUE 4
243
#define JIM_SIGNAL 5
244
#define JIM_EXIT 6
245
246
#define JIM_EVAL 7
247
248
#define JIM_MAX_CALLFRAME_DEPTH 1000
249
#define JIM_MAX_EVAL_DEPTH 2000
250
251
252
#define JIM_PRIV_FLAG_SHIFT 20
253
254
#define JIM_NONE 0
255
#define JIM_ERRMSG 1
256
#define JIM_ENUM_ABBREV 2
257
#define JIM_UNSHARED 4
258
#define JIM_MUSTEXIST 8
259
#define JIM_NORESULT 16
260
261
262
#define JIM_SUBST_NOVAR 1
263
#define JIM_SUBST_NOCMD 2
264
#define JIM_SUBST_NOESC 4
265
#define JIM_SUBST_FLAG 128
266
267
268
#define JIM_CASESENS 0
269
#define JIM_NOCASE 1
270
#define JIM_OPT_END 2
271
272
273
#define JIM_PATH_LEN 1024
274
275
276
#define JIM_NOTUSED(V) ((void) V)
277
278
#define JIM_LIBPATH "auto_path"
279
#define JIM_INTERACTIVE "tcl_interactive"
280
281
282
typedef struct Jim_Stack {
283
int len;
284
int maxlen;
285
void **vector;
286
} Jim_Stack;
287
288
289
typedef struct Jim_HashEntry {
290
void *key;
291
union {
292
void *val;
293
int intval;
294
} u;
295
struct Jim_HashEntry *next;
296
} Jim_HashEntry;
297
298
typedef struct Jim_HashTableType {
299
unsigned int (*hashFunction)(const void *key);
300
void *(*keyDup)(void *privdata, const void *key);
301
void *(*valDup)(void *privdata, const void *obj);
302
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
303
void (*keyDestructor)(void *privdata, void *key);
304
void (*valDestructor)(void *privdata, void *obj);
305
} Jim_HashTableType;
306
307
typedef struct Jim_HashTable {
308
Jim_HashEntry **table;
309
const Jim_HashTableType *type;
310
void *privdata;
311
unsigned int size;
312
unsigned int sizemask;
313
unsigned int used;
314
unsigned int collisions;
315
unsigned int uniq;
316
} Jim_HashTable;
317
318
typedef struct Jim_HashTableIterator {
319
Jim_HashTable *ht;
320
Jim_HashEntry *entry, *nextEntry;
321
int index;
322
} Jim_HashTableIterator;
323
324
325
#define JIM_HT_INITIAL_SIZE 16
326
327
328
#define Jim_FreeEntryVal(ht, entry) \
329
if ((ht)->type->valDestructor) \
330
(ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
331
332
#define Jim_SetHashVal(ht, entry, _val_) do { \
333
if ((ht)->type->valDup) \
334
(entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
335
else \
336
(entry)->u.val = (_val_); \
337
} while(0)
338
339
#define Jim_SetHashIntVal(ht, entry, _val_) (entry)->u.intval = (_val_)
340
341
#define Jim_FreeEntryKey(ht, entry) \
342
if ((ht)->type->keyDestructor) \
343
(ht)->type->keyDestructor((ht)->privdata, (entry)->key)
344
345
#define Jim_SetHashKey(ht, entry, _key_) do { \
346
if ((ht)->type->keyDup) \
347
(entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
348
else \
349
(entry)->key = (void *)(_key_); \
350
} while(0)
351
352
#define Jim_CompareHashKeys(ht, key1, key2) \
353
(((ht)->type->keyCompare) ? \
354
(ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
355
(key1) == (key2))
356
357
#define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
358
359
#define Jim_GetHashEntryKey(he) ((he)->key)
360
#define Jim_GetHashEntryVal(he) ((he)->u.val)
361
#define Jim_GetHashEntryIntVal(he) ((he)->u.intval)
362
#define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
363
#define Jim_GetHashTableSize(ht) ((ht)->size)
364
#define Jim_GetHashTableUsed(ht) ((ht)->used)
365
366
367
typedef struct Jim_Obj {
368
char *bytes;
369
const struct Jim_ObjType *typePtr;
370
int refCount;
371
int length;
372
373
union {
374
375
jim_wide wideValue;
376
377
int intValue;
378
379
double doubleValue;
380
381
void *ptr;
382
383
struct {
384
void *ptr1;
385
void *ptr2;
386
} twoPtrValue;
387
388
struct {
389
void *ptr;
390
int int1;
391
int int2;
392
} ptrIntValue;
393
394
struct {
395
struct Jim_VarVal *vv;
396
unsigned long callFrameId;
397
int global;
398
} varValue;
399
400
struct {
401
struct Jim_Obj *nsObj;
402
struct Jim_Cmd *cmdPtr;
403
unsigned long procEpoch;
404
} cmdValue;
405
406
struct {
407
struct Jim_Obj **ele;
408
int len;
409
int maxLen;
410
} listValue;
411
412
struct Jim_Dict *dictValue;
413
414
struct {
415
int maxLength;
416
int charLength;
417
} strValue;
418
419
struct {
420
unsigned long id;
421
struct Jim_Reference *refPtr;
422
} refValue;
423
424
struct {
425
struct Jim_Obj *fileNameObj;
426
int lineNumber;
427
} sourceValue;
428
429
struct {
430
struct Jim_Obj *varNameObjPtr;
431
struct Jim_Obj *indexObjPtr;
432
} dictSubstValue;
433
struct {
434
int line;
435
int argc;
436
} scriptLineValue;
437
} internalRep;
438
struct Jim_Obj *prevObjPtr;
439
struct Jim_Obj *nextObjPtr;
440
} Jim_Obj;
441
442
443
#define Jim_IncrRefCount(objPtr) \
444
++(objPtr)->refCount
445
#define Jim_DecrRefCount(interp, objPtr) \
446
if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
447
#define Jim_IsShared(objPtr) \
448
((objPtr)->refCount > 1)
449
450
#define Jim_FreeNewObj Jim_FreeObj
451
452
453
#define Jim_FreeIntRep(i,o) \
454
if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
455
(o)->typePtr->freeIntRepProc(i, o)
456
457
458
#define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
459
460
461
#define Jim_SetIntRepPtr(o, p) \
462
(o)->internalRep.ptr = (p)
463
464
465
struct Jim_Interp;
466
467
typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
468
struct Jim_Obj *objPtr);
469
typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
470
struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
471
typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
472
473
typedef struct Jim_ObjType {
474
const char *name;
475
Jim_FreeInternalRepProc *freeIntRepProc;
476
Jim_DupInternalRepProc *dupIntRepProc;
477
Jim_UpdateStringProc *updateStringProc;
478
int flags;
479
} Jim_ObjType;
480
481
482
#define JIM_TYPE_NONE 0
483
#define JIM_TYPE_REFERENCES 1
484
485
486
487
typedef struct Jim_CallFrame {
488
unsigned long id;
489
int level;
490
struct Jim_HashTable vars;
491
struct Jim_HashTable *staticVars;
492
struct Jim_CallFrame *parent;
493
Jim_Obj *const *argv;
494
int argc;
495
Jim_Obj *procArgsObjPtr;
496
Jim_Obj *procBodyObjPtr;
497
struct Jim_CallFrame *next;
498
Jim_Obj *nsObj;
499
Jim_Obj *unused_fileNameObj;
500
int unused_line;
501
Jim_Stack *localCommands;
502
struct Jim_Obj *tailcallObj;
503
struct Jim_Cmd *tailcallCmd;
504
} Jim_CallFrame;
505
506
507
typedef struct Jim_EvalFrame {
508
Jim_CallFrame *framePtr;
509
int level;
510
int procLevel;
511
struct Jim_Cmd *cmd;
512
struct Jim_EvalFrame *parent;
513
Jim_Obj *const *argv;
514
int argc;
515
Jim_Obj *scriptObj;
516
} Jim_EvalFrame;
517
518
typedef struct Jim_VarVal {
519
Jim_Obj *objPtr;
520
struct Jim_CallFrame *linkFramePtr;
521
int refCount;
522
} Jim_VarVal;
523
524
525
typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
526
Jim_Obj *const *argv);
527
typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
528
529
typedef struct Jim_Dict {
530
struct JimDictHashEntry {
531
int offset;
532
unsigned hash;
533
} *ht;
534
unsigned int size;
535
unsigned int sizemask;
536
unsigned int uniq;
537
Jim_Obj **table;
538
int len;
539
int maxLen;
540
unsigned int dummy;
541
} Jim_Dict;
542
543
typedef struct Jim_Cmd {
544
int inUse;
545
int isproc;
546
struct Jim_Cmd *prevCmd;
547
Jim_Obj *cmdNameObj;
548
union {
549
struct {
550
551
Jim_CmdProc *cmdProc;
552
Jim_DelCmdProc *delProc;
553
void *privData;
554
} native;
555
struct {
556
557
Jim_Obj *argListObjPtr;
558
Jim_Obj *bodyObjPtr;
559
Jim_HashTable *staticVars;
560
int argListLen;
561
int reqArity;
562
int optArity;
563
int argsPos;
564
int upcall;
565
struct Jim_ProcArg {
566
Jim_Obj *nameObjPtr;
567
Jim_Obj *defaultObjPtr;
568
} *arglist;
569
Jim_Obj *nsObj;
570
} proc;
571
} u;
572
} Jim_Cmd;
573
574
575
typedef struct Jim_PrngState {
576
unsigned char sbox[256];
577
unsigned int i, j;
578
} Jim_PrngState;
579
580
typedef struct Jim_Interp {
581
Jim_Obj *result;
582
int unused_errorLine;
583
Jim_Obj *currentFilenameObj;
584
int break_level;
585
int maxCallFrameDepth;
586
int maxEvalDepth;
587
int evalDepth;
588
int returnCode;
589
int returnLevel;
590
int exitCode;
591
long id;
592
int signal_level;
593
jim_wide sigmask;
594
int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
595
Jim_CallFrame *framePtr;
596
Jim_CallFrame *topFramePtr;
597
struct Jim_HashTable commands;
598
unsigned long procEpoch; /* Incremented every time the result
599
of procedures names lookup caching
600
may no longer be valid. */
601
unsigned long callFrameEpoch; /* Incremented every time a new
602
callframe is created. This id is used for the
603
'ID' field contained in the Jim_CallFrame
604
structure. */
605
int local;
606
int quitting;
607
int safeexpr;
608
Jim_Obj *liveList;
609
Jim_Obj *freeList;
610
Jim_Obj *unused_currentScriptObj;
611
Jim_EvalFrame topEvalFrame;
612
Jim_EvalFrame *evalFrame;
613
int procLevel;
614
Jim_Obj * const *unused_argv;
615
Jim_Obj *nullScriptObj;
616
Jim_Obj *emptyObj;
617
Jim_Obj *trueObj;
618
Jim_Obj *falseObj;
619
unsigned long referenceNextId;
620
struct Jim_HashTable references;
621
unsigned long lastCollectId; /* reference max Id of the last GC
622
execution. It's set to ~0 while the collection
623
is running as sentinel to avoid to recursive
624
calls via the [collect] command inside
625
finalizers. */
626
jim_wide lastCollectTime;
627
Jim_Obj *stackTrace;
628
Jim_Obj *errorProc;
629
Jim_Obj *unknown;
630
Jim_Obj *defer;
631
Jim_Obj *traceCmdObj;
632
int unknown_called;
633
int errorFlag;
634
void *cmdPrivData; /* Used to pass the private data pointer to
635
a command. It is set to what the user specified
636
via Jim_CreateCommand(). */
637
638
Jim_Cmd *oldCmdCache;
639
int oldCmdCacheSize;
640
struct Jim_CallFrame *freeFramesList;
641
struct Jim_HashTable assocData;
642
Jim_PrngState *prngState;
643
struct Jim_HashTable packages;
644
Jim_Stack *loadHandles;
645
} Jim_Interp;
646
647
#define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
648
#define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
649
650
#define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
651
#define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
652
#define Jim_GetResult(i) ((i)->result)
653
#define Jim_CmdPrivData(i) ((i)->cmdPrivData)
654
655
#define Jim_SetResult(i,o) do { \
656
Jim_Obj *_resultObjPtr_ = (o); \
657
Jim_IncrRefCount(_resultObjPtr_); \
658
Jim_DecrRefCount(i,(i)->result); \
659
(i)->result = _resultObjPtr_; \
660
} while(0)
661
662
663
#define Jim_GetId(i) (++(i)->id)
664
665
666
#define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
667
string representation must be fixed length. */
668
typedef struct Jim_Reference {
669
Jim_Obj *objPtr;
670
Jim_Obj *finalizerCmdNamePtr;
671
char tag[JIM_REFERENCE_TAGLEN+1];
672
} Jim_Reference;
673
674
675
#define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
676
#define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
677
678
#define JIM_EXPORT extern
679
680
681
682
JIM_EXPORT void *(*Jim_Allocator)(void *ptr, size_t size);
683
684
#define Jim_Free(P) Jim_Allocator((P), 0)
685
#define Jim_Realloc(P, S) Jim_Allocator((P), (S))
686
#define Jim_Alloc(S) Jim_Allocator(NULL, (S))
687
JIM_EXPORT char * Jim_StrDup (const char *s);
688
JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
689
690
691
JIM_EXPORT char **Jim_GetEnviron(void);
692
JIM_EXPORT void Jim_SetEnviron(char **env);
693
JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
694
#ifndef CLOCK_REALTIME
695
# define CLOCK_REALTIME 0
696
#endif
697
#ifndef CLOCK_MONOTONIC
698
# define CLOCK_MONOTONIC 1
699
#endif
700
#ifndef CLOCK_MONOTONIC_RAW
701
# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
702
#endif
703
JIM_EXPORT jim_wide Jim_GetTimeUsec(unsigned type);
704
705
706
JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
707
708
709
JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
710
711
#define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
712
713
JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
714
JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
715
JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
716
JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
717
JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
718
Jim_Obj *const *objv);
719
JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
720
JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
721
int objc, Jim_Obj *const *objv);
722
#define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
723
JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
724
JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
725
Jim_Obj **resObjPtrPtr, int flags);
726
727
728
JIM_EXPORT Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
729
int *lineptr);
730
731
JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
732
Jim_Obj *fileNameObj, int lineNumber);
733
734
735
736
JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
737
JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
738
JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
739
JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
740
JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
741
JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
742
JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
743
744
745
JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
746
const Jim_HashTableType *type, void *privdata);
747
JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
748
unsigned int size);
749
JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
750
void *val);
751
JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
752
const void *key, void *val);
753
JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
754
const void *key);
755
JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
756
JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
757
const void *key);
758
JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
759
(Jim_HashTable *ht);
760
JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
761
(Jim_HashTableIterator *iter);
762
763
764
JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
765
JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
766
JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
767
JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
768
Jim_Obj *objPtr);
769
JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
770
int *lenPtr);
771
JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
772
JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
773
774
775
JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
776
const char *s, int len);
777
JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
778
const char *s, int charlen);
779
JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
780
char *s, int len);
781
JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
782
const char *str, int len);
783
JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
784
Jim_Obj *appendObjPtr);
785
JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
786
Jim_Obj *objPtr, ...);
787
JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
788
JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
789
Jim_Obj *objPtr, int nocase);
790
JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
791
Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
792
Jim_Obj *lastObjPtr);
793
JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
794
Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
795
JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
796
Jim_Obj *fmtObjPtr, int flags);
797
JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
798
Jim_Obj *objPtr, const char *str);
799
JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
800
Jim_Obj *secondObjPtr, int nocase);
801
JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
802
803
804
JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
805
Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
806
JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
807
Jim_Obj *objPtr);
808
JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
809
JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
810
811
812
JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
813
JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
814
JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
815
JIM_EXPORT const char *Jim_ReturnCode(int code);
816
JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
817
818
819
JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
820
JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
821
const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
822
Jim_DelCmdProc *delProc);
823
JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
824
Jim_Obj *cmdNameObj);
825
JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
826
Jim_Obj *oldNameObj, Jim_Obj *newNameObj);
827
JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
828
Jim_Obj *objPtr, int flags);
829
JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
830
Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
831
JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
832
const char *name, Jim_Obj *objPtr);
833
JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
834
const char *name, Jim_Obj *objPtr);
835
JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
836
const char *name, const char *val);
837
JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
838
Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
839
Jim_CallFrame *targetCallFrame);
840
JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
841
Jim_Obj *nameObjPtr);
842
JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
843
Jim_Obj *nameObjPtr, int flags);
844
JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
845
Jim_Obj *nameObjPtr, int flags);
846
JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
847
const char *name, int flags);
848
JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
849
const char *name, int flags);
850
JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
851
Jim_Obj *nameObjPtr, int flags);
852
853
854
JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
855
Jim_Obj *levelObjPtr);
856
857
858
JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
859
JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
860
861
862
JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
863
int *indexPtr);
864
865
866
JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
867
Jim_Obj *const *elements, int len);
868
JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
869
Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
870
JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
871
Jim_Obj *listPtr, Jim_Obj *objPtr);
872
JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
873
Jim_Obj *listPtr, Jim_Obj *appendListPtr);
874
JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
875
JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
876
int listindex, Jim_Obj **objPtrPtr, int seterr);
877
JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
878
JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
879
Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
880
Jim_Obj *newObjPtr);
881
JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
882
Jim_Obj *const *objv);
883
JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
884
Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
885
886
887
JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
888
Jim_Obj *const *elements, int len);
889
JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
890
Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
891
JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
892
Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
893
Jim_Obj **objPtrPtr, int flags);
894
JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
895
Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
896
Jim_Obj *newObjPtr, int flags);
897
JIM_EXPORT Jim_Obj **Jim_DictPairs(Jim_Interp *interp,
898
Jim_Obj *dictPtr, int *len);
899
JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
900
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
901
902
#define JIM_DICTMATCH_KEYS 0x0001
903
#define JIM_DICTMATCH_VALUES 0x002
904
905
JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
906
JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
907
JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
908
JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
909
910
911
JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
912
int *intPtr);
913
914
915
JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
916
Jim_Obj *exprObjPtr);
917
JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
918
Jim_Obj *exprObjPtr, int *boolPtr);
919
920
921
JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
922
int *booleanPtr);
923
924
925
JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
926
jim_wide *widePtr);
927
JIM_EXPORT int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr,
928
jim_wide *widePtr);
929
JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
930
long *longPtr);
931
#define Jim_NewWideObj Jim_NewIntObj
932
JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
933
jim_wide wideValue);
934
935
936
JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
937
double *doublePtr);
938
JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
939
double doubleValue);
940
JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
941
942
943
JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
944
Jim_Obj *const *argv, const char *msg);
945
JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
946
const char * const *tablePtr, int *indexPtr, const char *name, int flags);
947
JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
948
const char *const *tablePtr);
949
JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
950
Jim_Obj *scriptObj, char *stateCharPtr);
951
952
JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
953
954
955
typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
956
JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
957
JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
958
Jim_InterpDeleteProc *delProc, void *data);
959
JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
960
JIM_EXPORT int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version);
961
962
963
964
965
JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
966
const char *name, const char *ver, int flags);
967
JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
968
const char *name, int flags);
969
#define Jim_PackageProvideCheck(INTERP, NAME) \
970
if (Jim_CheckAbiVersion(INTERP, JIM_ABI_VERSION) == JIM_ERR || Jim_PackageProvide(INTERP, NAME, "1.0", JIM_ERRMSG)) \
971
return JIM_ERR
972
973
974
JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
975
976
977
JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
978
JIM_EXPORT void Jim_HistoryLoad(const char *filename);
979
JIM_EXPORT void Jim_HistorySave(const char *filename);
980
JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
981
JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj);
982
JIM_EXPORT void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj);
983
JIM_EXPORT void Jim_HistoryAdd(const char *line);
984
JIM_EXPORT void Jim_HistoryShow(void);
985
JIM_EXPORT void Jim_HistorySetMaxLen(int length);
986
JIM_EXPORT int Jim_HistoryGetMaxLen(void);
987
988
989
JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
990
JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
991
JIM_EXPORT int Jim_IsBigEndian(void);
992
993
#define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
994
JIM_EXPORT void Jim_SignalSetIgnored(jim_wide mask);
995
996
997
JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
998
JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
999
1000
1001
JIM_EXPORT int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
1002
1003
1004
JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
1005
JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
1006
1007
#ifdef __cplusplus
1008
}
1009
#endif
1010
1011
#endif
1012
1013
#ifndef JIM_SUBCMD_H
1014
#define JIM_SUBCMD_H
1015
1016
1017
#ifdef __cplusplus
1018
extern "C" {
1019
#endif
1020
1021
1022
#define JIM_MODFLAG_HIDDEN 0x0001
1023
#define JIM_MODFLAG_FULLARGV 0x0002
1024
1025
1026
1027
typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1028
1029
typedef struct {
1030
const char *cmd;
1031
const char *args;
1032
jim_subcmd_function *function;
1033
short minargs;
1034
short maxargs;
1035
unsigned short flags;
1036
} jim_subcmd_type;
1037
1038
#define JIM_DEF_SUBCMD(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs }
1039
#define JIM_DEF_SUBCMD_HIDDEN(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs, JIM_MODFLAG_HIDDEN }
1040
1041
const jim_subcmd_type *
1042
Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
1043
1044
int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1045
1046
int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
1047
1048
void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type *ct, Jim_Obj *subcmd);
1049
1050
#ifdef __cplusplus
1051
}
1052
#endif
1053
1054
#endif
1055
#ifndef JIMREGEXP_H
1056
#define JIMREGEXP_H
1057
1058
1059
#ifdef __cplusplus
1060
extern "C" {
1061
#endif
1062
1063
#include <stdlib.h>
1064
1065
typedef struct {
1066
int rm_so;
1067
int rm_eo;
1068
} regmatch_t;
1069
1070
1071
typedef struct regexp {
1072
1073
int re_nsub;
1074
1075
1076
int cflags;
1077
int err;
1078
int regstart;
1079
int reganch;
1080
int regmust;
1081
int regmlen;
1082
int *program;
1083
1084
1085
const char *regparse;
1086
int p;
1087
int proglen;
1088
1089
1090
int eflags;
1091
const char *start;
1092
const char *reginput;
1093
const char *regbol;
1094
1095
1096
regmatch_t *pmatch;
1097
int nmatch;
1098
} regexp;
1099
1100
typedef regexp regex_t;
1101
1102
#define REG_EXTENDED 0
1103
#define REG_NEWLINE 1
1104
#define REG_ICASE 2
1105
1106
#define REG_NOTBOL 16
1107
1108
enum {
1109
REG_NOERROR,
1110
REG_NOMATCH,
1111
REG_BADPAT,
1112
REG_ERR_NULL_ARGUMENT,
1113
REG_ERR_UNKNOWN,
1114
REG_ERR_TOO_BIG,
1115
REG_ERR_NOMEM,
1116
REG_ERR_TOO_MANY_PAREN,
1117
REG_ERR_UNMATCHED_PAREN,
1118
REG_ERR_UNMATCHED_BRACES,
1119
REG_ERR_BAD_COUNT,
1120
REG_ERR_JUNK_ON_END,
1121
REG_ERR_OPERAND_COULD_BE_EMPTY,
1122
REG_ERR_NESTED_COUNT,
1123
REG_ERR_INTERNAL,
1124
REG_ERR_COUNT_FOLLOWS_NOTHING,
1125
REG_ERR_INVALID_ESCAPE,
1126
REG_ERR_CORRUPTED,
1127
REG_ERR_NULL_CHAR,
1128
REG_ERR_UNMATCHED_BRACKET,
1129
REG_ERR_NUM
1130
};
1131
1132
int jim_regcomp(regex_t *preg, const char *regex, int cflags);
1133
int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1134
size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
1135
void jim_regfree(regex_t *preg);
1136
1137
#ifdef __cplusplus
1138
}
1139
#endif
1140
1141
#endif
1142
#ifndef JIM_SIGNAL_H
1143
#define JIM_SIGNAL_H
1144
1145
#ifdef __cplusplus
1146
extern "C" {
1147
#endif
1148
1149
const char *Jim_SignalId(int sig);
1150
1151
#ifdef __cplusplus
1152
}
1153
#endif
1154
1155
#endif
1156
#ifndef JIMIOCOMPAT_H
1157
#define JIMIOCOMPAT_H
1158
1159
1160
#include <stdio.h>
1161
#include <errno.h>
1162
#include <sys/stat.h>
1163
1164
1165
void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
1166
1167
int Jim_OpenForWrite(const char *filename, int append);
1168
1169
int Jim_OpenForRead(const char *filename);
1170
1171
#if defined(__MINGW32__)
1172
#ifndef STRICT
1173
#define STRICT
1174
#endif
1175
#define WIN32_LEAN_AND_MEAN
1176
#include <windows.h>
1177
#include <fcntl.h>
1178
#include <io.h>
1179
#include <process.h>
1180
1181
typedef HANDLE phandle_t;
1182
#define JIM_BAD_PHANDLE INVALID_HANDLE_VALUE
1183
1184
1185
#define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
1186
#define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
1187
#define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
1188
#define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
1189
#define WNOHANG 1
1190
1191
int Jim_Errno(void);
1192
1193
long waitpid(phandle_t phandle, int *status, int nohang);
1194
1195
phandle_t JimWaitPid(long processid, int *status, int nohang);
1196
1197
long JimProcessPid(phandle_t phandle);
1198
1199
#define HAVE_PIPE
1200
#define pipe(P) _pipe((P), 0, O_NOINHERIT)
1201
1202
typedef struct __stat64 jim_stat_t;
1203
#define Jim_Stat _stat64
1204
#define Jim_FileStat _fstat64
1205
#define Jim_Lseek _lseeki64
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
#endif
1249
1250
#ifndef O_TEXT
1251
#define O_TEXT 0
1252
#endif
1253
1254
1255
int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb);
1256
1257
#endif
1258
int Jim_bootstrapInit(Jim_Interp *interp)
1259
{
1260
if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1261
return JIM_ERR;
1262
1263
return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1264
"\n"
1265
"proc package {cmd args} {\n"
1266
" if {$cmd eq \"require\"} {\n"
1267
" foreach path $::auto_path {\n"
1268
" lassign $args pkg\n"
1269
" set pkgpath $path/$pkg.tcl\n"
1270
" if {$path eq \".\"} {\n"
1271
" set pkgpath $pkg.tcl\n"
1272
" }\n"
1273
" if {[file exists $pkgpath]} {\n"
1274
" tailcall uplevel #0 [list source $pkgpath]\n"
1275
" }\n"
1276
" }\n"
1277
" }\n"
1278
"}\n"
1279
"set tcl_platform(bootstrap) 1\n"
1280
);
1281
}
1282
int Jim_initjimshInit(Jim_Interp *interp)
1283
{
1284
if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1285
return JIM_ERR;
1286
1287
return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1288
"\n"
1289
"\n"
1290
"\n"
1291
"proc _jimsh_init {} {\n"
1292
" rename _jimsh_init {}\n"
1293
" global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1294
"\n"
1295
"\n"
1296
" if {[exists jim::argv0]} {\n"
1297
" if {[string match \"*/*\" $jim::argv0]} {\n"
1298
" set jim::exe [file join [pwd] $jim::argv0]\n"
1299
" } else {\n"
1300
" foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1301
" set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1302
" if {[file executable $exec]} {\n"
1303
" set jim::exe $exec\n"
1304
" break\n"
1305
" }\n"
1306
" }\n"
1307
" }\n"
1308
" }\n"
1309
"\n"
1310
"\n"
1311
" lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1312
" if {[exists jim::exe]} {\n"
1313
" lappend p [file dirname $jim::exe]\n"
1314
" }\n"
1315
" lappend p {*}$auto_path\n"
1316
" set auto_path $p\n"
1317
"\n"
1318
" if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1319
" foreach src {.jimrc jimrc.tcl} {\n"
1320
" if {[file exists [env HOME]/$src]} {\n"
1321
" uplevel #0 source [env HOME]/$src\n"
1322
" break\n"
1323
" }\n"
1324
" }\n"
1325
" }\n"
1326
" return \"\"\n"
1327
"}\n"
1328
"\n"
1329
"if {$tcl_platform(platform) eq \"windows\"} {\n"
1330
" set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1331
"}\n"
1332
"\n"
1333
"\n"
1334
"set tcl::autocomplete_commands {array clock debug dict file history info namespace package signal socket string tcl::prefix zlib}\n"
1335
"\n"
1336
"\n"
1337
"\n"
1338
"proc tcl::autocomplete {prefix} {\n"
1339
" if {[set space [string first \" \" $prefix]] != -1} {\n"
1340
" set cmd [string range $prefix 0 $space-1]\n"
1341
" if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
1342
" set arg [string range $prefix $space+1 end]\n"
1343
"\n"
1344
" return [lmap p [$cmd -commands] {\n"
1345
" if {![string match \"${arg}*\" $p]} continue\n"
1346
" function \"$cmd $p\"\n"
1347
" }]\n"
1348
" }\n"
1349
" }\n"
1350
"\n"
1351
" if {[string match \"source *\" $prefix]} {\n"
1352
" set path [string range $prefix 7 end]\n"
1353
" return [lmap p [glob -nocomplain \"${path}*\"] {\n"
1354
" function \"source $p\"\n"
1355
" }]\n"
1356
" }\n"
1357
"\n"
1358
" return [lmap p [lsort [info commands $prefix*]] {\n"
1359
" if {[string match \"* *\" $p]} {\n"
1360
" continue\n"
1361
" }\n"
1362
" function $p\n"
1363
" }]\n"
1364
"}\n"
1365
"\n"
1366
"\n"
1367
"set tcl::stdhint_commands {array clock debug dict file history info namespace package signal string zlib}\n"
1368
"\n"
1369
"set tcl::stdhint_cols {\n"
1370
" none {0}\n"
1371
" black {30}\n"
1372
" red {31}\n"
1373
" green {32}\n"
1374
" yellow {33}\n"
1375
" blue {34}\n"
1376
" purple {35}\n"
1377
" cyan {36}\n"
1378
" normal {37}\n"
1379
" grey {30 1}\n"
1380
" gray {30 1}\n"
1381
" lred {31 1}\n"
1382
" lgreen {32 1}\n"
1383
" lyellow {33 1}\n"
1384
" lblue {34 1}\n"
1385
" lpurple {35 1}\n"
1386
" lcyan {36 1}\n"
1387
" white {37 1}\n"
1388
"}\n"
1389
"\n"
1390
"\n"
1391
"set tcl::stdhint_col $tcl::stdhint_cols(lcyan)\n"
1392
"\n"
1393
"\n"
1394
"proc tcl::stdhint {string} {\n"
1395
" set result \"\"\n"
1396
" if {[llength $string] >= 2} {\n"
1397
" lassign $string cmd arg\n"
1398
" if {$cmd in $::tcl::stdhint_commands || [info channel $cmd] ne \"\"} {\n"
1399
" catch {\n"
1400
" set help [$cmd -help $arg]\n"
1401
" if {[string match \"Usage: $cmd *\" $help]} {\n"
1402
" set n [llength $string]\n"
1403
" set subcmd [lindex $help $n]\n"
1404
" incr n\n"
1405
" set hint [join [lrange $help $n end]]\n"
1406
" set prefix \"\"\n"
1407
" if {![string match \"* \" $string]} {\n"
1408
" if {$n == 3 && $subcmd ne $arg} {\n"
1409
"\n"
1410
" set prefix \"[string range $subcmd [string length $arg] end] \"\n"
1411
" } else {\n"
1412
" set prefix \" \"\n"
1413
" }\n"
1414
" }\n"
1415
" set result [list $prefix$hint {*}$::tcl::stdhint_col]\n"
1416
" }\n"
1417
" }\n"
1418
" }\n"
1419
" }\n"
1420
" return $result\n"
1421
"}\n"
1422
"\n"
1423
"_jimsh_init\n"
1424
);
1425
}
1426
int Jim_globInit(Jim_Interp *interp)
1427
{
1428
if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1429
return JIM_ERR;
1430
1431
return Jim_EvalSource(interp, "glob.tcl", 1,
1432
"\n"
1433
"\n"
1434
"\n"
1435
"\n"
1436
"\n"
1437
"\n"
1438
"\n"
1439
"package require readdir\n"
1440
"\n"
1441
"\n"
1442
"proc glob.globdir {dir pattern} {\n"
1443
" if {[file exists $dir/$pattern]} {\n"
1444
"\n"
1445
" return [list $pattern]\n"
1446
" }\n"
1447
"\n"
1448
" set result {}\n"
1449
" set files [readdir $dir]\n"
1450
" lappend files . ..\n"
1451
"\n"
1452
" foreach name $files {\n"
1453
" if {[string match $pattern $name]} {\n"
1454
"\n"
1455
" if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1456
" continue\n"
1457
" }\n"
1458
" lappend result $name\n"
1459
" }\n"
1460
" }\n"
1461
"\n"
1462
" return $result\n"
1463
"}\n"
1464
"\n"
1465
"\n"
1466
"\n"
1467
"\n"
1468
"proc glob.explode {pattern} {\n"
1469
" set oldexp {}\n"
1470
" set newexp {\"\"}\n"
1471
"\n"
1472
" while 1 {\n"
1473
" set oldexp $newexp\n"
1474
" set newexp {}\n"
1475
" set ob [string first \\{ $pattern]\n"
1476
" set cb [string first \\} $pattern]\n"
1477
"\n"
1478
" if {$ob < $cb && $ob != -1} {\n"
1479
" set mid [string range $pattern 0 $ob-1]\n"
1480
" set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1481
" if {$pattern eq \"\"} {\n"
1482
" error \"unmatched open brace in glob pattern\"\n"
1483
" }\n"
1484
" set pattern [string range $pattern 1 end]\n"
1485
"\n"
1486
" foreach subs $subexp {\n"
1487
" foreach sub [split $subs ,] {\n"
1488
" foreach old $oldexp {\n"
1489
" lappend newexp $old$mid$sub\n"
1490
" }\n"
1491
" }\n"
1492
" }\n"
1493
" } elseif {$cb != -1} {\n"
1494
" set suf [string range $pattern 0 $cb-1]\n"
1495
" set rest [string range $pattern $cb end]\n"
1496
" break\n"
1497
" } else {\n"
1498
" set suf $pattern\n"
1499
" set rest \"\"\n"
1500
" break\n"
1501
" }\n"
1502
" }\n"
1503
"\n"
1504
" foreach old $oldexp {\n"
1505
" lappend newexp $old$suf\n"
1506
" }\n"
1507
" list $rest {*}$newexp\n"
1508
"}\n"
1509
"\n"
1510
"\n"
1511
"\n"
1512
"proc glob.glob {base pattern} {\n"
1513
" set dir [file dirname $pattern]\n"
1514
" if {$pattern eq $dir || $pattern eq \"\"} {\n"
1515
" return [list [file join $base $dir] $pattern]\n"
1516
" } elseif {$pattern eq [file tail $pattern]} {\n"
1517
" set dir \"\"\n"
1518
" }\n"
1519
"\n"
1520
"\n"
1521
" set dirlist [glob.glob $base $dir]\n"
1522
" set pattern [file tail $pattern]\n"
1523
"\n"
1524
"\n"
1525
" set result {}\n"
1526
" foreach {realdir dir} $dirlist {\n"
1527
" if {![file isdir $realdir]} {\n"
1528
" continue\n"
1529
" }\n"
1530
" if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1531
" append dir /\n"
1532
" }\n"
1533
" foreach name [glob.globdir $realdir $pattern] {\n"
1534
" lappend result [file join $realdir $name] $dir$name\n"
1535
" }\n"
1536
" }\n"
1537
" return $result\n"
1538
"}\n"
1539
"\n"
1540
"\n"
1541
"\n"
1542
"\n"
1543
"\n"
1544
"\n"
1545
"\n"
1546
"\n"
1547
"\n"
1548
"\n"
1549
"\n"
1550
"\n"
1551
"proc glob {args} {\n"
1552
" set nocomplain 0\n"
1553
" set base \"\"\n"
1554
" set tails 0\n"
1555
"\n"
1556
" set n 0\n"
1557
" foreach arg $args {\n"
1558
" if {[info exists param]} {\n"
1559
" set $param $arg\n"
1560
" unset param\n"
1561
" incr n\n"
1562
" continue\n"
1563
" }\n"
1564
" switch -glob -- $arg {\n"
1565
" -d* {\n"
1566
" set switch $arg\n"
1567
" set param base\n"
1568
" }\n"
1569
" -n* {\n"
1570
" set nocomplain 1\n"
1571
" }\n"
1572
" -ta* {\n"
1573
" set tails 1\n"
1574
" }\n"
1575
" -- {\n"
1576
" incr n\n"
1577
" break\n"
1578
" }\n"
1579
" -* {\n"
1580
" return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1581
" }\n"
1582
" * {\n"
1583
" break\n"
1584
" }\n"
1585
" }\n"
1586
" incr n\n"
1587
" }\n"
1588
" if {[info exists param]} {\n"
1589
" return -code error \"missing argument to \\\"$switch\\\"\"\n"
1590
" }\n"
1591
" if {[llength $args] <= $n} {\n"
1592
" return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1593
" }\n"
1594
"\n"
1595
" set args [lrange $args $n end]\n"
1596
"\n"
1597
" set result {}\n"
1598
" foreach pattern $args {\n"
1599
" set escpattern [string map {\n"
1600
" \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1601
" } $pattern]\n"
1602
" set patexps [lassign [glob.explode $escpattern] rest]\n"
1603
" if {$rest ne \"\"} {\n"
1604
" return -code error \"unmatched close brace in glob pattern\"\n"
1605
" }\n"
1606
" foreach patexp $patexps {\n"
1607
" set patexp [string map {\n"
1608
" \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1609
" } $patexp]\n"
1610
" foreach {realname name} [glob.glob $base $patexp] {\n"
1611
" incr n\n"
1612
" if {$tails} {\n"
1613
" lappend result $name\n"
1614
" } else {\n"
1615
" lappend result [file join $base $name]\n"
1616
" }\n"
1617
" }\n"
1618
" }\n"
1619
" }\n"
1620
"\n"
1621
" if {!$nocomplain && [llength $result] == 0} {\n"
1622
" set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1623
" return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1624
" }\n"
1625
"\n"
1626
" return $result\n"
1627
"}\n"
1628
);
1629
}
1630
int Jim_stdlibInit(Jim_Interp *interp)
1631
{
1632
if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1633
return JIM_ERR;
1634
1635
return Jim_EvalSource(interp, "stdlib.tcl", 1,
1636
"\n"
1637
"\n"
1638
"if {![exists -command ref]} {\n"
1639
"\n"
1640
" proc ref {args} {{count 0}} {\n"
1641
" format %08x [incr count]\n"
1642
" }\n"
1643
"}\n"
1644
"\n"
1645
"\n"
1646
"proc lambda {arglist args} {\n"
1647
" tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1648
"}\n"
1649
"\n"
1650
"proc lambda.finalizer {name val} {\n"
1651
" rename $name {}\n"
1652
"}\n"
1653
"\n"
1654
"\n"
1655
"proc curry {args} {\n"
1656
" alias [ref {} function lambda.finalizer] {*}$args\n"
1657
"}\n"
1658
"\n"
1659
"\n"
1660
"\n"
1661
"\n"
1662
"\n"
1663
"\n"
1664
"\n"
1665
"\n"
1666
"\n"
1667
"proc function {value} {\n"
1668
" return $value\n"
1669
"}\n"
1670
"\n"
1671
"\n"
1672
"proc stackdump {stacktrace} {\n"
1673
" set lines {}\n"
1674
" lappend lines \"Traceback (most recent call last):\"\n"
1675
" foreach {cmd l f p} [lreverse $stacktrace] {\n"
1676
" set line {}\n"
1677
" if {$f ne \"\"} {\n"
1678
" append line \" File \\\"$f\\\", line $l\"\n"
1679
" }\n"
1680
" if {$p ne \"\"} {\n"
1681
" append line \", in $p\"\n"
1682
" }\n"
1683
" if {$line ne \"\"} {\n"
1684
" lappend lines $line\n"
1685
" if {$cmd ne \"\"} {\n"
1686
" set nl [string first \\n $cmd 1]\n"
1687
" if {$nl >= 0} {\n"
1688
" set cmd [string range $cmd 0 $nl-1]...\n"
1689
" }\n"
1690
" lappend lines \" $cmd\"\n"
1691
" }\n"
1692
" }\n"
1693
" }\n"
1694
" if {[llength $lines] > 1} {\n"
1695
" return [join $lines \\n]\n"
1696
" }\n"
1697
"}\n"
1698
"\n"
1699
"\n"
1700
"\n"
1701
"proc defer {script} {\n"
1702
" upvar jim::defer v\n"
1703
" lappend v $script\n"
1704
"}\n"
1705
"\n"
1706
"\n"
1707
"\n"
1708
"proc errorInfo {msg {stacktrace \"\"}} {\n"
1709
" if {$stacktrace eq \"\"} {\n"
1710
"\n"
1711
" set stacktrace [info stacktrace]\n"
1712
" }\n"
1713
" lassign $stacktrace p f l cmd\n"
1714
" if {$f ne \"\"} {\n"
1715
" set result \"$f:$l: Error: \"\n"
1716
" }\n"
1717
" append result \"$msg\\n\"\n"
1718
" append result [stackdump $stacktrace]\n"
1719
"\n"
1720
"\n"
1721
" string trim $result\n"
1722
"}\n"
1723
"\n"
1724
"\n"
1725
"\n"
1726
"proc {info nameofexecutable} {} {\n"
1727
" if {[exists ::jim::exe]} {\n"
1728
" return $::jim::exe\n"
1729
" }\n"
1730
"}\n"
1731
"\n"
1732
"\n"
1733
"proc {dict update} {&varName args script} {\n"
1734
" set keys {}\n"
1735
" foreach {n v} $args {\n"
1736
" upvar $v var_$v\n"
1737
" if {[dict exists $varName $n]} {\n"
1738
" set var_$v [dict get $varName $n]\n"
1739
" }\n"
1740
" }\n"
1741
" catch {uplevel 1 $script} msg opts\n"
1742
" if {[info exists varName]} {\n"
1743
" foreach {n v} $args {\n"
1744
" if {[info exists var_$v]} {\n"
1745
" dict set varName $n [set var_$v]\n"
1746
" } else {\n"
1747
" dict unset varName $n\n"
1748
" }\n"
1749
" }\n"
1750
" }\n"
1751
" return {*}$opts $msg\n"
1752
"}\n"
1753
"\n"
1754
"proc {dict replace} {dictionary {args {key value}}} {\n"
1755
" if {[llength ${key value}] % 2} {\n"
1756
" tailcall {dict replace}\n"
1757
" }\n"
1758
" tailcall dict merge $dictionary ${key value}\n"
1759
"}\n"
1760
"\n"
1761
"\n"
1762
"proc {dict lappend} {varName key {args value}} {\n"
1763
" upvar $varName dict\n"
1764
" if {[exists dict] && [dict exists $dict $key]} {\n"
1765
" set list [dict get $dict $key]\n"
1766
" }\n"
1767
" lappend list {*}$value\n"
1768
" dict set dict $key $list\n"
1769
"}\n"
1770
"\n"
1771
"\n"
1772
"proc {dict append} {varName key {args value}} {\n"
1773
" upvar $varName dict\n"
1774
" if {[exists dict] && [dict exists $dict $key]} {\n"
1775
" set str [dict get $dict $key]\n"
1776
" }\n"
1777
" append str {*}$value\n"
1778
" dict set dict $key $str\n"
1779
"}\n"
1780
"\n"
1781
"\n"
1782
"proc {dict incr} {varName key {increment 1}} {\n"
1783
" upvar $varName dict\n"
1784
" if {[exists dict] && [dict exists $dict $key]} {\n"
1785
" set value [dict get $dict $key]\n"
1786
" }\n"
1787
" incr value $increment\n"
1788
" dict set dict $key $value\n"
1789
"}\n"
1790
"\n"
1791
"\n"
1792
"proc {dict remove} {dictionary {args key}} {\n"
1793
" foreach k $key {\n"
1794
" dict unset dictionary $k\n"
1795
" }\n"
1796
" return $dictionary\n"
1797
"}\n"
1798
"\n"
1799
"\n"
1800
"proc {dict for} {vars dictionary script} {\n"
1801
" if {[llength $vars] != 2} {\n"
1802
" return -code error \"must have exactly two variable names\"\n"
1803
" }\n"
1804
" dict size $dictionary\n"
1805
" tailcall foreach $vars $dictionary $script\n"
1806
"}\n"
1807
);
1808
}
1809
int Jim_tclcompatInit(Jim_Interp *interp)
1810
{
1811
if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1812
return JIM_ERR;
1813
1814
return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1815
"\n"
1816
"\n"
1817
"\n"
1818
"\n"
1819
"\n"
1820
"\n"
1821
"\n"
1822
"\n"
1823
"set env [env]\n"
1824
"\n"
1825
"\n"
1826
"if {[exists -command stdout]} {\n"
1827
"\n"
1828
" foreach p {gets flush close eof seek tell} {\n"
1829
" proc $p {chan args} {p} {\n"
1830
" tailcall $chan $p {*}$args\n"
1831
" }\n"
1832
" }\n"
1833
" unset p\n"
1834
"\n"
1835
"\n"
1836
"\n"
1837
" proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1838
" if {${-nonewline} ni {-nonewline {}}} {\n"
1839
" tailcall ${-nonewline} puts $msg\n"
1840
" }\n"
1841
" tailcall $chan puts {*}${-nonewline} $msg\n"
1842
" }\n"
1843
"\n"
1844
"\n"
1845
"\n"
1846
"\n"
1847
"\n"
1848
" proc read {{-nonewline {}} chan} {\n"
1849
" if {${-nonewline} ni {-nonewline {}}} {\n"
1850
" tailcall ${-nonewline} read {*}${chan}\n"
1851
" }\n"
1852
" tailcall $chan read {*}${-nonewline}\n"
1853
" }\n"
1854
"\n"
1855
" proc fconfigure {f args} {\n"
1856
" foreach {n v} $args {\n"
1857
" switch -glob -- $n {\n"
1858
" -bl* {\n"
1859
" $f ndelay $(!$v)\n"
1860
" }\n"
1861
" -bu* {\n"
1862
" $f buffering $v\n"
1863
" }\n"
1864
" -tr* {\n"
1865
"\n"
1866
" }\n"
1867
" default {\n"
1868
" return -code error \"fconfigure: unknown option $n\"\n"
1869
" }\n"
1870
" }\n"
1871
" }\n"
1872
" }\n"
1873
"}\n"
1874
"\n"
1875
"\n"
1876
"proc fileevent {args} {\n"
1877
" tailcall {*}$args\n"
1878
"}\n"
1879
"\n"
1880
"\n"
1881
"\n"
1882
"proc parray {arrayname {pattern *} {puts puts}} {\n"
1883
" upvar $arrayname a\n"
1884
"\n"
1885
" set max 0\n"
1886
" foreach name [array names a $pattern]] {\n"
1887
" if {[string length $name] > $max} {\n"
1888
" set max [string length $name]\n"
1889
" }\n"
1890
" }\n"
1891
" incr max [string length $arrayname]\n"
1892
" incr max 2\n"
1893
" foreach name [lsort [array names a $pattern]] {\n"
1894
" $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1895
" }\n"
1896
"}\n"
1897
"\n"
1898
"\n"
1899
"proc {file copy} {{force {}} source target} {\n"
1900
" try {\n"
1901
" if {$force ni {{} -force}} {\n"
1902
" error \"bad option \\\"$force\\\": should be -force\"\n"
1903
" }\n"
1904
"\n"
1905
" set in [open $source rb]\n"
1906
"\n"
1907
" if {[file exists $target]} {\n"
1908
" if {$force eq \"\"} {\n"
1909
" error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1910
" }\n"
1911
"\n"
1912
" if {$source eq $target} {\n"
1913
" return\n"
1914
" }\n"
1915
"\n"
1916
"\n"
1917
" file stat $source ss\n"
1918
" file stat $target ts\n"
1919
" if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1920
" return\n"
1921
" }\n"
1922
" }\n"
1923
" set out [open $target wb]\n"
1924
" $in copyto $out\n"
1925
" $out close\n"
1926
" } on error {msg opts} {\n"
1927
" incr opts(-level)\n"
1928
" return {*}$opts $msg\n"
1929
" } finally {\n"
1930
" catch {$in close}\n"
1931
" }\n"
1932
"}\n"
1933
"\n"
1934
"\n"
1935
"\n"
1936
"proc popen {cmd {mode r}} {\n"
1937
" lassign [pipe] r w\n"
1938
" try {\n"
1939
" if {[string match \"w*\" $mode]} {\n"
1940
" lappend cmd <@$r &\n"
1941
" set pids [exec {*}$cmd]\n"
1942
" $r close\n"
1943
" set f $w\n"
1944
" } else {\n"
1945
" lappend cmd >@$w &\n"
1946
" set pids [exec {*}$cmd]\n"
1947
" $w close\n"
1948
" set f $r\n"
1949
" }\n"
1950
" lambda {cmd args} {f pids} {\n"
1951
" if {$cmd eq \"pid\"} {\n"
1952
" return $pids\n"
1953
" }\n"
1954
" if {$cmd eq \"getfd\"} {\n"
1955
" $f getfd\n"
1956
" }\n"
1957
" if {$cmd eq \"close\"} {\n"
1958
" $f close\n"
1959
"\n"
1960
" set retopts {}\n"
1961
" foreach p $pids {\n"
1962
" lassign [wait $p] status - rc\n"
1963
" if {$status eq \"CHILDSTATUS\"} {\n"
1964
" if {$rc == 0} {\n"
1965
" continue\n"
1966
" }\n"
1967
" set msg \"child process exited abnormally\"\n"
1968
" } else {\n"
1969
" set msg \"child killed: received signal\"\n"
1970
" }\n"
1971
" set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n"
1972
" }\n"
1973
" return {*}$retopts\n"
1974
" }\n"
1975
" tailcall $f $cmd {*}$args\n"
1976
" }\n"
1977
" } on error {error opts} {\n"
1978
" $r close\n"
1979
" $w close\n"
1980
" error $error\n"
1981
" }\n"
1982
"}\n"
1983
"\n"
1984
"\n"
1985
"local proc pid {{channelId {}}} {\n"
1986
" if {$channelId eq \"\"} {\n"
1987
" tailcall upcall pid\n"
1988
" }\n"
1989
" if {[catch {$channelId tell}]} {\n"
1990
" return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
1991
" }\n"
1992
" if {[catch {$channelId pid} pids]} {\n"
1993
" return \"\"\n"
1994
" }\n"
1995
" return $pids\n"
1996
"}\n"
1997
"\n"
1998
"\n"
1999
"\n"
2000
"proc throw {code {msg \"\"}} {\n"
2001
" return -code $code $msg\n"
2002
"}\n"
2003
"\n"
2004
"\n"
2005
"proc {file delete force} {path} {\n"
2006
" foreach e [readdir $path] {\n"
2007
" file delete -force $path/$e\n"
2008
" }\n"
2009
" file delete $path\n"
2010
"}\n"
2011
);
2012
}
2013
2014
2015
#include <stdio.h>
2016
#include <string.h>
2017
#include <errno.h>
2018
#include <fcntl.h>
2019
#include <assert.h>
2020
#ifdef HAVE_UNISTD_H
2021
#include <unistd.h>
2022
#include <sys/stat.h>
2023
#endif
2024
#ifdef HAVE_UTIL_H
2025
#include <util.h>
2026
#endif
2027
#ifdef HAVE_PTY_H
2028
#include <pty.h>
2029
#endif
2030
2031
2032
#if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
2033
#include <sys/socket.h>
2034
#include <netinet/in.h>
2035
#include <netinet/tcp.h>
2036
#include <arpa/inet.h>
2037
#include <netdb.h>
2038
#ifdef HAVE_SYS_UN_H
2039
#include <sys/un.h>
2040
#endif
2041
#define HAVE_SOCKETS
2042
#elif defined (__MINGW32__)
2043
2044
#endif
2045
2046
#if defined(JIM_SSL)
2047
#include <openssl/ssl.h>
2048
#include <openssl/err.h>
2049
#endif
2050
2051
#ifdef HAVE_TERMIOS_H
2052
#endif
2053
2054
2055
#define AIO_CMD_LEN 32
2056
#define AIO_BUF_LEN 256
2057
#define AIO_WBUF_FULL_SIZE (64 * 1024)
2058
2059
#define AIO_KEEPOPEN 1
2060
#define AIO_NODELETE 2
2061
#define AIO_EOF 4
2062
#define AIO_WBUF_NONE 8
2063
#define AIO_NONBLOCK 16
2064
2065
enum wbuftype {
2066
WBUF_OPT_NONE,
2067
WBUF_OPT_LINE,
2068
WBUF_OPT_FULL,
2069
};
2070
2071
#if defined(JIM_IPV6)
2072
#define IPV6 1
2073
#else
2074
#define IPV6 0
2075
#ifndef PF_INET6
2076
#define PF_INET6 0
2077
#endif
2078
#endif
2079
#if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
2080
#define UNIX_SOCKETS 1
2081
#else
2082
#define UNIX_SOCKETS 0
2083
#endif
2084
2085
#ifndef MAXPATHLEN
2086
#define MAXPATHLEN JIM_PATH_LEN
2087
#endif
2088
2089
2090
2091
2092
static int JimReadableTimeout(int fd, long ms)
2093
{
2094
#ifdef HAVE_SELECT
2095
int retval;
2096
struct timeval tv;
2097
fd_set rfds;
2098
2099
FD_ZERO(&rfds);
2100
2101
FD_SET(fd, &rfds);
2102
tv.tv_sec = ms / 1000;
2103
tv.tv_usec = (ms % 1000) * 1000;
2104
2105
retval = select(fd + 1, &rfds, NULL, NULL, ms == 0 ? NULL : &tv);
2106
2107
if (retval > 0) {
2108
return JIM_OK;
2109
}
2110
return JIM_ERR;
2111
#else
2112
return JIM_OK;
2113
#endif
2114
}
2115
2116
2117
struct AioFile;
2118
2119
typedef struct {
2120
int (*writer)(struct AioFile *af, const char *buf, int len);
2121
int (*reader)(struct AioFile *af, char *buf, int len, int pending);
2122
int (*error)(const struct AioFile *af);
2123
const char *(*strerror)(struct AioFile *af);
2124
int (*verify)(struct AioFile *af);
2125
} JimAioFopsType;
2126
2127
typedef struct AioFile
2128
{
2129
Jim_Obj *filename;
2130
int wbuft;
2131
int flags;
2132
long timeout;
2133
int fd;
2134
int addr_family;
2135
void *ssl;
2136
const JimAioFopsType *fops;
2137
Jim_Obj *readbuf;
2138
Jim_Obj *writebuf;
2139
} AioFile;
2140
2141
static int stdio_writer(struct AioFile *af, const char *buf, int len)
2142
{
2143
return write(af->fd, buf, len);
2144
}
2145
2146
static int stdio_reader(struct AioFile *af, char *buf, int len, int nb)
2147
{
2148
if (nb || af->timeout == 0 || JimReadableTimeout(af->fd, af->timeout) == JIM_OK) {
2149
2150
int ret;
2151
2152
errno = 0;
2153
ret = read(af->fd, buf, len);
2154
if (ret <= 0 && errno != EAGAIN && errno != EINTR) {
2155
af->flags |= AIO_EOF;
2156
}
2157
return ret;
2158
}
2159
errno = ETIMEDOUT;
2160
return -1;
2161
}
2162
2163
static int stdio_error(const AioFile *af)
2164
{
2165
if (af->flags & AIO_EOF) {
2166
return JIM_OK;
2167
}
2168
2169
switch (errno) {
2170
case EAGAIN:
2171
case EINTR:
2172
case ETIMEDOUT:
2173
#ifdef ECONNRESET
2174
case ECONNRESET:
2175
#endif
2176
#ifdef ECONNABORTED
2177
case ECONNABORTED:
2178
#endif
2179
return JIM_OK;
2180
default:
2181
return JIM_ERR;
2182
}
2183
}
2184
2185
static const char *stdio_strerror(struct AioFile *af)
2186
{
2187
return strerror(errno);
2188
}
2189
2190
static const JimAioFopsType stdio_fops = {
2191
stdio_writer,
2192
stdio_reader,
2193
stdio_error,
2194
stdio_strerror,
2195
NULL,
2196
};
2197
2198
2199
static void aio_set_nonblocking(AioFile *af, int nb)
2200
{
2201
#ifdef O_NDELAY
2202
int old = !!(af->flags & AIO_NONBLOCK);
2203
if (old != nb) {
2204
int fmode = fcntl(af->fd, F_GETFL);
2205
if (nb) {
2206
fmode |= O_NDELAY;
2207
af->flags |= AIO_NONBLOCK;
2208
}
2209
else {
2210
fmode &= ~O_NDELAY;
2211
af->flags &= ~AIO_NONBLOCK;
2212
}
2213
(void)fcntl(af->fd, F_SETFL, fmode);
2214
}
2215
#endif
2216
}
2217
2218
static int aio_start_nonblocking(AioFile *af)
2219
{
2220
int old = !!(af->flags & AIO_NONBLOCK);
2221
if (af->timeout) {
2222
aio_set_nonblocking(af, 1);
2223
}
2224
return old;
2225
}
2226
2227
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
2228
static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
2229
const char *hdlfmt, int family, int flags);
2230
2231
2232
static const char *JimAioErrorString(AioFile *af)
2233
{
2234
if (af && af->fops)
2235
return af->fops->strerror(af);
2236
2237
return strerror(errno);
2238
}
2239
2240
static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
2241
{
2242
AioFile *af = Jim_CmdPrivData(interp);
2243
2244
if (name) {
2245
Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
2246
}
2247
else {
2248
Jim_SetResultString(interp, JimAioErrorString(af), -1);
2249
}
2250
}
2251
2252
static int aio_eof(AioFile *af)
2253
{
2254
return af->flags & AIO_EOF;
2255
}
2256
2257
static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
2258
{
2259
int ret = 0;
2260
if (!aio_eof(af)) {
2261
ret = af->fops->error(af);
2262
if (ret) {
2263
JimAioSetError(interp, af->filename);
2264
}
2265
}
2266
return ret;
2267
}
2268
2269
static void aio_consume(Jim_Obj *objPtr, int n)
2270
{
2271
assert(objPtr->bytes);
2272
assert(n <= objPtr->length);
2273
2274
2275
memmove(objPtr->bytes, objPtr->bytes + n, objPtr->length - n + 1);
2276
objPtr->length -= n;
2277
}
2278
2279
2280
static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask);
2281
2282
static int aio_flush(Jim_Interp *interp, AioFile *af)
2283
{
2284
int len;
2285
const char *pt = Jim_GetString(af->writebuf, &len);
2286
if (len) {
2287
int ret = af->fops->writer(af, pt, len);
2288
if (ret > 0) {
2289
2290
aio_consume(af->writebuf, ret);
2291
}
2292
if (ret < 0) {
2293
return JimCheckStreamError(interp, af);
2294
}
2295
if (Jim_Length(af->writebuf)) {
2296
#ifdef jim_ext_eventloop
2297
void *handler = Jim_FindFileHandler(interp, af->fd, JIM_EVENT_WRITABLE);
2298
if (handler == NULL) {
2299
Jim_CreateFileHandler(interp, af->fd, JIM_EVENT_WRITABLE, aio_autoflush, af, NULL);
2300
return JIM_OK;
2301
}
2302
else if (handler == af) {
2303
2304
return JIM_OK;
2305
}
2306
#endif
2307
2308
Jim_SetResultString(interp, "send buffer is full", -1);
2309
return JIM_ERR;
2310
}
2311
}
2312
return JIM_OK;
2313
}
2314
2315
static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask)
2316
{
2317
AioFile *af = clientData;
2318
2319
aio_flush(interp, af);
2320
if (Jim_Length(af->writebuf) == 0) {
2321
2322
return -1;
2323
}
2324
return 0;
2325
}
2326
2327
static int aio_read_len(Jim_Interp *interp, AioFile *af, int nb, char *buf, size_t buflen, int neededLen)
2328
{
2329
if (!af->readbuf) {
2330
af->readbuf = Jim_NewStringObj(interp, NULL, 0);
2331
}
2332
2333
if (neededLen >= 0) {
2334
neededLen -= Jim_Length(af->readbuf);
2335
if (neededLen <= 0) {
2336
return JIM_OK;
2337
}
2338
}
2339
2340
while (neededLen && !aio_eof(af)) {
2341
int retval;
2342
int readlen;
2343
2344
if (neededLen == -1) {
2345
readlen = AIO_BUF_LEN;
2346
}
2347
else {
2348
readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
2349
}
2350
retval = af->fops->reader(af, buf, readlen, nb);
2351
if (retval > 0) {
2352
Jim_AppendString(interp, af->readbuf, buf, retval);
2353
if (neededLen != -1) {
2354
neededLen -= retval;
2355
}
2356
continue;
2357
}
2358
if (JimCheckStreamError(interp, af)) {
2359
return JIM_ERR;
2360
}
2361
break;
2362
}
2363
2364
return JIM_OK;
2365
}
2366
2367
static Jim_Obj *aio_read_consume(Jim_Interp *interp, AioFile *af, int neededLen)
2368
{
2369
Jim_Obj *objPtr = NULL;
2370
2371
if (neededLen < 0 || af->readbuf == NULL || Jim_Length(af->readbuf) <= neededLen) {
2372
objPtr = af->readbuf;
2373
af->readbuf = NULL;
2374
}
2375
else if (af->readbuf) {
2376
2377
int len;
2378
const char *pt = Jim_GetString(af->readbuf, &len);
2379
2380
objPtr = Jim_NewStringObj(interp, pt, neededLen);
2381
aio_consume(af->readbuf, neededLen);
2382
}
2383
2384
return objPtr;
2385
}
2386
2387
static void JimAioDelProc(Jim_Interp *interp, void *privData)
2388
{
2389
AioFile *af = privData;
2390
2391
JIM_NOTUSED(interp);
2392
2393
2394
aio_flush(interp, af);
2395
Jim_DecrRefCount(interp, af->writebuf);
2396
2397
#if UNIX_SOCKETS
2398
if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) {
2399
2400
Jim_Obj *filenameObj = aio_sockname(interp, af->fd);
2401
if (filenameObj) {
2402
if (Jim_Length(filenameObj)) {
2403
remove(Jim_String(filenameObj));
2404
}
2405
Jim_FreeNewObj(interp, filenameObj);
2406
}
2407
}
2408
#endif
2409
2410
Jim_DecrRefCount(interp, af->filename);
2411
2412
#ifdef jim_ext_eventloop
2413
2414
Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
2415
#endif
2416
2417
#if defined(JIM_SSL)
2418
if (af->ssl != NULL) {
2419
SSL_free(af->ssl);
2420
}
2421
#endif
2422
if (!(af->flags & AIO_KEEPOPEN)) {
2423
close(af->fd);
2424
}
2425
if (af->readbuf) {
2426
Jim_FreeNewObj(interp, af->readbuf);
2427
}
2428
2429
Jim_Free(af);
2430
}
2431
2432
static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2433
{
2434
AioFile *af = Jim_CmdPrivData(interp);
2435
int nonewline = 0;
2436
jim_wide neededLen = -1;
2437
static const char * const options[] = { "-pending", "-nonewline", NULL };
2438
enum { OPT_PENDING, OPT_NONEWLINE };
2439
int option;
2440
int nb;
2441
Jim_Obj *objPtr;
2442
char buf[AIO_BUF_LEN];
2443
2444
if (argc) {
2445
if (*Jim_String(argv[0]) == '-') {
2446
if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2447
return JIM_ERR;
2448
}
2449
switch (option) {
2450
case OPT_PENDING:
2451
2452
break;
2453
case OPT_NONEWLINE:
2454
nonewline++;
2455
break;
2456
}
2457
}
2458
else {
2459
if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
2460
return JIM_ERR;
2461
if (neededLen < 0) {
2462
Jim_SetResultString(interp, "invalid parameter: negative len", -1);
2463
return JIM_ERR;
2464
}
2465
}
2466
argc--;
2467
argv++;
2468
}
2469
if (argc) {
2470
return -1;
2471
}
2472
2473
2474
nb = aio_start_nonblocking(af);
2475
2476
if (aio_read_len(interp, af, nb, buf, sizeof(buf), neededLen) != JIM_OK) {
2477
aio_set_nonblocking(af, nb);
2478
return JIM_ERR;
2479
}
2480
objPtr = aio_read_consume(interp, af, neededLen);
2481
2482
aio_set_nonblocking(af, nb);
2483
2484
if (objPtr) {
2485
if (nonewline) {
2486
int len;
2487
const char *s = Jim_GetString(objPtr, &len);
2488
2489
if (len > 0 && s[len - 1] == '\n') {
2490
objPtr->length--;
2491
objPtr->bytes[objPtr->length] = '\0';
2492
}
2493
}
2494
Jim_SetResult(interp, objPtr);
2495
}
2496
else {
2497
Jim_SetEmptyResult(interp);
2498
}
2499
return JIM_OK;
2500
}
2501
2502
int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2503
{
2504
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2505
2506
2507
if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2508
return ((AioFile *) cmdPtr->u.native.privData)->fd;
2509
}
2510
Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2511
return -1;
2512
}
2513
2514
static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2515
{
2516
AioFile *af = Jim_CmdPrivData(interp);
2517
2518
2519
aio_flush(interp, af);
2520
2521
Jim_SetResultInt(interp, af->fd);
2522
2523
return JIM_OK;
2524
}
2525
2526
static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2527
{
2528
AioFile *af = Jim_CmdPrivData(interp);
2529
jim_wide count = 0;
2530
jim_wide maxlen = JIM_WIDE_MAX;
2531
2532
char buf[AIO_BUF_LEN];
2533
2534
char *bufp = buf;
2535
int buflen = sizeof(buf);
2536
int ok = 1;
2537
Jim_Obj *objv[4];
2538
2539
if (argc == 2) {
2540
if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
2541
return JIM_ERR;
2542
}
2543
}
2544
2545
objv[0] = argv[0];
2546
objv[1] = Jim_NewStringObj(interp, "flush", -1);
2547
if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
2548
Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", argv[0]);
2549
return JIM_ERR;
2550
}
2551
2552
2553
objv[0] = argv[0];
2554
objv[1] = Jim_NewStringObj(interp, "puts", -1);
2555
objv[2] = Jim_NewStringObj(interp, "-nonewline", -1);
2556
Jim_IncrRefCount(objv[1]);
2557
Jim_IncrRefCount(objv[2]);
2558
2559
while (count < maxlen) {
2560
jim_wide len = maxlen - count;
2561
if (len > buflen) {
2562
len = buflen;
2563
}
2564
if (aio_read_len(interp, af, 0, bufp, buflen, len) != JIM_OK) {
2565
ok = 0;
2566
break;
2567
}
2568
objv[3] = aio_read_consume(interp, af, len);
2569
count += Jim_Length(objv[3]);
2570
if (Jim_EvalObjVector(interp, 4, objv) != JIM_OK) {
2571
ok = 0;
2572
break;
2573
}
2574
if (aio_eof(af)) {
2575
break;
2576
}
2577
if (count >= 16384 && bufp == buf) {
2578
2579
buflen = 65536;
2580
bufp = Jim_Alloc(buflen);
2581
}
2582
}
2583
2584
if (bufp != buf) {
2585
Jim_Free(bufp);
2586
}
2587
2588
Jim_DecrRefCount(interp, objv[1]);
2589
Jim_DecrRefCount(interp, objv[2]);
2590
2591
if (!ok) {
2592
return JIM_ERR;
2593
}
2594
2595
Jim_SetResultInt(interp, count);
2596
2597
return JIM_OK;
2598
}
2599
2600
static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2601
{
2602
AioFile *af = Jim_CmdPrivData(interp);
2603
char buf[AIO_BUF_LEN];
2604
Jim_Obj *objPtr = NULL;
2605
int len;
2606
int nb;
2607
char *nl = NULL;
2608
int offset = 0;
2609
2610
errno = 0;
2611
2612
2613
nb = aio_start_nonblocking(af);
2614
2615
if (!af->readbuf) {
2616
af->readbuf = Jim_NewStringObj(interp, NULL, 0);
2617
}
2618
2619
while (!aio_eof(af)) {
2620
const char *pt = Jim_GetString(af->readbuf, &len);
2621
nl = memchr(pt + offset, '\n', len - offset);
2622
if (nl) {
2623
2624
objPtr = Jim_NewStringObj(interp, pt, nl - pt);
2625
2626
aio_consume(af->readbuf, nl - pt + 1);
2627
break;
2628
}
2629
2630
offset = len;
2631
len = af->fops->reader(af, buf, AIO_BUF_LEN, nb);
2632
if (len <= 0) {
2633
break;
2634
}
2635
Jim_AppendString(interp, af->readbuf, buf, len);
2636
}
2637
2638
aio_set_nonblocking(af, nb);
2639
2640
if (!nl && aio_eof(af)) {
2641
2642
objPtr = af->readbuf;
2643
af->readbuf = NULL;
2644
}
2645
else if (!objPtr) {
2646
objPtr = Jim_NewStringObj(interp, NULL, 0);
2647
}
2648
2649
if (argc) {
2650
if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2651
Jim_FreeNewObj(interp, objPtr);
2652
return JIM_ERR;
2653
}
2654
2655
len = Jim_Length(objPtr);
2656
2657
if (!nl && len == 0) {
2658
2659
len = -1;
2660
}
2661
Jim_SetResultInt(interp, len);
2662
}
2663
else {
2664
Jim_SetResult(interp, objPtr);
2665
}
2666
return JIM_OK;
2667
}
2668
2669
static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2670
{
2671
AioFile *af = Jim_CmdPrivData(interp);
2672
int wlen;
2673
const char *wdata;
2674
Jim_Obj *strObj;
2675
int wnow = 0;
2676
int nl = 1;
2677
2678
if (argc == 2) {
2679
if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2680
return -1;
2681
}
2682
strObj = argv[1];
2683
nl = 0;
2684
}
2685
else {
2686
strObj = argv[0];
2687
}
2688
2689
Jim_AppendObj(interp, af->writebuf, strObj);
2690
if (nl) {
2691
Jim_AppendString(interp, af->writebuf, "\n", 1);
2692
}
2693
2694
2695
wdata = Jim_GetString(af->writebuf, &wlen);
2696
switch (af->wbuft) {
2697
case WBUF_OPT_NONE:
2698
2699
wnow = 1;
2700
break;
2701
2702
case WBUF_OPT_LINE:
2703
2704
if (nl || memchr(wdata, '\n', wlen) != NULL) {
2705
wnow = 1;
2706
}
2707
break;
2708
2709
case WBUF_OPT_FULL:
2710
if (wlen >= AIO_WBUF_FULL_SIZE) {
2711
wnow = 1;
2712
}
2713
break;
2714
}
2715
2716
if (wnow) {
2717
return aio_flush(interp, af);
2718
}
2719
return JIM_OK;
2720
}
2721
2722
static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2723
{
2724
#ifdef HAVE_ISATTY
2725
AioFile *af = Jim_CmdPrivData(interp);
2726
Jim_SetResultInt(interp, isatty(af->fd));
2727
#else
2728
Jim_SetResultInt(interp, 0);
2729
#endif
2730
2731
return JIM_OK;
2732
}
2733
2734
2735
static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2736
{
2737
AioFile *af = Jim_CmdPrivData(interp);
2738
return aio_flush(interp, af);
2739
}
2740
2741
static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2742
{
2743
AioFile *af = Jim_CmdPrivData(interp);
2744
2745
Jim_SetResultInt(interp, !!aio_eof(af));
2746
return JIM_OK;
2747
}
2748
2749
static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2750
{
2751
AioFile *af = Jim_CmdPrivData(interp);
2752
if (argc == 3) {
2753
int option = -1;
2754
#if defined(HAVE_SOCKETS)
2755
static const char * const options[] = { "r", "w", "-nodelete", NULL };
2756
enum { OPT_R, OPT_W, OPT_NODELETE };
2757
2758
if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2759
return JIM_ERR;
2760
}
2761
#endif
2762
switch (option) {
2763
#if defined(HAVE_SHUTDOWN)
2764
case OPT_R:
2765
case OPT_W:
2766
if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2767
return JIM_OK;
2768
}
2769
JimAioSetError(interp, NULL);
2770
return JIM_ERR;
2771
#endif
2772
#if UNIX_SOCKETS
2773
case OPT_NODELETE:
2774
if (af->addr_family == PF_UNIX) {
2775
af->flags |= AIO_NODELETE;
2776
break;
2777
}
2778
2779
#endif
2780
default:
2781
Jim_SetResultString(interp, "not supported", -1);
2782
return JIM_ERR;
2783
}
2784
}
2785
2786
2787
af->flags &= ~AIO_KEEPOPEN;
2788
2789
return Jim_DeleteCommand(interp, argv[0]);
2790
}
2791
2792
static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2793
{
2794
AioFile *af = Jim_CmdPrivData(interp);
2795
int orig = SEEK_SET;
2796
jim_wide offset;
2797
2798
if (argc == 2) {
2799
if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2800
orig = SEEK_SET;
2801
else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2802
orig = SEEK_CUR;
2803
else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2804
orig = SEEK_END;
2805
else {
2806
return -1;
2807
}
2808
}
2809
if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2810
return JIM_ERR;
2811
}
2812
if (orig != SEEK_CUR || offset != 0) {
2813
2814
aio_flush(interp, af);
2815
}
2816
if (Jim_Lseek(af->fd, offset, orig) == -1) {
2817
JimAioSetError(interp, af->filename);
2818
return JIM_ERR;
2819
}
2820
if (af->readbuf) {
2821
Jim_FreeNewObj(interp, af->readbuf);
2822
af->readbuf = NULL;
2823
}
2824
af->flags &= ~AIO_EOF;
2825
return JIM_OK;
2826
}
2827
2828
static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2829
{
2830
AioFile *af = Jim_CmdPrivData(interp);
2831
2832
Jim_SetResultInt(interp, Jim_Lseek(af->fd, 0, SEEK_CUR));
2833
return JIM_OK;
2834
}
2835
2836
static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2837
{
2838
AioFile *af = Jim_CmdPrivData(interp);
2839
2840
Jim_SetResult(interp, af->filename);
2841
return JIM_OK;
2842
}
2843
2844
#ifdef O_NDELAY
2845
static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2846
{
2847
AioFile *af = Jim_CmdPrivData(interp);
2848
2849
if (argc) {
2850
long nb;
2851
2852
if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2853
return JIM_ERR;
2854
}
2855
aio_set_nonblocking(af, nb);
2856
}
2857
Jim_SetResultInt(interp, (af->flags & AIO_NONBLOCK) ? 1 : 0);
2858
return JIM_OK;
2859
}
2860
#endif
2861
2862
2863
#ifdef HAVE_FSYNC
2864
static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2865
{
2866
AioFile *af = Jim_CmdPrivData(interp);
2867
2868
if (aio_flush(interp, af) != JIM_OK) {
2869
return JIM_ERR;
2870
}
2871
fsync(af->fd);
2872
return JIM_OK;
2873
}
2874
#endif
2875
2876
static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2877
{
2878
AioFile *af = Jim_CmdPrivData(interp);
2879
2880
static const char * const options[] = {
2881
"none",
2882
"line",
2883
"full",
2884
NULL
2885
};
2886
2887
if (Jim_GetEnum(interp, argv[0], options, &af->wbuft, NULL, JIM_ERRMSG) != JIM_OK) {
2888
return JIM_ERR;
2889
}
2890
2891
if (af->wbuft == WBUF_OPT_NONE) {
2892
return aio_flush(interp, af);
2893
}
2894
2895
return JIM_OK;
2896
}
2897
2898
static int aio_cmd_timeout(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2899
{
2900
#ifdef HAVE_SELECT
2901
AioFile *af = Jim_CmdPrivData(interp);
2902
if (argc == 1) {
2903
if (Jim_GetLong(interp, argv[0], &af->timeout) != JIM_OK) {
2904
return JIM_ERR;
2905
}
2906
}
2907
Jim_SetResultInt(interp, af->timeout);
2908
return JIM_OK;
2909
#else
2910
Jim_SetResultString(interp, "timeout not supported", -1);
2911
return JIM_ERR;
2912
#endif
2913
}
2914
2915
#ifdef jim_ext_eventloop
2916
static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask,
2917
int argc, Jim_Obj * const *argv)
2918
{
2919
if (argc == 0) {
2920
2921
Jim_Obj *objPtr = Jim_FindFileHandler(interp, af->fd, mask);
2922
if (objPtr) {
2923
Jim_SetResult(interp, objPtr);
2924
}
2925
return JIM_OK;
2926
}
2927
2928
2929
Jim_DeleteFileHandler(interp, af->fd, mask);
2930
2931
2932
if (Jim_Length(argv[0])) {
2933
Jim_CreateScriptFileHandler(interp, af->fd, mask, argv[0]);
2934
}
2935
2936
return JIM_OK;
2937
}
2938
2939
static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2940
{
2941
AioFile *af = Jim_CmdPrivData(interp);
2942
2943
return aio_eventinfo(interp, af, JIM_EVENT_READABLE, argc, argv);
2944
}
2945
2946
static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2947
{
2948
AioFile *af = Jim_CmdPrivData(interp);
2949
2950
return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, argc, argv);
2951
}
2952
2953
static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2954
{
2955
AioFile *af = Jim_CmdPrivData(interp);
2956
2957
return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, argc, argv);
2958
}
2959
#endif
2960
2961
#if defined(jim_ext_file) && defined(Jim_FileStat)
2962
static int aio_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2963
{
2964
jim_stat_t sb;
2965
AioFile *af = Jim_CmdPrivData(interp);
2966
2967
if (Jim_FileStat(af->fd, &sb) == -1) {
2968
JimAioSetError(interp, NULL);
2969
return JIM_ERR;
2970
}
2971
return Jim_FileStoreStatData(interp, argc == 0 ? NULL : argv[0], &sb);
2972
}
2973
#endif
2974
2975
2976
2977
2978
static const jim_subcmd_type aio_command_table[] = {
2979
{ "read",
2980
"?-nonewline|len?",
2981
aio_cmd_read,
2982
0,
2983
2,
2984
2985
},
2986
{ "copyto",
2987
"handle ?size?",
2988
aio_cmd_copy,
2989
1,
2990
2,
2991
2992
},
2993
{ "getfd",
2994
NULL,
2995
aio_cmd_getfd,
2996
0,
2997
0,
2998
2999
},
3000
{ "gets",
3001
"?var?",
3002
aio_cmd_gets,
3003
0,
3004
1,
3005
3006
},
3007
{ "puts",
3008
"?-nonewline? str",
3009
aio_cmd_puts,
3010
1,
3011
2,
3012
3013
},
3014
{ "isatty",
3015
NULL,
3016
aio_cmd_isatty,
3017
0,
3018
0,
3019
3020
},
3021
{ "flush",
3022
NULL,
3023
aio_cmd_flush,
3024
0,
3025
0,
3026
3027
},
3028
{ "eof",
3029
NULL,
3030
aio_cmd_eof,
3031
0,
3032
0,
3033
3034
},
3035
{ "close",
3036
"?r(ead)|w(rite)?",
3037
aio_cmd_close,
3038
0,
3039
1,
3040
JIM_MODFLAG_FULLARGV,
3041
3042
},
3043
{ "seek",
3044
"offset ?start|current|end",
3045
aio_cmd_seek,
3046
1,
3047
2,
3048
3049
},
3050
{ "tell",
3051
NULL,
3052
aio_cmd_tell,
3053
0,
3054
0,
3055
3056
},
3057
{ "filename",
3058
NULL,
3059
aio_cmd_filename,
3060
0,
3061
0,
3062
3063
},
3064
#ifdef O_NDELAY
3065
{ "ndelay",
3066
"?0|1?",
3067
aio_cmd_ndelay,
3068
0,
3069
1,
3070
3071
},
3072
#endif
3073
#ifdef HAVE_FSYNC
3074
{ "sync",
3075
NULL,
3076
aio_cmd_sync,
3077
0,
3078
0,
3079
3080
},
3081
#endif
3082
{ "buffering",
3083
"none|line|full",
3084
aio_cmd_buffering,
3085
1,
3086
1,
3087
3088
},
3089
#if defined(jim_ext_file) && defined(Jim_FileStat)
3090
{ "stat",
3091
"?var?",
3092
aio_cmd_stat,
3093
0,
3094
1,
3095
3096
},
3097
#endif
3098
#ifdef jim_ext_eventloop
3099
{ "readable",
3100
"?readable-script?",
3101
aio_cmd_readable,
3102
0,
3103
1,
3104
3105
},
3106
{ "writable",
3107
"?writable-script?",
3108
aio_cmd_writable,
3109
0,
3110
1,
3111
3112
},
3113
{ "onexception",
3114
"?exception-script?",
3115
aio_cmd_onexception,
3116
0,
3117
1,
3118
3119
},
3120
{ "timeout",
3121
"?ms?",
3122
aio_cmd_timeout,
3123
0,
3124
1,
3125
3126
},
3127
#endif
3128
{ NULL }
3129
};
3130
3131
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3132
{
3133
return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
3134
}
3135
3136
static int parse_posix_open_mode(Jim_Interp *interp, Jim_Obj *modeObj)
3137
{
3138
int i;
3139
int flags = 0;
3140
#ifndef O_NOCTTY
3141
3142
#define O_NOCTTY 0
3143
#endif
3144
static const char * const modetypes[] = {
3145
"RDONLY", "WRONLY", "RDWR", "APPEND", "BINARY", "CREAT", "EXCL", "NOCTTY", "TRUNC", NULL
3146
};
3147
static const int modeflags[] = {
3148
O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, 0, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC,
3149
};
3150
3151
for (i = 0; i < Jim_ListLength(interp, modeObj); i++) {
3152
int opt;
3153
Jim_Obj *objPtr = Jim_ListGetIndex(interp, modeObj, i);
3154
if (Jim_GetEnum(interp, objPtr, modetypes, &opt, "access mode", JIM_ERRMSG) != JIM_OK) {
3155
return -1;
3156
}
3157
flags |= modeflags[opt];
3158
}
3159
return flags;
3160
}
3161
3162
static int parse_open_mode(Jim_Interp *interp, Jim_Obj *filenameObj, Jim_Obj *modeObj)
3163
{
3164
3165
int flags;
3166
const char *mode = Jim_String(modeObj);
3167
if (*mode == 'R' || *mode == 'W') {
3168
return parse_posix_open_mode(interp, modeObj);
3169
}
3170
if (*mode == 'r') {
3171
flags = O_RDONLY;
3172
}
3173
else if (*mode == 'w') {
3174
flags = O_WRONLY | O_CREAT | O_TRUNC;
3175
}
3176
else if (*mode == 'a') {
3177
flags = O_WRONLY | O_CREAT | O_APPEND;
3178
}
3179
else {
3180
Jim_SetResultFormatted(interp, "%s: invalid open mode '%s'", Jim_String(filenameObj), mode);
3181
return -1;
3182
}
3183
mode++;
3184
3185
if (*mode == 'b') {
3186
#ifdef O_BINARY
3187
flags |= O_BINARY;
3188
#endif
3189
mode++;
3190
}
3191
3192
if (*mode == 't') {
3193
#ifdef O_TEXT
3194
flags |= O_TEXT;
3195
#endif
3196
mode++;
3197
}
3198
3199
if (*mode == '+') {
3200
mode++;
3201
3202
flags &= ~(O_RDONLY | O_WRONLY);
3203
flags |= O_RDWR;
3204
}
3205
3206
if (*mode == 'x') {
3207
mode++;
3208
#ifdef O_EXCL
3209
flags |= O_EXCL;
3210
#endif
3211
}
3212
3213
if (*mode == 'F') {
3214
mode++;
3215
#ifdef O_LARGEFILE
3216
flags |= O_LARGEFILE;
3217
#endif
3218
}
3219
3220
if (*mode == 'e') {
3221
3222
mode++;
3223
}
3224
return flags;
3225
}
3226
3227
static int JimAioOpenCommand(Jim_Interp *interp, int argc,
3228
Jim_Obj *const *argv)
3229
{
3230
int openflags;
3231
const char *filename;
3232
int fd = -1;
3233
int n = 0;
3234
int flags = 0;
3235
3236
if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-noclose")) {
3237
flags = AIO_KEEPOPEN;
3238
n++;
3239
}
3240
if (argc < 2 || argc > 3 + n) {
3241
Jim_WrongNumArgs(interp, 1, argv, "filename ?-noclose? ?mode?");
3242
return JIM_ERR;
3243
}
3244
3245
filename = Jim_String(argv[1]);
3246
3247
#ifdef jim_ext_tclcompat
3248
{
3249
3250
3251
if (*filename == '|') {
3252
Jim_Obj *evalObj[3];
3253
int i = 0;
3254
3255
evalObj[i++] = Jim_NewStringObj(interp, "::popen", -1);
3256
evalObj[i++] = Jim_NewStringObj(interp, filename + 1, -1);
3257
if (argc == 3 + n) {
3258
evalObj[i++] = argv[2 + n];
3259
}
3260
3261
return Jim_EvalObjVector(interp, i, evalObj);
3262
}
3263
}
3264
#endif
3265
if (argc == 3 + n) {
3266
openflags = parse_open_mode(interp, argv[1], argv[2 + n]);
3267
if (openflags == -1) {
3268
return JIM_ERR;
3269
}
3270
}
3271
else {
3272
openflags = O_RDONLY;
3273
}
3274
fd = open(filename, openflags, 0666);
3275
if (fd < 0) {
3276
JimAioSetError(interp, argv[1]);
3277
return JIM_ERR;
3278
}
3279
3280
return JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags) ? JIM_OK : JIM_ERR;
3281
}
3282
3283
3284
static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
3285
const char *hdlfmt, int family, int flags)
3286
{
3287
AioFile *af;
3288
char buf[AIO_CMD_LEN];
3289
Jim_Obj *cmdname;
3290
3291
snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
3292
cmdname = Jim_NewStringObj(interp, buf, -1);
3293
if (!filename) {
3294
filename = cmdname;
3295
}
3296
Jim_IncrRefCount(filename);
3297
3298
3299
af = Jim_Alloc(sizeof(*af));
3300
memset(af, 0, sizeof(*af));
3301
af->filename = filename;
3302
af->fd = fd;
3303
af->addr_family = family;
3304
af->fops = &stdio_fops;
3305
af->ssl = NULL;
3306
if (flags & AIO_WBUF_NONE) {
3307
af->wbuft = WBUF_OPT_NONE;
3308
}
3309
else {
3310
#ifdef HAVE_ISATTY
3311
af->wbuft = isatty(af->fd) ? WBUF_OPT_LINE : WBUF_OPT_FULL;
3312
#else
3313
af->wbuft = WBUF_OPT_FULL;
3314
#endif
3315
}
3316
3317
#ifdef FD_CLOEXEC
3318
if ((flags & AIO_KEEPOPEN) == 0) {
3319
(void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
3320
}
3321
#endif
3322
aio_set_nonblocking(af, !!(flags & AIO_NONBLOCK));
3323
3324
af->flags |= flags;
3325
3326
af->writebuf = Jim_NewStringObj(interp, NULL, 0);
3327
Jim_IncrRefCount(af->writebuf);
3328
3329
Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
3330
3331
Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, cmdname));
3332
3333
return af;
3334
}
3335
3336
#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
3337
static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
3338
const char *hdlfmt, int family, int flags)
3339
{
3340
if (JimMakeChannel(interp, p[0], filename, hdlfmt, family, flags)) {
3341
Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
3342
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
3343
if (JimMakeChannel(interp, p[1], filename, hdlfmt, family, flags)) {
3344
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
3345
Jim_SetResult(interp, objPtr);
3346
return JIM_OK;
3347
}
3348
}
3349
3350
3351
close(p[0]);
3352
close(p[1]);
3353
JimAioSetError(interp, NULL);
3354
return JIM_ERR;
3355
}
3356
#endif
3357
3358
#ifdef HAVE_PIPE
3359
static int JimCreatePipe(Jim_Interp *interp, Jim_Obj *filenameObj, int flags)
3360
{
3361
int p[2];
3362
3363
if (pipe(p) != 0) {
3364
JimAioSetError(interp, NULL);
3365
return JIM_ERR;
3366
}
3367
3368
return JimMakeChannelPair(interp, p, filenameObj, "aio.pipe%ld", 0, flags);
3369
}
3370
3371
3372
static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3373
{
3374
if (argc != 1) {
3375
Jim_WrongNumArgs(interp, 1, argv, "");
3376
return JIM_ERR;
3377
}
3378
return JimCreatePipe(interp, argv[0], 0);
3379
}
3380
#endif
3381
3382
#ifdef HAVE_OPENPTY
3383
static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3384
{
3385
int p[2];
3386
char path[MAXPATHLEN];
3387
3388
if (argc != 1) {
3389
Jim_WrongNumArgs(interp, 1, argv, "");
3390
return JIM_ERR;
3391
}
3392
3393
if (openpty(&p[0], &p[1], path, NULL, NULL) != 0) {
3394
JimAioSetError(interp, NULL);
3395
return JIM_ERR;
3396
}
3397
3398
3399
return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
3400
return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
3401
}
3402
#endif
3403
3404
3405
3406
int Jim_aioInit(Jim_Interp *interp)
3407
{
3408
if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
3409
return JIM_ERR;
3410
3411
#if defined(JIM_SSL)
3412
Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
3413
#endif
3414
3415
Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
3416
#ifdef HAVE_SOCKETS
3417
Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
3418
#endif
3419
#ifdef HAVE_PIPE
3420
Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
3421
#endif
3422
3423
3424
JimMakeChannel(interp, fileno(stdin), NULL, "stdin", 0, AIO_KEEPOPEN);
3425
JimMakeChannel(interp, fileno(stdout), NULL, "stdout", 0, AIO_KEEPOPEN);
3426
JimMakeChannel(interp, fileno(stderr), NULL, "stderr", 0, AIO_KEEPOPEN | AIO_WBUF_NONE);
3427
3428
return JIM_OK;
3429
}
3430
3431
#include <errno.h>
3432
#include <stdio.h>
3433
#include <string.h>
3434
3435
3436
#ifdef HAVE_DIRENT_H
3437
#include <dirent.h>
3438
#endif
3439
3440
int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3441
{
3442
const char *dirPath;
3443
DIR *dirPtr;
3444
struct dirent *entryPtr;
3445
int nocomplain = 0;
3446
3447
if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
3448
nocomplain = 1;
3449
}
3450
if (argc != 2 && !nocomplain) {
3451
Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
3452
return JIM_ERR;
3453
}
3454
3455
dirPath = Jim_String(argv[1 + nocomplain]);
3456
3457
dirPtr = opendir(dirPath);
3458
if (dirPtr == NULL) {
3459
if (nocomplain) {
3460
return JIM_OK;
3461
}
3462
Jim_SetResultString(interp, strerror(errno), -1);
3463
return JIM_ERR;
3464
}
3465
else {
3466
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3467
3468
while ((entryPtr = readdir(dirPtr)) != NULL) {
3469
if (entryPtr->d_name[0] == '.') {
3470
if (entryPtr->d_name[1] == '\0') {
3471
continue;
3472
}
3473
if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
3474
continue;
3475
}
3476
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
3477
}
3478
closedir(dirPtr);
3479
3480
Jim_SetResult(interp, listObj);
3481
3482
return JIM_OK;
3483
}
3484
}
3485
3486
int Jim_readdirInit(Jim_Interp *interp)
3487
{
3488
Jim_PackageProvideCheck(interp, "readdir");
3489
Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
3490
return JIM_OK;
3491
}
3492
3493
#include <stdlib.h>
3494
#include <string.h>
3495
3496
#if defined(JIM_REGEXP)
3497
#else
3498
#include <regex.h>
3499
#define jim_regcomp regcomp
3500
#define jim_regexec regexec
3501
#define jim_regerror regerror
3502
#define jim_regfree regfree
3503
#endif
3504
3505
static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3506
{
3507
jim_regfree(objPtr->internalRep.ptrIntValue.ptr);
3508
Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
3509
}
3510
3511
static const Jim_ObjType regexpObjType = {
3512
"regexp",
3513
FreeRegexpInternalRep,
3514
NULL,
3515
NULL,
3516
JIM_TYPE_NONE
3517
};
3518
3519
static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
3520
{
3521
regex_t *compre;
3522
const char *pattern;
3523
int ret;
3524
3525
3526
if (objPtr->typePtr == &regexpObjType &&
3527
objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) {
3528
3529
return objPtr->internalRep.ptrIntValue.ptr;
3530
}
3531
3532
3533
3534
3535
pattern = Jim_String(objPtr);
3536
compre = Jim_Alloc(sizeof(regex_t));
3537
3538
if ((ret = jim_regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
3539
char buf[100];
3540
3541
jim_regerror(ret, compre, buf, sizeof(buf));
3542
Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
3543
jim_regfree(compre);
3544
Jim_Free(compre);
3545
return NULL;
3546
}
3547
3548
Jim_FreeIntRep(interp, objPtr);
3549
3550
objPtr->typePtr = &regexpObjType;
3551
objPtr->internalRep.ptrIntValue.int1 = flags;
3552
objPtr->internalRep.ptrIntValue.ptr = compre;
3553
3554
return compre;
3555
}
3556
3557
int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3558
{
3559
int opt_indices = 0;
3560
int opt_all = 0;
3561
int opt_inline = 0;
3562
regex_t *regex;
3563
int match, i, j;
3564
int offset = 0;
3565
regmatch_t *pmatch = NULL;
3566
int source_len;
3567
int result = JIM_OK;
3568
const char *pattern;
3569
const char *source_str;
3570
int num_matches = 0;
3571
int num_vars;
3572
Jim_Obj *resultListObj = NULL;
3573
int regcomp_flags = 0;
3574
int eflags = 0;
3575
int option;
3576
enum {
3577
OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
3578
};
3579
static const char * const options[] = {
3580
"-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
3581
};
3582
3583
if (argc < 3) {
3584
wrongNumArgs:
3585
Jim_WrongNumArgs(interp, 1, argv,
3586
"?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
3587
return JIM_ERR;
3588
}
3589
3590
for (i = 1; i < argc; i++) {
3591
const char *opt = Jim_String(argv[i]);
3592
3593
if (*opt != '-') {
3594
break;
3595
}
3596
if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3597
return JIM_ERR;
3598
}
3599
if (option == OPT_END) {
3600
i++;
3601
break;
3602
}
3603
switch (option) {
3604
case OPT_INDICES:
3605
opt_indices = 1;
3606
break;
3607
3608
case OPT_NOCASE:
3609
regcomp_flags |= REG_ICASE;
3610
break;
3611
3612
case OPT_LINE:
3613
regcomp_flags |= REG_NEWLINE;
3614
break;
3615
3616
case OPT_ALL:
3617
opt_all = 1;
3618
break;
3619
3620
case OPT_INLINE:
3621
opt_inline = 1;
3622
break;
3623
3624
case OPT_START:
3625
if (++i == argc) {
3626
goto wrongNumArgs;
3627
}
3628
if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3629
return JIM_ERR;
3630
}
3631
break;
3632
}
3633
}
3634
if (argc - i < 2) {
3635
goto wrongNumArgs;
3636
}
3637
3638
regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3639
if (!regex) {
3640
return JIM_ERR;
3641
}
3642
3643
pattern = Jim_String(argv[i]);
3644
source_str = Jim_GetString(argv[i + 1], &source_len);
3645
3646
num_vars = argc - i - 2;
3647
3648
if (opt_inline) {
3649
if (num_vars) {
3650
Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
3651
-1);
3652
result = JIM_ERR;
3653
goto done;
3654
}
3655
num_vars = regex->re_nsub + 1;
3656
}
3657
3658
pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
3659
3660
if (offset) {
3661
if (offset < 0) {
3662
offset += source_len + 1;
3663
}
3664
if (offset > source_len) {
3665
source_str += source_len;
3666
}
3667
else if (offset > 0) {
3668
source_str += utf8_index(source_str, offset);
3669
}
3670
eflags |= REG_NOTBOL;
3671
}
3672
3673
if (opt_inline) {
3674
resultListObj = Jim_NewListObj(interp, NULL, 0);
3675
}
3676
3677
next_match:
3678
match = jim_regexec(regex, source_str, num_vars + 1, pmatch, eflags);
3679
if (match >= REG_BADPAT) {
3680
char buf[100];
3681
3682
jim_regerror(match, regex, buf, sizeof(buf));
3683
Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3684
result = JIM_ERR;
3685
goto done;
3686
}
3687
3688
if (match == REG_NOMATCH) {
3689
goto done;
3690
}
3691
3692
num_matches++;
3693
3694
if (opt_all && !opt_inline) {
3695
3696
goto try_next_match;
3697
}
3698
3699
3700
j = 0;
3701
for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
3702
Jim_Obj *resultObj;
3703
3704
if (opt_indices) {
3705
resultObj = Jim_NewListObj(interp, NULL, 0);
3706
}
3707
else {
3708
resultObj = Jim_NewStringObj(interp, "", 0);
3709
}
3710
3711
if (pmatch[j].rm_so == -1) {
3712
if (opt_indices) {
3713
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3714
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3715
}
3716
}
3717
else {
3718
if (opt_indices) {
3719
3720
int so = utf8_strlen(source_str, pmatch[j].rm_so);
3721
int eo = utf8_strlen(source_str, pmatch[j].rm_eo);
3722
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + so));
3723
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + eo - 1));
3724
}
3725
else {
3726
Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
3727
}
3728
}
3729
3730
if (opt_inline) {
3731
Jim_ListAppendElement(interp, resultListObj, resultObj);
3732
}
3733
else {
3734
3735
result = Jim_SetVariable(interp, argv[i], resultObj);
3736
3737
if (result != JIM_OK) {
3738
Jim_FreeObj(interp, resultObj);
3739
break;
3740
}
3741
}
3742
}
3743
3744
try_next_match:
3745
if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
3746
if (pmatch[0].rm_eo) {
3747
offset += utf8_strlen(source_str, pmatch[0].rm_eo);
3748
source_str += pmatch[0].rm_eo;
3749
}
3750
else {
3751
source_str++;
3752
offset++;
3753
}
3754
if (*source_str) {
3755
eflags = REG_NOTBOL;
3756
goto next_match;
3757
}
3758
}
3759
3760
done:
3761
if (result == JIM_OK) {
3762
if (opt_inline) {
3763
Jim_SetResult(interp, resultListObj);
3764
}
3765
else {
3766
Jim_SetResultInt(interp, num_matches);
3767
}
3768
}
3769
3770
Jim_Free(pmatch);
3771
return result;
3772
}
3773
3774
#define MAX_SUB_MATCHES 50
3775
3776
int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3777
{
3778
int regcomp_flags = 0;
3779
int regexec_flags = 0;
3780
int opt_all = 0;
3781
int opt_command = 0;
3782
int offset = 0;
3783
regex_t *regex;
3784
const char *p;
3785
int result = JIM_OK;
3786
regmatch_t pmatch[MAX_SUB_MATCHES + 1];
3787
int num_matches = 0;
3788
3789
int i, j, n;
3790
Jim_Obj *varname;
3791
Jim_Obj *resultObj;
3792
Jim_Obj *cmd_prefix = NULL;
3793
Jim_Obj *regcomp_obj = NULL;
3794
const char *source_str;
3795
int source_len;
3796
const char *replace_str = NULL;
3797
int replace_len;
3798
const char *pattern;
3799
int option;
3800
enum {
3801
OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END
3802
};
3803
static const char * const options[] = {
3804
"-nocase", "-line", "-all", "-start", "-command", "--", NULL
3805
};
3806
3807
if (argc < 4) {
3808
wrongNumArgs:
3809
Jim_WrongNumArgs(interp, 1, argv,
3810
"?-switch ...? exp string subSpec ?varName?");
3811
return JIM_ERR;
3812
}
3813
3814
for (i = 1; i < argc; i++) {
3815
const char *opt = Jim_String(argv[i]);
3816
3817
if (*opt != '-') {
3818
break;
3819
}
3820
if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3821
return JIM_ERR;
3822
}
3823
if (option == OPT_END) {
3824
i++;
3825
break;
3826
}
3827
switch (option) {
3828
case OPT_NOCASE:
3829
regcomp_flags |= REG_ICASE;
3830
break;
3831
3832
case OPT_LINE:
3833
regcomp_flags |= REG_NEWLINE;
3834
break;
3835
3836
case OPT_ALL:
3837
opt_all = 1;
3838
break;
3839
3840
case OPT_START:
3841
if (++i == argc) {
3842
goto wrongNumArgs;
3843
}
3844
if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3845
return JIM_ERR;
3846
}
3847
break;
3848
3849
case OPT_COMMAND:
3850
opt_command = 1;
3851
break;
3852
}
3853
}
3854
if (argc - i != 3 && argc - i != 4) {
3855
goto wrongNumArgs;
3856
}
3857
3858
3859
regcomp_obj = Jim_DuplicateObj(interp, argv[i]);
3860
Jim_IncrRefCount(regcomp_obj);
3861
regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags);
3862
if (!regex) {
3863
Jim_DecrRefCount(interp, regcomp_obj);
3864
return JIM_ERR;
3865
}
3866
pattern = Jim_String(argv[i]);
3867
3868
source_str = Jim_GetString(argv[i + 1], &source_len);
3869
if (opt_command) {
3870
cmd_prefix = argv[i + 2];
3871
if (Jim_ListLength(interp, cmd_prefix) == 0) {
3872
Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1);
3873
Jim_DecrRefCount(interp, regcomp_obj);
3874
return JIM_ERR;
3875
}
3876
Jim_IncrRefCount(cmd_prefix);
3877
}
3878
else {
3879
replace_str = Jim_GetString(argv[i + 2], &replace_len);
3880
}
3881
varname = argv[i + 3];
3882
3883
3884
resultObj = Jim_NewStringObj(interp, "", 0);
3885
3886
if (offset) {
3887
if (offset < 0) {
3888
offset += source_len + 1;
3889
}
3890
if (offset > source_len) {
3891
offset = source_len;
3892
}
3893
else if (offset < 0) {
3894
offset = 0;
3895
}
3896
}
3897
3898
offset = utf8_index(source_str, offset);
3899
3900
3901
Jim_AppendString(interp, resultObj, source_str, offset);
3902
3903
3904
n = source_len - offset;
3905
p = source_str + offset;
3906
do {
3907
int match = jim_regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
3908
3909
if (match >= REG_BADPAT) {
3910
char buf[100];
3911
3912
jim_regerror(match, regex, buf, sizeof(buf));
3913
Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3914
return JIM_ERR;
3915
}
3916
if (match == REG_NOMATCH) {
3917
break;
3918
}
3919
3920
num_matches++;
3921
3922
Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
3923
3924
if (opt_command) {
3925
3926
Jim_Obj *cmdListObj = Jim_DuplicateObj(interp, cmd_prefix);
3927
for (j = 0; j < MAX_SUB_MATCHES; j++) {
3928
if (pmatch[j].rm_so == -1) {
3929
break;
3930
}
3931
else {
3932
Jim_Obj *srcObj = Jim_NewStringObj(interp, p + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
3933
Jim_ListAppendElement(interp, cmdListObj, srcObj);
3934
}
3935
}
3936
Jim_IncrRefCount(cmdListObj);
3937
3938
result = Jim_EvalObj(interp, cmdListObj);
3939
Jim_DecrRefCount(interp, cmdListObj);
3940
if (result != JIM_OK) {
3941
goto cmd_error;
3942
}
3943
Jim_AppendString(interp, resultObj, Jim_String(Jim_GetResult(interp)), -1);
3944
}
3945
else {
3946
3947
for (j = 0; j < replace_len; j++) {
3948
int idx;
3949
int c = replace_str[j];
3950
3951
if (c == '&') {
3952
idx = 0;
3953
}
3954
else if (c == '\\' && j < replace_len) {
3955
c = replace_str[++j];
3956
if ((c >= '0') && (c <= '9')) {
3957
idx = c - '0';
3958
}
3959
else if ((c == '\\') || (c == '&')) {
3960
Jim_AppendString(interp, resultObj, replace_str + j, 1);
3961
continue;
3962
}
3963
else {
3964
Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
3965
continue;
3966
}
3967
}
3968
else {
3969
Jim_AppendString(interp, resultObj, replace_str + j, 1);
3970
continue;
3971
}
3972
if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
3973
Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
3974
pmatch[idx].rm_eo - pmatch[idx].rm_so);
3975
}
3976
}
3977
}
3978
3979
p += pmatch[0].rm_eo;
3980
n -= pmatch[0].rm_eo;
3981
3982
3983
if (!opt_all || n == 0) {
3984
break;
3985
}
3986
3987
3988
if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
3989
break;
3990
}
3991
3992
3993
if (pattern[0] == '\0' && n) {
3994
3995
Jim_AppendString(interp, resultObj, p, 1);
3996
p++;
3997
n--;
3998
}
3999
4000
if (pmatch[0].rm_eo == pmatch[0].rm_so) {
4001
4002
regexec_flags = REG_NOTBOL;
4003
}
4004
else {
4005
regexec_flags = 0;
4006
}
4007
4008
} while (n);
4009
4010
Jim_AppendString(interp, resultObj, p, -1);
4011
4012
cmd_error:
4013
if (result == JIM_OK) {
4014
4015
if (argc - i == 4) {
4016
result = Jim_SetVariable(interp, varname, resultObj);
4017
4018
if (result == JIM_OK) {
4019
Jim_SetResultInt(interp, num_matches);
4020
}
4021
else {
4022
Jim_FreeObj(interp, resultObj);
4023
}
4024
}
4025
else {
4026
Jim_SetResult(interp, resultObj);
4027
result = JIM_OK;
4028
}
4029
}
4030
else {
4031
Jim_FreeObj(interp, resultObj);
4032
}
4033
4034
if (opt_command) {
4035
Jim_DecrRefCount(interp, cmd_prefix);
4036
}
4037
4038
Jim_DecrRefCount(interp, regcomp_obj);
4039
4040
return result;
4041
}
4042
4043
int Jim_regexpInit(Jim_Interp *interp)
4044
{
4045
Jim_PackageProvideCheck(interp, "regexp");
4046
Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
4047
Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
4048
return JIM_OK;
4049
}
4050
4051
#include <limits.h>
4052
#include <stdlib.h>
4053
#include <string.h>
4054
#include <stdio.h>
4055
#include <errno.h>
4056
4057
4058
#ifdef HAVE_UTIMES
4059
#include <sys/time.h>
4060
#endif
4061
#ifdef HAVE_UNISTD_H
4062
#include <unistd.h>
4063
#elif defined(_MSC_VER)
4064
#include <direct.h>
4065
#define F_OK 0
4066
#define W_OK 2
4067
#define R_OK 4
4068
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
4069
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
4070
#endif
4071
4072
# ifndef MAXPATHLEN
4073
# ifdef PATH_MAX
4074
# define MAXPATHLEN PATH_MAX
4075
# else
4076
# define MAXPATHLEN JIM_PATH_LEN
4077
# endif
4078
# endif
4079
4080
#if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
4081
#define ISWINDOWS 1
4082
4083
#undef HAVE_SYMLINK
4084
#else
4085
#define ISWINDOWS 0
4086
#endif
4087
4088
4089
#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
4090
#define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
4091
#elif defined(HAVE_STRUCT_STAT_ST_MTIM)
4092
#define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
4093
#endif
4094
4095
4096
static void JimFixPath(char *path)
4097
{
4098
if (ISWINDOWS) {
4099
4100
char *p = path;
4101
while ((p = strchr(p, '\\')) != NULL) {
4102
*p++ = '/';
4103
}
4104
}
4105
}
4106
4107
4108
static const char *JimGetFileType(int mode)
4109
{
4110
if (S_ISREG(mode)) {
4111
return "file";
4112
}
4113
else if (S_ISDIR(mode)) {
4114
return "directory";
4115
}
4116
#ifdef S_ISCHR
4117
else if (S_ISCHR(mode)) {
4118
return "characterSpecial";
4119
}
4120
#endif
4121
#ifdef S_ISBLK
4122
else if (S_ISBLK(mode)) {
4123
return "blockSpecial";
4124
}
4125
#endif
4126
#ifdef S_ISFIFO
4127
else if (S_ISFIFO(mode)) {
4128
return "fifo";
4129
}
4130
#endif
4131
#ifdef S_ISLNK
4132
else if (S_ISLNK(mode)) {
4133
return "link";
4134
}
4135
#endif
4136
#ifdef S_ISSOCK
4137
else if (S_ISSOCK(mode)) {
4138
return "socket";
4139
}
4140
#endif
4141
return "unknown";
4142
}
4143
4144
static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
4145
{
4146
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
4147
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
4148
}
4149
4150
int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb)
4151
{
4152
4153
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
4154
4155
AppendStatElement(interp, listObj, "dev", sb->st_dev);
4156
AppendStatElement(interp, listObj, "ino", sb->st_ino);
4157
AppendStatElement(interp, listObj, "mode", sb->st_mode);
4158
AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
4159
AppendStatElement(interp, listObj, "uid", sb->st_uid);
4160
AppendStatElement(interp, listObj, "gid", sb->st_gid);
4161
AppendStatElement(interp, listObj, "size", sb->st_size);
4162
AppendStatElement(interp, listObj, "atime", sb->st_atime);
4163
AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
4164
AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
4165
#ifdef STAT_MTIME_US
4166
AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb));
4167
#endif
4168
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
4169
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
4170
4171
4172
if (varName) {
4173
Jim_Obj *objPtr;
4174
objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
4175
4176
if (objPtr) {
4177
Jim_Obj *objv[2];
4178
4179
objv[0] = objPtr;
4180
objv[1] = listObj;
4181
4182
objPtr = Jim_DictMerge(interp, 2, objv);
4183
if (objPtr == NULL) {
4184
4185
Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
4186
Jim_FreeNewObj(interp, listObj);
4187
return JIM_ERR;
4188
}
4189
4190
Jim_InvalidateStringRep(objPtr);
4191
4192
Jim_FreeNewObj(interp, listObj);
4193
listObj = objPtr;
4194
}
4195
Jim_SetVariable(interp, varName, listObj);
4196
}
4197
4198
4199
Jim_SetResult(interp, listObj);
4200
4201
return JIM_OK;
4202
}
4203
4204
static int JimPathLenNoTrailingSlashes(const char *path, int len)
4205
{
4206
int i;
4207
for (i = len; i > 1 && path[i - 1] == '/'; i--) {
4208
4209
if (ISWINDOWS && path[i - 2] == ':') {
4210
4211
break;
4212
}
4213
}
4214
return i;
4215
}
4216
4217
static Jim_Obj *JimStripTrailingSlashes(Jim_Interp *interp, Jim_Obj *objPtr)
4218
{
4219
int len = Jim_Length(objPtr);
4220
const char *path = Jim_String(objPtr);
4221
int i = JimPathLenNoTrailingSlashes(path, len);
4222
if (i != len) {
4223
objPtr = Jim_NewStringObj(interp, path, i);
4224
}
4225
Jim_IncrRefCount(objPtr);
4226
return objPtr;
4227
}
4228
4229
static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4230
{
4231
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
4232
const char *path = Jim_String(objPtr);
4233
const char *p = strrchr(path, '/');
4234
4235
if (!p) {
4236
Jim_SetResultString(interp, ".", -1);
4237
}
4238
else if (p[1] == 0) {
4239
4240
Jim_SetResult(interp, objPtr);
4241
}
4242
else if (p == path) {
4243
Jim_SetResultString(interp, "/", -1);
4244
}
4245
else if (ISWINDOWS && p[-1] == ':') {
4246
4247
Jim_SetResultString(interp, path, p - path + 1);
4248
}
4249
else {
4250
4251
int len = JimPathLenNoTrailingSlashes(path, p - path);
4252
Jim_SetResultString(interp, path, len);
4253
}
4254
Jim_DecrRefCount(interp, objPtr);
4255
return JIM_OK;
4256
}
4257
4258
static int file_cmd_split(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4259
{
4260
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
4261
const char *path = Jim_String(argv[0]);
4262
4263
if (*path == '/') {
4264
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "/", 1));
4265
}
4266
4267
while (1) {
4268
4269
while (*path == '/') {
4270
path++;
4271
}
4272
if (*path) {
4273
const char *pt = strchr(path, '/');
4274
if (pt) {
4275
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, pt - path));
4276
path = pt;
4277
continue;
4278
}
4279
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, -1));
4280
}
4281
break;
4282
}
4283
Jim_SetResult(interp, listObj);
4284
return JIM_OK;
4285
}
4286
4287
static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4288
{
4289
const char *path = Jim_String(argv[0]);
4290
const char *lastSlash = strrchr(path, '/');
4291
const char *p = strrchr(path, '.');
4292
4293
if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
4294
Jim_SetResult(interp, argv[0]);
4295
}
4296
else {
4297
Jim_SetResultString(interp, path, p - path);
4298
}
4299
return JIM_OK;
4300
}
4301
4302
static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4303
{
4304
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
4305
const char *path = Jim_String(objPtr);
4306
const char *lastSlash = strrchr(path, '/');
4307
const char *p = strrchr(path, '.');
4308
4309
if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
4310
p = "";
4311
}
4312
Jim_SetResultString(interp, p, -1);
4313
Jim_DecrRefCount(interp, objPtr);
4314
return JIM_OK;
4315
}
4316
4317
static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4318
{
4319
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
4320
const char *path = Jim_String(objPtr);
4321
const char *lastSlash = strrchr(path, '/');
4322
4323
if (lastSlash) {
4324
Jim_SetResultString(interp, lastSlash + 1, -1);
4325
}
4326
else {
4327
Jim_SetResult(interp, objPtr);
4328
}
4329
Jim_DecrRefCount(interp, objPtr);
4330
return JIM_OK;
4331
}
4332
4333
#ifndef HAVE_RESTRICT
4334
#define restrict
4335
#endif
4336
4337
static char *JimRealPath(const char *restrict path, char *restrict resolved_path, size_t len)
4338
{
4339
#if defined(HAVE__FULLPATH)
4340
return _fullpath(resolved_path, path, len);
4341
#elif defined(HAVE_REALPATH)
4342
return realpath(path, resolved_path);
4343
#else
4344
return NULL;
4345
#endif
4346
}
4347
4348
static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4349
{
4350
const char *path = Jim_String(argv[0]);
4351
char *newname = Jim_Alloc(MAXPATHLEN);
4352
4353
if (JimRealPath(path, newname, MAXPATHLEN)) {
4354
JimFixPath(newname);
4355
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
4356
return JIM_OK;
4357
}
4358
Jim_Free(newname);
4359
Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
4360
return JIM_ERR;
4361
}
4362
4363
static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4364
{
4365
int i;
4366
char *newname = Jim_Alloc(MAXPATHLEN + 1);
4367
char *last = newname;
4368
4369
*newname = 0;
4370
4371
4372
for (i = 0; i < argc; i++) {
4373
int len;
4374
const char *part = Jim_GetString(argv[i], &len);
4375
4376
if (*part == '/') {
4377
4378
last = newname;
4379
}
4380
else if (ISWINDOWS && strchr(part, ':')) {
4381
4382
last = newname;
4383
}
4384
else if (part[0] == '.') {
4385
if (part[1] == '/') {
4386
part += 2;
4387
len -= 2;
4388
}
4389
else if (part[1] == 0 && last != newname) {
4390
4391
continue;
4392
}
4393
}
4394
4395
4396
if (last != newname && last[-1] != '/') {
4397
*last++ = '/';
4398
}
4399
4400
if (len) {
4401
if (last + len - newname >= MAXPATHLEN) {
4402
Jim_Free(newname);
4403
Jim_SetResultString(interp, "Path too long", -1);
4404
return JIM_ERR;
4405
}
4406
memcpy(last, part, len);
4407
last += len;
4408
}
4409
4410
4411
if (last > newname + 1 && last[-1] == '/') {
4412
4413
if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
4414
*--last = 0;
4415
}
4416
}
4417
}
4418
4419
*last = 0;
4420
4421
4422
4423
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
4424
4425
return JIM_OK;
4426
}
4427
4428
static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
4429
{
4430
Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
4431
4432
return JIM_OK;
4433
}
4434
4435
static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4436
{
4437
return file_access(interp, argv[0], R_OK);
4438
}
4439
4440
static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4441
{
4442
return file_access(interp, argv[0], W_OK);
4443
}
4444
4445
static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4446
{
4447
#ifdef X_OK
4448
return file_access(interp, argv[0], X_OK);
4449
#else
4450
4451
Jim_SetResultBool(interp, 1);
4452
return JIM_OK;
4453
#endif
4454
}
4455
4456
static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4457
{
4458
return file_access(interp, argv[0], F_OK);
4459
}
4460
4461
static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4462
{
4463
int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
4464
4465
if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
4466
argc--;
4467
argv++;
4468
}
4469
4470
while (argc--) {
4471
const char *path = Jim_String(argv[0]);
4472
4473
if (unlink(path) == -1 && errno != ENOENT) {
4474
if (rmdir(path) == -1) {
4475
4476
if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
4477
Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
4478
strerror(errno));
4479
return JIM_ERR;
4480
}
4481
}
4482
}
4483
argv++;
4484
}
4485
return JIM_OK;
4486
}
4487
4488
#ifdef HAVE_MKDIR_ONE_ARG
4489
#define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
4490
#else
4491
#define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
4492
#endif
4493
4494
static int mkdir_all(char *path)
4495
{
4496
int ok = 1;
4497
4498
4499
goto first;
4500
4501
while (ok--) {
4502
4503
{
4504
char *slash = strrchr(path, '/');
4505
4506
if (slash && slash != path) {
4507
*slash = 0;
4508
if (mkdir_all(path) != 0) {
4509
return -1;
4510
}
4511
*slash = '/';
4512
}
4513
}
4514
first:
4515
if (MKDIR_DEFAULT(path) == 0) {
4516
return 0;
4517
}
4518
if (errno == ENOENT) {
4519
4520
continue;
4521
}
4522
4523
#if defined(__morphos__) && defined(__ixemul__)
4524
/*
4525
* MorphOS with ixemul returns ENOTDIR on SFS when the directory
4526
* already exists, but on RAM: it returns the correct EEXIST.
4527
*/
4528
if (errno == ENOTDIR) {
4529
errno = EEXIST;
4530
}
4531
#endif
4532
4533
if (errno == EEXIST) {
4534
jim_stat_t sb;
4535
4536
if (Jim_Stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
4537
return 0;
4538
}
4539
4540
errno = EEXIST;
4541
}
4542
4543
break;
4544
}
4545
return -1;
4546
}
4547
4548
static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4549
{
4550
while (argc--) {
4551
char *path = Jim_StrDup(Jim_String(argv[0]));
4552
int rc = mkdir_all(path);
4553
4554
Jim_Free(path);
4555
if (rc != 0) {
4556
Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
4557
strerror(errno));
4558
return JIM_ERR;
4559
}
4560
argv++;
4561
}
4562
return JIM_OK;
4563
}
4564
4565
static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4566
{
4567
int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);
4568
4569
if (fd < 0) {
4570
return JIM_ERR;
4571
}
4572
close(fd);
4573
4574
return JIM_OK;
4575
}
4576
4577
static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4578
{
4579
const char *source;
4580
const char *dest;
4581
int force = 0;
4582
4583
if (argc == 3) {
4584
if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
4585
return -1;
4586
}
4587
force++;
4588
argv++;
4589
argc--;
4590
}
4591
4592
source = Jim_String(argv[0]);
4593
dest = Jim_String(argv[1]);
4594
4595
if (!force && access(dest, F_OK) == 0) {
4596
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
4597
argv[1]);
4598
return JIM_ERR;
4599
}
4600
#if ISWINDOWS
4601
if (access(dest, F_OK) == 0) {
4602
4603
remove(dest);
4604
}
4605
#endif
4606
if (rename(source, dest) != 0) {
4607
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
4608
strerror(errno));
4609
return JIM_ERR;
4610
}
4611
4612
return JIM_OK;
4613
}
4614
4615
#if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4616
static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4617
{
4618
int ret;
4619
const char *source;
4620
const char *dest;
4621
static const char * const options[] = { "-hard", "-symbolic", NULL };
4622
enum { OPT_HARD, OPT_SYMBOLIC, };
4623
int option = OPT_HARD;
4624
4625
if (argc == 3) {
4626
if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
4627
return JIM_ERR;
4628
}
4629
argv++;
4630
argc--;
4631
}
4632
4633
dest = Jim_String(argv[0]);
4634
source = Jim_String(argv[1]);
4635
4636
if (option == OPT_HARD) {
4637
ret = link(source, dest);
4638
}
4639
else {
4640
ret = symlink(source, dest);
4641
}
4642
4643
if (ret != 0) {
4644
Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
4645
strerror(errno));
4646
return JIM_ERR;
4647
}
4648
4649
return JIM_OK;
4650
}
4651
#endif
4652
4653
static int file_stat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
4654
{
4655
const char *path = Jim_String(filename);
4656
4657
if (Jim_Stat(path, sb) == -1) {
4658
Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
4659
return JIM_ERR;
4660
}
4661
return JIM_OK;
4662
}
4663
4664
#ifdef Jim_LinkStat
4665
static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
4666
{
4667
const char *path = Jim_String(filename);
4668
4669
if (Jim_LinkStat(path, sb) == -1) {
4670
Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
4671
return JIM_ERR;
4672
}
4673
return JIM_OK;
4674
}
4675
#else
4676
#define file_lstat file_stat
4677
#endif
4678
4679
static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4680
{
4681
jim_stat_t sb;
4682
4683
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4684
return JIM_ERR;
4685
}
4686
Jim_SetResultInt(interp, sb.st_atime);
4687
return JIM_OK;
4688
}
4689
4690
static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us)
4691
{
4692
#ifdef HAVE_UTIMES
4693
struct timeval times[2];
4694
4695
times[1].tv_sec = times[0].tv_sec = us / 1000000;
4696
times[1].tv_usec = times[0].tv_usec = us % 1000000;
4697
4698
if (utimes(filename, times) != 0) {
4699
Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno));
4700
return JIM_ERR;
4701
}
4702
return JIM_OK;
4703
#else
4704
Jim_SetResultString(interp, "Not implemented", -1);
4705
return JIM_ERR;
4706
#endif
4707
}
4708
4709
static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4710
{
4711
jim_stat_t sb;
4712
4713
if (argc == 2) {
4714
jim_wide secs;
4715
if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
4716
return JIM_ERR;
4717
}
4718
return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
4719
}
4720
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4721
return JIM_ERR;
4722
}
4723
Jim_SetResultInt(interp, sb.st_mtime);
4724
return JIM_OK;
4725
}
4726
4727
#ifdef STAT_MTIME_US
4728
static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4729
{
4730
jim_stat_t sb;
4731
4732
if (argc == 2) {
4733
jim_wide us;
4734
if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
4735
return JIM_ERR;
4736
}
4737
return JimSetFileTimes(interp, Jim_String(argv[0]), us);
4738
}
4739
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4740
return JIM_ERR;
4741
}
4742
Jim_SetResultInt(interp, STAT_MTIME_US(sb));
4743
return JIM_OK;
4744
}
4745
#endif
4746
4747
static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4748
{
4749
return Jim_EvalPrefix(interp, "file copy", argc, argv);
4750
}
4751
4752
static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4753
{
4754
jim_stat_t sb;
4755
4756
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4757
return JIM_ERR;
4758
}
4759
Jim_SetResultInt(interp, sb.st_size);
4760
return JIM_OK;
4761
}
4762
4763
static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4764
{
4765
jim_stat_t sb;
4766
int ret = 0;
4767
4768
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4769
ret = S_ISDIR(sb.st_mode);
4770
}
4771
Jim_SetResultInt(interp, ret);
4772
return JIM_OK;
4773
}
4774
4775
static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4776
{
4777
jim_stat_t sb;
4778
int ret = 0;
4779
4780
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4781
ret = S_ISREG(sb.st_mode);
4782
}
4783
Jim_SetResultInt(interp, ret);
4784
return JIM_OK;
4785
}
4786
4787
#ifdef HAVE_GETEUID
4788
static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4789
{
4790
jim_stat_t sb;
4791
int ret = 0;
4792
4793
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4794
ret = (geteuid() == sb.st_uid);
4795
}
4796
Jim_SetResultInt(interp, ret);
4797
return JIM_OK;
4798
}
4799
#endif
4800
4801
#if defined(HAVE_READLINK)
4802
static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4803
{
4804
const char *path = Jim_String(argv[0]);
4805
char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
4806
4807
int linkLength = readlink(path, linkValue, MAXPATHLEN);
4808
4809
if (linkLength == -1) {
4810
Jim_Free(linkValue);
4811
Jim_SetResultFormatted(interp, "could not read link \"%#s\": %s", argv[0], strerror(errno));
4812
return JIM_ERR;
4813
}
4814
linkValue[linkLength] = 0;
4815
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
4816
return JIM_OK;
4817
}
4818
#endif
4819
4820
static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4821
{
4822
jim_stat_t sb;
4823
4824
if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4825
return JIM_ERR;
4826
}
4827
Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
4828
return JIM_OK;
4829
}
4830
4831
#ifdef Jim_LinkStat
4832
static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4833
{
4834
jim_stat_t sb;
4835
4836
if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4837
return JIM_ERR;
4838
}
4839
return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4840
}
4841
#else
4842
#define file_cmd_lstat file_cmd_stat
4843
#endif
4844
4845
static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4846
{
4847
jim_stat_t sb;
4848
4849
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4850
return JIM_ERR;
4851
}
4852
return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4853
}
4854
4855
static const jim_subcmd_type file_command_table[] = {
4856
{ "atime",
4857
"name",
4858
file_cmd_atime,
4859
1,
4860
1,
4861
4862
},
4863
{ "mtime",
4864
"name ?time?",
4865
file_cmd_mtime,
4866
1,
4867
2,
4868
4869
},
4870
#ifdef STAT_MTIME_US
4871
{ "mtimeus",
4872
"name ?time?",
4873
file_cmd_mtimeus,
4874
1,
4875
2,
4876
4877
},
4878
#endif
4879
{ "copy",
4880
"?-force? source dest",
4881
file_cmd_copy,
4882
2,
4883
3,
4884
4885
},
4886
{ "dirname",
4887
"name",
4888
file_cmd_dirname,
4889
1,
4890
1,
4891
4892
},
4893
{ "rootname",
4894
"name",
4895
file_cmd_rootname,
4896
1,
4897
1,
4898
4899
},
4900
{ "extension",
4901
"name",
4902
file_cmd_extension,
4903
1,
4904
1,
4905
4906
},
4907
{ "tail",
4908
"name",
4909
file_cmd_tail,
4910
1,
4911
1,
4912
4913
},
4914
{ "split",
4915
"name",
4916
file_cmd_split,
4917
1,
4918
1,
4919
4920
},
4921
{ "normalize",
4922
"name",
4923
file_cmd_normalize,
4924
1,
4925
1,
4926
4927
},
4928
{ "join",
4929
"name ?name ...?",
4930
file_cmd_join,
4931
1,
4932
-1,
4933
4934
},
4935
{ "readable",
4936
"name",
4937
file_cmd_readable,
4938
1,
4939
1,
4940
4941
},
4942
{ "writable",
4943
"name",
4944
file_cmd_writable,
4945
1,
4946
1,
4947
4948
},
4949
{ "executable",
4950
"name",
4951
file_cmd_executable,
4952
1,
4953
1,
4954
4955
},
4956
{ "exists",
4957
"name",
4958
file_cmd_exists,
4959
1,
4960
1,
4961
4962
},
4963
{ "delete",
4964
"?-force|--? name ...",
4965
file_cmd_delete,
4966
1,
4967
-1,
4968
4969
},
4970
{ "mkdir",
4971
"dir ...",
4972
file_cmd_mkdir,
4973
1,
4974
-1,
4975
4976
},
4977
{ "tempfile",
4978
"?template?",
4979
file_cmd_tempfile,
4980
0,
4981
1,
4982
4983
},
4984
{ "rename",
4985
"?-force? source dest",
4986
file_cmd_rename,
4987
2,
4988
3,
4989
4990
},
4991
#if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4992
{ "link",
4993
"?-symbolic|-hard? newname target",
4994
file_cmd_link,
4995
2,
4996
3,
4997
4998
},
4999
#endif
5000
#if defined(HAVE_READLINK)
5001
{ "readlink",
5002
"name",
5003
file_cmd_readlink,
5004
1,
5005
1,
5006
5007
},
5008
#endif
5009
{ "size",
5010
"name",
5011
file_cmd_size,
5012
1,
5013
1,
5014
5015
},
5016
{ "stat",
5017
"name ?var?",
5018
file_cmd_stat,
5019
1,
5020
2,
5021
5022
},
5023
{ "lstat",
5024
"name ?var?",
5025
file_cmd_lstat,
5026
1,
5027
2,
5028
5029
},
5030
{ "type",
5031
"name",
5032
file_cmd_type,
5033
1,
5034
1,
5035
5036
},
5037
#ifdef HAVE_GETEUID
5038
{ "owned",
5039
"name",
5040
file_cmd_owned,
5041
1,
5042
1,
5043
5044
},
5045
#endif
5046
{ "isdirectory",
5047
"name",
5048
file_cmd_isdirectory,
5049
1,
5050
1,
5051
5052
},
5053
{ "isfile",
5054
"name",
5055
file_cmd_isfile,
5056
1,
5057
1,
5058
5059
},
5060
{
5061
NULL
5062
}
5063
};
5064
5065
static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5066
{
5067
const char *path;
5068
5069
if (argc != 2) {
5070
Jim_WrongNumArgs(interp, 1, argv, "dirname");
5071
return JIM_ERR;
5072
}
5073
5074
path = Jim_String(argv[1]);
5075
5076
if (chdir(path) != 0) {
5077
Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
5078
strerror(errno));
5079
return JIM_ERR;
5080
}
5081
return JIM_OK;
5082
}
5083
5084
static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5085
{
5086
char *cwd = Jim_Alloc(MAXPATHLEN);
5087
5088
if (getcwd(cwd, MAXPATHLEN) == NULL) {
5089
Jim_SetResultString(interp, "Failed to get pwd", -1);
5090
Jim_Free(cwd);
5091
return JIM_ERR;
5092
}
5093
JimFixPath(cwd);
5094
Jim_SetResultString(interp, cwd, -1);
5095
5096
Jim_Free(cwd);
5097
return JIM_OK;
5098
}
5099
5100
int Jim_fileInit(Jim_Interp *interp)
5101
{
5102
Jim_PackageProvideCheck(interp, "file");
5103
Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
5104
Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
5105
Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
5106
return JIM_OK;
5107
}
5108
5109
#include <string.h>
5110
#include <ctype.h>
5111
5112
5113
#if (!(defined(HAVE_VFORK) || defined(HAVE_FORK)) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
5114
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5115
{
5116
Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
5117
int i, j;
5118
int rc;
5119
5120
5121
for (i = 1; i < argc; i++) {
5122
int len;
5123
const char *arg = Jim_GetString(argv[i], &len);
5124
5125
if (i > 1) {
5126
Jim_AppendString(interp, cmdlineObj, " ", 1);
5127
}
5128
if (strpbrk(arg, "\\\" ") == NULL) {
5129
5130
Jim_AppendString(interp, cmdlineObj, arg, len);
5131
continue;
5132
}
5133
5134
Jim_AppendString(interp, cmdlineObj, "\"", 1);
5135
for (j = 0; j < len; j++) {
5136
if (arg[j] == '\\' || arg[j] == '"') {
5137
Jim_AppendString(interp, cmdlineObj, "\\", 1);
5138
}
5139
Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
5140
}
5141
Jim_AppendString(interp, cmdlineObj, "\"", 1);
5142
}
5143
rc = system(Jim_String(cmdlineObj));
5144
5145
Jim_FreeNewObj(interp, cmdlineObj);
5146
5147
if (rc) {
5148
Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
5149
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
5150
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
5151
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
5152
Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
5153
return JIM_ERR;
5154
}
5155
5156
return JIM_OK;
5157
}
5158
5159
int Jim_execInit(Jim_Interp *interp)
5160
{
5161
Jim_PackageProvideCheck(interp, "exec");
5162
Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
5163
return JIM_OK;
5164
}
5165
#else
5166
5167
5168
#include <errno.h>
5169
#include <signal.h>
5170
#include <sys/stat.h>
5171
5172
struct WaitInfoTable;
5173
5174
static char **JimOriginalEnviron(void);
5175
static char **JimSaveEnv(char **env);
5176
static void JimRestoreEnv(char **env);
5177
static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
5178
phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
5179
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr);
5180
static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj);
5181
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
5182
5183
#if defined(__MINGW32__)
5184
static phandle_t JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
5185
#endif
5186
5187
static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
5188
{
5189
int len;
5190
const char *s = Jim_GetString(objPtr, &len);
5191
5192
if (len > 0 && s[len - 1] == '\n') {
5193
objPtr->length--;
5194
objPtr->bytes[objPtr->length] = '\0';
5195
}
5196
}
5197
5198
static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
5199
{
5200
char buf[256];
5201
int ret = 0;
5202
5203
while (1) {
5204
int retval = read(fd, buf, sizeof(buf));
5205
if (retval > 0) {
5206
ret = 1;
5207
Jim_AppendString(interp, strObj, buf, retval);
5208
}
5209
if (retval <= 0) {
5210
break;
5211
}
5212
}
5213
close(fd);
5214
return ret;
5215
}
5216
5217
static char **JimBuildEnv(Jim_Interp *interp)
5218
{
5219
int i;
5220
int size;
5221
int num;
5222
int n;
5223
char **envptr;
5224
char *envdata;
5225
5226
Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
5227
5228
if (!objPtr) {
5229
return JimOriginalEnviron();
5230
}
5231
5232
5233
5234
num = Jim_ListLength(interp, objPtr);
5235
if (num % 2) {
5236
5237
num--;
5238
}
5239
size = Jim_Length(objPtr) + 2;
5240
5241
envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
5242
envdata = (char *)&envptr[num / 2 + 1];
5243
5244
n = 0;
5245
for (i = 0; i < num; i += 2) {
5246
const char *s1, *s2;
5247
Jim_Obj *elemObj;
5248
5249
Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
5250
s1 = Jim_String(elemObj);
5251
Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
5252
s2 = Jim_String(elemObj);
5253
5254
envptr[n] = envdata;
5255
envdata += sprintf(envdata, "%s=%s", s1, s2);
5256
envdata++;
5257
n++;
5258
}
5259
envptr[n] = NULL;
5260
*envdata = 0;
5261
5262
return envptr;
5263
}
5264
5265
static void JimFreeEnv(char **env, char **original_environ)
5266
{
5267
if (env != original_environ) {
5268
Jim_Free(env);
5269
}
5270
}
5271
5272
static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
5273
{
5274
Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
5275
5276
if (pid <= 0) {
5277
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
5278
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
5279
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
5280
}
5281
else if (WIFEXITED(waitStatus)) {
5282
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
5283
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
5284
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
5285
}
5286
else {
5287
const char *type;
5288
const char *action;
5289
const char *signame;
5290
5291
if (WIFSIGNALED(waitStatus)) {
5292
type = "CHILDKILLED";
5293
action = "killed";
5294
signame = Jim_SignalId(WTERMSIG(waitStatus));
5295
}
5296
else {
5297
type = "CHILDSUSP";
5298
action = "suspended";
5299
signame = "none";
5300
}
5301
5302
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
5303
5304
if (errStrObj) {
5305
Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
5306
}
5307
5308
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
5309
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
5310
}
5311
return errorCode;
5312
}
5313
5314
static int JimCheckWaitStatus(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
5315
{
5316
if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
5317
return JIM_OK;
5318
}
5319
Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
5320
5321
return JIM_ERR;
5322
}
5323
5324
5325
struct WaitInfo
5326
{
5327
phandle_t phandle;
5328
int status;
5329
int flags;
5330
};
5331
5332
5333
struct WaitInfoTable {
5334
struct WaitInfo *info;
5335
int size;
5336
int used;
5337
int refcount;
5338
};
5339
5340
5341
#define WI_DETACHED 2
5342
5343
#define WAIT_TABLE_GROW_BY 4
5344
5345
static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
5346
{
5347
struct WaitInfoTable *table = privData;
5348
5349
if (--table->refcount == 0) {
5350
Jim_Free(table->info);
5351
Jim_Free(table);
5352
}
5353
}
5354
5355
static struct WaitInfoTable *JimAllocWaitInfoTable(void)
5356
{
5357
struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
5358
table->info = NULL;
5359
table->size = table->used = 0;
5360
table->refcount = 1;
5361
5362
return table;
5363
}
5364
5365
static int JimWaitRemove(struct WaitInfoTable *table, phandle_t phandle)
5366
{
5367
int i;
5368
5369
5370
for (i = 0; i < table->used; i++) {
5371
if (phandle == table->info[i].phandle) {
5372
if (i != table->used - 1) {
5373
table->info[i] = table->info[table->used - 1];
5374
}
5375
table->used--;
5376
return 0;
5377
}
5378
}
5379
return -1;
5380
}
5381
5382
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5383
{
5384
int outputId;
5385
int errorId;
5386
phandle_t *pidPtr;
5387
int numPids, result;
5388
int child_siginfo = 1;
5389
Jim_Obj *childErrObj;
5390
Jim_Obj *errStrObj;
5391
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5392
5393
if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
5394
Jim_Obj *listObj;
5395
int i;
5396
5397
argc--;
5398
numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
5399
if (numPids < 0) {
5400
return JIM_ERR;
5401
}
5402
5403
listObj = Jim_NewListObj(interp, NULL, 0);
5404
for (i = 0; i < numPids; i++) {
5405
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, JimProcessPid(pidPtr[i])));
5406
}
5407
Jim_SetResult(interp, listObj);
5408
JimDetachPids(table, numPids, pidPtr);
5409
Jim_Free(pidPtr);
5410
return JIM_OK;
5411
}
5412
5413
numPids =
5414
JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
5415
5416
if (numPids < 0) {
5417
return JIM_ERR;
5418
}
5419
5420
result = JIM_OK;
5421
5422
errStrObj = Jim_NewStringObj(interp, "", 0);
5423
5424
5425
if (outputId != -1) {
5426
if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
5427
result = JIM_ERR;
5428
Jim_SetResultErrno(interp, "error reading from output pipe");
5429
}
5430
}
5431
5432
5433
childErrObj = Jim_NewStringObj(interp, "", 0);
5434
Jim_IncrRefCount(childErrObj);
5435
5436
if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
5437
result = JIM_ERR;
5438
}
5439
5440
if (errorId != -1) {
5441
int ret;
5442
Jim_Lseek(errorId, 0, SEEK_SET);
5443
ret = JimAppendStreamToString(interp, errorId, errStrObj);
5444
if (ret < 0) {
5445
Jim_SetResultErrno(interp, "error reading from error pipe");
5446
result = JIM_ERR;
5447
}
5448
else if (ret > 0) {
5449
5450
child_siginfo = 0;
5451
}
5452
}
5453
5454
if (child_siginfo) {
5455
5456
Jim_AppendObj(interp, errStrObj, childErrObj);
5457
}
5458
Jim_DecrRefCount(interp, childErrObj);
5459
5460
5461
Jim_RemoveTrailingNewline(errStrObj);
5462
5463
5464
Jim_SetResult(interp, errStrObj);
5465
5466
return result;
5467
}
5468
5469
static long JimWaitForProcess(struct WaitInfoTable *table, phandle_t phandle, int *statusPtr)
5470
{
5471
if (JimWaitRemove(table, phandle) == 0) {
5472
5473
return waitpid(phandle, statusPtr, 0);
5474
}
5475
5476
5477
return -1;
5478
}
5479
5480
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr)
5481
{
5482
int j;
5483
5484
for (j = 0; j < numPids; j++) {
5485
5486
int i;
5487
for (i = 0; i < table->used; i++) {
5488
if (pidPtr[j] == table->info[i].phandle) {
5489
table->info[i].flags |= WI_DETACHED;
5490
break;
5491
}
5492
}
5493
}
5494
}
5495
5496
static int JimGetChannelFd(Jim_Interp *interp, const char *name)
5497
{
5498
Jim_Obj *objv[2];
5499
5500
objv[0] = Jim_NewStringObj(interp, name, -1);
5501
objv[1] = Jim_NewStringObj(interp, "getfd", -1);
5502
5503
if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
5504
jim_wide fd;
5505
if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
5506
return fd;
5507
}
5508
}
5509
return -1;
5510
}
5511
5512
static void JimReapDetachedPids(struct WaitInfoTable *table)
5513
{
5514
struct WaitInfo *waitPtr;
5515
int count;
5516
int dest;
5517
5518
if (!table) {
5519
return;
5520
}
5521
5522
waitPtr = table->info;
5523
dest = 0;
5524
for (count = table->used; count > 0; waitPtr++, count--) {
5525
if (waitPtr->flags & WI_DETACHED) {
5526
int status;
5527
long pid = waitpid(waitPtr->phandle, &status, WNOHANG);
5528
if (pid > 0) {
5529
5530
table->used--;
5531
continue;
5532
}
5533
}
5534
if (waitPtr != &table->info[dest]) {
5535
table->info[dest] = *waitPtr;
5536
}
5537
dest++;
5538
}
5539
}
5540
5541
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5542
{
5543
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5544
int nohang = 0;
5545
long pid;
5546
phandle_t phandle;
5547
int status;
5548
Jim_Obj *errCodeObj;
5549
5550
5551
if (argc == 1) {
5552
JimReapDetachedPids(table);
5553
return JIM_OK;
5554
}
5555
5556
if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
5557
nohang = 1;
5558
}
5559
if (argc != nohang + 2) {
5560
Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
5561
return JIM_ERR;
5562
}
5563
if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) {
5564
return JIM_ERR;
5565
}
5566
5567
5568
phandle = JimWaitPid(pid, &status, nohang ? WNOHANG : 0);
5569
if (phandle == JIM_BAD_PHANDLE) {
5570
pid = -1;
5571
}
5572
#ifndef __MINGW32__
5573
else if (pid < 0) {
5574
pid = phandle;
5575
}
5576
#endif
5577
5578
errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
5579
5580
if (phandle != JIM_BAD_PHANDLE && (WIFEXITED(status) || WIFSIGNALED(status))) {
5581
5582
JimWaitRemove(table, phandle);
5583
}
5584
Jim_SetResult(interp, errCodeObj);
5585
return JIM_OK;
5586
}
5587
5588
static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5589
{
5590
if (argc != 1) {
5591
Jim_WrongNumArgs(interp, 1, argv, "");
5592
return JIM_ERR;
5593
}
5594
5595
Jim_SetResultInt(interp, (jim_wide)getpid());
5596
return JIM_OK;
5597
}
5598
5599
static int
5600
JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
5601
int *inPipePtr, int *outPipePtr, int *errFilePtr)
5602
{
5603
phandle_t *pidPtr = NULL; /* Points to alloc-ed array holding all
5604
* the pids of child processes. */
5605
int numPids = 0; /* Actual number of processes that exist
5606
* at *pidPtr right now. */
5607
int cmdCount; /* Count of number of distinct commands
5608
* found in argc/argv. */
5609
const char *input = NULL; /* Describes input for pipeline, depending
5610
* on "inputFile". NULL means take input
5611
* from stdin/pipe. */
5612
int input_len = 0;
5613
5614
#define FILE_NAME 0
5615
#define FILE_APPEND 1
5616
#define FILE_HANDLE 2
5617
#define FILE_TEXT 3
5618
5619
int inputFile = FILE_NAME; /* 1 means input is name of input file.
5620
* 2 means input is filehandle name.
5621
* 0 means input holds actual
5622
* text to be input to command. */
5623
5624
int outputFile = FILE_NAME; /* 0 means output is the name of output file.
5625
* 1 means output is the name of output file, and append.
5626
* 2 means output is filehandle name.
5627
* All this is ignored if output is NULL
5628
*/
5629
int errorFile = FILE_NAME; /* 0 means error is the name of error file.
5630
* 1 means error is the name of error file, and append.
5631
* 2 means error is filehandle name.
5632
* All this is ignored if error is NULL
5633
*/
5634
const char *output = NULL; /* Holds name of output file to pipe to,
5635
* or NULL if output goes to stdout/pipe. */
5636
const char *error = NULL; /* Holds name of stderr file to pipe to,
5637
* or NULL if stderr goes to stderr/pipe. */
5638
int inputId = -1;
5639
int outputId = -1;
5640
int errorId = -1;
5641
int lastOutputId = -1;
5642
int pipeIds[2];
5643
int firstArg, lastArg; /* Indexes of first and last arguments in
5644
* current command. */
5645
int lastBar;
5646
int i;
5647
phandle_t phandle;
5648
char **save_environ;
5649
#if defined(HAVE_EXECVPE) && !defined(__MINGW32__)
5650
char **child_environ;
5651
#endif
5652
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5653
5654
5655
char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
5656
int arg_count = 0;
5657
5658
if (inPipePtr != NULL) {
5659
*inPipePtr = -1;
5660
}
5661
if (outPipePtr != NULL) {
5662
*outPipePtr = -1;
5663
}
5664
if (errFilePtr != NULL) {
5665
*errFilePtr = -1;
5666
}
5667
pipeIds[0] = pipeIds[1] = -1;
5668
5669
cmdCount = 1;
5670
lastBar = -1;
5671
for (i = 0; i < argc; i++) {
5672
const char *arg = Jim_String(argv[i]);
5673
5674
if (arg[0] == '<') {
5675
inputFile = FILE_NAME;
5676
input = arg + 1;
5677
if (*input == '<') {
5678
inputFile = FILE_TEXT;
5679
input_len = Jim_Length(argv[i]) - 2;
5680
input++;
5681
}
5682
else if (*input == '@') {
5683
inputFile = FILE_HANDLE;
5684
input++;
5685
}
5686
5687
if (!*input && ++i < argc) {
5688
input = Jim_GetString(argv[i], &input_len);
5689
}
5690
}
5691
else if (arg[0] == '>') {
5692
int dup_error = 0;
5693
5694
outputFile = FILE_NAME;
5695
5696
output = arg + 1;
5697
if (*output == '>') {
5698
outputFile = FILE_APPEND;
5699
output++;
5700
}
5701
if (*output == '&') {
5702
5703
output++;
5704
dup_error = 1;
5705
}
5706
if (*output == '@') {
5707
outputFile = FILE_HANDLE;
5708
output++;
5709
}
5710
if (!*output && ++i < argc) {
5711
output = Jim_String(argv[i]);
5712
}
5713
if (dup_error) {
5714
errorFile = outputFile;
5715
error = output;
5716
}
5717
}
5718
else if (arg[0] == '2' && arg[1] == '>') {
5719
error = arg + 2;
5720
errorFile = FILE_NAME;
5721
5722
if (*error == '@') {
5723
errorFile = FILE_HANDLE;
5724
error++;
5725
}
5726
else if (*error == '>') {
5727
errorFile = FILE_APPEND;
5728
error++;
5729
}
5730
if (!*error && ++i < argc) {
5731
error = Jim_String(argv[i]);
5732
}
5733
}
5734
else {
5735
if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
5736
if (i == lastBar + 1 || i == argc - 1) {
5737
Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
5738
goto badargs;
5739
}
5740
lastBar = i;
5741
cmdCount++;
5742
}
5743
5744
arg_array[arg_count++] = (char *)arg;
5745
continue;
5746
}
5747
5748
if (i >= argc) {
5749
Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
5750
goto badargs;
5751
}
5752
}
5753
5754
if (arg_count == 0) {
5755
Jim_SetResultString(interp, "didn't specify command to execute", -1);
5756
badargs:
5757
Jim_Free(arg_array);
5758
return -1;
5759
}
5760
5761
5762
save_environ = JimSaveEnv(JimBuildEnv(interp));
5763
5764
if (input != NULL) {
5765
if (inputFile == FILE_TEXT) {
5766
inputId = Jim_MakeTempFile(interp, NULL, 1);
5767
if (inputId == -1) {
5768
goto error;
5769
}
5770
if (write(inputId, input, input_len) != input_len) {
5771
Jim_SetResultErrno(interp, "couldn't write temp file");
5772
close(inputId);
5773
goto error;
5774
}
5775
Jim_Lseek(inputId, 0L, SEEK_SET);
5776
}
5777
else if (inputFile == FILE_HANDLE) {
5778
int fd = JimGetChannelFd(interp, input);
5779
5780
if (fd < 0) {
5781
goto error;
5782
}
5783
inputId = dup(fd);
5784
}
5785
else {
5786
inputId = Jim_OpenForRead(input);
5787
if (inputId == -1) {
5788
Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
5789
goto error;
5790
}
5791
}
5792
}
5793
else if (inPipePtr != NULL) {
5794
if (pipe(pipeIds) != 0) {
5795
Jim_SetResultErrno(interp, "couldn't create input pipe for command");
5796
goto error;
5797
}
5798
inputId = pipeIds[0];
5799
*inPipePtr = pipeIds[1];
5800
pipeIds[0] = pipeIds[1] = -1;
5801
}
5802
5803
if (output != NULL) {
5804
if (outputFile == FILE_HANDLE) {
5805
int fd = JimGetChannelFd(interp, output);
5806
if (fd < 0) {
5807
goto error;
5808
}
5809
lastOutputId = dup(fd);
5810
}
5811
else {
5812
lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
5813
if (lastOutputId == -1) {
5814
Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
5815
goto error;
5816
}
5817
}
5818
}
5819
else if (outPipePtr != NULL) {
5820
if (pipe(pipeIds) != 0) {
5821
Jim_SetResultErrno(interp, "couldn't create output pipe");
5822
goto error;
5823
}
5824
lastOutputId = pipeIds[1];
5825
*outPipePtr = pipeIds[0];
5826
pipeIds[0] = pipeIds[1] = -1;
5827
}
5828
5829
if (error != NULL) {
5830
if (errorFile == FILE_HANDLE) {
5831
if (strcmp(error, "1") == 0) {
5832
5833
if (lastOutputId != -1) {
5834
errorId = dup(lastOutputId);
5835
}
5836
else {
5837
5838
error = "stdout";
5839
}
5840
}
5841
if (errorId == -1) {
5842
int fd = JimGetChannelFd(interp, error);
5843
if (fd < 0) {
5844
goto error;
5845
}
5846
errorId = dup(fd);
5847
}
5848
}
5849
else {
5850
errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
5851
if (errorId == -1) {
5852
Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
5853
goto error;
5854
}
5855
}
5856
}
5857
else if (errFilePtr != NULL) {
5858
errorId = Jim_MakeTempFile(interp, NULL, 1);
5859
if (errorId == -1) {
5860
goto error;
5861
}
5862
*errFilePtr = dup(errorId);
5863
}
5864
5865
5866
pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
5867
for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
5868
int pipe_dup_err = 0;
5869
int origErrorId = errorId;
5870
5871
for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
5872
if (strcmp(arg_array[lastArg], "|") == 0) {
5873
break;
5874
}
5875
if (strcmp(arg_array[lastArg], "|&") == 0) {
5876
pipe_dup_err = 1;
5877
break;
5878
}
5879
}
5880
5881
if (lastArg == firstArg) {
5882
Jim_SetResultString(interp, "missing command to exec", -1);
5883
goto error;
5884
}
5885
5886
5887
arg_array[lastArg] = NULL;
5888
if (lastArg == arg_count) {
5889
outputId = lastOutputId;
5890
lastOutputId = -1;
5891
}
5892
else {
5893
if (pipe(pipeIds) != 0) {
5894
Jim_SetResultErrno(interp, "couldn't create pipe");
5895
goto error;
5896
}
5897
outputId = pipeIds[1];
5898
}
5899
5900
5901
if (pipe_dup_err) {
5902
errorId = outputId;
5903
}
5904
5905
5906
5907
#ifdef __MINGW32__
5908
phandle = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
5909
if (phandle == JIM_BAD_PHANDLE) {
5910
Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
5911
goto error;
5912
}
5913
#else
5914
i = strlen(arg_array[firstArg]);
5915
5916
#ifdef HAVE_EXECVPE
5917
child_environ = Jim_GetEnviron();
5918
#endif
5919
#ifdef HAVE_VFORK
5920
phandle = vfork();
5921
#else
5922
phandle = fork();
5923
#endif
5924
if (phandle < 0) {
5925
Jim_SetResultErrno(interp, "couldn't fork child process");
5926
goto error;
5927
}
5928
if (phandle == 0) {
5929
5930
5931
if (inputId != -1 && inputId != fileno(stdin)) {
5932
dup2(inputId, fileno(stdin));
5933
close(inputId);
5934
}
5935
if (outputId != -1 && outputId != fileno(stdout)) {
5936
dup2(outputId, fileno(stdout));
5937
if (outputId != errorId) {
5938
close(outputId);
5939
}
5940
}
5941
if (errorId != -1 && errorId != fileno(stderr)) {
5942
dup2(errorId, fileno(stderr));
5943
close(errorId);
5944
}
5945
5946
if (outPipePtr && *outPipePtr != -1) {
5947
close(*outPipePtr);
5948
}
5949
if (errFilePtr && *errFilePtr != -1) {
5950
close(*errFilePtr);
5951
}
5952
if (pipeIds[0] != -1) {
5953
close(pipeIds[0]);
5954
}
5955
if (lastOutputId != -1) {
5956
close(lastOutputId);
5957
}
5958
5959
execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
5960
5961
if (write(fileno(stderr), "couldn't exec \"", 15) &&
5962
write(fileno(stderr), arg_array[firstArg], i) &&
5963
write(fileno(stderr), "\"\n", 2)) {
5964
5965
}
5966
#ifdef JIM_MAINTAINER
5967
{
5968
5969
static char *const false_argv[2] = {"false", NULL};
5970
execvp(false_argv[0],false_argv);
5971
}
5972
#endif
5973
_exit(127);
5974
}
5975
#endif
5976
5977
5978
5979
if (table->used == table->size) {
5980
table->size += WAIT_TABLE_GROW_BY;
5981
table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
5982
}
5983
5984
table->info[table->used].phandle = phandle;
5985
table->info[table->used].flags = 0;
5986
table->used++;
5987
5988
pidPtr[numPids] = phandle;
5989
5990
5991
errorId = origErrorId;
5992
5993
5994
if (inputId != -1) {
5995
close(inputId);
5996
}
5997
if (outputId != -1) {
5998
close(outputId);
5999
}
6000
inputId = pipeIds[0];
6001
pipeIds[0] = pipeIds[1] = -1;
6002
}
6003
*pidArrayPtr = pidPtr;
6004
6005
6006
cleanup:
6007
if (inputId != -1) {
6008
close(inputId);
6009
}
6010
if (lastOutputId != -1) {
6011
close(lastOutputId);
6012
}
6013
if (errorId != -1) {
6014
close(errorId);
6015
}
6016
Jim_Free(arg_array);
6017
6018
JimRestoreEnv(save_environ);
6019
6020
return numPids;
6021
6022
6023
error:
6024
if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
6025
close(*inPipePtr);
6026
*inPipePtr = -1;
6027
}
6028
if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
6029
close(*outPipePtr);
6030
*outPipePtr = -1;
6031
}
6032
if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
6033
close(*errFilePtr);
6034
*errFilePtr = -1;
6035
}
6036
if (pipeIds[0] != -1) {
6037
close(pipeIds[0]);
6038
}
6039
if (pipeIds[1] != -1) {
6040
close(pipeIds[1]);
6041
}
6042
if (pidPtr != NULL) {
6043
for (i = 0; i < numPids; i++) {
6044
if (pidPtr[i] != JIM_BAD_PHANDLE) {
6045
JimDetachPids(table, 1, &pidPtr[i]);
6046
}
6047
}
6048
Jim_Free(pidPtr);
6049
}
6050
numPids = -1;
6051
goto cleanup;
6052
}
6053
6054
6055
static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj)
6056
{
6057
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
6058
int result = JIM_OK;
6059
int i;
6060
6061
6062
for (i = 0; i < numPids; i++) {
6063
int waitStatus = 0;
6064
long pid = JimWaitForProcess(table, pidPtr[i], &waitStatus);
6065
if (pid > 0) {
6066
if (JimCheckWaitStatus(interp, pid, waitStatus, errStrObj) != JIM_OK) {
6067
result = JIM_ERR;
6068
}
6069
}
6070
}
6071
Jim_Free(pidPtr);
6072
6073
return result;
6074
}
6075
6076
int Jim_execInit(Jim_Interp *interp)
6077
{
6078
struct WaitInfoTable *waitinfo;
6079
6080
Jim_PackageProvideCheck(interp, "exec");
6081
6082
waitinfo = JimAllocWaitInfoTable();
6083
Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
6084
waitinfo->refcount++;
6085
Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
6086
Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
6087
6088
return JIM_OK;
6089
}
6090
6091
#if defined(__MINGW32__)
6092
6093
6094
static int
6095
JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
6096
{
6097
int i;
6098
static char extensions[][5] = {".exe", "", ".bat"};
6099
6100
for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
6101
snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
6102
6103
if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
6104
continue;
6105
}
6106
if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
6107
continue;
6108
}
6109
return 0;
6110
}
6111
6112
return -1;
6113
}
6114
6115
static char **JimSaveEnv(char **env)
6116
{
6117
return env;
6118
}
6119
6120
static void JimRestoreEnv(char **env)
6121
{
6122
JimFreeEnv(env, Jim_GetEnviron());
6123
}
6124
6125
static char **JimOriginalEnviron(void)
6126
{
6127
return NULL;
6128
}
6129
6130
static Jim_Obj *
6131
JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
6132
{
6133
char *start, *special;
6134
int quote, i;
6135
6136
Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
6137
6138
for (i = 0; argv[i]; i++) {
6139
if (i > 0) {
6140
Jim_AppendString(interp, strObj, " ", 1);
6141
}
6142
6143
if (argv[i][0] == '\0') {
6144
quote = 1;
6145
}
6146
else {
6147
quote = 0;
6148
for (start = argv[i]; *start != '\0'; start++) {
6149
if (isspace(UCHAR(*start))) {
6150
quote = 1;
6151
break;
6152
}
6153
}
6154
}
6155
if (quote) {
6156
Jim_AppendString(interp, strObj, "\"" , 1);
6157
}
6158
6159
start = argv[i];
6160
for (special = argv[i]; ; ) {
6161
if ((*special == '\\') && (special[1] == '\\' ||
6162
special[1] == '"' || (quote && special[1] == '\0'))) {
6163
Jim_AppendString(interp, strObj, start, special - start);
6164
start = special;
6165
while (1) {
6166
special++;
6167
if (*special == '"' || (quote && *special == '\0')) {
6168
6169
Jim_AppendString(interp, strObj, start, special - start);
6170
break;
6171
}
6172
if (*special != '\\') {
6173
break;
6174
}
6175
}
6176
Jim_AppendString(interp, strObj, start, special - start);
6177
start = special;
6178
}
6179
if (*special == '"') {
6180
if (special == start) {
6181
Jim_AppendString(interp, strObj, "\"", 1);
6182
}
6183
else {
6184
Jim_AppendString(interp, strObj, start, special - start);
6185
}
6186
Jim_AppendString(interp, strObj, "\\\"", 2);
6187
start = special + 1;
6188
}
6189
if (*special == '\0') {
6190
break;
6191
}
6192
special++;
6193
}
6194
Jim_AppendString(interp, strObj, start, special - start);
6195
if (quote) {
6196
Jim_AppendString(interp, strObj, "\"", 1);
6197
}
6198
}
6199
return strObj;
6200
}
6201
6202
static phandle_t
6203
JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
6204
{
6205
STARTUPINFO startInfo;
6206
PROCESS_INFORMATION procInfo;
6207
HANDLE hProcess;
6208
char execPath[MAX_PATH];
6209
phandle_t phandle = INVALID_HANDLE_VALUE;
6210
Jim_Obj *cmdLineObj;
6211
char *winenv;
6212
6213
if (JimWinFindExecutable(argv[0], execPath) < 0) {
6214
return phandle;
6215
}
6216
argv[0] = execPath;
6217
6218
hProcess = GetCurrentProcess();
6219
cmdLineObj = JimWinBuildCommandLine(interp, argv);
6220
6221
6222
ZeroMemory(&startInfo, sizeof(startInfo));
6223
startInfo.cb = sizeof(startInfo);
6224
startInfo.dwFlags = STARTF_USESTDHANDLES;
6225
startInfo.hStdInput = INVALID_HANDLE_VALUE;
6226
startInfo.hStdOutput= INVALID_HANDLE_VALUE;
6227
startInfo.hStdError = INVALID_HANDLE_VALUE;
6228
6229
if (inputId == -1) {
6230
inputId = _fileno(stdin);
6231
}
6232
DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
6233
0, TRUE, DUPLICATE_SAME_ACCESS);
6234
if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
6235
goto end;
6236
}
6237
6238
if (outputId == -1) {
6239
outputId = _fileno(stdout);
6240
}
6241
DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
6242
0, TRUE, DUPLICATE_SAME_ACCESS);
6243
if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
6244
goto end;
6245
}
6246
6247
6248
if (errorId == -1) {
6249
errorId = _fileno(stderr);
6250
}
6251
DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
6252
0, TRUE, DUPLICATE_SAME_ACCESS);
6253
if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
6254
goto end;
6255
}
6256
6257
if (env == NULL) {
6258
6259
winenv = NULL;
6260
}
6261
else if (env[0] == NULL) {
6262
winenv = (char *)"\0";
6263
}
6264
else {
6265
winenv = env[0];
6266
}
6267
6268
if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
6269
0, winenv, NULL, &startInfo, &procInfo)) {
6270
goto end;
6271
}
6272
6273
6274
WaitForInputIdle(procInfo.hProcess, 5000);
6275
CloseHandle(procInfo.hThread);
6276
6277
phandle = procInfo.hProcess;
6278
6279
end:
6280
Jim_FreeNewObj(interp, cmdLineObj);
6281
if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
6282
CloseHandle(startInfo.hStdInput);
6283
}
6284
if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
6285
CloseHandle(startInfo.hStdOutput);
6286
}
6287
if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
6288
CloseHandle(startInfo.hStdError);
6289
}
6290
return phandle;
6291
}
6292
6293
#else
6294
6295
static char **JimOriginalEnviron(void)
6296
{
6297
return Jim_GetEnviron();
6298
}
6299
6300
static char **JimSaveEnv(char **env)
6301
{
6302
char **saveenv = Jim_GetEnviron();
6303
Jim_SetEnviron(env);
6304
return saveenv;
6305
}
6306
6307
static void JimRestoreEnv(char **env)
6308
{
6309
JimFreeEnv(Jim_GetEnviron(), env);
6310
Jim_SetEnviron(env);
6311
}
6312
#endif
6313
#endif
6314
6315
6316
#include <stdlib.h>
6317
#include <string.h>
6318
#include <stdio.h>
6319
#include <time.h>
6320
6321
6322
#ifdef HAVE_SYS_TIME_H
6323
#include <sys/time.h>
6324
#endif
6325
6326
struct clock_options {
6327
int gmt;
6328
const char *format;
6329
};
6330
6331
static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
6332
{
6333
static const char * const options[] = { "-gmt", "-format", NULL };
6334
enum { OPT_GMT, OPT_FORMAT, };
6335
int i;
6336
6337
for (i = 0; i < argc; i += 2) {
6338
int option;
6339
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
6340
return JIM_ERR;
6341
}
6342
switch (option) {
6343
case OPT_GMT:
6344
if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
6345
return JIM_ERR;
6346
}
6347
break;
6348
case OPT_FORMAT:
6349
opts->format = Jim_String(argv[i + 1]);
6350
break;
6351
}
6352
}
6353
return JIM_OK;
6354
}
6355
6356
static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6357
{
6358
6359
char buf[100];
6360
time_t t;
6361
jim_wide seconds;
6362
struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
6363
struct tm *tm;
6364
6365
if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
6366
return JIM_ERR;
6367
}
6368
if (argc % 2 == 0) {
6369
return -1;
6370
}
6371
if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
6372
return JIM_ERR;
6373
}
6374
6375
t = seconds;
6376
tm = options.gmt ? gmtime(&t) : localtime(&t);
6377
6378
if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
6379
Jim_SetResultString(interp, "format string too long or invalid time", -1);
6380
return JIM_ERR;
6381
}
6382
6383
Jim_SetResultString(interp, buf, -1);
6384
6385
return JIM_OK;
6386
}
6387
6388
#ifdef HAVE_STRPTIME
6389
static time_t jim_timegm(const struct tm *tm)
6390
{
6391
int m = tm->tm_mon + 1;
6392
int y = 1900 + tm->tm_year - (m <= 2);
6393
int era = (y >= 0 ? y : y - 399) / 400;
6394
unsigned yoe = (unsigned)(y - era * 400);
6395
unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1;
6396
unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
6397
long days = (era * 146097 + (int)doe - 719468);
6398
int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
6399
6400
return days * 24 * 60 * 60 + secs;
6401
}
6402
6403
static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6404
{
6405
char *pt;
6406
struct tm tm;
6407
time_t now = time(NULL);
6408
6409
struct clock_options options = { 0, NULL };
6410
6411
if (argc % 2 == 0) {
6412
return -1;
6413
}
6414
6415
if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
6416
return JIM_ERR;
6417
}
6418
if (options.format == NULL) {
6419
return -1;
6420
}
6421
6422
localtime_r(&now, &tm);
6423
6424
pt = strptime(Jim_String(argv[0]), options.format, &tm);
6425
if (pt == 0 || *pt != 0) {
6426
Jim_SetResultString(interp, "Failed to parse time according to format", -1);
6427
return JIM_ERR;
6428
}
6429
6430
6431
tm.tm_isdst = options.gmt ? 0 : -1;
6432
Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
6433
6434
return JIM_OK;
6435
}
6436
#endif
6437
6438
static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6439
{
6440
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000000);
6441
return JIM_OK;
6442
}
6443
6444
static int clock_cmd_clicks(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6445
{
6446
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW));
6447
return JIM_OK;
6448
}
6449
6450
static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6451
{
6452
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME));
6453
return JIM_OK;
6454
}
6455
6456
static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6457
{
6458
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000);
6459
return JIM_OK;
6460
}
6461
6462
static const jim_subcmd_type clock_command_table[] = {
6463
{ "clicks",
6464
NULL,
6465
clock_cmd_clicks,
6466
0,
6467
0,
6468
6469
},
6470
{ "format",
6471
"seconds ?-format string? ?-gmt boolean?",
6472
clock_cmd_format,
6473
1,
6474
5,
6475
6476
},
6477
{ "microseconds",
6478
NULL,
6479
clock_cmd_micros,
6480
0,
6481
0,
6482
6483
},
6484
{ "milliseconds",
6485
NULL,
6486
clock_cmd_millis,
6487
0,
6488
0,
6489
6490
},
6491
#ifdef HAVE_STRPTIME
6492
{ "scan",
6493
"str -format format ?-gmt boolean?",
6494
clock_cmd_scan,
6495
3,
6496
5,
6497
6498
},
6499
#endif
6500
{ "seconds",
6501
NULL,
6502
clock_cmd_seconds,
6503
0,
6504
0,
6505
6506
},
6507
{ NULL }
6508
};
6509
6510
int Jim_clockInit(Jim_Interp *interp)
6511
{
6512
Jim_PackageProvideCheck(interp, "clock");
6513
Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
6514
return JIM_OK;
6515
}
6516
6517
#include <limits.h>
6518
#include <stdlib.h>
6519
#include <string.h>
6520
#include <stdio.h>
6521
#include <errno.h>
6522
6523
6524
static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6525
{
6526
6527
Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
6528
Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
6529
return JIM_OK;
6530
}
6531
6532
static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6533
{
6534
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6535
Jim_Obj *patternObj;
6536
6537
if (!objPtr) {
6538
return JIM_OK;
6539
}
6540
6541
patternObj = (argc == 1) ? NULL : argv[1];
6542
6543
6544
if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
6545
if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
6546
6547
Jim_SetResult(interp, objPtr);
6548
return JIM_OK;
6549
}
6550
}
6551
6552
return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
6553
}
6554
6555
static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6556
{
6557
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6558
6559
if (!objPtr) {
6560
return JIM_OK;
6561
}
6562
6563
return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
6564
}
6565
6566
static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6567
{
6568
int i;
6569
int len;
6570
Jim_Obj *resultObj;
6571
Jim_Obj *objPtr;
6572
Jim_Obj **dictValuesObj;
6573
6574
if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
6575
6576
Jim_UnsetVariable(interp, argv[0], JIM_NONE);
6577
return JIM_OK;
6578
}
6579
6580
objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6581
6582
if (objPtr == NULL) {
6583
6584
return JIM_OK;
6585
}
6586
6587
dictValuesObj = Jim_DictPairs(interp, objPtr, &len);
6588
if (dictValuesObj == NULL) {
6589
6590
Jim_SetResultString(interp, "", -1);
6591
return JIM_OK;
6592
}
6593
6594
6595
resultObj = Jim_NewDictObj(interp, NULL, 0);
6596
6597
for (i = 0; i < len; i += 2) {
6598
if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
6599
Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
6600
}
6601
}
6602
6603
Jim_SetVariable(interp, argv[0], resultObj);
6604
return JIM_OK;
6605
}
6606
6607
static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6608
{
6609
Jim_Obj *objPtr;
6610
int len = 0;
6611
6612
6613
objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6614
if (objPtr) {
6615
len = Jim_DictSize(interp, objPtr);
6616
if (len < 0) {
6617
6618
Jim_SetResultInt(interp, 0);
6619
return JIM_OK;
6620
}
6621
}
6622
6623
Jim_SetResultInt(interp, len);
6624
6625
return JIM_OK;
6626
}
6627
6628
static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6629
{
6630
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
6631
if (objPtr) {
6632
return Jim_DictInfo(interp, objPtr);
6633
}
6634
Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
6635
return JIM_ERR;
6636
}
6637
6638
static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6639
{
6640
int i;
6641
int len;
6642
Jim_Obj *listObj = argv[1];
6643
Jim_Obj *dictObj;
6644
6645
len = Jim_ListLength(interp, listObj);
6646
if (len % 2) {
6647
Jim_SetResultString(interp, "list must have an even number of elements", -1);
6648
return JIM_ERR;
6649
}
6650
6651
dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
6652
if (!dictObj) {
6653
6654
return Jim_SetVariable(interp, argv[0], listObj);
6655
}
6656
else if (Jim_DictSize(interp, dictObj) < 0) {
6657
return JIM_ERR;
6658
}
6659
6660
if (Jim_IsShared(dictObj)) {
6661
dictObj = Jim_DuplicateObj(interp, dictObj);
6662
}
6663
6664
for (i = 0; i < len; i += 2) {
6665
Jim_Obj *nameObj;
6666
Jim_Obj *valueObj;
6667
6668
Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
6669
Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
6670
6671
Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
6672
}
6673
return Jim_SetVariable(interp, argv[0], dictObj);
6674
}
6675
6676
static const jim_subcmd_type array_command_table[] = {
6677
{ "exists",
6678
"arrayName",
6679
array_cmd_exists,
6680
1,
6681
1,
6682
6683
},
6684
{ "get",
6685
"arrayName ?pattern?",
6686
array_cmd_get,
6687
1,
6688
2,
6689
6690
},
6691
{ "names",
6692
"arrayName ?pattern?",
6693
array_cmd_names,
6694
1,
6695
2,
6696
6697
},
6698
{ "set",
6699
"arrayName list",
6700
array_cmd_set,
6701
2,
6702
2,
6703
6704
},
6705
{ "size",
6706
"arrayName",
6707
array_cmd_size,
6708
1,
6709
1,
6710
6711
},
6712
{ "stat",
6713
"arrayName",
6714
array_cmd_stat,
6715
1,
6716
1,
6717
6718
},
6719
{ "unset",
6720
"arrayName ?pattern?",
6721
array_cmd_unset,
6722
1,
6723
2,
6724
6725
},
6726
{ NULL
6727
}
6728
};
6729
6730
int Jim_arrayInit(Jim_Interp *interp)
6731
{
6732
Jim_PackageProvideCheck(interp, "array");
6733
Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
6734
return JIM_OK;
6735
}
6736
6737
#include <sys/types.h>
6738
#include <sys/time.h>
6739
#include <sys/wait.h>
6740
#include <unistd.h>
6741
#include <string.h>
6742
#include <errno.h>
6743
6744
6745
#ifdef HAVE_SYS_SYSINFO_H
6746
#include <sys/sysinfo.h>
6747
#endif
6748
6749
static void Jim_PosixSetError(Jim_Interp *interp)
6750
{
6751
Jim_SetResultString(interp, strerror(errno), -1);
6752
}
6753
6754
#if defined(HAVE_FORK)
6755
static int Jim_PosixForkCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
6756
{
6757
pid_t pid;
6758
6759
JIM_NOTUSED(argv);
6760
6761
if (argc != 1) {
6762
Jim_WrongNumArgs(interp, 1, argv, "");
6763
return JIM_ERR;
6764
}
6765
if ((pid = fork()) == -1) {
6766
Jim_PosixSetError(interp);
6767
return JIM_ERR;
6768
}
6769
Jim_SetResultInt(interp, (jim_wide) pid);
6770
return JIM_OK;
6771
}
6772
#endif
6773
6774
6775
int Jim_posixInit(Jim_Interp *interp)
6776
{
6777
Jim_PackageProvideCheck(interp, "posix");
6778
#ifdef HAVE_FORK
6779
Jim_CreateCommand(interp, "os.fork", Jim_PosixForkCommand, NULL, NULL);
6780
#endif
6781
return JIM_OK;
6782
}
6783
int Jim_InitStaticExtensions(Jim_Interp *interp)
6784
{
6785
extern int Jim_bootstrapInit(Jim_Interp *);
6786
extern int Jim_aioInit(Jim_Interp *);
6787
extern int Jim_readdirInit(Jim_Interp *);
6788
extern int Jim_regexpInit(Jim_Interp *);
6789
extern int Jim_fileInit(Jim_Interp *);
6790
extern int Jim_globInit(Jim_Interp *);
6791
extern int Jim_execInit(Jim_Interp *);
6792
extern int Jim_posixInit(Jim_Interp *);
6793
extern int Jim_clockInit(Jim_Interp *);
6794
extern int Jim_arrayInit(Jim_Interp *);
6795
extern int Jim_stdlibInit(Jim_Interp *);
6796
extern int Jim_tclcompatInit(Jim_Interp *);
6797
Jim_bootstrapInit(interp);
6798
Jim_aioInit(interp);
6799
Jim_readdirInit(interp);
6800
Jim_regexpInit(interp);
6801
Jim_fileInit(interp);
6802
Jim_globInit(interp);
6803
Jim_execInit(interp);
6804
Jim_posixInit(interp);
6805
Jim_clockInit(interp);
6806
Jim_arrayInit(interp);
6807
Jim_stdlibInit(interp);
6808
Jim_tclcompatInit(interp);
6809
return JIM_OK;
6810
}
6811
#ifndef JIM_TINY
6812
#define JIM_OPTIMIZATION
6813
#endif
6814
6815
#include <stdio.h>
6816
#include <stdlib.h>
6817
6818
#include <string.h>
6819
#include <stdarg.h>
6820
#include <ctype.h>
6821
#include <limits.h>
6822
#include <assert.h>
6823
#include <errno.h>
6824
#include <time.h>
6825
#include <setjmp.h>
6826
6827
6828
#ifdef HAVE_SYS_TIME_H
6829
#include <sys/time.h>
6830
#endif
6831
#ifdef HAVE_EXECINFO_H
6832
#include <execinfo.h>
6833
#endif
6834
#ifdef HAVE_CRT_EXTERNS_H
6835
#include <crt_externs.h>
6836
#endif
6837
6838
6839
#include <math.h>
6840
6841
6842
6843
6844
6845
#ifndef TCL_LIBRARY
6846
#define TCL_LIBRARY "."
6847
#endif
6848
#ifndef TCL_PLATFORM_OS
6849
#define TCL_PLATFORM_OS "unknown"
6850
#endif
6851
#ifndef TCL_PLATFORM_PLATFORM
6852
#define TCL_PLATFORM_PLATFORM "unknown"
6853
#endif
6854
#ifndef TCL_PLATFORM_PATH_SEPARATOR
6855
#define TCL_PLATFORM_PATH_SEPARATOR ":"
6856
#endif
6857
6858
6859
6860
6861
6862
6863
6864
#ifdef JIM_MAINTAINER
6865
#define JIM_DEBUG_COMMAND
6866
#define JIM_DEBUG_PANIC
6867
#endif
6868
6869
6870
6871
#define JIM_INTEGER_SPACE 24
6872
6873
#if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST)
6874
static const char *jim_tt_name(int type);
6875
#endif
6876
6877
#ifdef JIM_DEBUG_PANIC
6878
static void JimPanicDump(int fail_condition, const char *fmt, ...);
6879
#define JimPanic(X) JimPanicDump X
6880
#else
6881
#define JimPanic(X)
6882
#endif
6883
6884
#ifdef JIM_OPTIMIZATION
6885
static int JimIsWide(Jim_Obj *objPtr);
6886
#define JIM_IF_OPTIM(X) X
6887
#else
6888
#define JIM_IF_OPTIM(X)
6889
#endif
6890
6891
6892
static char JimEmptyStringRep[] = "";
6893
6894
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
6895
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
6896
int flags);
6897
static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *const *indexv, int indexc,
6898
Jim_Obj **resultObj, int flags);
6899
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
6900
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
6901
static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6902
static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
6903
const char *prefix, const char *const *tablePtr, const char *name);
6904
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
6905
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
6906
static int JimSign(jim_wide w);
6907
static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
6908
static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
6909
static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv);
6910
static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr);
6911
static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6912
6913
#define JIM_DICT_SUGAR 100
6914
6915
6916
6917
6918
#define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
6919
6920
#define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
6921
6922
static int utf8_tounicode_case(const char *s, int *uc, int upper)
6923
{
6924
int l = utf8_tounicode(s, uc);
6925
if (upper) {
6926
*uc = utf8_upper(*uc);
6927
}
6928
return l;
6929
}
6930
6931
static Jim_Obj *JimPushInterpObjImpl(Jim_Obj **iop, Jim_Obj *no)
6932
{
6933
Jim_Obj *io = *iop;
6934
Jim_IncrRefCount(no);
6935
*iop = no;
6936
return io;
6937
}
6938
6939
#define JimPushInterpObj(IO, NO) JimPushInterpObjImpl(&(IO), NO)
6940
#define JimPopInterpObj(I, IO, SO) do { Jim_DecrRefCount(I, IO); IO = SO; } while (0)
6941
6942
6943
#define JIM_CHARSET_SCAN 2
6944
#define JIM_CHARSET_GLOB 0
6945
6946
static const char *JimCharsetMatch(const char *pattern, int plen, int c, int flags)
6947
{
6948
int not = 0;
6949
int pchar;
6950
int match = 0;
6951
int nocase = 0;
6952
int n;
6953
6954
if (flags & JIM_NOCASE) {
6955
nocase++;
6956
c = utf8_upper(c);
6957
}
6958
6959
if (flags & JIM_CHARSET_SCAN) {
6960
if (*pattern == '^') {
6961
not++;
6962
pattern++;
6963
plen--;
6964
}
6965
6966
6967
if (*pattern == ']') {
6968
goto first;
6969
}
6970
}
6971
6972
while (plen && *pattern != ']') {
6973
6974
if (pattern[0] == '\\') {
6975
first:
6976
n = utf8_tounicode_case(pattern, &pchar, nocase);
6977
pattern += n;
6978
plen -= n;
6979
}
6980
else {
6981
6982
int start;
6983
int end;
6984
6985
n = utf8_tounicode_case(pattern, &start, nocase);
6986
pattern += n;
6987
plen -= n;
6988
if (pattern[0] == '-' && plen > 1) {
6989
6990
n = 1 + utf8_tounicode_case(pattern + 1, &end, nocase);
6991
pattern += n;
6992
plen -= n;
6993
6994
6995
if ((c >= start && c <= end) || (c >= end && c <= start)) {
6996
match = 1;
6997
}
6998
continue;
6999
}
7000
pchar = start;
7001
}
7002
7003
if (pchar == c) {
7004
match = 1;
7005
}
7006
}
7007
if (not) {
7008
match = !match;
7009
}
7010
7011
return match ? pattern : NULL;
7012
}
7013
7014
7015
7016
static int JimGlobMatch(const char *pattern, int plen, const char *string, int slen, int nocase)
7017
{
7018
int c;
7019
int pchar;
7020
int n;
7021
const char *p;
7022
while (plen) {
7023
switch (pattern[0]) {
7024
case '*':
7025
while (pattern[1] == '*' && plen) {
7026
pattern++;
7027
plen--;
7028
}
7029
pattern++;
7030
plen--;
7031
if (!plen) {
7032
return 1;
7033
}
7034
while (slen) {
7035
7036
if (JimGlobMatch(pattern, plen, string, slen, nocase))
7037
return 1;
7038
n = utf8_tounicode(string, &c);
7039
string += n;
7040
slen -= n;
7041
}
7042
return 0;
7043
7044
case '?':
7045
n = utf8_tounicode(string, &c);
7046
string += n;
7047
slen -= n;
7048
break;
7049
7050
case '[': {
7051
n = utf8_tounicode(string, &c);
7052
string += n;
7053
slen -= n;
7054
p = JimCharsetMatch(pattern + 1, plen - 1, c, nocase ? JIM_NOCASE : 0);
7055
if (!p) {
7056
return 0;
7057
}
7058
plen -= p - pattern;
7059
pattern = p;
7060
7061
if (!plen) {
7062
7063
continue;
7064
}
7065
break;
7066
}
7067
case '\\':
7068
if (pattern[1]) {
7069
pattern++;
7070
plen--;
7071
}
7072
7073
default:
7074
n = utf8_tounicode_case(string, &c, nocase);
7075
string += n;
7076
slen -= n;
7077
utf8_tounicode_case(pattern, &pchar, nocase);
7078
if (pchar != c) {
7079
return 0;
7080
}
7081
break;
7082
}
7083
n = utf8_tounicode_case(pattern, &pchar, nocase);
7084
pattern += n;
7085
plen -= n;
7086
if (!slen) {
7087
while (*pattern == '*' && plen) {
7088
pattern++;
7089
plen--;
7090
}
7091
break;
7092
}
7093
}
7094
if (!plen && !slen) {
7095
return 1;
7096
}
7097
return 0;
7098
}
7099
7100
static int JimStringCompareUtf8(const char *s1, int l1, const char *s2, int l2, int nocase)
7101
{
7102
int minlen = l1;
7103
if (l2 < l1) {
7104
minlen = l2;
7105
}
7106
while (minlen) {
7107
int c1, c2;
7108
s1 += utf8_tounicode_case(s1, &c1, nocase);
7109
s2 += utf8_tounicode_case(s2, &c2, nocase);
7110
if (c1 != c2) {
7111
return JimSign(c1 - c2);
7112
}
7113
minlen--;
7114
}
7115
7116
if (l1 < l2) {
7117
return -1;
7118
}
7119
if (l1 > l2) {
7120
return 1;
7121
}
7122
return 0;
7123
}
7124
7125
static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
7126
{
7127
int i;
7128
int l1bytelen;
7129
7130
if (!l1 || !l2 || l1 > l2) {
7131
return -1;
7132
}
7133
if (idx < 0)
7134
idx = 0;
7135
s2 += utf8_index(s2, idx);
7136
7137
l1bytelen = utf8_index(s1, l1);
7138
7139
for (i = idx; i <= l2 - l1; i++) {
7140
int c;
7141
if (memcmp(s2, s1, l1bytelen) == 0) {
7142
return i;
7143
}
7144
s2 += utf8_tounicode(s2, &c);
7145
}
7146
return -1;
7147
}
7148
7149
static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
7150
{
7151
const char *p;
7152
7153
if (!l1 || !l2 || l1 > l2)
7154
return -1;
7155
7156
7157
for (p = s2 + l2 - 1; p != s2 - 1; p--) {
7158
if (*p == *s1 && memcmp(s1, p, l1) == 0) {
7159
return p - s2;
7160
}
7161
}
7162
return -1;
7163
}
7164
7165
#ifdef JIM_UTF8
7166
static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
7167
{
7168
int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
7169
if (n > 0) {
7170
n = utf8_strlen(s2, n);
7171
}
7172
return n;
7173
}
7174
#endif
7175
7176
static int JimCheckConversion(const char *str, const char *endptr)
7177
{
7178
if (str[0] == '\0' || str == endptr) {
7179
return JIM_ERR;
7180
}
7181
7182
if (endptr[0] != '\0') {
7183
while (*endptr) {
7184
if (!isspace(UCHAR(*endptr))) {
7185
return JIM_ERR;
7186
}
7187
endptr++;
7188
}
7189
}
7190
return JIM_OK;
7191
}
7192
7193
static int JimNumberBase(const char *str, int *base, int *sign)
7194
{
7195
int i = 0;
7196
7197
*base = 0;
7198
7199
while (isspace(UCHAR(str[i]))) {
7200
i++;
7201
}
7202
7203
if (str[i] == '-') {
7204
*sign = -1;
7205
i++;
7206
}
7207
else {
7208
if (str[i] == '+') {
7209
i++;
7210
}
7211
*sign = 1;
7212
}
7213
7214
if (str[i] != '0') {
7215
7216
return 0;
7217
}
7218
7219
7220
switch (str[i + 1]) {
7221
case 'x': case 'X': *base = 16; break;
7222
case 'o': case 'O': *base = 8; break;
7223
case 'b': case 'B': *base = 2; break;
7224
case 'd': case 'D': *base = 10; break;
7225
default: return 0;
7226
}
7227
i += 2;
7228
7229
if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
7230
7231
return i;
7232
}
7233
7234
*base = 0;
7235
return 0;
7236
}
7237
7238
static long jim_strtol(const char *str, char **endptr)
7239
{
7240
int sign;
7241
int base;
7242
int i = JimNumberBase(str, &base, &sign);
7243
7244
if (base != 0) {
7245
long value = strtol(str + i, endptr, base);
7246
if (endptr == NULL || *endptr != str + i) {
7247
return value * sign;
7248
}
7249
}
7250
7251
7252
return strtol(str, endptr, 10);
7253
}
7254
7255
7256
static jim_wide jim_strtoull(const char *str, char **endptr)
7257
{
7258
#ifdef HAVE_LONG_LONG
7259
int sign;
7260
int base;
7261
int i = JimNumberBase(str, &base, &sign);
7262
7263
if (base != 0) {
7264
jim_wide value = strtoull(str + i, endptr, base);
7265
if (endptr == NULL || *endptr != str + i) {
7266
return value * sign;
7267
}
7268
}
7269
7270
7271
return strtoull(str, endptr, 10);
7272
#else
7273
return (unsigned long)jim_strtol(str, endptr);
7274
#endif
7275
}
7276
7277
int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
7278
{
7279
char *endptr;
7280
7281
if (base) {
7282
*widePtr = strtoull(str, &endptr, base);
7283
}
7284
else {
7285
*widePtr = jim_strtoull(str, &endptr);
7286
}
7287
7288
return JimCheckConversion(str, endptr);
7289
}
7290
7291
int Jim_StringToDouble(const char *str, double *doublePtr)
7292
{
7293
char *endptr;
7294
7295
7296
errno = 0;
7297
7298
*doublePtr = strtod(str, &endptr);
7299
7300
return JimCheckConversion(str, endptr);
7301
}
7302
7303
static jim_wide JimPowWide(jim_wide b, jim_wide e)
7304
{
7305
jim_wide res = 1;
7306
7307
7308
if (b == 1) {
7309
7310
return 1;
7311
}
7312
if (e < 0) {
7313
if (b != -1) {
7314
return 0;
7315
}
7316
e = -e;
7317
}
7318
while (e)
7319
{
7320
if (e & 1) {
7321
res *= b;
7322
}
7323
e >>= 1;
7324
b *= b;
7325
}
7326
return res;
7327
}
7328
7329
#ifdef JIM_DEBUG_PANIC
7330
static void JimPanicDump(int condition, const char *fmt, ...)
7331
{
7332
va_list ap;
7333
7334
if (!condition) {
7335
return;
7336
}
7337
7338
va_start(ap, fmt);
7339
7340
fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
7341
vfprintf(stderr, fmt, ap);
7342
fprintf(stderr, "\n\n");
7343
va_end(ap);
7344
7345
#if defined(HAVE_BACKTRACE)
7346
{
7347
void *array[40];
7348
int size, i;
7349
char **strings;
7350
7351
size = backtrace(array, 40);
7352
strings = backtrace_symbols(array, size);
7353
for (i = 0; i < size; i++)
7354
fprintf(stderr, "[backtrace] %s\n", strings[i]);
7355
fprintf(stderr, "[backtrace] Include the above lines and the output\n");
7356
fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
7357
}
7358
#endif
7359
7360
exit(1);
7361
}
7362
#endif
7363
7364
7365
void *JimDefaultAllocator(void *ptr, size_t size)
7366
{
7367
if (size == 0) {
7368
free(ptr);
7369
return NULL;
7370
}
7371
else if (ptr) {
7372
return realloc(ptr, size);
7373
}
7374
else {
7375
return malloc(size);
7376
}
7377
}
7378
7379
void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator;
7380
7381
char *Jim_StrDup(const char *s)
7382
{
7383
return Jim_StrDupLen(s, strlen(s));
7384
}
7385
7386
char *Jim_StrDupLen(const char *s, int l)
7387
{
7388
char *copy = Jim_Alloc(l + 1);
7389
7390
memcpy(copy, s, l);
7391
copy[l] = 0;
7392
return copy;
7393
}
7394
7395
7396
jim_wide Jim_GetTimeUsec(unsigned type)
7397
{
7398
long long now;
7399
struct timeval tv;
7400
7401
#if defined(HAVE_CLOCK_GETTIME)
7402
struct timespec ts;
7403
7404
if (clock_gettime(type, &ts) == 0) {
7405
now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
7406
}
7407
else
7408
#endif
7409
{
7410
gettimeofday(&tv, NULL);
7411
7412
now = tv.tv_sec * 1000000LL + tv.tv_usec;
7413
}
7414
7415
return now;
7416
}
7417
7418
7419
7420
7421
7422
static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
7423
static unsigned int JimHashTableNextPower(unsigned int size);
7424
static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
7425
7426
7427
7428
7429
unsigned int Jim_IntHashFunction(unsigned int key)
7430
{
7431
key += ~(key << 15);
7432
key ^= (key >> 10);
7433
key += (key << 3);
7434
key ^= (key >> 6);
7435
key += ~(key << 11);
7436
key ^= (key >> 16);
7437
return key;
7438
}
7439
7440
7441
unsigned int Jim_GenHashFunction(const unsigned char *string, int length)
7442
{
7443
unsigned result = 0;
7444
string += length;
7445
while (length--) {
7446
result += (result << 3) + (unsigned char)(*--string);
7447
}
7448
return result;
7449
}
7450
7451
7452
7453
static void JimResetHashTable(Jim_HashTable *ht)
7454
{
7455
ht->table = NULL;
7456
ht->size = 0;
7457
ht->sizemask = 0;
7458
ht->used = 0;
7459
ht->collisions = 0;
7460
#ifdef JIM_RANDOMISE_HASH
7461
ht->uniq = (rand() ^ time(NULL) ^ clock());
7462
#else
7463
ht->uniq = 0;
7464
#endif
7465
}
7466
7467
static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
7468
{
7469
iter->ht = ht;
7470
iter->index = -1;
7471
iter->entry = NULL;
7472
iter->nextEntry = NULL;
7473
}
7474
7475
7476
int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
7477
{
7478
JimResetHashTable(ht);
7479
ht->type = type;
7480
ht->privdata = privDataPtr;
7481
return JIM_OK;
7482
}
7483
7484
7485
void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
7486
{
7487
Jim_HashTable n;
7488
unsigned int realsize = JimHashTableNextPower(size), i;
7489
7490
if (size <= ht->used)
7491
return;
7492
7493
Jim_InitHashTable(&n, ht->type, ht->privdata);
7494
n.size = realsize;
7495
n.sizemask = realsize - 1;
7496
n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
7497
7498
n.uniq = ht->uniq;
7499
7500
7501
memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
7502
7503
n.used = ht->used;
7504
for (i = 0; ht->used > 0; i++) {
7505
Jim_HashEntry *he, *nextHe;
7506
7507
if (ht->table[i] == NULL)
7508
continue;
7509
7510
7511
he = ht->table[i];
7512
while (he) {
7513
unsigned int h;
7514
7515
nextHe = he->next;
7516
7517
h = Jim_HashKey(ht, he->key) & n.sizemask;
7518
he->next = n.table[h];
7519
n.table[h] = he;
7520
ht->used--;
7521
7522
he = nextHe;
7523
}
7524
}
7525
assert(ht->used == 0);
7526
Jim_Free(ht->table);
7527
7528
7529
*ht = n;
7530
}
7531
7532
int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
7533
{
7534
Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);;
7535
if (entry == NULL)
7536
return JIM_ERR;
7537
7538
7539
Jim_SetHashKey(ht, entry, key);
7540
Jim_SetHashVal(ht, entry, val);
7541
return JIM_OK;
7542
}
7543
7544
7545
int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
7546
{
7547
int existed;
7548
Jim_HashEntry *entry;
7549
7550
entry = JimInsertHashEntry(ht, key, 1);
7551
if (entry->key) {
7552
if (ht->type->valDestructor && ht->type->valDup) {
7553
void *newval = ht->type->valDup(ht->privdata, val);
7554
ht->type->valDestructor(ht->privdata, entry->u.val);
7555
entry->u.val = newval;
7556
}
7557
else {
7558
Jim_FreeEntryVal(ht, entry);
7559
Jim_SetHashVal(ht, entry, val);
7560
}
7561
existed = 1;
7562
}
7563
else {
7564
7565
Jim_SetHashKey(ht, entry, key);
7566
Jim_SetHashVal(ht, entry, val);
7567
existed = 0;
7568
}
7569
7570
return existed;
7571
}
7572
7573
int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
7574
{
7575
if (ht->used) {
7576
unsigned int h = Jim_HashKey(ht, key) & ht->sizemask;
7577
Jim_HashEntry *prevHe = NULL;
7578
Jim_HashEntry *he = ht->table[h];
7579
7580
while (he) {
7581
if (Jim_CompareHashKeys(ht, key, he->key)) {
7582
7583
if (prevHe)
7584
prevHe->next = he->next;
7585
else
7586
ht->table[h] = he->next;
7587
ht->used--;
7588
Jim_FreeEntryKey(ht, he);
7589
Jim_FreeEntryVal(ht, he);
7590
Jim_Free(he);
7591
return JIM_OK;
7592
}
7593
prevHe = he;
7594
he = he->next;
7595
}
7596
}
7597
7598
return JIM_ERR;
7599
}
7600
7601
void Jim_ClearHashTable(Jim_HashTable *ht)
7602
{
7603
unsigned int i;
7604
7605
7606
for (i = 0; ht->used > 0; i++) {
7607
Jim_HashEntry *he, *nextHe;
7608
7609
he = ht->table[i];
7610
while (he) {
7611
nextHe = he->next;
7612
Jim_FreeEntryKey(ht, he);
7613
Jim_FreeEntryVal(ht, he);
7614
Jim_Free(he);
7615
ht->used--;
7616
he = nextHe;
7617
}
7618
ht->table[i] = NULL;
7619
}
7620
}
7621
7622
int Jim_FreeHashTable(Jim_HashTable *ht)
7623
{
7624
Jim_ClearHashTable(ht);
7625
7626
Jim_Free(ht->table);
7627
7628
JimResetHashTable(ht);
7629
return JIM_OK;
7630
}
7631
7632
Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
7633
{
7634
Jim_HashEntry *he;
7635
unsigned int h;
7636
7637
if (ht->used == 0)
7638
return NULL;
7639
h = Jim_HashKey(ht, key) & ht->sizemask;
7640
he = ht->table[h];
7641
while (he) {
7642
if (Jim_CompareHashKeys(ht, key, he->key))
7643
return he;
7644
he = he->next;
7645
}
7646
return NULL;
7647
}
7648
7649
Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
7650
{
7651
Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
7652
JimInitHashTableIterator(ht, iter);
7653
return iter;
7654
}
7655
7656
Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
7657
{
7658
while (1) {
7659
if (iter->entry == NULL) {
7660
iter->index++;
7661
if (iter->index >= (signed)iter->ht->size)
7662
break;
7663
iter->entry = iter->ht->table[iter->index];
7664
}
7665
else {
7666
iter->entry = iter->nextEntry;
7667
}
7668
if (iter->entry) {
7669
iter->nextEntry = iter->entry->next;
7670
return iter->entry;
7671
}
7672
}
7673
return NULL;
7674
}
7675
7676
7677
7678
7679
static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
7680
{
7681
if (ht->size == 0)
7682
Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
7683
if (ht->size == ht->used)
7684
Jim_ExpandHashTable(ht, ht->size * 2);
7685
}
7686
7687
7688
static unsigned int JimHashTableNextPower(unsigned int size)
7689
{
7690
unsigned int i = JIM_HT_INITIAL_SIZE;
7691
7692
if (size >= 2147483648U)
7693
return 2147483648U;
7694
while (1) {
7695
if (i >= size)
7696
return i;
7697
i *= 2;
7698
}
7699
}
7700
7701
static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
7702
{
7703
unsigned int h;
7704
Jim_HashEntry *he;
7705
7706
7707
JimExpandHashTableIfNeeded(ht);
7708
7709
7710
h = Jim_HashKey(ht, key) & ht->sizemask;
7711
7712
he = ht->table[h];
7713
while (he) {
7714
if (Jim_CompareHashKeys(ht, key, he->key))
7715
return replace ? he : NULL;
7716
he = he->next;
7717
}
7718
7719
7720
he = Jim_Alloc(sizeof(*he));
7721
he->next = ht->table[h];
7722
ht->table[h] = he;
7723
ht->used++;
7724
he->key = NULL;
7725
7726
return he;
7727
}
7728
7729
7730
7731
static unsigned int JimStringCopyHTHashFunction(const void *key)
7732
{
7733
return Jim_GenHashFunction(key, strlen(key));
7734
}
7735
7736
static void *JimStringCopyHTDup(void *privdata, const void *key)
7737
{
7738
return Jim_StrDup(key);
7739
}
7740
7741
static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
7742
{
7743
return strcmp(key1, key2) == 0;
7744
}
7745
7746
static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
7747
{
7748
Jim_Free(key);
7749
}
7750
7751
static const Jim_HashTableType JimPackageHashTableType = {
7752
JimStringCopyHTHashFunction,
7753
JimStringCopyHTDup,
7754
NULL,
7755
JimStringCopyHTKeyCompare,
7756
JimStringCopyHTKeyDestructor,
7757
NULL
7758
};
7759
7760
typedef struct AssocDataValue
7761
{
7762
Jim_InterpDeleteProc *delProc;
7763
void *data;
7764
} AssocDataValue;
7765
7766
static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
7767
{
7768
AssocDataValue *assocPtr = (AssocDataValue *) data;
7769
7770
if (assocPtr->delProc != NULL)
7771
assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
7772
Jim_Free(data);
7773
}
7774
7775
static const Jim_HashTableType JimAssocDataHashTableType = {
7776
JimStringCopyHTHashFunction,
7777
JimStringCopyHTDup,
7778
NULL,
7779
JimStringCopyHTKeyCompare,
7780
JimStringCopyHTKeyDestructor,
7781
JimAssocDataHashTableValueDestructor
7782
};
7783
7784
void Jim_InitStack(Jim_Stack *stack)
7785
{
7786
stack->len = 0;
7787
stack->maxlen = 0;
7788
stack->vector = NULL;
7789
}
7790
7791
void Jim_FreeStack(Jim_Stack *stack)
7792
{
7793
Jim_Free(stack->vector);
7794
}
7795
7796
int Jim_StackLen(Jim_Stack *stack)
7797
{
7798
return stack->len;
7799
}
7800
7801
void Jim_StackPush(Jim_Stack *stack, void *element)
7802
{
7803
int neededLen = stack->len + 1;
7804
7805
if (neededLen > stack->maxlen) {
7806
stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
7807
stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
7808
}
7809
stack->vector[stack->len] = element;
7810
stack->len++;
7811
}
7812
7813
void *Jim_StackPop(Jim_Stack *stack)
7814
{
7815
if (stack->len == 0)
7816
return NULL;
7817
stack->len--;
7818
return stack->vector[stack->len];
7819
}
7820
7821
void *Jim_StackPeek(Jim_Stack *stack)
7822
{
7823
if (stack->len == 0)
7824
return NULL;
7825
return stack->vector[stack->len - 1];
7826
}
7827
7828
void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
7829
{
7830
int i;
7831
7832
for (i = 0; i < stack->len; i++)
7833
freeFunc(stack->vector[i]);
7834
}
7835
7836
7837
7838
#define JIM_TT_NONE 0
7839
#define JIM_TT_STR 1
7840
#define JIM_TT_ESC 2
7841
#define JIM_TT_VAR 3
7842
#define JIM_TT_DICTSUGAR 4
7843
#define JIM_TT_CMD 5
7844
7845
#define JIM_TT_SEP 6
7846
#define JIM_TT_EOL 7
7847
#define JIM_TT_EOF 8
7848
7849
#define JIM_TT_LINE 9
7850
#define JIM_TT_WORD 10
7851
7852
7853
#define JIM_TT_SUBEXPR_START 11
7854
#define JIM_TT_SUBEXPR_END 12
7855
#define JIM_TT_SUBEXPR_COMMA 13
7856
#define JIM_TT_EXPR_INT 14
7857
#define JIM_TT_EXPR_DOUBLE 15
7858
#define JIM_TT_EXPR_BOOLEAN 16
7859
7860
#define JIM_TT_EXPRSUGAR 17
7861
7862
7863
#define JIM_TT_EXPR_OP 20
7864
7865
#define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
7866
7867
#define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
7868
7869
#define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
7870
7871
struct JimParseMissing {
7872
int ch;
7873
int line;
7874
};
7875
7876
struct JimParserCtx
7877
{
7878
const char *p;
7879
int len;
7880
int linenr;
7881
const char *tstart;
7882
const char *tend;
7883
int tline;
7884
int tt;
7885
int eof;
7886
int inquote;
7887
int comment;
7888
struct JimParseMissing missing;
7889
const char *errmsg;
7890
};
7891
7892
static int JimParseScript(struct JimParserCtx *pc);
7893
static int JimParseSep(struct JimParserCtx *pc);
7894
static int JimParseEol(struct JimParserCtx *pc);
7895
static int JimParseCmd(struct JimParserCtx *pc);
7896
static int JimParseQuote(struct JimParserCtx *pc);
7897
static int JimParseVar(struct JimParserCtx *pc);
7898
static int JimParseBrace(struct JimParserCtx *pc);
7899
static int JimParseStr(struct JimParserCtx *pc);
7900
static int JimParseComment(struct JimParserCtx *pc);
7901
static void JimParseSubCmd(struct JimParserCtx *pc);
7902
static int JimParseSubQuote(struct JimParserCtx *pc);
7903
static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7904
7905
static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7906
{
7907
pc->p = prg;
7908
pc->len = len;
7909
pc->tstart = NULL;
7910
pc->tend = NULL;
7911
pc->tline = 0;
7912
pc->tt = JIM_TT_NONE;
7913
pc->eof = 0;
7914
pc->inquote = 0;
7915
pc->linenr = linenr;
7916
pc->comment = 1;
7917
pc->missing.ch = ' ';
7918
pc->missing.line = linenr;
7919
}
7920
7921
static int JimParseScript(struct JimParserCtx *pc)
7922
{
7923
while (1) {
7924
if (!pc->len) {
7925
pc->tstart = pc->p;
7926
pc->tend = pc->p - 1;
7927
pc->tline = pc->linenr;
7928
pc->tt = JIM_TT_EOL;
7929
if (pc->inquote) {
7930
pc->missing.ch = '"';
7931
}
7932
pc->eof = 1;
7933
return JIM_OK;
7934
}
7935
switch (*(pc->p)) {
7936
case '\\':
7937
if (*(pc->p + 1) == '\n' && !pc->inquote) {
7938
return JimParseSep(pc);
7939
}
7940
pc->comment = 0;
7941
return JimParseStr(pc);
7942
case ' ':
7943
case '\t':
7944
case '\r':
7945
case '\f':
7946
if (!pc->inquote)
7947
return JimParseSep(pc);
7948
pc->comment = 0;
7949
return JimParseStr(pc);
7950
case '\n':
7951
case ';':
7952
pc->comment = 1;
7953
if (!pc->inquote)
7954
return JimParseEol(pc);
7955
return JimParseStr(pc);
7956
case '[':
7957
pc->comment = 0;
7958
return JimParseCmd(pc);
7959
case '$':
7960
pc->comment = 0;
7961
if (JimParseVar(pc) == JIM_ERR) {
7962
7963
pc->tstart = pc->tend = pc->p++;
7964
pc->len--;
7965
pc->tt = JIM_TT_ESC;
7966
}
7967
return JIM_OK;
7968
case '#':
7969
if (pc->comment) {
7970
JimParseComment(pc);
7971
continue;
7972
}
7973
return JimParseStr(pc);
7974
default:
7975
pc->comment = 0;
7976
return JimParseStr(pc);
7977
}
7978
return JIM_OK;
7979
}
7980
}
7981
7982
static int JimParseSep(struct JimParserCtx *pc)
7983
{
7984
pc->tstart = pc->p;
7985
pc->tline = pc->linenr;
7986
while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
7987
if (*pc->p == '\n') {
7988
break;
7989
}
7990
if (*pc->p == '\\') {
7991
pc->p++;
7992
pc->len--;
7993
pc->linenr++;
7994
}
7995
pc->p++;
7996
pc->len--;
7997
}
7998
pc->tend = pc->p - 1;
7999
pc->tt = JIM_TT_SEP;
8000
return JIM_OK;
8001
}
8002
8003
static int JimParseEol(struct JimParserCtx *pc)
8004
{
8005
pc->tstart = pc->p;
8006
pc->tline = pc->linenr;
8007
while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
8008
if (*pc->p == '\n')
8009
pc->linenr++;
8010
pc->p++;
8011
pc->len--;
8012
}
8013
pc->tend = pc->p - 1;
8014
pc->tt = JIM_TT_EOL;
8015
return JIM_OK;
8016
}
8017
8018
8019
static void JimParseSubBrace(struct JimParserCtx *pc)
8020
{
8021
int level = 1;
8022
8023
8024
pc->p++;
8025
pc->len--;
8026
while (pc->len) {
8027
switch (*pc->p) {
8028
case '\\':
8029
if (pc->len > 1) {
8030
if (*++pc->p == '\n') {
8031
pc->linenr++;
8032
}
8033
pc->len--;
8034
}
8035
break;
8036
8037
case '{':
8038
level++;
8039
break;
8040
8041
case '}':
8042
if (--level == 0) {
8043
pc->tend = pc->p - 1;
8044
pc->p++;
8045
pc->len--;
8046
return;
8047
}
8048
break;
8049
8050
case '\n':
8051
pc->linenr++;
8052
break;
8053
}
8054
pc->p++;
8055
pc->len--;
8056
}
8057
pc->missing.ch = '{';
8058
pc->missing.line = pc->tline;
8059
pc->tend = pc->p - 1;
8060
}
8061
8062
static int JimParseSubQuote(struct JimParserCtx *pc)
8063
{
8064
int tt = JIM_TT_STR;
8065
int line = pc->tline;
8066
8067
8068
pc->p++;
8069
pc->len--;
8070
while (pc->len) {
8071
switch (*pc->p) {
8072
case '\\':
8073
if (pc->len > 1) {
8074
if (*++pc->p == '\n') {
8075
pc->linenr++;
8076
}
8077
pc->len--;
8078
tt = JIM_TT_ESC;
8079
}
8080
break;
8081
8082
case '"':
8083
pc->tend = pc->p - 1;
8084
pc->p++;
8085
pc->len--;
8086
return tt;
8087
8088
case '[':
8089
JimParseSubCmd(pc);
8090
tt = JIM_TT_ESC;
8091
continue;
8092
8093
case '\n':
8094
pc->linenr++;
8095
break;
8096
8097
case '$':
8098
tt = JIM_TT_ESC;
8099
break;
8100
}
8101
pc->p++;
8102
pc->len--;
8103
}
8104
pc->missing.ch = '"';
8105
pc->missing.line = line;
8106
pc->tend = pc->p - 1;
8107
return tt;
8108
}
8109
8110
static void JimParseSubCmd(struct JimParserCtx *pc)
8111
{
8112
int level = 1;
8113
int startofword = 1;
8114
int line = pc->tline;
8115
8116
8117
pc->p++;
8118
pc->len--;
8119
while (pc->len) {
8120
switch (*pc->p) {
8121
case '\\':
8122
if (pc->len > 1) {
8123
if (*++pc->p == '\n') {
8124
pc->linenr++;
8125
}
8126
pc->len--;
8127
}
8128
break;
8129
8130
case '[':
8131
level++;
8132
break;
8133
8134
case ']':
8135
if (--level == 0) {
8136
pc->tend = pc->p - 1;
8137
pc->p++;
8138
pc->len--;
8139
return;
8140
}
8141
break;
8142
8143
case '"':
8144
if (startofword) {
8145
JimParseSubQuote(pc);
8146
if (pc->missing.ch == '"') {
8147
return;
8148
}
8149
continue;
8150
}
8151
break;
8152
8153
case '{':
8154
JimParseSubBrace(pc);
8155
startofword = 0;
8156
continue;
8157
8158
case '\n':
8159
pc->linenr++;
8160
break;
8161
}
8162
startofword = isspace(UCHAR(*pc->p));
8163
pc->p++;
8164
pc->len--;
8165
}
8166
pc->missing.ch = '[';
8167
pc->missing.line = line;
8168
pc->tend = pc->p - 1;
8169
}
8170
8171
static int JimParseBrace(struct JimParserCtx *pc)
8172
{
8173
pc->tstart = pc->p + 1;
8174
pc->tline = pc->linenr;
8175
pc->tt = JIM_TT_STR;
8176
JimParseSubBrace(pc);
8177
return JIM_OK;
8178
}
8179
8180
static int JimParseCmd(struct JimParserCtx *pc)
8181
{
8182
pc->tstart = pc->p + 1;
8183
pc->tline = pc->linenr;
8184
pc->tt = JIM_TT_CMD;
8185
JimParseSubCmd(pc);
8186
return JIM_OK;
8187
}
8188
8189
static int JimParseQuote(struct JimParserCtx *pc)
8190
{
8191
pc->tstart = pc->p + 1;
8192
pc->tline = pc->linenr;
8193
pc->tt = JimParseSubQuote(pc);
8194
return JIM_OK;
8195
}
8196
8197
static int JimParseVar(struct JimParserCtx *pc)
8198
{
8199
8200
pc->p++;
8201
pc->len--;
8202
8203
#ifdef EXPRSUGAR_BRACKET
8204
if (*pc->p == '[') {
8205
8206
JimParseCmd(pc);
8207
pc->tt = JIM_TT_EXPRSUGAR;
8208
return JIM_OK;
8209
}
8210
#endif
8211
8212
pc->tstart = pc->p;
8213
pc->tt = JIM_TT_VAR;
8214
pc->tline = pc->linenr;
8215
8216
if (*pc->p == '{') {
8217
pc->tstart = ++pc->p;
8218
pc->len--;
8219
8220
while (pc->len && *pc->p != '}') {
8221
if (*pc->p == '\n') {
8222
pc->linenr++;
8223
}
8224
pc->p++;
8225
pc->len--;
8226
}
8227
pc->tend = pc->p - 1;
8228
if (pc->len) {
8229
pc->p++;
8230
pc->len--;
8231
}
8232
}
8233
else {
8234
while (1) {
8235
8236
if (pc->p[0] == ':' && pc->p[1] == ':') {
8237
while (*pc->p == ':') {
8238
pc->p++;
8239
pc->len--;
8240
}
8241
continue;
8242
}
8243
if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
8244
pc->p++;
8245
pc->len--;
8246
continue;
8247
}
8248
break;
8249
}
8250
8251
if (*pc->p == '(') {
8252
int count = 1;
8253
const char *paren = NULL;
8254
8255
pc->tt = JIM_TT_DICTSUGAR;
8256
8257
while (count && pc->len) {
8258
pc->p++;
8259
pc->len--;
8260
if (*pc->p == '\\' && pc->len >= 1) {
8261
pc->p++;
8262
pc->len--;
8263
}
8264
else if (*pc->p == '(') {
8265
count++;
8266
}
8267
else if (*pc->p == ')') {
8268
paren = pc->p;
8269
count--;
8270
}
8271
}
8272
if (count == 0) {
8273
pc->p++;
8274
pc->len--;
8275
}
8276
else if (paren) {
8277
8278
paren++;
8279
pc->len += (pc->p - paren);
8280
pc->p = paren;
8281
}
8282
#ifndef EXPRSUGAR_BRACKET
8283
if (*pc->tstart == '(') {
8284
pc->tt = JIM_TT_EXPRSUGAR;
8285
}
8286
#endif
8287
}
8288
pc->tend = pc->p - 1;
8289
}
8290
if (pc->tstart == pc->p) {
8291
pc->p--;
8292
pc->len++;
8293
return JIM_ERR;
8294
}
8295
return JIM_OK;
8296
}
8297
8298
static int JimParseStr(struct JimParserCtx *pc)
8299
{
8300
if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
8301
pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
8302
8303
if (*pc->p == '{') {
8304
return JimParseBrace(pc);
8305
}
8306
if (*pc->p == '"') {
8307
pc->inquote = 1;
8308
pc->p++;
8309
pc->len--;
8310
8311
pc->missing.line = pc->tline;
8312
}
8313
}
8314
pc->tstart = pc->p;
8315
pc->tline = pc->linenr;
8316
while (1) {
8317
if (pc->len == 0) {
8318
if (pc->inquote) {
8319
pc->missing.ch = '"';
8320
}
8321
pc->tend = pc->p - 1;
8322
pc->tt = JIM_TT_ESC;
8323
return JIM_OK;
8324
}
8325
switch (*pc->p) {
8326
case '\\':
8327
if (!pc->inquote && *(pc->p + 1) == '\n') {
8328
pc->tend = pc->p - 1;
8329
pc->tt = JIM_TT_ESC;
8330
return JIM_OK;
8331
}
8332
if (pc->len >= 2) {
8333
if (*(pc->p + 1) == '\n') {
8334
pc->linenr++;
8335
}
8336
pc->p++;
8337
pc->len--;
8338
}
8339
else if (pc->len == 1) {
8340
8341
pc->missing.ch = '\\';
8342
}
8343
break;
8344
case '(':
8345
8346
if (pc->len > 1 && pc->p[1] != '$') {
8347
break;
8348
}
8349
8350
case ')':
8351
8352
if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
8353
if (pc->p == pc->tstart) {
8354
8355
pc->p++;
8356
pc->len--;
8357
}
8358
pc->tend = pc->p - 1;
8359
pc->tt = JIM_TT_ESC;
8360
return JIM_OK;
8361
}
8362
break;
8363
8364
case '$':
8365
case '[':
8366
pc->tend = pc->p - 1;
8367
pc->tt = JIM_TT_ESC;
8368
return JIM_OK;
8369
case ' ':
8370
case '\t':
8371
case '\n':
8372
case '\r':
8373
case '\f':
8374
case ';':
8375
if (!pc->inquote) {
8376
pc->tend = pc->p - 1;
8377
pc->tt = JIM_TT_ESC;
8378
return JIM_OK;
8379
}
8380
else if (*pc->p == '\n') {
8381
pc->linenr++;
8382
}
8383
break;
8384
case '"':
8385
if (pc->inquote) {
8386
pc->tend = pc->p - 1;
8387
pc->tt = JIM_TT_ESC;
8388
pc->p++;
8389
pc->len--;
8390
pc->inquote = 0;
8391
return JIM_OK;
8392
}
8393
break;
8394
}
8395
pc->p++;
8396
pc->len--;
8397
}
8398
return JIM_OK;
8399
}
8400
8401
static int JimParseComment(struct JimParserCtx *pc)
8402
{
8403
while (*pc->p) {
8404
if (*pc->p == '\\') {
8405
pc->p++;
8406
pc->len--;
8407
if (pc->len == 0) {
8408
pc->missing.ch = '\\';
8409
return JIM_OK;
8410
}
8411
if (*pc->p == '\n') {
8412
pc->linenr++;
8413
}
8414
}
8415
else if (*pc->p == '\n') {
8416
pc->p++;
8417
pc->len--;
8418
pc->linenr++;
8419
break;
8420
}
8421
pc->p++;
8422
pc->len--;
8423
}
8424
return JIM_OK;
8425
}
8426
8427
8428
static int xdigitval(int c)
8429
{
8430
if (c >= '0' && c <= '9')
8431
return c - '0';
8432
if (c >= 'a' && c <= 'f')
8433
return c - 'a' + 10;
8434
if (c >= 'A' && c <= 'F')
8435
return c - 'A' + 10;
8436
return -1;
8437
}
8438
8439
static int odigitval(int c)
8440
{
8441
if (c >= '0' && c <= '7')
8442
return c - '0';
8443
return -1;
8444
}
8445
8446
static int JimEscape(char *dest, const char *s, int slen)
8447
{
8448
char *p = dest;
8449
int i, len;
8450
8451
for (i = 0; i < slen; i++) {
8452
switch (s[i]) {
8453
case '\\':
8454
switch (s[i + 1]) {
8455
case 'a':
8456
*p++ = 0x7;
8457
i++;
8458
break;
8459
case 'b':
8460
*p++ = 0x8;
8461
i++;
8462
break;
8463
case 'f':
8464
*p++ = 0xc;
8465
i++;
8466
break;
8467
case 'n':
8468
*p++ = 0xa;
8469
i++;
8470
break;
8471
case 'r':
8472
*p++ = 0xd;
8473
i++;
8474
break;
8475
case 't':
8476
*p++ = 0x9;
8477
i++;
8478
break;
8479
case 'u':
8480
case 'U':
8481
case 'x':
8482
{
8483
unsigned val = 0;
8484
int k;
8485
int maxchars = 2;
8486
8487
i++;
8488
8489
if (s[i] == 'U') {
8490
maxchars = 8;
8491
}
8492
else if (s[i] == 'u') {
8493
if (s[i + 1] == '{') {
8494
maxchars = 6;
8495
i++;
8496
}
8497
else {
8498
maxchars = 4;
8499
}
8500
}
8501
8502
for (k = 0; k < maxchars; k++) {
8503
int c = xdigitval(s[i + k + 1]);
8504
if (c == -1) {
8505
break;
8506
}
8507
val = (val << 4) | c;
8508
}
8509
8510
if (s[i] == '{') {
8511
if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
8512
8513
i--;
8514
k = 0;
8515
}
8516
else {
8517
8518
k++;
8519
}
8520
}
8521
if (k) {
8522
8523
if (s[i] == 'x') {
8524
*p++ = val;
8525
}
8526
else {
8527
p += utf8_fromunicode(p, val);
8528
}
8529
i += k;
8530
break;
8531
}
8532
8533
*p++ = s[i];
8534
}
8535
break;
8536
case 'v':
8537
*p++ = 0xb;
8538
i++;
8539
break;
8540
case '\0':
8541
*p++ = '\\';
8542
i++;
8543
break;
8544
case '\n':
8545
8546
*p++ = ' ';
8547
do {
8548
i++;
8549
} while (s[i + 1] == ' ' || s[i + 1] == '\t');
8550
break;
8551
case '0':
8552
case '1':
8553
case '2':
8554
case '3':
8555
case '4':
8556
case '5':
8557
case '6':
8558
case '7':
8559
8560
{
8561
int val = 0;
8562
int c = odigitval(s[i + 1]);
8563
8564
val = c;
8565
c = odigitval(s[i + 2]);
8566
if (c == -1) {
8567
*p++ = val;
8568
i++;
8569
break;
8570
}
8571
val = (val * 8) + c;
8572
c = odigitval(s[i + 3]);
8573
if (c == -1) {
8574
*p++ = val;
8575
i += 2;
8576
break;
8577
}
8578
val = (val * 8) + c;
8579
*p++ = val;
8580
i += 3;
8581
}
8582
break;
8583
default:
8584
*p++ = s[i + 1];
8585
i++;
8586
break;
8587
}
8588
break;
8589
default:
8590
*p++ = s[i];
8591
break;
8592
}
8593
}
8594
len = p - dest;
8595
*p = '\0';
8596
return len;
8597
}
8598
8599
static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
8600
{
8601
const char *start, *end;
8602
char *token;
8603
int len;
8604
8605
start = pc->tstart;
8606
end = pc->tend;
8607
len = (end - start) + 1;
8608
if (len < 0) {
8609
len = 0;
8610
}
8611
token = Jim_Alloc(len + 1);
8612
if (pc->tt != JIM_TT_ESC) {
8613
8614
memcpy(token, start, len);
8615
token[len] = '\0';
8616
}
8617
else {
8618
8619
len = JimEscape(token, start, len);
8620
}
8621
8622
return Jim_NewStringObjNoAlloc(interp, token, len);
8623
}
8624
8625
static int JimParseListSep(struct JimParserCtx *pc);
8626
static int JimParseListStr(struct JimParserCtx *pc);
8627
static int JimParseListQuote(struct JimParserCtx *pc);
8628
8629
static int JimParseList(struct JimParserCtx *pc)
8630
{
8631
if (isspace(UCHAR(*pc->p))) {
8632
return JimParseListSep(pc);
8633
}
8634
switch (*pc->p) {
8635
case '"':
8636
return JimParseListQuote(pc);
8637
8638
case '{':
8639
return JimParseBrace(pc);
8640
8641
default:
8642
if (pc->len) {
8643
return JimParseListStr(pc);
8644
}
8645
break;
8646
}
8647
8648
pc->tstart = pc->tend = pc->p;
8649
pc->tline = pc->linenr;
8650
pc->tt = JIM_TT_EOL;
8651
pc->eof = 1;
8652
return JIM_OK;
8653
}
8654
8655
static int JimParseListSep(struct JimParserCtx *pc)
8656
{
8657
pc->tstart = pc->p;
8658
pc->tline = pc->linenr;
8659
while (isspace(UCHAR(*pc->p))) {
8660
if (*pc->p == '\n') {
8661
pc->linenr++;
8662
}
8663
pc->p++;
8664
pc->len--;
8665
}
8666
pc->tend = pc->p - 1;
8667
pc->tt = JIM_TT_SEP;
8668
return JIM_OK;
8669
}
8670
8671
static int JimParseListQuote(struct JimParserCtx *pc)
8672
{
8673
pc->p++;
8674
pc->len--;
8675
8676
pc->tstart = pc->p;
8677
pc->tline = pc->linenr;
8678
pc->tt = JIM_TT_STR;
8679
8680
while (pc->len) {
8681
switch (*pc->p) {
8682
case '\\':
8683
pc->tt = JIM_TT_ESC;
8684
if (--pc->len == 0) {
8685
8686
pc->tend = pc->p;
8687
return JIM_OK;
8688
}
8689
pc->p++;
8690
break;
8691
case '\n':
8692
pc->linenr++;
8693
break;
8694
case '"':
8695
pc->tend = pc->p - 1;
8696
pc->p++;
8697
pc->len--;
8698
return JIM_OK;
8699
}
8700
pc->p++;
8701
pc->len--;
8702
}
8703
8704
pc->tend = pc->p - 1;
8705
return JIM_OK;
8706
}
8707
8708
static int JimParseListStr(struct JimParserCtx *pc)
8709
{
8710
pc->tstart = pc->p;
8711
pc->tline = pc->linenr;
8712
pc->tt = JIM_TT_STR;
8713
8714
while (pc->len) {
8715
if (isspace(UCHAR(*pc->p))) {
8716
pc->tend = pc->p - 1;
8717
return JIM_OK;
8718
}
8719
if (*pc->p == '\\') {
8720
if (--pc->len == 0) {
8721
8722
pc->tend = pc->p;
8723
return JIM_OK;
8724
}
8725
pc->tt = JIM_TT_ESC;
8726
pc->p++;
8727
}
8728
pc->p++;
8729
pc->len--;
8730
}
8731
pc->tend = pc->p - 1;
8732
return JIM_OK;
8733
}
8734
8735
8736
8737
Jim_Obj *Jim_NewObj(Jim_Interp *interp)
8738
{
8739
Jim_Obj *objPtr;
8740
8741
8742
if (interp->freeList != NULL) {
8743
8744
objPtr = interp->freeList;
8745
interp->freeList = objPtr->nextObjPtr;
8746
}
8747
else {
8748
8749
objPtr = Jim_Alloc(sizeof(*objPtr));
8750
}
8751
8752
objPtr->refCount = 0;
8753
8754
8755
objPtr->prevObjPtr = NULL;
8756
objPtr->nextObjPtr = interp->liveList;
8757
if (interp->liveList)
8758
interp->liveList->prevObjPtr = objPtr;
8759
interp->liveList = objPtr;
8760
8761
return objPtr;
8762
}
8763
8764
void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
8765
{
8766
8767
JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
8768
objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
8769
8770
8771
Jim_FreeIntRep(interp, objPtr);
8772
8773
if (objPtr->bytes != NULL) {
8774
if (objPtr->bytes != JimEmptyStringRep)
8775
Jim_Free(objPtr->bytes);
8776
}
8777
8778
if (objPtr->prevObjPtr)
8779
objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
8780
if (objPtr->nextObjPtr)
8781
objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
8782
if (interp->liveList == objPtr)
8783
interp->liveList = objPtr->nextObjPtr;
8784
#ifdef JIM_DISABLE_OBJECT_POOL
8785
Jim_Free(objPtr);
8786
#else
8787
8788
objPtr->prevObjPtr = NULL;
8789
objPtr->nextObjPtr = interp->freeList;
8790
if (interp->freeList)
8791
interp->freeList->prevObjPtr = objPtr;
8792
interp->freeList = objPtr;
8793
objPtr->refCount = -1;
8794
#endif
8795
}
8796
8797
8798
void Jim_InvalidateStringRep(Jim_Obj *objPtr)
8799
{
8800
if (objPtr->bytes != NULL) {
8801
if (objPtr->bytes != JimEmptyStringRep)
8802
Jim_Free(objPtr->bytes);
8803
}
8804
objPtr->bytes = NULL;
8805
}
8806
8807
8808
Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
8809
{
8810
Jim_Obj *dupPtr;
8811
8812
dupPtr = Jim_NewObj(interp);
8813
if (objPtr->bytes == NULL) {
8814
8815
dupPtr->bytes = NULL;
8816
}
8817
else if (objPtr->length == 0) {
8818
dupPtr->bytes = JimEmptyStringRep;
8819
dupPtr->length = 0;
8820
dupPtr->typePtr = NULL;
8821
return dupPtr;
8822
}
8823
else {
8824
dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
8825
dupPtr->length = objPtr->length;
8826
8827
memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
8828
}
8829
8830
8831
dupPtr->typePtr = objPtr->typePtr;
8832
if (objPtr->typePtr != NULL) {
8833
if (objPtr->typePtr->dupIntRepProc == NULL) {
8834
dupPtr->internalRep = objPtr->internalRep;
8835
}
8836
else {
8837
8838
objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
8839
}
8840
}
8841
return dupPtr;
8842
}
8843
8844
const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
8845
{
8846
if (objPtr->bytes == NULL) {
8847
8848
JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8849
objPtr->typePtr->updateStringProc(objPtr);
8850
}
8851
if (lenPtr)
8852
*lenPtr = objPtr->length;
8853
return objPtr->bytes;
8854
}
8855
8856
8857
int Jim_Length(Jim_Obj *objPtr)
8858
{
8859
if (objPtr->bytes == NULL) {
8860
8861
Jim_GetString(objPtr, NULL);
8862
}
8863
return objPtr->length;
8864
}
8865
8866
8867
const char *Jim_String(Jim_Obj *objPtr)
8868
{
8869
if (objPtr->bytes == NULL) {
8870
8871
Jim_GetString(objPtr, NULL);
8872
}
8873
return objPtr->bytes;
8874
}
8875
8876
static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
8877
{
8878
objPtr->bytes = Jim_StrDup(str);
8879
objPtr->length = strlen(str);
8880
}
8881
8882
static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8883
static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8884
8885
static const Jim_ObjType dictSubstObjType = {
8886
"dict-substitution",
8887
FreeDictSubstInternalRep,
8888
DupDictSubstInternalRep,
8889
NULL,
8890
JIM_TYPE_NONE,
8891
};
8892
8893
static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8894
static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8895
8896
static const Jim_ObjType interpolatedObjType = {
8897
"interpolated",
8898
FreeInterpolatedInternalRep,
8899
DupInterpolatedInternalRep,
8900
NULL,
8901
JIM_TYPE_NONE,
8902
};
8903
8904
static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8905
{
8906
Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8907
}
8908
8909
static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8910
{
8911
8912
dupPtr->internalRep = srcPtr->internalRep;
8913
8914
Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
8915
}
8916
8917
static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8918
static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8919
8920
static const Jim_ObjType stringObjType = {
8921
"string",
8922
NULL,
8923
DupStringInternalRep,
8924
NULL,
8925
JIM_TYPE_REFERENCES,
8926
};
8927
8928
static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8929
{
8930
JIM_NOTUSED(interp);
8931
8932
dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8933
dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8934
}
8935
8936
static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8937
{
8938
if (objPtr->typePtr != &stringObjType) {
8939
8940
if (objPtr->bytes == NULL) {
8941
8942
JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8943
objPtr->typePtr->updateStringProc(objPtr);
8944
}
8945
8946
Jim_FreeIntRep(interp, objPtr);
8947
8948
objPtr->typePtr = &stringObjType;
8949
objPtr->internalRep.strValue.maxLength = objPtr->length;
8950
8951
objPtr->internalRep.strValue.charLength = -1;
8952
}
8953
return JIM_OK;
8954
}
8955
8956
int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8957
{
8958
#ifdef JIM_UTF8
8959
SetStringFromAny(interp, objPtr);
8960
8961
if (objPtr->internalRep.strValue.charLength < 0) {
8962
objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
8963
}
8964
return objPtr->internalRep.strValue.charLength;
8965
#else
8966
return Jim_Length(objPtr);
8967
#endif
8968
}
8969
8970
8971
Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
8972
{
8973
Jim_Obj *objPtr = Jim_NewObj(interp);
8974
8975
8976
if (len == -1)
8977
len = strlen(s);
8978
8979
if (len == 0) {
8980
objPtr->bytes = JimEmptyStringRep;
8981
}
8982
else {
8983
objPtr->bytes = Jim_StrDupLen(s, len);
8984
}
8985
objPtr->length = len;
8986
8987
8988
objPtr->typePtr = NULL;
8989
return objPtr;
8990
}
8991
8992
8993
Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
8994
{
8995
#ifdef JIM_UTF8
8996
8997
int bytelen = utf8_index(s, charlen);
8998
8999
Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
9000
9001
9002
objPtr->typePtr = &stringObjType;
9003
objPtr->internalRep.strValue.maxLength = bytelen;
9004
objPtr->internalRep.strValue.charLength = charlen;
9005
9006
return objPtr;
9007
#else
9008
return Jim_NewStringObj(interp, s, charlen);
9009
#endif
9010
}
9011
9012
Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
9013
{
9014
Jim_Obj *objPtr = Jim_NewObj(interp);
9015
9016
objPtr->bytes = s;
9017
objPtr->length = (len == -1) ? strlen(s) : len;
9018
objPtr->typePtr = NULL;
9019
return objPtr;
9020
}
9021
9022
static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
9023
{
9024
int needlen;
9025
9026
if (len == -1)
9027
len = strlen(str);
9028
needlen = objPtr->length + len;
9029
if (objPtr->internalRep.strValue.maxLength < needlen ||
9030
objPtr->internalRep.strValue.maxLength == 0) {
9031
needlen *= 2;
9032
9033
if (needlen < 7) {
9034
needlen = 7;
9035
}
9036
if (objPtr->bytes == JimEmptyStringRep) {
9037
objPtr->bytes = Jim_Alloc(needlen + 1);
9038
}
9039
else {
9040
objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
9041
}
9042
objPtr->internalRep.strValue.maxLength = needlen;
9043
}
9044
memcpy(objPtr->bytes + objPtr->length, str, len);
9045
objPtr->bytes[objPtr->length + len] = '\0';
9046
9047
if (objPtr->internalRep.strValue.charLength >= 0) {
9048
9049
objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
9050
}
9051
objPtr->length += len;
9052
}
9053
9054
void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
9055
{
9056
JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
9057
SetStringFromAny(interp, objPtr);
9058
StringAppendString(objPtr, str, len);
9059
}
9060
9061
void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
9062
{
9063
int len;
9064
const char *str = Jim_GetString(appendObjPtr, &len);
9065
Jim_AppendString(interp, objPtr, str, len);
9066
}
9067
9068
void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
9069
{
9070
va_list ap;
9071
9072
SetStringFromAny(interp, objPtr);
9073
va_start(ap, objPtr);
9074
while (1) {
9075
const char *s = va_arg(ap, const char *);
9076
9077
if (s == NULL)
9078
break;
9079
Jim_AppendString(interp, objPtr, s, -1);
9080
}
9081
va_end(ap);
9082
}
9083
9084
int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
9085
{
9086
if (aObjPtr == bObjPtr) {
9087
return 1;
9088
}
9089
else {
9090
int Alen, Blen;
9091
const char *sA = Jim_GetString(aObjPtr, &Alen);
9092
const char *sB = Jim_GetString(bObjPtr, &Blen);
9093
9094
return Alen == Blen && memcmp(sA, sB, Alen) == 0;
9095
}
9096
}
9097
9098
int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
9099
{
9100
int plen, slen;
9101
const char *pattern = Jim_GetString(patternObjPtr, &plen);
9102
const char *string = Jim_GetString(objPtr, &slen);
9103
return JimGlobMatch(pattern, plen, string, slen, nocase);
9104
}
9105
9106
int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
9107
{
9108
const char *s1 = Jim_String(firstObjPtr);
9109
int l1 = Jim_Utf8Length(interp, firstObjPtr);
9110
const char *s2 = Jim_String(secondObjPtr);
9111
int l2 = Jim_Utf8Length(interp, secondObjPtr);
9112
return JimStringCompareUtf8(s1, l1, s2, l2, nocase);
9113
}
9114
9115
static int JimRelToAbsIndex(int len, int idx)
9116
{
9117
if (idx < 0 && idx > -INT_MAX)
9118
return len + idx;
9119
return idx;
9120
}
9121
9122
static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
9123
{
9124
int rangeLen;
9125
9126
if (*firstPtr > *lastPtr) {
9127
rangeLen = 0;
9128
}
9129
else {
9130
rangeLen = *lastPtr - *firstPtr + 1;
9131
if (rangeLen) {
9132
if (*firstPtr < 0) {
9133
rangeLen += *firstPtr;
9134
*firstPtr = 0;
9135
}
9136
if (*lastPtr >= len) {
9137
rangeLen -= (*lastPtr - (len - 1));
9138
*lastPtr = len - 1;
9139
}
9140
}
9141
}
9142
if (rangeLen < 0)
9143
rangeLen = 0;
9144
9145
*rangeLenPtr = rangeLen;
9146
}
9147
9148
static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
9149
int len, int *first, int *last, int *range)
9150
{
9151
if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
9152
return JIM_ERR;
9153
}
9154
if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
9155
return JIM_ERR;
9156
}
9157
*first = JimRelToAbsIndex(len, *first);
9158
*last = JimRelToAbsIndex(len, *last);
9159
JimRelToAbsRange(len, first, last, range);
9160
return JIM_OK;
9161
}
9162
9163
Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
9164
Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
9165
{
9166
int first, last;
9167
const char *str;
9168
int rangeLen;
9169
int bytelen;
9170
9171
str = Jim_GetString(strObjPtr, &bytelen);
9172
9173
if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
9174
return NULL;
9175
}
9176
9177
if (first == 0 && rangeLen == bytelen) {
9178
return strObjPtr;
9179
}
9180
return Jim_NewStringObj(interp, str + first, rangeLen);
9181
}
9182
9183
Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
9184
Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
9185
{
9186
#ifdef JIM_UTF8
9187
int first, last;
9188
const char *str;
9189
int len, rangeLen;
9190
int bytelen;
9191
9192
str = Jim_GetString(strObjPtr, &bytelen);
9193
len = Jim_Utf8Length(interp, strObjPtr);
9194
9195
if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
9196
return NULL;
9197
}
9198
9199
if (first == 0 && rangeLen == len) {
9200
return strObjPtr;
9201
}
9202
if (len == bytelen) {
9203
9204
return Jim_NewStringObj(interp, str + first, rangeLen);
9205
}
9206
return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
9207
#else
9208
return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
9209
#endif
9210
}
9211
9212
Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
9213
Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
9214
{
9215
int first, last;
9216
const char *str;
9217
int len, rangeLen;
9218
Jim_Obj *objPtr;
9219
9220
len = Jim_Utf8Length(interp, strObjPtr);
9221
9222
if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
9223
return NULL;
9224
}
9225
9226
if (last < first) {
9227
return strObjPtr;
9228
}
9229
9230
str = Jim_String(strObjPtr);
9231
9232
9233
objPtr = Jim_NewStringObjUtf8(interp, str, first);
9234
9235
9236
if (newStrObj) {
9237
Jim_AppendObj(interp, objPtr, newStrObj);
9238
}
9239
9240
9241
Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
9242
9243
return objPtr;
9244
}
9245
9246
static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
9247
{
9248
while (*str) {
9249
int c;
9250
str += utf8_tounicode(str, &c);
9251
dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
9252
}
9253
*dest = 0;
9254
}
9255
9256
static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
9257
{
9258
char *buf;
9259
int len;
9260
const char *str;
9261
9262
str = Jim_GetString(strObjPtr, &len);
9263
9264
#ifdef JIM_UTF8
9265
len *= 2;
9266
#endif
9267
buf = Jim_Alloc(len + 1);
9268
JimStrCopyUpperLower(buf, str, 0);
9269
return Jim_NewStringObjNoAlloc(interp, buf, -1);
9270
}
9271
9272
static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
9273
{
9274
char *buf;
9275
const char *str;
9276
int len;
9277
9278
str = Jim_GetString(strObjPtr, &len);
9279
9280
#ifdef JIM_UTF8
9281
len *= 2;
9282
#endif
9283
buf = Jim_Alloc(len + 1);
9284
JimStrCopyUpperLower(buf, str, 1);
9285
return Jim_NewStringObjNoAlloc(interp, buf, -1);
9286
}
9287
9288
static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
9289
{
9290
char *buf, *p;
9291
int len;
9292
int c;
9293
const char *str;
9294
9295
str = Jim_GetString(strObjPtr, &len);
9296
9297
#ifdef JIM_UTF8
9298
len *= 2;
9299
#endif
9300
buf = p = Jim_Alloc(len + 1);
9301
9302
str += utf8_tounicode(str, &c);
9303
p += utf8_getchars(p, utf8_title(c));
9304
9305
JimStrCopyUpperLower(p, str, 0);
9306
9307
return Jim_NewStringObjNoAlloc(interp, buf, -1);
9308
}
9309
9310
static const char *utf8_memchr(const char *str, int len, int c)
9311
{
9312
#ifdef JIM_UTF8
9313
while (len) {
9314
int sc;
9315
int n = utf8_tounicode(str, &sc);
9316
if (sc == c) {
9317
return str;
9318
}
9319
str += n;
9320
len -= n;
9321
}
9322
return NULL;
9323
#else
9324
return memchr(str, c, len);
9325
#endif
9326
}
9327
9328
static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
9329
{
9330
while (len) {
9331
int c;
9332
int n = utf8_tounicode(str, &c);
9333
9334
if (utf8_memchr(trimchars, trimlen, c) == NULL) {
9335
9336
break;
9337
}
9338
str += n;
9339
len -= n;
9340
}
9341
return str;
9342
}
9343
9344
static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
9345
{
9346
str += len;
9347
9348
while (len) {
9349
int c;
9350
int n = utf8_prev_len(str, len);
9351
9352
len -= n;
9353
str -= n;
9354
9355
n = utf8_tounicode(str, &c);
9356
9357
if (utf8_memchr(trimchars, trimlen, c) == NULL) {
9358
return str + n;
9359
}
9360
}
9361
9362
return NULL;
9363
}
9364
9365
static const char default_trim_chars[] = " \t\n\r";
9366
9367
static int default_trim_chars_len = sizeof(default_trim_chars);
9368
9369
static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9370
{
9371
int len;
9372
const char *str = Jim_GetString(strObjPtr, &len);
9373
const char *trimchars = default_trim_chars;
9374
int trimcharslen = default_trim_chars_len;
9375
const char *newstr;
9376
9377
if (trimcharsObjPtr) {
9378
trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
9379
}
9380
9381
newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
9382
if (newstr == str) {
9383
return strObjPtr;
9384
}
9385
9386
return Jim_NewStringObj(interp, newstr, len - (newstr - str));
9387
}
9388
9389
static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9390
{
9391
int len;
9392
const char *trimchars = default_trim_chars;
9393
int trimcharslen = default_trim_chars_len;
9394
const char *nontrim;
9395
9396
if (trimcharsObjPtr) {
9397
trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
9398
}
9399
9400
SetStringFromAny(interp, strObjPtr);
9401
9402
len = Jim_Length(strObjPtr);
9403
nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
9404
9405
if (nontrim == NULL) {
9406
9407
return Jim_NewEmptyStringObj(interp);
9408
}
9409
if (nontrim == strObjPtr->bytes + len) {
9410
9411
return strObjPtr;
9412
}
9413
9414
if (Jim_IsShared(strObjPtr)) {
9415
strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
9416
}
9417
else {
9418
9419
strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
9420
strObjPtr->length = (nontrim - strObjPtr->bytes);
9421
}
9422
9423
return strObjPtr;
9424
}
9425
9426
static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9427
{
9428
9429
Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
9430
9431
9432
strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
9433
9434
9435
if (objPtr != strObjPtr && objPtr->refCount == 0) {
9436
9437
Jim_FreeNewObj(interp, objPtr);
9438
}
9439
9440
return strObjPtr;
9441
}
9442
9443
9444
#ifdef HAVE_ISASCII
9445
#define jim_isascii isascii
9446
#else
9447
static int jim_isascii(int c)
9448
{
9449
return !(c & ~0x7f);
9450
}
9451
#endif
9452
9453
static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
9454
{
9455
static const char * const strclassnames[] = {
9456
"integer", "alpha", "alnum", "ascii", "digit",
9457
"double", "lower", "upper", "space", "xdigit",
9458
"control", "print", "graph", "punct", "boolean",
9459
NULL
9460
};
9461
enum {
9462
STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
9463
STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
9464
STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
9465
};
9466
int strclass;
9467
int len;
9468
int i;
9469
const char *str;
9470
int (*isclassfunc)(int c) = NULL;
9471
9472
if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
9473
return JIM_ERR;
9474
}
9475
9476
str = Jim_GetString(strObjPtr, &len);
9477
if (len == 0) {
9478
Jim_SetResultBool(interp, !strict);
9479
return JIM_OK;
9480
}
9481
9482
switch (strclass) {
9483
case STR_IS_INTEGER:
9484
{
9485
jim_wide w;
9486
Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
9487
return JIM_OK;
9488
}
9489
9490
case STR_IS_DOUBLE:
9491
{
9492
double d;
9493
Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
9494
return JIM_OK;
9495
}
9496
9497
case STR_IS_BOOLEAN:
9498
{
9499
int b;
9500
Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
9501
return JIM_OK;
9502
}
9503
9504
case STR_IS_ALPHA: isclassfunc = isalpha; break;
9505
case STR_IS_ALNUM: isclassfunc = isalnum; break;
9506
case STR_IS_ASCII: isclassfunc = jim_isascii; break;
9507
case STR_IS_DIGIT: isclassfunc = isdigit; break;
9508
case STR_IS_LOWER: isclassfunc = islower; break;
9509
case STR_IS_UPPER: isclassfunc = isupper; break;
9510
case STR_IS_SPACE: isclassfunc = isspace; break;
9511
case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
9512
case STR_IS_CONTROL: isclassfunc = iscntrl; break;
9513
case STR_IS_PRINT: isclassfunc = isprint; break;
9514
case STR_IS_GRAPH: isclassfunc = isgraph; break;
9515
case STR_IS_PUNCT: isclassfunc = ispunct; break;
9516
default:
9517
return JIM_ERR;
9518
}
9519
9520
for (i = 0; i < len; i++) {
9521
if (!isclassfunc(UCHAR(str[i]))) {
9522
Jim_SetResultBool(interp, 0);
9523
return JIM_OK;
9524
}
9525
}
9526
Jim_SetResultBool(interp, 1);
9527
return JIM_OK;
9528
}
9529
9530
9531
9532
static const Jim_ObjType comparedStringObjType = {
9533
"compared-string",
9534
NULL,
9535
NULL,
9536
NULL,
9537
JIM_TYPE_REFERENCES,
9538
};
9539
9540
int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
9541
{
9542
if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
9543
return 1;
9544
}
9545
else {
9546
if (strcmp(str, Jim_String(objPtr)) != 0)
9547
return 0;
9548
9549
if (objPtr->typePtr != &comparedStringObjType) {
9550
Jim_FreeIntRep(interp, objPtr);
9551
objPtr->typePtr = &comparedStringObjType;
9552
}
9553
objPtr->internalRep.ptr = (char *)str;
9554
return 1;
9555
}
9556
}
9557
9558
static int qsortCompareStringPointers(const void *a, const void *b)
9559
{
9560
char *const *sa = (char *const *)a;
9561
char *const *sb = (char *const *)b;
9562
9563
return strcmp(*sa, *sb);
9564
}
9565
9566
9567
9568
static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
9569
static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
9570
9571
static const Jim_ObjType sourceObjType = {
9572
"source",
9573
FreeSourceInternalRep,
9574
DupSourceInternalRep,
9575
NULL,
9576
JIM_TYPE_REFERENCES,
9577
};
9578
9579
void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9580
{
9581
Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
9582
}
9583
9584
void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9585
{
9586
dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
9587
Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
9588
}
9589
9590
static const Jim_ObjType scriptLineObjType = {
9591
"scriptline",
9592
NULL,
9593
NULL,
9594
NULL,
9595
JIM_NONE,
9596
};
9597
9598
static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
9599
{
9600
Jim_Obj *objPtr;
9601
9602
#ifdef DEBUG_SHOW_SCRIPT
9603
char buf[100];
9604
snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
9605
objPtr = Jim_NewStringObj(interp, buf, -1);
9606
#else
9607
objPtr = Jim_NewEmptyStringObj(interp);
9608
#endif
9609
objPtr->typePtr = &scriptLineObjType;
9610
objPtr->internalRep.scriptLineValue.argc = argc;
9611
objPtr->internalRep.scriptLineValue.line = line;
9612
9613
return objPtr;
9614
}
9615
9616
static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
9617
static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
9618
9619
static const Jim_ObjType scriptObjType = {
9620
"script",
9621
FreeScriptInternalRep,
9622
DupScriptInternalRep,
9623
NULL,
9624
JIM_TYPE_NONE,
9625
};
9626
9627
typedef struct ScriptToken
9628
{
9629
Jim_Obj *objPtr;
9630
int type;
9631
} ScriptToken;
9632
9633
typedef struct ScriptObj
9634
{
9635
ScriptToken *token;
9636
Jim_Obj *fileNameObj;
9637
int len;
9638
int substFlags;
9639
int inUse; /* Used to share a ScriptObj. Currently
9640
only used by Jim_EvalObj() as protection against
9641
shimmering of the currently evaluated object. */
9642
int firstline;
9643
int linenr;
9644
int missing;
9645
} ScriptObj;
9646
9647
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9648
static int JimParseCheckMissing(Jim_Interp *interp, int ch);
9649
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
9650
static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script);
9651
9652
void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9653
{
9654
int i;
9655
struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
9656
9657
if (--script->inUse != 0)
9658
return;
9659
for (i = 0; i < script->len; i++) {
9660
Jim_DecrRefCount(interp, script->token[i].objPtr);
9661
}
9662
Jim_Free(script->token);
9663
Jim_DecrRefCount(interp, script->fileNameObj);
9664
Jim_Free(script);
9665
}
9666
9667
void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9668
{
9669
JIM_NOTUSED(interp);
9670
JIM_NOTUSED(srcPtr);
9671
9672
dupPtr->typePtr = NULL;
9673
}
9674
9675
typedef struct
9676
{
9677
const char *token;
9678
int len;
9679
int type;
9680
int line;
9681
} ParseToken;
9682
9683
typedef struct
9684
{
9685
9686
ParseToken *list;
9687
int size;
9688
int count;
9689
ParseToken static_list[20];
9690
} ParseTokenList;
9691
9692
static void ScriptTokenListInit(ParseTokenList *tokenlist)
9693
{
9694
tokenlist->list = tokenlist->static_list;
9695
tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
9696
tokenlist->count = 0;
9697
}
9698
9699
static void ScriptTokenListFree(ParseTokenList *tokenlist)
9700
{
9701
if (tokenlist->list != tokenlist->static_list) {
9702
Jim_Free(tokenlist->list);
9703
}
9704
}
9705
9706
static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
9707
int line)
9708
{
9709
ParseToken *t;
9710
9711
if (tokenlist->count == tokenlist->size) {
9712
9713
tokenlist->size *= 2;
9714
if (tokenlist->list != tokenlist->static_list) {
9715
tokenlist->list =
9716
Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
9717
}
9718
else {
9719
9720
tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
9721
memcpy(tokenlist->list, tokenlist->static_list,
9722
tokenlist->count * sizeof(*tokenlist->list));
9723
}
9724
}
9725
t = &tokenlist->list[tokenlist->count++];
9726
t->token = token;
9727
t->len = len;
9728
t->type = type;
9729
t->line = line;
9730
}
9731
9732
static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
9733
{
9734
int expand = 1;
9735
int count = 0;
9736
9737
9738
if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
9739
if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
9740
9741
expand = -1;
9742
t++;
9743
}
9744
else {
9745
if (script->missing == ' ') {
9746
9747
script->missing = '}';
9748
script->linenr = t[1].line;
9749
}
9750
}
9751
}
9752
9753
9754
while (!TOKEN_IS_SEP(t->type)) {
9755
t++;
9756
count++;
9757
}
9758
9759
return count * expand;
9760
}
9761
9762
static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
9763
{
9764
Jim_Obj *objPtr;
9765
9766
if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
9767
9768
int len = t->len;
9769
char *str = Jim_Alloc(len + 1);
9770
len = JimEscape(str, t->token, len);
9771
objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
9772
}
9773
else {
9774
objPtr = Jim_NewStringObj(interp, t->token, t->len);
9775
}
9776
return objPtr;
9777
}
9778
9779
static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9780
ParseTokenList *tokenlist)
9781
{
9782
int i;
9783
struct ScriptToken *token;
9784
9785
int lineargs = 0;
9786
9787
ScriptToken *linefirst;
9788
int count;
9789
int linenr;
9790
9791
#ifdef DEBUG_SHOW_SCRIPT_TOKENS
9792
printf("==== Tokens ====\n");
9793
for (i = 0; i < tokenlist->count; i++) {
9794
printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
9795
tokenlist->list[i].len, tokenlist->list[i].token);
9796
}
9797
#endif
9798
9799
9800
count = tokenlist->count;
9801
for (i = 0; i < tokenlist->count; i++) {
9802
if (tokenlist->list[i].type == JIM_TT_EOL) {
9803
count++;
9804
}
9805
}
9806
linenr = script->firstline = tokenlist->list[0].line;
9807
9808
token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
9809
9810
9811
linefirst = token++;
9812
9813
for (i = 0; i < tokenlist->count; ) {
9814
9815
int wordtokens;
9816
9817
9818
while (tokenlist->list[i].type == JIM_TT_SEP) {
9819
i++;
9820
}
9821
9822
wordtokens = JimCountWordTokens(script, tokenlist->list + i);
9823
9824
if (wordtokens == 0) {
9825
9826
if (lineargs) {
9827
linefirst->type = JIM_TT_LINE;
9828
linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
9829
Jim_IncrRefCount(linefirst->objPtr);
9830
9831
9832
lineargs = 0;
9833
linefirst = token++;
9834
}
9835
i++;
9836
continue;
9837
}
9838
else if (wordtokens != 1) {
9839
9840
token->type = JIM_TT_WORD;
9841
token->objPtr = Jim_NewIntObj(interp, wordtokens);
9842
Jim_IncrRefCount(token->objPtr);
9843
token++;
9844
if (wordtokens < 0) {
9845
9846
i++;
9847
wordtokens = -wordtokens - 1;
9848
lineargs--;
9849
}
9850
}
9851
9852
if (lineargs == 0) {
9853
9854
linenr = tokenlist->list[i].line;
9855
}
9856
lineargs++;
9857
9858
9859
while (wordtokens--) {
9860
const ParseToken *t = &tokenlist->list[i++];
9861
9862
token->type = t->type;
9863
token->objPtr = JimMakeScriptObj(interp, t);
9864
Jim_IncrRefCount(token->objPtr);
9865
9866
Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
9867
token++;
9868
}
9869
}
9870
9871
if (lineargs == 0) {
9872
token--;
9873
}
9874
9875
script->len = token - script->token;
9876
9877
JimPanic((script->len >= count, "allocated script array is too short"));
9878
9879
#ifdef DEBUG_SHOW_SCRIPT
9880
printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9881
for (i = 0; i < script->len; i++) {
9882
const ScriptToken *t = &script->token[i];
9883
printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9884
}
9885
#endif
9886
9887
}
9888
9889
int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9890
{
9891
ScriptObj *script = JimGetScript(interp, scriptObj);
9892
if (stateCharPtr) {
9893
*stateCharPtr = script->missing;
9894
}
9895
return script->missing == ' ' || script->missing == '}';
9896
}
9897
9898
static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9899
{
9900
const char *msg;
9901
9902
switch (ch) {
9903
case '\\':
9904
case ' ':
9905
return JIM_OK;
9906
9907
case '[':
9908
msg = "unmatched \"[\"";
9909
break;
9910
case '{':
9911
msg = "missing close-brace";
9912
break;
9913
case '}':
9914
msg = "extra characters after close-brace";
9915
break;
9916
case '"':
9917
default:
9918
msg = "missing quote";
9919
break;
9920
}
9921
9922
Jim_SetResultString(interp, msg, -1);
9923
return JIM_ERR;
9924
}
9925
9926
Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr)
9927
{
9928
int line;
9929
Jim_Obj *fileNameObj;
9930
9931
if (objPtr->typePtr == &sourceObjType) {
9932
fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9933
line = objPtr->internalRep.sourceValue.lineNumber;
9934
}
9935
else if (objPtr->typePtr == &scriptObjType) {
9936
ScriptObj *script = JimGetScript(interp, objPtr);
9937
fileNameObj = script->fileNameObj;
9938
line = script->firstline;
9939
}
9940
else {
9941
fileNameObj = interp->emptyObj;
9942
line = 1;
9943
}
9944
*lineptr = line;
9945
return fileNameObj;
9946
}
9947
9948
void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
9949
Jim_Obj *fileNameObj, int lineNumber)
9950
{
9951
JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object"));
9952
Jim_FreeIntRep(interp, objPtr);
9953
Jim_IncrRefCount(fileNameObj);
9954
objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
9955
objPtr->internalRep.sourceValue.lineNumber = lineNumber;
9956
objPtr->typePtr = &sourceObjType;
9957
}
9958
9959
static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9960
ParseTokenList *tokenlist)
9961
{
9962
int i;
9963
struct ScriptToken *token;
9964
9965
token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
9966
9967
for (i = 0; i < tokenlist->count; i++) {
9968
const ParseToken *t = &tokenlist->list[i];
9969
9970
9971
token->type = t->type;
9972
token->objPtr = JimMakeScriptObj(interp, t);
9973
Jim_IncrRefCount(token->objPtr);
9974
token++;
9975
}
9976
9977
script->len = i;
9978
}
9979
9980
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9981
{
9982
int scriptTextLen;
9983
const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9984
struct JimParserCtx parser;
9985
struct ScriptObj *script;
9986
ParseTokenList tokenlist;
9987
Jim_Obj *fileNameObj;
9988
int line;
9989
9990
9991
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
9992
9993
9994
ScriptTokenListInit(&tokenlist);
9995
9996
JimParserInit(&parser, scriptText, scriptTextLen, line);
9997
while (!parser.eof) {
9998
JimParseScript(&parser);
9999
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
10000
parser.tline);
10001
}
10002
10003
10004
ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
10005
10006
10007
script = Jim_Alloc(sizeof(*script));
10008
memset(script, 0, sizeof(*script));
10009
script->inUse = 1;
10010
script->fileNameObj = fileNameObj;
10011
Jim_IncrRefCount(script->fileNameObj);
10012
script->missing = parser.missing.ch;
10013
script->linenr = parser.missing.line;
10014
10015
ScriptObjAddTokens(interp, script, &tokenlist);
10016
10017
10018
ScriptTokenListFree(&tokenlist);
10019
10020
10021
Jim_FreeIntRep(interp, objPtr);
10022
Jim_SetIntRepPtr(objPtr, script);
10023
objPtr->typePtr = &scriptObjType;
10024
}
10025
10026
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
10027
{
10028
if (objPtr == interp->emptyObj) {
10029
10030
objPtr = interp->nullScriptObj;
10031
}
10032
10033
if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
10034
JimSetScriptFromAny(interp, objPtr);
10035
}
10036
10037
return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
10038
}
10039
10040
void Jim_InterpIncrProcEpoch(Jim_Interp *interp)
10041
{
10042
interp->procEpoch++;
10043
10044
10045
while (interp->oldCmdCache) {
10046
Jim_Cmd *next = interp->oldCmdCache->prevCmd;
10047
Jim_Free(interp->oldCmdCache);
10048
interp->oldCmdCache = next;
10049
}
10050
interp->oldCmdCacheSize = 0;
10051
}
10052
10053
static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
10054
{
10055
cmdPtr->inUse++;
10056
}
10057
10058
static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
10059
{
10060
if (--cmdPtr->inUse == 0) {
10061
if (cmdPtr->isproc) {
10062
Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
10063
Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
10064
Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
10065
if (cmdPtr->u.proc.staticVars) {
10066
Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
10067
Jim_Free(cmdPtr->u.proc.staticVars);
10068
}
10069
}
10070
else {
10071
10072
if (cmdPtr->u.native.delProc) {
10073
cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
10074
}
10075
}
10076
if (cmdPtr->prevCmd) {
10077
10078
JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
10079
}
10080
10081
cmdPtr->prevCmd = interp->oldCmdCache;
10082
interp->oldCmdCache = cmdPtr;
10083
if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) {
10084
Jim_InterpIncrProcEpoch(interp);
10085
}
10086
}
10087
}
10088
10089
static void JimIncrVarRef(Jim_VarVal *vv)
10090
{
10091
vv->refCount++;
10092
}
10093
10094
static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv)
10095
{
10096
assert(vv->refCount > 0);
10097
if (--vv->refCount == 0) {
10098
if (vv->objPtr) {
10099
Jim_DecrRefCount(interp, vv->objPtr);
10100
}
10101
Jim_Free(vv);
10102
}
10103
}
10104
10105
static void JimVariablesHTValDestructor(void *interp, void *val)
10106
{
10107
JimDecrVarRef(interp, val);
10108
}
10109
10110
static unsigned int JimObjectHTHashFunction(const void *key)
10111
{
10112
Jim_Obj *keyObj = (Jim_Obj *)key;
10113
int length;
10114
const char *string;
10115
10116
#ifdef JIM_OPTIMIZATION
10117
if (JimIsWide(keyObj) && keyObj->bytes == NULL) {
10118
10119
jim_wide objValue = JimWideValue(keyObj);
10120
if (objValue > INT_MIN && objValue < INT_MAX) {
10121
unsigned result = 0;
10122
unsigned value = (unsigned)objValue;
10123
10124
if (objValue < 0) {
10125
value = (unsigned)-objValue;
10126
}
10127
10128
10129
do {
10130
result += (result << 3) + (value % 10 + '0');
10131
value /= 10;
10132
} while (value);
10133
10134
if (objValue < 0) {
10135
result += (result << 3) + '-';
10136
}
10137
return result;
10138
}
10139
}
10140
#endif
10141
string = Jim_GetString(keyObj, &length);
10142
return Jim_GenHashFunction((const unsigned char *)string, length);
10143
}
10144
10145
static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
10146
{
10147
return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
10148
}
10149
10150
static void *JimObjectHTKeyValDup(void *privdata, const void *val)
10151
{
10152
Jim_IncrRefCount((Jim_Obj *)val);
10153
return (void *)val;
10154
}
10155
10156
static void JimObjectHTKeyValDestructor(void *interp, void *val)
10157
{
10158
Jim_DecrRefCount(interp, (Jim_Obj *)val);
10159
}
10160
10161
10162
static void *JimVariablesHTValDup(void *privdata, const void *val)
10163
{
10164
JimIncrVarRef((Jim_VarVal *)val);
10165
return (void *)val;
10166
}
10167
10168
static const Jim_HashTableType JimVariablesHashTableType = {
10169
JimObjectHTHashFunction,
10170
JimObjectHTKeyValDup,
10171
JimVariablesHTValDup,
10172
JimObjectHTKeyCompare,
10173
JimObjectHTKeyValDestructor,
10174
JimVariablesHTValDestructor
10175
};
10176
10177
10178
static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length)
10179
{
10180
int len;
10181
const char *str = Jim_GetString(objPtr, &len);
10182
if (len >= 2 && str[0] == ':' && str[1] == ':') {
10183
while (len && *str == ':') {
10184
len--;
10185
str++;
10186
}
10187
}
10188
*length = len;
10189
return str;
10190
}
10191
10192
static unsigned int JimCommandsHT_HashFunction(const void *key)
10193
{
10194
int len;
10195
const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len);
10196
return Jim_GenHashFunction((const unsigned char *)str, len);
10197
}
10198
10199
static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2)
10200
{
10201
int len1, len2;
10202
const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
10203
const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
10204
return len1 == len2 && memcmp(str1, str2, len1) == 0;
10205
}
10206
10207
static void JimCommandsHT_ValDestructor(void *interp, void *val)
10208
{
10209
JimDecrCmdRefCount(interp, val);
10210
}
10211
10212
static const Jim_HashTableType JimCommandsHashTableType = {
10213
JimCommandsHT_HashFunction,
10214
JimObjectHTKeyValDup,
10215
NULL,
10216
JimCommandsHT_KeyCompare,
10217
JimObjectHTKeyValDestructor,
10218
JimCommandsHT_ValDestructor
10219
};
10220
10221
10222
10223
Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
10224
{
10225
#ifdef jim_ext_namespace
10226
Jim_Obj *resultObj;
10227
10228
const char *name = Jim_String(nameObjPtr);
10229
if (name[0] == ':' && name[1] == ':') {
10230
return nameObjPtr;
10231
}
10232
Jim_IncrRefCount(nameObjPtr);
10233
resultObj = Jim_NewStringObj(interp, "::", -1);
10234
Jim_AppendObj(interp, resultObj, nameObjPtr);
10235
Jim_DecrRefCount(interp, nameObjPtr);
10236
10237
return resultObj;
10238
#else
10239
return nameObjPtr;
10240
#endif
10241
}
10242
10243
static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr)
10244
{
10245
#ifdef jim_ext_namespace
10246
if (Jim_Length(interp->framePtr->nsObj)) {
10247
int len;
10248
const char *name = Jim_GetString(objPtr, &len);
10249
if (len < 2 || name[0] != ':' || name[1] != ':') {
10250
10251
objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
10252
Jim_AppendStrings(interp, objPtr, "::", name, NULL);
10253
}
10254
}
10255
#endif
10256
Jim_IncrRefCount(objPtr);
10257
return objPtr;
10258
}
10259
10260
static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd)
10261
{
10262
JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name"));
10263
10264
if (interp->local) {
10265
Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr);
10266
if (he) {
10267
10268
cmd->prevCmd = Jim_GetHashEntryVal(he);
10269
Jim_SetHashVal(&interp->commands, he, cmd);
10270
10271
Jim_InterpIncrProcEpoch(interp);
10272
return;
10273
}
10274
}
10275
10276
10277
10278
Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd);
10279
}
10280
10281
int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
10282
Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
10283
{
10284
Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
10285
10286
10287
memset(cmdPtr, 0, sizeof(*cmdPtr));
10288
cmdPtr->inUse = 1;
10289
cmdPtr->u.native.delProc = delProc;
10290
cmdPtr->u.native.cmdProc = cmdProc;
10291
cmdPtr->u.native.privData = privData;
10292
10293
Jim_IncrRefCount(cmdNameObj);
10294
JimCreateCommand(interp, cmdNameObj, cmdPtr);
10295
Jim_DecrRefCount(interp, cmdNameObj);
10296
10297
return JIM_OK;
10298
}
10299
10300
10301
int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
10302
Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
10303
{
10304
return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc);
10305
}
10306
10307
static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
10308
{
10309
int len, i;
10310
10311
len = Jim_ListLength(interp, staticsListObjPtr);
10312
if (len == 0) {
10313
return JIM_OK;
10314
}
10315
10316
cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
10317
Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
10318
for (i = 0; i < len; i++) {
10319
Jim_Obj *initObjPtr = NULL;
10320
Jim_Obj *nameObjPtr;
10321
Jim_VarVal *vv = NULL;
10322
Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
10323
int subLen = Jim_ListLength(interp, objPtr);
10324
int byref = 0;
10325
10326
10327
if (subLen != 1 && subLen != 2) {
10328
Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
10329
objPtr);
10330
return JIM_ERR;
10331
}
10332
10333
nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
10334
10335
10336
if (subLen == 1) {
10337
int len;
10338
const char *pt = Jim_GetString(nameObjPtr, &len);
10339
if (*pt == '&') {
10340
10341
nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1);
10342
byref = 1;
10343
}
10344
}
10345
Jim_IncrRefCount(nameObjPtr);
10346
10347
if (subLen == 1) {
10348
switch (SetVariableFromAny(interp, nameObjPtr)) {
10349
case JIM_DICT_SUGAR:
10350
10351
if (byref) {
10352
Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr);
10353
}
10354
else {
10355
Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr);
10356
}
10357
Jim_DecrRefCount(interp, nameObjPtr);
10358
return JIM_ERR;
10359
10360
case JIM_OK:
10361
if (byref) {
10362
vv = nameObjPtr->internalRep.varValue.vv;
10363
}
10364
else {
10365
initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
10366
}
10367
break;
10368
10369
case JIM_ERR:
10370
10371
Jim_SetResultFormatted(interp,
10372
"variable for initialization of static \"%#s\" not found in the local context",
10373
nameObjPtr);
10374
Jim_DecrRefCount(interp, nameObjPtr);
10375
return JIM_ERR;
10376
}
10377
}
10378
else {
10379
initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
10380
}
10381
10382
if (vv == NULL) {
10383
vv = Jim_Alloc(sizeof(*vv));
10384
vv->objPtr = initObjPtr;
10385
Jim_IncrRefCount(vv->objPtr);
10386
vv->linkFramePtr = NULL;
10387
vv->refCount = 0;
10388
}
10389
10390
if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) {
10391
Jim_SetResultFormatted(interp,
10392
"static variable name \"%#s\" duplicated in statics list", nameObjPtr);
10393
JimIncrVarRef(vv);
10394
JimDecrVarRef(interp, vv);
10395
Jim_DecrRefCount(interp, nameObjPtr);
10396
return JIM_ERR;
10397
}
10398
10399
Jim_DecrRefCount(interp, nameObjPtr);
10400
}
10401
return JIM_OK;
10402
}
10403
10404
10405
#ifdef jim_ext_namespace
10406
static const char *Jim_memrchr(const char *p, int c, int len)
10407
{
10408
int i;
10409
for (i = len; i > 0; i--) {
10410
if (p[i] == c) {
10411
return p + i;
10412
}
10413
}
10414
return NULL;
10415
}
10416
#endif
10417
10418
static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr)
10419
{
10420
#ifdef jim_ext_namespace
10421
if (cmdPtr->isproc) {
10422
int len;
10423
const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len);
10424
10425
const char *pt = Jim_memrchr(cmdname, ':', len);
10426
if (pt && pt != cmdname && pt[-1] == ':') {
10427
pt++;
10428
Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
10429
cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2);
10430
Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
10431
10432
Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname));
10433
if (Jim_FindHashEntry(&interp->commands, tempObj)) {
10434
10435
Jim_InterpIncrProcEpoch(interp);
10436
}
10437
Jim_FreeNewObj(interp, tempObj);
10438
}
10439
}
10440
#endif
10441
}
10442
10443
static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
10444
Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
10445
{
10446
Jim_Cmd *cmdPtr;
10447
int argListLen;
10448
int i;
10449
10450
argListLen = Jim_ListLength(interp, argListObjPtr);
10451
10452
10453
cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
10454
assert(cmdPtr);
10455
memset(cmdPtr, 0, sizeof(*cmdPtr));
10456
cmdPtr->inUse = 1;
10457
cmdPtr->isproc = 1;
10458
cmdPtr->u.proc.argListObjPtr = argListObjPtr;
10459
cmdPtr->u.proc.argListLen = argListLen;
10460
cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
10461
cmdPtr->u.proc.argsPos = -1;
10462
cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
10463
cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
10464
Jim_IncrRefCount(argListObjPtr);
10465
Jim_IncrRefCount(bodyObjPtr);
10466
Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
10467
10468
10469
if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
10470
goto err;
10471
}
10472
10473
10474
10475
for (i = 0; i < argListLen; i++) {
10476
Jim_Obj *argPtr;
10477
Jim_Obj *nameObjPtr;
10478
Jim_Obj *defaultObjPtr;
10479
int len;
10480
10481
10482
argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
10483
len = Jim_ListLength(interp, argPtr);
10484
if (len == 0) {
10485
Jim_SetResultString(interp, "argument with no name", -1);
10486
err:
10487
JimDecrCmdRefCount(interp, cmdPtr);
10488
return NULL;
10489
}
10490
if (len > 2) {
10491
Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
10492
goto err;
10493
}
10494
10495
if (len == 2) {
10496
10497
nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
10498
defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
10499
}
10500
else {
10501
10502
nameObjPtr = argPtr;
10503
defaultObjPtr = NULL;
10504
}
10505
10506
10507
if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
10508
if (cmdPtr->u.proc.argsPos >= 0) {
10509
Jim_SetResultString(interp, "'args' specified more than once", -1);
10510
goto err;
10511
}
10512
cmdPtr->u.proc.argsPos = i;
10513
}
10514
else {
10515
if (len == 2) {
10516
cmdPtr->u.proc.optArity++;
10517
}
10518
else {
10519
cmdPtr->u.proc.reqArity++;
10520
}
10521
}
10522
10523
cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
10524
cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
10525
}
10526
10527
return cmdPtr;
10528
}
10529
10530
int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj)
10531
{
10532
int ret = JIM_OK;
10533
10534
nameObj = JimQualifyName(interp, nameObj);
10535
10536
if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) {
10537
Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj);
10538
ret = JIM_ERR;
10539
}
10540
Jim_DecrRefCount(interp, nameObj);
10541
10542
return ret;
10543
}
10544
10545
int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj)
10546
{
10547
int ret = JIM_ERR;
10548
Jim_HashEntry *he;
10549
Jim_Cmd *cmdPtr;
10550
10551
if (Jim_Length(newNameObj) == 0) {
10552
return Jim_DeleteCommand(interp, oldNameObj);
10553
}
10554
10555
10556
10557
oldNameObj = JimQualifyName(interp, oldNameObj);
10558
newNameObj = JimQualifyName(interp, newNameObj);
10559
10560
10561
he = Jim_FindHashEntry(&interp->commands, oldNameObj);
10562
if (he == NULL) {
10563
Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj);
10564
}
10565
else if (Jim_FindHashEntry(&interp->commands, newNameObj)) {
10566
Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj);
10567
}
10568
else {
10569
cmdPtr = Jim_GetHashEntryVal(he);
10570
if (cmdPtr->prevCmd) {
10571
Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj);
10572
}
10573
else {
10574
10575
JimIncrCmdRefCount(cmdPtr);
10576
JimUpdateProcNamespace(interp, cmdPtr, newNameObj);
10577
Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr);
10578
10579
10580
Jim_DeleteHashEntry(&interp->commands, oldNameObj);
10581
10582
10583
Jim_InterpIncrProcEpoch(interp);
10584
10585
ret = JIM_OK;
10586
}
10587
}
10588
10589
Jim_DecrRefCount(interp, oldNameObj);
10590
Jim_DecrRefCount(interp, newNameObj);
10591
10592
return ret;
10593
}
10594
10595
10596
static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10597
{
10598
Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
10599
}
10600
10601
static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10602
{
10603
dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
10604
dupPtr->typePtr = srcPtr->typePtr;
10605
Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
10606
}
10607
10608
static const Jim_ObjType commandObjType = {
10609
"command",
10610
FreeCommandInternalRep,
10611
DupCommandInternalRep,
10612
NULL,
10613
JIM_TYPE_REFERENCES,
10614
};
10615
10616
Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10617
{
10618
Jim_Cmd *cmd;
10619
10620
if (objPtr->typePtr == &commandObjType
10621
&& objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch
10622
#ifdef jim_ext_namespace
10623
&& Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
10624
#endif
10625
&& objPtr->internalRep.cmdValue.cmdPtr->inUse) {
10626
10627
cmd = objPtr->internalRep.cmdValue.cmdPtr;
10628
}
10629
else {
10630
Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr);
10631
Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj);
10632
#ifdef jim_ext_namespace
10633
if (he == NULL && Jim_Length(interp->framePtr->nsObj)) {
10634
he = Jim_FindHashEntry(&interp->commands, objPtr);
10635
}
10636
#endif
10637
if (he == NULL) {
10638
if (flags & JIM_ERRMSG) {
10639
Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
10640
}
10641
Jim_DecrRefCount(interp, qualifiedNameObj);
10642
return NULL;
10643
}
10644
cmd = Jim_GetHashEntryVal(he);
10645
10646
cmd->cmdNameObj = Jim_GetHashEntryKey(he);
10647
10648
10649
Jim_FreeIntRep(interp, objPtr);
10650
objPtr->typePtr = &commandObjType;
10651
objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
10652
objPtr->internalRep.cmdValue.cmdPtr = cmd;
10653
objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
10654
Jim_IncrRefCount(interp->framePtr->nsObj);
10655
Jim_DecrRefCount(interp, qualifiedNameObj);
10656
}
10657
while (cmd->u.proc.upcall) {
10658
cmd = cmd->prevCmd;
10659
}
10660
return cmd;
10661
}
10662
10663
10664
10665
static const Jim_ObjType variableObjType = {
10666
"variable",
10667
NULL,
10668
NULL,
10669
NULL,
10670
JIM_TYPE_REFERENCES,
10671
};
10672
10673
static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
10674
{
10675
const char *varName;
10676
Jim_CallFrame *framePtr;
10677
int global;
10678
int len;
10679
Jim_VarVal *vv;
10680
10681
10682
if (objPtr->typePtr == &variableObjType) {
10683
framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
10684
if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
10685
10686
return JIM_OK;
10687
}
10688
10689
}
10690
else if (objPtr->typePtr == &dictSubstObjType) {
10691
return JIM_DICT_SUGAR;
10692
}
10693
10694
varName = Jim_GetString(objPtr, &len);
10695
10696
10697
if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
10698
return JIM_DICT_SUGAR;
10699
}
10700
10701
if (varName[0] == ':' && varName[1] == ':') {
10702
while (*varName == ':') {
10703
varName++;
10704
len--;
10705
}
10706
global = 1;
10707
framePtr = interp->topFramePtr;
10708
10709
Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len);
10710
vv = JimFindVariable(&framePtr->vars, tempObj);
10711
Jim_FreeNewObj(interp, tempObj);
10712
}
10713
else {
10714
global = 0;
10715
framePtr = interp->framePtr;
10716
10717
vv = JimFindVariable(&framePtr->vars, objPtr);
10718
if (vv == NULL && framePtr->staticVars) {
10719
10720
vv = JimFindVariable(framePtr->staticVars, objPtr);
10721
}
10722
}
10723
10724
if (vv == NULL) {
10725
return JIM_ERR;
10726
}
10727
10728
10729
Jim_FreeIntRep(interp, objPtr);
10730
objPtr->typePtr = &variableObjType;
10731
objPtr->internalRep.varValue.callFrameId = framePtr->id;
10732
objPtr->internalRep.varValue.vv = vv;
10733
objPtr->internalRep.varValue.global = global;
10734
return JIM_OK;
10735
}
10736
10737
10738
static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
10739
static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
10740
10741
static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv)
10742
{
10743
return Jim_AddHashEntry(ht, nameObjPtr, vv);
10744
}
10745
10746
static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
10747
{
10748
Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr);
10749
if (he) {
10750
return (Jim_VarVal *)Jim_GetHashEntryVal(he);
10751
}
10752
return NULL;
10753
}
10754
10755
static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
10756
{
10757
return Jim_DeleteHashEntry(ht, nameObjPtr);
10758
}
10759
10760
static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
10761
{
10762
const char *name;
10763
Jim_CallFrame *framePtr;
10764
int global;
10765
int len;
10766
10767
10768
Jim_VarVal *vv = Jim_Alloc(sizeof(*vv));
10769
10770
vv->objPtr = valObjPtr;
10771
Jim_IncrRefCount(valObjPtr);
10772
vv->linkFramePtr = NULL;
10773
vv->refCount = 0;
10774
10775
name = Jim_GetString(nameObjPtr, &len);
10776
if (name[0] == ':' && name[1] == ':') {
10777
while (*name == ':') {
10778
name++;
10779
len--;
10780
}
10781
framePtr = interp->topFramePtr;
10782
global = 1;
10783
JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv);
10784
}
10785
else {
10786
framePtr = interp->framePtr;
10787
global = 0;
10788
JimSetNewVariable(&framePtr->vars, nameObjPtr, vv);
10789
}
10790
10791
10792
Jim_FreeIntRep(interp, nameObjPtr);
10793
nameObjPtr->typePtr = &variableObjType;
10794
nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
10795
nameObjPtr->internalRep.varValue.vv = vv;
10796
nameObjPtr->internalRep.varValue.global = global;
10797
10798
return vv;
10799
}
10800
10801
int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
10802
{
10803
int err;
10804
Jim_VarVal *vv;
10805
10806
switch (SetVariableFromAny(interp, nameObjPtr)) {
10807
case JIM_DICT_SUGAR:
10808
return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
10809
10810
case JIM_ERR:
10811
JimCreateVariable(interp, nameObjPtr, valObjPtr);
10812
break;
10813
10814
case JIM_OK:
10815
vv = nameObjPtr->internalRep.varValue.vv;
10816
if (vv->linkFramePtr == NULL) {
10817
Jim_IncrRefCount(valObjPtr);
10818
Jim_DecrRefCount(interp, vv->objPtr);
10819
vv->objPtr = valObjPtr;
10820
}
10821
else {
10822
Jim_CallFrame *savedCallFrame;
10823
10824
savedCallFrame = interp->framePtr;
10825
interp->framePtr = vv->linkFramePtr;
10826
err = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
10827
interp->framePtr = savedCallFrame;
10828
if (err != JIM_OK)
10829
return err;
10830
}
10831
}
10832
return JIM_OK;
10833
}
10834
10835
int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
10836
{
10837
Jim_Obj *nameObjPtr;
10838
int result;
10839
10840
nameObjPtr = Jim_NewStringObj(interp, name, -1);
10841
Jim_IncrRefCount(nameObjPtr);
10842
result = Jim_SetVariable(interp, nameObjPtr, objPtr);
10843
Jim_DecrRefCount(interp, nameObjPtr);
10844
return result;
10845
}
10846
10847
int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
10848
{
10849
Jim_CallFrame *savedFramePtr;
10850
int result;
10851
10852
savedFramePtr = interp->framePtr;
10853
interp->framePtr = interp->topFramePtr;
10854
result = Jim_SetVariableStr(interp, name, objPtr);
10855
interp->framePtr = savedFramePtr;
10856
return result;
10857
}
10858
10859
int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
10860
{
10861
Jim_Obj *valObjPtr;
10862
int result;
10863
10864
valObjPtr = Jim_NewStringObj(interp, val, -1);
10865
Jim_IncrRefCount(valObjPtr);
10866
result = Jim_SetVariableStr(interp, name, valObjPtr);
10867
Jim_DecrRefCount(interp, valObjPtr);
10868
return result;
10869
}
10870
10871
int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
10872
Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
10873
{
10874
const char *varName;
10875
const char *targetName;
10876
Jim_CallFrame *framePtr;
10877
Jim_VarVal *vv;
10878
int len;
10879
int varnamelen;
10880
10881
10882
switch (SetVariableFromAny(interp, nameObjPtr)) {
10883
case JIM_DICT_SUGAR:
10884
10885
Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
10886
return JIM_ERR;
10887
10888
case JIM_OK:
10889
vv = nameObjPtr->internalRep.varValue.vv;
10890
10891
if (vv->linkFramePtr == NULL) {
10892
Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
10893
return JIM_ERR;
10894
}
10895
10896
10897
vv->linkFramePtr = NULL;
10898
break;
10899
}
10900
10901
10902
10903
varName = Jim_GetString(nameObjPtr, &varnamelen);
10904
10905
if (varName[0] == ':' && varName[1] == ':') {
10906
while (*varName == ':') {
10907
varName++;
10908
varnamelen--;
10909
}
10910
10911
framePtr = interp->topFramePtr;
10912
}
10913
else {
10914
framePtr = interp->framePtr;
10915
}
10916
10917
targetName = Jim_GetString(targetNameObjPtr, &len);
10918
if (targetName[0] == ':' && targetName[1] == ':') {
10919
while (*targetName == ':') {
10920
targetName++;
10921
len--;
10922
}
10923
targetNameObjPtr = Jim_NewStringObj(interp, targetName, len);
10924
targetCallFrame = interp->topFramePtr;
10925
}
10926
Jim_IncrRefCount(targetNameObjPtr);
10927
10928
if (framePtr->level < targetCallFrame->level) {
10929
Jim_SetResultFormatted(interp,
10930
"bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
10931
nameObjPtr);
10932
Jim_DecrRefCount(interp, targetNameObjPtr);
10933
return JIM_ERR;
10934
}
10935
10936
10937
if (framePtr == targetCallFrame) {
10938
Jim_Obj *objPtr = targetNameObjPtr;
10939
10940
10941
while (1) {
10942
if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) {
10943
Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
10944
Jim_DecrRefCount(interp, targetNameObjPtr);
10945
return JIM_ERR;
10946
}
10947
if (SetVariableFromAny(interp, objPtr) != JIM_OK)
10948
break;
10949
vv = objPtr->internalRep.varValue.vv;
10950
if (vv->linkFramePtr != targetCallFrame)
10951
break;
10952
objPtr = vv->objPtr;
10953
}
10954
}
10955
10956
10957
Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
10958
10959
nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame;
10960
Jim_DecrRefCount(interp, targetNameObjPtr);
10961
return JIM_OK;
10962
}
10963
10964
Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10965
{
10966
if (interp->safeexpr) {
10967
return nameObjPtr;
10968
}
10969
switch (SetVariableFromAny(interp, nameObjPtr)) {
10970
case JIM_OK:{
10971
Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv;
10972
10973
if (vv->linkFramePtr == NULL) {
10974
return vv->objPtr;
10975
}
10976
else {
10977
Jim_Obj *objPtr;
10978
10979
10980
Jim_CallFrame *savedCallFrame = interp->framePtr;
10981
10982
interp->framePtr = vv->linkFramePtr;
10983
objPtr = Jim_GetVariable(interp, vv->objPtr, flags);
10984
interp->framePtr = savedCallFrame;
10985
if (objPtr) {
10986
return objPtr;
10987
}
10988
10989
}
10990
}
10991
break;
10992
10993
case JIM_DICT_SUGAR:
10994
10995
return JimDictSugarGet(interp, nameObjPtr, flags);
10996
}
10997
if (flags & JIM_ERRMSG) {
10998
Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
10999
}
11000
return NULL;
11001
}
11002
11003
Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11004
{
11005
Jim_CallFrame *savedFramePtr;
11006
Jim_Obj *objPtr;
11007
11008
savedFramePtr = interp->framePtr;
11009
interp->framePtr = interp->topFramePtr;
11010
objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
11011
interp->framePtr = savedFramePtr;
11012
11013
return objPtr;
11014
}
11015
11016
Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
11017
{
11018
Jim_Obj *nameObjPtr, *varObjPtr;
11019
11020
nameObjPtr = Jim_NewStringObj(interp, name, -1);
11021
Jim_IncrRefCount(nameObjPtr);
11022
varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
11023
Jim_DecrRefCount(interp, nameObjPtr);
11024
return varObjPtr;
11025
}
11026
11027
Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
11028
{
11029
Jim_CallFrame *savedFramePtr;
11030
Jim_Obj *objPtr;
11031
11032
savedFramePtr = interp->framePtr;
11033
interp->framePtr = interp->topFramePtr;
11034
objPtr = Jim_GetVariableStr(interp, name, flags);
11035
interp->framePtr = savedFramePtr;
11036
11037
return objPtr;
11038
}
11039
11040
int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11041
{
11042
Jim_VarVal *vv;
11043
int retval;
11044
Jim_CallFrame *framePtr;
11045
11046
retval = SetVariableFromAny(interp, nameObjPtr);
11047
if (retval == JIM_DICT_SUGAR) {
11048
11049
return JimDictSugarSet(interp, nameObjPtr, NULL);
11050
}
11051
else if (retval == JIM_OK) {
11052
vv = nameObjPtr->internalRep.varValue.vv;
11053
11054
11055
if (vv->linkFramePtr) {
11056
framePtr = interp->framePtr;
11057
interp->framePtr = vv->linkFramePtr;
11058
retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE);
11059
interp->framePtr = framePtr;
11060
}
11061
else {
11062
if (nameObjPtr->internalRep.varValue.global) {
11063
int len;
11064
const char *name = Jim_GetString(nameObjPtr, &len);
11065
while (*name == ':') {
11066
name++;
11067
len--;
11068
}
11069
framePtr = interp->topFramePtr;
11070
Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len);
11071
retval = JimUnsetVariable(&framePtr->vars, tempObj);
11072
Jim_FreeNewObj(interp, tempObj);
11073
}
11074
else {
11075
framePtr = interp->framePtr;
11076
retval = JimUnsetVariable(&framePtr->vars, nameObjPtr);
11077
}
11078
11079
if (retval == JIM_OK) {
11080
11081
framePtr->id = interp->callFrameEpoch++;
11082
}
11083
}
11084
}
11085
if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
11086
Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
11087
}
11088
return retval;
11089
}
11090
11091
11092
11093
static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
11094
Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
11095
{
11096
const char *str, *p;
11097
int len, keyLen;
11098
Jim_Obj *varObjPtr, *keyObjPtr;
11099
11100
str = Jim_GetString(objPtr, &len);
11101
11102
p = strchr(str, '(');
11103
JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
11104
11105
varObjPtr = Jim_NewStringObj(interp, str, p - str);
11106
11107
p++;
11108
keyLen = (str + len) - p;
11109
if (str[len - 1] == ')') {
11110
keyLen--;
11111
}
11112
11113
11114
keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
11115
11116
Jim_IncrRefCount(varObjPtr);
11117
Jim_IncrRefCount(keyObjPtr);
11118
*varPtrPtr = varObjPtr;
11119
*keyPtrPtr = keyObjPtr;
11120
}
11121
11122
static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
11123
{
11124
int err;
11125
11126
SetDictSubstFromAny(interp, objPtr);
11127
11128
err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
11129
&objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
11130
11131
if (err == JIM_OK) {
11132
11133
Jim_SetEmptyResult(interp);
11134
}
11135
else {
11136
if (!valObjPtr) {
11137
11138
if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
11139
Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
11140
objPtr);
11141
return err;
11142
}
11143
}
11144
11145
Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
11146
(valObjPtr ? "set" : "unset"), objPtr);
11147
}
11148
return err;
11149
}
11150
11151
static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
11152
Jim_Obj *keyObjPtr, int flags)
11153
{
11154
Jim_Obj *dictObjPtr;
11155
Jim_Obj *resObjPtr = NULL;
11156
int ret;
11157
11158
dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
11159
if (!dictObjPtr) {
11160
return NULL;
11161
}
11162
11163
ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
11164
if (ret != JIM_OK) {
11165
Jim_SetResultFormatted(interp,
11166
"can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
11167
ret < 0 ? "variable isn't" : "no such element in");
11168
}
11169
else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
11170
11171
Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
11172
}
11173
11174
return resObjPtr;
11175
}
11176
11177
11178
static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11179
{
11180
SetDictSubstFromAny(interp, objPtr);
11181
11182
return JimDictExpandArrayVariable(interp,
11183
objPtr->internalRep.dictSubstValue.varNameObjPtr,
11184
objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
11185
}
11186
11187
11188
11189
void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11190
{
11191
Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
11192
Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
11193
}
11194
11195
static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11196
{
11197
11198
dupPtr->internalRep = srcPtr->internalRep;
11199
11200
Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
11201
Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
11202
}
11203
11204
11205
static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11206
{
11207
if (objPtr->typePtr != &dictSubstObjType) {
11208
Jim_Obj *varObjPtr, *keyObjPtr;
11209
11210
if (objPtr->typePtr == &interpolatedObjType) {
11211
11212
11213
varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
11214
keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
11215
11216
Jim_IncrRefCount(varObjPtr);
11217
Jim_IncrRefCount(keyObjPtr);
11218
}
11219
else {
11220
JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
11221
}
11222
11223
Jim_FreeIntRep(interp, objPtr);
11224
objPtr->typePtr = &dictSubstObjType;
11225
objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
11226
objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
11227
}
11228
}
11229
11230
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
11231
{
11232
Jim_Obj *resObjPtr = NULL;
11233
Jim_Obj *substKeyObjPtr = NULL;
11234
11235
if (interp->safeexpr) {
11236
return objPtr;
11237
}
11238
11239
SetDictSubstFromAny(interp, objPtr);
11240
11241
if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
11242
&substKeyObjPtr, JIM_NONE)
11243
!= JIM_OK) {
11244
return NULL;
11245
}
11246
Jim_IncrRefCount(substKeyObjPtr);
11247
resObjPtr =
11248
JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
11249
substKeyObjPtr, 0);
11250
Jim_DecrRefCount(interp, substKeyObjPtr);
11251
11252
return resObjPtr;
11253
}
11254
11255
11256
static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
11257
{
11258
Jim_CallFrame *cf;
11259
11260
if (interp->freeFramesList) {
11261
cf = interp->freeFramesList;
11262
interp->freeFramesList = cf->next;
11263
11264
cf->argv = NULL;
11265
cf->argc = 0;
11266
cf->procArgsObjPtr = NULL;
11267
cf->procBodyObjPtr = NULL;
11268
cf->next = NULL;
11269
cf->staticVars = NULL;
11270
cf->localCommands = NULL;
11271
cf->tailcallObj = NULL;
11272
cf->tailcallCmd = NULL;
11273
}
11274
else {
11275
cf = Jim_Alloc(sizeof(*cf));
11276
memset(cf, 0, sizeof(*cf));
11277
11278
Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
11279
}
11280
11281
cf->id = interp->callFrameEpoch++;
11282
cf->parent = parent;
11283
cf->level = parent ? parent->level + 1 : 0;
11284
cf->nsObj = nsObj;
11285
Jim_IncrRefCount(nsObj);
11286
11287
return cf;
11288
}
11289
11290
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
11291
{
11292
11293
if (localCommands) {
11294
Jim_Obj *cmdNameObj;
11295
11296
while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
11297
Jim_HashTable *ht = &interp->commands;
11298
Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj);
11299
if (he) {
11300
Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
11301
if (cmd->prevCmd) {
11302
Jim_Cmd *prevCmd = cmd->prevCmd;
11303
cmd->prevCmd = NULL;
11304
11305
11306
JimDecrCmdRefCount(interp, cmd);
11307
11308
11309
Jim_SetHashVal(ht, he, prevCmd);
11310
}
11311
else {
11312
Jim_DeleteHashEntry(ht, cmdNameObj);
11313
}
11314
}
11315
Jim_DecrRefCount(interp, cmdNameObj);
11316
}
11317
Jim_FreeStack(localCommands);
11318
Jim_Free(localCommands);
11319
}
11320
return JIM_OK;
11321
}
11322
11323
static int JimInvokeDefer(Jim_Interp *interp, int retcode)
11324
{
11325
Jim_Obj *objPtr;
11326
11327
11328
if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) {
11329
return retcode;
11330
}
11331
objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE);
11332
11333
if (objPtr) {
11334
int ret = JIM_OK;
11335
int i;
11336
int listLen = Jim_ListLength(interp, objPtr);
11337
Jim_Obj *resultObjPtr;
11338
11339
Jim_IncrRefCount(objPtr);
11340
11341
resultObjPtr = Jim_GetResult(interp);
11342
Jim_IncrRefCount(resultObjPtr);
11343
Jim_SetEmptyResult(interp);
11344
11345
11346
for (i = listLen; i > 0; i--) {
11347
11348
Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
11349
ret = Jim_EvalObj(interp, scriptObjPtr);
11350
if (ret != JIM_OK) {
11351
break;
11352
}
11353
}
11354
11355
if (ret == JIM_OK || retcode == JIM_ERR) {
11356
11357
Jim_SetResult(interp, resultObjPtr);
11358
}
11359
else {
11360
retcode = ret;
11361
}
11362
11363
Jim_DecrRefCount(interp, resultObjPtr);
11364
Jim_DecrRefCount(interp, objPtr);
11365
}
11366
return retcode;
11367
}
11368
11369
#define JIM_FCF_FULL 0
11370
#define JIM_FCF_REUSE 1
11371
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
11372
{
11373
JimDeleteLocalProcs(interp, cf->localCommands);
11374
11375
if (cf->procArgsObjPtr)
11376
Jim_DecrRefCount(interp, cf->procArgsObjPtr);
11377
if (cf->procBodyObjPtr)
11378
Jim_DecrRefCount(interp, cf->procBodyObjPtr);
11379
Jim_DecrRefCount(interp, cf->nsObj);
11380
if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
11381
Jim_FreeHashTable(&cf->vars);
11382
else {
11383
Jim_ClearHashTable(&cf->vars);
11384
}
11385
cf->next = interp->freeFramesList;
11386
interp->freeFramesList = cf;
11387
}
11388
11389
11390
11391
int Jim_IsBigEndian(void)
11392
{
11393
union {
11394
unsigned short s;
11395
unsigned char c[2];
11396
} uval = {0x0102};
11397
11398
return uval.c[0] == 1;
11399
}
11400
11401
11402
Jim_Interp *Jim_CreateInterp(void)
11403
{
11404
Jim_Interp *i = Jim_Alloc(sizeof(*i));
11405
11406
memset(i, 0, sizeof(*i));
11407
11408
i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
11409
i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
11410
i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
11411
11412
Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
11413
#ifdef JIM_REFERENCES
11414
Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
11415
#endif
11416
Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
11417
Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
11418
i->emptyObj = Jim_NewEmptyStringObj(i);
11419
i->trueObj = Jim_NewIntObj(i, 1);
11420
i->falseObj = Jim_NewIntObj(i, 0);
11421
i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
11422
i->result = i->emptyObj;
11423
i->stackTrace = Jim_NewListObj(i, NULL, 0);
11424
i->unknown = Jim_NewStringObj(i, "unknown", -1);
11425
i->defer = Jim_NewStringObj(i, "jim::defer", -1);
11426
i->errorProc = i->emptyObj;
11427
i->nullScriptObj = Jim_NewEmptyStringObj(i);
11428
i->evalFrame = &i->topEvalFrame;
11429
i->currentFilenameObj = Jim_NewEmptyStringObj(i);
11430
Jim_IncrRefCount(i->emptyObj);
11431
Jim_IncrRefCount(i->result);
11432
Jim_IncrRefCount(i->stackTrace);
11433
Jim_IncrRefCount(i->unknown);
11434
Jim_IncrRefCount(i->defer);
11435
Jim_IncrRefCount(i->nullScriptObj);
11436
Jim_IncrRefCount(i->errorProc);
11437
Jim_IncrRefCount(i->trueObj);
11438
Jim_IncrRefCount(i->falseObj);
11439
Jim_IncrRefCount(i->currentFilenameObj);
11440
11441
11442
Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
11443
Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
11444
11445
Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
11446
Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
11447
Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
11448
Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
11449
Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
11450
Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
11451
Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0");
11452
Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
11453
Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
11454
Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4));
11455
11456
return i;
11457
}
11458
11459
void Jim_FreeInterp(Jim_Interp *i)
11460
{
11461
Jim_CallFrame *cf, *cfx;
11462
11463
Jim_Obj *objPtr, *nextObjPtr;
11464
11465
i->quitting = 1;
11466
11467
11468
for (cf = i->framePtr; cf; cf = cfx) {
11469
11470
JimInvokeDefer(i, JIM_OK);
11471
cfx = cf->parent;
11472
JimFreeCallFrame(i, cf, JIM_FCF_FULL);
11473
}
11474
11475
Jim_DecrRefCount(i, i->emptyObj);
11476
Jim_DecrRefCount(i, i->trueObj);
11477
Jim_DecrRefCount(i, i->falseObj);
11478
Jim_DecrRefCount(i, i->result);
11479
Jim_DecrRefCount(i, i->stackTrace);
11480
Jim_DecrRefCount(i, i->errorProc);
11481
Jim_DecrRefCount(i, i->unknown);
11482
Jim_DecrRefCount(i, i->defer);
11483
Jim_DecrRefCount(i, i->nullScriptObj);
11484
Jim_DecrRefCount(i, i->currentFilenameObj);
11485
11486
Jim_FreeHashTable(&i->commands);
11487
11488
11489
Jim_InterpIncrProcEpoch(i);
11490
11491
#ifdef JIM_REFERENCES
11492
Jim_FreeHashTable(&i->references);
11493
#endif
11494
Jim_FreeHashTable(&i->packages);
11495
Jim_Free(i->prngState);
11496
Jim_FreeHashTable(&i->assocData);
11497
if (i->traceCmdObj) {
11498
Jim_DecrRefCount(i, i->traceCmdObj);
11499
}
11500
11501
#ifdef JIM_MAINTAINER
11502
if (i->liveList != NULL) {
11503
objPtr = i->liveList;
11504
11505
printf("\n-------------------------------------\n");
11506
printf("Objects still in the free list:\n");
11507
while (objPtr) {
11508
const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
11509
Jim_String(objPtr);
11510
11511
if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
11512
printf("%p (%d) %-10s: '%.20s...'\n",
11513
(void *)objPtr, objPtr->refCount, type, objPtr->bytes);
11514
}
11515
else {
11516
printf("%p (%d) %-10s: '%s'\n",
11517
(void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
11518
}
11519
if (objPtr->typePtr == &sourceObjType) {
11520
printf("FILE %s LINE %d\n",
11521
Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
11522
objPtr->internalRep.sourceValue.lineNumber);
11523
}
11524
objPtr = objPtr->nextObjPtr;
11525
}
11526
printf("-------------------------------------\n\n");
11527
JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
11528
}
11529
#endif
11530
11531
11532
objPtr = i->freeList;
11533
while (objPtr) {
11534
nextObjPtr = objPtr->nextObjPtr;
11535
Jim_Free(objPtr);
11536
objPtr = nextObjPtr;
11537
}
11538
11539
11540
for (cf = i->freeFramesList; cf; cf = cfx) {
11541
cfx = cf->next;
11542
if (cf->vars.table)
11543
Jim_FreeHashTable(&cf->vars);
11544
Jim_Free(cf);
11545
}
11546
11547
11548
Jim_Free(i);
11549
}
11550
11551
Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
11552
{
11553
long level;
11554
const char *str;
11555
Jim_CallFrame *framePtr;
11556
11557
if (levelObjPtr) {
11558
str = Jim_String(levelObjPtr);
11559
if (str[0] == '#') {
11560
char *endptr;
11561
11562
level = jim_strtol(str + 1, &endptr);
11563
if (str[1] == '\0' || endptr[0] != '\0') {
11564
level = -1;
11565
}
11566
}
11567
else {
11568
if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
11569
level = -1;
11570
}
11571
else {
11572
11573
level = interp->framePtr->level - level;
11574
}
11575
}
11576
}
11577
else {
11578
str = "1";
11579
level = interp->framePtr->level - 1;
11580
}
11581
11582
if (level == 0) {
11583
return interp->topFramePtr;
11584
}
11585
if (level > 0) {
11586
11587
for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
11588
if (framePtr->level == level) {
11589
return framePtr;
11590
}
11591
}
11592
}
11593
11594
Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
11595
return NULL;
11596
}
11597
11598
static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level)
11599
{
11600
Jim_CallFrame *framePtr;
11601
11602
if (level == 0) {
11603
return interp->framePtr;
11604
}
11605
11606
if (level < 0) {
11607
11608
level = interp->framePtr->level + level;
11609
}
11610
11611
if (level > 0) {
11612
11613
for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
11614
if (framePtr->level == level) {
11615
return framePtr;
11616
}
11617
}
11618
}
11619
return NULL;
11620
}
11621
11622
static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel)
11623
{
11624
Jim_EvalFrame *evalFrame;
11625
11626
if (proclevel == 0) {
11627
return interp->evalFrame;
11628
}
11629
11630
if (proclevel < 0) {
11631
11632
proclevel = interp->procLevel + proclevel;
11633
}
11634
11635
if (proclevel >= 0) {
11636
11637
for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) {
11638
if (evalFrame->procLevel == proclevel) {
11639
return evalFrame;
11640
}
11641
}
11642
}
11643
return NULL;
11644
}
11645
11646
static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame)
11647
{
11648
if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) {
11649
Jim_EvalFrame *e;
11650
for (e = frame->parent; e; e = e->parent) {
11651
if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) {
11652
break;
11653
}
11654
}
11655
if (e && e->cmd && e->cmd->cmdNameObj) {
11656
return e->cmd->cmdNameObj;
11657
}
11658
}
11659
return NULL;
11660
}
11661
11662
static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj)
11663
{
11664
Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
11665
Jim_Obj *fileNameObj = interp->emptyObj;
11666
int linenr = 1;
11667
11668
if (frame->scriptObj) {
11669
ScriptObj *script = JimGetScript(interp, frame->scriptObj);
11670
fileNameObj = script->fileNameObj;
11671
linenr = script->linenr;
11672
}
11673
11674
Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj);
11675
Jim_ListAppendElement(interp, listObj, fileNameObj);
11676
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr));
11677
Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc));
11678
}
11679
11680
static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
11681
{
11682
11683
Jim_IncrRefCount(stackTraceObj);
11684
Jim_DecrRefCount(interp, interp->stackTrace);
11685
interp->stackTrace = stackTraceObj;
11686
interp->errorFlag = 1;
11687
}
11688
11689
static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script)
11690
{
11691
if (!interp->errorFlag) {
11692
int i;
11693
Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0);
11694
11695
if (interp->procLevel == 0 && script) {
11696
Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
11697
Jim_ListAppendElement(interp, stackTrace, script->fileNameObj);
11698
Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr));
11699
Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
11700
}
11701
else {
11702
for (i = 0; i <= interp->procLevel; i++) {
11703
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
11704
if (frame) {
11705
JimAddStackFrame(interp, frame, stackTrace);
11706
}
11707
}
11708
}
11709
JimSetStackTrace(interp, stackTrace);
11710
}
11711
}
11712
11713
int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
11714
void *data)
11715
{
11716
AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
11717
11718
assocEntryPtr->delProc = delProc;
11719
assocEntryPtr->data = data;
11720
return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
11721
}
11722
11723
void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
11724
{
11725
Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
11726
11727
if (entryPtr != NULL) {
11728
AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
11729
return assocEntryPtr->data;
11730
}
11731
return NULL;
11732
}
11733
11734
int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
11735
{
11736
return Jim_DeleteHashEntry(&interp->assocData, key);
11737
}
11738
11739
int Jim_GetExitCode(Jim_Interp *interp)
11740
{
11741
return interp->exitCode;
11742
}
11743
11744
static void UpdateStringOfInt(struct Jim_Obj *objPtr);
11745
static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
11746
11747
static const Jim_ObjType intObjType = {
11748
"int",
11749
NULL,
11750
NULL,
11751
UpdateStringOfInt,
11752
JIM_TYPE_NONE,
11753
};
11754
11755
static const Jim_ObjType coercedDoubleObjType = {
11756
"coerced-double",
11757
NULL,
11758
NULL,
11759
UpdateStringOfInt,
11760
JIM_TYPE_NONE,
11761
};
11762
11763
11764
static void UpdateStringOfInt(struct Jim_Obj *objPtr)
11765
{
11766
char buf[JIM_INTEGER_SPACE + 1];
11767
jim_wide wideValue = JimWideValue(objPtr);
11768
int pos = 0;
11769
11770
if (wideValue == 0) {
11771
buf[pos++] = '0';
11772
}
11773
else {
11774
char tmp[JIM_INTEGER_SPACE];
11775
int num = 0;
11776
int i;
11777
11778
if (wideValue < 0) {
11779
buf[pos++] = '-';
11780
i = wideValue % 10;
11781
tmp[num++] = (i > 0) ? (10 - i) : -i;
11782
wideValue /= -10;
11783
}
11784
11785
while (wideValue) {
11786
tmp[num++] = wideValue % 10;
11787
wideValue /= 10;
11788
}
11789
11790
for (i = 0; i < num; i++) {
11791
buf[pos++] = '0' + tmp[num - i - 1];
11792
}
11793
}
11794
buf[pos] = 0;
11795
11796
JimSetStringBytes(objPtr, buf);
11797
}
11798
11799
static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11800
{
11801
jim_wide wideValue;
11802
const char *str;
11803
11804
if (objPtr->typePtr == &coercedDoubleObjType) {
11805
11806
objPtr->typePtr = &intObjType;
11807
return JIM_OK;
11808
}
11809
11810
11811
str = Jim_String(objPtr);
11812
11813
if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
11814
if (flags & JIM_ERRMSG) {
11815
Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
11816
}
11817
return JIM_ERR;
11818
}
11819
if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
11820
Jim_SetResultString(interp, "Integer value too big to be represented", -1);
11821
return JIM_ERR;
11822
}
11823
11824
Jim_FreeIntRep(interp, objPtr);
11825
objPtr->typePtr = &intObjType;
11826
objPtr->internalRep.wideValue = wideValue;
11827
return JIM_OK;
11828
}
11829
11830
#ifdef JIM_OPTIMIZATION
11831
static int JimIsWide(Jim_Obj *objPtr)
11832
{
11833
return objPtr->typePtr == &intObjType;
11834
}
11835
#endif
11836
11837
int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11838
{
11839
if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
11840
return JIM_ERR;
11841
*widePtr = JimWideValue(objPtr);
11842
return JIM_OK;
11843
}
11844
11845
int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11846
{
11847
int ret = JIM_OK;
11848
11849
if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) {
11850
SetIntFromAny(interp, objPtr, 0);
11851
}
11852
if (objPtr->typePtr == &intObjType) {
11853
*widePtr = JimWideValue(objPtr);
11854
}
11855
else {
11856
JimPanic((interp->safeexpr, "interp->safeexpr is set"));
11857
interp->safeexpr++;
11858
ret = Jim_EvalExpression(interp, objPtr);
11859
interp->safeexpr--;
11860
11861
if (ret == JIM_OK) {
11862
ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr);
11863
}
11864
if (ret != JIM_OK) {
11865
Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr);
11866
}
11867
}
11868
return ret;
11869
}
11870
11871
11872
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11873
{
11874
if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
11875
return JIM_ERR;
11876
*widePtr = JimWideValue(objPtr);
11877
return JIM_OK;
11878
}
11879
11880
int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
11881
{
11882
jim_wide wideValue;
11883
int retval;
11884
11885
retval = Jim_GetWide(interp, objPtr, &wideValue);
11886
if (retval == JIM_OK) {
11887
*longPtr = (long)wideValue;
11888
return JIM_OK;
11889
}
11890
return JIM_ERR;
11891
}
11892
11893
Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
11894
{
11895
Jim_Obj *objPtr;
11896
11897
objPtr = Jim_NewObj(interp);
11898
objPtr->typePtr = &intObjType;
11899
objPtr->bytes = NULL;
11900
objPtr->internalRep.wideValue = wideValue;
11901
return objPtr;
11902
}
11903
11904
#define JIM_DOUBLE_SPACE 30
11905
11906
static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
11907
static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
11908
11909
static const Jim_ObjType doubleObjType = {
11910
"double",
11911
NULL,
11912
NULL,
11913
UpdateStringOfDouble,
11914
JIM_TYPE_NONE,
11915
};
11916
11917
#if !HAVE_DECL_ISNAN
11918
#undef isnan
11919
#define isnan(X) ((X) != (X))
11920
#endif
11921
#if !HAVE_DECL_ISINF
11922
#undef isinf
11923
#define isinf(X) (1.0 / (X) == 0.0)
11924
#endif
11925
11926
static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
11927
{
11928
double value = objPtr->internalRep.doubleValue;
11929
11930
if (isnan(value)) {
11931
JimSetStringBytes(objPtr, "NaN");
11932
return;
11933
}
11934
if (isinf(value)) {
11935
if (value < 0) {
11936
JimSetStringBytes(objPtr, "-Inf");
11937
}
11938
else {
11939
JimSetStringBytes(objPtr, "Inf");
11940
}
11941
return;
11942
}
11943
{
11944
char buf[JIM_DOUBLE_SPACE + 1];
11945
int i;
11946
int len = sprintf(buf, "%.12g", value);
11947
11948
11949
for (i = 0; i < len; i++) {
11950
if (buf[i] == '.' || buf[i] == 'e') {
11951
#if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
11952
char *e = strchr(buf, 'e');
11953
if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
11954
11955
e += 2;
11956
memmove(e, e + 1, len - (e - buf));
11957
}
11958
#endif
11959
break;
11960
}
11961
}
11962
if (buf[i] == '\0') {
11963
buf[i++] = '.';
11964
buf[i++] = '0';
11965
buf[i] = '\0';
11966
}
11967
JimSetStringBytes(objPtr, buf);
11968
}
11969
}
11970
11971
static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11972
{
11973
double doubleValue;
11974
jim_wide wideValue;
11975
const char *str;
11976
11977
#ifdef HAVE_LONG_LONG
11978
11979
#define MIN_INT_IN_DOUBLE -(1LL << 53)
11980
#define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
11981
11982
if (objPtr->typePtr == &intObjType
11983
&& JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
11984
&& JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
11985
11986
11987
objPtr->typePtr = &coercedDoubleObjType;
11988
return JIM_OK;
11989
}
11990
#endif
11991
str = Jim_String(objPtr);
11992
11993
if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
11994
11995
Jim_FreeIntRep(interp, objPtr);
11996
objPtr->typePtr = &coercedDoubleObjType;
11997
objPtr->internalRep.wideValue = wideValue;
11998
return JIM_OK;
11999
}
12000
else {
12001
12002
if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
12003
Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
12004
return JIM_ERR;
12005
}
12006
12007
Jim_FreeIntRep(interp, objPtr);
12008
}
12009
objPtr->typePtr = &doubleObjType;
12010
objPtr->internalRep.doubleValue = doubleValue;
12011
return JIM_OK;
12012
}
12013
12014
int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
12015
{
12016
if (objPtr->typePtr == &coercedDoubleObjType) {
12017
*doublePtr = JimWideValue(objPtr);
12018
return JIM_OK;
12019
}
12020
if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
12021
return JIM_ERR;
12022
12023
if (objPtr->typePtr == &coercedDoubleObjType) {
12024
*doublePtr = JimWideValue(objPtr);
12025
}
12026
else {
12027
*doublePtr = objPtr->internalRep.doubleValue;
12028
}
12029
return JIM_OK;
12030
}
12031
12032
Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
12033
{
12034
Jim_Obj *objPtr;
12035
12036
objPtr = Jim_NewObj(interp);
12037
objPtr->typePtr = &doubleObjType;
12038
objPtr->bytes = NULL;
12039
objPtr->internalRep.doubleValue = doubleValue;
12040
return objPtr;
12041
}
12042
12043
static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
12044
12045
int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
12046
{
12047
if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
12048
return JIM_ERR;
12049
*booleanPtr = (int) JimWideValue(objPtr);
12050
return JIM_OK;
12051
}
12052
12053
static const char * const jim_true_false_strings[8] = {
12054
"1", "true", "yes", "on",
12055
"0", "false", "no", "off"
12056
};
12057
12058
static const int jim_true_false_lens[8] = {
12059
1, 4, 3, 2,
12060
1, 5, 2, 3,
12061
};
12062
12063
static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
12064
{
12065
int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings,
12066
sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings));
12067
if (index < 0) {
12068
if (flags & JIM_ERRMSG) {
12069
Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
12070
}
12071
return JIM_ERR;
12072
}
12073
12074
12075
Jim_FreeIntRep(interp, objPtr);
12076
objPtr->typePtr = &intObjType;
12077
12078
objPtr->internalRep.wideValue = index < 4 ? 1 : 0;
12079
return JIM_OK;
12080
}
12081
12082
static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
12083
static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
12084
static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
12085
static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
12086
static void UpdateStringOfList(struct Jim_Obj *objPtr);
12087
static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12088
12089
static const Jim_ObjType listObjType = {
12090
"list",
12091
FreeListInternalRep,
12092
DupListInternalRep,
12093
UpdateStringOfList,
12094
JIM_TYPE_NONE,
12095
};
12096
12097
void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
12098
{
12099
int i;
12100
12101
for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
12102
Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
12103
}
12104
Jim_Free(objPtr->internalRep.listValue.ele);
12105
}
12106
12107
void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
12108
{
12109
int i;
12110
12111
JIM_NOTUSED(interp);
12112
12113
dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
12114
dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
12115
dupPtr->internalRep.listValue.ele =
12116
Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
12117
memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
12118
sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
12119
for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
12120
Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
12121
}
12122
dupPtr->typePtr = &listObjType;
12123
}
12124
12125
#define JIM_ELESTR_SIMPLE 0
12126
#define JIM_ELESTR_BRACE 1
12127
#define JIM_ELESTR_QUOTE 2
12128
static unsigned char ListElementQuotingType(const char *s, int len)
12129
{
12130
int i, level, blevel, trySimple = 1;
12131
12132
12133
if (len == 0)
12134
return JIM_ELESTR_BRACE;
12135
if (s[0] == '"' || s[0] == '{') {
12136
trySimple = 0;
12137
goto testbrace;
12138
}
12139
for (i = 0; i < len; i++) {
12140
switch (s[i]) {
12141
case ' ':
12142
case '$':
12143
case '"':
12144
case '[':
12145
case ']':
12146
case ';':
12147
case '\\':
12148
case '\r':
12149
case '\n':
12150
case '\t':
12151
case '\f':
12152
case '\v':
12153
trySimple = 0;
12154
12155
case '{':
12156
case '}':
12157
goto testbrace;
12158
}
12159
}
12160
return JIM_ELESTR_SIMPLE;
12161
12162
testbrace:
12163
12164
if (s[len - 1] == '\\')
12165
return JIM_ELESTR_QUOTE;
12166
level = 0;
12167
blevel = 0;
12168
for (i = 0; i < len; i++) {
12169
switch (s[i]) {
12170
case '{':
12171
level++;
12172
break;
12173
case '}':
12174
level--;
12175
if (level < 0)
12176
return JIM_ELESTR_QUOTE;
12177
break;
12178
case '[':
12179
blevel++;
12180
break;
12181
case ']':
12182
blevel--;
12183
break;
12184
case '\\':
12185
if (s[i + 1] == '\n')
12186
return JIM_ELESTR_QUOTE;
12187
else if (s[i + 1] != '\0')
12188
i++;
12189
break;
12190
}
12191
}
12192
if (blevel < 0) {
12193
return JIM_ELESTR_QUOTE;
12194
}
12195
12196
if (level == 0) {
12197
if (!trySimple)
12198
return JIM_ELESTR_BRACE;
12199
for (i = 0; i < len; i++) {
12200
switch (s[i]) {
12201
case ' ':
12202
case '$':
12203
case '"':
12204
case '[':
12205
case ']':
12206
case ';':
12207
case '\\':
12208
case '\r':
12209
case '\n':
12210
case '\t':
12211
case '\f':
12212
case '\v':
12213
return JIM_ELESTR_BRACE;
12214
break;
12215
}
12216
}
12217
return JIM_ELESTR_SIMPLE;
12218
}
12219
return JIM_ELESTR_QUOTE;
12220
}
12221
12222
static int BackslashQuoteString(const char *s, int len, char *q)
12223
{
12224
char *p = q;
12225
12226
while (len--) {
12227
switch (*s) {
12228
case ' ':
12229
case '$':
12230
case '"':
12231
case '[':
12232
case ']':
12233
case '{':
12234
case '}':
12235
case ';':
12236
case '\\':
12237
*p++ = '\\';
12238
*p++ = *s++;
12239
break;
12240
case '\n':
12241
*p++ = '\\';
12242
*p++ = 'n';
12243
s++;
12244
break;
12245
case '\r':
12246
*p++ = '\\';
12247
*p++ = 'r';
12248
s++;
12249
break;
12250
case '\t':
12251
*p++ = '\\';
12252
*p++ = 't';
12253
s++;
12254
break;
12255
case '\f':
12256
*p++ = '\\';
12257
*p++ = 'f';
12258
s++;
12259
break;
12260
case '\v':
12261
*p++ = '\\';
12262
*p++ = 'v';
12263
s++;
12264
break;
12265
default:
12266
*p++ = *s++;
12267
break;
12268
}
12269
}
12270
*p = '\0';
12271
12272
return p - q;
12273
}
12274
12275
static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
12276
{
12277
#define STATIC_QUOTING_LEN 32
12278
int i, bufLen, realLength;
12279
const char *strRep;
12280
char *p;
12281
unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
12282
12283
12284
if (objc > STATIC_QUOTING_LEN) {
12285
quotingType = Jim_Alloc(objc);
12286
}
12287
else {
12288
quotingType = staticQuoting;
12289
}
12290
bufLen = 0;
12291
for (i = 0; i < objc; i++) {
12292
int len;
12293
12294
strRep = Jim_GetString(objv[i], &len);
12295
quotingType[i] = ListElementQuotingType(strRep, len);
12296
switch (quotingType[i]) {
12297
case JIM_ELESTR_SIMPLE:
12298
if (i != 0 || strRep[0] != '#') {
12299
bufLen += len;
12300
break;
12301
}
12302
12303
quotingType[i] = JIM_ELESTR_BRACE;
12304
12305
case JIM_ELESTR_BRACE:
12306
bufLen += len + 2;
12307
break;
12308
case JIM_ELESTR_QUOTE:
12309
bufLen += len * 2;
12310
break;
12311
}
12312
bufLen++;
12313
}
12314
bufLen++;
12315
12316
12317
p = objPtr->bytes = Jim_Alloc(bufLen + 1);
12318
realLength = 0;
12319
for (i = 0; i < objc; i++) {
12320
int len, qlen;
12321
12322
strRep = Jim_GetString(objv[i], &len);
12323
12324
switch (quotingType[i]) {
12325
case JIM_ELESTR_SIMPLE:
12326
memcpy(p, strRep, len);
12327
p += len;
12328
realLength += len;
12329
break;
12330
case JIM_ELESTR_BRACE:
12331
*p++ = '{';
12332
memcpy(p, strRep, len);
12333
p += len;
12334
*p++ = '}';
12335
realLength += len + 2;
12336
break;
12337
case JIM_ELESTR_QUOTE:
12338
if (i == 0 && strRep[0] == '#') {
12339
*p++ = '\\';
12340
realLength++;
12341
}
12342
qlen = BackslashQuoteString(strRep, len, p);
12343
p += qlen;
12344
realLength += qlen;
12345
break;
12346
}
12347
12348
if (i + 1 != objc) {
12349
*p++ = ' ';
12350
realLength++;
12351
}
12352
}
12353
*p = '\0';
12354
objPtr->length = realLength;
12355
12356
if (quotingType != staticQuoting) {
12357
Jim_Free(quotingType);
12358
}
12359
}
12360
12361
static void UpdateStringOfList(struct Jim_Obj *objPtr)
12362
{
12363
JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
12364
}
12365
12366
static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
12367
{
12368
struct JimParserCtx parser;
12369
const char *str;
12370
int strLen;
12371
Jim_Obj *fileNameObj;
12372
int linenr;
12373
12374
if (objPtr->typePtr == &listObjType) {
12375
return JIM_OK;
12376
}
12377
12378
12379
if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
12380
Jim_Dict *dict = objPtr->internalRep.dictValue;
12381
12382
12383
objPtr->typePtr = &listObjType;
12384
objPtr->internalRep.listValue.len = dict->len;
12385
objPtr->internalRep.listValue.maxLen = dict->maxLen;
12386
objPtr->internalRep.listValue.ele = dict->table;
12387
12388
12389
Jim_Free(dict->ht);
12390
12391
12392
Jim_Free(dict);
12393
return JIM_OK;
12394
}
12395
12396
12397
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr);
12398
Jim_IncrRefCount(fileNameObj);
12399
12400
12401
str = Jim_GetString(objPtr, &strLen);
12402
12403
Jim_FreeIntRep(interp, objPtr);
12404
objPtr->typePtr = &listObjType;
12405
objPtr->internalRep.listValue.len = 0;
12406
objPtr->internalRep.listValue.maxLen = 0;
12407
objPtr->internalRep.listValue.ele = NULL;
12408
12409
12410
if (strLen) {
12411
JimParserInit(&parser, str, strLen, linenr);
12412
while (!parser.eof) {
12413
Jim_Obj *elementPtr;
12414
12415
JimParseList(&parser);
12416
if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
12417
continue;
12418
elementPtr = JimParserGetTokenObj(interp, &parser);
12419
Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
12420
ListAppendElement(objPtr, elementPtr);
12421
}
12422
}
12423
Jim_DecrRefCount(interp, fileNameObj);
12424
return JIM_OK;
12425
}
12426
12427
Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12428
{
12429
Jim_Obj *objPtr;
12430
12431
objPtr = Jim_NewObj(interp);
12432
objPtr->typePtr = &listObjType;
12433
objPtr->bytes = NULL;
12434
objPtr->internalRep.listValue.ele = NULL;
12435
objPtr->internalRep.listValue.len = 0;
12436
objPtr->internalRep.listValue.maxLen = 0;
12437
12438
if (len) {
12439
ListInsertElements(objPtr, 0, len, elements);
12440
}
12441
12442
return objPtr;
12443
}
12444
12445
static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
12446
Jim_Obj ***listVec)
12447
{
12448
*listLen = Jim_ListLength(interp, listObj);
12449
*listVec = listObj->internalRep.listValue.ele;
12450
}
12451
12452
12453
static int JimSign(jim_wide w)
12454
{
12455
if (w == 0) {
12456
return 0;
12457
}
12458
else if (w < 0) {
12459
return -1;
12460
}
12461
return 1;
12462
}
12463
12464
12465
struct lsort_info {
12466
jmp_buf jmpbuf;
12467
Jim_Obj *command;
12468
Jim_Interp *interp;
12469
enum {
12470
JIM_LSORT_ASCII,
12471
JIM_LSORT_NOCASE,
12472
JIM_LSORT_INTEGER,
12473
JIM_LSORT_REAL,
12474
JIM_LSORT_COMMAND,
12475
JIM_LSORT_DICT
12476
} type;
12477
int order;
12478
Jim_Obj **indexv;
12479
int indexc;
12480
int unique;
12481
int (*subfn)(Jim_Obj **, Jim_Obj **);
12482
};
12483
12484
static struct lsort_info *sort_info;
12485
12486
static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12487
{
12488
Jim_Obj *lObj, *rObj;
12489
12490
if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK ||
12491
Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) {
12492
longjmp(sort_info->jmpbuf, JIM_ERR);
12493
}
12494
return sort_info->subfn(&lObj, &rObj);
12495
}
12496
12497
12498
static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12499
{
12500
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
12501
}
12502
12503
static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12504
{
12505
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
12506
}
12507
12508
static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12509
{
12510
12511
const char *left = Jim_String(*lhsObj);
12512
const char *right = Jim_String(*rhsObj);
12513
12514
while (1) {
12515
if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) {
12516
12517
jim_wide lint, rint;
12518
char *lend, *rend;
12519
lint = jim_strtoull(left, &lend);
12520
rint = jim_strtoull(right, &rend);
12521
if (lint != rint) {
12522
return JimSign(lint - rint) * sort_info->order;
12523
}
12524
if (lend -left != rend - right) {
12525
return JimSign((lend - left) - (rend - right)) * sort_info->order;
12526
}
12527
left = lend;
12528
right = rend;
12529
}
12530
else {
12531
int cl, cr;
12532
left += utf8_tounicode_case(left, &cl, 1);
12533
right += utf8_tounicode_case(right, &cr, 1);
12534
if (cl != cr) {
12535
return JimSign(cl - cr) * sort_info->order;
12536
}
12537
if (cl == 0) {
12538
12539
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
12540
}
12541
}
12542
}
12543
}
12544
12545
static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12546
{
12547
jim_wide lhs = 0, rhs = 0;
12548
12549
if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
12550
Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
12551
longjmp(sort_info->jmpbuf, JIM_ERR);
12552
}
12553
12554
return JimSign(lhs - rhs) * sort_info->order;
12555
}
12556
12557
static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12558
{
12559
double lhs = 0, rhs = 0;
12560
12561
if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
12562
Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
12563
longjmp(sort_info->jmpbuf, JIM_ERR);
12564
}
12565
if (lhs == rhs) {
12566
return 0;
12567
}
12568
if (lhs > rhs) {
12569
return sort_info->order;
12570
}
12571
return -sort_info->order;
12572
}
12573
12574
static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12575
{
12576
Jim_Obj *compare_script;
12577
int rc;
12578
12579
jim_wide ret = 0;
12580
12581
12582
compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
12583
Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
12584
Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
12585
12586
rc = Jim_EvalObj(sort_info->interp, compare_script);
12587
12588
if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
12589
longjmp(sort_info->jmpbuf, rc);
12590
}
12591
12592
return JimSign(ret) * sort_info->order;
12593
}
12594
12595
static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
12596
{
12597
int src;
12598
int dst = 0;
12599
Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
12600
12601
for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
12602
if (comp(&ele[dst], &ele[src]) == 0) {
12603
12604
Jim_DecrRefCount(sort_info->interp, ele[dst]);
12605
}
12606
else {
12607
12608
dst++;
12609
}
12610
ele[dst] = ele[src];
12611
}
12612
12613
12614
dst++;
12615
if (dst < listObjPtr->internalRep.listValue.len) {
12616
ele[dst] = ele[src];
12617
}
12618
12619
12620
listObjPtr->internalRep.listValue.len = dst;
12621
}
12622
12623
12624
static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
12625
{
12626
struct lsort_info *prev_info;
12627
12628
typedef int (qsort_comparator) (const void *, const void *);
12629
int (*fn) (Jim_Obj **, Jim_Obj **);
12630
Jim_Obj **vector;
12631
int len;
12632
int rc;
12633
12634
JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
12635
SetListFromAny(interp, listObjPtr);
12636
12637
12638
prev_info = sort_info;
12639
sort_info = info;
12640
12641
vector = listObjPtr->internalRep.listValue.ele;
12642
len = listObjPtr->internalRep.listValue.len;
12643
switch (info->type) {
12644
case JIM_LSORT_ASCII:
12645
fn = ListSortString;
12646
break;
12647
case JIM_LSORT_NOCASE:
12648
fn = ListSortStringNoCase;
12649
break;
12650
case JIM_LSORT_INTEGER:
12651
fn = ListSortInteger;
12652
break;
12653
case JIM_LSORT_REAL:
12654
fn = ListSortReal;
12655
break;
12656
case JIM_LSORT_COMMAND:
12657
fn = ListSortCommand;
12658
break;
12659
case JIM_LSORT_DICT:
12660
fn = ListSortDict;
12661
break;
12662
default:
12663
fn = NULL;
12664
JimPanic((1, "ListSort called with invalid sort type"));
12665
return -1;
12666
}
12667
12668
if (info->indexc) {
12669
12670
info->subfn = fn;
12671
fn = ListSortIndexHelper;
12672
}
12673
12674
if ((rc = setjmp(info->jmpbuf)) == 0) {
12675
qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
12676
12677
if (info->unique && len > 1) {
12678
ListRemoveDuplicates(listObjPtr, fn);
12679
}
12680
12681
Jim_InvalidateStringRep(listObjPtr);
12682
}
12683
sort_info = prev_info;
12684
12685
return rc;
12686
}
12687
12688
12689
static void ListEnsureLength(Jim_Obj *listPtr, int idx)
12690
{
12691
assert(idx >= 0);
12692
if (idx >= listPtr->internalRep.listValue.maxLen) {
12693
if (idx < 4) {
12694
12695
idx = 4;
12696
}
12697
listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
12698
sizeof(Jim_Obj *) * idx);
12699
12700
listPtr->internalRep.listValue.maxLen = idx;
12701
}
12702
}
12703
12704
static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
12705
{
12706
int currentLen = listPtr->internalRep.listValue.len;
12707
int requiredLen = currentLen + elemc;
12708
int i;
12709
Jim_Obj **point;
12710
12711
if (elemc == 0) {
12712
12713
return;
12714
}
12715
12716
if (requiredLen > listPtr->internalRep.listValue.maxLen) {
12717
if (currentLen) {
12718
12719
requiredLen *= 2;
12720
}
12721
ListEnsureLength(listPtr, requiredLen);
12722
}
12723
if (idx < 0) {
12724
idx = currentLen;
12725
}
12726
point = listPtr->internalRep.listValue.ele + idx;
12727
memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
12728
for (i = 0; i < elemc; ++i) {
12729
point[i] = elemVec[i];
12730
Jim_IncrRefCount(point[i]);
12731
}
12732
listPtr->internalRep.listValue.len += elemc;
12733
}
12734
12735
static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
12736
{
12737
ListInsertElements(listPtr, -1, 1, &objPtr);
12738
}
12739
12740
static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
12741
{
12742
ListInsertElements(listPtr, -1,
12743
appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
12744
}
12745
12746
void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
12747
{
12748
JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
12749
SetListFromAny(interp, listPtr);
12750
Jim_InvalidateStringRep(listPtr);
12751
ListAppendElement(listPtr, objPtr);
12752
}
12753
12754
void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
12755
{
12756
JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
12757
SetListFromAny(interp, listPtr);
12758
SetListFromAny(interp, appendListPtr);
12759
Jim_InvalidateStringRep(listPtr);
12760
ListAppendList(listPtr, appendListPtr);
12761
}
12762
12763
int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
12764
{
12765
SetListFromAny(interp, objPtr);
12766
return objPtr->internalRep.listValue.len;
12767
}
12768
12769
void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
12770
int objc, Jim_Obj *const *objVec)
12771
{
12772
JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
12773
SetListFromAny(interp, listPtr);
12774
if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
12775
idx = listPtr->internalRep.listValue.len;
12776
else if (idx < 0)
12777
idx = 0;
12778
Jim_InvalidateStringRep(listPtr);
12779
ListInsertElements(listPtr, idx, objc, objVec);
12780
}
12781
12782
Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
12783
{
12784
SetListFromAny(interp, listPtr);
12785
if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
12786
(idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
12787
return NULL;
12788
}
12789
if (idx < 0)
12790
idx = listPtr->internalRep.listValue.len + idx;
12791
return listPtr->internalRep.listValue.ele[idx];
12792
}
12793
12794
int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
12795
{
12796
*objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
12797
if (*objPtrPtr == NULL) {
12798
if (flags & JIM_ERRMSG) {
12799
Jim_SetResultString(interp, "list index out of range", -1);
12800
}
12801
return JIM_ERR;
12802
}
12803
return JIM_OK;
12804
}
12805
12806
static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr,
12807
Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags)
12808
{
12809
int i;
12810
int static_idxes[5];
12811
int *idxes = static_idxes;
12812
int ret = JIM_OK;
12813
12814
if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) {
12815
idxes = Jim_Alloc(indexc * sizeof(*idxes));
12816
}
12817
12818
for (i = 0; i < indexc; i++) {
12819
ret = Jim_GetIndex(interp, indexv[i], &idxes[i]);
12820
if (ret != JIM_OK) {
12821
goto err;
12822
}
12823
}
12824
12825
for (i = 0; i < indexc; i++) {
12826
Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]);
12827
if (!objPtr) {
12828
if (flags & JIM_ERRMSG) {
12829
if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) {
12830
Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
12831
}
12832
else {
12833
Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr);
12834
}
12835
}
12836
return -1;
12837
}
12838
listPtr = objPtr;
12839
}
12840
*resultObj = listPtr;
12841
err:
12842
if (idxes != static_idxes)
12843
Jim_Free(idxes);
12844
return ret;
12845
}
12846
12847
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
12848
Jim_Obj *newObjPtr, int flags)
12849
{
12850
SetListFromAny(interp, listPtr);
12851
if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
12852
(idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
12853
if (flags & JIM_ERRMSG) {
12854
Jim_SetResultString(interp, "list index out of range", -1);
12855
}
12856
return JIM_ERR;
12857
}
12858
if (idx < 0)
12859
idx = listPtr->internalRep.listValue.len + idx;
12860
Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
12861
listPtr->internalRep.listValue.ele[idx] = newObjPtr;
12862
Jim_IncrRefCount(newObjPtr);
12863
return JIM_OK;
12864
}
12865
12866
int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
12867
Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
12868
{
12869
Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
12870
int shared, i, idx;
12871
12872
varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
12873
if (objPtr == NULL)
12874
return JIM_ERR;
12875
if ((shared = Jim_IsShared(objPtr)))
12876
varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12877
for (i = 0; i < indexc - 1; i++) {
12878
listObjPtr = objPtr;
12879
if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
12880
goto err;
12881
12882
objPtr = Jim_ListGetIndex(interp, listObjPtr, idx);
12883
if (objPtr == NULL) {
12884
Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
12885
goto err;
12886
}
12887
if (Jim_IsShared(objPtr)) {
12888
objPtr = Jim_DuplicateObj(interp, objPtr);
12889
ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
12890
}
12891
Jim_InvalidateStringRep(listObjPtr);
12892
}
12893
if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
12894
goto err;
12895
if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
12896
goto err;
12897
Jim_InvalidateStringRep(objPtr);
12898
Jim_InvalidateStringRep(varObjPtr);
12899
if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
12900
goto err;
12901
Jim_SetResult(interp, varObjPtr);
12902
return JIM_OK;
12903
err:
12904
if (shared) {
12905
Jim_FreeNewObj(interp, varObjPtr);
12906
}
12907
return JIM_ERR;
12908
}
12909
12910
Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
12911
{
12912
int i;
12913
int listLen = Jim_ListLength(interp, listObjPtr);
12914
Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
12915
12916
for (i = 0; i < listLen; ) {
12917
Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
12918
if (++i != listLen) {
12919
Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
12920
}
12921
}
12922
return resObjPtr;
12923
}
12924
12925
Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
12926
{
12927
int i;
12928
12929
for (i = 0; i < objc; i++) {
12930
if (!Jim_IsList(objv[i]))
12931
break;
12932
}
12933
if (i == objc) {
12934
Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
12935
12936
for (i = 0; i < objc; i++)
12937
ListAppendList(objPtr, objv[i]);
12938
return objPtr;
12939
}
12940
else {
12941
12942
int len = 0, objLen;
12943
char *bytes, *p;
12944
12945
12946
for (i = 0; i < objc; i++) {
12947
len += Jim_Length(objv[i]);
12948
}
12949
if (objc)
12950
len += objc - 1;
12951
12952
p = bytes = Jim_Alloc(len + 1);
12953
for (i = 0; i < objc; i++) {
12954
const char *s = Jim_GetString(objv[i], &objLen);
12955
12956
12957
while (objLen && isspace(UCHAR(*s))) {
12958
s++;
12959
objLen--;
12960
len--;
12961
}
12962
12963
while (objLen && isspace(UCHAR(s[objLen - 1]))) {
12964
12965
if (objLen > 1 && s[objLen - 2] == '\\') {
12966
break;
12967
}
12968
objLen--;
12969
len--;
12970
}
12971
memcpy(p, s, objLen);
12972
p += objLen;
12973
if (i + 1 != objc) {
12974
if (objLen)
12975
*p++ = ' ';
12976
else {
12977
len--;
12978
}
12979
}
12980
}
12981
*p = '\0';
12982
return Jim_NewStringObjNoAlloc(interp, bytes, len);
12983
}
12984
}
12985
12986
Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
12987
Jim_Obj *lastObjPtr)
12988
{
12989
int first, last;
12990
int len, rangeLen;
12991
12992
if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
12993
Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
12994
return NULL;
12995
len = Jim_ListLength(interp, listObjPtr);
12996
first = JimRelToAbsIndex(len, first);
12997
last = JimRelToAbsIndex(len, last);
12998
JimRelToAbsRange(len, &first, &last, &rangeLen);
12999
if (first == 0 && last == len) {
13000
return listObjPtr;
13001
}
13002
return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
13003
}
13004
13005
static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13006
static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13007
static void UpdateStringOfDict(struct Jim_Obj *objPtr);
13008
static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13009
13010
13011
static const Jim_ObjType dictObjType = {
13012
"dict",
13013
FreeDictInternalRep,
13014
DupDictInternalRep,
13015
UpdateStringOfDict,
13016
JIM_TYPE_NONE,
13017
};
13018
13019
static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict)
13020
{
13021
int i;
13022
for (i = 0; i < dict->len; i++) {
13023
Jim_DecrRefCount(interp, dict->table[i]);
13024
}
13025
Jim_Free(dict->table);
13026
Jim_Free(dict->ht);
13027
Jim_Free(dict);
13028
}
13029
13030
enum {
13031
DICT_HASH_FIND = -1,
13032
DICT_HASH_REMOVE = -2,
13033
DICT_HASH_ADD = -3,
13034
};
13035
13036
static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset)
13037
{
13038
unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq);
13039
unsigned idx = h & dict->sizemask;
13040
int tvoffset = 0;
13041
unsigned peturb = h;
13042
unsigned first_removed = ~0;
13043
13044
if (dict->len) {
13045
while ((tvoffset = dict->ht[idx].offset)) {
13046
if (tvoffset == -1) {
13047
if (first_removed == ~0) {
13048
first_removed = idx;
13049
}
13050
}
13051
else if (dict->ht[idx].hash == h) {
13052
if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) {
13053
break;
13054
}
13055
}
13056
13057
peturb >>= 5;
13058
idx = (5 * idx + 1 + peturb) & dict->sizemask;
13059
}
13060
}
13061
13062
switch (op_tvoffset) {
13063
case DICT_HASH_FIND:
13064
13065
break;
13066
case DICT_HASH_REMOVE:
13067
if (tvoffset) {
13068
13069
dict->ht[idx].offset = -1;
13070
dict->dummy++;
13071
}
13072
13073
break;
13074
case DICT_HASH_ADD:
13075
if (tvoffset == 0) {
13076
13077
if (first_removed != ~0) {
13078
idx = first_removed;
13079
dict->dummy--;
13080
}
13081
dict->ht[idx].offset = dict->len + 1;
13082
dict->ht[idx].hash = h;
13083
}
13084
13085
break;
13086
default:
13087
assert(tvoffset);
13088
13089
dict->ht[idx].offset = op_tvoffset;
13090
break;
13091
}
13092
13093
return tvoffset;
13094
}
13095
13096
static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size)
13097
{
13098
int i;
13099
struct JimDictHashEntry *prevht = dict->ht;
13100
int prevsize = dict->size;
13101
13102
dict->size = JimHashTableNextPower(size);
13103
dict->sizemask = dict->size - 1;
13104
13105
13106
dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht));
13107
memset(dict->ht, 0, dict->size * sizeof(*dict->ht));
13108
13109
13110
for (i = 0; i < prevsize; i++) {
13111
if (prevht[i].offset > 0) {
13112
13113
unsigned h = prevht[i].hash;
13114
unsigned idx = h & dict->sizemask;
13115
unsigned peturb = h;
13116
13117
while (dict->ht[idx].offset) {
13118
peturb >>= 5;
13119
idx = (5 * idx + 1 + peturb) & dict->sizemask;
13120
}
13121
dict->ht[idx].offset = prevht[i].offset;
13122
dict->ht[idx].hash = h;
13123
}
13124
}
13125
Jim_Free(prevht);
13126
}
13127
13128
static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr)
13129
{
13130
if (dict->size <= dict->len + dict->dummy) {
13131
JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8);
13132
}
13133
return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD);
13134
}
13135
13136
static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size)
13137
{
13138
Jim_Dict *dict = Jim_Alloc(sizeof(*dict));
13139
memset(dict, 0, sizeof(*dict));
13140
13141
if (ht_size) {
13142
JimDictExpandHashTable(dict, ht_size);
13143
}
13144
if (table_size) {
13145
dict->table = Jim_Alloc(table_size * sizeof(*dict->table));
13146
dict->maxLen = table_size;
13147
}
13148
#ifdef JIM_RANDOMISE_HASH
13149
dict->uniq = (rand() ^ time(NULL) ^ clock());
13150
#endif
13151
return dict;
13152
}
13153
13154
static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13155
{
13156
JimFreeDict(interp, objPtr->internalRep.dictValue);
13157
}
13158
13159
static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13160
{
13161
Jim_Dict *oldDict = srcPtr->internalRep.dictValue;
13162
int i;
13163
13164
13165
Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size);
13166
13167
13168
for (i = 0; i < oldDict->len; i++) {
13169
newDict->table[i] = oldDict->table[i];
13170
Jim_IncrRefCount(newDict->table[i]);
13171
}
13172
newDict->len = oldDict->len;
13173
13174
13175
newDict->uniq = oldDict->uniq;
13176
13177
13178
memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size);
13179
13180
dupPtr->internalRep.dictValue = newDict;
13181
dupPtr->typePtr = &dictObjType;
13182
}
13183
13184
static void UpdateStringOfDict(struct Jim_Obj *objPtr)
13185
{
13186
JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len);
13187
}
13188
13189
static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13190
{
13191
int listlen;
13192
13193
if (objPtr->typePtr == &dictObjType) {
13194
return JIM_OK;
13195
}
13196
13197
if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
13198
Jim_String(objPtr);
13199
}
13200
13201
listlen = Jim_ListLength(interp, objPtr);
13202
if (listlen % 2) {
13203
Jim_SetResultString(interp, "missing value to go with key", -1);
13204
return JIM_ERR;
13205
}
13206
else {
13207
13208
Jim_Dict *dict = JimDictNew(interp, 0, listlen);
13209
int i;
13210
13211
13212
dict->table = objPtr->internalRep.listValue.ele;
13213
dict->maxLen = objPtr->internalRep.listValue.maxLen;
13214
13215
13216
for (i = 0; i < listlen; i += 2) {
13217
int tvoffset = JimDictAdd(dict, dict->table[i]);
13218
if (tvoffset) {
13219
13220
13221
Jim_DecrRefCount(interp, dict->table[tvoffset]);
13222
13223
dict->table[tvoffset] = dict->table[i + 1];
13224
13225
Jim_DecrRefCount(interp, dict->table[i]);
13226
}
13227
else {
13228
if (dict->len != i) {
13229
dict->table[dict->len++] = dict->table[i];
13230
dict->table[dict->len++] = dict->table[i + 1];
13231
}
13232
else {
13233
dict->len += 2;
13234
}
13235
}
13236
}
13237
13238
objPtr->typePtr = &dictObjType;
13239
objPtr->internalRep.dictValue = dict;
13240
13241
return JIM_OK;
13242
}
13243
}
13244
13245
13246
13247
static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
13248
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
13249
{
13250
Jim_Dict *dict = objPtr->internalRep.dictValue;
13251
if (valueObjPtr == NULL) {
13252
13253
int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE);
13254
if (tvoffset) {
13255
13256
Jim_DecrRefCount(interp, dict->table[tvoffset - 1]);
13257
Jim_DecrRefCount(interp, dict->table[tvoffset]);
13258
dict->len -= 2;
13259
if (tvoffset != dict->len + 1) {
13260
13261
dict->table[tvoffset - 1] = dict->table[dict->len];
13262
dict->table[tvoffset] = dict->table[dict->len + 1];
13263
13264
13265
JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset);
13266
}
13267
return JIM_OK;
13268
}
13269
return JIM_ERR;
13270
}
13271
else {
13272
13273
int tvoffset = JimDictAdd(dict, keyObjPtr);
13274
if (tvoffset) {
13275
13276
Jim_IncrRefCount(valueObjPtr);
13277
Jim_DecrRefCount(interp, dict->table[tvoffset]);
13278
dict->table[tvoffset] = valueObjPtr;
13279
}
13280
else {
13281
if (dict->maxLen == dict->len) {
13282
13283
if (dict->maxLen < 4) {
13284
dict->maxLen = 4;
13285
}
13286
else {
13287
dict->maxLen *= 2;
13288
}
13289
dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table));
13290
}
13291
Jim_IncrRefCount(keyObjPtr);
13292
Jim_IncrRefCount(valueObjPtr);
13293
13294
dict->table[dict->len++] = keyObjPtr;
13295
dict->table[dict->len++] = valueObjPtr;
13296
13297
}
13298
return JIM_OK;
13299
}
13300
}
13301
13302
int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
13303
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
13304
{
13305
JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
13306
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
13307
return JIM_ERR;
13308
}
13309
Jim_InvalidateStringRep(objPtr);
13310
return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
13311
}
13312
13313
Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
13314
{
13315
Jim_Obj *objPtr;
13316
int i;
13317
13318
JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
13319
13320
objPtr = Jim_NewObj(interp);
13321
objPtr->typePtr = &dictObjType;
13322
objPtr->bytes = NULL;
13323
13324
objPtr->internalRep.dictValue = JimDictNew(interp, len, len);
13325
for (i = 0; i < len; i += 2)
13326
DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
13327
return objPtr;
13328
}
13329
13330
int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
13331
Jim_Obj **objPtrPtr, int flags)
13332
{
13333
int tvoffset;
13334
Jim_Dict *dict;
13335
13336
if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
13337
return -1;
13338
}
13339
dict = dictPtr->internalRep.dictValue;
13340
tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND);
13341
if (tvoffset == 0) {
13342
if (flags & JIM_ERRMSG) {
13343
Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
13344
}
13345
return JIM_ERR;
13346
}
13347
*objPtrPtr = dict->table[tvoffset];
13348
return JIM_OK;
13349
}
13350
13351
Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len)
13352
{
13353
13354
if (Jim_IsList(dictPtr)) {
13355
Jim_Obj **table;
13356
JimListGetElements(interp, dictPtr, len, &table);
13357
if (*len % 2 == 0) {
13358
return table;
13359
}
13360
13361
}
13362
if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
13363
13364
*len = 1;
13365
return NULL;
13366
}
13367
*len = dictPtr->internalRep.dictValue->len;
13368
return dictPtr->internalRep.dictValue->table;
13369
}
13370
13371
13372
int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
13373
Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
13374
{
13375
int i;
13376
13377
if (keyc == 0) {
13378
*objPtrPtr = dictPtr;
13379
return JIM_OK;
13380
}
13381
13382
for (i = 0; i < keyc; i++) {
13383
Jim_Obj *objPtr;
13384
13385
int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
13386
if (rc != JIM_OK) {
13387
return rc;
13388
}
13389
dictPtr = objPtr;
13390
}
13391
*objPtrPtr = dictPtr;
13392
return JIM_OK;
13393
}
13394
13395
int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
13396
Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
13397
{
13398
Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
13399
int shared, i;
13400
13401
varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
13402
if (objPtr == NULL) {
13403
if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
13404
13405
return JIM_ERR;
13406
}
13407
varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
13408
if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
13409
Jim_FreeNewObj(interp, varObjPtr);
13410
return JIM_ERR;
13411
}
13412
}
13413
if ((shared = Jim_IsShared(objPtr)))
13414
varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
13415
for (i = 0; i < keyc; i++) {
13416
dictObjPtr = objPtr;
13417
13418
13419
if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
13420
goto err;
13421
}
13422
13423
if (i == keyc - 1) {
13424
13425
if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
13426
if (newObjPtr || (flags & JIM_MUSTEXIST)) {
13427
goto err;
13428
}
13429
}
13430
break;
13431
}
13432
13433
13434
Jim_InvalidateStringRep(dictObjPtr);
13435
if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
13436
newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
13437
if (Jim_IsShared(objPtr)) {
13438
objPtr = Jim_DuplicateObj(interp, objPtr);
13439
DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
13440
}
13441
}
13442
else {
13443
if (newObjPtr == NULL) {
13444
goto err;
13445
}
13446
objPtr = Jim_NewDictObj(interp, NULL, 0);
13447
DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
13448
}
13449
}
13450
13451
Jim_InvalidateStringRep(objPtr);
13452
Jim_InvalidateStringRep(varObjPtr);
13453
if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
13454
goto err;
13455
}
13456
13457
if (!(flags & JIM_NORESULT)) {
13458
Jim_SetResult(interp, varObjPtr);
13459
}
13460
return JIM_OK;
13461
err:
13462
if (shared) {
13463
Jim_FreeNewObj(interp, varObjPtr);
13464
}
13465
return JIM_ERR;
13466
}
13467
13468
static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
13469
static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13470
13471
static const Jim_ObjType indexObjType = {
13472
"index",
13473
NULL,
13474
NULL,
13475
UpdateStringOfIndex,
13476
JIM_TYPE_NONE,
13477
};
13478
13479
static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
13480
{
13481
if (objPtr->internalRep.intValue == -1) {
13482
JimSetStringBytes(objPtr, "end");
13483
}
13484
else {
13485
char buf[JIM_INTEGER_SPACE + 1];
13486
if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) {
13487
sprintf(buf, "%d", objPtr->internalRep.intValue);
13488
}
13489
else {
13490
13491
sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
13492
}
13493
JimSetStringBytes(objPtr, buf);
13494
}
13495
}
13496
13497
static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
13498
{
13499
jim_wide idx;
13500
int end = 0;
13501
const char *str;
13502
Jim_Obj *exprObj = objPtr;
13503
13504
JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object"));
13505
13506
13507
str = Jim_String(objPtr);
13508
13509
13510
if (strncmp(str, "end", 3) == 0) {
13511
end = 1;
13512
str += 3;
13513
idx = 0;
13514
switch (*str) {
13515
case '\0':
13516
exprObj = NULL;
13517
break;
13518
13519
case '-':
13520
case '+':
13521
exprObj = Jim_NewStringObj(interp, str, -1);
13522
break;
13523
13524
default:
13525
goto badindex;
13526
}
13527
}
13528
if (exprObj) {
13529
int ret;
13530
Jim_IncrRefCount(exprObj);
13531
ret = Jim_GetWideExpr(interp, exprObj, &idx);
13532
Jim_DecrRefCount(interp, exprObj);
13533
if (ret != JIM_OK) {
13534
goto badindex;
13535
}
13536
}
13537
13538
if (end) {
13539
if (idx > 0) {
13540
idx = INT_MAX;
13541
}
13542
else {
13543
13544
idx--;
13545
}
13546
}
13547
else if (idx < 0) {
13548
idx = -INT_MAX;
13549
}
13550
13551
13552
Jim_FreeIntRep(interp, objPtr);
13553
objPtr->typePtr = &indexObjType;
13554
objPtr->internalRep.intValue = idx;
13555
return JIM_OK;
13556
13557
badindex:
13558
Jim_SetResultFormatted(interp,
13559
"bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr);
13560
return JIM_ERR;
13561
}
13562
13563
int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
13564
{
13565
13566
if (objPtr->typePtr == &intObjType) {
13567
jim_wide val = JimWideValue(objPtr);
13568
13569
if (val < 0)
13570
*indexPtr = -INT_MAX;
13571
else if (val > INT_MAX)
13572
*indexPtr = INT_MAX;
13573
else
13574
*indexPtr = (int)val;
13575
return JIM_OK;
13576
}
13577
if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
13578
return JIM_ERR;
13579
*indexPtr = objPtr->internalRep.intValue;
13580
return JIM_OK;
13581
}
13582
13583
13584
13585
static const char * const jimReturnCodes[] = {
13586
"ok",
13587
"error",
13588
"return",
13589
"break",
13590
"continue",
13591
"signal",
13592
"exit",
13593
"eval",
13594
NULL
13595
};
13596
13597
#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
13598
13599
static const Jim_ObjType returnCodeObjType = {
13600
"return-code",
13601
NULL,
13602
NULL,
13603
NULL,
13604
JIM_TYPE_NONE,
13605
};
13606
13607
const char *Jim_ReturnCode(int code)
13608
{
13609
if (code < 0 || code >= (int)jimReturnCodesSize) {
13610
return "?";
13611
}
13612
else {
13613
return jimReturnCodes[code];
13614
}
13615
}
13616
13617
static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
13618
{
13619
int returnCode;
13620
jim_wide wideValue;
13621
13622
13623
if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
13624
returnCode = (int)wideValue;
13625
else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
13626
Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
13627
return JIM_ERR;
13628
}
13629
13630
Jim_FreeIntRep(interp, objPtr);
13631
objPtr->typePtr = &returnCodeObjType;
13632
objPtr->internalRep.intValue = returnCode;
13633
return JIM_OK;
13634
}
13635
13636
int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
13637
{
13638
if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
13639
return JIM_ERR;
13640
*intPtr = objPtr->internalRep.intValue;
13641
return JIM_OK;
13642
}
13643
13644
static int JimParseExprOperator(struct JimParserCtx *pc);
13645
static int JimParseExprNumber(struct JimParserCtx *pc);
13646
static int JimParseExprIrrational(struct JimParserCtx *pc);
13647
static int JimParseExprBoolean(struct JimParserCtx *pc);
13648
13649
13650
enum
13651
{
13652
13653
13654
13655
JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
13656
JIM_EXPROP_DIV,
13657
JIM_EXPROP_MOD,
13658
JIM_EXPROP_SUB,
13659
JIM_EXPROP_ADD,
13660
JIM_EXPROP_LSHIFT,
13661
JIM_EXPROP_RSHIFT,
13662
JIM_EXPROP_ROTL,
13663
JIM_EXPROP_ROTR,
13664
JIM_EXPROP_LT,
13665
JIM_EXPROP_GT,
13666
JIM_EXPROP_LTE,
13667
JIM_EXPROP_GTE,
13668
JIM_EXPROP_NUMEQ,
13669
JIM_EXPROP_NUMNE,
13670
JIM_EXPROP_BITAND,
13671
JIM_EXPROP_BITXOR,
13672
JIM_EXPROP_BITOR,
13673
JIM_EXPROP_LOGICAND,
13674
JIM_EXPROP_LOGICOR,
13675
JIM_EXPROP_TERNARY,
13676
JIM_EXPROP_COLON,
13677
JIM_EXPROP_POW,
13678
13679
13680
JIM_EXPROP_STREQ,
13681
JIM_EXPROP_STRNE,
13682
JIM_EXPROP_STRIN,
13683
JIM_EXPROP_STRNI,
13684
JIM_EXPROP_STRLT,
13685
JIM_EXPROP_STRGT,
13686
JIM_EXPROP_STRLE,
13687
JIM_EXPROP_STRGE,
13688
13689
13690
JIM_EXPROP_NOT,
13691
JIM_EXPROP_BITNOT,
13692
JIM_EXPROP_UNARYMINUS,
13693
JIM_EXPROP_UNARYPLUS,
13694
13695
13696
JIM_EXPROP_FUNC_INT,
13697
JIM_EXPROP_FUNC_WIDE,
13698
JIM_EXPROP_FUNC_ABS,
13699
JIM_EXPROP_FUNC_DOUBLE,
13700
JIM_EXPROP_FUNC_ROUND,
13701
JIM_EXPROP_FUNC_RAND,
13702
JIM_EXPROP_FUNC_SRAND,
13703
13704
13705
JIM_EXPROP_FUNC_SIN,
13706
JIM_EXPROP_FUNC_COS,
13707
JIM_EXPROP_FUNC_TAN,
13708
JIM_EXPROP_FUNC_ASIN,
13709
JIM_EXPROP_FUNC_ACOS,
13710
JIM_EXPROP_FUNC_ATAN,
13711
JIM_EXPROP_FUNC_ATAN2,
13712
JIM_EXPROP_FUNC_SINH,
13713
JIM_EXPROP_FUNC_COSH,
13714
JIM_EXPROP_FUNC_TANH,
13715
JIM_EXPROP_FUNC_CEIL,
13716
JIM_EXPROP_FUNC_FLOOR,
13717
JIM_EXPROP_FUNC_EXP,
13718
JIM_EXPROP_FUNC_LOG,
13719
JIM_EXPROP_FUNC_LOG10,
13720
JIM_EXPROP_FUNC_SQRT,
13721
JIM_EXPROP_FUNC_POW,
13722
JIM_EXPROP_FUNC_HYPOT,
13723
JIM_EXPROP_FUNC_FMOD,
13724
};
13725
13726
struct JimExprNode {
13727
int type;
13728
struct Jim_Obj *objPtr;
13729
13730
struct JimExprNode *left;
13731
struct JimExprNode *right;
13732
struct JimExprNode *ternary;
13733
};
13734
13735
13736
typedef struct Jim_ExprOperator
13737
{
13738
const char *name;
13739
int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
13740
unsigned char precedence;
13741
unsigned char arity;
13742
unsigned char attr;
13743
unsigned char namelen;
13744
} Jim_ExprOperator;
13745
13746
static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
13747
static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
13748
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
13749
13750
static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
13751
{
13752
int intresult = 1;
13753
int rc, bA = 0;
13754
double dA, dC = 0;
13755
jim_wide wA, wC = 0;
13756
Jim_Obj *A;
13757
13758
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13759
return rc;
13760
}
13761
13762
if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
13763
switch (node->type) {
13764
case JIM_EXPROP_FUNC_INT:
13765
case JIM_EXPROP_FUNC_WIDE:
13766
case JIM_EXPROP_FUNC_ROUND:
13767
case JIM_EXPROP_UNARYPLUS:
13768
wC = wA;
13769
break;
13770
case JIM_EXPROP_FUNC_DOUBLE:
13771
dC = wA;
13772
intresult = 0;
13773
break;
13774
case JIM_EXPROP_FUNC_ABS:
13775
wC = wA >= 0 ? wA : -wA;
13776
break;
13777
case JIM_EXPROP_UNARYMINUS:
13778
wC = -wA;
13779
break;
13780
case JIM_EXPROP_NOT:
13781
wC = !wA;
13782
break;
13783
default:
13784
abort();
13785
}
13786
}
13787
else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
13788
switch (node->type) {
13789
case JIM_EXPROP_FUNC_INT:
13790
case JIM_EXPROP_FUNC_WIDE:
13791
wC = dA;
13792
break;
13793
case JIM_EXPROP_FUNC_ROUND:
13794
wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
13795
break;
13796
case JIM_EXPROP_FUNC_DOUBLE:
13797
case JIM_EXPROP_UNARYPLUS:
13798
dC = dA;
13799
intresult = 0;
13800
break;
13801
case JIM_EXPROP_FUNC_ABS:
13802
#ifdef JIM_MATH_FUNCTIONS
13803
dC = fabs(dA);
13804
#else
13805
dC = dA >= 0 ? dA : -dA;
13806
#endif
13807
intresult = 0;
13808
break;
13809
case JIM_EXPROP_UNARYMINUS:
13810
dC = -dA;
13811
intresult = 0;
13812
break;
13813
case JIM_EXPROP_NOT:
13814
wC = !dA;
13815
break;
13816
default:
13817
abort();
13818
}
13819
}
13820
else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) {
13821
switch (node->type) {
13822
case JIM_EXPROP_NOT:
13823
wC = !bA;
13824
break;
13825
default:
13826
abort();
13827
}
13828
}
13829
13830
if (rc == JIM_OK) {
13831
if (intresult) {
13832
Jim_SetResultInt(interp, wC);
13833
}
13834
else {
13835
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
13836
}
13837
}
13838
13839
Jim_DecrRefCount(interp, A);
13840
13841
return rc;
13842
}
13843
13844
static double JimRandDouble(Jim_Interp *interp)
13845
{
13846
unsigned long x;
13847
JimRandomBytes(interp, &x, sizeof(x));
13848
13849
return (double)x / (double)~0UL;
13850
}
13851
13852
static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
13853
{
13854
jim_wide wA;
13855
Jim_Obj *A;
13856
int rc;
13857
13858
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13859
return rc;
13860
}
13861
13862
rc = Jim_GetWide(interp, A, &wA);
13863
if (rc == JIM_OK) {
13864
switch (node->type) {
13865
case JIM_EXPROP_BITNOT:
13866
Jim_SetResultInt(interp, ~wA);
13867
break;
13868
case JIM_EXPROP_FUNC_SRAND:
13869
JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
13870
Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
13871
break;
13872
default:
13873
abort();
13874
}
13875
}
13876
13877
Jim_DecrRefCount(interp, A);
13878
13879
return rc;
13880
}
13881
13882
static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
13883
{
13884
JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
13885
13886
Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
13887
13888
return JIM_OK;
13889
}
13890
13891
#ifdef JIM_MATH_FUNCTIONS
13892
static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
13893
{
13894
int rc;
13895
double dA, dC;
13896
Jim_Obj *A;
13897
13898
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13899
return rc;
13900
}
13901
13902
rc = Jim_GetDouble(interp, A, &dA);
13903
if (rc == JIM_OK) {
13904
switch (node->type) {
13905
case JIM_EXPROP_FUNC_SIN:
13906
dC = sin(dA);
13907
break;
13908
case JIM_EXPROP_FUNC_COS:
13909
dC = cos(dA);
13910
break;
13911
case JIM_EXPROP_FUNC_TAN:
13912
dC = tan(dA);
13913
break;
13914
case JIM_EXPROP_FUNC_ASIN:
13915
dC = asin(dA);
13916
break;
13917
case JIM_EXPROP_FUNC_ACOS:
13918
dC = acos(dA);
13919
break;
13920
case JIM_EXPROP_FUNC_ATAN:
13921
dC = atan(dA);
13922
break;
13923
case JIM_EXPROP_FUNC_SINH:
13924
dC = sinh(dA);
13925
break;
13926
case JIM_EXPROP_FUNC_COSH:
13927
dC = cosh(dA);
13928
break;
13929
case JIM_EXPROP_FUNC_TANH:
13930
dC = tanh(dA);
13931
break;
13932
case JIM_EXPROP_FUNC_CEIL:
13933
dC = ceil(dA);
13934
break;
13935
case JIM_EXPROP_FUNC_FLOOR:
13936
dC = floor(dA);
13937
break;
13938
case JIM_EXPROP_FUNC_EXP:
13939
dC = exp(dA);
13940
break;
13941
case JIM_EXPROP_FUNC_LOG:
13942
dC = log(dA);
13943
break;
13944
case JIM_EXPROP_FUNC_LOG10:
13945
dC = log10(dA);
13946
break;
13947
case JIM_EXPROP_FUNC_SQRT:
13948
dC = sqrt(dA);
13949
break;
13950
default:
13951
abort();
13952
}
13953
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
13954
}
13955
13956
Jim_DecrRefCount(interp, A);
13957
13958
return rc;
13959
}
13960
#endif
13961
13962
13963
static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
13964
{
13965
jim_wide wA, wB;
13966
int rc;
13967
Jim_Obj *A, *B;
13968
13969
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13970
return rc;
13971
}
13972
if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
13973
Jim_DecrRefCount(interp, A);
13974
return rc;
13975
}
13976
13977
rc = JIM_ERR;
13978
13979
if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
13980
jim_wide wC;
13981
13982
rc = JIM_OK;
13983
13984
switch (node->type) {
13985
case JIM_EXPROP_LSHIFT:
13986
wC = wA << wB;
13987
break;
13988
case JIM_EXPROP_RSHIFT:
13989
wC = wA >> wB;
13990
break;
13991
case JIM_EXPROP_BITAND:
13992
wC = wA & wB;
13993
break;
13994
case JIM_EXPROP_BITXOR:
13995
wC = wA ^ wB;
13996
break;
13997
case JIM_EXPROP_BITOR:
13998
wC = wA | wB;
13999
break;
14000
case JIM_EXPROP_MOD:
14001
if (wB == 0) {
14002
wC = 0;
14003
Jim_SetResultString(interp, "Division by zero", -1);
14004
rc = JIM_ERR;
14005
}
14006
else {
14007
int negative = 0;
14008
14009
if (wB < 0) {
14010
wB = -wB;
14011
wA = -wA;
14012
negative = 1;
14013
}
14014
wC = wA % wB;
14015
if (wC < 0) {
14016
wC += wB;
14017
}
14018
if (negative) {
14019
wC = -wC;
14020
}
14021
}
14022
break;
14023
case JIM_EXPROP_ROTL:
14024
case JIM_EXPROP_ROTR:{
14025
14026
unsigned long uA = (unsigned long)wA;
14027
unsigned long uB = (unsigned long)wB;
14028
const unsigned int S = sizeof(unsigned long) * 8;
14029
14030
14031
uB %= S;
14032
14033
if (node->type == JIM_EXPROP_ROTR) {
14034
uB = S - uB;
14035
}
14036
wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
14037
break;
14038
}
14039
default:
14040
abort();
14041
}
14042
Jim_SetResultInt(interp, wC);
14043
}
14044
14045
Jim_DecrRefCount(interp, A);
14046
Jim_DecrRefCount(interp, B);
14047
14048
return rc;
14049
}
14050
14051
14052
14053
static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
14054
{
14055
int rc = JIM_OK;
14056
double dA, dB, dC = 0;
14057
jim_wide wA, wB, wC = 0;
14058
Jim_Obj *A, *B;
14059
14060
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14061
return rc;
14062
}
14063
if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14064
Jim_DecrRefCount(interp, A);
14065
return rc;
14066
}
14067
14068
if ((A->typePtr != &doubleObjType || A->bytes) &&
14069
(B->typePtr != &doubleObjType || B->bytes) &&
14070
JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
14071
14072
14073
14074
switch (node->type) {
14075
case JIM_EXPROP_POW:
14076
case JIM_EXPROP_FUNC_POW:
14077
if (wA == 0 && wB < 0) {
14078
Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
14079
rc = JIM_ERR;
14080
goto done;
14081
}
14082
wC = JimPowWide(wA, wB);
14083
goto intresult;
14084
case JIM_EXPROP_ADD:
14085
wC = wA + wB;
14086
goto intresult;
14087
case JIM_EXPROP_SUB:
14088
wC = wA - wB;
14089
goto intresult;
14090
case JIM_EXPROP_MUL:
14091
wC = wA * wB;
14092
goto intresult;
14093
case JIM_EXPROP_DIV:
14094
if (wB == 0) {
14095
Jim_SetResultString(interp, "Division by zero", -1);
14096
rc = JIM_ERR;
14097
goto done;
14098
}
14099
else {
14100
if (wB < 0) {
14101
wB = -wB;
14102
wA = -wA;
14103
}
14104
wC = wA / wB;
14105
if (wA % wB < 0) {
14106
wC--;
14107
}
14108
goto intresult;
14109
}
14110
case JIM_EXPROP_LT:
14111
wC = wA < wB;
14112
goto intresult;
14113
case JIM_EXPROP_GT:
14114
wC = wA > wB;
14115
goto intresult;
14116
case JIM_EXPROP_LTE:
14117
wC = wA <= wB;
14118
goto intresult;
14119
case JIM_EXPROP_GTE:
14120
wC = wA >= wB;
14121
goto intresult;
14122
case JIM_EXPROP_NUMEQ:
14123
wC = wA == wB;
14124
goto intresult;
14125
case JIM_EXPROP_NUMNE:
14126
wC = wA != wB;
14127
goto intresult;
14128
}
14129
}
14130
if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
14131
switch (node->type) {
14132
#ifndef JIM_MATH_FUNCTIONS
14133
case JIM_EXPROP_POW:
14134
case JIM_EXPROP_FUNC_POW:
14135
case JIM_EXPROP_FUNC_ATAN2:
14136
case JIM_EXPROP_FUNC_HYPOT:
14137
case JIM_EXPROP_FUNC_FMOD:
14138
Jim_SetResultString(interp, "unsupported", -1);
14139
rc = JIM_ERR;
14140
goto done;
14141
#else
14142
case JIM_EXPROP_POW:
14143
case JIM_EXPROP_FUNC_POW:
14144
dC = pow(dA, dB);
14145
goto doubleresult;
14146
case JIM_EXPROP_FUNC_ATAN2:
14147
dC = atan2(dA, dB);
14148
goto doubleresult;
14149
case JIM_EXPROP_FUNC_HYPOT:
14150
dC = hypot(dA, dB);
14151
goto doubleresult;
14152
case JIM_EXPROP_FUNC_FMOD:
14153
dC = fmod(dA, dB);
14154
goto doubleresult;
14155
#endif
14156
case JIM_EXPROP_ADD:
14157
dC = dA + dB;
14158
goto doubleresult;
14159
case JIM_EXPROP_SUB:
14160
dC = dA - dB;
14161
goto doubleresult;
14162
case JIM_EXPROP_MUL:
14163
dC = dA * dB;
14164
goto doubleresult;
14165
case JIM_EXPROP_DIV:
14166
if (dB == 0) {
14167
#ifdef INFINITY
14168
dC = dA < 0 ? -INFINITY : INFINITY;
14169
#else
14170
dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
14171
#endif
14172
}
14173
else {
14174
dC = dA / dB;
14175
}
14176
goto doubleresult;
14177
case JIM_EXPROP_LT:
14178
wC = dA < dB;
14179
goto intresult;
14180
case JIM_EXPROP_GT:
14181
wC = dA > dB;
14182
goto intresult;
14183
case JIM_EXPROP_LTE:
14184
wC = dA <= dB;
14185
goto intresult;
14186
case JIM_EXPROP_GTE:
14187
wC = dA >= dB;
14188
goto intresult;
14189
case JIM_EXPROP_NUMEQ:
14190
wC = dA == dB;
14191
goto intresult;
14192
case JIM_EXPROP_NUMNE:
14193
wC = dA != dB;
14194
goto intresult;
14195
}
14196
}
14197
else {
14198
14199
14200
14201
int i = Jim_StringCompareObj(interp, A, B, 0);
14202
14203
switch (node->type) {
14204
case JIM_EXPROP_LT:
14205
wC = i < 0;
14206
goto intresult;
14207
case JIM_EXPROP_GT:
14208
wC = i > 0;
14209
goto intresult;
14210
case JIM_EXPROP_LTE:
14211
wC = i <= 0;
14212
goto intresult;
14213
case JIM_EXPROP_GTE:
14214
wC = i >= 0;
14215
goto intresult;
14216
case JIM_EXPROP_NUMEQ:
14217
wC = i == 0;
14218
goto intresult;
14219
case JIM_EXPROP_NUMNE:
14220
wC = i != 0;
14221
goto intresult;
14222
}
14223
}
14224
14225
rc = JIM_ERR;
14226
done:
14227
Jim_DecrRefCount(interp, A);
14228
Jim_DecrRefCount(interp, B);
14229
return rc;
14230
intresult:
14231
Jim_SetResultInt(interp, wC);
14232
goto done;
14233
doubleresult:
14234
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
14235
goto done;
14236
}
14237
14238
static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
14239
{
14240
int listlen;
14241
int i;
14242
14243
listlen = Jim_ListLength(interp, listObjPtr);
14244
for (i = 0; i < listlen; i++) {
14245
if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
14246
return 1;
14247
}
14248
}
14249
return 0;
14250
}
14251
14252
14253
14254
static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
14255
{
14256
Jim_Obj *A, *B;
14257
jim_wide wC;
14258
int comp, rc;
14259
14260
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14261
return rc;
14262
}
14263
if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14264
Jim_DecrRefCount(interp, A);
14265
return rc;
14266
}
14267
14268
switch (node->type) {
14269
case JIM_EXPROP_STREQ:
14270
case JIM_EXPROP_STRNE:
14271
wC = Jim_StringEqObj(A, B);
14272
if (node->type == JIM_EXPROP_STRNE) {
14273
wC = !wC;
14274
}
14275
break;
14276
case JIM_EXPROP_STRLT:
14277
case JIM_EXPROP_STRGT:
14278
case JIM_EXPROP_STRLE:
14279
case JIM_EXPROP_STRGE:
14280
comp = Jim_StringCompareObj(interp, A, B, 0);
14281
if (node->type == JIM_EXPROP_STRLT) {
14282
wC = comp == -1;
14283
} else if (node->type == JIM_EXPROP_STRGT) {
14284
wC = comp == 1;
14285
} else if (node->type == JIM_EXPROP_STRLE) {
14286
wC = comp == -1 || comp == 0;
14287
} else {
14288
wC = comp == 0 || comp == 1;
14289
}
14290
break;
14291
case JIM_EXPROP_STRIN:
14292
wC = JimSearchList(interp, B, A);
14293
break;
14294
case JIM_EXPROP_STRNI:
14295
wC = !JimSearchList(interp, B, A);
14296
break;
14297
default:
14298
abort();
14299
}
14300
Jim_SetResultInt(interp, wC);
14301
14302
Jim_DecrRefCount(interp, A);
14303
Jim_DecrRefCount(interp, B);
14304
14305
return rc;
14306
}
14307
14308
static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
14309
{
14310
long l;
14311
double d;
14312
int b;
14313
int ret = -1;
14314
14315
14316
Jim_IncrRefCount(obj);
14317
14318
if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
14319
ret = (l != 0);
14320
}
14321
else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
14322
ret = (d != 0);
14323
}
14324
else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
14325
ret = (b != 0);
14326
}
14327
14328
Jim_DecrRefCount(interp, obj);
14329
return ret;
14330
}
14331
14332
static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
14333
{
14334
14335
int result = JimExprGetTermBoolean(interp, node->left);
14336
14337
if (result == 1) {
14338
14339
result = JimExprGetTermBoolean(interp, node->right);
14340
}
14341
if (result == -1) {
14342
return JIM_ERR;
14343
}
14344
Jim_SetResultInt(interp, result);
14345
return JIM_OK;
14346
}
14347
14348
static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
14349
{
14350
14351
int result = JimExprGetTermBoolean(interp, node->left);
14352
14353
if (result == 0) {
14354
14355
result = JimExprGetTermBoolean(interp, node->right);
14356
}
14357
if (result == -1) {
14358
return JIM_ERR;
14359
}
14360
Jim_SetResultInt(interp, result);
14361
return JIM_OK;
14362
}
14363
14364
static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
14365
{
14366
14367
int result = JimExprGetTermBoolean(interp, node->left);
14368
14369
if (result == 1) {
14370
14371
return JimExprEvalTermNode(interp, node->right);
14372
}
14373
else if (result == 0) {
14374
14375
return JimExprEvalTermNode(interp, node->ternary);
14376
}
14377
14378
return JIM_ERR;
14379
}
14380
14381
enum
14382
{
14383
OP_FUNC = 0x0001,
14384
OP_RIGHT_ASSOC = 0x0002,
14385
};
14386
14387
#define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
14388
#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
14389
14390
static const struct Jim_ExprOperator Jim_ExprOperators[] = {
14391
OPRINIT("*", 110, 2, JimExprOpBin),
14392
OPRINIT("/", 110, 2, JimExprOpBin),
14393
OPRINIT("%", 110, 2, JimExprOpIntBin),
14394
14395
OPRINIT("-", 100, 2, JimExprOpBin),
14396
OPRINIT("+", 100, 2, JimExprOpBin),
14397
14398
OPRINIT("<<", 90, 2, JimExprOpIntBin),
14399
OPRINIT(">>", 90, 2, JimExprOpIntBin),
14400
14401
OPRINIT("<<<", 90, 2, JimExprOpIntBin),
14402
OPRINIT(">>>", 90, 2, JimExprOpIntBin),
14403
14404
OPRINIT("<", 80, 2, JimExprOpBin),
14405
OPRINIT(">", 80, 2, JimExprOpBin),
14406
OPRINIT("<=", 80, 2, JimExprOpBin),
14407
OPRINIT(">=", 80, 2, JimExprOpBin),
14408
14409
OPRINIT("==", 70, 2, JimExprOpBin),
14410
OPRINIT("!=", 70, 2, JimExprOpBin),
14411
14412
OPRINIT("&", 50, 2, JimExprOpIntBin),
14413
OPRINIT("^", 49, 2, JimExprOpIntBin),
14414
OPRINIT("|", 48, 2, JimExprOpIntBin),
14415
14416
OPRINIT("&&", 10, 2, JimExprOpAnd),
14417
OPRINIT("||", 9, 2, JimExprOpOr),
14418
OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
14419
OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
14420
14421
14422
OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
14423
14424
OPRINIT("eq", 60, 2, JimExprOpStrBin),
14425
OPRINIT("ne", 60, 2, JimExprOpStrBin),
14426
14427
OPRINIT("in", 55, 2, JimExprOpStrBin),
14428
OPRINIT("ni", 55, 2, JimExprOpStrBin),
14429
14430
OPRINIT("lt", 75, 2, JimExprOpStrBin),
14431
OPRINIT("gt", 75, 2, JimExprOpStrBin),
14432
OPRINIT("le", 75, 2, JimExprOpStrBin),
14433
OPRINIT("ge", 75, 2, JimExprOpStrBin),
14434
14435
OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14436
OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
14437
OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14438
OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14439
14440
14441
14442
OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
14443
OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
14444
OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
14445
OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
14446
OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
14447
OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
14448
OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
14449
14450
#ifdef JIM_MATH_FUNCTIONS
14451
OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14452
OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14453
OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14454
OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14455
OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14456
OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14457
OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
14458
OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14459
OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14460
OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14461
OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14462
OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14463
OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14464
OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14465
OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14466
OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14467
OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
14468
OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
14469
OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
14470
#endif
14471
};
14472
#undef OPRINIT
14473
#undef OPRINIT_ATTR
14474
14475
#define JIM_EXPR_OPERATORS_NUM \
14476
(sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
14477
14478
static int JimParseExpression(struct JimParserCtx *pc)
14479
{
14480
pc->errmsg = NULL;
14481
14482
while (1) {
14483
14484
while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
14485
if (*pc->p == '\n') {
14486
pc->linenr++;
14487
}
14488
pc->p++;
14489
pc->len--;
14490
}
14491
14492
if (*pc->p == '#') {
14493
JimParseComment(pc);
14494
14495
continue;
14496
}
14497
break;
14498
}
14499
14500
14501
pc->tline = pc->linenr;
14502
pc->tstart = pc->p;
14503
14504
if (pc->len == 0) {
14505
pc->tend = pc->p;
14506
pc->tt = JIM_TT_EOL;
14507
pc->eof = 1;
14508
return JIM_OK;
14509
}
14510
switch (*(pc->p)) {
14511
case '(':
14512
pc->tt = JIM_TT_SUBEXPR_START;
14513
goto singlechar;
14514
case ')':
14515
pc->tt = JIM_TT_SUBEXPR_END;
14516
goto singlechar;
14517
case ',':
14518
pc->tt = JIM_TT_SUBEXPR_COMMA;
14519
singlechar:
14520
pc->tend = pc->p;
14521
pc->p++;
14522
pc->len--;
14523
break;
14524
case '[':
14525
return JimParseCmd(pc);
14526
case '$':
14527
if (JimParseVar(pc) == JIM_ERR)
14528
return JimParseExprOperator(pc);
14529
else {
14530
14531
if (pc->tt == JIM_TT_EXPRSUGAR) {
14532
pc->errmsg = "nesting expr in expr is not allowed";
14533
return JIM_ERR;
14534
}
14535
return JIM_OK;
14536
}
14537
break;
14538
case '0':
14539
case '1':
14540
case '2':
14541
case '3':
14542
case '4':
14543
case '5':
14544
case '6':
14545
case '7':
14546
case '8':
14547
case '9':
14548
case '.':
14549
return JimParseExprNumber(pc);
14550
case '"':
14551
return JimParseQuote(pc);
14552
case '{':
14553
return JimParseBrace(pc);
14554
14555
case 'N':
14556
case 'I':
14557
case 'n':
14558
case 'i':
14559
if (JimParseExprIrrational(pc) == JIM_ERR)
14560
if (JimParseExprBoolean(pc) == JIM_ERR)
14561
return JimParseExprOperator(pc);
14562
break;
14563
case 't':
14564
case 'f':
14565
case 'o':
14566
case 'y':
14567
if (JimParseExprBoolean(pc) == JIM_ERR)
14568
return JimParseExprOperator(pc);
14569
break;
14570
default:
14571
return JimParseExprOperator(pc);
14572
break;
14573
}
14574
return JIM_OK;
14575
}
14576
14577
static int JimParseExprNumber(struct JimParserCtx *pc)
14578
{
14579
char *end;
14580
14581
14582
pc->tt = JIM_TT_EXPR_INT;
14583
14584
jim_strtoull(pc->p, (char **)&pc->p);
14585
14586
if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
14587
if (strtod(pc->tstart, &end)) { }
14588
if (end == pc->tstart)
14589
return JIM_ERR;
14590
if (end > pc->p) {
14591
14592
pc->tt = JIM_TT_EXPR_DOUBLE;
14593
pc->p = end;
14594
}
14595
}
14596
pc->tend = pc->p - 1;
14597
pc->len -= (pc->p - pc->tstart);
14598
return JIM_OK;
14599
}
14600
14601
static int JimParseExprIrrational(struct JimParserCtx *pc)
14602
{
14603
const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
14604
int i;
14605
14606
for (i = 0; irrationals[i]; i++) {
14607
const char *irr = irrationals[i];
14608
14609
if (strncmp(irr, pc->p, 3) == 0) {
14610
pc->p += 3;
14611
pc->len -= 3;
14612
pc->tend = pc->p - 1;
14613
pc->tt = JIM_TT_EXPR_DOUBLE;
14614
return JIM_OK;
14615
}
14616
}
14617
return JIM_ERR;
14618
}
14619
14620
static int JimParseExprBoolean(struct JimParserCtx *pc)
14621
{
14622
int i;
14623
for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) {
14624
if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) {
14625
pc->p += jim_true_false_lens[i];
14626
pc->len -= jim_true_false_lens[i];
14627
pc->tend = pc->p - 1;
14628
pc->tt = JIM_TT_EXPR_BOOLEAN;
14629
return JIM_OK;
14630
}
14631
}
14632
return JIM_ERR;
14633
}
14634
14635
static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
14636
{
14637
static Jim_ExprOperator dummy_op;
14638
if (opcode < JIM_TT_EXPR_OP) {
14639
return &dummy_op;
14640
}
14641
return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
14642
}
14643
14644
static int JimParseExprOperator(struct JimParserCtx *pc)
14645
{
14646
int i;
14647
const struct Jim_ExprOperator *bestOp = NULL;
14648
int bestLen = 0;
14649
14650
14651
for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
14652
const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
14653
14654
if (op->name[0] != pc->p[0]) {
14655
continue;
14656
}
14657
14658
if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
14659
bestOp = op;
14660
bestLen = op->namelen;
14661
}
14662
}
14663
if (bestOp == NULL) {
14664
return JIM_ERR;
14665
}
14666
14667
14668
if (bestOp->attr & OP_FUNC) {
14669
const char *p = pc->p + bestLen;
14670
int len = pc->len - bestLen;
14671
14672
while (len && isspace(UCHAR(*p))) {
14673
len--;
14674
p++;
14675
}
14676
if (*p != '(') {
14677
pc->errmsg = "function requires parentheses";
14678
return JIM_ERR;
14679
}
14680
}
14681
pc->tend = pc->p + bestLen - 1;
14682
pc->p += bestLen;
14683
pc->len -= bestLen;
14684
14685
pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
14686
return JIM_OK;
14687
}
14688
14689
14690
static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14691
static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14692
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
14693
14694
static const Jim_ObjType exprObjType = {
14695
"expression",
14696
FreeExprInternalRep,
14697
DupExprInternalRep,
14698
NULL,
14699
JIM_TYPE_NONE,
14700
};
14701
14702
14703
struct ExprTree
14704
{
14705
struct JimExprNode *expr;
14706
struct JimExprNode *nodes;
14707
int len;
14708
int inUse;
14709
};
14710
14711
static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
14712
{
14713
int i;
14714
for (i = 0; i < num; i++) {
14715
if (nodes[i].objPtr) {
14716
Jim_DecrRefCount(interp, nodes[i].objPtr);
14717
}
14718
}
14719
Jim_Free(nodes);
14720
}
14721
14722
static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
14723
{
14724
ExprTreeFreeNodes(interp, expr->nodes, expr->len);
14725
Jim_Free(expr);
14726
}
14727
14728
static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14729
{
14730
struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
14731
14732
if (expr) {
14733
if (--expr->inUse != 0) {
14734
return;
14735
}
14736
14737
ExprTreeFree(interp, expr);
14738
}
14739
}
14740
14741
static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14742
{
14743
JIM_NOTUSED(interp);
14744
JIM_NOTUSED(srcPtr);
14745
14746
14747
dupPtr->typePtr = NULL;
14748
}
14749
14750
struct ExprBuilder {
14751
int parencount;
14752
int level;
14753
ParseToken *token;
14754
ParseToken *first_token;
14755
Jim_Stack stack;
14756
Jim_Obj *exprObjPtr;
14757
Jim_Obj *fileNameObj;
14758
struct JimExprNode *nodes;
14759
struct JimExprNode *next;
14760
};
14761
14762
#ifdef DEBUG_SHOW_EXPR
14763
static void JimShowExprNode(struct JimExprNode *node, int level)
14764
{
14765
int i;
14766
for (i = 0; i < level; i++) {
14767
printf(" ");
14768
}
14769
if (TOKEN_IS_EXPR_OP(node->type)) {
14770
printf("%s\n", jim_tt_name(node->type));
14771
if (node->left) {
14772
JimShowExprNode(node->left, level + 1);
14773
}
14774
if (node->right) {
14775
JimShowExprNode(node->right, level + 1);
14776
}
14777
if (node->ternary) {
14778
JimShowExprNode(node->ternary, level + 1);
14779
}
14780
}
14781
else {
14782
printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
14783
}
14784
}
14785
#endif
14786
14787
#define EXPR_UNTIL_CLOSE 0x0001
14788
#define EXPR_FUNC_ARGS 0x0002
14789
#define EXPR_TERNARY 0x0004
14790
14791
static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) {
14792
int rc;
14793
struct JimExprNode *node;
14794
14795
int exp_stacklen = builder->stack.len + exp_numterms;
14796
14797
if (builder->level++ > 200) {
14798
Jim_SetResultString(interp, "Expression too complex", -1);
14799
return JIM_ERR;
14800
}
14801
14802
while (builder->token->type != JIM_TT_EOL) {
14803
ParseToken *t = builder->token++;
14804
int prevtt;
14805
14806
if (t == builder->first_token) {
14807
prevtt = JIM_TT_NONE;
14808
}
14809
else {
14810
prevtt = t[-1].type;
14811
}
14812
14813
if (t->type == JIM_TT_SUBEXPR_START) {
14814
if (builder->stack.len == exp_stacklen) {
14815
Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
14816
return JIM_ERR;
14817
}
14818
builder->parencount++;
14819
rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
14820
if (rc != JIM_OK) {
14821
return rc;
14822
}
14823
14824
}
14825
else if (t->type == JIM_TT_SUBEXPR_END) {
14826
if (!(flags & EXPR_UNTIL_CLOSE)) {
14827
if (builder->stack.len == exp_stacklen && builder->level > 1) {
14828
builder->token--;
14829
builder->level--;
14830
return JIM_OK;
14831
}
14832
Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
14833
return JIM_ERR;
14834
}
14835
builder->parencount--;
14836
if (builder->stack.len == exp_stacklen) {
14837
14838
break;
14839
}
14840
}
14841
else if (t->type == JIM_TT_SUBEXPR_COMMA) {
14842
if (!(flags & EXPR_FUNC_ARGS)) {
14843
if (builder->stack.len == exp_stacklen) {
14844
14845
builder->token--;
14846
builder->level--;
14847
return JIM_OK;
14848
}
14849
Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
14850
return JIM_ERR;
14851
}
14852
else {
14853
14854
if (builder->stack.len > exp_stacklen) {
14855
Jim_SetResultFormatted(interp, "too many arguments to math function");
14856
return JIM_ERR;
14857
}
14858
}
14859
14860
}
14861
else if (t->type == JIM_EXPROP_COLON) {
14862
if (!(flags & EXPR_TERNARY)) {
14863
if (builder->level != 1) {
14864
14865
builder->token--;
14866
builder->level--;
14867
return JIM_OK;
14868
}
14869
Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
14870
return JIM_ERR;
14871
}
14872
if (builder->stack.len == exp_stacklen) {
14873
14874
builder->token--;
14875
builder->level--;
14876
return JIM_OK;
14877
}
14878
14879
}
14880
else if (TOKEN_IS_EXPR_OP(t->type)) {
14881
const struct Jim_ExprOperator *op;
14882
14883
14884
if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
14885
if (t->type == JIM_EXPROP_SUB) {
14886
t->type = JIM_EXPROP_UNARYMINUS;
14887
}
14888
else if (t->type == JIM_EXPROP_ADD) {
14889
t->type = JIM_EXPROP_UNARYPLUS;
14890
}
14891
}
14892
14893
op = JimExprOperatorInfoByOpcode(t->type);
14894
14895
if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
14896
14897
builder->token--;
14898
break;
14899
}
14900
14901
if (op->attr & OP_FUNC) {
14902
if (builder->token->type != JIM_TT_SUBEXPR_START) {
14903
Jim_SetResultString(interp, "missing arguments for math function", -1);
14904
return JIM_ERR;
14905
}
14906
builder->token++;
14907
if (op->arity == 0) {
14908
if (builder->token->type != JIM_TT_SUBEXPR_END) {
14909
Jim_SetResultString(interp, "too many arguments for math function", -1);
14910
return JIM_ERR;
14911
}
14912
builder->token++;
14913
goto noargs;
14914
}
14915
builder->parencount++;
14916
14917
14918
rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
14919
}
14920
else if (t->type == JIM_EXPROP_TERNARY) {
14921
14922
rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
14923
}
14924
else {
14925
rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
14926
}
14927
14928
if (rc != JIM_OK) {
14929
return rc;
14930
}
14931
14932
noargs:
14933
node = builder->next++;
14934
node->type = t->type;
14935
14936
if (op->arity >= 3) {
14937
node->ternary = Jim_StackPop(&builder->stack);
14938
if (node->ternary == NULL) {
14939
goto missingoperand;
14940
}
14941
}
14942
if (op->arity >= 2) {
14943
node->right = Jim_StackPop(&builder->stack);
14944
if (node->right == NULL) {
14945
goto missingoperand;
14946
}
14947
}
14948
if (op->arity >= 1) {
14949
node->left = Jim_StackPop(&builder->stack);
14950
if (node->left == NULL) {
14951
missingoperand:
14952
Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
14953
builder->next--;
14954
return JIM_ERR;
14955
14956
}
14957
}
14958
14959
14960
Jim_StackPush(&builder->stack, node);
14961
}
14962
else {
14963
Jim_Obj *objPtr = NULL;
14964
14965
14966
14967
14968
if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
14969
Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
14970
return JIM_ERR;
14971
}
14972
14973
14974
if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
14975
char *endptr;
14976
if (t->type == JIM_TT_EXPR_INT) {
14977
objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
14978
}
14979
else {
14980
objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
14981
}
14982
if (endptr != t->token + t->len) {
14983
14984
Jim_FreeNewObj(interp, objPtr);
14985
objPtr = NULL;
14986
}
14987
}
14988
14989
if (!objPtr) {
14990
14991
objPtr = Jim_NewStringObj(interp, t->token, t->len);
14992
if (t->type == JIM_TT_CMD) {
14993
14994
Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
14995
}
14996
}
14997
14998
14999
node = builder->next++;
15000
node->objPtr = objPtr;
15001
Jim_IncrRefCount(node->objPtr);
15002
node->type = t->type;
15003
Jim_StackPush(&builder->stack, node);
15004
}
15005
}
15006
15007
if (builder->stack.len == exp_stacklen) {
15008
builder->level--;
15009
return JIM_OK;
15010
}
15011
15012
if ((flags & EXPR_FUNC_ARGS)) {
15013
Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
15014
}
15015
else {
15016
if (builder->stack.len < exp_stacklen) {
15017
if (builder->level == 0) {
15018
Jim_SetResultFormatted(interp, "empty expression");
15019
}
15020
else {
15021
Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
15022
}
15023
}
15024
else {
15025
Jim_SetResultFormatted(interp, "extra terms after expression");
15026
}
15027
}
15028
15029
return JIM_ERR;
15030
}
15031
15032
static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
15033
{
15034
struct ExprTree *expr;
15035
struct ExprBuilder builder;
15036
int rc;
15037
struct JimExprNode *top = NULL;
15038
15039
builder.parencount = 0;
15040
builder.level = 0;
15041
builder.token = builder.first_token = tokenlist->list;
15042
builder.exprObjPtr = exprObjPtr;
15043
builder.fileNameObj = fileNameObj;
15044
15045
builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
15046
memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
15047
builder.next = builder.nodes;
15048
Jim_InitStack(&builder.stack);
15049
15050
rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
15051
15052
if (rc == JIM_OK) {
15053
top = Jim_StackPop(&builder.stack);
15054
15055
if (builder.parencount) {
15056
Jim_SetResultString(interp, "missing close parenthesis", -1);
15057
rc = JIM_ERR;
15058
}
15059
}
15060
15061
15062
Jim_FreeStack(&builder.stack);
15063
15064
if (rc != JIM_OK) {
15065
ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
15066
return NULL;
15067
}
15068
15069
expr = Jim_Alloc(sizeof(*expr));
15070
expr->inUse = 1;
15071
expr->expr = top;
15072
expr->nodes = builder.nodes;
15073
expr->len = builder.next - builder.nodes;
15074
15075
assert(expr->len <= tokenlist->count - 1);
15076
15077
return expr;
15078
}
15079
15080
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
15081
{
15082
int exprTextLen;
15083
const char *exprText;
15084
struct JimParserCtx parser;
15085
struct ExprTree *expr;
15086
ParseTokenList tokenlist;
15087
int line;
15088
Jim_Obj *fileNameObj;
15089
int rc = JIM_ERR;
15090
15091
15092
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
15093
Jim_IncrRefCount(fileNameObj);
15094
15095
exprText = Jim_GetString(objPtr, &exprTextLen);
15096
15097
15098
ScriptTokenListInit(&tokenlist);
15099
15100
JimParserInit(&parser, exprText, exprTextLen, line);
15101
while (!parser.eof) {
15102
if (JimParseExpression(&parser) != JIM_OK) {
15103
ScriptTokenListFree(&tokenlist);
15104
Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
15105
if (parser.errmsg) {
15106
Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL);
15107
}
15108
expr = NULL;
15109
goto err;
15110
}
15111
15112
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15113
parser.tline);
15114
}
15115
15116
#ifdef DEBUG_SHOW_EXPR_TOKENS
15117
{
15118
int i;
15119
printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
15120
for (i = 0; i < tokenlist.count; i++) {
15121
printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
15122
tokenlist.list[i].len, tokenlist.list[i].token);
15123
}
15124
}
15125
#endif
15126
15127
if (tokenlist.count <= 1) {
15128
Jim_SetResultString(interp, "empty expression", -1);
15129
rc = JIM_ERR;
15130
}
15131
else {
15132
rc = JimParseCheckMissing(interp, parser.missing.ch);
15133
}
15134
if (rc != JIM_OK) {
15135
ScriptTokenListFree(&tokenlist);
15136
Jim_DecrRefCount(interp, fileNameObj);
15137
return rc;
15138
}
15139
15140
15141
expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
15142
15143
15144
ScriptTokenListFree(&tokenlist);
15145
15146
if (!expr) {
15147
goto err;
15148
}
15149
15150
#ifdef DEBUG_SHOW_EXPR
15151
printf("==== Expr ====\n");
15152
JimShowExprNode(expr->expr, 0);
15153
#endif
15154
15155
rc = JIM_OK;
15156
15157
err:
15158
15159
Jim_DecrRefCount(interp, fileNameObj);
15160
Jim_FreeIntRep(interp, objPtr);
15161
Jim_SetIntRepPtr(objPtr, expr);
15162
objPtr->typePtr = &exprObjType;
15163
return rc;
15164
}
15165
15166
static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
15167
{
15168
if (objPtr->typePtr != &exprObjType) {
15169
if (SetExprFromAny(interp, objPtr) != JIM_OK) {
15170
return NULL;
15171
}
15172
}
15173
return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
15174
}
15175
15176
#ifdef JIM_OPTIMIZATION
15177
static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
15178
{
15179
if (node->type == JIM_TT_EXPR_INT)
15180
return node->objPtr;
15181
else if (node->type == JIM_TT_VAR)
15182
return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
15183
else if (node->type == JIM_TT_DICTSUGAR)
15184
return JimExpandDictSugar(interp, node->objPtr);
15185
else
15186
return NULL;
15187
}
15188
#endif
15189
15190
15191
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
15192
{
15193
if (TOKEN_IS_EXPR_OP(node->type)) {
15194
const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
15195
return op->funcop(interp, node);
15196
}
15197
else {
15198
Jim_Obj *objPtr;
15199
15200
15201
switch (node->type) {
15202
case JIM_TT_EXPR_INT:
15203
case JIM_TT_EXPR_DOUBLE:
15204
case JIM_TT_EXPR_BOOLEAN:
15205
case JIM_TT_STR:
15206
Jim_SetResult(interp, node->objPtr);
15207
return JIM_OK;
15208
15209
case JIM_TT_VAR:
15210
objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
15211
if (objPtr) {
15212
Jim_SetResult(interp, objPtr);
15213
return JIM_OK;
15214
}
15215
return JIM_ERR;
15216
15217
case JIM_TT_DICTSUGAR:
15218
objPtr = JimExpandDictSugar(interp, node->objPtr);
15219
if (objPtr) {
15220
Jim_SetResult(interp, objPtr);
15221
return JIM_OK;
15222
}
15223
return JIM_ERR;
15224
15225
case JIM_TT_ESC:
15226
if (interp->safeexpr) {
15227
return JIM_ERR;
15228
}
15229
if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
15230
Jim_SetResult(interp, objPtr);
15231
return JIM_OK;
15232
}
15233
return JIM_ERR;
15234
15235
case JIM_TT_CMD:
15236
if (interp->safeexpr) {
15237
return JIM_ERR;
15238
}
15239
return Jim_EvalObj(interp, node->objPtr);
15240
15241
default:
15242
15243
return JIM_ERR;
15244
}
15245
}
15246
}
15247
15248
static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
15249
{
15250
int rc = JimExprEvalTermNode(interp, node);
15251
if (rc == JIM_OK) {
15252
*objPtrPtr = Jim_GetResult(interp);
15253
Jim_IncrRefCount(*objPtrPtr);
15254
}
15255
return rc;
15256
}
15257
15258
static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
15259
{
15260
if (JimExprEvalTermNode(interp, node) == JIM_OK) {
15261
return ExprBool(interp, Jim_GetResult(interp));
15262
}
15263
return -1;
15264
}
15265
15266
int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
15267
{
15268
struct ExprTree *expr;
15269
int retcode = JIM_OK;
15270
15271
Jim_IncrRefCount(exprObjPtr);
15272
expr = JimGetExpression(interp, exprObjPtr);
15273
if (!expr) {
15274
retcode = JIM_ERR;
15275
goto done;
15276
}
15277
15278
#ifdef JIM_OPTIMIZATION
15279
if (!interp->safeexpr) {
15280
Jim_Obj *objPtr;
15281
15282
15283
switch (expr->len) {
15284
case 1:
15285
objPtr = JimExprIntValOrVar(interp, expr->expr);
15286
if (objPtr) {
15287
Jim_SetResult(interp, objPtr);
15288
goto done;
15289
}
15290
break;
15291
15292
case 2:
15293
if (expr->expr->type == JIM_EXPROP_NOT) {
15294
objPtr = JimExprIntValOrVar(interp, expr->expr->left);
15295
15296
if (objPtr && JimIsWide(objPtr)) {
15297
Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
15298
goto done;
15299
}
15300
}
15301
break;
15302
15303
case 3:
15304
objPtr = JimExprIntValOrVar(interp, expr->expr->left);
15305
if (objPtr && JimIsWide(objPtr)) {
15306
Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
15307
if (objPtr2 && JimIsWide(objPtr2)) {
15308
jim_wide wideValueA = JimWideValue(objPtr);
15309
jim_wide wideValueB = JimWideValue(objPtr2);
15310
int cmpRes;
15311
switch (expr->expr->type) {
15312
case JIM_EXPROP_LT:
15313
cmpRes = wideValueA < wideValueB;
15314
break;
15315
case JIM_EXPROP_LTE:
15316
cmpRes = wideValueA <= wideValueB;
15317
break;
15318
case JIM_EXPROP_GT:
15319
cmpRes = wideValueA > wideValueB;
15320
break;
15321
case JIM_EXPROP_GTE:
15322
cmpRes = wideValueA >= wideValueB;
15323
break;
15324
case JIM_EXPROP_NUMEQ:
15325
cmpRes = wideValueA == wideValueB;
15326
break;
15327
case JIM_EXPROP_NUMNE:
15328
cmpRes = wideValueA != wideValueB;
15329
break;
15330
default:
15331
goto noopt;
15332
}
15333
Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
15334
goto done;
15335
}
15336
}
15337
break;
15338
}
15339
}
15340
noopt:
15341
#endif
15342
15343
expr->inUse++;
15344
15345
15346
retcode = JimExprEvalTermNode(interp, expr->expr);
15347
15348
15349
Jim_FreeIntRep(interp, exprObjPtr);
15350
exprObjPtr->typePtr = &exprObjType;
15351
Jim_SetIntRepPtr(exprObjPtr, expr);
15352
15353
done:
15354
Jim_DecrRefCount(interp, exprObjPtr);
15355
15356
return retcode;
15357
}
15358
15359
int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
15360
{
15361
int retcode = Jim_EvalExpression(interp, exprObjPtr);
15362
15363
if (retcode == JIM_OK) {
15364
switch (ExprBool(interp, Jim_GetResult(interp))) {
15365
case 0:
15366
*boolPtr = 0;
15367
break;
15368
15369
case 1:
15370
*boolPtr = 1;
15371
break;
15372
15373
case -1:
15374
retcode = JIM_ERR;
15375
break;
15376
}
15377
}
15378
return retcode;
15379
}
15380
15381
15382
15383
15384
typedef struct ScanFmtPartDescr
15385
{
15386
const char *arg;
15387
const char *prefix;
15388
size_t width;
15389
int pos;
15390
char type;
15391
char modifier;
15392
} ScanFmtPartDescr;
15393
15394
15395
typedef struct ScanFmtStringObj
15396
{
15397
jim_wide size;
15398
char *stringRep;
15399
size_t count;
15400
size_t convCount;
15401
size_t maxPos;
15402
const char *error;
15403
char *scratch;
15404
ScanFmtPartDescr descr[1];
15405
} ScanFmtStringObj;
15406
15407
15408
static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
15409
static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
15410
static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
15411
15412
static const Jim_ObjType scanFmtStringObjType = {
15413
"scanformatstring",
15414
FreeScanFmtInternalRep,
15415
DupScanFmtInternalRep,
15416
UpdateStringOfScanFmt,
15417
JIM_TYPE_NONE,
15418
};
15419
15420
void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
15421
{
15422
JIM_NOTUSED(interp);
15423
Jim_Free((char *)objPtr->internalRep.ptr);
15424
objPtr->internalRep.ptr = 0;
15425
}
15426
15427
void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
15428
{
15429
size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
15430
ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
15431
15432
JIM_NOTUSED(interp);
15433
memcpy(newVec, srcPtr->internalRep.ptr, size);
15434
dupPtr->internalRep.ptr = newVec;
15435
dupPtr->typePtr = &scanFmtStringObjType;
15436
}
15437
15438
static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
15439
{
15440
JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
15441
}
15442
15443
15444
static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
15445
{
15446
ScanFmtStringObj *fmtObj;
15447
char *buffer;
15448
int maxCount, i, approxSize, lastPos = -1;
15449
const char *fmt = Jim_String(objPtr);
15450
int maxFmtLen = Jim_Length(objPtr);
15451
const char *fmtEnd = fmt + maxFmtLen;
15452
int curr;
15453
15454
Jim_FreeIntRep(interp, objPtr);
15455
15456
for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
15457
if (fmt[i] == '%')
15458
++maxCount;
15459
15460
approxSize = sizeof(ScanFmtStringObj)
15461
+(maxCount + 1) * sizeof(ScanFmtPartDescr)
15462
+maxFmtLen * sizeof(char) + 3 + 1
15463
+ maxFmtLen * sizeof(char) + 1
15464
+ maxFmtLen * sizeof(char)
15465
+(maxCount + 1) * sizeof(char)
15466
+1;
15467
fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
15468
memset(fmtObj, 0, approxSize);
15469
fmtObj->size = approxSize;
15470
fmtObj->maxPos = 0;
15471
fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
15472
fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
15473
memcpy(fmtObj->stringRep, fmt, maxFmtLen);
15474
buffer = fmtObj->stringRep + maxFmtLen + 1;
15475
objPtr->internalRep.ptr = fmtObj;
15476
objPtr->typePtr = &scanFmtStringObjType;
15477
for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
15478
int width = 0, skip;
15479
ScanFmtPartDescr *descr = &fmtObj->descr[curr];
15480
15481
fmtObj->count++;
15482
descr->width = 0;
15483
15484
if (*fmt != '%' || fmt[1] == '%') {
15485
descr->type = 0;
15486
descr->prefix = &buffer[i];
15487
for (; fmt < fmtEnd; ++fmt) {
15488
if (*fmt == '%') {
15489
if (fmt[1] != '%')
15490
break;
15491
++fmt;
15492
}
15493
buffer[i++] = *fmt;
15494
}
15495
buffer[i++] = 0;
15496
}
15497
15498
++fmt;
15499
15500
if (fmt >= fmtEnd)
15501
goto done;
15502
descr->pos = 0;
15503
if (*fmt == '*') {
15504
descr->pos = -1;
15505
++fmt;
15506
}
15507
else
15508
fmtObj->convCount++;
15509
15510
if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
15511
fmt += skip;
15512
15513
if (descr->pos != -1 && *fmt == '$') {
15514
int prev;
15515
15516
++fmt;
15517
descr->pos = width;
15518
width = 0;
15519
15520
if ((lastPos == 0 && descr->pos > 0)
15521
|| (lastPos > 0 && descr->pos == 0)) {
15522
fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
15523
return JIM_ERR;
15524
}
15525
15526
for (prev = 0; prev < curr; ++prev) {
15527
if (fmtObj->descr[prev].pos == -1)
15528
continue;
15529
if (fmtObj->descr[prev].pos == descr->pos) {
15530
fmtObj->error =
15531
"variable is assigned by multiple \"%n$\" conversion specifiers";
15532
return JIM_ERR;
15533
}
15534
}
15535
if (descr->pos < 0) {
15536
fmtObj->error =
15537
"\"%n$\" conversion specifier is negative";
15538
return JIM_ERR;
15539
}
15540
15541
if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
15542
descr->width = width;
15543
fmt += skip;
15544
}
15545
if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
15546
fmtObj->maxPos = descr->pos;
15547
}
15548
else {
15549
15550
descr->width = width;
15551
}
15552
}
15553
15554
if (lastPos == -1)
15555
lastPos = descr->pos;
15556
15557
if (*fmt == '[') {
15558
int swapped = 1, beg = i, end, j;
15559
15560
descr->type = '[';
15561
descr->arg = &buffer[i];
15562
++fmt;
15563
if (*fmt == '^')
15564
buffer[i++] = *fmt++;
15565
if (*fmt == ']')
15566
buffer[i++] = *fmt++;
15567
while (*fmt && *fmt != ']')
15568
buffer[i++] = *fmt++;
15569
if (*fmt != ']') {
15570
fmtObj->error = "unmatched [ in format string";
15571
return JIM_ERR;
15572
}
15573
end = i;
15574
buffer[i++] = 0;
15575
15576
while (swapped) {
15577
swapped = 0;
15578
for (j = beg + 1; j < end - 1; ++j) {
15579
if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
15580
char tmp = buffer[j - 1];
15581
15582
buffer[j - 1] = buffer[j + 1];
15583
buffer[j + 1] = tmp;
15584
swapped = 1;
15585
}
15586
}
15587
}
15588
}
15589
else {
15590
15591
if (fmt < fmtEnd && strchr("hlL", *fmt))
15592
descr->modifier = tolower((int)*fmt++);
15593
15594
if (fmt >= fmtEnd) {
15595
fmtObj->error = "missing scan conversion character";
15596
return JIM_ERR;
15597
}
15598
15599
descr->type = *fmt;
15600
if (strchr("efgcsndoxui", *fmt) == 0) {
15601
fmtObj->error = "bad scan conversion character";
15602
return JIM_ERR;
15603
}
15604
else if (*fmt == 'c' && descr->width != 0) {
15605
fmtObj->error = "field width may not be specified in %c " "conversion";
15606
return JIM_ERR;
15607
}
15608
else if (*fmt == 'u' && descr->modifier == 'l') {
15609
fmtObj->error = "unsigned wide not supported";
15610
return JIM_ERR;
15611
}
15612
}
15613
curr++;
15614
}
15615
done:
15616
return JIM_OK;
15617
}
15618
15619
15620
15621
#define FormatGetCnvCount(_fo_) \
15622
((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
15623
#define FormatGetMaxPos(_fo_) \
15624
((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
15625
#define FormatGetError(_fo_) \
15626
((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
15627
15628
static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
15629
{
15630
char *buffer = Jim_StrDup(str);
15631
char *p = buffer;
15632
15633
while (*str) {
15634
int c;
15635
int n;
15636
15637
if (!sdescr && isspace(UCHAR(*str)))
15638
break;
15639
15640
n = utf8_tounicode(str, &c);
15641
if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN))
15642
break;
15643
while (n--)
15644
*p++ = *str++;
15645
}
15646
*p = 0;
15647
return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
15648
}
15649
15650
15651
static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen,
15652
ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
15653
{
15654
const char *tok;
15655
const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
15656
size_t scanned = 0;
15657
size_t anchor = pos;
15658
int i;
15659
Jim_Obj *tmpObj = NULL;
15660
15661
15662
*valObjPtr = 0;
15663
if (descr->prefix) {
15664
for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) {
15665
15666
if (isspace(UCHAR(descr->prefix[i])))
15667
while (pos < str_bytelen && isspace(UCHAR(str[pos])))
15668
++pos;
15669
else if (descr->prefix[i] != str[pos])
15670
break;
15671
else
15672
++pos;
15673
}
15674
if (pos >= str_bytelen) {
15675
return -1;
15676
}
15677
else if (descr->prefix[i] != 0)
15678
return 0;
15679
}
15680
15681
if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
15682
while (isspace(UCHAR(str[pos])))
15683
++pos;
15684
15685
15686
scanned = pos - anchor;
15687
15688
15689
if (descr->type == 'n') {
15690
15691
*valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
15692
}
15693
else if (pos >= str_bytelen) {
15694
15695
return -1;
15696
}
15697
else if (descr->type == 'c') {
15698
int c;
15699
scanned += utf8_tounicode(&str[pos], &c);
15700
*valObjPtr = Jim_NewIntObj(interp, c);
15701
return scanned;
15702
}
15703
else {
15704
15705
if (descr->width > 0) {
15706
size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos);
15707
size_t tLen = descr->width > sLen ? sLen : descr->width;
15708
15709
tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
15710
tok = tmpObj->bytes;
15711
}
15712
else {
15713
15714
tok = &str[pos];
15715
}
15716
switch (descr->type) {
15717
case 'd':
15718
case 'o':
15719
case 'x':
15720
case 'u':
15721
case 'i':{
15722
char *endp;
15723
jim_wide w;
15724
15725
int base = descr->type == 'o' ? 8
15726
: descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
15727
15728
15729
if (base == 0) {
15730
w = jim_strtoull(tok, &endp);
15731
}
15732
else {
15733
w = strtoull(tok, &endp, base);
15734
}
15735
15736
if (endp != tok) {
15737
15738
*valObjPtr = Jim_NewIntObj(interp, w);
15739
15740
15741
scanned += endp - tok;
15742
}
15743
else {
15744
scanned = *tok ? 0 : -1;
15745
}
15746
break;
15747
}
15748
case 's':
15749
case '[':{
15750
*valObjPtr = JimScanAString(interp, descr->arg, tok);
15751
scanned += Jim_Length(*valObjPtr);
15752
break;
15753
}
15754
case 'e':
15755
case 'f':
15756
case 'g':{
15757
char *endp;
15758
double value = strtod(tok, &endp);
15759
15760
if (endp != tok) {
15761
15762
*valObjPtr = Jim_NewDoubleObj(interp, value);
15763
15764
scanned += endp - tok;
15765
}
15766
else {
15767
scanned = *tok ? 0 : -1;
15768
}
15769
break;
15770
}
15771
}
15772
if (tmpObj) {
15773
Jim_FreeNewObj(interp, tmpObj);
15774
}
15775
}
15776
return scanned;
15777
}
15778
15779
15780
Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
15781
{
15782
size_t i, pos;
15783
int scanned = 1;
15784
const char *str = Jim_String(strObjPtr);
15785
int str_bytelen = Jim_Length(strObjPtr);
15786
Jim_Obj *resultList = 0;
15787
Jim_Obj **resultVec = 0;
15788
int resultc;
15789
Jim_Obj *emptyStr = 0;
15790
ScanFmtStringObj *fmtObj;
15791
15792
15793
JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
15794
15795
fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
15796
15797
if (fmtObj->error != 0) {
15798
if (flags & JIM_ERRMSG)
15799
Jim_SetResultString(interp, fmtObj->error, -1);
15800
return 0;
15801
}
15802
15803
emptyStr = Jim_NewEmptyStringObj(interp);
15804
Jim_IncrRefCount(emptyStr);
15805
15806
resultList = Jim_NewListObj(interp, NULL, 0);
15807
if (fmtObj->maxPos > 0) {
15808
for (i = 0; i < fmtObj->maxPos; ++i)
15809
Jim_ListAppendElement(interp, resultList, emptyStr);
15810
JimListGetElements(interp, resultList, &resultc, &resultVec);
15811
}
15812
15813
for (i = 0, pos = 0; i < fmtObj->count; ++i) {
15814
ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
15815
Jim_Obj *value = 0;
15816
15817
15818
if (descr->type == 0)
15819
continue;
15820
15821
if (scanned > 0)
15822
scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value);
15823
15824
if (scanned == -1 && i == 0)
15825
goto eof;
15826
15827
pos += scanned;
15828
15829
15830
if (value == 0)
15831
value = Jim_NewEmptyStringObj(interp);
15832
15833
if (descr->pos == -1) {
15834
Jim_FreeNewObj(interp, value);
15835
}
15836
else if (descr->pos == 0)
15837
15838
Jim_ListAppendElement(interp, resultList, value);
15839
else if (resultVec[descr->pos - 1] == emptyStr) {
15840
15841
Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
15842
Jim_IncrRefCount(value);
15843
resultVec[descr->pos - 1] = value;
15844
}
15845
else {
15846
15847
Jim_FreeNewObj(interp, value);
15848
goto err;
15849
}
15850
}
15851
Jim_DecrRefCount(interp, emptyStr);
15852
return resultList;
15853
eof:
15854
Jim_DecrRefCount(interp, emptyStr);
15855
Jim_FreeNewObj(interp, resultList);
15856
return (Jim_Obj *)EOF;
15857
err:
15858
Jim_DecrRefCount(interp, emptyStr);
15859
Jim_FreeNewObj(interp, resultList);
15860
return 0;
15861
}
15862
15863
15864
static void JimPrngInit(Jim_Interp *interp)
15865
{
15866
#define PRNG_SEED_SIZE 256
15867
int i;
15868
unsigned int *seed;
15869
time_t t = time(NULL);
15870
15871
interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
15872
15873
seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
15874
for (i = 0; i < PRNG_SEED_SIZE; i++) {
15875
seed[i] = (rand() ^ t ^ clock());
15876
}
15877
JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
15878
Jim_Free(seed);
15879
}
15880
15881
15882
static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
15883
{
15884
Jim_PrngState *prng;
15885
unsigned char *destByte = (unsigned char *)dest;
15886
unsigned int si, sj, x;
15887
15888
15889
if (interp->prngState == NULL)
15890
JimPrngInit(interp);
15891
prng = interp->prngState;
15892
15893
for (x = 0; x < len; x++) {
15894
prng->i = (prng->i + 1) & 0xff;
15895
si = prng->sbox[prng->i];
15896
prng->j = (prng->j + si) & 0xff;
15897
sj = prng->sbox[prng->j];
15898
prng->sbox[prng->i] = sj;
15899
prng->sbox[prng->j] = si;
15900
*destByte++ = prng->sbox[(si + sj) & 0xff];
15901
}
15902
}
15903
15904
15905
static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
15906
{
15907
int i;
15908
Jim_PrngState *prng;
15909
15910
15911
if (interp->prngState == NULL)
15912
JimPrngInit(interp);
15913
prng = interp->prngState;
15914
15915
15916
for (i = 0; i < 256; i++)
15917
prng->sbox[i] = i;
15918
15919
for (i = 0; i < seedLen; i++) {
15920
unsigned char t;
15921
15922
t = prng->sbox[i & 0xFF];
15923
prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
15924
prng->sbox[seed[i]] = t;
15925
}
15926
prng->i = prng->j = 0;
15927
15928
for (i = 0; i < 256; i += seedLen) {
15929
JimRandomBytes(interp, seed, seedLen);
15930
}
15931
}
15932
15933
15934
static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15935
{
15936
jim_wide wideValue, increment = 1;
15937
Jim_Obj *intObjPtr;
15938
15939
if (argc != 2 && argc != 3) {
15940
Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
15941
return JIM_ERR;
15942
}
15943
if (argc == 3) {
15944
if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK)
15945
return JIM_ERR;
15946
}
15947
intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
15948
if (!intObjPtr) {
15949
15950
wideValue = 0;
15951
}
15952
else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
15953
return JIM_ERR;
15954
}
15955
if (!intObjPtr || Jim_IsShared(intObjPtr)) {
15956
intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
15957
if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
15958
Jim_FreeNewObj(interp, intObjPtr);
15959
return JIM_ERR;
15960
}
15961
}
15962
else {
15963
15964
Jim_InvalidateStringRep(intObjPtr);
15965
JimWideValue(intObjPtr) = wideValue + increment;
15966
15967
if (argv[1]->typePtr != &variableObjType) {
15968
15969
Jim_SetVariable(interp, argv[1], intObjPtr);
15970
}
15971
}
15972
Jim_SetResult(interp, intObjPtr);
15973
return JIM_OK;
15974
}
15975
15976
15977
#define JIM_EVAL_SARGV_LEN 8
15978
#define JIM_EVAL_SINTV_LEN 8
15979
15980
static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv)
15981
{
15982
JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object"));
15983
15984
int ret;
15985
Jim_Obj *nargv[7];
15986
Jim_Obj *traceCmdObj = interp->traceCmdObj;
15987
Jim_Obj *resultObj = Jim_GetResult(interp);
15988
ScriptObj *script = NULL;
15989
15990
15991
15992
if (interp->evalFrame->scriptObj) {
15993
script = JimGetScript(interp, interp->evalFrame->scriptObj);
15994
}
15995
15996
nargv[0] = traceCmdObj;
15997
nargv[1] = Jim_NewStringObj(interp, type, -1);
15998
nargv[2] = script ? script->fileNameObj : interp->emptyObj;
15999
nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1);
16000
nargv[4] = resultObj;
16001
nargv[5] = argv[0];
16002
nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1);
16003
16004
16005
interp->traceCmdObj = NULL;
16006
16007
Jim_IncrRefCount(resultObj);
16008
ret = Jim_EvalObjVector(interp, 7, nargv);
16009
Jim_DecrRefCount(interp, resultObj);
16010
16011
if (ret == JIM_OK || ret == JIM_RETURN) {
16012
16013
interp->traceCmdObj = traceCmdObj;
16014
Jim_SetEmptyResult(interp);
16015
ret = JIM_OK;
16016
}
16017
else {
16018
16019
Jim_DecrRefCount(interp, traceCmdObj);
16020
}
16021
return ret;
16022
}
16023
16024
16025
static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16026
{
16027
int retcode;
16028
16029
if (interp->unknown_called > 50) {
16030
return JIM_ERR;
16031
}
16032
16033
16034
16035
if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
16036
return JIM_ERR;
16037
16038
interp->unknown_called++;
16039
16040
retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
16041
interp->unknown_called--;
16042
16043
return retcode;
16044
}
16045
16046
static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj)
16047
{
16048
memset(frame, 0, sizeof(*frame));
16049
frame->parent = interp->evalFrame;
16050
frame->level = frame->parent->level + 1;
16051
frame->procLevel = interp->procLevel;
16052
frame->framePtr = interp->framePtr;
16053
if (scriptObj) {
16054
frame->scriptObj = scriptObj;
16055
}
16056
else {
16057
frame->scriptObj = frame->parent->scriptObj;
16058
}
16059
interp->evalFrame = frame;
16060
#if 0
16061
if (frame->scriptObj) {
16062
printf("script: %.*s\n", 20, Jim_String(frame->scriptObj));
16063
}
16064
#endif
16065
}
16066
16067
static void JimPopEvalFrame(Jim_Interp *interp)
16068
{
16069
interp->evalFrame = interp->evalFrame->parent;
16070
}
16071
16072
16073
static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
16074
{
16075
int retcode;
16076
Jim_Cmd *cmdPtr;
16077
void *prevPrivData;
16078
Jim_Obj *tailcallObj = NULL;
16079
16080
#if 0
16081
printf("invoke");
16082
int j;
16083
for (j = 0; j < objc; j++) {
16084
printf(" '%s'", Jim_String(objv[j]));
16085
}
16086
printf("\n");
16087
#endif
16088
16089
cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
16090
if (cmdPtr == NULL) {
16091
return JimUnknown(interp, objc, objv);
16092
}
16093
JimIncrCmdRefCount(cmdPtr);
16094
16095
if (interp->evalDepth == interp->maxEvalDepth) {
16096
Jim_SetResultString(interp, "Infinite eval recursion", -1);
16097
retcode = JIM_ERR;
16098
goto out;
16099
}
16100
interp->evalDepth++;
16101
prevPrivData = interp->cmdPrivData;
16102
16103
tailcall:
16104
16105
interp->evalFrame->argc = objc;
16106
interp->evalFrame->argv = objv;
16107
interp->evalFrame->cmd = cmdPtr;
16108
16109
if (!interp->traceCmdObj ||
16110
(retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) {
16111
16112
Jim_SetEmptyResult(interp);
16113
if (cmdPtr->isproc) {
16114
retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
16115
}
16116
else {
16117
interp->cmdPrivData = cmdPtr->u.native.privData;
16118
retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
16119
}
16120
if (retcode == JIM_ERR) {
16121
JimSetErrorStack(interp, NULL);
16122
}
16123
}
16124
16125
if (tailcallObj) {
16126
16127
Jim_DecrRefCount(interp, tailcallObj);
16128
tailcallObj = NULL;
16129
}
16130
16131
16132
interp->evalFrame->argc = 0;
16133
interp->evalFrame->argv = NULL;
16134
16135
16136
if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) {
16137
JimDecrCmdRefCount(interp, cmdPtr);
16138
16139
16140
cmdPtr = interp->framePtr->tailcallCmd;
16141
interp->framePtr->tailcallCmd = NULL;
16142
tailcallObj = interp->framePtr->tailcallObj;
16143
interp->framePtr->tailcallObj = NULL;
16144
objc = tailcallObj->internalRep.listValue.len;
16145
objv = tailcallObj->internalRep.listValue.ele;
16146
goto tailcall;
16147
}
16148
16149
interp->cmdPrivData = prevPrivData;
16150
interp->evalDepth--;
16151
16152
out:
16153
JimDecrCmdRefCount(interp, cmdPtr);
16154
16155
if (retcode == JIM_ERR) {
16156
JimSetErrorStack(interp, NULL);
16157
}
16158
16159
if (interp->framePtr->tailcallObj) {
16160
JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
16161
Jim_DecrRefCount(interp, interp->framePtr->tailcallObj);
16162
interp->framePtr->tailcallCmd = NULL;
16163
interp->framePtr->tailcallObj = NULL;
16164
}
16165
16166
return retcode;
16167
}
16168
16169
int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
16170
{
16171
int i, retcode;
16172
Jim_EvalFrame frame;
16173
16174
16175
for (i = 0; i < objc; i++)
16176
Jim_IncrRefCount(objv[i]);
16177
16178
16179
JimPushEvalFrame(interp, &frame, NULL);
16180
16181
retcode = JimInvokeCommand(interp, objc, objv);
16182
16183
JimPopEvalFrame(interp);
16184
16185
16186
for (i = 0; i < objc; i++)
16187
Jim_DecrRefCount(interp, objv[i]);
16188
16189
return retcode;
16190
}
16191
16192
int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
16193
{
16194
int ret;
16195
Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
16196
16197
nargv[0] = prefix;
16198
memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
16199
ret = Jim_EvalObjVector(interp, objc + 1, nargv);
16200
Jim_Free(nargv);
16201
return ret;
16202
}
16203
16204
static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
16205
{
16206
Jim_Obj *objPtr;
16207
int ret = JIM_ERR;
16208
16209
switch (token->type) {
16210
case JIM_TT_STR:
16211
case JIM_TT_ESC:
16212
objPtr = token->objPtr;
16213
break;
16214
case JIM_TT_VAR:
16215
objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
16216
break;
16217
case JIM_TT_DICTSUGAR:
16218
objPtr = JimExpandDictSugar(interp, token->objPtr);
16219
break;
16220
case JIM_TT_EXPRSUGAR:
16221
ret = Jim_EvalExpression(interp, token->objPtr);
16222
if (ret == JIM_OK) {
16223
objPtr = Jim_GetResult(interp);
16224
}
16225
else {
16226
objPtr = NULL;
16227
}
16228
break;
16229
case JIM_TT_CMD:
16230
ret = Jim_EvalObj(interp, token->objPtr);
16231
if (ret == JIM_OK || ret == JIM_RETURN) {
16232
objPtr = interp->result;
16233
} else {
16234
16235
objPtr = NULL;
16236
}
16237
break;
16238
default:
16239
JimPanic((1,
16240
"default token type (%d) reached " "in Jim_SubstObj().", token->type));
16241
objPtr = NULL;
16242
break;
16243
}
16244
if (objPtr) {
16245
*objPtrPtr = objPtr;
16246
return JIM_OK;
16247
}
16248
return ret;
16249
}
16250
16251
static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
16252
{
16253
int totlen = 0, i;
16254
Jim_Obj **intv;
16255
Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
16256
Jim_Obj *objPtr;
16257
char *s;
16258
16259
if (tokens <= JIM_EVAL_SINTV_LEN)
16260
intv = sintv;
16261
else
16262
intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
16263
16264
for (i = 0; i < tokens; i++) {
16265
switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
16266
case JIM_OK:
16267
case JIM_RETURN:
16268
break;
16269
case JIM_BREAK:
16270
if (flags & JIM_SUBST_FLAG) {
16271
16272
tokens = i;
16273
continue;
16274
}
16275
16276
16277
case JIM_CONTINUE:
16278
if (flags & JIM_SUBST_FLAG) {
16279
intv[i] = NULL;
16280
continue;
16281
}
16282
16283
16284
default:
16285
while (i--) {
16286
Jim_DecrRefCount(interp, intv[i]);
16287
}
16288
if (intv != sintv) {
16289
Jim_Free(intv);
16290
}
16291
return NULL;
16292
}
16293
Jim_IncrRefCount(intv[i]);
16294
Jim_String(intv[i]);
16295
totlen += intv[i]->length;
16296
}
16297
16298
16299
if (tokens == 1 && intv[0] && intv == sintv) {
16300
16301
intv[0]->refCount--;
16302
return intv[0];
16303
}
16304
16305
objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
16306
16307
if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
16308
&& token[2].type == JIM_TT_VAR) {
16309
16310
objPtr->typePtr = &interpolatedObjType;
16311
objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
16312
objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
16313
Jim_IncrRefCount(intv[2]);
16314
}
16315
else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
16316
16317
int line;
16318
Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line);
16319
Jim_SetSourceInfo(interp, objPtr, fileNameObj, line);
16320
}
16321
16322
16323
s = objPtr->bytes = Jim_Alloc(totlen + 1);
16324
objPtr->length = totlen;
16325
for (i = 0; i < tokens; i++) {
16326
if (intv[i]) {
16327
memcpy(s, intv[i]->bytes, intv[i]->length);
16328
s += intv[i]->length;
16329
Jim_DecrRefCount(interp, intv[i]);
16330
}
16331
}
16332
objPtr->bytes[totlen] = '\0';
16333
16334
if (intv != sintv) {
16335
Jim_Free(intv);
16336
}
16337
16338
return objPtr;
16339
}
16340
16341
16342
static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
16343
{
16344
int retcode = JIM_OK;
16345
Jim_EvalFrame frame;
16346
16347
JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
16348
16349
JimPushEvalFrame(interp, &frame, NULL);
16350
16351
if (listPtr->internalRep.listValue.len) {
16352
Jim_IncrRefCount(listPtr);
16353
retcode = JimInvokeCommand(interp,
16354
listPtr->internalRep.listValue.len,
16355
listPtr->internalRep.listValue.ele);
16356
Jim_DecrRefCount(interp, listPtr);
16357
}
16358
16359
JimPopEvalFrame(interp);
16360
16361
return retcode;
16362
}
16363
16364
int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
16365
{
16366
SetListFromAny(interp, listPtr);
16367
return JimEvalObjList(interp, listPtr);
16368
}
16369
16370
int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
16371
{
16372
int i;
16373
ScriptObj *script;
16374
ScriptToken *token;
16375
int retcode = JIM_OK;
16376
Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
16377
Jim_EvalFrame frame;
16378
16379
if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
16380
return JimEvalObjList(interp, scriptObjPtr);
16381
}
16382
16383
Jim_IncrRefCount(scriptObjPtr);
16384
script = JimGetScript(interp, scriptObjPtr);
16385
if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
16386
JimSetErrorStack(interp, script);
16387
Jim_DecrRefCount(interp, scriptObjPtr);
16388
return JIM_ERR;
16389
}
16390
16391
Jim_SetEmptyResult(interp);
16392
16393
token = script->token;
16394
16395
#ifdef JIM_OPTIMIZATION
16396
if (script->len == 0) {
16397
Jim_DecrRefCount(interp, scriptObjPtr);
16398
return JIM_OK;
16399
}
16400
if (script->len == 3
16401
&& token[1].objPtr->typePtr == &commandObjType
16402
&& token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
16403
&& token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
16404
&& token[2].objPtr->typePtr == &variableObjType) {
16405
16406
Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
16407
16408
if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16409
JimWideValue(objPtr)++;
16410
Jim_InvalidateStringRep(objPtr);
16411
Jim_DecrRefCount(interp, scriptObjPtr);
16412
Jim_SetResult(interp, objPtr);
16413
return JIM_OK;
16414
}
16415
}
16416
#endif
16417
16418
script->inUse++;
16419
16420
JimPushEvalFrame(interp, &frame, scriptObjPtr);
16421
16422
16423
interp->errorFlag = 0;
16424
argv = sargv;
16425
16426
for (i = 0; i < script->len && retcode == JIM_OK; ) {
16427
int argc;
16428
int j;
16429
16430
16431
argc = token[i].objPtr->internalRep.scriptLineValue.argc;
16432
script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
16433
16434
16435
if (argc > JIM_EVAL_SARGV_LEN)
16436
argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
16437
16438
16439
i++;
16440
16441
for (j = 0; j < argc; j++) {
16442
long wordtokens = 1;
16443
int expand = 0;
16444
Jim_Obj *wordObjPtr = NULL;
16445
16446
if (token[i].type == JIM_TT_WORD) {
16447
wordtokens = JimWideValue(token[i++].objPtr);
16448
if (wordtokens < 0) {
16449
expand = 1;
16450
wordtokens = -wordtokens;
16451
}
16452
}
16453
16454
if (wordtokens == 1) {
16455
16456
switch (token[i].type) {
16457
case JIM_TT_ESC:
16458
case JIM_TT_STR:
16459
wordObjPtr = token[i].objPtr;
16460
break;
16461
case JIM_TT_VAR:
16462
wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
16463
break;
16464
case JIM_TT_EXPRSUGAR:
16465
retcode = Jim_EvalExpression(interp, token[i].objPtr);
16466
if (retcode == JIM_OK) {
16467
wordObjPtr = Jim_GetResult(interp);
16468
}
16469
else {
16470
wordObjPtr = NULL;
16471
}
16472
break;
16473
case JIM_TT_DICTSUGAR:
16474
wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
16475
break;
16476
case JIM_TT_CMD:
16477
retcode = Jim_EvalObj(interp, token[i].objPtr);
16478
if (retcode == JIM_OK) {
16479
wordObjPtr = Jim_GetResult(interp);
16480
}
16481
break;
16482
default:
16483
JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
16484
}
16485
}
16486
else {
16487
wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
16488
}
16489
16490
if (!wordObjPtr) {
16491
if (retcode == JIM_OK) {
16492
retcode = JIM_ERR;
16493
}
16494
break;
16495
}
16496
16497
Jim_IncrRefCount(wordObjPtr);
16498
i += wordtokens;
16499
16500
if (!expand) {
16501
argv[j] = wordObjPtr;
16502
}
16503
else {
16504
16505
int len = Jim_ListLength(interp, wordObjPtr);
16506
int newargc = argc + len - 1;
16507
int k;
16508
16509
if (len > 1) {
16510
if (argv == sargv) {
16511
if (newargc > JIM_EVAL_SARGV_LEN) {
16512
argv = Jim_Alloc(sizeof(*argv) * newargc);
16513
memcpy(argv, sargv, sizeof(*argv) * j);
16514
}
16515
}
16516
else {
16517
16518
argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
16519
}
16520
}
16521
16522
16523
for (k = 0; k < len; k++) {
16524
argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
16525
Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
16526
}
16527
16528
Jim_DecrRefCount(interp, wordObjPtr);
16529
16530
16531
j--;
16532
argc += len - 1;
16533
}
16534
}
16535
16536
if (retcode == JIM_OK && argc) {
16537
16538
retcode = JimInvokeCommand(interp, argc, argv);
16539
16540
if (Jim_CheckSignal(interp)) {
16541
retcode = JIM_SIGNAL;
16542
}
16543
}
16544
16545
16546
while (j-- > 0) {
16547
Jim_DecrRefCount(interp, argv[j]);
16548
}
16549
16550
if (argv != sargv) {
16551
Jim_Free(argv);
16552
argv = sargv;
16553
}
16554
}
16555
16556
16557
if (retcode == JIM_ERR) {
16558
JimSetErrorStack(interp, NULL);
16559
}
16560
16561
JimPopEvalFrame(interp);
16562
16563
Jim_FreeIntRep(interp, scriptObjPtr);
16564
scriptObjPtr->typePtr = &scriptObjType;
16565
Jim_SetIntRepPtr(scriptObjPtr, script);
16566
Jim_DecrRefCount(interp, scriptObjPtr);
16567
16568
return retcode;
16569
}
16570
16571
static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
16572
{
16573
int retcode;
16574
16575
const char *varname = Jim_String(argNameObj);
16576
if (*varname == '&') {
16577
16578
Jim_Obj *objPtr;
16579
Jim_CallFrame *savedCallFrame = interp->framePtr;
16580
16581
interp->framePtr = interp->framePtr->parent;
16582
objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
16583
interp->framePtr = savedCallFrame;
16584
if (!objPtr) {
16585
return JIM_ERR;
16586
}
16587
16588
16589
objPtr = Jim_NewStringObj(interp, varname + 1, -1);
16590
Jim_IncrRefCount(objPtr);
16591
retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
16592
Jim_DecrRefCount(interp, objPtr);
16593
}
16594
else {
16595
retcode = Jim_SetVariable(interp, argNameObj, argValObj);
16596
}
16597
return retcode;
16598
}
16599
16600
static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
16601
{
16602
16603
Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
16604
int i;
16605
16606
for (i = 0; i < cmd->u.proc.argListLen; i++) {
16607
Jim_AppendString(interp, argmsg, " ", 1);
16608
16609
if (i == cmd->u.proc.argsPos) {
16610
if (cmd->u.proc.arglist[i].defaultObjPtr) {
16611
16612
Jim_AppendString(interp, argmsg, "?", 1);
16613
Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
16614
Jim_AppendString(interp, argmsg, " ...?", -1);
16615
}
16616
else {
16617
16618
Jim_AppendString(interp, argmsg, "?arg ...?", -1);
16619
}
16620
}
16621
else {
16622
if (cmd->u.proc.arglist[i].defaultObjPtr) {
16623
Jim_AppendString(interp, argmsg, "?", 1);
16624
Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
16625
Jim_AppendString(interp, argmsg, "?", 1);
16626
}
16627
else {
16628
const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
16629
if (*arg == '&') {
16630
arg++;
16631
}
16632
Jim_AppendString(interp, argmsg, arg, -1);
16633
}
16634
}
16635
}
16636
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
16637
}
16638
16639
#ifdef jim_ext_namespace
16640
int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
16641
{
16642
Jim_CallFrame *callFramePtr;
16643
int retcode;
16644
16645
16646
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
16647
callFramePtr->argv = interp->evalFrame->argv;
16648
callFramePtr->argc = interp->evalFrame->argc;
16649
callFramePtr->procArgsObjPtr = NULL;
16650
callFramePtr->procBodyObjPtr = scriptObj;
16651
callFramePtr->staticVars = NULL;
16652
Jim_IncrRefCount(scriptObj);
16653
interp->framePtr = callFramePtr;
16654
16655
16656
if (interp->framePtr->level == interp->maxCallFrameDepth) {
16657
Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
16658
retcode = JIM_ERR;
16659
}
16660
else {
16661
16662
retcode = Jim_EvalObj(interp, scriptObj);
16663
}
16664
16665
16666
interp->framePtr = interp->framePtr->parent;
16667
JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
16668
16669
return retcode;
16670
}
16671
#endif
16672
16673
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
16674
{
16675
Jim_CallFrame *callFramePtr;
16676
int i, d, retcode, optargs;
16677
16678
16679
if (argc - 1 < cmd->u.proc.reqArity ||
16680
(cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
16681
JimSetProcWrongArgs(interp, argv[0], cmd);
16682
return JIM_ERR;
16683
}
16684
16685
if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
16686
16687
return JIM_OK;
16688
}
16689
16690
16691
if (interp->framePtr->level == interp->maxCallFrameDepth) {
16692
Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
16693
return JIM_ERR;
16694
}
16695
16696
16697
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
16698
callFramePtr->argv = argv;
16699
callFramePtr->argc = argc;
16700
callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
16701
callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
16702
callFramePtr->staticVars = cmd->u.proc.staticVars;
16703
16704
interp->procLevel++;
16705
16706
Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
16707
Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
16708
interp->framePtr = callFramePtr;
16709
16710
16711
optargs = (argc - 1 - cmd->u.proc.reqArity);
16712
16713
16714
i = 1;
16715
for (d = 0; d < cmd->u.proc.argListLen; d++) {
16716
Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
16717
if (d == cmd->u.proc.argsPos) {
16718
16719
Jim_Obj *listObjPtr;
16720
int argsLen = 0;
16721
if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
16722
argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
16723
}
16724
listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
16725
16726
16727
if (cmd->u.proc.arglist[d].defaultObjPtr) {
16728
nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
16729
}
16730
retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
16731
if (retcode != JIM_OK) {
16732
goto badargset;
16733
}
16734
16735
i += argsLen;
16736
continue;
16737
}
16738
16739
16740
if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
16741
retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
16742
}
16743
else {
16744
16745
retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
16746
}
16747
if (retcode != JIM_OK) {
16748
goto badargset;
16749
}
16750
}
16751
16752
if (interp->traceCmdObj == NULL ||
16753
(retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) {
16754
16755
retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
16756
}
16757
16758
badargset:
16759
16760
16761
retcode = JimInvokeDefer(interp, retcode);
16762
interp->framePtr = interp->framePtr->parent;
16763
JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
16764
16765
16766
if (retcode == JIM_RETURN) {
16767
if (--interp->returnLevel <= 0) {
16768
retcode = interp->returnCode;
16769
interp->returnCode = JIM_OK;
16770
interp->returnLevel = 0;
16771
}
16772
}
16773
interp->procLevel--;
16774
16775
return retcode;
16776
}
16777
16778
int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
16779
{
16780
int retval;
16781
Jim_Obj *scriptObjPtr;
16782
16783
scriptObjPtr = Jim_NewStringObj(interp, script, -1);
16784
Jim_IncrRefCount(scriptObjPtr);
16785
if (filename) {
16786
Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
16787
}
16788
retval = Jim_EvalObj(interp, scriptObjPtr);
16789
Jim_DecrRefCount(interp, scriptObjPtr);
16790
return retval;
16791
}
16792
16793
int Jim_Eval(Jim_Interp *interp, const char *script)
16794
{
16795
return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
16796
}
16797
16798
16799
int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
16800
{
16801
int retval;
16802
Jim_CallFrame *savedFramePtr = interp->framePtr;
16803
16804
interp->framePtr = interp->topFramePtr;
16805
retval = Jim_Eval(interp, script);
16806
interp->framePtr = savedFramePtr;
16807
16808
return retval;
16809
}
16810
16811
int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
16812
{
16813
int retval;
16814
Jim_CallFrame *savedFramePtr = interp->framePtr;
16815
16816
interp->framePtr = interp->topFramePtr;
16817
retval = Jim_EvalFile(interp, filename);
16818
interp->framePtr = savedFramePtr;
16819
16820
return retval;
16821
}
16822
16823
#include <sys/stat.h>
16824
16825
static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename)
16826
{
16827
jim_stat_t sb;
16828
int fd;
16829
char *buf;
16830
int readlen;
16831
16832
if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) {
16833
Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
16834
return NULL;
16835
}
16836
buf = Jim_Alloc(sb.st_size + 1);
16837
readlen = read(fd, buf, sb.st_size);
16838
close(fd);
16839
if (readlen < 0) {
16840
Jim_Free(buf);
16841
Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
16842
return NULL;
16843
}
16844
else {
16845
Jim_Obj *objPtr;
16846
buf[readlen] = 0;
16847
16848
objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
16849
16850
return objPtr;
16851
}
16852
}
16853
16854
16855
int Jim_EvalFile(Jim_Interp *interp, const char *filename)
16856
{
16857
Jim_Obj *filenameObj;
16858
Jim_Obj *oldFilenameObj;
16859
Jim_Obj *scriptObjPtr;
16860
int retcode;
16861
16862
scriptObjPtr = JimReadTextFile(interp, filename);
16863
if (!scriptObjPtr) {
16864
return JIM_ERR;
16865
}
16866
16867
filenameObj = Jim_NewStringObj(interp, filename, -1);
16868
Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1);
16869
16870
oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj);
16871
16872
retcode = Jim_EvalObj(interp, scriptObjPtr);
16873
16874
JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj);
16875
16876
16877
if (retcode == JIM_RETURN) {
16878
if (--interp->returnLevel <= 0) {
16879
retcode = interp->returnCode;
16880
interp->returnCode = JIM_OK;
16881
interp->returnLevel = 0;
16882
}
16883
}
16884
16885
return retcode;
16886
}
16887
16888
static void JimParseSubst(struct JimParserCtx *pc, int flags)
16889
{
16890
pc->tstart = pc->p;
16891
pc->tline = pc->linenr;
16892
16893
if (pc->len == 0) {
16894
pc->tend = pc->p;
16895
pc->tt = JIM_TT_EOL;
16896
pc->eof = 1;
16897
return;
16898
}
16899
if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
16900
JimParseCmd(pc);
16901
return;
16902
}
16903
if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
16904
if (JimParseVar(pc) == JIM_OK) {
16905
return;
16906
}
16907
16908
pc->tstart = pc->p;
16909
16910
pc->p++;
16911
pc->len--;
16912
}
16913
while (pc->len) {
16914
if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
16915
break;
16916
}
16917
if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
16918
break;
16919
}
16920
if (*pc->p == '\\' && pc->len > 1) {
16921
pc->p++;
16922
pc->len--;
16923
}
16924
pc->p++;
16925
pc->len--;
16926
}
16927
pc->tend = pc->p - 1;
16928
pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
16929
}
16930
16931
16932
static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
16933
{
16934
int scriptTextLen;
16935
const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
16936
struct JimParserCtx parser;
16937
struct ScriptObj *script = Jim_Alloc(sizeof(*script));
16938
ParseTokenList tokenlist;
16939
16940
16941
ScriptTokenListInit(&tokenlist);
16942
16943
JimParserInit(&parser, scriptText, scriptTextLen, 1);
16944
while (1) {
16945
JimParseSubst(&parser, flags);
16946
if (parser.eof) {
16947
16948
break;
16949
}
16950
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
16951
parser.tline);
16952
}
16953
16954
16955
script->inUse = 1;
16956
script->substFlags = flags;
16957
script->fileNameObj = interp->emptyObj;
16958
Jim_IncrRefCount(script->fileNameObj);
16959
SubstObjAddTokens(interp, script, &tokenlist);
16960
16961
16962
ScriptTokenListFree(&tokenlist);
16963
16964
#ifdef DEBUG_SHOW_SUBST
16965
{
16966
int i;
16967
16968
printf("==== Subst ====\n");
16969
for (i = 0; i < script->len; i++) {
16970
printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
16971
Jim_String(script->token[i].objPtr));
16972
}
16973
}
16974
#endif
16975
16976
16977
Jim_FreeIntRep(interp, objPtr);
16978
Jim_SetIntRepPtr(objPtr, script);
16979
objPtr->typePtr = &scriptObjType;
16980
return JIM_OK;
16981
}
16982
16983
static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
16984
{
16985
if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
16986
SetSubstFromAny(interp, objPtr, flags);
16987
return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
16988
}
16989
16990
int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
16991
{
16992
ScriptObj *script;
16993
16994
JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object"));
16995
16996
script = Jim_GetSubst(interp, substObjPtr, flags);
16997
16998
Jim_IncrRefCount(substObjPtr);
16999
script->inUse++;
17000
17001
*resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
17002
17003
script->inUse--;
17004
Jim_DecrRefCount(interp, substObjPtr);
17005
if (*resObjPtrPtr == NULL) {
17006
return JIM_ERR;
17007
}
17008
return JIM_OK;
17009
}
17010
17011
void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
17012
{
17013
Jim_Obj *objPtr;
17014
Jim_Obj *listObjPtr;
17015
17016
JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
17017
17018
listObjPtr = Jim_NewListObj(interp, argv, argc);
17019
17020
if (msg && *msg) {
17021
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
17022
}
17023
Jim_IncrRefCount(listObjPtr);
17024
objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
17025
Jim_DecrRefCount(interp, listObjPtr);
17026
17027
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
17028
}
17029
17030
typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
17031
Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type);
17032
17033
#define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
17034
17035
static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
17036
JimHashtableIteratorCallbackType *callback, int type)
17037
{
17038
Jim_HashEntry *he;
17039
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
17040
17041
17042
if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
17043
he = Jim_FindHashEntry(ht, patternObjPtr);
17044
if (he) {
17045
callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
17046
patternObjPtr, type);
17047
}
17048
}
17049
else {
17050
Jim_HashTableIterator htiter;
17051
JimInitHashTableIterator(ht, &htiter);
17052
while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
17053
callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
17054
patternObjPtr, type);
17055
}
17056
}
17057
return listObjPtr;
17058
}
17059
17060
17061
#define JIM_CMDLIST_COMMANDS 0
17062
#define JIM_CMDLIST_PROCS 1
17063
#define JIM_CMDLIST_CHANNELS 2
17064
17065
static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
17066
Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
17067
{
17068
Jim_Cmd *cmdPtr = (Jim_Cmd *)value;
17069
17070
if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
17071
17072
return;
17073
}
17074
17075
Jim_IncrRefCount(keyObj);
17076
17077
if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) {
17078
int match = 1;
17079
if (patternObj) {
17080
int plen, slen;
17081
const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
17082
const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
17083
#ifdef JIM_NO_INTROSPECTION
17084
17085
match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
17086
#else
17087
match = JimGlobMatch(pattern, plen, str, slen, 0);
17088
#endif
17089
}
17090
if (match) {
17091
Jim_ListAppendElement(interp, listObjPtr, keyObj);
17092
}
17093
}
17094
Jim_DecrRefCount(interp, keyObj);
17095
}
17096
17097
static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
17098
{
17099
return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
17100
}
17101
17102
17103
#define JIM_VARLIST_GLOBALS 0
17104
#define JIM_VARLIST_LOCALS 1
17105
#define JIM_VARLIST_VARS 2
17106
#define JIM_VARLIST_MASK 0x000f
17107
17108
#define JIM_VARLIST_VALUES 0x1000
17109
17110
static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
17111
Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
17112
{
17113
Jim_VarVal *vv = (Jim_VarVal *)value;
17114
17115
if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) {
17116
if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) {
17117
Jim_ListAppendElement(interp, listObjPtr, keyObj);
17118
if (type & JIM_VARLIST_VALUES) {
17119
Jim_ListAppendElement(interp, listObjPtr, vv->objPtr);
17120
}
17121
}
17122
}
17123
}
17124
17125
17126
static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
17127
{
17128
if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
17129
return interp->emptyObj;
17130
}
17131
else {
17132
Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
17133
return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch,
17134
mode);
17135
}
17136
}
17137
17138
static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
17139
{
17140
long level;
17141
17142
if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
17143
Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level);
17144
if (targetCallFrame && targetCallFrame != interp->topFramePtr) {
17145
#ifdef JIM_NO_INTROSPECTION
17146
17147
*objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1);
17148
#else
17149
*objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
17150
#endif
17151
return JIM_OK;
17152
}
17153
}
17154
Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
17155
return JIM_ERR;
17156
}
17157
17158
static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
17159
{
17160
long level;
17161
17162
if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
17163
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level);
17164
if (frame) {
17165
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
17166
17167
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
17168
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1));
17169
if (frame->scriptObj) {
17170
ScriptObj *script = JimGetScript(interp, frame->scriptObj);
17171
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1));
17172
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr));
17173
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1));
17174
Jim_ListAppendElement(interp, listObj, script->fileNameObj);
17175
}
17176
#ifndef JIM_NO_INTROSPECTION
17177
{
17178
Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc);
17179
17180
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1));
17181
Jim_ListAppendElement(interp, listObj, cmdObj);
17182
}
17183
#endif
17184
{
17185
Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
17186
if (procNameObj) {
17187
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1));
17188
Jim_ListAppendElement(interp, listObj, procNameObj);
17189
}
17190
}
17191
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1));
17192
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level));
17193
17194
*objPtrPtr = listObj;
17195
return JIM_OK;
17196
}
17197
}
17198
Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
17199
return JIM_ERR;
17200
}
17201
17202
17203
static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17204
{
17205
if (argc != 2 && argc != 3) {
17206
Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
17207
return JIM_ERR;
17208
}
17209
if (argc == 3) {
17210
if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
17211
Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
17212
return JIM_ERR;
17213
}
17214
else {
17215
fputs(Jim_String(argv[2]), stdout);
17216
}
17217
}
17218
else {
17219
puts(Jim_String(argv[1]));
17220
}
17221
return JIM_OK;
17222
}
17223
17224
17225
static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
17226
{
17227
jim_wide wideValue, res;
17228
double doubleValue, doubleRes;
17229
int i;
17230
17231
res = (op == JIM_EXPROP_ADD) ? 0 : 1;
17232
17233
for (i = 1; i < argc; i++) {
17234
if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
17235
goto trydouble;
17236
if (op == JIM_EXPROP_ADD)
17237
res += wideValue;
17238
else
17239
res *= wideValue;
17240
}
17241
Jim_SetResultInt(interp, res);
17242
return JIM_OK;
17243
trydouble:
17244
doubleRes = (double)res;
17245
for (; i < argc; i++) {
17246
if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
17247
return JIM_ERR;
17248
if (op == JIM_EXPROP_ADD)
17249
doubleRes += doubleValue;
17250
else
17251
doubleRes *= doubleValue;
17252
}
17253
Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17254
return JIM_OK;
17255
}
17256
17257
17258
static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
17259
{
17260
jim_wide wideValue, res = 0;
17261
double doubleValue, doubleRes = 0;
17262
int i = 2;
17263
17264
if (argc < 2) {
17265
Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
17266
return JIM_ERR;
17267
}
17268
else if (argc == 2) {
17269
if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
17270
if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
17271
return JIM_ERR;
17272
}
17273
else {
17274
if (op == JIM_EXPROP_SUB)
17275
doubleRes = -doubleValue;
17276
else
17277
doubleRes = 1.0 / doubleValue;
17278
Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17279
return JIM_OK;
17280
}
17281
}
17282
if (op == JIM_EXPROP_SUB) {
17283
res = -wideValue;
17284
Jim_SetResultInt(interp, res);
17285
}
17286
else {
17287
doubleRes = 1.0 / wideValue;
17288
Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17289
}
17290
return JIM_OK;
17291
}
17292
else {
17293
if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
17294
if (Jim_GetDouble(interp, argv[1], &doubleRes)
17295
!= JIM_OK) {
17296
return JIM_ERR;
17297
}
17298
else {
17299
goto trydouble;
17300
}
17301
}
17302
}
17303
for (i = 2; i < argc; i++) {
17304
if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
17305
doubleRes = (double)res;
17306
goto trydouble;
17307
}
17308
if (op == JIM_EXPROP_SUB)
17309
res -= wideValue;
17310
else {
17311
if (wideValue == 0) {
17312
Jim_SetResultString(interp, "Division by zero", -1);
17313
return JIM_ERR;
17314
}
17315
res /= wideValue;
17316
}
17317
}
17318
Jim_SetResultInt(interp, res);
17319
return JIM_OK;
17320
trydouble:
17321
for (; i < argc; i++) {
17322
if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
17323
return JIM_ERR;
17324
if (op == JIM_EXPROP_SUB)
17325
doubleRes -= doubleValue;
17326
else
17327
doubleRes /= doubleValue;
17328
}
17329
Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17330
return JIM_OK;
17331
}
17332
17333
17334
17335
static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17336
{
17337
return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
17338
}
17339
17340
17341
static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17342
{
17343
return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
17344
}
17345
17346
17347
static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17348
{
17349
return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
17350
}
17351
17352
17353
static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17354
{
17355
return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
17356
}
17357
17358
17359
static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17360
{
17361
if (argc != 2 && argc != 3) {
17362
Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
17363
return JIM_ERR;
17364
}
17365
if (argc == 2) {
17366
Jim_Obj *objPtr;
17367
17368
objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17369
if (!objPtr)
17370
return JIM_ERR;
17371
Jim_SetResult(interp, objPtr);
17372
return JIM_OK;
17373
}
17374
17375
if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17376
return JIM_ERR;
17377
Jim_SetResult(interp, argv[2]);
17378
return JIM_OK;
17379
}
17380
17381
static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17382
{
17383
int i = 1;
17384
int complain = 1;
17385
17386
while (i < argc) {
17387
if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
17388
i++;
17389
break;
17390
}
17391
if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
17392
complain = 0;
17393
i++;
17394
continue;
17395
}
17396
break;
17397
}
17398
17399
while (i < argc) {
17400
if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
17401
&& complain) {
17402
return JIM_ERR;
17403
}
17404
i++;
17405
}
17406
17407
Jim_SetEmptyResult(interp);
17408
return JIM_OK;
17409
}
17410
17411
static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
17412
{
17413
if (retval == JIM_BREAK || retval == JIM_CONTINUE) {
17414
if (--interp->break_level > 0) {
17415
return 1;
17416
}
17417
}
17418
return 0;
17419
}
17420
17421
17422
static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17423
{
17424
if (argc != 3) {
17425
Jim_WrongNumArgs(interp, 1, argv, "condition body");
17426
return JIM_ERR;
17427
}
17428
17429
17430
while (1) {
17431
int boolean = 0, retval;
17432
17433
if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
17434
return retval;
17435
if (!boolean)
17436
break;
17437
17438
if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
17439
if (JimCheckLoopRetcode(interp, retval)) {
17440
return retval;
17441
}
17442
switch (retval) {
17443
case JIM_BREAK:
17444
goto out;
17445
case JIM_CONTINUE:
17446
continue;
17447
default:
17448
return retval;
17449
}
17450
}
17451
}
17452
out:
17453
Jim_SetEmptyResult(interp);
17454
return JIM_OK;
17455
}
17456
17457
17458
static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17459
{
17460
int retval;
17461
int boolean = 1;
17462
int immediate = 0;
17463
Jim_Obj *varNamePtr = NULL;
17464
Jim_Obj *stopVarNamePtr = NULL;
17465
17466
if (argc != 5) {
17467
Jim_WrongNumArgs(interp, 1, argv, "start test next body");
17468
return JIM_ERR;
17469
}
17470
17471
17472
if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
17473
return retval;
17474
}
17475
17476
retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
17477
17478
17479
#ifdef JIM_OPTIMIZATION
17480
if (retval == JIM_OK && boolean) {
17481
ScriptObj *incrScript;
17482
struct ExprTree *expr;
17483
jim_wide stop, currentVal;
17484
Jim_Obj *objPtr;
17485
int cmpOffset;
17486
17487
17488
expr = JimGetExpression(interp, argv[2]);
17489
incrScript = JimGetScript(interp, argv[3]);
17490
17491
17492
if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
17493
goto evalstart;
17494
}
17495
17496
if (incrScript->token[1].type != JIM_TT_ESC) {
17497
goto evalstart;
17498
}
17499
17500
if (expr->expr->type == JIM_EXPROP_LT) {
17501
cmpOffset = 0;
17502
}
17503
else if (expr->expr->type == JIM_EXPROP_LTE) {
17504
cmpOffset = 1;
17505
}
17506
else {
17507
goto evalstart;
17508
}
17509
17510
if (expr->expr->left->type != JIM_TT_VAR) {
17511
goto evalstart;
17512
}
17513
17514
if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
17515
goto evalstart;
17516
}
17517
17518
17519
if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
17520
goto evalstart;
17521
}
17522
17523
17524
if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
17525
goto evalstart;
17526
}
17527
17528
17529
if (expr->expr->right->type == JIM_TT_EXPR_INT) {
17530
if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
17531
goto evalstart;
17532
}
17533
}
17534
else {
17535
stopVarNamePtr = expr->expr->right->objPtr;
17536
Jim_IncrRefCount(stopVarNamePtr);
17537
17538
stop = 0;
17539
}
17540
17541
17542
varNamePtr = expr->expr->left->objPtr;
17543
Jim_IncrRefCount(varNamePtr);
17544
17545
objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
17546
if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
17547
goto testcond;
17548
}
17549
17550
17551
while (retval == JIM_OK) {
17552
17553
17554
17555
17556
if (stopVarNamePtr) {
17557
objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
17558
if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
17559
goto testcond;
17560
}
17561
}
17562
17563
if (currentVal >= stop + cmpOffset) {
17564
break;
17565
}
17566
17567
17568
retval = Jim_EvalObj(interp, argv[4]);
17569
if (JimCheckLoopRetcode(interp, retval)) {
17570
immediate++;
17571
goto out;
17572
}
17573
if (retval == JIM_OK || retval == JIM_CONTINUE) {
17574
retval = JIM_OK;
17575
17576
objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
17577
17578
17579
if (objPtr == NULL) {
17580
retval = JIM_ERR;
17581
goto out;
17582
}
17583
if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
17584
currentVal = ++JimWideValue(objPtr);
17585
Jim_InvalidateStringRep(objPtr);
17586
}
17587
else {
17588
if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
17589
Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
17590
++currentVal)) != JIM_OK) {
17591
goto evalnext;
17592
}
17593
}
17594
}
17595
}
17596
goto out;
17597
}
17598
evalstart:
17599
#endif
17600
17601
while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
17602
17603
retval = Jim_EvalObj(interp, argv[4]);
17604
if (JimCheckLoopRetcode(interp, retval)) {
17605
immediate++;
17606
break;
17607
}
17608
if (retval == JIM_OK || retval == JIM_CONTINUE) {
17609
17610
JIM_IF_OPTIM(evalnext:)
17611
retval = Jim_EvalObj(interp, argv[3]);
17612
if (retval == JIM_OK || retval == JIM_CONTINUE) {
17613
17614
JIM_IF_OPTIM(testcond:)
17615
retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
17616
}
17617
}
17618
}
17619
JIM_IF_OPTIM(out:)
17620
if (stopVarNamePtr) {
17621
Jim_DecrRefCount(interp, stopVarNamePtr);
17622
}
17623
if (varNamePtr) {
17624
Jim_DecrRefCount(interp, varNamePtr);
17625
}
17626
17627
if (!immediate) {
17628
if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
17629
Jim_SetEmptyResult(interp);
17630
return JIM_OK;
17631
}
17632
}
17633
17634
return retval;
17635
}
17636
17637
17638
static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17639
{
17640
int retval;
17641
jim_wide i;
17642
jim_wide limit;
17643
jim_wide incr = 1;
17644
Jim_Obj *bodyObjPtr;
17645
17646
if (argc < 4 || argc > 6) {
17647
Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body");
17648
return JIM_ERR;
17649
}
17650
17651
retval = Jim_GetWideExpr(interp, argv[2], &i);
17652
if (argc > 4 && retval == JIM_OK) {
17653
retval = Jim_GetWideExpr(interp, argv[3], &limit);
17654
}
17655
if (argc > 5 && retval == JIM_OK) {
17656
Jim_GetWideExpr(interp, argv[4], &incr);
17657
}
17658
if (retval != JIM_OK) {
17659
return retval;
17660
}
17661
if (argc == 4) {
17662
limit = i;
17663
i = 0;
17664
}
17665
bodyObjPtr = argv[argc - 1];
17666
17667
retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i));
17668
17669
while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
17670
retval = Jim_EvalObj(interp, bodyObjPtr);
17671
if (JimCheckLoopRetcode(interp, retval)) {
17672
return retval;
17673
}
17674
if (retval == JIM_OK || retval == JIM_CONTINUE) {
17675
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17676
17677
retval = JIM_OK;
17678
17679
17680
i += incr;
17681
17682
if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
17683
if (argv[1]->typePtr != &variableObjType) {
17684
if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
17685
return JIM_ERR;
17686
}
17687
}
17688
JimWideValue(objPtr) = i;
17689
Jim_InvalidateStringRep(objPtr);
17690
17691
if (argv[1]->typePtr != &variableObjType) {
17692
if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
17693
retval = JIM_ERR;
17694
break;
17695
}
17696
}
17697
}
17698
else {
17699
objPtr = Jim_NewIntObj(interp, i);
17700
retval = Jim_SetVariable(interp, argv[1], objPtr);
17701
if (retval != JIM_OK) {
17702
Jim_FreeNewObj(interp, objPtr);
17703
}
17704
}
17705
}
17706
}
17707
17708
if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
17709
Jim_SetEmptyResult(interp);
17710
return JIM_OK;
17711
}
17712
return retval;
17713
}
17714
17715
typedef struct {
17716
Jim_Obj *objPtr;
17717
int idx;
17718
} Jim_ListIter;
17719
17720
static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
17721
{
17722
iter->objPtr = objPtr;
17723
iter->idx = 0;
17724
}
17725
17726
static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
17727
{
17728
if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
17729
return NULL;
17730
}
17731
return iter->objPtr->internalRep.listValue.ele[iter->idx++];
17732
}
17733
17734
static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
17735
{
17736
return iter->idx >= Jim_ListLength(interp, iter->objPtr);
17737
}
17738
17739
17740
static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
17741
{
17742
int result = JIM_OK;
17743
int i, numargs;
17744
Jim_ListIter twoiters[2];
17745
Jim_ListIter *iters;
17746
Jim_Obj *script;
17747
Jim_Obj *resultObj;
17748
17749
if (argc < 4 || argc % 2 != 0) {
17750
Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
17751
return JIM_ERR;
17752
}
17753
script = argv[argc - 1];
17754
numargs = (argc - 1 - 1);
17755
17756
if (numargs == 2) {
17757
iters = twoiters;
17758
}
17759
else {
17760
iters = Jim_Alloc(numargs * sizeof(*iters));
17761
}
17762
for (i = 0; i < numargs; i++) {
17763
JimListIterInit(&iters[i], argv[i + 1]);
17764
if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
17765
result = JIM_ERR;
17766
}
17767
}
17768
if (result != JIM_OK) {
17769
Jim_SetResultString(interp, "foreach varlist is empty", -1);
17770
goto empty_varlist;
17771
}
17772
17773
if (doMap) {
17774
resultObj = Jim_NewListObj(interp, NULL, 0);
17775
}
17776
else {
17777
resultObj = interp->emptyObj;
17778
}
17779
Jim_IncrRefCount(resultObj);
17780
17781
while (1) {
17782
17783
for (i = 0; i < numargs; i += 2) {
17784
if (!JimListIterDone(interp, &iters[i + 1])) {
17785
break;
17786
}
17787
}
17788
if (i == numargs) {
17789
17790
break;
17791
}
17792
17793
17794
for (i = 0; i < numargs; i += 2) {
17795
Jim_Obj *varName;
17796
17797
17798
JimListIterInit(&iters[i], argv[i + 1]);
17799
while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
17800
Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
17801
if (!valObj) {
17802
17803
valObj = interp->emptyObj;
17804
}
17805
17806
Jim_IncrRefCount(valObj);
17807
result = Jim_SetVariable(interp, varName, valObj);
17808
Jim_DecrRefCount(interp, valObj);
17809
if (result != JIM_OK) {
17810
goto err;
17811
}
17812
}
17813
}
17814
result = Jim_EvalObj(interp, script);
17815
if (JimCheckLoopRetcode(interp, result)) {
17816
goto err;
17817
}
17818
switch (result) {
17819
case JIM_OK:
17820
if (doMap) {
17821
Jim_ListAppendElement(interp, resultObj, interp->result);
17822
}
17823
break;
17824
case JIM_CONTINUE:
17825
break;
17826
case JIM_BREAK:
17827
goto out;
17828
default:
17829
goto err;
17830
}
17831
}
17832
out:
17833
result = JIM_OK;
17834
Jim_SetResult(interp, resultObj);
17835
err:
17836
Jim_DecrRefCount(interp, resultObj);
17837
empty_varlist:
17838
if (numargs > 2) {
17839
Jim_Free(iters);
17840
}
17841
return result;
17842
}
17843
17844
17845
static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17846
{
17847
return JimForeachMapHelper(interp, argc, argv, 0);
17848
}
17849
17850
17851
static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17852
{
17853
return JimForeachMapHelper(interp, argc, argv, 1);
17854
}
17855
17856
17857
static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17858
{
17859
int result = JIM_ERR;
17860
int i;
17861
Jim_ListIter iter;
17862
Jim_Obj *resultObj;
17863
17864
if (argc < 2) {
17865
Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
17866
return JIM_ERR;
17867
}
17868
17869
JimListIterInit(&iter, argv[1]);
17870
17871
for (i = 2; i < argc; i++) {
17872
Jim_Obj *valObj = JimListIterNext(interp, &iter);
17873
result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
17874
if (result != JIM_OK) {
17875
return result;
17876
}
17877
}
17878
17879
resultObj = Jim_NewListObj(interp, NULL, 0);
17880
while (!JimListIterDone(interp, &iter)) {
17881
Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
17882
}
17883
17884
Jim_SetResult(interp, resultObj);
17885
17886
return JIM_OK;
17887
}
17888
17889
17890
static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17891
{
17892
int boolean, retval, current = 1, falsebody = 0;
17893
17894
if (argc >= 3) {
17895
while (1) {
17896
17897
if (current >= argc)
17898
goto err;
17899
if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
17900
!= JIM_OK)
17901
return retval;
17902
17903
if (current >= argc)
17904
goto err;
17905
if (Jim_CompareStringImmediate(interp, argv[current], "then"))
17906
current++;
17907
17908
if (current >= argc)
17909
goto err;
17910
if (boolean)
17911
return Jim_EvalObj(interp, argv[current]);
17912
17913
if (++current >= argc) {
17914
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
17915
return JIM_OK;
17916
}
17917
falsebody = current++;
17918
if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
17919
17920
if (current != argc - 1)
17921
goto err;
17922
return Jim_EvalObj(interp, argv[current]);
17923
}
17924
else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
17925
continue;
17926
17927
else if (falsebody != argc - 1)
17928
goto err;
17929
return Jim_EvalObj(interp, argv[falsebody]);
17930
}
17931
return JIM_OK;
17932
}
17933
err:
17934
Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
17935
return JIM_ERR;
17936
}
17937
17938
17939
int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
17940
Jim_Obj *stringObj, int flags)
17941
{
17942
Jim_Obj *parms[5];
17943
int argc = 0;
17944
long eq;
17945
int rc;
17946
17947
parms[argc++] = commandObj;
17948
if (flags & JIM_NOCASE) {
17949
parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
17950
}
17951
if (flags & JIM_OPT_END) {
17952
parms[argc++] = Jim_NewStringObj(interp, "--", -1);
17953
}
17954
parms[argc++] = patternObj;
17955
parms[argc++] = stringObj;
17956
17957
rc = Jim_EvalObjVector(interp, argc, parms);
17958
17959
if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
17960
eq = -rc;
17961
}
17962
17963
return eq;
17964
}
17965
17966
17967
static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17968
{
17969
enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
17970
int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
17971
int match_flags = 0;
17972
Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
17973
Jim_Obj **caseList;
17974
17975
if (argc < 3) {
17976
wrongnumargs:
17977
Jim_WrongNumArgs(interp, 1, argv, "?options? string "
17978
"pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
17979
return JIM_ERR;
17980
}
17981
for (opt = 1; opt < argc; ++opt) {
17982
const char *option = Jim_String(argv[opt]);
17983
17984
if (*option != '-')
17985
break;
17986
else if (strncmp(option, "--", 2) == 0) {
17987
++opt;
17988
break;
17989
}
17990
else if (strncmp(option, "-exact", 2) == 0)
17991
matchOpt = SWITCH_EXACT;
17992
else if (strncmp(option, "-glob", 2) == 0)
17993
matchOpt = SWITCH_GLOB;
17994
else if (strncmp(option, "-regexp", 2) == 0) {
17995
matchOpt = SWITCH_RE;
17996
match_flags |= JIM_OPT_END;
17997
}
17998
else if (strncmp(option, "-command", 2) == 0) {
17999
matchOpt = SWITCH_CMD;
18000
if ((argc - opt) < 2)
18001
goto wrongnumargs;
18002
command = argv[++opt];
18003
}
18004
else {
18005
Jim_SetResultFormatted(interp,
18006
"bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
18007
argv[opt]);
18008
return JIM_ERR;
18009
}
18010
if ((argc - opt) < 2)
18011
goto wrongnumargs;
18012
}
18013
strObj = argv[opt++];
18014
patCount = argc - opt;
18015
if (patCount == 1) {
18016
JimListGetElements(interp, argv[opt], &patCount, &caseList);
18017
}
18018
else
18019
caseList = (Jim_Obj **)&argv[opt];
18020
if (patCount == 0 || patCount % 2 != 0)
18021
goto wrongnumargs;
18022
for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
18023
Jim_Obj *patObj = caseList[i];
18024
18025
if (!Jim_CompareStringImmediate(interp, patObj, "default")
18026
|| i < (patCount - 2)) {
18027
switch (matchOpt) {
18028
case SWITCH_EXACT:
18029
if (Jim_StringEqObj(strObj, patObj))
18030
scriptObj = caseList[i + 1];
18031
break;
18032
case SWITCH_GLOB:
18033
if (Jim_StringMatchObj(interp, patObj, strObj, 0))
18034
scriptObj = caseList[i + 1];
18035
break;
18036
case SWITCH_RE:
18037
command = Jim_NewStringObj(interp, "regexp", -1);
18038
18039
case SWITCH_CMD:{
18040
int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags);
18041
18042
if (argc - opt == 1) {
18043
JimListGetElements(interp, argv[opt], &patCount, &caseList);
18044
}
18045
18046
if (rc < 0) {
18047
return -rc;
18048
}
18049
if (rc)
18050
scriptObj = caseList[i + 1];
18051
break;
18052
}
18053
}
18054
}
18055
else {
18056
scriptObj = caseList[i + 1];
18057
}
18058
}
18059
for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
18060
scriptObj = caseList[i + 1];
18061
if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
18062
Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
18063
return JIM_ERR;
18064
}
18065
Jim_SetEmptyResult(interp);
18066
if (scriptObj) {
18067
return Jim_EvalObj(interp, scriptObj);
18068
}
18069
return JIM_OK;
18070
}
18071
18072
18073
static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18074
{
18075
Jim_Obj *listObjPtr;
18076
18077
listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
18078
Jim_SetResult(interp, listObjPtr);
18079
return JIM_OK;
18080
}
18081
18082
18083
static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18084
{
18085
Jim_Obj *objPtr;
18086
int ret;
18087
18088
if (argc < 2) {
18089
Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
18090
return JIM_ERR;
18091
}
18092
ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
18093
if (ret < 0) {
18094
ret = JIM_OK;
18095
Jim_SetEmptyResult(interp);
18096
}
18097
else if (ret == JIM_OK) {
18098
Jim_SetResult(interp, objPtr);
18099
}
18100
return ret;
18101
}
18102
18103
18104
static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18105
{
18106
if (argc != 2) {
18107
Jim_WrongNumArgs(interp, 1, argv, "list");
18108
return JIM_ERR;
18109
}
18110
Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
18111
return JIM_OK;
18112
}
18113
18114
18115
static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18116
{
18117
static const char * const options[] = {
18118
"-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
18119
"-stride", "-index", NULL
18120
};
18121
enum
18122
{ OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
18123
OPT_COMMAND, OPT_STRIDE, OPT_INDEX };
18124
int i;
18125
int opt_bool = 0;
18126
int opt_not = 0;
18127
int opt_all = 0;
18128
int opt_inline = 0;
18129
int opt_match = OPT_EXACT;
18130
int listlen;
18131
int rc = JIM_OK;
18132
Jim_Obj *listObjPtr = NULL;
18133
Jim_Obj *commandObj = NULL;
18134
Jim_Obj *indexObj = NULL;
18135
int match_flags = 0;
18136
long stride = 1;
18137
18138
if (argc < 3) {
18139
wrongargs:
18140
Jim_WrongNumArgs(interp, 1, argv,
18141
"?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
18142
return JIM_ERR;
18143
}
18144
18145
for (i = 1; i < argc - 2; i++) {
18146
int option;
18147
18148
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
18149
return JIM_ERR;
18150
}
18151
switch (option) {
18152
case OPT_BOOL:
18153
opt_bool = 1;
18154
opt_inline = 0;
18155
break;
18156
case OPT_NOT:
18157
opt_not = 1;
18158
break;
18159
case OPT_NOCASE:
18160
match_flags |= JIM_NOCASE;
18161
break;
18162
case OPT_INLINE:
18163
opt_inline = 1;
18164
opt_bool = 0;
18165
break;
18166
case OPT_ALL:
18167
opt_all = 1;
18168
break;
18169
case OPT_REGEXP:
18170
opt_match = option;
18171
match_flags |= JIM_OPT_END;
18172
break;
18173
case OPT_COMMAND:
18174
if (i >= argc - 2) {
18175
goto wrongargs;
18176
}
18177
commandObj = argv[++i];
18178
18179
case OPT_EXACT:
18180
case OPT_GLOB:
18181
opt_match = option;
18182
break;
18183
case OPT_INDEX:
18184
if (i >= argc - 2) {
18185
goto wrongargs;
18186
}
18187
indexObj = argv[++i];
18188
break;
18189
case OPT_STRIDE:
18190
if (i >= argc - 2) {
18191
goto wrongargs;
18192
}
18193
if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
18194
return JIM_ERR;
18195
}
18196
if (stride < 1) {
18197
Jim_SetResultString(interp, "stride length must be at least 1", -1);
18198
return JIM_ERR;
18199
}
18200
break;
18201
}
18202
}
18203
18204
argc -= i;
18205
if (argc < 2) {
18206
goto wrongargs;
18207
}
18208
argv += i;
18209
18210
listlen = Jim_ListLength(interp, argv[0]);
18211
if (listlen % stride) {
18212
Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
18213
return JIM_ERR;
18214
}
18215
18216
if (opt_all) {
18217
listObjPtr = Jim_NewListObj(interp, NULL, 0);
18218
}
18219
if (opt_match == OPT_REGEXP) {
18220
commandObj = Jim_NewStringObj(interp, "regexp", -1);
18221
}
18222
if (commandObj) {
18223
Jim_IncrRefCount(commandObj);
18224
}
18225
18226
for (i = 0; i < listlen; i += stride) {
18227
int eq = 0;
18228
Jim_Obj *searchListObj;
18229
Jim_Obj *objPtr;
18230
int offset;
18231
18232
if (indexObj) {
18233
int indexlen = Jim_ListLength(interp, indexObj);
18234
if (stride == 1) {
18235
searchListObj = Jim_ListGetIndex(interp, argv[0], i);
18236
}
18237
else {
18238
searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride);
18239
}
18240
Jim_IncrRefCount(searchListObj);
18241
rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG);
18242
if (rc != JIM_OK) {
18243
Jim_DecrRefCount(interp, searchListObj);
18244
rc = JIM_ERR;
18245
goto done;
18246
}
18247
18248
offset = 0;
18249
}
18250
else {
18251
18252
searchListObj = argv[0];
18253
offset = i;
18254
objPtr = Jim_ListGetIndex(interp, searchListObj, i);
18255
Jim_IncrRefCount(searchListObj);
18256
}
18257
18258
switch (opt_match) {
18259
case OPT_EXACT:
18260
eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0;
18261
break;
18262
18263
case OPT_GLOB:
18264
eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags);
18265
break;
18266
18267
case OPT_REGEXP:
18268
case OPT_COMMAND:
18269
eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags);
18270
if (eq < 0) {
18271
Jim_DecrRefCount(interp, searchListObj);
18272
rc = JIM_ERR;
18273
goto done;
18274
}
18275
break;
18276
}
18277
18278
18279
if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
18280
Jim_Obj *resultObj;
18281
18282
if (opt_bool) {
18283
resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
18284
}
18285
else if (!opt_inline) {
18286
resultObj = Jim_NewIntObj(interp, i);
18287
}
18288
else if (stride == 1) {
18289
resultObj = objPtr;
18290
}
18291
else if (opt_all) {
18292
18293
ListInsertElements(listObjPtr, -1, stride,
18294
searchListObj->internalRep.listValue.ele + offset);
18295
18296
resultObj = NULL;
18297
}
18298
else {
18299
resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride);
18300
}
18301
18302
if (opt_all) {
18303
18304
if (stride == 1) {
18305
Jim_ListAppendElement(interp, listObjPtr, resultObj);
18306
}
18307
}
18308
else {
18309
Jim_SetResult(interp, resultObj);
18310
Jim_DecrRefCount(interp, searchListObj);
18311
goto done;
18312
}
18313
}
18314
Jim_DecrRefCount(interp, searchListObj);
18315
}
18316
18317
if (opt_all) {
18318
Jim_SetResult(interp, listObjPtr);
18319
listObjPtr = NULL;
18320
}
18321
else {
18322
18323
if (opt_bool) {
18324
Jim_SetResultBool(interp, opt_not);
18325
}
18326
else if (!opt_inline) {
18327
Jim_SetResultInt(interp, -1);
18328
}
18329
}
18330
18331
done:
18332
if (listObjPtr) {
18333
Jim_FreeNewObj(interp, listObjPtr);
18334
}
18335
if (commandObj) {
18336
Jim_DecrRefCount(interp, commandObj);
18337
}
18338
return rc;
18339
}
18340
18341
18342
static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18343
{
18344
Jim_Obj *listObjPtr;
18345
int new_obj = 0;
18346
int i;
18347
18348
if (argc < 2) {
18349
Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
18350
return JIM_ERR;
18351
}
18352
listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
18353
if (!listObjPtr) {
18354
18355
listObjPtr = Jim_NewListObj(interp, NULL, 0);
18356
new_obj = 1;
18357
}
18358
else if (Jim_IsShared(listObjPtr)) {
18359
listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
18360
new_obj = 1;
18361
}
18362
for (i = 2; i < argc; i++)
18363
Jim_ListAppendElement(interp, listObjPtr, argv[i]);
18364
if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
18365
if (new_obj)
18366
Jim_FreeNewObj(interp, listObjPtr);
18367
return JIM_ERR;
18368
}
18369
Jim_SetResult(interp, listObjPtr);
18370
return JIM_OK;
18371
}
18372
18373
18374
static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18375
{
18376
int idx, len;
18377
Jim_Obj *listPtr;
18378
18379
if (argc < 3) {
18380
Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
18381
return JIM_ERR;
18382
}
18383
listPtr = argv[1];
18384
if (Jim_IsShared(listPtr))
18385
listPtr = Jim_DuplicateObj(interp, listPtr);
18386
if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
18387
goto err;
18388
len = Jim_ListLength(interp, listPtr);
18389
if (idx >= len)
18390
idx = len;
18391
else if (idx < 0)
18392
idx = len + idx + 1;
18393
Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
18394
Jim_SetResult(interp, listPtr);
18395
return JIM_OK;
18396
err:
18397
if (listPtr != argv[1]) {
18398
Jim_FreeNewObj(interp, listPtr);
18399
}
18400
return JIM_ERR;
18401
}
18402
18403
18404
static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18405
{
18406
int first, last, len, rangeLen;
18407
Jim_Obj *listObj;
18408
Jim_Obj *newListObj;
18409
18410
if (argc < 4) {
18411
Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
18412
return JIM_ERR;
18413
}
18414
if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
18415
Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
18416
return JIM_ERR;
18417
}
18418
18419
listObj = argv[1];
18420
len = Jim_ListLength(interp, listObj);
18421
18422
first = JimRelToAbsIndex(len, first);
18423
last = JimRelToAbsIndex(len, last);
18424
JimRelToAbsRange(len, &first, &last, &rangeLen);
18425
18426
18427
if (first > len) {
18428
first = len;
18429
}
18430
18431
18432
newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
18433
18434
18435
ListInsertElements(newListObj, -1, argc - 4, argv + 4);
18436
18437
18438
ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
18439
18440
Jim_SetResult(interp, newListObj);
18441
return JIM_OK;
18442
}
18443
18444
18445
static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18446
{
18447
if (argc < 3) {
18448
Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value");
18449
return JIM_ERR;
18450
}
18451
else if (argc == 3) {
18452
18453
if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
18454
return JIM_ERR;
18455
Jim_SetResult(interp, argv[2]);
18456
return JIM_OK;
18457
}
18458
return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
18459
}
18460
18461
18462
static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
18463
{
18464
static const char * const options[] = {
18465
"-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique",
18466
"-stride", "-dictionary", NULL
18467
};
18468
enum {
18469
OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE,
18470
OPT_STRIDE, OPT_DICT
18471
};
18472
Jim_Obj *resObj;
18473
int i;
18474
int retCode;
18475
int shared;
18476
long stride = 1;
18477
Jim_Obj **elements;
18478
int listlen;
18479
18480
struct lsort_info info;
18481
18482
if (argc < 2) {
18483
wrongargs:
18484
Jim_WrongNumArgs(interp, 1, argv, "?options? list");
18485
return JIM_ERR;
18486
}
18487
18488
info.type = JIM_LSORT_ASCII;
18489
info.order = 1;
18490
info.indexc = 0;
18491
info.unique = 0;
18492
info.command = NULL;
18493
info.interp = interp;
18494
18495
for (i = 1; i < (argc - 1); i++) {
18496
int option;
18497
18498
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
18499
!= JIM_OK)
18500
return JIM_ERR;
18501
switch (option) {
18502
case OPT_ASCII:
18503
info.type = JIM_LSORT_ASCII;
18504
break;
18505
case OPT_DICT:
18506
info.type = JIM_LSORT_DICT;
18507
break;
18508
case OPT_NOCASE:
18509
info.type = JIM_LSORT_NOCASE;
18510
break;
18511
case OPT_INTEGER:
18512
info.type = JIM_LSORT_INTEGER;
18513
break;
18514
case OPT_REAL:
18515
info.type = JIM_LSORT_REAL;
18516
break;
18517
case OPT_INCREASING:
18518
info.order = 1;
18519
break;
18520
case OPT_DECREASING:
18521
info.order = -1;
18522
break;
18523
case OPT_UNIQUE:
18524
info.unique = 1;
18525
break;
18526
case OPT_COMMAND:
18527
if (i >= (argc - 2)) {
18528
Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
18529
return JIM_ERR;
18530
}
18531
info.type = JIM_LSORT_COMMAND;
18532
info.command = argv[i + 1];
18533
i++;
18534
break;
18535
case OPT_STRIDE:
18536
if (i >= argc - 2) {
18537
goto wrongargs;
18538
}
18539
if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
18540
return JIM_ERR;
18541
}
18542
if (stride < 2) {
18543
Jim_SetResultString(interp, "stride length must be at least 2", -1);
18544
return JIM_ERR;
18545
}
18546
break;
18547
case OPT_INDEX:
18548
if (i >= (argc - 2)) {
18549
badindex:
18550
Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
18551
return JIM_ERR;
18552
}
18553
JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv);
18554
if (info.indexc == 0) {
18555
goto badindex;
18556
}
18557
i++;
18558
break;
18559
}
18560
}
18561
resObj = argv[argc - 1];
18562
JimListGetElements(interp, resObj, &listlen, &elements);
18563
if (listlen <= 1) {
18564
18565
Jim_SetResult(interp, resObj);
18566
return JIM_OK;
18567
}
18568
18569
if (stride > 1) {
18570
Jim_Obj *tmpListObj;
18571
int i;
18572
18573
if (listlen % stride) {
18574
Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
18575
return JIM_ERR;
18576
}
18577
18578
tmpListObj = Jim_NewListObj(interp, NULL, 0);
18579
Jim_IncrRefCount(tmpListObj);
18580
for (i = 0; i < listlen; i += stride) {
18581
Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride));
18582
}
18583
retCode = ListSortElements(interp, tmpListObj, &info);
18584
if (retCode == JIM_OK) {
18585
resObj = Jim_NewListObj(interp, NULL, 0);
18586
18587
for (i = 0; i < listlen; i += stride) {
18588
Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride));
18589
}
18590
Jim_SetResult(interp, resObj);
18591
}
18592
Jim_DecrRefCount(interp, tmpListObj);
18593
}
18594
else {
18595
if ((shared = Jim_IsShared(resObj))) {
18596
resObj = Jim_DuplicateObj(interp, resObj);
18597
}
18598
retCode = ListSortElements(interp, resObj, &info);
18599
if (retCode == JIM_OK) {
18600
Jim_SetResult(interp, resObj);
18601
}
18602
else if (shared) {
18603
Jim_FreeNewObj(interp, resObj);
18604
}
18605
}
18606
return retCode;
18607
}
18608
18609
18610
static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18611
{
18612
Jim_Obj *stringObjPtr;
18613
int i;
18614
18615
if (argc < 2) {
18616
Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
18617
return JIM_ERR;
18618
}
18619
if (argc == 2) {
18620
stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
18621
if (!stringObjPtr)
18622
return JIM_ERR;
18623
}
18624
else {
18625
int new_obj = 0;
18626
stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
18627
if (!stringObjPtr) {
18628
18629
stringObjPtr = Jim_NewEmptyStringObj(interp);
18630
new_obj = 1;
18631
}
18632
else if (Jim_IsShared(stringObjPtr)) {
18633
new_obj = 1;
18634
stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
18635
}
18636
for (i = 2; i < argc; i++) {
18637
Jim_AppendObj(interp, stringObjPtr, argv[i]);
18638
}
18639
if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
18640
if (new_obj) {
18641
Jim_FreeNewObj(interp, stringObjPtr);
18642
}
18643
return JIM_ERR;
18644
}
18645
}
18646
Jim_SetResult(interp, stringObjPtr);
18647
return JIM_OK;
18648
}
18649
18650
18651
18652
18653
18654
static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18655
{
18656
int rc;
18657
18658
if (argc < 2) {
18659
Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
18660
return JIM_ERR;
18661
}
18662
18663
if (argc == 2) {
18664
rc = Jim_EvalObj(interp, argv[1]);
18665
}
18666
else {
18667
rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
18668
}
18669
18670
return rc;
18671
}
18672
18673
18674
static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18675
{
18676
if (argc >= 2) {
18677
int retcode;
18678
Jim_CallFrame *savedCallFrame, *targetCallFrame;
18679
const char *str;
18680
18681
18682
savedCallFrame = interp->framePtr;
18683
18684
18685
str = Jim_String(argv[1]);
18686
if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
18687
targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
18688
argc--;
18689
argv++;
18690
}
18691
else {
18692
targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
18693
}
18694
if (targetCallFrame == NULL) {
18695
return JIM_ERR;
18696
}
18697
if (argc < 2) {
18698
Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
18699
return JIM_ERR;
18700
}
18701
18702
interp->framePtr = targetCallFrame;
18703
if (argc == 2) {
18704
retcode = Jim_EvalObj(interp, argv[1]);
18705
}
18706
else {
18707
retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
18708
}
18709
interp->framePtr = savedCallFrame;
18710
return retcode;
18711
}
18712
else {
18713
Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
18714
return JIM_ERR;
18715
}
18716
}
18717
18718
18719
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18720
{
18721
int retcode;
18722
18723
if (argc == 2) {
18724
retcode = Jim_EvalExpression(interp, argv[1]);
18725
}
18726
#ifndef JIM_COMPAT
18727
else {
18728
Jim_WrongNumArgs(interp, 1, argv, "expression");
18729
retcode = JIM_ERR;
18730
}
18731
#else
18732
else if (argc > 2) {
18733
Jim_Obj *objPtr;
18734
18735
objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
18736
Jim_IncrRefCount(objPtr);
18737
retcode = Jim_EvalExpression(interp, objPtr);
18738
Jim_DecrRefCount(interp, objPtr);
18739
}
18740
else {
18741
Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
18742
return JIM_ERR;
18743
}
18744
#endif
18745
return retcode;
18746
}
18747
18748
static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
18749
{
18750
if (argc != 1 && argc != 2) {
18751
Jim_WrongNumArgs(interp, 1, argv, "?level?");
18752
return JIM_ERR;
18753
}
18754
if (argc == 2) {
18755
long level;
18756
int ret = Jim_GetLong(interp, argv[1], &level);
18757
if (ret != JIM_OK) {
18758
return ret;
18759
}
18760
interp->break_level = level;
18761
}
18762
return retcode;
18763
}
18764
18765
18766
static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18767
{
18768
return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK);
18769
}
18770
18771
18772
static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18773
{
18774
return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE);
18775
}
18776
18777
18778
static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18779
{
18780
Jim_Obj *listObj;
18781
int i;
18782
jim_wide skip = 0;
18783
jim_wide last = 0;
18784
18785
if (argc > 1) {
18786
if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) {
18787
return JIM_ERR;
18788
}
18789
}
18790
if (argc > 2) {
18791
if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) {
18792
return JIM_ERR;
18793
}
18794
}
18795
18796
listObj = Jim_NewListObj(interp, NULL, 0);
18797
for (i = skip; i <= interp->procLevel; i++) {
18798
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
18799
if (frame->procLevel < last) {
18800
break;
18801
}
18802
JimAddStackFrame(interp, frame, listObj);
18803
}
18804
Jim_SetResult(interp, listObj);
18805
return JIM_OK;
18806
}
18807
18808
18809
static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18810
{
18811
int i;
18812
Jim_Obj *stackTraceObj = NULL;
18813
Jim_Obj *errorCodeObj = NULL;
18814
int returnCode = JIM_OK;
18815
long level = 1;
18816
18817
for (i = 1; i < argc - 1; i += 2) {
18818
if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
18819
if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
18820
return JIM_ERR;
18821
}
18822
}
18823
else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
18824
stackTraceObj = argv[i + 1];
18825
}
18826
else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
18827
errorCodeObj = argv[i + 1];
18828
}
18829
else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
18830
if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
18831
Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
18832
return JIM_ERR;
18833
}
18834
}
18835
else {
18836
break;
18837
}
18838
}
18839
18840
if (i != argc - 1 && i != argc) {
18841
Jim_WrongNumArgs(interp, 1, argv,
18842
"?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
18843
}
18844
18845
18846
if (stackTraceObj && returnCode == JIM_ERR) {
18847
JimSetStackTrace(interp, stackTraceObj);
18848
}
18849
18850
if (errorCodeObj && returnCode == JIM_ERR) {
18851
Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
18852
}
18853
interp->returnCode = returnCode;
18854
interp->returnLevel = level;
18855
18856
if (i == argc - 1) {
18857
Jim_SetResult(interp, argv[i]);
18858
}
18859
return level == 0 ? returnCode : JIM_RETURN;
18860
}
18861
18862
18863
static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18864
{
18865
if (interp->framePtr->level == 0) {
18866
Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
18867
return JIM_ERR;
18868
}
18869
else if (argc >= 2) {
18870
18871
Jim_CallFrame *cf = interp->framePtr->parent;
18872
18873
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
18874
if (cmdPtr == NULL) {
18875
return JIM_ERR;
18876
}
18877
18878
JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
18879
18880
18881
JimIncrCmdRefCount(cmdPtr);
18882
cf->tailcallCmd = cmdPtr;
18883
18884
18885
JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
18886
18887
cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
18888
Jim_IncrRefCount(cf->tailcallObj);
18889
18890
18891
return JIM_EVAL;
18892
}
18893
return JIM_OK;
18894
}
18895
18896
static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18897
{
18898
Jim_Obj *cmdList;
18899
Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
18900
18901
18902
cmdList = Jim_DuplicateObj(interp, prefixListObj);
18903
Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
18904
18905
return JimEvalObjList(interp, cmdList);
18906
}
18907
18908
static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
18909
{
18910
Jim_Obj *prefixListObj = privData;
18911
Jim_DecrRefCount(interp, prefixListObj);
18912
}
18913
18914
static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18915
{
18916
Jim_Obj *prefixListObj;
18917
18918
if (argc < 3) {
18919
Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
18920
return JIM_ERR;
18921
}
18922
18923
prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
18924
Jim_IncrRefCount(prefixListObj);
18925
Jim_SetResult(interp, argv[1]);
18926
18927
return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete);
18928
}
18929
18930
18931
static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18932
{
18933
Jim_Cmd *cmd;
18934
18935
if (argc != 4 && argc != 5) {
18936
Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
18937
return JIM_ERR;
18938
}
18939
18940
if (argc == 4) {
18941
cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
18942
}
18943
else {
18944
cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
18945
}
18946
18947
if (cmd) {
18948
18949
Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]);
18950
JimCreateCommand(interp, nameObjPtr, cmd);
18951
18952
18953
JimUpdateProcNamespace(interp, cmd, nameObjPtr);
18954
Jim_DecrRefCount(interp, nameObjPtr);
18955
18956
18957
Jim_SetResult(interp, argv[1]);
18958
return JIM_OK;
18959
}
18960
return JIM_ERR;
18961
}
18962
18963
18964
static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18965
{
18966
if (argc != 2) {
18967
Jim_WrongNumArgs(interp, 1, argv, "callback");
18968
return JIM_ERR;
18969
}
18970
18971
if (interp->traceCmdObj) {
18972
Jim_DecrRefCount(interp, interp->traceCmdObj);
18973
interp->traceCmdObj = NULL;
18974
}
18975
18976
if (Jim_Length(argv[1])) {
18977
18978
interp->traceCmdObj = argv[1];
18979
Jim_IncrRefCount(interp->traceCmdObj);
18980
}
18981
return JIM_OK;
18982
}
18983
18984
18985
static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18986
{
18987
int retcode;
18988
18989
if (argc < 2) {
18990
Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
18991
return JIM_ERR;
18992
}
18993
18994
18995
interp->local++;
18996
retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
18997
interp->local--;
18998
18999
19000
19001
if (retcode == 0) {
19002
Jim_Obj *cmdNameObj = Jim_GetResult(interp);
19003
19004
if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
19005
return JIM_ERR;
19006
}
19007
if (interp->framePtr->localCommands == NULL) {
19008
interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
19009
Jim_InitStack(interp->framePtr->localCommands);
19010
}
19011
Jim_IncrRefCount(cmdNameObj);
19012
Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
19013
}
19014
19015
return retcode;
19016
}
19017
19018
19019
static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19020
{
19021
if (argc < 2) {
19022
Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
19023
return JIM_ERR;
19024
}
19025
else {
19026
int retcode;
19027
19028
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
19029
if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
19030
Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
19031
return JIM_ERR;
19032
}
19033
19034
cmdPtr->u.proc.upcall++;
19035
JimIncrCmdRefCount(cmdPtr);
19036
19037
19038
retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
19039
19040
19041
cmdPtr->u.proc.upcall--;
19042
JimDecrCmdRefCount(interp, cmdPtr);
19043
19044
return retcode;
19045
}
19046
}
19047
19048
19049
static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19050
{
19051
if (argc < 2) {
19052
Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
19053
return JIM_ERR;
19054
}
19055
else {
19056
int ret;
19057
Jim_Cmd *cmd;
19058
Jim_Obj *argListObjPtr;
19059
Jim_Obj *bodyObjPtr;
19060
Jim_Obj *nsObj = NULL;
19061
Jim_Obj **nargv;
19062
19063
int len = Jim_ListLength(interp, argv[1]);
19064
if (len != 2 && len != 3) {
19065
Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
19066
return JIM_ERR;
19067
}
19068
19069
if (len == 3) {
19070
#ifdef jim_ext_namespace
19071
19072
nsObj = Jim_ListGetIndex(interp, argv[1], 2);
19073
#else
19074
Jim_SetResultString(interp, "namespaces not enabled", -1);
19075
return JIM_ERR;
19076
#endif
19077
}
19078
argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
19079
bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
19080
19081
cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
19082
19083
if (cmd) {
19084
19085
nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
19086
nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
19087
Jim_IncrRefCount(nargv[0]);
19088
memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
19089
ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
19090
Jim_DecrRefCount(interp, nargv[0]);
19091
Jim_Free(nargv);
19092
19093
JimDecrCmdRefCount(interp, cmd);
19094
return ret;
19095
}
19096
return JIM_ERR;
19097
}
19098
}
19099
19100
19101
19102
static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19103
{
19104
Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
19105
return JIM_OK;
19106
}
19107
19108
19109
static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19110
{
19111
int i;
19112
Jim_CallFrame *targetCallFrame;
19113
19114
19115
if (argc > 3 && (argc % 2 == 0)) {
19116
targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
19117
argc--;
19118
argv++;
19119
}
19120
else {
19121
targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
19122
}
19123
if (targetCallFrame == NULL) {
19124
return JIM_ERR;
19125
}
19126
19127
19128
if (argc < 3) {
19129
Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
19130
return JIM_ERR;
19131
}
19132
19133
19134
for (i = 1; i < argc; i += 2) {
19135
if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
19136
return JIM_ERR;
19137
}
19138
return JIM_OK;
19139
}
19140
19141
19142
static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19143
{
19144
int i;
19145
19146
if (argc < 2) {
19147
Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
19148
return JIM_ERR;
19149
}
19150
19151
if (interp->framePtr->level == 0)
19152
return JIM_OK;
19153
for (i = 1; i < argc; i++) {
19154
19155
const char *name = Jim_String(argv[i]);
19156
if (name[0] != ':' || name[1] != ':') {
19157
if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
19158
return JIM_ERR;
19159
}
19160
}
19161
return JIM_OK;
19162
}
19163
19164
static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
19165
Jim_Obj *objPtr, int nocase)
19166
{
19167
int numMaps;
19168
const char *str, *noMatchStart = NULL;
19169
int strLen, i;
19170
Jim_Obj *resultObjPtr;
19171
19172
numMaps = Jim_ListLength(interp, mapListObjPtr);
19173
if (numMaps % 2) {
19174
Jim_SetResultString(interp, "list must contain an even number of elements", -1);
19175
return NULL;
19176
}
19177
19178
str = Jim_String(objPtr);
19179
strLen = Jim_Utf8Length(interp, objPtr);
19180
19181
19182
resultObjPtr = Jim_NewStringObj(interp, "", 0);
19183
while (strLen) {
19184
for (i = 0; i < numMaps; i += 2) {
19185
Jim_Obj *eachObjPtr;
19186
const char *k;
19187
int kl;
19188
19189
eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
19190
k = Jim_String(eachObjPtr);
19191
kl = Jim_Utf8Length(interp, eachObjPtr);
19192
19193
if (strLen >= kl && kl) {
19194
int rc;
19195
rc = JimStringCompareUtf8(str, kl, k, kl, nocase);
19196
if (rc == 0) {
19197
if (noMatchStart) {
19198
Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
19199
noMatchStart = NULL;
19200
}
19201
Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
19202
str += utf8_index(str, kl);
19203
strLen -= kl;
19204
break;
19205
}
19206
}
19207
}
19208
if (i == numMaps) {
19209
int c;
19210
if (noMatchStart == NULL)
19211
noMatchStart = str;
19212
str += utf8_tounicode(str, &c);
19213
strLen--;
19214
}
19215
}
19216
if (noMatchStart) {
19217
Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
19218
}
19219
return resultObjPtr;
19220
}
19221
19222
19223
static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19224
{
19225
int len;
19226
int opt_case = 1;
19227
int option;
19228
static const char * const nocase_options[] = {
19229
"-nocase", NULL
19230
};
19231
static const char * const nocase_length_options[] = {
19232
"-nocase", "-length", NULL
19233
};
19234
19235
enum {
19236
OPT_BYTELENGTH,
19237
OPT_BYTERANGE,
19238
OPT_CAT,
19239
OPT_COMPARE,
19240
OPT_EQUAL,
19241
OPT_FIRST,
19242
OPT_INDEX,
19243
OPT_IS,
19244
OPT_LAST,
19245
OPT_LENGTH,
19246
OPT_MAP,
19247
OPT_MATCH,
19248
OPT_RANGE,
19249
OPT_REPEAT,
19250
OPT_REPLACE,
19251
OPT_REVERSE,
19252
OPT_TOLOWER,
19253
OPT_TOTITLE,
19254
OPT_TOUPPER,
19255
OPT_TRIM,
19256
OPT_TRIMLEFT,
19257
OPT_TRIMRIGHT,
19258
OPT_COUNT
19259
};
19260
static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
19261
JIM_DEF_SUBCMD("bytelength", "string", 1, 1),
19262
JIM_DEF_SUBCMD("byterange", "string first last", 3, 3),
19263
JIM_DEF_SUBCMD("cat", "?...?", 0, -1),
19264
JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5),
19265
JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5),
19266
JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3),
19267
JIM_DEF_SUBCMD("index", "string index", 2, 2),
19268
JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3),
19269
JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3),
19270
JIM_DEF_SUBCMD("length","string", 1, 1),
19271
JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3),
19272
JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3),
19273
JIM_DEF_SUBCMD("range", "string first last", 3, 3),
19274
JIM_DEF_SUBCMD("repeat", "string count", 2, 2),
19275
JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4),
19276
JIM_DEF_SUBCMD("reverse", "string", 1, 1),
19277
JIM_DEF_SUBCMD("tolower", "string", 1, 1),
19278
JIM_DEF_SUBCMD("totitle", "string", 1, 1),
19279
JIM_DEF_SUBCMD("toupper", "string", 1, 1),
19280
JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2),
19281
JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2),
19282
JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2),
19283
{ }
19284
};
19285
const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
19286
if (!ct) {
19287
return JIM_ERR;
19288
}
19289
if (ct->function) {
19290
19291
return ct->function(interp, argc, argv);
19292
}
19293
19294
option = ct - cmds;
19295
19296
switch (option) {
19297
case OPT_LENGTH:
19298
Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2]));
19299
return JIM_OK;
19300
19301
case OPT_BYTELENGTH:
19302
Jim_SetResultInt(interp, Jim_Length(argv[2]));
19303
return JIM_OK;
19304
19305
case OPT_CAT:{
19306
Jim_Obj *objPtr;
19307
if (argc == 3) {
19308
19309
objPtr = argv[2];
19310
}
19311
else {
19312
int i;
19313
19314
objPtr = Jim_NewStringObj(interp, "", 0);
19315
19316
for (i = 2; i < argc; i++) {
19317
Jim_AppendObj(interp, objPtr, argv[i]);
19318
}
19319
}
19320
Jim_SetResult(interp, objPtr);
19321
return JIM_OK;
19322
}
19323
19324
case OPT_COMPARE:
19325
case OPT_EQUAL:
19326
{
19327
19328
long opt_length = -1;
19329
int n = argc - 4;
19330
int i = 2;
19331
while (n > 0) {
19332
int subopt;
19333
if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
19334
JIM_ENUM_ABBREV) != JIM_OK) {
19335
badcompareargs:
19336
Jim_SubCmdArgError(interp, ct, argv[0]);
19337
return JIM_ERR;
19338
}
19339
if (subopt == 0) {
19340
19341
opt_case = 0;
19342
n--;
19343
}
19344
else {
19345
19346
if (n < 2) {
19347
goto badcompareargs;
19348
}
19349
if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
19350
return JIM_ERR;
19351
}
19352
n -= 2;
19353
}
19354
}
19355
if (n) {
19356
goto badcompareargs;
19357
}
19358
argv += argc - 2;
19359
if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
19360
19361
Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
19362
}
19363
else {
19364
const char *s1 = Jim_String(argv[0]);
19365
int l1 = Jim_Utf8Length(interp, argv[0]);
19366
const char *s2 = Jim_String(argv[1]);
19367
int l2 = Jim_Utf8Length(interp, argv[1]);
19368
if (opt_length >= 0) {
19369
if (l1 > opt_length) {
19370
l1 = opt_length;
19371
}
19372
if (l2 > opt_length) {
19373
l2 = opt_length;
19374
}
19375
}
19376
n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case);
19377
Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
19378
}
19379
return JIM_OK;
19380
}
19381
19382
case OPT_MATCH:
19383
if (argc != 4 &&
19384
(argc != 5 ||
19385
Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
19386
JIM_ENUM_ABBREV) != JIM_OK)) {
19387
Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
19388
return JIM_ERR;
19389
}
19390
if (opt_case == 0) {
19391
argv++;
19392
}
19393
Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
19394
return JIM_OK;
19395
19396
case OPT_MAP:{
19397
Jim_Obj *objPtr;
19398
19399
if (argc != 4 &&
19400
(argc != 5 ||
19401
Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
19402
JIM_ENUM_ABBREV) != JIM_OK)) {
19403
Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
19404
return JIM_ERR;
19405
}
19406
19407
if (opt_case == 0) {
19408
argv++;
19409
}
19410
objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
19411
if (objPtr == NULL) {
19412
return JIM_ERR;
19413
}
19414
Jim_SetResult(interp, objPtr);
19415
return JIM_OK;
19416
}
19417
19418
case OPT_RANGE:{
19419
Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
19420
if (objPtr == NULL) {
19421
return JIM_ERR;
19422
}
19423
Jim_SetResult(interp, objPtr);
19424
return JIM_OK;
19425
}
19426
19427
case OPT_BYTERANGE:{
19428
Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
19429
if (objPtr == NULL) {
19430
return JIM_ERR;
19431
}
19432
Jim_SetResult(interp, objPtr);
19433
return JIM_OK;
19434
}
19435
19436
case OPT_REPLACE:{
19437
Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
19438
if (objPtr == NULL) {
19439
return JIM_ERR;
19440
}
19441
Jim_SetResult(interp, objPtr);
19442
return JIM_OK;
19443
}
19444
19445
19446
case OPT_REPEAT:{
19447
Jim_Obj *objPtr;
19448
jim_wide count;
19449
19450
if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) {
19451
return JIM_ERR;
19452
}
19453
objPtr = Jim_NewStringObj(interp, "", 0);
19454
if (count > 0) {
19455
while (count--) {
19456
Jim_AppendObj(interp, objPtr, argv[2]);
19457
}
19458
}
19459
Jim_SetResult(interp, objPtr);
19460
return JIM_OK;
19461
}
19462
19463
case OPT_REVERSE:{
19464
char *buf, *p;
19465
const char *str;
19466
int i;
19467
19468
str = Jim_GetString(argv[2], &len);
19469
buf = Jim_Alloc(len + 1);
19470
assert(buf);
19471
p = buf + len;
19472
*p = 0;
19473
for (i = 0; i < len; ) {
19474
int c;
19475
int l = utf8_tounicode(str, &c);
19476
memcpy(p - l, str, l);
19477
p -= l;
19478
i += l;
19479
str += l;
19480
}
19481
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19482
return JIM_OK;
19483
}
19484
19485
case OPT_INDEX:{
19486
int idx;
19487
const char *str;
19488
19489
if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
19490
return JIM_ERR;
19491
}
19492
str = Jim_String(argv[2]);
19493
len = Jim_Utf8Length(interp, argv[2]);
19494
idx = JimRelToAbsIndex(len, idx);
19495
if (idx < 0 || idx >= len || str == NULL) {
19496
Jim_SetResultString(interp, "", 0);
19497
}
19498
else if (len == Jim_Length(argv[2])) {
19499
19500
Jim_SetResultString(interp, str + idx, 1);
19501
}
19502
else {
19503
int c;
19504
int i = utf8_index(str, idx);
19505
Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
19506
}
19507
return JIM_OK;
19508
}
19509
19510
case OPT_FIRST:
19511
case OPT_LAST:{
19512
int idx = 0, l1, l2;
19513
const char *s1, *s2;
19514
19515
s1 = Jim_String(argv[2]);
19516
s2 = Jim_String(argv[3]);
19517
l1 = Jim_Utf8Length(interp, argv[2]);
19518
l2 = Jim_Utf8Length(interp, argv[3]);
19519
if (argc == 5) {
19520
if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
19521
return JIM_ERR;
19522
}
19523
idx = JimRelToAbsIndex(l2, idx);
19524
if (idx < 0) {
19525
idx = 0;
19526
}
19527
}
19528
else if (option == OPT_LAST) {
19529
idx = l2;
19530
}
19531
if (option == OPT_FIRST) {
19532
Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
19533
}
19534
else {
19535
#ifdef JIM_UTF8
19536
Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
19537
#else
19538
Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
19539
#endif
19540
}
19541
return JIM_OK;
19542
}
19543
19544
case OPT_TRIM:
19545
Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL));
19546
return JIM_OK;
19547
case OPT_TRIMLEFT:
19548
Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL));
19549
return JIM_OK;
19550
case OPT_TRIMRIGHT:{
19551
Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL));
19552
return JIM_OK;
19553
}
19554
19555
case OPT_TOLOWER:
19556
Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
19557
return JIM_OK;
19558
case OPT_TOUPPER:
19559
Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
19560
return JIM_OK;
19561
case OPT_TOTITLE:
19562
Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
19563
return JIM_OK;
19564
19565
case OPT_IS:
19566
if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) {
19567
Jim_SubCmdArgError(interp, ct, argv[0]);
19568
return JIM_ERR;
19569
}
19570
return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
19571
}
19572
return JIM_OK;
19573
}
19574
19575
19576
static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19577
{
19578
long i, count = 1;
19579
jim_wide start, elapsed;
19580
19581
if (argc < 2) {
19582
Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
19583
return JIM_ERR;
19584
}
19585
if (argc == 3) {
19586
if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
19587
return JIM_ERR;
19588
}
19589
if (count < 0)
19590
return JIM_OK;
19591
i = count;
19592
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19593
while (i-- > 0) {
19594
int retval;
19595
19596
retval = Jim_EvalObj(interp, argv[1]);
19597
if (retval != JIM_OK) {
19598
return retval;
19599
}
19600
}
19601
elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19602
if (elapsed < count * 10) {
19603
Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count));
19604
}
19605
else {
19606
Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count);
19607
}
19608
Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1);
19609
return JIM_OK;
19610
}
19611
19612
19613
static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19614
{
19615
long us = 0;
19616
jim_wide start, delta, overhead;
19617
Jim_Obj *objPtr;
19618
double us_per_iter;
19619
int count;
19620
int n;
19621
19622
if (argc < 2) {
19623
Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?");
19624
return JIM_ERR;
19625
}
19626
if (argc == 3) {
19627
if (Jim_GetLong(interp, argv[2], &us) != JIM_OK)
19628
return JIM_ERR;
19629
us *= 1000;
19630
}
19631
if (us < 1) {
19632
19633
us = 1000 * 1000;
19634
}
19635
19636
19637
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19638
count = 0;
19639
do {
19640
int retval = Jim_EvalObj(interp, argv[1]);
19641
delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19642
if (retval != JIM_OK) {
19643
return retval;
19644
}
19645
count++;
19646
} while (delta < us);
19647
19648
19649
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19650
n = 0;
19651
do {
19652
int retval = Jim_EvalObj(interp, interp->nullScriptObj);
19653
overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19654
if (retval != JIM_OK) {
19655
return retval;
19656
}
19657
n++;
19658
} while (n < count);
19659
19660
delta -= overhead;
19661
19662
us_per_iter = (double)delta / count;
19663
objPtr = Jim_NewListObj(interp, NULL, 0);
19664
19665
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1));
19666
Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter));
19667
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1));
19668
Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter));
19669
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1));
19670
Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count));
19671
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1));
19672
Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta));
19673
Jim_SetResult(interp, objPtr);
19674
return JIM_OK;
19675
}
19676
19677
19678
static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19679
{
19680
long exitCode = 0;
19681
19682
if (argc > 2) {
19683
Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
19684
return JIM_ERR;
19685
}
19686
if (argc == 2) {
19687
if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
19688
return JIM_ERR;
19689
Jim_SetResult(interp, argv[1]);
19690
}
19691
interp->exitCode = exitCode;
19692
return JIM_EXIT;
19693
}
19694
19695
static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc)
19696
{
19697
int len = Jim_ListLength(interp, retcodeListObj);
19698
int i;
19699
for (i = 0; i < len; i++) {
19700
int returncode;
19701
if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) {
19702
return JIM_ERR;
19703
}
19704
if (rc == returncode) {
19705
return JIM_OK;
19706
}
19707
}
19708
return -1;
19709
}
19710
19711
19712
static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv)
19713
{
19714
static const char * const wrongargs_catchtry[2] = {
19715
"?-?no?code ... --? script ?resultVarName? ?optionVarName?",
19716
"?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
19717
};
19718
int exitCode = 0;
19719
int i;
19720
int sig = 0;
19721
int ok;
19722
Jim_Obj *finallyScriptObj = NULL;
19723
Jim_Obj *msgVarObj = NULL;
19724
Jim_Obj *optsVarObj = NULL;
19725
Jim_Obj *handlerScriptObj = NULL;
19726
Jim_Obj *errorCodeObj;
19727
int idx;
19728
19729
19730
jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
19731
static const int max_ignore_code = sizeof(ignore_mask) * 8;
19732
19733
JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper"));
19734
19735
Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
19736
19737
for (i = 1; i < argc - 1; i++) {
19738
const char *arg = Jim_String(argv[i]);
19739
jim_wide option;
19740
int ignore;
19741
19742
19743
if (strcmp(arg, "--") == 0) {
19744
i++;
19745
break;
19746
}
19747
if (*arg != '-') {
19748
break;
19749
}
19750
19751
if (strncmp(arg, "-no", 3) == 0) {
19752
arg += 3;
19753
ignore = 1;
19754
}
19755
else {
19756
arg++;
19757
ignore = 0;
19758
}
19759
19760
if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
19761
option = -1;
19762
}
19763
if (option < 0) {
19764
option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
19765
}
19766
if (option < 0) {
19767
goto wrongargs;
19768
}
19769
19770
if (ignore) {
19771
ignore_mask |= ((jim_wide)1 << option);
19772
}
19773
else {
19774
ignore_mask &= (~((jim_wide)1 << option));
19775
}
19776
}
19777
19778
idx = i;
19779
19780
if (argc - idx < 1) {
19781
wrongargs:
19782
Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]);
19783
return JIM_ERR;
19784
}
19785
19786
if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
19787
sig++;
19788
}
19789
19790
interp->signal_level += sig;
19791
if (Jim_CheckSignal(interp)) {
19792
19793
exitCode = JIM_SIGNAL;
19794
}
19795
else {
19796
exitCode = Jim_EvalObj(interp, argv[idx]);
19797
19798
interp->errorFlag = 0;
19799
}
19800
interp->signal_level -= sig;
19801
19802
errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
19803
19804
idx++;
19805
if (istry) {
19806
while (idx < argc) {
19807
int option;
19808
int ret;
19809
static const char * const try_options[] = { "on", "trap", "finally", NULL };
19810
enum { TRY_ON, TRY_TRAP, TRY_FINALLY, };
19811
19812
if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) {
19813
return JIM_ERR;
19814
}
19815
switch (option) {
19816
case TRY_ON:
19817
case TRY_TRAP:
19818
if (idx + 4 > argc) {
19819
goto wrongargs;
19820
}
19821
if (option == TRY_ON) {
19822
ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
19823
if (ret > JIM_OK) {
19824
goto wrongargs;
19825
}
19826
}
19827
else if (errorCodeObj) {
19828
int len = Jim_ListLength(interp, argv[idx + 1]);
19829
int i;
19830
19831
ret = JIM_OK;
19832
19833
for (i = 0; i < len; i++) {
19834
Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
19835
Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
19836
if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
19837
ret = -1;
19838
break;
19839
}
19840
}
19841
}
19842
else {
19843
19844
ret = -1;
19845
}
19846
19847
if (ret == JIM_OK && handlerScriptObj == NULL) {
19848
msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
19849
optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
19850
handlerScriptObj = argv[idx + 3];
19851
}
19852
idx += 4;
19853
break;
19854
case TRY_FINALLY:
19855
if (idx + 2 != argc) {
19856
goto wrongargs;
19857
}
19858
finallyScriptObj = argv[idx + 1];
19859
idx += 2;
19860
break;
19861
}
19862
}
19863
}
19864
else {
19865
if (argc - idx >= 1) {
19866
msgVarObj = argv[idx];
19867
idx++;
19868
if (argc - idx >= 1) {
19869
optsVarObj = argv[idx];
19870
idx++;
19871
}
19872
}
19873
}
19874
19875
19876
if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
19877
19878
if (finallyScriptObj) {
19879
Jim_EvalObj(interp, finallyScriptObj);
19880
}
19881
return exitCode;
19882
}
19883
19884
if (sig && exitCode == JIM_SIGNAL) {
19885
19886
if (interp->signal_set_result) {
19887
interp->signal_set_result(interp, interp->sigmask);
19888
}
19889
else if (!istry) {
19890
Jim_SetResultInt(interp, interp->sigmask);
19891
}
19892
interp->sigmask = 0;
19893
}
19894
19895
ok = 1;
19896
if (msgVarObj && Jim_Length(msgVarObj)) {
19897
if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) {
19898
ok = 0;
19899
}
19900
}
19901
if (ok && optsVarObj && Jim_Length(optsVarObj)) {
19902
Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
19903
19904
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
19905
Jim_ListAppendElement(interp, optListObj,
19906
Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
19907
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
19908
Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
19909
if (exitCode == JIM_ERR) {
19910
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
19911
-1));
19912
Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
19913
19914
if (errorCodeObj) {
19915
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
19916
Jim_ListAppendElement(interp, optListObj, errorCodeObj);
19917
}
19918
}
19919
if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) {
19920
ok = 0;
19921
}
19922
}
19923
if (ok && handlerScriptObj) {
19924
19925
exitCode = Jim_EvalObj(interp, handlerScriptObj);
19926
}
19927
19928
if (finallyScriptObj) {
19929
19930
Jim_Obj *prevResultObj = Jim_GetResult(interp);
19931
Jim_IncrRefCount(prevResultObj);
19932
int ret = Jim_EvalObj(interp, finallyScriptObj);
19933
if (ret == JIM_OK) {
19934
Jim_SetResult(interp, prevResultObj);
19935
}
19936
else {
19937
exitCode = ret;
19938
}
19939
Jim_DecrRefCount(interp, prevResultObj);
19940
}
19941
if (!istry) {
19942
Jim_SetResultInt(interp, exitCode);
19943
exitCode = JIM_OK;
19944
}
19945
return exitCode;
19946
}
19947
19948
19949
static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19950
{
19951
return JimCatchTryHelper(interp, 0, argc, argv);
19952
}
19953
19954
19955
static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19956
{
19957
return JimCatchTryHelper(interp, 1, argc, argv);
19958
}
19959
19960
19961
19962
static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19963
{
19964
if (argc != 3) {
19965
Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
19966
return JIM_ERR;
19967
}
19968
19969
return Jim_RenameCommand(interp, argv[1], argv[2]);
19970
}
19971
19972
#define JIM_DICTMATCH_KEYS 0x0001
19973
#define JIM_DICTMATCH_VALUES 0x002
19974
19975
int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
19976
{
19977
Jim_Obj *listObjPtr;
19978
Jim_Dict *dict;
19979
int i;
19980
19981
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
19982
return JIM_ERR;
19983
}
19984
dict = objPtr->internalRep.dictValue;
19985
19986
listObjPtr = Jim_NewListObj(interp, NULL, 0);
19987
19988
for (i = 0; i < dict->len; i += 2 ) {
19989
Jim_Obj *keyObj = dict->table[i];
19990
Jim_Obj *valObj = dict->table[i + 1];
19991
if (patternObj) {
19992
Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj;
19993
if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) {
19994
19995
continue;
19996
}
19997
}
19998
if (return_types & JIM_DICTMATCH_KEYS) {
19999
Jim_ListAppendElement(interp, listObjPtr, keyObj);
20000
}
20001
if (return_types & JIM_DICTMATCH_VALUES) {
20002
Jim_ListAppendElement(interp, listObjPtr, valObj);
20003
}
20004
}
20005
20006
Jim_SetResult(interp, listObjPtr);
20007
return JIM_OK;
20008
}
20009
20010
int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
20011
{
20012
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20013
return -1;
20014
}
20015
return objPtr->internalRep.dictValue->len / 2;
20016
}
20017
20018
Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
20019
{
20020
Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
20021
int i;
20022
20023
JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
20024
20025
20026
20027
for (i = 0; i < objc; i++) {
20028
Jim_Obj **table;
20029
int tablelen;
20030
int j;
20031
20032
table = Jim_DictPairs(interp, objv[i], &tablelen);
20033
if (tablelen && !table) {
20034
Jim_FreeNewObj(interp, objPtr);
20035
return NULL;
20036
}
20037
for (j = 0; j < tablelen; j += 2) {
20038
DictAddElement(interp, objPtr, table[j], table[j + 1]);
20039
}
20040
}
20041
return objPtr;
20042
}
20043
20044
int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
20045
{
20046
char buffer[100];
20047
Jim_Obj *output;
20048
Jim_Dict *dict;
20049
20050
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20051
return JIM_ERR;
20052
}
20053
20054
dict = objPtr->internalRep.dictValue;
20055
20056
20057
snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size);
20058
output = Jim_NewStringObj(interp, buffer, -1);
20059
Jim_SetResult(interp, output);
20060
return JIM_OK;
20061
}
20062
20063
static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
20064
{
20065
Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
20066
20067
Jim_AppendString(interp, prefixObj, " ", 1);
20068
Jim_AppendString(interp, prefixObj, subcmd, -1);
20069
20070
return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
20071
}
20072
20073
static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
20074
{
20075
int i;
20076
Jim_Obj *objPtr;
20077
Jim_Obj *dictObj;
20078
Jim_Obj **dictValues;
20079
int len;
20080
int ret = JIM_OK;
20081
20082
20083
dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
20084
if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
20085
return JIM_ERR;
20086
}
20087
20088
dictValues = Jim_DictPairs(interp, objPtr, &len);
20089
if (len && dictValues == NULL) {
20090
return JIM_ERR;
20091
}
20092
for (i = 0; i < len; i += 2) {
20093
if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
20094
return JIM_ERR;
20095
}
20096
}
20097
20098
20099
if (Jim_Length(scriptObj)) {
20100
ret = Jim_EvalObj(interp, scriptObj);
20101
20102
20103
if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
20104
20105
Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
20106
for (i = 0; i < keyc; i++) {
20107
newkeyv[i] = keyv[i];
20108
}
20109
20110
for (i = 0; i < len; i += 2) {
20111
20112
if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) {
20113
20114
objPtr = Jim_GetVariable(interp, dictValues[i], 0);
20115
newkeyv[keyc] = dictValues[i];
20116
Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT);
20117
}
20118
}
20119
Jim_Free(newkeyv);
20120
}
20121
}
20122
20123
return ret;
20124
}
20125
20126
20127
static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20128
{
20129
Jim_Obj *objPtr;
20130
int types = JIM_DICTMATCH_KEYS;
20131
20132
enum {
20133
OPT_CREATE,
20134
OPT_GET,
20135
OPT_GETDEF,
20136
OPT_GETWITHDEFAULT,
20137
OPT_SET,
20138
OPT_UNSET,
20139
OPT_EXISTS,
20140
OPT_KEYS,
20141
OPT_SIZE,
20142
OPT_INFO,
20143
OPT_MERGE,
20144
OPT_WITH,
20145
OPT_APPEND,
20146
OPT_LAPPEND,
20147
OPT_INCR,
20148
OPT_REMOVE,
20149
OPT_VALUES,
20150
OPT_FOR,
20151
OPT_REPLACE,
20152
OPT_UPDATE,
20153
OPT_COUNT
20154
};
20155
static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
20156
JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2),
20157
JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1),
20158
JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1),
20159
JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1),
20160
JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1),
20161
JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1),
20162
JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1),
20163
JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2),
20164
JIM_DEF_SUBCMD("size", "dictionary", 1, 1),
20165
JIM_DEF_SUBCMD("info", "dictionary", 1, 1),
20166
JIM_DEF_SUBCMD("merge", "?...?", 0, -1),
20167
JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1),
20168
JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1),
20169
JIM_DEF_SUBCMD("lappend", "varName key ?value ...?", 2, -1),
20170
JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3),
20171
JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1),
20172
JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2),
20173
JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3),
20174
JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1),
20175
JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1),
20176
{ }
20177
};
20178
const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
20179
if (!ct) {
20180
return JIM_ERR;
20181
}
20182
if (ct->function) {
20183
20184
return ct->function(interp, argc, argv);
20185
}
20186
20187
20188
switch (ct - cmds) {
20189
case OPT_GET:
20190
if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
20191
JIM_ERRMSG) != JIM_OK) {
20192
return JIM_ERR;
20193
}
20194
Jim_SetResult(interp, objPtr);
20195
return JIM_OK;
20196
20197
case OPT_GETDEF:
20198
case OPT_GETWITHDEFAULT:{
20199
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG);
20200
if (rc == -1) {
20201
20202
return JIM_ERR;
20203
}
20204
if (rc == JIM_ERR) {
20205
Jim_SetResult(interp, argv[argc - 1]);
20206
}
20207
else {
20208
Jim_SetResult(interp, objPtr);
20209
}
20210
return JIM_OK;
20211
}
20212
20213
case OPT_SET:
20214
return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
20215
20216
case OPT_EXISTS:{
20217
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
20218
if (rc < 0) {
20219
return JIM_ERR;
20220
}
20221
Jim_SetResultBool(interp, rc == JIM_OK);
20222
return JIM_OK;
20223
}
20224
20225
case OPT_UNSET:
20226
if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
20227
return JIM_ERR;
20228
}
20229
return JIM_OK;
20230
20231
case OPT_VALUES:
20232
types = JIM_DICTMATCH_VALUES;
20233
20234
case OPT_KEYS:
20235
return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
20236
20237
case OPT_SIZE:
20238
if (Jim_DictSize(interp, argv[2]) < 0) {
20239
return JIM_ERR;
20240
}
20241
Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
20242
return JIM_OK;
20243
20244
case OPT_MERGE:
20245
if (argc == 2) {
20246
return JIM_OK;
20247
}
20248
objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
20249
if (objPtr == NULL) {
20250
return JIM_ERR;
20251
}
20252
Jim_SetResult(interp, objPtr);
20253
return JIM_OK;
20254
20255
case OPT_CREATE:
20256
objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
20257
Jim_SetResult(interp, objPtr);
20258
return JIM_OK;
20259
20260
case OPT_INFO:
20261
return Jim_DictInfo(interp, argv[2]);
20262
20263
case OPT_WITH:
20264
return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
20265
20266
case OPT_UPDATE:
20267
if (argc < 6 || argc % 2) {
20268
20269
argc = 2;
20270
}
20271
20272
default:
20273
return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2);
20274
}
20275
}
20276
20277
20278
static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20279
{
20280
static const char * const options[] = {
20281
"-nobackslashes", "-nocommands", "-novariables", NULL
20282
};
20283
enum
20284
{ OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
20285
int i;
20286
int flags = JIM_SUBST_FLAG;
20287
Jim_Obj *objPtr;
20288
20289
if (argc < 2) {
20290
Jim_WrongNumArgs(interp, 1, argv, "?options? string");
20291
return JIM_ERR;
20292
}
20293
for (i = 1; i < (argc - 1); i++) {
20294
int option;
20295
20296
if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
20297
JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
20298
return JIM_ERR;
20299
}
20300
switch (option) {
20301
case OPT_NOBACKSLASHES:
20302
flags |= JIM_SUBST_NOESC;
20303
break;
20304
case OPT_NOCOMMANDS:
20305
flags |= JIM_SUBST_NOCMD;
20306
break;
20307
case OPT_NOVARIABLES:
20308
flags |= JIM_SUBST_NOVAR;
20309
break;
20310
}
20311
}
20312
if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
20313
return JIM_ERR;
20314
}
20315
Jim_SetResult(interp, objPtr);
20316
return JIM_OK;
20317
}
20318
20319
#ifdef jim_ext_namespace
20320
static int JimIsGlobalNamespace(Jim_Obj *objPtr)
20321
{
20322
int len;
20323
const char *str = Jim_GetString(objPtr, &len);
20324
return len >= 2 && str[0] == ':' && str[1] == ':';
20325
}
20326
#endif
20327
20328
20329
static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20330
{
20331
Jim_Obj *objPtr;
20332
int mode = 0;
20333
20334
20335
enum {
20336
INFO_ALIAS,
20337
INFO_ARGS,
20338
INFO_BODY,
20339
INFO_CHANNELS,
20340
INFO_COMMANDS,
20341
INFO_COMPLETE,
20342
INFO_EXISTS,
20343
INFO_FRAME,
20344
INFO_GLOBALS,
20345
INFO_HOSTNAME,
20346
INFO_LEVEL,
20347
INFO_LOCALS,
20348
INFO_NAMEOFEXECUTABLE,
20349
INFO_PATCHLEVEL,
20350
INFO_PROCS,
20351
INFO_REFERENCES,
20352
INFO_RETURNCODES,
20353
INFO_SCRIPT,
20354
INFO_SOURCE,
20355
INFO_STACKTRACE,
20356
INFO_STATICS,
20357
INFO_VARS,
20358
INFO_VERSION,
20359
INFO_COUNT
20360
};
20361
static const jim_subcmd_type cmds[INFO_COUNT + 1] = {
20362
JIM_DEF_SUBCMD("alias", "command", 1, 1),
20363
JIM_DEF_SUBCMD("args", "procname", 1, 1),
20364
JIM_DEF_SUBCMD("body", "procname", 1, 1),
20365
JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1),
20366
JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1),
20367
JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2),
20368
JIM_DEF_SUBCMD("exists", "varName", 1, 1),
20369
JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1),
20370
JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1),
20371
JIM_DEF_SUBCMD("hostname", NULL, 0, 0),
20372
JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1),
20373
JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1),
20374
JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0),
20375
JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0),
20376
JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1),
20377
JIM_DEF_SUBCMD("references", NULL, 0, 0),
20378
JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1),
20379
JIM_DEF_SUBCMD("script", "?filename?", 0, 1),
20380
JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3),
20381
JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0),
20382
JIM_DEF_SUBCMD("statics", "procname", 1, 1),
20383
JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1),
20384
JIM_DEF_SUBCMD("version", NULL, 0, 0),
20385
{ }
20386
};
20387
const jim_subcmd_type *ct;
20388
#ifdef jim_ext_namespace
20389
int nons = 0;
20390
20391
if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
20392
20393
argc--;
20394
argv++;
20395
nons = 1;
20396
}
20397
#endif
20398
ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
20399
if (!ct) {
20400
return JIM_ERR;
20401
}
20402
if (ct->function) {
20403
20404
return ct->function(interp, argc, argv);
20405
}
20406
20407
int option = ct - cmds;
20408
20409
switch (option) {
20410
case INFO_EXISTS:
20411
Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
20412
return JIM_OK;
20413
20414
case INFO_ALIAS:{
20415
Jim_Cmd *cmdPtr;
20416
20417
if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
20418
return JIM_ERR;
20419
}
20420
if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
20421
Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
20422
return JIM_ERR;
20423
}
20424
Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
20425
return JIM_OK;
20426
}
20427
20428
case INFO_CHANNELS:
20429
mode++;
20430
#ifndef jim_ext_aio
20431
Jim_SetResultString(interp, "aio not enabled", -1);
20432
return JIM_ERR;
20433
#endif
20434
20435
case INFO_PROCS:
20436
mode++;
20437
20438
case INFO_COMMANDS:
20439
20440
#ifdef jim_ext_namespace
20441
if (!nons) {
20442
if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
20443
return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
20444
}
20445
}
20446
#endif
20447
Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
20448
return JIM_OK;
20449
20450
case INFO_VARS:
20451
mode++;
20452
20453
case INFO_LOCALS:
20454
mode++;
20455
20456
case INFO_GLOBALS:
20457
20458
#ifdef jim_ext_namespace
20459
if (!nons) {
20460
if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
20461
return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
20462
}
20463
}
20464
#endif
20465
Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
20466
return JIM_OK;
20467
20468
case INFO_SCRIPT:
20469
if (argc == 3) {
20470
Jim_IncrRefCount(argv[2]);
20471
Jim_DecrRefCount(interp, interp->currentFilenameObj);
20472
interp->currentFilenameObj = argv[2];
20473
}
20474
Jim_SetResult(interp, interp->currentFilenameObj);
20475
return JIM_OK;
20476
20477
case INFO_SOURCE:{
20478
Jim_Obj *resObjPtr;
20479
Jim_Obj *fileNameObj;
20480
20481
if (argc == 4) {
20482
Jim_SubCmdArgError(interp, ct, argv[0]);
20483
return JIM_ERR;
20484
}
20485
if (argc == 5) {
20486
jim_wide line;
20487
if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
20488
return JIM_ERR;
20489
}
20490
resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
20491
Jim_SetSourceInfo(interp, resObjPtr, argv[3], line);
20492
}
20493
else {
20494
int line;
20495
fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line);
20496
resObjPtr = Jim_NewListObj(interp, NULL, 0);
20497
Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
20498
Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
20499
}
20500
Jim_SetResult(interp, resObjPtr);
20501
return JIM_OK;
20502
}
20503
20504
case INFO_STACKTRACE:
20505
Jim_SetResult(interp, interp->stackTrace);
20506
return JIM_OK;
20507
20508
case INFO_LEVEL:
20509
if (argc == 2) {
20510
Jim_SetResultInt(interp, interp->framePtr->level);
20511
}
20512
else {
20513
if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) {
20514
return JIM_ERR;
20515
}
20516
Jim_SetResult(interp, objPtr);
20517
}
20518
return JIM_OK;
20519
20520
case INFO_FRAME:
20521
if (argc == 2) {
20522
Jim_SetResultInt(interp, interp->procLevel + 1);
20523
}
20524
else {
20525
if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) {
20526
return JIM_ERR;
20527
}
20528
Jim_SetResult(interp, objPtr);
20529
}
20530
return JIM_OK;
20531
20532
case INFO_BODY:
20533
case INFO_STATICS:
20534
case INFO_ARGS:{
20535
Jim_Cmd *cmdPtr;
20536
20537
if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
20538
return JIM_ERR;
20539
}
20540
if (!cmdPtr->isproc) {
20541
Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
20542
return JIM_ERR;
20543
}
20544
switch (option) {
20545
#ifdef JIM_NO_INTROSPECTION
20546
default:
20547
Jim_SetResultString(interp, "unsupported", -1);
20548
return JIM_ERR;
20549
#else
20550
case INFO_BODY:
20551
Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
20552
break;
20553
case INFO_ARGS:
20554
Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
20555
break;
20556
#endif
20557
case INFO_STATICS:
20558
if (cmdPtr->u.proc.staticVars) {
20559
Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
20560
NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
20561
}
20562
break;
20563
}
20564
return JIM_OK;
20565
}
20566
20567
case INFO_VERSION:
20568
case INFO_PATCHLEVEL:{
20569
char buf[(JIM_INTEGER_SPACE * 2) + 1];
20570
20571
sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
20572
Jim_SetResultString(interp, buf, -1);
20573
return JIM_OK;
20574
}
20575
20576
case INFO_COMPLETE: {
20577
char missing;
20578
20579
Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
20580
if (missing != ' ' && argc == 4) {
20581
Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
20582
}
20583
return JIM_OK;
20584
}
20585
20586
case INFO_HOSTNAME:
20587
20588
return Jim_Eval(interp, "os.gethostname");
20589
20590
case INFO_NAMEOFEXECUTABLE:
20591
20592
return Jim_Eval(interp, "{info nameofexecutable}");
20593
20594
case INFO_RETURNCODES:
20595
if (argc == 2) {
20596
int i;
20597
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
20598
20599
for (i = 0; jimReturnCodes[i]; i++) {
20600
Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
20601
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
20602
jimReturnCodes[i], -1));
20603
}
20604
20605
Jim_SetResult(interp, listObjPtr);
20606
}
20607
else if (argc == 3) {
20608
long code;
20609
const char *name;
20610
20611
if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
20612
return JIM_ERR;
20613
}
20614
name = Jim_ReturnCode(code);
20615
if (*name == '?') {
20616
Jim_SetResultInt(interp, code);
20617
}
20618
else {
20619
Jim_SetResultString(interp, name, -1);
20620
}
20621
}
20622
return JIM_OK;
20623
case INFO_REFERENCES:
20624
#ifdef JIM_REFERENCES
20625
return JimInfoReferences(interp, argc, argv);
20626
#else
20627
Jim_SetResultString(interp, "not supported", -1);
20628
return JIM_ERR;
20629
#endif
20630
default:
20631
abort();
20632
}
20633
}
20634
20635
20636
static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20637
{
20638
Jim_Obj *objPtr;
20639
int result = 0;
20640
20641
static const char * const options[] = {
20642
"-command", "-proc", "-alias", "-var", NULL
20643
};
20644
enum
20645
{
20646
OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
20647
};
20648
int option;
20649
20650
if (argc == 2) {
20651
option = OPT_VAR;
20652
objPtr = argv[1];
20653
}
20654
else if (argc == 3) {
20655
if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
20656
return JIM_ERR;
20657
}
20658
objPtr = argv[2];
20659
}
20660
else {
20661
Jim_WrongNumArgs(interp, 1, argv, "?option? name");
20662
return JIM_ERR;
20663
}
20664
20665
if (option == OPT_VAR) {
20666
result = Jim_GetVariable(interp, objPtr, 0) != NULL;
20667
}
20668
else {
20669
20670
Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
20671
20672
if (cmd) {
20673
switch (option) {
20674
case OPT_COMMAND:
20675
result = 1;
20676
break;
20677
20678
case OPT_ALIAS:
20679
result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
20680
break;
20681
20682
case OPT_PROC:
20683
result = cmd->isproc;
20684
break;
20685
}
20686
}
20687
}
20688
Jim_SetResultBool(interp, result);
20689
return JIM_OK;
20690
}
20691
20692
20693
static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20694
{
20695
const char *str, *splitChars, *noMatchStart;
20696
int splitLen, strLen;
20697
Jim_Obj *resObjPtr;
20698
int c;
20699
int len;
20700
20701
if (argc != 2 && argc != 3) {
20702
Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
20703
return JIM_ERR;
20704
}
20705
20706
str = Jim_GetString(argv[1], &len);
20707
if (len == 0) {
20708
return JIM_OK;
20709
}
20710
strLen = Jim_Utf8Length(interp, argv[1]);
20711
20712
20713
if (argc == 2) {
20714
splitChars = " \n\t\r";
20715
splitLen = 4;
20716
}
20717
else {
20718
splitChars = Jim_String(argv[2]);
20719
splitLen = Jim_Utf8Length(interp, argv[2]);
20720
}
20721
20722
noMatchStart = str;
20723
resObjPtr = Jim_NewListObj(interp, NULL, 0);
20724
20725
20726
if (splitLen) {
20727
Jim_Obj *objPtr;
20728
while (strLen--) {
20729
const char *sc = splitChars;
20730
int scLen = splitLen;
20731
int sl = utf8_tounicode(str, &c);
20732
while (scLen--) {
20733
int pc;
20734
sc += utf8_tounicode(sc, &pc);
20735
if (c == pc) {
20736
objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
20737
Jim_ListAppendElement(interp, resObjPtr, objPtr);
20738
noMatchStart = str + sl;
20739
break;
20740
}
20741
}
20742
str += sl;
20743
}
20744
objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
20745
Jim_ListAppendElement(interp, resObjPtr, objPtr);
20746
}
20747
else {
20748
Jim_Obj **commonObj = NULL;
20749
#define NUM_COMMON (128 - 9)
20750
while (strLen--) {
20751
int n = utf8_tounicode(str, &c);
20752
#ifdef JIM_OPTIMIZATION
20753
if (c >= 9 && c < 128) {
20754
20755
c -= 9;
20756
if (!commonObj) {
20757
commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
20758
memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
20759
}
20760
if (!commonObj[c]) {
20761
commonObj[c] = Jim_NewStringObj(interp, str, 1);
20762
}
20763
Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
20764
str++;
20765
continue;
20766
}
20767
#endif
20768
Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
20769
str += n;
20770
}
20771
Jim_Free(commonObj);
20772
}
20773
20774
Jim_SetResult(interp, resObjPtr);
20775
return JIM_OK;
20776
}
20777
20778
20779
static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20780
{
20781
const char *joinStr;
20782
int joinStrLen;
20783
20784
if (argc != 2 && argc != 3) {
20785
Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
20786
return JIM_ERR;
20787
}
20788
20789
if (argc == 2) {
20790
joinStr = " ";
20791
joinStrLen = 1;
20792
}
20793
else {
20794
joinStr = Jim_GetString(argv[2], &joinStrLen);
20795
}
20796
Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
20797
return JIM_OK;
20798
}
20799
20800
20801
static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20802
{
20803
Jim_Obj *objPtr;
20804
20805
if (argc < 2) {
20806
Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
20807
return JIM_ERR;
20808
}
20809
objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
20810
if (objPtr == NULL)
20811
return JIM_ERR;
20812
Jim_SetResult(interp, objPtr);
20813
return JIM_OK;
20814
}
20815
20816
20817
static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20818
{
20819
Jim_Obj *listPtr, **outVec;
20820
int outc, i;
20821
20822
if (argc < 3) {
20823
Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
20824
return JIM_ERR;
20825
}
20826
if (argv[2]->typePtr != &scanFmtStringObjType)
20827
SetScanFmtFromAny(interp, argv[2]);
20828
if (FormatGetError(argv[2]) != 0) {
20829
Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
20830
return JIM_ERR;
20831
}
20832
if (argc > 3) {
20833
int maxPos = FormatGetMaxPos(argv[2]);
20834
int count = FormatGetCnvCount(argv[2]);
20835
20836
if (maxPos > argc - 3) {
20837
Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
20838
return JIM_ERR;
20839
}
20840
else if (count > argc - 3) {
20841
Jim_SetResultString(interp, "different numbers of variable names and "
20842
"field specifiers", -1);
20843
return JIM_ERR;
20844
}
20845
else if (count < argc - 3) {
20846
Jim_SetResultString(interp, "variable is not assigned by any "
20847
"conversion specifiers", -1);
20848
return JIM_ERR;
20849
}
20850
}
20851
listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
20852
if (listPtr == 0)
20853
return JIM_ERR;
20854
if (argc > 3) {
20855
int rc = JIM_OK;
20856
int count = 0;
20857
20858
if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
20859
int len = Jim_ListLength(interp, listPtr);
20860
20861
if (len != 0) {
20862
JimListGetElements(interp, listPtr, &outc, &outVec);
20863
for (i = 0; i < outc; ++i) {
20864
if (Jim_Length(outVec[i]) > 0) {
20865
++count;
20866
if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
20867
rc = JIM_ERR;
20868
}
20869
}
20870
}
20871
}
20872
Jim_FreeNewObj(interp, listPtr);
20873
}
20874
else {
20875
count = -1;
20876
}
20877
if (rc == JIM_OK) {
20878
Jim_SetResultInt(interp, count);
20879
}
20880
return rc;
20881
}
20882
else {
20883
if (listPtr == (Jim_Obj *)EOF) {
20884
Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
20885
return JIM_OK;
20886
}
20887
Jim_SetResult(interp, listPtr);
20888
}
20889
return JIM_OK;
20890
}
20891
20892
20893
static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20894
{
20895
if (argc != 2 && argc != 3) {
20896
Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
20897
return JIM_ERR;
20898
}
20899
Jim_SetResult(interp, argv[1]);
20900
if (argc == 3) {
20901
JimSetStackTrace(interp, argv[2]);
20902
return JIM_ERR;
20903
}
20904
return JIM_ERR;
20905
}
20906
20907
20908
static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20909
{
20910
Jim_Obj *objPtr;
20911
20912
if (argc != 4) {
20913
Jim_WrongNumArgs(interp, 1, argv, "list first last");
20914
return JIM_ERR;
20915
}
20916
if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
20917
return JIM_ERR;
20918
Jim_SetResult(interp, objPtr);
20919
return JIM_OK;
20920
}
20921
20922
20923
static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20924
{
20925
Jim_Obj *objPtr;
20926
jim_wide count;
20927
20928
if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) {
20929
Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
20930
return JIM_ERR;
20931
}
20932
if (count == 0 || argc == 2) {
20933
Jim_SetEmptyResult(interp);
20934
return JIM_OK;
20935
}
20936
20937
argc -= 2;
20938
argv += 2;
20939
20940
objPtr = Jim_NewListObj(interp, NULL, 0);
20941
ListEnsureLength(objPtr, argc * count);
20942
while (count--) {
20943
ListInsertElements(objPtr, -1, argc, argv);
20944
}
20945
20946
Jim_SetResult(interp, objPtr);
20947
return JIM_OK;
20948
}
20949
20950
char **Jim_GetEnviron(void)
20951
{
20952
#if defined(HAVE__NSGETENVIRON)
20953
return *_NSGetEnviron();
20954
#else
20955
#if !defined(NO_ENVIRON_EXTERN)
20956
extern char **environ;
20957
#endif
20958
20959
return environ;
20960
#endif
20961
}
20962
20963
void Jim_SetEnviron(char **env)
20964
{
20965
#if defined(HAVE__NSGETENVIRON)
20966
*_NSGetEnviron() = env;
20967
#else
20968
#if !defined(NO_ENVIRON_EXTERN)
20969
extern char **environ;
20970
#endif
20971
20972
environ = env;
20973
#endif
20974
}
20975
20976
20977
static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20978
{
20979
const char *key;
20980
const char *val;
20981
20982
if (argc == 1) {
20983
char **e = Jim_GetEnviron();
20984
20985
int i;
20986
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
20987
20988
for (i = 0; e[i]; i++) {
20989
const char *equals = strchr(e[i], '=');
20990
20991
if (equals) {
20992
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
20993
equals - e[i]));
20994
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
20995
}
20996
}
20997
20998
Jim_SetResult(interp, listObjPtr);
20999
return JIM_OK;
21000
}
21001
21002
if (argc > 3) {
21003
Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
21004
return JIM_ERR;
21005
}
21006
key = Jim_String(argv[1]);
21007
val = getenv(key);
21008
if (val == NULL) {
21009
if (argc < 3) {
21010
Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
21011
return JIM_ERR;
21012
}
21013
val = Jim_String(argv[2]);
21014
}
21015
Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
21016
return JIM_OK;
21017
}
21018
21019
21020
static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21021
{
21022
int retval;
21023
21024
if (argc != 2) {
21025
Jim_WrongNumArgs(interp, 1, argv, "fileName");
21026
return JIM_ERR;
21027
}
21028
retval = Jim_EvalFile(interp, Jim_String(argv[1]));
21029
if (retval == JIM_RETURN)
21030
return JIM_OK;
21031
return retval;
21032
}
21033
21034
21035
static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21036
{
21037
Jim_Obj *revObjPtr, **ele;
21038
int len;
21039
21040
if (argc != 2) {
21041
Jim_WrongNumArgs(interp, 1, argv, "list");
21042
return JIM_ERR;
21043
}
21044
JimListGetElements(interp, argv[1], &len, &ele);
21045
revObjPtr = Jim_NewListObj(interp, NULL, 0);
21046
ListEnsureLength(revObjPtr, len);
21047
len--;
21048
while (len >= 0)
21049
ListAppendElement(revObjPtr, ele[len--]);
21050
Jim_SetResult(interp, revObjPtr);
21051
return JIM_OK;
21052
}
21053
21054
static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
21055
{
21056
jim_wide len;
21057
21058
if (step == 0)
21059
return -1;
21060
if (start == end)
21061
return 0;
21062
else if (step > 0 && start > end)
21063
return -1;
21064
else if (step < 0 && end > start)
21065
return -1;
21066
len = end - start;
21067
if (len < 0)
21068
len = -len;
21069
if (step < 0)
21070
step = -step;
21071
len = 1 + ((len - 1) / step);
21072
if (len > INT_MAX)
21073
len = INT_MAX;
21074
return (int)((len < 0) ? -1 : len);
21075
}
21076
21077
21078
static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21079
{
21080
jim_wide start = 0, end, step = 1;
21081
int len, i;
21082
Jim_Obj *objPtr;
21083
21084
if (argc < 2 || argc > 4) {
21085
Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
21086
return JIM_ERR;
21087
}
21088
if (argc == 2) {
21089
if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK)
21090
return JIM_ERR;
21091
}
21092
else {
21093
if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK ||
21094
Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK)
21095
return JIM_ERR;
21096
if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK)
21097
return JIM_ERR;
21098
}
21099
if ((len = JimRangeLen(start, end, step)) == -1) {
21100
Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
21101
return JIM_ERR;
21102
}
21103
objPtr = Jim_NewListObj(interp, NULL, 0);
21104
ListEnsureLength(objPtr, len);
21105
for (i = 0; i < len; i++)
21106
ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
21107
Jim_SetResult(interp, objPtr);
21108
return JIM_OK;
21109
}
21110
21111
21112
static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21113
{
21114
jim_wide min = 0, max = 0, len, maxMul;
21115
21116
if (argc < 1 || argc > 3) {
21117
Jim_WrongNumArgs(interp, 1, argv, "?min? max");
21118
return JIM_ERR;
21119
}
21120
if (argc == 1) {
21121
max = JIM_WIDE_MAX;
21122
} else if (argc == 2) {
21123
if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK)
21124
return JIM_ERR;
21125
} else if (argc == 3) {
21126
if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK ||
21127
Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK)
21128
return JIM_ERR;
21129
}
21130
len = max-min;
21131
if (len < 0) {
21132
Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
21133
return JIM_ERR;
21134
}
21135
maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
21136
while (1) {
21137
jim_wide r;
21138
21139
JimRandomBytes(interp, &r, sizeof(jim_wide));
21140
if (r < 0 || r >= maxMul) continue;
21141
r = (len == 0) ? 0 : r%len;
21142
Jim_SetResultInt(interp, min+r);
21143
return JIM_OK;
21144
}
21145
}
21146
21147
static const struct {
21148
const char *name;
21149
Jim_CmdProc *cmdProc;
21150
} Jim_CoreCommandsTable[] = {
21151
{"alias", Jim_AliasCoreCommand},
21152
{"set", Jim_SetCoreCommand},
21153
{"unset", Jim_UnsetCoreCommand},
21154
{"puts", Jim_PutsCoreCommand},
21155
{"+", Jim_AddCoreCommand},
21156
{"*", Jim_MulCoreCommand},
21157
{"-", Jim_SubCoreCommand},
21158
{"/", Jim_DivCoreCommand},
21159
{"incr", Jim_IncrCoreCommand},
21160
{"while", Jim_WhileCoreCommand},
21161
{"loop", Jim_LoopCoreCommand},
21162
{"for", Jim_ForCoreCommand},
21163
{"foreach", Jim_ForeachCoreCommand},
21164
{"lmap", Jim_LmapCoreCommand},
21165
{"lassign", Jim_LassignCoreCommand},
21166
{"if", Jim_IfCoreCommand},
21167
{"switch", Jim_SwitchCoreCommand},
21168
{"list", Jim_ListCoreCommand},
21169
{"lindex", Jim_LindexCoreCommand},
21170
{"lset", Jim_LsetCoreCommand},
21171
{"lsearch", Jim_LsearchCoreCommand},
21172
{"llength", Jim_LlengthCoreCommand},
21173
{"lappend", Jim_LappendCoreCommand},
21174
{"linsert", Jim_LinsertCoreCommand},
21175
{"lreplace", Jim_LreplaceCoreCommand},
21176
{"lsort", Jim_LsortCoreCommand},
21177
{"append", Jim_AppendCoreCommand},
21178
{"eval", Jim_EvalCoreCommand},
21179
{"uplevel", Jim_UplevelCoreCommand},
21180
{"expr", Jim_ExprCoreCommand},
21181
{"break", Jim_BreakCoreCommand},
21182
{"continue", Jim_ContinueCoreCommand},
21183
{"proc", Jim_ProcCoreCommand},
21184
{"xtrace", Jim_XtraceCoreCommand},
21185
{"concat", Jim_ConcatCoreCommand},
21186
{"return", Jim_ReturnCoreCommand},
21187
{"upvar", Jim_UpvarCoreCommand},
21188
{"global", Jim_GlobalCoreCommand},
21189
{"string", Jim_StringCoreCommand},
21190
{"time", Jim_TimeCoreCommand},
21191
{"timerate", Jim_TimeRateCoreCommand},
21192
{"exit", Jim_ExitCoreCommand},
21193
{"catch", Jim_CatchCoreCommand},
21194
{"try", Jim_TryCoreCommand},
21195
#ifdef JIM_REFERENCES
21196
{"ref", Jim_RefCoreCommand},
21197
{"getref", Jim_GetrefCoreCommand},
21198
{"setref", Jim_SetrefCoreCommand},
21199
{"finalize", Jim_FinalizeCoreCommand},
21200
{"collect", Jim_CollectCoreCommand},
21201
#endif
21202
{"rename", Jim_RenameCoreCommand},
21203
{"dict", Jim_DictCoreCommand},
21204
{"subst", Jim_SubstCoreCommand},
21205
{"info", Jim_InfoCoreCommand},
21206
{"exists", Jim_ExistsCoreCommand},
21207
{"split", Jim_SplitCoreCommand},
21208
{"join", Jim_JoinCoreCommand},
21209
{"format", Jim_FormatCoreCommand},
21210
{"scan", Jim_ScanCoreCommand},
21211
{"error", Jim_ErrorCoreCommand},
21212
{"lrange", Jim_LrangeCoreCommand},
21213
{"lrepeat", Jim_LrepeatCoreCommand},
21214
{"env", Jim_EnvCoreCommand},
21215
{"source", Jim_SourceCoreCommand},
21216
{"lreverse", Jim_LreverseCoreCommand},
21217
{"range", Jim_RangeCoreCommand},
21218
{"rand", Jim_RandCoreCommand},
21219
{"tailcall", Jim_TailcallCoreCommand},
21220
{"local", Jim_LocalCoreCommand},
21221
{"upcall", Jim_UpcallCoreCommand},
21222
{"apply", Jim_ApplyCoreCommand},
21223
{"stacktrace", Jim_StacktraceCoreCommand},
21224
{NULL, NULL},
21225
};
21226
21227
void Jim_RegisterCoreCommands(Jim_Interp *interp)
21228
{
21229
int i = 0;
21230
21231
while (Jim_CoreCommandsTable[i].name != NULL) {
21232
Jim_CreateCommand(interp,
21233
Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
21234
i++;
21235
}
21236
}
21237
21238
void Jim_MakeErrorMessage(Jim_Interp *interp)
21239
{
21240
Jim_Obj *argv[2];
21241
21242
argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
21243
argv[1] = interp->result;
21244
21245
Jim_EvalObjVector(interp, 2, argv);
21246
}
21247
21248
static char **JimSortStringTable(const char *const *tablePtr)
21249
{
21250
int count;
21251
char **tablePtrSorted;
21252
21253
21254
for (count = 0; tablePtr[count]; count++) {
21255
}
21256
21257
21258
tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
21259
memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
21260
qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
21261
tablePtrSorted[count] = NULL;
21262
21263
return tablePtrSorted;
21264
}
21265
21266
static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
21267
const char *prefix, const char *const *tablePtr, const char *name)
21268
{
21269
char **tablePtrSorted;
21270
int i;
21271
21272
if (name == NULL) {
21273
name = "option";
21274
}
21275
21276
Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
21277
tablePtrSorted = JimSortStringTable(tablePtr);
21278
for (i = 0; tablePtrSorted[i]; i++) {
21279
if (tablePtrSorted[i + 1] == NULL && i > 0) {
21280
Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
21281
}
21282
Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
21283
if (tablePtrSorted[i + 1]) {
21284
Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
21285
}
21286
}
21287
Jim_Free(tablePtrSorted);
21288
}
21289
21290
21291
int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
21292
{
21293
if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
21294
int i;
21295
char **tablePtrSorted = JimSortStringTable(tablePtr);
21296
Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
21297
for (i = 0; tablePtrSorted[i]; i++) {
21298
Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
21299
}
21300
Jim_Free(tablePtrSorted);
21301
return JIM_OK;
21302
}
21303
return JIM_ERR;
21304
}
21305
21306
static const Jim_ObjType getEnumObjType = {
21307
"get-enum",
21308
NULL,
21309
NULL,
21310
NULL,
21311
JIM_TYPE_REFERENCES
21312
};
21313
21314
int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
21315
const char *const *tablePtr, int *indexPtr, const char *name, int flags)
21316
{
21317
const char *bad = "bad ";
21318
const char *const *entryPtr = NULL;
21319
int i;
21320
int match = -1;
21321
int arglen;
21322
const char *arg;
21323
21324
if (objPtr->typePtr == &getEnumObjType) {
21325
if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
21326
*indexPtr = objPtr->internalRep.ptrIntValue.int2;
21327
return JIM_OK;
21328
}
21329
}
21330
21331
arg = Jim_GetString(objPtr, &arglen);
21332
21333
*indexPtr = -1;
21334
21335
for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
21336
if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
21337
21338
match = i;
21339
goto found;
21340
}
21341
if (flags & JIM_ENUM_ABBREV) {
21342
if (strncmp(arg, *entryPtr, arglen) == 0) {
21343
if (*arg == '-' && arglen == 1) {
21344
break;
21345
}
21346
if (match >= 0) {
21347
bad = "ambiguous ";
21348
goto ambiguous;
21349
}
21350
match = i;
21351
}
21352
}
21353
}
21354
21355
21356
if (match >= 0) {
21357
found:
21358
21359
Jim_FreeIntRep(interp, objPtr);
21360
objPtr->typePtr = &getEnumObjType;
21361
objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
21362
objPtr->internalRep.ptrIntValue.int1 = flags;
21363
objPtr->internalRep.ptrIntValue.int2 = match;
21364
21365
*indexPtr = match;
21366
return JIM_OK;
21367
}
21368
21369
ambiguous:
21370
if (flags & JIM_ERRMSG) {
21371
JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
21372
}
21373
return JIM_ERR;
21374
}
21375
21376
int Jim_FindByName(const char *name, const char * const array[], size_t len)
21377
{
21378
int i;
21379
21380
for (i = 0; i < (int)len; i++) {
21381
if (array[i] && strcmp(array[i], name) == 0) {
21382
return i;
21383
}
21384
}
21385
return -1;
21386
}
21387
21388
int Jim_IsDict(Jim_Obj *objPtr)
21389
{
21390
return objPtr->typePtr == &dictObjType;
21391
}
21392
21393
int Jim_IsList(Jim_Obj *objPtr)
21394
{
21395
return objPtr->typePtr == &listObjType;
21396
}
21397
21398
void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
21399
{
21400
21401
int len = strlen(format);
21402
int extra = 0;
21403
int n = 0;
21404
const char *params[5];
21405
int nobjparam = 0;
21406
Jim_Obj *objparam[5];
21407
char *buf;
21408
va_list args;
21409
int i;
21410
21411
va_start(args, format);
21412
21413
for (i = 0; i < len && n < 5; i++) {
21414
int l;
21415
21416
if (strncmp(format + i, "%s", 2) == 0) {
21417
params[n] = va_arg(args, char *);
21418
21419
l = strlen(params[n]);
21420
}
21421
else if (strncmp(format + i, "%#s", 3) == 0) {
21422
Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
21423
21424
params[n] = Jim_GetString(objPtr, &l);
21425
objparam[nobjparam++] = objPtr;
21426
Jim_IncrRefCount(objPtr);
21427
}
21428
else {
21429
if (format[i] == '%') {
21430
i++;
21431
}
21432
continue;
21433
}
21434
n++;
21435
extra += l;
21436
}
21437
21438
len += extra;
21439
buf = Jim_Alloc(len + 1);
21440
len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
21441
21442
va_end(args);
21443
21444
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
21445
21446
for (i = 0; i < nobjparam; i++) {
21447
Jim_DecrRefCount(interp, objparam[i]);
21448
}
21449
}
21450
21451
int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version)
21452
{
21453
if (abi_version != JIM_ABI_VERSION) {
21454
Jim_SetResultString(interp, "ABI version mismatch", -1);
21455
return JIM_ERR;
21456
}
21457
return JIM_OK;
21458
}
21459
21460
21461
#ifndef jim_ext_package
21462
int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
21463
{
21464
return JIM_OK;
21465
}
21466
#endif
21467
#ifndef jim_ext_aio
21468
int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
21469
{
21470
return -1;
21471
}
21472
#endif
21473
21474
21475
#include <stdio.h>
21476
#include <string.h>
21477
21478
21479
static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21480
{
21481
21482
return JIM_OK;
21483
}
21484
21485
static const jim_subcmd_type dummy_subcmd = {
21486
"dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
21487
};
21488
21489
static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
21490
{
21491
21492
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
21493
Jim_Obj *sortCmd[2];
21494
21495
for (; ct->cmd; ct++) {
21496
if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
21497
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1));
21498
}
21499
}
21500
21501
21502
sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1);
21503
sortCmd[1] = listObj;
21504
21505
if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) {
21506
return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep));
21507
}
21508
21509
return Jim_GetResult(interp);
21510
}
21511
21512
static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
21513
Jim_Obj *cmd, Jim_Obj *subcmd)
21514
{
21515
Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type,
21516
subcmd, subcmd_cmd_list(interp, command_table, ", "));
21517
}
21518
21519
static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
21520
Jim_Obj *const *argv)
21521
{
21522
Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s",
21523
argv[0], subcmd_cmd_list(interp, command_table, ", "));
21524
}
21525
21526
static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
21527
{
21528
if (cmd) {
21529
Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
21530
}
21531
Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
21532
if (ct->args && *ct->args) {
21533
Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
21534
}
21535
}
21536
21537
void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd)
21538
{
21539
Jim_SetResultString(interp, "wrong # args: should be \"", -1);
21540
add_cmd_usage(interp, ct, subcmd);
21541
Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
21542
}
21543
21544
static const Jim_ObjType subcmdLookupObjType = {
21545
"subcmd-lookup",
21546
NULL,
21547
NULL,
21548
NULL,
21549
JIM_TYPE_REFERENCES
21550
};
21551
21552
const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
21553
int argc, Jim_Obj *const *argv)
21554
{
21555
const jim_subcmd_type *ct;
21556
const jim_subcmd_type *partial = 0;
21557
int cmdlen;
21558
Jim_Obj *cmd;
21559
const char *cmdstr;
21560
int help = 0;
21561
int argsok = 1;
21562
21563
if (argc < 2) {
21564
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
21565
"Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
21566
return 0;
21567
}
21568
21569
cmd = argv[1];
21570
21571
21572
if (cmd->typePtr == &subcmdLookupObjType) {
21573
if (cmd->internalRep.ptrIntValue.ptr == command_table) {
21574
ct = command_table + cmd->internalRep.ptrIntValue.int1;
21575
goto found;
21576
}
21577
}
21578
21579
21580
if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
21581
if (argc == 2) {
21582
21583
show_cmd_usage(interp, command_table, argc, argv);
21584
return &dummy_subcmd;
21585
}
21586
help = 1;
21587
21588
21589
cmd = argv[2];
21590
}
21591
21592
21593
if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
21594
Jim_SetResult(interp, subcmd_cmd_list(interp, command_table, " "));
21595
return &dummy_subcmd;
21596
}
21597
21598
cmdstr = Jim_GetString(cmd, &cmdlen);
21599
21600
for (ct = command_table; ct->cmd; ct++) {
21601
if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
21602
21603
break;
21604
}
21605
if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
21606
if (partial) {
21607
21608
if (help) {
21609
21610
show_cmd_usage(interp, command_table, argc, argv);
21611
return &dummy_subcmd;
21612
}
21613
bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
21614
return 0;
21615
}
21616
partial = ct;
21617
}
21618
continue;
21619
}
21620
21621
21622
if (partial && !ct->cmd) {
21623
ct = partial;
21624
}
21625
21626
if (!ct->cmd) {
21627
21628
if (help) {
21629
21630
show_cmd_usage(interp, command_table, argc, argv);
21631
return &dummy_subcmd;
21632
}
21633
bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
21634
return 0;
21635
}
21636
21637
if (help) {
21638
Jim_SetResultString(interp, "Usage: ", -1);
21639
21640
add_cmd_usage(interp, ct, argv[0]);
21641
return &dummy_subcmd;
21642
}
21643
21644
21645
Jim_FreeIntRep(interp, cmd);
21646
cmd->typePtr = &subcmdLookupObjType;
21647
cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
21648
cmd->internalRep.ptrIntValue.int1 = ct - command_table;
21649
21650
found:
21651
21652
21653
if (argc - 2 < ct->minargs) {
21654
argsok = 0;
21655
}
21656
else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) {
21657
argsok = 0;
21658
}
21659
else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) {
21660
21661
argsok = 0;
21662
}
21663
if (!argsok) {
21664
Jim_SetResultString(interp, "wrong # args: should be \"", -1);
21665
21666
add_cmd_usage(interp, ct, argv[0]);
21667
Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
21668
21669
return 0;
21670
}
21671
21672
21673
return ct;
21674
}
21675
21676
int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
21677
{
21678
int ret = JIM_ERR;
21679
21680
if (ct) {
21681
if (ct->flags & JIM_MODFLAG_FULLARGV) {
21682
ret = ct->function(interp, argc, argv);
21683
}
21684
else {
21685
ret = ct->function(interp, argc - 2, argv + 2);
21686
}
21687
if (ret < 0) {
21688
Jim_SubCmdArgError(interp, ct, argv[0]);
21689
ret = JIM_ERR;
21690
}
21691
}
21692
return ret;
21693
}
21694
21695
int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21696
{
21697
const jim_subcmd_type *ct =
21698
Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
21699
21700
return Jim_CallSubCmd(interp, ct, argc, argv);
21701
}
21702
21703
#include <ctype.h>
21704
#include <stdlib.h>
21705
#include <string.h>
21706
#include <stdio.h>
21707
#include <assert.h>
21708
21709
21710
int utf8_fromunicode(char *p, unsigned uc)
21711
{
21712
if (uc <= 0x7f) {
21713
*p = uc;
21714
return 1;
21715
}
21716
else if (uc <= 0x7ff) {
21717
*p++ = 0xc0 | ((uc & 0x7c0) >> 6);
21718
*p = 0x80 | (uc & 0x3f);
21719
return 2;
21720
}
21721
else if (uc <= 0xffff) {
21722
*p++ = 0xe0 | ((uc & 0xf000) >> 12);
21723
*p++ = 0x80 | ((uc & 0xfc0) >> 6);
21724
*p = 0x80 | (uc & 0x3f);
21725
return 3;
21726
}
21727
21728
else {
21729
*p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
21730
*p++ = 0x80 | ((uc & 0x3f000) >> 12);
21731
*p++ = 0x80 | ((uc & 0xfc0) >> 6);
21732
*p = 0x80 | (uc & 0x3f);
21733
return 4;
21734
}
21735
}
21736
21737
#include <ctype.h>
21738
#include <string.h>
21739
#include <stdio.h>
21740
21741
21742
#define JIM_INTEGER_SPACE 24
21743
#define MAX_FLOAT_WIDTH 320
21744
21745
Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
21746
{
21747
const char *span, *format, *formatEnd, *msg;
21748
int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
21749
static const char * const mixedXPG =
21750
"cannot mix \"%\" and \"%n$\" conversion specifiers";
21751
static const char * const badIndex[2] = {
21752
"not enough arguments for all format specifiers",
21753
"\"%n$\" argument index out of range"
21754
};
21755
int formatLen;
21756
Jim_Obj *resultPtr;
21757
21758
char *num_buffer = NULL;
21759
int num_buffer_size = 0;
21760
21761
span = format = Jim_GetString(fmtObjPtr, &formatLen);
21762
formatEnd = format + formatLen;
21763
resultPtr = Jim_NewEmptyStringObj(interp);
21764
21765
while (format != formatEnd) {
21766
char *end;
21767
int gotMinus, sawFlag;
21768
int gotPrecision, useShort;
21769
long width, precision;
21770
int newXpg;
21771
int ch;
21772
int step;
21773
int doubleType;
21774
char pad = ' ';
21775
char spec[2*JIM_INTEGER_SPACE + 12];
21776
char *p;
21777
21778
int formatted_chars;
21779
int formatted_bytes;
21780
const char *formatted_buf;
21781
21782
step = utf8_tounicode(format, &ch);
21783
format += step;
21784
if (ch != '%') {
21785
numBytes += step;
21786
continue;
21787
}
21788
if (numBytes) {
21789
Jim_AppendString(interp, resultPtr, span, numBytes);
21790
numBytes = 0;
21791
}
21792
21793
21794
step = utf8_tounicode(format, &ch);
21795
if (ch == '%') {
21796
span = format;
21797
numBytes = step;
21798
format += step;
21799
continue;
21800
}
21801
21802
21803
newXpg = 0;
21804
if (isdigit(ch)) {
21805
int position = strtoul(format, &end, 10);
21806
if (*end == '$') {
21807
newXpg = 1;
21808
objIndex = position - 1;
21809
format = end + 1;
21810
step = utf8_tounicode(format, &ch);
21811
}
21812
}
21813
if (newXpg) {
21814
if (gotSequential) {
21815
msg = mixedXPG;
21816
goto errorMsg;
21817
}
21818
gotXpg = 1;
21819
} else {
21820
if (gotXpg) {
21821
msg = mixedXPG;
21822
goto errorMsg;
21823
}
21824
gotSequential = 1;
21825
}
21826
if ((objIndex < 0) || (objIndex >= objc)) {
21827
msg = badIndex[gotXpg];
21828
goto errorMsg;
21829
}
21830
21831
p = spec;
21832
*p++ = '%';
21833
21834
gotMinus = 0;
21835
sawFlag = 1;
21836
do {
21837
switch (ch) {
21838
case '-':
21839
gotMinus = 1;
21840
break;
21841
case '0':
21842
pad = ch;
21843
break;
21844
case ' ':
21845
case '+':
21846
case '#':
21847
break;
21848
default:
21849
sawFlag = 0;
21850
continue;
21851
}
21852
*p++ = ch;
21853
format += step;
21854
step = utf8_tounicode(format, &ch);
21855
21856
} while (sawFlag && (p - spec <= 5));
21857
21858
21859
width = 0;
21860
if (isdigit(ch)) {
21861
width = strtoul(format, &end, 10);
21862
format = end;
21863
step = utf8_tounicode(format, &ch);
21864
} else if (ch == '*') {
21865
if (objIndex >= objc - 1) {
21866
msg = badIndex[gotXpg];
21867
goto errorMsg;
21868
}
21869
if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
21870
goto error;
21871
}
21872
if (width < 0) {
21873
width = -width;
21874
if (!gotMinus) {
21875
*p++ = '-';
21876
gotMinus = 1;
21877
}
21878
}
21879
objIndex++;
21880
format += step;
21881
step = utf8_tounicode(format, &ch);
21882
}
21883
21884
21885
gotPrecision = precision = 0;
21886
if (ch == '.') {
21887
gotPrecision = 1;
21888
format += step;
21889
step = utf8_tounicode(format, &ch);
21890
}
21891
if (isdigit(ch)) {
21892
precision = strtoul(format, &end, 10);
21893
format = end;
21894
step = utf8_tounicode(format, &ch);
21895
} else if (ch == '*') {
21896
if (objIndex >= objc - 1) {
21897
msg = badIndex[gotXpg];
21898
goto errorMsg;
21899
}
21900
if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
21901
goto error;
21902
}
21903
21904
21905
if (precision < 0) {
21906
precision = 0;
21907
}
21908
objIndex++;
21909
format += step;
21910
step = utf8_tounicode(format, &ch);
21911
}
21912
21913
21914
useShort = 0;
21915
if (ch == 'h') {
21916
useShort = 1;
21917
format += step;
21918
step = utf8_tounicode(format, &ch);
21919
} else if (ch == 'l') {
21920
21921
format += step;
21922
step = utf8_tounicode(format, &ch);
21923
if (ch == 'l') {
21924
format += step;
21925
step = utf8_tounicode(format, &ch);
21926
}
21927
}
21928
21929
format += step;
21930
span = format;
21931
21932
21933
if (ch == 'i') {
21934
ch = 'd';
21935
}
21936
21937
doubleType = 0;
21938
21939
switch (ch) {
21940
case '\0':
21941
msg = "format string ended in middle of field specifier";
21942
goto errorMsg;
21943
case 's': {
21944
formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
21945
formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
21946
if (gotPrecision && (precision < formatted_chars)) {
21947
21948
formatted_chars = precision;
21949
formatted_bytes = utf8_index(formatted_buf, precision);
21950
}
21951
break;
21952
}
21953
case 'c': {
21954
jim_wide code;
21955
21956
if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
21957
goto error;
21958
}
21959
21960
formatted_bytes = utf8_getchars(spec, code);
21961
formatted_buf = spec;
21962
formatted_chars = 1;
21963
break;
21964
}
21965
case 'b': {
21966
unsigned jim_wide w;
21967
int length;
21968
int i;
21969
int j;
21970
21971
if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
21972
goto error;
21973
}
21974
length = sizeof(w) * 8;
21975
21976
21977
21978
if (num_buffer_size < length + 1) {
21979
num_buffer_size = length + 1;
21980
num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
21981
}
21982
21983
j = 0;
21984
for (i = length; i > 0; ) {
21985
i--;
21986
if (w & ((unsigned jim_wide)1 << i)) {
21987
num_buffer[j++] = '1';
21988
}
21989
else if (j || i == 0) {
21990
num_buffer[j++] = '0';
21991
}
21992
}
21993
num_buffer[j] = 0;
21994
formatted_chars = formatted_bytes = j;
21995
formatted_buf = num_buffer;
21996
break;
21997
}
21998
21999
case 'e':
22000
case 'E':
22001
case 'f':
22002
case 'g':
22003
case 'G':
22004
doubleType = 1;
22005
22006
case 'd':
22007
case 'u':
22008
case 'o':
22009
case 'x':
22010
case 'X': {
22011
jim_wide w;
22012
double d;
22013
int length;
22014
22015
22016
if (width) {
22017
p += sprintf(p, "%ld", width);
22018
}
22019
if (gotPrecision) {
22020
p += sprintf(p, ".%ld", precision);
22021
}
22022
22023
22024
if (doubleType) {
22025
if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
22026
goto error;
22027
}
22028
length = MAX_FLOAT_WIDTH;
22029
}
22030
else {
22031
if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
22032
goto error;
22033
}
22034
length = JIM_INTEGER_SPACE;
22035
if (useShort) {
22036
if (ch == 'd') {
22037
w = (short)w;
22038
}
22039
else {
22040
w = (unsigned short)w;
22041
}
22042
}
22043
*p++ = 'l';
22044
#ifdef HAVE_LONG_LONG
22045
if (sizeof(long long) == sizeof(jim_wide)) {
22046
*p++ = 'l';
22047
}
22048
#endif
22049
}
22050
22051
*p++ = (char) ch;
22052
*p = '\0';
22053
22054
22055
if (width > 10000 || length > 10000 || precision > 10000) {
22056
Jim_SetResultString(interp, "format too long", -1);
22057
goto error;
22058
}
22059
22060
22061
22062
if (width > length) {
22063
length = width;
22064
}
22065
if (gotPrecision) {
22066
length += precision;
22067
}
22068
22069
22070
if (num_buffer_size < length + 1) {
22071
num_buffer_size = length + 1;
22072
num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
22073
}
22074
22075
if (doubleType) {
22076
snprintf(num_buffer, length + 1, spec, d);
22077
}
22078
else {
22079
formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
22080
}
22081
formatted_chars = formatted_bytes = strlen(num_buffer);
22082
formatted_buf = num_buffer;
22083
break;
22084
}
22085
22086
default: {
22087
22088
spec[0] = ch;
22089
spec[1] = '\0';
22090
Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
22091
goto error;
22092
}
22093
}
22094
22095
if (!gotMinus) {
22096
while (formatted_chars < width) {
22097
Jim_AppendString(interp, resultPtr, &pad, 1);
22098
formatted_chars++;
22099
}
22100
}
22101
22102
Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
22103
22104
while (formatted_chars < width) {
22105
Jim_AppendString(interp, resultPtr, &pad, 1);
22106
formatted_chars++;
22107
}
22108
22109
objIndex += gotSequential;
22110
}
22111
if (numBytes) {
22112
Jim_AppendString(interp, resultPtr, span, numBytes);
22113
}
22114
22115
Jim_Free(num_buffer);
22116
return resultPtr;
22117
22118
errorMsg:
22119
Jim_SetResultString(interp, msg, -1);
22120
error:
22121
Jim_FreeNewObj(interp, resultPtr);
22122
Jim_Free(num_buffer);
22123
return NULL;
22124
}
22125
22126
22127
#if defined(JIM_REGEXP)
22128
#include <stdio.h>
22129
#include <ctype.h>
22130
#include <stdlib.h>
22131
#include <string.h>
22132
22133
22134
22135
#define REG_MAX_PAREN 100
22136
22137
22138
22139
#define END 0
22140
#define BOL 1
22141
#define EOL 2
22142
#define ANY 3
22143
#define ANYOF 4
22144
#define ANYBUT 5
22145
#define BRANCH 6
22146
#define BACK 7
22147
#define EXACTLY 8
22148
#define NOTHING 9
22149
#define REP 10
22150
#define REPMIN 11
22151
#define REPX 12
22152
#define REPXMIN 13
22153
#define BOLX 14
22154
#define EOLX 15
22155
#define WORDA 16
22156
#define WORDZ 17
22157
22158
#define OPENNC 1000
22159
#define OPEN 1001
22160
22161
22162
22163
22164
#define CLOSENC 2000
22165
#define CLOSE 2001
22166
#define CLOSE_END (CLOSE+REG_MAX_PAREN)
22167
22168
#define REG_MAGIC 0xFADED00D
22169
22170
22171
#define OP(preg, p) (preg->program[p])
22172
#define NEXT(preg, p) (preg->program[p + 1])
22173
#define OPERAND(p) ((p) + 2)
22174
22175
22176
22177
22178
#define FAIL(R,M) { (R)->err = (M); return (M); }
22179
#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
22180
#define META "^$.[()|?{+*"
22181
22182
#define HASWIDTH 1
22183
#define SIMPLE 2
22184
#define SPSTART 4
22185
#define WORST 0
22186
22187
#define MAX_REP_COUNT 1000000
22188
22189
static int reg(regex_t *preg, int paren, int *flagp );
22190
static int regpiece(regex_t *preg, int *flagp );
22191
static int regbranch(regex_t *preg, int *flagp );
22192
static int regatom(regex_t *preg, int *flagp );
22193
static int regnode(regex_t *preg, int op );
22194
static int regnext(regex_t *preg, int p );
22195
static void regc(regex_t *preg, int b );
22196
static int reginsert(regex_t *preg, int op, int size, int opnd );
22197
static void regtail(regex_t *preg, int p, int val);
22198
static void regoptail(regex_t *preg, int p, int val );
22199
static int regopsize(regex_t *preg, int p );
22200
22201
static int reg_range_find(const int *string, int c);
22202
static const char *str_find(const char *string, int c, int nocase);
22203
static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
22204
22205
22206
#ifdef DEBUG
22207
static int regnarrate = 0;
22208
static void regdump(regex_t *preg);
22209
static const char *regprop( int op );
22210
#endif
22211
22212
22213
static int str_int_len(const int *seq)
22214
{
22215
int n = 0;
22216
while (*seq++) {
22217
n++;
22218
}
22219
return n;
22220
}
22221
22222
int jim_regcomp(regex_t *preg, const char *exp, int cflags)
22223
{
22224
int scan;
22225
int longest;
22226
unsigned len;
22227
int flags;
22228
22229
#ifdef DEBUG
22230
fprintf(stderr, "Compiling: '%s'\n", exp);
22231
#endif
22232
memset(preg, 0, sizeof(*preg));
22233
22234
if (exp == NULL)
22235
FAIL(preg, REG_ERR_NULL_ARGUMENT);
22236
22237
22238
preg->cflags = cflags;
22239
preg->regparse = exp;
22240
22241
22242
preg->proglen = (strlen(exp) + 1) * 5;
22243
preg->program = malloc(preg->proglen * sizeof(int));
22244
if (preg->program == NULL)
22245
FAIL(preg, REG_ERR_NOMEM);
22246
22247
regc(preg, REG_MAGIC);
22248
if (reg(preg, 0, &flags) == 0) {
22249
return preg->err;
22250
}
22251
22252
22253
if (preg->re_nsub >= REG_MAX_PAREN)
22254
FAIL(preg,REG_ERR_TOO_BIG);
22255
22256
22257
preg->regstart = 0;
22258
preg->reganch = 0;
22259
preg->regmust = 0;
22260
preg->regmlen = 0;
22261
scan = 1;
22262
if (OP(preg, regnext(preg, scan)) == END) {
22263
scan = OPERAND(scan);
22264
22265
22266
if (OP(preg, scan) == EXACTLY) {
22267
preg->regstart = preg->program[OPERAND(scan)];
22268
}
22269
else if (OP(preg, scan) == BOL)
22270
preg->reganch++;
22271
22272
if (flags&SPSTART) {
22273
longest = 0;
22274
len = 0;
22275
for (; scan != 0; scan = regnext(preg, scan)) {
22276
if (OP(preg, scan) == EXACTLY) {
22277
int plen = str_int_len(preg->program + OPERAND(scan));
22278
if (plen >= len) {
22279
longest = OPERAND(scan);
22280
len = plen;
22281
}
22282
}
22283
}
22284
preg->regmust = longest;
22285
preg->regmlen = len;
22286
}
22287
}
22288
22289
#ifdef DEBUG
22290
regdump(preg);
22291
#endif
22292
22293
return 0;
22294
}
22295
22296
static int reg(regex_t *preg, int paren, int *flagp )
22297
{
22298
int ret;
22299
int br;
22300
int ender;
22301
int parno = 0;
22302
int flags;
22303
22304
*flagp = HASWIDTH;
22305
22306
22307
if (paren) {
22308
if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
22309
22310
preg->regparse += 2;
22311
parno = -1;
22312
}
22313
else {
22314
parno = ++preg->re_nsub;
22315
}
22316
ret = regnode(preg, OPEN+parno);
22317
} else
22318
ret = 0;
22319
22320
22321
br = regbranch(preg, &flags);
22322
if (br == 0)
22323
return 0;
22324
if (ret != 0)
22325
regtail(preg, ret, br);
22326
else
22327
ret = br;
22328
if (!(flags&HASWIDTH))
22329
*flagp &= ~HASWIDTH;
22330
*flagp |= flags&SPSTART;
22331
while (*preg->regparse == '|') {
22332
preg->regparse++;
22333
br = regbranch(preg, &flags);
22334
if (br == 0)
22335
return 0;
22336
regtail(preg, ret, br);
22337
if (!(flags&HASWIDTH))
22338
*flagp &= ~HASWIDTH;
22339
*flagp |= flags&SPSTART;
22340
}
22341
22342
22343
ender = regnode(preg, (paren) ? CLOSE+parno : END);
22344
regtail(preg, ret, ender);
22345
22346
22347
for (br = ret; br != 0; br = regnext(preg, br))
22348
regoptail(preg, br, ender);
22349
22350
22351
if (paren && *preg->regparse++ != ')') {
22352
preg->err = REG_ERR_UNMATCHED_PAREN;
22353
return 0;
22354
} else if (!paren && *preg->regparse != '\0') {
22355
if (*preg->regparse == ')') {
22356
preg->err = REG_ERR_UNMATCHED_PAREN;
22357
return 0;
22358
} else {
22359
preg->err = REG_ERR_JUNK_ON_END;
22360
return 0;
22361
}
22362
}
22363
22364
return(ret);
22365
}
22366
22367
static int regbranch(regex_t *preg, int *flagp )
22368
{
22369
int ret;
22370
int chain;
22371
int latest;
22372
int flags;
22373
22374
*flagp = WORST;
22375
22376
ret = regnode(preg, BRANCH);
22377
chain = 0;
22378
while (*preg->regparse != '\0' && *preg->regparse != ')' &&
22379
*preg->regparse != '|') {
22380
latest = regpiece(preg, &flags);
22381
if (latest == 0)
22382
return 0;
22383
*flagp |= flags&HASWIDTH;
22384
if (chain == 0) {
22385
*flagp |= flags&SPSTART;
22386
}
22387
else {
22388
regtail(preg, chain, latest);
22389
}
22390
chain = latest;
22391
}
22392
if (chain == 0)
22393
(void) regnode(preg, NOTHING);
22394
22395
return(ret);
22396
}
22397
22398
static int regpiece(regex_t *preg, int *flagp)
22399
{
22400
int ret;
22401
char op;
22402
int next;
22403
int flags;
22404
int min;
22405
int max;
22406
22407
ret = regatom(preg, &flags);
22408
if (ret == 0)
22409
return 0;
22410
22411
op = *preg->regparse;
22412
if (!ISMULT(op)) {
22413
*flagp = flags;
22414
return(ret);
22415
}
22416
22417
if (!(flags&HASWIDTH) && op != '?') {
22418
preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
22419
return 0;
22420
}
22421
22422
22423
if (op == '{') {
22424
char *end;
22425
22426
min = strtoul(preg->regparse + 1, &end, 10);
22427
if (end == preg->regparse + 1) {
22428
preg->err = REG_ERR_BAD_COUNT;
22429
return 0;
22430
}
22431
if (*end == '}') {
22432
max = min;
22433
}
22434
else if (*end == '\0') {
22435
preg->err = REG_ERR_UNMATCHED_BRACES;
22436
return 0;
22437
}
22438
else {
22439
preg->regparse = end;
22440
max = strtoul(preg->regparse + 1, &end, 10);
22441
if (*end != '}') {
22442
preg->err = REG_ERR_UNMATCHED_BRACES;
22443
return 0;
22444
}
22445
}
22446
if (end == preg->regparse + 1) {
22447
max = MAX_REP_COUNT;
22448
}
22449
else if (max < min || max >= 100) {
22450
preg->err = REG_ERR_BAD_COUNT;
22451
return 0;
22452
}
22453
if (min >= 100) {
22454
preg->err = REG_ERR_BAD_COUNT;
22455
return 0;
22456
}
22457
22458
preg->regparse = strchr(preg->regparse, '}');
22459
}
22460
else {
22461
min = (op == '+');
22462
max = (op == '?' ? 1 : MAX_REP_COUNT);
22463
}
22464
22465
if (preg->regparse[1] == '?') {
22466
preg->regparse++;
22467
next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
22468
}
22469
else {
22470
next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
22471
}
22472
preg->program[ret + 2] = max;
22473
preg->program[ret + 3] = min;
22474
preg->program[ret + 4] = 0;
22475
22476
*flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
22477
22478
if (!(flags & SIMPLE)) {
22479
int back = regnode(preg, BACK);
22480
regtail(preg, back, ret);
22481
regtail(preg, next, back);
22482
}
22483
22484
preg->regparse++;
22485
if (ISMULT(*preg->regparse)) {
22486
preg->err = REG_ERR_NESTED_COUNT;
22487
return 0;
22488
}
22489
22490
return ret;
22491
}
22492
22493
static void reg_addrange(regex_t *preg, int lower, int upper)
22494
{
22495
if (lower > upper) {
22496
reg_addrange(preg, upper, lower);
22497
}
22498
22499
regc(preg, upper - lower + 1);
22500
regc(preg, lower);
22501
}
22502
22503
static void reg_addrange_str(regex_t *preg, const char *str)
22504
{
22505
while (*str) {
22506
reg_addrange(preg, *str, *str);
22507
str++;
22508
}
22509
}
22510
22511
static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
22512
{
22513
int l = utf8_tounicode(s, uc);
22514
if (upper) {
22515
*uc = utf8_upper(*uc);
22516
}
22517
return l;
22518
}
22519
22520
static int hexdigitval(int c)
22521
{
22522
if (c >= '0' && c <= '9')
22523
return c - '0';
22524
if (c >= 'a' && c <= 'f')
22525
return c - 'a' + 10;
22526
if (c >= 'A' && c <= 'F')
22527
return c - 'A' + 10;
22528
return -1;
22529
}
22530
22531
static int parse_hex(const char *s, int n, int *uc)
22532
{
22533
int val = 0;
22534
int k;
22535
22536
for (k = 0; k < n; k++) {
22537
int c = hexdigitval(*s++);
22538
if (c == -1) {
22539
break;
22540
}
22541
val = (val << 4) | c;
22542
}
22543
if (k) {
22544
*uc = val;
22545
}
22546
return k;
22547
}
22548
22549
static int reg_decode_escape(const char *s, int *ch)
22550
{
22551
int n;
22552
const char *s0 = s;
22553
22554
*ch = *s++;
22555
22556
switch (*ch) {
22557
case 'b': *ch = '\b'; break;
22558
case 'e': *ch = 27; break;
22559
case 'f': *ch = '\f'; break;
22560
case 'n': *ch = '\n'; break;
22561
case 'r': *ch = '\r'; break;
22562
case 't': *ch = '\t'; break;
22563
case 'v': *ch = '\v'; break;
22564
case 'u':
22565
if (*s == '{') {
22566
22567
n = parse_hex(s + 1, 6, ch);
22568
if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
22569
s += n + 2;
22570
}
22571
else {
22572
22573
*ch = 'u';
22574
}
22575
}
22576
else if ((n = parse_hex(s, 4, ch)) > 0) {
22577
s += n;
22578
}
22579
break;
22580
case 'U':
22581
if ((n = parse_hex(s, 8, ch)) > 0) {
22582
s += n;
22583
}
22584
break;
22585
case 'x':
22586
if ((n = parse_hex(s, 2, ch)) > 0) {
22587
s += n;
22588
}
22589
break;
22590
case '\0':
22591
s--;
22592
*ch = '\\';
22593
break;
22594
}
22595
return s - s0;
22596
}
22597
22598
static int regatom(regex_t *preg, int *flagp)
22599
{
22600
int ret;
22601
int flags;
22602
int nocase = (preg->cflags & REG_ICASE);
22603
22604
int ch;
22605
int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
22606
22607
*flagp = WORST;
22608
22609
preg->regparse += n;
22610
switch (ch) {
22611
22612
case '^':
22613
ret = regnode(preg, BOL);
22614
break;
22615
case '$':
22616
ret = regnode(preg, EOL);
22617
break;
22618
case '.':
22619
ret = regnode(preg, ANY);
22620
*flagp |= HASWIDTH|SIMPLE;
22621
break;
22622
case '[': {
22623
const char *pattern = preg->regparse;
22624
22625
if (*pattern == '^') {
22626
ret = regnode(preg, ANYBUT);
22627
pattern++;
22628
} else
22629
ret = regnode(preg, ANYOF);
22630
22631
22632
if (*pattern == ']' || *pattern == '-') {
22633
reg_addrange(preg, *pattern, *pattern);
22634
pattern++;
22635
}
22636
22637
while (*pattern != ']') {
22638
22639
int start;
22640
int end;
22641
22642
enum {
22643
CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
22644
CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
22645
CC_NUM
22646
};
22647
int cc;
22648
22649
if (!*pattern) {
22650
preg->err = REG_ERR_UNMATCHED_BRACKET;
22651
return 0;
22652
}
22653
22654
pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
22655
if (start == '\\') {
22656
22657
switch (*pattern) {
22658
case 's':
22659
pattern++;
22660
cc = CC_SPACE;
22661
goto cc_switch;
22662
case 'd':
22663
pattern++;
22664
cc = CC_DIGIT;
22665
goto cc_switch;
22666
case 'w':
22667
pattern++;
22668
reg_addrange(preg, '_', '_');
22669
cc = CC_ALNUM;
22670
goto cc_switch;
22671
}
22672
pattern += reg_decode_escape(pattern, &start);
22673
if (start == 0) {
22674
preg->err = REG_ERR_NULL_CHAR;
22675
return 0;
22676
}
22677
if (start == '\\' && *pattern == 0) {
22678
preg->err = REG_ERR_INVALID_ESCAPE;
22679
return 0;
22680
}
22681
}
22682
if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
22683
22684
pattern += utf8_tounicode(pattern, &end);
22685
pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
22686
if (end == '\\') {
22687
pattern += reg_decode_escape(pattern, &end);
22688
if (end == 0) {
22689
preg->err = REG_ERR_NULL_CHAR;
22690
return 0;
22691
}
22692
if (end == '\\' && *pattern == 0) {
22693
preg->err = REG_ERR_INVALID_ESCAPE;
22694
return 0;
22695
}
22696
}
22697
22698
reg_addrange(preg, start, end);
22699
continue;
22700
}
22701
if (start == '[' && pattern[0] == ':') {
22702
static const char *character_class[] = {
22703
":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
22704
":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
22705
};
22706
22707
for (cc = 0; cc < CC_NUM; cc++) {
22708
n = strlen(character_class[cc]);
22709
if (strncmp(pattern, character_class[cc], n) == 0) {
22710
if (pattern[n] != ']') {
22711
preg->err = REG_ERR_UNMATCHED_BRACKET;
22712
return 0;
22713
}
22714
22715
pattern += n + 1;
22716
break;
22717
}
22718
}
22719
if (cc != CC_NUM) {
22720
cc_switch:
22721
switch (cc) {
22722
case CC_ALNUM:
22723
reg_addrange(preg, '0', '9');
22724
22725
case CC_ALPHA:
22726
if ((preg->cflags & REG_ICASE) == 0) {
22727
reg_addrange(preg, 'a', 'z');
22728
}
22729
reg_addrange(preg, 'A', 'Z');
22730
break;
22731
case CC_SPACE:
22732
reg_addrange_str(preg, " \t\r\n\f\v");
22733
break;
22734
case CC_BLANK:
22735
reg_addrange_str(preg, " \t");
22736
break;
22737
case CC_UPPER:
22738
reg_addrange(preg, 'A', 'Z');
22739
break;
22740
case CC_LOWER:
22741
reg_addrange(preg, 'a', 'z');
22742
break;
22743
case CC_XDIGIT:
22744
reg_addrange(preg, 'a', 'f');
22745
reg_addrange(preg, 'A', 'F');
22746
22747
case CC_DIGIT:
22748
reg_addrange(preg, '0', '9');
22749
break;
22750
case CC_CNTRL:
22751
reg_addrange(preg, 0, 31);
22752
reg_addrange(preg, 127, 127);
22753
break;
22754
case CC_PRINT:
22755
reg_addrange(preg, ' ', '~');
22756
break;
22757
case CC_GRAPH:
22758
reg_addrange(preg, '!', '~');
22759
break;
22760
case CC_PUNCT:
22761
reg_addrange(preg, '!', '/');
22762
reg_addrange(preg, ':', '@');
22763
reg_addrange(preg, '[', '`');
22764
reg_addrange(preg, '{', '~');
22765
break;
22766
}
22767
continue;
22768
}
22769
}
22770
22771
reg_addrange(preg, start, start);
22772
}
22773
regc(preg, '\0');
22774
22775
if (*pattern) {
22776
pattern++;
22777
}
22778
preg->regparse = pattern;
22779
22780
*flagp |= HASWIDTH|SIMPLE;
22781
}
22782
break;
22783
case '(':
22784
ret = reg(preg, 1, &flags);
22785
if (ret == 0)
22786
return 0;
22787
*flagp |= flags&(HASWIDTH|SPSTART);
22788
break;
22789
case '\0':
22790
case '|':
22791
case ')':
22792
preg->err = REG_ERR_INTERNAL;
22793
return 0;
22794
case '?':
22795
case '+':
22796
case '*':
22797
case '{':
22798
preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
22799
return 0;
22800
case '\\':
22801
ch = *preg->regparse++;
22802
switch (ch) {
22803
case '\0':
22804
preg->err = REG_ERR_INVALID_ESCAPE;
22805
return 0;
22806
case 'A':
22807
ret = regnode(preg, BOLX);
22808
break;
22809
case 'Z':
22810
ret = regnode(preg, EOLX);
22811
break;
22812
case '<':
22813
case 'm':
22814
ret = regnode(preg, WORDA);
22815
break;
22816
case '>':
22817
case 'M':
22818
ret = regnode(preg, WORDZ);
22819
break;
22820
case 'd':
22821
case 'D':
22822
ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
22823
reg_addrange(preg, '0', '9');
22824
regc(preg, '\0');
22825
*flagp |= HASWIDTH|SIMPLE;
22826
break;
22827
case 'w':
22828
case 'W':
22829
ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
22830
if ((preg->cflags & REG_ICASE) == 0) {
22831
reg_addrange(preg, 'a', 'z');
22832
}
22833
reg_addrange(preg, 'A', 'Z');
22834
reg_addrange(preg, '0', '9');
22835
reg_addrange(preg, '_', '_');
22836
regc(preg, '\0');
22837
*flagp |= HASWIDTH|SIMPLE;
22838
break;
22839
case 's':
22840
case 'S':
22841
ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
22842
reg_addrange_str(preg," \t\r\n\f\v");
22843
regc(preg, '\0');
22844
*flagp |= HASWIDTH|SIMPLE;
22845
break;
22846
22847
default:
22848
22849
22850
preg->regparse--;
22851
goto de_fault;
22852
}
22853
break;
22854
de_fault:
22855
default: {
22856
int added = 0;
22857
22858
22859
preg->regparse -= n;
22860
22861
ret = regnode(preg, EXACTLY);
22862
22863
22864
22865
while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
22866
n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
22867
if (ch == '\\' && preg->regparse[n]) {
22868
if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
22869
22870
break;
22871
}
22872
n += reg_decode_escape(preg->regparse + n, &ch);
22873
if (ch == 0) {
22874
preg->err = REG_ERR_NULL_CHAR;
22875
return 0;
22876
}
22877
}
22878
22879
22880
if (ISMULT(preg->regparse[n])) {
22881
22882
if (added) {
22883
22884
break;
22885
}
22886
22887
regc(preg, ch);
22888
added++;
22889
preg->regparse += n;
22890
break;
22891
}
22892
22893
22894
regc(preg, ch);
22895
added++;
22896
preg->regparse += n;
22897
}
22898
regc(preg, '\0');
22899
22900
*flagp |= HASWIDTH;
22901
if (added == 1)
22902
*flagp |= SIMPLE;
22903
break;
22904
}
22905
break;
22906
}
22907
22908
return(ret);
22909
}
22910
22911
static void reg_grow(regex_t *preg, int n)
22912
{
22913
if (preg->p + n >= preg->proglen) {
22914
preg->proglen = (preg->p + n) * 2;
22915
preg->program = realloc(preg->program, preg->proglen * sizeof(int));
22916
}
22917
}
22918
22919
22920
static int regnode(regex_t *preg, int op)
22921
{
22922
reg_grow(preg, 2);
22923
22924
22925
preg->program[preg->p++] = op;
22926
preg->program[preg->p++] = 0;
22927
22928
22929
return preg->p - 2;
22930
}
22931
22932
static void regc(regex_t *preg, int b )
22933
{
22934
reg_grow(preg, 1);
22935
preg->program[preg->p++] = b;
22936
}
22937
22938
static int reginsert(regex_t *preg, int op, int size, int opnd )
22939
{
22940
reg_grow(preg, size);
22941
22942
22943
memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
22944
22945
memset(preg->program + opnd, 0, sizeof(int) * size);
22946
22947
preg->program[opnd] = op;
22948
22949
preg->p += size;
22950
22951
return opnd + size;
22952
}
22953
22954
static void regtail(regex_t *preg, int p, int val)
22955
{
22956
int scan;
22957
int temp;
22958
int offset;
22959
22960
22961
scan = p;
22962
for (;;) {
22963
temp = regnext(preg, scan);
22964
if (temp == 0)
22965
break;
22966
scan = temp;
22967
}
22968
22969
if (OP(preg, scan) == BACK)
22970
offset = scan - val;
22971
else
22972
offset = val - scan;
22973
22974
preg->program[scan + 1] = offset;
22975
}
22976
22977
22978
static void regoptail(regex_t *preg, int p, int val )
22979
{
22980
22981
if (p != 0 && OP(preg, p) == BRANCH) {
22982
regtail(preg, OPERAND(p), val);
22983
}
22984
}
22985
22986
22987
static int regtry(regex_t *preg, const char *string );
22988
static int regmatch(regex_t *preg, int prog);
22989
static int regrepeat(regex_t *preg, int p, int max);
22990
22991
int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
22992
{
22993
const char *s;
22994
int scan;
22995
22996
22997
if (preg == NULL || preg->program == NULL || string == NULL) {
22998
return REG_ERR_NULL_ARGUMENT;
22999
}
23000
23001
23002
if (*preg->program != REG_MAGIC) {
23003
return REG_ERR_CORRUPTED;
23004
}
23005
23006
#ifdef DEBUG
23007
fprintf(stderr, "regexec: %s\n", string);
23008
regdump(preg);
23009
#endif
23010
23011
preg->eflags = eflags;
23012
preg->pmatch = pmatch;
23013
preg->nmatch = nmatch;
23014
preg->start = string;
23015
23016
23017
for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
23018
int op = OP(preg, scan);
23019
if (op == END)
23020
break;
23021
if (op == REPX || op == REPXMIN)
23022
preg->program[scan + 4] = 0;
23023
}
23024
23025
23026
if (preg->regmust != 0) {
23027
s = string;
23028
while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
23029
if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
23030
break;
23031
}
23032
s++;
23033
}
23034
if (s == NULL)
23035
return REG_NOMATCH;
23036
}
23037
23038
23039
preg->regbol = string;
23040
23041
23042
if (preg->reganch) {
23043
if (eflags & REG_NOTBOL) {
23044
23045
goto nextline;
23046
}
23047
while (1) {
23048
if (regtry(preg, string)) {
23049
return REG_NOERROR;
23050
}
23051
if (*string) {
23052
nextline:
23053
if (preg->cflags & REG_NEWLINE) {
23054
23055
string = strchr(string, '\n');
23056
if (string) {
23057
preg->regbol = ++string;
23058
continue;
23059
}
23060
}
23061
}
23062
return REG_NOMATCH;
23063
}
23064
}
23065
23066
23067
s = string;
23068
if (preg->regstart != '\0') {
23069
23070
while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
23071
if (regtry(preg, s))
23072
return REG_NOERROR;
23073
s++;
23074
}
23075
}
23076
else
23077
23078
while (1) {
23079
if (regtry(preg, s))
23080
return REG_NOERROR;
23081
if (*s == '\0') {
23082
break;
23083
}
23084
else {
23085
int c;
23086
s += utf8_tounicode(s, &c);
23087
}
23088
}
23089
23090
23091
return REG_NOMATCH;
23092
}
23093
23094
23095
static int regtry( regex_t *preg, const char *string )
23096
{
23097
int i;
23098
23099
preg->reginput = string;
23100
23101
for (i = 0; i < preg->nmatch; i++) {
23102
preg->pmatch[i].rm_so = -1;
23103
preg->pmatch[i].rm_eo = -1;
23104
}
23105
if (regmatch(preg, 1)) {
23106
preg->pmatch[0].rm_so = string - preg->start;
23107
preg->pmatch[0].rm_eo = preg->reginput - preg->start;
23108
return(1);
23109
} else
23110
return(0);
23111
}
23112
23113
static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
23114
{
23115
const char *s = string;
23116
while (proglen && *s) {
23117
int ch;
23118
int n = reg_utf8_tounicode_case(s, &ch, nocase);
23119
if (ch != *prog) {
23120
return -1;
23121
}
23122
prog++;
23123
s += n;
23124
proglen--;
23125
}
23126
if (proglen == 0) {
23127
return s - string;
23128
}
23129
return -1;
23130
}
23131
23132
static int reg_range_find(const int *range, int c)
23133
{
23134
while (*range) {
23135
23136
if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
23137
return 1;
23138
}
23139
range += 2;
23140
}
23141
return 0;
23142
}
23143
23144
static const char *str_find(const char *string, int c, int nocase)
23145
{
23146
if (nocase) {
23147
23148
c = utf8_upper(c);
23149
}
23150
while (*string) {
23151
int ch;
23152
int n = reg_utf8_tounicode_case(string, &ch, nocase);
23153
if (c == ch) {
23154
return string;
23155
}
23156
string += n;
23157
}
23158
return NULL;
23159
}
23160
23161
static int reg_iseol(regex_t *preg, int ch)
23162
{
23163
if (preg->cflags & REG_NEWLINE) {
23164
return ch == '\0' || ch == '\n';
23165
}
23166
else {
23167
return ch == '\0';
23168
}
23169
}
23170
23171
static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
23172
{
23173
int nextch = '\0';
23174
const char *save;
23175
int no;
23176
int c;
23177
23178
int max = preg->program[scan + 2];
23179
int min = preg->program[scan + 3];
23180
int next = regnext(preg, scan);
23181
23182
if (OP(preg, next) == EXACTLY) {
23183
nextch = preg->program[OPERAND(next)];
23184
}
23185
save = preg->reginput;
23186
no = regrepeat(preg, scan + 5, max);
23187
if (no < min) {
23188
return 0;
23189
}
23190
if (matchmin) {
23191
23192
max = no;
23193
no = min;
23194
}
23195
23196
while (1) {
23197
if (matchmin) {
23198
if (no > max) {
23199
break;
23200
}
23201
}
23202
else {
23203
if (no < min) {
23204
break;
23205
}
23206
}
23207
preg->reginput = save + utf8_index(save, no);
23208
reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
23209
23210
if (reg_iseol(preg, nextch) || c == nextch) {
23211
if (regmatch(preg, next)) {
23212
return(1);
23213
}
23214
}
23215
if (matchmin) {
23216
23217
no++;
23218
}
23219
else {
23220
23221
no--;
23222
}
23223
}
23224
return(0);
23225
}
23226
23227
static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
23228
{
23229
int *scanpt = preg->program + scan;
23230
23231
int max = scanpt[2];
23232
int min = scanpt[3];
23233
23234
23235
if (scanpt[4] < min) {
23236
23237
scanpt[4]++;
23238
if (regmatch(preg, scan + 5)) {
23239
return 1;
23240
}
23241
scanpt[4]--;
23242
return 0;
23243
}
23244
if (scanpt[4] > max) {
23245
return 0;
23246
}
23247
23248
if (matchmin) {
23249
23250
if (regmatch(preg, regnext(preg, scan))) {
23251
return 1;
23252
}
23253
23254
scanpt[4]++;
23255
if (regmatch(preg, scan + 5)) {
23256
return 1;
23257
}
23258
scanpt[4]--;
23259
return 0;
23260
}
23261
23262
if (scanpt[4] < max) {
23263
scanpt[4]++;
23264
if (regmatch(preg, scan + 5)) {
23265
return 1;
23266
}
23267
scanpt[4]--;
23268
}
23269
23270
return regmatch(preg, regnext(preg, scan));
23271
}
23272
23273
23274
static int regmatch(regex_t *preg, int prog)
23275
{
23276
int scan;
23277
int next;
23278
const char *save;
23279
23280
scan = prog;
23281
23282
#ifdef DEBUG
23283
if (scan != 0 && regnarrate)
23284
fprintf(stderr, "%s(\n", regprop(scan));
23285
#endif
23286
while (scan != 0) {
23287
int n;
23288
int c;
23289
#ifdef DEBUG
23290
if (regnarrate) {
23291
fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
23292
}
23293
#endif
23294
next = regnext(preg, scan);
23295
n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
23296
23297
switch (OP(preg, scan)) {
23298
case BOLX:
23299
if ((preg->eflags & REG_NOTBOL)) {
23300
return(0);
23301
}
23302
23303
case BOL:
23304
if (preg->reginput != preg->regbol) {
23305
return(0);
23306
}
23307
break;
23308
case EOLX:
23309
if (c != 0) {
23310
23311
return 0;
23312
}
23313
break;
23314
case EOL:
23315
if (!reg_iseol(preg, c)) {
23316
return(0);
23317
}
23318
break;
23319
case WORDA:
23320
23321
if ((!isalnum(UCHAR(c))) && c != '_')
23322
return(0);
23323
23324
if (preg->reginput > preg->regbol &&
23325
(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
23326
return(0);
23327
break;
23328
case WORDZ:
23329
23330
if (preg->reginput > preg->regbol) {
23331
23332
if (reg_iseol(preg, c) || !(isalnum(UCHAR(c)) || c == '_')) {
23333
c = preg->reginput[-1];
23334
23335
if (isalnum(UCHAR(c)) || c == '_') {
23336
break;
23337
}
23338
}
23339
}
23340
23341
return(0);
23342
23343
case ANY:
23344
if (reg_iseol(preg, c))
23345
return 0;
23346
preg->reginput += n;
23347
break;
23348
case EXACTLY: {
23349
int opnd;
23350
int len;
23351
int slen;
23352
23353
opnd = OPERAND(scan);
23354
len = str_int_len(preg->program + opnd);
23355
23356
slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
23357
if (slen < 0) {
23358
return(0);
23359
}
23360
preg->reginput += slen;
23361
}
23362
break;
23363
case ANYOF:
23364
if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
23365
return(0);
23366
}
23367
preg->reginput += n;
23368
break;
23369
case ANYBUT:
23370
if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
23371
return(0);
23372
}
23373
preg->reginput += n;
23374
break;
23375
case NOTHING:
23376
break;
23377
case BACK:
23378
break;
23379
case BRANCH:
23380
if (OP(preg, next) != BRANCH)
23381
next = OPERAND(scan);
23382
else {
23383
do {
23384
save = preg->reginput;
23385
if (regmatch(preg, OPERAND(scan))) {
23386
return(1);
23387
}
23388
preg->reginput = save;
23389
scan = regnext(preg, scan);
23390
} while (scan != 0 && OP(preg, scan) == BRANCH);
23391
return(0);
23392
23393
}
23394
break;
23395
case REP:
23396
case REPMIN:
23397
return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
23398
23399
case REPX:
23400
case REPXMIN:
23401
return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
23402
23403
case END:
23404
return 1;
23405
23406
case OPENNC:
23407
case CLOSENC:
23408
return regmatch(preg, next);
23409
23410
default:
23411
if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
23412
save = preg->reginput;
23413
if (regmatch(preg, next)) {
23414
if (OP(preg, scan) < CLOSE) {
23415
int no = OP(preg, scan) - OPEN;
23416
if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
23417
preg->pmatch[no].rm_so = save - preg->start;
23418
}
23419
}
23420
else {
23421
int no = OP(preg, scan) - CLOSE;
23422
if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
23423
preg->pmatch[no].rm_eo = save - preg->start;
23424
}
23425
}
23426
return(1);
23427
}
23428
23429
preg->reginput = save;
23430
return(0);
23431
}
23432
return REG_ERR_INTERNAL;
23433
}
23434
23435
scan = next;
23436
}
23437
23438
return REG_ERR_INTERNAL;
23439
}
23440
23441
static int regrepeat(regex_t *preg, int p, int max)
23442
{
23443
int count = 0;
23444
const char *scan;
23445
int opnd;
23446
int ch;
23447
int n;
23448
23449
scan = preg->reginput;
23450
opnd = OPERAND(p);
23451
switch (OP(preg, p)) {
23452
case ANY:
23453
while (!reg_iseol(preg, *scan) && count < max) {
23454
count++;
23455
scan += utf8_charlen(*scan);
23456
}
23457
break;
23458
case EXACTLY:
23459
while (count < max) {
23460
n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23461
if (preg->program[opnd] != ch) {
23462
break;
23463
}
23464
count++;
23465
scan += n;
23466
}
23467
break;
23468
case ANYOF:
23469
while (count < max) {
23470
n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23471
if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
23472
break;
23473
}
23474
count++;
23475
scan += n;
23476
}
23477
break;
23478
case ANYBUT:
23479
while (count < max) {
23480
n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23481
if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
23482
break;
23483
}
23484
count++;
23485
scan += n;
23486
}
23487
break;
23488
default:
23489
preg->err = REG_ERR_INTERNAL;
23490
count = 0;
23491
break;
23492
}
23493
preg->reginput = scan;
23494
23495
return(count);
23496
}
23497
23498
static int regnext(regex_t *preg, int p )
23499
{
23500
int offset;
23501
23502
offset = NEXT(preg, p);
23503
23504
if (offset == 0)
23505
return 0;
23506
23507
if (OP(preg, p) == BACK)
23508
return(p-offset);
23509
else
23510
return(p+offset);
23511
}
23512
23513
static int regopsize(regex_t *preg, int p )
23514
{
23515
23516
switch (OP(preg, p)) {
23517
case REP:
23518
case REPMIN:
23519
case REPX:
23520
case REPXMIN:
23521
return 5;
23522
23523
case ANYOF:
23524
case ANYBUT:
23525
case EXACTLY: {
23526
int s = p + 2;
23527
while (preg->program[s++]) {
23528
}
23529
return s - p;
23530
}
23531
}
23532
return 2;
23533
}
23534
23535
23536
size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
23537
{
23538
static const char *error_strings[] = {
23539
"success",
23540
"no match",
23541
"bad pattern",
23542
"null argument",
23543
"unknown error",
23544
"too big",
23545
"out of memory",
23546
"too many ()",
23547
"parentheses () not balanced",
23548
"braces {} not balanced",
23549
"invalid repetition count(s)",
23550
"extra characters",
23551
"*+ of empty atom",
23552
"nested count",
23553
"internal error",
23554
"count follows nothing",
23555
"invalid escape \\ sequence",
23556
"corrupted program",
23557
"contains null char",
23558
"brackets [] not balanced",
23559
};
23560
const char *err;
23561
23562
if (errcode < 0 || errcode >= REG_ERR_NUM) {
23563
err = "Bad error code";
23564
}
23565
else {
23566
err = error_strings[errcode];
23567
}
23568
23569
return snprintf(errbuf, errbuf_size, "%s", err);
23570
}
23571
23572
void jim_regfree(regex_t *preg)
23573
{
23574
free(preg->program);
23575
}
23576
23577
#endif
23578
#include <string.h>
23579
23580
void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
23581
{
23582
Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
23583
}
23584
23585
#if defined(__MINGW32__)
23586
#include <sys/stat.h>
23587
23588
int Jim_Errno(void)
23589
{
23590
switch (GetLastError()) {
23591
case ERROR_FILE_NOT_FOUND: return ENOENT;
23592
case ERROR_PATH_NOT_FOUND: return ENOENT;
23593
case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
23594
case ERROR_ACCESS_DENIED: return EACCES;
23595
case ERROR_INVALID_HANDLE: return EBADF;
23596
case ERROR_BAD_ENVIRONMENT: return E2BIG;
23597
case ERROR_BAD_FORMAT: return ENOEXEC;
23598
case ERROR_INVALID_ACCESS: return EACCES;
23599
case ERROR_INVALID_DRIVE: return ENOENT;
23600
case ERROR_CURRENT_DIRECTORY: return EACCES;
23601
case ERROR_NOT_SAME_DEVICE: return EXDEV;
23602
case ERROR_NO_MORE_FILES: return ENOENT;
23603
case ERROR_WRITE_PROTECT: return EROFS;
23604
case ERROR_BAD_UNIT: return ENXIO;
23605
case ERROR_NOT_READY: return EBUSY;
23606
case ERROR_BAD_COMMAND: return EIO;
23607
case ERROR_CRC: return EIO;
23608
case ERROR_BAD_LENGTH: return EIO;
23609
case ERROR_SEEK: return EIO;
23610
case ERROR_WRITE_FAULT: return EIO;
23611
case ERROR_READ_FAULT: return EIO;
23612
case ERROR_GEN_FAILURE: return EIO;
23613
case ERROR_SHARING_VIOLATION: return EACCES;
23614
case ERROR_LOCK_VIOLATION: return EACCES;
23615
case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
23616
case ERROR_HANDLE_DISK_FULL: return ENOSPC;
23617
case ERROR_NOT_SUPPORTED: return ENODEV;
23618
case ERROR_REM_NOT_LIST: return EBUSY;
23619
case ERROR_DUP_NAME: return EEXIST;
23620
case ERROR_BAD_NETPATH: return ENOENT;
23621
case ERROR_NETWORK_BUSY: return EBUSY;
23622
case ERROR_DEV_NOT_EXIST: return ENODEV;
23623
case ERROR_TOO_MANY_CMDS: return EAGAIN;
23624
case ERROR_ADAP_HDW_ERR: return EIO;
23625
case ERROR_BAD_NET_RESP: return EIO;
23626
case ERROR_UNEXP_NET_ERR: return EIO;
23627
case ERROR_NETNAME_DELETED: return ENOENT;
23628
case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
23629
case ERROR_BAD_DEV_TYPE: return ENODEV;
23630
case ERROR_BAD_NET_NAME: return ENOENT;
23631
case ERROR_TOO_MANY_NAMES: return ENFILE;
23632
case ERROR_TOO_MANY_SESS: return EIO;
23633
case ERROR_SHARING_PAUSED: return EAGAIN;
23634
case ERROR_REDIR_PAUSED: return EAGAIN;
23635
case ERROR_FILE_EXISTS: return EEXIST;
23636
case ERROR_CANNOT_MAKE: return ENOSPC;
23637
case ERROR_OUT_OF_STRUCTURES: return ENFILE;
23638
case ERROR_ALREADY_ASSIGNED: return EEXIST;
23639
case ERROR_INVALID_PASSWORD: return EPERM;
23640
case ERROR_NET_WRITE_FAULT: return EIO;
23641
case ERROR_NO_PROC_SLOTS: return EAGAIN;
23642
case ERROR_DISK_CHANGE: return EXDEV;
23643
case ERROR_BROKEN_PIPE: return EPIPE;
23644
case ERROR_OPEN_FAILED: return ENOENT;
23645
case ERROR_DISK_FULL: return ENOSPC;
23646
case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
23647
case ERROR_INVALID_TARGET_HANDLE: return EBADF;
23648
case ERROR_INVALID_NAME: return ENOENT;
23649
case ERROR_PROC_NOT_FOUND: return ESRCH;
23650
case ERROR_WAIT_NO_CHILDREN: return ECHILD;
23651
case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
23652
case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
23653
case ERROR_SEEK_ON_DEVICE: return ESPIPE;
23654
case ERROR_BUSY_DRIVE: return EAGAIN;
23655
case ERROR_DIR_NOT_EMPTY: return EEXIST;
23656
case ERROR_NOT_LOCKED: return EACCES;
23657
case ERROR_BAD_PATHNAME: return ENOENT;
23658
case ERROR_LOCK_FAILED: return EACCES;
23659
case ERROR_ALREADY_EXISTS: return EEXIST;
23660
case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
23661
case ERROR_BAD_PIPE: return EPIPE;
23662
case ERROR_PIPE_BUSY: return EAGAIN;
23663
case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
23664
case ERROR_DIRECTORY: return ENOTDIR;
23665
}
23666
return EINVAL;
23667
}
23668
23669
long JimProcessPid(phandle_t pid)
23670
{
23671
if (pid == INVALID_HANDLE_VALUE) {
23672
return -1;
23673
}
23674
return GetProcessId(pid);
23675
}
23676
23677
phandle_t JimWaitPid(long pid, int *status, int nohang)
23678
{
23679
if (pid > 0) {
23680
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
23681
if (h) {
23682
long pid = waitpid(h, status, nohang);
23683
CloseHandle(h);
23684
if (pid > 0) {
23685
return h;
23686
}
23687
}
23688
}
23689
return JIM_BAD_PHANDLE;
23690
}
23691
23692
long waitpid(phandle_t phandle, int *status, int nohang)
23693
{
23694
long pid;
23695
DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE);
23696
if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
23697
23698
return -1;
23699
}
23700
GetExitCodeProcess(phandle, &ret);
23701
*status = ret;
23702
23703
pid = GetProcessId(phandle);
23704
CloseHandle(phandle);
23705
return pid;
23706
}
23707
23708
int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
23709
{
23710
char name[MAX_PATH];
23711
HANDLE handle;
23712
23713
if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
23714
return -1;
23715
}
23716
23717
handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
23718
CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
23719
NULL);
23720
23721
if (handle == INVALID_HANDLE_VALUE) {
23722
goto error;
23723
}
23724
23725
Jim_SetResultString(interp, name, -1);
23726
return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT);
23727
23728
error:
23729
Jim_SetResultErrno(interp, name);
23730
DeleteFile(name);
23731
return -1;
23732
}
23733
23734
int Jim_OpenForWrite(const char *filename, int append)
23735
{
23736
if (strcmp(filename, "/dev/null") == 0) {
23737
filename = "nul:";
23738
}
23739
int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
23740
if (fd >= 0 && append) {
23741
23742
_lseek(fd, 0L, SEEK_END);
23743
}
23744
return fd;
23745
}
23746
23747
int Jim_OpenForRead(const char *filename)
23748
{
23749
if (strcmp(filename, "/dev/null") == 0) {
23750
filename = "nul:";
23751
}
23752
return _open(filename, _O_RDONLY | _O_TEXT, 0);
23753
}
23754
23755
#elif defined(HAVE_UNISTD_H)
23756
23757
23758
23759
int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
23760
{
23761
int fd;
23762
mode_t mask;
23763
Jim_Obj *filenameObj;
23764
23765
if (filename_template == NULL) {
23766
const char *tmpdir = getenv("TMPDIR");
23767
if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
23768
tmpdir = "/tmp/";
23769
}
23770
filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
23771
if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
23772
Jim_AppendString(interp, filenameObj, "/", 1);
23773
}
23774
Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
23775
}
23776
else {
23777
filenameObj = Jim_NewStringObj(interp, filename_template, -1);
23778
}
23779
23780
23781
#ifdef HAVE_UMASK
23782
mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
23783
#endif
23784
#ifdef HAVE_MKSTEMP
23785
fd = mkstemp(filenameObj->bytes);
23786
#else
23787
if (mktemp(filenameObj->bytes) == NULL) {
23788
fd = -1;
23789
}
23790
else {
23791
fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
23792
}
23793
#endif
23794
#ifdef HAVE_UMASK
23795
umask(mask);
23796
#endif
23797
if (fd < 0) {
23798
Jim_SetResultErrno(interp, Jim_String(filenameObj));
23799
Jim_FreeNewObj(interp, filenameObj);
23800
return -1;
23801
}
23802
if (unlink_file) {
23803
remove(Jim_String(filenameObj));
23804
}
23805
23806
Jim_SetResult(interp, filenameObj);
23807
return fd;
23808
}
23809
23810
int Jim_OpenForWrite(const char *filename, int append)
23811
{
23812
return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
23813
}
23814
23815
int Jim_OpenForRead(const char *filename)
23816
{
23817
return open(filename, O_RDONLY, 0);
23818
}
23819
23820
#endif
23821
23822
#if defined(_WIN32) || defined(WIN32)
23823
#ifndef STRICT
23824
#define STRICT
23825
#endif
23826
#define WIN32_LEAN_AND_MEAN
23827
#include <windows.h>
23828
23829
#if defined(HAVE_DLOPEN_COMPAT)
23830
void *dlopen(const char *path, int mode)
23831
{
23832
JIM_NOTUSED(mode);
23833
23834
return (void *)LoadLibraryA(path);
23835
}
23836
23837
int dlclose(void *handle)
23838
{
23839
FreeLibrary((HANDLE)handle);
23840
return 0;
23841
}
23842
23843
void *dlsym(void *handle, const char *symbol)
23844
{
23845
return GetProcAddress((HMODULE)handle, symbol);
23846
}
23847
23848
char *dlerror(void)
23849
{
23850
static char msg[121];
23851
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
23852
LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
23853
return msg;
23854
}
23855
#endif
23856
23857
#ifdef _MSC_VER
23858
23859
#include <sys/timeb.h>
23860
23861
23862
int gettimeofday(struct timeval *tv, void *unused)
23863
{
23864
struct _timeb tb;
23865
23866
_ftime(&tb);
23867
tv->tv_sec = tb.time;
23868
tv->tv_usec = tb.millitm * 1000;
23869
23870
return 0;
23871
}
23872
23873
23874
DIR *opendir(const char *name)
23875
{
23876
DIR *dir = 0;
23877
23878
if (name && name[0]) {
23879
size_t base_length = strlen(name);
23880
const char *all =
23881
strchr("/\\", name[base_length - 1]) ? "*" : "/*";
23882
23883
if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
23884
(dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
23885
strcat(strcpy(dir->name, name), all);
23886
23887
if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
23888
dir->result.d_name = 0;
23889
else {
23890
Jim_Free(dir->name);
23891
Jim_Free(dir);
23892
dir = 0;
23893
}
23894
}
23895
else {
23896
Jim_Free(dir);
23897
dir = 0;
23898
errno = ENOMEM;
23899
}
23900
}
23901
else {
23902
errno = EINVAL;
23903
}
23904
return dir;
23905
}
23906
23907
int closedir(DIR * dir)
23908
{
23909
int result = -1;
23910
23911
if (dir) {
23912
if (dir->handle != -1)
23913
result = _findclose(dir->handle);
23914
Jim_Free(dir->name);
23915
Jim_Free(dir);
23916
}
23917
if (result == -1)
23918
errno = EBADF;
23919
return result;
23920
}
23921
23922
struct dirent *readdir(DIR * dir)
23923
{
23924
struct dirent *result = 0;
23925
23926
if (dir && dir->handle != -1) {
23927
if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
23928
result = &dir->result;
23929
result->d_name = dir->info.name;
23930
}
23931
}
23932
else {
23933
errno = EBADF;
23934
}
23935
return result;
23936
}
23937
#endif
23938
#endif
23939
#include <stdio.h>
23940
#include <signal.h>
23941
23942
23943
23944
23945
23946
23947
#ifndef SIGPIPE
23948
#define SIGPIPE 13
23949
#endif
23950
#ifndef SIGINT
23951
#define SIGINT 2
23952
#endif
23953
23954
const char *Jim_SignalId(int sig)
23955
{
23956
static char buf[10];
23957
switch (sig) {
23958
case SIGINT: return "SIGINT";
23959
case SIGPIPE: return "SIGPIPE";
23960
23961
}
23962
snprintf(buf, sizeof(buf), "%d", sig);
23963
return buf;
23964
}
23965
#ifndef JIM_BOOTSTRAP_LIB_ONLY
23966
#include <errno.h>
23967
#include <string.h>
23968
#include <stdio.h>
23969
23970
23971
#ifdef USE_LINENOISE
23972
#ifdef HAVE_UNISTD_H
23973
#include <unistd.h>
23974
#endif
23975
#ifdef HAVE_SYS_STAT_H
23976
#include <sys/stat.h>
23977
#endif
23978
#include "linenoise.h"
23979
#else
23980
#define MAX_LINE_LEN 512
23981
#endif
23982
23983
#ifdef USE_LINENOISE
23984
struct JimCompletionInfo {
23985
Jim_Interp *interp;
23986
Jim_Obj *completion_command;
23987
Jim_Obj *hints_command;
23988
23989
};
23990
23991
static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp);
23992
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
23993
static const char completion_callback_assoc_key[] = "interactive-completion";
23994
static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata);
23995
static void JimFreeHintsCallback(void *hint, void *userdata);
23996
#endif
23997
23998
char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
23999
{
24000
#ifdef USE_LINENOISE
24001
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24002
char *result;
24003
Jim_Obj *objPtr;
24004
long mlmode = 0;
24005
if (compinfo->completion_command) {
24006
linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
24007
}
24008
if (compinfo->hints_command) {
24009
linenoiseSetHintsCallback(JimHintsCallback, compinfo);
24010
linenoiseSetFreeHintsCallback(JimFreeHintsCallback);
24011
}
24012
objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
24013
if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
24014
linenoiseSetMultiLine(mlmode);
24015
}
24016
24017
result = linenoise(prompt);
24018
24019
linenoiseSetCompletionCallback(NULL, NULL);
24020
linenoiseSetHintsCallback(NULL, NULL);
24021
linenoiseSetFreeHintsCallback(NULL);
24022
return result;
24023
#else
24024
int len;
24025
char *line = Jim_Alloc(MAX_LINE_LEN);
24026
24027
fputs(prompt, stdout);
24028
fflush(stdout);
24029
24030
if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
24031
Jim_Free(line);
24032
return NULL;
24033
}
24034
len = strlen(line);
24035
if (len && line[len - 1] == '\n') {
24036
line[len - 1] = '\0';
24037
}
24038
return line;
24039
#endif
24040
}
24041
24042
void Jim_HistoryLoad(const char *filename)
24043
{
24044
#ifdef USE_LINENOISE
24045
linenoiseHistoryLoad(filename);
24046
#endif
24047
}
24048
24049
void Jim_HistoryAdd(const char *line)
24050
{
24051
#ifdef USE_LINENOISE
24052
linenoiseHistoryAdd(line);
24053
#endif
24054
}
24055
24056
void Jim_HistorySave(const char *filename)
24057
{
24058
#ifdef USE_LINENOISE
24059
#ifdef HAVE_UMASK
24060
mode_t mask;
24061
24062
mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
24063
#endif
24064
linenoiseHistorySave(filename);
24065
#ifdef HAVE_UMASK
24066
umask(mask);
24067
#endif
24068
#endif
24069
}
24070
24071
void Jim_HistoryShow(void)
24072
{
24073
#ifdef USE_LINENOISE
24074
24075
int i;
24076
int len;
24077
char **history = linenoiseHistory(&len);
24078
for (i = 0; i < len; i++) {
24079
printf("%4d %s\n", i + 1, history[i]);
24080
}
24081
#endif
24082
}
24083
24084
void Jim_HistorySetMaxLen(int length)
24085
{
24086
#ifdef USE_LINENOISE
24087
linenoiseHistorySetMaxLen(length);
24088
#endif
24089
}
24090
24091
int Jim_HistoryGetMaxLen(void)
24092
{
24093
#ifdef USE_LINENOISE
24094
return linenoiseHistoryGetMaxLen();
24095
#endif
24096
return 0;
24097
}
24098
24099
#ifdef USE_LINENOISE
24100
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
24101
{
24102
struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
24103
Jim_Obj *objv[2];
24104
int ret;
24105
24106
objv[0] = info->completion_command;
24107
objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
24108
24109
ret = Jim_EvalObjVector(info->interp, 2, objv);
24110
24111
24112
if (ret == JIM_OK) {
24113
int i;
24114
Jim_Obj *listObj = Jim_GetResult(info->interp);
24115
int len = Jim_ListLength(info->interp, listObj);
24116
for (i = 0; i < len; i++) {
24117
linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
24118
}
24119
}
24120
}
24121
24122
static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata)
24123
{
24124
struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
24125
Jim_Obj *objv[2];
24126
int ret;
24127
char *result = NULL;
24128
24129
objv[0] = info->hints_command;
24130
objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
24131
24132
ret = Jim_EvalObjVector(info->interp, 2, objv);
24133
24134
24135
if (ret == JIM_OK) {
24136
Jim_Obj *listObj = Jim_GetResult(info->interp);
24137
Jim_IncrRefCount(listObj);
24138
24139
int len = Jim_ListLength(info->interp, listObj);
24140
if (len >= 1) {
24141
long x;
24142
result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0)));
24143
if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) {
24144
*color = x;
24145
}
24146
if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) {
24147
*bold = x;
24148
}
24149
}
24150
Jim_DecrRefCount(info->interp, listObj);
24151
}
24152
return result;
24153
}
24154
24155
static void JimFreeHintsCallback(void *hint, void *userdata)
24156
{
24157
Jim_Free(hint);
24158
}
24159
24160
static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
24161
{
24162
struct JimCompletionInfo *compinfo = data;
24163
24164
if (compinfo->completion_command) {
24165
Jim_DecrRefCount(interp, compinfo->completion_command);
24166
}
24167
if (compinfo->hints_command) {
24168
Jim_DecrRefCount(interp, compinfo->hints_command);
24169
}
24170
24171
Jim_Free(compinfo);
24172
}
24173
24174
static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp)
24175
{
24176
struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
24177
if (compinfo == NULL) {
24178
compinfo = Jim_Alloc(sizeof(*compinfo));
24179
compinfo->interp = interp;
24180
compinfo->completion_command = NULL;
24181
compinfo->hints_command = NULL;
24182
Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
24183
}
24184
return compinfo;
24185
}
24186
#endif
24187
24188
void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj)
24189
{
24190
#ifdef USE_LINENOISE
24191
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24192
24193
if (completionCommandObj) {
24194
24195
Jim_IncrRefCount(completionCommandObj);
24196
}
24197
if (compinfo->completion_command) {
24198
Jim_DecrRefCount(interp, compinfo->completion_command);
24199
}
24200
compinfo->completion_command = completionCommandObj;
24201
#endif
24202
}
24203
24204
void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj)
24205
{
24206
#ifdef USE_LINENOISE
24207
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24208
24209
if (hintsCommandObj) {
24210
24211
Jim_IncrRefCount(hintsCommandObj);
24212
}
24213
if (compinfo->hints_command) {
24214
Jim_DecrRefCount(interp, compinfo->hints_command);
24215
}
24216
compinfo->hints_command = hintsCommandObj;
24217
#endif
24218
}
24219
24220
int Jim_InteractivePrompt(Jim_Interp *interp)
24221
{
24222
int retcode = JIM_OK;
24223
char *history_file = NULL;
24224
#ifdef USE_LINENOISE
24225
const char *home;
24226
24227
home = getenv("HOME");
24228
if (home && isatty(STDIN_FILENO)) {
24229
int history_len = strlen(home) + sizeof("/.jim_history");
24230
history_file = Jim_Alloc(history_len);
24231
snprintf(history_file, history_len, "%s/.jim_history", home);
24232
Jim_HistoryLoad(history_file);
24233
}
24234
24235
Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
24236
Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1));
24237
#endif
24238
24239
printf("Welcome to Jim version %d.%d\n",
24240
JIM_VERSION / 100, JIM_VERSION % 100);
24241
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
24242
24243
while (1) {
24244
Jim_Obj *scriptObjPtr;
24245
const char *result;
24246
int reslen;
24247
char prompt[20];
24248
24249
if (retcode != JIM_OK) {
24250
const char *retcodestr = Jim_ReturnCode(retcode);
24251
24252
if (*retcodestr == '?') {
24253
snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
24254
}
24255
else {
24256
snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
24257
}
24258
}
24259
else {
24260
strcpy(prompt, ". ");
24261
}
24262
24263
scriptObjPtr = Jim_NewStringObj(interp, "", 0);
24264
Jim_IncrRefCount(scriptObjPtr);
24265
while (1) {
24266
char state;
24267
char *line;
24268
24269
line = Jim_HistoryGetline(interp, prompt);
24270
if (line == NULL) {
24271
if (errno == EINTR) {
24272
continue;
24273
}
24274
Jim_DecrRefCount(interp, scriptObjPtr);
24275
retcode = JIM_OK;
24276
goto out;
24277
}
24278
if (Jim_Length(scriptObjPtr) != 0) {
24279
24280
Jim_AppendString(interp, scriptObjPtr, "\n", 1);
24281
}
24282
Jim_AppendString(interp, scriptObjPtr, line, -1);
24283
Jim_Free(line);
24284
if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
24285
break;
24286
24287
snprintf(prompt, sizeof(prompt), "%c> ", state);
24288
}
24289
#ifdef USE_LINENOISE
24290
if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
24291
24292
Jim_HistoryShow();
24293
Jim_DecrRefCount(interp, scriptObjPtr);
24294
continue;
24295
}
24296
24297
Jim_HistoryAdd(Jim_String(scriptObjPtr));
24298
if (history_file) {
24299
Jim_HistorySave(history_file);
24300
}
24301
#endif
24302
retcode = Jim_EvalObj(interp, scriptObjPtr);
24303
Jim_DecrRefCount(interp, scriptObjPtr);
24304
24305
if (retcode == JIM_EXIT) {
24306
break;
24307
}
24308
if (retcode == JIM_ERR) {
24309
Jim_MakeErrorMessage(interp);
24310
}
24311
result = Jim_GetString(Jim_GetResult(interp), &reslen);
24312
if (reslen) {
24313
if (fwrite(result, reslen, 1, stdout) == 0) {
24314
24315
}
24316
putchar('\n');
24317
}
24318
}
24319
out:
24320
Jim_Free(history_file);
24321
24322
return retcode;
24323
}
24324
24325
#include <stdio.h>
24326
#include <stdlib.h>
24327
#include <string.h>
24328
24329
24330
24331
extern int Jim_initjimshInit(Jim_Interp *interp);
24332
24333
static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
24334
{
24335
int n;
24336
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
24337
24338
24339
for (n = 0; n < argc; n++) {
24340
Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
24341
24342
Jim_ListAppendElement(interp, listObj, obj);
24343
}
24344
24345
Jim_SetVariableStr(interp, "argv", listObj);
24346
Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
24347
}
24348
24349
static void JimPrintErrorMessage(Jim_Interp *interp)
24350
{
24351
Jim_MakeErrorMessage(interp);
24352
fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
24353
}
24354
24355
void usage(const char* executable_name)
24356
{
24357
printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
24358
printf("Usage: %s\n", executable_name);
24359
printf("or : %s [options] [filename]\n", executable_name);
24360
printf("\n");
24361
printf("Without options: Interactive mode\n");
24362
printf("\n");
24363
printf("Options:\n");
24364
printf(" --version : prints the version string\n");
24365
printf(" --help : prints this text\n");
24366
printf(" -e CMD : executes command CMD\n");
24367
printf(" NOTE: all subsequent options will be passed as arguments to the command\n");
24368
printf(" [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
24369
printf(" NOTE: all subsequent options will be passed to the script\n\n");
24370
}
24371
24372
int main(int argc, char *const argv[])
24373
{
24374
int retcode;
24375
Jim_Interp *interp;
24376
char *const orig_argv0 = argv[0];
24377
24378
24379
if (argc > 1 && strcmp(argv[1], "--version") == 0) {
24380
printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
24381
return 0;
24382
}
24383
else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
24384
usage(argv[0]);
24385
return 0;
24386
}
24387
24388
24389
interp = Jim_CreateInterp();
24390
Jim_RegisterCoreCommands(interp);
24391
24392
24393
if (Jim_InitStaticExtensions(interp) != JIM_OK) {
24394
JimPrintErrorMessage(interp);
24395
}
24396
24397
Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
24398
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
24399
#ifdef USE_LINENOISE
24400
Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1");
24401
#else
24402
Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0");
24403
#endif
24404
retcode = Jim_initjimshInit(interp);
24405
24406
if (argc == 1) {
24407
24408
if (retcode == JIM_ERR) {
24409
JimPrintErrorMessage(interp);
24410
}
24411
if (retcode != JIM_EXIT) {
24412
JimSetArgv(interp, 0, NULL);
24413
retcode = Jim_InteractivePrompt(interp);
24414
}
24415
}
24416
else {
24417
24418
if (argc > 2 && strcmp(argv[1], "-e") == 0) {
24419
24420
JimSetArgv(interp, argc - 3, argv + 3);
24421
retcode = Jim_Eval(interp, argv[2]);
24422
if (retcode != JIM_ERR) {
24423
int len;
24424
const char *msg = Jim_GetString(Jim_GetResult(interp), &len);
24425
if (fwrite(msg, len, 1, stdout) == 0) {
24426
24427
}
24428
putchar('\n');
24429
}
24430
}
24431
else {
24432
Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
24433
JimSetArgv(interp, argc - 2, argv + 2);
24434
if (strcmp(argv[1], "-") == 0) {
24435
retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
24436
} else {
24437
retcode = Jim_EvalFile(interp, argv[1]);
24438
}
24439
}
24440
if (retcode == JIM_ERR) {
24441
JimPrintErrorMessage(interp);
24442
}
24443
}
24444
if (retcode == JIM_EXIT) {
24445
retcode = Jim_GetExitCode(interp);
24446
}
24447
else if (retcode == JIM_ERR) {
24448
retcode = 1;
24449
}
24450
else {
24451
retcode = 0;
24452
}
24453
Jim_FreeInterp(interp);
24454
return retcode;
24455
}
24456
#endif
24457

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button