Charles Hardin <ckhardin@gmail.com> - Tcl Server
[openocd.git] / src / jim.c
1 /* Jim - A small embeddable Tcl interpreter
2 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
3 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * A copy of the license is also included in the source distribution
12 * of Jim, as a TXT file name called LICENSE.
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #define __JIM_CORE__
22 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
23
24 #ifdef __ECOS
25 #include <pkgconf/jimtcl.h>
26 #endif
27 #ifndef JIM_ANSIC
28 #define JIM_DYNLIB /* Dynamic library support for UNIX and WIN32 */
29 #endif /* JIM_ANSIC */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <ctype.h>
36 #include <limits.h>
37 #include <assert.h>
38 #include <errno.h>
39 #include <time.h>
40
41 /* Include the platform dependent libraries for
42 * dynamic loading of libraries. */
43 #ifdef JIM_DYNLIB
44 #if defined(_WIN32) || defined(WIN32)
45 #ifndef WIN32
46 #define WIN32 1
47 #endif
48 #define STRICT
49 #define WIN32_LEAN_AND_MEAN
50 #include <windows.h>
51 #if _MSC_VER >= 1000
52 #pragma warning(disable:4146)
53 #endif /* _MSC_VER */
54 #else
55 #include <dlfcn.h>
56 #endif /* WIN32 */
57 #endif /* JIM_DYNLIB */
58
59 #ifdef __ECOS
60 #include <cyg/jimtcl/jim.h>
61 #else
62 #include "jim.h"
63 #endif
64
65 #ifdef HAVE_BACKTRACE
66 #include <execinfo.h>
67 #endif
68
69 /* -----------------------------------------------------------------------------
70 * Global variables
71 * ---------------------------------------------------------------------------*/
72
73 /* A shared empty string for the objects string representation.
74 * Jim_InvalidateStringRep knows about it and don't try to free. */
75 static char *JimEmptyStringRep = (char*) "";
76
77 /* -----------------------------------------------------------------------------
78 * Required prototypes of not exported functions
79 * ---------------------------------------------------------------------------*/
80 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
81 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
82 static void JimRegisterCoreApi(Jim_Interp *interp);
83
84 static Jim_HashTableType JimVariablesHashTableType;
85
86 /* -----------------------------------------------------------------------------
87 * Utility functions
88 * ---------------------------------------------------------------------------*/
89
90 /*
91 * Convert a string to a jim_wide INTEGER.
92 * This function originates from BSD.
93 *
94 * Ignores `locale' stuff. Assumes that the upper and lower case
95 * alphabets and digits are each contiguous.
96 */
97 #ifdef HAVE_LONG_LONG
98 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
99 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
100 {
101 register const char *s;
102 register unsigned jim_wide acc;
103 register unsigned char c;
104 register unsigned jim_wide qbase, cutoff;
105 register int neg, any, cutlim;
106
107 /*
108 * Skip white space and pick up leading +/- sign if any.
109 * If base is 0, allow 0x for hex and 0 for octal, else
110 * assume decimal; if base is already 16, allow 0x.
111 */
112 s = nptr;
113 do {
114 c = *s++;
115 } while (isspace(c));
116 if (c == '-') {
117 neg = 1;
118 c = *s++;
119 } else {
120 neg = 0;
121 if (c == '+')
122 c = *s++;
123 }
124 if ((base == 0 || base == 16) &&
125 c == '0' && (*s == 'x' || *s == 'X')) {
126 c = s[1];
127 s += 2;
128 base = 16;
129 }
130 if (base == 0)
131 base = c == '0' ? 8 : 10;
132
133 /*
134 * Compute the cutoff value between legal numbers and illegal
135 * numbers. That is the largest legal value, divided by the
136 * base. An input number that is greater than this value, if
137 * followed by a legal input character, is too big. One that
138 * is equal to this value may be valid or not; the limit
139 * between valid and invalid numbers is then based on the last
140 * digit. For instance, if the range for quads is
141 * [-9223372036854775808..9223372036854775807] and the input base
142 * is 10, cutoff will be set to 922337203685477580 and cutlim to
143 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
144 * accumulated a value > 922337203685477580, or equal but the
145 * next digit is > 7 (or 8), the number is too big, and we will
146 * return a range error.
147 *
148 * Set any if any `digits' consumed; make it negative to indicate
149 * overflow.
150 */
151 qbase = (unsigned)base;
152 cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
153 : LLONG_MAX;
154 cutlim = (int)(cutoff % qbase);
155 cutoff /= qbase;
156 for (acc = 0, any = 0;; c = *s++) {
157 if (!JimIsAscii(c))
158 break;
159 if (isdigit(c))
160 c -= '0';
161 else if (isalpha(c))
162 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
163 else
164 break;
165 if (c >= base)
166 break;
167 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
168 any = -1;
169 else {
170 any = 1;
171 acc *= qbase;
172 acc += c;
173 }
174 }
175 if (any < 0) {
176 acc = neg ? LLONG_MIN : LLONG_MAX;
177 errno = ERANGE;
178 } else if (neg)
179 acc = -acc;
180 if (endptr != 0)
181 *endptr = (char *)(any ? s - 1 : nptr);
182 return (acc);
183 }
184 #endif
185
186 /* Glob-style pattern matching. */
187 static int JimStringMatch(const char *pattern, int patternLen,
188 const char *string, int stringLen, int nocase)
189 {
190 while(patternLen) {
191 switch(pattern[0]) {
192 case '*':
193 while (pattern[1] == '*') {
194 pattern++;
195 patternLen--;
196 }
197 if (patternLen == 1)
198 return 1; /* match */
199 while(stringLen) {
200 if (JimStringMatch(pattern+1, patternLen-1,
201 string, stringLen, nocase))
202 return 1; /* match */
203 string++;
204 stringLen--;
205 }
206 return 0; /* no match */
207 break;
208 case '?':
209 if (stringLen == 0)
210 return 0; /* no match */
211 string++;
212 stringLen--;
213 break;
214 case '[':
215 {
216 int not, match;
217
218 pattern++;
219 patternLen--;
220 not = pattern[0] == '^';
221 if (not) {
222 pattern++;
223 patternLen--;
224 }
225 match = 0;
226 while(1) {
227 if (pattern[0] == '\\') {
228 pattern++;
229 patternLen--;
230 if (pattern[0] == string[0])
231 match = 1;
232 } else if (pattern[0] == ']') {
233 break;
234 } else if (patternLen == 0) {
235 pattern--;
236 patternLen++;
237 break;
238 } else if (pattern[1] == '-' && patternLen >= 3) {
239 int start = pattern[0];
240 int end = pattern[2];
241 int c = string[0];
242 if (start > end) {
243 int t = start;
244 start = end;
245 end = t;
246 }
247 if (nocase) {
248 start = tolower(start);
249 end = tolower(end);
250 c = tolower(c);
251 }
252 pattern += 2;
253 patternLen -= 2;
254 if (c >= start && c <= end)
255 match = 1;
256 } else {
257 if (!nocase) {
258 if (pattern[0] == string[0])
259 match = 1;
260 } else {
261 if (tolower((int)pattern[0]) == tolower((int)string[0]))
262 match = 1;
263 }
264 }
265 pattern++;
266 patternLen--;
267 }
268 if (not)
269 match = !match;
270 if (!match)
271 return 0; /* no match */
272 string++;
273 stringLen--;
274 break;
275 }
276 case '\\':
277 if (patternLen >= 2) {
278 pattern++;
279 patternLen--;
280 }
281 /* fall through */
282 default:
283 if (!nocase) {
284 if (pattern[0] != string[0])
285 return 0; /* no match */
286 } else {
287 if (tolower((int)pattern[0]) != tolower((int)string[0]))
288 return 0; /* no match */
289 }
290 string++;
291 stringLen--;
292 break;
293 }
294 pattern++;
295 patternLen--;
296 if (stringLen == 0) {
297 while(*pattern == '*') {
298 pattern++;
299 patternLen--;
300 }
301 break;
302 }
303 }
304 if (patternLen == 0 && stringLen == 0)
305 return 1;
306 return 0;
307 }
308
309 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
310 int nocase)
311 {
312 unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
313
314 if (nocase == 0) {
315 while(l1 && l2) {
316 if (*u1 != *u2)
317 return (int)*u1-*u2;
318 u1++; u2++; l1--; l2--;
319 }
320 if (!l1 && !l2) return 0;
321 return l1-l2;
322 } else {
323 while(l1 && l2) {
324 if (tolower((int)*u1) != tolower((int)*u2))
325 return tolower((int)*u1)-tolower((int)*u2);
326 u1++; u2++; l1--; l2--;
327 }
328 if (!l1 && !l2) return 0;
329 return l1-l2;
330 }
331 }
332
333 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
334 * The index of the first occurrence of s1 in s2 is returned.
335 * If s1 is not found inside s2, -1 is returned. */
336 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
337 {
338 int i;
339
340 if (!l1 || !l2 || l1 > l2) return -1;
341 if (index < 0) index = 0;
342 s2 += index;
343 for (i = index; i <= l2-l1; i++) {
344 if (memcmp(s2, s1, l1) == 0)
345 return i;
346 s2++;
347 }
348 return -1;
349 }
350
351 int Jim_WideToString(char *buf, jim_wide wideValue)
352 {
353 const char *fmt = "%" JIM_WIDE_MODIFIER;
354 return sprintf(buf, fmt, wideValue);
355 }
356
357 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
358 {
359 char *endptr;
360
361 #ifdef HAVE_LONG_LONG
362 *widePtr = JimStrtoll(str, &endptr, base);
363 #else
364 *widePtr = strtol(str, &endptr, base);
365 #endif
366 if ((str[0] == '\0') || (str == endptr) )
367 return JIM_ERR;
368 if (endptr[0] != '\0') {
369 while(*endptr) {
370 if (!isspace((int)*endptr))
371 return JIM_ERR;
372 endptr++;
373 }
374 }
375 return JIM_OK;
376 }
377
378 int Jim_StringToIndex(const char *str, int *intPtr)
379 {
380 char *endptr;
381
382 *intPtr = strtol(str, &endptr, 10);
383 if ( (str[0] == '\0') || (str == endptr) )
384 return JIM_ERR;
385 if (endptr[0] != '\0') {
386 while(*endptr) {
387 if (!isspace((int)*endptr))
388 return JIM_ERR;
389 endptr++;
390 }
391 }
392 return JIM_OK;
393 }
394
395 /* The string representation of references has two features in order
396 * to make the GC faster. The first is that every reference starts
397 * with a non common character '~', in order to make the string matching
398 * fater. The second is that the reference string rep his 32 characters
399 * in length, this allows to avoid to check every object with a string
400 * repr < 32, and usually there are many of this objects. */
401
402 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
403
404 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
405 {
406 const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
407 sprintf(buf, fmt, refPtr->tag, id);
408 return JIM_REFERENCE_SPACE;
409 }
410
411 int Jim_DoubleToString(char *buf, double doubleValue)
412 {
413 char *s;
414 int len;
415
416 len = sprintf(buf, "%.17g", doubleValue);
417 s = buf;
418 while(*s) {
419 if (*s == '.') return len;
420 s++;
421 }
422 /* Add a final ".0" if it's a number. But not
423 * for NaN or InF */
424 if (isdigit((int)buf[0])
425 || ((buf[0] == '-' || buf[0] == '+')
426 && isdigit((int)buf[1]))) {
427 s[0] = '.';
428 s[1] = '0';
429 s[2] = '\0';
430 return len+2;
431 }
432 return len;
433 }
434
435 int Jim_StringToDouble(const char *str, double *doublePtr)
436 {
437 char *endptr;
438
439 *doublePtr = strtod(str, &endptr);
440 if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
441 return JIM_ERR;
442 return JIM_OK;
443 }
444
445 static jim_wide JimPowWide(jim_wide b, jim_wide e)
446 {
447 jim_wide i, res = 1;
448 if ((b==0 && e!=0) || (e<0)) return 0;
449 for(i=0; i<e; i++) {res *= b;}
450 return res;
451 }
452
453 /* -----------------------------------------------------------------------------
454 * Special functions
455 * ---------------------------------------------------------------------------*/
456
457 /* Note that 'interp' may be NULL if not available in the
458 * context of the panic. It's only useful to get the error
459 * file descriptor, it will default to stderr otherwise. */
460 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
461 {
462 va_list ap;
463
464 va_start(ap, fmt);
465 /*
466 * Send it here first.. Assuming STDIO still works
467 */
468 fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
469 vfprintf(stderr, fmt, ap);
470 fprintf(stderr, JIM_NL JIM_NL);
471 va_end(ap);
472
473 #ifdef HAVE_BACKTRACE
474 {
475 void *array[40];
476 int size, i;
477 char **strings;
478
479 size = backtrace(array, 40);
480 strings = backtrace_symbols(array, size);
481 for (i = 0; i < size; i++)
482 fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
483 fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
484 fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
485 }
486 #endif
487
488 /* This may actually crash... we do it last */
489 if( interp && interp->cookie_stderr ){
490 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
491 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
492 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL JIM_NL );
493 }
494 abort();
495 }
496
497 /* -----------------------------------------------------------------------------
498 * Memory allocation
499 * ---------------------------------------------------------------------------*/
500
501 /* Macro used for memory debugging.
502 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
503 * and similary for Jim_Realloc and Jim_Free */
504 #if 0
505 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
506 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
507 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
508 #endif
509
510 void *Jim_Alloc(int size)
511 {
512 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
513 if (size==0)
514 size=1;
515 void *p = malloc(size);
516 if (p == NULL)
517 Jim_Panic(NULL,"malloc: Out of memory");
518 return p;
519 }
520
521 void Jim_Free(void *ptr) {
522 free(ptr);
523 }
524
525 void *Jim_Realloc(void *ptr, int size)
526 {
527 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
528 if (size==0)
529 size=1;
530 void *p = realloc(ptr, size);
531 if (p == NULL)
532 Jim_Panic(NULL,"realloc: Out of memory");
533 return p;
534 }
535
536 char *Jim_StrDup(const char *s)
537 {
538 int l = strlen(s);
539 char *copy = Jim_Alloc(l+1);
540
541 memcpy(copy, s, l+1);
542 return copy;
543 }
544
545 char *Jim_StrDupLen(const char *s, int l)
546 {
547 char *copy = Jim_Alloc(l+1);
548
549 memcpy(copy, s, l+1);
550 copy[l] = 0; /* Just to be sure, original could be substring */
551 return copy;
552 }
553
554 /* -----------------------------------------------------------------------------
555 * Time related functions
556 * ---------------------------------------------------------------------------*/
557 /* Returns microseconds of CPU used since start. */
558 static jim_wide JimClock(void)
559 {
560 #if (defined WIN32) && !(defined JIM_ANSIC)
561 LARGE_INTEGER t, f;
562 QueryPerformanceFrequency(&f);
563 QueryPerformanceCounter(&t);
564 return (long)((t.QuadPart * 1000000) / f.QuadPart);
565 #else /* !WIN32 */
566 clock_t clocks = clock();
567
568 return (long)(clocks*(1000000/CLOCKS_PER_SEC));
569 #endif /* WIN32 */
570 }
571
572 /* -----------------------------------------------------------------------------
573 * Hash Tables
574 * ---------------------------------------------------------------------------*/
575
576 /* -------------------------- private prototypes ---------------------------- */
577 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
578 static unsigned int JimHashTableNextPower(unsigned int size);
579 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
580
581 /* -------------------------- hash functions -------------------------------- */
582
583 /* Thomas Wang's 32 bit Mix Function */
584 unsigned int Jim_IntHashFunction(unsigned int key)
585 {
586 key += ~(key << 15);
587 key ^= (key >> 10);
588 key += (key << 3);
589 key ^= (key >> 6);
590 key += ~(key << 11);
591 key ^= (key >> 16);
592 return key;
593 }
594
595 /* Identity hash function for integer keys */
596 unsigned int Jim_IdentityHashFunction(unsigned int key)
597 {
598 return key;
599 }
600
601 /* Generic hash function (we are using to multiply by 9 and add the byte
602 * as Tcl) */
603 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
604 {
605 unsigned int h = 0;
606 while(len--)
607 h += (h<<3)+*buf++;
608 return h;
609 }
610
611 /* ----------------------------- API implementation ------------------------- */
612 /* reset an hashtable already initialized with ht_init().
613 * NOTE: This function should only called by ht_destroy(). */
614 static void JimResetHashTable(Jim_HashTable *ht)
615 {
616 ht->table = NULL;
617 ht->size = 0;
618 ht->sizemask = 0;
619 ht->used = 0;
620 ht->collisions = 0;
621 }
622
623 /* Initialize the hash table */
624 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
625 void *privDataPtr)
626 {
627 JimResetHashTable(ht);
628 ht->type = type;
629 ht->privdata = privDataPtr;
630 return JIM_OK;
631 }
632
633 /* Resize the table to the minimal size that contains all the elements,
634 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
635 int Jim_ResizeHashTable(Jim_HashTable *ht)
636 {
637 int minimal = ht->used;
638
639 if (minimal < JIM_HT_INITIAL_SIZE)
640 minimal = JIM_HT_INITIAL_SIZE;
641 return Jim_ExpandHashTable(ht, minimal);
642 }
643
644 /* Expand or create the hashtable */
645 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
646 {
647 Jim_HashTable n; /* the new hashtable */
648 unsigned int realsize = JimHashTableNextPower(size), i;
649
650 /* the size is invalid if it is smaller than the number of
651 * elements already inside the hashtable */
652 if (ht->used >= size)
653 return JIM_ERR;
654
655 Jim_InitHashTable(&n, ht->type, ht->privdata);
656 n.size = realsize;
657 n.sizemask = realsize-1;
658 n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
659
660 /* Initialize all the pointers to NULL */
661 memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
662
663 /* Copy all the elements from the old to the new table:
664 * note that if the old hash table is empty ht->size is zero,
665 * so Jim_ExpandHashTable just creates an hash table. */
666 n.used = ht->used;
667 for (i = 0; i < ht->size && ht->used > 0; i++) {
668 Jim_HashEntry *he, *nextHe;
669
670 if (ht->table[i] == NULL) continue;
671
672 /* For each hash entry on this slot... */
673 he = ht->table[i];
674 while(he) {
675 unsigned int h;
676
677 nextHe = he->next;
678 /* Get the new element index */
679 h = Jim_HashKey(ht, he->key) & n.sizemask;
680 he->next = n.table[h];
681 n.table[h] = he;
682 ht->used--;
683 /* Pass to the next element */
684 he = nextHe;
685 }
686 }
687 assert(ht->used == 0);
688 Jim_Free(ht->table);
689
690 /* Remap the new hashtable in the old */
691 *ht = n;
692 return JIM_OK;
693 }
694
695 /* Add an element to the target hash table */
696 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
697 {
698 int index;
699 Jim_HashEntry *entry;
700
701 /* Get the index of the new element, or -1 if
702 * the element already exists. */
703 if ((index = JimInsertHashEntry(ht, key)) == -1)
704 return JIM_ERR;
705
706 /* Allocates the memory and stores key */
707 entry = Jim_Alloc(sizeof(*entry));
708 entry->next = ht->table[index];
709 ht->table[index] = entry;
710
711 /* Set the hash entry fields. */
712 Jim_SetHashKey(ht, entry, key);
713 Jim_SetHashVal(ht, entry, val);
714 ht->used++;
715 return JIM_OK;
716 }
717
718 /* Add an element, discarding the old if the key already exists */
719 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
720 {
721 Jim_HashEntry *entry;
722
723 /* Try to add the element. If the key
724 * does not exists Jim_AddHashEntry will suceed. */
725 if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
726 return JIM_OK;
727 /* It already exists, get the entry */
728 entry = Jim_FindHashEntry(ht, key);
729 /* Free the old value and set the new one */
730 Jim_FreeEntryVal(ht, entry);
731 Jim_SetHashVal(ht, entry, val);
732 return JIM_OK;
733 }
734
735 /* Search and remove an element */
736 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
737 {
738 unsigned int h;
739 Jim_HashEntry *he, *prevHe;
740
741 if (ht->size == 0)
742 return JIM_ERR;
743 h = Jim_HashKey(ht, key) & ht->sizemask;
744 he = ht->table[h];
745
746 prevHe = NULL;
747 while(he) {
748 if (Jim_CompareHashKeys(ht, key, he->key)) {
749 /* Unlink the element from the list */
750 if (prevHe)
751 prevHe->next = he->next;
752 else
753 ht->table[h] = he->next;
754 Jim_FreeEntryKey(ht, he);
755 Jim_FreeEntryVal(ht, he);
756 Jim_Free(he);
757 ht->used--;
758 return JIM_OK;
759 }
760 prevHe = he;
761 he = he->next;
762 }
763 return JIM_ERR; /* not found */
764 }
765
766 /* Destroy an entire hash table */
767 int Jim_FreeHashTable(Jim_HashTable *ht)
768 {
769 unsigned int i;
770
771 /* Free all the elements */
772 for (i = 0; i < ht->size && ht->used > 0; i++) {
773 Jim_HashEntry *he, *nextHe;
774
775 if ((he = ht->table[i]) == NULL) continue;
776 while(he) {
777 nextHe = he->next;
778 Jim_FreeEntryKey(ht, he);
779 Jim_FreeEntryVal(ht, he);
780 Jim_Free(he);
781 ht->used--;
782 he = nextHe;
783 }
784 }
785 /* Free the table and the allocated cache structure */
786 Jim_Free(ht->table);
787 /* Re-initialize the table */
788 JimResetHashTable(ht);
789 return JIM_OK; /* never fails */
790 }
791
792 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
793 {
794 Jim_HashEntry *he;
795 unsigned int h;
796
797 if (ht->size == 0) return NULL;
798 h = Jim_HashKey(ht, key) & ht->sizemask;
799 he = ht->table[h];
800 while(he) {
801 if (Jim_CompareHashKeys(ht, key, he->key))
802 return he;
803 he = he->next;
804 }
805 return NULL;
806 }
807
808 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
809 {
810 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
811
812 iter->ht = ht;
813 iter->index = -1;
814 iter->entry = NULL;
815 iter->nextEntry = NULL;
816 return iter;
817 }
818
819 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
820 {
821 while (1) {
822 if (iter->entry == NULL) {
823 iter->index++;
824 if (iter->index >=
825 (signed)iter->ht->size) break;
826 iter->entry = iter->ht->table[iter->index];
827 } else {
828 iter->entry = iter->nextEntry;
829 }
830 if (iter->entry) {
831 /* We need to save the 'next' here, the iterator user
832 * may delete the entry we are returning. */
833 iter->nextEntry = iter->entry->next;
834 return iter->entry;
835 }
836 }
837 return NULL;
838 }
839
840 /* ------------------------- private functions ------------------------------ */
841
842 /* Expand the hash table if needed */
843 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
844 {
845 /* If the hash table is empty expand it to the intial size,
846 * if the table is "full" dobule its size. */
847 if (ht->size == 0)
848 return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
849 if (ht->size == ht->used)
850 return Jim_ExpandHashTable(ht, ht->size*2);
851 return JIM_OK;
852 }
853
854 /* Our hash table capability is a power of two */
855 static unsigned int JimHashTableNextPower(unsigned int size)
856 {
857 unsigned int i = JIM_HT_INITIAL_SIZE;
858
859 if (size >= 2147483648U)
860 return 2147483648U;
861 while(1) {
862 if (i >= size)
863 return i;
864 i *= 2;
865 }
866 }
867
868 /* Returns the index of a free slot that can be populated with
869 * an hash entry for the given 'key'.
870 * If the key already exists, -1 is returned. */
871 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
872 {
873 unsigned int h;
874 Jim_HashEntry *he;
875
876 /* Expand the hashtable if needed */
877 if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
878 return -1;
879 /* Compute the key hash value */
880 h = Jim_HashKey(ht, key) & ht->sizemask;
881 /* Search if this slot does not already contain the given key */
882 he = ht->table[h];
883 while(he) {
884 if (Jim_CompareHashKeys(ht, key, he->key))
885 return -1;
886 he = he->next;
887 }
888 return h;
889 }
890
891 /* ----------------------- StringCopy Hash Table Type ------------------------*/
892
893 static unsigned int JimStringCopyHTHashFunction(const void *key)
894 {
895 return Jim_GenHashFunction(key, strlen(key));
896 }
897
898 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
899 {
900 int len = strlen(key);
901 char *copy = Jim_Alloc(len+1);
902 JIM_NOTUSED(privdata);
903
904 memcpy(copy, key, len);
905 copy[len] = '\0';
906 return copy;
907 }
908
909 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
910 {
911 int len = strlen(val);
912 char *copy = Jim_Alloc(len+1);
913 JIM_NOTUSED(privdata);
914
915 memcpy(copy, val, len);
916 copy[len] = '\0';
917 return copy;
918 }
919
920 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
921 const void *key2)
922 {
923 JIM_NOTUSED(privdata);
924
925 return strcmp(key1, key2) == 0;
926 }
927
928 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
929 {
930 JIM_NOTUSED(privdata);
931
932 Jim_Free((void*)key); /* ATTENTION: const cast */
933 }
934
935 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
936 {
937 JIM_NOTUSED(privdata);
938
939 Jim_Free((void*)val); /* ATTENTION: const cast */
940 }
941
942 static Jim_HashTableType JimStringCopyHashTableType = {
943 JimStringCopyHTHashFunction, /* hash function */
944 JimStringCopyHTKeyDup, /* key dup */
945 NULL, /* val dup */
946 JimStringCopyHTKeyCompare, /* key compare */
947 JimStringCopyHTKeyDestructor, /* key destructor */
948 NULL /* val destructor */
949 };
950
951 /* This is like StringCopy but does not auto-duplicate the key.
952 * It's used for intepreter's shared strings. */
953 static Jim_HashTableType JimSharedStringsHashTableType = {
954 JimStringCopyHTHashFunction, /* hash function */
955 NULL, /* key dup */
956 NULL, /* val dup */
957 JimStringCopyHTKeyCompare, /* key compare */
958 JimStringCopyHTKeyDestructor, /* key destructor */
959 NULL /* val destructor */
960 };
961
962 /* This is like StringCopy but also automatically handle dynamic
963 * allocated C strings as values. */
964 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
965 JimStringCopyHTHashFunction, /* hash function */
966 JimStringCopyHTKeyDup, /* key dup */
967 JimStringKeyValCopyHTValDup, /* val dup */
968 JimStringCopyHTKeyCompare, /* key compare */
969 JimStringCopyHTKeyDestructor, /* key destructor */
970 JimStringKeyValCopyHTValDestructor, /* val destructor */
971 };
972
973 typedef struct AssocDataValue {
974 Jim_InterpDeleteProc *delProc;
975 void *data;
976 } AssocDataValue;
977
978 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
979 {
980 AssocDataValue *assocPtr = (AssocDataValue *)data;
981 if (assocPtr->delProc != NULL)
982 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
983 Jim_Free(data);
984 }
985
986 static Jim_HashTableType JimAssocDataHashTableType = {
987 JimStringCopyHTHashFunction, /* hash function */
988 JimStringCopyHTKeyDup, /* key dup */
989 NULL, /* val dup */
990 JimStringCopyHTKeyCompare, /* key compare */
991 JimStringCopyHTKeyDestructor, /* key destructor */
992 JimAssocDataHashTableValueDestructor /* val destructor */
993 };
994
995 /* -----------------------------------------------------------------------------
996 * Stack - This is a simple generic stack implementation. It is used for
997 * example in the 'expr' expression compiler.
998 * ---------------------------------------------------------------------------*/
999 void Jim_InitStack(Jim_Stack *stack)
1000 {
1001 stack->len = 0;
1002 stack->maxlen = 0;
1003 stack->vector = NULL;
1004 }
1005
1006 void Jim_FreeStack(Jim_Stack *stack)
1007 {
1008 Jim_Free(stack->vector);
1009 }
1010
1011 int Jim_StackLen(Jim_Stack *stack)
1012 {
1013 return stack->len;
1014 }
1015
1016 void Jim_StackPush(Jim_Stack *stack, void *element) {
1017 int neededLen = stack->len+1;
1018 if (neededLen > stack->maxlen) {
1019 stack->maxlen = neededLen*2;
1020 stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1021 }
1022 stack->vector[stack->len] = element;
1023 stack->len++;
1024 }
1025
1026 void *Jim_StackPop(Jim_Stack *stack)
1027 {
1028 if (stack->len == 0) return NULL;
1029 stack->len--;
1030 return stack->vector[stack->len];
1031 }
1032
1033 void *Jim_StackPeek(Jim_Stack *stack)
1034 {
1035 if (stack->len == 0) return NULL;
1036 return stack->vector[stack->len-1];
1037 }
1038
1039 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1040 {
1041 int i;
1042
1043 for (i = 0; i < stack->len; i++)
1044 freeFunc(stack->vector[i]);
1045 }
1046
1047 /* -----------------------------------------------------------------------------
1048 * Parser
1049 * ---------------------------------------------------------------------------*/
1050
1051 /* Token types */
1052 #define JIM_TT_NONE -1 /* No token returned */
1053 #define JIM_TT_STR 0 /* simple string */
1054 #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
1055 #define JIM_TT_VAR 2 /* var substitution */
1056 #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
1057 #define JIM_TT_CMD 4 /* command substitution */
1058 #define JIM_TT_SEP 5 /* word separator */
1059 #define JIM_TT_EOL 6 /* line separator */
1060
1061 /* Additional token types needed for expressions */
1062 #define JIM_TT_SUBEXPR_START 7
1063 #define JIM_TT_SUBEXPR_END 8
1064 #define JIM_TT_EXPR_NUMBER 9
1065 #define JIM_TT_EXPR_OPERATOR 10
1066
1067 /* Parser states */
1068 #define JIM_PS_DEF 0 /* Default state */
1069 #define JIM_PS_QUOTE 1 /* Inside "" */
1070
1071 /* Parser context structure. The same context is used both to parse
1072 * Tcl scripts and lists. */
1073 struct JimParserCtx {
1074 const char *prg; /* Program text */
1075 const char *p; /* Pointer to the point of the program we are parsing */
1076 int len; /* Left length of 'prg' */
1077 int linenr; /* Current line number */
1078 const char *tstart;
1079 const char *tend; /* Returned token is at tstart-tend in 'prg'. */
1080 int tline; /* Line number of the returned token */
1081 int tt; /* Token type */
1082 int eof; /* Non zero if EOF condition is true. */
1083 int state; /* Parser state */
1084 int comment; /* Non zero if the next chars may be a comment. */
1085 };
1086
1087 #define JimParserEof(c) ((c)->eof)
1088 #define JimParserTstart(c) ((c)->tstart)
1089 #define JimParserTend(c) ((c)->tend)
1090 #define JimParserTtype(c) ((c)->tt)
1091 #define JimParserTline(c) ((c)->tline)
1092
1093 static int JimParseScript(struct JimParserCtx *pc);
1094 static int JimParseSep(struct JimParserCtx *pc);
1095 static int JimParseEol(struct JimParserCtx *pc);
1096 static int JimParseCmd(struct JimParserCtx *pc);
1097 static int JimParseVar(struct JimParserCtx *pc);
1098 static int JimParseBrace(struct JimParserCtx *pc);
1099 static int JimParseStr(struct JimParserCtx *pc);
1100 static int JimParseComment(struct JimParserCtx *pc);
1101 static char *JimParserGetToken(struct JimParserCtx *pc,
1102 int *lenPtr, int *typePtr, int *linePtr);
1103
1104 /* Initialize a parser context.
1105 * 'prg' is a pointer to the program text, linenr is the line
1106 * number of the first line contained in the program. */
1107 void JimParserInit(struct JimParserCtx *pc, const char *prg,
1108 int len, int linenr)
1109 {
1110 pc->prg = prg;
1111 pc->p = prg;
1112 pc->len = len;
1113 pc->tstart = NULL;
1114 pc->tend = NULL;
1115 pc->tline = 0;
1116 pc->tt = JIM_TT_NONE;
1117 pc->eof = 0;
1118 pc->state = JIM_PS_DEF;
1119 pc->linenr = linenr;
1120 pc->comment = 1;
1121 }
1122
1123 int JimParseScript(struct JimParserCtx *pc)
1124 {
1125 while(1) { /* the while is used to reiterate with continue if needed */
1126 if (!pc->len) {
1127 pc->tstart = pc->p;
1128 pc->tend = pc->p-1;
1129 pc->tline = pc->linenr;
1130 pc->tt = JIM_TT_EOL;
1131 pc->eof = 1;
1132 return JIM_OK;
1133 }
1134 switch(*(pc->p)) {
1135 case '\\':
1136 if (*(pc->p+1) == '\n')
1137 return JimParseSep(pc);
1138 else {
1139 pc->comment = 0;
1140 return JimParseStr(pc);
1141 }
1142 break;
1143 case ' ':
1144 case '\t':
1145 case '\r':
1146 if (pc->state == JIM_PS_DEF)
1147 return JimParseSep(pc);
1148 else {
1149 pc->comment = 0;
1150 return JimParseStr(pc);
1151 }
1152 break;
1153 case '\n':
1154 case ';':
1155 pc->comment = 1;
1156 if (pc->state == JIM_PS_DEF)
1157 return JimParseEol(pc);
1158 else
1159 return JimParseStr(pc);
1160 break;
1161 case '[':
1162 pc->comment = 0;
1163 return JimParseCmd(pc);
1164 break;
1165 case '$':
1166 pc->comment = 0;
1167 if (JimParseVar(pc) == JIM_ERR) {
1168 pc->tstart = pc->tend = pc->p++; pc->len--;
1169 pc->tline = pc->linenr;
1170 pc->tt = JIM_TT_STR;
1171 return JIM_OK;
1172 } else
1173 return JIM_OK;
1174 break;
1175 case '#':
1176 if (pc->comment) {
1177 JimParseComment(pc);
1178 continue;
1179 } else {
1180 return JimParseStr(pc);
1181 }
1182 default:
1183 pc->comment = 0;
1184 return JimParseStr(pc);
1185 break;
1186 }
1187 return JIM_OK;
1188 }
1189 }
1190
1191 int JimParseSep(struct JimParserCtx *pc)
1192 {
1193 pc->tstart = pc->p;
1194 pc->tline = pc->linenr;
1195 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1196 (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1197 if (*pc->p == '\\') {
1198 pc->p++; pc->len--;
1199 pc->linenr++;
1200 }
1201 pc->p++; pc->len--;
1202 }
1203 pc->tend = pc->p-1;
1204 pc->tt = JIM_TT_SEP;
1205 return JIM_OK;
1206 }
1207
1208 int JimParseEol(struct JimParserCtx *pc)
1209 {
1210 pc->tstart = pc->p;
1211 pc->tline = pc->linenr;
1212 while (*pc->p == ' ' || *pc->p == '\n' ||
1213 *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1214 if (*pc->p == '\n')
1215 pc->linenr++;
1216 pc->p++; pc->len--;
1217 }
1218 pc->tend = pc->p-1;
1219 pc->tt = JIM_TT_EOL;
1220 return JIM_OK;
1221 }
1222
1223 /* Todo. Don't stop if ']' appears inside {} or quoted.
1224 * Also should handle the case of puts [string length "]"] */
1225 int JimParseCmd(struct JimParserCtx *pc)
1226 {
1227 int level = 1;
1228 int blevel = 0;
1229
1230 pc->tstart = ++pc->p; pc->len--;
1231 pc->tline = pc->linenr;
1232 while (1) {
1233 if (pc->len == 0) {
1234 break;
1235 } else if (*pc->p == '[' && blevel == 0) {
1236 level++;
1237 } else if (*pc->p == ']' && blevel == 0) {
1238 level--;
1239 if (!level) break;
1240 } else if (*pc->p == '\\') {
1241 pc->p++; pc->len--;
1242 } else if (*pc->p == '{') {
1243 blevel++;
1244 } else if (*pc->p == '}') {
1245 if (blevel != 0)
1246 blevel--;
1247 } else if (*pc->p == '\n')
1248 pc->linenr++;
1249 pc->p++; pc->len--;
1250 }
1251 pc->tend = pc->p-1;
1252 pc->tt = JIM_TT_CMD;
1253 if (*pc->p == ']') {
1254 pc->p++; pc->len--;
1255 }
1256 return JIM_OK;
1257 }
1258
1259 int JimParseVar(struct JimParserCtx *pc)
1260 {
1261 int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1262
1263 pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1264 pc->tline = pc->linenr;
1265 if (*pc->p == '{') {
1266 pc->tstart = ++pc->p; pc->len--;
1267 brace = 1;
1268 }
1269 if (brace) {
1270 while (!stop) {
1271 if (*pc->p == '}' || pc->len == 0) {
1272 stop = 1;
1273 if (pc->len == 0)
1274 continue;
1275 }
1276 else if (*pc->p == '\n')
1277 pc->linenr++;
1278 pc->p++; pc->len--;
1279 }
1280 if (pc->len == 0)
1281 pc->tend = pc->p-1;
1282 else
1283 pc->tend = pc->p-2;
1284 } else {
1285 while (!stop) {
1286 if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1287 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1288 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1289 stop = 1;
1290 else {
1291 pc->p++; pc->len--;
1292 }
1293 }
1294 /* Parse [dict get] syntax sugar. */
1295 if (*pc->p == '(') {
1296 while (*pc->p != ')' && pc->len) {
1297 pc->p++; pc->len--;
1298 if (*pc->p == '\\' && pc->len >= 2) {
1299 pc->p += 2; pc->len -= 2;
1300 }
1301 }
1302 if (*pc->p != '\0') {
1303 pc->p++; pc->len--;
1304 }
1305 ttype = JIM_TT_DICTSUGAR;
1306 }
1307 pc->tend = pc->p-1;
1308 }
1309 /* Check if we parsed just the '$' character.
1310 * That's not a variable so an error is returned
1311 * to tell the state machine to consider this '$' just
1312 * a string. */
1313 if (pc->tstart == pc->p) {
1314 pc->p--; pc->len++;
1315 return JIM_ERR;
1316 }
1317 pc->tt = ttype;
1318 return JIM_OK;
1319 }
1320
1321 int JimParseBrace(struct JimParserCtx *pc)
1322 {
1323 int level = 1;
1324
1325 pc->tstart = ++pc->p; pc->len--;
1326 pc->tline = pc->linenr;
1327 while (1) {
1328 if (*pc->p == '\\' && pc->len >= 2) {
1329 pc->p++; pc->len--;
1330 if (*pc->p == '\n')
1331 pc->linenr++;
1332 } else if (*pc->p == '{') {
1333 level++;
1334 } else if (pc->len == 0 || *pc->p == '}') {
1335 level--;
1336 if (pc->len == 0 || level == 0) {
1337 pc->tend = pc->p-1;
1338 if (pc->len != 0) {
1339 pc->p++; pc->len--;
1340 }
1341 pc->tt = JIM_TT_STR;
1342 return JIM_OK;
1343 }
1344 } else if (*pc->p == '\n') {
1345 pc->linenr++;
1346 }
1347 pc->p++; pc->len--;
1348 }
1349 return JIM_OK; /* unreached */
1350 }
1351
1352 int JimParseStr(struct JimParserCtx *pc)
1353 {
1354 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1355 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1356 if (newword && *pc->p == '{') {
1357 return JimParseBrace(pc);
1358 } else if (newword && *pc->p == '"') {
1359 pc->state = JIM_PS_QUOTE;
1360 pc->p++; pc->len--;
1361 }
1362 pc->tstart = pc->p;
1363 pc->tline = pc->linenr;
1364 while (1) {
1365 if (pc->len == 0) {
1366 pc->tend = pc->p-1;
1367 pc->tt = JIM_TT_ESC;
1368 return JIM_OK;
1369 }
1370 switch(*pc->p) {
1371 case '\\':
1372 if (pc->state == JIM_PS_DEF &&
1373 *(pc->p+1) == '\n') {
1374 pc->tend = pc->p-1;
1375 pc->tt = JIM_TT_ESC;
1376 return JIM_OK;
1377 }
1378 if (pc->len >= 2) {
1379 pc->p++; pc->len--;
1380 }
1381 break;
1382 case '$':
1383 case '[':
1384 pc->tend = pc->p-1;
1385 pc->tt = JIM_TT_ESC;
1386 return JIM_OK;
1387 case ' ':
1388 case '\t':
1389 case '\n':
1390 case '\r':
1391 case ';':
1392 if (pc->state == JIM_PS_DEF) {
1393 pc->tend = pc->p-1;
1394 pc->tt = JIM_TT_ESC;
1395 return JIM_OK;
1396 } else if (*pc->p == '\n') {
1397 pc->linenr++;
1398 }
1399 break;
1400 case '"':
1401 if (pc->state == JIM_PS_QUOTE) {
1402 pc->tend = pc->p-1;
1403 pc->tt = JIM_TT_ESC;
1404 pc->p++; pc->len--;
1405 pc->state = JIM_PS_DEF;
1406 return JIM_OK;
1407 }
1408 break;
1409 }
1410 pc->p++; pc->len--;
1411 }
1412 return JIM_OK; /* unreached */
1413 }
1414
1415 int JimParseComment(struct JimParserCtx *pc)
1416 {
1417 while (*pc->p) {
1418 if (*pc->p == '\n') {
1419 pc->linenr++;
1420 if (*(pc->p-1) != '\\') {
1421 pc->p++; pc->len--;
1422 return JIM_OK;
1423 }
1424 }
1425 pc->p++; pc->len--;
1426 }
1427 return JIM_OK;
1428 }
1429
1430 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1431 static int xdigitval(int c)
1432 {
1433 if (c >= '0' && c <= '9') return c-'0';
1434 if (c >= 'a' && c <= 'f') return c-'a'+10;
1435 if (c >= 'A' && c <= 'F') return c-'A'+10;
1436 return -1;
1437 }
1438
1439 static int odigitval(int c)
1440 {
1441 if (c >= '0' && c <= '7') return c-'0';
1442 return -1;
1443 }
1444
1445 /* Perform Tcl escape substitution of 's', storing the result
1446 * string into 'dest'. The escaped string is guaranteed to
1447 * be the same length or shorted than the source string.
1448 * Slen is the length of the string at 's', if it's -1 the string
1449 * length will be calculated by the function.
1450 *
1451 * The function returns the length of the resulting string. */
1452 static int JimEscape(char *dest, const char *s, int slen)
1453 {
1454 char *p = dest;
1455 int i, len;
1456
1457 if (slen == -1)
1458 slen = strlen(s);
1459
1460 for (i = 0; i < slen; i++) {
1461 switch(s[i]) {
1462 case '\\':
1463 switch(s[i+1]) {
1464 case 'a': *p++ = 0x7; i++; break;
1465 case 'b': *p++ = 0x8; i++; break;
1466 case 'f': *p++ = 0xc; i++; break;
1467 case 'n': *p++ = 0xa; i++; break;
1468 case 'r': *p++ = 0xd; i++; break;
1469 case 't': *p++ = 0x9; i++; break;
1470 case 'v': *p++ = 0xb; i++; break;
1471 case '\0': *p++ = '\\'; i++; break;
1472 case '\n': *p++ = ' '; i++; break;
1473 default:
1474 if (s[i+1] == 'x') {
1475 int val = 0;
1476 int c = xdigitval(s[i+2]);
1477 if (c == -1) {
1478 *p++ = 'x';
1479 i++;
1480 break;
1481 }
1482 val = c;
1483 c = xdigitval(s[i+3]);
1484 if (c == -1) {
1485 *p++ = val;
1486 i += 2;
1487 break;
1488 }
1489 val = (val*16)+c;
1490 *p++ = val;
1491 i += 3;
1492 break;
1493 } else if (s[i+1] >= '0' && s[i+1] <= '7')
1494 {
1495 int val = 0;
1496 int c = odigitval(s[i+1]);
1497 val = c;
1498 c = odigitval(s[i+2]);
1499 if (c == -1) {
1500 *p++ = val;
1501 i ++;
1502 break;
1503 }
1504 val = (val*8)+c;
1505 c = odigitval(s[i+3]);
1506 if (c == -1) {
1507 *p++ = val;
1508 i += 2;
1509 break;
1510 }
1511 val = (val*8)+c;
1512 *p++ = val;
1513 i += 3;
1514 } else {
1515 *p++ = s[i+1];
1516 i++;
1517 }
1518 break;
1519 }
1520 break;
1521 default:
1522 *p++ = s[i];
1523 break;
1524 }
1525 }
1526 len = p-dest;
1527 *p++ = '\0';
1528 return len;
1529 }
1530
1531 /* Returns a dynamically allocated copy of the current token in the
1532 * parser context. The function perform conversion of escapes if
1533 * the token is of type JIM_TT_ESC.
1534 *
1535 * Note that after the conversion, tokens that are grouped with
1536 * braces in the source code, are always recognizable from the
1537 * identical string obtained in a different way from the type.
1538 *
1539 * For exmple the string:
1540 *
1541 * {expand}$a
1542 *
1543 * will return as first token "expand", of type JIM_TT_STR
1544 *
1545 * While the string:
1546 *
1547 * expand$a
1548 *
1549 * will return as first token "expand", of type JIM_TT_ESC
1550 */
1551 char *JimParserGetToken(struct JimParserCtx *pc,
1552 int *lenPtr, int *typePtr, int *linePtr)
1553 {
1554 const char *start, *end;
1555 char *token;
1556 int len;
1557
1558 start = JimParserTstart(pc);
1559 end = JimParserTend(pc);
1560 if (start > end) {
1561 if (lenPtr) *lenPtr = 0;
1562 if (typePtr) *typePtr = JimParserTtype(pc);
1563 if (linePtr) *linePtr = JimParserTline(pc);
1564 token = Jim_Alloc(1);
1565 token[0] = '\0';
1566 return token;
1567 }
1568 len = (end-start)+1;
1569 token = Jim_Alloc(len+1);
1570 if (JimParserTtype(pc) != JIM_TT_ESC) {
1571 /* No escape conversion needed? Just copy it. */
1572 memcpy(token, start, len);
1573 token[len] = '\0';
1574 } else {
1575 /* Else convert the escape chars. */
1576 len = JimEscape(token, start, len);
1577 }
1578 if (lenPtr) *lenPtr = len;
1579 if (typePtr) *typePtr = JimParserTtype(pc);
1580 if (linePtr) *linePtr = JimParserTline(pc);
1581 return token;
1582 }
1583
1584 /* The following functin is not really part of the parsing engine of Jim,
1585 * but it somewhat related. Given an string and its length, it tries
1586 * to guess if the script is complete or there are instead " " or { }
1587 * open and not completed. This is useful for interactive shells
1588 * implementation and for [info complete].
1589 *
1590 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1591 * '{' on scripts incomplete missing one or more '}' to be balanced.
1592 * '"' on scripts incomplete missing a '"' char.
1593 *
1594 * If the script is complete, 1 is returned, otherwise 0. */
1595 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1596 {
1597 int level = 0;
1598 int state = ' ';
1599
1600 while(len) {
1601 switch (*s) {
1602 case '\\':
1603 if (len > 1)
1604 s++;
1605 break;
1606 case '"':
1607 if (state == ' ') {
1608 state = '"';
1609 } else if (state == '"') {
1610 state = ' ';
1611 }
1612 break;
1613 case '{':
1614 if (state == '{') {
1615 level++;
1616 } else if (state == ' ') {
1617 state = '{';
1618 level++;
1619 }
1620 break;
1621 case '}':
1622 if (state == '{') {
1623 level--;
1624 if (level == 0)
1625 state = ' ';
1626 }
1627 break;
1628 }
1629 s++;
1630 len--;
1631 }
1632 if (stateCharPtr)
1633 *stateCharPtr = state;
1634 return state == ' ';
1635 }
1636
1637 /* -----------------------------------------------------------------------------
1638 * Tcl Lists parsing
1639 * ---------------------------------------------------------------------------*/
1640 static int JimParseListSep(struct JimParserCtx *pc);
1641 static int JimParseListStr(struct JimParserCtx *pc);
1642
1643 int JimParseList(struct JimParserCtx *pc)
1644 {
1645 if (pc->len == 0) {
1646 pc->tstart = pc->tend = pc->p;
1647 pc->tline = pc->linenr;
1648 pc->tt = JIM_TT_EOL;
1649 pc->eof = 1;
1650 return JIM_OK;
1651 }
1652 switch(*pc->p) {
1653 case ' ':
1654 case '\n':
1655 case '\t':
1656 case '\r':
1657 if (pc->state == JIM_PS_DEF)
1658 return JimParseListSep(pc);
1659 else
1660 return JimParseListStr(pc);
1661 break;
1662 default:
1663 return JimParseListStr(pc);
1664 break;
1665 }
1666 return JIM_OK;
1667 }
1668
1669 int JimParseListSep(struct JimParserCtx *pc)
1670 {
1671 pc->tstart = pc->p;
1672 pc->tline = pc->linenr;
1673 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1674 {
1675 pc->p++; pc->len--;
1676 }
1677 pc->tend = pc->p-1;
1678 pc->tt = JIM_TT_SEP;
1679 return JIM_OK;
1680 }
1681
1682 int JimParseListStr(struct JimParserCtx *pc)
1683 {
1684 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1685 pc->tt == JIM_TT_NONE);
1686 if (newword && *pc->p == '{') {
1687 return JimParseBrace(pc);
1688 } else if (newword && *pc->p == '"') {
1689 pc->state = JIM_PS_QUOTE;
1690 pc->p++; pc->len--;
1691 }
1692 pc->tstart = pc->p;
1693 pc->tline = pc->linenr;
1694 while (1) {
1695 if (pc->len == 0) {
1696 pc->tend = pc->p-1;
1697 pc->tt = JIM_TT_ESC;
1698 return JIM_OK;
1699 }
1700 switch(*pc->p) {
1701 case '\\':
1702 pc->p++; pc->len--;
1703 break;
1704 case ' ':
1705 case '\t':
1706 case '\n':
1707 case '\r':
1708 if (pc->state == JIM_PS_DEF) {
1709 pc->tend = pc->p-1;
1710 pc->tt = JIM_TT_ESC;
1711 return JIM_OK;
1712 } else if (*pc->p == '\n') {
1713 pc->linenr++;
1714 }
1715 break;
1716 case '"':
1717 if (pc->state == JIM_PS_QUOTE) {
1718 pc->tend = pc->p-1;
1719 pc->tt = JIM_TT_ESC;
1720 pc->p++; pc->len--;
1721 pc->state = JIM_PS_DEF;
1722 return JIM_OK;
1723 }
1724 break;
1725 }
1726 pc->p++; pc->len--;
1727 }
1728 return JIM_OK; /* unreached */
1729 }
1730
1731 /* -----------------------------------------------------------------------------
1732 * Jim_Obj related functions
1733 * ---------------------------------------------------------------------------*/
1734
1735 /* Return a new initialized object. */
1736 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1737 {
1738 Jim_Obj *objPtr;
1739
1740 /* -- Check if there are objects in the free list -- */
1741 if (interp->freeList != NULL) {
1742 /* -- Unlink the object from the free list -- */
1743 objPtr = interp->freeList;
1744 interp->freeList = objPtr->nextObjPtr;
1745 } else {
1746 /* -- No ready to use objects: allocate a new one -- */
1747 objPtr = Jim_Alloc(sizeof(*objPtr));
1748 }
1749
1750 /* Object is returned with refCount of 0. Every
1751 * kind of GC implemented should take care to don't try
1752 * to scan objects with refCount == 0. */
1753 objPtr->refCount = 0;
1754 /* All the other fields are left not initialized to save time.
1755 * The caller will probably want set they to the right
1756 * value anyway. */
1757
1758 /* -- Put the object into the live list -- */
1759 objPtr->prevObjPtr = NULL;
1760 objPtr->nextObjPtr = interp->liveList;
1761 if (interp->liveList)
1762 interp->liveList->prevObjPtr = objPtr;
1763 interp->liveList = objPtr;
1764
1765 return objPtr;
1766 }
1767
1768 /* Free an object. Actually objects are never freed, but
1769 * just moved to the free objects list, where they will be
1770 * reused by Jim_NewObj(). */
1771 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1772 {
1773 /* Check if the object was already freed, panic. */
1774 if (objPtr->refCount != 0) {
1775 Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1776 objPtr->refCount);
1777 }
1778 /* Free the internal representation */
1779 Jim_FreeIntRep(interp, objPtr);
1780 /* Free the string representation */
1781 if (objPtr->bytes != NULL) {
1782 if (objPtr->bytes != JimEmptyStringRep)
1783 Jim_Free(objPtr->bytes);
1784 }
1785 /* Unlink the object from the live objects list */
1786 if (objPtr->prevObjPtr)
1787 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1788 if (objPtr->nextObjPtr)
1789 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1790 if (interp->liveList == objPtr)
1791 interp->liveList = objPtr->nextObjPtr;
1792 /* Link the object into the free objects list */
1793 objPtr->prevObjPtr = NULL;
1794 objPtr->nextObjPtr = interp->freeList;
1795 if (interp->freeList)
1796 interp->freeList->prevObjPtr = objPtr;
1797 interp->freeList = objPtr;
1798 objPtr->refCount = -1;
1799 }
1800
1801 /* Invalidate the string representation of an object. */
1802 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1803 {
1804 if (objPtr->bytes != NULL) {
1805 if (objPtr->bytes != JimEmptyStringRep)
1806 Jim_Free(objPtr->bytes);
1807 }
1808 objPtr->bytes = NULL;
1809 }
1810
1811 #define Jim_SetStringRep(o, b, l) \
1812 do { (o)->bytes = b; (o)->length = l; } while (0)
1813
1814 /* Set the initial string representation for an object.
1815 * Does not try to free an old one. */
1816 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1817 {
1818 if (length == 0) {
1819 objPtr->bytes = JimEmptyStringRep;
1820 objPtr->length = 0;
1821 } else {
1822 objPtr->bytes = Jim_Alloc(length+1);
1823 objPtr->length = length;
1824 memcpy(objPtr->bytes, bytes, length);
1825 objPtr->bytes[length] = '\0';
1826 }
1827 }
1828
1829 /* Duplicate an object. The returned object has refcount = 0. */
1830 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1831 {
1832 Jim_Obj *dupPtr;
1833
1834 dupPtr = Jim_NewObj(interp);
1835 if (objPtr->bytes == NULL) {
1836 /* Object does not have a valid string representation. */
1837 dupPtr->bytes = NULL;
1838 } else {
1839 Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1840 }
1841 if (objPtr->typePtr != NULL) {
1842 if (objPtr->typePtr->dupIntRepProc == NULL) {
1843 dupPtr->internalRep = objPtr->internalRep;
1844 } else {
1845 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1846 }
1847 dupPtr->typePtr = objPtr->typePtr;
1848 } else {
1849 dupPtr->typePtr = NULL;
1850 }
1851 return dupPtr;
1852 }
1853
1854 /* Return the string representation for objPtr. If the object
1855 * string representation is invalid, calls the method to create
1856 * a new one starting from the internal representation of the object. */
1857 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1858 {
1859 if (objPtr->bytes == NULL) {
1860 /* Invalid string repr. Generate it. */
1861 if (objPtr->typePtr->updateStringProc == NULL) {
1862 Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1863 objPtr->typePtr->name);
1864 }
1865 objPtr->typePtr->updateStringProc(objPtr);
1866 }
1867 if (lenPtr)
1868 *lenPtr = objPtr->length;
1869 return objPtr->bytes;
1870 }
1871
1872 /* Just returns the length of the object's string rep */
1873 int Jim_Length(Jim_Obj *objPtr)
1874 {
1875 int len;
1876
1877 Jim_GetString(objPtr, &len);
1878 return len;
1879 }
1880
1881 /* -----------------------------------------------------------------------------
1882 * String Object
1883 * ---------------------------------------------------------------------------*/
1884 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1885 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1886
1887 static Jim_ObjType stringObjType = {
1888 "string",
1889 NULL,
1890 DupStringInternalRep,
1891 NULL,
1892 JIM_TYPE_REFERENCES,
1893 };
1894
1895 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1896 {
1897 JIM_NOTUSED(interp);
1898
1899 /* This is a bit subtle: the only caller of this function
1900 * should be Jim_DuplicateObj(), that will copy the
1901 * string representaion. After the copy, the duplicated
1902 * object will not have more room in teh buffer than
1903 * srcPtr->length bytes. So we just set it to length. */
1904 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1905 }
1906
1907 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1908 {
1909 /* Get a fresh string representation. */
1910 (void) Jim_GetString(objPtr, NULL);
1911 /* Free any other internal representation. */
1912 Jim_FreeIntRep(interp, objPtr);
1913 /* Set it as string, i.e. just set the maxLength field. */
1914 objPtr->typePtr = &stringObjType;
1915 objPtr->internalRep.strValue.maxLength = objPtr->length;
1916 return JIM_OK;
1917 }
1918
1919 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1920 {
1921 Jim_Obj *objPtr = Jim_NewObj(interp);
1922
1923 if (len == -1)
1924 len = strlen(s);
1925 /* Alloc/Set the string rep. */
1926 if (len == 0) {
1927 objPtr->bytes = JimEmptyStringRep;
1928 objPtr->length = 0;
1929 } else {
1930 objPtr->bytes = Jim_Alloc(len+1);
1931 objPtr->length = len;
1932 memcpy(objPtr->bytes, s, len);
1933 objPtr->bytes[len] = '\0';
1934 }
1935
1936 /* No typePtr field for the vanilla string object. */
1937 objPtr->typePtr = NULL;
1938 return objPtr;
1939 }
1940
1941 /* This version does not try to duplicate the 's' pointer, but
1942 * use it directly. */
1943 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
1944 {
1945 Jim_Obj *objPtr = Jim_NewObj(interp);
1946
1947 if (len == -1)
1948 len = strlen(s);
1949 Jim_SetStringRep(objPtr, s, len);
1950 objPtr->typePtr = NULL;
1951 return objPtr;
1952 }
1953
1954 /* Low-level string append. Use it only against objects
1955 * of type "string". */
1956 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
1957 {
1958 int needlen;
1959
1960 if (len == -1)
1961 len = strlen(str);
1962 needlen = objPtr->length + len;
1963 if (objPtr->internalRep.strValue.maxLength < needlen ||
1964 objPtr->internalRep.strValue.maxLength == 0) {
1965 if (objPtr->bytes == JimEmptyStringRep) {
1966 objPtr->bytes = Jim_Alloc((needlen*2)+1);
1967 } else {
1968 objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
1969 }
1970 objPtr->internalRep.strValue.maxLength = needlen*2;
1971 }
1972 memcpy(objPtr->bytes + objPtr->length, str, len);
1973 objPtr->bytes[objPtr->length+len] = '\0';
1974 objPtr->length += len;
1975 }
1976
1977 /* Low-level wrapper to append an object. */
1978 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
1979 {
1980 int len;
1981 const char *str;
1982
1983 str = Jim_GetString(appendObjPtr, &len);
1984 StringAppendString(objPtr, str, len);
1985 }
1986
1987 /* Higher level API to append strings to objects. */
1988 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
1989 int len)
1990 {
1991 if (Jim_IsShared(objPtr))
1992 Jim_Panic(interp,"Jim_AppendString called with shared object");
1993 if (objPtr->typePtr != &stringObjType)
1994 SetStringFromAny(interp, objPtr);
1995 StringAppendString(objPtr, str, len);
1996 }
1997
1998 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
1999 Jim_Obj *appendObjPtr)
2000 {
2001 int len;
2002 const char *str;
2003
2004 str = Jim_GetString(appendObjPtr, &len);
2005 Jim_AppendString(interp, objPtr, str, len);
2006 }
2007
2008 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2009 {
2010 va_list ap;
2011
2012 if (objPtr->typePtr != &stringObjType)
2013 SetStringFromAny(interp, objPtr);
2014 va_start(ap, objPtr);
2015 while (1) {
2016 char *s = va_arg(ap, char*);
2017
2018 if (s == NULL) break;
2019 Jim_AppendString(interp, objPtr, s, -1);
2020 }
2021 va_end(ap);
2022 }
2023
2024 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2025 {
2026 const char *aStr, *bStr;
2027 int aLen, bLen, i;
2028
2029 if (aObjPtr == bObjPtr) return 1;
2030 aStr = Jim_GetString(aObjPtr, &aLen);
2031 bStr = Jim_GetString(bObjPtr, &bLen);
2032 if (aLen != bLen) return 0;
2033 if (nocase == 0)
2034 return memcmp(aStr, bStr, aLen) == 0;
2035 for (i = 0; i < aLen; i++) {
2036 if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2037 return 0;
2038 }
2039 return 1;
2040 }
2041
2042 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2043 int nocase)
2044 {
2045 const char *pattern, *string;
2046 int patternLen, stringLen;
2047
2048 pattern = Jim_GetString(patternObjPtr, &patternLen);
2049 string = Jim_GetString(objPtr, &stringLen);
2050 return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2051 }
2052
2053 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2054 Jim_Obj *secondObjPtr, int nocase)
2055 {
2056 const char *s1, *s2;
2057 int l1, l2;
2058
2059 s1 = Jim_GetString(firstObjPtr, &l1);
2060 s2 = Jim_GetString(secondObjPtr, &l2);
2061 return JimStringCompare(s1, l1, s2, l2, nocase);
2062 }
2063
2064 /* Convert a range, as returned by Jim_GetRange(), into
2065 * an absolute index into an object of the specified length.
2066 * This function may return negative values, or values
2067 * bigger or equal to the length of the list if the index
2068 * is out of range. */
2069 static int JimRelToAbsIndex(int len, int index)
2070 {
2071 if (index < 0)
2072 return len + index;
2073 return index;
2074 }
2075
2076 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2077 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2078 * for implementation of commands like [string range] and [lrange].
2079 *
2080 * The resulting range is guaranteed to address valid elements of
2081 * the structure. */
2082 static void JimRelToAbsRange(int len, int first, int last,
2083 int *firstPtr, int *lastPtr, int *rangeLenPtr)
2084 {
2085 int rangeLen;
2086
2087 if (first > last) {
2088 rangeLen = 0;
2089 } else {
2090 rangeLen = last-first+1;
2091 if (rangeLen) {
2092 if (first < 0) {
2093 rangeLen += first;
2094 first = 0;
2095 }
2096 if (last >= len) {
2097 rangeLen -= (last-(len-1));
2098 last = len-1;
2099 }
2100 }
2101 }
2102 if (rangeLen < 0) rangeLen = 0;
2103
2104 *firstPtr = first;
2105 *lastPtr = last;
2106 *rangeLenPtr = rangeLen;
2107 }
2108
2109 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2110 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2111 {
2112 int first, last;
2113 const char *str;
2114 int len, rangeLen;
2115
2116 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2117 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2118 return NULL;
2119 str = Jim_GetString(strObjPtr, &len);
2120 first = JimRelToAbsIndex(len, first);
2121 last = JimRelToAbsIndex(len, last);
2122 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2123 return Jim_NewStringObj(interp, str+first, rangeLen);
2124 }
2125
2126 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2127 {
2128 char *buf = Jim_Alloc(strObjPtr->length+1);
2129 int i;
2130
2131 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2132 for (i = 0; i < strObjPtr->length; i++)
2133 buf[i] = tolower(buf[i]);
2134 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2135 }
2136
2137 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2138 {
2139 char *buf = Jim_Alloc(strObjPtr->length+1);
2140 int i;
2141
2142 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2143 for (i = 0; i < strObjPtr->length; i++)
2144 buf[i] = toupper(buf[i]);
2145 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2146 }
2147
2148 /* This is the core of the [format] command.
2149 * TODO: Lots of things work - via a hack
2150 * However, no format item can be >= JIM_MAX_FMT
2151 */
2152 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2153 int objc, Jim_Obj *const *objv)
2154 {
2155 const char *fmt, *_fmt;
2156 int fmtLen;
2157 Jim_Obj *resObjPtr;
2158
2159
2160 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2161 _fmt = fmt;
2162 resObjPtr = Jim_NewStringObj(interp, "", 0);
2163 while (fmtLen) {
2164 const char *p = fmt;
2165 char spec[2], c;
2166 jim_wide wideValue;
2167 double doubleValue;
2168 /* we cheat and use Sprintf()! */
2169 #define JIM_MAX_FMT 2048
2170 char sprintf_buf[JIM_MAX_FMT];
2171 char fmt_str[100];
2172 char *cp;
2173 int width;
2174 int ljust;
2175 int zpad;
2176 int spad;
2177 int altfm;
2178 int forceplus;
2179
2180 while (*fmt != '%' && fmtLen) {
2181 fmt++; fmtLen--;
2182 }
2183 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2184 if (fmtLen == 0)
2185 break;
2186 fmt++; fmtLen--; /* skip '%' */
2187 zpad = 0;
2188 spad = 0;
2189 width = -1;
2190 ljust = 0;
2191 altfm = 0;
2192 forceplus = 0;
2193 next_fmt:
2194 if( fmtLen <= 0 ){
2195 break;
2196 }
2197 switch( *fmt ){
2198 /* terminals */
2199 case 'b': /* binary - not all printfs() do this */
2200 case 's': /* string */
2201 case 'i': /* integer */
2202 case 'd': /* decimal */
2203 case 'x': /* hex */
2204 case 'X': /* CAP hex */
2205 case 'c': /* char */
2206 case 'o': /* octal */
2207 case 'u': /* unsigned */
2208 case 'f': /* float */
2209 break;
2210
2211 /* non-terminals */
2212 case '0': /* zero pad */
2213 zpad = 1;
2214 *fmt++; fmtLen--;
2215 goto next_fmt;
2216 break;
2217 case '+':
2218 forceplus = 1;
2219 *fmt++; fmtLen--;
2220 goto next_fmt;
2221 break;
2222 case ' ': /* sign space */
2223 spad = 1;
2224 *fmt++; fmtLen--;
2225 goto next_fmt;
2226 break;
2227 case '-':
2228 ljust = 1;
2229 *fmt++; fmtLen--;
2230 goto next_fmt;
2231 break;
2232 case '#':
2233 altfm = 1;
2234 *fmt++; fmtLen--;
2235 goto next_fmt;
2236
2237 case '1':
2238 case '2':
2239 case '3':
2240 case '4':
2241 case '5':
2242 case '6':
2243 case '7':
2244 case '8':
2245 case '9':
2246 width = 0;
2247 while( isdigit(*fmt) && (fmtLen > 0) ){
2248 width = (width * 10) + (*fmt - '0');
2249 fmt++; fmtLen--;
2250 }
2251 goto next_fmt;
2252 case '*':
2253 /* suck up the next item as an integer */
2254 *fmt++; fmtLen--;
2255 objc--;
2256 if( objc <= 0 ){
2257 goto not_enough_args;
2258 }
2259 if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2260 Jim_FreeNewObj(interp, resObjPtr );
2261 return NULL;
2262 }
2263 width = wideValue;
2264 if( width < 0 ){
2265 ljust = 1;
2266 width = -width;
2267 }
2268 objv++;
2269 goto next_fmt;
2270 break;
2271 }
2272
2273
2274 if (*fmt != '%') {
2275 if (objc == 0) {
2276 not_enough_args:
2277 Jim_FreeNewObj(interp, resObjPtr);
2278 Jim_SetResultString(interp,
2279 "not enough arguments for all format specifiers", -1);
2280 return NULL;
2281 } else {
2282 objc--;
2283 }
2284 }
2285
2286 /*
2287 * Create the formatter
2288 * cause we cheat and use sprintf()
2289 */
2290 cp = fmt_str;
2291 *cp++ = '%';
2292 if( altfm ){
2293 *cp++ = '#';
2294 }
2295 if( forceplus ){
2296 *cp++ = '+';
2297 } else if( spad ){
2298 /* PLUS overrides */
2299 *cp++ = ' ';
2300 }
2301 if( ljust ){
2302 *cp++ = '-';
2303 }
2304 if( zpad ){
2305 *cp++ = '0';
2306 }
2307 if( width > 0 ){
2308 sprintf( cp, "%d", width );
2309 /* skip ahead */
2310 cp = strchr(cp,0);
2311 }
2312 *cp = 0;
2313
2314 /* here we do the work */
2315 /* actually - we make sprintf() do it for us */
2316 switch(*fmt) {
2317 case 's':
2318 *cp++ = 's';
2319 *cp = 0;
2320 /* BUG: we do not handled embeded NULLs */
2321 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2322 break;
2323 case 'c':
2324 *cp++ = 'c';
2325 *cp = 0;
2326 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2327 Jim_FreeNewObj(interp, resObjPtr);
2328 return NULL;
2329 }
2330 c = (char) wideValue;
2331 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2332 break;
2333 case 'f':
2334 case 'F':
2335 case 'g':
2336 case 'G':
2337 case 'e':
2338 case 'E':
2339 *cp++ = *fmt;
2340 *cp = 0;
2341 if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2342 Jim_FreeNewObj( interp, resObjPtr );
2343 return NULL;
2344 }
2345 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2346 break;
2347 case 'b':
2348 case 'd':
2349 case 'i':
2350 case 'u':
2351 case 'x':
2352 case 'X':
2353 /* jim widevaluse are 64bit */
2354 if( sizeof(jim_wide) == sizeof(long long) ){
2355 *cp++ = 'l';
2356 *cp++ = 'l';
2357 } else {
2358 *cp++ = 'l';
2359 }
2360 *cp++ = *fmt;
2361 *cp = 0;
2362 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2363 Jim_FreeNewObj(interp, resObjPtr);
2364 return NULL;
2365 }
2366 snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2367 break;
2368 case '%':
2369 sprintf_buf[0] = '%';
2370 sprintf_buf[1] = 0;
2371 objv--; /* undo the objv++ below */
2372 break;
2373 default:
2374 spec[0] = *fmt; spec[1] = '\0';
2375 Jim_FreeNewObj(interp, resObjPtr);
2376 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2377 Jim_AppendStrings(interp, Jim_GetResult(interp),
2378 "bad field specifier \"", spec, "\"", NULL);
2379 return NULL;
2380 }
2381 /* force terminate */
2382 #if 0
2383 printf("FMT was: %s\n", fmt_str );
2384 printf("RES was: |%s|\n", sprintf_buf );
2385 #endif
2386
2387 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2388 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2389 /* next obj */
2390 objv++;
2391 fmt++;
2392 fmtLen--;
2393 }
2394 return resObjPtr;
2395 }
2396
2397 /* -----------------------------------------------------------------------------
2398 * Compared String Object
2399 * ---------------------------------------------------------------------------*/
2400
2401 /* This is strange object that allows to compare a C literal string
2402 * with a Jim object in very short time if the same comparison is done
2403 * multiple times. For example every time the [if] command is executed,
2404 * Jim has to check if a given argument is "else". This comparions if
2405 * the code has no errors are true most of the times, so we can cache
2406 * inside the object the pointer of the string of the last matching
2407 * comparison. Because most C compilers perform literal sharing,
2408 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2409 * this works pretty well even if comparisons are at different places
2410 * inside the C code. */
2411
2412 static Jim_ObjType comparedStringObjType = {
2413 "compared-string",
2414 NULL,
2415 NULL,
2416 NULL,
2417 JIM_TYPE_REFERENCES,
2418 };
2419
2420 /* The only way this object is exposed to the API is via the following
2421 * function. Returns true if the string and the object string repr.
2422 * are the same, otherwise zero is returned.
2423 *
2424 * Note: this isn't binary safe, but it hardly needs to be.*/
2425 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2426 const char *str)
2427 {
2428 if (objPtr->typePtr == &comparedStringObjType &&
2429 objPtr->internalRep.ptr == str)
2430 return 1;
2431 else {
2432 const char *objStr = Jim_GetString(objPtr, NULL);
2433 if (strcmp(str, objStr) != 0) return 0;
2434 if (objPtr->typePtr != &comparedStringObjType) {
2435 Jim_FreeIntRep(interp, objPtr);
2436 objPtr->typePtr = &comparedStringObjType;
2437 }
2438 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2439 return 1;
2440 }
2441 }
2442
2443 int qsortCompareStringPointers(const void *a, const void *b)
2444 {
2445 char * const *sa = (char * const *)a;
2446 char * const *sb = (char * const *)b;
2447 return strcmp(*sa, *sb);
2448 }
2449
2450 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2451 const char **tablePtr, int *indexPtr, const char *name, int flags)
2452 {
2453 const char **entryPtr = NULL;
2454 char **tablePtrSorted;
2455 int i, count = 0;
2456
2457 *indexPtr = -1;
2458 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2459 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2460 *indexPtr = i;
2461 return JIM_OK;
2462 }
2463 count++; /* If nothing matches, this will reach the len of tablePtr */
2464 }
2465 if (flags & JIM_ERRMSG) {
2466 if (name == NULL)
2467 name = "option";
2468 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2469 Jim_AppendStrings(interp, Jim_GetResult(interp),
2470 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2471 NULL);
2472 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2473 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2474 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2475 for (i = 0; i < count; i++) {
2476 if (i+1 == count && count > 1)
2477 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2478 Jim_AppendString(interp, Jim_GetResult(interp),
2479 tablePtrSorted[i], -1);
2480 if (i+1 != count)
2481 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2482 }
2483 Jim_Free(tablePtrSorted);
2484 }
2485 return JIM_ERR;
2486 }
2487
2488 /* -----------------------------------------------------------------------------
2489 * Source Object
2490 *
2491 * This object is just a string from the language point of view, but
2492 * in the internal representation it contains the filename and line number
2493 * where this given token was read. This information is used by
2494 * Jim_EvalObj() if the object passed happens to be of type "source".
2495 *
2496 * This allows to propagate the information about line numbers and file
2497 * names and give error messages with absolute line numbers.
2498 *
2499 * Note that this object uses shared strings for filenames, and the
2500 * pointer to the filename together with the line number is taken into
2501 * the space for the "inline" internal represenation of the Jim_Object,
2502 * so there is almost memory zero-overhead.
2503 *
2504 * Also the object will be converted to something else if the given
2505 * token it represents in the source file is not something to be
2506 * evaluated (not a script), and will be specialized in some other way,
2507 * so the time overhead is alzo null.
2508 * ---------------------------------------------------------------------------*/
2509
2510 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2511 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2512
2513 static Jim_ObjType sourceObjType = {
2514 "source",
2515 FreeSourceInternalRep,
2516 DupSourceInternalRep,
2517 NULL,
2518 JIM_TYPE_REFERENCES,
2519 };
2520
2521 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2522 {
2523 Jim_ReleaseSharedString(interp,
2524 objPtr->internalRep.sourceValue.fileName);
2525 }
2526
2527 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2528 {
2529 dupPtr->internalRep.sourceValue.fileName =
2530 Jim_GetSharedString(interp,
2531 srcPtr->internalRep.sourceValue.fileName);
2532 dupPtr->internalRep.sourceValue.lineNumber =
2533 dupPtr->internalRep.sourceValue.lineNumber;
2534 dupPtr->typePtr = &sourceObjType;
2535 }
2536
2537 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2538 const char *fileName, int lineNumber)
2539 {
2540 if (Jim_IsShared(objPtr))
2541 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2542 if (objPtr->typePtr != NULL)
2543 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2544 objPtr->internalRep.sourceValue.fileName =
2545 Jim_GetSharedString(interp, fileName);
2546 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2547 objPtr->typePtr = &sourceObjType;
2548 }
2549
2550 /* -----------------------------------------------------------------------------
2551 * Script Object
2552 * ---------------------------------------------------------------------------*/
2553
2554 #define JIM_CMDSTRUCT_EXPAND -1
2555
2556 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2557 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2558 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2559
2560 static Jim_ObjType scriptObjType = {
2561 "script",
2562 FreeScriptInternalRep,
2563 DupScriptInternalRep,
2564 NULL,
2565 JIM_TYPE_REFERENCES,
2566 };
2567
2568 /* The ScriptToken structure represents every token into a scriptObj.
2569 * Every token contains an associated Jim_Obj that can be specialized
2570 * by commands operating on it. */
2571 typedef struct ScriptToken {
2572 int type;
2573 Jim_Obj *objPtr;
2574 int linenr;
2575 } ScriptToken;
2576
2577 /* This is the script object internal representation. An array of
2578 * ScriptToken structures, with an associated command structure array.
2579 * The command structure is a pre-computed representation of the
2580 * command length and arguments structure as a simple liner array
2581 * of integers.
2582 *
2583 * For example the script:
2584 *
2585 * puts hello
2586 * set $i $x$y [foo]BAR
2587 *
2588 * will produce a ScriptObj with the following Tokens:
2589 *
2590 * ESC puts
2591 * SEP
2592 * ESC hello
2593 * EOL
2594 * ESC set
2595 * EOL
2596 * VAR i
2597 * SEP
2598 * VAR x
2599 * VAR y
2600 * SEP
2601 * CMD foo
2602 * ESC BAR
2603 * EOL
2604 *
2605 * This is a description of the tokens, separators, and of lines.
2606 * The command structure instead represents the number of arguments
2607 * of every command, followed by the tokens of which every argument
2608 * is composed. So for the example script, the cmdstruct array will
2609 * contain:
2610 *
2611 * 2 1 1 4 1 1 2 2
2612 *
2613 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2614 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2615 * composed of single tokens (1 1) and the last two of double tokens
2616 * (2 2).
2617 *
2618 * The precomputation of the command structure makes Jim_Eval() faster,
2619 * and simpler because there aren't dynamic lengths / allocations.
2620 *
2621 * -- {expand} handling --
2622 *
2623 * Expand is handled in a special way. When a command
2624 * contains at least an argument with the {expand} prefix,
2625 * the command structure presents a -1 before the integer
2626 * describing the number of arguments. This is used in order
2627 * to send the command exection to a different path in case
2628 * of {expand} and guarantee a fast path for the more common
2629 * case. Also, the integers describing the number of tokens
2630 * are expressed with negative sign, to allow for fast check
2631 * of what's an {expand}-prefixed argument and what not.
2632 *
2633 * For example the command:
2634 *
2635 * list {expand}{1 2}
2636 *
2637 * Will produce the following cmdstruct array:
2638 *
2639 * -1 2 1 -2
2640 *
2641 * -- the substFlags field of the structure --
2642 *
2643 * The scriptObj structure is used to represent both "script" objects
2644 * and "subst" objects. In the second case, the cmdStruct related
2645 * fields are not used at all, but there is an additional field used
2646 * that is 'substFlags': this represents the flags used to turn
2647 * the string into the intenral representation used to perform the
2648 * substitution. If this flags are not what the application requires
2649 * the scriptObj is created again. For example the script:
2650 *
2651 * subst -nocommands $string
2652 * subst -novariables $string
2653 *
2654 * Will recreate the internal representation of the $string object
2655 * two times.
2656 */
2657 typedef struct ScriptObj {
2658 int len; /* Length as number of tokens. */
2659 int commands; /* number of top-level commands in script. */
2660 ScriptToken *token; /* Tokens array. */
2661 int *cmdStruct; /* commands structure */
2662 int csLen; /* length of the cmdStruct array. */
2663 int substFlags; /* flags used for the compilation of "subst" objects */
2664 int inUse; /* Used to share a ScriptObj. Currently
2665 only used by Jim_EvalObj() as protection against
2666 shimmering of the currently evaluated object. */
2667 char *fileName;
2668 } ScriptObj;
2669
2670 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2671 {
2672 int i;
2673 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2674
2675 script->inUse--;
2676 if (script->inUse != 0) return;
2677 for (i = 0; i < script->len; i++) {
2678 if (script->token[i].objPtr != NULL)
2679 Jim_DecrRefCount(interp, script->token[i].objPtr);
2680 }
2681 Jim_Free(script->token);
2682 Jim_Free(script->cmdStruct);
2683 Jim_Free(script->fileName);
2684 Jim_Free(script);
2685 }
2686
2687 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2688 {
2689 JIM_NOTUSED(interp);
2690 JIM_NOTUSED(srcPtr);
2691
2692 /* Just returns an simple string. */
2693 dupPtr->typePtr = NULL;
2694 }
2695
2696 /* Add a new token to the internal repr of a script object */
2697 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2698 char *strtoken, int len, int type, char *filename, int linenr)
2699 {
2700 int prevtype;
2701 struct ScriptToken *token;
2702
2703 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2704 script->token[script->len-1].type;
2705 /* Skip tokens without meaning, like words separators
2706 * following a word separator or an end of command and
2707 * so on. */
2708 if (prevtype == JIM_TT_EOL) {
2709 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2710 Jim_Free(strtoken);
2711 return;
2712 }
2713 } else if (prevtype == JIM_TT_SEP) {
2714 if (type == JIM_TT_SEP) {
2715 Jim_Free(strtoken);
2716 return;
2717 } else if (type == JIM_TT_EOL) {
2718 /* If an EOL is following by a SEP, drop the previous
2719 * separator. */
2720 script->len--;
2721 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2722 }
2723 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2724 type == JIM_TT_ESC && len == 0)
2725 {
2726 /* Don't add empty tokens used in interpolation */
2727 Jim_Free(strtoken);
2728 return;
2729 }
2730 /* Make space for a new istruction */
2731 script->len++;
2732 script->token = Jim_Realloc(script->token,
2733 sizeof(ScriptToken)*script->len);
2734 /* Initialize the new token */
2735 token = script->token+(script->len-1);
2736 token->type = type;
2737 /* Every object is intially as a string, but the
2738 * internal type may be specialized during execution of the
2739 * script. */
2740 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2741 /* To add source info to SEP and EOL tokens is useless because
2742 * they will never by called as arguments of Jim_EvalObj(). */
2743 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2744 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2745 Jim_IncrRefCount(token->objPtr);
2746 token->linenr = linenr;
2747 }
2748
2749 /* Add an integer into the command structure field of the script object. */
2750 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2751 {
2752 script->csLen++;
2753 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2754 sizeof(int)*script->csLen);
2755 script->cmdStruct[script->csLen-1] = val;
2756 }
2757
2758 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2759 * of objPtr. Search nested script objects recursively. */
2760 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2761 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2762 {
2763 int i;
2764
2765 for (i = 0; i < script->len; i++) {
2766 if (script->token[i].objPtr != objPtr &&
2767 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2768 return script->token[i].objPtr;
2769 }
2770 /* Enter recursively on scripts only if the object
2771 * is not the same as the one we are searching for
2772 * shared occurrences. */
2773 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2774 script->token[i].objPtr != objPtr) {
2775 Jim_Obj *foundObjPtr;
2776
2777 ScriptObj *subScript =
2778 script->token[i].objPtr->internalRep.ptr;
2779 /* Don't recursively enter the script we are trying
2780 * to make shared to avoid circular references. */
2781 if (subScript == scriptBarrier) continue;
2782 if (subScript != script) {
2783 foundObjPtr =
2784 ScriptSearchLiteral(interp, subScript,
2785 scriptBarrier, objPtr);
2786 if (foundObjPtr != NULL)
2787 return foundObjPtr;
2788 }
2789 }
2790 }
2791 return NULL;
2792 }
2793
2794 /* Share literals of a script recursively sharing sub-scripts literals. */
2795 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2796 ScriptObj *topLevelScript)
2797 {
2798 int i, j;
2799
2800 return;
2801 /* Try to share with toplevel object. */
2802 if (topLevelScript != NULL) {
2803 for (i = 0; i < script->len; i++) {
2804 Jim_Obj *foundObjPtr;
2805 char *str = script->token[i].objPtr->bytes;
2806
2807 if (script->token[i].objPtr->refCount != 1) continue;
2808 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2809 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2810 foundObjPtr = ScriptSearchLiteral(interp,
2811 topLevelScript,
2812 script, /* barrier */
2813 script->token[i].objPtr);
2814 if (foundObjPtr != NULL) {
2815 Jim_IncrRefCount(foundObjPtr);
2816 Jim_DecrRefCount(interp,
2817 script->token[i].objPtr);
2818 script->token[i].objPtr = foundObjPtr;
2819 }
2820 }
2821 }
2822 /* Try to share locally */
2823 for (i = 0; i < script->len; i++) {
2824 char *str = script->token[i].objPtr->bytes;
2825
2826 if (script->token[i].objPtr->refCount != 1) continue;
2827 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2828 for (j = 0; j < script->len; j++) {
2829 if (script->token[i].objPtr !=
2830 script->token[j].objPtr &&
2831 Jim_StringEqObj(script->token[i].objPtr,
2832 script->token[j].objPtr, 0))
2833 {
2834 Jim_IncrRefCount(script->token[j].objPtr);
2835 Jim_DecrRefCount(interp,
2836 script->token[i].objPtr);
2837 script->token[i].objPtr =
2838 script->token[j].objPtr;
2839 }
2840 }
2841 }
2842 }
2843
2844 /* This method takes the string representation of an object
2845 * as a Tcl script, and generates the pre-parsed internal representation
2846 * of the script. */
2847 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2848 {
2849 int scriptTextLen;
2850 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2851 struct JimParserCtx parser;
2852 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2853 ScriptToken *token;
2854 int args, tokens, start, end, i;
2855 int initialLineNumber;
2856 int propagateSourceInfo = 0;
2857
2858 script->len = 0;
2859 script->csLen = 0;
2860 script->commands = 0;
2861 script->token = NULL;
2862 script->cmdStruct = NULL;
2863 script->inUse = 1;
2864 /* Try to get information about filename / line number */
2865 if (objPtr->typePtr == &sourceObjType) {
2866 script->fileName =
2867 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2868 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2869 propagateSourceInfo = 1;
2870 } else {
2871 script->fileName = Jim_StrDup("?");
2872 initialLineNumber = 1;
2873 }
2874
2875 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2876 while(!JimParserEof(&parser)) {
2877 char *token;
2878 int len, type, linenr;
2879
2880 JimParseScript(&parser);
2881 token = JimParserGetToken(&parser, &len, &type, &linenr);
2882 ScriptObjAddToken(interp, script, token, len, type,
2883 propagateSourceInfo ? script->fileName : NULL,
2884 linenr);
2885 }
2886 token = script->token;
2887
2888 /* Compute the command structure array
2889 * (see the ScriptObj struct definition for more info) */
2890 start = 0; /* Current command start token index */
2891 end = -1; /* Current command end token index */
2892 while (1) {
2893 int expand = 0; /* expand flag. set to 1 on {expand} form. */
2894 int interpolation = 0; /* set to 1 if there is at least one
2895 argument of the command obtained via
2896 interpolation of more tokens. */
2897 /* Search for the end of command, while
2898 * count the number of args. */
2899 start = ++end;
2900 if (start >= script->len) break;
2901 args = 1; /* Number of args in current command */
2902 while (token[end].type != JIM_TT_EOL) {
2903 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2904 token[end-1].type == JIM_TT_EOL)
2905 {
2906 if (token[end].type == JIM_TT_STR &&
2907 token[end+1].type != JIM_TT_SEP &&
2908 token[end+1].type != JIM_TT_EOL &&
2909 (!strcmp(token[end].objPtr->bytes, "expand") ||
2910 !strcmp(token[end].objPtr->bytes, "*")))
2911 expand++;
2912 }
2913 if (token[end].type == JIM_TT_SEP)
2914 args++;
2915 end++;
2916 }
2917 interpolation = !((end-start+1) == args*2);
2918 /* Add the 'number of arguments' info into cmdstruct.
2919 * Negative value if there is list expansion involved. */
2920 if (expand)
2921 ScriptObjAddInt(script, -1);
2922 ScriptObjAddInt(script, args);
2923 /* Now add info about the number of tokens. */
2924 tokens = 0; /* Number of tokens in current argument. */
2925 expand = 0;
2926 for (i = start; i <= end; i++) {
2927 if (token[i].type == JIM_TT_SEP ||
2928 token[i].type == JIM_TT_EOL)
2929 {
2930 if (tokens == 1 && expand)
2931 expand = 0;
2932 ScriptObjAddInt(script,
2933 expand ? -tokens : tokens);
2934
2935 expand = 0;
2936 tokens = 0;
2937 continue;
2938 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2939 (!strcmp(token[i].objPtr->bytes, "expand") ||
2940 !strcmp(token[i].objPtr->bytes, "*")))
2941 {
2942 expand++;
2943 }
2944 tokens++;
2945 }
2946 }
2947 /* Perform literal sharing, but only for objects that appear
2948 * to be scripts written as literals inside the source code,
2949 * and not computed at runtime. Literal sharing is a costly
2950 * operation that should be done only against objects that
2951 * are likely to require compilation only the first time, and
2952 * then are executed multiple times. */
2953 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2954 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2955 if (bodyObjPtr->typePtr == &scriptObjType) {
2956 ScriptObj *bodyScript =
2957 bodyObjPtr->internalRep.ptr;
2958 ScriptShareLiterals(interp, script, bodyScript);
2959 }
2960 } else if (propagateSourceInfo) {
2961 ScriptShareLiterals(interp, script, NULL);
2962 }
2963 /* Free the old internal rep and set the new one. */
2964 Jim_FreeIntRep(interp, objPtr);
2965 Jim_SetIntRepPtr(objPtr, script);
2966 objPtr->typePtr = &scriptObjType;
2967 return JIM_OK;
2968 }
2969
2970 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
2971 {
2972 if (objPtr->typePtr != &scriptObjType) {
2973 SetScriptFromAny(interp, objPtr);
2974 }
2975 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
2976 }
2977
2978 /* -----------------------------------------------------------------------------
2979 * Commands
2980 * ---------------------------------------------------------------------------*/
2981
2982 /* Commands HashTable Type.
2983 *
2984 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
2985 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
2986 {
2987 Jim_Cmd *cmdPtr = (void*) val;
2988
2989 if (cmdPtr->cmdProc == NULL) {
2990 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2991 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2992 if (cmdPtr->staticVars) {
2993 Jim_FreeHashTable(cmdPtr->staticVars);
2994 Jim_Free(cmdPtr->staticVars);
2995 }
2996 } else if (cmdPtr->delProc != NULL) {
2997 /* If it was a C coded command, call the delProc if any */
2998 cmdPtr->delProc(interp, cmdPtr->privData);
2999 }
3000 Jim_Free(val);
3001 }
3002
3003 static Jim_HashTableType JimCommandsHashTableType = {
3004 JimStringCopyHTHashFunction, /* hash function */
3005 JimStringCopyHTKeyDup, /* key dup */
3006 NULL, /* val dup */
3007 JimStringCopyHTKeyCompare, /* key compare */
3008 JimStringCopyHTKeyDestructor, /* key destructor */
3009 Jim_CommandsHT_ValDestructor /* val destructor */
3010 };
3011
3012 /* ------------------------- Commands related functions --------------------- */
3013
3014 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3015 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3016 {
3017 Jim_HashEntry *he;
3018 Jim_Cmd *cmdPtr;
3019
3020 he = Jim_FindHashEntry(&interp->commands, cmdName);
3021 if (he == NULL) { /* New command to create */
3022 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3023 cmdPtr->cmdProc = cmdProc;
3024 cmdPtr->privData = privData;
3025 cmdPtr->delProc = delProc;
3026 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3027 } else {
3028 Jim_InterpIncrProcEpoch(interp);
3029 /* Free the arglist/body objects if it was a Tcl procedure */
3030 cmdPtr = he->val;
3031 if (cmdPtr->cmdProc == NULL) {
3032 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3033 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3034 if (cmdPtr->staticVars) {
3035 Jim_FreeHashTable(cmdPtr->staticVars);
3036 Jim_Free(cmdPtr->staticVars);
3037 }
3038 cmdPtr->staticVars = NULL;
3039 } else if (cmdPtr->delProc != NULL) {
3040 /* If it was a C coded command, call the delProc if any */
3041 cmdPtr->delProc(interp, cmdPtr->privData);
3042 }
3043 cmdPtr->cmdProc = cmdProc;
3044 cmdPtr->privData = privData;
3045 }
3046 /* There is no need to increment the 'proc epoch' because
3047 * creation of a new procedure can never affect existing
3048 * cached commands. We don't do negative caching. */
3049 return JIM_OK;
3050 }
3051
3052 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3053 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3054 int arityMin, int arityMax)
3055 {
3056 Jim_Cmd *cmdPtr;
3057
3058 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3059 cmdPtr->cmdProc = NULL; /* Not a C coded command */
3060 cmdPtr->argListObjPtr = argListObjPtr;
3061 cmdPtr->bodyObjPtr = bodyObjPtr;
3062 Jim_IncrRefCount(argListObjPtr);
3063 Jim_IncrRefCount(bodyObjPtr);
3064 cmdPtr->arityMin = arityMin;
3065 cmdPtr->arityMax = arityMax;
3066 cmdPtr->staticVars = NULL;
3067
3068 /* Create the statics hash table. */
3069 if (staticsListObjPtr) {
3070 int len, i;
3071
3072 Jim_ListLength(interp, staticsListObjPtr, &len);
3073 if (len != 0) {
3074 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3075 Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
3076 interp);
3077 for (i = 0; i < len; i++) {
3078 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3079 Jim_Var *varPtr;
3080 int subLen;
3081
3082 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3083 /* Check if it's composed of two elements. */
3084 Jim_ListLength(interp, objPtr, &subLen);
3085 if (subLen == 1 || subLen == 2) {
3086 /* Try to get the variable value from the current
3087 * environment. */
3088 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3089 if (subLen == 1) {
3090 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3091 JIM_NONE);
3092 if (initObjPtr == NULL) {
3093 Jim_SetResult(interp,
3094 Jim_NewEmptyStringObj(interp));
3095 Jim_AppendStrings(interp, Jim_GetResult(interp),
3096 "variable for initialization of static \"",
3097 Jim_GetString(nameObjPtr, NULL),
3098 "\" not found in the local context",
3099 NULL);
3100 goto err;
3101 }
3102 } else {
3103 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3104 }
3105 varPtr = Jim_Alloc(sizeof(*varPtr));
3106 varPtr->objPtr = initObjPtr;
3107 Jim_IncrRefCount(initObjPtr);
3108 varPtr->linkFramePtr = NULL;
3109 if (Jim_AddHashEntry(cmdPtr->staticVars,
3110 Jim_GetString(nameObjPtr, NULL),
3111 varPtr) != JIM_OK)
3112 {
3113 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3114 Jim_AppendStrings(interp, Jim_GetResult(interp),
3115 "static variable name \"",
3116 Jim_GetString(objPtr, NULL), "\"",
3117 " duplicated in statics list", NULL);
3118 Jim_DecrRefCount(interp, initObjPtr);
3119 Jim_Free(varPtr);
3120 goto err;
3121 }
3122 } else {
3123 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3124 Jim_AppendStrings(interp, Jim_GetResult(interp),
3125 "too many fields in static specifier \"",
3126 objPtr, "\"", NULL);
3127 goto err;
3128 }
3129 }
3130 }
3131 }
3132
3133 /* Add the new command */
3134
3135 /* it may already exist, so we try to delete the old one */
3136 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3137 /* There was an old procedure with the same name, this requires
3138 * a 'proc epoch' update. */
3139 Jim_InterpIncrProcEpoch(interp);
3140 }
3141 /* If a procedure with the same name didn't existed there is no need
3142 * to increment the 'proc epoch' because creation of a new procedure
3143 * can never affect existing cached commands. We don't do
3144 * negative caching. */
3145 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3146 return JIM_OK;
3147
3148 err:
3149 Jim_FreeHashTable(cmdPtr->staticVars);
3150 Jim_Free(cmdPtr->staticVars);
3151 Jim_DecrRefCount(interp, argListObjPtr);
3152 Jim_DecrRefCount(interp, bodyObjPtr);
3153 Jim_Free(cmdPtr);
3154 return JIM_ERR;
3155 }
3156
3157 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3158 {
3159 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3160 return JIM_ERR;
3161 Jim_InterpIncrProcEpoch(interp);
3162 return JIM_OK;
3163 }
3164
3165 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
3166 const char *newName)
3167 {
3168 Jim_Cmd *cmdPtr;
3169 Jim_HashEntry *he;
3170 Jim_Cmd *copyCmdPtr;
3171
3172 if (newName[0] == '\0') /* Delete! */
3173 return Jim_DeleteCommand(interp, oldName);
3174 /* Rename */
3175 he = Jim_FindHashEntry(&interp->commands, oldName);
3176 if (he == NULL)
3177 return JIM_ERR; /* Invalid command name */
3178 cmdPtr = he->val;
3179 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3180 *copyCmdPtr = *cmdPtr;
3181 /* In order to avoid that a procedure will get arglist/body/statics
3182 * freed by the hash table methods, fake a C-coded command
3183 * setting cmdPtr->cmdProc as not NULL */
3184 cmdPtr->cmdProc = (void*)1;
3185 /* Also make sure delProc is NULL. */
3186 cmdPtr->delProc = NULL;
3187 /* Destroy the old command, and make sure the new is freed
3188 * as well. */
3189 Jim_DeleteHashEntry(&interp->commands, oldName);
3190 Jim_DeleteHashEntry(&interp->commands, newName);
3191 /* Now the new command. We are sure it can't fail because
3192 * the target name was already freed. */
3193 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3194 /* Increment the epoch */
3195 Jim_InterpIncrProcEpoch(interp);
3196 return JIM_OK;
3197 }
3198
3199 /* -----------------------------------------------------------------------------
3200 * Command object
3201 * ---------------------------------------------------------------------------*/
3202
3203 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3204
3205 static Jim_ObjType commandObjType = {
3206 "command",
3207 NULL,
3208 NULL,
3209 NULL,
3210 JIM_TYPE_REFERENCES,
3211 };
3212
3213 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3214 {
3215 Jim_HashEntry *he;
3216 const char *cmdName;
3217
3218 /* Get the string representation */
3219 cmdName = Jim_GetString(objPtr, NULL);
3220 /* Lookup this name into the commands hash table */
3221 he = Jim_FindHashEntry(&interp->commands, cmdName);
3222 if (he == NULL)
3223 return JIM_ERR;
3224
3225 /* Free the old internal repr and set the new one. */
3226 Jim_FreeIntRep(interp, objPtr);
3227 objPtr->typePtr = &commandObjType;
3228 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3229 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3230 return JIM_OK;
3231 }
3232
3233 /* This function returns the command structure for the command name
3234 * stored in objPtr. It tries to specialize the objPtr to contain
3235 * a cached info instead to perform the lookup into the hash table
3236 * every time. The information cached may not be uptodate, in such
3237 * a case the lookup is performed and the cache updated. */
3238 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3239 {
3240 if ((objPtr->typePtr != &commandObjType ||
3241 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3242 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3243 if (flags & JIM_ERRMSG) {
3244 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3245 Jim_AppendStrings(interp, Jim_GetResult(interp),
3246 "invalid command name \"", objPtr->bytes, "\"",
3247 NULL);
3248 }
3249 return NULL;
3250 }
3251 return objPtr->internalRep.cmdValue.cmdPtr;
3252 }
3253
3254 /* -----------------------------------------------------------------------------
3255 * Variables
3256 * ---------------------------------------------------------------------------*/
3257
3258 /* Variables HashTable Type.
3259 *
3260 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3261 static void JimVariablesHTValDestructor(void *interp, void *val)
3262 {
3263 Jim_Var *varPtr = (void*) val;
3264
3265 Jim_DecrRefCount(interp, varPtr->objPtr);
3266 Jim_Free(val);
3267 }
3268
3269 static Jim_HashTableType JimVariablesHashTableType = {
3270 JimStringCopyHTHashFunction, /* hash function */
3271 JimStringCopyHTKeyDup, /* key dup */
3272 NULL, /* val dup */
3273 JimStringCopyHTKeyCompare, /* key compare */
3274 JimStringCopyHTKeyDestructor, /* key destructor */
3275 JimVariablesHTValDestructor /* val destructor */
3276 };
3277
3278 /* -----------------------------------------------------------------------------
3279 * Variable object
3280 * ---------------------------------------------------------------------------*/
3281
3282 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3283
3284 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3285
3286 static Jim_ObjType variableObjType = {
3287 "variable",
3288 NULL,
3289 NULL,
3290 NULL,
3291 JIM_TYPE_REFERENCES,
3292 };
3293
3294 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3295 * is in the form "varname(key)". */
3296 static int Jim_NameIsDictSugar(const char *str, int len)
3297 {
3298 if (len == -1)
3299 len = strlen(str);
3300 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3301 return 1;
3302 return 0;
3303 }
3304
3305 /* This method should be called only by the variable API.
3306 * It returns JIM_OK on success (variable already exists),
3307 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3308 * a variable name, but syntax glue for [dict] i.e. the last
3309 * character is ')' */
3310 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3311 {
3312 Jim_HashEntry *he;
3313 const char *varName;
3314 int len;
3315
3316 /* Check if the object is already an uptodate variable */
3317 if (objPtr->typePtr == &variableObjType &&
3318 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3319 return JIM_OK; /* nothing to do */
3320 /* Get the string representation */
3321 varName = Jim_GetString(objPtr, &len);
3322 /* Make sure it's not syntax glue to get/set dict. */
3323 if (Jim_NameIsDictSugar(varName, len))
3324 return JIM_DICT_SUGAR;
3325 /* Lookup this name into the variables hash table */
3326 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3327 if (he == NULL) {
3328 /* Try with static vars. */
3329 if (interp->framePtr->staticVars == NULL)
3330 return JIM_ERR;
3331 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3332 return JIM_ERR;
3333 }
3334 /* Free the old internal repr and set the new one. */
3335 Jim_FreeIntRep(interp, objPtr);
3336 objPtr->typePtr = &variableObjType;
3337 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3338 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3339 return JIM_OK;
3340 }
3341
3342 /* -------------------- Variables related functions ------------------------- */
3343 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3344 Jim_Obj *valObjPtr);
3345 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3346
3347 /* For now that's dummy. Variables lookup should be optimized
3348 * in many ways, with caching of lookups, and possibly with
3349 * a table of pre-allocated vars in every CallFrame for local vars.
3350 * All the caching should also have an 'epoch' mechanism similar
3351 * to the one used by Tcl for procedures lookup caching. */
3352
3353 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3354 {
3355 const char *name;
3356 Jim_Var *var;
3357 int err;
3358
3359 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3360 /* Check for [dict] syntax sugar. */
3361 if (err == JIM_DICT_SUGAR)
3362 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3363 /* New variable to create */
3364 name = Jim_GetString(nameObjPtr, NULL);
3365
3366 var = Jim_Alloc(sizeof(*var));
3367 var->objPtr = valObjPtr;
3368 Jim_IncrRefCount(valObjPtr);
3369 var->linkFramePtr = NULL;
3370 /* Insert the new variable */
3371 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3372 /* Make the object int rep a variable */
3373 Jim_FreeIntRep(interp, nameObjPtr);
3374 nameObjPtr->typePtr = &variableObjType;
3375 nameObjPtr->internalRep.varValue.callFrameId =
3376 interp->framePtr->id;
3377 nameObjPtr->internalRep.varValue.varPtr = var;
3378 } else {
3379 var = nameObjPtr->internalRep.varValue.varPtr;
3380 if (var->linkFramePtr == NULL) {
3381 Jim_IncrRefCount(valObjPtr);
3382 Jim_DecrRefCount(interp, var->objPtr);
3383 var->objPtr = valObjPtr;
3384 } else { /* Else handle the link */
3385 Jim_CallFrame *savedCallFrame;
3386
3387 savedCallFrame = interp->framePtr;
3388 interp->framePtr = var->linkFramePtr;
3389 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3390 interp->framePtr = savedCallFrame;
3391 if (err != JIM_OK)
3392 return err;
3393 }
3394 }
3395 return JIM_OK;
3396 }
3397
3398 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3399 {
3400 Jim_Obj *nameObjPtr;
3401 int result;
3402
3403 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3404 Jim_IncrRefCount(nameObjPtr);
3405 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3406 Jim_DecrRefCount(interp, nameObjPtr);
3407 return result;
3408 }
3409
3410 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3411 {
3412 Jim_CallFrame *savedFramePtr;
3413 int result;
3414
3415 savedFramePtr = interp->framePtr;
3416 interp->framePtr = interp->topFramePtr;
3417 result = Jim_SetVariableStr(interp, name, objPtr);
3418 interp->framePtr = savedFramePtr;
3419 return result;
3420 }
3421
3422 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3423 {
3424 Jim_Obj *nameObjPtr, *valObjPtr;
3425 int result;
3426
3427 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3428 valObjPtr = Jim_NewStringObj(interp, val, -1);
3429 Jim_IncrRefCount(nameObjPtr);
3430 Jim_IncrRefCount(valObjPtr);
3431 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3432 Jim_DecrRefCount(interp, nameObjPtr);
3433 Jim_DecrRefCount(interp, valObjPtr);
3434 return result;
3435 }
3436
3437 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3438 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3439 {
3440 const char *varName;
3441 int len;
3442
3443 /* Check for cycles. */
3444 if (interp->framePtr == targetCallFrame) {
3445 Jim_Obj *objPtr = targetNameObjPtr;
3446 Jim_Var *varPtr;
3447 /* Cycles are only possible with 'uplevel 0' */
3448 while(1) {
3449 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3450 Jim_SetResultString(interp,
3451 "can't upvar from variable to itself", -1);
3452 return JIM_ERR;
3453 }
3454 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3455 break;
3456 varPtr = objPtr->internalRep.varValue.varPtr;
3457 if (varPtr->linkFramePtr != targetCallFrame) break;
3458 objPtr = varPtr->objPtr;
3459 }
3460 }
3461 varName = Jim_GetString(nameObjPtr, &len);
3462 if (Jim_NameIsDictSugar(varName, len)) {
3463 Jim_SetResultString(interp,
3464 "Dict key syntax invalid as link source", -1);
3465 return JIM_ERR;
3466 }
3467 /* Perform the binding */
3468 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3469 /* We are now sure 'nameObjPtr' type is variableObjType */
3470 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3471 return JIM_OK;
3472 }
3473
3474 /* Return the Jim_Obj pointer associated with a variable name,
3475 * or NULL if the variable was not found in the current context.
3476 * The same optimization discussed in the comment to the
3477 * 'SetVariable' function should apply here. */
3478 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3479 {
3480 int err;
3481
3482 /* All the rest is handled here */
3483 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3484 /* Check for [dict] syntax sugar. */
3485 if (err == JIM_DICT_SUGAR)
3486 return JimDictSugarGet(interp, nameObjPtr);
3487 if (flags & JIM_ERRMSG) {
3488 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3489 Jim_AppendStrings(interp, Jim_GetResult(interp),
3490 "can't read \"", nameObjPtr->bytes,
3491 "\": no such variable", NULL);
3492 }
3493 return NULL;
3494 } else {
3495 Jim_Var *varPtr;
3496 Jim_Obj *objPtr;
3497 Jim_CallFrame *savedCallFrame;
3498
3499 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3500 if (varPtr->linkFramePtr == NULL)
3501 return varPtr->objPtr;
3502 /* The variable is a link? Resolve it. */
3503 savedCallFrame = interp->framePtr;
3504 interp->framePtr = varPtr->linkFramePtr;
3505 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3506 if (objPtr == NULL && flags & JIM_ERRMSG) {
3507 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3508 Jim_AppendStrings(interp, Jim_GetResult(interp),
3509 "can't read \"", nameObjPtr->bytes,
3510 "\": no such variable", NULL);
3511 }
3512 interp->framePtr = savedCallFrame;
3513 return objPtr;
3514 }
3515 }
3516
3517 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3518 int flags)
3519 {
3520 Jim_CallFrame *savedFramePtr;
3521 Jim_Obj *objPtr;
3522
3523 savedFramePtr = interp->framePtr;
3524 interp->framePtr = interp->topFramePtr;
3525 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3526 interp->framePtr = savedFramePtr;
3527
3528 return objPtr;
3529 }
3530
3531 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3532 {
3533 Jim_Obj *nameObjPtr, *varObjPtr;
3534
3535 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3536 Jim_IncrRefCount(nameObjPtr);
3537 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3538 Jim_DecrRefCount(interp, nameObjPtr);
3539 return varObjPtr;
3540 }
3541
3542 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3543 int flags)
3544 {
3545 Jim_CallFrame *savedFramePtr;
3546 Jim_Obj *objPtr;
3547
3548 savedFramePtr = interp->framePtr;
3549 interp->framePtr = interp->topFramePtr;
3550 objPtr = Jim_GetVariableStr(interp, name, flags);
3551 interp->framePtr = savedFramePtr;
3552
3553 return objPtr;
3554 }
3555
3556 /* Unset a variable.
3557 * Note: On success unset invalidates all the variable objects created
3558 * in the current call frame incrementing. */
3559 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3560 {
3561 const char *name;
3562 Jim_Var *varPtr;
3563 int err;
3564
3565 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3566 /* Check for [dict] syntax sugar. */
3567 if (err == JIM_DICT_SUGAR)
3568 return JimDictSugarSet(interp, nameObjPtr, NULL);
3569 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3570 Jim_AppendStrings(interp, Jim_GetResult(interp),
3571 "can't unset \"", nameObjPtr->bytes,
3572 "\": no such variable", NULL);
3573 return JIM_ERR; /* var not found */
3574 }
3575 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3576 /* If it's a link call UnsetVariable recursively */
3577 if (varPtr->linkFramePtr) {
3578 int retval;
3579
3580 Jim_CallFrame *savedCallFrame;
3581
3582 savedCallFrame = interp->framePtr;
3583 interp->framePtr = varPtr->linkFramePtr;
3584 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3585 interp->framePtr = savedCallFrame;
3586 if (retval != JIM_OK && flags & JIM_ERRMSG) {
3587 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3588 Jim_AppendStrings(interp, Jim_GetResult(interp),
3589 "can't unset \"", nameObjPtr->bytes,
3590 "\": no such variable", NULL);
3591 }
3592 return retval;
3593 } else {
3594 name = Jim_GetString(nameObjPtr, NULL);
3595 if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3596 != JIM_OK) return JIM_ERR;
3597 /* Change the callframe id, invalidating var lookup caching */
3598 JimChangeCallFrameId(interp, interp->framePtr);
3599 return JIM_OK;
3600 }
3601 }
3602
3603 /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
3604
3605 /* Given a variable name for [dict] operation syntax sugar,
3606 * this function returns two objects, the first with the name
3607 * of the variable to set, and the second with the rispective key.
3608 * For example "foo(bar)" will return objects with string repr. of
3609 * "foo" and "bar".
3610 *
3611 * The returned objects have refcount = 1. The function can't fail. */
3612 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3613 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3614 {
3615 const char *str, *p;
3616 char *t;
3617 int len, keyLen, nameLen;
3618 Jim_Obj *varObjPtr, *keyObjPtr;
3619
3620 str = Jim_GetString(objPtr, &len);
3621 p = strchr(str, '(');
3622 p++;
3623 keyLen = len-((p-str)+1);
3624 nameLen = (p-str)-1;
3625 /* Create the objects with the variable name and key. */
3626 t = Jim_Alloc(nameLen+1);
3627 memcpy(t, str, nameLen);
3628 t[nameLen] = '\0';
3629 varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3630
3631 t = Jim_Alloc(keyLen+1);
3632 memcpy(t, p, keyLen);
3633 t[keyLen] = '\0';
3634 keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3635
3636 Jim_IncrRefCount(varObjPtr);
3637 Jim_IncrRefCount(keyObjPtr);
3638 *varPtrPtr = varObjPtr;
3639 *keyPtrPtr = keyObjPtr;
3640 }
3641
3642 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3643 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3644 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3645 Jim_Obj *valObjPtr)
3646 {
3647 Jim_Obj *varObjPtr, *keyObjPtr;
3648 int err = JIM_OK;
3649
3650 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3651 err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3652 valObjPtr);
3653 Jim_DecrRefCount(interp, varObjPtr);
3654 Jim_DecrRefCount(interp, keyObjPtr);
3655 return err;
3656 }
3657
3658 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3659 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3660 {
3661 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3662
3663 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3664 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3665 if (!dictObjPtr) {
3666 resObjPtr = NULL;
3667 goto err;
3668 }
3669 if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3670 != JIM_OK) {
3671 resObjPtr = NULL;
3672 }
3673 err:
3674 Jim_DecrRefCount(interp, varObjPtr);
3675 Jim_DecrRefCount(interp, keyObjPtr);
3676 return resObjPtr;
3677 }
3678
3679 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3680
3681 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3682 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3683 Jim_Obj *dupPtr);
3684
3685 static Jim_ObjType dictSubstObjType = {
3686 "dict-substitution",
3687 FreeDictSubstInternalRep,
3688 DupDictSubstInternalRep,
3689 NULL,
3690 JIM_TYPE_NONE,
3691 };
3692
3693 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3694 {
3695 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3696 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3697 }
3698
3699 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3700 Jim_Obj *dupPtr)
3701 {
3702 JIM_NOTUSED(interp);
3703
3704 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3705 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3706 dupPtr->internalRep.dictSubstValue.indexObjPtr =
3707 srcPtr->internalRep.dictSubstValue.indexObjPtr;
3708 dupPtr->typePtr = &dictSubstObjType;
3709 }
3710
3711 /* This function is used to expand [dict get] sugar in the form
3712 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3713 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3714 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3715 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3716 * the [dict]ionary contained in variable VARNAME. */
3717 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3718 {
3719 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3720 Jim_Obj *substKeyObjPtr = NULL;
3721
3722 if (objPtr->typePtr != &dictSubstObjType) {
3723 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3724 Jim_FreeIntRep(interp, objPtr);
3725 objPtr->typePtr = &dictSubstObjType;
3726 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3727 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3728 }
3729 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3730 &substKeyObjPtr, JIM_NONE)
3731 != JIM_OK) {
3732 substKeyObjPtr = NULL;
3733 goto err;
3734 }
3735 Jim_IncrRefCount(substKeyObjPtr);
3736 dictObjPtr = Jim_GetVariable(interp,
3737 objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3738 if (!dictObjPtr) {
3739 resObjPtr = NULL;
3740 goto err;
3741 }
3742 if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3743 != JIM_OK) {
3744 resObjPtr = NULL;
3745 goto err;
3746 }
3747 err:
3748 if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3749 return resObjPtr;
3750 }
3751
3752 /* -----------------------------------------------------------------------------
3753 * CallFrame
3754 * ---------------------------------------------------------------------------*/
3755
3756 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3757 {
3758 Jim_CallFrame *cf;
3759 if (interp->freeFramesList) {
3760 cf = interp->freeFramesList;
3761 interp->freeFramesList = cf->nextFramePtr;
3762 } else {
3763 cf = Jim_Alloc(sizeof(*cf));
3764 cf->vars.table = NULL;
3765 }
3766
3767 cf->id = interp->callFrameEpoch++;
3768 cf->parentCallFrame = NULL;
3769 cf->argv = NULL;
3770 cf->argc = 0;
3771 cf->procArgsObjPtr = NULL;
3772 cf->procBodyObjPtr = NULL;
3773 cf->nextFramePtr = NULL;
3774 cf->staticVars = NULL;
3775 if (cf->vars.table == NULL)
3776 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3777 return cf;
3778 }
3779
3780 /* Used to invalidate every caching related to callframe stability. */
3781 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3782 {
3783 cf->id = interp->callFrameEpoch++;
3784 }
3785
3786 #define JIM_FCF_NONE 0 /* no flags */
3787 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3788 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3789 int flags)
3790 {
3791 if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3792 if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3793 if (!(flags & JIM_FCF_NOHT))
3794 Jim_FreeHashTable(&cf->vars);
3795 else {
3796 int i;
3797 Jim_HashEntry **table = cf->vars.table, *he;
3798
3799 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3800 he = table[i];
3801 while (he != NULL) {
3802 Jim_HashEntry *nextEntry = he->next;
3803 Jim_Var *varPtr = (void*) he->val;
3804
3805 Jim_DecrRefCount(interp, varPtr->objPtr);
3806 Jim_Free(he->val);
3807 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3808 Jim_Free(he);
3809 table[i] = NULL;
3810 he = nextEntry;
3811 }
3812 }
3813 cf->vars.used = 0;
3814 }
3815 cf->nextFramePtr = interp->freeFramesList;
3816 interp->freeFramesList = cf;
3817 }
3818
3819 /* -----------------------------------------------------------------------------
3820 * References
3821 * ---------------------------------------------------------------------------*/
3822
3823 /* References HashTable Type.
3824 *
3825 * Keys are jim_wide integers, dynamically allocated for now but in the
3826 * future it's worth to cache this 8 bytes objects. Values are poitners
3827 * to Jim_References. */
3828 static void JimReferencesHTValDestructor(void *interp, void *val)
3829 {
3830 Jim_Reference *refPtr = (void*) val;
3831
3832 Jim_DecrRefCount(interp, refPtr->objPtr);
3833 if (refPtr->finalizerCmdNamePtr != NULL) {
3834 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3835 }
3836 Jim_Free(val);
3837 }
3838
3839 unsigned int JimReferencesHTHashFunction(const void *key)
3840 {
3841 /* Only the least significant bits are used. */
3842 const jim_wide *widePtr = key;
3843 unsigned int intValue = (unsigned int) *widePtr;
3844 return Jim_IntHashFunction(intValue);
3845 }
3846
3847 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3848 {
3849 /* Only the least significant bits are used. */
3850 const jim_wide *widePtr = key;
3851 unsigned int intValue = (unsigned int) *widePtr;
3852 return intValue; /* identity function. */
3853 }
3854
3855 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3856 {
3857 void *copy = Jim_Alloc(sizeof(jim_wide));
3858 JIM_NOTUSED(privdata);
3859
3860 memcpy(copy, key, sizeof(jim_wide));
3861 return copy;
3862 }
3863
3864 int JimReferencesHTKeyCompare(void *privdata, const void *key1,
3865 const void *key2)
3866 {
3867 JIM_NOTUSED(privdata);
3868
3869 return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3870 }
3871
3872 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3873 {
3874 JIM_NOTUSED(privdata);
3875
3876 Jim_Free((void*)key);
3877 }
3878
3879 static Jim_HashTableType JimReferencesHashTableType = {
3880 JimReferencesHTHashFunction, /* hash function */
3881 JimReferencesHTKeyDup, /* key dup */
3882 NULL, /* val dup */
3883 JimReferencesHTKeyCompare, /* key compare */
3884 JimReferencesHTKeyDestructor, /* key destructor */
3885 JimReferencesHTValDestructor /* val destructor */
3886 };
3887
3888 /* -----------------------------------------------------------------------------
3889 * Reference object type and References API
3890 * ---------------------------------------------------------------------------*/
3891
3892 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3893
3894 static Jim_ObjType referenceObjType = {
3895 "reference",
3896 NULL,
3897 NULL,
3898 UpdateStringOfReference,
3899 JIM_TYPE_REFERENCES,
3900 };
3901
3902 void UpdateStringOfReference(struct Jim_Obj *objPtr)
3903 {
3904 int len;
3905 char buf[JIM_REFERENCE_SPACE+1];
3906 Jim_Reference *refPtr;
3907
3908 refPtr = objPtr->internalRep.refValue.refPtr;
3909 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3910 objPtr->bytes = Jim_Alloc(len+1);
3911 memcpy(objPtr->bytes, buf, len+1);
3912 objPtr->length = len;
3913 }
3914
3915 /* returns true if 'c' is a valid reference tag character.
3916 * i.e. inside the range [_a-zA-Z0-9] */
3917 static int isrefchar(int c)
3918 {
3919 if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3920 (c >= '0' && c <= '9')) return 1;
3921 return 0;
3922 }
3923
3924 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3925 {
3926 jim_wide wideValue;
3927 int i, len;
3928 const char *str, *start, *end;
3929 char refId[21];
3930 Jim_Reference *refPtr;
3931 Jim_HashEntry *he;
3932
3933 /* Get the string representation */
3934 str = Jim_GetString(objPtr, &len);
3935 /* Check if it looks like a reference */
3936 if (len < JIM_REFERENCE_SPACE) goto badformat;
3937 /* Trim spaces */
3938 start = str;
3939 end = str+len-1;
3940 while (*start == ' ') start++;
3941 while (*end == ' ' && end > start) end--;
3942 if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3943 /* <reference.<1234567>.%020> */
3944 if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3945 if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3946 /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3947 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3948 if (!isrefchar(start[12+i])) goto badformat;
3949 }
3950 /* Extract info from the refernece. */
3951 memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3952 refId[20] = '\0';
3953 /* Try to convert the ID into a jim_wide */
3954 if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3955 /* Check if the reference really exists! */
3956 he = Jim_FindHashEntry(&interp->references, &wideValue);
3957 if (he == NULL) {
3958 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3959 Jim_AppendStrings(interp, Jim_GetResult(interp),
3960 "Invalid reference ID \"", str, "\"", NULL);
3961 return JIM_ERR;
3962 }
3963 refPtr = he->val;
3964 /* Free the old internal repr and set the new one. */
3965 Jim_FreeIntRep(interp, objPtr);
3966 objPtr->typePtr = &referenceObjType;
3967 objPtr->internalRep.refValue.id = wideValue;
3968 objPtr->internalRep.refValue.refPtr = refPtr;
3969 return JIM_OK;
3970
3971 badformat:
3972 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3973 Jim_AppendStrings(interp, Jim_GetResult(interp),
3974 "expected reference but got \"", str, "\"", NULL);
3975 return JIM_ERR;
3976 }
3977
3978 /* Returns a new reference pointing to objPtr, having cmdNamePtr
3979 * as finalizer command (or NULL if there is no finalizer).
3980 * The returned reference object has refcount = 0. */
3981 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
3982 Jim_Obj *cmdNamePtr)
3983 {
3984 struct Jim_Reference *refPtr;
3985 jim_wide wideValue = interp->referenceNextId;
3986 Jim_Obj *refObjPtr;
3987 const char *tag;
3988 int tagLen, i;
3989
3990 /* Perform the Garbage Collection if needed. */
3991 Jim_CollectIfNeeded(interp);
3992
3993 refPtr = Jim_Alloc(sizeof(*refPtr));
3994 refPtr->objPtr = objPtr;
3995 Jim_IncrRefCount(objPtr);
3996 refPtr->finalizerCmdNamePtr = cmdNamePtr;
3997 if (cmdNamePtr)
3998 Jim_IncrRefCount(cmdNamePtr);
3999 Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4000 refObjPtr = Jim_NewObj(interp);
4001 refObjPtr->typePtr = &referenceObjType;
4002 refObjPtr->bytes = NULL;
4003 refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4004 refObjPtr->internalRep.refValue.refPtr = refPtr;
4005 interp->referenceNextId++;
4006 /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4007 * that does not pass the 'isrefchar' test is replaced with '_' */
4008 tag = Jim_GetString(tagPtr, &tagLen);
4009 if (tagLen > JIM_REFERENCE_TAGLEN)
4010 tagLen = JIM_REFERENCE_TAGLEN;
4011 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4012 if (i < tagLen)
4013 refPtr->tag[i] = tag[i];
4014 else
4015 refPtr->tag[i] = '_';
4016 }
4017 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4018 return refObjPtr;
4019 }
4020
4021 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4022 {
4023 if (objPtr->typePtr != &referenceObjType &&
4024 SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4025 return NULL;
4026 return objPtr->internalRep.refValue.refPtr;
4027 }
4028
4029 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4030 {
4031 Jim_Reference *refPtr;
4032
4033 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4034 return JIM_ERR;
4035 Jim_IncrRefCount(cmdNamePtr);
4036 if (refPtr->finalizerCmdNamePtr)
4037 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4038 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4039 return JIM_OK;
4040 }
4041
4042 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4043 {
4044 Jim_Reference *refPtr;
4045
4046 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4047 return JIM_ERR;
4048 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4049 return JIM_OK;
4050 }
4051
4052 /* -----------------------------------------------------------------------------
4053 * References Garbage Collection
4054 * ---------------------------------------------------------------------------*/
4055
4056 /* This the hash table type for the "MARK" phase of the GC */
4057 static Jim_HashTableType JimRefMarkHashTableType = {
4058 JimReferencesHTHashFunction, /* hash function */
4059 JimReferencesHTKeyDup, /* key dup */
4060 NULL, /* val dup */
4061 JimReferencesHTKeyCompare, /* key compare */
4062 JimReferencesHTKeyDestructor, /* key destructor */
4063 NULL /* val destructor */
4064 };
4065
4066 /* #define JIM_DEBUG_GC 1 */
4067
4068 /* Performs the garbage collection. */
4069 int Jim_Collect(Jim_Interp *interp)
4070 {
4071 Jim_HashTable marks;
4072 Jim_HashTableIterator *htiter;
4073 Jim_HashEntry *he;
4074 Jim_Obj *objPtr;
4075 int collected = 0;
4076
4077 /* Avoid recursive calls */
4078 if (interp->lastCollectId == -1) {
4079 /* Jim_Collect() already running. Return just now. */
4080 return 0;
4081 }
4082 interp->lastCollectId = -1;
4083
4084 /* Mark all the references found into the 'mark' hash table.
4085 * The references are searched in every live object that
4086 * is of a type that can contain references. */
4087 Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4088 objPtr = interp->liveList;
4089 while(objPtr) {
4090 if (objPtr->typePtr == NULL ||
4091 objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4092 const char *str, *p;
4093 int len;
4094
4095 /* If the object is of type reference, to get the
4096 * Id is simple... */
4097 if (objPtr->typePtr == &referenceObjType) {
4098 Jim_AddHashEntry(&marks,
4099 &objPtr->internalRep.refValue.id, NULL);
4100 #ifdef JIM_DEBUG_GC
4101 Jim_fprintf(interp,interp->cookie_stdout,
4102 "MARK (reference): %d refcount: %d" JIM_NL,
4103 (int) objPtr->internalRep.refValue.id,
4104 objPtr->refCount);
4105 #endif
4106 objPtr = objPtr->nextObjPtr;
4107 continue;
4108 }
4109 /* Get the string repr of the object we want
4110 * to scan for references. */
4111 p = str = Jim_GetString(objPtr, &len);
4112 /* Skip objects too little to contain references. */
4113 if (len < JIM_REFERENCE_SPACE) {
4114 objPtr = objPtr->nextObjPtr;
4115 continue;
4116 }
4117 /* Extract references from the object string repr. */
4118 while(1) {
4119 int i;
4120 jim_wide id;
4121 char buf[21];
4122
4123 if ((p = strstr(p, "<reference.<")) == NULL)
4124 break;
4125 /* Check if it's a valid reference. */
4126 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4127 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4128 for (i = 21; i <= 40; i++)
4129 if (!isdigit((int)p[i]))
4130 break;
4131 /* Get the ID */
4132 memcpy(buf, p+21, 20);
4133 buf[20] = '\0';
4134 Jim_StringToWide(buf, &id, 10);
4135
4136 /* Ok, a reference for the given ID
4137 * was found. Mark it. */
4138 Jim_AddHashEntry(&marks, &id, NULL);
4139 #ifdef JIM_DEBUG_GC
4140 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4141 #endif
4142 p += JIM_REFERENCE_SPACE;
4143 }
4144 }
4145 objPtr = objPtr->nextObjPtr;
4146 }
4147
4148 /* Run the references hash table to destroy every reference that
4149 * is not referenced outside (not present in the mark HT). */
4150 htiter = Jim_GetHashTableIterator(&interp->references);
4151 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4152 const jim_wide *refId;
4153 Jim_Reference *refPtr;
4154
4155 refId = he->key;
4156 /* Check if in the mark phase we encountered
4157 * this reference. */
4158 if (Jim_FindHashEntry(&marks, refId) == NULL) {
4159 #ifdef JIM_DEBUG_GC
4160 Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4161 #endif
4162 collected++;
4163 /* Drop the reference, but call the
4164 * finalizer first if registered. */
4165 refPtr = he->val;
4166 if (refPtr->finalizerCmdNamePtr) {
4167 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4168 Jim_Obj *objv[3], *oldResult;
4169
4170 JimFormatReference(refstr, refPtr, *refId);
4171
4172 objv[0] = refPtr->finalizerCmdNamePtr;
4173 objv[1] = Jim_NewStringObjNoAlloc(interp,
4174 refstr, 32);
4175 objv[2] = refPtr->objPtr;
4176 Jim_IncrRefCount(objv[0]);
4177 Jim_IncrRefCount(objv[1]);
4178 Jim_IncrRefCount(objv[2]);
4179
4180 /* Drop the reference itself */
4181 Jim_DeleteHashEntry(&interp->references, refId);
4182
4183 /* Call the finalizer. Errors ignored. */
4184 oldResult = interp->result;
4185 Jim_IncrRefCount(oldResult);
4186 Jim_EvalObjVector(interp, 3, objv);
4187 Jim_SetResult(interp, oldResult);
4188 Jim_DecrRefCount(interp, oldResult);
4189
4190 Jim_DecrRefCount(interp, objv[0]);
4191 Jim_DecrRefCount(interp, objv[1]);
4192 Jim_DecrRefCount(interp, objv[2]);
4193 } else {
4194 Jim_DeleteHashEntry(&interp->references, refId);
4195 }
4196 }
4197 }
4198 Jim_FreeHashTableIterator(htiter);
4199 Jim_FreeHashTable(&marks);
4200 interp->lastCollectId = interp->referenceNextId;
4201 interp->lastCollectTime = time(NULL);
4202 return collected;
4203 }
4204
4205 #define JIM_COLLECT_ID_PERIOD 5000
4206 #define JIM_COLLECT_TIME_PERIOD 300
4207
4208 void Jim_CollectIfNeeded(Jim_Interp *interp)
4209 {
4210 jim_wide elapsedId;
4211 int elapsedTime;
4212
4213 elapsedId = interp->referenceNextId - interp->lastCollectId;
4214 elapsedTime = time(NULL) - interp->lastCollectTime;
4215
4216
4217 if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4218 elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4219 Jim_Collect(interp);
4220 }
4221 }
4222
4223 /* -----------------------------------------------------------------------------
4224 * Interpreter related functions
4225 * ---------------------------------------------------------------------------*/
4226
4227 Jim_Interp *Jim_CreateInterp(void)
4228 {
4229 Jim_Interp *i = Jim_Alloc(sizeof(*i));
4230 Jim_Obj *pathPtr;
4231
4232 i->errorLine = 0;
4233 i->errorFileName = Jim_StrDup("");
4234 i->numLevels = 0;
4235 i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4236 i->returnCode = JIM_OK;
4237 i->exitCode = 0;
4238 i->procEpoch = 0;
4239 i->callFrameEpoch = 0;
4240 i->liveList = i->freeList = NULL;
4241 i->scriptFileName = Jim_StrDup("");
4242 i->referenceNextId = 0;
4243 i->lastCollectId = 0;
4244 i->lastCollectTime = time(NULL);
4245 i->freeFramesList = NULL;
4246 i->prngState = NULL;
4247 i->evalRetcodeLevel = -1;
4248 i->cookie_stdin = stdin;
4249 i->cookie_stdout = stdout;
4250 i->cookie_stderr = stderr;
4251 i->cb_fwrite = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4252 i->cb_fread = ((size_t (*)( void *, size_t, size_t, void *))(fread));
4253 i->cb_vfprintf = ((int (*)( void *, const char *fmt, va_list ))(vfprintf));
4254 i->cb_fflush = ((int (*)( void *))(fflush));
4255 i->cb_fgets = ((char * (*)( char *, int, void *))(fgets));
4256
4257 /* Note that we can create objects only after the
4258 * interpreter liveList and freeList pointers are
4259 * initialized to NULL. */
4260 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4261 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4262 Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4263 NULL);
4264 Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4265 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4266 Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4267 i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4268 i->emptyObj = Jim_NewEmptyStringObj(i);
4269 i->result = i->emptyObj;
4270 i->stackTrace = Jim_NewListObj(i, NULL, 0);
4271 i->unknown = Jim_NewStringObj(i, "unknown", -1);
4272 Jim_IncrRefCount(i->emptyObj);
4273 Jim_IncrRefCount(i->result);
4274 Jim_IncrRefCount(i->stackTrace);
4275 Jim_IncrRefCount(i->unknown);
4276
4277 /* Initialize key variables every interpreter should contain */
4278 pathPtr = Jim_NewStringObj(i, "./", -1);
4279 Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4280 Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4281
4282 /* Export the core API to extensions */
4283 JimRegisterCoreApi(i);
4284 return i;
4285 }
4286
4287 /* This is the only function Jim exports directly without
4288 * to use the STUB system. It is only used by embedders
4289 * in order to get an interpreter with the Jim API pointers
4290 * registered. */
4291 Jim_Interp *ExportedJimCreateInterp(void)
4292 {
4293 return Jim_CreateInterp();
4294 }
4295
4296 void Jim_FreeInterp(Jim_Interp *i)
4297 {
4298 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4299 Jim_Obj *objPtr, *nextObjPtr;
4300
4301 Jim_DecrRefCount(i, i->emptyObj);
4302 Jim_DecrRefCount(i, i->result);
4303 Jim_DecrRefCount(i, i->stackTrace);
4304 Jim_DecrRefCount(i, i->unknown);
4305 Jim_Free((void*)i->errorFileName);
4306 Jim_Free((void*)i->scriptFileName);
4307 Jim_FreeHashTable(&i->commands);
4308 Jim_FreeHashTable(&i->references);
4309 Jim_FreeHashTable(&i->stub);
4310 Jim_FreeHashTable(&i->assocData);
4311 Jim_FreeHashTable(&i->packages);
4312 Jim_Free(i->prngState);
4313 /* Free the call frames list */
4314 while(cf) {
4315 prevcf = cf->parentCallFrame;
4316 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4317 cf = prevcf;
4318 }
4319 /* Check that the live object list is empty, otherwise
4320 * there is a memory leak. */
4321 if (i->liveList != NULL) {
4322 Jim_Obj *objPtr = i->liveList;
4323
4324 Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4325 Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4326 while(objPtr) {
4327 const char *type = objPtr->typePtr ?
4328 objPtr->typePtr->name : "";
4329 Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4330 objPtr, type,
4331 objPtr->bytes ? objPtr->bytes
4332 : "(null)", objPtr->refCount);
4333 if (objPtr->typePtr == &sourceObjType) {
4334 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4335 objPtr->internalRep.sourceValue.fileName,
4336 objPtr->internalRep.sourceValue.lineNumber);
4337 }
4338 objPtr = objPtr->nextObjPtr;
4339 }
4340 Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4341 Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4342 }
4343 /* Free all the freed objects. */
4344 objPtr = i->freeList;
4345 while (objPtr) {
4346 nextObjPtr = objPtr->nextObjPtr;
4347 Jim_Free(objPtr);
4348 objPtr = nextObjPtr;
4349 }
4350 /* Free cached CallFrame structures */
4351 cf = i->freeFramesList;
4352 while(cf) {
4353 nextcf = cf->nextFramePtr;
4354 if (cf->vars.table != NULL)
4355 Jim_Free(cf->vars.table);
4356 Jim_Free(cf);
4357 cf = nextcf;
4358 }
4359 /* Free the sharedString hash table. Make sure to free it
4360 * after every other Jim_Object was freed. */
4361 Jim_FreeHashTable(&i->sharedStrings);
4362 /* Free the interpreter structure. */
4363 Jim_Free(i);
4364 }
4365
4366 /* Store the call frame relative to the level represented by
4367 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4368 * level is assumed to be '1'.
4369 *
4370 * If a newLevelptr int pointer is specified, the function stores
4371 * the absolute level integer value of the new target callframe into
4372 * *newLevelPtr. (this is used to adjust interp->numLevels
4373 * in the implementation of [uplevel], so that [info level] will
4374 * return a correct information).
4375 *
4376 * This function accepts the 'level' argument in the form
4377 * of the commands [uplevel] and [upvar].
4378 *
4379 * For a function accepting a relative integer as level suitable
4380 * for implementation of [info level ?level?] check the
4381 * GetCallFrameByInteger() function. */
4382 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4383 Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4384 {
4385 long level;
4386 const char *str;
4387 Jim_CallFrame *framePtr;
4388
4389 if (newLevelPtr) *newLevelPtr = interp->numLevels;
4390 if (levelObjPtr) {
4391 str = Jim_GetString(levelObjPtr, NULL);
4392 if (str[0] == '#') {
4393 char *endptr;
4394 /* speedup for the toplevel (level #0) */
4395 if (str[1] == '0' && str[2] == '\0') {
4396 if (newLevelPtr) *newLevelPtr = 0;
4397 *framePtrPtr = interp->topFramePtr;
4398 return JIM_OK;
4399 }
4400
4401 level = strtol(str+1, &endptr, 0);
4402 if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4403 goto badlevel;
4404 /* An 'absolute' level is converted into the
4405 * 'number of levels to go back' format. */
4406 level = interp->numLevels - level;
4407 if (level < 0) goto badlevel;
4408 } else {
4409 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4410 goto badlevel;
4411 }
4412 } else {
4413 str = "1"; /* Needed to format the error message. */
4414 level = 1;
4415 }
4416 /* Lookup */
4417 framePtr = interp->framePtr;
4418 if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4419 while (level--) {
4420 framePtr = framePtr->parentCallFrame;
4421 if (framePtr == NULL) goto badlevel;
4422 }
4423 *framePtrPtr = framePtr;
4424 return JIM_OK;
4425 badlevel:
4426 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4427 Jim_AppendStrings(interp, Jim_GetResult(interp),
4428 "bad level \"", str, "\"", NULL);
4429 return JIM_ERR;
4430 }
4431
4432 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4433 * as a relative integer like in the [info level ?level?] command. */
4434 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4435 Jim_CallFrame **framePtrPtr)
4436 {
4437 jim_wide level;
4438 jim_wide relLevel; /* level relative to the current one. */
4439 Jim_CallFrame *framePtr;
4440
4441 if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4442 goto badlevel;
4443 if (level > 0) {
4444 /* An 'absolute' level is converted into the
4445 * 'number of levels to go back' format. */
4446 relLevel = interp->numLevels - level;
4447 } else {
4448 relLevel = -level;
4449 }
4450 /* Lookup */
4451 framePtr = interp->framePtr;
4452 while (relLevel--) {
4453 framePtr = framePtr->parentCallFrame;
4454 if (framePtr == NULL) goto badlevel;
4455 }
4456 *framePtrPtr = framePtr;
4457 return JIM_OK;
4458 badlevel:
4459 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4460 Jim_AppendStrings(interp, Jim_GetResult(interp),
4461 "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4462 return JIM_ERR;
4463 }
4464
4465 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4466 {
4467 Jim_Free((void*)interp->errorFileName);
4468 interp->errorFileName = Jim_StrDup(filename);
4469 }
4470
4471 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4472 {
4473 interp->errorLine = linenr;
4474 }
4475
4476 static void JimResetStackTrace(Jim_Interp *interp)
4477 {
4478 Jim_DecrRefCount(interp, interp->stackTrace);
4479 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4480 Jim_IncrRefCount(interp->stackTrace);
4481 }
4482
4483 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4484 const char *filename, int linenr)
4485 {
4486 if (Jim_IsShared(interp->stackTrace)) {
4487 interp->stackTrace =
4488 Jim_DuplicateObj(interp, interp->stackTrace);
4489 Jim_IncrRefCount(interp->stackTrace);
4490 }
4491 Jim_ListAppendElement(interp, interp->stackTrace,
4492 Jim_NewStringObj(interp, procname, -1));
4493 Jim_ListAppendElement(interp, interp->stackTrace,
4494 Jim_NewStringObj(interp, filename, -1));
4495 Jim_ListAppendElement(interp, interp->stackTrace,
4496 Jim_NewIntObj(interp, linenr));
4497 }
4498
4499 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4500 {
4501 AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4502 assocEntryPtr->delProc = delProc;
4503 assocEntryPtr->data = data;
4504 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4505 }
4506
4507 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4508 {
4509 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4510 if (entryPtr != NULL) {
4511 AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4512 return assocEntryPtr->data;
4513 }
4514 return NULL;
4515 }
4516
4517 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4518 {
4519 return Jim_DeleteHashEntry(&interp->assocData, key);
4520 }
4521
4522 int Jim_GetExitCode(Jim_Interp *interp) {
4523 return interp->exitCode;
4524 }
4525
4526 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4527 {
4528 if (fp != NULL) interp->cookie_stdin = fp;
4529 return interp->cookie_stdin;
4530 }
4531
4532 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4533 {
4534 if (fp != NULL) interp->cookie_stdout = fp;
4535 return interp->cookie_stdout;
4536 }
4537
4538 void *Jim_SetStderr(Jim_Interp *interp, void *fp)
4539 {
4540 if (fp != NULL) interp->cookie_stderr = fp;
4541 return interp->cookie_stderr;
4542 }
4543
4544 /* -----------------------------------------------------------------------------
4545 * Shared strings.
4546 * Every interpreter has an hash table where to put shared dynamically
4547 * allocate strings that are likely to be used a lot of times.
4548 * For example, in the 'source' object type, there is a pointer to
4549 * the filename associated with that object. Every script has a lot
4550 * of this objects with the identical file name, so it is wise to share
4551 * this info.
4552 *
4553 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4554 * returns the pointer to the shared string. Every time a reference
4555 * to the string is no longer used, the user should call
4556 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4557 * a given string, it is removed from the hash table.
4558 * ---------------------------------------------------------------------------*/
4559 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4560 {
4561 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4562
4563 if (he == NULL) {
4564 char *strCopy = Jim_StrDup(str);
4565
4566 Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4567 return strCopy;
4568 } else {
4569 long refCount = (long) he->val;
4570
4571 refCount++;
4572 he->val = (void*) refCount;
4573 return he->key;
4574 }
4575 }
4576
4577 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4578 {
4579 long refCount;
4580 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4581
4582 if (he == NULL)
4583 Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4584 "unknown shared string '%s'", str);
4585 refCount = (long) he->val;
4586 refCount--;
4587 if (refCount == 0) {
4588 Jim_DeleteHashEntry(&interp->sharedStrings, str);
4589 } else {
4590 he->val = (void*) refCount;
4591 }
4592 }
4593
4594 /* -----------------------------------------------------------------------------
4595 * Integer object
4596 * ---------------------------------------------------------------------------*/
4597 #define JIM_INTEGER_SPACE 24
4598
4599 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4600 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4601
4602 static Jim_ObjType intObjType = {
4603 "int",
4604 NULL,
4605 NULL,
4606 UpdateStringOfInt,
4607 JIM_TYPE_NONE,
4608 };
4609
4610 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4611 {
4612 int len;
4613 char buf[JIM_INTEGER_SPACE+1];
4614
4615 len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4616 objPtr->bytes = Jim_Alloc(len+1);
4617 memcpy(objPtr->bytes, buf, len+1);
4618 objPtr->length = len;
4619 }
4620
4621 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4622 {
4623 jim_wide wideValue;
4624 const char *str;
4625
4626 /* Get the string representation */
4627 str = Jim_GetString(objPtr, NULL);
4628 /* Try to convert into a jim_wide */
4629 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4630 if (flags & JIM_ERRMSG) {
4631 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4632 Jim_AppendStrings(interp, Jim_GetResult(interp),
4633 "expected integer but got \"", str, "\"", NULL);
4634 }
4635 return JIM_ERR;
4636 }
4637 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4638 errno == ERANGE) {
4639 Jim_SetResultString(interp,
4640 "Integer value too big to be represented", -1);
4641 return JIM_ERR;
4642 }
4643 /* Free the old internal repr and set the new one. */
4644 Jim_FreeIntRep(interp, objPtr);
4645 objPtr->typePtr = &intObjType;
4646 objPtr->internalRep.wideValue = wideValue;
4647 return JIM_OK;
4648 }
4649
4650 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4651 {
4652 if (objPtr->typePtr != &intObjType &&
4653 SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4654 return JIM_ERR;
4655 *widePtr = objPtr->internalRep.wideValue;
4656 return JIM_OK;
4657 }
4658
4659 /* Get a wide but does not set an error if the format is bad. */
4660 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4661 jim_wide *widePtr)
4662 {
4663 if (objPtr->typePtr != &intObjType &&
4664 SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4665 return JIM_ERR;
4666 *widePtr = objPtr->internalRep.wideValue;
4667 return JIM_OK;
4668 }
4669
4670 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4671 {
4672 jim_wide wideValue;
4673 int retval;
4674
4675 retval = Jim_GetWide(interp, objPtr, &wideValue);
4676 if (retval == JIM_OK) {
4677 *longPtr = (long) wideValue;
4678 return JIM_OK;
4679 }
4680 return JIM_ERR;
4681 }
4682
4683 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4684 {
4685 if (Jim_IsShared(objPtr))
4686 Jim_Panic(interp,"Jim_SetWide called with shared object");
4687 if (objPtr->typePtr != &intObjType) {
4688 Jim_FreeIntRep(interp, objPtr);
4689 objPtr->typePtr = &intObjType;
4690 }
4691 Jim_InvalidateStringRep(objPtr);
4692 objPtr->internalRep.wideValue = wideValue;
4693 }
4694
4695 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4696 {
4697 Jim_Obj *objPtr;
4698
4699 objPtr = Jim_NewObj(interp);
4700 objPtr->typePtr = &intObjType;
4701 objPtr->bytes = NULL;
4702 objPtr->internalRep.wideValue = wideValue;
4703 return objPtr;
4704 }
4705
4706 /* -----------------------------------------------------------------------------
4707 * Double object
4708 * ---------------------------------------------------------------------------*/
4709 #define JIM_DOUBLE_SPACE 30
4710
4711 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4712 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4713
4714 static Jim_ObjType doubleObjType = {
4715 "double",
4716 NULL,
4717 NULL,
4718 UpdateStringOfDouble,
4719 JIM_TYPE_NONE,
4720 };
4721
4722 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4723 {
4724 int len;
4725 char buf[JIM_DOUBLE_SPACE+1];
4726
4727 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4728 objPtr->bytes = Jim_Alloc(len+1);
4729 memcpy(objPtr->bytes, buf, len+1);
4730 objPtr->length = len;
4731 }
4732
4733 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4734 {
4735 double doubleValue;
4736 const char *str;
4737
4738 /* Get the string representation */
4739 str = Jim_GetString(objPtr, NULL);
4740 /* Try to convert into a double */
4741 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4742 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4743 Jim_AppendStrings(interp, Jim_GetResult(interp),
4744 "expected number but got '", str, "'", NULL);
4745 return JIM_ERR;
4746 }
4747 /* Free the old internal repr and set the new one. */
4748 Jim_FreeIntRep(interp, objPtr);
4749 objPtr->typePtr = &doubleObjType;
4750 objPtr->internalRep.doubleValue = doubleValue;
4751 return JIM_OK;
4752 }
4753
4754 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4755 {
4756 if (objPtr->typePtr != &doubleObjType &&
4757 SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4758 return JIM_ERR;
4759 *doublePtr = objPtr->internalRep.doubleValue;
4760 return JIM_OK;
4761 }
4762
4763 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4764 {
4765 if (Jim_IsShared(objPtr))
4766 Jim_Panic(interp,"Jim_SetDouble called with shared object");
4767 if (objPtr->typePtr != &doubleObjType) {
4768 Jim_FreeIntRep(interp, objPtr);
4769 objPtr->typePtr = &doubleObjType;
4770 }
4771 Jim_InvalidateStringRep(objPtr);
4772 objPtr->internalRep.doubleValue = doubleValue;
4773 }
4774
4775 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4776 {
4777 Jim_Obj *objPtr;
4778
4779 objPtr = Jim_NewObj(interp);
4780 objPtr->typePtr = &doubleObjType;
4781 objPtr->bytes = NULL;
4782 objPtr->internalRep.doubleValue = doubleValue;
4783 return objPtr;
4784 }
4785
4786 /* -----------------------------------------------------------------------------
4787 * List object
4788 * ---------------------------------------------------------------------------*/
4789 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4790 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4791 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4792 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4793 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4794
4795 /* Note that while the elements of the list may contain references,
4796 * the list object itself can't. This basically means that the
4797 * list object string representation as a whole can't contain references
4798 * that are not presents in the single elements. */
4799 static Jim_ObjType listObjType = {
4800 "list",
4801 FreeListInternalRep,
4802 DupListInternalRep,
4803 UpdateStringOfList,
4804 JIM_TYPE_NONE,
4805 };
4806
4807 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4808 {
4809 int i;
4810
4811 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4812 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4813 }
4814 Jim_Free(objPtr->internalRep.listValue.ele);
4815 }
4816
4817 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4818 {
4819 int i;
4820 JIM_NOTUSED(interp);
4821
4822 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4823 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4824 dupPtr->internalRep.listValue.ele =
4825 Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4826 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4827 sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4828 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4829 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4830 }
4831 dupPtr->typePtr = &listObjType;
4832 }
4833
4834 /* The following function checks if a given string can be encoded
4835 * into a list element without any kind of quoting, surrounded by braces,
4836 * or using escapes to quote. */
4837 #define JIM_ELESTR_SIMPLE 0
4838 #define JIM_ELESTR_BRACE 1
4839 #define JIM_ELESTR_QUOTE 2
4840 static int ListElementQuotingType(const char *s, int len)
4841 {
4842 int i, level, trySimple = 1;
4843
4844 /* Try with the SIMPLE case */
4845 if (len == 0) return JIM_ELESTR_BRACE;
4846 if (s[0] == '"' || s[0] == '{') {
4847 trySimple = 0;
4848 goto testbrace;
4849 }
4850 for (i = 0; i < len; i++) {
4851 switch(s[i]) {
4852 case ' ':
4853 case '$':
4854 case '"':
4855 case '[':
4856 case ']':
4857 case ';':
4858 case '\\':
4859 case '\r':
4860 case '\n':
4861 case '\t':
4862 case '\f':
4863 case '\v':
4864 trySimple = 0;
4865 case '{':
4866 case '}':
4867 goto testbrace;
4868 }
4869 }
4870 return JIM_ELESTR_SIMPLE;
4871
4872 testbrace:
4873 /* Test if it's possible to do with braces */
4874 if (s[len-1] == '\\' ||
4875 s[len-1] == ']') return JIM_ELESTR_QUOTE;
4876 level = 0;
4877 for (i = 0; i < len; i++) {
4878 switch(s[i]) {
4879 case '{': level++; break;
4880 case '}': level--;
4881 if (level < 0) return JIM_ELESTR_QUOTE;
4882 break;
4883 case '\\':
4884 if (s[i+1] == '\n')
4885 return JIM_ELESTR_QUOTE;
4886 else
4887 if (s[i+1] != '\0') i++;
4888 break;
4889 }
4890 }
4891 if (level == 0) {
4892 if (!trySimple) return JIM_ELESTR_BRACE;
4893 for (i = 0; i < len; i++) {
4894 switch(s[i]) {
4895 case ' ':
4896 case '$':
4897 case '"':
4898 case '[':
4899 case ']':
4900 case ';':
4901 case '\\':
4902 case '\r':
4903 case '\n':
4904 case '\t':
4905 case '\f':
4906 case '\v':
4907 return JIM_ELESTR_BRACE;
4908 break;
4909 }
4910 }
4911 return JIM_ELESTR_SIMPLE;
4912 }
4913 return JIM_ELESTR_QUOTE;
4914 }
4915
4916 /* Returns the malloc-ed representation of a string
4917 * using backslash to quote special chars. */
4918 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4919 {
4920 char *q = Jim_Alloc(len*2+1), *p;
4921
4922 p = q;
4923 while(*s) {
4924 switch (*s) {
4925 case ' ':
4926 case '$':
4927 case '"':
4928 case '[':
4929 case ']':
4930 case '{':
4931 case '}':
4932 case ';':
4933 case '\\':
4934 *p++ = '\\';
4935 *p++ = *s++;
4936 break;
4937 case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4938 case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4939 case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4940 case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4941 case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4942 default:
4943 *p++ = *s++;
4944 break;
4945 }
4946 }
4947 *p = '\0';
4948 *qlenPtr = p-q;
4949 return q;
4950 }
4951
4952 void UpdateStringOfList(struct Jim_Obj *objPtr)
4953 {
4954 int i, bufLen, realLength;
4955 const char *strRep;
4956 char *p;
4957 int *quotingType;
4958 Jim_Obj **ele = objPtr->internalRep.listValue.ele;
4959
4960 /* (Over) Estimate the space needed. */
4961 quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
4962 bufLen = 0;
4963 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4964 int len;
4965
4966 strRep = Jim_GetString(ele[i], &len);
4967 quotingType[i] = ListElementQuotingType(strRep, len);
4968 switch (quotingType[i]) {
4969 case JIM_ELESTR_SIMPLE: bufLen += len; break;
4970 case JIM_ELESTR_BRACE: bufLen += len+2; break;
4971 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
4972 }
4973 bufLen++; /* elements separator. */
4974 }
4975 bufLen++;
4976
4977 /* Generate the string rep. */
4978 p = objPtr->bytes = Jim_Alloc(bufLen+1);
4979 realLength = 0;
4980 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4981 int len, qlen;
4982 const char *strRep = Jim_GetString(ele[i], &len);
4983 char *q;
4984
4985 switch(quotingType[i]) {
4986 case JIM_ELESTR_SIMPLE:
4987 memcpy(p, strRep, len);
4988 p += len;
4989 realLength += len;
4990 break;
4991 case JIM_ELESTR_BRACE:
4992 *p++ = '{';
4993 memcpy(p, strRep, len);
4994 p += len;
4995 *p++ = '}';
4996 realLength += len+2;
4997 break;
4998 case JIM_ELESTR_QUOTE:
4999 q = BackslashQuoteString(strRep, len, &qlen);
5000 memcpy(p, q, qlen);
5001 Jim_Free(q);
5002 p += qlen;
5003 realLength += qlen;
5004 break;
5005 }
5006 /* Add a separating space */
5007 if (i+1 != objPtr->internalRep.listValue.len) {
5008 *p++ = ' ';
5009 realLength ++;
5010 }
5011 }
5012 *p = '\0'; /* nul term. */
5013 objPtr->length = realLength;
5014 Jim_Free(quotingType);
5015 }
5016
5017 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5018 {
5019 struct JimParserCtx parser;
5020 const char *str;
5021 int strLen;
5022
5023 /* Get the string representation */
5024 str = Jim_GetString(objPtr, &strLen);
5025
5026 /* Free the old internal repr just now and initialize the
5027 * new one just now. The string->list conversion can't fail. */
5028 Jim_FreeIntRep(interp, objPtr);
5029 objPtr->typePtr = &listObjType;
5030 objPtr->internalRep.listValue.len = 0;
5031 objPtr->internalRep.listValue.maxLen = 0;
5032 objPtr->internalRep.listValue.ele = NULL;
5033
5034 /* Convert into a list */
5035 JimParserInit(&parser, str, strLen, 1);
5036 while(!JimParserEof(&parser)) {
5037 char *token;
5038 int tokenLen, type;
5039 Jim_Obj *elementPtr;
5040
5041 JimParseList(&parser);
5042 if (JimParserTtype(&parser) != JIM_TT_STR &&
5043 JimParserTtype(&parser) != JIM_TT_ESC)
5044 continue;
5045 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5046 elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5047 ListAppendElement(objPtr, elementPtr);
5048 }
5049 return JIM_OK;
5050 }
5051
5052 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
5053 int len)
5054 {
5055 Jim_Obj *objPtr;
5056 int i;
5057
5058 objPtr = Jim_NewObj(interp);
5059 objPtr->typePtr = &listObjType;
5060 objPtr->bytes = NULL;
5061 objPtr->internalRep.listValue.ele = NULL;
5062 objPtr->internalRep.listValue.len = 0;
5063 objPtr->internalRep.listValue.maxLen = 0;
5064 for (i = 0; i < len; i++) {
5065 ListAppendElement(objPtr, elements[i]);
5066 }
5067 return objPtr;
5068 }
5069
5070 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5071 * length of the vector. Note that the user of this function should make
5072 * sure that the list object can't shimmer while the vector returned
5073 * is in use, this vector is the one stored inside the internal representation
5074 * of the list object. This function is not exported, extensions should
5075 * always access to the List object elements using Jim_ListIndex(). */
5076 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5077 Jim_Obj ***listVec)
5078 {
5079 Jim_ListLength(interp, listObj, argc);
5080 assert(listObj->typePtr == &listObjType);
5081 *listVec = listObj->internalRep.listValue.ele;
5082 }
5083
5084 /* ListSortElements type values */
5085 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5086 JIM_LSORT_NOCASE_DECR};
5087
5088 /* Sort the internal rep of a list. */
5089 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5090 {
5091 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5092 }
5093
5094 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5095 {
5096 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5097 }
5098
5099 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5100 {
5101 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5102 }
5103
5104 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5105 {
5106 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5107 }
5108
5109 /* Sort a list *in place*. MUST be called with non-shared objects. */
5110 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5111 {
5112 typedef int (qsort_comparator)(const void *, const void *);
5113 int (*fn)(Jim_Obj**, Jim_Obj**);
5114 Jim_Obj **vector;
5115 int len;
5116
5117 if (Jim_IsShared(listObjPtr))
5118 Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5119 if (listObjPtr->typePtr != &listObjType)
5120 SetListFromAny(interp, listObjPtr);
5121
5122 vector = listObjPtr->internalRep.listValue.ele;
5123 len = listObjPtr->internalRep.listValue.len;
5124 switch (type) {
5125 case JIM_LSORT_ASCII: fn = ListSortString; break;
5126 case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
5127 case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
5128 case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
5129 default:
5130 fn = NULL; /* avoid warning */
5131 Jim_Panic(interp,"ListSort called with invalid sort type");
5132 }
5133 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5134 Jim_InvalidateStringRep(listObjPtr);
5135 }
5136
5137 /* This is the low-level function to append an element to a list.
5138 * The higher-level Jim_ListAppendElement() performs shared object
5139 * check and invalidate the string repr. This version is used
5140 * in the internals of the List Object and is not exported.
5141 *
5142 * NOTE: this function can be called only against objects
5143 * with internal type of List. */
5144 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5145 {
5146 int requiredLen = listPtr->internalRep.listValue.len + 1;
5147
5148 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5149 int maxLen = requiredLen * 2;
5150
5151 listPtr->internalRep.listValue.ele =
5152 Jim_Realloc(listPtr->internalRep.listValue.ele,
5153 sizeof(Jim_Obj*)*maxLen);
5154 listPtr->internalRep.listValue.maxLen = maxLen;
5155 }
5156 listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5157 objPtr;
5158 listPtr->internalRep.listValue.len ++;
5159 Jim_IncrRefCount(objPtr);
5160 }
5161
5162 /* This is the low-level function to insert elements into a list.
5163 * The higher-level Jim_ListInsertElements() performs shared object
5164 * check and invalidate the string repr. This version is used
5165 * in the internals of the List Object and is not exported.
5166 *
5167 * NOTE: this function can be called only against objects
5168 * with internal type of List. */
5169 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5170 Jim_Obj *const *elemVec)
5171 {
5172 int currentLen = listPtr->internalRep.listValue.len;
5173 int requiredLen = currentLen + elemc;
5174 int i;
5175 Jim_Obj **point;
5176
5177 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5178 int maxLen = requiredLen * 2;
5179
5180 listPtr->internalRep.listValue.ele =
5181 Jim_Realloc(listPtr->internalRep.listValue.ele,
5182 sizeof(Jim_Obj*)*maxLen);
5183 listPtr->internalRep.listValue.maxLen = maxLen;
5184 }
5185 point = listPtr->internalRep.listValue.ele + index;
5186 memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5187 for (i=0; i < elemc; ++i) {
5188 point[i] = elemVec[i];
5189 Jim_IncrRefCount(point[i]);
5190 }
5191 listPtr->internalRep.listValue.len += elemc;
5192 }
5193
5194 /* Appends every element of appendListPtr into listPtr.
5195 * Both have to be of the list type. */
5196 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5197 {
5198 int i, oldLen = listPtr->internalRep.listValue.len;
5199 int appendLen = appendListPtr->internalRep.listValue.len;
5200 int requiredLen = oldLen + appendLen;
5201
5202 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5203 int maxLen = requiredLen * 2;
5204
5205 listPtr->internalRep.listValue.ele =
5206 Jim_Realloc(listPtr->internalRep.listValue.ele,
5207 sizeof(Jim_Obj*)*maxLen);
5208 listPtr->internalRep.listValue.maxLen = maxLen;
5209 }
5210 for (i = 0; i < appendLen; i++) {
5211 Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5212 listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5213 Jim_IncrRefCount(objPtr);
5214 }
5215 listPtr->internalRep.listValue.len += appendLen;
5216 }
5217
5218 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5219 {
5220 if (Jim_IsShared(listPtr))
5221 Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5222 if (listPtr->typePtr != &listObjType)
5223 SetListFromAny(interp, listPtr);
5224 Jim_InvalidateStringRep(listPtr);
5225 ListAppendElement(listPtr, objPtr);
5226 }
5227
5228 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5229 {
5230 if (Jim_IsShared(listPtr))
5231 Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5232 if (listPtr->typePtr != &listObjType)
5233 SetListFromAny(interp, listPtr);
5234 Jim_InvalidateStringRep(listPtr);
5235 ListAppendList(listPtr, appendListPtr);
5236 }
5237
5238 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5239 {
5240 if (listPtr->typePtr != &listObjType)
5241 SetListFromAny(interp, listPtr);
5242 *intPtr = listPtr->internalRep.listValue.len;
5243 }
5244
5245 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5246 int objc, Jim_Obj *const *objVec)
5247 {
5248 if (Jim_IsShared(listPtr))
5249 Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5250 if (listPtr->typePtr != &listObjType)
5251 SetListFromAny(interp, listPtr);
5252 if (index >= 0 && index > listPtr->internalRep.listValue.len)
5253 index = listPtr->internalRep.listValue.len;
5254 else if (index < 0 )
5255 index = 0;
5256 Jim_InvalidateStringRep(listPtr);
5257 ListInsertElements(listPtr, index, objc, objVec);
5258 }
5259
5260 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5261 Jim_Obj **objPtrPtr, int flags)
5262 {
5263 if (listPtr->typePtr != &listObjType)
5264 SetListFromAny(interp, listPtr);
5265 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5266 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5267 if (flags & JIM_ERRMSG) {
5268 Jim_SetResultString(interp,
5269 "list index out of range", -1);
5270 }
5271 return JIM_ERR;
5272 }
5273 if (index < 0)
5274 index = listPtr->internalRep.listValue.len+index;
5275 *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5276 return JIM_OK;
5277 }
5278
5279 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5280 Jim_Obj *newObjPtr, int flags)
5281 {
5282 if (listPtr->typePtr != &listObjType)
5283 SetListFromAny(interp, listPtr);
5284 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5285 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5286 if (flags & JIM_ERRMSG) {
5287 Jim_SetResultString(interp,
5288 "list index out of range", -1);
5289 }
5290 return JIM_ERR;
5291 }
5292 if (index < 0)
5293 index = listPtr->internalRep.listValue.len+index;
5294 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5295 listPtr->internalRep.listValue.ele[index] = newObjPtr;
5296 Jim_IncrRefCount(newObjPtr);
5297 return JIM_OK;
5298 }
5299
5300 /* Modify the list stored into the variable named 'varNamePtr'
5301 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5302 * with the new element 'newObjptr'. */
5303 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5304 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5305 {
5306 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5307 int shared, i, index;
5308
5309 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5310 if (objPtr == NULL)
5311 return JIM_ERR;
5312 if ((shared = Jim_IsShared(objPtr)))
5313 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5314 for (i = 0; i < indexc-1; i++) {
5315 listObjPtr = objPtr;
5316 if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5317 goto err;
5318 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5319 JIM_ERRMSG) != JIM_OK) {
5320 goto err;
5321 }
5322 if (Jim_IsShared(objPtr)) {
5323 objPtr = Jim_DuplicateObj(interp, objPtr);
5324 ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5325 }
5326 Jim_InvalidateStringRep(listObjPtr);
5327 }
5328 if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5329 goto err;
5330 if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5331 goto err;
5332 Jim_InvalidateStringRep(objPtr);
5333 Jim_InvalidateStringRep(varObjPtr);
5334 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5335 goto err;
5336 Jim_SetResult(interp, varObjPtr);
5337 return JIM_OK;
5338 err:
5339 if (shared) {
5340 Jim_FreeNewObj(interp, varObjPtr);
5341 }
5342 return JIM_ERR;
5343 }
5344
5345 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5346 {
5347 int i;
5348
5349 /* If all the objects in objv are lists without string rep.
5350 * it's possible to return a list as result, that's the
5351 * concatenation of all the lists. */
5352 for (i = 0; i < objc; i++) {
5353 if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5354 break;
5355 }
5356 if (i == objc) {
5357 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5358 for (i = 0; i < objc; i++)
5359 Jim_ListAppendList(interp, objPtr, objv[i]);
5360 return objPtr;
5361 } else {
5362 /* Else... we have to glue strings together */
5363 int len = 0, objLen;
5364 char *bytes, *p;
5365
5366 /* Compute the length */
5367 for (i = 0; i < objc; i++) {
5368 Jim_GetString(objv[i], &objLen);
5369 len += objLen;
5370 }
5371 if (objc) len += objc-1;
5372 /* Create the string rep, and a stinrg object holding it. */
5373 p = bytes = Jim_Alloc(len+1);
5374 for (i = 0; i < objc; i++) {
5375 const char *s = Jim_GetString(objv[i], &objLen);
5376 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5377 {
5378 s++; objLen--; len--;
5379 }
5380 while (objLen && (s[objLen-1] == ' ' ||
5381 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5382 objLen--; len--;
5383 }
5384 memcpy(p, s, objLen);
5385 p += objLen;
5386 if (objLen && i+1 != objc) {
5387 *p++ = ' ';
5388 } else if (i+1 != objc) {
5389 /* Drop the space calcuated for this
5390 * element that is instead null. */
5391 len--;
5392 }
5393 }
5394 *p = '\0';
5395 return Jim_NewStringObjNoAlloc(interp, bytes, len);
5396 }
5397 }
5398
5399 /* Returns a list composed of the elements in the specified range.
5400 * first and start are directly accepted as Jim_Objects and
5401 * processed for the end?-index? case. */
5402 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5403 {
5404 int first, last;
5405 int len, rangeLen;
5406
5407 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5408 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5409 return NULL;
5410 Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5411 first = JimRelToAbsIndex(len, first);
5412 last = JimRelToAbsIndex(len, last);
5413 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5414 return Jim_NewListObj(interp,
5415 listObjPtr->internalRep.listValue.ele+first, rangeLen);
5416 }
5417
5418 /* -----------------------------------------------------------------------------
5419 * Dict object
5420 * ---------------------------------------------------------------------------*/
5421 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5422 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5423 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5424 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5425
5426 /* Dict HashTable Type.
5427 *
5428 * Keys and Values are Jim objects. */
5429
5430 unsigned int JimObjectHTHashFunction(const void *key)
5431 {
5432 const char *str;
5433 Jim_Obj *objPtr = (Jim_Obj*) key;
5434 int len, h;
5435
5436 str = Jim_GetString(objPtr, &len);
5437 h = Jim_GenHashFunction((unsigned char*)str, len);
5438 return h;
5439 }
5440
5441 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5442 {
5443 JIM_NOTUSED(privdata);
5444
5445 return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5446 }
5447
5448 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5449 {
5450 Jim_Obj *objPtr = val;
5451
5452 Jim_DecrRefCount(interp, objPtr);
5453 }
5454
5455 static Jim_HashTableType JimDictHashTableType = {
5456 JimObjectHTHashFunction, /* hash function */
5457 NULL, /* key dup */
5458 NULL, /* val dup */
5459 JimObjectHTKeyCompare, /* key compare */
5460 (void(*)(void*, const void*)) /* ATTENTION: const cast */
5461 JimObjectHTKeyValDestructor, /* key destructor */
5462 JimObjectHTKeyValDestructor /* val destructor */
5463 };
5464
5465 /* Note that while the elements of the dict may contain references,
5466 * the list object itself can't. This basically means that the
5467 * dict object string representation as a whole can't contain references
5468 * that are not presents in the single elements. */
5469 static Jim_ObjType dictObjType = {
5470 "dict",
5471 FreeDictInternalRep,
5472 DupDictInternalRep,
5473 UpdateStringOfDict,
5474 JIM_TYPE_NONE,
5475 };
5476
5477 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5478 {
5479 JIM_NOTUSED(interp);
5480
5481 Jim_FreeHashTable(objPtr->internalRep.ptr);
5482 Jim_Free(objPtr->internalRep.ptr);
5483 }
5484
5485 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5486 {
5487 Jim_HashTable *ht, *dupHt;
5488 Jim_HashTableIterator *htiter;
5489 Jim_HashEntry *he;
5490
5491 /* Create a new hash table */
5492 ht = srcPtr->internalRep.ptr;
5493 dupHt = Jim_Alloc(sizeof(*dupHt));
5494 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5495 if (ht->size != 0)
5496 Jim_ExpandHashTable(dupHt, ht->size);
5497 /* Copy every element from the source to the dup hash table */
5498 htiter = Jim_GetHashTableIterator(ht);
5499 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5500 const Jim_Obj *keyObjPtr = he->key;
5501 Jim_Obj *valObjPtr = he->val;
5502
5503 Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
5504 Jim_IncrRefCount(valObjPtr);
5505 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5506 }
5507 Jim_FreeHashTableIterator(htiter);
5508
5509 dupPtr->internalRep.ptr = dupHt;
5510 dupPtr->typePtr = &dictObjType;
5511 }
5512
5513 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5514 {
5515 int i, bufLen, realLength;
5516 const char *strRep;
5517 char *p;
5518 int *quotingType, objc;
5519 Jim_HashTable *ht;
5520 Jim_HashTableIterator *htiter;
5521 Jim_HashEntry *he;
5522 Jim_Obj **objv;
5523
5524 /* Trun the hash table into a flat vector of Jim_Objects. */
5525 ht = objPtr->internalRep.ptr;
5526 objc = ht->used*2;
5527 objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5528 htiter = Jim_GetHashTableIterator(ht);
5529 i = 0;
5530 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5531 objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
5532 objv[i++] = he->val;
5533 }
5534 Jim_FreeHashTableIterator(htiter);
5535 /* (Over) Estimate the space needed. */
5536 quotingType = Jim_Alloc(sizeof(int)*objc);
5537 bufLen = 0;
5538 for (i = 0; i < objc; i++) {
5539 int len;
5540
5541 strRep = Jim_GetString(objv[i], &len);
5542 quotingType[i] = ListElementQuotingType(strRep, len);
5543 switch (quotingType[i]) {
5544 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5545 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5546 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5547 }
5548 bufLen++; /* elements separator. */
5549 }
5550 bufLen++;
5551
5552 /* Generate the string rep. */
5553 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5554 realLength = 0;
5555 for (i = 0; i < objc; i++) {
5556 int len, qlen;
5557 const char *strRep = Jim_GetString(objv[i], &len);
5558 char *q;
5559
5560 switch(quotingType[i]) {
5561 case JIM_ELESTR_SIMPLE:
5562 memcpy(p, strRep, len);
5563 p += len;
5564 realLength += len;
5565 break;
5566 case JIM_ELESTR_BRACE:
5567 *p++ = '{';
5568 memcpy(p, strRep, len);
5569 p += len;
5570 *p++ = '}';
5571 realLength += len+2;
5572 break;
5573 case JIM_ELESTR_QUOTE:
5574 q = BackslashQuoteString(strRep, len, &qlen);
5575 memcpy(p, q, qlen);
5576 Jim_Free(q);
5577 p += qlen;
5578 realLength += qlen;
5579 break;
5580 }
5581 /* Add a separating space */
5582 if (i+1 != objc) {
5583 *p++ = ' ';
5584 realLength ++;
5585 }
5586 }
5587 *p = '\0'; /* nul term. */
5588 objPtr->length = realLength;
5589 Jim_Free(quotingType);
5590 Jim_Free(objv);
5591 }
5592
5593 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5594 {
5595 struct JimParserCtx parser;
5596 Jim_HashTable *ht;
5597 Jim_Obj *objv[2];
5598 const char *str;
5599 int i, strLen;
5600
5601 /* Get the string representation */
5602 str = Jim_GetString(objPtr, &strLen);
5603
5604 /* Free the old internal repr just now and initialize the
5605 * new one just now. The string->list conversion can't fail. */
5606 Jim_FreeIntRep(interp, objPtr);
5607 ht = Jim_Alloc(sizeof(*ht));
5608 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5609 objPtr->typePtr = &dictObjType;
5610 objPtr->internalRep.ptr = ht;
5611
5612 /* Convert into a dict */
5613 JimParserInit(&parser, str, strLen, 1);
5614 i = 0;
5615 while(!JimParserEof(&parser)) {
5616 char *token;
5617 int tokenLen, type;
5618
5619 JimParseList(&parser);
5620 if (JimParserTtype(&parser) != JIM_TT_STR &&
5621 JimParserTtype(&parser) != JIM_TT_ESC)
5622 continue;
5623 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5624 objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5625 if (i == 2) {
5626 i = 0;
5627 Jim_IncrRefCount(objv[0]);
5628 Jim_IncrRefCount(objv[1]);
5629 if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5630 Jim_HashEntry *he;
5631 he = Jim_FindHashEntry(ht, objv[0]);
5632 Jim_DecrRefCount(interp, objv[0]);
5633 /* ATTENTION: const cast */
5634 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5635 he->val = objv[1];
5636 }
5637 }
5638 }
5639 if (i) {
5640 Jim_FreeNewObj(interp, objv[0]);
5641 objPtr->typePtr = NULL;
5642 Jim_FreeHashTable(ht);
5643 Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5644 return JIM_ERR;
5645 }
5646 return JIM_OK;
5647 }
5648
5649 /* Dict object API */
5650
5651 /* Add an element to a dict. objPtr must be of the "dict" type.
5652 * The higer-level exported function is Jim_DictAddElement().
5653 * If an element with the specified key already exists, the value
5654 * associated is replaced with the new one.
5655 *
5656 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5657 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5658 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5659 {
5660 Jim_HashTable *ht = objPtr->internalRep.ptr;
5661
5662 if (valueObjPtr == NULL) { /* unset */
5663 Jim_DeleteHashEntry(ht, keyObjPtr);
5664 return;
5665 }
5666 Jim_IncrRefCount(keyObjPtr);
5667 Jim_IncrRefCount(valueObjPtr);
5668 if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5669 Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5670 Jim_DecrRefCount(interp, keyObjPtr);
5671 /* ATTENTION: const cast */
5672 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5673 he->val = valueObjPtr;
5674 }
5675 }
5676
5677 /* Add an element, higher-level interface for DictAddElement().
5678 * If valueObjPtr == NULL, the key is removed if it exists. */
5679 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5680 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5681 {
5682 if (Jim_IsShared(objPtr))
5683 Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5684 if (objPtr->typePtr != &dictObjType) {
5685 if (SetDictFromAny(interp, objPtr) != JIM_OK)
5686 return JIM_ERR;
5687 }
5688 DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5689 Jim_InvalidateStringRep(objPtr);
5690 return JIM_OK;
5691 }
5692
5693 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5694 {
5695 Jim_Obj *objPtr;
5696 int i;
5697
5698 if (len % 2)
5699 Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5700
5701 objPtr = Jim_NewObj(interp);
5702 objPtr->typePtr = &dictObjType;
5703 objPtr->bytes = NULL;
5704 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5705 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5706 for (i = 0; i < len; i += 2)
5707 DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5708 return objPtr;
5709 }
5710
5711 /* Return the value associated to the specified dict key */
5712 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5713 Jim_Obj **objPtrPtr, int flags)
5714 {
5715 Jim_HashEntry *he;
5716 Jim_HashTable *ht;
5717
5718 if (dictPtr->typePtr != &dictObjType) {
5719 if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5720 return JIM_ERR;
5721 }
5722 ht = dictPtr->internalRep.ptr;
5723 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5724 if (flags & JIM_ERRMSG) {
5725 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5726 Jim_AppendStrings(interp, Jim_GetResult(interp),
5727 "key \"", Jim_GetString(keyPtr, NULL),
5728 "\" not found in dictionary", NULL);
5729 }
5730 return JIM_ERR;
5731 }
5732 *objPtrPtr = he->val;
5733 return JIM_OK;
5734 }
5735
5736 /* Return the value associated to the specified dict keys */
5737 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5738 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5739 {
5740 Jim_Obj *objPtr;
5741 int i;
5742
5743 if (keyc == 0) {
5744 *objPtrPtr = dictPtr;
5745 return JIM_OK;
5746 }
5747
5748 for (i = 0; i < keyc; i++) {
5749 if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5750 != JIM_OK)
5751 return JIM_ERR;
5752 dictPtr = objPtr;
5753 }
5754 *objPtrPtr = objPtr;
5755 return JIM_OK;
5756 }
5757
5758 /* Modify the dict stored into the variable named 'varNamePtr'
5759 * setting the element specified by the 'keyc' keys objects in 'keyv',
5760 * with the new value of the element 'newObjPtr'.
5761 *
5762 * If newObjPtr == NULL the operation is to remove the given key
5763 * from the dictionary. */
5764 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5765 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5766 {
5767 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5768 int shared, i;
5769
5770 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5771 if (objPtr == NULL) {
5772 if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5773 return JIM_ERR;
5774 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5775 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5776 Jim_FreeNewObj(interp, varObjPtr);
5777 return JIM_ERR;
5778 }
5779 }
5780 if ((shared = Jim_IsShared(objPtr)))
5781 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5782 for (i = 0; i < keyc-1; i++) {
5783 dictObjPtr = objPtr;
5784
5785 /* Check if it's a valid dictionary */
5786 if (dictObjPtr->typePtr != &dictObjType) {
5787 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5788 goto err;
5789 }
5790 /* Check if the given key exists. */
5791 Jim_InvalidateStringRep(dictObjPtr);
5792 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5793 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5794 {
5795 /* This key exists at the current level.
5796 * Make sure it's not shared!. */
5797 if (Jim_IsShared(objPtr)) {
5798 objPtr = Jim_DuplicateObj(interp, objPtr);
5799 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5800 }
5801 } else {
5802 /* Key not found. If it's an [unset] operation
5803 * this is an error. Only the last key may not
5804 * exist. */
5805 if (newObjPtr == NULL)
5806 goto err;
5807 /* Otherwise set an empty dictionary
5808 * as key's value. */
5809 objPtr = Jim_NewDictObj(interp, NULL, 0);
5810 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5811 }
5812 }
5813 if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5814 != JIM_OK)
5815 goto err;
5816 Jim_InvalidateStringRep(objPtr);
5817 Jim_InvalidateStringRep(varObjPtr);
5818 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5819 goto err;
5820 Jim_SetResult(interp, varObjPtr);
5821 return JIM_OK;
5822 err:
5823 if (shared) {
5824 Jim_FreeNewObj(interp, varObjPtr);
5825 }
5826 return JIM_ERR;
5827 }
5828
5829 /* -----------------------------------------------------------------------------
5830 * Index object
5831 * ---------------------------------------------------------------------------*/
5832 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5833 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5834
5835 static Jim_ObjType indexObjType = {
5836 "index",
5837 NULL,
5838 NULL,
5839 UpdateStringOfIndex,
5840 JIM_TYPE_NONE,
5841 };
5842
5843 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5844 {
5845 int len;
5846 char buf[JIM_INTEGER_SPACE+1];
5847
5848 if (objPtr->internalRep.indexValue >= 0)
5849 len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5850 else if (objPtr->internalRep.indexValue == -1)
5851 len = sprintf(buf, "end");
5852 else {
5853 len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5854 }
5855 objPtr->bytes = Jim_Alloc(len+1);
5856 memcpy(objPtr->bytes, buf, len+1);
5857 objPtr->length = len;
5858 }
5859
5860 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5861 {
5862 int index, end = 0;
5863 const char *str;
5864
5865 /* Get the string representation */
5866 str = Jim_GetString(objPtr, NULL);
5867 /* Try to convert into an index */
5868 if (!strcmp(str, "end")) {
5869 index = 0;
5870 end = 1;
5871 } else {
5872 if (!strncmp(str, "end-", 4)) {
5873 str += 4;
5874 end = 1;
5875 }
5876 if (Jim_StringToIndex(str, &index) != JIM_OK) {
5877 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5878 Jim_AppendStrings(interp, Jim_GetResult(interp),
5879 "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5880 "must be integer or end?-integer?", NULL);
5881 return JIM_ERR;
5882 }
5883 }
5884 if (end) {
5885 if (index < 0)
5886 index = INT_MAX;
5887 else
5888 index = -(index+1);
5889 } else if (!end && index < 0)
5890 index = -INT_MAX;
5891 /* Free the old internal repr and set the new one. */
5892 Jim_FreeIntRep(interp, objPtr);
5893 objPtr->typePtr = &indexObjType;
5894 objPtr->internalRep.indexValue = index;
5895 return JIM_OK;
5896 }
5897
5898 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5899 {
5900 /* Avoid shimmering if the object is an integer. */
5901 if (objPtr->typePtr == &intObjType) {
5902 jim_wide val = objPtr->internalRep.wideValue;
5903 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5904 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5905 return JIM_OK;
5906 }
5907 }
5908 if (objPtr->typePtr != &indexObjType &&
5909 SetIndexFromAny(interp, objPtr) == JIM_ERR)
5910 return JIM_ERR;
5911 *indexPtr = objPtr->internalRep.indexValue;
5912 return JIM_OK;
5913 }
5914
5915 /* -----------------------------------------------------------------------------
5916 * Return Code Object.
5917 * ---------------------------------------------------------------------------*/
5918
5919 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5920
5921 static Jim_ObjType returnCodeObjType = {
5922 "return-code",
5923 NULL,
5924 NULL,
5925 NULL,
5926 JIM_TYPE_NONE,
5927 };
5928
5929 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5930 {
5931 const char *str;
5932 int strLen, returnCode;
5933 jim_wide wideValue;
5934
5935 /* Get the string representation */
5936 str = Jim_GetString(objPtr, &strLen);
5937 /* Try to convert into an integer */
5938 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5939 returnCode = (int) wideValue;
5940 else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5941 returnCode = JIM_OK;
5942 else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5943 returnCode = JIM_ERR;
5944 else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5945 returnCode = JIM_RETURN;
5946 else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5947 returnCode = JIM_BREAK;
5948 else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5949 returnCode = JIM_CONTINUE;
5950 else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5951 returnCode = JIM_EVAL;
5952 else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5953 returnCode = JIM_EXIT;
5954 else {
5955 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5956 Jim_AppendStrings(interp, Jim_GetResult(interp),
5957 "expected return code but got '", str, "'",
5958 NULL);
5959 return JIM_ERR;
5960 }
5961 /* Free the old internal repr and set the new one. */
5962 Jim_FreeIntRep(interp, objPtr);
5963 objPtr->typePtr = &returnCodeObjType;
5964 objPtr->internalRep.returnCode = returnCode;
5965 return JIM_OK;
5966 }
5967
5968 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
5969 {
5970 if (objPtr->typePtr != &returnCodeObjType &&
5971 SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
5972 return JIM_ERR;
5973 *intPtr = objPtr->internalRep.returnCode;
5974 return JIM_OK;
5975 }
5976
5977 /* -----------------------------------------------------------------------------
5978 * Expression Parsing
5979 * ---------------------------------------------------------------------------*/
5980 static int JimParseExprOperator(struct JimParserCtx *pc);
5981 static int JimParseExprNumber(struct JimParserCtx *pc);
5982 static int JimParseExprIrrational(struct JimParserCtx *pc);
5983
5984 /* Exrp's Stack machine operators opcodes. */
5985
5986 /* Binary operators (numbers) */
5987 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
5988 #define JIM_EXPROP_MUL 0
5989 #define JIM_EXPROP_DIV 1
5990 #define JIM_EXPROP_MOD 2
5991 #define JIM_EXPROP_SUB 3
5992 #define JIM_EXPROP_ADD 4
5993 #define JIM_EXPROP_LSHIFT 5
5994 #define JIM_EXPROP_RSHIFT 6
5995 #define JIM_EXPROP_ROTL 7
5996 #define JIM_EXPROP_ROTR 8
5997 #define JIM_EXPROP_LT 9
5998 #define JIM_EXPROP_GT 10
5999 #define JIM_EXPROP_LTE 11
6000 #define JIM_EXPROP_GTE 12
6001 #define JIM_EXPROP_NUMEQ 13
6002 #define JIM_EXPROP_NUMNE 14
6003 #define JIM_EXPROP_BITAND 15
6004 #define JIM_EXPROP_BITXOR 16
6005 #define JIM_EXPROP_BITOR 17
6006 #define JIM_EXPROP_LOGICAND 18
6007 #define JIM_EXPROP_LOGICOR 19
6008 #define JIM_EXPROP_LOGICAND_LEFT 20
6009 #define JIM_EXPROP_LOGICOR_LEFT 21
6010 #define JIM_EXPROP_POW 22
6011 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6012
6013 /* Binary operators (strings) */
6014 #define JIM_EXPROP_STREQ 23
6015 #define JIM_EXPROP_STRNE 24
6016
6017 /* Unary operators (numbers) */
6018 #define JIM_EXPROP_NOT 25
6019 #define JIM_EXPROP_BITNOT 26
6020 #define JIM_EXPROP_UNARYMINUS 27
6021 #define JIM_EXPROP_UNARYPLUS 28
6022 #define JIM_EXPROP_LOGICAND_RIGHT 29
6023 #define JIM_EXPROP_LOGICOR_RIGHT 30
6024
6025 /* Ternary operators */
6026 #define JIM_EXPROP_TERNARY 31
6027
6028 /* Operands */
6029 #define JIM_EXPROP_NUMBER 32
6030 #define JIM_EXPROP_COMMAND 33
6031 #define JIM_EXPROP_VARIABLE 34
6032 #define JIM_EXPROP_DICTSUGAR 35
6033 #define JIM_EXPROP_SUBST 36
6034 #define JIM_EXPROP_STRING 37
6035
6036 /* Operators table */
6037 typedef struct Jim_ExprOperator {
6038 const char *name;
6039 int precedence;
6040 int arity;
6041 int opcode;
6042 } Jim_ExprOperator;
6043
6044 /* name - precedence - arity - opcode */
6045 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6046 {"!", 300, 1, JIM_EXPROP_NOT},
6047 {"~", 300, 1, JIM_EXPROP_BITNOT},
6048 {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6049 {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6050
6051 {"**", 250, 2, JIM_EXPROP_POW},
6052
6053 {"*", 200, 2, JIM_EXPROP_MUL},
6054 {"/", 200, 2, JIM_EXPROP_DIV},
6055 {"%", 200, 2, JIM_EXPROP_MOD},
6056
6057 {"-", 100, 2, JIM_EXPROP_SUB},
6058 {"+", 100, 2, JIM_EXPROP_ADD},
6059
6060 {"<<<", 90, 3, JIM_EXPROP_ROTL},
6061 {">>>", 90, 3, JIM_EXPROP_ROTR},
6062 {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6063 {">>", 90, 2, JIM_EXPROP_RSHIFT},
6064
6065 {"<", 80, 2, JIM_EXPROP_LT},
6066 {">", 80, 2, JIM_EXPROP_GT},
6067 {"<=", 80, 2, JIM_EXPROP_LTE},
6068 {">=", 80, 2, JIM_EXPROP_GTE},
6069
6070 {"==", 70, 2, JIM_EXPROP_NUMEQ},
6071 {"!=", 70, 2, JIM_EXPROP_NUMNE},
6072
6073 {"eq", 60, 2, JIM_EXPROP_STREQ},
6074 {"ne", 60, 2, JIM_EXPROP_STRNE},
6075
6076 {"&", 50, 2, JIM_EXPROP_BITAND},
6077 {"^", 49, 2, JIM_EXPROP_BITXOR},
6078 {"|", 48, 2, JIM_EXPROP_BITOR},
6079
6080 {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6081 {"||", 10, 2, JIM_EXPROP_LOGICOR},
6082
6083 {"?", 5, 3, JIM_EXPROP_TERNARY},
6084 /* private operators */
6085 {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6086 {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6087 {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6088 {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6089 };
6090
6091 #define JIM_EXPR_OPERATORS_NUM \
6092 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6093
6094 int JimParseExpression(struct JimParserCtx *pc)
6095 {
6096 /* Discard spaces and quoted newline */
6097 while(*(pc->p) == ' ' ||
6098 *(pc->p) == '\t' ||
6099 *(pc->p) == '\r' ||
6100 *(pc->p) == '\n' ||
6101 (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6102 pc->p++; pc->len--;
6103 }
6104
6105 if (pc->len == 0) {
6106 pc->tstart = pc->tend = pc->p;
6107 pc->tline = pc->linenr;
6108 pc->tt = JIM_TT_EOL;
6109 pc->eof = 1;
6110 return JIM_OK;
6111 }
6112 switch(*(pc->p)) {
6113 case '(':
6114 pc->tstart = pc->tend = pc->p;
6115 pc->tline = pc->linenr;
6116 pc->tt = JIM_TT_SUBEXPR_START;
6117 pc->p++; pc->len--;
6118 break;
6119 case ')':
6120 pc->tstart = pc->tend = pc->p;
6121 pc->tline = pc->linenr;
6122 pc->tt = JIM_TT_SUBEXPR_END;
6123 pc->p++; pc->len--;
6124 break;
6125 case '[':
6126 return JimParseCmd(pc);
6127 break;
6128 case '$':
6129 if (JimParseVar(pc) == JIM_ERR)
6130 return JimParseExprOperator(pc);
6131 else
6132 return JIM_OK;
6133 break;
6134 case '-':
6135 if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6136 isdigit((int)*(pc->p+1)))
6137 return JimParseExprNumber(pc);
6138 else
6139 return JimParseExprOperator(pc);
6140 break;
6141 case '0': case '1': case '2': case '3': case '4':
6142 case '5': case '6': case '7': case '8': case '9': case '.':
6143 return JimParseExprNumber(pc);
6144 break;
6145 case '"':
6146 case '{':
6147 /* Here it's possible to reuse the List String parsing. */
6148 pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6149 return JimParseListStr(pc);
6150 break;
6151 case 'N': case 'I':
6152 case 'n': case 'i':
6153 if (JimParseExprIrrational(pc) == JIM_ERR)
6154 return JimParseExprOperator(pc);
6155 break;
6156 default:
6157 return JimParseExprOperator(pc);
6158 break;
6159 }
6160 return JIM_OK;
6161 }
6162
6163 int JimParseExprNumber(struct JimParserCtx *pc)
6164 {
6165 int allowdot = 1;
6166 int allowhex = 0;
6167
6168 pc->tstart = pc->p;
6169 pc->tline = pc->linenr;
6170 if (*pc->p == '-') {
6171 pc->p++; pc->len--;
6172 }
6173 while ( isdigit((int)*pc->p)
6174 || (allowhex && isxdigit((int)*pc->p) )
6175 || (allowdot && *pc->p == '.')
6176 || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6177 (*pc->p == 'x' || *pc->p == 'X'))
6178 )
6179 {
6180 if ((*pc->p == 'x') || (*pc->p == 'X')) {
6181 allowhex = 1;
6182 allowdot = 0;
6183 }
6184 if (*pc->p == '.')
6185 allowdot = 0;
6186 pc->p++; pc->len--;
6187 if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6188 pc->p += 2; pc->len -= 2;
6189 }
6190 }
6191 pc->tend = pc->p-1;
6192 pc->tt = JIM_TT_EXPR_NUMBER;
6193 return JIM_OK;
6194 }
6195
6196 int JimParseExprIrrational(struct JimParserCtx *pc)
6197 {
6198 const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6199 const char **token;
6200 for (token = Tokens; *token != NULL; token++) {
6201 int len = strlen(*token);
6202 if (strncmp(*token, pc->p, len) == 0) {
6203 pc->tstart = pc->p;
6204 pc->tend = pc->p + len - 1;
6205 pc->p += len; pc->len -= len;
6206 pc->tline = pc->linenr;
6207 pc->tt = JIM_TT_EXPR_NUMBER;
6208 return JIM_OK;
6209 }
6210 }
6211 return JIM_ERR;
6212 }
6213
6214 int JimParseExprOperator(struct JimParserCtx *pc)
6215 {
6216 int i;
6217 int bestIdx = -1, bestLen = 0;
6218
6219 /* Try to get the longest match. */
6220 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6221 const char *opname;
6222 int oplen;
6223
6224 opname = Jim_ExprOperators[i].name;
6225 if (opname == NULL) continue;
6226 oplen = strlen(opname);
6227
6228 if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6229 bestIdx = i;
6230 bestLen = oplen;
6231 }
6232 }
6233 if (bestIdx == -1) return JIM_ERR;
6234 pc->tstart = pc->p;
6235 pc->tend = pc->p + bestLen - 1;
6236 pc->p += bestLen; pc->len -= bestLen;
6237 pc->tline = pc->linenr;
6238 pc->tt = JIM_TT_EXPR_OPERATOR;
6239 return JIM_OK;
6240 }
6241
6242 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6243 {
6244 int i;
6245 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6246 if (Jim_ExprOperators[i].name &&
6247 strcmp(opname, Jim_ExprOperators[i].name) == 0)
6248 return &Jim_ExprOperators[i];
6249 return NULL;
6250 }
6251
6252 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6253 {
6254 int i;
6255 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6256 if (Jim_ExprOperators[i].opcode == opcode)
6257 return &Jim_ExprOperators[i];
6258 return NULL;
6259 }
6260
6261 /* -----------------------------------------------------------------------------
6262 * Expression Object
6263 * ---------------------------------------------------------------------------*/
6264 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6265 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6266 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6267
6268 static Jim_ObjType exprObjType = {
6269 "expression",
6270 FreeExprInternalRep,
6271 DupExprInternalRep,
6272 NULL,
6273 JIM_TYPE_REFERENCES,
6274 };
6275
6276 /* Expr bytecode structure */
6277 typedef struct ExprByteCode {
6278 int *opcode; /* Integer array of opcodes. */
6279 Jim_Obj **obj; /* Array of associated Jim Objects. */
6280 int len; /* Bytecode length */
6281 int inUse; /* Used for sharing. */
6282 } ExprByteCode;
6283
6284 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6285 {
6286 int i;
6287 ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6288
6289 expr->inUse--;
6290 if (expr->inUse != 0) return;
6291 for (i = 0; i < expr->len; i++)
6292 Jim_DecrRefCount(interp, expr->obj[i]);
6293 Jim_Free(expr->opcode);
6294 Jim_Free(expr->obj);
6295 Jim_Free(expr);
6296 }
6297
6298 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6299 {
6300 JIM_NOTUSED(interp);
6301 JIM_NOTUSED(srcPtr);
6302
6303 /* Just returns an simple string. */
6304 dupPtr->typePtr = NULL;
6305 }
6306
6307 /* Add a new instruction to an expression bytecode structure. */
6308 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6309 int opcode, char *str, int len)
6310 {
6311 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6312 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6313 expr->opcode[expr->len] = opcode;
6314 expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6315 Jim_IncrRefCount(expr->obj[expr->len]);
6316 expr->len++;
6317 }
6318
6319 /* Check if an expr program looks correct. */
6320 static int ExprCheckCorrectness(ExprByteCode *expr)
6321 {
6322 int i;
6323 int stacklen = 0;
6324
6325 /* Try to check if there are stack underflows,
6326 * and make sure at the end of the program there is
6327 * a single result on the stack. */
6328 for (i = 0; i < expr->len; i++) {
6329 switch(expr->opcode[i]) {
6330 case JIM_EXPROP_NUMBER:
6331 case JIM_EXPROP_STRING:
6332 case JIM_EXPROP_SUBST:
6333 case JIM_EXPROP_VARIABLE:
6334 case JIM_EXPROP_DICTSUGAR:
6335 case JIM_EXPROP_COMMAND:
6336 stacklen++;
6337 break;
6338 case JIM_EXPROP_NOT:
6339 case JIM_EXPROP_BITNOT:
6340 case JIM_EXPROP_UNARYMINUS:
6341 case JIM_EXPROP_UNARYPLUS:
6342 /* Unary operations */
6343 if (stacklen < 1) return JIM_ERR;
6344 break;
6345 case JIM_EXPROP_ADD:
6346 case JIM_EXPROP_SUB:
6347 case JIM_EXPROP_MUL:
6348 case JIM_EXPROP_DIV:
6349 case JIM_EXPROP_MOD:
6350 case JIM_EXPROP_LT:
6351 case JIM_EXPROP_GT:
6352 case JIM_EXPROP_LTE:
6353 case JIM_EXPROP_GTE:
6354 case JIM_EXPROP_ROTL:
6355 case JIM_EXPROP_ROTR:
6356 case JIM_EXPROP_LSHIFT:
6357 case JIM_EXPROP_RSHIFT:
6358 case JIM_EXPROP_NUMEQ:
6359 case JIM_EXPROP_NUMNE:
6360 case JIM_EXPROP_STREQ:
6361 case JIM_EXPROP_STRNE:
6362 case JIM_EXPROP_BITAND:
6363 case JIM_EXPROP_BITXOR:
6364 case JIM_EXPROP_BITOR:
6365 case JIM_EXPROP_LOGICAND:
6366 case JIM_EXPROP_LOGICOR:
6367 case JIM_EXPROP_POW:
6368 /* binary operations */
6369 if (stacklen < 2) return JIM_ERR;
6370 stacklen--;
6371 break;
6372 default:
6373 Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6374 break;
6375 }
6376 }
6377 if (stacklen != 1) return JIM_ERR;
6378 return JIM_OK;
6379 }
6380
6381 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6382 ScriptObj *topLevelScript)
6383 {
6384 int i;
6385
6386 return;
6387 for (i = 0; i < expr->len; i++) {
6388 Jim_Obj *foundObjPtr;
6389
6390 if (expr->obj[i] == NULL) continue;
6391 foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6392 NULL, expr->obj[i]);
6393 if (foundObjPtr != NULL) {
6394 Jim_IncrRefCount(foundObjPtr);
6395 Jim_DecrRefCount(interp, expr->obj[i]);
6396 expr->obj[i] = foundObjPtr;
6397 }
6398 }
6399 }
6400
6401 /* This procedure converts every occurrence of || and && opereators
6402 * in lazy unary versions.
6403 *
6404 * a b || is converted into:
6405 *
6406 * a <offset> |L b |R
6407 *
6408 * a b && is converted into:
6409 *
6410 * a <offset> &L b &R
6411 *
6412 * "|L" checks if 'a' is true:
6413 * 1) if it is true pushes 1 and skips <offset> istructions to reach
6414 * the opcode just after |R.
6415 * 2) if it is false does nothing.
6416 * "|R" checks if 'b' is true:
6417 * 1) if it is true pushes 1, otherwise pushes 0.
6418 *
6419 * "&L" checks if 'a' is true:
6420 * 1) if it is true does nothing.
6421 * 2) If it is false pushes 0 and skips <offset> istructions to reach
6422 * the opcode just after &R
6423 * "&R" checks if 'a' is true:
6424 * if it is true pushes 1, otherwise pushes 0.
6425 */
6426 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6427 {
6428 while (1) {
6429 int index = -1, leftindex, arity, i, offset;
6430 Jim_ExprOperator *op;
6431
6432 /* Search for || or && */
6433 for (i = 0; i < expr->len; i++) {
6434 if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6435 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6436 index = i;
6437 break;
6438 }
6439 }
6440 if (index == -1) return;
6441 /* Search for the end of the first operator */
6442 leftindex = index-1;
6443 arity = 1;
6444 while(arity) {
6445 switch(expr->opcode[leftindex]) {
6446 case JIM_EXPROP_NUMBER:
6447 case JIM_EXPROP_COMMAND:
6448 case JIM_EXPROP_VARIABLE:
6449 case JIM_EXPROP_DICTSUGAR:
6450 case JIM_EXPROP_SUBST:
6451 case JIM_EXPROP_STRING:
6452 break;
6453 default:
6454 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6455 if (op == NULL) {
6456 Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6457 }
6458 arity += op->arity;
6459 break;
6460 }
6461 arity--;
6462 leftindex--;
6463 }
6464 leftindex++;
6465 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6466 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6467 memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6468 sizeof(int)*(expr->len-leftindex));
6469 memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6470 sizeof(Jim_Obj*)*(expr->len-leftindex));
6471 expr->len += 2;
6472 index += 2;
6473 offset = (index-leftindex)-1;
6474 Jim_DecrRefCount(interp, expr->obj[index]);
6475 if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6476 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6477 expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6478 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6479 expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6480 } else {
6481 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6482 expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6483 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6484 expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6485 }
6486 expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6487 expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6488 Jim_IncrRefCount(expr->obj[index]);
6489 Jim_IncrRefCount(expr->obj[leftindex]);
6490 Jim_IncrRefCount(expr->obj[leftindex+1]);
6491 }
6492 }
6493
6494 /* This method takes the string representation of an expression
6495 * and generates a program for the Expr's stack-based VM. */
6496 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6497 {
6498 int exprTextLen;
6499 const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6500 struct JimParserCtx parser;
6501 int i, shareLiterals;
6502 ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6503 Jim_Stack stack;
6504 Jim_ExprOperator *op;
6505
6506 /* Perform literal sharing with the current procedure
6507 * running only if this expression appears to be not generated
6508 * at runtime. */
6509 shareLiterals = objPtr->typePtr == &sourceObjType;
6510
6511 expr->opcode = NULL;
6512 expr->obj = NULL;
6513 expr->len = 0;
6514 expr->inUse = 1;
6515
6516 Jim_InitStack(&stack);
6517 JimParserInit(&parser, exprText, exprTextLen, 1);
6518 while(!JimParserEof(&parser)) {
6519 char *token;
6520 int len, type;
6521
6522 if (JimParseExpression(&parser) != JIM_OK) {
6523 Jim_SetResultString(interp, "Syntax error in expression", -1);
6524 goto err;
6525 }
6526 token = JimParserGetToken(&parser, &len, &type, NULL);
6527 if (type == JIM_TT_EOL) {
6528 Jim_Free(token);
6529 break;
6530 }
6531 switch(type) {
6532 case JIM_TT_STR:
6533 ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6534 break;
6535 case JIM_TT_ESC:
6536 ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6537 break;
6538 case JIM_TT_VAR:
6539 ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6540 break;
6541 case JIM_TT_DICTSUGAR:
6542 ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6543 break;
6544 case JIM_TT_CMD:
6545 ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6546 break;
6547 case JIM_TT_EXPR_NUMBER:
6548 ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6549 break;
6550 case JIM_TT_EXPR_OPERATOR:
6551 op = JimExprOperatorInfo(token);
6552 while(1) {
6553 Jim_ExprOperator *stackTopOp;
6554
6555 if (Jim_StackPeek(&stack) != NULL) {
6556 stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6557 } else {
6558 stackTopOp = NULL;
6559 }
6560 if (Jim_StackLen(&stack) && op->arity != 1 &&
6561 stackTopOp && stackTopOp->precedence >= op->precedence)
6562 {
6563 ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6564 Jim_StackPeek(&stack), -1);
6565 Jim_StackPop(&stack);
6566 } else {
6567 break;
6568 }
6569 }
6570 Jim_StackPush(&stack, token);
6571 break;
6572 case JIM_TT_SUBEXPR_START:
6573 Jim_StackPush(&stack, Jim_StrDup("("));
6574 Jim_Free(token);
6575 break;
6576 case JIM_TT_SUBEXPR_END:
6577 {
6578 int found = 0;
6579 while(Jim_StackLen(&stack)) {
6580 char *opstr = Jim_StackPop(&stack);
6581 if (!strcmp(opstr, "(")) {
6582 Jim_Free(opstr);
6583 found = 1;
6584 break;
6585 }
6586 op = JimExprOperatorInfo(opstr);
6587 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6588 }
6589 if (!found) {
6590 Jim_SetResultString(interp,
6591 "Unexpected close parenthesis", -1);
6592 goto err;
6593 }
6594 }
6595 Jim_Free(token);
6596 break;
6597 default:
6598 Jim_Panic(interp,"Default reached in SetExprFromAny()");
6599 break;
6600 }
6601 }
6602 while (Jim_StackLen(&stack)) {
6603 char *opstr = Jim_StackPop(&stack);
6604 op = JimExprOperatorInfo(opstr);
6605 if (op == NULL && !strcmp(opstr, "(")) {
6606 Jim_Free(opstr);
6607 Jim_SetResultString(interp, "Missing close parenthesis", -1);
6608 goto err;
6609 }
6610 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6611 }
6612 /* Check program correctness. */
6613 if (ExprCheckCorrectness(expr) != JIM_OK) {
6614 Jim_SetResultString(interp, "Invalid expression", -1);
6615 goto err;
6616 }
6617
6618 /* Free the stack used for the compilation. */
6619 Jim_FreeStackElements(&stack, Jim_Free);
6620 Jim_FreeStack(&stack);
6621
6622 /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6623 ExprMakeLazy(interp, expr);
6624
6625 /* Perform literal sharing */
6626 if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6627 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6628 if (bodyObjPtr->typePtr == &scriptObjType) {
6629 ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6630 ExprShareLiterals(interp, expr, bodyScript);
6631 }
6632 }
6633
6634 /* Free the old internal rep and set the new one. */
6635 Jim_FreeIntRep(interp, objPtr);
6636 Jim_SetIntRepPtr(objPtr, expr);
6637 objPtr->typePtr = &exprObjType;
6638 return JIM_OK;
6639
6640 err: /* we jump here on syntax/compile errors. */
6641 Jim_FreeStackElements(&stack, Jim_Free);
6642 Jim_FreeStack(&stack);
6643 Jim_Free(expr->opcode);
6644 for (i = 0; i < expr->len; i++) {
6645 Jim_DecrRefCount(interp,expr->obj[i]);
6646 }
6647 Jim_Free(expr->obj);
6648 Jim_Free(expr);
6649 return JIM_ERR;
6650 }
6651
6652 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6653 {
6654 if (objPtr->typePtr != &exprObjType) {
6655 if (SetExprFromAny(interp, objPtr) != JIM_OK)
6656 return NULL;
6657 }
6658 return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6659 }
6660
6661 /* -----------------------------------------------------------------------------
6662 * Expressions evaluation.
6663 * Jim uses a specialized stack-based virtual machine for expressions,
6664 * that takes advantage of the fact that expr's operators
6665 * can't be redefined.
6666 *
6667 * Jim_EvalExpression() uses the bytecode compiled by
6668 * SetExprFromAny() method of the "expression" object.
6669 *
6670 * On success a Tcl Object containing the result of the evaluation
6671 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6672 * returned.
6673 * On error the function returns a retcode != to JIM_OK and set a suitable
6674 * error on the interp.
6675 * ---------------------------------------------------------------------------*/
6676 #define JIM_EE_STATICSTACK_LEN 10
6677
6678 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6679 Jim_Obj **exprResultPtrPtr)
6680 {
6681 ExprByteCode *expr;
6682 Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6683 int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6684
6685 Jim_IncrRefCount(exprObjPtr);
6686 expr = Jim_GetExpression(interp, exprObjPtr);
6687 if (!expr) {
6688 Jim_DecrRefCount(interp, exprObjPtr);
6689 return JIM_ERR; /* error in expression. */
6690 }
6691 /* In order to avoid that the internal repr gets freed due to
6692 * shimmering of the exprObjPtr's object, we make the internal rep
6693 * shared. */
6694 expr->inUse++;
6695
6696 /* The stack-based expr VM itself */
6697
6698 /* Stack allocation. Expr programs have the feature that
6699 * a program of length N can't require a stack longer than
6700 * N. */
6701 if (expr->len > JIM_EE_STATICSTACK_LEN)
6702 stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6703 else
6704 stack = staticStack;
6705
6706 /* Execute every istruction */
6707 for (i = 0; i < expr->len; i++) {
6708 Jim_Obj *A, *B, *objPtr;
6709 jim_wide wA, wB, wC;
6710 double dA, dB, dC;
6711 const char *sA, *sB;
6712 int Alen, Blen, retcode;
6713 int opcode = expr->opcode[i];
6714
6715 if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6716 stack[stacklen++] = expr->obj[i];
6717 Jim_IncrRefCount(expr->obj[i]);
6718 } else if (opcode == JIM_EXPROP_VARIABLE) {
6719 objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6720 if (objPtr == NULL) {
6721 error = 1;
6722 goto err;
6723 }
6724 stack[stacklen++] = objPtr;
6725 Jim_IncrRefCount(objPtr);
6726 } else if (opcode == JIM_EXPROP_SUBST) {
6727 if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6728 &objPtr, JIM_NONE)) != JIM_OK)
6729 {
6730 error = 1;
6731 errRetCode = retcode;
6732 goto err;
6733 }
6734 stack[stacklen++] = objPtr;
6735 Jim_IncrRefCount(objPtr);
6736 } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6737 objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6738 if (objPtr == NULL) {
6739 error = 1;
6740 goto err;
6741 }
6742 stack[stacklen++] = objPtr;
6743 Jim_IncrRefCount(objPtr);
6744 } else if (opcode == JIM_EXPROP_COMMAND) {
6745 if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6746 error = 1;
6747 errRetCode = retcode;
6748 goto err;
6749 }
6750 stack[stacklen++] = interp->result;
6751 Jim_IncrRefCount(interp->result);
6752 } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6753 opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6754 {
6755 /* Note that there isn't to increment the
6756 * refcount of objects. the references are moved
6757 * from stack to A and B. */
6758 B = stack[--stacklen];
6759 A = stack[--stacklen];
6760
6761 /* --- Integer --- */
6762 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6763 (B->typePtr == &doubleObjType && !B->bytes) ||
6764 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6765 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6766 goto trydouble;
6767 }
6768 Jim_DecrRefCount(interp, A);
6769 Jim_DecrRefCount(interp, B);
6770 switch(expr->opcode[i]) {
6771 case JIM_EXPROP_ADD: wC = wA+wB; break;
6772 case JIM_EXPROP_SUB: wC = wA-wB; break;
6773 case JIM_EXPROP_MUL: wC = wA*wB; break;
6774 case JIM_EXPROP_LT: wC = wA<wB; break;
6775 case JIM_EXPROP_GT: wC = wA>wB; break;
6776 case JIM_EXPROP_LTE: wC = wA<=wB; break;
6777 case JIM_EXPROP_GTE: wC = wA>=wB; break;
6778 case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6779 case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6780 case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6781 case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6782 case JIM_EXPROP_BITAND: wC = wA&wB; break;
6783 case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6784 case JIM_EXPROP_BITOR: wC = wA|wB; break;
6785 case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6786 case JIM_EXPROP_LOGICAND_LEFT:
6787 if (wA == 0) {
6788 i += (int)wB;
6789 wC = 0;
6790 } else {
6791 continue;
6792 }
6793 break;
6794 case JIM_EXPROP_LOGICOR_LEFT:
6795 if (wA != 0) {
6796 i += (int)wB;
6797 wC = 1;
6798 } else {
6799 continue;
6800 }
6801 break;
6802 case JIM_EXPROP_DIV:
6803 if (wB == 0) goto divbyzero;
6804 wC = wA/wB;
6805 break;
6806 case JIM_EXPROP_MOD:
6807 if (wB == 0) goto divbyzero;
6808 wC = wA%wB;
6809 break;
6810 case JIM_EXPROP_ROTL: {
6811 /* uint32_t would be better. But not everyone has inttypes.h?*/
6812 unsigned long uA = (unsigned long)wA;
6813 #ifdef _MSC_VER
6814 wC = _rotl(uA,(unsigned long)wB);
6815 #else
6816 const unsigned int S = sizeof(unsigned long) * 8;
6817 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6818 #endif
6819 break;
6820 }
6821 case JIM_EXPROP_ROTR: {
6822 unsigned long uA = (unsigned long)wA;
6823 #ifdef _MSC_VER
6824 wC = _rotr(uA,(unsigned long)wB);
6825 #else
6826 const unsigned int S = sizeof(unsigned long) * 8;
6827 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6828 #endif
6829 break;
6830 }
6831
6832 default:
6833 wC = 0; /* avoid gcc warning */
6834 break;
6835 }
6836 stack[stacklen] = Jim_NewIntObj(interp, wC);
6837 Jim_IncrRefCount(stack[stacklen]);
6838 stacklen++;
6839 continue;
6840 trydouble:
6841 /* --- Double --- */
6842 if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6843 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6844 Jim_DecrRefCount(interp, A);
6845 Jim_DecrRefCount(interp, B);
6846 error = 1;
6847 goto err;
6848 }
6849 Jim_DecrRefCount(interp, A);
6850 Jim_DecrRefCount(interp, B);
6851 switch(expr->opcode[i]) {
6852 case JIM_EXPROP_ROTL:
6853 case JIM_EXPROP_ROTR:
6854 case JIM_EXPROP_LSHIFT:
6855 case JIM_EXPROP_RSHIFT:
6856 case JIM_EXPROP_BITAND:
6857 case JIM_EXPROP_BITXOR:
6858 case JIM_EXPROP_BITOR:
6859 case JIM_EXPROP_MOD:
6860 case JIM_EXPROP_POW:
6861 Jim_SetResultString(interp,
6862 "Got floating-point value where integer was expected", -1);
6863 error = 1;
6864 goto err;
6865 break;
6866 case JIM_EXPROP_ADD: dC = dA+dB; break;
6867 case JIM_EXPROP_SUB: dC = dA-dB; break;
6868 case JIM_EXPROP_MUL: dC = dA*dB; break;
6869 case JIM_EXPROP_LT: dC = dA<dB; break;
6870 case JIM_EXPROP_GT: dC = dA>dB; break;
6871 case JIM_EXPROP_LTE: dC = dA<=dB; break;
6872 case JIM_EXPROP_GTE: dC = dA>=dB; break;
6873 case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6874 case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6875 case JIM_EXPROP_LOGICAND_LEFT:
6876 if (dA == 0) {
6877 i += (int)dB;
6878 dC = 0;
6879 } else {
6880 continue;
6881 }
6882 break;
6883 case JIM_EXPROP_LOGICOR_LEFT:
6884 if (dA != 0) {
6885 i += (int)dB;
6886 dC = 1;
6887 } else {
6888 continue;
6889 }
6890 break;
6891 case JIM_EXPROP_DIV:
6892 if (dB == 0) goto divbyzero;
6893 dC = dA/dB;
6894 break;
6895 default:
6896 dC = 0; /* avoid gcc warning */
6897 break;
6898 }
6899 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6900 Jim_IncrRefCount(stack[stacklen]);
6901 stacklen++;
6902 } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6903 B = stack[--stacklen];
6904 A = stack[--stacklen];
6905 sA = Jim_GetString(A, &Alen);
6906 sB = Jim_GetString(B, &Blen);
6907 switch(expr->opcode[i]) {
6908 case JIM_EXPROP_STREQ:
6909 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6910 wC = 1;
6911 else
6912 wC = 0;
6913 break;
6914 case JIM_EXPROP_STRNE:
6915 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6916 wC = 1;
6917 else
6918 wC = 0;
6919 break;
6920 default:
6921 wC = 0; /* avoid gcc warning */
6922 break;
6923 }
6924 Jim_DecrRefCount(interp, A);
6925 Jim_DecrRefCount(interp, B);
6926 stack[stacklen] = Jim_NewIntObj(interp, wC);
6927 Jim_IncrRefCount(stack[stacklen]);
6928 stacklen++;
6929 } else if (opcode == JIM_EXPROP_NOT ||
6930 opcode == JIM_EXPROP_BITNOT ||
6931 opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6932 opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6933 /* Note that there isn't to increment the
6934 * refcount of objects. the references are moved
6935 * from stack to A and B. */
6936 A = stack[--stacklen];
6937
6938 /* --- Integer --- */
6939 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6940 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6941 goto trydouble_unary;
6942 }
6943 Jim_DecrRefCount(interp, A);
6944 switch(expr->opcode[i]) {
6945 case JIM_EXPROP_NOT: wC = !wA; break;
6946 case JIM_EXPROP_BITNOT: wC = ~wA; break;
6947 case JIM_EXPROP_LOGICAND_RIGHT:
6948 case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6949 default:
6950 wC = 0; /* avoid gcc warning */
6951 break;
6952 }
6953 stack[stacklen] = Jim_NewIntObj(interp, wC);
6954 Jim_IncrRefCount(stack[stacklen]);
6955 stacklen++;
6956 continue;
6957 trydouble_unary:
6958 /* --- Double --- */
6959 if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
6960 Jim_DecrRefCount(interp, A);
6961 error = 1;
6962 goto err;
6963 }
6964 Jim_DecrRefCount(interp, A);
6965 switch(expr->opcode[i]) {
6966 case JIM_EXPROP_NOT: dC = !dA; break;
6967 case JIM_EXPROP_LOGICAND_RIGHT:
6968 case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
6969 case JIM_EXPROP_BITNOT:
6970 Jim_SetResultString(interp,
6971 "Got floating-point value where integer was expected", -1);
6972 error = 1;
6973 goto err;
6974 break;
6975 default:
6976 dC = 0; /* avoid gcc warning */
6977 break;
6978 }
6979 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6980 Jim_IncrRefCount(stack[stacklen]);
6981 stacklen++;
6982 } else {
6983 Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
6984 }
6985 }
6986 err:
6987 /* There is no need to decerement the inUse field because
6988 * this reference is transfered back into the exprObjPtr. */
6989 Jim_FreeIntRep(interp, exprObjPtr);
6990 exprObjPtr->typePtr = &exprObjType;
6991 Jim_SetIntRepPtr(exprObjPtr, expr);
6992 Jim_DecrRefCount(interp, exprObjPtr);
6993 if (!error) {
6994 *exprResultPtrPtr = stack[0];
6995 Jim_IncrRefCount(stack[0]);
6996 errRetCode = JIM_OK;
6997 }
6998 for (i = 0; i < stacklen; i++) {
6999 Jim_DecrRefCount(interp, stack[i]);
7000 }
7001 if (stack != staticStack)
7002 Jim_Free(stack);
7003 return errRetCode;
7004 divbyzero:
7005 error = 1;
7006 Jim_SetResultString(interp, "Division by zero", -1);
7007 goto err;
7008 }
7009
7010 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7011 {
7012 int retcode;
7013 jim_wide wideValue;
7014 double doubleValue;
7015 Jim_Obj *exprResultPtr;
7016
7017 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7018 if (retcode != JIM_OK)
7019 return retcode;
7020 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7021 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7022 {
7023 Jim_DecrRefCount(interp, exprResultPtr);
7024 return JIM_ERR;
7025 } else {
7026 Jim_DecrRefCount(interp, exprResultPtr);
7027 *boolPtr = doubleValue != 0;
7028 return JIM_OK;
7029 }
7030 }
7031 Jim_DecrRefCount(interp, exprResultPtr);
7032 *boolPtr = wideValue != 0;
7033 return JIM_OK;
7034 }
7035
7036 /* -----------------------------------------------------------------------------
7037 * ScanFormat String Object
7038 * ---------------------------------------------------------------------------*/
7039
7040 /* This Jim_Obj will held a parsed representation of a format string passed to
7041 * the Jim_ScanString command. For error diagnostics, the scanformat string has
7042 * to be parsed in its entirely first and then, if correct, can be used for
7043 * scanning. To avoid endless re-parsing, the parsed representation will be
7044 * stored in an internal representation and re-used for performance reason. */
7045
7046 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7047 * scanformat string. This part will later be used to extract information
7048 * out from the string to be parsed by Jim_ScanString */
7049
7050 typedef struct ScanFmtPartDescr {
7051 char type; /* Type of conversion (e.g. c, d, f) */
7052 char modifier; /* Modify type (e.g. l - long, h - short */
7053 size_t width; /* Maximal width of input to be converted */
7054 int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
7055 char *arg; /* Specification of a CHARSET conversion */
7056 char *prefix; /* Prefix to be scanned literally before conversion */
7057 } ScanFmtPartDescr;
7058
7059 /* The ScanFmtStringObj will held the internal representation of a scanformat
7060 * string parsed and separated in part descriptions. Furthermore it contains
7061 * the original string representation of the scanformat string to allow for
7062 * fast update of the Jim_Obj's string representation part.
7063 *
7064 * As add-on the internal object representation add some scratch pad area
7065 * for usage by Jim_ScanString to avoid endless allocating and freeing of
7066 * memory for purpose of string scanning.
7067 *
7068 * The error member points to a static allocated string in case of a mal-
7069 * formed scanformat string or it contains '0' (NULL) in case of a valid
7070 * parse representation.
7071 *
7072 * The whole memory of the internal representation is allocated as a single
7073 * area of memory that will be internally separated. So freeing and duplicating
7074 * of such an object is cheap */
7075
7076 typedef struct ScanFmtStringObj {
7077 jim_wide size; /* Size of internal repr in bytes */
7078 char *stringRep; /* Original string representation */
7079 size_t count; /* Number of ScanFmtPartDescr contained */
7080 size_t convCount; /* Number of conversions that will assign */
7081 size_t maxPos; /* Max position index if XPG3 is used */
7082 const char *error; /* Ptr to error text (NULL if no error */
7083 char *scratch; /* Some scratch pad used by Jim_ScanString */
7084 ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
7085 } ScanFmtStringObj;
7086
7087
7088 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7089 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7090 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7091
7092 static Jim_ObjType scanFmtStringObjType = {
7093 "scanformatstring",
7094 FreeScanFmtInternalRep,
7095 DupScanFmtInternalRep,
7096 UpdateStringOfScanFmt,
7097 JIM_TYPE_NONE,
7098 };
7099
7100 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7101 {
7102 JIM_NOTUSED(interp);
7103 Jim_Free((char*)objPtr->internalRep.ptr);
7104 objPtr->internalRep.ptr = 0;
7105 }
7106
7107 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7108 {
7109 size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7110 ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7111
7112 JIM_NOTUSED(interp);
7113 memcpy(newVec, srcPtr->internalRep.ptr, size);
7114 dupPtr->internalRep.ptr = newVec;
7115 dupPtr->typePtr = &scanFmtStringObjType;
7116 }
7117
7118 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7119 {
7120 char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7121
7122 objPtr->bytes = Jim_StrDup(bytes);
7123 objPtr->length = strlen(bytes);
7124 }
7125
7126 /* SetScanFmtFromAny will parse a given string and create the internal
7127 * representation of the format specification. In case of an error
7128 * the error data member of the internal representation will be set
7129 * to an descriptive error text and the function will be left with
7130 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7131 * specification */
7132
7133 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7134 {
7135 ScanFmtStringObj *fmtObj;
7136 char *buffer;
7137 int maxCount, i, approxSize, lastPos = -1;
7138 const char *fmt = objPtr->bytes;
7139 int maxFmtLen = objPtr->length;
7140 const char *fmtEnd = fmt + maxFmtLen;
7141 int curr;
7142
7143 Jim_FreeIntRep(interp, objPtr);
7144 /* Count how many conversions could take place maximally */
7145 for (i=0, maxCount=0; i < maxFmtLen; ++i)
7146 if (fmt[i] == '%')
7147 ++maxCount;
7148 /* Calculate an approximation of the memory necessary */
7149 approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
7150 + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7151 + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
7152 + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
7153 + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
7154 + (maxCount +1) * sizeof(char) /* '\0' for every partial */
7155 + 1; /* safety byte */
7156 fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7157 memset(fmtObj, 0, approxSize);
7158 fmtObj->size = approxSize;
7159 fmtObj->maxPos = 0;
7160 fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7161 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7162 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7163 buffer = fmtObj->stringRep + maxFmtLen + 1;
7164 objPtr->internalRep.ptr = fmtObj;
7165 objPtr->typePtr = &scanFmtStringObjType;
7166 for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7167 int width=0, skip;
7168 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7169 fmtObj->count++;
7170 descr->width = 0; /* Assume width unspecified */
7171 /* Overread and store any "literal" prefix */
7172 if (*fmt != '%' || fmt[1] == '%') {
7173 descr->type = 0;
7174 descr->prefix = &buffer[i];
7175 for (; fmt < fmtEnd; ++fmt) {
7176 if (*fmt == '%') {
7177 if (fmt[1] != '%') break;
7178 ++fmt;
7179 }
7180 buffer[i++] = *fmt;
7181 }
7182 buffer[i++] = 0;
7183 }
7184 /* Skip the conversion introducing '%' sign */
7185 ++fmt;
7186 /* End reached due to non-conversion literal only? */
7187 if (fmt >= fmtEnd)
7188 goto done;
7189 descr->pos = 0; /* Assume "natural" positioning */
7190 if (*fmt == '*') {
7191 descr->pos = -1; /* Okay, conversion will not be assigned */
7192 ++fmt;
7193 } else
7194 fmtObj->convCount++; /* Otherwise count as assign-conversion */
7195 /* Check if next token is a number (could be width or pos */
7196 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7197 fmt += skip;
7198 /* Was the number a XPG3 position specifier? */
7199 if (descr->pos != -1 && *fmt == '$') {
7200 int prev;
7201 ++fmt;
7202 descr->pos = width;
7203 width = 0;
7204 /* Look if "natural" postioning and XPG3 one was mixed */
7205 if ((lastPos == 0 && descr->pos > 0)
7206 || (lastPos > 0 && descr->pos == 0)) {
7207 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7208 return JIM_ERR;
7209 }
7210 /* Look if this position was already used */
7211 for (prev=0; prev < curr; ++prev) {
7212 if (fmtObj->descr[prev].pos == -1) continue;
7213 if (fmtObj->descr[prev].pos == descr->pos) {
7214 fmtObj->error = "same \"%n$\" conversion specifier "
7215 "used more than once";
7216 return JIM_ERR;
7217 }
7218 }
7219 /* Try to find a width after the XPG3 specifier */
7220 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7221 descr->width = width;
7222 fmt += skip;
7223 }
7224 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7225 fmtObj->maxPos = descr->pos;
7226 } else {
7227 /* Number was not a XPG3, so it has to be a width */
7228 descr->width = width;
7229 }
7230 }
7231 /* If positioning mode was undetermined yet, fix this */
7232 if (lastPos == -1)
7233 lastPos = descr->pos;
7234 /* Handle CHARSET conversion type ... */
7235 if (*fmt == '[') {
7236 int swapped = 1, beg = i, end, j;
7237 descr->type = '[';
7238 descr->arg = &buffer[i];
7239 ++fmt;
7240 if (*fmt == '^') buffer[i++] = *fmt++;
7241 if (*fmt == ']') buffer[i++] = *fmt++;
7242 while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7243 if (*fmt != ']') {
7244 fmtObj->error = "unmatched [ in format string";
7245 return JIM_ERR;
7246 }
7247 end = i;
7248 buffer[i++] = 0;
7249 /* In case a range fence was given "backwards", swap it */
7250 while (swapped) {
7251 swapped = 0;
7252 for (j=beg+1; j < end-1; ++j) {
7253 if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7254 char tmp = buffer[j-1];
7255 buffer[j-1] = buffer[j+1];
7256 buffer[j+1] = tmp;
7257 swapped = 1;
7258 }
7259 }
7260 }
7261 } else {
7262 /* Remember any valid modifier if given */
7263 if (strchr("hlL", *fmt) != 0)
7264 descr->modifier = tolower((int)*fmt++);
7265
7266 descr->type = *fmt;
7267 if (strchr("efgcsndoxui", *fmt) == 0) {
7268 fmtObj->error = "bad scan conversion character";
7269 return JIM_ERR;
7270 } else if (*fmt == 'c' && descr->width != 0) {
7271 fmtObj->error = "field width may not be specified in %c "
7272 "conversion";
7273 return JIM_ERR;
7274 } else if (*fmt == 'u' && descr->modifier == 'l') {
7275 fmtObj->error = "unsigned wide not supported";
7276 return JIM_ERR;
7277 }
7278 }
7279 curr++;
7280 }
7281 done:
7282 if (fmtObj->convCount == 0) {
7283 fmtObj->error = "no any conversion specifier given";
7284 return JIM_ERR;
7285 }
7286 return JIM_OK;
7287 }
7288
7289 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7290
7291 #define FormatGetCnvCount(_fo_) \
7292 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7293 #define FormatGetMaxPos(_fo_) \
7294 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7295 #define FormatGetError(_fo_) \
7296 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7297
7298 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7299 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7300 * bitvector implementation in Jim? */
7301
7302 static int JimTestBit(const char *bitvec, char ch)
7303 {
7304 div_t pos = div(ch-1, 8);
7305 return bitvec[pos.quot] & (1 << pos.rem);
7306 }
7307
7308 static void JimSetBit(char *bitvec, char ch)
7309 {
7310 div_t pos = div(ch-1, 8);
7311 bitvec[pos.quot] |= (1 << pos.rem);
7312 }
7313
7314 #if 0 /* currently not used */
7315 static void JimClearBit(char *bitvec, char ch)
7316 {
7317 div_t pos = div(ch-1, 8);
7318 bitvec[pos.quot] &= ~(1 << pos.rem);
7319 }
7320 #endif
7321
7322 /* JimScanAString is used to scan an unspecified string that ends with
7323 * next WS, or a string that is specified via a charset. The charset
7324 * is currently implemented in a way to only allow for usage with
7325 * ASCII. Whenever we will switch to UNICODE, another idea has to
7326 * be born :-/
7327 *
7328 * FIXME: Works only with ASCII */
7329
7330 static Jim_Obj *
7331 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7332 {
7333 size_t i;
7334 Jim_Obj *result;
7335 char charset[256/8+1]; /* A Charset may contain max 256 chars */
7336 char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7337
7338 /* First init charset to nothing or all, depending if a specified
7339 * or an unspecified string has to be parsed */
7340 memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7341 if (sdescr) {
7342 /* There was a set description given, that means we are parsing
7343 * a specified string. So we have to build a corresponding
7344 * charset reflecting the description */
7345 int notFlag = 0;
7346 /* Should the set be negated at the end? */
7347 if (*sdescr == '^') {
7348 notFlag = 1;
7349 ++sdescr;
7350 }
7351 /* Here '-' is meant literally and not to define a range */
7352 if (*sdescr == '-') {
7353 JimSetBit(charset, '-');
7354 ++sdescr;
7355 }
7356 while (*sdescr) {
7357 if (sdescr[1] == '-' && sdescr[2] != 0) {
7358 /* Handle range definitions */
7359 int i;
7360 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7361 JimSetBit(charset, (char)i);
7362 sdescr += 3;
7363 } else {
7364 /* Handle verbatim character definitions */
7365 JimSetBit(charset, *sdescr++);
7366 }
7367 }
7368 /* Negate the charset if there was a NOT given */
7369 for (i=0; notFlag && i < sizeof(charset); ++i)
7370 charset[i] = ~charset[i];
7371 }
7372 /* And after all the mess above, the real work begin ... */
7373 while (str && *str) {
7374 if (!sdescr && isspace((int)*str))
7375 break; /* EOS via WS if unspecified */
7376 if (JimTestBit(charset, *str)) *buffer++ = *str++;
7377 else break; /* EOS via mismatch if specified scanning */
7378 }
7379 *buffer = 0; /* Close the string properly ... */
7380 result = Jim_NewStringObj(interp, anchor, -1);
7381 Jim_Free(anchor); /* ... and free it afer usage */
7382 return result;
7383 }
7384
7385 /* ScanOneEntry will scan one entry out of the string passed as argument.
7386 * It use the sscanf() function for this task. After extracting and
7387 * converting of the value, the count of scanned characters will be
7388 * returned of -1 in case of no conversion tool place and string was
7389 * already scanned thru */
7390
7391 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7392 ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7393 {
7394 # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7395 ? sizeof(jim_wide) \
7396 : sizeof(double))
7397 char buffer[MAX_SIZE];
7398 char *value = buffer;
7399 const char *tok;
7400 const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7401 size_t sLen = strlen(&str[pos]), scanned = 0;
7402 size_t anchor = pos;
7403 int i;
7404
7405 /* First pessimiticly assume, we will not scan anything :-) */
7406 *valObjPtr = 0;
7407 if (descr->prefix) {
7408 /* There was a prefix given before the conversion, skip it and adjust
7409 * the string-to-be-parsed accordingly */
7410 for (i=0; str[pos] && descr->prefix[i]; ++i) {
7411 /* If prefix require, skip WS */
7412 if (isspace((int)descr->prefix[i]))
7413 while (str[pos] && isspace((int)str[pos])) ++pos;
7414 else if (descr->prefix[i] != str[pos])
7415 break; /* Prefix do not match here, leave the loop */
7416 else
7417 ++pos; /* Prefix matched so far, next round */
7418 }
7419 if (str[pos] == 0)
7420 return -1; /* All of str consumed: EOF condition */
7421 else if (descr->prefix[i] != 0)
7422 return 0; /* Not whole prefix consumed, no conversion possible */
7423 }
7424 /* For all but following conversion, skip leading WS */
7425 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7426 while (isspace((int)str[pos])) ++pos;
7427 /* Determine how much skipped/scanned so far */
7428 scanned = pos - anchor;
7429 if (descr->type == 'n') {
7430 /* Return pseudo conversion means: how much scanned so far? */
7431 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7432 } else if (str[pos] == 0) {
7433 /* Cannot scan anything, as str is totally consumed */
7434 return -1;
7435 } else {
7436 /* Processing of conversions follows ... */
7437 if (descr->width > 0) {
7438 /* Do not try to scan as fas as possible but only the given width.
7439 * To ensure this, we copy the part that should be scanned. */
7440 size_t tLen = descr->width > sLen ? sLen : descr->width;
7441 tok = Jim_StrDupLen(&str[pos], tLen);
7442 } else {
7443 /* As no width was given, simply refer to the original string */
7444 tok = &str[pos];
7445 }
7446 switch (descr->type) {
7447 case 'c':
7448 *valObjPtr = Jim_NewIntObj(interp, *tok);
7449 scanned += 1;
7450 break;
7451 case 'd': case 'o': case 'x': case 'u': case 'i': {
7452 char *endp; /* Position where the number finished */
7453 int base = descr->type == 'o' ? 8
7454 : descr->type == 'x' ? 16
7455 : descr->type == 'i' ? 0
7456 : 10;
7457
7458 do {
7459 /* Try to scan a number with the given base */
7460 if (descr->modifier == 'l')
7461 #ifdef HAVE_LONG_LONG
7462 *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7463 #else
7464 *(jim_wide*)value = strtol(tok, &endp, base);
7465 #endif
7466 else
7467 if (descr->type == 'u')
7468 *(long*)value = strtoul(tok, &endp, base);
7469 else
7470 *(long*)value = strtol(tok, &endp, base);
7471 /* If scanning failed, and base was undetermined, simply
7472 * put it to 10 and try once more. This should catch the
7473 * case where %i begin to parse a number prefix (e.g.
7474 * '0x' but no further digits follows. This will be
7475 * handled as a ZERO followed by a char 'x' by Tcl */
7476 if (endp == tok && base == 0) base = 10;
7477 else break;
7478 } while (1);
7479 if (endp != tok) {
7480 /* There was some number sucessfully scanned! */
7481 if (descr->modifier == 'l')
7482 *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7483 else
7484 *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7485 /* Adjust the number-of-chars scanned so far */
7486 scanned += endp - tok;
7487 } else {
7488 /* Nothing was scanned. We have to determine if this
7489 * happened due to e.g. prefix mismatch or input str
7490 * exhausted */
7491 scanned = *tok ? 0 : -1;
7492 }
7493 break;
7494 }
7495 case 's': case '[': {
7496 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7497 scanned += Jim_Length(*valObjPtr);
7498 break;
7499 }
7500 case 'e': case 'f': case 'g': {
7501 char *endp;
7502
7503 *(double*)value = strtod(tok, &endp);
7504 if (endp != tok) {
7505 /* There was some number sucessfully scanned! */
7506 *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7507 /* Adjust the number-of-chars scanned so far */
7508 scanned += endp - tok;
7509 } else {
7510 /* Nothing was scanned. We have to determine if this
7511 * happened due to e.g. prefix mismatch or input str
7512 * exhausted */
7513 scanned = *tok ? 0 : -1;
7514 }
7515 break;
7516 }
7517 }
7518 /* If a substring was allocated (due to pre-defined width) do not
7519 * forget to free it */
7520 if (tok != &str[pos])
7521 Jim_Free((char*)tok);
7522 }
7523 return scanned;
7524 }
7525
7526 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7527 * string and returns all converted (and not ignored) values in a list back
7528 * to the caller. If an error occured, a NULL pointer will be returned */
7529
7530 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7531 Jim_Obj *fmtObjPtr, int flags)
7532 {
7533 size_t i, pos;
7534 int scanned = 1;
7535 const char *str = Jim_GetString(strObjPtr, 0);
7536 Jim_Obj *resultList = 0;
7537 Jim_Obj **resultVec;
7538 int resultc;
7539 Jim_Obj *emptyStr = 0;
7540 ScanFmtStringObj *fmtObj;
7541
7542 /* If format specification is not an object, convert it! */
7543 if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7544 SetScanFmtFromAny(interp, fmtObjPtr);
7545 fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7546 /* Check if format specification was valid */
7547 if (fmtObj->error != 0) {
7548 if (flags & JIM_ERRMSG)
7549 Jim_SetResultString(interp, fmtObj->error, -1);
7550 return 0;
7551 }
7552 /* Allocate a new "shared" empty string for all unassigned conversions */
7553 emptyStr = Jim_NewEmptyStringObj(interp);
7554 Jim_IncrRefCount(emptyStr);
7555 /* Create a list and fill it with empty strings up to max specified XPG3 */
7556 resultList = Jim_NewListObj(interp, 0, 0);
7557 if (fmtObj->maxPos > 0) {
7558 for (i=0; i < fmtObj->maxPos; ++i)
7559 Jim_ListAppendElement(interp, resultList, emptyStr);
7560 JimListGetElements(interp, resultList, &resultc, &resultVec);
7561 }
7562 /* Now handle every partial format description */
7563 for (i=0, pos=0; i < fmtObj->count; ++i) {
7564 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7565 Jim_Obj *value = 0;
7566 /* Only last type may be "literal" w/o conversion - skip it! */
7567 if (descr->type == 0) continue;
7568 /* As long as any conversion could be done, we will proceed */
7569 if (scanned > 0)
7570 scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7571 /* In case our first try results in EOF, we will leave */
7572 if (scanned == -1 && i == 0)
7573 goto eof;
7574 /* Advance next pos-to-be-scanned for the amount scanned already */
7575 pos += scanned;
7576 /* value == 0 means no conversion took place so take empty string */
7577 if (value == 0)
7578 value = Jim_NewEmptyStringObj(interp);
7579 /* If value is a non-assignable one, skip it */
7580 if (descr->pos == -1) {
7581 Jim_FreeNewObj(interp, value);
7582 } else if (descr->pos == 0)
7583 /* Otherwise append it to the result list if no XPG3 was given */
7584 Jim_ListAppendElement(interp, resultList, value);
7585 else if (resultVec[descr->pos-1] == emptyStr) {
7586 /* But due to given XPG3, put the value into the corr. slot */
7587 Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7588 Jim_IncrRefCount(value);
7589 resultVec[descr->pos-1] = value;
7590 } else {
7591 /* Otherwise, the slot was already used - free obj and ERROR */
7592 Jim_FreeNewObj(interp, value);
7593 goto err;
7594 }
7595 }
7596 Jim_DecrRefCount(interp, emptyStr);
7597 return resultList;
7598 eof:
7599 Jim_DecrRefCount(interp, emptyStr);
7600 Jim_FreeNewObj(interp, resultList);
7601 return (Jim_Obj*)EOF;
7602 err:
7603 Jim_DecrRefCount(interp, emptyStr);
7604 Jim_FreeNewObj(interp, resultList);
7605 return 0;
7606 }
7607
7608 /* -----------------------------------------------------------------------------
7609 * Pseudo Random Number Generation
7610 * ---------------------------------------------------------------------------*/
7611 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7612 int seedLen);
7613
7614 /* Initialize the sbox with the numbers from 0 to 255 */
7615 static void JimPrngInit(Jim_Interp *interp)
7616 {
7617 int i;
7618 unsigned int seed[256];
7619
7620 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7621 for (i = 0; i < 256; i++)
7622 seed[i] = (rand() ^ time(NULL) ^ clock());
7623 JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7624 }
7625
7626 /* Generates N bytes of random data */
7627 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7628 {
7629 Jim_PrngState *prng;
7630 unsigned char *destByte = (unsigned char*) dest;
7631 unsigned int si, sj, x;
7632
7633 /* initialization, only needed the first time */
7634 if (interp->prngState == NULL)
7635 JimPrngInit(interp);
7636 prng = interp->prngState;
7637 /* generates 'len' bytes of pseudo-random numbers */
7638 for (x = 0; x < len; x++) {
7639 prng->i = (prng->i+1) & 0xff;
7640 si = prng->sbox[prng->i];
7641 prng->j = (prng->j + si) & 0xff;
7642 sj = prng->sbox[prng->j];
7643 prng->sbox[prng->i] = sj;
7644 prng->sbox[prng->j] = si;
7645 *destByte++ = prng->sbox[(si+sj)&0xff];
7646 }
7647 }
7648
7649 /* Re-seed the generator with user-provided bytes */
7650 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7651 int seedLen)
7652 {
7653 int i;
7654 unsigned char buf[256];
7655 Jim_PrngState *prng;
7656
7657 /* initialization, only needed the first time */
7658 if (interp->prngState == NULL)
7659 JimPrngInit(interp);
7660 prng = interp->prngState;
7661
7662 /* Set the sbox[i] with i */
7663 for (i = 0; i < 256; i++)
7664 prng->sbox[i] = i;
7665 /* Now use the seed to perform a random permutation of the sbox */
7666 for (i = 0; i < seedLen; i++) {
7667 unsigned char t;
7668
7669 t = prng->sbox[i&0xFF];
7670 prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7671 prng->sbox[seed[i]] = t;
7672 }
7673 prng->i = prng->j = 0;
7674 /* discard the first 256 bytes of stream. */
7675 JimRandomBytes(interp, buf, 256);
7676 }
7677
7678 /* -----------------------------------------------------------------------------
7679 * Dynamic libraries support (WIN32 not supported)
7680 * ---------------------------------------------------------------------------*/
7681
7682 #ifdef JIM_DYNLIB
7683 #ifdef WIN32
7684 #define RTLD_LAZY 0
7685 void * dlopen(const char *path, int mode)
7686 {
7687 JIM_NOTUSED(mode);
7688
7689 return (void *)LoadLibraryA(path);
7690 }
7691 int dlclose(void *handle)
7692 {
7693 FreeLibrary((HANDLE)handle);
7694 return 0;
7695 }
7696 void *dlsym(void *handle, const char *symbol)
7697 {
7698 return GetProcAddress((HMODULE)handle, symbol);
7699 }
7700 static char win32_dlerror_string[121];
7701 const char *dlerror()
7702 {
7703 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7704 LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7705 return win32_dlerror_string;
7706 }
7707 #endif /* WIN32 */
7708
7709 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7710 {
7711 Jim_Obj *libPathObjPtr;
7712 int prefixc, i;
7713 void *handle;
7714 int (*onload)(Jim_Interp *interp);
7715
7716 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7717 if (libPathObjPtr == NULL) {
7718 prefixc = 0;
7719 libPathObjPtr = NULL;
7720 } else {
7721 Jim_IncrRefCount(libPathObjPtr);
7722 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7723 }
7724
7725 for (i = -1; i < prefixc; i++) {
7726 if (i < 0) {
7727 handle = dlopen(pathName, RTLD_LAZY);
7728 } else {
7729 FILE *fp;
7730 char buf[JIM_PATH_LEN];
7731 const char *prefix;
7732 int prefixlen;
7733 Jim_Obj *prefixObjPtr;
7734
7735 buf[0] = '\0';
7736 if (Jim_ListIndex(interp, libPathObjPtr, i,
7737 &prefixObjPtr, JIM_NONE) != JIM_OK)
7738 continue;
7739 prefix = Jim_GetString(prefixObjPtr, NULL);
7740 prefixlen = strlen(prefix);
7741 if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7742 continue;
7743 if (prefixlen && prefix[prefixlen-1] == '/')
7744 sprintf(buf, "%s%s", prefix, pathName);
7745 else
7746 sprintf(buf, "%s/%s", prefix, pathName);
7747 printf("opening '%s'\n", buf);
7748 fp = fopen(buf, "r");
7749 if (fp == NULL)
7750 continue;
7751 fclose(fp);
7752 handle = dlopen(buf, RTLD_LAZY);
7753 printf("got handle %p\n", handle);
7754 }
7755 if (handle == NULL) {
7756 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7757 Jim_AppendStrings(interp, Jim_GetResult(interp),
7758 "error loading extension \"", pathName,
7759 "\": ", dlerror(), NULL);
7760 if (i < 0)
7761 continue;
7762 goto err;
7763 }
7764 if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7765 Jim_SetResultString(interp,
7766 "No Jim_OnLoad symbol found on extension", -1);
7767 goto err;
7768 }
7769 if (onload(interp) == JIM_ERR) {
7770 dlclose(handle);
7771 goto err;
7772 }
7773 Jim_SetEmptyResult(interp);
7774 if (libPathObjPtr != NULL)
7775 Jim_DecrRefCount(interp, libPathObjPtr);
7776 return JIM_OK;
7777 }
7778 err:
7779 if (libPathObjPtr != NULL)
7780 Jim_DecrRefCount(interp, libPathObjPtr);
7781 return JIM_ERR;
7782 }
7783 #else /* JIM_DYNLIB */
7784 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7785 {
7786 JIM_NOTUSED(interp);
7787 JIM_NOTUSED(pathName);
7788
7789 Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7790 return JIM_ERR;
7791 }
7792 #endif/* JIM_DYNLIB */
7793
7794 /* -----------------------------------------------------------------------------
7795 * Packages handling
7796 * ---------------------------------------------------------------------------*/
7797
7798 #define JIM_PKG_ANY_VERSION -1
7799
7800 /* Convert a string of the type "1.2" into an integer.
7801 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
7802 * to the integer with value 102 */
7803 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7804 int *intPtr, int flags)
7805 {
7806 char *copy;
7807 jim_wide major, minor;
7808 char *majorStr, *minorStr, *p;
7809
7810 if (v[0] == '\0') {
7811 *intPtr = JIM_PKG_ANY_VERSION;
7812 return JIM_OK;
7813 }
7814
7815 copy = Jim_StrDup(v);
7816 p = strchr(copy, '.');
7817 if (p == NULL) goto badfmt;
7818 *p = '\0';
7819 majorStr = copy;
7820 minorStr = p+1;
7821
7822 if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7823 Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7824 goto badfmt;
7825 *intPtr = (int)(major*100+minor);
7826 Jim_Free(copy);
7827 return JIM_OK;
7828
7829 badfmt:
7830 Jim_Free(copy);
7831 if (flags & JIM_ERRMSG) {
7832 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7833 Jim_AppendStrings(interp, Jim_GetResult(interp),
7834 "invalid package version '", v, "'", NULL);
7835 }
7836 return JIM_ERR;
7837 }
7838
7839 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7840 static int JimPackageMatchVersion(int needed, int actual, int flags)
7841 {
7842 if (needed == JIM_PKG_ANY_VERSION) return 1;
7843 if (flags & JIM_MATCHVER_EXACT) {
7844 return needed == actual;
7845 } else {
7846 return needed/100 == actual/100 && (needed <= actual);
7847 }
7848 }
7849
7850 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7851 int flags)
7852 {
7853 int intVersion;
7854 /* Check if the version format is ok */
7855 if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7856 return JIM_ERR;
7857 /* If the package was already provided returns an error. */
7858 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7859 if (flags & JIM_ERRMSG) {
7860 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7861 Jim_AppendStrings(interp, Jim_GetResult(interp),
7862 "package '", name, "' was already provided", NULL);
7863 }
7864 return JIM_ERR;
7865 }
7866 Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7867 return JIM_OK;
7868 }
7869
7870 #ifndef JIM_ANSIC
7871
7872 #ifndef WIN32
7873 # include <sys/types.h>
7874 # include <dirent.h>
7875 #else
7876 # include <io.h>
7877 /* Posix dirent.h compatiblity layer for WIN32.
7878 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7879 * Copyright Salvatore Sanfilippo ,2005.
7880 *
7881 * Permission to use, copy, modify, and distribute this software and its
7882 * documentation for any purpose is hereby granted without fee, provided
7883 * that this copyright and permissions notice appear in all copies and
7884 * derivatives.
7885 *
7886 * This software is supplied "as is" without express or implied warranty.
7887 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7888 */
7889
7890 struct dirent {
7891 char *d_name;
7892 };
7893
7894 typedef struct DIR {
7895 long handle; /* -1 for failed rewind */
7896 struct _finddata_t info;
7897 struct dirent result; /* d_name null iff first time */
7898 char *name; /* null-terminated char string */
7899 } DIR;
7900
7901 DIR *opendir(const char *name)
7902 {
7903 DIR *dir = 0;
7904
7905 if(name && name[0]) {
7906 size_t base_length = strlen(name);
7907 const char *all = /* search pattern must end with suitable wildcard */
7908 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7909
7910 if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7911 (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7912 {
7913 strcat(strcpy(dir->name, name), all);
7914
7915 if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7916 dir->result.d_name = 0;
7917 else { /* rollback */
7918 Jim_Free(dir->name);
7919 Jim_Free(dir);
7920 dir = 0;
7921 }
7922 } else { /* rollback */
7923 Jim_Free(dir);
7924 dir = 0;
7925 errno = ENOMEM;
7926 }
7927 } else {
7928 errno = EINVAL;
7929 }
7930 return dir;
7931 }
7932
7933 int closedir(DIR *dir)
7934 {
7935 int result = -1;
7936
7937 if(dir) {
7938 if(dir->handle != -1)
7939 result = _findclose(dir->handle);
7940 Jim_Free(dir->name);
7941 Jim_Free(dir);
7942 }
7943 if(result == -1) /* map all errors to EBADF */
7944 errno = EBADF;
7945 return result;
7946 }
7947
7948 struct dirent *readdir(DIR *dir)
7949 {
7950 struct dirent *result = 0;
7951
7952 if(dir && dir->handle != -1) {
7953 if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7954 result = &dir->result;
7955 result->d_name = dir->info.name;
7956 }
7957 } else {
7958 errno = EBADF;
7959 }
7960 return result;
7961 }
7962
7963 #endif /* WIN32 */
7964
7965 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7966 int prefixc, const char *pkgName, int pkgVer, int flags)
7967 {
7968 int bestVer = -1, i;
7969 int pkgNameLen = strlen(pkgName);
7970 char *bestPackage = NULL;
7971 struct dirent *de;
7972
7973 for (i = 0; i < prefixc; i++) {
7974 DIR *dir;
7975 char buf[JIM_PATH_LEN];
7976 int prefixLen;
7977
7978 if (prefixes[i] == NULL) continue;
7979 strncpy(buf, prefixes[i], JIM_PATH_LEN);
7980 buf[JIM_PATH_LEN-1] = '\0';
7981 prefixLen = strlen(buf);
7982 if (prefixLen && buf[prefixLen-1] == '/')
7983 buf[prefixLen-1] = '\0';
7984
7985 if ((dir = opendir(buf)) == NULL) continue;
7986 while ((de = readdir(dir)) != NULL) {
7987 char *fileName = de->d_name;
7988 int fileNameLen = strlen(fileName);
7989
7990 if (strncmp(fileName, "jim-", 4) == 0 &&
7991 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
7992 *(fileName+4+pkgNameLen) == '-' &&
7993 fileNameLen > 4 && /* note that this is not really useful */
7994 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
7995 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
7996 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
7997 {
7998 char ver[6]; /* xx.yy<nulterm> */
7999 char *p = strrchr(fileName, '.');
8000 int verLen, fileVer;
8001
8002 verLen = p - (fileName+4+pkgNameLen+1);
8003 if (verLen < 3 || verLen > 5) continue;
8004 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8005 ver[verLen] = '\0';
8006 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8007 != JIM_OK) continue;
8008 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8009 (bestVer == -1 || bestVer < fileVer))
8010 {
8011 bestVer = fileVer;
8012 Jim_Free(bestPackage);
8013 bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8014 sprintf(bestPackage, "%s/%s", buf, fileName);
8015 }
8016 }
8017 }
8018 closedir(dir);
8019 }
8020 return bestPackage;
8021 }
8022
8023 #else /* JIM_ANSIC */
8024
8025 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8026 int prefixc, const char *pkgName, int pkgVer, int flags)
8027 {
8028 JIM_NOTUSED(interp);
8029 JIM_NOTUSED(prefixes);
8030 JIM_NOTUSED(prefixc);
8031 JIM_NOTUSED(pkgName);
8032 JIM_NOTUSED(pkgVer);
8033 JIM_NOTUSED(flags);
8034 return NULL;
8035 }
8036
8037 #endif /* JIM_ANSIC */
8038
8039 /* Search for a suitable package under every dir specified by jim_libpath
8040 * and load it if possible. If a suitable package was loaded with success
8041 * JIM_OK is returned, otherwise JIM_ERR is returned. */
8042 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8043 int flags)
8044 {
8045 Jim_Obj *libPathObjPtr;
8046 char **prefixes, *best;
8047 int prefixc, i, retCode = JIM_OK;
8048
8049 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8050 if (libPathObjPtr == NULL) {
8051 prefixc = 0;
8052 libPathObjPtr = NULL;
8053 } else {
8054 Jim_IncrRefCount(libPathObjPtr);
8055 Jim_ListLength(interp, libPathObjPtr, &prefixc);
8056 }
8057
8058 prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8059 for (i = 0; i < prefixc; i++) {
8060 Jim_Obj *prefixObjPtr;
8061 if (Jim_ListIndex(interp, libPathObjPtr, i,
8062 &prefixObjPtr, JIM_NONE) != JIM_OK)
8063 {
8064 prefixes[i] = NULL;
8065 continue;
8066 }
8067 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8068 }
8069 /* Scan every directory to find the "best" package. */
8070 best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8071 if (best != NULL) {
8072 char *p = strrchr(best, '.');
8073 /* Try to load/source it */
8074 if (p && strcmp(p, ".tcl") == 0) {
8075 retCode = Jim_EvalFile(interp, best);
8076 } else {
8077 retCode = Jim_LoadLibrary(interp, best);
8078 }
8079 } else {
8080 retCode = JIM_ERR;
8081 }
8082 Jim_Free(best);
8083 for (i = 0; i < prefixc; i++)
8084 Jim_Free(prefixes[i]);
8085 Jim_Free(prefixes);
8086 if (libPathObjPtr)
8087 Jim_DecrRefCount(interp, libPathObjPtr);
8088 return retCode;
8089 }
8090
8091 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8092 const char *ver, int flags)
8093 {
8094 Jim_HashEntry *he;
8095 int requiredVer;
8096
8097 if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8098 return NULL;
8099 he = Jim_FindHashEntry(&interp->packages, name);
8100 if (he == NULL) {
8101 /* Try to load the package. */
8102 if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8103 he = Jim_FindHashEntry(&interp->packages, name);
8104 if (he == NULL) {
8105 return "?";
8106 }
8107 return he->val;
8108 }
8109 /* No way... return an error. */
8110 if (flags & JIM_ERRMSG) {
8111 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8112 Jim_AppendStrings(interp, Jim_GetResult(interp),
8113 "Can't find package '", name, "'", NULL);
8114 }
8115 return NULL;
8116 } else {
8117 int actualVer;
8118 if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8119 != JIM_OK)
8120 {
8121 return NULL;
8122 }
8123 /* Check if version matches. */
8124 if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8125 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8126 Jim_AppendStrings(interp, Jim_GetResult(interp),
8127 "Package '", name, "' already loaded, but with version ",
8128 he->val, NULL);
8129 return NULL;
8130 }
8131 return he->val;
8132 }
8133 }
8134
8135 /* -----------------------------------------------------------------------------
8136 * Eval
8137 * ---------------------------------------------------------------------------*/
8138 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8139 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8140
8141 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8142 Jim_Obj *const *argv);
8143
8144 /* Handle calls to the [unknown] command */
8145 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8146 {
8147 Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8148 int retCode;
8149
8150 /* If the [unknown] command does not exists returns
8151 * just now */
8152 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8153 return JIM_ERR;
8154
8155 /* The object interp->unknown just contains
8156 * the "unknown" string, it is used in order to
8157 * avoid to lookup the unknown command every time
8158 * but instread to cache the result. */
8159 if (argc+1 <= JIM_EVAL_SARGV_LEN)
8160 v = sv;
8161 else
8162 v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8163 /* Make a copy of the arguments vector, but shifted on
8164 * the right of one position. The command name of the
8165 * command will be instead the first argument of the
8166 * [unknonw] call. */
8167 memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8168 v[0] = interp->unknown;
8169 /* Call it */
8170 retCode = Jim_EvalObjVector(interp, argc+1, v);
8171 /* Clean up */
8172 if (v != sv)
8173 Jim_Free(v);
8174 return retCode;
8175 }
8176
8177 /* Eval the object vector 'objv' composed of 'objc' elements.
8178 * Every element is used as single argument.
8179 * Jim_EvalObj() will call this function every time its object
8180 * argument is of "list" type, with no string representation.
8181 *
8182 * This is possible because the string representation of a
8183 * list object generated by the UpdateStringOfList is made
8184 * in a way that ensures that every list element is a different
8185 * command argument. */
8186 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8187 {
8188 int i, retcode;
8189 Jim_Cmd *cmdPtr;
8190
8191 /* Incr refcount of arguments. */
8192 for (i = 0; i < objc; i++)
8193 Jim_IncrRefCount(objv[i]);
8194 /* Command lookup */
8195 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8196 if (cmdPtr == NULL) {
8197 retcode = JimUnknown(interp, objc, objv);
8198 } else {
8199 /* Call it -- Make sure result is an empty object. */
8200 Jim_SetEmptyResult(interp);
8201 if (cmdPtr->cmdProc) {
8202 interp->cmdPrivData = cmdPtr->privData;
8203 retcode = cmdPtr->cmdProc(interp, objc, objv);
8204 } else {
8205 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8206 if (retcode == JIM_ERR) {
8207 JimAppendStackTrace(interp,
8208 Jim_GetString(objv[0], NULL), "?", 1);
8209 }
8210 }
8211 }
8212 /* Decr refcount of arguments and return the retcode */
8213 for (i = 0; i < objc; i++)
8214 Jim_DecrRefCount(interp, objv[i]);
8215 return retcode;
8216 }
8217
8218 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8219 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8220 * The returned object has refcount = 0. */
8221 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8222 int tokens, Jim_Obj **objPtrPtr)
8223 {
8224 int totlen = 0, i, retcode;
8225 Jim_Obj **intv;
8226 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8227 Jim_Obj *objPtr;
8228 char *s;
8229
8230 if (tokens <= JIM_EVAL_SINTV_LEN)
8231 intv = sintv;
8232 else
8233 intv = Jim_Alloc(sizeof(Jim_Obj*)*
8234 tokens);
8235 /* Compute every token forming the argument
8236 * in the intv objects vector. */
8237 for (i = 0; i < tokens; i++) {
8238 switch(token[i].type) {
8239 case JIM_TT_ESC:
8240 case JIM_TT_STR:
8241 intv[i] = token[i].objPtr;
8242 break;
8243 case JIM_TT_VAR:
8244 intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8245 if (!intv[i]) {
8246 retcode = JIM_ERR;
8247 goto err;
8248 }
8249 break;
8250 case JIM_TT_DICTSUGAR:
8251 intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8252 if (!intv[i]) {
8253 retcode = JIM_ERR;
8254 goto err;
8255 }
8256 break;
8257 case JIM_TT_CMD:
8258 retcode = Jim_EvalObj(interp, token[i].objPtr);
8259 if (retcode != JIM_OK)
8260 goto err;
8261 intv[i] = Jim_GetResult(interp);
8262 break;
8263 default:
8264 Jim_Panic(interp,
8265 "default token type reached "
8266 "in Jim_InterpolateTokens().");
8267 break;
8268 }
8269 Jim_IncrRefCount(intv[i]);
8270 /* Make sure there is a valid
8271 * string rep, and add the string
8272 * length to the total legnth. */
8273 Jim_GetString(intv[i], NULL);
8274 totlen += intv[i]->length;
8275 }
8276 /* Concatenate every token in an unique
8277 * object. */
8278 objPtr = Jim_NewStringObjNoAlloc(interp,
8279 NULL, 0);
8280 s = objPtr->bytes = Jim_Alloc(totlen+1);
8281 objPtr->length = totlen;
8282 for (i = 0; i < tokens; i++) {
8283 memcpy(s, intv[i]->bytes, intv[i]->length);
8284 s += intv[i]->length;
8285 Jim_DecrRefCount(interp, intv[i]);
8286 }
8287 objPtr->bytes[totlen] = '\0';
8288 /* Free the intv vector if not static. */
8289 if (tokens > JIM_EVAL_SINTV_LEN)
8290 Jim_Free(intv);
8291 *objPtrPtr = objPtr;
8292 return JIM_OK;
8293 err:
8294 i--;
8295 for (; i >= 0; i--)
8296 Jim_DecrRefCount(interp, intv[i]);
8297 if (tokens > JIM_EVAL_SINTV_LEN)
8298 Jim_Free(intv);
8299 return retcode;
8300 }
8301
8302 /* Helper of Jim_EvalObj() to perform argument expansion.
8303 * Basically this function append an argument to 'argv'
8304 * (and increments argc by reference accordingly), performing
8305 * expansion of the list object if 'expand' is non-zero, or
8306 * just adding objPtr to argv if 'expand' is zero. */
8307 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8308 int *argcPtr, int expand, Jim_Obj *objPtr)
8309 {
8310 if (!expand) {
8311 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8312 /* refcount of objPtr not incremented because
8313 * we are actually transfering a reference from
8314 * the old 'argv' to the expanded one. */
8315 (*argv)[*argcPtr] = objPtr;
8316 (*argcPtr)++;
8317 } else {
8318 int len, i;
8319
8320 Jim_ListLength(interp, objPtr, &len);
8321 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8322 for (i = 0; i < len; i++) {
8323 (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8324 Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8325 (*argcPtr)++;
8326 }
8327 /* The original object reference is no longer needed,
8328 * after the expansion it is no longer present on
8329 * the argument vector, but the single elements are
8330 * in its place. */
8331 Jim_DecrRefCount(interp, objPtr);
8332 }
8333 }
8334
8335 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8336 {
8337 int i, j = 0, len;
8338 ScriptObj *script;
8339 ScriptToken *token;
8340 int *cs; /* command structure array */
8341 int retcode = JIM_OK;
8342 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8343
8344 interp->errorFlag = 0;
8345
8346 /* If the object is of type "list" and there is no
8347 * string representation for this object, we can call
8348 * a specialized version of Jim_EvalObj() */
8349 if (scriptObjPtr->typePtr == &listObjType &&
8350 scriptObjPtr->internalRep.listValue.len &&
8351 scriptObjPtr->bytes == NULL) {
8352 Jim_IncrRefCount(scriptObjPtr);
8353 retcode = Jim_EvalObjVector(interp,
8354 scriptObjPtr->internalRep.listValue.len,
8355 scriptObjPtr->internalRep.listValue.ele);
8356 Jim_DecrRefCount(interp, scriptObjPtr);
8357 return retcode;
8358 }
8359
8360 Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8361 script = Jim_GetScript(interp, scriptObjPtr);
8362 /* Now we have to make sure the internal repr will not be
8363 * freed on shimmering.
8364 *
8365 * Think for example to this:
8366 *
8367 * set x {llength $x; ... some more code ...}; eval $x
8368 *
8369 * In order to preserve the internal rep, we increment the
8370 * inUse field of the script internal rep structure. */
8371 script->inUse++;
8372
8373 token = script->token;
8374 len = script->len;
8375 cs = script->cmdStruct;
8376 i = 0; /* 'i' is the current token index. */
8377
8378 /* Reset the interpreter result. This is useful to
8379 * return the emtpy result in the case of empty program. */
8380 Jim_SetEmptyResult(interp);
8381
8382 /* Execute every command sequentially, returns on
8383 * error (i.e. if a command does not return JIM_OK) */
8384 while (i < len) {
8385 int expand = 0;
8386 int argc = *cs++; /* Get the number of arguments */
8387 Jim_Cmd *cmd;
8388
8389 /* Set the expand flag if needed. */
8390 if (argc == -1) {
8391 expand++;
8392 argc = *cs++;
8393 }
8394 /* Allocate the arguments vector */
8395 if (argc <= JIM_EVAL_SARGV_LEN)
8396 argv = sargv;
8397 else
8398 argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8399 /* Populate the arguments objects. */
8400 for (j = 0; j < argc; j++) {
8401 int tokens = *cs++;
8402
8403 /* tokens is negative if expansion is needed.
8404 * for this argument. */
8405 if (tokens < 0) {
8406 tokens = (-tokens)-1;
8407 i++;
8408 }
8409 if (tokens == 1) {
8410 /* Fast path if the token does not
8411 * need interpolation */
8412 switch(token[i].type) {
8413 case JIM_TT_ESC:
8414 case JIM_TT_STR:
8415 argv[j] = token[i].objPtr;
8416 break;
8417 case JIM_TT_VAR:
8418 tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8419 JIM_ERRMSG);
8420 if (!tmpObjPtr) {
8421 retcode = JIM_ERR;
8422 goto err;
8423 }
8424 argv[j] = tmpObjPtr;
8425 break;
8426 case JIM_TT_DICTSUGAR:
8427 tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8428 if (!tmpObjPtr) {
8429 retcode = JIM_ERR;
8430 goto err;
8431 }
8432 argv[j] = tmpObjPtr;
8433 break;
8434 case JIM_TT_CMD:
8435 retcode = Jim_EvalObj(interp, token[i].objPtr);
8436 if (retcode != JIM_OK)
8437 goto err;
8438 argv[j] = Jim_GetResult(interp);
8439 break;
8440 default:
8441 Jim_Panic(interp,
8442 "default token type reached "
8443 "in Jim_EvalObj().");
8444 break;
8445 }
8446 Jim_IncrRefCount(argv[j]);
8447 i += 2;
8448 } else {
8449 /* For interpolation we call an helper
8450 * function doing the work for us. */
8451 if ((retcode = Jim_InterpolateTokens(interp,
8452 token+i, tokens, &tmpObjPtr)) != JIM_OK)
8453 {
8454 goto err;
8455 }
8456 argv[j] = tmpObjPtr;
8457 Jim_IncrRefCount(argv[j]);
8458 i += tokens+1;
8459 }
8460 }
8461 /* Handle {expand} expansion */
8462 if (expand) {
8463 int *ecs = cs - argc;
8464 int eargc = 0;
8465 Jim_Obj **eargv = NULL;
8466
8467 for (j = 0; j < argc; j++) {
8468 Jim_ExpandArgument( interp, &eargv, &eargc,
8469 ecs[j] < 0, argv[j]);
8470 }
8471 if (argv != sargv)
8472 Jim_Free(argv);
8473 argc = eargc;
8474 argv = eargv;
8475 j = argc;
8476 if (argc == 0) {
8477 /* Nothing to do with zero args. */
8478 Jim_Free(eargv);
8479 continue;
8480 }
8481 }
8482 /* Lookup the command to call */
8483 cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8484 if (cmd != NULL) {
8485 /* Call it -- Make sure result is an empty object. */
8486 Jim_SetEmptyResult(interp);
8487 if (cmd->cmdProc) {
8488 interp->cmdPrivData = cmd->privData;
8489 retcode = cmd->cmdProc(interp, argc, argv);
8490 } else {
8491 retcode = JimCallProcedure(interp, cmd, argc, argv);
8492 if (retcode == JIM_ERR) {
8493 JimAppendStackTrace(interp,
8494 Jim_GetString(argv[0], NULL), script->fileName,
8495 token[i-argc*2].linenr);
8496 }
8497 }
8498 } else {
8499 /* Call [unknown] */
8500 retcode = JimUnknown(interp, argc, argv);
8501 }
8502 if (retcode != JIM_OK) {
8503 i -= argc*2; /* point to the command name. */
8504 goto err;
8505 }
8506 /* Decrement the arguments count */
8507 for (j = 0; j < argc; j++) {
8508 Jim_DecrRefCount(interp, argv[j]);
8509 }
8510
8511 if (argv != sargv) {
8512 Jim_Free(argv);
8513 argv = NULL;
8514 }
8515 }
8516 /* Note that we don't have to decrement inUse, because the
8517 * following code transfers our use of the reference again to
8518 * the script object. */
8519 j = 0; /* on normal termination, the argv array is already
8520 Jim_DecrRefCount-ed. */
8521 err:
8522 /* Handle errors. */
8523 if (retcode == JIM_ERR && !interp->errorFlag) {
8524 interp->errorFlag = 1;
8525 JimSetErrorFileName(interp, script->fileName);
8526 JimSetErrorLineNumber(interp, token[i].linenr);
8527 JimResetStackTrace(interp);
8528 }
8529 Jim_FreeIntRep(interp, scriptObjPtr);
8530 scriptObjPtr->typePtr = &scriptObjType;
8531 Jim_SetIntRepPtr(scriptObjPtr, script);
8532 Jim_DecrRefCount(interp, scriptObjPtr);
8533 for (i = 0; i < j; i++) {
8534 Jim_DecrRefCount(interp, argv[i]);
8535 }
8536 if (argv != sargv)
8537 Jim_Free(argv);
8538 return retcode;
8539 }
8540
8541 /* Call a procedure implemented in Tcl.
8542 * It's possible to speed-up a lot this function, currently
8543 * the callframes are not cached, but allocated and
8544 * destroied every time. What is expecially costly is
8545 * to create/destroy the local vars hash table every time.
8546 *
8547 * This can be fixed just implementing callframes caching
8548 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8549 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8550 Jim_Obj *const *argv)
8551 {
8552 int i, retcode;
8553 Jim_CallFrame *callFramePtr;
8554
8555 /* Check arity */
8556 if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8557 argc > cmd->arityMax)) {
8558 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8559 Jim_AppendStrings(interp, objPtr,
8560 "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8561 (cmd->arityMin > 1) ? " " : "",
8562 Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8563 Jim_SetResult(interp, objPtr);
8564 return JIM_ERR;
8565 }
8566 /* Check if there are too nested calls */
8567 if (interp->numLevels == interp->maxNestingDepth) {
8568 Jim_SetResultString(interp,
8569 "Too many nested calls. Infinite recursion?", -1);
8570 return JIM_ERR;
8571 }
8572 /* Create a new callframe */
8573 callFramePtr = JimCreateCallFrame(interp);
8574 callFramePtr->parentCallFrame = interp->framePtr;
8575 callFramePtr->argv = argv;
8576 callFramePtr->argc = argc;
8577 callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8578 callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8579 callFramePtr->staticVars = cmd->staticVars;
8580 Jim_IncrRefCount(cmd->argListObjPtr);
8581 Jim_IncrRefCount(cmd->bodyObjPtr);
8582 interp->framePtr = callFramePtr;
8583 interp->numLevels ++;
8584 /* Set arguments */
8585 for (i = 0; i < cmd->arityMin-1; i++) {
8586 Jim_Obj *objPtr;
8587
8588 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8589 Jim_SetVariable(interp, objPtr, argv[i+1]);
8590 }
8591 if (cmd->arityMax == -1) {
8592 Jim_Obj *listObjPtr, *objPtr;
8593
8594 listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8595 argc-cmd->arityMin);
8596 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8597 Jim_SetVariable(interp, objPtr, listObjPtr);
8598 }
8599 /* Eval the body */
8600 retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8601
8602 /* Destroy the callframe */
8603 interp->numLevels --;
8604 interp->framePtr = interp->framePtr->parentCallFrame;
8605 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8606 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8607 } else {
8608 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8609 }
8610 /* Handle the JIM_EVAL return code */
8611 if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8612 int savedLevel = interp->evalRetcodeLevel;
8613
8614 interp->evalRetcodeLevel = interp->numLevels;
8615 while (retcode == JIM_EVAL) {
8616 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8617 Jim_IncrRefCount(resultScriptObjPtr);
8618 retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8619 Jim_DecrRefCount(interp, resultScriptObjPtr);
8620 }
8621 interp->evalRetcodeLevel = savedLevel;
8622 }
8623 /* Handle the JIM_RETURN return code */
8624 if (retcode == JIM_RETURN) {
8625 retcode = interp->returnCode;
8626 interp->returnCode = JIM_OK;
8627 }
8628 return retcode;
8629 }
8630
8631 int Jim_Eval(Jim_Interp *interp, const char *script)
8632 {
8633 Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8634 int retval;
8635
8636 Jim_IncrRefCount(scriptObjPtr);
8637 retval = Jim_EvalObj(interp, scriptObjPtr);
8638 Jim_DecrRefCount(interp, scriptObjPtr);
8639 return retval;
8640 }
8641
8642 /* Execute script in the scope of the global level */
8643 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8644 {
8645 Jim_CallFrame *savedFramePtr;
8646 int retval;
8647
8648 savedFramePtr = interp->framePtr;
8649 interp->framePtr = interp->topFramePtr;
8650 retval = Jim_Eval(interp, script);
8651 interp->framePtr = savedFramePtr;
8652 return retval;
8653 }
8654
8655 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8656 {
8657 Jim_CallFrame *savedFramePtr;
8658 int retval;
8659
8660 savedFramePtr = interp->framePtr;
8661 interp->framePtr = interp->topFramePtr;
8662 retval = Jim_EvalObj(interp, scriptObjPtr);
8663 interp->framePtr = savedFramePtr;
8664 /* Try to report the error (if any) via the bgerror proc */
8665 if (retval != JIM_OK) {
8666 Jim_Obj *objv[2];
8667
8668 objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8669 objv[1] = Jim_GetResult(interp);
8670 Jim_IncrRefCount(objv[0]);
8671 Jim_IncrRefCount(objv[1]);
8672 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8673 /* Report the error to stderr. */
8674 Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8675 Jim_PrintErrorMessage(interp);
8676 }
8677 Jim_DecrRefCount(interp, objv[0]);
8678 Jim_DecrRefCount(interp, objv[1]);
8679 }
8680 return retval;
8681 }
8682
8683 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8684 {
8685 char *prg = NULL;
8686 FILE *fp;
8687 int nread, totread, maxlen, buflen;
8688 int retval;
8689 Jim_Obj *scriptObjPtr;
8690
8691 if ((fp = fopen(filename, "r")) == NULL) {
8692 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8693 Jim_AppendStrings(interp, Jim_GetResult(interp),
8694 "Error loading script \"", filename, "\": ",
8695 strerror(errno), NULL);
8696 return JIM_ERR;
8697 }
8698 buflen = 1024;
8699 maxlen = totread = 0;
8700 while (1) {
8701 if (maxlen < totread+buflen+1) {
8702 maxlen = totread+buflen+1;
8703 prg = Jim_Realloc(prg, maxlen);
8704 }
8705 /* do not use Jim_fread() - this is really a file */
8706 if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8707 totread += nread;
8708 }
8709 prg[totread] = '\0';
8710 /* do not use Jim_fclose() - this is really a file */
8711 fclose(fp);
8712
8713 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8714 JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8715 Jim_IncrRefCount(scriptObjPtr);
8716 retval = Jim_EvalObj(interp, scriptObjPtr);
8717 Jim_DecrRefCount(interp, scriptObjPtr);
8718 return retval;
8719 }
8720
8721 /* -----------------------------------------------------------------------------
8722 * Subst
8723 * ---------------------------------------------------------------------------*/
8724 static int JimParseSubstStr(struct JimParserCtx *pc)
8725 {
8726 pc->tstart = pc->p;
8727 pc->tline = pc->linenr;
8728 while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8729 pc->p++; pc->len--;
8730 }
8731 pc->tend = pc->p-1;
8732 pc->tt = JIM_TT_ESC;
8733 return JIM_OK;
8734 }
8735
8736 static int JimParseSubst(struct JimParserCtx *pc, int flags)
8737 {
8738 int retval;
8739
8740 if (pc->len == 0) {
8741 pc->tstart = pc->tend = pc->p;
8742 pc->tline = pc->linenr;
8743 pc->tt = JIM_TT_EOL;
8744 pc->eof = 1;
8745 return JIM_OK;
8746 }
8747 switch(*pc->p) {
8748 case '[':
8749 retval = JimParseCmd(pc);
8750 if (flags & JIM_SUBST_NOCMD) {
8751 pc->tstart--;
8752 pc->tend++;
8753 pc->tt = (flags & JIM_SUBST_NOESC) ?
8754 JIM_TT_STR : JIM_TT_ESC;
8755 }
8756 return retval;
8757 break;
8758 case '$':
8759 if (JimParseVar(pc) == JIM_ERR) {
8760 pc->tstart = pc->tend = pc->p++; pc->len--;
8761 pc->tline = pc->linenr;
8762 pc->tt = JIM_TT_STR;
8763 } else {
8764 if (flags & JIM_SUBST_NOVAR) {
8765 pc->tstart--;
8766 if (flags & JIM_SUBST_NOESC)
8767 pc->tt = JIM_TT_STR;
8768 else
8769 pc->tt = JIM_TT_ESC;
8770 if (*pc->tstart == '{') {
8771 pc->tstart--;
8772 if (*(pc->tend+1))
8773 pc->tend++;
8774 }
8775 }
8776 }
8777 break;
8778 default:
8779 retval = JimParseSubstStr(pc);
8780 if (flags & JIM_SUBST_NOESC)
8781 pc->tt = JIM_TT_STR;
8782 return retval;
8783 break;
8784 }
8785 return JIM_OK;
8786 }
8787
8788 /* The subst object type reuses most of the data structures and functions
8789 * of the script object. Script's data structures are a bit more complex
8790 * for what is needed for [subst]itution tasks, but the reuse helps to
8791 * deal with a single data structure at the cost of some more memory
8792 * usage for substitutions. */
8793 static Jim_ObjType substObjType = {
8794 "subst",
8795 FreeScriptInternalRep,
8796 DupScriptInternalRep,
8797 NULL,
8798 JIM_TYPE_REFERENCES,
8799 };
8800
8801 /* This method takes the string representation of an object
8802 * as a Tcl string where to perform [subst]itution, and generates
8803 * the pre-parsed internal representation. */
8804 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8805 {
8806 int scriptTextLen;
8807 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8808 struct JimParserCtx parser;
8809 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8810
8811 script->len = 0;
8812 script->csLen = 0;
8813 script->commands = 0;
8814 script->token = NULL;
8815 script->cmdStruct = NULL;
8816 script->inUse = 1;
8817 script->substFlags = flags;
8818 script->fileName = NULL;
8819
8820 JimParserInit(&parser, scriptText, scriptTextLen, 1);
8821 while(1) {
8822 char *token;
8823 int len, type, linenr;
8824
8825 JimParseSubst(&parser, flags);
8826 if (JimParserEof(&parser)) break;
8827 token = JimParserGetToken(&parser, &len, &type, &linenr);
8828 ScriptObjAddToken(interp, script, token, len, type,
8829 NULL, linenr);
8830 }
8831 /* Free the old internal rep and set the new one. */
8832 Jim_FreeIntRep(interp, objPtr);
8833 Jim_SetIntRepPtr(objPtr, script);
8834 objPtr->typePtr = &scriptObjType;
8835 return JIM_OK;
8836 }
8837
8838 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8839 {
8840 struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8841
8842 if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8843 SetSubstFromAny(interp, objPtr, flags);
8844 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8845 }
8846
8847 /* Performs commands,variables,blackslashes substitution,
8848 * storing the result object (with refcount 0) into
8849 * resObjPtrPtr. */
8850 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8851 Jim_Obj **resObjPtrPtr, int flags)
8852 {
8853 ScriptObj *script;
8854 ScriptToken *token;
8855 int i, len, retcode = JIM_OK;
8856 Jim_Obj *resObjPtr, *savedResultObjPtr;
8857
8858 script = Jim_GetSubst(interp, substObjPtr, flags);
8859 #ifdef JIM_OPTIMIZATION
8860 /* Fast path for a very common case with array-alike syntax,
8861 * that's: $foo($bar) */
8862 if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8863 Jim_Obj *varObjPtr = script->token[0].objPtr;
8864
8865 Jim_IncrRefCount(varObjPtr);
8866 resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8867 if (resObjPtr == NULL) {
8868 Jim_DecrRefCount(interp, varObjPtr);
8869 return JIM_ERR;
8870 }
8871 Jim_DecrRefCount(interp, varObjPtr);
8872 *resObjPtrPtr = resObjPtr;
8873 return JIM_OK;
8874 }
8875 #endif
8876
8877 Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8878 /* In order to preserve the internal rep, we increment the
8879 * inUse field of the script internal rep structure. */
8880 script->inUse++;
8881
8882 token = script->token;
8883 len = script->len;
8884
8885 /* Save the interp old result, to set it again before
8886 * to return. */
8887 savedResultObjPtr = interp->result;
8888 Jim_IncrRefCount(savedResultObjPtr);
8889
8890 /* Perform the substitution. Starts with an empty object
8891 * and adds every token (performing the appropriate
8892 * var/command/escape substitution). */
8893 resObjPtr = Jim_NewStringObj(interp, "", 0);
8894 for (i = 0; i < len; i++) {
8895 Jim_Obj *objPtr;
8896
8897 switch(token[i].type) {
8898 case JIM_TT_STR:
8899 case JIM_TT_ESC:
8900 Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8901 break;
8902 case JIM_TT_VAR:
8903 objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8904 if (objPtr == NULL) goto err;
8905 Jim_IncrRefCount(objPtr);
8906 Jim_AppendObj(interp, resObjPtr, objPtr);
8907 Jim_DecrRefCount(interp, objPtr);
8908 break;
8909 case JIM_TT_CMD:
8910 if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8911 goto err;
8912 Jim_AppendObj(interp, resObjPtr, interp->result);
8913 break;
8914 default:
8915 Jim_Panic(interp,
8916 "default token type (%d) reached "
8917 "in Jim_SubstObj().", token[i].type);
8918 break;
8919 }
8920 }
8921 ok:
8922 if (retcode == JIM_OK)
8923 Jim_SetResult(interp, savedResultObjPtr);
8924 Jim_DecrRefCount(interp, savedResultObjPtr);
8925 /* Note that we don't have to decrement inUse, because the
8926 * following code transfers our use of the reference again to
8927 * the script object. */
8928 Jim_FreeIntRep(interp, substObjPtr);
8929 substObjPtr->typePtr = &scriptObjType;
8930 Jim_SetIntRepPtr(substObjPtr, script);
8931 Jim_DecrRefCount(interp, substObjPtr);
8932 *resObjPtrPtr = resObjPtr;
8933 return retcode;
8934 err:
8935 Jim_FreeNewObj(interp, resObjPtr);
8936 retcode = JIM_ERR;
8937 goto ok;
8938 }
8939
8940 /* -----------------------------------------------------------------------------
8941 * API Input/Export functions
8942 * ---------------------------------------------------------------------------*/
8943
8944 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8945 {
8946 Jim_HashEntry *he;
8947
8948 he = Jim_FindHashEntry(&interp->stub, funcname);
8949 if (!he)
8950 return JIM_ERR;
8951 memcpy(targetPtrPtr, &he->val, sizeof(void*));
8952 return JIM_OK;
8953 }
8954
8955 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
8956 {
8957 return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
8958 }
8959
8960 #define JIM_REGISTER_API(name) \
8961 Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
8962
8963 void JimRegisterCoreApi(Jim_Interp *interp)
8964 {
8965 interp->getApiFuncPtr = Jim_GetApi;
8966 JIM_REGISTER_API(Alloc);
8967 JIM_REGISTER_API(Free);
8968 JIM_REGISTER_API(Eval);
8969 JIM_REGISTER_API(EvalGlobal);
8970 JIM_REGISTER_API(EvalFile);
8971 JIM_REGISTER_API(EvalObj);
8972 JIM_REGISTER_API(EvalObjBackground);
8973 JIM_REGISTER_API(EvalObjVector);
8974 JIM_REGISTER_API(InitHashTable);
8975 JIM_REGISTER_API(ExpandHashTable);
8976 JIM_REGISTER_API(AddHashEntry);
8977 JIM_REGISTER_API(ReplaceHashEntry);
8978 JIM_REGISTER_API(DeleteHashEntry);
8979 JIM_REGISTER_API(FreeHashTable);
8980 JIM_REGISTER_API(FindHashEntry);
8981 JIM_REGISTER_API(ResizeHashTable);
8982 JIM_REGISTER_API(GetHashTableIterator);
8983 JIM_REGISTER_API(NextHashEntry);
8984 JIM_REGISTER_API(NewObj);
8985 JIM_REGISTER_API(FreeObj);
8986 JIM_REGISTER_API(InvalidateStringRep);
8987 JIM_REGISTER_API(InitStringRep);
8988 JIM_REGISTER_API(DuplicateObj);
8989 JIM_REGISTER_API(GetString);
8990 JIM_REGISTER_API(Length);
8991 JIM_REGISTER_API(InvalidateStringRep);
8992 JIM_REGISTER_API(NewStringObj);
8993 JIM_REGISTER_API(NewStringObjNoAlloc);
8994 JIM_REGISTER_API(AppendString);
8995 JIM_REGISTER_API(AppendObj);
8996 JIM_REGISTER_API(AppendStrings);
8997 JIM_REGISTER_API(StringEqObj);
8998 JIM_REGISTER_API(StringMatchObj);
8999 JIM_REGISTER_API(StringRangeObj);
9000 JIM_REGISTER_API(FormatString);
9001 JIM_REGISTER_API(CompareStringImmediate);
9002 JIM_REGISTER_API(NewReference);
9003 JIM_REGISTER_API(GetReference);
9004 JIM_REGISTER_API(SetFinalizer);
9005 JIM_REGISTER_API(GetFinalizer);
9006 JIM_REGISTER_API(CreateInterp);
9007 JIM_REGISTER_API(FreeInterp);
9008 JIM_REGISTER_API(GetExitCode);
9009 JIM_REGISTER_API(SetStdin);
9010 JIM_REGISTER_API(SetStdout);
9011 JIM_REGISTER_API(SetStderr);
9012 JIM_REGISTER_API(CreateCommand);
9013 JIM_REGISTER_API(CreateProcedure);
9014 JIM_REGISTER_API(DeleteCommand);
9015 JIM_REGISTER_API(RenameCommand);
9016 JIM_REGISTER_API(GetCommand);
9017 JIM_REGISTER_API(SetVariable);
9018 JIM_REGISTER_API(SetVariableStr);
9019 JIM_REGISTER_API(SetGlobalVariableStr);
9020 JIM_REGISTER_API(SetVariableStrWithStr);
9021 JIM_REGISTER_API(SetVariableLink);
9022 JIM_REGISTER_API(GetVariable);
9023 JIM_REGISTER_API(GetCallFrameByLevel);
9024 JIM_REGISTER_API(Collect);
9025 JIM_REGISTER_API(CollectIfNeeded);
9026 JIM_REGISTER_API(GetIndex);
9027 JIM_REGISTER_API(NewListObj);
9028 JIM_REGISTER_API(ListAppendElement);
9029 JIM_REGISTER_API(ListAppendList);
9030 JIM_REGISTER_API(ListLength);
9031 JIM_REGISTER_API(ListIndex);
9032 JIM_REGISTER_API(SetListIndex);
9033 JIM_REGISTER_API(ConcatObj);
9034 JIM_REGISTER_API(NewDictObj);
9035 JIM_REGISTER_API(DictKey);
9036 JIM_REGISTER_API(DictKeysVector);
9037 JIM_REGISTER_API(GetIndex);
9038 JIM_REGISTER_API(GetReturnCode);
9039 JIM_REGISTER_API(EvalExpression);
9040 JIM_REGISTER_API(GetBoolFromExpr);
9041 JIM_REGISTER_API(GetWide);
9042 JIM_REGISTER_API(GetLong);
9043 JIM_REGISTER_API(SetWide);
9044 JIM_REGISTER_API(NewIntObj);
9045 JIM_REGISTER_API(GetDouble);
9046 JIM_REGISTER_API(SetDouble);
9047 JIM_REGISTER_API(NewDoubleObj);
9048 JIM_REGISTER_API(WrongNumArgs);
9049 JIM_REGISTER_API(SetDictKeysVector);
9050 JIM_REGISTER_API(SubstObj);
9051 JIM_REGISTER_API(RegisterApi);
9052 JIM_REGISTER_API(PrintErrorMessage);
9053 JIM_REGISTER_API(InteractivePrompt);
9054 JIM_REGISTER_API(RegisterCoreCommands);
9055 JIM_REGISTER_API(GetSharedString);
9056 JIM_REGISTER_API(ReleaseSharedString);
9057 JIM_REGISTER_API(Panic);
9058 JIM_REGISTER_API(StrDup);
9059 JIM_REGISTER_API(UnsetVariable);
9060 JIM_REGISTER_API(GetVariableStr);
9061 JIM_REGISTER_API(GetGlobalVariable);
9062 JIM_REGISTER_API(GetGlobalVariableStr);
9063 JIM_REGISTER_API(GetAssocData);
9064 JIM_REGISTER_API(SetAssocData);
9065 JIM_REGISTER_API(DeleteAssocData);
9066 JIM_REGISTER_API(GetEnum);
9067 JIM_REGISTER_API(ScriptIsComplete);
9068 JIM_REGISTER_API(PackageRequire);
9069 JIM_REGISTER_API(PackageProvide);
9070 JIM_REGISTER_API(InitStack);
9071 JIM_REGISTER_API(FreeStack);
9072 JIM_REGISTER_API(StackLen);
9073 JIM_REGISTER_API(StackPush);
9074 JIM_REGISTER_API(StackPop);
9075 JIM_REGISTER_API(StackPeek);
9076 JIM_REGISTER_API(FreeStackElements);
9077 }
9078
9079 /* -----------------------------------------------------------------------------
9080 * Core commands utility functions
9081 * ---------------------------------------------------------------------------*/
9082 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
9083 const char *msg)
9084 {
9085 int i;
9086 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9087
9088 Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9089 for (i = 0; i < argc; i++) {
9090 Jim_AppendObj(interp, objPtr, argv[i]);
9091 if (!(i+1 == argc && msg[0] == '\0'))
9092 Jim_AppendString(interp, objPtr, " ", 1);
9093 }
9094 Jim_AppendString(interp, objPtr, msg, -1);
9095 Jim_AppendString(interp, objPtr, "\"", 1);
9096 Jim_SetResult(interp, objPtr);
9097 }
9098
9099 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9100 {
9101 Jim_HashTableIterator *htiter;
9102 Jim_HashEntry *he;
9103 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9104 const char *pattern;
9105 int patternLen;
9106
9107 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9108 htiter = Jim_GetHashTableIterator(&interp->commands);
9109 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9110 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9111 strlen((const char*)he->key), 0))
9112 continue;
9113 Jim_ListAppendElement(interp, listObjPtr,
9114 Jim_NewStringObj(interp, he->key, -1));
9115 }
9116 Jim_FreeHashTableIterator(htiter);
9117 return listObjPtr;
9118 }
9119
9120 #define JIM_VARLIST_GLOBALS 0
9121 #define JIM_VARLIST_LOCALS 1
9122 #define JIM_VARLIST_VARS 2
9123
9124 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9125 int mode)
9126 {
9127 Jim_HashTableIterator *htiter;
9128 Jim_HashEntry *he;
9129 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9130 const char *pattern;
9131 int patternLen;
9132
9133 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9134 if (mode == JIM_VARLIST_GLOBALS) {
9135 htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9136 } else {
9137 /* For [info locals], if we are at top level an emtpy list
9138 * is returned. I don't agree, but we aim at compatibility (SS) */
9139 if (mode == JIM_VARLIST_LOCALS &&
9140 interp->framePtr == interp->topFramePtr)
9141 return listObjPtr;
9142 htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9143 }
9144 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9145 Jim_Var *varPtr = (Jim_Var*) he->val;
9146 if (mode == JIM_VARLIST_LOCALS) {
9147 if (varPtr->linkFramePtr != NULL)
9148 continue;
9149 }
9150 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9151 strlen((const char*)he->key), 0))
9152 continue;
9153 Jim_ListAppendElement(interp, listObjPtr,
9154 Jim_NewStringObj(interp, he->key, -1));
9155 }
9156 Jim_FreeHashTableIterator(htiter);
9157 return listObjPtr;
9158 }
9159
9160 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9161 Jim_Obj **objPtrPtr)
9162 {
9163 Jim_CallFrame *targetCallFrame;
9164
9165 if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9166 != JIM_OK)
9167 return JIM_ERR;
9168 /* No proc call at toplevel callframe */
9169 if (targetCallFrame == interp->topFramePtr) {
9170 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9171 Jim_AppendStrings(interp, Jim_GetResult(interp),
9172 "bad level \"",
9173 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9174 return JIM_ERR;
9175 }
9176 *objPtrPtr = Jim_NewListObj(interp,
9177 targetCallFrame->argv,
9178 targetCallFrame->argc);
9179 return JIM_OK;
9180 }
9181
9182 /* -----------------------------------------------------------------------------
9183 * Core commands
9184 * ---------------------------------------------------------------------------*/
9185
9186 /* fake [puts] -- not the real puts, just for debugging. */
9187 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9188 Jim_Obj *const *argv)
9189 {
9190 const char *str;
9191 int len, nonewline = 0;
9192
9193 if (argc != 2 && argc != 3) {
9194 Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9195 return JIM_ERR;
9196 }
9197 if (argc == 3) {
9198 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9199 {
9200 Jim_SetResultString(interp, "The second argument must "
9201 "be -nonewline", -1);
9202 return JIM_OK;
9203 } else {
9204 nonewline = 1;
9205 argv++;
9206 }
9207 }
9208 str = Jim_GetString(argv[1], &len);
9209 Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9210 if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9211 return JIM_OK;
9212 }
9213
9214 /* Helper for [+] and [*] */
9215 static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
9216 Jim_Obj *const *argv, int op)
9217 {
9218 jim_wide wideValue, res;
9219 double doubleValue, doubleRes;
9220 int i;
9221
9222 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9223
9224 for (i = 1; i < argc; i++) {
9225 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9226 goto trydouble;
9227 if (op == JIM_EXPROP_ADD)
9228 res += wideValue;
9229 else
9230 res *= wideValue;
9231 }
9232 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9233 return JIM_OK;
9234 trydouble:
9235 doubleRes = (double) res;
9236 for (;i < argc; i++) {
9237 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9238 return JIM_ERR;
9239 if (op == JIM_EXPROP_ADD)
9240 doubleRes += doubleValue;
9241 else
9242 doubleRes *= doubleValue;
9243 }
9244 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9245 return JIM_OK;
9246 }
9247
9248 /* Helper for [-] and [/] */
9249 static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9250 Jim_Obj *const *argv, int op)
9251 {
9252 jim_wide wideValue, res = 0;
9253 double doubleValue, doubleRes = 0;
9254 int i = 2;
9255
9256 if (argc < 2) {
9257 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9258 return JIM_ERR;
9259 } else if (argc == 2) {
9260 /* The arity = 2 case is different. For [- x] returns -x,
9261 * while [/ x] returns 1/x. */
9262 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9263 if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9264 JIM_OK)
9265 {
9266 return JIM_ERR;
9267 } else {
9268 if (op == JIM_EXPROP_SUB)
9269 doubleRes = -doubleValue;
9270 else
9271 doubleRes = 1.0/doubleValue;
9272 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9273 doubleRes));
9274 return JIM_OK;
9275 }
9276 }
9277 if (op == JIM_EXPROP_SUB) {
9278 res = -wideValue;
9279 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9280 } else {
9281 doubleRes = 1.0/wideValue;
9282 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9283 doubleRes));
9284 }
9285 return JIM_OK;
9286 } else {
9287 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9288 if (Jim_GetDouble(interp, argv[1], &doubleRes)
9289 != JIM_OK) {
9290 return JIM_ERR;
9291 } else {
9292 goto trydouble;
9293 }
9294 }
9295 }
9296 for (i = 2; i < argc; i++) {
9297 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9298 doubleRes = (double) res;
9299 goto trydouble;
9300 }
9301 if (op == JIM_EXPROP_SUB)
9302 res -= wideValue;
9303 else
9304 res /= wideValue;
9305 }
9306 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9307 return JIM_OK;
9308 trydouble:
9309 for (;i < argc; i++) {
9310 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9311 return JIM_ERR;
9312 if (op == JIM_EXPROP_SUB)
9313 doubleRes -= doubleValue;
9314 else
9315 doubleRes /= doubleValue;
9316 }
9317 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9318 return JIM_OK;
9319 }
9320
9321
9322 /* [+] */
9323 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9324 Jim_Obj *const *argv)
9325 {
9326 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9327 }
9328
9329 /* [*] */
9330 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9331 Jim_Obj *const *argv)
9332 {
9333 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9334 }
9335
9336 /* [-] */
9337 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9338 Jim_Obj *const *argv)
9339 {
9340 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9341 }
9342
9343 /* [/] */
9344 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9345 Jim_Obj *const *argv)
9346 {
9347 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9348 }
9349
9350 /* [set] */
9351 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9352 Jim_Obj *const *argv)
9353 {
9354 if (argc != 2 && argc != 3) {
9355 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9356 return JIM_ERR;
9357 }
9358 if (argc == 2) {
9359 Jim_Obj *objPtr;
9360 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9361 if (!objPtr)
9362 return JIM_ERR;
9363 Jim_SetResult(interp, objPtr);
9364 return JIM_OK;
9365 }
9366 /* argc == 3 case. */
9367 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9368 return JIM_ERR;
9369 Jim_SetResult(interp, argv[2]);
9370 return JIM_OK;
9371 }
9372
9373 /* [unset] */
9374 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9375 Jim_Obj *const *argv)
9376 {
9377 int i;
9378
9379 if (argc < 2) {
9380 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9381 return JIM_ERR;
9382 }
9383 for (i = 1; i < argc; i++) {
9384 if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9385 return JIM_ERR;
9386 }
9387 return JIM_OK;
9388 }
9389
9390 /* [incr] */
9391 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9392 Jim_Obj *const *argv)
9393 {
9394 jim_wide wideValue, increment = 1;
9395 Jim_Obj *intObjPtr;
9396
9397 if (argc != 2 && argc != 3) {
9398 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9399 return JIM_ERR;
9400 }
9401 if (argc == 3) {
9402 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9403 return JIM_ERR;
9404 }
9405 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9406 if (!intObjPtr) return JIM_ERR;
9407 if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9408 return JIM_ERR;
9409 if (Jim_IsShared(intObjPtr)) {
9410 intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9411 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9412 Jim_FreeNewObj(interp, intObjPtr);
9413 return JIM_ERR;
9414 }
9415 } else {
9416 Jim_SetWide(interp, intObjPtr, wideValue+increment);
9417 /* The following step is required in order to invalidate the
9418 * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9419 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9420 return JIM_ERR;
9421 }
9422 }
9423 Jim_SetResult(interp, intObjPtr);
9424 return JIM_OK;
9425 }
9426
9427 /* [while] */
9428 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9429 Jim_Obj *const *argv)
9430 {
9431 if (argc != 3) {
9432 Jim_WrongNumArgs(interp, 1, argv, "condition body");
9433 return JIM_ERR;
9434 }
9435 /* Try to run a specialized version of while if the expression
9436 * is in one of the following forms:
9437 *
9438 * $a < CONST, $a < $b
9439 * $a <= CONST, $a <= $b
9440 * $a > CONST, $a > $b
9441 * $a >= CONST, $a >= $b
9442 * $a != CONST, $a != $b
9443 * $a == CONST, $a == $b
9444 * $a
9445 * !$a
9446 * CONST
9447 */
9448
9449 #ifdef JIM_OPTIMIZATION
9450 {
9451 ExprByteCode *expr;
9452 Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9453 int exprLen, retval;
9454
9455 /* STEP 1 -- Check if there are the conditions to run the specialized
9456 * version of while */
9457
9458 if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9459 if (expr->len <= 0 || expr->len > 3) goto noopt;
9460 switch(expr->len) {
9461 case 1:
9462 if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9463 expr->opcode[0] != JIM_EXPROP_NUMBER)
9464 goto noopt;
9465 break;
9466 case 2:
9467 if (expr->opcode[1] != JIM_EXPROP_NOT ||
9468 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9469 goto noopt;
9470 break;
9471 case 3:
9472 if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9473 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9474 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9475 goto noopt;
9476 switch(expr->opcode[2]) {
9477 case JIM_EXPROP_LT:
9478 case JIM_EXPROP_LTE:
9479 case JIM_EXPROP_GT:
9480 case JIM_EXPROP_GTE:
9481 case JIM_EXPROP_NUMEQ:
9482 case JIM_EXPROP_NUMNE:
9483 /* nothing to do */
9484 break;
9485 default:
9486 goto noopt;
9487 }
9488 break;
9489 default:
9490 Jim_Panic(interp,
9491 "Unexpected default reached in Jim_WhileCoreCommand()");
9492 break;
9493 }
9494
9495 /* STEP 2 -- conditions meet. Initialization. Take different
9496 * branches for different expression lengths. */
9497 exprLen = expr->len;
9498
9499 if (exprLen == 1) {
9500 jim_wide wideValue;
9501
9502 if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9503 varAObjPtr = expr->obj[0];
9504 Jim_IncrRefCount(varAObjPtr);
9505 } else {
9506 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9507 goto noopt;
9508 }
9509 while (1) {
9510 if (varAObjPtr) {
9511 if (!(objPtr =
9512 Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9513 Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9514 {
9515 Jim_DecrRefCount(interp, varAObjPtr);
9516 goto noopt;
9517 }
9518 }
9519 if (!wideValue) break;
9520 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9521 switch(retval) {
9522 case JIM_BREAK:
9523 if (varAObjPtr)
9524 Jim_DecrRefCount(interp, varAObjPtr);
9525 goto out;
9526 break;
9527 case JIM_CONTINUE:
9528 continue;
9529 break;
9530 default:
9531 if (varAObjPtr)
9532 Jim_DecrRefCount(interp, varAObjPtr);
9533 return retval;
9534 }
9535 }
9536 }
9537 if (varAObjPtr)
9538 Jim_DecrRefCount(interp, varAObjPtr);
9539 } else if (exprLen == 3) {
9540 jim_wide wideValueA, wideValueB, cmpRes = 0;
9541 int cmpType = expr->opcode[2];
9542
9543 varAObjPtr = expr->obj[0];
9544 Jim_IncrRefCount(varAObjPtr);
9545 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9546 varBObjPtr = expr->obj[1];
9547 Jim_IncrRefCount(varBObjPtr);
9548 } else {
9549 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9550 goto noopt;
9551 }
9552 while (1) {
9553 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9554 Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9555 {
9556 Jim_DecrRefCount(interp, varAObjPtr);
9557 if (varBObjPtr)
9558 Jim_DecrRefCount(interp, varBObjPtr);
9559 goto noopt;
9560 }
9561 if (varBObjPtr) {
9562 if (!(objPtr =
9563 Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9564 Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9565 {
9566 Jim_DecrRefCount(interp, varAObjPtr);
9567 if (varBObjPtr)
9568 Jim_DecrRefCount(interp, varBObjPtr);
9569 goto noopt;
9570 }
9571 }
9572 switch(cmpType) {
9573 case JIM_EXPROP_LT:
9574 cmpRes = wideValueA < wideValueB; break;
9575 case JIM_EXPROP_LTE:
9576 cmpRes = wideValueA <= wideValueB; break;
9577 case JIM_EXPROP_GT:
9578 cmpRes = wideValueA > wideValueB; break;
9579 case JIM_EXPROP_GTE:
9580 cmpRes = wideValueA >= wideValueB; break;
9581 case JIM_EXPROP_NUMEQ:
9582 cmpRes = wideValueA == wideValueB; break;
9583 case JIM_EXPROP_NUMNE:
9584 cmpRes = wideValueA != wideValueB; break;
9585 }
9586 if (!cmpRes) break;
9587 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9588 switch(retval) {
9589 case JIM_BREAK:
9590 Jim_DecrRefCount(interp, varAObjPtr);
9591 if (varBObjPtr)
9592 Jim_DecrRefCount(interp, varBObjPtr);
9593 goto out;
9594 break;
9595 case JIM_CONTINUE:
9596 continue;
9597 break;
9598 default:
9599 Jim_DecrRefCount(interp, varAObjPtr);
9600 if (varBObjPtr)
9601 Jim_DecrRefCount(interp, varBObjPtr);
9602 return retval;
9603 }
9604 }
9605 }
9606 Jim_DecrRefCount(interp, varAObjPtr);
9607 if (varBObjPtr)
9608 Jim_DecrRefCount(interp, varBObjPtr);
9609 } else {
9610 /* TODO: case for len == 2 */
9611 goto noopt;
9612 }
9613 Jim_SetEmptyResult(interp);
9614 return JIM_OK;
9615 }
9616 noopt:
9617 #endif
9618
9619 /* The general purpose implementation of while starts here */
9620 while (1) {
9621 int boolean, retval;
9622
9623 if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9624 &boolean)) != JIM_OK)
9625 return retval;
9626 if (!boolean) break;
9627 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9628 switch(retval) {
9629 case JIM_BREAK:
9630 goto out;
9631 break;
9632 case JIM_CONTINUE:
9633 continue;
9634 break;
9635 default:
9636 return retval;
9637 }
9638 }
9639 }
9640 out:
9641 Jim_SetEmptyResult(interp);
9642 return JIM_OK;
9643 }
9644
9645 /* [for] */
9646 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9647 Jim_Obj *const *argv)
9648 {
9649 int retval;
9650
9651 if (argc != 5) {
9652 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9653 return JIM_ERR;
9654 }
9655 /* Check if the for is on the form:
9656 * for {set i CONST} {$i < CONST} {incr i}
9657 * for {set i CONST} {$i < $j} {incr i}
9658 * for {set i CONST} {$i <= CONST} {incr i}
9659 * for {set i CONST} {$i <= $j} {incr i}
9660 * XXX: NOTE: if variable traces are implemented, this optimization
9661 * need to be modified to check for the proc epoch at every variable
9662 * update. */
9663 #ifdef JIM_OPTIMIZATION
9664 {
9665 ScriptObj *initScript, *incrScript;
9666 ExprByteCode *expr;
9667 jim_wide start, stop, currentVal;
9668 unsigned jim_wide procEpoch = interp->procEpoch;
9669 Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9670 int cmpType;
9671 struct Jim_Cmd *cmdPtr;
9672
9673 /* Do it only if there aren't shared arguments */
9674 if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9675 goto evalstart;
9676 initScript = Jim_GetScript(interp, argv[1]);
9677 expr = Jim_GetExpression(interp, argv[2]);
9678 incrScript = Jim_GetScript(interp, argv[3]);
9679
9680 /* Ensure proper lengths to start */
9681 if (initScript->len != 6) goto evalstart;
9682 if (incrScript->len != 4) goto evalstart;
9683 if (expr->len != 3) goto evalstart;
9684 /* Ensure proper token types. */
9685 if (initScript->token[2].type != JIM_TT_ESC ||
9686 initScript->token[4].type != JIM_TT_ESC ||
9687 incrScript->token[2].type != JIM_TT_ESC ||
9688 expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9689 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9690 expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9691 (expr->opcode[2] != JIM_EXPROP_LT &&
9692 expr->opcode[2] != JIM_EXPROP_LTE))
9693 goto evalstart;
9694 cmpType = expr->opcode[2];
9695 /* Initialization command must be [set] */
9696 cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9697 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9698 goto evalstart;
9699 /* Update command must be incr */
9700 cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9701 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9702 goto evalstart;
9703 /* set, incr, expression must be about the same variable */
9704 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9705 incrScript->token[2].objPtr, 0))
9706 goto evalstart;
9707 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9708 expr->obj[0], 0))
9709 goto evalstart;
9710 /* Check that the initialization and comparison are valid integers */
9711 if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9712 goto evalstart;
9713 if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9714 Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9715 {
9716 goto evalstart;
9717 }
9718
9719 /* Initialization */
9720 varNamePtr = expr->obj[0];
9721 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9722 stopVarNamePtr = expr->obj[1];
9723 Jim_IncrRefCount(stopVarNamePtr);
9724 }
9725 Jim_IncrRefCount(varNamePtr);
9726
9727 /* --- OPTIMIZED FOR --- */
9728 /* Start to loop */
9729 objPtr = Jim_NewIntObj(interp, start);
9730 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9731 Jim_DecrRefCount(interp, varNamePtr);
9732 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9733 Jim_FreeNewObj(interp, objPtr);
9734 goto evalstart;
9735 }
9736 while (1) {
9737 /* === Check condition === */
9738 /* Common code: */
9739 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9740 if (objPtr == NULL ||
9741 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9742 {
9743 Jim_DecrRefCount(interp, varNamePtr);
9744 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9745 goto testcond;
9746 }
9747 /* Immediate or Variable? get the 'stop' value if the latter. */
9748 if (stopVarNamePtr) {
9749 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9750 if (objPtr == NULL ||
9751 Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9752 {
9753 Jim_DecrRefCount(interp, varNamePtr);
9754 Jim_DecrRefCount(interp, stopVarNamePtr);
9755 goto testcond;
9756 }
9757 }
9758 if (cmpType == JIM_EXPROP_LT) {
9759 if (currentVal >= stop) break;
9760 } else {
9761 if (currentVal > stop) break;
9762 }
9763 /* Eval body */
9764 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9765 switch(retval) {
9766 case JIM_BREAK:
9767 if (stopVarNamePtr)
9768 Jim_DecrRefCount(interp, stopVarNamePtr);
9769 Jim_DecrRefCount(interp, varNamePtr);
9770 goto out;
9771 case JIM_CONTINUE:
9772 /* nothing to do */
9773 break;
9774 default:
9775 if (stopVarNamePtr)
9776 Jim_DecrRefCount(interp, stopVarNamePtr);
9777 Jim_DecrRefCount(interp, varNamePtr);
9778 return retval;
9779 }
9780 }
9781 /* If there was a change in procedures/command continue
9782 * with the usual [for] command implementation */
9783 if (procEpoch != interp->procEpoch) {
9784 if (stopVarNamePtr)
9785 Jim_DecrRefCount(interp, stopVarNamePtr);
9786 Jim_DecrRefCount(interp, varNamePtr);
9787 goto evalnext;
9788 }
9789 /* Increment */
9790 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9791 if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9792 objPtr->internalRep.wideValue ++;
9793 Jim_InvalidateStringRep(objPtr);
9794 } else {
9795 Jim_Obj *auxObjPtr;
9796
9797 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9798 if (stopVarNamePtr)
9799 Jim_DecrRefCount(interp, stopVarNamePtr);
9800 Jim_DecrRefCount(interp, varNamePtr);
9801 goto evalnext;
9802 }
9803 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9804 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9805 if (stopVarNamePtr)
9806 Jim_DecrRefCount(interp, stopVarNamePtr);
9807 Jim_DecrRefCount(interp, varNamePtr);
9808 Jim_FreeNewObj(interp, auxObjPtr);
9809 goto evalnext;
9810 }
9811 }
9812 }
9813 if (stopVarNamePtr)
9814 Jim_DecrRefCount(interp, stopVarNamePtr);
9815 Jim_DecrRefCount(interp, varNamePtr);
9816 Jim_SetEmptyResult(interp);
9817 return JIM_OK;
9818 }
9819 #endif
9820 evalstart:
9821 /* Eval start */
9822 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9823 return retval;
9824 while (1) {
9825 int boolean;
9826 testcond:
9827 /* Test the condition */
9828 if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9829 != JIM_OK)
9830 return retval;
9831 if (!boolean) break;
9832 /* Eval body */
9833 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9834 switch(retval) {
9835 case JIM_BREAK:
9836 goto out;
9837 break;
9838 case JIM_CONTINUE:
9839 /* Nothing to do */
9840 break;
9841 default:
9842 return retval;
9843 }
9844 }
9845 evalnext:
9846 /* Eval next */
9847 if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9848 switch(retval) {
9849 case JIM_BREAK:
9850 goto out;
9851 break;
9852 case JIM_CONTINUE:
9853 continue;
9854 break;
9855 default:
9856 return retval;
9857 }
9858 }
9859 }
9860 out:
9861 Jim_SetEmptyResult(interp);
9862 return JIM_OK;
9863 }
9864
9865 /* foreach + lmap implementation. */
9866 static int JimForeachMapHelper(Jim_Interp *interp, int argc,
9867 Jim_Obj *const *argv, int doMap)
9868 {
9869 int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9870 int nbrOfLoops = 0;
9871 Jim_Obj *emptyStr, *script, *mapRes = NULL;
9872
9873 if (argc < 4 || argc % 2 != 0) {
9874 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9875 return JIM_ERR;
9876 }
9877 if (doMap) {
9878 mapRes = Jim_NewListObj(interp, NULL, 0);
9879 Jim_IncrRefCount(mapRes);
9880 }
9881 emptyStr = Jim_NewEmptyStringObj(interp);
9882 Jim_IncrRefCount(emptyStr);
9883 script = argv[argc-1]; /* Last argument is a script */
9884 nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
9885 listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9886 listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9887 /* Initialize iterators and remember max nbr elements each list */
9888 memset(listsIdx, 0, nbrOfLists * sizeof(int));
9889 /* Remember lengths of all lists and calculate how much rounds to loop */
9890 for (i=0; i < nbrOfLists*2; i += 2) {
9891 div_t cnt;
9892 int count;
9893 Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9894 Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9895 if (listsEnd[i] == 0) {
9896 Jim_SetResultString(interp, "foreach varlist is empty", -1);
9897 goto err;
9898 }
9899 cnt = div(listsEnd[i+1], listsEnd[i]);
9900 count = cnt.quot + (cnt.rem ? 1 : 0);
9901 if (count > nbrOfLoops)
9902 nbrOfLoops = count;
9903 }
9904 for (; nbrOfLoops-- > 0; ) {
9905 for (i=0; i < nbrOfLists; ++i) {
9906 int varIdx = 0, var = i * 2;
9907 while (varIdx < listsEnd[var]) {
9908 Jim_Obj *varName, *ele;
9909 int lst = i * 2 + 1;
9910 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9911 != JIM_OK)
9912 goto err;
9913 if (listsIdx[i] < listsEnd[lst]) {
9914 if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9915 != JIM_OK)
9916 goto err;
9917 if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9918 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9919 goto err;
9920 }
9921 ++listsIdx[i]; /* Remember next iterator of current list */
9922 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9923 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9924 goto err;
9925 }
9926 ++varIdx; /* Next variable */
9927 }
9928 }
9929 switch (result = Jim_EvalObj(interp, script)) {
9930 case JIM_OK:
9931 if (doMap)
9932 Jim_ListAppendElement(interp, mapRes, interp->result);
9933 break;
9934 case JIM_CONTINUE:
9935 break;
9936 case JIM_BREAK:
9937 goto out;
9938 break;
9939 default:
9940 goto err;
9941 }
9942 }
9943 out:
9944 result = JIM_OK;
9945 if (doMap)
9946 Jim_SetResult(interp, mapRes);
9947 else
9948 Jim_SetEmptyResult(interp);
9949 err:
9950 if (doMap)
9951 Jim_DecrRefCount(interp, mapRes);
9952 Jim_DecrRefCount(interp, emptyStr);
9953 Jim_Free(listsIdx);
9954 Jim_Free(listsEnd);
9955 return result;
9956 }
9957
9958 /* [foreach] */
9959 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
9960 Jim_Obj *const *argv)
9961 {
9962 return JimForeachMapHelper(interp, argc, argv, 0);
9963 }
9964
9965 /* [lmap] */
9966 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
9967 Jim_Obj *const *argv)
9968 {
9969 return JimForeachMapHelper(interp, argc, argv, 1);
9970 }
9971
9972 /* [if] */
9973 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
9974 Jim_Obj *const *argv)
9975 {
9976 int boolean, retval, current = 1, falsebody = 0;
9977 if (argc >= 3) {
9978 while (1) {
9979 /* Far not enough arguments given! */
9980 if (current >= argc) goto err;
9981 if ((retval = Jim_GetBoolFromExpr(interp,
9982 argv[current++], &boolean))
9983 != JIM_OK)
9984 return retval;
9985 /* There lacks something, isn't it? */
9986 if (current >= argc) goto err;
9987 if (Jim_CompareStringImmediate(interp, argv[current],
9988 "then")) current++;
9989 /* Tsk tsk, no then-clause? */
9990 if (current >= argc) goto err;
9991 if (boolean)
9992 return Jim_EvalObj(interp, argv[current]);
9993 /* Ok: no else-clause follows */
9994 if (++current >= argc) return JIM_OK;
9995 falsebody = current++;
9996 if (Jim_CompareStringImmediate(interp, argv[falsebody],
9997 "else")) {
9998 /* IIICKS - else-clause isn't last cmd? */
9999 if (current != argc-1) goto err;
10000 return Jim_EvalObj(interp, argv[current]);
10001 } else if (Jim_CompareStringImmediate(interp,
10002 argv[falsebody], "elseif"))
10003 /* Ok: elseif follows meaning all the stuff
10004 * again (how boring...) */
10005 continue;
10006 /* OOPS - else-clause is not last cmd?*/
10007 else if (falsebody != argc-1)
10008 goto err;
10009 return Jim_EvalObj(interp, argv[falsebody]);
10010 }
10011 return JIM_OK;
10012 }
10013 err:
10014 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10015 return JIM_ERR;
10016 }
10017
10018 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10019
10020 /* [switch] */
10021 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
10022 Jim_Obj *const *argv)
10023 {
10024 int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10025 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10026 Jim_Obj *script = 0;
10027 if (argc < 3) goto wrongnumargs;
10028 for (opt=1; opt < argc; ++opt) {
10029 const char *option = Jim_GetString(argv[opt], 0);
10030 if (*option != '-') break;
10031 else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10032 else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10033 else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10034 else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10035 else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10036 if ((argc - opt) < 2) goto wrongnumargs;
10037 command = argv[++opt];
10038 } else {
10039 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10040 Jim_AppendStrings(interp, Jim_GetResult(interp),
10041 "bad option \"", option, "\": must be -exact, -glob, "
10042 "-regexp, -command procname or --", 0);
10043 goto err;
10044 }
10045 if ((argc - opt) < 2) goto wrongnumargs;
10046 }
10047 strObj = argv[opt++];
10048 patCount = argc - opt;
10049 if (patCount == 1) {
10050 Jim_Obj **vector;
10051 JimListGetElements(interp, argv[opt], &patCount, &vector);
10052 caseList = vector;
10053 } else
10054 caseList = &argv[opt];
10055 if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10056 for (i=0; script == 0 && i < patCount; i += 2) {
10057 Jim_Obj *patObj = caseList[i];
10058 if (!Jim_CompareStringImmediate(interp, patObj, "default")
10059 || i < (patCount-2)) {
10060 switch (matchOpt) {
10061 case SWITCH_EXACT:
10062 if (Jim_StringEqObj(strObj, patObj, 0))
10063 script = caseList[i+1];
10064 break;
10065 case SWITCH_GLOB:
10066 if (Jim_StringMatchObj(patObj, strObj, 0))
10067 script = caseList[i+1];
10068 break;
10069 case SWITCH_RE:
10070 command = Jim_NewStringObj(interp, "regexp", -1);
10071 /* Fall thru intentionally */
10072 case SWITCH_CMD: {
10073 Jim_Obj *parms[] = {command, patObj, strObj};
10074 int rc = Jim_EvalObjVector(interp, 3, parms);
10075 long matching;
10076 /* After the execution of a command we need to
10077 * make sure to reconvert the object into a list
10078 * again. Only for the single-list style [switch]. */
10079 if (argc-opt == 1) {
10080 Jim_Obj **vector;
10081 JimListGetElements(interp, argv[opt], &patCount,
10082 &vector);
10083 caseList = vector;
10084 }
10085 /* command is here already decref'd */
10086 if (rc != JIM_OK) {
10087 retcode = rc;
10088 goto err;
10089 }
10090 rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10091 if (rc != JIM_OK) {
10092 retcode = rc;
10093 goto err;
10094 }
10095 if (matching)
10096 script = caseList[i+1];
10097 break;
10098 }
10099 default:
10100 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10101 Jim_AppendStrings(interp, Jim_GetResult(interp),
10102 "internal error: no such option implemented", 0);
10103 goto err;
10104 }
10105 } else {
10106 script = caseList[i+1];
10107 }
10108 }
10109 for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10110 i += 2)
10111 script = caseList[i+1];
10112 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10113 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10114 Jim_AppendStrings(interp, Jim_GetResult(interp),
10115 "no body specified for pattern \"",
10116 Jim_GetString(caseList[i-2], 0), "\"", 0);
10117 goto err;
10118 }
10119 retcode = JIM_OK;
10120 Jim_SetEmptyResult(interp);
10121 if (script != 0)
10122 retcode = Jim_EvalObj(interp, script);
10123 return retcode;
10124 wrongnumargs:
10125 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10126 "pattern body ... ?default body? or "
10127 "{pattern body ?pattern body ...?}");
10128 err:
10129 return retcode;
10130 }
10131
10132 /* [list] */
10133 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
10134 Jim_Obj *const *argv)
10135 {
10136 Jim_Obj *listObjPtr;
10137
10138 listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10139 Jim_SetResult(interp, listObjPtr);
10140 return JIM_OK;
10141 }
10142
10143 /* [lindex] */
10144 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
10145 Jim_Obj *const *argv)
10146 {
10147 Jim_Obj *objPtr, *listObjPtr;
10148 int i;
10149 int index;
10150
10151 if (argc < 3) {
10152 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10153 return JIM_ERR;
10154 }
10155 objPtr = argv[1];
10156 Jim_IncrRefCount(objPtr);
10157 for (i = 2; i < argc; i++) {
10158 listObjPtr = objPtr;
10159 if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10160 Jim_DecrRefCount(interp, listObjPtr);
10161 return JIM_ERR;
10162 }
10163 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10164 JIM_NONE) != JIM_OK) {
10165 /* Returns an empty object if the index
10166 * is out of range. */
10167 Jim_DecrRefCount(interp, listObjPtr);
10168 Jim_SetEmptyResult(interp);
10169 return JIM_OK;
10170 }
10171 Jim_IncrRefCount(objPtr);
10172 Jim_DecrRefCount(interp, listObjPtr);
10173 }
10174 Jim_SetResult(interp, objPtr);
10175 Jim_DecrRefCount(interp, objPtr);
10176 return JIM_OK;
10177 }
10178
10179 /* [llength] */
10180 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
10181 Jim_Obj *const *argv)
10182 {
10183 int len;
10184
10185 if (argc != 2) {
10186 Jim_WrongNumArgs(interp, 1, argv, "list");
10187 return JIM_ERR;
10188 }
10189 Jim_ListLength(interp, argv[1], &len);
10190 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10191 return JIM_OK;
10192 }
10193
10194 /* [lappend] */
10195 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
10196 Jim_Obj *const *argv)
10197 {
10198 Jim_Obj *listObjPtr;
10199 int shared, i;
10200
10201 if (argc < 2) {
10202 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10203 return JIM_ERR;
10204 }
10205 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10206 if (!listObjPtr) {
10207 /* Create the list if it does not exists */
10208 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10209 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10210 Jim_FreeNewObj(interp, listObjPtr);
10211 return JIM_ERR;
10212 }
10213 }
10214 shared = Jim_IsShared(listObjPtr);
10215 if (shared)
10216 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10217 for (i = 2; i < argc; i++)
10218 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10219 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10220 if (shared)
10221 Jim_FreeNewObj(interp, listObjPtr);
10222 return JIM_ERR;
10223 }
10224 Jim_SetResult(interp, listObjPtr);
10225 return JIM_OK;
10226 }
10227
10228 /* [linsert] */
10229 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10230 Jim_Obj *const *argv)
10231 {
10232 int index, len;
10233 Jim_Obj *listPtr;
10234
10235 if (argc < 4) {
10236 Jim_WrongNumArgs(interp, 1, argv, "list index element "
10237 "?element ...?");
10238 return JIM_ERR;
10239 }
10240 listPtr = argv[1];
10241 if (Jim_IsShared(listPtr))
10242 listPtr = Jim_DuplicateObj(interp, listPtr);
10243 if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10244 goto err;
10245 Jim_ListLength(interp, listPtr, &len);
10246 if (index >= len)
10247 index = len;
10248 else if (index < 0)
10249 index = len + index + 1;
10250 Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10251 Jim_SetResult(interp, listPtr);
10252 return JIM_OK;
10253 err:
10254 if (listPtr != argv[1]) {
10255 Jim_FreeNewObj(interp, listPtr);
10256 }
10257 return JIM_ERR;
10258 }
10259
10260 /* [lset] */
10261 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10262 Jim_Obj *const *argv)
10263 {
10264 if (argc < 3) {
10265 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10266 return JIM_ERR;
10267 } else if (argc == 3) {
10268 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10269 return JIM_ERR;
10270 Jim_SetResult(interp, argv[2]);
10271 return JIM_OK;
10272 }
10273 if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10274 == JIM_ERR) return JIM_ERR;
10275 return JIM_OK;
10276 }
10277
10278 /* [lsort] */
10279 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10280 {
10281 const char *options[] = {
10282 "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10283 };
10284 enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10285 Jim_Obj *resObj;
10286 int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10287 int decreasing = 0;
10288
10289 if (argc < 2) {
10290 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10291 return JIM_ERR;
10292 }
10293 for (i = 1; i < (argc-1); i++) {
10294 int option;
10295
10296 if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10297 != JIM_OK)
10298 return JIM_ERR;
10299 switch(option) {
10300 case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10301 case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10302 case OPT_INCREASING: decreasing = 0; break;
10303 case OPT_DECREASING: decreasing = 1; break;
10304 }
10305 }
10306 if (decreasing) {
10307 switch(lsortType) {
10308 case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10309 case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10310 }
10311 }
10312 resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10313 ListSortElements(interp, resObj, lsortType);
10314 Jim_SetResult(interp, resObj);
10315 return JIM_OK;
10316 }
10317
10318 /* [append] */
10319 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10320 Jim_Obj *const *argv)
10321 {
10322 Jim_Obj *stringObjPtr;
10323 int shared, i;
10324
10325 if (argc < 2) {
10326 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10327 return JIM_ERR;
10328 }
10329 if (argc == 2) {
10330 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10331 if (!stringObjPtr) return JIM_ERR;
10332 } else {
10333 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10334 if (!stringObjPtr) {
10335 /* Create the string if it does not exists */
10336 stringObjPtr = Jim_NewEmptyStringObj(interp);
10337 if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10338 != JIM_OK) {
10339 Jim_FreeNewObj(interp, stringObjPtr);
10340 return JIM_ERR;
10341 }
10342 }
10343 }
10344 shared = Jim_IsShared(stringObjPtr);
10345 if (shared)
10346 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10347 for (i = 2; i < argc; i++)
10348 Jim_AppendObj(interp, stringObjPtr, argv[i]);
10349 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10350 if (shared)
10351 Jim_FreeNewObj(interp, stringObjPtr);
10352 return JIM_ERR;
10353 }
10354 Jim_SetResult(interp, stringObjPtr);
10355 return JIM_OK;
10356 }
10357
10358 /* [debug] */
10359 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10360 Jim_Obj *const *argv)
10361 {
10362 const char *options[] = {
10363 "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10364 "exprbc",
10365 NULL
10366 };
10367 enum {
10368 OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10369 OPT_EXPRLEN, OPT_EXPRBC
10370 };
10371 int option;
10372
10373 if (argc < 2) {
10374 Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10375 return JIM_ERR;
10376 }
10377 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10378 JIM_ERRMSG) != JIM_OK)
10379 return JIM_ERR;
10380 if (option == OPT_REFCOUNT) {
10381 if (argc != 3) {
10382 Jim_WrongNumArgs(interp, 2, argv, "object");
10383 return JIM_ERR;
10384 }
10385 Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10386 return JIM_OK;
10387 } else if (option == OPT_OBJCOUNT) {
10388 int freeobj = 0, liveobj = 0;
10389 char buf[256];
10390 Jim_Obj *objPtr;
10391
10392 if (argc != 2) {
10393 Jim_WrongNumArgs(interp, 2, argv, "");
10394 return JIM_ERR;
10395 }
10396 /* Count the number of free objects. */
10397 objPtr = interp->freeList;
10398 while (objPtr) {
10399 freeobj++;
10400 objPtr = objPtr->nextObjPtr;
10401 }
10402 /* Count the number of live objects. */
10403 objPtr = interp->liveList;
10404 while (objPtr) {
10405 liveobj++;
10406 objPtr = objPtr->nextObjPtr;
10407 }
10408 /* Set the result string and return. */
10409 sprintf(buf, "free %d used %d", freeobj, liveobj);
10410 Jim_SetResultString(interp, buf, -1);
10411 return JIM_OK;
10412 } else if (option == OPT_OBJECTS) {
10413 Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10414 /* Count the number of live objects. */
10415 objPtr = interp->liveList;
10416 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10417 while (objPtr) {
10418 char buf[128];
10419 const char *type = objPtr->typePtr ?
10420 objPtr->typePtr->name : "";
10421 subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10422 sprintf(buf, "%p", objPtr);
10423 Jim_ListAppendElement(interp, subListObjPtr,
10424 Jim_NewStringObj(interp, buf, -1));
10425 Jim_ListAppendElement(interp, subListObjPtr,
10426 Jim_NewStringObj(interp, type, -1));
10427 Jim_ListAppendElement(interp, subListObjPtr,
10428 Jim_NewIntObj(interp, objPtr->refCount));
10429 Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10430 Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10431 objPtr = objPtr->nextObjPtr;
10432 }
10433 Jim_SetResult(interp, listObjPtr);
10434 return JIM_OK;
10435 } else if (option == OPT_INVSTR) {
10436 Jim_Obj *objPtr;
10437
10438 if (argc != 3) {
10439 Jim_WrongNumArgs(interp, 2, argv, "object");
10440 return JIM_ERR;
10441 }
10442 objPtr = argv[2];
10443 if (objPtr->typePtr != NULL)
10444 Jim_InvalidateStringRep(objPtr);
10445 Jim_SetEmptyResult(interp);
10446 return JIM_OK;
10447 } else if (option == OPT_SCRIPTLEN) {
10448 ScriptObj *script;
10449 if (argc != 3) {
10450 Jim_WrongNumArgs(interp, 2, argv, "script");
10451 return JIM_ERR;
10452 }
10453 script = Jim_GetScript(interp, argv[2]);
10454 Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10455 return JIM_OK;
10456 } else if (option == OPT_EXPRLEN) {
10457 ExprByteCode *expr;
10458 if (argc != 3) {
10459 Jim_WrongNumArgs(interp, 2, argv, "expression");
10460 return JIM_ERR;
10461 }
10462 expr = Jim_GetExpression(interp, argv[2]);
10463 if (expr == NULL)
10464 return JIM_ERR;
10465 Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10466 return JIM_OK;
10467 } else if (option == OPT_EXPRBC) {
10468 Jim_Obj *objPtr;
10469 ExprByteCode *expr;
10470 int i;
10471
10472 if (argc != 3) {
10473 Jim_WrongNumArgs(interp, 2, argv, "expression");
10474 return JIM_ERR;
10475 }
10476 expr = Jim_GetExpression(interp, argv[2]);
10477 if (expr == NULL)
10478 return JIM_ERR;
10479 objPtr = Jim_NewListObj(interp, NULL, 0);
10480 for (i = 0; i < expr->len; i++) {
10481 const char *type;
10482 Jim_ExprOperator *op;
10483
10484 switch(expr->opcode[i]) {
10485 case JIM_EXPROP_NUMBER: type = "number"; break;
10486 case JIM_EXPROP_COMMAND: type = "command"; break;
10487 case JIM_EXPROP_VARIABLE: type = "variable"; break;
10488 case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10489 case JIM_EXPROP_SUBST: type = "subst"; break;
10490 case JIM_EXPROP_STRING: type = "string"; break;
10491 default:
10492 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10493 if (op == NULL) {
10494 type = "private";
10495 } else {
10496 type = "operator";
10497 }
10498 break;
10499 }
10500 Jim_ListAppendElement(interp, objPtr,
10501 Jim_NewStringObj(interp, type, -1));
10502 Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10503 }
10504 Jim_SetResult(interp, objPtr);
10505 return JIM_OK;
10506 } else {
10507 Jim_SetResultString(interp,
10508 "bad option. Valid options are refcount, "
10509 "objcount, objects, invstr", -1);
10510 return JIM_ERR;
10511 }
10512 return JIM_OK; /* unreached */
10513 }
10514
10515 /* [eval] */
10516 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10517 Jim_Obj *const *argv)
10518 {
10519 if (argc == 2) {
10520 return Jim_EvalObj(interp, argv[1]);
10521 } else if (argc > 2) {
10522 Jim_Obj *objPtr;
10523 int retcode;
10524
10525 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10526 Jim_IncrRefCount(objPtr);
10527 retcode = Jim_EvalObj(interp, objPtr);
10528 Jim_DecrRefCount(interp, objPtr);
10529 return retcode;
10530 } else {
10531 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10532 return JIM_ERR;
10533 }
10534 }
10535
10536 /* [uplevel] */
10537 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10538 Jim_Obj *const *argv)
10539 {
10540 if (argc >= 2) {
10541 int retcode, newLevel, oldLevel;
10542 Jim_CallFrame *savedCallFrame, *targetCallFrame;
10543 Jim_Obj *objPtr;
10544 const char *str;
10545
10546 /* Save the old callframe pointer */
10547 savedCallFrame = interp->framePtr;
10548
10549 /* Lookup the target frame pointer */
10550 str = Jim_GetString(argv[1], NULL);
10551 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10552 {
10553 if (Jim_GetCallFrameByLevel(interp, argv[1],
10554 &targetCallFrame,
10555 &newLevel) != JIM_OK)
10556 return JIM_ERR;
10557 argc--;
10558 argv++;
10559 } else {
10560 if (Jim_GetCallFrameByLevel(interp, NULL,
10561 &targetCallFrame,
10562 &newLevel) != JIM_OK)
10563 return JIM_ERR;
10564 }
10565 if (argc < 2) {
10566 argc++;
10567 argv--;
10568 Jim_WrongNumArgs(interp, 1, argv,
10569 "?level? command ?arg ...?");
10570 return JIM_ERR;
10571 }
10572 /* Eval the code in the target callframe. */
10573 interp->framePtr = targetCallFrame;
10574 oldLevel = interp->numLevels;
10575 interp->numLevels = newLevel;
10576 if (argc == 2) {
10577 retcode = Jim_EvalObj(interp, argv[1]);
10578 } else {
10579 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10580 Jim_IncrRefCount(objPtr);
10581 retcode = Jim_EvalObj(interp, objPtr);
10582 Jim_DecrRefCount(interp, objPtr);
10583 }
10584 interp->numLevels = oldLevel;
10585 interp->framePtr = savedCallFrame;
10586 return retcode;
10587 } else {
10588 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10589 return JIM_ERR;
10590 }
10591 }
10592
10593 /* [expr] */
10594 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10595 Jim_Obj *const *argv)
10596 {
10597 Jim_Obj *exprResultPtr;
10598 int retcode;
10599
10600 if (argc == 2) {
10601 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10602 } else if (argc > 2) {
10603 Jim_Obj *objPtr;
10604
10605 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10606 Jim_IncrRefCount(objPtr);
10607 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10608 Jim_DecrRefCount(interp, objPtr);
10609 } else {
10610 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10611 return JIM_ERR;
10612 }
10613 if (retcode != JIM_OK) return retcode;
10614 Jim_SetResult(interp, exprResultPtr);
10615 Jim_DecrRefCount(interp, exprResultPtr);
10616 return JIM_OK;
10617 }
10618
10619 /* [break] */
10620 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10621 Jim_Obj *const *argv)
10622 {
10623 if (argc != 1) {
10624 Jim_WrongNumArgs(interp, 1, argv, "");
10625 return JIM_ERR;
10626 }
10627 return JIM_BREAK;
10628 }
10629
10630 /* [continue] */
10631 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10632 Jim_Obj *const *argv)
10633 {
10634 if (argc != 1) {
10635 Jim_WrongNumArgs(interp, 1, argv, "");
10636 return JIM_ERR;
10637 }
10638 return JIM_CONTINUE;
10639 }
10640
10641 /* [return] */
10642 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10643 Jim_Obj *const *argv)
10644 {
10645 if (argc == 1) {
10646 return JIM_RETURN;
10647 } else if (argc == 2) {
10648 Jim_SetResult(interp, argv[1]);
10649 interp->returnCode = JIM_OK;
10650 return JIM_RETURN;
10651 } else if (argc == 3 || argc == 4) {
10652 int returnCode;
10653 if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10654 return JIM_ERR;
10655 interp->returnCode = returnCode;
10656 if (argc == 4)
10657 Jim_SetResult(interp, argv[3]);
10658 return JIM_RETURN;
10659 } else {
10660 Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10661 return JIM_ERR;
10662 }
10663 return JIM_RETURN; /* unreached */
10664 }
10665
10666 /* [tailcall] */
10667 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10668 Jim_Obj *const *argv)
10669 {
10670 Jim_Obj *objPtr;
10671
10672 objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10673 Jim_SetResult(interp, objPtr);
10674 return JIM_EVAL;
10675 }
10676
10677 /* [proc] */
10678 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
10679 Jim_Obj *const *argv)
10680 {
10681 int argListLen;
10682 int arityMin, arityMax;
10683
10684 if (argc != 4 && argc != 5) {
10685 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10686 return JIM_ERR;
10687 }
10688 Jim_ListLength(interp, argv[2], &argListLen);
10689 arityMin = arityMax = argListLen+1;
10690 if (argListLen) {
10691 const char *str;
10692 int len;
10693 Jim_Obj *lastArgPtr;
10694
10695 Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10696 str = Jim_GetString(lastArgPtr, &len);
10697 if (len == 4 && memcmp(str, "args", 4) == 0) {
10698 arityMin--;
10699 arityMax = -1;
10700 }
10701 }
10702 if (argc == 4) {
10703 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10704 argv[2], NULL, argv[3], arityMin, arityMax);
10705 } else {
10706 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10707 argv[2], argv[3], argv[4], arityMin, arityMax);
10708 }
10709 }
10710
10711 /* [concat] */
10712 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
10713 Jim_Obj *const *argv)
10714 {
10715 Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10716 return JIM_OK;
10717 }
10718
10719 /* [upvar] */
10720 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
10721 Jim_Obj *const *argv)
10722 {
10723 const char *str;
10724 int i;
10725 Jim_CallFrame *targetCallFrame;
10726
10727 /* Lookup the target frame pointer */
10728 str = Jim_GetString(argv[1], NULL);
10729 if (argc > 3 &&
10730 ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10731 {
10732 if (Jim_GetCallFrameByLevel(interp, argv[1],
10733 &targetCallFrame, NULL) != JIM_OK)
10734 return JIM_ERR;
10735 argc--;
10736 argv++;
10737 } else {
10738 if (Jim_GetCallFrameByLevel(interp, NULL,
10739 &targetCallFrame, NULL) != JIM_OK)
10740 return JIM_ERR;
10741 }
10742 /* Check for arity */
10743 if (argc < 3 || ((argc-1)%2) != 0) {
10744 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10745 return JIM_ERR;
10746 }
10747 /* Now... for every other/local couple: */
10748 for (i = 1; i < argc; i += 2) {
10749 if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10750 targetCallFrame) != JIM_OK) return JIM_ERR;
10751 }
10752 return JIM_OK;
10753 }
10754
10755 /* [global] */
10756 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
10757 Jim_Obj *const *argv)
10758 {
10759 int i;
10760
10761 if (argc < 2) {
10762 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10763 return JIM_ERR;
10764 }
10765 /* Link every var to the toplevel having the same name */
10766 if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10767 for (i = 1; i < argc; i++) {
10768 if (Jim_SetVariableLink(interp, argv[i], argv[i],
10769 interp->topFramePtr) != JIM_OK) return JIM_ERR;
10770 }
10771 return JIM_OK;
10772 }
10773
10774 /* does the [string map] operation. On error NULL is returned,
10775 * otherwise a new string object with the result, having refcount = 0,
10776 * is returned. */
10777 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10778 Jim_Obj *objPtr, int nocase)
10779 {
10780 int numMaps;
10781 const char **key, *str, *noMatchStart = NULL;
10782 Jim_Obj **value;
10783 int *keyLen, strLen, i;
10784 Jim_Obj *resultObjPtr;
10785
10786 Jim_ListLength(interp, mapListObjPtr, &numMaps);
10787 if (numMaps % 2) {
10788 Jim_SetResultString(interp,
10789 "list must contain an even number of elements", -1);
10790 return NULL;
10791 }
10792 /* Initialization */
10793 numMaps /= 2;
10794 key = Jim_Alloc(sizeof(char*)*numMaps);
10795 keyLen = Jim_Alloc(sizeof(int)*numMaps);
10796 value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10797 resultObjPtr = Jim_NewStringObj(interp, "", 0);
10798 for (i = 0; i < numMaps; i++) {
10799 Jim_Obj *eleObjPtr;
10800
10801 Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10802 key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10803 Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10804 value[i] = eleObjPtr;
10805 }
10806 str = Jim_GetString(objPtr, &strLen);
10807 /* Map it */
10808 while(strLen) {
10809 for (i = 0; i < numMaps; i++) {
10810 if (strLen >= keyLen[i] && keyLen[i]) {
10811 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10812 nocase))
10813 {
10814 if (noMatchStart) {
10815 Jim_AppendString(interp, resultObjPtr,
10816 noMatchStart, str-noMatchStart);
10817 noMatchStart = NULL;
10818 }
10819 Jim_AppendObj(interp, resultObjPtr, value[i]);
10820 str += keyLen[i];
10821 strLen -= keyLen[i];
10822 break;
10823 }
10824 }
10825 }
10826 if (i == numMaps) { /* no match */
10827 if (noMatchStart == NULL)
10828 noMatchStart = str;
10829 str ++;
10830 strLen --;
10831 }
10832 }
10833 if (noMatchStart) {
10834 Jim_AppendString(interp, resultObjPtr,
10835 noMatchStart, str-noMatchStart);
10836 }
10837 Jim_Free((void*)key);
10838 Jim_Free(keyLen);
10839 Jim_Free(value);
10840 return resultObjPtr;
10841 }
10842
10843 /* [string] */
10844 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
10845 Jim_Obj *const *argv)
10846 {
10847 int option;
10848 const char *options[] = {
10849 "length", "compare", "match", "equal", "range", "map", "repeat",
10850 "index", "first", "tolower", "toupper", NULL
10851 };
10852 enum {
10853 OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10854 OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10855 };
10856
10857 if (argc < 2) {
10858 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10859 return JIM_ERR;
10860 }
10861 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10862 JIM_ERRMSG) != JIM_OK)
10863 return JIM_ERR;
10864
10865 if (option == OPT_LENGTH) {
10866 int len;
10867
10868 if (argc != 3) {
10869 Jim_WrongNumArgs(interp, 2, argv, "string");
10870 return JIM_ERR;
10871 }
10872 Jim_GetString(argv[2], &len);
10873 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10874 return JIM_OK;
10875 } else if (option == OPT_COMPARE) {
10876 int nocase = 0;
10877 if ((argc != 4 && argc != 5) ||
10878 (argc == 5 && Jim_CompareStringImmediate(interp,
10879 argv[2], "-nocase") == 0)) {
10880 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10881 return JIM_ERR;
10882 }
10883 if (argc == 5) {
10884 nocase = 1;
10885 argv++;
10886 }
10887 Jim_SetResult(interp, Jim_NewIntObj(interp,
10888 Jim_StringCompareObj(argv[2],
10889 argv[3], nocase)));
10890 return JIM_OK;
10891 } else if (option == OPT_MATCH) {
10892 int nocase = 0;
10893 if ((argc != 4 && argc != 5) ||
10894 (argc == 5 && Jim_CompareStringImmediate(interp,
10895 argv[2], "-nocase") == 0)) {
10896 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10897 "string");
10898 return JIM_ERR;
10899 }
10900 if (argc == 5) {
10901 nocase = 1;
10902 argv++;
10903 }
10904 Jim_SetResult(interp,
10905 Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10906 argv[3], nocase)));
10907 return JIM_OK;
10908 } else if (option == OPT_EQUAL) {
10909 if (argc != 4) {
10910 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10911 return JIM_ERR;
10912 }
10913 Jim_SetResult(interp,
10914 Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10915 argv[3], 0)));
10916 return JIM_OK;
10917 } else if (option == OPT_RANGE) {
10918 Jim_Obj *objPtr;
10919
10920 if (argc != 5) {
10921 Jim_WrongNumArgs(interp, 2, argv, "string first last");
10922 return JIM_ERR;
10923 }
10924 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10925 if (objPtr == NULL)
10926 return JIM_ERR;
10927 Jim_SetResult(interp, objPtr);
10928 return JIM_OK;
10929 } else if (option == OPT_MAP) {
10930 int nocase = 0;
10931 Jim_Obj *objPtr;
10932
10933 if ((argc != 4 && argc != 5) ||
10934 (argc == 5 && Jim_CompareStringImmediate(interp,
10935 argv[2], "-nocase") == 0)) {
10936 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10937 "string");
10938 return JIM_ERR;
10939 }
10940 if (argc == 5) {
10941 nocase = 1;
10942 argv++;
10943 }
10944 objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10945 if (objPtr == NULL)
10946 return JIM_ERR;
10947 Jim_SetResult(interp, objPtr);
10948 return JIM_OK;
10949 } else if (option == OPT_REPEAT) {
10950 Jim_Obj *objPtr;
10951 jim_wide count;
10952
10953 if (argc != 4) {
10954 Jim_WrongNumArgs(interp, 2, argv, "string count");
10955 return JIM_ERR;
10956 }
10957 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
10958 return JIM_ERR;
10959 objPtr = Jim_NewStringObj(interp, "", 0);
10960 while (count--) {
10961 Jim_AppendObj(interp, objPtr, argv[2]);
10962 }
10963 Jim_SetResult(interp, objPtr);
10964 return JIM_OK;
10965 } else if (option == OPT_INDEX) {
10966 int index, len;
10967 const char *str;
10968
10969 if (argc != 4) {
10970 Jim_WrongNumArgs(interp, 2, argv, "string index");
10971 return JIM_ERR;
10972 }
10973 if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
10974 return JIM_ERR;
10975 str = Jim_GetString(argv[2], &len);
10976 if (index != INT_MIN && index != INT_MAX)
10977 index = JimRelToAbsIndex(len, index);
10978 if (index < 0 || index >= len) {
10979 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10980 return JIM_OK;
10981 } else {
10982 Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
10983 return JIM_OK;
10984 }
10985 } else if (option == OPT_FIRST) {
10986 int index = 0, l1, l2;
10987 const char *s1, *s2;
10988
10989 if (argc != 4 && argc != 5) {
10990 Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
10991 return JIM_ERR;
10992 }
10993 s1 = Jim_GetString(argv[2], &l1);
10994 s2 = Jim_GetString(argv[3], &l2);
10995 if (argc == 5) {
10996 if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
10997 return JIM_ERR;
10998 index = JimRelToAbsIndex(l2, index);
10999 }
11000 Jim_SetResult(interp, Jim_NewIntObj(interp,
11001 JimStringFirst(s1, l1, s2, l2, index)));
11002 return JIM_OK;
11003 } else if (option == OPT_TOLOWER) {
11004 if (argc != 3) {
11005 Jim_WrongNumArgs(interp, 2, argv, "string");
11006 return JIM_ERR;
11007 }
11008 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11009 } else if (option == OPT_TOUPPER) {
11010 if (argc != 3) {
11011 Jim_WrongNumArgs(interp, 2, argv, "string");
11012 return JIM_ERR;
11013 }
11014 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11015 }
11016 return JIM_OK;
11017 }
11018
11019 /* [time] */
11020 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
11021 Jim_Obj *const *argv)
11022 {
11023 long i, count = 1;
11024 jim_wide start, elapsed;
11025 char buf [256];
11026 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11027
11028 if (argc < 2) {
11029 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11030 return JIM_ERR;
11031 }
11032 if (argc == 3) {
11033 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11034 return JIM_ERR;
11035 }
11036 if (count < 0)
11037 return JIM_OK;
11038 i = count;
11039 start = JimClock();
11040 while (i-- > 0) {
11041 int retval;
11042
11043 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11044 return retval;
11045 }
11046 elapsed = JimClock() - start;
11047 sprintf(buf, fmt, elapsed/count);
11048 Jim_SetResultString(interp, buf, -1);
11049 return JIM_OK;
11050 }
11051
11052 /* [exit] */
11053 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
11054 Jim_Obj *const *argv)
11055 {
11056 long exitCode = 0;
11057
11058 if (argc > 2) {
11059 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11060 return JIM_ERR;
11061 }
11062 if (argc == 2) {
11063 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11064 return JIM_ERR;
11065 }
11066 interp->exitCode = exitCode;
11067 return JIM_EXIT;
11068 }
11069
11070 /* [catch] */
11071 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
11072 Jim_Obj *const *argv)
11073 {
11074 int exitCode = 0;
11075
11076 if (argc != 2 && argc != 3) {
11077 Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11078 return JIM_ERR;
11079 }
11080 exitCode = Jim_EvalObj(interp, argv[1]);
11081 if (argc == 3) {
11082 if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11083 != JIM_OK)
11084 return JIM_ERR;
11085 }
11086 Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11087 return JIM_OK;
11088 }
11089
11090 /* [ref] */
11091 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
11092 Jim_Obj *const *argv)
11093 {
11094 if (argc != 3 && argc != 4) {
11095 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11096 return JIM_ERR;
11097 }
11098 if (argc == 3) {
11099 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11100 } else {
11101 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11102 argv[3]));
11103 }
11104 return JIM_OK;
11105 }
11106
11107 /* [getref] */
11108 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
11109 Jim_Obj *const *argv)
11110 {
11111 Jim_Reference *refPtr;
11112
11113 if (argc != 2) {
11114 Jim_WrongNumArgs(interp, 1, argv, "reference");
11115 return JIM_ERR;
11116 }
11117 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11118 return JIM_ERR;
11119 Jim_SetResult(interp, refPtr->objPtr);
11120 return JIM_OK;
11121 }
11122
11123 /* [setref] */
11124 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
11125 Jim_Obj *const *argv)
11126 {
11127 Jim_Reference *refPtr;
11128
11129 if (argc != 3) {
11130 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11131 return JIM_ERR;
11132 }
11133 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11134 return JIM_ERR;
11135 Jim_IncrRefCount(argv[2]);
11136 Jim_DecrRefCount(interp, refPtr->objPtr);
11137 refPtr->objPtr = argv[2];
11138 Jim_SetResult(interp, argv[2]);
11139 return JIM_OK;
11140 }
11141
11142 /* [collect] */
11143 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
11144 Jim_Obj *const *argv)
11145 {
11146 if (argc != 1) {
11147 Jim_WrongNumArgs(interp, 1, argv, "");
11148 return JIM_ERR;
11149 }
11150 Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11151 return JIM_OK;
11152 }
11153
11154 /* [finalize] reference ?newValue? */
11155 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
11156 Jim_Obj *const *argv)
11157 {
11158 if (argc != 2 && argc != 3) {
11159 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11160 return JIM_ERR;
11161 }
11162 if (argc == 2) {
11163 Jim_Obj *cmdNamePtr;
11164
11165 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11166 return JIM_ERR;
11167 if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11168 Jim_SetResult(interp, cmdNamePtr);
11169 } else {
11170 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11171 return JIM_ERR;
11172 Jim_SetResult(interp, argv[2]);
11173 }
11174 return JIM_OK;
11175 }
11176
11177 /* TODO */
11178 /* [info references] (list of all the references/finalizers) */
11179
11180 /* [rename] */
11181 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
11182 Jim_Obj *const *argv)
11183 {
11184 const char *oldName, *newName;
11185
11186 if (argc != 3) {
11187 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11188 return JIM_ERR;
11189 }
11190 oldName = Jim_GetString(argv[1], NULL);
11191 newName = Jim_GetString(argv[2], NULL);
11192 if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11193 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11194 Jim_AppendStrings(interp, Jim_GetResult(interp),
11195 "can't rename \"", oldName, "\": ",
11196 "command doesn't exist", NULL);
11197 return JIM_ERR;
11198 }
11199 return JIM_OK;
11200 }
11201
11202 /* [dict] */
11203 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
11204 Jim_Obj *const *argv)
11205 {
11206 int option;
11207 const char *options[] = {
11208 "create", "get", "set", "unset", "exists", NULL
11209 };
11210 enum {
11211 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11212 };
11213
11214 if (argc < 2) {
11215 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11216 return JIM_ERR;
11217 }
11218
11219 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11220 JIM_ERRMSG) != JIM_OK)
11221 return JIM_ERR;
11222
11223 if (option == OPT_CREATE) {
11224 Jim_Obj *objPtr;
11225
11226 if (argc % 2) {
11227 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11228 return JIM_ERR;
11229 }
11230 objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11231 Jim_SetResult(interp, objPtr);
11232 return JIM_OK;
11233 } else if (option == OPT_GET) {
11234 Jim_Obj *objPtr;
11235
11236 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11237 JIM_ERRMSG) != JIM_OK)
11238 return JIM_ERR;
11239 Jim_SetResult(interp, objPtr);
11240 return JIM_OK;
11241 } else if (option == OPT_SET) {
11242 if (argc < 5) {
11243 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11244 return JIM_ERR;
11245 }
11246 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11247 argv[argc-1]);
11248 } else if (option == OPT_UNSET) {
11249 if (argc < 4) {
11250 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11251 return JIM_ERR;
11252 }
11253 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11254 NULL);
11255 } else if (option == OPT_EXIST) {
11256 Jim_Obj *objPtr;
11257 int exists;
11258
11259 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11260 JIM_ERRMSG) == JIM_OK)
11261 exists = 1;
11262 else
11263 exists = 0;
11264 Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11265 return JIM_OK;
11266 } else {
11267 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11268 Jim_AppendStrings(interp, Jim_GetResult(interp),
11269 "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11270 " must be create, get, set", NULL);
11271 return JIM_ERR;
11272 }
11273 return JIM_OK;
11274 }
11275
11276 /* [load] */
11277 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11278 Jim_Obj *const *argv)
11279 {
11280 if (argc < 2) {
11281 Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11282 return JIM_ERR;
11283 }
11284 return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11285 }
11286
11287 /* [subst] */
11288 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11289 Jim_Obj *const *argv)
11290 {
11291 int i, flags = 0;
11292 Jim_Obj *objPtr;
11293
11294 if (argc < 2) {
11295 Jim_WrongNumArgs(interp, 1, argv,
11296 "?-nobackslashes? ?-nocommands? ?-novariables? string");
11297 return JIM_ERR;
11298 }
11299 i = argc-2;
11300 while(i--) {
11301 if (Jim_CompareStringImmediate(interp, argv[i+1],
11302 "-nobackslashes"))
11303 flags |= JIM_SUBST_NOESC;
11304 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11305 "-novariables"))
11306 flags |= JIM_SUBST_NOVAR;
11307 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11308 "-nocommands"))
11309 flags |= JIM_SUBST_NOCMD;
11310 else {
11311 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11312 Jim_AppendStrings(interp, Jim_GetResult(interp),
11313 "bad option \"", Jim_GetString(argv[i+1], NULL),
11314 "\": must be -nobackslashes, -nocommands, or "
11315 "-novariables", NULL);
11316 return JIM_ERR;
11317 }
11318 }
11319 if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11320 return JIM_ERR;
11321 Jim_SetResult(interp, objPtr);
11322 return JIM_OK;
11323 }
11324
11325 /* [info] */
11326 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11327 Jim_Obj *const *argv)
11328 {
11329 int cmd, result = JIM_OK;
11330 static const char *commands[] = {
11331 "body", "commands", "exists", "globals", "level", "locals",
11332 "vars", "version", "complete", "args", NULL
11333 };
11334 enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11335 INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11336
11337 if (argc < 2) {
11338 Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11339 return JIM_ERR;
11340 }
11341 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11342 != JIM_OK) {
11343 return JIM_ERR;
11344 }
11345
11346 if (cmd == INFO_COMMANDS) {
11347 if (argc != 2 && argc != 3) {
11348 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11349 return JIM_ERR;
11350 }
11351 if (argc == 3)
11352 Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11353 else
11354 Jim_SetResult(interp, JimCommandsList(interp, NULL));
11355 } else if (cmd == INFO_EXISTS) {
11356 Jim_Obj *exists;
11357 if (argc != 3) {
11358 Jim_WrongNumArgs(interp, 2, argv, "varName");
11359 return JIM_ERR;
11360 }
11361 exists = Jim_GetVariable(interp, argv[2], 0);
11362 Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11363 } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11364 int mode;
11365 switch (cmd) {
11366 case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11367 case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
11368 case INFO_VARS: mode = JIM_VARLIST_VARS; break;
11369 default: mode = 0; /* avoid warning */; break;
11370 }
11371 if (argc != 2 && argc != 3) {
11372 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11373 return JIM_ERR;
11374 }
11375 if (argc == 3)
11376 Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11377 else
11378 Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11379 } else if (cmd == INFO_LEVEL) {
11380 Jim_Obj *objPtr;
11381 switch (argc) {
11382 case 2:
11383 Jim_SetResult(interp,
11384 Jim_NewIntObj(interp, interp->numLevels));
11385 break;
11386 case 3:
11387 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11388 return JIM_ERR;
11389 Jim_SetResult(interp, objPtr);
11390 break;
11391 default:
11392 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11393 return JIM_ERR;
11394 }
11395 } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11396 Jim_Cmd *cmdPtr;
11397
11398 if (argc != 3) {
11399 Jim_WrongNumArgs(interp, 2, argv, "procname");
11400 return JIM_ERR;
11401 }
11402 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11403 return JIM_ERR;
11404 if (cmdPtr->cmdProc != NULL) {
11405 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11406 Jim_AppendStrings(interp, Jim_GetResult(interp),
11407 "command \"", Jim_GetString(argv[2], NULL),
11408 "\" is not a procedure", NULL);
11409 return JIM_ERR;
11410 }
11411 if (cmd == INFO_BODY)
11412 Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11413 else
11414 Jim_SetResult(interp, cmdPtr->argListObjPtr);
11415 } else if (cmd == INFO_VERSION) {
11416 char buf[(JIM_INTEGER_SPACE * 2) + 1];
11417 sprintf(buf, "%d.%d",
11418 JIM_VERSION / 100, JIM_VERSION % 100);
11419 Jim_SetResultString(interp, buf, -1);
11420 } else if (cmd == INFO_COMPLETE) {
11421 const char *s;
11422 int len;
11423
11424 if (argc != 3) {
11425 Jim_WrongNumArgs(interp, 2, argv, "script");
11426 return JIM_ERR;
11427 }
11428 s = Jim_GetString(argv[2], &len);
11429 Jim_SetResult(interp,
11430 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11431 }
11432 return result;
11433 }
11434
11435 /* [split] */
11436 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11437 Jim_Obj *const *argv)
11438 {
11439 const char *str, *splitChars, *noMatchStart;
11440 int splitLen, strLen, i;
11441 Jim_Obj *resObjPtr;
11442
11443 if (argc != 2 && argc != 3) {
11444 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11445 return JIM_ERR;
11446 }
11447 /* Init */
11448 if (argc == 2) {
11449 splitChars = " \n\t\r";
11450 splitLen = 4;
11451 } else {
11452 splitChars = Jim_GetString(argv[2], &splitLen);
11453 }
11454 str = Jim_GetString(argv[1], &strLen);
11455 if (!strLen) return JIM_OK;
11456 noMatchStart = str;
11457 resObjPtr = Jim_NewListObj(interp, NULL, 0);
11458 /* Split */
11459 if (splitLen) {
11460 while (strLen) {
11461 for (i = 0; i < splitLen; i++) {
11462 if (*str == splitChars[i]) {
11463 Jim_Obj *objPtr;
11464
11465 objPtr = Jim_NewStringObj(interp, noMatchStart,
11466 (str-noMatchStart));
11467 Jim_ListAppendElement(interp, resObjPtr, objPtr);
11468 noMatchStart = str+1;
11469 break;
11470 }
11471 }
11472 str ++;
11473 strLen --;
11474 }
11475 Jim_ListAppendElement(interp, resObjPtr,
11476 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11477 } else {
11478 /* This handles the special case of splitchars eq {}. This
11479 * is trivial but we want to perform object sharing as Tcl does. */
11480 Jim_Obj *objCache[256];
11481 const unsigned char *u = (unsigned char*) str;
11482 memset(objCache, 0, sizeof(objCache));
11483 for (i = 0; i < strLen; i++) {
11484 int c = u[i];
11485
11486 if (objCache[c] == NULL)
11487 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11488 Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11489 }
11490 }
11491 Jim_SetResult(interp, resObjPtr);
11492 return JIM_OK;
11493 }
11494
11495 /* [join] */
11496 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11497 Jim_Obj *const *argv)
11498 {
11499 const char *joinStr;
11500 int joinStrLen, i, listLen;
11501 Jim_Obj *resObjPtr;
11502
11503 if (argc != 2 && argc != 3) {
11504 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11505 return JIM_ERR;
11506 }
11507 /* Init */
11508 if (argc == 2) {
11509 joinStr = " ";
11510 joinStrLen = 1;
11511 } else {
11512 joinStr = Jim_GetString(argv[2], &joinStrLen);
11513 }
11514 Jim_ListLength(interp, argv[1], &listLen);
11515 resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11516 /* Split */
11517 for (i = 0; i < listLen; i++) {
11518 Jim_Obj *objPtr;
11519
11520 Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11521 Jim_AppendObj(interp, resObjPtr, objPtr);
11522 if (i+1 != listLen) {
11523 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11524 }
11525 }
11526 Jim_SetResult(interp, resObjPtr);
11527 return JIM_OK;
11528 }
11529
11530 /* [format] */
11531 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11532 Jim_Obj *const *argv)
11533 {
11534 Jim_Obj *objPtr;
11535
11536 if (argc < 2) {
11537 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11538 return JIM_ERR;
11539 }
11540 objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11541 if (objPtr == NULL)
11542 return JIM_ERR;
11543 Jim_SetResult(interp, objPtr);
11544 return JIM_OK;
11545 }
11546
11547 /* [scan] */
11548 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11549 Jim_Obj *const *argv)
11550 {
11551 Jim_Obj *listPtr, **outVec;
11552 int outc, i, count = 0;
11553
11554 if (argc < 3) {
11555 Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11556 return JIM_ERR;
11557 }
11558 if (argv[2]->typePtr != &scanFmtStringObjType)
11559 SetScanFmtFromAny(interp, argv[2]);
11560 if (FormatGetError(argv[2]) != 0) {
11561 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11562 return JIM_ERR;
11563 }
11564 if (argc > 3) {
11565 int maxPos = FormatGetMaxPos(argv[2]);
11566 int count = FormatGetCnvCount(argv[2]);
11567 if (maxPos > argc-3) {
11568 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11569 return JIM_ERR;
11570 } else if (count != 0 && count < argc-3) {
11571 Jim_SetResultString(interp, "variable is not assigned by any "
11572 "conversion specifiers", -1);
11573 return JIM_ERR;
11574 } else if (count > argc-3) {
11575 Jim_SetResultString(interp, "different numbers of variable names and "
11576 "field specifiers", -1);
11577 return JIM_ERR;
11578 }
11579 }
11580 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11581 if (listPtr == 0)
11582 return JIM_ERR;
11583 if (argc > 3) {
11584 int len = 0;
11585 if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11586 Jim_ListLength(interp, listPtr, &len);
11587 if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11588 Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11589 return JIM_OK;
11590 }
11591 JimListGetElements(interp, listPtr, &outc, &outVec);
11592 for (i = 0; i < outc; ++i) {
11593 if (Jim_Length(outVec[i]) > 0) {
11594 ++count;
11595 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11596 goto err;
11597 }
11598 }
11599 Jim_FreeNewObj(interp, listPtr);
11600 Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11601 } else {
11602 if (listPtr == (Jim_Obj*)EOF) {
11603 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11604 return JIM_OK;
11605 }
11606 Jim_SetResult(interp, listPtr);
11607 }
11608 return JIM_OK;
11609 err:
11610 Jim_FreeNewObj(interp, listPtr);
11611 return JIM_ERR;
11612 }
11613
11614 /* [error] */
11615 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11616 Jim_Obj *const *argv)
11617 {
11618 if (argc != 2) {
11619 Jim_WrongNumArgs(interp, 1, argv, "message");
11620 return JIM_ERR;
11621 }
11622 Jim_SetResult(interp, argv[1]);
11623 return JIM_ERR;
11624 }
11625
11626 /* [lrange] */
11627 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11628 Jim_Obj *const *argv)
11629 {
11630 Jim_Obj *objPtr;
11631
11632 if (argc != 4) {
11633 Jim_WrongNumArgs(interp, 1, argv, "list first last");
11634 return JIM_ERR;
11635 }
11636 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11637 return JIM_ERR;
11638 Jim_SetResult(interp, objPtr);
11639 return JIM_OK;
11640 }
11641
11642 /* [env] */
11643 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11644 Jim_Obj *const *argv)
11645 {
11646 const char *key;
11647 char *val;
11648
11649 if (argc != 2) {
11650 Jim_WrongNumArgs(interp, 1, argv, "varName");
11651 return JIM_ERR;
11652 }
11653 key = Jim_GetString(argv[1], NULL);
11654 val = getenv(key);
11655 if (val == NULL) {
11656 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11657 Jim_AppendStrings(interp, Jim_GetResult(interp),
11658 "environment variable \"",
11659 key, "\" does not exist", NULL);
11660 return JIM_ERR;
11661 }
11662 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11663 return JIM_OK;
11664 }
11665
11666 /* [source] */
11667 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11668 Jim_Obj *const *argv)
11669 {
11670 int retval;
11671
11672 if (argc != 2) {
11673 Jim_WrongNumArgs(interp, 1, argv, "fileName");
11674 return JIM_ERR;
11675 }
11676 retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11677 if (retval == JIM_RETURN)
11678 return JIM_OK;
11679 return retval;
11680 }
11681
11682 /* [lreverse] */
11683 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11684 Jim_Obj *const *argv)
11685 {
11686 Jim_Obj *revObjPtr, **ele;
11687 int len;
11688
11689 if (argc != 2) {
11690 Jim_WrongNumArgs(interp, 1, argv, "list");
11691 return JIM_ERR;
11692 }
11693 JimListGetElements(interp, argv[1], &len, &ele);
11694 len--;
11695 revObjPtr = Jim_NewListObj(interp, NULL, 0);
11696 while (len >= 0)
11697 ListAppendElement(revObjPtr, ele[len--]);
11698 Jim_SetResult(interp, revObjPtr);
11699 return JIM_OK;
11700 }
11701
11702 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11703 {
11704 jim_wide len;
11705
11706 if (step == 0) return -1;
11707 if (start == end) return 0;
11708 else if (step > 0 && start > end) return -1;
11709 else if (step < 0 && end > start) return -1;
11710 len = end-start;
11711 if (len < 0) len = -len; /* abs(len) */
11712 if (step < 0) step = -step; /* abs(step) */
11713 len = 1 + ((len-1)/step);
11714 /* We can truncate safely to INT_MAX, the range command
11715 * will always return an error for a such long range
11716 * because Tcl lists can't be so long. */
11717 if (len > INT_MAX) len = INT_MAX;
11718 return (int)((len < 0) ? -1 : len);
11719 }
11720
11721 /* [range] */
11722 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11723 Jim_Obj *const *argv)
11724 {
11725 jim_wide start = 0, end, step = 1;
11726 int len, i;
11727 Jim_Obj *objPtr;
11728
11729 if (argc < 2 || argc > 4) {
11730 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11731 return JIM_ERR;
11732 }
11733 if (argc == 2) {
11734 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11735 return JIM_ERR;
11736 } else {
11737 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11738 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11739 return JIM_ERR;
11740 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11741 return JIM_ERR;
11742 }
11743 if ((len = JimRangeLen(start, end, step)) == -1) {
11744 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11745 return JIM_ERR;
11746 }
11747 objPtr = Jim_NewListObj(interp, NULL, 0);
11748 for (i = 0; i < len; i++)
11749 ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11750 Jim_SetResult(interp, objPtr);
11751 return JIM_OK;
11752 }
11753
11754 /* [rand] */
11755 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11756 Jim_Obj *const *argv)
11757 {
11758 jim_wide min = 0, max, len, maxMul;
11759
11760 if (argc < 1 || argc > 3) {
11761 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11762 return JIM_ERR;
11763 }
11764 if (argc == 1) {
11765 max = JIM_WIDE_MAX;
11766 } else if (argc == 2) {
11767 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11768 return JIM_ERR;
11769 } else if (argc == 3) {
11770 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11771 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11772 return JIM_ERR;
11773 }
11774 len = max-min;
11775 if (len < 0) {
11776 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11777 return JIM_ERR;
11778 }
11779 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11780 while (1) {
11781 jim_wide r;
11782
11783 JimRandomBytes(interp, &r, sizeof(jim_wide));
11784 if (r < 0 || r >= maxMul) continue;
11785 r = (len == 0) ? 0 : r%len;
11786 Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11787 return JIM_OK;
11788 }
11789 }
11790
11791 /* [package] */
11792 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
11793 Jim_Obj *const *argv)
11794 {
11795 int option;
11796 const char *options[] = {
11797 "require", "provide", NULL
11798 };
11799 enum {OPT_REQUIRE, OPT_PROVIDE};
11800
11801 if (argc < 2) {
11802 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11803 return JIM_ERR;
11804 }
11805 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11806 JIM_ERRMSG) != JIM_OK)
11807 return JIM_ERR;
11808
11809 if (option == OPT_REQUIRE) {
11810 int exact = 0;
11811 const char *ver;
11812
11813 if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11814 exact = 1;
11815 argv++;
11816 argc--;
11817 }
11818 if (argc != 3 && argc != 4) {
11819 Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11820 return JIM_ERR;
11821 }
11822 ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11823 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11824 JIM_ERRMSG);
11825 if (ver == NULL)
11826 return JIM_ERR;
11827 Jim_SetResultString(interp, ver, -1);
11828 } else if (option == OPT_PROVIDE) {
11829 if (argc != 4) {
11830 Jim_WrongNumArgs(interp, 2, argv, "package version");
11831 return JIM_ERR;
11832 }
11833 return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11834 Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11835 }
11836 return JIM_OK;
11837 }
11838
11839 static struct {
11840 const char *name;
11841 Jim_CmdProc cmdProc;
11842 } Jim_CoreCommandsTable[] = {
11843 {"set", Jim_SetCoreCommand},
11844 {"unset", Jim_UnsetCoreCommand},
11845 {"puts", Jim_PutsCoreCommand},
11846 {"+", Jim_AddCoreCommand},
11847 {"*", Jim_MulCoreCommand},
11848 {"-", Jim_SubCoreCommand},
11849 {"/", Jim_DivCoreCommand},
11850 {"incr", Jim_IncrCoreCommand},
11851 {"while", Jim_WhileCoreCommand},
11852 {"for", Jim_ForCoreCommand},
11853 {"foreach", Jim_ForeachCoreCommand},
11854 {"lmap", Jim_LmapCoreCommand},
11855 {"if", Jim_IfCoreCommand},
11856 {"switch", Jim_SwitchCoreCommand},
11857 {"list", Jim_ListCoreCommand},
11858 {"lindex", Jim_LindexCoreCommand},
11859 {"lset", Jim_LsetCoreCommand},
11860 {"llength", Jim_LlengthCoreCommand},
11861 {"lappend", Jim_LappendCoreCommand},
11862 {"linsert", Jim_LinsertCoreCommand},
11863 {"lsort", Jim_LsortCoreCommand},
11864 {"append", Jim_AppendCoreCommand},
11865 {"debug", Jim_DebugCoreCommand},
11866 {"eval", Jim_EvalCoreCommand},
11867 {"uplevel", Jim_UplevelCoreCommand},
11868 {"expr", Jim_ExprCoreCommand},
11869 {"break", Jim_BreakCoreCommand},
11870 {"continue", Jim_ContinueCoreCommand},
11871 {"proc", Jim_ProcCoreCommand},
11872 {"concat", Jim_ConcatCoreCommand},
11873 {"return", Jim_ReturnCoreCommand},
11874 {"upvar", Jim_UpvarCoreCommand},
11875 {"global", Jim_GlobalCoreCommand},
11876 {"string", Jim_StringCoreCommand},
11877 {"time", Jim_TimeCoreCommand},
11878 {"exit", Jim_ExitCoreCommand},
11879 {"catch", Jim_CatchCoreCommand},
11880 {"ref", Jim_RefCoreCommand},
11881 {"getref", Jim_GetrefCoreCommand},
11882 {"setref", Jim_SetrefCoreCommand},
11883 {"finalize", Jim_FinalizeCoreCommand},
11884 {"collect", Jim_CollectCoreCommand},
11885 {"rename", Jim_RenameCoreCommand},
11886 {"dict", Jim_DictCoreCommand},
11887 {"load", Jim_LoadCoreCommand},
11888 {"subst", Jim_SubstCoreCommand},
11889 {"info", Jim_InfoCoreCommand},
11890 {"split", Jim_SplitCoreCommand},
11891 {"join", Jim_JoinCoreCommand},
11892 {"format", Jim_FormatCoreCommand},
11893 {"scan", Jim_ScanCoreCommand},
11894 {"error", Jim_ErrorCoreCommand},
11895 {"lrange", Jim_LrangeCoreCommand},
11896 {"env", Jim_EnvCoreCommand},
11897 {"source", Jim_SourceCoreCommand},
11898 {"lreverse", Jim_LreverseCoreCommand},
11899 {"range", Jim_RangeCoreCommand},
11900 {"rand", Jim_RandCoreCommand},
11901 {"package", Jim_PackageCoreCommand},
11902 {"tailcall", Jim_TailcallCoreCommand},
11903 {NULL, NULL},
11904 };
11905
11906 /* Some Jim core command is actually a procedure written in Jim itself. */
11907 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11908 {
11909 Jim_Eval(interp, (char*)
11910 "proc lambda {arglist args} {\n"
11911 " set name [ref {} function lambdaFinalizer]\n"
11912 " uplevel 1 [list proc $name $arglist {expand}$args]\n"
11913 " return $name\n"
11914 "}\n"
11915 "proc lambdaFinalizer {name val} {\n"
11916 " rename $name {}\n"
11917 "}\n"
11918 );
11919 }
11920
11921 void Jim_RegisterCoreCommands(Jim_Interp *interp)
11922 {
11923 int i = 0;
11924
11925 while(Jim_CoreCommandsTable[i].name != NULL) {
11926 Jim_CreateCommand(interp,
11927 Jim_CoreCommandsTable[i].name,
11928 Jim_CoreCommandsTable[i].cmdProc,
11929 NULL, NULL);
11930 i++;
11931 }
11932 Jim_RegisterCoreProcedures(interp);
11933 }
11934
11935 /* -----------------------------------------------------------------------------
11936 * Interactive prompt
11937 * ---------------------------------------------------------------------------*/
11938 void Jim_PrintErrorMessage(Jim_Interp *interp)
11939 {
11940 int len, i;
11941
11942 Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL,
11943 interp->errorFileName, interp->errorLine);
11944 Jim_fprintf(interp,interp->cookie_stderr, " %s" JIM_NL,
11945 Jim_GetString(interp->result, NULL));
11946 Jim_ListLength(interp, interp->stackTrace, &len);
11947 for (i = 0; i < len; i+= 3) {
11948 Jim_Obj *objPtr;
11949 const char *proc, *file, *line;
11950
11951 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
11952 proc = Jim_GetString(objPtr, NULL);
11953 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
11954 JIM_NONE);
11955 file = Jim_GetString(objPtr, NULL);
11956 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
11957 JIM_NONE);
11958 line = Jim_GetString(objPtr, NULL);
11959 Jim_fprintf( interp, interp->cookie_stderr,
11960 "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
11961 proc, file, line);
11962 }
11963 }
11964
11965 int Jim_InteractivePrompt(Jim_Interp *interp)
11966 {
11967 int retcode = JIM_OK;
11968 Jim_Obj *scriptObjPtr;
11969
11970 Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
11971 "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
11972 JIM_VERSION / 100, JIM_VERSION % 100);
11973 Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
11974 while (1) {
11975 char buf[1024];
11976 const char *result;
11977 const char *retcodestr[] = {
11978 "ok", "error", "return", "break", "continue", "eval", "exit"
11979 };
11980 int reslen;
11981
11982 if (retcode != 0) {
11983 if (retcode >= 2 && retcode <= 6)
11984 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
11985 else
11986 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
11987 } else
11988 Jim_fprintf( interp, interp->cookie_stdout, ". ");
11989 Jim_fflush( interp, interp->cookie_stdout);
11990 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
11991 Jim_IncrRefCount(scriptObjPtr);
11992 while(1) {
11993 const char *str;
11994 char state;
11995 int len;
11996
11997 if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
11998 Jim_DecrRefCount(interp, scriptObjPtr);
11999 goto out;
12000 }
12001 Jim_AppendString(interp, scriptObjPtr, buf, -1);
12002 str = Jim_GetString(scriptObjPtr, &len);
12003 if (Jim_ScriptIsComplete(str, len, &state))
12004 break;
12005 Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12006 Jim_fflush( interp, interp->cookie_stdout);
12007 }
12008 retcode = Jim_EvalObj(interp, scriptObjPtr);
12009 Jim_DecrRefCount(interp, scriptObjPtr);
12010 result = Jim_GetString(Jim_GetResult(interp), &reslen);
12011 if (retcode == JIM_ERR) {
12012 Jim_PrintErrorMessage(interp);
12013 } else if (retcode == JIM_EXIT) {
12014 exit(Jim_GetExitCode(interp));
12015 } else {
12016 if (reslen) {
12017 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12018 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12019 }
12020 }
12021 }
12022 out:
12023 return 0;
12024 }
12025
12026 /* -----------------------------------------------------------------------------
12027 * Jim's idea of STDIO..
12028 * ---------------------------------------------------------------------------*/
12029
12030 int
12031 Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12032 {
12033 int r;
12034
12035 va_list ap;
12036 va_start(ap,fmt);
12037 r = Jim_vfprintf( interp, cookie, fmt,ap );
12038 va_end(ap);
12039 return r;
12040 }
12041
12042
12043 int
12044 Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12045 {
12046 if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12047 errno = ENOTSUP;
12048 return -1;
12049 }
12050 return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12051 }
12052
12053 size_t
12054 Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12055 {
12056 if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12057 errno = ENOTSUP;
12058 return 0;
12059 }
12060 return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12061 }
12062
12063 size_t
12064 Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12065 {
12066 if( (interp == NULL) || (interp->cb_fread == NULL) ){
12067 errno = ENOTSUP;
12068 return 0;
12069 }
12070 return (*(interp->cb_fread))( ptr, size, n, cookie);
12071 }
12072
12073 int
12074 Jim_fflush( Jim_Interp *interp, void *cookie )
12075 {
12076 if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12077 /* pretend all is well */
12078 return 0;
12079 }
12080 return (*(interp->cb_fflush))( cookie );
12081 }
12082
12083 char *
12084 Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12085 {
12086 if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12087 errno = ENOTSUP;
12088 return NULL;
12089 }
12090 return (*(interp->cb_fgets))( s, size, cookie );
12091 }
12092
12093
12094
12095
12096
12097
12098 /*
12099 * Local Variables: **
12100 * tab-width: 4 **
12101 * c-basic-offset: 4 **
12102 * End: **
12103 */
12104

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)