]> git.sur5r.net Git - openocd/blob - src/jim.c
a466a08bbdebeb8d3d34ad74d7553f3d07091440
[openocd] / 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')
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')
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')
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     FILE *fp = interp ? interp->stderr_ : stderr;
464
465     va_start(ap, fmt);
466     fprintf(fp, JIM_NL "JIM INTERPRETER PANIC: ");
467     vfprintf(fp, fmt, ap);
468     fprintf(fp, JIM_NL JIM_NL);
469     va_end(ap);
470 #ifdef HAVE_BACKTRACE
471     {
472         void *array[40];
473         int size, i;
474         char **strings;
475
476         size = backtrace(array, 40);
477         strings = backtrace_symbols(array, size);
478         for (i = 0; i < size; i++)
479             fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
480         fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
481         fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
482     }
483 #endif
484     abort();
485 }
486
487 /* -----------------------------------------------------------------------------
488  * Memory allocation
489  * ---------------------------------------------------------------------------*/
490
491 /* Macro used for memory debugging.
492  * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
493  * and similary for Jim_Realloc and Jim_Free */
494 #if 0
495 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
496 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
497 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
498 #endif
499
500 void *Jim_Alloc(int size)
501 {
502         /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
503         if (size==0)
504                 size=1;
505     void *p = malloc(size);
506     if (p == NULL)
507         Jim_Panic(NULL,"malloc: Out of memory");
508     return p;
509 }
510
511 void Jim_Free(void *ptr) {
512     free(ptr);
513 }
514
515 void *Jim_Realloc(void *ptr, int size)
516 {
517         /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
518         if (size==0)
519                 size=1;
520     void *p = realloc(ptr, size);
521     if (p == NULL)
522         Jim_Panic(NULL,"realloc: Out of memory");
523     return p;
524 }
525
526 char *Jim_StrDup(const char *s)
527 {
528     int l = strlen(s);
529     char *copy = Jim_Alloc(l+1);
530
531     memcpy(copy, s, l+1);
532     return copy;
533 }
534
535 char *Jim_StrDupLen(const char *s, int l)
536 {
537     char *copy = Jim_Alloc(l+1);
538     
539     memcpy(copy, s, l+1);
540     copy[l] = 0;    /* Just to be sure, original could be substring */
541     return copy;
542 }
543
544 /* -----------------------------------------------------------------------------
545  * Time related functions
546  * ---------------------------------------------------------------------------*/
547 /* Returns microseconds of CPU used since start. */
548 static jim_wide JimClock(void)
549 {
550 #if (defined WIN32) && !(defined JIM_ANSIC)
551     LARGE_INTEGER t, f;
552     QueryPerformanceFrequency(&f);
553     QueryPerformanceCounter(&t);
554     return (long)((t.QuadPart * 1000000) / f.QuadPart);
555 #else /* !WIN32 */
556     clock_t clocks = clock();
557
558     return (long)(clocks*(1000000/CLOCKS_PER_SEC));
559 #endif /* WIN32 */
560 }
561
562 /* -----------------------------------------------------------------------------
563  * Hash Tables
564  * ---------------------------------------------------------------------------*/
565
566 /* -------------------------- private prototypes ---------------------------- */
567 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
568 static unsigned int JimHashTableNextPower(unsigned int size);
569 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
570
571 /* -------------------------- hash functions -------------------------------- */
572
573 /* Thomas Wang's 32 bit Mix Function */
574 unsigned int Jim_IntHashFunction(unsigned int key)
575 {
576     key += ~(key << 15);
577     key ^=  (key >> 10);
578     key +=  (key << 3);
579     key ^=  (key >> 6);
580     key += ~(key << 11);
581     key ^=  (key >> 16);
582     return key;
583 }
584
585 /* Identity hash function for integer keys */
586 unsigned int Jim_IdentityHashFunction(unsigned int key)
587 {
588     return key;
589 }
590
591 /* Generic hash function (we are using to multiply by 9 and add the byte
592  * as Tcl) */
593 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
594 {
595     unsigned int h = 0;
596     while(len--)
597         h += (h<<3)+*buf++;
598     return h;
599 }
600
601 /* ----------------------------- API implementation ------------------------- */
602 /* reset an hashtable already initialized with ht_init().
603  * NOTE: This function should only called by ht_destroy(). */
604 static void JimResetHashTable(Jim_HashTable *ht)
605 {
606     ht->table = NULL;
607     ht->size = 0;
608     ht->sizemask = 0;
609     ht->used = 0;
610     ht->collisions = 0;
611 }
612
613 /* Initialize the hash table */
614 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
615         void *privDataPtr)
616 {
617     JimResetHashTable(ht);
618     ht->type = type;
619     ht->privdata = privDataPtr;
620     return JIM_OK;
621 }
622
623 /* Resize the table to the minimal size that contains all the elements,
624  * but with the invariant of a USER/BUCKETS ration near to <= 1 */
625 int Jim_ResizeHashTable(Jim_HashTable *ht)
626 {
627     int minimal = ht->used;
628
629     if (minimal < JIM_HT_INITIAL_SIZE)
630         minimal = JIM_HT_INITIAL_SIZE;
631     return Jim_ExpandHashTable(ht, minimal);
632 }
633
634 /* Expand or create the hashtable */
635 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
636 {
637     Jim_HashTable n; /* the new hashtable */
638     unsigned int realsize = JimHashTableNextPower(size), i;
639
640     /* the size is invalid if it is smaller than the number of
641      * elements already inside the hashtable */
642     if (ht->used >= size)
643         return JIM_ERR;
644
645     Jim_InitHashTable(&n, ht->type, ht->privdata);
646     n.size = realsize;
647     n.sizemask = realsize-1;
648     n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
649
650     /* Initialize all the pointers to NULL */
651     memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
652
653     /* Copy all the elements from the old to the new table:
654      * note that if the old hash table is empty ht->size is zero,
655      * so Jim_ExpandHashTable just creates an hash table. */
656     n.used = ht->used;
657     for (i = 0; i < ht->size && ht->used > 0; i++) {
658         Jim_HashEntry *he, *nextHe;
659
660         if (ht->table[i] == NULL) continue;
661         
662         /* For each hash entry on this slot... */
663         he = ht->table[i];
664         while(he) {
665             unsigned int h;
666
667             nextHe = he->next;
668             /* Get the new element index */
669             h = Jim_HashKey(ht, he->key) & n.sizemask;
670             he->next = n.table[h];
671             n.table[h] = he;
672             ht->used--;
673             /* Pass to the next element */
674             he = nextHe;
675         }
676     }
677     assert(ht->used == 0);
678     Jim_Free(ht->table);
679
680     /* Remap the new hashtable in the old */
681     *ht = n;
682     return JIM_OK;
683 }
684
685 /* Add an element to the target hash table */
686 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
687 {
688     int index;
689     Jim_HashEntry *entry;
690
691     /* Get the index of the new element, or -1 if
692      * the element already exists. */
693     if ((index = JimInsertHashEntry(ht, key)) == -1)
694         return JIM_ERR;
695
696     /* Allocates the memory and stores key */
697     entry = Jim_Alloc(sizeof(*entry));
698     entry->next = ht->table[index];
699     ht->table[index] = entry;
700
701     /* Set the hash entry fields. */
702     Jim_SetHashKey(ht, entry, key);
703     Jim_SetHashVal(ht, entry, val);
704     ht->used++;
705     return JIM_OK;
706 }
707
708 /* Add an element, discarding the old if the key already exists */
709 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
710 {
711     Jim_HashEntry *entry;
712
713     /* Try to add the element. If the key
714      * does not exists Jim_AddHashEntry will suceed. */
715     if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
716         return JIM_OK;
717     /* It already exists, get the entry */
718     entry = Jim_FindHashEntry(ht, key);
719     /* Free the old value and set the new one */
720     Jim_FreeEntryVal(ht, entry);
721     Jim_SetHashVal(ht, entry, val);
722     return JIM_OK;
723 }
724
725 /* Search and remove an element */
726 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
727 {
728     unsigned int h;
729     Jim_HashEntry *he, *prevHe;
730
731     if (ht->size == 0)
732         return JIM_ERR;
733     h = Jim_HashKey(ht, key) & ht->sizemask;
734     he = ht->table[h];
735
736     prevHe = NULL;
737     while(he) {
738         if (Jim_CompareHashKeys(ht, key, he->key)) {
739             /* Unlink the element from the list */
740             if (prevHe)
741                 prevHe->next = he->next;
742             else
743                 ht->table[h] = he->next;
744             Jim_FreeEntryKey(ht, he);
745             Jim_FreeEntryVal(ht, he);
746             Jim_Free(he);
747             ht->used--;
748             return JIM_OK;
749         }
750         prevHe = he;
751         he = he->next;
752     }
753     return JIM_ERR; /* not found */
754 }
755
756 /* Destroy an entire hash table */
757 int Jim_FreeHashTable(Jim_HashTable *ht)
758 {
759     unsigned int i;
760
761     /* Free all the elements */
762     for (i = 0; i < ht->size && ht->used > 0; i++) {
763         Jim_HashEntry *he, *nextHe;
764
765         if ((he = ht->table[i]) == NULL) continue;
766         while(he) {
767             nextHe = he->next;
768             Jim_FreeEntryKey(ht, he);
769             Jim_FreeEntryVal(ht, he);
770             Jim_Free(he);
771             ht->used--;
772             he = nextHe;
773         }
774     }
775     /* Free the table and the allocated cache structure */
776     Jim_Free(ht->table);
777     /* Re-initialize the table */
778     JimResetHashTable(ht);
779     return JIM_OK; /* never fails */
780 }
781
782 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
783 {
784     Jim_HashEntry *he;
785     unsigned int h;
786
787     if (ht->size == 0) return NULL;
788     h = Jim_HashKey(ht, key) & ht->sizemask;
789     he = ht->table[h];
790     while(he) {
791         if (Jim_CompareHashKeys(ht, key, he->key))
792             return he;
793         he = he->next;
794     }
795     return NULL;
796 }
797
798 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
799 {
800     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
801
802     iter->ht = ht;
803     iter->index = -1;
804     iter->entry = NULL;
805     iter->nextEntry = NULL;
806     return iter;
807 }
808
809 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
810 {
811     while (1) {
812         if (iter->entry == NULL) {
813             iter->index++;
814             if (iter->index >=
815                     (signed)iter->ht->size) break;
816             iter->entry = iter->ht->table[iter->index];
817         } else {
818             iter->entry = iter->nextEntry;
819         }
820         if (iter->entry) {
821             /* We need to save the 'next' here, the iterator user
822              * may delete the entry we are returning. */
823             iter->nextEntry = iter->entry->next;
824             return iter->entry;
825         }
826     }
827     return NULL;
828 }
829
830 /* ------------------------- private functions ------------------------------ */
831
832 /* Expand the hash table if needed */
833 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
834 {
835     /* If the hash table is empty expand it to the intial size,
836      * if the table is "full" dobule its size. */
837     if (ht->size == 0)
838         return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
839     if (ht->size == ht->used)
840         return Jim_ExpandHashTable(ht, ht->size*2);
841     return JIM_OK;
842 }
843
844 /* Our hash table capability is a power of two */
845 static unsigned int JimHashTableNextPower(unsigned int size)
846 {
847     unsigned int i = JIM_HT_INITIAL_SIZE;
848
849     if (size >= 2147483648U)
850         return 2147483648U;
851     while(1) {
852         if (i >= size)
853             return i;
854         i *= 2;
855     }
856 }
857
858 /* Returns the index of a free slot that can be populated with
859  * an hash entry for the given 'key'.
860  * If the key already exists, -1 is returned. */
861 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
862 {
863     unsigned int h;
864     Jim_HashEntry *he;
865
866     /* Expand the hashtable if needed */
867     if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
868         return -1;
869     /* Compute the key hash value */
870     h = Jim_HashKey(ht, key) & ht->sizemask;
871     /* Search if this slot does not already contain the given key */
872     he = ht->table[h];
873     while(he) {
874         if (Jim_CompareHashKeys(ht, key, he->key))
875             return -1;
876         he = he->next;
877     }
878     return h;
879 }
880
881 /* ----------------------- StringCopy Hash Table Type ------------------------*/
882
883 static unsigned int JimStringCopyHTHashFunction(const void *key)
884 {
885     return Jim_GenHashFunction(key, strlen(key));
886 }
887
888 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
889 {
890     int len = strlen(key);
891     char *copy = Jim_Alloc(len+1);
892     JIM_NOTUSED(privdata);
893
894     memcpy(copy, key, len);
895     copy[len] = '\0';
896     return copy;
897 }
898
899 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
900 {
901     int len = strlen(val);
902     char *copy = Jim_Alloc(len+1);
903     JIM_NOTUSED(privdata);
904
905     memcpy(copy, val, len);
906     copy[len] = '\0';
907     return copy;
908 }
909
910 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
911         const void *key2)
912 {
913     JIM_NOTUSED(privdata);
914
915     return strcmp(key1, key2) == 0;
916 }
917
918 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
919 {
920     JIM_NOTUSED(privdata);
921
922     Jim_Free((void*)key); /* ATTENTION: const cast */
923 }
924
925 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
926 {
927     JIM_NOTUSED(privdata);
928
929     Jim_Free((void*)val); /* ATTENTION: const cast */
930 }
931
932 static Jim_HashTableType JimStringCopyHashTableType = {
933     JimStringCopyHTHashFunction,        /* hash function */
934     JimStringCopyHTKeyDup,              /* key dup */
935     NULL,                               /* val dup */
936     JimStringCopyHTKeyCompare,          /* key compare */
937     JimStringCopyHTKeyDestructor,       /* key destructor */
938     NULL                                /* val destructor */
939 };
940
941 /* This is like StringCopy but does not auto-duplicate the key.
942  * It's used for intepreter's shared strings. */
943 static Jim_HashTableType JimSharedStringsHashTableType = {
944     JimStringCopyHTHashFunction,        /* hash function */
945     NULL,                               /* key dup */
946     NULL,                               /* val dup */
947     JimStringCopyHTKeyCompare,          /* key compare */
948     JimStringCopyHTKeyDestructor,       /* key destructor */
949     NULL                                /* val destructor */
950 };
951
952 /* This is like StringCopy but also automatically handle dynamic
953  * allocated C strings as values. */
954 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
955     JimStringCopyHTHashFunction,        /* hash function */
956     JimStringCopyHTKeyDup,              /* key dup */
957     JimStringKeyValCopyHTValDup,        /* val dup */
958     JimStringCopyHTKeyCompare,          /* key compare */
959     JimStringCopyHTKeyDestructor,       /* key destructor */
960     JimStringKeyValCopyHTValDestructor, /* val destructor */
961 };
962
963 typedef struct AssocDataValue {
964     Jim_InterpDeleteProc *delProc;
965     void *data;
966 } AssocDataValue;
967
968 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
969 {
970     AssocDataValue *assocPtr = (AssocDataValue *)data;
971     if (assocPtr->delProc != NULL)
972         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
973     Jim_Free(data);
974 }
975
976 static Jim_HashTableType JimAssocDataHashTableType = {
977     JimStringCopyHTHashFunction,         /* hash function */
978     JimStringCopyHTKeyDup,               /* key dup */
979     NULL,                                /* val dup */
980     JimStringCopyHTKeyCompare,           /* key compare */
981     JimStringCopyHTKeyDestructor,        /* key destructor */
982     JimAssocDataHashTableValueDestructor /* val destructor */
983 };
984
985 /* -----------------------------------------------------------------------------
986  * Stack - This is a simple generic stack implementation. It is used for
987  * example in the 'expr' expression compiler.
988  * ---------------------------------------------------------------------------*/
989 void Jim_InitStack(Jim_Stack *stack)
990 {
991     stack->len = 0;
992     stack->maxlen = 0;
993     stack->vector = NULL;
994 }
995
996 void Jim_FreeStack(Jim_Stack *stack)
997 {
998     Jim_Free(stack->vector);
999 }
1000
1001 int Jim_StackLen(Jim_Stack *stack)
1002 {
1003     return stack->len;
1004 }
1005
1006 void Jim_StackPush(Jim_Stack *stack, void *element) {
1007     int neededLen = stack->len+1;
1008     if (neededLen > stack->maxlen) {
1009         stack->maxlen = neededLen*2;
1010         stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1011     }
1012     stack->vector[stack->len] = element;
1013     stack->len++;
1014 }
1015
1016 void *Jim_StackPop(Jim_Stack *stack)
1017 {
1018     if (stack->len == 0) return NULL;
1019     stack->len--;
1020     return stack->vector[stack->len];
1021 }
1022
1023 void *Jim_StackPeek(Jim_Stack *stack)
1024 {
1025     if (stack->len == 0) return NULL;
1026     return stack->vector[stack->len-1];
1027 }
1028
1029 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1030 {
1031     int i;
1032
1033     for (i = 0; i < stack->len; i++)
1034         freeFunc(stack->vector[i]);
1035 }
1036
1037 /* -----------------------------------------------------------------------------
1038  * Parser
1039  * ---------------------------------------------------------------------------*/
1040
1041 /* Token types */
1042 #define JIM_TT_NONE -1        /* No token returned */
1043 #define JIM_TT_STR 0        /* simple string */
1044 #define JIM_TT_ESC 1        /* string that needs escape chars conversion */
1045 #define JIM_TT_VAR 2        /* var substitution */
1046 #define JIM_TT_DICTSUGAR 3    /* Syntax sugar for [dict get], $foo(bar) */
1047 #define JIM_TT_CMD 4        /* command substitution */
1048 #define JIM_TT_SEP 5        /* word separator */
1049 #define JIM_TT_EOL 6        /* line separator */
1050
1051 /* Additional token types needed for expressions */
1052 #define JIM_TT_SUBEXPR_START 7
1053 #define JIM_TT_SUBEXPR_END 8
1054 #define JIM_TT_EXPR_NUMBER 9
1055 #define JIM_TT_EXPR_OPERATOR 10
1056
1057 /* Parser states */
1058 #define JIM_PS_DEF 0        /* Default state */
1059 #define JIM_PS_QUOTE 1        /* Inside "" */
1060
1061 /* Parser context structure. The same context is used both to parse
1062  * Tcl scripts and lists. */
1063 struct JimParserCtx {
1064     const char *prg;     /* Program text */
1065     const char *p;       /* Pointer to the point of the program we are parsing */
1066     int len;             /* Left length of 'prg' */
1067     int linenr;          /* Current line number */
1068     const char *tstart;
1069     const char *tend;    /* Returned token is at tstart-tend in 'prg'. */
1070     int tline;           /* Line number of the returned token */
1071     int tt;              /* Token type */
1072     int eof;             /* Non zero if EOF condition is true. */
1073     int state;           /* Parser state */
1074     int comment;         /* Non zero if the next chars may be a comment. */
1075 };
1076
1077 #define JimParserEof(c) ((c)->eof)
1078 #define JimParserTstart(c) ((c)->tstart)
1079 #define JimParserTend(c) ((c)->tend)
1080 #define JimParserTtype(c) ((c)->tt)
1081 #define JimParserTline(c) ((c)->tline)
1082
1083 static int JimParseScript(struct JimParserCtx *pc);
1084 static int JimParseSep(struct JimParserCtx *pc);
1085 static int JimParseEol(struct JimParserCtx *pc);
1086 static int JimParseCmd(struct JimParserCtx *pc);
1087 static int JimParseVar(struct JimParserCtx *pc);
1088 static int JimParseBrace(struct JimParserCtx *pc);
1089 static int JimParseStr(struct JimParserCtx *pc);
1090 static int JimParseComment(struct JimParserCtx *pc);
1091 static char *JimParserGetToken(struct JimParserCtx *pc,
1092         int *lenPtr, int *typePtr, int *linePtr);
1093
1094 /* Initialize a parser context.
1095  * 'prg' is a pointer to the program text, linenr is the line
1096  * number of the first line contained in the program. */
1097 void JimParserInit(struct JimParserCtx *pc, const char *prg, 
1098         int len, int linenr)
1099 {
1100     pc->prg = prg;
1101     pc->p = prg;
1102     pc->len = len;
1103     pc->tstart = NULL;
1104     pc->tend = NULL;
1105     pc->tline = 0;
1106     pc->tt = JIM_TT_NONE;
1107     pc->eof = 0;
1108     pc->state = JIM_PS_DEF;
1109     pc->linenr = linenr;
1110     pc->comment = 1;
1111 }
1112
1113 int JimParseScript(struct JimParserCtx *pc)
1114 {
1115     while(1) { /* the while is used to reiterate with continue if needed */
1116         if (!pc->len) {
1117             pc->tstart = pc->p;
1118             pc->tend = pc->p-1;
1119             pc->tline = pc->linenr;
1120             pc->tt = JIM_TT_EOL;
1121             pc->eof = 1;
1122             return JIM_OK;
1123         }
1124         switch(*(pc->p)) {
1125         case '\\':
1126             if (*(pc->p+1) == '\n')
1127                 return JimParseSep(pc);
1128             else {
1129                 pc->comment = 0;
1130                 return JimParseStr(pc);
1131             }
1132             break;
1133         case ' ':
1134         case '\t':
1135         case '\r':
1136             if (pc->state == JIM_PS_DEF)
1137                 return JimParseSep(pc);
1138             else {
1139                 pc->comment = 0;
1140                 return JimParseStr(pc);
1141             }
1142             break;
1143         case '\n':
1144         case ';':
1145             pc->comment = 1;
1146             if (pc->state == JIM_PS_DEF)
1147                 return JimParseEol(pc);
1148             else
1149                 return JimParseStr(pc);
1150             break;
1151         case '[':
1152             pc->comment = 0;
1153             return JimParseCmd(pc);
1154             break;
1155         case '$':
1156             pc->comment = 0;
1157             if (JimParseVar(pc) == JIM_ERR) {
1158                 pc->tstart = pc->tend = pc->p++; pc->len--;
1159                 pc->tline = pc->linenr;
1160                 pc->tt = JIM_TT_STR;
1161                 return JIM_OK;
1162             } else
1163                 return JIM_OK;
1164             break;
1165         case '#':
1166             if (pc->comment) {
1167                 JimParseComment(pc);
1168                 continue;
1169             } else {
1170                 return JimParseStr(pc);
1171             }
1172         default:
1173             pc->comment = 0;
1174             return JimParseStr(pc);
1175             break;
1176         }
1177         return JIM_OK;
1178     }
1179 }
1180
1181 int JimParseSep(struct JimParserCtx *pc)
1182 {
1183     pc->tstart = pc->p;
1184     pc->tline = pc->linenr;
1185     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1186            (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1187         if (*pc->p == '\\') {
1188             pc->p++; pc->len--;
1189             pc->linenr++;
1190         }
1191         pc->p++; pc->len--;
1192     }
1193     pc->tend = pc->p-1;
1194     pc->tt = JIM_TT_SEP;
1195     return JIM_OK;
1196 }
1197
1198 int JimParseEol(struct JimParserCtx *pc)
1199 {
1200     pc->tstart = pc->p;
1201     pc->tline = pc->linenr;
1202     while (*pc->p == ' ' || *pc->p == '\n' ||
1203            *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1204         if (*pc->p == '\n')
1205             pc->linenr++;
1206         pc->p++; pc->len--;
1207     }
1208     pc->tend = pc->p-1;
1209     pc->tt = JIM_TT_EOL;
1210     return JIM_OK;
1211 }
1212
1213 /* Todo. Don't stop if ']' appears inside {} or quoted.
1214  * Also should handle the case of puts [string length "]"] */
1215 int JimParseCmd(struct JimParserCtx *pc)
1216 {
1217     int level = 1;
1218     int blevel = 0;
1219
1220     pc->tstart = ++pc->p; pc->len--;
1221     pc->tline = pc->linenr;
1222     while (1) {
1223         if (pc->len == 0) {
1224             break;
1225         } else if (*pc->p == '[' && blevel == 0) {
1226             level++;
1227         } else if (*pc->p == ']' && blevel == 0) {
1228             level--;
1229             if (!level) break;
1230         } else if (*pc->p == '\\') {
1231             pc->p++; pc->len--;
1232         } else if (*pc->p == '{') {
1233             blevel++;
1234         } else if (*pc->p == '}') {
1235             if (blevel != 0)
1236                 blevel--;
1237         } else if (*pc->p == '\n')
1238             pc->linenr++;
1239         pc->p++; pc->len--;
1240     }
1241     pc->tend = pc->p-1;
1242     pc->tt = JIM_TT_CMD;
1243     if (*pc->p == ']') {
1244         pc->p++; pc->len--;
1245     }
1246     return JIM_OK;
1247 }
1248
1249 int JimParseVar(struct JimParserCtx *pc)
1250 {
1251     int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1252
1253     pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1254     pc->tline = pc->linenr;
1255     if (*pc->p == '{') {
1256         pc->tstart = ++pc->p; pc->len--;
1257         brace = 1;
1258     }
1259     if (brace) {
1260         while (!stop) {
1261             if (*pc->p == '}' || pc->len == 0) {
1262                 stop = 1;
1263                 if (pc->len == 0)
1264                     continue;
1265             }
1266             else if (*pc->p == '\n')
1267                 pc->linenr++;
1268             pc->p++; pc->len--;
1269         }
1270         if (pc->len == 0)
1271             pc->tend = pc->p-1;
1272         else
1273             pc->tend = pc->p-2;
1274     } else {
1275         while (!stop) {
1276             if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1277                 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1278                 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1279                 stop = 1;
1280             else {
1281                 pc->p++; pc->len--;
1282             }
1283         }
1284         /* Parse [dict get] syntax sugar. */
1285         if (*pc->p == '(') {
1286             while (*pc->p != ')' && pc->len) {
1287                 pc->p++; pc->len--;
1288                 if (*pc->p == '\\' && pc->len >= 2) {
1289                     pc->p += 2; pc->len -= 2;
1290                 }
1291             }
1292             if (*pc->p != '\0') {
1293                 pc->p++; pc->len--;
1294             }
1295             ttype = JIM_TT_DICTSUGAR;
1296         }
1297         pc->tend = pc->p-1;
1298     }
1299     /* Check if we parsed just the '$' character.
1300      * That's not a variable so an error is returned
1301      * to tell the state machine to consider this '$' just
1302      * a string. */
1303     if (pc->tstart == pc->p) {
1304         pc->p--; pc->len++;
1305         return JIM_ERR;
1306     }
1307     pc->tt = ttype;
1308     return JIM_OK;
1309 }
1310
1311 int JimParseBrace(struct JimParserCtx *pc)
1312 {
1313     int level = 1;
1314
1315     pc->tstart = ++pc->p; pc->len--;
1316     pc->tline = pc->linenr;
1317     while (1) {
1318         if (*pc->p == '\\' && pc->len >= 2) {
1319             pc->p++; pc->len--;
1320             if (*pc->p == '\n')
1321                 pc->linenr++;
1322         } else if (*pc->p == '{') {
1323             level++;
1324         } else if (pc->len == 0 || *pc->p == '}') {
1325             level--;
1326             if (pc->len == 0 || level == 0) {
1327                 pc->tend = pc->p-1;
1328                 if (pc->len != 0) {
1329                     pc->p++; pc->len--;
1330                 }
1331                 pc->tt = JIM_TT_STR;
1332                 return JIM_OK;
1333             }
1334         } else if (*pc->p == '\n') {
1335             pc->linenr++;
1336         }
1337         pc->p++; pc->len--;
1338     }
1339     return JIM_OK; /* unreached */
1340 }
1341
1342 int JimParseStr(struct JimParserCtx *pc)
1343 {
1344     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1345             pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1346     if (newword && *pc->p == '{') {
1347         return JimParseBrace(pc);
1348     } else if (newword && *pc->p == '"') {
1349         pc->state = JIM_PS_QUOTE;
1350         pc->p++; pc->len--;
1351     }
1352     pc->tstart = pc->p;
1353     pc->tline = pc->linenr;
1354     while (1) {
1355         if (pc->len == 0) {
1356             pc->tend = pc->p-1;
1357             pc->tt = JIM_TT_ESC;
1358             return JIM_OK;
1359         }
1360         switch(*pc->p) {
1361         case '\\':
1362             if (pc->state == JIM_PS_DEF &&
1363                 *(pc->p+1) == '\n') {
1364                 pc->tend = pc->p-1;
1365                 pc->tt = JIM_TT_ESC;
1366                 return JIM_OK;
1367             }
1368             if (pc->len >= 2) {
1369                 pc->p++; pc->len--;
1370             }
1371             break;
1372         case '$':
1373         case '[':
1374             pc->tend = pc->p-1;
1375             pc->tt = JIM_TT_ESC;
1376             return JIM_OK;
1377         case ' ':
1378         case '\t':
1379         case '\n':
1380         case '\r':
1381         case ';':
1382             if (pc->state == JIM_PS_DEF) {
1383                 pc->tend = pc->p-1;
1384                 pc->tt = JIM_TT_ESC;
1385                 return JIM_OK;
1386             } else if (*pc->p == '\n') {
1387                 pc->linenr++;
1388             }
1389             break;
1390         case '"':
1391             if (pc->state == JIM_PS_QUOTE) {
1392                 pc->tend = pc->p-1;
1393                 pc->tt = JIM_TT_ESC;
1394                 pc->p++; pc->len--;
1395                 pc->state = JIM_PS_DEF;
1396                 return JIM_OK;
1397             }
1398             break;
1399         }
1400         pc->p++; pc->len--;
1401     }
1402     return JIM_OK; /* unreached */
1403 }
1404
1405 int JimParseComment(struct JimParserCtx *pc)
1406 {
1407     while (*pc->p) {
1408         if (*pc->p == '\n') {
1409             pc->linenr++;
1410             if (*(pc->p-1) != '\\') {
1411                 pc->p++; pc->len--;
1412                 return JIM_OK;
1413             }
1414         }
1415         pc->p++; pc->len--;
1416     }
1417     return JIM_OK;
1418 }
1419
1420 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1421 static int xdigitval(int c)
1422 {
1423     if (c >= '0' && c <= '9') return c-'0';
1424     if (c >= 'a' && c <= 'f') return c-'a'+10;
1425     if (c >= 'A' && c <= 'F') return c-'A'+10;
1426     return -1;
1427 }
1428
1429 static int odigitval(int c)
1430 {
1431     if (c >= '0' && c <= '7') return c-'0';
1432     return -1;
1433 }
1434
1435 /* Perform Tcl escape substitution of 's', storing the result
1436  * string into 'dest'. The escaped string is guaranteed to
1437  * be the same length or shorted than the source string.
1438  * Slen is the length of the string at 's', if it's -1 the string
1439  * length will be calculated by the function.
1440  *
1441  * The function returns the length of the resulting string. */
1442 static int JimEscape(char *dest, const char *s, int slen)
1443 {
1444     char *p = dest;
1445     int i, len;
1446     
1447     if (slen == -1)
1448         slen = strlen(s);
1449
1450     for (i = 0; i < slen; i++) {
1451         switch(s[i]) {
1452         case '\\':
1453             switch(s[i+1]) {
1454             case 'a': *p++ = 0x7; i++; break;
1455             case 'b': *p++ = 0x8; i++; break;
1456             case 'f': *p++ = 0xc; i++; break;
1457             case 'n': *p++ = 0xa; i++; break;
1458             case 'r': *p++ = 0xd; i++; break;
1459             case 't': *p++ = 0x9; i++; break;
1460             case 'v': *p++ = 0xb; i++; break;
1461             case '\0': *p++ = '\\'; i++; break;
1462             case '\n': *p++ = ' '; i++; break;
1463             default:
1464                   if (s[i+1] == 'x') {
1465                     int val = 0;
1466                     int c = xdigitval(s[i+2]);
1467                     if (c == -1) {
1468                         *p++ = 'x';
1469                         i++;
1470                         break;
1471                     }
1472                     val = c;
1473                     c = xdigitval(s[i+3]);
1474                     if (c == -1) {
1475                         *p++ = val;
1476                         i += 2;
1477                         break;
1478                     }
1479                     val = (val*16)+c;
1480                     *p++ = val;
1481                     i += 3;
1482                     break;
1483                   } else if (s[i+1] >= '0' && s[i+1] <= '7')
1484                   {
1485                     int val = 0;
1486                     int c = odigitval(s[i+1]);
1487                     val = c;
1488                     c = odigitval(s[i+2]);
1489                     if (c == -1) {
1490                         *p++ = val;
1491                         i ++;
1492                         break;
1493                     }
1494                     val = (val*8)+c;
1495                     c = odigitval(s[i+3]);
1496                     if (c == -1) {
1497                         *p++ = val;
1498                         i += 2;
1499                         break;
1500                     }
1501                     val = (val*8)+c;
1502                     *p++ = val;
1503                     i += 3;
1504                   } else {
1505                     *p++ = s[i+1];
1506                     i++;
1507                   }
1508                   break;
1509             }
1510             break;
1511         default:
1512             *p++ = s[i];
1513             break;
1514         }
1515     }
1516     len = p-dest;
1517     *p++ = '\0';
1518     return len;
1519 }
1520
1521 /* Returns a dynamically allocated copy of the current token in the
1522  * parser context. The function perform conversion of escapes if
1523  * the token is of type JIM_TT_ESC.
1524  *
1525  * Note that after the conversion, tokens that are grouped with
1526  * braces in the source code, are always recognizable from the
1527  * identical string obtained in a different way from the type.
1528  *
1529  * For exmple the string:
1530  *
1531  * {expand}$a
1532  * 
1533  * will return as first token "expand", of type JIM_TT_STR
1534  *
1535  * While the string:
1536  *
1537  * expand$a
1538  *
1539  * will return as first token "expand", of type JIM_TT_ESC
1540  */
1541 char *JimParserGetToken(struct JimParserCtx *pc,
1542         int *lenPtr, int *typePtr, int *linePtr)
1543 {
1544     const char *start, *end;
1545     char *token;
1546     int len;
1547
1548     start = JimParserTstart(pc);
1549     end = JimParserTend(pc);
1550     if (start > end) {
1551         if (lenPtr) *lenPtr = 0;
1552         if (typePtr) *typePtr = JimParserTtype(pc);
1553         if (linePtr) *linePtr = JimParserTline(pc);
1554         token = Jim_Alloc(1);
1555         token[0] = '\0';
1556         return token;
1557     }
1558     len = (end-start)+1;
1559     token = Jim_Alloc(len+1);
1560     if (JimParserTtype(pc) != JIM_TT_ESC) {
1561         /* No escape conversion needed? Just copy it. */
1562         memcpy(token, start, len);
1563         token[len] = '\0';
1564     } else {
1565         /* Else convert the escape chars. */
1566         len = JimEscape(token, start, len);
1567     }
1568     if (lenPtr) *lenPtr = len;
1569     if (typePtr) *typePtr = JimParserTtype(pc);
1570     if (linePtr) *linePtr = JimParserTline(pc);
1571     return token;
1572 }
1573
1574 /* The following functin is not really part of the parsing engine of Jim,
1575  * but it somewhat related. Given an string and its length, it tries
1576  * to guess if the script is complete or there are instead " " or { }
1577  * open and not completed. This is useful for interactive shells
1578  * implementation and for [info complete].
1579  *
1580  * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1581  * '{' on scripts incomplete missing one or more '}' to be balanced.
1582  * '"' on scripts incomplete missing a '"' char.
1583  *
1584  * If the script is complete, 1 is returned, otherwise 0. */
1585 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1586 {
1587     int level = 0;
1588     int state = ' ';
1589
1590     while(len) {
1591         switch (*s) {
1592             case '\\':
1593                 if (len > 1)
1594                     s++;
1595                 break;
1596             case '"':
1597                 if (state == ' ') {
1598                     state = '"';
1599                 } else if (state == '"') {
1600                     state = ' ';
1601                 }
1602                 break;
1603             case '{':
1604                 if (state == '{') {
1605                     level++;
1606                 } else if (state == ' ') {
1607                     state = '{';
1608                     level++;
1609                 }
1610                 break;
1611             case '}':
1612                 if (state == '{') {
1613                     level--;
1614                     if (level == 0)
1615                         state = ' ';
1616                 }
1617                 break;
1618         }
1619         s++;
1620         len--;
1621     }
1622     if (stateCharPtr)
1623         *stateCharPtr = state;
1624     return state == ' ';
1625 }
1626
1627 /* -----------------------------------------------------------------------------
1628  * Tcl Lists parsing
1629  * ---------------------------------------------------------------------------*/
1630 static int JimParseListSep(struct JimParserCtx *pc);
1631 static int JimParseListStr(struct JimParserCtx *pc);
1632
1633 int JimParseList(struct JimParserCtx *pc)
1634 {
1635     if (pc->len == 0) {
1636         pc->tstart = pc->tend = pc->p;
1637         pc->tline = pc->linenr;
1638         pc->tt = JIM_TT_EOL;
1639         pc->eof = 1;
1640         return JIM_OK;
1641     }
1642     switch(*pc->p) {
1643     case ' ':
1644     case '\n':
1645     case '\t':
1646     case '\r':
1647         if (pc->state == JIM_PS_DEF)
1648             return JimParseListSep(pc);
1649         else
1650             return JimParseListStr(pc);
1651         break;
1652     default:
1653         return JimParseListStr(pc);
1654         break;
1655     }
1656     return JIM_OK;
1657 }
1658
1659 int JimParseListSep(struct JimParserCtx *pc)
1660 {
1661     pc->tstart = pc->p;
1662     pc->tline = pc->linenr;
1663     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1664     {
1665         pc->p++; pc->len--;
1666     }
1667     pc->tend = pc->p-1;
1668     pc->tt = JIM_TT_SEP;
1669     return JIM_OK;
1670 }
1671
1672 int JimParseListStr(struct JimParserCtx *pc)
1673 {
1674     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1675             pc->tt == JIM_TT_NONE);
1676     if (newword && *pc->p == '{') {
1677         return JimParseBrace(pc);
1678     } else if (newword && *pc->p == '"') {
1679         pc->state = JIM_PS_QUOTE;
1680         pc->p++; pc->len--;
1681     }
1682     pc->tstart = pc->p;
1683     pc->tline = pc->linenr;
1684     while (1) {
1685         if (pc->len == 0) {
1686             pc->tend = pc->p-1;
1687             pc->tt = JIM_TT_ESC;
1688             return JIM_OK;
1689         }
1690         switch(*pc->p) {
1691         case '\\':
1692             pc->p++; pc->len--;
1693             break;
1694         case ' ':
1695         case '\t':
1696         case '\n':
1697         case '\r':
1698             if (pc->state == JIM_PS_DEF) {
1699                 pc->tend = pc->p-1;
1700                 pc->tt = JIM_TT_ESC;
1701                 return JIM_OK;
1702             } else if (*pc->p == '\n') {
1703                 pc->linenr++;
1704             }
1705             break;
1706         case '"':
1707             if (pc->state == JIM_PS_QUOTE) {
1708                 pc->tend = pc->p-1;
1709                 pc->tt = JIM_TT_ESC;
1710                 pc->p++; pc->len--;
1711                 pc->state = JIM_PS_DEF;
1712                 return JIM_OK;
1713             }
1714             break;
1715         }
1716         pc->p++; pc->len--;
1717     }
1718     return JIM_OK; /* unreached */
1719 }
1720
1721 /* -----------------------------------------------------------------------------
1722  * Jim_Obj related functions
1723  * ---------------------------------------------------------------------------*/
1724
1725 /* Return a new initialized object. */
1726 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1727 {
1728     Jim_Obj *objPtr;
1729
1730     /* -- Check if there are objects in the free list -- */
1731     if (interp->freeList != NULL) {
1732         /* -- Unlink the object from the free list -- */
1733         objPtr = interp->freeList;
1734         interp->freeList = objPtr->nextObjPtr;
1735     } else {
1736         /* -- No ready to use objects: allocate a new one -- */
1737         objPtr = Jim_Alloc(sizeof(*objPtr));
1738     }
1739
1740     /* Object is returned with refCount of 0. Every
1741      * kind of GC implemented should take care to don't try
1742      * to scan objects with refCount == 0. */
1743     objPtr->refCount = 0;
1744     /* All the other fields are left not initialized to save time.
1745      * The caller will probably want set they to the right
1746      * value anyway. */
1747
1748     /* -- Put the object into the live list -- */
1749     objPtr->prevObjPtr = NULL;
1750     objPtr->nextObjPtr = interp->liveList;
1751     if (interp->liveList)
1752         interp->liveList->prevObjPtr = objPtr;
1753     interp->liveList = objPtr;
1754
1755     return objPtr;
1756 }
1757
1758 /* Free an object. Actually objects are never freed, but
1759  * just moved to the free objects list, where they will be
1760  * reused by Jim_NewObj(). */
1761 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1762 {
1763     /* Check if the object was already freed, panic. */
1764     if (objPtr->refCount != 0)  {
1765         Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1766                 objPtr->refCount);
1767     }
1768     /* Free the internal representation */
1769     Jim_FreeIntRep(interp, objPtr);
1770     /* Free the string representation */
1771     if (objPtr->bytes != NULL) {
1772         if (objPtr->bytes != JimEmptyStringRep)
1773             Jim_Free(objPtr->bytes);
1774     }
1775     /* Unlink the object from the live objects list */
1776     if (objPtr->prevObjPtr)
1777         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1778     if (objPtr->nextObjPtr)
1779         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1780     if (interp->liveList == objPtr)
1781         interp->liveList = objPtr->nextObjPtr;
1782     /* Link the object into the free objects list */
1783     objPtr->prevObjPtr = NULL;
1784     objPtr->nextObjPtr = interp->freeList;
1785     if (interp->freeList)
1786         interp->freeList->prevObjPtr = objPtr;
1787     interp->freeList = objPtr;
1788     objPtr->refCount = -1;
1789 }
1790
1791 /* Invalidate the string representation of an object. */
1792 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1793 {
1794     if (objPtr->bytes != NULL) {
1795         if (objPtr->bytes != JimEmptyStringRep)
1796             Jim_Free(objPtr->bytes);
1797     }
1798     objPtr->bytes = NULL;
1799 }
1800
1801 #define Jim_SetStringRep(o, b, l) \
1802     do { (o)->bytes = b; (o)->length = l; } while (0)
1803
1804 /* Set the initial string representation for an object.
1805  * Does not try to free an old one. */
1806 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1807 {
1808     if (length == 0) {
1809         objPtr->bytes = JimEmptyStringRep;
1810         objPtr->length = 0;
1811     } else {
1812         objPtr->bytes = Jim_Alloc(length+1);
1813         objPtr->length = length;
1814         memcpy(objPtr->bytes, bytes, length);
1815         objPtr->bytes[length] = '\0';
1816     }
1817 }
1818
1819 /* Duplicate an object. The returned object has refcount = 0. */
1820 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1821 {
1822     Jim_Obj *dupPtr;
1823
1824     dupPtr = Jim_NewObj(interp);
1825     if (objPtr->bytes == NULL) {
1826         /* Object does not have a valid string representation. */
1827         dupPtr->bytes = NULL;
1828     } else {
1829         Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1830     }
1831     if (objPtr->typePtr != NULL) {
1832         if (objPtr->typePtr->dupIntRepProc == NULL) {
1833             dupPtr->internalRep = objPtr->internalRep;
1834         } else {
1835             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1836         }
1837         dupPtr->typePtr = objPtr->typePtr;
1838     } else {
1839         dupPtr->typePtr = NULL;
1840     }
1841     return dupPtr;
1842 }
1843
1844 /* Return the string representation for objPtr. If the object
1845  * string representation is invalid, calls the method to create
1846  * a new one starting from the internal representation of the object. */
1847 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1848 {
1849     if (objPtr->bytes == NULL) {
1850         /* Invalid string repr. Generate it. */
1851         if (objPtr->typePtr->updateStringProc == NULL) {
1852             Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1853                 objPtr->typePtr->name);
1854         }
1855         objPtr->typePtr->updateStringProc(objPtr);
1856     }
1857     if (lenPtr)
1858         *lenPtr = objPtr->length;
1859     return objPtr->bytes;
1860 }
1861
1862 /* Just returns the length of the object's string rep */
1863 int Jim_Length(Jim_Obj *objPtr)
1864 {
1865     int len;
1866
1867     Jim_GetString(objPtr, &len);
1868     return len;
1869 }
1870
1871 /* -----------------------------------------------------------------------------
1872  * String Object
1873  * ---------------------------------------------------------------------------*/
1874 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1875 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1876
1877 static Jim_ObjType stringObjType = {
1878     "string",
1879     NULL,
1880     DupStringInternalRep,
1881     NULL,
1882     JIM_TYPE_REFERENCES,
1883 };
1884
1885 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1886 {
1887     JIM_NOTUSED(interp);
1888
1889     /* This is a bit subtle: the only caller of this function
1890      * should be Jim_DuplicateObj(), that will copy the
1891      * string representaion. After the copy, the duplicated
1892      * object will not have more room in teh buffer than
1893      * srcPtr->length bytes. So we just set it to length. */
1894     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1895 }
1896
1897 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1898 {
1899     /* Get a fresh string representation. */
1900     (void) Jim_GetString(objPtr, NULL);
1901     /* Free any other internal representation. */
1902     Jim_FreeIntRep(interp, objPtr);
1903     /* Set it as string, i.e. just set the maxLength field. */
1904     objPtr->typePtr = &stringObjType;
1905     objPtr->internalRep.strValue.maxLength = objPtr->length;
1906     return JIM_OK;
1907 }
1908
1909 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1910 {
1911     Jim_Obj *objPtr = Jim_NewObj(interp);
1912
1913     if (len == -1)
1914         len = strlen(s);
1915     /* Alloc/Set the string rep. */
1916     if (len == 0) {
1917         objPtr->bytes = JimEmptyStringRep;
1918         objPtr->length = 0;
1919     } else {
1920         objPtr->bytes = Jim_Alloc(len+1);
1921         objPtr->length = len;
1922         memcpy(objPtr->bytes, s, len);
1923         objPtr->bytes[len] = '\0';
1924     }
1925
1926     /* No typePtr field for the vanilla string object. */
1927     objPtr->typePtr = NULL;
1928     return objPtr;
1929 }
1930
1931 /* This version does not try to duplicate the 's' pointer, but
1932  * use it directly. */
1933 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
1934 {
1935     Jim_Obj *objPtr = Jim_NewObj(interp);
1936
1937     if (len == -1)
1938         len = strlen(s);
1939     Jim_SetStringRep(objPtr, s, len);
1940     objPtr->typePtr = NULL;
1941     return objPtr;
1942 }
1943
1944 /* Low-level string append. Use it only against objects
1945  * of type "string". */
1946 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
1947 {
1948     int needlen;
1949
1950     if (len == -1)
1951         len = strlen(str);
1952     needlen = objPtr->length + len;
1953     if (objPtr->internalRep.strValue.maxLength < needlen ||
1954         objPtr->internalRep.strValue.maxLength == 0) {
1955         if (objPtr->bytes == JimEmptyStringRep) {
1956             objPtr->bytes = Jim_Alloc((needlen*2)+1);
1957         } else {
1958             objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
1959         }
1960         objPtr->internalRep.strValue.maxLength = needlen*2;
1961     }
1962     memcpy(objPtr->bytes + objPtr->length, str, len);
1963     objPtr->bytes[objPtr->length+len] = '\0';
1964     objPtr->length += len;
1965 }
1966
1967 /* Low-level wrapper to append an object. */
1968 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
1969 {
1970     int len;
1971     const char *str;
1972
1973     str = Jim_GetString(appendObjPtr, &len);
1974     StringAppendString(objPtr, str, len);
1975 }
1976
1977 /* Higher level API to append strings to objects. */
1978 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
1979         int len)
1980 {
1981     if (Jim_IsShared(objPtr))
1982         Jim_Panic(interp,"Jim_AppendString called with shared object");
1983     if (objPtr->typePtr != &stringObjType)
1984         SetStringFromAny(interp, objPtr);
1985     StringAppendString(objPtr, str, len);
1986 }
1987
1988 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
1989         Jim_Obj *appendObjPtr)
1990 {
1991     int len;
1992     const char *str;
1993
1994     str = Jim_GetString(appendObjPtr, &len);
1995     Jim_AppendString(interp, objPtr, str, len);
1996 }
1997
1998 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
1999 {
2000     va_list ap;
2001
2002     if (objPtr->typePtr != &stringObjType)
2003         SetStringFromAny(interp, objPtr);
2004     va_start(ap, objPtr);
2005     while (1) {
2006         char *s = va_arg(ap, char*);
2007
2008         if (s == NULL) break;
2009         Jim_AppendString(interp, objPtr, s, -1);
2010     }
2011     va_end(ap);
2012 }
2013
2014 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2015 {
2016     const char *aStr, *bStr;
2017     int aLen, bLen, i;
2018
2019     if (aObjPtr == bObjPtr) return 1;
2020     aStr = Jim_GetString(aObjPtr, &aLen);
2021     bStr = Jim_GetString(bObjPtr, &bLen);
2022     if (aLen != bLen) return 0;
2023     if (nocase == 0)
2024         return memcmp(aStr, bStr, aLen) == 0;
2025     for (i = 0; i < aLen; i++) {
2026         if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2027             return 0;
2028     }
2029     return 1;
2030 }
2031
2032 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2033         int nocase)
2034 {
2035     const char *pattern, *string;
2036     int patternLen, stringLen;
2037
2038     pattern = Jim_GetString(patternObjPtr, &patternLen);
2039     string = Jim_GetString(objPtr, &stringLen);
2040     return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2041 }
2042
2043 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2044         Jim_Obj *secondObjPtr, int nocase)
2045 {
2046     const char *s1, *s2;
2047     int l1, l2;
2048
2049     s1 = Jim_GetString(firstObjPtr, &l1);
2050     s2 = Jim_GetString(secondObjPtr, &l2);
2051     return JimStringCompare(s1, l1, s2, l2, nocase);
2052 }
2053
2054 /* Convert a range, as returned by Jim_GetRange(), into
2055  * an absolute index into an object of the specified length.
2056  * This function may return negative values, or values
2057  * bigger or equal to the length of the list if the index
2058  * is out of range. */
2059 static int JimRelToAbsIndex(int len, int index)
2060 {
2061     if (index < 0)
2062         return len + index;
2063     return index;
2064 }
2065
2066 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2067  * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2068  * for implementation of commands like [string range] and [lrange].
2069  *
2070  * The resulting range is guaranteed to address valid elements of
2071  * the structure. */
2072 static void JimRelToAbsRange(int len, int first, int last,
2073         int *firstPtr, int *lastPtr, int *rangeLenPtr)
2074 {
2075     int rangeLen;
2076
2077     if (first > last) {
2078         rangeLen = 0;
2079     } else {
2080         rangeLen = last-first+1;
2081         if (rangeLen) {
2082             if (first < 0) {
2083                 rangeLen += first;
2084                 first = 0;
2085             }
2086             if (last >= len) {
2087                 rangeLen -= (last-(len-1));
2088                 last = len-1;
2089             }
2090         }
2091     }
2092     if (rangeLen < 0) rangeLen = 0;
2093
2094     *firstPtr = first;
2095     *lastPtr = last;
2096     *rangeLenPtr = rangeLen;
2097 }
2098
2099 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2100         Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2101 {
2102     int first, last;
2103     const char *str;
2104     int len, rangeLen;
2105
2106     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2107         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2108         return NULL;
2109     str = Jim_GetString(strObjPtr, &len);
2110     first = JimRelToAbsIndex(len, first);
2111     last = JimRelToAbsIndex(len, last);
2112     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2113     return Jim_NewStringObj(interp, str+first, rangeLen);
2114 }
2115
2116 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2117 {
2118     char *buf = Jim_Alloc(strObjPtr->length+1);
2119     int i;
2120
2121     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2122     for (i = 0; i < strObjPtr->length; i++)
2123         buf[i] = tolower(buf[i]);
2124     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2125 }
2126
2127 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2128 {
2129     char *buf = Jim_Alloc(strObjPtr->length+1);
2130     int i;
2131
2132     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2133     for (i = 0; i < strObjPtr->length; i++)
2134         buf[i] = toupper(buf[i]);
2135     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2136 }
2137
2138 /* This is the core of the [format] command.
2139  * TODO: Export it, make it real... for now only %s and %%
2140  * specifiers supported. */
2141 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2142         int objc, Jim_Obj *const *objv)
2143 {
2144     const char *fmt;
2145     int fmtLen;
2146     Jim_Obj *resObjPtr;
2147
2148     fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2149     resObjPtr = Jim_NewStringObj(interp, "", 0);
2150     while (fmtLen) {
2151         const char *p = fmt;
2152         char spec[2], c;
2153         jim_wide wideValue;
2154
2155         while (*fmt != '%' && fmtLen) {
2156             fmt++; fmtLen--;
2157         }
2158         Jim_AppendString(interp, resObjPtr, p, fmt-p);
2159         if (fmtLen == 0)
2160             break;
2161         fmt++; fmtLen--; /* skip '%' */
2162         if (*fmt != '%') {
2163             if (objc == 0) {
2164                 Jim_FreeNewObj(interp, resObjPtr);
2165                 Jim_SetResultString(interp,
2166                         "not enough arguments for all format specifiers", -1);
2167                 return NULL;
2168             } else {
2169                 objc--;
2170             }
2171         }
2172         switch(*fmt) {
2173         case 's':
2174             Jim_AppendObj(interp, resObjPtr, objv[0]);
2175             objv++;
2176             break;
2177         case 'c':
2178             if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2179                 Jim_FreeNewObj(interp, resObjPtr);
2180                 return NULL;
2181             }
2182             c = (char) wideValue;
2183             Jim_AppendString(interp, resObjPtr, &c, 1);
2184             break;
2185         case 'd':
2186             if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2187                 Jim_FreeNewObj(interp, resObjPtr);
2188                 return NULL;
2189             }
2190             Jim_AppendObj(interp, resObjPtr, objv[0]);
2191             break;
2192         case '%':
2193             Jim_AppendString(interp, resObjPtr, "%" , 1);
2194             break;
2195         default:
2196             spec[0] = *fmt; spec[1] = '\0';
2197             Jim_FreeNewObj(interp, resObjPtr);
2198             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2199             Jim_AppendStrings(interp, Jim_GetResult(interp),
2200                     "bad field specifier \"",  spec, "\"", NULL);
2201             return NULL;
2202         }
2203         fmt++;
2204         fmtLen--;
2205     }
2206     return resObjPtr;
2207 }
2208
2209 /* -----------------------------------------------------------------------------
2210  * Compared String Object
2211  * ---------------------------------------------------------------------------*/
2212
2213 /* This is strange object that allows to compare a C literal string
2214  * with a Jim object in very short time if the same comparison is done
2215  * multiple times. For example every time the [if] command is executed,
2216  * Jim has to check if a given argument is "else". This comparions if
2217  * the code has no errors are true most of the times, so we can cache
2218  * inside the object the pointer of the string of the last matching
2219  * comparison. Because most C compilers perform literal sharing,
2220  * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2221  * this works pretty well even if comparisons are at different places
2222  * inside the C code. */
2223
2224 static Jim_ObjType comparedStringObjType = {
2225     "compared-string",
2226     NULL,
2227     NULL,
2228     NULL,
2229     JIM_TYPE_REFERENCES,
2230 };
2231
2232 /* The only way this object is exposed to the API is via the following
2233  * function. Returns true if the string and the object string repr.
2234  * are the same, otherwise zero is returned.
2235  *
2236  * Note: this isn't binary safe, but it hardly needs to be.*/
2237 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2238         const char *str)
2239 {
2240     if (objPtr->typePtr == &comparedStringObjType &&
2241         objPtr->internalRep.ptr == str)
2242         return 1;
2243     else {
2244         const char *objStr = Jim_GetString(objPtr, NULL);
2245         if (strcmp(str, objStr) != 0) return 0;
2246         if (objPtr->typePtr != &comparedStringObjType) {
2247             Jim_FreeIntRep(interp, objPtr);
2248             objPtr->typePtr = &comparedStringObjType;
2249         }
2250         objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2251         return 1;
2252     }
2253 }
2254
2255 int qsortCompareStringPointers(const void *a, const void *b)
2256 {
2257     char * const *sa = (char * const *)a;
2258     char * const *sb = (char * const *)b;
2259     return strcmp(*sa, *sb);
2260 }
2261
2262 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2263         const char **tablePtr, int *indexPtr, const char *name, int flags)
2264 {
2265     const char **entryPtr = NULL;
2266     char **tablePtrSorted;
2267     int i, count = 0;
2268
2269     *indexPtr = -1;
2270     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2271         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2272             *indexPtr = i;
2273             return JIM_OK;
2274         }
2275         count++; /* If nothing matches, this will reach the len of tablePtr */
2276     }
2277     if (flags & JIM_ERRMSG) {
2278         if (name == NULL)
2279             name = "option";
2280         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2281         Jim_AppendStrings(interp, Jim_GetResult(interp),
2282             "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2283             NULL);
2284         tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2285         memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2286         qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2287         for (i = 0; i < count; i++) {
2288             if (i+1 == count && count > 1)
2289                 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2290             Jim_AppendString(interp, Jim_GetResult(interp),
2291                     tablePtrSorted[i], -1);
2292             if (i+1 != count)
2293                 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2294         }
2295         Jim_Free(tablePtrSorted);
2296     }
2297     return JIM_ERR;
2298 }
2299
2300 /* -----------------------------------------------------------------------------
2301  * Source Object
2302  *
2303  * This object is just a string from the language point of view, but
2304  * in the internal representation it contains the filename and line number
2305  * where this given token was read. This information is used by
2306  * Jim_EvalObj() if the object passed happens to be of type "source".
2307  *
2308  * This allows to propagate the information about line numbers and file
2309  * names and give error messages with absolute line numbers.
2310  *
2311  * Note that this object uses shared strings for filenames, and the
2312  * pointer to the filename together with the line number is taken into
2313  * the space for the "inline" internal represenation of the Jim_Object,
2314  * so there is almost memory zero-overhead.
2315  *
2316  * Also the object will be converted to something else if the given
2317  * token it represents in the source file is not something to be
2318  * evaluated (not a script), and will be specialized in some other way,
2319  * so the time overhead is alzo null.
2320  * ---------------------------------------------------------------------------*/
2321
2322 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2323 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2324
2325 static Jim_ObjType sourceObjType = {
2326     "source",
2327     FreeSourceInternalRep,
2328     DupSourceInternalRep,
2329     NULL,
2330     JIM_TYPE_REFERENCES,
2331 };
2332
2333 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2334 {
2335     Jim_ReleaseSharedString(interp,
2336             objPtr->internalRep.sourceValue.fileName);
2337 }
2338
2339 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2340 {
2341     dupPtr->internalRep.sourceValue.fileName =
2342         Jim_GetSharedString(interp,
2343                 srcPtr->internalRep.sourceValue.fileName);
2344     dupPtr->internalRep.sourceValue.lineNumber =
2345         dupPtr->internalRep.sourceValue.lineNumber;
2346     dupPtr->typePtr = &sourceObjType;
2347 }
2348
2349 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2350         const char *fileName, int lineNumber)
2351 {
2352     if (Jim_IsShared(objPtr))
2353         Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2354     if (objPtr->typePtr != NULL)
2355         Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2356     objPtr->internalRep.sourceValue.fileName =
2357         Jim_GetSharedString(interp, fileName);
2358     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2359     objPtr->typePtr = &sourceObjType;
2360 }
2361
2362 /* -----------------------------------------------------------------------------
2363  * Script Object
2364  * ---------------------------------------------------------------------------*/
2365
2366 #define JIM_CMDSTRUCT_EXPAND -1
2367
2368 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2369 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2370 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2371
2372 static Jim_ObjType scriptObjType = {
2373     "script",
2374     FreeScriptInternalRep,
2375     DupScriptInternalRep,
2376     NULL,
2377     JIM_TYPE_REFERENCES,
2378 };
2379
2380 /* The ScriptToken structure represents every token into a scriptObj.
2381  * Every token contains an associated Jim_Obj that can be specialized
2382  * by commands operating on it. */
2383 typedef struct ScriptToken {
2384     int type;
2385     Jim_Obj *objPtr;
2386     int linenr;
2387 } ScriptToken;
2388
2389 /* This is the script object internal representation. An array of
2390  * ScriptToken structures, with an associated command structure array.
2391  * The command structure is a pre-computed representation of the
2392  * command length and arguments structure as a simple liner array
2393  * of integers.
2394  * 
2395  * For example the script:
2396  *
2397  * puts hello
2398  * set $i $x$y [foo]BAR
2399  *
2400  * will produce a ScriptObj with the following Tokens:
2401  *
2402  * ESC puts
2403  * SEP
2404  * ESC hello
2405  * EOL
2406  * ESC set
2407  * EOL
2408  * VAR i
2409  * SEP
2410  * VAR x
2411  * VAR y
2412  * SEP
2413  * CMD foo
2414  * ESC BAR
2415  * EOL
2416  *
2417  * This is a description of the tokens, separators, and of lines.
2418  * The command structure instead represents the number of arguments
2419  * of every command, followed by the tokens of which every argument
2420  * is composed. So for the example script, the cmdstruct array will
2421  * contain:
2422  *
2423  * 2 1 1 4 1 1 2 2
2424  *
2425  * Because "puts hello" has two args (2), composed of single tokens (1 1)
2426  * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2427  * composed of single tokens (1 1) and the last two of double tokens
2428  * (2 2).
2429  *
2430  * The precomputation of the command structure makes Jim_Eval() faster,
2431  * and simpler because there aren't dynamic lengths / allocations.
2432  *
2433  * -- {expand} handling --
2434  *
2435  * Expand is handled in a special way. When a command
2436  * contains at least an argument with the {expand} prefix,
2437  * the command structure presents a -1 before the integer
2438  * describing the number of arguments. This is used in order
2439  * to send the command exection to a different path in case
2440  * of {expand} and guarantee a fast path for the more common
2441  * case. Also, the integers describing the number of tokens
2442  * are expressed with negative sign, to allow for fast check
2443  * of what's an {expand}-prefixed argument and what not.
2444  *
2445  * For example the command:
2446  *
2447  * list {expand}{1 2}
2448  *
2449  * Will produce the following cmdstruct array:
2450  *
2451  * -1 2 1 -2
2452  *
2453  * -- the substFlags field of the structure --
2454  *
2455  * The scriptObj structure is used to represent both "script" objects
2456  * and "subst" objects. In the second case, the cmdStruct related
2457  * fields are not used at all, but there is an additional field used
2458  * that is 'substFlags': this represents the flags used to turn
2459  * the string into the intenral representation used to perform the
2460  * substitution. If this flags are not what the application requires
2461  * the scriptObj is created again. For example the script:
2462  *
2463  * subst -nocommands $string
2464  * subst -novariables $string
2465  *
2466  * Will recreate the internal representation of the $string object
2467  * two times.
2468  */
2469 typedef struct ScriptObj {
2470     int len; /* Length as number of tokens. */
2471     int commands; /* number of top-level commands in script. */
2472     ScriptToken *token; /* Tokens array. */
2473     int *cmdStruct; /* commands structure */
2474     int csLen; /* length of the cmdStruct array. */
2475     int substFlags; /* flags used for the compilation of "subst" objects */
2476     int inUse; /* Used to share a ScriptObj. Currently
2477               only used by Jim_EvalObj() as protection against
2478               shimmering of the currently evaluated object. */
2479     char *fileName;
2480 } ScriptObj;
2481
2482 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2483 {
2484     int i;
2485     struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2486
2487     script->inUse--;
2488     if (script->inUse != 0) return;
2489     for (i = 0; i < script->len; i++) {
2490         if (script->token[i].objPtr != NULL)
2491             Jim_DecrRefCount(interp, script->token[i].objPtr);
2492     }
2493     Jim_Free(script->token);
2494     Jim_Free(script->cmdStruct);
2495     Jim_Free(script->fileName);
2496     Jim_Free(script);
2497 }
2498
2499 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2500 {
2501     JIM_NOTUSED(interp);
2502     JIM_NOTUSED(srcPtr);
2503
2504     /* Just returns an simple string. */
2505     dupPtr->typePtr = NULL;
2506 }
2507
2508 /* Add a new token to the internal repr of a script object */
2509 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2510         char *strtoken, int len, int type, char *filename, int linenr)
2511 {
2512     int prevtype;
2513     struct ScriptToken *token;
2514
2515     prevtype = (script->len == 0) ? JIM_TT_EOL : \
2516         script->token[script->len-1].type;
2517     /* Skip tokens without meaning, like words separators
2518      * following a word separator or an end of command and
2519      * so on. */
2520     if (prevtype == JIM_TT_EOL) {
2521         if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2522             Jim_Free(strtoken);
2523             return;
2524         }
2525     } else if (prevtype == JIM_TT_SEP) {
2526         if (type == JIM_TT_SEP) {
2527             Jim_Free(strtoken);
2528             return;
2529         } else if (type == JIM_TT_EOL) {
2530             /* If an EOL is following by a SEP, drop the previous
2531              * separator. */
2532             script->len--;
2533             Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2534         }
2535     } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2536             type == JIM_TT_ESC && len == 0)
2537     {
2538         /* Don't add empty tokens used in interpolation */
2539         Jim_Free(strtoken);
2540         return;
2541     }
2542     /* Make space for a new istruction */
2543     script->len++;
2544     script->token = Jim_Realloc(script->token,
2545             sizeof(ScriptToken)*script->len);
2546     /* Initialize the new token */
2547     token = script->token+(script->len-1);
2548     token->type = type;
2549     /* Every object is intially as a string, but the
2550      * internal type may be specialized during execution of the
2551      * script. */
2552     token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2553     /* To add source info to SEP and EOL tokens is useless because
2554      * they will never by called as arguments of Jim_EvalObj(). */
2555     if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2556         JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2557     Jim_IncrRefCount(token->objPtr);
2558     token->linenr = linenr;
2559 }
2560
2561 /* Add an integer into the command structure field of the script object. */
2562 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2563 {
2564     script->csLen++;
2565     script->cmdStruct = Jim_Realloc(script->cmdStruct,
2566                     sizeof(int)*script->csLen);
2567     script->cmdStruct[script->csLen-1] = val;
2568 }
2569
2570 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2571  * of objPtr. Search nested script objects recursively. */
2572 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2573         ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2574 {
2575     int i;
2576
2577     for (i = 0; i < script->len; i++) {
2578         if (script->token[i].objPtr != objPtr &&
2579             Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2580             return script->token[i].objPtr;
2581         }
2582         /* Enter recursively on scripts only if the object
2583          * is not the same as the one we are searching for
2584          * shared occurrences. */
2585         if (script->token[i].objPtr->typePtr == &scriptObjType &&
2586             script->token[i].objPtr != objPtr) {
2587             Jim_Obj *foundObjPtr;
2588
2589             ScriptObj *subScript =
2590                 script->token[i].objPtr->internalRep.ptr;
2591             /* Don't recursively enter the script we are trying
2592              * to make shared to avoid circular references. */
2593             if (subScript == scriptBarrier) continue;
2594             if (subScript != script) {
2595                 foundObjPtr =
2596                     ScriptSearchLiteral(interp, subScript,
2597                             scriptBarrier, objPtr);
2598                 if (foundObjPtr != NULL)
2599                     return foundObjPtr;
2600             }
2601         }
2602     }
2603     return NULL;
2604 }
2605
2606 /* Share literals of a script recursively sharing sub-scripts literals. */
2607 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2608         ScriptObj *topLevelScript)
2609 {
2610     int i, j;
2611
2612     return;
2613     /* Try to share with toplevel object. */
2614     if (topLevelScript != NULL) {
2615         for (i = 0; i < script->len; i++) {
2616             Jim_Obj *foundObjPtr;
2617             char *str = script->token[i].objPtr->bytes;
2618
2619             if (script->token[i].objPtr->refCount != 1) continue;
2620             if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2621             if (strchr(str, ' ') || strchr(str, '\n')) continue;
2622             foundObjPtr = ScriptSearchLiteral(interp,
2623                     topLevelScript,
2624                     script, /* barrier */
2625                     script->token[i].objPtr);
2626             if (foundObjPtr != NULL) {
2627                 Jim_IncrRefCount(foundObjPtr);
2628                 Jim_DecrRefCount(interp,
2629                         script->token[i].objPtr);
2630                 script->token[i].objPtr = foundObjPtr;
2631             }
2632         }
2633     }
2634     /* Try to share locally */
2635     for (i = 0; i < script->len; i++) {
2636         char *str = script->token[i].objPtr->bytes;
2637
2638         if (script->token[i].objPtr->refCount != 1) continue;
2639         if (strchr(str, ' ') || strchr(str, '\n')) continue;
2640         for (j = 0; j < script->len; j++) {
2641             if (script->token[i].objPtr !=
2642                     script->token[j].objPtr &&
2643                 Jim_StringEqObj(script->token[i].objPtr,
2644                             script->token[j].objPtr, 0))
2645             {
2646                 Jim_IncrRefCount(script->token[j].objPtr);
2647                 Jim_DecrRefCount(interp,
2648                         script->token[i].objPtr);
2649                 script->token[i].objPtr =
2650                     script->token[j].objPtr;
2651             }
2652         }
2653     }
2654 }
2655
2656 /* This method takes the string representation of an object
2657  * as a Tcl script, and generates the pre-parsed internal representation
2658  * of the script. */
2659 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2660 {
2661     int scriptTextLen;
2662     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2663     struct JimParserCtx parser;
2664     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2665     ScriptToken *token;
2666     int args, tokens, start, end, i;
2667     int initialLineNumber;
2668     int propagateSourceInfo = 0;
2669
2670     script->len = 0;
2671     script->csLen = 0;
2672     script->commands = 0;
2673     script->token = NULL;
2674     script->cmdStruct = NULL;
2675     script->inUse = 1;
2676     /* Try to get information about filename / line number */
2677     if (objPtr->typePtr == &sourceObjType) {
2678         script->fileName =
2679             Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2680         initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2681         propagateSourceInfo = 1;
2682     } else {
2683         script->fileName = Jim_StrDup("?");
2684         initialLineNumber = 1;
2685     }
2686
2687     JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2688     while(!JimParserEof(&parser)) {
2689         char *token;
2690         int len, type, linenr;
2691
2692         JimParseScript(&parser);
2693         token = JimParserGetToken(&parser, &len, &type, &linenr);
2694         ScriptObjAddToken(interp, script, token, len, type,
2695                 propagateSourceInfo ? script->fileName : NULL,
2696                 linenr);
2697     }
2698     token = script->token;
2699
2700     /* Compute the command structure array
2701      * (see the ScriptObj struct definition for more info) */
2702     start = 0; /* Current command start token index */
2703     end = -1; /* Current command end token index */
2704     while (1) {
2705         int expand = 0; /* expand flag. set to 1 on {expand} form. */
2706         int interpolation = 0; /* set to 1 if there is at least one
2707                       argument of the command obtained via
2708                       interpolation of more tokens. */
2709         /* Search for the end of command, while
2710          * count the number of args. */
2711         start = ++end;
2712         if (start >= script->len) break;
2713         args = 1; /* Number of args in current command */
2714         while (token[end].type != JIM_TT_EOL) {
2715             if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2716                     token[end-1].type == JIM_TT_EOL)
2717             {
2718                 if (token[end].type == JIM_TT_STR &&
2719                     token[end+1].type != JIM_TT_SEP &&
2720                     token[end+1].type != JIM_TT_EOL &&
2721                     (!strcmp(token[end].objPtr->bytes, "expand") ||
2722                      !strcmp(token[end].objPtr->bytes, "*")))
2723                     expand++;
2724             }
2725             if (token[end].type == JIM_TT_SEP)
2726                 args++;
2727             end++;
2728         }
2729         interpolation = !((end-start+1) == args*2);
2730         /* Add the 'number of arguments' info into cmdstruct.
2731          * Negative value if there is list expansion involved. */
2732         if (expand)
2733             ScriptObjAddInt(script, -1);
2734         ScriptObjAddInt(script, args);
2735         /* Now add info about the number of tokens. */
2736         tokens = 0; /* Number of tokens in current argument. */
2737         expand = 0;
2738         for (i = start; i <= end; i++) {
2739             if (token[i].type == JIM_TT_SEP ||
2740                 token[i].type == JIM_TT_EOL)
2741             {
2742                 if (tokens == 1 && expand)
2743                     expand = 0;
2744                 ScriptObjAddInt(script,
2745                         expand ? -tokens : tokens);
2746
2747                 expand = 0;
2748                 tokens = 0;
2749                 continue;
2750             } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2751                    (!strcmp(token[i].objPtr->bytes, "expand") ||
2752                     !strcmp(token[i].objPtr->bytes, "*")))
2753             {
2754                 expand++;
2755             }
2756             tokens++;
2757         }
2758     }
2759     /* Perform literal sharing, but only for objects that appear
2760      * to be scripts written as literals inside the source code,
2761      * and not computed at runtime. Literal sharing is a costly
2762      * operation that should be done only against objects that
2763      * are likely to require compilation only the first time, and
2764      * then are executed multiple times. */
2765     if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2766         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2767         if (bodyObjPtr->typePtr == &scriptObjType) {
2768             ScriptObj *bodyScript =
2769                 bodyObjPtr->internalRep.ptr;
2770             ScriptShareLiterals(interp, script, bodyScript);
2771         }
2772     } else if (propagateSourceInfo) {
2773         ScriptShareLiterals(interp, script, NULL);
2774     }
2775     /* Free the old internal rep and set the new one. */
2776     Jim_FreeIntRep(interp, objPtr);
2777     Jim_SetIntRepPtr(objPtr, script);
2778     objPtr->typePtr = &scriptObjType;
2779     return JIM_OK;
2780 }
2781
2782 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
2783 {
2784     if (objPtr->typePtr != &scriptObjType) {
2785         SetScriptFromAny(interp, objPtr);
2786     }
2787     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
2788 }
2789
2790 /* -----------------------------------------------------------------------------
2791  * Commands
2792  * ---------------------------------------------------------------------------*/
2793
2794 /* Commands HashTable Type.
2795  *
2796  * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
2797 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
2798 {
2799     Jim_Cmd *cmdPtr = (void*) val;
2800
2801     if (cmdPtr->cmdProc == NULL) {
2802         Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2803         Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2804         if (cmdPtr->staticVars) {
2805             Jim_FreeHashTable(cmdPtr->staticVars);
2806             Jim_Free(cmdPtr->staticVars);
2807         }
2808     } else if (cmdPtr->delProc != NULL) {
2809             /* If it was a C coded command, call the delProc if any */
2810             cmdPtr->delProc(interp, cmdPtr->privData);
2811     }
2812     Jim_Free(val);
2813 }
2814
2815 static Jim_HashTableType JimCommandsHashTableType = {
2816     JimStringCopyHTHashFunction,        /* hash function */
2817     JimStringCopyHTKeyDup,        /* key dup */
2818     NULL,                    /* val dup */
2819     JimStringCopyHTKeyCompare,        /* key compare */
2820     JimStringCopyHTKeyDestructor,        /* key destructor */
2821     Jim_CommandsHT_ValDestructor        /* val destructor */
2822 };
2823
2824 /* ------------------------- Commands related functions --------------------- */
2825
2826 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
2827         Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
2828 {
2829     Jim_HashEntry *he;
2830     Jim_Cmd *cmdPtr;
2831
2832     he = Jim_FindHashEntry(&interp->commands, cmdName);
2833     if (he == NULL) { /* New command to create */
2834         cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2835         cmdPtr->cmdProc = cmdProc;
2836         cmdPtr->privData = privData;
2837         cmdPtr->delProc = delProc;
2838         Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2839     } else {
2840         Jim_InterpIncrProcEpoch(interp);
2841         /* Free the arglist/body objects if it was a Tcl procedure */
2842         cmdPtr = he->val;
2843         if (cmdPtr->cmdProc == NULL) {
2844             Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2845             Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2846             if (cmdPtr->staticVars) {
2847                 Jim_FreeHashTable(cmdPtr->staticVars);
2848                 Jim_Free(cmdPtr->staticVars);
2849             }
2850             cmdPtr->staticVars = NULL;
2851         } else if (cmdPtr->delProc != NULL) {
2852             /* If it was a C coded command, call the delProc if any */
2853             cmdPtr->delProc(interp, cmdPtr->privData);
2854         }
2855         cmdPtr->cmdProc = cmdProc;
2856         cmdPtr->privData = privData;
2857     }
2858     /* There is no need to increment the 'proc epoch' because
2859      * creation of a new procedure can never affect existing
2860      * cached commands. We don't do negative caching. */
2861     return JIM_OK;
2862 }
2863
2864 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
2865         Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
2866         int arityMin, int arityMax)
2867 {
2868     Jim_Cmd *cmdPtr;
2869
2870     cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2871     cmdPtr->cmdProc = NULL; /* Not a C coded command */
2872     cmdPtr->argListObjPtr = argListObjPtr;
2873     cmdPtr->bodyObjPtr = bodyObjPtr;
2874     Jim_IncrRefCount(argListObjPtr);
2875     Jim_IncrRefCount(bodyObjPtr);
2876     cmdPtr->arityMin = arityMin;
2877     cmdPtr->arityMax = arityMax;
2878     cmdPtr->staticVars = NULL;
2879    
2880     /* Create the statics hash table. */
2881     if (staticsListObjPtr) {
2882         int len, i;
2883
2884         Jim_ListLength(interp, staticsListObjPtr, &len);
2885         if (len != 0) {
2886             cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
2887             Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
2888                     interp);
2889             for (i = 0; i < len; i++) {
2890                 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
2891                 Jim_Var *varPtr;
2892                 int subLen;
2893
2894                 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
2895                 /* Check if it's composed of two elements. */
2896                 Jim_ListLength(interp, objPtr, &subLen);
2897                 if (subLen == 1 || subLen == 2) {
2898                     /* Try to get the variable value from the current
2899                      * environment. */
2900                     Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
2901                     if (subLen == 1) {
2902                         initObjPtr = Jim_GetVariable(interp, nameObjPtr,
2903                                 JIM_NONE);
2904                         if (initObjPtr == NULL) {
2905                             Jim_SetResult(interp,
2906                                     Jim_NewEmptyStringObj(interp));
2907                             Jim_AppendStrings(interp, Jim_GetResult(interp),
2908                                 "variable for initialization of static \"",
2909                                 Jim_GetString(nameObjPtr, NULL),
2910                                 "\" not found in the local context",
2911                                 NULL);
2912                             goto err;
2913                         }
2914                     } else {
2915                         Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
2916                     }
2917                     varPtr = Jim_Alloc(sizeof(*varPtr));
2918                     varPtr->objPtr = initObjPtr;
2919                     Jim_IncrRefCount(initObjPtr);
2920                     varPtr->linkFramePtr = NULL;
2921                     if (Jim_AddHashEntry(cmdPtr->staticVars,
2922                             Jim_GetString(nameObjPtr, NULL),
2923                             varPtr) != JIM_OK)
2924                     {
2925                         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2926                         Jim_AppendStrings(interp, Jim_GetResult(interp),
2927                             "static variable name \"",
2928                             Jim_GetString(objPtr, NULL), "\"",
2929                             " duplicated in statics list", NULL);
2930                         Jim_DecrRefCount(interp, initObjPtr);
2931                         Jim_Free(varPtr);
2932                         goto err;
2933                     }
2934                 } else {
2935                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2936                     Jim_AppendStrings(interp, Jim_GetResult(interp),
2937                         "too many fields in static specifier \"",
2938                         objPtr, "\"", NULL);
2939                     goto err;
2940                 }
2941             }
2942         }
2943     }
2944
2945     /* Add the new command */
2946
2947     /* it may already exist, so we try to delete the old one */
2948     if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
2949         /* There was an old procedure with the same name, this requires
2950          * a 'proc epoch' update. */
2951         Jim_InterpIncrProcEpoch(interp);
2952     }
2953     /* If a procedure with the same name didn't existed there is no need
2954      * to increment the 'proc epoch' because creation of a new procedure
2955      * can never affect existing cached commands. We don't do
2956      * negative caching. */
2957     Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2958     return JIM_OK;
2959
2960 err:
2961     Jim_FreeHashTable(cmdPtr->staticVars);
2962     Jim_Free(cmdPtr->staticVars);
2963     Jim_DecrRefCount(interp, argListObjPtr);
2964     Jim_DecrRefCount(interp, bodyObjPtr);
2965     Jim_Free(cmdPtr);
2966     return JIM_ERR;
2967 }
2968
2969 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
2970 {
2971     if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
2972         return JIM_ERR;
2973     Jim_InterpIncrProcEpoch(interp);
2974     return JIM_OK;
2975 }
2976
2977 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, 
2978         const char *newName)
2979 {
2980     Jim_Cmd *cmdPtr;
2981     Jim_HashEntry *he;
2982     Jim_Cmd *copyCmdPtr;
2983
2984     if (newName[0] == '\0') /* Delete! */
2985         return Jim_DeleteCommand(interp, oldName);
2986     /* Rename */
2987     he = Jim_FindHashEntry(&interp->commands, oldName);
2988     if (he == NULL)
2989         return JIM_ERR; /* Invalid command name */
2990     cmdPtr = he->val;
2991     copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
2992     *copyCmdPtr = *cmdPtr;
2993     /* In order to avoid that a procedure will get arglist/body/statics
2994      * freed by the hash table methods, fake a C-coded command
2995      * setting cmdPtr->cmdProc as not NULL */
2996     cmdPtr->cmdProc = (void*)1;
2997     /* Also make sure delProc is NULL. */
2998     cmdPtr->delProc = NULL;
2999     /* Destroy the old command, and make sure the new is freed
3000      * as well. */
3001     Jim_DeleteHashEntry(&interp->commands, oldName);
3002     Jim_DeleteHashEntry(&interp->commands, newName);
3003     /* Now the new command. We are sure it can't fail because
3004      * the target name was already freed. */
3005     Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3006     /* Increment the epoch */
3007     Jim_InterpIncrProcEpoch(interp);
3008     return JIM_OK;
3009 }
3010
3011 /* -----------------------------------------------------------------------------
3012  * Command object
3013  * ---------------------------------------------------------------------------*/
3014
3015 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3016
3017 static Jim_ObjType commandObjType = {
3018     "command",
3019     NULL,
3020     NULL,
3021     NULL,
3022     JIM_TYPE_REFERENCES,
3023 };
3024
3025 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3026 {
3027     Jim_HashEntry *he;
3028     const char *cmdName;
3029
3030     /* Get the string representation */
3031     cmdName = Jim_GetString(objPtr, NULL);
3032     /* Lookup this name into the commands hash table */
3033     he = Jim_FindHashEntry(&interp->commands, cmdName);
3034     if (he == NULL)
3035         return JIM_ERR;
3036
3037     /* Free the old internal repr and set the new one. */
3038     Jim_FreeIntRep(interp, objPtr);
3039     objPtr->typePtr = &commandObjType;
3040     objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3041     objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3042     return JIM_OK;
3043 }
3044
3045 /* This function returns the command structure for the command name
3046  * stored in objPtr. It tries to specialize the objPtr to contain
3047  * a cached info instead to perform the lookup into the hash table
3048  * every time. The information cached may not be uptodate, in such
3049  * a case the lookup is performed and the cache updated. */
3050 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3051 {
3052     if ((objPtr->typePtr != &commandObjType ||
3053         objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3054         SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3055         if (flags & JIM_ERRMSG) {
3056             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3057             Jim_AppendStrings(interp, Jim_GetResult(interp),
3058                 "invalid command name \"", objPtr->bytes, "\"",
3059                 NULL);
3060         }
3061         return NULL;
3062     }
3063     return objPtr->internalRep.cmdValue.cmdPtr;
3064 }
3065
3066 /* -----------------------------------------------------------------------------
3067  * Variables
3068  * ---------------------------------------------------------------------------*/
3069
3070 /* Variables HashTable Type.
3071  *
3072  * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3073 static void JimVariablesHTValDestructor(void *interp, void *val)
3074 {
3075     Jim_Var *varPtr = (void*) val;
3076
3077     Jim_DecrRefCount(interp, varPtr->objPtr);
3078     Jim_Free(val);
3079 }
3080
3081 static Jim_HashTableType JimVariablesHashTableType = {
3082     JimStringCopyHTHashFunction,        /* hash function */
3083     JimStringCopyHTKeyDup,              /* key dup */
3084     NULL,                               /* val dup */
3085     JimStringCopyHTKeyCompare,        /* key compare */
3086     JimStringCopyHTKeyDestructor,     /* key destructor */
3087     JimVariablesHTValDestructor       /* val destructor */
3088 };
3089
3090 /* -----------------------------------------------------------------------------
3091  * Variable object
3092  * ---------------------------------------------------------------------------*/
3093
3094 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3095
3096 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3097
3098 static Jim_ObjType variableObjType = {
3099     "variable",
3100     NULL,
3101     NULL,
3102     NULL,
3103     JIM_TYPE_REFERENCES,
3104 };
3105
3106 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3107  * is in the form "varname(key)". */
3108 static int Jim_NameIsDictSugar(const char *str, int len)
3109 {
3110     if (len == -1)
3111         len = strlen(str);
3112     if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3113         return 1;
3114     return 0;
3115 }
3116
3117 /* This method should be called only by the variable API.
3118  * It returns JIM_OK on success (variable already exists),
3119  * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3120  * a variable name, but syntax glue for [dict] i.e. the last
3121  * character is ')' */
3122 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3123 {
3124     Jim_HashEntry *he;
3125     const char *varName;
3126     int len;
3127
3128     /* Check if the object is already an uptodate variable */
3129     if (objPtr->typePtr == &variableObjType &&
3130         objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3131         return JIM_OK; /* nothing to do */
3132     /* Get the string representation */
3133     varName = Jim_GetString(objPtr, &len);
3134     /* Make sure it's not syntax glue to get/set dict. */
3135     if (Jim_NameIsDictSugar(varName, len))
3136             return JIM_DICT_SUGAR;
3137     /* Lookup this name into the variables hash table */
3138     he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3139     if (he == NULL) {
3140         /* Try with static vars. */
3141         if (interp->framePtr->staticVars == NULL)
3142             return JIM_ERR;
3143         if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3144             return JIM_ERR;
3145     }
3146     /* Free the old internal repr and set the new one. */
3147     Jim_FreeIntRep(interp, objPtr);
3148     objPtr->typePtr = &variableObjType;
3149     objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3150     objPtr->internalRep.varValue.varPtr = (void*)he->val;
3151     return JIM_OK;
3152 }
3153
3154 /* -------------------- Variables related functions ------------------------- */
3155 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3156         Jim_Obj *valObjPtr);
3157 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3158
3159 /* For now that's dummy. Variables lookup should be optimized
3160  * in many ways, with caching of lookups, and possibly with
3161  * a table of pre-allocated vars in every CallFrame for local vars.
3162  * All the caching should also have an 'epoch' mechanism similar
3163  * to the one used by Tcl for procedures lookup caching. */
3164
3165 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3166 {
3167     const char *name;
3168     Jim_Var *var;
3169     int err;
3170
3171     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3172         /* Check for [dict] syntax sugar. */
3173         if (err == JIM_DICT_SUGAR)
3174             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3175         /* New variable to create */
3176         name = Jim_GetString(nameObjPtr, NULL);
3177
3178         var = Jim_Alloc(sizeof(*var));
3179         var->objPtr = valObjPtr;
3180         Jim_IncrRefCount(valObjPtr);
3181         var->linkFramePtr = NULL;
3182         /* Insert the new variable */
3183         Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3184         /* Make the object int rep a variable */
3185         Jim_FreeIntRep(interp, nameObjPtr);
3186         nameObjPtr->typePtr = &variableObjType;
3187         nameObjPtr->internalRep.varValue.callFrameId =
3188             interp->framePtr->id;
3189         nameObjPtr->internalRep.varValue.varPtr = var;
3190     } else {
3191         var = nameObjPtr->internalRep.varValue.varPtr;
3192         if (var->linkFramePtr == NULL) {
3193             Jim_IncrRefCount(valObjPtr);
3194             Jim_DecrRefCount(interp, var->objPtr);
3195             var->objPtr = valObjPtr;
3196         } else { /* Else handle the link */
3197             Jim_CallFrame *savedCallFrame;
3198
3199             savedCallFrame = interp->framePtr;
3200             interp->framePtr = var->linkFramePtr;
3201             err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3202             interp->framePtr = savedCallFrame;
3203             if (err != JIM_OK)
3204                 return err;
3205         }
3206     }
3207     return JIM_OK;
3208 }
3209
3210 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3211 {
3212     Jim_Obj *nameObjPtr;
3213     int result;
3214
3215     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3216     Jim_IncrRefCount(nameObjPtr);
3217     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3218     Jim_DecrRefCount(interp, nameObjPtr);
3219     return result;
3220 }
3221
3222 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3223 {
3224     Jim_CallFrame *savedFramePtr;
3225     int result;
3226
3227     savedFramePtr = interp->framePtr;
3228     interp->framePtr = interp->topFramePtr;
3229     result = Jim_SetVariableStr(interp, name, objPtr);
3230     interp->framePtr = savedFramePtr;
3231     return result;
3232 }
3233
3234 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3235 {
3236     Jim_Obj *nameObjPtr, *valObjPtr;
3237     int result;
3238
3239     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3240     valObjPtr = Jim_NewStringObj(interp, val, -1);
3241     Jim_IncrRefCount(nameObjPtr);
3242     Jim_IncrRefCount(valObjPtr);
3243     result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3244     Jim_DecrRefCount(interp, nameObjPtr);
3245     Jim_DecrRefCount(interp, valObjPtr);
3246     return result;
3247 }
3248
3249 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3250         Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3251 {
3252     const char *varName;
3253     int len;
3254
3255     /* Check for cycles. */
3256     if (interp->framePtr == targetCallFrame) {
3257         Jim_Obj *objPtr = targetNameObjPtr;
3258         Jim_Var *varPtr;
3259         /* Cycles are only possible with 'uplevel 0' */
3260         while(1) {
3261             if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3262                 Jim_SetResultString(interp,
3263                     "can't upvar from variable to itself", -1);
3264                 return JIM_ERR;
3265             }
3266             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3267                 break;
3268             varPtr = objPtr->internalRep.varValue.varPtr;
3269             if (varPtr->linkFramePtr != targetCallFrame) break;
3270             objPtr = varPtr->objPtr;
3271         }
3272     }
3273     varName = Jim_GetString(nameObjPtr, &len);
3274     if (Jim_NameIsDictSugar(varName, len)) {
3275         Jim_SetResultString(interp,
3276             "Dict key syntax invalid as link source", -1);
3277         return JIM_ERR;
3278     }
3279     /* Perform the binding */
3280     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3281     /* We are now sure 'nameObjPtr' type is variableObjType */
3282     nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3283     return JIM_OK;
3284 }
3285
3286 /* Return the Jim_Obj pointer associated with a variable name,
3287  * or NULL if the variable was not found in the current context.
3288  * The same optimization discussed in the comment to the
3289  * 'SetVariable' function should apply here. */
3290 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3291 {
3292     int err;
3293
3294     /* All the rest is handled here */
3295     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3296         /* Check for [dict] syntax sugar. */
3297         if (err == JIM_DICT_SUGAR)
3298             return JimDictSugarGet(interp, nameObjPtr);
3299         if (flags & JIM_ERRMSG) {
3300             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3301             Jim_AppendStrings(interp, Jim_GetResult(interp),
3302                 "can't read \"", nameObjPtr->bytes,
3303                 "\": no such variable", NULL);
3304         }
3305         return NULL;
3306     } else {
3307         Jim_Var *varPtr;
3308         Jim_Obj *objPtr;
3309         Jim_CallFrame *savedCallFrame;
3310
3311         varPtr = nameObjPtr->internalRep.varValue.varPtr;
3312         if (varPtr->linkFramePtr == NULL)
3313             return varPtr->objPtr;
3314         /* The variable is a link? Resolve it. */
3315         savedCallFrame = interp->framePtr;
3316         interp->framePtr = varPtr->linkFramePtr;
3317         objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3318         if (objPtr == NULL && flags & JIM_ERRMSG) {
3319             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3320             Jim_AppendStrings(interp, Jim_GetResult(interp),
3321                 "can't read \"", nameObjPtr->bytes,
3322                 "\": no such variable", NULL);
3323         }
3324         interp->framePtr = savedCallFrame;
3325         return objPtr;
3326     }
3327 }
3328
3329 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3330         int flags)
3331 {
3332     Jim_CallFrame *savedFramePtr;
3333     Jim_Obj *objPtr;
3334
3335     savedFramePtr = interp->framePtr;
3336     interp->framePtr = interp->topFramePtr;
3337     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3338     interp->framePtr = savedFramePtr;
3339
3340     return objPtr;
3341 }
3342
3343 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3344 {
3345     Jim_Obj *nameObjPtr, *varObjPtr;
3346
3347     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3348     Jim_IncrRefCount(nameObjPtr);
3349     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3350     Jim_DecrRefCount(interp, nameObjPtr);
3351     return varObjPtr;
3352 }
3353
3354 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3355         int flags)
3356 {
3357     Jim_CallFrame *savedFramePtr;
3358     Jim_Obj *objPtr;
3359
3360     savedFramePtr = interp->framePtr;
3361     interp->framePtr = interp->topFramePtr;
3362     objPtr = Jim_GetVariableStr(interp, name, flags);
3363     interp->framePtr = savedFramePtr;
3364
3365     return objPtr;
3366 }
3367
3368 /* Unset a variable.
3369  * Note: On success unset invalidates all the variable objects created
3370  * in the current call frame incrementing. */
3371 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3372 {
3373     const char *name;
3374     Jim_Var *varPtr;
3375     int err;
3376     
3377     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3378         /* Check for [dict] syntax sugar. */
3379         if (err == JIM_DICT_SUGAR)
3380             return JimDictSugarSet(interp, nameObjPtr, NULL);
3381         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3382         Jim_AppendStrings(interp, Jim_GetResult(interp),
3383             "can't unset \"", nameObjPtr->bytes,
3384             "\": no such variable", NULL);
3385         return JIM_ERR; /* var not found */
3386     }
3387     varPtr = nameObjPtr->internalRep.varValue.varPtr;
3388     /* If it's a link call UnsetVariable recursively */
3389     if (varPtr->linkFramePtr) {
3390         int retval;
3391
3392         Jim_CallFrame *savedCallFrame;
3393
3394         savedCallFrame = interp->framePtr;
3395         interp->framePtr = varPtr->linkFramePtr;
3396         retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3397         interp->framePtr = savedCallFrame;
3398         if (retval != JIM_OK && flags & JIM_ERRMSG) {
3399             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3400             Jim_AppendStrings(interp, Jim_GetResult(interp),
3401                 "can't unset \"", nameObjPtr->bytes,
3402                 "\": no such variable", NULL);
3403         }
3404         return retval;
3405     } else {
3406         name = Jim_GetString(nameObjPtr, NULL);
3407         if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3408                 != JIM_OK) return JIM_ERR;
3409         /* Change the callframe id, invalidating var lookup caching */
3410         JimChangeCallFrameId(interp, interp->framePtr);
3411         return JIM_OK;
3412     }
3413 }
3414
3415 /* ----------  Dict syntax sugar (similar to array Tcl syntax) -------------- */
3416
3417 /* Given a variable name for [dict] operation syntax sugar,
3418  * this function returns two objects, the first with the name
3419  * of the variable to set, and the second with the rispective key.
3420  * For example "foo(bar)" will return objects with string repr. of
3421  * "foo" and "bar".
3422  *
3423  * The returned objects have refcount = 1. The function can't fail. */
3424 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3425         Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3426 {
3427     const char *str, *p;
3428     char *t;
3429     int len, keyLen, nameLen;
3430     Jim_Obj *varObjPtr, *keyObjPtr;
3431
3432     str = Jim_GetString(objPtr, &len);
3433     p = strchr(str, '(');
3434     p++;
3435     keyLen = len-((p-str)+1);
3436     nameLen = (p-str)-1;
3437     /* Create the objects with the variable name and key. */
3438     t = Jim_Alloc(nameLen+1);
3439     memcpy(t, str, nameLen);
3440     t[nameLen] = '\0';
3441     varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3442
3443     t = Jim_Alloc(keyLen+1);
3444     memcpy(t, p, keyLen);
3445     t[keyLen] = '\0';
3446     keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3447
3448     Jim_IncrRefCount(varObjPtr);
3449     Jim_IncrRefCount(keyObjPtr);
3450     *varPtrPtr = varObjPtr;
3451     *keyPtrPtr = keyObjPtr;
3452 }
3453
3454 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3455  * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3456 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3457         Jim_Obj *valObjPtr)
3458 {
3459     Jim_Obj *varObjPtr, *keyObjPtr;
3460     int err = JIM_OK;
3461
3462     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3463     err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3464             valObjPtr);
3465     Jim_DecrRefCount(interp, varObjPtr);
3466     Jim_DecrRefCount(interp, keyObjPtr);
3467     return err;
3468 }
3469
3470 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3471 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3472 {
3473     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3474
3475     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3476     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3477     if (!dictObjPtr) {
3478         resObjPtr = NULL;
3479         goto err;
3480     }
3481     if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3482             != JIM_OK) {
3483         resObjPtr = NULL;
3484     }
3485 err:
3486     Jim_DecrRefCount(interp, varObjPtr);
3487     Jim_DecrRefCount(interp, keyObjPtr);
3488     return resObjPtr;
3489 }
3490
3491 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3492
3493 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3494 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3495         Jim_Obj *dupPtr);
3496
3497 static Jim_ObjType dictSubstObjType = {
3498     "dict-substitution",
3499     FreeDictSubstInternalRep,
3500     DupDictSubstInternalRep,
3501     NULL,
3502     JIM_TYPE_NONE,
3503 };
3504
3505 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3506 {
3507     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3508     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3509 }
3510
3511 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3512         Jim_Obj *dupPtr)
3513 {
3514     JIM_NOTUSED(interp);
3515
3516     dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3517         srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3518     dupPtr->internalRep.dictSubstValue.indexObjPtr =
3519         srcPtr->internalRep.dictSubstValue.indexObjPtr;
3520     dupPtr->typePtr = &dictSubstObjType;
3521 }
3522
3523 /* This function is used to expand [dict get] sugar in the form
3524  * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3525  * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3526  * object that is *guaranteed* to be in the form VARNAME(INDEX).
3527  * The 'index' part is [subst]ituted, and is used to lookup a key inside
3528  * the [dict]ionary contained in variable VARNAME. */
3529 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3530 {
3531     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3532     Jim_Obj *substKeyObjPtr = NULL;
3533
3534     if (objPtr->typePtr != &dictSubstObjType) {
3535         JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3536         Jim_FreeIntRep(interp, objPtr);
3537         objPtr->typePtr = &dictSubstObjType;
3538         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3539         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3540     }
3541     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3542                 &substKeyObjPtr, JIM_NONE)
3543             != JIM_OK) {
3544         substKeyObjPtr = NULL;
3545         goto err;
3546     }
3547     Jim_IncrRefCount(substKeyObjPtr);
3548     dictObjPtr = Jim_GetVariable(interp,
3549             objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3550     if (!dictObjPtr) {
3551         resObjPtr = NULL;
3552         goto err;
3553     }
3554     if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3555             != JIM_OK) {
3556         resObjPtr = NULL;
3557         goto err;
3558     }
3559 err:
3560     if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3561     return resObjPtr;
3562 }
3563
3564 /* -----------------------------------------------------------------------------
3565  * CallFrame
3566  * ---------------------------------------------------------------------------*/
3567
3568 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3569 {
3570     Jim_CallFrame *cf;
3571     if (interp->freeFramesList) {
3572         cf = interp->freeFramesList;
3573         interp->freeFramesList = cf->nextFramePtr;
3574     } else {
3575         cf = Jim_Alloc(sizeof(*cf));
3576         cf->vars.table = NULL;
3577     }
3578
3579     cf->id = interp->callFrameEpoch++;
3580     cf->parentCallFrame = NULL;
3581     cf->argv = NULL;
3582     cf->argc = 0;
3583     cf->procArgsObjPtr = NULL;
3584     cf->procBodyObjPtr = NULL;
3585     cf->nextFramePtr = NULL;
3586     cf->staticVars = NULL;
3587     if (cf->vars.table == NULL)
3588         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3589     return cf;
3590 }
3591
3592 /* Used to invalidate every caching related to callframe stability. */
3593 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3594 {
3595     cf->id = interp->callFrameEpoch++;
3596 }
3597
3598 #define JIM_FCF_NONE 0 /* no flags */
3599 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3600 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3601         int flags)
3602 {
3603     if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3604     if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3605     if (!(flags & JIM_FCF_NOHT))
3606         Jim_FreeHashTable(&cf->vars);
3607     else {
3608         int i;
3609         Jim_HashEntry **table = cf->vars.table, *he;
3610
3611         for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3612             he = table[i];
3613             while (he != NULL) {
3614                 Jim_HashEntry *nextEntry = he->next;
3615                 Jim_Var *varPtr = (void*) he->val;
3616
3617                 Jim_DecrRefCount(interp, varPtr->objPtr);
3618                 Jim_Free(he->val);
3619                 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3620                 Jim_Free(he);
3621                 table[i] = NULL;
3622                 he = nextEntry;
3623             }
3624         }
3625         cf->vars.used = 0;
3626     }
3627     cf->nextFramePtr = interp->freeFramesList;
3628     interp->freeFramesList = cf;
3629 }
3630
3631 /* -----------------------------------------------------------------------------
3632  * References
3633  * ---------------------------------------------------------------------------*/
3634
3635 /* References HashTable Type.
3636  *
3637  * Keys are jim_wide integers, dynamically allocated for now but in the
3638  * future it's worth to cache this 8 bytes objects. Values are poitners
3639  * to Jim_References. */
3640 static void JimReferencesHTValDestructor(void *interp, void *val)
3641 {
3642     Jim_Reference *refPtr = (void*) val;
3643
3644     Jim_DecrRefCount(interp, refPtr->objPtr);
3645     if (refPtr->finalizerCmdNamePtr != NULL) {
3646         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3647     }
3648     Jim_Free(val);
3649 }
3650
3651 unsigned int JimReferencesHTHashFunction(const void *key)
3652 {
3653     /* Only the least significant bits are used. */
3654     const jim_wide *widePtr = key;
3655     unsigned int intValue = (unsigned int) *widePtr;
3656     return Jim_IntHashFunction(intValue);
3657 }
3658
3659 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3660 {
3661     /* Only the least significant bits are used. */
3662     const jim_wide *widePtr = key;
3663     unsigned int intValue = (unsigned int) *widePtr;
3664     return intValue; /* identity function. */
3665 }
3666
3667 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3668 {
3669     void *copy = Jim_Alloc(sizeof(jim_wide));
3670     JIM_NOTUSED(privdata);
3671
3672     memcpy(copy, key, sizeof(jim_wide));
3673     return copy;
3674 }
3675
3676 int JimReferencesHTKeyCompare(void *privdata, const void *key1, 
3677         const void *key2)
3678 {
3679     JIM_NOTUSED(privdata);
3680
3681     return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3682 }
3683
3684 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3685 {
3686     JIM_NOTUSED(privdata);
3687
3688     Jim_Free((void*)key);
3689 }
3690
3691 static Jim_HashTableType JimReferencesHashTableType = {
3692     JimReferencesHTHashFunction,    /* hash function */
3693     JimReferencesHTKeyDup,          /* key dup */
3694     NULL,                           /* val dup */
3695     JimReferencesHTKeyCompare,      /* key compare */
3696     JimReferencesHTKeyDestructor,   /* key destructor */
3697     JimReferencesHTValDestructor    /* val destructor */
3698 };
3699
3700 /* -----------------------------------------------------------------------------
3701  * Reference object type and References API
3702  * ---------------------------------------------------------------------------*/
3703
3704 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3705
3706 static Jim_ObjType referenceObjType = {
3707     "reference",
3708     NULL,
3709     NULL,
3710     UpdateStringOfReference,
3711     JIM_TYPE_REFERENCES,
3712 };
3713
3714 void UpdateStringOfReference(struct Jim_Obj *objPtr)
3715 {
3716     int len;
3717     char buf[JIM_REFERENCE_SPACE+1];
3718     Jim_Reference *refPtr;
3719
3720     refPtr = objPtr->internalRep.refValue.refPtr;
3721     len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3722     objPtr->bytes = Jim_Alloc(len+1);
3723     memcpy(objPtr->bytes, buf, len+1);
3724     objPtr->length = len;
3725 }
3726
3727 /* returns true if 'c' is a valid reference tag character.
3728  * i.e. inside the range [_a-zA-Z0-9] */
3729 static int isrefchar(int c)
3730 {
3731     if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3732         (c >= '0' && c <= '9')) return 1;
3733     return 0;
3734 }
3735
3736 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3737 {
3738     jim_wide wideValue;
3739     int i, len;
3740     const char *str, *start, *end;
3741     char refId[21];
3742     Jim_Reference *refPtr;
3743     Jim_HashEntry *he;
3744
3745     /* Get the string representation */
3746     str = Jim_GetString(objPtr, &len);
3747     /* Check if it looks like a reference */
3748     if (len < JIM_REFERENCE_SPACE) goto badformat;
3749     /* Trim spaces */
3750     start = str;
3751     end = str+len-1;
3752     while (*start == ' ') start++;
3753     while (*end == ' ' && end > start) end--;
3754     if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3755     /* <reference.<1234567>.%020> */
3756     if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3757     if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3758     /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3759     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3760         if (!isrefchar(start[12+i])) goto badformat;
3761     }
3762     /* Extract info from the refernece. */
3763     memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3764     refId[20] = '\0';
3765     /* Try to convert the ID into a jim_wide */
3766     if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3767     /* Check if the reference really exists! */
3768     he = Jim_FindHashEntry(&interp->references, &wideValue);
3769     if (he == NULL) {
3770         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3771         Jim_AppendStrings(interp, Jim_GetResult(interp),
3772                 "Invalid reference ID \"", str, "\"", NULL);
3773         return JIM_ERR;
3774     }
3775     refPtr = he->val;
3776     /* Free the old internal repr and set the new one. */
3777     Jim_FreeIntRep(interp, objPtr);
3778     objPtr->typePtr = &referenceObjType;
3779     objPtr->internalRep.refValue.id = wideValue;
3780     objPtr->internalRep.refValue.refPtr = refPtr;
3781     return JIM_OK;
3782
3783 badformat:
3784     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3785     Jim_AppendStrings(interp, Jim_GetResult(interp),
3786             "expected reference but got \"", str, "\"", NULL);
3787     return JIM_ERR;
3788 }
3789
3790 /* Returns a new reference pointing to objPtr, having cmdNamePtr
3791  * as finalizer command (or NULL if there is no finalizer).
3792  * The returned reference object has refcount = 0. */
3793 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
3794         Jim_Obj *cmdNamePtr)
3795 {
3796     struct Jim_Reference *refPtr;
3797     jim_wide wideValue = interp->referenceNextId;
3798     Jim_Obj *refObjPtr;
3799     const char *tag;
3800     int tagLen, i;
3801
3802     /* Perform the Garbage Collection if needed. */
3803     Jim_CollectIfNeeded(interp);
3804
3805     refPtr = Jim_Alloc(sizeof(*refPtr));
3806     refPtr->objPtr = objPtr;
3807     Jim_IncrRefCount(objPtr);
3808     refPtr->finalizerCmdNamePtr = cmdNamePtr;
3809     if (cmdNamePtr)
3810         Jim_IncrRefCount(cmdNamePtr);
3811     Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
3812     refObjPtr = Jim_NewObj(interp);
3813     refObjPtr->typePtr = &referenceObjType;
3814     refObjPtr->bytes = NULL;
3815     refObjPtr->internalRep.refValue.id = interp->referenceNextId;
3816     refObjPtr->internalRep.refValue.refPtr = refPtr;
3817     interp->referenceNextId++;
3818     /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
3819      * that does not pass the 'isrefchar' test is replaced with '_' */
3820     tag = Jim_GetString(tagPtr, &tagLen);
3821     if (tagLen > JIM_REFERENCE_TAGLEN)
3822         tagLen = JIM_REFERENCE_TAGLEN;
3823     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3824         if (i < tagLen)
3825             refPtr->tag[i] = tag[i];
3826         else
3827             refPtr->tag[i] = '_';
3828     }
3829     refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
3830     return refObjPtr;
3831 }
3832
3833 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
3834 {
3835     if (objPtr->typePtr != &referenceObjType &&
3836         SetReferenceFromAny(interp, objPtr) == JIM_ERR)
3837         return NULL;
3838     return objPtr->internalRep.refValue.refPtr;
3839 }
3840
3841 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
3842 {
3843     Jim_Reference *refPtr;
3844
3845     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3846         return JIM_ERR;
3847     Jim_IncrRefCount(cmdNamePtr);
3848     if (refPtr->finalizerCmdNamePtr)
3849         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3850     refPtr->finalizerCmdNamePtr = cmdNamePtr;
3851     return JIM_OK;
3852 }
3853
3854 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
3855 {
3856     Jim_Reference *refPtr;
3857
3858     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3859         return JIM_ERR;
3860     *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
3861     return JIM_OK;
3862 }
3863
3864 /* -----------------------------------------------------------------------------
3865  * References Garbage Collection
3866  * ---------------------------------------------------------------------------*/
3867
3868 /* This the hash table type for the "MARK" phase of the GC */
3869 static Jim_HashTableType JimRefMarkHashTableType = {
3870     JimReferencesHTHashFunction,    /* hash function */
3871     JimReferencesHTKeyDup,          /* key dup */
3872     NULL,                           /* val dup */
3873     JimReferencesHTKeyCompare,      /* key compare */
3874     JimReferencesHTKeyDestructor,   /* key destructor */
3875     NULL                            /* val destructor */
3876 };
3877
3878 /* #define JIM_DEBUG_GC 1 */
3879
3880 /* Performs the garbage collection. */
3881 int Jim_Collect(Jim_Interp *interp)
3882 {
3883     Jim_HashTable marks;
3884     Jim_HashTableIterator *htiter;
3885     Jim_HashEntry *he;
3886     Jim_Obj *objPtr;
3887     int collected = 0;
3888
3889     /* Avoid recursive calls */
3890     if (interp->lastCollectId == -1) {
3891         /* Jim_Collect() already running. Return just now. */
3892         return 0;
3893     }
3894     interp->lastCollectId = -1;
3895
3896     /* Mark all the references found into the 'mark' hash table.
3897      * The references are searched in every live object that
3898      * is of a type that can contain references. */
3899     Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
3900     objPtr = interp->liveList;
3901     while(objPtr) {
3902         if (objPtr->typePtr == NULL ||
3903             objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
3904             const char *str, *p;
3905             int len;
3906
3907             /* If the object is of type reference, to get the
3908              * Id is simple... */
3909             if (objPtr->typePtr == &referenceObjType) {
3910                 Jim_AddHashEntry(&marks,
3911                     &objPtr->internalRep.refValue.id, NULL);
3912 #ifdef JIM_DEBUG_GC
3913                 fprintf(interp->stdout_,
3914                     "MARK (reference): %d refcount: %d" JIM_NL, 
3915                     (int) objPtr->internalRep.refValue.id,
3916                     objPtr->refCount);
3917 #endif
3918                 objPtr = objPtr->nextObjPtr;
3919                 continue;
3920             }
3921             /* Get the string repr of the object we want
3922              * to scan for references. */
3923             p = str = Jim_GetString(objPtr, &len);
3924             /* Skip objects too little to contain references. */
3925             if (len < JIM_REFERENCE_SPACE) {
3926                 objPtr = objPtr->nextObjPtr;
3927                 continue;
3928             }
3929             /* Extract references from the object string repr. */
3930             while(1) {
3931                 int i;
3932                 jim_wide id;
3933                 char buf[21];
3934
3935                 if ((p = strstr(p, "<reference.<")) == NULL)
3936                     break;
3937                 /* Check if it's a valid reference. */
3938                 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
3939                 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
3940                 for (i = 21; i <= 40; i++)
3941                     if (!isdigit((int)p[i]))
3942                         break;
3943                 /* Get the ID */
3944                 memcpy(buf, p+21, 20);
3945                 buf[20] = '\0';
3946                 Jim_StringToWide(buf, &id, 10);
3947
3948                 /* Ok, a reference for the given ID
3949                  * was found. Mark it. */
3950                 Jim_AddHashEntry(&marks, &id, NULL);
3951 #ifdef JIM_DEBUG_GC
3952                 fprintf(interp->stdout_,"MARK: %d" JIM_NL, (int)id);
3953 #endif
3954                 p += JIM_REFERENCE_SPACE;
3955             }
3956         }
3957         objPtr = objPtr->nextObjPtr;
3958     }
3959
3960     /* Run the references hash table to destroy every reference that
3961      * is not referenced outside (not present in the mark HT). */
3962     htiter = Jim_GetHashTableIterator(&interp->references);
3963     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
3964         const jim_wide *refId;
3965         Jim_Reference *refPtr;
3966
3967         refId = he->key;
3968         /* Check if in the mark phase we encountered
3969          * this reference. */
3970         if (Jim_FindHashEntry(&marks, refId) == NULL) {
3971 #ifdef JIM_DEBUG_GC
3972             fprintf(interp->stdout_,"COLLECTING %d" JIM_NL, (int)*refId);
3973 #endif
3974             collected++;
3975             /* Drop the reference, but call the
3976              * finalizer first if registered. */
3977             refPtr = he->val;
3978             if (refPtr->finalizerCmdNamePtr) {
3979                 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
3980                 Jim_Obj *objv[3], *oldResult;
3981
3982                 JimFormatReference(refstr, refPtr, *refId);
3983
3984                 objv[0] = refPtr->finalizerCmdNamePtr;
3985                 objv[1] = Jim_NewStringObjNoAlloc(interp,
3986                         refstr, 32);
3987                 objv[2] = refPtr->objPtr;
3988                 Jim_IncrRefCount(objv[0]);
3989                 Jim_IncrRefCount(objv[1]);
3990                 Jim_IncrRefCount(objv[2]);
3991
3992                 /* Drop the reference itself */
3993                 Jim_DeleteHashEntry(&interp->references, refId);
3994
3995                 /* Call the finalizer. Errors ignored. */
3996                 oldResult = interp->result;
3997                 Jim_IncrRefCount(oldResult);
3998                 Jim_EvalObjVector(interp, 3, objv);
3999                 Jim_SetResult(interp, oldResult);
4000                 Jim_DecrRefCount(interp, oldResult);
4001
4002                 Jim_DecrRefCount(interp, objv[0]);
4003                 Jim_DecrRefCount(interp, objv[1]);
4004                 Jim_DecrRefCount(interp, objv[2]);
4005             } else {
4006                 Jim_DeleteHashEntry(&interp->references, refId);
4007             }
4008         }
4009     }
4010     Jim_FreeHashTableIterator(htiter);
4011     Jim_FreeHashTable(&marks);
4012     interp->lastCollectId = interp->referenceNextId;
4013     interp->lastCollectTime = time(NULL);
4014     return collected;
4015 }
4016
4017 #define JIM_COLLECT_ID_PERIOD 5000
4018 #define JIM_COLLECT_TIME_PERIOD 300
4019
4020 void Jim_CollectIfNeeded(Jim_Interp *interp)
4021 {
4022     jim_wide elapsedId;
4023     int elapsedTime;
4024     
4025     elapsedId = interp->referenceNextId - interp->lastCollectId;
4026     elapsedTime = time(NULL) - interp->lastCollectTime;
4027
4028
4029     if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4030         elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4031         Jim_Collect(interp);
4032     }
4033 }
4034
4035 /* -----------------------------------------------------------------------------
4036  * Interpreter related functions
4037  * ---------------------------------------------------------------------------*/
4038
4039 Jim_Interp *Jim_CreateInterp(void)
4040 {
4041     Jim_Interp *i = Jim_Alloc(sizeof(*i));
4042     Jim_Obj *pathPtr;
4043
4044     i->errorLine = 0;
4045     i->errorFileName = Jim_StrDup("");
4046     i->numLevels = 0;
4047     i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4048     i->returnCode = JIM_OK;
4049     i->exitCode = 0;
4050     i->procEpoch = 0;
4051     i->callFrameEpoch = 0;
4052     i->liveList = i->freeList = NULL;
4053     i->scriptFileName = Jim_StrDup("");
4054     i->referenceNextId = 0;
4055     i->lastCollectId = 0;
4056     i->lastCollectTime = time(NULL);
4057     i->freeFramesList = NULL;
4058     i->prngState = NULL;
4059     i->evalRetcodeLevel = -1;
4060     i->stdin_ = stdin;
4061     i->stdout_ = stdout;
4062     i->stderr_ = stderr;
4063
4064     /* Note that we can create objects only after the
4065      * interpreter liveList and freeList pointers are
4066      * initialized to NULL. */
4067     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4068     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4069     Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4070             NULL);
4071     Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4072     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4073     Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4074     i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4075     i->emptyObj = Jim_NewEmptyStringObj(i);
4076     i->result = i->emptyObj;
4077     i->stackTrace = Jim_NewListObj(i, NULL, 0);
4078     i->unknown = Jim_NewStringObj(i, "unknown", -1);
4079     Jim_IncrRefCount(i->emptyObj);
4080     Jim_IncrRefCount(i->result);
4081     Jim_IncrRefCount(i->stackTrace);
4082     Jim_IncrRefCount(i->unknown);
4083
4084     /* Initialize key variables every interpreter should contain */
4085     pathPtr = Jim_NewStringObj(i, "./", -1);
4086     Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4087     Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4088
4089     /* Export the core API to extensions */
4090     JimRegisterCoreApi(i);
4091     return i;
4092 }
4093
4094 /* This is the only function Jim exports directly without
4095  * to use the STUB system. It is only used by embedders
4096  * in order to get an interpreter with the Jim API pointers
4097  * registered. */
4098 Jim_Interp *ExportedJimCreateInterp(void)
4099 {
4100     return Jim_CreateInterp();
4101 }
4102
4103 void Jim_FreeInterp(Jim_Interp *i)
4104 {
4105     Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4106     Jim_Obj *objPtr, *nextObjPtr;
4107
4108     Jim_DecrRefCount(i, i->emptyObj);
4109     Jim_DecrRefCount(i, i->result);
4110     Jim_DecrRefCount(i, i->stackTrace);
4111     Jim_DecrRefCount(i, i->unknown);
4112     Jim_Free((void*)i->errorFileName);
4113     Jim_Free((void*)i->scriptFileName);
4114     Jim_FreeHashTable(&i->commands);
4115     Jim_FreeHashTable(&i->references);
4116     Jim_FreeHashTable(&i->stub);
4117     Jim_FreeHashTable(&i->assocData);
4118     Jim_FreeHashTable(&i->packages);
4119     Jim_Free(i->prngState);
4120     /* Free the call frames list */
4121     while(cf) {
4122         prevcf = cf->parentCallFrame;
4123         JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4124         cf = prevcf;
4125     }
4126     /* Check that the live object list is empty, otherwise
4127      * there is a memory leak. */
4128     if (i->liveList != NULL) {
4129         Jim_Obj *objPtr = i->liveList;
4130     
4131         fprintf(i->stdout_,JIM_NL "-------------------------------------" JIM_NL);
4132         fprintf(i->stdout_,"Objects still in the free list:" JIM_NL);
4133         while(objPtr) {
4134             const char *type = objPtr->typePtr ?
4135                 objPtr->typePtr->name : "";
4136             fprintf(i->stdout_,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4137                     objPtr, type,
4138                     objPtr->bytes ? objPtr->bytes
4139                     : "(null)", objPtr->refCount);
4140             if (objPtr->typePtr == &sourceObjType) {
4141                 fprintf(i->stdout_, "FILE %s LINE %d" JIM_NL,
4142                 objPtr->internalRep.sourceValue.fileName,
4143                 objPtr->internalRep.sourceValue.lineNumber);
4144             }
4145             objPtr = objPtr->nextObjPtr;
4146         }
4147         fprintf(stdout, "-------------------------------------" JIM_NL JIM_NL);
4148         Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4149     }
4150     /* Free all the freed objects. */
4151     objPtr = i->freeList;
4152     while (objPtr) {
4153         nextObjPtr = objPtr->nextObjPtr;
4154         Jim_Free(objPtr);
4155         objPtr = nextObjPtr;
4156     }
4157     /* Free cached CallFrame structures */
4158     cf = i->freeFramesList;
4159     while(cf) {
4160         nextcf = cf->nextFramePtr;
4161         if (cf->vars.table != NULL)
4162             Jim_Free(cf->vars.table);
4163         Jim_Free(cf);
4164         cf = nextcf;
4165     }
4166     /* Free the sharedString hash table. Make sure to free it
4167      * after every other Jim_Object was freed. */
4168     Jim_FreeHashTable(&i->sharedStrings);
4169     /* Free the interpreter structure. */
4170     Jim_Free(i);
4171 }
4172
4173 /* Store the call frame relative to the level represented by
4174  * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4175  * level is assumed to be '1'.
4176  *
4177  * If a newLevelptr int pointer is specified, the function stores
4178  * the absolute level integer value of the new target callframe into
4179  * *newLevelPtr. (this is used to adjust interp->numLevels
4180  * in the implementation of [uplevel], so that [info level] will
4181  * return a correct information).
4182  *
4183  * This function accepts the 'level' argument in the form
4184  * of the commands [uplevel] and [upvar].
4185  *
4186  * For a function accepting a relative integer as level suitable
4187  * for implementation of [info level ?level?] check the
4188  * GetCallFrameByInteger() function. */
4189 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4190         Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4191 {
4192     long level;
4193     const char *str;
4194     Jim_CallFrame *framePtr;
4195
4196     if (newLevelPtr) *newLevelPtr = interp->numLevels;
4197     if (levelObjPtr) {
4198         str = Jim_GetString(levelObjPtr, NULL);
4199         if (str[0] == '#') {
4200             char *endptr;
4201             /* speedup for the toplevel (level #0) */
4202             if (str[1] == '0' && str[2] == '\0') {
4203                 if (newLevelPtr) *newLevelPtr = 0;
4204                 *framePtrPtr = interp->topFramePtr;
4205                 return JIM_OK;
4206             }
4207
4208             level = strtol(str+1, &endptr, 0);
4209             if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4210                 goto badlevel;
4211             /* An 'absolute' level is converted into the
4212              * 'number of levels to go back' format. */
4213             level = interp->numLevels - level;
4214             if (level < 0) goto badlevel;
4215         } else {
4216             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4217                 goto badlevel;
4218         }
4219     } else {
4220         str = "1"; /* Needed to format the error message. */
4221         level = 1;
4222     }
4223     /* Lookup */
4224     framePtr = interp->framePtr;
4225     if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4226     while (level--) {
4227         framePtr = framePtr->parentCallFrame;
4228         if (framePtr == NULL) goto badlevel;
4229     }
4230     *framePtrPtr = framePtr;
4231     return JIM_OK;
4232 badlevel:
4233     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4234     Jim_AppendStrings(interp, Jim_GetResult(interp),
4235             "bad level \"", str, "\"", NULL);
4236     return JIM_ERR;
4237 }
4238
4239 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4240  * as a relative integer like in the [info level ?level?] command. */
4241 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4242         Jim_CallFrame **framePtrPtr)
4243 {
4244     jim_wide level;
4245     jim_wide relLevel; /* level relative to the current one. */
4246     Jim_CallFrame *framePtr;
4247
4248     if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4249         goto badlevel;
4250     if (level > 0) {
4251         /* An 'absolute' level is converted into the
4252          * 'number of levels to go back' format. */
4253         relLevel = interp->numLevels - level;
4254     } else {
4255         relLevel = -level;
4256     }
4257     /* Lookup */
4258     framePtr = interp->framePtr;
4259     while (relLevel--) {
4260         framePtr = framePtr->parentCallFrame;
4261         if (framePtr == NULL) goto badlevel;
4262     }
4263     *framePtrPtr = framePtr;
4264     return JIM_OK;
4265 badlevel:
4266     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4267     Jim_AppendStrings(interp, Jim_GetResult(interp),
4268             "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4269     return JIM_ERR;
4270 }
4271
4272 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4273 {
4274     Jim_Free((void*)interp->errorFileName);
4275     interp->errorFileName = Jim_StrDup(filename);
4276 }
4277
4278 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4279 {
4280     interp->errorLine = linenr;
4281 }
4282
4283 static void JimResetStackTrace(Jim_Interp *interp)
4284 {
4285     Jim_DecrRefCount(interp, interp->stackTrace);
4286     interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4287     Jim_IncrRefCount(interp->stackTrace);
4288 }
4289
4290 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4291         const char *filename, int linenr)
4292 {
4293     if (Jim_IsShared(interp->stackTrace)) {
4294         interp->stackTrace =
4295             Jim_DuplicateObj(interp, interp->stackTrace);
4296         Jim_IncrRefCount(interp->stackTrace);
4297     }
4298     Jim_ListAppendElement(interp, interp->stackTrace,
4299             Jim_NewStringObj(interp, procname, -1));
4300     Jim_ListAppendElement(interp, interp->stackTrace,
4301             Jim_NewStringObj(interp, filename, -1));
4302     Jim_ListAppendElement(interp, interp->stackTrace,
4303             Jim_NewIntObj(interp, linenr));
4304 }
4305
4306 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4307 {
4308     AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4309     assocEntryPtr->delProc = delProc;
4310     assocEntryPtr->data = data;
4311     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4312 }
4313
4314 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4315 {
4316     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4317     if (entryPtr != NULL) {
4318         AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4319         return assocEntryPtr->data;
4320     }
4321     return NULL;
4322 }
4323
4324 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4325 {
4326     return Jim_DeleteHashEntry(&interp->assocData, key);
4327 }
4328
4329 int Jim_GetExitCode(Jim_Interp *interp) {
4330     return interp->exitCode;
4331 }
4332
4333 FILE *Jim_SetStdin(Jim_Interp *interp, FILE *fp)
4334 {
4335     if (fp != NULL) interp->stdin_ = fp;
4336     return interp->stdin_;
4337 }
4338
4339 FILE *Jim_SetStdout(Jim_Interp *interp, FILE *fp)
4340 {
4341     if (fp != NULL) interp->stdout_ = fp;
4342     return interp->stdout_;
4343 }
4344
4345 FILE *Jim_SetStderr(Jim_Interp *interp, FILE *fp)
4346 {
4347     if (fp != NULL) interp->stderr_ = fp;
4348     return interp->stderr_;
4349 }
4350
4351 /* -----------------------------------------------------------------------------
4352  * Shared strings.
4353  * Every interpreter has an hash table where to put shared dynamically
4354  * allocate strings that are likely to be used a lot of times.
4355  * For example, in the 'source' object type, there is a pointer to
4356  * the filename associated with that object. Every script has a lot
4357  * of this objects with the identical file name, so it is wise to share
4358  * this info.
4359  *
4360  * The API is trivial: Jim_GetSharedString(interp, "foobar")
4361  * returns the pointer to the shared string. Every time a reference
4362  * to the string is no longer used, the user should call
4363  * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4364  * a given string, it is removed from the hash table.
4365  * ---------------------------------------------------------------------------*/
4366 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4367 {
4368     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4369
4370     if (he == NULL) {
4371         char *strCopy = Jim_StrDup(str);
4372
4373         Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4374         return strCopy;
4375     } else {
4376         long refCount = (long) he->val;
4377
4378         refCount++;
4379         he->val = (void*) refCount;
4380         return he->key;
4381     }
4382 }
4383
4384 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4385 {
4386     long refCount;
4387     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4388
4389     if (he == NULL)
4390         Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4391               "unknown shared string '%s'", str);
4392     refCount = (long) he->val;
4393     refCount--;
4394     if (refCount == 0) {
4395         Jim_DeleteHashEntry(&interp->sharedStrings, str);
4396     } else {
4397         he->val = (void*) refCount;
4398     }
4399 }
4400
4401 /* -----------------------------------------------------------------------------
4402  * Integer object
4403  * ---------------------------------------------------------------------------*/
4404 #define JIM_INTEGER_SPACE 24
4405
4406 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4407 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4408
4409 static Jim_ObjType intObjType = {
4410     "int",
4411     NULL,
4412     NULL,
4413     UpdateStringOfInt,
4414     JIM_TYPE_NONE,
4415 };
4416
4417 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4418 {
4419     int len;
4420     char buf[JIM_INTEGER_SPACE+1];
4421
4422     len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4423     objPtr->bytes = Jim_Alloc(len+1);
4424     memcpy(objPtr->bytes, buf, len+1);
4425     objPtr->length = len;
4426 }
4427
4428 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4429 {
4430     jim_wide wideValue;
4431     const char *str;
4432
4433     /* Get the string representation */
4434     str = Jim_GetString(objPtr, NULL);
4435     /* Try to convert into a jim_wide */
4436     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4437         if (flags & JIM_ERRMSG) {
4438             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4439             Jim_AppendStrings(interp, Jim_GetResult(interp),
4440                     "expected integer but got \"", str, "\"", NULL);
4441         }
4442         return JIM_ERR;
4443     }
4444     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4445         errno == ERANGE) {
4446         Jim_SetResultString(interp,
4447             "Integer value too big to be represented", -1);
4448         return JIM_ERR;
4449     }
4450     /* Free the old internal repr and set the new one. */
4451     Jim_FreeIntRep(interp, objPtr);
4452     objPtr->typePtr = &intObjType;
4453     objPtr->internalRep.wideValue = wideValue;
4454     return JIM_OK;
4455 }
4456
4457 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4458 {
4459     if (objPtr->typePtr != &intObjType &&
4460         SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4461         return JIM_ERR;
4462     *widePtr = objPtr->internalRep.wideValue;
4463     return JIM_OK;
4464 }
4465
4466 /* Get a wide but does not set an error if the format is bad. */
4467 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4468         jim_wide *widePtr)
4469 {
4470     if (objPtr->typePtr != &intObjType &&
4471         SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4472         return JIM_ERR;
4473     *widePtr = objPtr->internalRep.wideValue;
4474     return JIM_OK;
4475 }
4476
4477 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4478 {
4479     jim_wide wideValue;
4480     int retval;
4481
4482     retval = Jim_GetWide(interp, objPtr, &wideValue);
4483     if (retval == JIM_OK) {
4484         *longPtr = (long) wideValue;
4485         return JIM_OK;
4486     }
4487     return JIM_ERR;
4488 }
4489
4490 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4491 {
4492     if (Jim_IsShared(objPtr))
4493         Jim_Panic(interp,"Jim_SetWide called with shared object");
4494     if (objPtr->typePtr != &intObjType) {
4495         Jim_FreeIntRep(interp, objPtr);
4496         objPtr->typePtr = &intObjType;
4497     }
4498     Jim_InvalidateStringRep(objPtr);
4499     objPtr->internalRep.wideValue = wideValue;
4500 }
4501
4502 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4503 {
4504     Jim_Obj *objPtr;
4505
4506     objPtr = Jim_NewObj(interp);
4507     objPtr->typePtr = &intObjType;
4508     objPtr->bytes = NULL;
4509     objPtr->internalRep.wideValue = wideValue;
4510     return objPtr;
4511 }
4512
4513 /* -----------------------------------------------------------------------------
4514  * Double object
4515  * ---------------------------------------------------------------------------*/
4516 #define JIM_DOUBLE_SPACE 30
4517
4518 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4519 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4520
4521 static Jim_ObjType doubleObjType = {
4522     "double",
4523     NULL,
4524     NULL,
4525     UpdateStringOfDouble,
4526     JIM_TYPE_NONE,
4527 };
4528
4529 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4530 {
4531     int len;
4532     char buf[JIM_DOUBLE_SPACE+1];
4533
4534     len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4535     objPtr->bytes = Jim_Alloc(len+1);
4536     memcpy(objPtr->bytes, buf, len+1);
4537     objPtr->length = len;
4538 }
4539
4540 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4541 {
4542     double doubleValue;
4543     const char *str;
4544
4545     /* Get the string representation */
4546     str = Jim_GetString(objPtr, NULL);
4547     /* Try to convert into a double */
4548     if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4549         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4550         Jim_AppendStrings(interp, Jim_GetResult(interp),
4551                 "expected number but got '", str, "'", NULL);
4552         return JIM_ERR;
4553     }
4554     /* Free the old internal repr and set the new one. */
4555     Jim_FreeIntRep(interp, objPtr);
4556     objPtr->typePtr = &doubleObjType;
4557     objPtr->internalRep.doubleValue = doubleValue;
4558     return JIM_OK;
4559 }
4560
4561 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4562 {
4563     if (objPtr->typePtr != &doubleObjType &&
4564         SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4565         return JIM_ERR;
4566     *doublePtr = objPtr->internalRep.doubleValue;
4567     return JIM_OK;
4568 }
4569
4570 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4571 {
4572     if (Jim_IsShared(objPtr))
4573         Jim_Panic(interp,"Jim_SetDouble called with shared object");
4574     if (objPtr->typePtr != &doubleObjType) {
4575         Jim_FreeIntRep(interp, objPtr);
4576         objPtr->typePtr = &doubleObjType;
4577     }
4578     Jim_InvalidateStringRep(objPtr);
4579     objPtr->internalRep.doubleValue = doubleValue;
4580 }
4581
4582 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4583 {
4584     Jim_Obj *objPtr;
4585
4586     objPtr = Jim_NewObj(interp);
4587     objPtr->typePtr = &doubleObjType;
4588     objPtr->bytes = NULL;
4589     objPtr->internalRep.doubleValue = doubleValue;
4590     return objPtr;
4591 }
4592
4593 /* -----------------------------------------------------------------------------
4594  * List object
4595  * ---------------------------------------------------------------------------*/
4596 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4597 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4598 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4599 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4600 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4601
4602 /* Note that while the elements of the list may contain references,
4603  * the list object itself can't. This basically means that the
4604  * list object string representation as a whole can't contain references
4605  * that are not presents in the single elements. */
4606 static Jim_ObjType listObjType = {
4607     "list",
4608     FreeListInternalRep,
4609     DupListInternalRep,
4610     UpdateStringOfList,
4611     JIM_TYPE_NONE,
4612 };
4613
4614 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4615 {
4616     int i;
4617
4618     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4619         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4620     }
4621     Jim_Free(objPtr->internalRep.listValue.ele);
4622 }
4623
4624 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4625 {
4626     int i;
4627     JIM_NOTUSED(interp);
4628
4629     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4630     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4631     dupPtr->internalRep.listValue.ele =
4632         Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4633     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4634             sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4635     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4636         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4637     }
4638     dupPtr->typePtr = &listObjType;
4639 }
4640
4641 /* The following function checks if a given string can be encoded
4642  * into a list element without any kind of quoting, surrounded by braces,
4643  * or using escapes to quote. */
4644 #define JIM_ELESTR_SIMPLE 0
4645 #define JIM_ELESTR_BRACE 1
4646 #define JIM_ELESTR_QUOTE 2
4647 static int ListElementQuotingType(const char *s, int len)
4648 {
4649     int i, level, trySimple = 1;
4650
4651     /* Try with the SIMPLE case */
4652     if (len == 0) return JIM_ELESTR_BRACE;
4653     if (s[0] == '"' || s[0] == '{') {
4654         trySimple = 0;
4655         goto testbrace;
4656     }
4657     for (i = 0; i < len; i++) {
4658         switch(s[i]) {
4659         case ' ':
4660         case '$':
4661         case '"':
4662         case '[':
4663         case ']':
4664         case ';':
4665         case '\\':
4666         case '\r':
4667         case '\n':
4668         case '\t':
4669         case '\f':
4670         case '\v':
4671             trySimple = 0;
4672         case '{':
4673         case '}':
4674             goto testbrace;
4675         }
4676     }
4677     return JIM_ELESTR_SIMPLE;
4678
4679 testbrace:
4680     /* Test if it's possible to do with braces */
4681     if (s[len-1] == '\\' ||
4682         s[len-1] == ']') return JIM_ELESTR_QUOTE;
4683     level = 0;
4684     for (i = 0; i < len; i++) {
4685         switch(s[i]) {
4686         case '{': level++; break;
4687         case '}': level--;
4688               if (level < 0) return JIM_ELESTR_QUOTE;
4689               break;
4690         case '\\':
4691               if (s[i+1] == '\n')
4692                   return JIM_ELESTR_QUOTE;
4693               else
4694                   if (s[i+1] != '\0') i++;
4695               break;
4696         }
4697     }
4698     if (level == 0) {
4699         if (!trySimple) return JIM_ELESTR_BRACE;
4700         for (i = 0; i < len; i++) {
4701             switch(s[i]) {
4702             case ' ':
4703             case '$':
4704             case '"':
4705             case '[':
4706             case ']':
4707             case ';':
4708             case '\\':
4709             case '\r':
4710             case '\n':
4711             case '\t':
4712             case '\f':
4713             case '\v':
4714                 return JIM_ELESTR_BRACE;
4715                 break;
4716             }
4717         }
4718         return JIM_ELESTR_SIMPLE;
4719     }
4720     return JIM_ELESTR_QUOTE;
4721 }
4722
4723 /* Returns the malloc-ed representation of a string
4724  * using backslash to quote special chars. */
4725 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4726 {
4727     char *q = Jim_Alloc(len*2+1), *p;
4728
4729     p = q;
4730     while(*s) {
4731         switch (*s) {
4732         case ' ':
4733         case '$':
4734         case '"':
4735         case '[':
4736         case ']':
4737         case '{':
4738         case '}':
4739         case ';':
4740         case '\\':
4741             *p++ = '\\';
4742             *p++ = *s++;
4743             break;
4744         case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4745         case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4746         case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4747         case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4748         case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4749         default:
4750             *p++ = *s++;
4751             break;
4752         }
4753     }
4754     *p = '\0';
4755     *qlenPtr = p-q;
4756     return q;
4757 }
4758
4759 void UpdateStringOfList(struct Jim_Obj *objPtr)
4760 {
4761     int i, bufLen, realLength;
4762     const char *strRep;
4763     char *p;
4764     int *quotingType;
4765     Jim_Obj **ele = objPtr->internalRep.listValue.ele;
4766
4767     /* (Over) Estimate the space needed. */
4768     quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
4769     bufLen = 0;
4770     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4771         int len;
4772
4773         strRep = Jim_GetString(ele[i], &len);
4774         quotingType[i] = ListElementQuotingType(strRep, len);
4775         switch (quotingType[i]) {
4776         case JIM_ELESTR_SIMPLE: bufLen += len; break;
4777         case JIM_ELESTR_BRACE: bufLen += len+2; break;
4778         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
4779         }
4780         bufLen++; /* elements separator. */
4781     }
4782     bufLen++;
4783
4784     /* Generate the string rep. */
4785     p = objPtr->bytes = Jim_Alloc(bufLen+1);
4786     realLength = 0;
4787     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4788         int len, qlen;
4789         const char *strRep = Jim_GetString(ele[i], &len);
4790         char *q;
4791
4792         switch(quotingType[i]) {
4793         case JIM_ELESTR_SIMPLE:
4794             memcpy(p, strRep, len);
4795             p += len;
4796             realLength += len;
4797             break;
4798         case JIM_ELESTR_BRACE:
4799             *p++ = '{';
4800             memcpy(p, strRep, len);
4801             p += len;
4802             *p++ = '}';
4803             realLength += len+2;
4804             break;
4805         case JIM_ELESTR_QUOTE:
4806             q = BackslashQuoteString(strRep, len, &qlen);
4807             memcpy(p, q, qlen);
4808             Jim_Free(q);
4809             p += qlen;
4810             realLength += qlen;
4811             break;
4812         }
4813         /* Add a separating space */
4814         if (i+1 != objPtr->internalRep.listValue.len) {
4815             *p++ = ' ';
4816             realLength ++;
4817         }
4818     }
4819     *p = '\0'; /* nul term. */
4820     objPtr->length = realLength;
4821     Jim_Free(quotingType);
4822 }
4823
4824 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
4825 {
4826     struct JimParserCtx parser;
4827     const char *str;
4828     int strLen;
4829
4830     /* Get the string representation */
4831     str = Jim_GetString(objPtr, &strLen);
4832
4833     /* Free the old internal repr just now and initialize the
4834      * new one just now. The string->list conversion can't fail. */
4835     Jim_FreeIntRep(interp, objPtr);
4836     objPtr->typePtr = &listObjType;
4837     objPtr->internalRep.listValue.len = 0;
4838     objPtr->internalRep.listValue.maxLen = 0;
4839     objPtr->internalRep.listValue.ele = NULL;
4840
4841     /* Convert into a list */
4842     JimParserInit(&parser, str, strLen, 1);
4843     while(!JimParserEof(&parser)) {
4844         char *token;
4845         int tokenLen, type;
4846         Jim_Obj *elementPtr;
4847
4848         JimParseList(&parser);
4849         if (JimParserTtype(&parser) != JIM_TT_STR &&
4850             JimParserTtype(&parser) != JIM_TT_ESC)
4851             continue;
4852         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
4853         elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
4854         ListAppendElement(objPtr, elementPtr);
4855     }
4856     return JIM_OK;
4857 }
4858
4859 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, 
4860         int len)
4861 {
4862     Jim_Obj *objPtr;
4863     int i;
4864
4865     objPtr = Jim_NewObj(interp);
4866     objPtr->typePtr = &listObjType;
4867     objPtr->bytes = NULL;
4868     objPtr->internalRep.listValue.ele = NULL;
4869     objPtr->internalRep.listValue.len = 0;
4870     objPtr->internalRep.listValue.maxLen = 0;
4871     for (i = 0; i < len; i++) {
4872         ListAppendElement(objPtr, elements[i]);
4873     }
4874     return objPtr;
4875 }
4876
4877 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
4878  * length of the vector. Note that the user of this function should make
4879  * sure that the list object can't shimmer while the vector returned
4880  * is in use, this vector is the one stored inside the internal representation
4881  * of the list object. This function is not exported, extensions should
4882  * always access to the List object elements using Jim_ListIndex(). */
4883 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
4884         Jim_Obj ***listVec)
4885 {
4886     Jim_ListLength(interp, listObj, argc);
4887     assert(listObj->typePtr == &listObjType);
4888     *listVec = listObj->internalRep.listValue.ele;
4889 }
4890
4891 /* ListSortElements type values */
4892 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
4893       JIM_LSORT_NOCASE_DECR};
4894
4895 /* Sort the internal rep of a list. */
4896 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4897 {
4898     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
4899 }
4900
4901 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4902 {
4903     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
4904 }
4905
4906 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4907 {
4908     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
4909 }
4910
4911 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4912 {
4913     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
4914 }
4915
4916 /* Sort a list *in place*. MUST be called with non-shared objects. */
4917 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
4918 {
4919     typedef int (qsort_comparator)(const void *, const void *);
4920     int (*fn)(Jim_Obj**, Jim_Obj**);
4921     Jim_Obj **vector;
4922     int len;
4923
4924     if (Jim_IsShared(listObjPtr))
4925         Jim_Panic(interp,"Jim_ListSortElements called with shared object");
4926     if (listObjPtr->typePtr != &listObjType)
4927         SetListFromAny(interp, listObjPtr);
4928
4929     vector = listObjPtr->internalRep.listValue.ele;
4930     len = listObjPtr->internalRep.listValue.len;
4931     switch (type) {
4932         case JIM_LSORT_ASCII: fn = ListSortString;  break;
4933         case JIM_LSORT_NOCASE: fn = ListSortStringNoCase;  break;
4934         case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr;  break;
4935         case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr;  break;
4936         default:
4937             fn = NULL; /* avoid warning */
4938             Jim_Panic(interp,"ListSort called with invalid sort type");
4939     }
4940     qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
4941     Jim_InvalidateStringRep(listObjPtr);
4942 }
4943
4944 /* This is the low-level function to append an element to a list.
4945  * The higher-level Jim_ListAppendElement() performs shared object
4946  * check and invalidate the string repr. This version is used
4947  * in the internals of the List Object and is not exported.
4948  *
4949  * NOTE: this function can be called only against objects
4950  * with internal type of List. */
4951 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
4952 {
4953     int requiredLen = listPtr->internalRep.listValue.len + 1;
4954
4955     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4956         int maxLen = requiredLen * 2;
4957
4958         listPtr->internalRep.listValue.ele =
4959             Jim_Realloc(listPtr->internalRep.listValue.ele,
4960                     sizeof(Jim_Obj*)*maxLen);
4961         listPtr->internalRep.listValue.maxLen = maxLen;
4962     }
4963     listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
4964         objPtr;
4965     listPtr->internalRep.listValue.len ++;
4966     Jim_IncrRefCount(objPtr);
4967 }
4968
4969 /* This is the low-level function to insert elements into a list.
4970  * The higher-level Jim_ListInsertElements() performs shared object
4971  * check and invalidate the string repr. This version is used
4972  * in the internals of the List Object and is not exported.
4973  *
4974  * NOTE: this function can be called only against objects
4975  * with internal type of List. */
4976 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
4977         Jim_Obj *const *elemVec)
4978 {
4979     int currentLen = listPtr->internalRep.listValue.len;
4980     int requiredLen = currentLen + elemc;
4981     int i;
4982     Jim_Obj **point;
4983
4984     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4985         int maxLen = requiredLen * 2;
4986
4987         listPtr->internalRep.listValue.ele =
4988             Jim_Realloc(listPtr->internalRep.listValue.ele,
4989                     sizeof(Jim_Obj*)*maxLen);
4990         listPtr->internalRep.listValue.maxLen = maxLen;
4991     }
4992     point = listPtr->internalRep.listValue.ele + index;
4993     memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
4994     for (i=0; i < elemc; ++i) {
4995         point[i] = elemVec[i];
4996         Jim_IncrRefCount(point[i]);
4997     }
4998     listPtr->internalRep.listValue.len += elemc;
4999 }
5000
5001 /* Appends every element of appendListPtr into listPtr.
5002  * Both have to be of the list type. */
5003 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5004 {
5005     int i, oldLen = listPtr->internalRep.listValue.len;
5006     int appendLen = appendListPtr->internalRep.listValue.len;
5007     int requiredLen = oldLen + appendLen;
5008
5009     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5010         int maxLen = requiredLen * 2;
5011
5012         listPtr->internalRep.listValue.ele =
5013             Jim_Realloc(listPtr->internalRep.listValue.ele,
5014                     sizeof(Jim_Obj*)*maxLen);
5015         listPtr->internalRep.listValue.maxLen = maxLen;
5016     }
5017     for (i = 0; i < appendLen; i++) {
5018         Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5019         listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5020         Jim_IncrRefCount(objPtr);
5021     }
5022     listPtr->internalRep.listValue.len += appendLen;
5023 }
5024
5025 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5026 {
5027     if (Jim_IsShared(listPtr))
5028         Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5029     if (listPtr->typePtr != &listObjType)
5030         SetListFromAny(interp, listPtr);
5031     Jim_InvalidateStringRep(listPtr);
5032     ListAppendElement(listPtr, objPtr);
5033 }
5034
5035 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5036 {
5037     if (Jim_IsShared(listPtr))
5038         Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5039     if (listPtr->typePtr != &listObjType)
5040         SetListFromAny(interp, listPtr);
5041     Jim_InvalidateStringRep(listPtr);
5042     ListAppendList(listPtr, appendListPtr);
5043 }
5044
5045 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5046 {
5047     if (listPtr->typePtr != &listObjType)
5048         SetListFromAny(interp, listPtr);
5049     *intPtr = listPtr->internalRep.listValue.len;
5050 }
5051
5052 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5053         int objc, Jim_Obj *const *objVec)
5054 {
5055     if (Jim_IsShared(listPtr))
5056         Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5057     if (listPtr->typePtr != &listObjType)
5058         SetListFromAny(interp, listPtr);
5059     if (index >= 0 && index > listPtr->internalRep.listValue.len)
5060         index = listPtr->internalRep.listValue.len;
5061     else if (index < 0 ) 
5062         index = 0;
5063     Jim_InvalidateStringRep(listPtr);
5064     ListInsertElements(listPtr, index, objc, objVec);
5065 }
5066
5067 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5068         Jim_Obj **objPtrPtr, int flags)
5069 {
5070     if (listPtr->typePtr != &listObjType)
5071         SetListFromAny(interp, listPtr);
5072     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5073         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5074         if (flags & JIM_ERRMSG) {
5075             Jim_SetResultString(interp,
5076                 "list index out of range", -1);
5077         }
5078         return JIM_ERR;
5079     }
5080     if (index < 0)
5081         index = listPtr->internalRep.listValue.len+index;
5082     *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5083     return JIM_OK;
5084 }
5085
5086 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5087         Jim_Obj *newObjPtr, int flags)
5088 {
5089     if (listPtr->typePtr != &listObjType)
5090         SetListFromAny(interp, listPtr);
5091     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5092         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5093         if (flags & JIM_ERRMSG) {
5094             Jim_SetResultString(interp,
5095                 "list index out of range", -1);
5096         }
5097         return JIM_ERR;
5098     }
5099     if (index < 0)
5100         index = listPtr->internalRep.listValue.len+index;
5101     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5102     listPtr->internalRep.listValue.ele[index] = newObjPtr;
5103     Jim_IncrRefCount(newObjPtr);
5104     return JIM_OK;
5105 }
5106
5107 /* Modify the list stored into the variable named 'varNamePtr'
5108  * setting the element specified by the 'indexc' indexes objects in 'indexv',
5109  * with the new element 'newObjptr'. */
5110 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5111         Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5112 {
5113     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5114     int shared, i, index;
5115
5116     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5117     if (objPtr == NULL)
5118         return JIM_ERR;
5119     if ((shared = Jim_IsShared(objPtr)))
5120         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5121     for (i = 0; i < indexc-1; i++) {
5122         listObjPtr = objPtr;
5123         if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5124             goto err;
5125         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5126                     JIM_ERRMSG) != JIM_OK) {
5127             goto err;
5128         }
5129         if (Jim_IsShared(objPtr)) {
5130             objPtr = Jim_DuplicateObj(interp, objPtr);
5131             ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5132         }
5133         Jim_InvalidateStringRep(listObjPtr);
5134     }
5135     if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5136         goto err;
5137     if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5138         goto err;
5139     Jim_InvalidateStringRep(objPtr);
5140     Jim_InvalidateStringRep(varObjPtr);
5141     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5142         goto err;
5143     Jim_SetResult(interp, varObjPtr);
5144     return JIM_OK;
5145 err:
5146     if (shared) {
5147         Jim_FreeNewObj(interp, varObjPtr);
5148     }
5149     return JIM_ERR;
5150 }
5151
5152 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5153 {
5154     int i;
5155
5156     /* If all the objects in objv are lists without string rep.
5157      * it's possible to return a list as result, that's the
5158      * concatenation of all the lists. */
5159     for (i = 0; i < objc; i++) {
5160         if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5161             break;
5162     }
5163     if (i == objc) {
5164         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5165         for (i = 0; i < objc; i++)
5166             Jim_ListAppendList(interp, objPtr, objv[i]);
5167         return objPtr;
5168     } else {
5169         /* Else... we have to glue strings together */
5170         int len = 0, objLen;
5171         char *bytes, *p;
5172
5173         /* Compute the length */
5174         for (i = 0; i < objc; i++) {
5175             Jim_GetString(objv[i], &objLen);
5176             len += objLen;
5177         }
5178         if (objc) len += objc-1;
5179         /* Create the string rep, and a stinrg object holding it. */
5180         p = bytes = Jim_Alloc(len+1);
5181         for (i = 0; i < objc; i++) {
5182             const char *s = Jim_GetString(objv[i], &objLen);
5183             while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5184             {
5185                 s++; objLen--; len--;
5186             }
5187             while (objLen && (s[objLen-1] == ' ' ||
5188                 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5189                 objLen--; len--;
5190             }
5191             memcpy(p, s, objLen);
5192             p += objLen;
5193             if (objLen && i+1 != objc) {
5194                 *p++ = ' ';
5195             } else if (i+1 != objc) {
5196                 /* Drop the space calcuated for this
5197                  * element that is instead null. */
5198                 len--;
5199             }
5200         }
5201         *p = '\0';
5202         return Jim_NewStringObjNoAlloc(interp, bytes, len);
5203     }
5204 }
5205
5206 /* Returns a list composed of the elements in the specified range.
5207  * first and start are directly accepted as Jim_Objects and
5208  * processed for the end?-index? case. */
5209 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5210 {
5211     int first, last;
5212     int len, rangeLen;
5213
5214     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5215         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5216         return NULL;
5217     Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5218     first = JimRelToAbsIndex(len, first);
5219     last = JimRelToAbsIndex(len, last);
5220     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5221     return Jim_NewListObj(interp,
5222             listObjPtr->internalRep.listValue.ele+first, rangeLen);
5223 }
5224
5225 /* -----------------------------------------------------------------------------
5226  * Dict object
5227  * ---------------------------------------------------------------------------*/
5228 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5229 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5230 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5231 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5232
5233 /* Dict HashTable Type.
5234  *
5235  * Keys and Values are Jim objects. */
5236
5237 unsigned int JimObjectHTHashFunction(const void *key)
5238 {
5239     const char *str;
5240     Jim_Obj *objPtr = (Jim_Obj*) key;
5241     int len, h;
5242
5243     str = Jim_GetString(objPtr, &len);
5244     h = Jim_GenHashFunction((unsigned char*)str, len);
5245     return h;
5246 }
5247
5248 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5249 {
5250     JIM_NOTUSED(privdata);
5251
5252     return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5253 }
5254
5255 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5256 {
5257     Jim_Obj *objPtr = val;
5258
5259     Jim_DecrRefCount(interp, objPtr);
5260 }
5261
5262 static Jim_HashTableType JimDictHashTableType = {
5263     JimObjectHTHashFunction,            /* hash function */
5264     NULL,                               /* key dup */
5265     NULL,                               /* val dup */
5266     JimObjectHTKeyCompare,              /* key compare */
5267     (void(*)(void*, const void*))       /* ATTENTION: const cast */
5268         JimObjectHTKeyValDestructor,    /* key destructor */
5269     JimObjectHTKeyValDestructor         /* val destructor */
5270 };
5271
5272 /* Note that while the elements of the dict may contain references,
5273  * the list object itself can't. This basically means that the
5274  * dict object string representation as a whole can't contain references
5275  * that are not presents in the single elements. */
5276 static Jim_ObjType dictObjType = {
5277     "dict",
5278     FreeDictInternalRep,
5279     DupDictInternalRep,
5280     UpdateStringOfDict,
5281     JIM_TYPE_NONE,
5282 };
5283
5284 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5285 {
5286     JIM_NOTUSED(interp);
5287
5288     Jim_FreeHashTable(objPtr->internalRep.ptr);
5289     Jim_Free(objPtr->internalRep.ptr);
5290 }
5291
5292 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5293 {
5294     Jim_HashTable *ht, *dupHt;
5295     Jim_HashTableIterator *htiter;
5296     Jim_HashEntry *he;
5297
5298     /* Create a new hash table */
5299     ht = srcPtr->internalRep.ptr;
5300     dupHt = Jim_Alloc(sizeof(*dupHt));
5301     Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5302     if (ht->size != 0)
5303         Jim_ExpandHashTable(dupHt, ht->size);
5304     /* Copy every element from the source to the dup hash table */
5305     htiter = Jim_GetHashTableIterator(ht);
5306     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5307         const Jim_Obj *keyObjPtr = he->key;
5308         Jim_Obj *valObjPtr = he->val;
5309
5310         Jim_IncrRefCount((Jim_Obj*)keyObjPtr);  /* ATTENTION: const cast */
5311         Jim_IncrRefCount(valObjPtr);
5312         Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5313     }
5314     Jim_FreeHashTableIterator(htiter);
5315
5316     dupPtr->internalRep.ptr = dupHt;
5317     dupPtr->typePtr = &dictObjType;
5318 }
5319
5320 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5321 {
5322     int i, bufLen, realLength;
5323     const char *strRep;
5324     char *p;
5325     int *quotingType, objc;
5326     Jim_HashTable *ht;
5327     Jim_HashTableIterator *htiter;
5328     Jim_HashEntry *he;
5329     Jim_Obj **objv;
5330
5331     /* Trun the hash table into a flat vector of Jim_Objects. */
5332     ht = objPtr->internalRep.ptr;
5333     objc = ht->used*2;
5334     objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5335     htiter = Jim_GetHashTableIterator(ht);
5336     i = 0;
5337     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5338         objv[i++] = (Jim_Obj*)he->key;  /* ATTENTION: const cast */
5339         objv[i++] = he->val;
5340     }
5341     Jim_FreeHashTableIterator(htiter);
5342     /* (Over) Estimate the space needed. */
5343     quotingType = Jim_Alloc(sizeof(int)*objc);
5344     bufLen = 0;
5345     for (i = 0; i < objc; i++) {
5346         int len;
5347
5348         strRep = Jim_GetString(objv[i], &len);
5349         quotingType[i] = ListElementQuotingType(strRep, len);
5350         switch (quotingType[i]) {
5351         case JIM_ELESTR_SIMPLE: bufLen += len; break;
5352         case JIM_ELESTR_BRACE: bufLen += len+2; break;
5353         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5354         }
5355         bufLen++; /* elements separator. */
5356     }
5357     bufLen++;
5358
5359     /* Generate the string rep. */
5360     p = objPtr->bytes = Jim_Alloc(bufLen+1);
5361     realLength = 0;
5362     for (i = 0; i < objc; i++) {
5363         int len, qlen;
5364         const char *strRep = Jim_GetString(objv[i], &len);
5365         char *q;
5366
5367         switch(quotingType[i]) {
5368         case JIM_ELESTR_SIMPLE:
5369             memcpy(p, strRep, len);
5370             p += len;
5371             realLength += len;
5372             break;
5373         case JIM_ELESTR_BRACE:
5374             *p++ = '{';
5375             memcpy(p, strRep, len);
5376             p += len;
5377             *p++ = '}';
5378             realLength += len+2;
5379             break;
5380         case JIM_ELESTR_QUOTE:
5381             q = BackslashQuoteString(strRep, len, &qlen);
5382             memcpy(p, q, qlen);
5383             Jim_Free(q);
5384             p += qlen;
5385             realLength += qlen;
5386             break;
5387         }
5388         /* Add a separating space */
5389         if (i+1 != objc) {
5390             *p++ = ' ';
5391             realLength ++;
5392         }
5393     }
5394     *p = '\0'; /* nul term. */
5395     objPtr->length = realLength;
5396     Jim_Free(quotingType);
5397     Jim_Free(objv);
5398 }
5399
5400 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5401 {
5402     struct JimParserCtx parser;
5403     Jim_HashTable *ht;
5404     Jim_Obj *objv[2];
5405     const char *str;
5406     int i, strLen;
5407
5408     /* Get the string representation */
5409     str = Jim_GetString(objPtr, &strLen);
5410
5411     /* Free the old internal repr just now and initialize the
5412      * new one just now. The string->list conversion can't fail. */
5413     Jim_FreeIntRep(interp, objPtr);
5414     ht = Jim_Alloc(sizeof(*ht));
5415     Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5416     objPtr->typePtr = &dictObjType;
5417     objPtr->internalRep.ptr = ht;
5418
5419     /* Convert into a dict */
5420     JimParserInit(&parser, str, strLen, 1);
5421     i = 0;
5422     while(!JimParserEof(&parser)) {
5423         char *token;
5424         int tokenLen, type;
5425
5426         JimParseList(&parser);
5427         if (JimParserTtype(&parser) != JIM_TT_STR &&
5428             JimParserTtype(&parser) != JIM_TT_ESC)
5429             continue;
5430         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5431         objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5432         if (i == 2) {
5433             i = 0;
5434             Jim_IncrRefCount(objv[0]);
5435             Jim_IncrRefCount(objv[1]);
5436             if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5437                 Jim_HashEntry *he;
5438                 he = Jim_FindHashEntry(ht, objv[0]);
5439                 Jim_DecrRefCount(interp, objv[0]);
5440                 /* ATTENTION: const cast */
5441                 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5442                 he->val = objv[1];
5443             }
5444         }
5445     }
5446     if (i) {
5447         Jim_FreeNewObj(interp, objv[0]);
5448         objPtr->typePtr = NULL;
5449         Jim_FreeHashTable(ht);
5450         Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5451         return JIM_ERR;
5452     }
5453     return JIM_OK;
5454 }
5455
5456 /* Dict object API */
5457
5458 /* Add an element to a dict. objPtr must be of the "dict" type.
5459  * The higer-level exported function is Jim_DictAddElement().
5460  * If an element with the specified key already exists, the value
5461  * associated is replaced with the new one.
5462  *
5463  * if valueObjPtr == NULL, the key is instead removed if it exists. */
5464 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5465         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5466 {
5467     Jim_HashTable *ht = objPtr->internalRep.ptr;
5468
5469     if (valueObjPtr == NULL) { /* unset */
5470         Jim_DeleteHashEntry(ht, keyObjPtr);
5471         return;
5472     }
5473     Jim_IncrRefCount(keyObjPtr);
5474     Jim_IncrRefCount(valueObjPtr);
5475     if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5476         Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5477         Jim_DecrRefCount(interp, keyObjPtr);
5478         /* ATTENTION: const cast */
5479         Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5480         he->val = valueObjPtr;
5481     }
5482 }
5483
5484 /* Add an element, higher-level interface for DictAddElement().
5485  * If valueObjPtr == NULL, the key is removed if it exists. */
5486 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5487         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5488 {
5489     if (Jim_IsShared(objPtr))
5490         Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5491     if (objPtr->typePtr != &dictObjType) {
5492         if (SetDictFromAny(interp, objPtr) != JIM_OK)
5493             return JIM_ERR;
5494     }
5495     DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5496     Jim_InvalidateStringRep(objPtr);
5497     return JIM_OK;
5498 }
5499
5500 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5501 {
5502     Jim_Obj *objPtr;
5503     int i;
5504
5505     if (len % 2)
5506         Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5507
5508     objPtr = Jim_NewObj(interp);
5509     objPtr->typePtr = &dictObjType;
5510     objPtr->bytes = NULL;
5511     objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5512     Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5513     for (i = 0; i < len; i += 2)
5514         DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5515     return objPtr;
5516 }
5517
5518 /* Return the value associated to the specified dict key */
5519 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5520         Jim_Obj **objPtrPtr, int flags)
5521 {
5522     Jim_HashEntry *he;
5523     Jim_HashTable *ht;
5524
5525     if (dictPtr->typePtr != &dictObjType) {
5526         if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5527             return JIM_ERR;
5528     }
5529     ht = dictPtr->internalRep.ptr;
5530     if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5531         if (flags & JIM_ERRMSG) {
5532             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5533             Jim_AppendStrings(interp, Jim_GetResult(interp),
5534                     "key \"", Jim_GetString(keyPtr, NULL),
5535                     "\" not found in dictionary", NULL);
5536         }
5537         return JIM_ERR;
5538     }
5539     *objPtrPtr = he->val;
5540     return JIM_OK;
5541 }
5542
5543 /* Return the value associated to the specified dict keys */
5544 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5545         Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5546 {
5547     Jim_Obj *objPtr;
5548     int i;
5549
5550     if (keyc == 0) {
5551         *objPtrPtr = dictPtr;
5552         return JIM_OK;
5553     }
5554
5555     for (i = 0; i < keyc; i++) {
5556         if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5557                 != JIM_OK)
5558             return JIM_ERR;
5559         dictPtr = objPtr;
5560     }
5561     *objPtrPtr = objPtr;
5562     return JIM_OK;
5563 }
5564
5565 /* Modify the dict stored into the variable named 'varNamePtr'
5566  * setting the element specified by the 'keyc' keys objects in 'keyv',
5567  * with the new value of the element 'newObjPtr'.
5568  *
5569  * If newObjPtr == NULL the operation is to remove the given key
5570  * from the dictionary. */
5571 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5572         Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5573 {
5574     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5575     int shared, i;
5576
5577     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5578     if (objPtr == NULL) {
5579         if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5580             return JIM_ERR;
5581         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5582         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5583             Jim_FreeNewObj(interp, varObjPtr);
5584             return JIM_ERR;
5585         }
5586     }
5587     if ((shared = Jim_IsShared(objPtr)))
5588         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5589     for (i = 0; i < keyc-1; i++) {
5590         dictObjPtr = objPtr;
5591
5592         /* Check if it's a valid dictionary */
5593         if (dictObjPtr->typePtr != &dictObjType) {
5594             if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5595                 goto err;
5596         }
5597         /* Check if the given key exists. */
5598         Jim_InvalidateStringRep(dictObjPtr);
5599         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5600             newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5601         {
5602             /* This key exists at the current level.
5603              * Make sure it's not shared!. */
5604             if (Jim_IsShared(objPtr)) {
5605                 objPtr = Jim_DuplicateObj(interp, objPtr);
5606                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5607             }
5608         } else {
5609             /* Key not found. If it's an [unset] operation
5610              * this is an error. Only the last key may not
5611              * exist. */
5612             if (newObjPtr == NULL)
5613                 goto err;
5614             /* Otherwise set an empty dictionary
5615              * as key's value. */
5616             objPtr = Jim_NewDictObj(interp, NULL, 0);
5617             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5618         }
5619     }
5620     if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5621             != JIM_OK)
5622         goto err;
5623     Jim_InvalidateStringRep(objPtr);
5624     Jim_InvalidateStringRep(varObjPtr);
5625     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5626         goto err;
5627     Jim_SetResult(interp, varObjPtr);
5628     return JIM_OK;
5629 err:
5630     if (shared) {
5631         Jim_FreeNewObj(interp, varObjPtr);
5632     }
5633     return JIM_ERR;
5634 }
5635
5636 /* -----------------------------------------------------------------------------
5637  * Index object
5638  * ---------------------------------------------------------------------------*/
5639 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5640 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5641
5642 static Jim_ObjType indexObjType = {
5643     "index",
5644     NULL,
5645     NULL,
5646     UpdateStringOfIndex,
5647     JIM_TYPE_NONE,
5648 };
5649
5650 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5651 {
5652     int len;
5653     char buf[JIM_INTEGER_SPACE+1];
5654
5655     if (objPtr->internalRep.indexValue >= 0)
5656         len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5657     else if (objPtr->internalRep.indexValue == -1)
5658         len = sprintf(buf, "end");
5659     else {
5660         len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5661     }
5662     objPtr->bytes = Jim_Alloc(len+1);
5663     memcpy(objPtr->bytes, buf, len+1);
5664     objPtr->length = len;
5665 }
5666
5667 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5668 {
5669     int index, end = 0;
5670     const char *str;
5671
5672     /* Get the string representation */
5673     str = Jim_GetString(objPtr, NULL);
5674     /* Try to convert into an index */
5675     if (!strcmp(str, "end")) {
5676         index = 0;
5677         end = 1;
5678     } else {
5679         if (!strncmp(str, "end-", 4)) {
5680             str += 4;
5681             end = 1;
5682         }
5683         if (Jim_StringToIndex(str, &index) != JIM_OK) {
5684             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5685             Jim_AppendStrings(interp, Jim_GetResult(interp),
5686                     "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5687                     "must be integer or end?-integer?", NULL);
5688             return JIM_ERR;
5689         }
5690     }
5691     if (end) {
5692         if (index < 0)
5693             index = INT_MAX;
5694         else
5695             index = -(index+1);
5696     } else if (!end && index < 0)
5697         index = -INT_MAX;
5698     /* Free the old internal repr and set the new one. */
5699     Jim_FreeIntRep(interp, objPtr);
5700     objPtr->typePtr = &indexObjType;
5701     objPtr->internalRep.indexValue = index;
5702     return JIM_OK;
5703 }
5704
5705 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5706 {
5707     /* Avoid shimmering if the object is an integer. */
5708     if (objPtr->typePtr == &intObjType) {
5709         jim_wide val = objPtr->internalRep.wideValue;
5710         if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5711             *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5712             return JIM_OK;
5713         }
5714     }
5715     if (objPtr->typePtr != &indexObjType &&
5716         SetIndexFromAny(interp, objPtr) == JIM_ERR)
5717         return JIM_ERR;
5718     *indexPtr = objPtr->internalRep.indexValue;
5719     return JIM_OK;
5720 }
5721
5722 /* -----------------------------------------------------------------------------
5723  * Return Code Object.
5724  * ---------------------------------------------------------------------------*/
5725
5726 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5727
5728 static Jim_ObjType returnCodeObjType = {
5729     "return-code",
5730     NULL,
5731     NULL,
5732     NULL,
5733     JIM_TYPE_NONE,
5734 };
5735
5736 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5737 {
5738     const char *str;
5739     int strLen, returnCode;
5740     jim_wide wideValue;
5741
5742     /* Get the string representation */
5743     str = Jim_GetString(objPtr, &strLen);
5744     /* Try to convert into an integer */
5745     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5746         returnCode = (int) wideValue;
5747     else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5748         returnCode = JIM_OK;
5749     else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5750         returnCode = JIM_ERR;
5751     else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5752         returnCode = JIM_RETURN;
5753     else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5754         returnCode = JIM_BREAK;
5755     else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5756         returnCode = JIM_CONTINUE;
5757     else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5758         returnCode = JIM_EVAL;
5759     else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5760         returnCode = JIM_EXIT;
5761     else {
5762         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5763         Jim_AppendStrings(interp, Jim_GetResult(interp),
5764                 "expected return code but got '", str, "'",
5765                 NULL);
5766         return JIM_ERR;
5767     }
5768     /* Free the old internal repr and set the new one. */
5769     Jim_FreeIntRep(interp, objPtr);
5770     objPtr->typePtr = &returnCodeObjType;
5771     objPtr->internalRep.returnCode = returnCode;
5772     return JIM_OK;
5773 }
5774
5775 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
5776 {
5777     if (objPtr->typePtr != &returnCodeObjType &&
5778         SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
5779         return JIM_ERR;
5780     *intPtr = objPtr->internalRep.returnCode;
5781     return JIM_OK;
5782 }
5783
5784 /* -----------------------------------------------------------------------------
5785  * Expression Parsing
5786  * ---------------------------------------------------------------------------*/
5787 static int JimParseExprOperator(struct JimParserCtx *pc);
5788 static int JimParseExprNumber(struct JimParserCtx *pc);
5789 static int JimParseExprIrrational(struct JimParserCtx *pc);
5790
5791 /* Exrp's Stack machine operators opcodes. */
5792
5793 /* Binary operators (numbers) */
5794 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
5795 #define JIM_EXPROP_MUL 0
5796 #define JIM_EXPROP_DIV 1
5797 #define JIM_EXPROP_MOD 2
5798 #define JIM_EXPROP_SUB 3
5799 #define JIM_EXPROP_ADD 4
5800 #define JIM_EXPROP_LSHIFT 5
5801 #define JIM_EXPROP_RSHIFT 6
5802 #define JIM_EXPROP_ROTL 7
5803 #define JIM_EXPROP_ROTR 8
5804 #define JIM_EXPROP_LT 9
5805 #define JIM_EXPROP_GT 10
5806 #define JIM_EXPROP_LTE 11
5807 #define JIM_EXPROP_GTE 12
5808 #define JIM_EXPROP_NUMEQ 13
5809 #define JIM_EXPROP_NUMNE 14
5810 #define JIM_EXPROP_BITAND 15
5811 #define JIM_EXPROP_BITXOR 16
5812 #define JIM_EXPROP_BITOR 17
5813 #define JIM_EXPROP_LOGICAND 18
5814 #define JIM_EXPROP_LOGICOR 19
5815 #define JIM_EXPROP_LOGICAND_LEFT 20
5816 #define JIM_EXPROP_LOGICOR_LEFT 21
5817 #define JIM_EXPROP_POW 22
5818 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
5819
5820 /* Binary operators (strings) */
5821 #define JIM_EXPROP_STREQ 23
5822 #define JIM_EXPROP_STRNE 24
5823
5824 /* Unary operators (numbers) */
5825 #define JIM_EXPROP_NOT 25
5826 #define JIM_EXPROP_BITNOT 26
5827 #define JIM_EXPROP_UNARYMINUS 27
5828 #define JIM_EXPROP_UNARYPLUS 28
5829 #define JIM_EXPROP_LOGICAND_RIGHT 29
5830 #define JIM_EXPROP_LOGICOR_RIGHT 30
5831
5832 /* Ternary operators */
5833 #define JIM_EXPROP_TERNARY 31
5834
5835 /* Operands */
5836 #define JIM_EXPROP_NUMBER 32
5837 #define JIM_EXPROP_COMMAND 33
5838 #define JIM_EXPROP_VARIABLE 34
5839 #define JIM_EXPROP_DICTSUGAR 35
5840 #define JIM_EXPROP_SUBST 36
5841 #define JIM_EXPROP_STRING 37
5842
5843 /* Operators table */
5844 typedef struct Jim_ExprOperator {
5845     const char *name;
5846     int precedence;
5847     int arity;
5848     int opcode;
5849 } Jim_ExprOperator;
5850
5851 /* name - precedence - arity - opcode */
5852 static struct Jim_ExprOperator Jim_ExprOperators[] = {
5853     {"!", 300, 1, JIM_EXPROP_NOT},
5854     {"~", 300, 1, JIM_EXPROP_BITNOT},
5855     {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
5856     {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
5857
5858     {"**", 250, 2, JIM_EXPROP_POW},
5859
5860     {"*", 200, 2, JIM_EXPROP_MUL},
5861     {"/", 200, 2, JIM_EXPROP_DIV},
5862     {"%", 200, 2, JIM_EXPROP_MOD},
5863
5864     {"-", 100, 2, JIM_EXPROP_SUB},
5865     {"+", 100, 2, JIM_EXPROP_ADD},
5866
5867     {"<<<", 90, 3, JIM_EXPROP_ROTL},
5868     {">>>", 90, 3, JIM_EXPROP_ROTR},
5869     {"<<", 90, 2, JIM_EXPROP_LSHIFT},
5870     {">>", 90, 2, JIM_EXPROP_RSHIFT},
5871
5872     {"<",  80, 2, JIM_EXPROP_LT},
5873     {">",  80, 2, JIM_EXPROP_GT},
5874     {"<=", 80, 2, JIM_EXPROP_LTE},
5875     {">=", 80, 2, JIM_EXPROP_GTE},
5876
5877     {"==", 70, 2, JIM_EXPROP_NUMEQ},
5878     {"!=", 70, 2, JIM_EXPROP_NUMNE},
5879
5880     {"eq", 60, 2, JIM_EXPROP_STREQ},
5881     {"ne", 60, 2, JIM_EXPROP_STRNE},
5882
5883     {"&", 50, 2, JIM_EXPROP_BITAND},
5884     {"^", 49, 2, JIM_EXPROP_BITXOR},
5885     {"|", 48, 2, JIM_EXPROP_BITOR},
5886
5887     {"&&", 10, 2, JIM_EXPROP_LOGICAND},
5888     {"||", 10, 2, JIM_EXPROP_LOGICOR},
5889
5890     {"?", 5, 3, JIM_EXPROP_TERNARY},
5891     /* private operators */
5892     {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
5893     {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
5894     {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
5895     {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
5896 };
5897
5898 #define JIM_EXPR_OPERATORS_NUM \
5899     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
5900
5901 int JimParseExpression(struct JimParserCtx *pc)
5902 {
5903     /* Discard spaces and quoted newline */
5904     while(*(pc->p) == ' ' ||
5905           *(pc->p) == '\t' ||
5906           *(pc->p) == '\r' ||
5907           *(pc->p) == '\n' ||
5908             (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
5909         pc->p++; pc->len--;
5910     }
5911
5912     if (pc->len == 0) {
5913         pc->tstart = pc->tend = pc->p;
5914         pc->tline = pc->linenr;
5915         pc->tt = JIM_TT_EOL;
5916         pc->eof = 1;
5917         return JIM_OK;
5918     }
5919     switch(*(pc->p)) {
5920     case '(':
5921         pc->tstart = pc->tend = pc->p;
5922         pc->tline = pc->linenr;
5923         pc->tt = JIM_TT_SUBEXPR_START;
5924         pc->p++; pc->len--;
5925         break;
5926     case ')':
5927         pc->tstart = pc->tend = pc->p;
5928         pc->tline = pc->linenr;
5929         pc->tt = JIM_TT_SUBEXPR_END;
5930         pc->p++; pc->len--;
5931         break;
5932     case '[':
5933         return JimParseCmd(pc);
5934         break;
5935     case '$':
5936         if (JimParseVar(pc) == JIM_ERR)
5937             return JimParseExprOperator(pc);
5938         else
5939             return JIM_OK;
5940         break;
5941     case '-':
5942         if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
5943             isdigit((int)*(pc->p+1)))
5944             return JimParseExprNumber(pc);
5945         else
5946             return JimParseExprOperator(pc);
5947         break;
5948     case '0': case '1': case '2': case '3': case '4':
5949     case '5': case '6': case '7': case '8': case '9': case '.':
5950         return JimParseExprNumber(pc);
5951         break;
5952     case '"':
5953     case '{':
5954         /* Here it's possible to reuse the List String parsing. */
5955         pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
5956         return JimParseListStr(pc);
5957         break;
5958     case 'N': case 'I':
5959     case 'n': case 'i':
5960         if (JimParseExprIrrational(pc) == JIM_ERR)
5961             return JimParseExprOperator(pc);
5962         break;
5963     default:
5964         return JimParseExprOperator(pc);
5965         break;
5966     }
5967     return JIM_OK;
5968 }
5969
5970 int JimParseExprNumber(struct JimParserCtx *pc)
5971 {
5972     int allowdot = 1;
5973     int allowhex = 0;
5974
5975     pc->tstart = pc->p;
5976     pc->tline = pc->linenr;
5977     if (*pc->p == '-') {
5978         pc->p++; pc->len--;
5979     }
5980     while (  isdigit((int)*pc->p) 
5981           || (allowhex && isxdigit((int)*pc->p) )
5982           || (allowdot && *pc->p == '.') 
5983           || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
5984               (*pc->p == 'x' || *pc->p == 'X'))
5985           )
5986     {
5987         if ((*pc->p == 'x') || (*pc->p == 'X')) {
5988             allowhex = 1;
5989             allowdot = 0;
5990                 }
5991         if (*pc->p == '.')
5992             allowdot = 0;
5993         pc->p++; pc->len--;
5994         if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
5995             pc->p += 2; pc->len -= 2;
5996         }
5997     }
5998     pc->tend = pc->p-1;
5999     pc->tt = JIM_TT_EXPR_NUMBER;
6000     return JIM_OK;
6001 }
6002
6003 int JimParseExprIrrational(struct JimParserCtx *pc)
6004 {
6005     const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6006     const char **token;
6007     for (token = Tokens; *token != NULL; token++) {
6008         int len = strlen(*token);
6009         if (strncmp(*token, pc->p, len) == 0) {
6010             pc->tstart = pc->p;
6011             pc->tend = pc->p + len - 1;
6012             pc->p += len; pc->len -= len;
6013             pc->tline = pc->linenr;
6014             pc->tt = JIM_TT_EXPR_NUMBER;
6015             return JIM_OK;
6016         }
6017     }
6018     return JIM_ERR;
6019 }
6020
6021 int JimParseExprOperator(struct JimParserCtx *pc)
6022 {
6023     int i;
6024     int bestIdx = -1, bestLen = 0;
6025
6026     /* Try to get the longest match. */
6027     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6028         const char *opname;
6029         int oplen;
6030
6031         opname = Jim_ExprOperators[i].name;
6032         if (opname == NULL) continue;
6033         oplen = strlen(opname);
6034
6035         if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6036             bestIdx = i;
6037             bestLen = oplen;
6038         }
6039     }
6040     if (bestIdx == -1) return JIM_ERR;
6041     pc->tstart = pc->p;
6042     pc->tend = pc->p + bestLen - 1;
6043     pc->p += bestLen; pc->len -= bestLen;
6044     pc->tline = pc->linenr;
6045     pc->tt = JIM_TT_EXPR_OPERATOR;
6046     return JIM_OK;
6047 }
6048
6049 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6050 {
6051     int i;
6052     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6053         if (Jim_ExprOperators[i].name &&
6054             strcmp(opname, Jim_ExprOperators[i].name) == 0)
6055             return &Jim_ExprOperators[i];
6056     return NULL;
6057 }
6058
6059 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6060 {
6061     int i;
6062     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6063         if (Jim_ExprOperators[i].opcode == opcode)
6064             return &Jim_ExprOperators[i];
6065     return NULL;
6066 }
6067
6068 /* -----------------------------------------------------------------------------
6069  * Expression Object
6070  * ---------------------------------------------------------------------------*/
6071 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6072 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6073 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6074
6075 static Jim_ObjType exprObjType = {
6076     "expression",
6077     FreeExprInternalRep,
6078     DupExprInternalRep,
6079     NULL,
6080     JIM_TYPE_REFERENCES,
6081 };
6082
6083 /* Expr bytecode structure */
6084 typedef struct ExprByteCode {
6085     int *opcode;        /* Integer array of opcodes. */
6086     Jim_Obj **obj;      /* Array of associated Jim Objects. */
6087     int len;            /* Bytecode length */
6088     int inUse;          /* Used for sharing. */
6089 } ExprByteCode;
6090
6091 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6092 {
6093     int i;
6094     ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6095
6096     expr->inUse--;
6097     if (expr->inUse != 0) return;
6098     for (i = 0; i < expr->len; i++)
6099         Jim_DecrRefCount(interp, expr->obj[i]);
6100     Jim_Free(expr->opcode);
6101     Jim_Free(expr->obj);
6102     Jim_Free(expr);
6103 }
6104
6105 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6106 {
6107     JIM_NOTUSED(interp);
6108     JIM_NOTUSED(srcPtr);
6109
6110     /* Just returns an simple string. */
6111     dupPtr->typePtr = NULL;
6112 }
6113
6114 /* Add a new instruction to an expression bytecode structure. */
6115 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6116         int opcode, char *str, int len)
6117 {
6118     expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6119     expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6120     expr->opcode[expr->len] = opcode;
6121     expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6122     Jim_IncrRefCount(expr->obj[expr->len]);
6123     expr->len++;
6124 }
6125
6126 /* Check if an expr program looks correct. */
6127 static int ExprCheckCorrectness(ExprByteCode *expr)
6128 {
6129     int i;
6130     int stacklen = 0;
6131
6132     /* Try to check if there are stack underflows,
6133      * and make sure at the end of the program there is
6134      * a single result on the stack. */
6135     for (i = 0; i < expr->len; i++) {
6136         switch(expr->opcode[i]) {
6137         case JIM_EXPROP_NUMBER:
6138         case JIM_EXPROP_STRING:
6139         case JIM_EXPROP_SUBST:
6140         case JIM_EXPROP_VARIABLE:
6141         case JIM_EXPROP_DICTSUGAR:
6142         case JIM_EXPROP_COMMAND:
6143             stacklen++;
6144             break;
6145         case JIM_EXPROP_NOT:
6146         case JIM_EXPROP_BITNOT:
6147         case JIM_EXPROP_UNARYMINUS:
6148         case JIM_EXPROP_UNARYPLUS:
6149             /* Unary operations */
6150             if (stacklen < 1) return JIM_ERR;
6151             break;
6152         case JIM_EXPROP_ADD:
6153         case JIM_EXPROP_SUB:
6154         case JIM_EXPROP_MUL:
6155         case JIM_EXPROP_DIV:
6156         case JIM_EXPROP_MOD:
6157         case JIM_EXPROP_LT:
6158         case JIM_EXPROP_GT:
6159         case JIM_EXPROP_LTE:
6160         case JIM_EXPROP_GTE:
6161         case JIM_EXPROP_ROTL:
6162         case JIM_EXPROP_ROTR:
6163         case JIM_EXPROP_LSHIFT:
6164         case JIM_EXPROP_RSHIFT:
6165         case JIM_EXPROP_NUMEQ:
6166         case JIM_EXPROP_NUMNE:
6167         case JIM_EXPROP_STREQ:
6168         case JIM_EXPROP_STRNE:
6169         case JIM_EXPROP_BITAND:
6170         case JIM_EXPROP_BITXOR:
6171         case JIM_EXPROP_BITOR:
6172         case JIM_EXPROP_LOGICAND:
6173         case JIM_EXPROP_LOGICOR:
6174         case JIM_EXPROP_POW:
6175             /* binary operations */
6176             if (stacklen < 2) return JIM_ERR;
6177             stacklen--;
6178             break;
6179         default:
6180             Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6181             break;
6182         }
6183     }
6184     if (stacklen != 1) return JIM_ERR;
6185     return JIM_OK;
6186 }
6187
6188 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6189         ScriptObj *topLevelScript)
6190 {
6191     int i;
6192
6193     return;
6194     for (i = 0; i < expr->len; i++) {
6195         Jim_Obj *foundObjPtr;
6196
6197         if (expr->obj[i] == NULL) continue;
6198         foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6199                 NULL, expr->obj[i]);
6200         if (foundObjPtr != NULL) {
6201             Jim_IncrRefCount(foundObjPtr);
6202             Jim_DecrRefCount(interp, expr->obj[i]);
6203             expr->obj[i] = foundObjPtr;
6204         }
6205     }
6206 }
6207
6208 /* This procedure converts every occurrence of || and && opereators
6209  * in lazy unary versions.
6210  *
6211  * a b || is converted into:
6212  *
6213  * a <offset> |L b |R
6214  *
6215  * a b && is converted into:
6216  *
6217  * a <offset> &L b &R
6218  *
6219  * "|L" checks if 'a' is true:
6220  *   1) if it is true pushes 1 and skips <offset> istructions to reach
6221  *      the opcode just after |R.
6222  *   2) if it is false does nothing.
6223  * "|R" checks if 'b' is true:
6224  *   1) if it is true pushes 1, otherwise pushes 0.
6225  *
6226  * "&L" checks if 'a' is true:
6227  *   1) if it is true does nothing.
6228  *   2) If it is false pushes 0 and skips <offset> istructions to reach
6229  *      the opcode just after &R
6230  * "&R" checks if 'a' is true:
6231  *      if it is true pushes 1, otherwise pushes 0.
6232  */
6233 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6234 {
6235     while (1) {
6236         int index = -1, leftindex, arity, i, offset;
6237         Jim_ExprOperator *op;
6238
6239         /* Search for || or && */
6240         for (i = 0; i < expr->len; i++) {
6241             if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6242                 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6243                 index = i;
6244                 break;
6245             }
6246         }
6247         if (index == -1) return;
6248         /* Search for the end of the first operator */
6249         leftindex = index-1;
6250         arity = 1;
6251         while(arity) {
6252             switch(expr->opcode[leftindex]) {
6253             case JIM_EXPROP_NUMBER:
6254             case JIM_EXPROP_COMMAND:
6255             case JIM_EXPROP_VARIABLE:
6256             case JIM_EXPROP_DICTSUGAR:
6257             case JIM_EXPROP_SUBST:
6258             case JIM_EXPROP_STRING:
6259                 break;
6260             default:
6261                 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6262                 if (op == NULL) {
6263                     Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6264                 }
6265                 arity += op->arity;
6266                 break;
6267             }
6268             arity--;
6269             leftindex--;
6270         }
6271         leftindex++;
6272         expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6273         expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6274         memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6275                 sizeof(int)*(expr->len-leftindex));
6276         memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6277                 sizeof(Jim_Obj*)*(expr->len-leftindex));
6278         expr->len += 2;
6279         index += 2;
6280         offset = (index-leftindex)-1;
6281         Jim_DecrRefCount(interp, expr->obj[index]);
6282         if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6283             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6284             expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6285             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6286             expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6287         } else {
6288             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6289             expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6290             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6291             expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6292         }
6293         expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6294         expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6295         Jim_IncrRefCount(expr->obj[index]);
6296         Jim_IncrRefCount(expr->obj[leftindex]);
6297         Jim_IncrRefCount(expr->obj[leftindex+1]);
6298     }
6299 }
6300
6301 /* This method takes the string representation of an expression
6302  * and generates a program for the Expr's stack-based VM. */
6303 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6304 {
6305     int exprTextLen;
6306     const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6307     struct JimParserCtx parser;
6308     int i, shareLiterals;
6309     ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6310     Jim_Stack stack;
6311     Jim_ExprOperator *op;
6312
6313     /* Perform literal sharing with the current procedure
6314      * running only if this expression appears to be not generated
6315      * at runtime. */
6316     shareLiterals = objPtr->typePtr == &sourceObjType;
6317
6318     expr->opcode = NULL;
6319     expr->obj = NULL;
6320     expr->len = 0;
6321     expr->inUse = 1;
6322
6323     Jim_InitStack(&stack);
6324     JimParserInit(&parser, exprText, exprTextLen, 1);
6325     while(!JimParserEof(&parser)) {
6326         char *token;
6327         int len, type;
6328
6329         if (JimParseExpression(&parser) != JIM_OK) {
6330             Jim_SetResultString(interp, "Syntax error in expression", -1);
6331             goto err;
6332         }
6333         token = JimParserGetToken(&parser, &len, &type, NULL);
6334         if (type == JIM_TT_EOL) {
6335             Jim_Free(token);
6336             break;
6337         }
6338         switch(type) {
6339         case JIM_TT_STR:
6340             ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6341             break;
6342         case JIM_TT_ESC:
6343             ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6344             break;
6345         case JIM_TT_VAR:
6346             ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6347             break;
6348         case JIM_TT_DICTSUGAR:
6349             ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6350             break;
6351         case JIM_TT_CMD:
6352             ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6353             break;
6354         case JIM_TT_EXPR_NUMBER:
6355             ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6356             break;
6357         case JIM_TT_EXPR_OPERATOR:
6358             op = JimExprOperatorInfo(token);
6359             while(1) {
6360                 Jim_ExprOperator *stackTopOp;
6361
6362                 if (Jim_StackPeek(&stack) != NULL) {
6363                     stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6364                 } else {
6365                     stackTopOp = NULL;
6366                 }
6367                 if (Jim_StackLen(&stack) && op->arity != 1 &&
6368                     stackTopOp && stackTopOp->precedence >= op->precedence)
6369                 {
6370                     ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6371                         Jim_StackPeek(&stack), -1);
6372                     Jim_StackPop(&stack);
6373                 } else {
6374                     break;
6375                 }
6376             }
6377             Jim_StackPush(&stack, token);
6378             break;
6379         case JIM_TT_SUBEXPR_START:
6380             Jim_StackPush(&stack, Jim_StrDup("("));
6381             Jim_Free(token);
6382             break;
6383         case JIM_TT_SUBEXPR_END:
6384             {
6385                 int found = 0;
6386                 while(Jim_StackLen(&stack)) {
6387                     char *opstr = Jim_StackPop(&stack);
6388                     if (!strcmp(opstr, "(")) {
6389                         Jim_Free(opstr);
6390                         found = 1;
6391                         break;
6392                     }
6393                     op = JimExprOperatorInfo(opstr);
6394                     ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6395                 }
6396                 if (!found) {
6397                     Jim_SetResultString(interp,
6398                         "Unexpected close parenthesis", -1);
6399                     goto err;
6400                 }
6401             }
6402             Jim_Free(token);
6403             break;
6404         default:
6405             Jim_Panic(interp,"Default reached in SetExprFromAny()");
6406             break;
6407         }
6408     }
6409     while (Jim_StackLen(&stack)) {
6410         char *opstr = Jim_StackPop(&stack);
6411         op = JimExprOperatorInfo(opstr);
6412         if (op == NULL && !strcmp(opstr, "(")) {
6413             Jim_Free(opstr);
6414             Jim_SetResultString(interp, "Missing close parenthesis", -1);
6415             goto err;
6416         }
6417         ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6418     }
6419     /* Check program correctness. */
6420     if (ExprCheckCorrectness(expr) != JIM_OK) {
6421         Jim_SetResultString(interp, "Invalid expression", -1);
6422         goto err;
6423     }
6424
6425     /* Free the stack used for the compilation. */
6426     Jim_FreeStackElements(&stack, Jim_Free);
6427     Jim_FreeStack(&stack);
6428
6429     /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6430     ExprMakeLazy(interp, expr);
6431
6432     /* Perform literal sharing */
6433     if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6434         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6435         if (bodyObjPtr->typePtr == &scriptObjType) {
6436             ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6437             ExprShareLiterals(interp, expr, bodyScript);
6438         }
6439     }
6440
6441     /* Free the old internal rep and set the new one. */
6442     Jim_FreeIntRep(interp, objPtr);
6443     Jim_SetIntRepPtr(objPtr, expr);
6444     objPtr->typePtr = &exprObjType;
6445     return JIM_OK;
6446
6447 err:    /* we jump here on syntax/compile errors. */
6448     Jim_FreeStackElements(&stack, Jim_Free);
6449     Jim_FreeStack(&stack);
6450     Jim_Free(expr->opcode);
6451     for (i = 0; i < expr->len; i++) {
6452         Jim_DecrRefCount(interp,expr->obj[i]);
6453     }
6454     Jim_Free(expr->obj);
6455     Jim_Free(expr);
6456     return JIM_ERR;
6457 }
6458
6459 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6460 {
6461     if (objPtr->typePtr != &exprObjType) {
6462         if (SetExprFromAny(interp, objPtr) != JIM_OK)
6463             return NULL;
6464     }
6465     return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6466 }
6467
6468 /* -----------------------------------------------------------------------------
6469  * Expressions evaluation.
6470  * Jim uses a specialized stack-based virtual machine for expressions,
6471  * that takes advantage of the fact that expr's operators
6472  * can't be redefined.
6473  *
6474  * Jim_EvalExpression() uses the bytecode compiled by
6475  * SetExprFromAny() method of the "expression" object.
6476  *
6477  * On success a Tcl Object containing the result of the evaluation
6478  * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6479  * returned.
6480  * On error the function returns a retcode != to JIM_OK and set a suitable
6481  * error on the interp.
6482  * ---------------------------------------------------------------------------*/
6483 #define JIM_EE_STATICSTACK_LEN 10
6484
6485 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6486         Jim_Obj **exprResultPtrPtr)
6487 {
6488     ExprByteCode *expr;
6489     Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6490     int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6491
6492     Jim_IncrRefCount(exprObjPtr);
6493     expr = Jim_GetExpression(interp, exprObjPtr);
6494     if (!expr) {
6495         Jim_DecrRefCount(interp, exprObjPtr);
6496         return JIM_ERR; /* error in expression. */
6497     }
6498     /* In order to avoid that the internal repr gets freed due to
6499      * shimmering of the exprObjPtr's object, we make the internal rep
6500      * shared. */
6501     expr->inUse++;
6502
6503     /* The stack-based expr VM itself */
6504
6505     /* Stack allocation. Expr programs have the feature that
6506      * a program of length N can't require a stack longer than
6507      * N. */
6508     if (expr->len > JIM_EE_STATICSTACK_LEN)
6509         stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6510     else
6511         stack = staticStack;
6512
6513     /* Execute every istruction */
6514     for (i = 0; i < expr->len; i++) {
6515         Jim_Obj *A, *B, *objPtr;
6516         jim_wide wA, wB, wC;
6517         double dA, dB, dC;
6518         const char *sA, *sB;
6519         int Alen, Blen, retcode;
6520         int opcode = expr->opcode[i];
6521
6522         if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6523             stack[stacklen++] = expr->obj[i];
6524             Jim_IncrRefCount(expr->obj[i]);
6525         } else if (opcode == JIM_EXPROP_VARIABLE) {
6526             objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6527             if (objPtr == NULL) {
6528                 error = 1;
6529                 goto err;
6530             }
6531             stack[stacklen++] = objPtr;
6532             Jim_IncrRefCount(objPtr);
6533         } else if (opcode == JIM_EXPROP_SUBST) {
6534             if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6535                         &objPtr, JIM_NONE)) != JIM_OK)
6536             {
6537                 error = 1;
6538                 errRetCode = retcode;
6539                 goto err;
6540             }
6541             stack[stacklen++] = objPtr;
6542             Jim_IncrRefCount(objPtr);
6543         } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6544             objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6545             if (objPtr == NULL) {
6546                 error = 1;
6547                 goto err;
6548             }
6549             stack[stacklen++] = objPtr;
6550             Jim_IncrRefCount(objPtr);
6551         } else if (opcode == JIM_EXPROP_COMMAND) {
6552             if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6553                 error = 1;
6554                 errRetCode = retcode;
6555                 goto err;
6556             }
6557             stack[stacklen++] = interp->result;
6558             Jim_IncrRefCount(interp->result);
6559         } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6560                    opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6561         {
6562             /* Note that there isn't to increment the
6563              * refcount of objects. the references are moved
6564              * from stack to A and B. */
6565             B = stack[--stacklen];
6566             A = stack[--stacklen];
6567
6568             /* --- Integer --- */
6569             if ((A->typePtr == &doubleObjType && !A->bytes) ||
6570                 (B->typePtr == &doubleObjType && !B->bytes) ||
6571                 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6572                 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6573                 goto trydouble;
6574             }
6575             Jim_DecrRefCount(interp, A);
6576             Jim_DecrRefCount(interp, B);
6577             switch(expr->opcode[i]) {
6578             case JIM_EXPROP_ADD: wC = wA+wB; break;
6579             case JIM_EXPROP_SUB: wC = wA-wB; break;
6580             case JIM_EXPROP_MUL: wC = wA*wB; break;
6581             case JIM_EXPROP_LT: wC = wA<wB; break;
6582             case JIM_EXPROP_GT: wC = wA>wB; break;
6583             case JIM_EXPROP_LTE: wC = wA<=wB; break;
6584             case JIM_EXPROP_GTE: wC = wA>=wB; break;
6585             case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6586             case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6587             case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6588             case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6589             case JIM_EXPROP_BITAND: wC = wA&wB; break;
6590             case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6591             case JIM_EXPROP_BITOR: wC = wA|wB; break;
6592             case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6593             case JIM_EXPROP_LOGICAND_LEFT:
6594                 if (wA == 0) {
6595                     i += (int)wB;
6596                     wC = 0;
6597                 } else {
6598                     continue;
6599                 }
6600                 break;
6601             case JIM_EXPROP_LOGICOR_LEFT:
6602                 if (wA != 0) {
6603                     i += (int)wB;
6604                     wC = 1;
6605                 } else {
6606                     continue;
6607                 }
6608                 break;
6609             case JIM_EXPROP_DIV:
6610                 if (wB == 0) goto divbyzero;
6611                 wC = wA/wB;
6612                 break;
6613             case JIM_EXPROP_MOD:
6614                 if (wB == 0) goto divbyzero;
6615                 wC = wA%wB;
6616                 break;
6617             case JIM_EXPROP_ROTL: {
6618                 /* uint32_t would be better. But not everyone has inttypes.h?*/
6619                 unsigned long uA = (unsigned long)wA;
6620 #ifdef _MSC_VER
6621                 wC = _rotl(uA,(unsigned long)wB);
6622 #else
6623                 const unsigned int S = sizeof(unsigned long) * 8;
6624                 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6625 #endif
6626                 break;
6627             }
6628             case JIM_EXPROP_ROTR: {
6629                 unsigned long uA = (unsigned long)wA;
6630 #ifdef _MSC_VER
6631                 wC = _rotr(uA,(unsigned long)wB);
6632 #else
6633                 const unsigned int S = sizeof(unsigned long) * 8;
6634                 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6635 #endif
6636                 break;
6637             }
6638
6639             default:
6640                 wC = 0; /* avoid gcc warning */
6641                 break;
6642             }
6643             stack[stacklen] = Jim_NewIntObj(interp, wC);
6644             Jim_IncrRefCount(stack[stacklen]);
6645             stacklen++;
6646             continue;
6647 trydouble:
6648             /* --- Double --- */
6649             if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6650                 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6651                 Jim_DecrRefCount(interp, A);
6652                 Jim_DecrRefCount(interp, B);
6653                 error = 1;
6654                 goto err;
6655             }
6656             Jim_DecrRefCount(interp, A);
6657             Jim_DecrRefCount(interp, B);
6658             switch(expr->opcode[i]) {
6659             case JIM_EXPROP_ROTL:
6660             case JIM_EXPROP_ROTR:
6661             case JIM_EXPROP_LSHIFT:
6662             case JIM_EXPROP_RSHIFT:
6663             case JIM_EXPROP_BITAND:
6664             case JIM_EXPROP_BITXOR:
6665             case JIM_EXPROP_BITOR:
6666             case JIM_EXPROP_MOD:
6667             case JIM_EXPROP_POW:
6668                 Jim_SetResultString(interp,
6669                     "Got floating-point value where integer was expected", -1);
6670                 error = 1;
6671                 goto err;
6672                 break;
6673             case JIM_EXPROP_ADD: dC = dA+dB; break;
6674             case JIM_EXPROP_SUB: dC = dA-dB; break;
6675             case JIM_EXPROP_MUL: dC = dA*dB; break;
6676             case JIM_EXPROP_LT: dC = dA<dB; break;
6677             case JIM_EXPROP_GT: dC = dA>dB; break;
6678             case JIM_EXPROP_LTE: dC = dA<=dB; break;
6679             case JIM_EXPROP_GTE: dC = dA>=dB; break;
6680             case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6681             case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6682             case JIM_EXPROP_LOGICAND_LEFT:
6683                 if (dA == 0) {
6684                     i += (int)dB;
6685                     dC = 0;
6686                 } else {
6687                     continue;
6688                 }
6689                 break;
6690             case JIM_EXPROP_LOGICOR_LEFT:
6691                 if (dA != 0) {
6692                     i += (int)dB;
6693                     dC = 1;
6694                 } else {
6695                     continue;
6696                 }
6697                 break;
6698             case JIM_EXPROP_DIV:
6699                 if (dB == 0) goto divbyzero;
6700                 dC = dA/dB;
6701                 break;
6702             default:
6703                 dC = 0; /* avoid gcc warning */
6704                 break;
6705             }
6706             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6707             Jim_IncrRefCount(stack[stacklen]);
6708             stacklen++;
6709         } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6710             B = stack[--stacklen];
6711             A = stack[--stacklen];
6712             sA = Jim_GetString(A, &Alen);
6713             sB = Jim_GetString(B, &Blen);
6714             switch(expr->opcode[i]) {
6715             case JIM_EXPROP_STREQ:
6716                 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6717                     wC = 1;
6718                 else
6719                     wC = 0;
6720                 break;
6721             case JIM_EXPROP_STRNE:
6722                 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6723                     wC = 1;
6724                 else
6725                     wC = 0;
6726                 break;
6727             default:
6728                 wC = 0; /* avoid gcc warning */
6729                 break;
6730             }
6731             Jim_DecrRefCount(interp, A);
6732             Jim_DecrRefCount(interp, B);
6733             stack[stacklen] = Jim_NewIntObj(interp, wC);
6734             Jim_IncrRefCount(stack[stacklen]);
6735             stacklen++;
6736         } else if (opcode == JIM_EXPROP_NOT ||
6737                    opcode == JIM_EXPROP_BITNOT ||
6738                    opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6739                    opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6740             /* Note that there isn't to increment the
6741              * refcount of objects. the references are moved
6742              * from stack to A and B. */
6743             A = stack[--stacklen];
6744
6745             /* --- Integer --- */
6746             if ((A->typePtr == &doubleObjType && !A->bytes) ||
6747                 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6748                 goto trydouble_unary;
6749             }
6750             Jim_DecrRefCount(interp, A);
6751             switch(expr->opcode[i]) {
6752             case JIM_EXPROP_NOT: wC = !wA; break;
6753             case JIM_EXPROP_BITNOT: wC = ~wA; break;
6754             case JIM_EXPROP_LOGICAND_RIGHT:
6755             case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6756             default:
6757                 wC = 0; /* avoid gcc warning */
6758                 break;
6759             }
6760             stack[stacklen] = Jim_NewIntObj(interp, wC);
6761             Jim_IncrRefCount(stack[stacklen]);
6762             stacklen++;
6763             continue;
6764 trydouble_unary:
6765             /* --- Double --- */
6766             if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
6767                 Jim_DecrRefCount(interp, A);
6768                 error = 1;
6769                 goto err;
6770             }
6771             Jim_DecrRefCount(interp, A);
6772             switch(expr->opcode[i]) {
6773             case JIM_EXPROP_NOT: dC = !dA; break;
6774             case JIM_EXPROP_LOGICAND_RIGHT:
6775             case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
6776             case JIM_EXPROP_BITNOT:
6777                 Jim_SetResultString(interp,
6778                     "Got floating-point value where integer was expected", -1);
6779                 error = 1;
6780                 goto err;
6781                 break;
6782             default:
6783                 dC = 0; /* avoid gcc warning */
6784                 break;
6785             }
6786             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6787             Jim_IncrRefCount(stack[stacklen]);
6788             stacklen++;
6789         } else {
6790             Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
6791         }
6792     }
6793 err:
6794     /* There is no need to decerement the inUse field because
6795      * this reference is transfered back into the exprObjPtr. */
6796     Jim_FreeIntRep(interp, exprObjPtr);
6797     exprObjPtr->typePtr = &exprObjType;
6798     Jim_SetIntRepPtr(exprObjPtr, expr);
6799     Jim_DecrRefCount(interp, exprObjPtr);
6800     if (!error) {
6801         *exprResultPtrPtr = stack[0];
6802         Jim_IncrRefCount(stack[0]);
6803         errRetCode = JIM_OK;
6804     }
6805     for (i = 0; i < stacklen; i++) {
6806         Jim_DecrRefCount(interp, stack[i]);
6807     }
6808     if (stack != staticStack)
6809         Jim_Free(stack);
6810     return errRetCode;
6811 divbyzero:
6812     error = 1;
6813     Jim_SetResultString(interp, "Division by zero", -1);
6814     goto err;
6815 }
6816
6817 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
6818 {
6819     int retcode;
6820     jim_wide wideValue;
6821     double doubleValue;
6822     Jim_Obj *exprResultPtr;
6823
6824     retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
6825     if (retcode != JIM_OK)
6826         return retcode;
6827     if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
6828         if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
6829         {
6830             Jim_DecrRefCount(interp, exprResultPtr);
6831             return JIM_ERR;
6832         } else {
6833             Jim_DecrRefCount(interp, exprResultPtr);
6834             *boolPtr = doubleValue != 0;
6835             return JIM_OK;
6836         }
6837     }
6838     Jim_DecrRefCount(interp, exprResultPtr);
6839     *boolPtr = wideValue != 0;
6840     return JIM_OK;
6841 }
6842
6843 /* -----------------------------------------------------------------------------
6844  * ScanFormat String Object
6845  * ---------------------------------------------------------------------------*/
6846
6847 /* This Jim_Obj will held a parsed representation of a format string passed to
6848  * the Jim_ScanString command. For error diagnostics, the scanformat string has
6849  * to be parsed in its entirely first and then, if correct, can be used for
6850  * scanning. To avoid endless re-parsing, the parsed representation will be
6851  * stored in an internal representation and re-used for performance reason. */
6852  
6853 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
6854  * scanformat string. This part will later be used to extract information
6855  * out from the string to be parsed by Jim_ScanString */
6856  
6857 typedef struct ScanFmtPartDescr {
6858     char type;         /* Type of conversion (e.g. c, d, f) */
6859     char modifier;     /* Modify type (e.g. l - long, h - short */
6860     size_t  width;     /* Maximal width of input to be converted */
6861     int  pos;          /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */ 
6862     char *arg;         /* Specification of a CHARSET conversion */
6863     char *prefix;      /* Prefix to be scanned literally before conversion */
6864 } ScanFmtPartDescr;
6865
6866 /* The ScanFmtStringObj will held the internal representation of a scanformat
6867  * string parsed and separated in part descriptions. Furthermore it contains
6868  * the original string representation of the scanformat string to allow for
6869  * fast update of the Jim_Obj's string representation part.
6870  *
6871  * As add-on the internal object representation add some scratch pad area
6872  * for usage by Jim_ScanString to avoid endless allocating and freeing of
6873  * memory for purpose of string scanning.
6874  *
6875  * The error member points to a static allocated string in case of a mal-
6876  * formed scanformat string or it contains '0' (NULL) in case of a valid
6877  * parse representation.
6878  *
6879  * The whole memory of the internal representation is allocated as a single
6880  * area of memory that will be internally separated. So freeing and duplicating
6881  * of such an object is cheap */
6882
6883 typedef struct ScanFmtStringObj {
6884     jim_wide        size;         /* Size of internal repr in bytes */
6885     char            *stringRep;   /* Original string representation */
6886     size_t          count;        /* Number of ScanFmtPartDescr contained */
6887     size_t          convCount;    /* Number of conversions that will assign */
6888     size_t          maxPos;       /* Max position index if XPG3 is used */
6889     const char      *error;       /* Ptr to error text (NULL if no error */
6890     char            *scratch;     /* Some scratch pad used by Jim_ScanString */
6891     ScanFmtPartDescr descr[1];    /* The vector of partial descriptions */
6892 } ScanFmtStringObj;
6893
6894
6895 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6896 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6897 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
6898
6899 static Jim_ObjType scanFmtStringObjType = {
6900     "scanformatstring",
6901     FreeScanFmtInternalRep,
6902     DupScanFmtInternalRep,
6903     UpdateStringOfScanFmt,
6904     JIM_TYPE_NONE,
6905 };
6906
6907 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6908 {
6909     JIM_NOTUSED(interp);
6910     Jim_Free((char*)objPtr->internalRep.ptr);
6911     objPtr->internalRep.ptr = 0;
6912 }
6913
6914 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6915 {
6916     size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
6917     ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
6918
6919     JIM_NOTUSED(interp);
6920     memcpy(newVec, srcPtr->internalRep.ptr, size);
6921     dupPtr->internalRep.ptr = newVec;
6922     dupPtr->typePtr = &scanFmtStringObjType;
6923 }
6924
6925 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
6926 {
6927     char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
6928
6929     objPtr->bytes = Jim_StrDup(bytes);
6930     objPtr->length = strlen(bytes);
6931 }
6932
6933 /* SetScanFmtFromAny will parse a given string and create the internal
6934  * representation of the format specification. In case of an error
6935  * the error data member of the internal representation will be set
6936  * to an descriptive error text and the function will be left with
6937  * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
6938  * specification */
6939
6940 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6941 {
6942     ScanFmtStringObj *fmtObj;
6943     char *buffer;
6944     int maxCount, i, approxSize, lastPos = -1;
6945     const char *fmt = objPtr->bytes;
6946     int maxFmtLen = objPtr->length;
6947     const char *fmtEnd = fmt + maxFmtLen;
6948     int curr;
6949
6950     Jim_FreeIntRep(interp, objPtr);
6951     /* Count how many conversions could take place maximally */
6952     for (i=0, maxCount=0; i < maxFmtLen; ++i)
6953         if (fmt[i] == '%')
6954             ++maxCount;
6955     /* Calculate an approximation of the memory necessary */
6956     approxSize = sizeof(ScanFmtStringObj)           /* Size of the container */
6957         + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
6958         + maxFmtLen * sizeof(char) + 3 + 1          /* Scratch + "%n" + '\0' */
6959         + maxFmtLen * sizeof(char) + 1              /* Original stringrep */
6960         + maxFmtLen * sizeof(char)                  /* Arg for CHARSETs */
6961         + (maxCount +1) * sizeof(char)              /* '\0' for every partial */
6962         + 1;                                        /* safety byte */
6963     fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
6964     memset(fmtObj, 0, approxSize);
6965     fmtObj->size = approxSize;
6966     fmtObj->maxPos = 0;
6967     fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
6968     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
6969     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
6970     buffer = fmtObj->stringRep + maxFmtLen + 1;
6971     objPtr->internalRep.ptr = fmtObj;
6972     objPtr->typePtr = &scanFmtStringObjType;
6973     for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
6974         int width=0, skip;
6975         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
6976         fmtObj->count++;
6977         descr->width = 0;                   /* Assume width unspecified */ 
6978         /* Overread and store any "literal" prefix */
6979         if (*fmt != '%' || fmt[1] == '%') {
6980             descr->type = 0;
6981             descr->prefix = &buffer[i];
6982             for (; fmt < fmtEnd; ++fmt) {
6983                 if (*fmt == '%') {
6984                     if (fmt[1] != '%') break;
6985                     ++fmt;
6986                 }
6987                 buffer[i++] = *fmt;
6988             }
6989             buffer[i++] = 0;
6990         } 
6991         /* Skip the conversion introducing '%' sign */
6992         ++fmt;      
6993         /* End reached due to non-conversion literal only? */
6994         if (fmt >= fmtEnd)
6995             goto done;
6996         descr->pos = 0;                     /* Assume "natural" positioning */
6997         if (*fmt == '*') {
6998             descr->pos = -1;       /* Okay, conversion will not be assigned */
6999             ++fmt;
7000         } else
7001             fmtObj->convCount++;    /* Otherwise count as assign-conversion */
7002         /* Check if next token is a number (could be width or pos */
7003         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7004             fmt += skip;
7005             /* Was the number a XPG3 position specifier? */
7006             if (descr->pos != -1 && *fmt == '$') {
7007                 int prev;
7008                 ++fmt;
7009                 descr->pos = width;
7010                 width = 0;
7011                 /* Look if "natural" postioning and XPG3 one was mixed */
7012                 if ((lastPos == 0 && descr->pos > 0)
7013                         || (lastPos > 0 && descr->pos == 0)) {
7014                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7015                     return JIM_ERR;
7016                 }
7017                 /* Look if this position was already used */
7018                 for (prev=0; prev < curr; ++prev) {
7019                     if (fmtObj->descr[prev].pos == -1) continue;
7020                     if (fmtObj->descr[prev].pos == descr->pos) {
7021                         fmtObj->error = "same \"%n$\" conversion specifier "
7022                             "used more than once";
7023                         return JIM_ERR;
7024                     }
7025                 }
7026                 /* Try to find a width after the XPG3 specifier */
7027                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7028                     descr->width = width;
7029                     fmt += skip;
7030                 }
7031                 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7032                     fmtObj->maxPos = descr->pos;
7033             } else {
7034                 /* Number was not a XPG3, so it has to be a width */
7035                 descr->width = width;
7036             }
7037         }
7038         /* If positioning mode was undetermined yet, fix this */
7039         if (lastPos == -1)
7040             lastPos = descr->pos;
7041         /* Handle CHARSET conversion type ... */
7042         if (*fmt == '[') {
7043             int swapped = 1, beg = i, end, j;
7044             descr->type = '[';
7045             descr->arg = &buffer[i];
7046             ++fmt;
7047             if (*fmt == '^') buffer[i++] = *fmt++;
7048             if (*fmt == ']') buffer[i++] = *fmt++;
7049             while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7050             if (*fmt != ']') {
7051                 fmtObj->error = "unmatched [ in format string";
7052                 return JIM_ERR;
7053             } 
7054             end = i;
7055             buffer[i++] = 0;
7056             /* In case a range fence was given "backwards", swap it */
7057             while (swapped) {
7058                 swapped = 0;
7059                 for (j=beg+1; j < end-1; ++j) {
7060                     if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7061                         char tmp = buffer[j-1];
7062                         buffer[j-1] = buffer[j+1];
7063                         buffer[j+1] = tmp;
7064                         swapped = 1;
7065                     }
7066                 }
7067             }
7068         } else {
7069             /* Remember any valid modifier if given */
7070             if (strchr("hlL", *fmt) != 0)
7071                 descr->modifier = tolower((int)*fmt++);
7072             
7073             descr->type = *fmt;
7074             if (strchr("efgcsndoxui", *fmt) == 0) {
7075                 fmtObj->error = "bad scan conversion character";
7076                 return JIM_ERR;
7077             } else if (*fmt == 'c' && descr->width != 0) {
7078                 fmtObj->error = "field width may not be specified in %c "
7079                     "conversion";
7080                 return JIM_ERR;
7081             } else if (*fmt == 'u' && descr->modifier == 'l') {
7082                 fmtObj->error = "unsigned wide not supported";
7083                 return JIM_ERR;
7084             }
7085         }
7086         curr++;
7087     }
7088 done:
7089     if (fmtObj->convCount == 0) {
7090         fmtObj->error = "no any conversion specifier given";
7091         return JIM_ERR;
7092     }
7093     return JIM_OK;
7094 }
7095
7096 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7097
7098 #define FormatGetCnvCount(_fo_) \
7099     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7100 #define FormatGetMaxPos(_fo_) \
7101     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7102 #define FormatGetError(_fo_) \
7103     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7104
7105 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7106  * charsets ([a-z123]) within scanning. Later on perhaps a base for a 
7107  * bitvector implementation in Jim? */ 
7108
7109 static int JimTestBit(const char *bitvec, char ch)
7110 {
7111     div_t pos = div(ch-1, 8);
7112     return bitvec[pos.quot] & (1 << pos.rem);
7113 }
7114
7115 static void JimSetBit(char *bitvec, char ch)
7116 {
7117     div_t pos = div(ch-1, 8);
7118     bitvec[pos.quot] |= (1 << pos.rem);
7119 }
7120
7121 #if 0 /* currently not used */
7122 static void JimClearBit(char *bitvec, char ch)
7123 {
7124     div_t pos = div(ch-1, 8);
7125     bitvec[pos.quot] &= ~(1 << pos.rem);
7126 }
7127 #endif
7128
7129 /* JimScanAString is used to scan an unspecified string that ends with
7130  * next WS, or a string that is specified via a charset. The charset
7131  * is currently implemented in a way to only allow for usage with
7132  * ASCII. Whenever we will switch to UNICODE, another idea has to
7133  * be born :-/
7134  *
7135  * FIXME: Works only with ASCII */
7136
7137 static Jim_Obj *
7138 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7139 {
7140     size_t i;
7141     Jim_Obj *result;
7142     char charset[256/8+1];  /* A Charset may contain max 256 chars */
7143     char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7144
7145     /* First init charset to nothing or all, depending if a specified
7146      * or an unspecified string has to be parsed */
7147     memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7148     if (sdescr) {
7149         /* There was a set description given, that means we are parsing
7150          * a specified string. So we have to build a corresponding 
7151          * charset reflecting the description */
7152         int notFlag = 0;
7153         /* Should the set be negated at the end? */
7154         if (*sdescr == '^') {
7155             notFlag = 1;
7156             ++sdescr;
7157         }
7158         /* Here '-' is meant literally and not to define a range */
7159         if (*sdescr == '-') {
7160             JimSetBit(charset, '-');
7161             ++sdescr;
7162         }
7163         while (*sdescr) {
7164             if (sdescr[1] == '-' && sdescr[2] != 0) {
7165                 /* Handle range definitions */
7166                 int i;
7167                 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7168                     JimSetBit(charset, (char)i);
7169                 sdescr += 3;
7170             } else {
7171                 /* Handle verbatim character definitions */
7172                 JimSetBit(charset, *sdescr++);
7173             }
7174         }
7175         /* Negate the charset if there was a NOT given */
7176         for (i=0; notFlag && i < sizeof(charset); ++i)
7177             charset[i] = ~charset[i];
7178     } 
7179     /* And after all the mess above, the real work begin ... */
7180     while (str && *str) {
7181         if (!sdescr && isspace((int)*str))
7182             break; /* EOS via WS if unspecified */
7183         if (JimTestBit(charset, *str)) *buffer++ = *str++;
7184         else break;             /* EOS via mismatch if specified scanning */
7185     }
7186     *buffer = 0;                /* Close the string properly ... */
7187     result = Jim_NewStringObj(interp, anchor, -1);
7188     Jim_Free(anchor);           /* ... and free it afer usage */
7189     return result;
7190 }
7191
7192 /* ScanOneEntry will scan one entry out of the string passed as argument.
7193  * It use the sscanf() function for this task. After extracting and
7194  * converting of the value, the count of scanned characters will be
7195  * returned of -1 in case of no conversion tool place and string was
7196  * already scanned thru */
7197
7198 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7199         ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7200 {
7201 #   define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7202         ? sizeof(jim_wide)                             \
7203         : sizeof(double))
7204     char buffer[MAX_SIZE];
7205     char *value = buffer;
7206     const char *tok;
7207     const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7208     size_t sLen = strlen(&str[pos]), scanned = 0;
7209     size_t anchor = pos;
7210     int i;
7211
7212     /* First pessimiticly assume, we will not scan anything :-) */
7213     *valObjPtr = 0;
7214     if (descr->prefix) {
7215         /* There was a prefix given before the conversion, skip it and adjust
7216          * the string-to-be-parsed accordingly */
7217         for (i=0; str[pos] && descr->prefix[i]; ++i) {
7218             /* If prefix require, skip WS */
7219             if (isspace((int)descr->prefix[i]))
7220                 while (str[pos] && isspace((int)str[pos])) ++pos;
7221             else if (descr->prefix[i] != str[pos]) 
7222                 break;  /* Prefix do not match here, leave the loop */
7223             else
7224                 ++pos;  /* Prefix matched so far, next round */
7225         }
7226         if (str[pos] == 0)
7227             return -1;  /* All of str consumed: EOF condition */
7228         else if (descr->prefix[i] != 0)
7229             return 0;   /* Not whole prefix consumed, no conversion possible */
7230     }
7231     /* For all but following conversion, skip leading WS */
7232     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7233         while (isspace((int)str[pos])) ++pos;
7234     /* Determine how much skipped/scanned so far */
7235     scanned = pos - anchor;
7236     if (descr->type == 'n') {
7237         /* Return pseudo conversion means: how much scanned so far? */
7238         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7239     } else if (str[pos] == 0) {
7240         /* Cannot scan anything, as str is totally consumed */
7241         return -1;
7242     } else {
7243         /* Processing of conversions follows ... */
7244         if (descr->width > 0) {
7245             /* Do not try to scan as fas as possible but only the given width.
7246              * To ensure this, we copy the part that should be scanned. */
7247             size_t tLen = descr->width > sLen ? sLen : descr->width;
7248             tok = Jim_StrDupLen(&str[pos], tLen);
7249         } else {
7250             /* As no width was given, simply refer to the original string */
7251             tok = &str[pos];
7252         }
7253         switch (descr->type) {
7254             case 'c':
7255                 *valObjPtr = Jim_NewIntObj(interp, *tok);
7256                 scanned += 1;
7257                 break;
7258             case 'd': case 'o': case 'x': case 'u': case 'i': {
7259                 char *endp;  /* Position where the number finished */
7260                 int base = descr->type == 'o' ? 8
7261                     : descr->type == 'x' ? 16
7262                     : descr->type == 'i' ? 0
7263                     : 10;
7264                     
7265                 do {
7266                     /* Try to scan a number with the given base */
7267                     if (descr->modifier == 'l')
7268 #ifdef HAVE_LONG_LONG
7269                       *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7270 #else
7271                       *(jim_wide*)value = strtol(tok, &endp, base);
7272 #endif
7273                     else
7274                       if (descr->type == 'u')
7275                         *(long*)value = strtoul(tok, &endp, base);
7276                       else
7277                         *(long*)value = strtol(tok, &endp, base);
7278                     /* If scanning failed, and base was undetermined, simply
7279                      * put it to 10 and try once more. This should catch the
7280                      * case where %i begin to parse a number prefix (e.g. 
7281                      * '0x' but no further digits follows. This will be
7282                      * handled as a ZERO followed by a char 'x' by Tcl */
7283                     if (endp == tok && base == 0) base = 10;
7284                     else break;
7285                 } while (1);
7286                 if (endp != tok) {
7287                     /* There was some number sucessfully scanned! */
7288                     if (descr->modifier == 'l')
7289                         *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7290                     else
7291                         *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7292                     /* Adjust the number-of-chars scanned so far */
7293                     scanned += endp - tok;
7294                 } else {
7295                     /* Nothing was scanned. We have to determine if this
7296                      * happened due to e.g. prefix mismatch or input str
7297                      * exhausted */
7298                     scanned = *tok ? 0 : -1;
7299                 }
7300                 break;
7301             }
7302             case 's': case '[': {
7303                 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7304                 scanned += Jim_Length(*valObjPtr);
7305                 break;
7306             }
7307             case 'e': case 'f': case 'g': {
7308                 char *endp;
7309
7310                 *(double*)value = strtod(tok, &endp);
7311                 if (endp != tok) {
7312                     /* There was some number sucessfully scanned! */
7313                     *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7314                     /* Adjust the number-of-chars scanned so far */
7315                     scanned += endp - tok;
7316                 } else {
7317                     /* Nothing was scanned. We have to determine if this
7318                      * happened due to e.g. prefix mismatch or input str
7319                      * exhausted */
7320                     scanned = *tok ? 0 : -1;
7321                 }
7322                 break;
7323             }
7324         }
7325         /* If a substring was allocated (due to pre-defined width) do not
7326          * forget to free it */
7327         if (tok != &str[pos])
7328             Jim_Free((char*)tok);
7329     }
7330     return scanned;
7331 }
7332
7333 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7334  * string and returns all converted (and not ignored) values in a list back
7335  * to the caller. If an error occured, a NULL pointer will be returned */
7336
7337 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7338         Jim_Obj *fmtObjPtr, int flags)
7339 {
7340     size_t i, pos;
7341     int scanned = 1;
7342     const char *str = Jim_GetString(strObjPtr, 0);
7343     Jim_Obj *resultList = 0;
7344     Jim_Obj **resultVec;
7345     int resultc;
7346     Jim_Obj *emptyStr = 0;
7347     ScanFmtStringObj *fmtObj;
7348
7349     /* If format specification is not an object, convert it! */
7350     if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7351         SetScanFmtFromAny(interp, fmtObjPtr);
7352     fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7353     /* Check if format specification was valid */
7354     if (fmtObj->error != 0) {
7355         if (flags & JIM_ERRMSG)
7356             Jim_SetResultString(interp, fmtObj->error, -1);
7357         return 0;
7358     }
7359     /* Allocate a new "shared" empty string for all unassigned conversions */
7360     emptyStr = Jim_NewEmptyStringObj(interp);
7361     Jim_IncrRefCount(emptyStr);
7362     /* Create a list and fill it with empty strings up to max specified XPG3 */
7363     resultList = Jim_NewListObj(interp, 0, 0);
7364     if (fmtObj->maxPos > 0) {
7365         for (i=0; i < fmtObj->maxPos; ++i)
7366             Jim_ListAppendElement(interp, resultList, emptyStr);
7367         JimListGetElements(interp, resultList, &resultc, &resultVec);
7368     }
7369     /* Now handle every partial format description */
7370     for (i=0, pos=0; i < fmtObj->count; ++i) {
7371         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7372         Jim_Obj *value = 0;
7373         /* Only last type may be "literal" w/o conversion - skip it! */
7374         if (descr->type == 0) continue;
7375         /* As long as any conversion could be done, we will proceed */
7376         if (scanned > 0)
7377             scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7378         /* In case our first try results in EOF, we will leave */
7379         if (scanned == -1 && i == 0)
7380             goto eof;
7381         /* Advance next pos-to-be-scanned for the amount scanned already */
7382         pos += scanned;
7383         /* value == 0 means no conversion took place so take empty string */
7384         if (value == 0)
7385             value = Jim_NewEmptyStringObj(interp);
7386         /* If value is a non-assignable one, skip it */
7387         if (descr->pos == -1) {
7388             Jim_FreeNewObj(interp, value);
7389         } else if (descr->pos == 0)
7390             /* Otherwise append it to the result list if no XPG3 was given */
7391             Jim_ListAppendElement(interp, resultList, value);
7392         else if (resultVec[descr->pos-1] == emptyStr) {
7393             /* But due to given XPG3, put the value into the corr. slot */
7394             Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7395             Jim_IncrRefCount(value);
7396             resultVec[descr->pos-1] = value;
7397         } else {
7398             /* Otherwise, the slot was already used - free obj and ERROR */
7399             Jim_FreeNewObj(interp, value);
7400             goto err;
7401         }
7402     }
7403     Jim_DecrRefCount(interp, emptyStr);
7404     return resultList;
7405 eof:
7406     Jim_DecrRefCount(interp, emptyStr);
7407     Jim_FreeNewObj(interp, resultList);
7408     return (Jim_Obj*)EOF;
7409 err:
7410     Jim_DecrRefCount(interp, emptyStr);
7411     Jim_FreeNewObj(interp, resultList);
7412     return 0;
7413 }
7414
7415 /* -----------------------------------------------------------------------------
7416  * Pseudo Random Number Generation
7417  * ---------------------------------------------------------------------------*/
7418 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7419         int seedLen);
7420
7421 /* Initialize the sbox with the numbers from 0 to 255 */
7422 static void JimPrngInit(Jim_Interp *interp)
7423 {
7424     int i;
7425     unsigned int seed[256];
7426
7427     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7428     for (i = 0; i < 256; i++)
7429         seed[i] = (rand() ^ time(NULL) ^ clock());
7430     JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7431 }
7432
7433 /* Generates N bytes of random data */
7434 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7435 {
7436     Jim_PrngState *prng;
7437     unsigned char *destByte = (unsigned char*) dest;
7438     unsigned int si, sj, x;
7439
7440     /* initialization, only needed the first time */
7441     if (interp->prngState == NULL)
7442         JimPrngInit(interp);
7443     prng = interp->prngState;
7444     /* generates 'len' bytes of pseudo-random numbers */
7445     for (x = 0; x < len; x++) {
7446         prng->i = (prng->i+1) & 0xff;
7447         si = prng->sbox[prng->i];
7448         prng->j = (prng->j + si) & 0xff;
7449         sj = prng->sbox[prng->j];
7450         prng->sbox[prng->i] = sj;
7451         prng->sbox[prng->j] = si;
7452         *destByte++ = prng->sbox[(si+sj)&0xff];
7453     }
7454 }
7455
7456 /* Re-seed the generator with user-provided bytes */
7457 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7458         int seedLen)
7459 {
7460     int i;
7461     unsigned char buf[256];
7462     Jim_PrngState *prng;
7463
7464     /* initialization, only needed the first time */
7465     if (interp->prngState == NULL)
7466         JimPrngInit(interp);
7467     prng = interp->prngState;
7468
7469     /* Set the sbox[i] with i */
7470     for (i = 0; i < 256; i++)
7471         prng->sbox[i] = i;
7472     /* Now use the seed to perform a random permutation of the sbox */
7473     for (i = 0; i < seedLen; i++) {
7474         unsigned char t;
7475
7476         t = prng->sbox[i&0xFF];
7477         prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7478         prng->sbox[seed[i]] = t;
7479     }
7480     prng->i = prng->j = 0;
7481     /* discard the first 256 bytes of stream. */
7482     JimRandomBytes(interp, buf, 256);
7483 }
7484
7485 /* -----------------------------------------------------------------------------
7486  * Dynamic libraries support (WIN32 not supported)
7487  * ---------------------------------------------------------------------------*/
7488
7489 #ifdef JIM_DYNLIB
7490 #ifdef WIN32
7491 #define RTLD_LAZY 0
7492 void * dlopen(const char *path, int mode) 
7493 {
7494     JIM_NOTUSED(mode);
7495
7496     return (void *)LoadLibraryA(path);
7497 }
7498 int dlclose(void *handle)
7499 {
7500     FreeLibrary((HANDLE)handle);
7501     return 0;
7502 }
7503 void *dlsym(void *handle, const char *symbol)
7504 {
7505     return GetProcAddress((HMODULE)handle, symbol);
7506 }
7507 static char win32_dlerror_string[121];
7508 const char *dlerror()
7509 {
7510     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7511                    LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7512     return win32_dlerror_string;
7513 }
7514 #endif /* WIN32 */
7515
7516 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7517 {
7518     Jim_Obj *libPathObjPtr;
7519     int prefixc, i;
7520     void *handle;
7521     int (*onload)(Jim_Interp *interp);
7522
7523     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7524     if (libPathObjPtr == NULL) {
7525         prefixc = 0;
7526         libPathObjPtr = NULL;
7527     } else {
7528         Jim_IncrRefCount(libPathObjPtr);
7529         Jim_ListLength(interp, libPathObjPtr, &prefixc);
7530     }
7531
7532     for (i = -1; i < prefixc; i++) {
7533         if (i < 0) {
7534             handle = dlopen(pathName, RTLD_LAZY);
7535         } else {
7536             FILE *fp;
7537             char buf[JIM_PATH_LEN];
7538             const char *prefix;
7539             int prefixlen;
7540             Jim_Obj *prefixObjPtr;
7541             
7542             buf[0] = '\0';
7543             if (Jim_ListIndex(interp, libPathObjPtr, i,
7544                     &prefixObjPtr, JIM_NONE) != JIM_OK)
7545                 continue;
7546             prefix = Jim_GetString(prefixObjPtr, NULL);
7547             prefixlen = strlen(prefix);
7548             if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7549                 continue;
7550             if (prefixlen && prefix[prefixlen-1] == '/')
7551                 sprintf(buf, "%s%s", prefix, pathName);
7552             else
7553                 sprintf(buf, "%s/%s", prefix, pathName);
7554             printf("opening '%s'\n", buf);
7555             fp = fopen(buf, "r");
7556             if (fp == NULL)
7557                 continue;
7558             fclose(fp);
7559             handle = dlopen(buf, RTLD_LAZY);
7560             printf("got handle %p\n", handle);
7561         }
7562         if (handle == NULL) {
7563             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7564             Jim_AppendStrings(interp, Jim_GetResult(interp),
7565                 "error loading extension \"", pathName,
7566                 "\": ", dlerror(), NULL);
7567             if (i < 0)
7568                 continue;
7569             goto err;
7570         }
7571         if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7572             Jim_SetResultString(interp,
7573                     "No Jim_OnLoad symbol found on extension", -1);
7574             goto err;
7575         }
7576         if (onload(interp) == JIM_ERR) {
7577             dlclose(handle);
7578             goto err;
7579         }
7580         Jim_SetEmptyResult(interp);
7581         if (libPathObjPtr != NULL)
7582             Jim_DecrRefCount(interp, libPathObjPtr);
7583         return JIM_OK;
7584     }
7585 err:
7586     if (libPathObjPtr != NULL)
7587         Jim_DecrRefCount(interp, libPathObjPtr);
7588     return JIM_ERR;
7589 }
7590 #else /* JIM_DYNLIB */
7591 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7592 {
7593     JIM_NOTUSED(interp);
7594     JIM_NOTUSED(pathName);
7595
7596     Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7597     return JIM_ERR;
7598 }
7599 #endif/* JIM_DYNLIB */
7600
7601 /* -----------------------------------------------------------------------------
7602  * Packages handling
7603  * ---------------------------------------------------------------------------*/
7604
7605 #define JIM_PKG_ANY_VERSION -1
7606
7607 /* Convert a string of the type "1.2" into an integer.
7608  * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted 
7609  * to the integer with value 102 */
7610 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7611         int *intPtr, int flags)
7612 {
7613     char *copy;
7614     jim_wide major, minor;
7615     char *majorStr, *minorStr, *p;
7616
7617     if (v[0] == '\0') {
7618         *intPtr = JIM_PKG_ANY_VERSION;
7619         return JIM_OK;
7620     }
7621
7622     copy = Jim_StrDup(v);
7623     p = strchr(copy, '.');
7624     if (p == NULL) goto badfmt;
7625     *p = '\0';
7626     majorStr = copy;
7627     minorStr = p+1;
7628
7629     if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7630         Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7631         goto badfmt;
7632     *intPtr = (int)(major*100+minor);
7633     Jim_Free(copy);
7634     return JIM_OK;
7635
7636 badfmt:
7637     Jim_Free(copy);
7638     if (flags & JIM_ERRMSG) {
7639         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7640         Jim_AppendStrings(interp, Jim_GetResult(interp),
7641                 "invalid package version '", v, "'", NULL);
7642     }
7643     return JIM_ERR;
7644 }
7645
7646 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7647 static int JimPackageMatchVersion(int needed, int actual, int flags)
7648 {
7649     if (needed == JIM_PKG_ANY_VERSION) return 1;
7650     if (flags & JIM_MATCHVER_EXACT) {
7651         return needed == actual;
7652     } else {
7653         return needed/100 == actual/100 && (needed <= actual);
7654     }
7655 }
7656
7657 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7658         int flags)
7659 {
7660     int intVersion;
7661     /* Check if the version format is ok */
7662     if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7663         return JIM_ERR;
7664     /* If the package was already provided returns an error. */
7665     if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7666         if (flags & JIM_ERRMSG) {
7667             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7668             Jim_AppendStrings(interp, Jim_GetResult(interp),
7669                     "package '", name, "' was already provided", NULL);
7670         }
7671         return JIM_ERR;
7672     }
7673     Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7674     return JIM_OK;
7675 }
7676
7677 #ifndef JIM_ANSIC
7678
7679 #ifndef WIN32
7680 # include <sys/types.h>
7681 # include <dirent.h>
7682 #else
7683 # include <io.h>
7684 /* Posix dirent.h compatiblity layer for WIN32.
7685  * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7686  * Copyright Salvatore Sanfilippo ,2005.
7687  *
7688  * Permission to use, copy, modify, and distribute this software and its
7689  * documentation for any purpose is hereby granted without fee, provided
7690  * that this copyright and permissions notice appear in all copies and
7691  * derivatives.
7692  *
7693  * This software is supplied "as is" without express or implied warranty.
7694  * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7695  */
7696
7697 struct dirent {
7698     char *d_name;
7699 };
7700
7701 typedef struct DIR {
7702     long                handle; /* -1 for failed rewind */
7703     struct _finddata_t  info;
7704     struct dirent       result; /* d_name null iff first time */
7705     char                *name;  /* null-terminated char string */
7706 } DIR;
7707
7708 DIR *opendir(const char *name)
7709 {
7710     DIR *dir = 0;
7711
7712     if(name && name[0]) {
7713         size_t base_length = strlen(name);
7714         const char *all = /* search pattern must end with suitable wildcard */
7715             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7716
7717         if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7718            (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7719         {
7720             strcat(strcpy(dir->name, name), all);
7721
7722             if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7723                 dir->result.d_name = 0;
7724             else { /* rollback */
7725                 Jim_Free(dir->name);
7726                 Jim_Free(dir);
7727                 dir = 0;
7728             }
7729         } else { /* rollback */
7730             Jim_Free(dir);
7731             dir   = 0;
7732             errno = ENOMEM;
7733         }
7734     } else {
7735         errno = EINVAL;
7736     }
7737     return dir;
7738 }
7739
7740 int closedir(DIR *dir)
7741 {
7742     int result = -1;
7743
7744     if(dir) {
7745         if(dir->handle != -1)
7746             result = _findclose(dir->handle);
7747         Jim_Free(dir->name);
7748         Jim_Free(dir);
7749     }
7750     if(result == -1) /* map all errors to EBADF */
7751         errno = EBADF;
7752     return result;
7753 }
7754
7755 struct dirent *readdir(DIR *dir)
7756 {
7757     struct dirent *result = 0;
7758
7759     if(dir && dir->handle != -1) {
7760         if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7761             result         = &dir->result;
7762             result->d_name = dir->info.name;
7763         }
7764     } else {
7765         errno = EBADF;
7766     }
7767     return result;
7768 }
7769
7770 #endif /* WIN32 */
7771
7772 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7773         int prefixc, const char *pkgName, int pkgVer, int flags)
7774 {
7775     int bestVer = -1, i;
7776     int pkgNameLen = strlen(pkgName);
7777     char *bestPackage = NULL;
7778     struct dirent *de;
7779
7780     for (i = 0; i < prefixc; i++) {
7781         DIR *dir;
7782         char buf[JIM_PATH_LEN];
7783         int prefixLen;
7784
7785         if (prefixes[i] == NULL) continue;
7786         strncpy(buf, prefixes[i], JIM_PATH_LEN);
7787         buf[JIM_PATH_LEN-1] = '\0';
7788         prefixLen = strlen(buf);
7789         if (prefixLen && buf[prefixLen-1] == '/')
7790             buf[prefixLen-1] = '\0';
7791
7792         if ((dir = opendir(buf)) == NULL) continue;
7793         while ((de = readdir(dir)) != NULL) {
7794             char *fileName = de->d_name;
7795             int fileNameLen = strlen(fileName);
7796
7797             if (strncmp(fileName, "jim-", 4) == 0 &&
7798                 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
7799                 *(fileName+4+pkgNameLen) == '-' &&
7800                 fileNameLen > 4 && /* note that this is not really useful */
7801                 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
7802                  strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
7803                  strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
7804             {
7805                 char ver[6]; /* xx.yy<nulterm> */
7806                 char *p = strrchr(fileName, '.');
7807                 int verLen, fileVer;
7808
7809                 verLen = p - (fileName+4+pkgNameLen+1);
7810                 if (verLen < 3 || verLen > 5) continue;
7811                 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
7812                 ver[verLen] = '\0';
7813                 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
7814                         != JIM_OK) continue;
7815                 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
7816                     (bestVer == -1 || bestVer < fileVer))
7817                 {
7818                     bestVer = fileVer;
7819                     Jim_Free(bestPackage);
7820                     bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
7821                     sprintf(bestPackage, "%s/%s", buf, fileName);
7822                 }
7823             }
7824         }
7825         closedir(dir);
7826     }
7827     return bestPackage;
7828 }
7829
7830 #else /* JIM_ANSIC */
7831
7832 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7833         int prefixc, const char *pkgName, int pkgVer, int flags)
7834 {
7835     JIM_NOTUSED(interp);
7836     JIM_NOTUSED(prefixes);
7837     JIM_NOTUSED(prefixc);
7838     JIM_NOTUSED(pkgName);
7839     JIM_NOTUSED(pkgVer);
7840     JIM_NOTUSED(flags);
7841     return NULL;
7842 }
7843
7844 #endif /* JIM_ANSIC */
7845
7846 /* Search for a suitable package under every dir specified by jim_libpath
7847  * and load it if possible. If a suitable package was loaded with success
7848  * JIM_OK is returned, otherwise JIM_ERR is returned. */
7849 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
7850         int flags)
7851 {
7852     Jim_Obj *libPathObjPtr;
7853     char **prefixes, *best;
7854     int prefixc, i, retCode = JIM_OK;
7855
7856     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7857     if (libPathObjPtr == NULL) {
7858         prefixc = 0;
7859         libPathObjPtr = NULL;
7860     } else {
7861         Jim_IncrRefCount(libPathObjPtr);
7862         Jim_ListLength(interp, libPathObjPtr, &prefixc);
7863     }
7864
7865     prefixes = Jim_Alloc(sizeof(char*)*prefixc);
7866     for (i = 0; i < prefixc; i++) {
7867             Jim_Obj *prefixObjPtr;
7868             if (Jim_ListIndex(interp, libPathObjPtr, i,
7869                     &prefixObjPtr, JIM_NONE) != JIM_OK)
7870             {
7871                 prefixes[i] = NULL;
7872                 continue;
7873             }
7874             prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
7875     }
7876     /* Scan every directory to find the "best" package. */
7877     best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
7878     if (best != NULL) {
7879         char *p = strrchr(best, '.');
7880         /* Try to load/source it */
7881         if (p && strcmp(p, ".tcl") == 0) {
7882             retCode = Jim_EvalFile(interp, best);
7883         } else {
7884             retCode = Jim_LoadLibrary(interp, best);
7885         }
7886     } else {
7887         retCode = JIM_ERR;
7888     }
7889     Jim_Free(best);
7890     for (i = 0; i < prefixc; i++)
7891         Jim_Free(prefixes[i]);
7892     Jim_Free(prefixes);
7893     if (libPathObjPtr)
7894         Jim_DecrRefCount(interp, libPathObjPtr);
7895     return retCode;
7896 }
7897
7898 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
7899         const char *ver, int flags)
7900 {
7901     Jim_HashEntry *he;
7902     int requiredVer;
7903
7904     if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
7905         return NULL;
7906     he = Jim_FindHashEntry(&interp->packages, name);
7907     if (he == NULL) {
7908         /* Try to load the package. */
7909         if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
7910             he = Jim_FindHashEntry(&interp->packages, name);
7911             if (he == NULL) {
7912                 return "?";
7913             }
7914             return he->val;
7915         }
7916         /* No way... return an error. */
7917         if (flags & JIM_ERRMSG) {
7918             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7919             Jim_AppendStrings(interp, Jim_GetResult(interp),
7920                     "Can't find package '", name, "'", NULL);
7921         }
7922         return NULL;
7923     } else {
7924         int actualVer;
7925         if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
7926                 != JIM_OK)
7927         {
7928             return NULL;
7929         }
7930         /* Check if version matches. */
7931         if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
7932             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7933             Jim_AppendStrings(interp, Jim_GetResult(interp),
7934                     "Package '", name, "' already loaded, but with version ",
7935                     he->val, NULL);
7936             return NULL;
7937         }
7938         return he->val;
7939     }
7940 }
7941
7942 /* -----------------------------------------------------------------------------
7943  * Eval
7944  * ---------------------------------------------------------------------------*/
7945 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
7946 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
7947
7948 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
7949         Jim_Obj *const *argv);
7950
7951 /* Handle calls to the [unknown] command */
7952 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
7953 {
7954     Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
7955     int retCode;
7956
7957     /* If the [unknown] command does not exists returns
7958      * just now */
7959     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
7960         return JIM_ERR;
7961
7962     /* The object interp->unknown just contains
7963      * the "unknown" string, it is used in order to
7964      * avoid to lookup the unknown command every time
7965      * but instread to cache the result. */
7966     if (argc+1 <= JIM_EVAL_SARGV_LEN)
7967         v = sv;
7968     else
7969         v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
7970     /* Make a copy of the arguments vector, but shifted on
7971      * the right of one position. The command name of the
7972      * command will be instead the first argument of the
7973      * [unknonw] call. */
7974     memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
7975     v[0] = interp->unknown;
7976     /* Call it */
7977     retCode = Jim_EvalObjVector(interp, argc+1, v);
7978     /* Clean up */
7979     if (v != sv)
7980         Jim_Free(v);
7981     return retCode;
7982 }
7983
7984 /* Eval the object vector 'objv' composed of 'objc' elements.
7985  * Every element is used as single argument.
7986  * Jim_EvalObj() will call this function every time its object
7987  * argument is of "list" type, with no string representation.
7988  *
7989  * This is possible because the string representation of a
7990  * list object generated by the UpdateStringOfList is made
7991  * in a way that ensures that every list element is a different
7992  * command argument. */
7993 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
7994 {
7995     int i, retcode;
7996     Jim_Cmd *cmdPtr;
7997
7998     /* Incr refcount of arguments. */
7999     for (i = 0; i < objc; i++)
8000         Jim_IncrRefCount(objv[i]);
8001     /* Command lookup */
8002     cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8003     if (cmdPtr == NULL) {
8004         retcode = JimUnknown(interp, objc, objv);
8005     } else {
8006         /* Call it -- Make sure result is an empty object. */
8007         Jim_SetEmptyResult(interp);
8008         if (cmdPtr->cmdProc) {
8009             interp->cmdPrivData = cmdPtr->privData;
8010             retcode = cmdPtr->cmdProc(interp, objc, objv);
8011         } else {
8012             retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8013             if (retcode == JIM_ERR) {
8014                 JimAppendStackTrace(interp,
8015                     Jim_GetString(objv[0], NULL), "?", 1);
8016             }
8017         }
8018     }
8019     /* Decr refcount of arguments and return the retcode */
8020     for (i = 0; i < objc; i++)
8021         Jim_DecrRefCount(interp, objv[i]);
8022     return retcode;
8023 }
8024
8025 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8026  * via *objPtrPtr. This function is only called by Jim_EvalObj().
8027  * The returned object has refcount = 0. */
8028 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8029         int tokens, Jim_Obj **objPtrPtr)
8030 {
8031     int totlen = 0, i, retcode;
8032     Jim_Obj **intv;
8033     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8034     Jim_Obj *objPtr;
8035     char *s;
8036
8037     if (tokens <= JIM_EVAL_SINTV_LEN)
8038         intv = sintv;
8039     else
8040         intv = Jim_Alloc(sizeof(Jim_Obj*)*
8041                 tokens);
8042     /* Compute every token forming the argument
8043      * in the intv objects vector. */
8044     for (i = 0; i < tokens; i++) {
8045         switch(token[i].type) {
8046         case JIM_TT_ESC:
8047         case JIM_TT_STR:
8048             intv[i] = token[i].objPtr;
8049             break;
8050         case JIM_TT_VAR:
8051             intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8052             if (!intv[i]) {
8053                 retcode = JIM_ERR;
8054                 goto err;
8055             }
8056             break;
8057         case JIM_TT_DICTSUGAR:
8058             intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8059             if (!intv[i]) {
8060                 retcode = JIM_ERR;
8061                 goto err;
8062             }
8063             break;
8064         case JIM_TT_CMD:
8065             retcode = Jim_EvalObj(interp, token[i].objPtr);
8066             if (retcode != JIM_OK)
8067                 goto err;
8068             intv[i] = Jim_GetResult(interp);
8069             break;
8070         default:
8071             Jim_Panic(interp,
8072               "default token type reached "
8073               "in Jim_InterpolateTokens().");
8074             break;
8075         }
8076         Jim_IncrRefCount(intv[i]);
8077         /* Make sure there is a valid
8078          * string rep, and add the string
8079          * length to the total legnth. */
8080         Jim_GetString(intv[i], NULL);
8081         totlen += intv[i]->length;
8082     }
8083     /* Concatenate every token in an unique
8084      * object. */
8085     objPtr = Jim_NewStringObjNoAlloc(interp,
8086             NULL, 0);
8087     s = objPtr->bytes = Jim_Alloc(totlen+1);
8088     objPtr->length = totlen;
8089     for (i = 0; i < tokens; i++) {
8090         memcpy(s, intv[i]->bytes, intv[i]->length);
8091         s += intv[i]->length;
8092         Jim_DecrRefCount(interp, intv[i]);
8093     }
8094     objPtr->bytes[totlen] = '\0';
8095     /* Free the intv vector if not static. */
8096     if (tokens > JIM_EVAL_SINTV_LEN)
8097         Jim_Free(intv);
8098     *objPtrPtr = objPtr;
8099     return JIM_OK;
8100 err:
8101     i--;
8102     for (; i >= 0; i--)
8103         Jim_DecrRefCount(interp, intv[i]);
8104     if (tokens > JIM_EVAL_SINTV_LEN)
8105         Jim_Free(intv);
8106     return retcode;
8107 }
8108
8109 /* Helper of Jim_EvalObj() to perform argument expansion.
8110  * Basically this function append an argument to 'argv'
8111  * (and increments argc by reference accordingly), performing
8112  * expansion of the list object if 'expand' is non-zero, or
8113  * just adding objPtr to argv if 'expand' is zero. */
8114 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8115         int *argcPtr, int expand, Jim_Obj *objPtr)
8116 {
8117     if (!expand) {
8118         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8119         /* refcount of objPtr not incremented because
8120          * we are actually transfering a reference from
8121          * the old 'argv' to the expanded one. */
8122         (*argv)[*argcPtr] = objPtr;
8123         (*argcPtr)++;
8124     } else {
8125         int len, i;
8126
8127         Jim_ListLength(interp, objPtr, &len);
8128         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8129         for (i = 0; i < len; i++) {
8130             (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8131             Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8132             (*argcPtr)++;
8133         }
8134         /* The original object reference is no longer needed,
8135          * after the expansion it is no longer present on
8136          * the argument vector, but the single elements are
8137          * in its place. */
8138         Jim_DecrRefCount(interp, objPtr);
8139     }
8140 }
8141
8142 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8143 {
8144     int i, j = 0, len;
8145     ScriptObj *script;
8146     ScriptToken *token;
8147     int *cs; /* command structure array */
8148     int retcode = JIM_OK;
8149     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8150
8151     interp->errorFlag = 0;
8152
8153     /* If the object is of type "list" and there is no
8154      * string representation for this object, we can call
8155      * a specialized version of Jim_EvalObj() */
8156     if (scriptObjPtr->typePtr == &listObjType &&
8157         scriptObjPtr->internalRep.listValue.len &&
8158         scriptObjPtr->bytes == NULL) {
8159         Jim_IncrRefCount(scriptObjPtr);
8160         retcode = Jim_EvalObjVector(interp,
8161                 scriptObjPtr->internalRep.listValue.len,
8162                 scriptObjPtr->internalRep.listValue.ele);
8163         Jim_DecrRefCount(interp, scriptObjPtr);
8164         return retcode;
8165     }
8166
8167     Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8168     script = Jim_GetScript(interp, scriptObjPtr);
8169     /* Now we have to make sure the internal repr will not be
8170      * freed on shimmering.
8171      *
8172      * Think for example to this:
8173      *
8174      * set x {llength $x; ... some more code ...}; eval $x
8175      *
8176      * In order to preserve the internal rep, we increment the
8177      * inUse field of the script internal rep structure. */
8178     script->inUse++;
8179
8180     token = script->token;
8181     len = script->len;
8182     cs = script->cmdStruct;
8183     i = 0; /* 'i' is the current token index. */
8184
8185     /* Reset the interpreter result. This is useful to
8186      * return the emtpy result in the case of empty program. */
8187     Jim_SetEmptyResult(interp);
8188
8189     /* Execute every command sequentially, returns on
8190      * error (i.e. if a command does not return JIM_OK) */
8191     while (i < len) {
8192         int expand = 0;
8193         int argc = *cs++; /* Get the number of arguments */
8194         Jim_Cmd *cmd;
8195
8196         /* Set the expand flag if needed. */
8197         if (argc == -1) {
8198             expand++;
8199             argc = *cs++;
8200         }
8201         /* Allocate the arguments vector */
8202         if (argc <= JIM_EVAL_SARGV_LEN)
8203             argv = sargv;
8204         else
8205             argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8206         /* Populate the arguments objects. */
8207         for (j = 0; j < argc; j++) {
8208             int tokens = *cs++;
8209
8210             /* tokens is negative if expansion is needed.
8211              * for this argument. */
8212             if (tokens < 0) {
8213                 tokens = (-tokens)-1;
8214                 i++;
8215             }
8216             if (tokens == 1) {
8217                 /* Fast path if the token does not
8218                  * need interpolation */
8219                 switch(token[i].type) {
8220                 case JIM_TT_ESC:
8221                 case JIM_TT_STR:
8222                     argv[j] = token[i].objPtr;
8223                     break;
8224                 case JIM_TT_VAR:
8225                     tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8226                             JIM_ERRMSG);
8227                     if (!tmpObjPtr) {
8228                         retcode = JIM_ERR;
8229                         goto err;
8230                     }
8231                     argv[j] = tmpObjPtr;
8232                     break;
8233                 case JIM_TT_DICTSUGAR:
8234                     tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8235                     if (!tmpObjPtr) {
8236                         retcode = JIM_ERR;
8237                         goto err;
8238                     }
8239                     argv[j] = tmpObjPtr;
8240                     break;
8241                 case JIM_TT_CMD:
8242                     retcode = Jim_EvalObj(interp, token[i].objPtr);
8243                     if (retcode != JIM_OK)
8244                         goto err;
8245                     argv[j] = Jim_GetResult(interp);
8246                     break;
8247                 default:
8248                     Jim_Panic(interp,
8249                       "default token type reached "
8250                       "in Jim_EvalObj().");
8251                     break;
8252                 }
8253                 Jim_IncrRefCount(argv[j]);
8254                 i += 2;
8255             } else {
8256                 /* For interpolation we call an helper
8257                  * function doing the work for us. */
8258                 if ((retcode = Jim_InterpolateTokens(interp,
8259                         token+i, tokens, &tmpObjPtr)) != JIM_OK)
8260                 {
8261                     goto err;
8262                 }
8263                 argv[j] = tmpObjPtr;
8264                 Jim_IncrRefCount(argv[j]);
8265                 i += tokens+1;
8266             }
8267         }
8268         /* Handle {expand} expansion */
8269         if (expand) {
8270             int *ecs = cs - argc;
8271             int eargc = 0;
8272             Jim_Obj **eargv = NULL;
8273
8274             for (j = 0; j < argc; j++) {
8275                 Jim_ExpandArgument( interp, &eargv, &eargc,
8276                         ecs[j] < 0, argv[j]);
8277             }
8278             if (argv != sargv)
8279                 Jim_Free(argv);
8280             argc = eargc;
8281             argv = eargv;
8282             j = argc;
8283             if (argc == 0) {
8284                 /* Nothing to do with zero args. */
8285                 Jim_Free(eargv);
8286                 continue;
8287             }
8288         }
8289         /* Lookup the command to call */
8290         cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8291         if (cmd != NULL) {
8292             /* Call it -- Make sure result is an empty object. */
8293             Jim_SetEmptyResult(interp);
8294             if (cmd->cmdProc) {
8295                 interp->cmdPrivData = cmd->privData;
8296                 retcode = cmd->cmdProc(interp, argc, argv);
8297             } else {
8298                 retcode = JimCallProcedure(interp, cmd, argc, argv);
8299                 if (retcode == JIM_ERR) {
8300                     JimAppendStackTrace(interp,
8301                         Jim_GetString(argv[0], NULL), script->fileName,
8302                         token[i-argc*2].linenr);
8303                 }
8304             }
8305         } else {
8306             /* Call [unknown] */
8307             retcode = JimUnknown(interp, argc, argv);
8308         }
8309         if (retcode != JIM_OK) {
8310             i -= argc*2; /* point to the command name. */
8311             goto err;
8312         }
8313         /* Decrement the arguments count */
8314         for (j = 0; j < argc; j++) {
8315             Jim_DecrRefCount(interp, argv[j]);
8316         }
8317
8318         if (argv != sargv) {
8319             Jim_Free(argv);
8320             argv = NULL;
8321         }
8322     }
8323     /* Note that we don't have to decrement inUse, because the
8324      * following code transfers our use of the reference again to
8325      * the script object. */
8326     j = 0; /* on normal termination, the argv array is already
8327           Jim_DecrRefCount-ed. */
8328 err:
8329     /* Handle errors. */
8330     if (retcode == JIM_ERR && !interp->errorFlag) {
8331         interp->errorFlag = 1;
8332         JimSetErrorFileName(interp, script->fileName);
8333         JimSetErrorLineNumber(interp, token[i].linenr);
8334         JimResetStackTrace(interp);
8335     }
8336     Jim_FreeIntRep(interp, scriptObjPtr);
8337     scriptObjPtr->typePtr = &scriptObjType;
8338     Jim_SetIntRepPtr(scriptObjPtr, script);
8339     Jim_DecrRefCount(interp, scriptObjPtr);
8340     for (i = 0; i < j; i++) {
8341         Jim_DecrRefCount(interp, argv[i]);
8342     }
8343     if (argv != sargv)
8344         Jim_Free(argv);
8345     return retcode;
8346 }
8347
8348 /* Call a procedure implemented in Tcl.
8349  * It's possible to speed-up a lot this function, currently
8350  * the callframes are not cached, but allocated and
8351  * destroied every time. What is expecially costly is
8352  * to create/destroy the local vars hash table every time.
8353  *
8354  * This can be fixed just implementing callframes caching
8355  * in JimCreateCallFrame() and JimFreeCallFrame(). */
8356 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8357         Jim_Obj *const *argv)
8358 {
8359     int i, retcode;
8360     Jim_CallFrame *callFramePtr;
8361
8362     /* Check arity */
8363     if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8364         argc > cmd->arityMax)) {
8365         Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8366         Jim_AppendStrings(interp, objPtr,
8367             "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8368             (cmd->arityMin > 1) ? " " : "",
8369             Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8370         Jim_SetResult(interp, objPtr);
8371         return JIM_ERR;
8372     }
8373     /* Check if there are too nested calls */
8374     if (interp->numLevels == interp->maxNestingDepth) {
8375         Jim_SetResultString(interp,
8376             "Too many nested calls. Infinite recursion?", -1);
8377         return JIM_ERR;
8378     }
8379     /* Create a new callframe */
8380     callFramePtr = JimCreateCallFrame(interp);
8381     callFramePtr->parentCallFrame = interp->framePtr;
8382     callFramePtr->argv = argv;
8383     callFramePtr->argc = argc;
8384     callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8385     callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8386     callFramePtr->staticVars = cmd->staticVars;
8387     Jim_IncrRefCount(cmd->argListObjPtr);
8388     Jim_IncrRefCount(cmd->bodyObjPtr);
8389     interp->framePtr = callFramePtr;
8390     interp->numLevels ++;
8391     /* Set arguments */
8392     for (i = 0; i < cmd->arityMin-1; i++) {
8393         Jim_Obj *objPtr;
8394
8395         Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8396         Jim_SetVariable(interp, objPtr, argv[i+1]);
8397     }
8398     if (cmd->arityMax == -1) {
8399         Jim_Obj *listObjPtr, *objPtr;
8400
8401         listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8402                 argc-cmd->arityMin);
8403         Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8404         Jim_SetVariable(interp, objPtr, listObjPtr);
8405     }
8406     /* Eval the body */
8407     retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8408
8409     /* Destroy the callframe */
8410     interp->numLevels --;
8411     interp->framePtr = interp->framePtr->parentCallFrame;
8412     if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8413         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8414     } else {
8415         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8416     }
8417     /* Handle the JIM_EVAL return code */
8418     if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8419         int savedLevel = interp->evalRetcodeLevel;
8420
8421         interp->evalRetcodeLevel = interp->numLevels;
8422         while (retcode == JIM_EVAL) {
8423             Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8424             Jim_IncrRefCount(resultScriptObjPtr);
8425             retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8426             Jim_DecrRefCount(interp, resultScriptObjPtr);
8427         }
8428         interp->evalRetcodeLevel = savedLevel;
8429     }
8430     /* Handle the JIM_RETURN return code */
8431     if (retcode == JIM_RETURN) {
8432         retcode = interp->returnCode;
8433         interp->returnCode = JIM_OK;
8434     }
8435     return retcode;
8436 }
8437
8438 int Jim_Eval(Jim_Interp *interp, const char *script)
8439 {
8440     Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8441     int retval;
8442
8443     Jim_IncrRefCount(scriptObjPtr);
8444     retval = Jim_EvalObj(interp, scriptObjPtr);
8445     Jim_DecrRefCount(interp, scriptObjPtr);
8446     return retval;
8447 }
8448
8449 /* Execute script in the scope of the global level */
8450 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8451 {
8452     Jim_CallFrame *savedFramePtr;
8453     int retval;
8454
8455     savedFramePtr = interp->framePtr;
8456     interp->framePtr = interp->topFramePtr;
8457     retval = Jim_Eval(interp, script);
8458     interp->framePtr = savedFramePtr;
8459     return retval;
8460 }
8461
8462 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8463 {
8464     Jim_CallFrame *savedFramePtr;
8465     int retval;
8466
8467     savedFramePtr = interp->framePtr;
8468     interp->framePtr = interp->topFramePtr;
8469     retval = Jim_EvalObj(interp, scriptObjPtr);
8470     interp->framePtr = savedFramePtr;
8471     /* Try to report the error (if any) via the bgerror proc */
8472     if (retval != JIM_OK) {
8473         Jim_Obj *objv[2];
8474
8475         objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8476         objv[1] = Jim_GetResult(interp);
8477         Jim_IncrRefCount(objv[0]);
8478         Jim_IncrRefCount(objv[1]);
8479         if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8480             /* Report the error to stderr. */
8481             fprintf(interp->stderr_, "Background error:" JIM_NL);
8482             Jim_PrintErrorMessage(interp);
8483         }
8484         Jim_DecrRefCount(interp, objv[0]);
8485         Jim_DecrRefCount(interp, objv[1]);
8486     }
8487     return retval;
8488 }
8489
8490 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8491 {
8492     char *prg = NULL;
8493     FILE *fp;
8494     int nread, totread, maxlen, buflen;
8495     int retval;
8496     Jim_Obj *scriptObjPtr;
8497     
8498     if ((fp = fopen(filename, "r")) == NULL) {
8499         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8500         Jim_AppendStrings(interp, Jim_GetResult(interp),
8501             "Error loading script \"", filename, "\": ",
8502             strerror(errno), NULL);
8503         return JIM_ERR;
8504     }
8505     buflen = 1024;
8506     maxlen = totread = 0;
8507     while (1) {
8508         if (maxlen < totread+buflen+1) {
8509             maxlen = totread+buflen+1;
8510             prg = Jim_Realloc(prg, maxlen);
8511         }
8512         if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8513         totread += nread;
8514     }
8515     prg[totread] = '\0';
8516     fclose(fp);
8517
8518     scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8519     JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8520     Jim_IncrRefCount(scriptObjPtr);
8521     retval = Jim_EvalObj(interp, scriptObjPtr);
8522     Jim_DecrRefCount(interp, scriptObjPtr);
8523     return retval;
8524 }
8525
8526 /* -----------------------------------------------------------------------------
8527  * Subst
8528  * ---------------------------------------------------------------------------*/
8529 static int JimParseSubstStr(struct JimParserCtx *pc)
8530 {
8531     pc->tstart = pc->p;
8532     pc->tline = pc->linenr;
8533     while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8534         pc->p++; pc->len--;
8535     }
8536     pc->tend = pc->p-1;
8537     pc->tt = JIM_TT_ESC;
8538     return JIM_OK;
8539 }
8540
8541 static int JimParseSubst(struct JimParserCtx *pc, int flags)
8542 {
8543     int retval;
8544
8545     if (pc->len == 0) {
8546         pc->tstart = pc->tend = pc->p;
8547         pc->tline = pc->linenr;
8548         pc->tt = JIM_TT_EOL;
8549         pc->eof = 1;
8550         return JIM_OK;
8551     }
8552     switch(*pc->p) {
8553     case '[':
8554         retval = JimParseCmd(pc);
8555         if (flags & JIM_SUBST_NOCMD) {
8556             pc->tstart--;
8557             pc->tend++;
8558             pc->tt = (flags & JIM_SUBST_NOESC) ?
8559                 JIM_TT_STR : JIM_TT_ESC;
8560         }
8561         return retval;
8562         break;
8563     case '$':
8564         if (JimParseVar(pc) == JIM_ERR) {
8565             pc->tstart = pc->tend = pc->p++; pc->len--;
8566             pc->tline = pc->linenr;
8567             pc->tt = JIM_TT_STR;
8568         } else {
8569             if (flags & JIM_SUBST_NOVAR) {
8570                 pc->tstart--;
8571                 if (flags & JIM_SUBST_NOESC)
8572                     pc->tt = JIM_TT_STR;
8573                 else
8574                     pc->tt = JIM_TT_ESC;
8575                 if (*pc->tstart == '{') {
8576                     pc->tstart--;
8577                     if (*(pc->tend+1))
8578                         pc->tend++;
8579                 }
8580             }
8581         }
8582         break;
8583     default:
8584         retval = JimParseSubstStr(pc);
8585         if (flags & JIM_SUBST_NOESC)
8586             pc->tt = JIM_TT_STR;
8587         return retval;
8588         break;
8589     }
8590     return JIM_OK;
8591 }
8592
8593 /* The subst object type reuses most of the data structures and functions
8594  * of the script object. Script's data structures are a bit more complex
8595  * for what is needed for [subst]itution tasks, but the reuse helps to
8596  * deal with a single data structure at the cost of some more memory
8597  * usage for substitutions. */
8598 static Jim_ObjType substObjType = {
8599     "subst",
8600     FreeScriptInternalRep,
8601     DupScriptInternalRep,
8602     NULL,
8603     JIM_TYPE_REFERENCES,
8604 };
8605
8606 /* This method takes the string representation of an object
8607  * as a Tcl string where to perform [subst]itution, and generates
8608  * the pre-parsed internal representation. */
8609 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8610 {
8611     int scriptTextLen;
8612     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8613     struct JimParserCtx parser;
8614     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8615
8616     script->len = 0;
8617     script->csLen = 0;
8618     script->commands = 0;
8619     script->token = NULL;
8620     script->cmdStruct = NULL;
8621     script->inUse = 1;
8622     script->substFlags = flags;
8623     script->fileName = NULL;
8624
8625     JimParserInit(&parser, scriptText, scriptTextLen, 1);
8626     while(1) {
8627         char *token;
8628         int len, type, linenr;
8629
8630         JimParseSubst(&parser, flags);
8631         if (JimParserEof(&parser)) break;
8632         token = JimParserGetToken(&parser, &len, &type, &linenr);
8633         ScriptObjAddToken(interp, script, token, len, type,
8634                 NULL, linenr);
8635     }
8636     /* Free the old internal rep and set the new one. */
8637     Jim_FreeIntRep(interp, objPtr);
8638     Jim_SetIntRepPtr(objPtr, script);
8639     objPtr->typePtr = &scriptObjType;
8640     return JIM_OK;
8641 }
8642
8643 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8644 {
8645     struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8646
8647     if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8648         SetSubstFromAny(interp, objPtr, flags);
8649     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8650 }
8651
8652 /* Performs commands,variables,blackslashes substitution,
8653  * storing the result object (with refcount 0) into
8654  * resObjPtrPtr. */
8655 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8656         Jim_Obj **resObjPtrPtr, int flags)
8657 {
8658     ScriptObj *script;
8659     ScriptToken *token;
8660     int i, len, retcode = JIM_OK;
8661     Jim_Obj *resObjPtr, *savedResultObjPtr;
8662
8663     script = Jim_GetSubst(interp, substObjPtr, flags);
8664 #ifdef JIM_OPTIMIZATION
8665     /* Fast path for a very common case with array-alike syntax,
8666      * that's: $foo($bar) */
8667     if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8668         Jim_Obj *varObjPtr = script->token[0].objPtr;
8669         
8670         Jim_IncrRefCount(varObjPtr);
8671         resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8672         if (resObjPtr == NULL) {
8673             Jim_DecrRefCount(interp, varObjPtr);
8674             return JIM_ERR;
8675         }
8676         Jim_DecrRefCount(interp, varObjPtr);
8677         *resObjPtrPtr = resObjPtr;
8678         return JIM_OK;
8679     }
8680 #endif
8681
8682     Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8683     /* In order to preserve the internal rep, we increment the
8684      * inUse field of the script internal rep structure. */
8685     script->inUse++;
8686
8687     token = script->token;
8688     len = script->len;
8689
8690     /* Save the interp old result, to set it again before
8691      * to return. */
8692     savedResultObjPtr = interp->result;
8693     Jim_IncrRefCount(savedResultObjPtr);
8694     
8695     /* Perform the substitution. Starts with an empty object
8696      * and adds every token (performing the appropriate
8697      * var/command/escape substitution). */
8698     resObjPtr = Jim_NewStringObj(interp, "", 0);
8699     for (i = 0; i < len; i++) {
8700         Jim_Obj *objPtr;
8701
8702         switch(token[i].type) {
8703         case JIM_TT_STR:
8704         case JIM_TT_ESC:
8705             Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8706             break;
8707         case JIM_TT_VAR:
8708             objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8709             if (objPtr == NULL) goto err;
8710             Jim_IncrRefCount(objPtr);
8711             Jim_AppendObj(interp, resObjPtr, objPtr);
8712             Jim_DecrRefCount(interp, objPtr);
8713             break;
8714         case JIM_TT_CMD:
8715             if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8716                 goto err;
8717             Jim_AppendObj(interp, resObjPtr, interp->result);
8718             break;
8719         default:
8720             Jim_Panic(interp,
8721               "default token type (%d) reached "
8722               "in Jim_SubstObj().", token[i].type);
8723             break;
8724         }
8725     }
8726 ok:
8727     if (retcode == JIM_OK)
8728         Jim_SetResult(interp, savedResultObjPtr);
8729     Jim_DecrRefCount(interp, savedResultObjPtr);
8730     /* Note that we don't have to decrement inUse, because the
8731      * following code transfers our use of the reference again to
8732      * the script object. */
8733     Jim_FreeIntRep(interp, substObjPtr);
8734     substObjPtr->typePtr = &scriptObjType;
8735     Jim_SetIntRepPtr(substObjPtr, script);
8736     Jim_DecrRefCount(interp, substObjPtr);
8737     *resObjPtrPtr = resObjPtr;
8738     return retcode;
8739 err:
8740     Jim_FreeNewObj(interp, resObjPtr);
8741     retcode = JIM_ERR;
8742     goto ok;
8743 }
8744
8745 /* -----------------------------------------------------------------------------
8746  * API Input/Export functions
8747  * ---------------------------------------------------------------------------*/
8748
8749 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8750 {
8751     Jim_HashEntry *he;
8752
8753     he = Jim_FindHashEntry(&interp->stub, funcname);
8754     if (!he)
8755         return JIM_ERR;
8756     memcpy(targetPtrPtr, &he->val, sizeof(void*));
8757     return JIM_OK;
8758 }
8759
8760 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
8761 {
8762     return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
8763 }
8764
8765 #define JIM_REGISTER_API(name) \
8766     Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
8767
8768 void JimRegisterCoreApi(Jim_Interp *interp)
8769 {
8770   interp->getApiFuncPtr = Jim_GetApi;
8771   JIM_REGISTER_API(Alloc);
8772   JIM_REGISTER_API(Free);
8773   JIM_REGISTER_API(Eval);
8774   JIM_REGISTER_API(EvalGlobal);
8775   JIM_REGISTER_API(EvalFile);
8776   JIM_REGISTER_API(EvalObj);
8777   JIM_REGISTER_API(EvalObjBackground);
8778   JIM_REGISTER_API(EvalObjVector);
8779   JIM_REGISTER_API(InitHashTable);
8780   JIM_REGISTER_API(ExpandHashTable);
8781   JIM_REGISTER_API(AddHashEntry);
8782   JIM_REGISTER_API(ReplaceHashEntry);
8783   JIM_REGISTER_API(DeleteHashEntry);
8784   JIM_REGISTER_API(FreeHashTable);
8785   JIM_REGISTER_API(FindHashEntry);
8786   JIM_REGISTER_API(ResizeHashTable);
8787   JIM_REGISTER_API(GetHashTableIterator);
8788   JIM_REGISTER_API(NextHashEntry);
8789   JIM_REGISTER_API(NewObj);
8790   JIM_REGISTER_API(FreeObj);
8791   JIM_REGISTER_API(InvalidateStringRep);
8792   JIM_REGISTER_API(InitStringRep);
8793   JIM_REGISTER_API(DuplicateObj);
8794   JIM_REGISTER_API(GetString);
8795   JIM_REGISTER_API(Length);
8796   JIM_REGISTER_API(InvalidateStringRep);
8797   JIM_REGISTER_API(NewStringObj);
8798   JIM_REGISTER_API(NewStringObjNoAlloc);
8799   JIM_REGISTER_API(AppendString);
8800   JIM_REGISTER_API(AppendObj);
8801   JIM_REGISTER_API(AppendStrings);
8802   JIM_REGISTER_API(StringEqObj);
8803   JIM_REGISTER_API(StringMatchObj);
8804   JIM_REGISTER_API(StringRangeObj);
8805   JIM_REGISTER_API(FormatString);
8806   JIM_REGISTER_API(CompareStringImmediate);
8807   JIM_REGISTER_API(NewReference);
8808   JIM_REGISTER_API(GetReference);
8809   JIM_REGISTER_API(SetFinalizer);
8810   JIM_REGISTER_API(GetFinalizer);
8811   JIM_REGISTER_API(CreateInterp);
8812   JIM_REGISTER_API(FreeInterp);
8813   JIM_REGISTER_API(GetExitCode);
8814   JIM_REGISTER_API(SetStdin);
8815   JIM_REGISTER_API(SetStdout);
8816   JIM_REGISTER_API(SetStderr);
8817   JIM_REGISTER_API(CreateCommand);
8818   JIM_REGISTER_API(CreateProcedure);
8819   JIM_REGISTER_API(DeleteCommand);
8820   JIM_REGISTER_API(RenameCommand);
8821   JIM_REGISTER_API(GetCommand);
8822   JIM_REGISTER_API(SetVariable);
8823   JIM_REGISTER_API(SetVariableStr);
8824   JIM_REGISTER_API(SetGlobalVariableStr);
8825   JIM_REGISTER_API(SetVariableStrWithStr);
8826   JIM_REGISTER_API(SetVariableLink);
8827   JIM_REGISTER_API(GetVariable);
8828   JIM_REGISTER_API(GetCallFrameByLevel);
8829   JIM_REGISTER_API(Collect);
8830   JIM_REGISTER_API(CollectIfNeeded);
8831   JIM_REGISTER_API(GetIndex);
8832   JIM_REGISTER_API(NewListObj);
8833   JIM_REGISTER_API(ListAppendElement);
8834   JIM_REGISTER_API(ListAppendList);
8835   JIM_REGISTER_API(ListLength);
8836   JIM_REGISTER_API(ListIndex);
8837   JIM_REGISTER_API(SetListIndex);
8838   JIM_REGISTER_API(ConcatObj);
8839   JIM_REGISTER_API(NewDictObj);
8840   JIM_REGISTER_API(DictKey);
8841   JIM_REGISTER_API(DictKeysVector);
8842   JIM_REGISTER_API(GetIndex);
8843   JIM_REGISTER_API(GetReturnCode);
8844   JIM_REGISTER_API(EvalExpression);
8845   JIM_REGISTER_API(GetBoolFromExpr);
8846   JIM_REGISTER_API(GetWide);
8847   JIM_REGISTER_API(GetLong);
8848   JIM_REGISTER_API(SetWide);
8849   JIM_REGISTER_API(NewIntObj);
8850   JIM_REGISTER_API(GetDouble);
8851   JIM_REGISTER_API(SetDouble);
8852   JIM_REGISTER_API(NewDoubleObj);
8853   JIM_REGISTER_API(WrongNumArgs);
8854   JIM_REGISTER_API(SetDictKeysVector);
8855   JIM_REGISTER_API(SubstObj);
8856   JIM_REGISTER_API(RegisterApi);
8857   JIM_REGISTER_API(PrintErrorMessage);
8858   JIM_REGISTER_API(InteractivePrompt);
8859   JIM_REGISTER_API(RegisterCoreCommands);
8860   JIM_REGISTER_API(GetSharedString);
8861   JIM_REGISTER_API(ReleaseSharedString);
8862   JIM_REGISTER_API(Panic);
8863   JIM_REGISTER_API(StrDup);
8864   JIM_REGISTER_API(UnsetVariable);
8865   JIM_REGISTER_API(GetVariableStr);
8866   JIM_REGISTER_API(GetGlobalVariable);
8867   JIM_REGISTER_API(GetGlobalVariableStr);
8868   JIM_REGISTER_API(GetAssocData);
8869   JIM_REGISTER_API(SetAssocData);
8870   JIM_REGISTER_API(DeleteAssocData);
8871   JIM_REGISTER_API(GetEnum);
8872   JIM_REGISTER_API(ScriptIsComplete);
8873   JIM_REGISTER_API(PackageRequire);
8874   JIM_REGISTER_API(PackageProvide);
8875   JIM_REGISTER_API(InitStack);
8876   JIM_REGISTER_API(FreeStack);
8877   JIM_REGISTER_API(StackLen);
8878   JIM_REGISTER_API(StackPush);
8879   JIM_REGISTER_API(StackPop);
8880   JIM_REGISTER_API(StackPeek);
8881   JIM_REGISTER_API(FreeStackElements);
8882 }
8883
8884 /* -----------------------------------------------------------------------------
8885  * Core commands utility functions
8886  * ---------------------------------------------------------------------------*/
8887 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, 
8888         const char *msg)
8889 {
8890     int i;
8891     Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8892
8893     Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
8894     for (i = 0; i < argc; i++) {
8895         Jim_AppendObj(interp, objPtr, argv[i]);
8896         if (!(i+1 == argc && msg[0] == '\0'))
8897             Jim_AppendString(interp, objPtr, " ", 1);
8898     }
8899     Jim_AppendString(interp, objPtr, msg, -1);
8900     Jim_AppendString(interp, objPtr, "\"", 1);
8901     Jim_SetResult(interp, objPtr);
8902 }
8903
8904 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
8905 {
8906     Jim_HashTableIterator *htiter;
8907     Jim_HashEntry *he;
8908     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
8909     const char *pattern;
8910     int patternLen;
8911     
8912     pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
8913     htiter = Jim_GetHashTableIterator(&interp->commands);
8914     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8915         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
8916                     strlen((const char*)he->key), 0))
8917             continue;
8918         Jim_ListAppendElement(interp, listObjPtr,
8919                 Jim_NewStringObj(interp, he->key, -1));
8920     }
8921     Jim_FreeHashTableIterator(htiter);
8922     return listObjPtr;
8923 }
8924
8925 #define JIM_VARLIST_GLOBALS 0
8926 #define JIM_VARLIST_LOCALS 1
8927 #define JIM_VARLIST_VARS 2
8928
8929 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
8930         int mode)
8931 {
8932     Jim_HashTableIterator *htiter;
8933     Jim_HashEntry *he;
8934     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
8935     const char *pattern;
8936     int patternLen;
8937     
8938     pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
8939     if (mode == JIM_VARLIST_GLOBALS) {
8940         htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
8941     } else {
8942         /* For [info locals], if we are at top level an emtpy list
8943          * is returned. I don't agree, but we aim at compatibility (SS) */
8944         if (mode == JIM_VARLIST_LOCALS &&
8945             interp->framePtr == interp->topFramePtr)
8946             return listObjPtr;
8947         htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
8948     }
8949     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8950         Jim_Var *varPtr = (Jim_Var*) he->val;
8951         if (mode == JIM_VARLIST_LOCALS) {
8952             if (varPtr->linkFramePtr != NULL)
8953                 continue;
8954         }
8955         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
8956                     strlen((const char*)he->key), 0))
8957             continue;
8958         Jim_ListAppendElement(interp, listObjPtr,
8959                 Jim_NewStringObj(interp, he->key, -1));
8960     }
8961     Jim_FreeHashTableIterator(htiter);
8962     return listObjPtr;
8963 }
8964
8965 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
8966         Jim_Obj **objPtrPtr)
8967 {
8968     Jim_CallFrame *targetCallFrame;
8969
8970     if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
8971             != JIM_OK)
8972         return JIM_ERR;
8973     /* No proc call at toplevel callframe */
8974     if (targetCallFrame == interp->topFramePtr) {
8975         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8976         Jim_AppendStrings(interp, Jim_GetResult(interp),
8977                 "bad level \"",
8978                 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
8979         return JIM_ERR;
8980     }
8981     *objPtrPtr = Jim_NewListObj(interp,
8982             targetCallFrame->argv,
8983             targetCallFrame->argc);
8984     return JIM_OK;
8985 }
8986
8987 /* -----------------------------------------------------------------------------
8988  * Core commands
8989  * ---------------------------------------------------------------------------*/
8990
8991 /* fake [puts] -- not the real puts, just for debugging. */
8992 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
8993         Jim_Obj *const *argv)
8994 {
8995     const char *str;
8996     int len, nonewline = 0;
8997     
8998     if (argc != 2 && argc != 3) {
8999         Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9000         return JIM_ERR;
9001     }
9002     if (argc == 3) {
9003         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9004         {
9005             Jim_SetResultString(interp, "The second argument must "
9006                     "be -nonewline", -1);
9007             return JIM_OK;
9008         } else {
9009             nonewline = 1;
9010             argv++;
9011         }
9012     }
9013     str = Jim_GetString(argv[1], &len);
9014     fwrite(str, 1, len, interp->stdout_);
9015     if (!nonewline) fprintf(interp->stdout_, JIM_NL);
9016     return JIM_OK;
9017 }
9018
9019 /* Helper for [+] and [*] */
9020 static int Jim_AddMulHelper(Jim_Interp *interp, int argc, 
9021         Jim_Obj *const *argv, int op)
9022 {
9023     jim_wide wideValue, res;
9024     double doubleValue, doubleRes;
9025     int i;
9026
9027     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9028     
9029     for (i = 1; i < argc; i++) {
9030         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9031             goto trydouble;
9032         if (op == JIM_EXPROP_ADD)
9033             res += wideValue;
9034         else
9035             res *= wideValue;
9036     }
9037     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9038     return JIM_OK;
9039 trydouble:
9040     doubleRes = (double) res;
9041     for (;i < argc; i++) {
9042         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9043             return JIM_ERR;
9044         if (op == JIM_EXPROP_ADD)
9045             doubleRes += doubleValue;
9046         else
9047             doubleRes *= doubleValue;
9048     }
9049     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9050     return JIM_OK;
9051 }
9052
9053 /* Helper for [-] and [/] */
9054 static int Jim_SubDivHelper(Jim_Interp *interp, int argc, 
9055         Jim_Obj *const *argv, int op)
9056 {
9057     jim_wide wideValue, res = 0;
9058     double doubleValue, doubleRes = 0;
9059     int i = 2;
9060
9061     if (argc < 2) {
9062         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9063         return JIM_ERR;
9064     } else if (argc == 2) {
9065         /* The arity = 2 case is different. For [- x] returns -x,
9066          * while [/ x] returns 1/x. */
9067         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9068             if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9069                     JIM_OK)
9070             {
9071                 return JIM_ERR;
9072             } else {
9073                 if (op == JIM_EXPROP_SUB)
9074                     doubleRes = -doubleValue;
9075                 else
9076                     doubleRes = 1.0/doubleValue;
9077                 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9078                             doubleRes));
9079                 return JIM_OK;
9080             }
9081         }
9082         if (op == JIM_EXPROP_SUB) {
9083             res = -wideValue;
9084             Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9085         } else {
9086             doubleRes = 1.0/wideValue;
9087             Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9088                         doubleRes));
9089         }
9090         return JIM_OK;
9091     } else {
9092         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9093             if (Jim_GetDouble(interp, argv[1], &doubleRes)
9094                     != JIM_OK) {
9095                 return JIM_ERR;
9096             } else {
9097                 goto trydouble;
9098             }
9099         }
9100     }
9101     for (i = 2; i < argc; i++) {
9102         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9103             doubleRes = (double) res;
9104             goto trydouble;
9105         }
9106         if (op == JIM_EXPROP_SUB)
9107             res -= wideValue;
9108         else
9109             res /= wideValue;
9110     }
9111     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9112     return JIM_OK;
9113 trydouble:
9114     for (;i < argc; i++) {
9115         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9116             return JIM_ERR;
9117         if (op == JIM_EXPROP_SUB)
9118             doubleRes -= doubleValue;
9119         else
9120             doubleRes /= doubleValue;
9121     }
9122     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9123     return JIM_OK;
9124 }
9125
9126
9127 /* [+] */
9128 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9129         Jim_Obj *const *argv)
9130 {
9131     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9132 }
9133
9134 /* [*] */
9135 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9136         Jim_Obj *const *argv)
9137 {
9138     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9139 }
9140
9141 /* [-] */
9142 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9143         Jim_Obj *const *argv)
9144 {
9145     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9146 }
9147
9148 /* [/] */
9149 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9150         Jim_Obj *const *argv)
9151 {
9152     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9153 }
9154
9155 /* [set] */
9156 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9157         Jim_Obj *const *argv)
9158 {
9159     if (argc != 2 && argc != 3) {
9160         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9161         return JIM_ERR;
9162     }
9163     if (argc == 2) {
9164         Jim_Obj *objPtr;
9165         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9166         if (!objPtr)
9167             return JIM_ERR;
9168         Jim_SetResult(interp, objPtr);
9169         return JIM_OK;
9170     }
9171     /* argc == 3 case. */
9172     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9173         return JIM_ERR;
9174     Jim_SetResult(interp, argv[2]);
9175     return JIM_OK;
9176 }
9177
9178 /* [unset] */
9179 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, 
9180         Jim_Obj *const *argv)
9181 {
9182     int i;
9183
9184     if (argc < 2) {
9185         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9186         return JIM_ERR;
9187     }
9188     for (i = 1; i < argc; i++) {
9189         if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9190             return JIM_ERR;
9191     }
9192     return JIM_OK;
9193 }
9194
9195 /* [incr] */
9196 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, 
9197         Jim_Obj *const *argv)
9198 {
9199     jim_wide wideValue, increment = 1;
9200     Jim_Obj *intObjPtr;
9201
9202     if (argc != 2 && argc != 3) {
9203         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9204         return JIM_ERR;
9205     }
9206     if (argc == 3) {
9207         if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9208             return JIM_ERR;
9209     }
9210     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9211     if (!intObjPtr) return JIM_ERR;
9212     if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9213         return JIM_ERR;
9214     if (Jim_IsShared(intObjPtr)) {
9215         intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9216         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9217             Jim_FreeNewObj(interp, intObjPtr);
9218             return JIM_ERR;
9219         }
9220     } else {
9221         Jim_SetWide(interp, intObjPtr, wideValue+increment);
9222         /* The following step is required in order to invalidate the
9223          * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9224         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9225             return JIM_ERR;
9226         }
9227     }
9228     Jim_SetResult(interp, intObjPtr);
9229     return JIM_OK;
9230 }
9231
9232 /* [while] */
9233 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, 
9234         Jim_Obj *const *argv)
9235 {
9236     if (argc != 3) {
9237         Jim_WrongNumArgs(interp, 1, argv, "condition body");
9238         return JIM_ERR;
9239     }
9240     /* Try to run a specialized version of while if the expression
9241      * is in one of the following forms:
9242      *
9243      *   $a < CONST, $a < $b
9244      *   $a <= CONST, $a <= $b
9245      *   $a > CONST, $a > $b
9246      *   $a >= CONST, $a >= $b
9247      *   $a != CONST, $a != $b
9248      *   $a == CONST, $a == $b
9249      *   $a
9250      *   !$a
9251      *   CONST
9252      */
9253
9254 #ifdef JIM_OPTIMIZATION
9255     {
9256         ExprByteCode *expr;
9257         Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9258         int exprLen, retval;
9259
9260         /* STEP 1 -- Check if there are the conditions to run the specialized
9261          * version of while */
9262         
9263         if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9264         if (expr->len <= 0 || expr->len > 3) goto noopt;
9265         switch(expr->len) {
9266         case 1:
9267             if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9268                 expr->opcode[0] != JIM_EXPROP_NUMBER)
9269                 goto noopt;
9270             break;
9271         case 2:
9272             if (expr->opcode[1] != JIM_EXPROP_NOT ||
9273                 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9274                 goto noopt;
9275             break;
9276         case 3:
9277             if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9278                 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9279                  expr->opcode[1] != JIM_EXPROP_VARIABLE))
9280                 goto noopt;
9281             switch(expr->opcode[2]) {
9282             case JIM_EXPROP_LT:
9283             case JIM_EXPROP_LTE:
9284             case JIM_EXPROP_GT:
9285             case JIM_EXPROP_GTE:
9286             case JIM_EXPROP_NUMEQ:
9287             case JIM_EXPROP_NUMNE:
9288                 /* nothing to do */
9289                 break;
9290             default:
9291                 goto noopt;
9292             }
9293             break;
9294         default:
9295             Jim_Panic(interp,
9296                 "Unexpected default reached in Jim_WhileCoreCommand()");
9297             break;
9298         }
9299
9300         /* STEP 2 -- conditions meet. Initialization. Take different
9301          * branches for different expression lengths. */
9302         exprLen = expr->len;
9303
9304         if (exprLen == 1) {
9305             jim_wide wideValue;
9306
9307             if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9308                 varAObjPtr = expr->obj[0];
9309                 Jim_IncrRefCount(varAObjPtr);
9310             } else {
9311                 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9312                     goto noopt;
9313             }
9314             while (1) {
9315                 if (varAObjPtr) {
9316                     if (!(objPtr =
9317                                Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9318                         Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9319                     {
9320                         Jim_DecrRefCount(interp, varAObjPtr);
9321                         goto noopt;
9322                     }
9323                 }
9324                 if (!wideValue) break;
9325                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9326                     switch(retval) {
9327                     case JIM_BREAK:
9328                         if (varAObjPtr)
9329                             Jim_DecrRefCount(interp, varAObjPtr);
9330                         goto out;
9331                         break;
9332                     case JIM_CONTINUE:
9333                         continue;
9334                         break;
9335                     default:
9336                         if (varAObjPtr)
9337                             Jim_DecrRefCount(interp, varAObjPtr);
9338                         return retval;
9339                     }
9340                 }
9341             }
9342             if (varAObjPtr)
9343                 Jim_DecrRefCount(interp, varAObjPtr);
9344         } else if (exprLen == 3) {
9345             jim_wide wideValueA, wideValueB, cmpRes = 0;
9346             int cmpType = expr->opcode[2];
9347
9348             varAObjPtr = expr->obj[0];
9349             Jim_IncrRefCount(varAObjPtr);
9350             if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9351                 varBObjPtr = expr->obj[1];
9352                 Jim_IncrRefCount(varBObjPtr);
9353             } else {
9354                 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9355                     goto noopt;
9356             }
9357             while (1) {
9358                 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9359                     Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9360                 {
9361                     Jim_DecrRefCount(interp, varAObjPtr);
9362                     if (varBObjPtr)
9363                         Jim_DecrRefCount(interp, varBObjPtr);
9364                     goto noopt;
9365                 }
9366                 if (varBObjPtr) {
9367                     if (!(objPtr =
9368                                Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9369                         Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9370                     {
9371                         Jim_DecrRefCount(interp, varAObjPtr);
9372                         if (varBObjPtr)
9373                             Jim_DecrRefCount(interp, varBObjPtr);
9374                         goto noopt;
9375                     }
9376                 }
9377                 switch(cmpType) {
9378                 case JIM_EXPROP_LT:
9379                     cmpRes = wideValueA < wideValueB; break;
9380                 case JIM_EXPROP_LTE:
9381                     cmpRes = wideValueA <= wideValueB; break;
9382                 case JIM_EXPROP_GT:
9383                     cmpRes = wideValueA > wideValueB; break;
9384                 case JIM_EXPROP_GTE:
9385                     cmpRes = wideValueA >= wideValueB; break;
9386                 case JIM_EXPROP_NUMEQ:
9387                     cmpRes = wideValueA == wideValueB; break;
9388                 case JIM_EXPROP_NUMNE:
9389                     cmpRes = wideValueA != wideValueB; break;
9390                 }
9391                 if (!cmpRes) break;
9392                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9393                     switch(retval) {
9394                     case JIM_BREAK:
9395                         Jim_DecrRefCount(interp, varAObjPtr);
9396                         if (varBObjPtr)
9397                             Jim_DecrRefCount(interp, varBObjPtr);
9398                         goto out;
9399                         break;
9400                     case JIM_CONTINUE:
9401                         continue;
9402                         break;
9403                     default:
9404                         Jim_DecrRefCount(interp, varAObjPtr);
9405                         if (varBObjPtr)
9406                             Jim_DecrRefCount(interp, varBObjPtr);
9407                         return retval;
9408                     }
9409                 }
9410             }
9411             Jim_DecrRefCount(interp, varAObjPtr);
9412             if (varBObjPtr)
9413                 Jim_DecrRefCount(interp, varBObjPtr);
9414         } else {
9415             /* TODO: case for len == 2 */
9416             goto noopt;
9417         }
9418         Jim_SetEmptyResult(interp);
9419         return JIM_OK;
9420     }
9421 noopt:
9422 #endif
9423
9424     /* The general purpose implementation of while starts here */
9425     while (1) {
9426         int boolean, retval;
9427
9428         if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9429                         &boolean)) != JIM_OK)
9430             return retval;
9431         if (!boolean) break;
9432         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9433             switch(retval) {
9434             case JIM_BREAK:
9435                 goto out;
9436                 break;
9437             case JIM_CONTINUE:
9438                 continue;
9439                 break;
9440             default:
9441                 return retval;
9442             }
9443         }
9444     }
9445 out:
9446     Jim_SetEmptyResult(interp);
9447     return JIM_OK;
9448 }
9449
9450 /* [for] */
9451 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, 
9452         Jim_Obj *const *argv)
9453 {
9454     int retval;
9455
9456     if (argc != 5) {
9457         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9458         return JIM_ERR;
9459     }
9460     /* Check if the for is on the form:
9461      *      for {set i CONST} {$i < CONST} {incr i}
9462      *      for {set i CONST} {$i < $j} {incr i}
9463      *      for {set i CONST} {$i <= CONST} {incr i}
9464      *      for {set i CONST} {$i <= $j} {incr i}
9465      * XXX: NOTE: if variable traces are implemented, this optimization
9466      * need to be modified to check for the proc epoch at every variable
9467      * update. */
9468 #ifdef JIM_OPTIMIZATION
9469     {
9470         ScriptObj *initScript, *incrScript;
9471         ExprByteCode *expr;
9472         jim_wide start, stop, currentVal;
9473         unsigned jim_wide procEpoch = interp->procEpoch;
9474         Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9475         int cmpType;
9476         struct Jim_Cmd *cmdPtr;
9477
9478         /* Do it only if there aren't shared arguments */
9479         if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9480             goto evalstart;
9481         initScript = Jim_GetScript(interp, argv[1]);
9482         expr = Jim_GetExpression(interp, argv[2]);
9483         incrScript = Jim_GetScript(interp, argv[3]);
9484
9485         /* Ensure proper lengths to start */
9486         if (initScript->len != 6) goto evalstart;
9487         if (incrScript->len != 4) goto evalstart;
9488         if (expr->len != 3) goto evalstart;
9489         /* Ensure proper token types. */
9490         if (initScript->token[2].type != JIM_TT_ESC ||
9491             initScript->token[4].type != JIM_TT_ESC ||
9492             incrScript->token[2].type != JIM_TT_ESC ||
9493             expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9494             (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9495              expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9496             (expr->opcode[2] != JIM_EXPROP_LT &&
9497              expr->opcode[2] != JIM_EXPROP_LTE))
9498             goto evalstart;
9499         cmpType = expr->opcode[2];
9500         /* Initialization command must be [set] */
9501         cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9502         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9503             goto evalstart;
9504         /* Update command must be incr */
9505         cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9506         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9507             goto evalstart;
9508         /* set, incr, expression must be about the same variable */
9509         if (!Jim_StringEqObj(initScript->token[2].objPtr,
9510                             incrScript->token[2].objPtr, 0))
9511             goto evalstart;
9512         if (!Jim_StringEqObj(initScript->token[2].objPtr,
9513                             expr->obj[0], 0))
9514             goto evalstart;
9515         /* Check that the initialization and comparison are valid integers */
9516         if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9517             goto evalstart;
9518         if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9519             Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9520         {
9521             goto evalstart;
9522         }
9523
9524         /* Initialization */
9525         varNamePtr = expr->obj[0];
9526         if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9527             stopVarNamePtr = expr->obj[1];
9528             Jim_IncrRefCount(stopVarNamePtr);
9529         }
9530         Jim_IncrRefCount(varNamePtr);
9531
9532         /* --- OPTIMIZED FOR --- */
9533         /* Start to loop */
9534         objPtr = Jim_NewIntObj(interp, start);
9535         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9536             Jim_DecrRefCount(interp, varNamePtr);
9537             if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9538             Jim_FreeNewObj(interp, objPtr);
9539             goto evalstart;
9540         }
9541         while (1) {
9542             /* === Check condition === */
9543             /* Common code: */
9544             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9545             if (objPtr == NULL ||
9546                 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9547             {
9548                 Jim_DecrRefCount(interp, varNamePtr);
9549                 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9550                 goto testcond;
9551             }
9552             /* Immediate or Variable? get the 'stop' value if the latter. */
9553             if (stopVarNamePtr) {
9554                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9555                 if (objPtr == NULL ||
9556                     Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9557                 {
9558                     Jim_DecrRefCount(interp, varNamePtr);
9559                     Jim_DecrRefCount(interp, stopVarNamePtr);
9560                     goto testcond;
9561                 }
9562             }
9563             if (cmpType == JIM_EXPROP_LT) {
9564                 if (currentVal >= stop) break;
9565             } else {
9566                 if (currentVal > stop) break;
9567             }
9568             /* Eval body */
9569             if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9570                 switch(retval) {
9571                 case JIM_BREAK:
9572                     if (stopVarNamePtr)
9573                         Jim_DecrRefCount(interp, stopVarNamePtr);
9574                     Jim_DecrRefCount(interp, varNamePtr);
9575                     goto out;
9576                 case JIM_CONTINUE:
9577                     /* nothing to do */
9578                     break;
9579                 default:
9580                     if (stopVarNamePtr)
9581                         Jim_DecrRefCount(interp, stopVarNamePtr);
9582                     Jim_DecrRefCount(interp, varNamePtr);
9583                     return retval;
9584                 }
9585             }
9586             /* If there was a change in procedures/command continue
9587              * with the usual [for] command implementation */
9588             if (procEpoch != interp->procEpoch) {
9589                 if (stopVarNamePtr)
9590                     Jim_DecrRefCount(interp, stopVarNamePtr);
9591                 Jim_DecrRefCount(interp, varNamePtr);
9592                 goto evalnext;
9593             }
9594             /* Increment */
9595             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9596             if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9597                 objPtr->internalRep.wideValue ++;
9598                 Jim_InvalidateStringRep(objPtr);
9599             } else {
9600                 Jim_Obj *auxObjPtr;
9601
9602                 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9603                     if (stopVarNamePtr)
9604                         Jim_DecrRefCount(interp, stopVarNamePtr);
9605                     Jim_DecrRefCount(interp, varNamePtr);
9606                     goto evalnext;
9607                 }
9608                 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9609                 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9610                     if (stopVarNamePtr)
9611                         Jim_DecrRefCount(interp, stopVarNamePtr);
9612                     Jim_DecrRefCount(interp, varNamePtr);
9613                     Jim_FreeNewObj(interp, auxObjPtr);
9614                     goto evalnext;
9615                 }
9616             }
9617         }
9618         if (stopVarNamePtr)
9619             Jim_DecrRefCount(interp, stopVarNamePtr);
9620         Jim_DecrRefCount(interp, varNamePtr);
9621         Jim_SetEmptyResult(interp);
9622         return JIM_OK;
9623     }
9624 #endif
9625 evalstart:
9626     /* Eval start */
9627     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9628         return retval;
9629     while (1) {
9630         int boolean;
9631 testcond:
9632         /* Test the condition */
9633         if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9634                 != JIM_OK)
9635             return retval;
9636         if (!boolean) break;
9637         /* Eval body */
9638         if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9639             switch(retval) {
9640             case JIM_BREAK:
9641                 goto out;
9642                 break;
9643             case JIM_CONTINUE:
9644                 /* Nothing to do */
9645                 break;
9646             default:
9647                 return retval;
9648             }
9649         }
9650 evalnext:
9651         /* Eval next */
9652         if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9653             switch(retval) {
9654             case JIM_BREAK:
9655                 goto out;
9656                 break;
9657             case JIM_CONTINUE:
9658                 continue;
9659                 break;
9660             default:
9661                 return retval;
9662             }
9663         }
9664     }
9665 out:
9666     Jim_SetEmptyResult(interp);
9667     return JIM_OK;
9668 }
9669
9670 /* foreach + lmap implementation. */
9671 static int JimForeachMapHelper(Jim_Interp *interp, int argc, 
9672         Jim_Obj *const *argv, int doMap)
9673 {
9674     int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9675     int nbrOfLoops = 0;
9676     Jim_Obj *emptyStr, *script, *mapRes = NULL;
9677
9678     if (argc < 4 || argc % 2 != 0) {
9679         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9680         return JIM_ERR;
9681     }
9682     if (doMap) {
9683         mapRes = Jim_NewListObj(interp, NULL, 0);
9684         Jim_IncrRefCount(mapRes);
9685     }
9686     emptyStr = Jim_NewEmptyStringObj(interp);
9687     Jim_IncrRefCount(emptyStr);
9688     script = argv[argc-1];            /* Last argument is a script */
9689     nbrOfLists = (argc - 1 - 1) / 2;  /* argc - 'foreach' - script */
9690     listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9691     listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9692     /* Initialize iterators and remember max nbr elements each list */
9693     memset(listsIdx, 0, nbrOfLists * sizeof(int));
9694     /* Remember lengths of all lists and calculate how much rounds to loop */
9695     for (i=0; i < nbrOfLists*2; i += 2) {
9696         div_t cnt;
9697         int count;
9698         Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9699         Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9700         if (listsEnd[i] == 0) {
9701             Jim_SetResultString(interp, "foreach varlist is empty", -1);
9702             goto err;
9703         }
9704         cnt = div(listsEnd[i+1], listsEnd[i]);
9705         count = cnt.quot + (cnt.rem ? 1 : 0);
9706         if (count > nbrOfLoops)
9707             nbrOfLoops = count;
9708     }
9709     for (; nbrOfLoops-- > 0; ) {
9710         for (i=0; i < nbrOfLists; ++i) {
9711             int varIdx = 0, var = i * 2;
9712             while (varIdx < listsEnd[var]) {
9713                 Jim_Obj *varName, *ele;
9714                 int lst = i * 2 + 1;
9715                 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9716                         != JIM_OK)
9717                         goto err;
9718                 if (listsIdx[i] < listsEnd[lst]) {
9719                     if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9720                         != JIM_OK)
9721                         goto err;
9722                     if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9723                         Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9724                         goto err;
9725                     }
9726                     ++listsIdx[i];  /* Remember next iterator of current list */ 
9727                 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9728                     Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9729                     goto err;
9730                 }
9731                 ++varIdx;  /* Next variable */
9732             }
9733         }
9734         switch (result = Jim_EvalObj(interp, script)) {
9735             case JIM_OK:
9736                 if (doMap)
9737                     Jim_ListAppendElement(interp, mapRes, interp->result);
9738                 break;
9739             case JIM_CONTINUE:
9740                 break;
9741             case JIM_BREAK:
9742                 goto out;
9743                 break;
9744             default:
9745                 goto err;
9746         }
9747     }
9748 out:
9749     result = JIM_OK;
9750     if (doMap)
9751         Jim_SetResult(interp, mapRes);
9752     else
9753         Jim_SetEmptyResult(interp);
9754 err:
9755     if (doMap)
9756         Jim_DecrRefCount(interp, mapRes);
9757     Jim_DecrRefCount(interp, emptyStr);
9758     Jim_Free(listsIdx);
9759     Jim_Free(listsEnd);
9760     return result;
9761 }
9762
9763 /* [foreach] */
9764 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, 
9765         Jim_Obj *const *argv)
9766 {
9767     return JimForeachMapHelper(interp, argc, argv, 0);
9768 }
9769
9770 /* [lmap] */
9771 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, 
9772         Jim_Obj *const *argv)
9773 {
9774     return JimForeachMapHelper(interp, argc, argv, 1);
9775 }
9776
9777 /* [if] */
9778 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, 
9779         Jim_Obj *const *argv)
9780 {
9781     int boolean, retval, current = 1, falsebody = 0;
9782     if (argc >= 3) {
9783         while (1) {
9784             /* Far not enough arguments given! */
9785             if (current >= argc) goto err;
9786             if ((retval = Jim_GetBoolFromExpr(interp,
9787                         argv[current++], &boolean))
9788                     != JIM_OK)
9789                 return retval;
9790             /* There lacks something, isn't it? */
9791             if (current >= argc) goto err;
9792             if (Jim_CompareStringImmediate(interp, argv[current],
9793                         "then")) current++;
9794             /* Tsk tsk, no then-clause? */
9795             if (current >= argc) goto err;
9796             if (boolean)
9797                 return Jim_EvalObj(interp, argv[current]);
9798              /* Ok: no else-clause follows */
9799             if (++current >= argc) return JIM_OK;
9800             falsebody = current++;
9801             if (Jim_CompareStringImmediate(interp, argv[falsebody],
9802                         "else")) {
9803                 /* IIICKS - else-clause isn't last cmd? */
9804                 if (current != argc-1) goto err;
9805                 return Jim_EvalObj(interp, argv[current]);
9806             } else if (Jim_CompareStringImmediate(interp,
9807                         argv[falsebody], "elseif"))
9808                 /* Ok: elseif follows meaning all the stuff
9809                  * again (how boring...) */
9810                 continue;
9811             /* OOPS - else-clause is not last cmd?*/
9812             else if (falsebody != argc-1)
9813                 goto err;
9814             return Jim_EvalObj(interp, argv[falsebody]);
9815         }
9816         return JIM_OK;
9817     }
9818 err:
9819     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
9820     return JIM_ERR;
9821 }
9822
9823 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
9824
9825 /* [switch] */
9826 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, 
9827         Jim_Obj *const *argv)
9828 {
9829     int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
9830     Jim_Obj *command = 0, *const *caseList = 0, *strObj;
9831     Jim_Obj *script = 0;
9832     if (argc < 3) goto wrongnumargs;
9833     for (opt=1; opt < argc; ++opt) {
9834         const char *option = Jim_GetString(argv[opt], 0);
9835         if (*option != '-') break;
9836         else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
9837         else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
9838         else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
9839         else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
9840         else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
9841             if ((argc - opt) < 2) goto wrongnumargs;
9842             command = argv[++opt]; 
9843         } else {
9844             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9845             Jim_AppendStrings(interp, Jim_GetResult(interp),
9846                 "bad option \"", option, "\": must be -exact, -glob, "
9847                 "-regexp, -command procname or --", 0);
9848             goto err;            
9849         }
9850         if ((argc - opt) < 2) goto wrongnumargs;
9851     }
9852     strObj = argv[opt++];
9853     patCount = argc - opt;
9854     if (patCount == 1) {
9855         Jim_Obj **vector;
9856         JimListGetElements(interp, argv[opt], &patCount, &vector);
9857         caseList = vector;
9858     } else
9859         caseList = &argv[opt];
9860     if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
9861     for (i=0; script == 0 && i < patCount; i += 2) {
9862         Jim_Obj *patObj = caseList[i];
9863         if (!Jim_CompareStringImmediate(interp, patObj, "default")
9864             || i < (patCount-2)) {
9865             switch (matchOpt) {
9866                 case SWITCH_EXACT:
9867                     if (Jim_StringEqObj(strObj, patObj, 0))
9868                         script = caseList[i+1];
9869                     break;
9870                 case SWITCH_GLOB:
9871                     if (Jim_StringMatchObj(patObj, strObj, 0))
9872                         script = caseList[i+1];
9873                     break;
9874                 case SWITCH_RE:
9875                     command = Jim_NewStringObj(interp, "regexp", -1);
9876                     /* Fall thru intentionally */
9877                 case SWITCH_CMD: {
9878                     Jim_Obj *parms[] = {command, patObj, strObj};
9879                     int rc = Jim_EvalObjVector(interp, 3, parms);
9880                     long matching;
9881                     /* After the execution of a command we need to
9882                      * make sure to reconvert the object into a list
9883                      * again. Only for the single-list style [switch]. */
9884                     if (argc-opt == 1) {
9885                         Jim_Obj **vector;
9886                         JimListGetElements(interp, argv[opt], &patCount,
9887                                 &vector);
9888                         caseList = vector;
9889                     }
9890                     /* command is here already decref'd */
9891                     if (rc != JIM_OK) {
9892                         retcode = rc;
9893                         goto err;
9894                     }
9895                     rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
9896                     if (rc != JIM_OK) {
9897                         retcode = rc;
9898                         goto err;
9899                     }
9900                     if (matching)
9901                         script = caseList[i+1];
9902                     break;
9903                 }
9904                 default:
9905                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9906                     Jim_AppendStrings(interp, Jim_GetResult(interp),
9907                         "internal error: no such option implemented", 0);
9908                     goto err;
9909             }
9910         } else {
9911           script = caseList[i+1];
9912         }
9913     }
9914     for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
9915         i += 2)
9916         script = caseList[i+1];
9917     if (script && Jim_CompareStringImmediate(interp, script, "-")) {
9918         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9919         Jim_AppendStrings(interp, Jim_GetResult(interp),
9920             "no body specified for pattern \"",
9921             Jim_GetString(caseList[i-2], 0), "\"", 0);
9922         goto err;
9923     }
9924     retcode = JIM_OK;
9925     Jim_SetEmptyResult(interp);
9926     if (script != 0)
9927         retcode = Jim_EvalObj(interp, script);
9928     return retcode;
9929 wrongnumargs:
9930     Jim_WrongNumArgs(interp, 1, argv, "?options? string "
9931         "pattern body ... ?default body?   or   "
9932         "{pattern body ?pattern body ...?}");
9933 err:
9934     return retcode;        
9935 }
9936
9937 /* [list] */
9938 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, 
9939         Jim_Obj *const *argv)
9940 {
9941     Jim_Obj *listObjPtr;
9942
9943     listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
9944     Jim_SetResult(interp, listObjPtr);
9945     return JIM_OK;
9946 }
9947
9948 /* [lindex] */
9949 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, 
9950         Jim_Obj *const *argv)
9951 {
9952     Jim_Obj *objPtr, *listObjPtr;
9953     int i;
9954     int index;
9955
9956     if (argc < 3) {
9957         Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
9958         return JIM_ERR;
9959     }
9960     objPtr = argv[1];
9961     Jim_IncrRefCount(objPtr);
9962     for (i = 2; i < argc; i++) {
9963         listObjPtr = objPtr;
9964         if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
9965             Jim_DecrRefCount(interp, listObjPtr);
9966             return JIM_ERR;
9967         }
9968         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
9969                     JIM_NONE) != JIM_OK) {
9970             /* Returns an empty object if the index
9971              * is out of range. */
9972             Jim_DecrRefCount(interp, listObjPtr);
9973             Jim_SetEmptyResult(interp);
9974             return JIM_OK;
9975         }
9976         Jim_IncrRefCount(objPtr);
9977         Jim_DecrRefCount(interp, listObjPtr);
9978     }
9979     Jim_SetResult(interp, objPtr);
9980     Jim_DecrRefCount(interp, objPtr);
9981     return JIM_OK;
9982 }
9983
9984 /* [llength] */
9985 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, 
9986         Jim_Obj *const *argv)
9987 {
9988     int len;
9989
9990     if (argc != 2) {
9991         Jim_WrongNumArgs(interp, 1, argv, "list");
9992         return JIM_ERR;
9993     }
9994     Jim_ListLength(interp, argv[1], &len);
9995     Jim_SetResult(interp, Jim_NewIntObj(interp, len));
9996     return JIM_OK;
9997 }
9998
9999 /* [lappend] */
10000 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, 
10001         Jim_Obj *const *argv)
10002 {
10003     Jim_Obj *listObjPtr;
10004     int shared, i;
10005
10006     if (argc < 2) {
10007         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10008         return JIM_ERR;
10009     }
10010     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10011     if (!listObjPtr) {
10012         /* Create the list if it does not exists */
10013         listObjPtr = Jim_NewListObj(interp, NULL, 0);
10014         if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10015             Jim_FreeNewObj(interp, listObjPtr);
10016             return JIM_ERR;
10017         }
10018     }
10019     shared = Jim_IsShared(listObjPtr);
10020     if (shared)
10021         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10022     for (i = 2; i < argc; i++)
10023         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10024     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10025         if (shared)
10026             Jim_FreeNewObj(interp, listObjPtr);
10027         return JIM_ERR;
10028     }
10029     Jim_SetResult(interp, listObjPtr);
10030     return JIM_OK;
10031 }
10032
10033 /* [linsert] */
10034 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, 
10035         Jim_Obj *const *argv)
10036 {
10037     int index, len;
10038     Jim_Obj *listPtr;
10039
10040     if (argc < 4) {
10041         Jim_WrongNumArgs(interp, 1, argv, "list index element "
10042             "?element ...?");
10043         return JIM_ERR;
10044     }
10045     listPtr = argv[1];
10046     if (Jim_IsShared(listPtr))
10047         listPtr = Jim_DuplicateObj(interp, listPtr);
10048     if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10049         goto err;
10050     Jim_ListLength(interp, listPtr, &len);
10051     if (index >= len)
10052         index = len;
10053     else if (index < 0)
10054         index = len + index + 1;
10055     Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10056     Jim_SetResult(interp, listPtr);
10057     return JIM_OK;
10058 err:
10059     if (listPtr != argv[1]) {
10060         Jim_FreeNewObj(interp, listPtr);
10061     }
10062     return JIM_ERR;
10063 }
10064
10065 /* [lset] */
10066 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, 
10067         Jim_Obj *const *argv)
10068 {
10069     if (argc < 3) {
10070         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10071         return JIM_ERR;
10072     } else if (argc == 3) {
10073         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10074             return JIM_ERR;
10075         Jim_SetResult(interp, argv[2]);
10076         return JIM_OK;
10077     }
10078     if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10079             == JIM_ERR) return JIM_ERR;
10080     return JIM_OK;
10081 }
10082
10083 /* [lsort] */
10084 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10085 {
10086     const char *options[] = {
10087         "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10088     };
10089     enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10090     Jim_Obj *resObj;
10091     int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10092     int decreasing = 0;
10093
10094     if (argc < 2) {
10095         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10096         return JIM_ERR;
10097     }
10098     for (i = 1; i < (argc-1); i++) {
10099         int option;
10100
10101         if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10102                 != JIM_OK)
10103             return JIM_ERR;
10104         switch(option) {
10105         case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10106         case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10107         case OPT_INCREASING: decreasing = 0; break;
10108         case OPT_DECREASING: decreasing = 1; break;
10109         }
10110     }
10111     if (decreasing) {
10112         switch(lsortType) {
10113         case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10114         case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10115         }
10116     }
10117     resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10118     ListSortElements(interp, resObj, lsortType);
10119     Jim_SetResult(interp, resObj);
10120     return JIM_OK;
10121 }
10122
10123 /* [append] */
10124 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, 
10125         Jim_Obj *const *argv)
10126 {
10127     Jim_Obj *stringObjPtr;
10128     int shared, i;
10129
10130     if (argc < 2) {
10131         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10132         return JIM_ERR;
10133     }
10134     if (argc == 2) {
10135         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10136         if (!stringObjPtr) return JIM_ERR;
10137     } else {
10138         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10139         if (!stringObjPtr) {
10140             /* Create the string if it does not exists */
10141             stringObjPtr = Jim_NewEmptyStringObj(interp);
10142             if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10143                     != JIM_OK) {
10144                 Jim_FreeNewObj(interp, stringObjPtr);
10145                 return JIM_ERR;
10146             }
10147         }
10148     }
10149     shared = Jim_IsShared(stringObjPtr);
10150     if (shared)
10151         stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10152     for (i = 2; i < argc; i++)
10153         Jim_AppendObj(interp, stringObjPtr, argv[i]);
10154     if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10155         if (shared)
10156             Jim_FreeNewObj(interp, stringObjPtr);
10157         return JIM_ERR;
10158     }
10159     Jim_SetResult(interp, stringObjPtr);
10160     return JIM_OK;
10161 }
10162
10163 /* [debug] */
10164 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, 
10165         Jim_Obj *const *argv)
10166 {
10167     const char *options[] = {
10168         "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10169         "exprbc",
10170         NULL
10171     };
10172     enum {
10173         OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10174         OPT_EXPRLEN, OPT_EXPRBC
10175     };
10176     int option;
10177
10178     if (argc < 2) {
10179         Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10180         return JIM_ERR;
10181     }
10182     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10183                 JIM_ERRMSG) != JIM_OK)
10184         return JIM_ERR;
10185     if (option == OPT_REFCOUNT) {
10186         if (argc != 3) {
10187             Jim_WrongNumArgs(interp, 2, argv, "object");
10188             return JIM_ERR;
10189         }
10190         Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10191         return JIM_OK;
10192     } else if (option == OPT_OBJCOUNT) {
10193         int freeobj = 0, liveobj = 0;
10194         char buf[256];
10195         Jim_Obj *objPtr;
10196
10197         if (argc != 2) {
10198             Jim_WrongNumArgs(interp, 2, argv, "");
10199             return JIM_ERR;
10200         }
10201         /* Count the number of free objects. */
10202         objPtr = interp->freeList;
10203         while (objPtr) {
10204             freeobj++;
10205             objPtr = objPtr->nextObjPtr;
10206         }
10207         /* Count the number of live objects. */
10208         objPtr = interp->liveList;
10209         while (objPtr) {
10210             liveobj++;
10211             objPtr = objPtr->nextObjPtr;
10212         }
10213         /* Set the result string and return. */
10214         sprintf(buf, "free %d used %d", freeobj, liveobj);
10215         Jim_SetResultString(interp, buf, -1);
10216         return JIM_OK;
10217     } else if (option == OPT_OBJECTS) {
10218         Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10219         /* Count the number of live objects. */
10220         objPtr = interp->liveList;
10221         listObjPtr = Jim_NewListObj(interp, NULL, 0);
10222         while (objPtr) {
10223             char buf[128];
10224             const char *type = objPtr->typePtr ?
10225                 objPtr->typePtr->name : "";
10226             subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10227             sprintf(buf, "%p", objPtr);
10228             Jim_ListAppendElement(interp, subListObjPtr,
10229                 Jim_NewStringObj(interp, buf, -1));
10230             Jim_ListAppendElement(interp, subListObjPtr,
10231                 Jim_NewStringObj(interp, type, -1));
10232             Jim_ListAppendElement(interp, subListObjPtr,
10233                 Jim_NewIntObj(interp, objPtr->refCount));
10234             Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10235             Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10236             objPtr = objPtr->nextObjPtr;
10237         }
10238         Jim_SetResult(interp, listObjPtr);
10239         return JIM_OK;
10240     } else if (option == OPT_INVSTR) {
10241         Jim_Obj *objPtr;
10242
10243         if (argc != 3) {
10244             Jim_WrongNumArgs(interp, 2, argv, "object");
10245             return JIM_ERR;
10246         }
10247         objPtr = argv[2];
10248         if (objPtr->typePtr != NULL)
10249             Jim_InvalidateStringRep(objPtr);
10250         Jim_SetEmptyResult(interp);
10251         return JIM_OK;
10252     } else if (option == OPT_SCRIPTLEN) {
10253         ScriptObj *script;
10254         if (argc != 3) {
10255             Jim_WrongNumArgs(interp, 2, argv, "script");
10256             return JIM_ERR;
10257         }
10258         script = Jim_GetScript(interp, argv[2]);
10259         Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10260         return JIM_OK;
10261     } else if (option == OPT_EXPRLEN) {
10262         ExprByteCode *expr;
10263         if (argc != 3) {
10264             Jim_WrongNumArgs(interp, 2, argv, "expression");
10265             return JIM_ERR;
10266         }
10267         expr = Jim_GetExpression(interp, argv[2]);
10268         if (expr == NULL)
10269             return JIM_ERR;
10270         Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10271         return JIM_OK;
10272     } else if (option == OPT_EXPRBC) {
10273         Jim_Obj *objPtr;
10274         ExprByteCode *expr;
10275         int i;
10276
10277         if (argc != 3) {
10278             Jim_WrongNumArgs(interp, 2, argv, "expression");
10279             return JIM_ERR;
10280         }
10281         expr = Jim_GetExpression(interp, argv[2]);
10282         if (expr == NULL)
10283             return JIM_ERR;
10284         objPtr = Jim_NewListObj(interp, NULL, 0);
10285         for (i = 0; i < expr->len; i++) {
10286             const char *type;
10287             Jim_ExprOperator *op;
10288
10289             switch(expr->opcode[i]) {
10290             case JIM_EXPROP_NUMBER: type = "number"; break;
10291             case JIM_EXPROP_COMMAND: type = "command"; break;
10292             case JIM_EXPROP_VARIABLE: type = "variable"; break;
10293             case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10294             case JIM_EXPROP_SUBST: type = "subst"; break;
10295             case JIM_EXPROP_STRING: type = "string"; break;
10296             default:
10297                 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10298                 if (op == NULL) {
10299                     type = "private";
10300                 } else {
10301                     type = "operator";
10302                 }
10303                 break;
10304             }
10305             Jim_ListAppendElement(interp, objPtr,
10306                     Jim_NewStringObj(interp, type, -1));
10307             Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10308         }
10309         Jim_SetResult(interp, objPtr);
10310         return JIM_OK;
10311     } else {
10312         Jim_SetResultString(interp,
10313             "bad option. Valid options are refcount, "
10314             "objcount, objects, invstr", -1);
10315         return JIM_ERR;
10316     }
10317     return JIM_OK; /* unreached */
10318 }
10319
10320 /* [eval] */
10321 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, 
10322         Jim_Obj *const *argv)
10323 {
10324     if (argc == 2) {
10325         return Jim_EvalObj(interp, argv[1]);
10326     } else if (argc > 2) {
10327         Jim_Obj *objPtr;
10328         int retcode;
10329
10330         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10331         Jim_IncrRefCount(objPtr);
10332         retcode = Jim_EvalObj(interp, objPtr);
10333         Jim_DecrRefCount(interp, objPtr);
10334         return retcode;
10335     } else {
10336         Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10337         return JIM_ERR;
10338     }
10339 }
10340
10341 /* [uplevel] */
10342 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, 
10343         Jim_Obj *const *argv)
10344 {
10345     if (argc >= 2) {
10346         int retcode, newLevel, oldLevel;
10347         Jim_CallFrame *savedCallFrame, *targetCallFrame;
10348         Jim_Obj *objPtr;
10349         const char *str;
10350
10351         /* Save the old callframe pointer */
10352         savedCallFrame = interp->framePtr;
10353
10354         /* Lookup the target frame pointer */
10355         str = Jim_GetString(argv[1], NULL);
10356         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10357         {
10358             if (Jim_GetCallFrameByLevel(interp, argv[1],
10359                         &targetCallFrame,
10360                         &newLevel) != JIM_OK)
10361                 return JIM_ERR;
10362             argc--;
10363             argv++;
10364         } else {
10365             if (Jim_GetCallFrameByLevel(interp, NULL,
10366                         &targetCallFrame,
10367                         &newLevel) != JIM_OK)
10368                 return JIM_ERR;
10369         }
10370         if (argc < 2) {
10371             argc++;
10372             argv--;
10373             Jim_WrongNumArgs(interp, 1, argv,
10374                     "?level? command ?arg ...?");
10375             return JIM_ERR;
10376         }
10377         /* Eval the code in the target callframe. */
10378         interp->framePtr = targetCallFrame;
10379         oldLevel = interp->numLevels;
10380         interp->numLevels = newLevel;
10381         if (argc == 2) {
10382             retcode = Jim_EvalObj(interp, argv[1]);
10383         } else {
10384             objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10385             Jim_IncrRefCount(objPtr);
10386             retcode = Jim_EvalObj(interp, objPtr);
10387             Jim_DecrRefCount(interp, objPtr);
10388         }
10389         interp->numLevels = oldLevel;
10390         interp->framePtr = savedCallFrame;
10391         return retcode;
10392     } else {
10393         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10394         return JIM_ERR;
10395     }
10396 }
10397
10398 /* [expr] */
10399 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, 
10400         Jim_Obj *const *argv)
10401 {
10402     Jim_Obj *exprResultPtr;
10403     int retcode;
10404
10405     if (argc == 2) {
10406         retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10407     } else if (argc > 2) {
10408         Jim_Obj *objPtr;
10409
10410         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10411         Jim_IncrRefCount(objPtr);
10412         retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10413         Jim_DecrRefCount(interp, objPtr);
10414     } else {
10415         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10416         return JIM_ERR;
10417     }
10418     if (retcode != JIM_OK) return retcode;
10419     Jim_SetResult(interp, exprResultPtr);
10420     Jim_DecrRefCount(interp, exprResultPtr);
10421     return JIM_OK;
10422 }
10423
10424 /* [break] */
10425 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, 
10426         Jim_Obj *const *argv)
10427 {
10428     if (argc != 1) {
10429         Jim_WrongNumArgs(interp, 1, argv, "");
10430         return JIM_ERR;
10431     }
10432     return JIM_BREAK;
10433 }
10434
10435 /* [continue] */
10436 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10437         Jim_Obj *const *argv)
10438 {
10439     if (argc != 1) {
10440         Jim_WrongNumArgs(interp, 1, argv, "");
10441         return JIM_ERR;
10442     }
10443     return JIM_CONTINUE;
10444 }
10445
10446 /* [return] */
10447 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, 
10448         Jim_Obj *const *argv)
10449 {
10450     if (argc == 1) {
10451         return JIM_RETURN;
10452     } else if (argc == 2) {
10453         Jim_SetResult(interp, argv[1]);
10454         interp->returnCode = JIM_OK;
10455         return JIM_RETURN;
10456     } else if (argc == 3 || argc == 4) {
10457         int returnCode;
10458         if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10459             return JIM_ERR;
10460         interp->returnCode = returnCode;
10461         if (argc == 4)
10462             Jim_SetResult(interp, argv[3]);
10463         return JIM_RETURN;
10464     } else {
10465         Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10466         return JIM_ERR;
10467     }
10468     return JIM_RETURN; /* unreached */
10469 }
10470
10471 /* [tailcall] */
10472 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10473         Jim_Obj *const *argv)
10474 {
10475     Jim_Obj *objPtr;
10476
10477     objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10478     Jim_SetResult(interp, objPtr);
10479     return JIM_EVAL;
10480 }
10481
10482 /* [proc] */
10483 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, 
10484         Jim_Obj *const *argv)
10485 {
10486     int argListLen;
10487     int arityMin, arityMax;
10488
10489     if (argc != 4 && argc != 5) {
10490         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10491         return JIM_ERR;
10492     }
10493     Jim_ListLength(interp, argv[2], &argListLen);
10494     arityMin = arityMax = argListLen+1;
10495     if (argListLen) {
10496         const char *str;
10497         int len;
10498         Jim_Obj *lastArgPtr;
10499         
10500         Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10501         str = Jim_GetString(lastArgPtr, &len);
10502         if (len == 4 && memcmp(str, "args", 4) == 0) {
10503             arityMin--;
10504             arityMax = -1;
10505         }
10506     }
10507     if (argc == 4) {
10508         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10509                 argv[2], NULL, argv[3], arityMin, arityMax);
10510     } else {
10511         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10512                 argv[2], argv[3], argv[4], arityMin, arityMax);
10513     }
10514 }
10515
10516 /* [concat] */
10517 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, 
10518         Jim_Obj *const *argv)
10519 {
10520     Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10521     return JIM_OK;
10522 }
10523
10524 /* [upvar] */
10525 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, 
10526         Jim_Obj *const *argv)
10527 {
10528     const char *str;
10529     int i;
10530     Jim_CallFrame *targetCallFrame;
10531
10532     /* Lookup the target frame pointer */
10533     str = Jim_GetString(argv[1], NULL);
10534     if (argc > 3 && 
10535         ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10536     {
10537         if (Jim_GetCallFrameByLevel(interp, argv[1],
10538                     &targetCallFrame, NULL) != JIM_OK)
10539             return JIM_ERR;
10540         argc--;
10541         argv++;
10542     } else {
10543         if (Jim_GetCallFrameByLevel(interp, NULL,
10544                     &targetCallFrame, NULL) != JIM_OK)
10545             return JIM_ERR;
10546     }
10547     /* Check for arity */
10548     if (argc < 3 || ((argc-1)%2) != 0) {
10549         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10550         return JIM_ERR;
10551     }
10552     /* Now... for every other/local couple: */
10553     for (i = 1; i < argc; i += 2) {
10554         if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10555                 targetCallFrame) != JIM_OK) return JIM_ERR;
10556     }
10557     return JIM_OK;
10558 }
10559
10560 /* [global] */
10561 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, 
10562         Jim_Obj *const *argv)
10563 {
10564     int i;
10565
10566     if (argc < 2) {
10567         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10568         return JIM_ERR;
10569     }
10570     /* Link every var to the toplevel having the same name */
10571     if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10572     for (i = 1; i < argc; i++) {
10573         if (Jim_SetVariableLink(interp, argv[i], argv[i],
10574                 interp->topFramePtr) != JIM_OK) return JIM_ERR;
10575     }
10576     return JIM_OK;
10577 }
10578
10579 /* does the [string map] operation. On error NULL is returned,
10580  * otherwise a new string object with the result, having refcount = 0,
10581  * is returned. */
10582 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10583         Jim_Obj *objPtr, int nocase)
10584 {
10585     int numMaps;
10586     const char **key, *str, *noMatchStart = NULL;
10587     Jim_Obj **value;
10588     int *keyLen, strLen, i;
10589     Jim_Obj *resultObjPtr;
10590     
10591     Jim_ListLength(interp, mapListObjPtr, &numMaps);
10592     if (numMaps % 2) {
10593         Jim_SetResultString(interp,
10594                 "list must contain an even number of elements", -1);
10595         return NULL;
10596     }
10597     /* Initialization */
10598     numMaps /= 2;
10599     key = Jim_Alloc(sizeof(char*)*numMaps);
10600     keyLen = Jim_Alloc(sizeof(int)*numMaps);
10601     value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10602     resultObjPtr = Jim_NewStringObj(interp, "", 0);
10603     for (i = 0; i < numMaps; i++) {
10604         Jim_Obj *eleObjPtr;
10605
10606         Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10607         key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10608         Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10609         value[i] = eleObjPtr;
10610     }
10611     str = Jim_GetString(objPtr, &strLen);
10612     /* Map it */
10613     while(strLen) {
10614         for (i = 0; i < numMaps; i++) {
10615             if (strLen >= keyLen[i] && keyLen[i]) {
10616                 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10617                             nocase))
10618                 {
10619                     if (noMatchStart) {
10620                         Jim_AppendString(interp, resultObjPtr,
10621                                 noMatchStart, str-noMatchStart);
10622                         noMatchStart = NULL;
10623                     }
10624                     Jim_AppendObj(interp, resultObjPtr, value[i]);
10625                     str += keyLen[i];
10626                     strLen -= keyLen[i];
10627                     break;
10628                 }
10629             }
10630         }
10631         if (i == numMaps) { /* no match */
10632             if (noMatchStart == NULL)
10633                 noMatchStart = str;
10634             str ++;
10635             strLen --;
10636         }
10637     }
10638     if (noMatchStart) {
10639         Jim_AppendString(interp, resultObjPtr,
10640             noMatchStart, str-noMatchStart);
10641     }
10642     Jim_Free((void*)key);
10643     Jim_Free(keyLen);
10644     Jim_Free(value);
10645     return resultObjPtr;
10646 }
10647
10648 /* [string] */
10649 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, 
10650         Jim_Obj *const *argv)
10651 {
10652     int option;
10653     const char *options[] = {
10654         "length", "compare", "match", "equal", "range", "map", "repeat",
10655         "index", "first", "tolower", "toupper", NULL
10656     };
10657     enum {
10658         OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10659         OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10660     };
10661
10662     if (argc < 2) {
10663         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10664         return JIM_ERR;
10665     }
10666     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10667                 JIM_ERRMSG) != JIM_OK)
10668         return JIM_ERR;
10669
10670     if (option == OPT_LENGTH) {
10671         int len;
10672
10673         if (argc != 3) {
10674             Jim_WrongNumArgs(interp, 2, argv, "string");
10675             return JIM_ERR;
10676         }
10677         Jim_GetString(argv[2], &len);
10678         Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10679         return JIM_OK;
10680     } else if (option == OPT_COMPARE) {
10681         int nocase = 0;
10682         if ((argc != 4 && argc != 5) ||
10683             (argc == 5 && Jim_CompareStringImmediate(interp,
10684                 argv[2], "-nocase") == 0)) {
10685             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10686             return JIM_ERR;
10687         }
10688         if (argc == 5) {
10689             nocase = 1;
10690             argv++;
10691         }
10692         Jim_SetResult(interp, Jim_NewIntObj(interp,
10693                     Jim_StringCompareObj(argv[2],
10694                             argv[3], nocase)));
10695         return JIM_OK;
10696     } else if (option == OPT_MATCH) {
10697         int nocase = 0;
10698         if ((argc != 4 && argc != 5) ||
10699             (argc == 5 && Jim_CompareStringImmediate(interp,
10700                 argv[2], "-nocase") == 0)) {
10701             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10702                     "string");
10703             return JIM_ERR;
10704         }
10705         if (argc == 5) {
10706             nocase = 1;
10707             argv++;
10708         }
10709         Jim_SetResult(interp,
10710             Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10711                     argv[3], nocase)));
10712         return JIM_OK;
10713     } else if (option == OPT_EQUAL) {
10714         if (argc != 4) {
10715             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10716             return JIM_ERR;
10717         }
10718         Jim_SetResult(interp,
10719             Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10720                     argv[3], 0)));
10721         return JIM_OK;
10722     } else if (option == OPT_RANGE) {
10723         Jim_Obj *objPtr;
10724
10725         if (argc != 5) {
10726             Jim_WrongNumArgs(interp, 2, argv, "string first last");
10727             return JIM_ERR;
10728         }
10729         objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10730         if (objPtr == NULL)
10731             return JIM_ERR;
10732         Jim_SetResult(interp, objPtr);
10733         return JIM_OK;
10734     } else if (option == OPT_MAP) {
10735         int nocase = 0;
10736         Jim_Obj *objPtr;
10737
10738         if ((argc != 4 && argc != 5) ||
10739             (argc == 5 && Jim_CompareStringImmediate(interp,
10740                 argv[2], "-nocase") == 0)) {
10741             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10742                     "string");
10743             return JIM_ERR;
10744         }
10745         if (argc == 5) {
10746             nocase = 1;
10747             argv++;
10748         }
10749         objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10750         if (objPtr == NULL)
10751             return JIM_ERR;
10752         Jim_SetResult(interp, objPtr);
10753         return JIM_OK;
10754     } else if (option == OPT_REPEAT) {
10755         Jim_Obj *objPtr;
10756         jim_wide count;
10757
10758         if (argc != 4) {
10759             Jim_WrongNumArgs(interp, 2, argv, "string count");
10760             return JIM_ERR;
10761         }
10762         if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
10763             return JIM_ERR;
10764         objPtr = Jim_NewStringObj(interp, "", 0);
10765         while (count--) {
10766             Jim_AppendObj(interp, objPtr, argv[2]);
10767         }
10768         Jim_SetResult(interp, objPtr);
10769         return JIM_OK;
10770     } else if (option == OPT_INDEX) {
10771         int index, len;
10772         const char *str;
10773
10774         if (argc != 4) {
10775             Jim_WrongNumArgs(interp, 2, argv, "string index");
10776             return JIM_ERR;
10777         }
10778         if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
10779             return JIM_ERR;
10780         str = Jim_GetString(argv[2], &len);
10781         if (index != INT_MIN && index != INT_MAX)
10782             index = JimRelToAbsIndex(len, index);
10783         if (index < 0 || index >= len) {
10784             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10785             return JIM_OK;
10786         } else {
10787             Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
10788             return JIM_OK;
10789         }
10790     } else if (option == OPT_FIRST) {
10791         int index = 0, l1, l2;
10792         const char *s1, *s2;
10793
10794         if (argc != 4 && argc != 5) {
10795             Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
10796             return JIM_ERR;
10797         }
10798         s1 = Jim_GetString(argv[2], &l1);
10799         s2 = Jim_GetString(argv[3], &l2);
10800         if (argc == 5) {
10801             if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
10802                 return JIM_ERR;
10803             index = JimRelToAbsIndex(l2, index);
10804         }
10805         Jim_SetResult(interp, Jim_NewIntObj(interp,
10806                     JimStringFirst(s1, l1, s2, l2, index)));
10807         return JIM_OK;
10808     } else if (option == OPT_TOLOWER) {
10809         if (argc != 3) {
10810             Jim_WrongNumArgs(interp, 2, argv, "string");
10811             return JIM_ERR;
10812         }
10813         Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
10814     } else if (option == OPT_TOUPPER) {
10815         if (argc != 3) {
10816             Jim_WrongNumArgs(interp, 2, argv, "string");
10817             return JIM_ERR;
10818         }
10819         Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
10820     }
10821     return JIM_OK;
10822 }
10823
10824 /* [time] */
10825 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, 
10826         Jim_Obj *const *argv)
10827 {
10828     long i, count = 1;
10829     jim_wide start, elapsed;
10830     char buf [256];
10831     const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
10832
10833     if (argc < 2) {
10834         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
10835         return JIM_ERR;
10836     }
10837     if (argc == 3) {
10838         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
10839             return JIM_ERR;
10840     }
10841     if (count < 0)
10842         return JIM_OK;
10843     i = count;
10844     start = JimClock();
10845     while (i-- > 0) {
10846         int retval;
10847
10848         if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10849             return retval;
10850     }
10851     elapsed = JimClock() - start;
10852     sprintf(buf, fmt, elapsed/count);
10853     Jim_SetResultString(interp, buf, -1);
10854     return JIM_OK;
10855 }
10856
10857 /* [exit] */
10858 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, 
10859         Jim_Obj *const *argv)
10860 {
10861     long exitCode = 0;
10862
10863     if (argc > 2) {
10864         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
10865         return JIM_ERR;
10866     }
10867     if (argc == 2) {
10868         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
10869             return JIM_ERR;
10870     }
10871     interp->exitCode = exitCode;
10872     return JIM_EXIT;
10873 }
10874
10875 /* [catch] */
10876 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, 
10877         Jim_Obj *const *argv)
10878 {
10879     int exitCode = 0;
10880
10881     if (argc != 2 && argc != 3) {
10882         Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
10883         return JIM_ERR;
10884     }
10885     exitCode = Jim_EvalObj(interp, argv[1]);
10886     if (argc == 3) {
10887         if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
10888                 != JIM_OK)
10889             return JIM_ERR;
10890     }
10891     Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
10892     return JIM_OK;
10893 }
10894
10895 /* [ref] */
10896 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, 
10897         Jim_Obj *const *argv)
10898 {
10899     if (argc != 3 && argc != 4) {
10900         Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
10901         return JIM_ERR;
10902     }
10903     if (argc == 3) {
10904         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
10905     } else {
10906         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
10907                     argv[3]));
10908     }
10909     return JIM_OK;
10910 }
10911
10912 /* [getref] */
10913 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, 
10914         Jim_Obj *const *argv)
10915 {
10916     Jim_Reference *refPtr;
10917
10918     if (argc != 2) {
10919         Jim_WrongNumArgs(interp, 1, argv, "reference");
10920         return JIM_ERR;
10921     }
10922     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10923         return JIM_ERR;
10924     Jim_SetResult(interp, refPtr->objPtr);
10925     return JIM_OK;
10926 }
10927
10928 /* [setref] */
10929 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, 
10930         Jim_Obj *const *argv)
10931 {
10932     Jim_Reference *refPtr;
10933
10934     if (argc != 3) {
10935         Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
10936         return JIM_ERR;
10937     }
10938     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10939         return JIM_ERR;
10940     Jim_IncrRefCount(argv[2]);
10941     Jim_DecrRefCount(interp, refPtr->objPtr);
10942     refPtr->objPtr = argv[2];
10943     Jim_SetResult(interp, argv[2]);
10944     return JIM_OK;
10945 }
10946
10947 /* [collect] */
10948 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, 
10949         Jim_Obj *const *argv)
10950 {
10951     if (argc != 1) {
10952         Jim_WrongNumArgs(interp, 1, argv, "");
10953         return JIM_ERR;
10954     }
10955     Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
10956     return JIM_OK;
10957 }
10958
10959 /* [finalize] reference ?newValue? */
10960 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, 
10961         Jim_Obj *const *argv)
10962 {
10963     if (argc != 2 && argc != 3) {
10964         Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
10965         return JIM_ERR;
10966     }
10967     if (argc == 2) {
10968         Jim_Obj *cmdNamePtr;
10969
10970         if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
10971             return JIM_ERR;
10972         if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
10973             Jim_SetResult(interp, cmdNamePtr);
10974     } else {
10975         if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
10976             return JIM_ERR;
10977         Jim_SetResult(interp, argv[2]);
10978     }
10979     return JIM_OK;
10980 }
10981
10982 /* TODO */
10983 /* [info references] (list of all the references/finalizers) */
10984
10985 /* [rename] */
10986 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, 
10987         Jim_Obj *const *argv)
10988 {
10989     const char *oldName, *newName;
10990
10991     if (argc != 3) {
10992         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
10993         return JIM_ERR;
10994     }
10995     oldName = Jim_GetString(argv[1], NULL);
10996     newName = Jim_GetString(argv[2], NULL);
10997     if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
10998         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10999         Jim_AppendStrings(interp, Jim_GetResult(interp),
11000             "can't rename \"", oldName, "\": ",
11001             "command doesn't exist", NULL);
11002         return JIM_ERR;
11003     }
11004     return JIM_OK;
11005 }
11006
11007 /* [dict] */
11008 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, 
11009         Jim_Obj *const *argv)
11010 {
11011     int option;
11012     const char *options[] = {
11013         "create", "get", "set", "unset", "exists", NULL
11014     };
11015     enum {
11016         OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11017     };
11018
11019     if (argc < 2) {
11020         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11021         return JIM_ERR;
11022     }
11023
11024     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11025                 JIM_ERRMSG) != JIM_OK)
11026         return JIM_ERR;
11027
11028     if (option == OPT_CREATE) {
11029         Jim_Obj *objPtr;
11030
11031         if (argc % 2) {
11032             Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11033             return JIM_ERR;
11034         }
11035         objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11036         Jim_SetResult(interp, objPtr);
11037         return JIM_OK;
11038     } else if (option == OPT_GET) {
11039         Jim_Obj *objPtr;
11040
11041         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11042                 JIM_ERRMSG) != JIM_OK)
11043             return JIM_ERR;
11044         Jim_SetResult(interp, objPtr);
11045         return JIM_OK;
11046     } else if (option == OPT_SET) {
11047         if (argc < 5) {
11048             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11049             return JIM_ERR;
11050         }
11051         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11052                     argv[argc-1]);
11053     } else if (option == OPT_UNSET) {
11054         if (argc < 4) {
11055             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11056             return JIM_ERR;
11057         }
11058         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11059                     NULL);
11060     } else if (option == OPT_EXIST) {
11061         Jim_Obj *objPtr;
11062         int exists;
11063
11064         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11065                 JIM_ERRMSG) == JIM_OK)
11066             exists = 1;
11067         else
11068             exists = 0;
11069         Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11070         return JIM_OK;
11071     } else {
11072         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11073         Jim_AppendStrings(interp, Jim_GetResult(interp),
11074             "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11075             " must be create, get, set", NULL);
11076         return JIM_ERR;
11077     }
11078     return JIM_OK;
11079 }
11080
11081 /* [load] */
11082 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc, 
11083         Jim_Obj *const *argv)
11084 {
11085     if (argc < 2) {
11086         Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11087         return JIM_ERR;
11088     }
11089     return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11090 }
11091
11092 /* [subst] */
11093 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, 
11094         Jim_Obj *const *argv)
11095 {
11096     int i, flags = 0;
11097     Jim_Obj *objPtr;
11098
11099     if (argc < 2) {
11100         Jim_WrongNumArgs(interp, 1, argv,
11101             "?-nobackslashes? ?-nocommands? ?-novariables? string");
11102         return JIM_ERR;
11103     }
11104     i = argc-2;
11105     while(i--) {
11106         if (Jim_CompareStringImmediate(interp, argv[i+1],
11107                     "-nobackslashes"))
11108             flags |= JIM_SUBST_NOESC;
11109         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11110                     "-novariables"))
11111             flags |= JIM_SUBST_NOVAR;
11112         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11113                     "-nocommands"))
11114             flags |= JIM_SUBST_NOCMD;
11115         else {
11116             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11117             Jim_AppendStrings(interp, Jim_GetResult(interp),
11118                 "bad option \"", Jim_GetString(argv[i+1], NULL),
11119                 "\": must be -nobackslashes, -nocommands, or "
11120                 "-novariables", NULL);
11121             return JIM_ERR;
11122         }
11123     }
11124     if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11125         return JIM_ERR;
11126     Jim_SetResult(interp, objPtr);
11127     return JIM_OK;
11128 }
11129
11130 /* [info] */
11131 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, 
11132         Jim_Obj *const *argv)
11133 {
11134     int cmd, result = JIM_OK;
11135     static const char *commands[] = {
11136         "body", "commands", "exists", "globals", "level", "locals",
11137         "vars", "version", "complete", "args", NULL
11138     };
11139     enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11140           INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11141     
11142     if (argc < 2) {
11143         Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11144         return JIM_ERR;
11145     }
11146     if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11147         != JIM_OK) {
11148         return JIM_ERR;
11149     }
11150     
11151     if (cmd == INFO_COMMANDS) {
11152         if (argc != 2 && argc != 3) {
11153             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11154             return JIM_ERR;
11155         }
11156         if (argc == 3)
11157             Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11158         else
11159             Jim_SetResult(interp, JimCommandsList(interp, NULL));
11160     } else if (cmd == INFO_EXISTS) {
11161         Jim_Obj *exists;
11162         if (argc != 3) {
11163             Jim_WrongNumArgs(interp, 2, argv, "varName");
11164             return JIM_ERR;
11165         }
11166         exists = Jim_GetVariable(interp, argv[2], 0);
11167         Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11168     } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11169         int mode;
11170         switch (cmd) {
11171             case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11172             case INFO_LOCALS:  mode = JIM_VARLIST_LOCALS; break;
11173             case INFO_VARS:    mode = JIM_VARLIST_VARS; break;
11174             default: mode = 0; /* avoid warning */; break;
11175         }
11176         if (argc != 2 && argc != 3) {
11177             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11178             return JIM_ERR;
11179         }
11180         if (argc == 3)
11181             Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11182         else
11183             Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11184     } else if (cmd == INFO_LEVEL) {
11185         Jim_Obj *objPtr;
11186         switch (argc) {
11187             case 2:
11188                 Jim_SetResult(interp,
11189                               Jim_NewIntObj(interp, interp->numLevels));
11190                 break;
11191             case 3:
11192                 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11193                     return JIM_ERR;
11194                 Jim_SetResult(interp, objPtr);
11195                 break;
11196             default:
11197                 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11198                 return JIM_ERR;
11199         }
11200     } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11201         Jim_Cmd *cmdPtr;
11202
11203         if (argc != 3) {
11204             Jim_WrongNumArgs(interp, 2, argv, "procname");
11205             return JIM_ERR;
11206         }
11207         if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11208             return JIM_ERR;
11209         if (cmdPtr->cmdProc != NULL) {
11210             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11211             Jim_AppendStrings(interp, Jim_GetResult(interp),
11212                 "command \"", Jim_GetString(argv[2], NULL),
11213                 "\" is not a procedure", NULL);
11214             return JIM_ERR;
11215         }
11216         if (cmd == INFO_BODY)
11217             Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11218         else
11219             Jim_SetResult(interp, cmdPtr->argListObjPtr);
11220     } else if (cmd == INFO_VERSION) {
11221         char buf[(JIM_INTEGER_SPACE * 2) + 1];
11222         sprintf(buf, "%d.%d", 
11223                 JIM_VERSION / 100, JIM_VERSION % 100);
11224         Jim_SetResultString(interp, buf, -1);
11225     } else if (cmd == INFO_COMPLETE) {
11226         const char *s;
11227         int len;
11228
11229         if (argc != 3) {
11230             Jim_WrongNumArgs(interp, 2, argv, "script");
11231             return JIM_ERR;
11232         }
11233         s = Jim_GetString(argv[2], &len);
11234         Jim_SetResult(interp,
11235                 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11236     }
11237     return result;
11238 }
11239
11240 /* [split] */
11241 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, 
11242         Jim_Obj *const *argv)
11243 {
11244     const char *str, *splitChars, *noMatchStart;
11245     int splitLen, strLen, i;
11246     Jim_Obj *resObjPtr;
11247
11248     if (argc != 2 && argc != 3) {
11249         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11250         return JIM_ERR;
11251     }
11252     /* Init */
11253     if (argc == 2) {
11254         splitChars = " \n\t\r";
11255         splitLen = 4;
11256     } else {
11257         splitChars = Jim_GetString(argv[2], &splitLen);
11258     }
11259     str = Jim_GetString(argv[1], &strLen);
11260     if (!strLen) return JIM_OK;
11261     noMatchStart = str;
11262     resObjPtr = Jim_NewListObj(interp, NULL, 0);
11263     /* Split */
11264     if (splitLen) {
11265         while (strLen) {
11266             for (i = 0; i < splitLen; i++) {
11267                 if (*str == splitChars[i]) {
11268                     Jim_Obj *objPtr;
11269
11270                     objPtr = Jim_NewStringObj(interp, noMatchStart,
11271                             (str-noMatchStart));
11272                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
11273                     noMatchStart = str+1;
11274                     break;
11275                 }
11276             }
11277             str ++;
11278             strLen --;
11279         }
11280         Jim_ListAppendElement(interp, resObjPtr,
11281                 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11282     } else {
11283         /* This handles the special case of splitchars eq {}. This
11284          * is trivial but we want to perform object sharing as Tcl does. */
11285         Jim_Obj *objCache[256];
11286         const unsigned char *u = (unsigned char*) str;
11287         memset(objCache, 0, sizeof(objCache));
11288         for (i = 0; i < strLen; i++) {
11289             int c = u[i];
11290             
11291             if (objCache[c] == NULL)
11292                 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11293             Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11294         }
11295     }
11296     Jim_SetResult(interp, resObjPtr);
11297     return JIM_OK;
11298 }
11299
11300 /* [join] */
11301 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, 
11302         Jim_Obj *const *argv)
11303 {
11304     const char *joinStr;
11305     int joinStrLen, i, listLen;
11306     Jim_Obj *resObjPtr;
11307
11308     if (argc != 2 && argc != 3) {
11309         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11310         return JIM_ERR;
11311     }
11312     /* Init */
11313     if (argc == 2) {
11314         joinStr = " ";
11315         joinStrLen = 1;
11316     } else {
11317         joinStr = Jim_GetString(argv[2], &joinStrLen);
11318     }
11319     Jim_ListLength(interp, argv[1], &listLen);
11320     resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11321     /* Split */
11322     for (i = 0; i < listLen; i++) {
11323         Jim_Obj *objPtr;
11324
11325         Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11326         Jim_AppendObj(interp, resObjPtr, objPtr);
11327         if (i+1 != listLen) {
11328             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11329         }
11330     }
11331     Jim_SetResult(interp, resObjPtr);
11332     return JIM_OK;
11333 }
11334
11335 /* [format] */
11336 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11337         Jim_Obj *const *argv)
11338 {
11339     Jim_Obj *objPtr;
11340
11341     if (argc < 2) {
11342         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11343         return JIM_ERR;
11344     }
11345     objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11346     if (objPtr == NULL)
11347         return JIM_ERR;
11348     Jim_SetResult(interp, objPtr);
11349     return JIM_OK;
11350 }
11351
11352 /* [scan] */
11353 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11354         Jim_Obj *const *argv)
11355 {
11356     Jim_Obj *listPtr, **outVec;
11357     int outc, i, count = 0;
11358
11359     if (argc < 3) {
11360         Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11361         return JIM_ERR;
11362     } 
11363     if (argv[2]->typePtr != &scanFmtStringObjType)
11364         SetScanFmtFromAny(interp, argv[2]);
11365     if (FormatGetError(argv[2]) != 0) {
11366         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11367         return JIM_ERR;
11368     }
11369     if (argc > 3) {
11370         int maxPos = FormatGetMaxPos(argv[2]);
11371         int count = FormatGetCnvCount(argv[2]);
11372         if (maxPos > argc-3) {
11373             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11374             return JIM_ERR;
11375         } else if (count != 0 && count < argc-3) {
11376             Jim_SetResultString(interp, "variable is not assigned by any "
11377                 "conversion specifiers", -1);
11378             return JIM_ERR;
11379         } else if (count > argc-3) {
11380             Jim_SetResultString(interp, "different numbers of variable names and "
11381                 "field specifiers", -1);
11382             return JIM_ERR;
11383         }
11384     } 
11385     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11386     if (listPtr == 0)
11387         return JIM_ERR;
11388     if (argc > 3) {
11389         int len = 0;
11390         if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11391             Jim_ListLength(interp, listPtr, &len);
11392         if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11393             Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11394             return JIM_OK;
11395         }
11396         JimListGetElements(interp, listPtr, &outc, &outVec);
11397         for (i = 0; i < outc; ++i) {
11398             if (Jim_Length(outVec[i]) > 0) {
11399                 ++count;
11400                 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11401                     goto err;
11402             }
11403         }
11404         Jim_FreeNewObj(interp, listPtr);
11405         Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11406     } else {
11407         if (listPtr == (Jim_Obj*)EOF) {
11408             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11409             return JIM_OK;
11410         }
11411         Jim_SetResult(interp, listPtr);
11412     }
11413     return JIM_OK;
11414 err:
11415     Jim_FreeNewObj(interp, listPtr);
11416     return JIM_ERR;
11417 }
11418
11419 /* [error] */
11420 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11421         Jim_Obj *const *argv)
11422 {
11423     if (argc != 2) {
11424         Jim_WrongNumArgs(interp, 1, argv, "message");
11425         return JIM_ERR;
11426     }
11427     Jim_SetResult(interp, argv[1]);
11428     return JIM_ERR;
11429 }
11430
11431 /* [lrange] */
11432 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11433         Jim_Obj *const *argv)
11434 {
11435     Jim_Obj *objPtr;
11436
11437     if (argc != 4) {
11438         Jim_WrongNumArgs(interp, 1, argv, "list first last");
11439         return JIM_ERR;
11440     }
11441     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11442         return JIM_ERR;
11443     Jim_SetResult(interp, objPtr);
11444     return JIM_OK;
11445 }
11446
11447 /* [env] */
11448 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11449         Jim_Obj *const *argv)
11450 {
11451     const char *key;
11452     char *val;
11453
11454     if (argc != 2) {
11455         Jim_WrongNumArgs(interp, 1, argv, "varName");
11456         return JIM_ERR;
11457     }
11458     key = Jim_GetString(argv[1], NULL);
11459     val = getenv(key);
11460     if (val == NULL) {
11461         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11462         Jim_AppendStrings(interp, Jim_GetResult(interp),
11463                 "environment variable \"",
11464                 key, "\" does not exist", NULL);
11465         return JIM_ERR;
11466     }
11467     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11468     return JIM_OK;
11469 }
11470
11471 /* [source] */
11472 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11473         Jim_Obj *const *argv)
11474 {
11475     int retval;
11476
11477     if (argc != 2) {
11478         Jim_WrongNumArgs(interp, 1, argv, "fileName");
11479         return JIM_ERR;
11480     }
11481     retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11482     if (retval == JIM_RETURN)
11483         return JIM_OK;
11484     return retval;
11485 }
11486
11487 /* [lreverse] */
11488 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11489         Jim_Obj *const *argv)
11490 {
11491     Jim_Obj *revObjPtr, **ele;
11492     int len;
11493
11494     if (argc != 2) {
11495         Jim_WrongNumArgs(interp, 1, argv, "list");
11496         return JIM_ERR;
11497     }
11498     JimListGetElements(interp, argv[1], &len, &ele);
11499     len--;
11500     revObjPtr = Jim_NewListObj(interp, NULL, 0);
11501     while (len >= 0)
11502         ListAppendElement(revObjPtr, ele[len--]);
11503     Jim_SetResult(interp, revObjPtr);
11504     return JIM_OK;
11505 }
11506
11507 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11508 {
11509     jim_wide len;
11510
11511     if (step == 0) return -1;
11512     if (start == end) return 0;
11513     else if (step > 0 && start > end) return -1;
11514     else if (step < 0 && end > start) return -1;
11515     len = end-start;
11516     if (len < 0) len = -len; /* abs(len) */
11517     if (step < 0) step = -step; /* abs(step) */
11518     len = 1 + ((len-1)/step);
11519     /* We can truncate safely to INT_MAX, the range command
11520      * will always return an error for a such long range
11521      * because Tcl lists can't be so long. */
11522     if (len > INT_MAX) len = INT_MAX;
11523     return (int)((len < 0) ? -1 : len);
11524 }
11525
11526 /* [range] */
11527 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11528         Jim_Obj *const *argv)
11529 {
11530     jim_wide start = 0, end, step = 1;
11531     int len, i;
11532     Jim_Obj *objPtr;
11533
11534     if (argc < 2 || argc > 4) {
11535         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11536         return JIM_ERR;
11537     }
11538     if (argc == 2) {
11539         if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11540             return JIM_ERR;
11541     } else {
11542         if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11543             Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11544             return JIM_ERR;
11545         if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11546             return JIM_ERR;
11547     }
11548     if ((len = JimRangeLen(start, end, step)) == -1) {
11549         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11550         return JIM_ERR;
11551     }
11552     objPtr = Jim_NewListObj(interp, NULL, 0);
11553     for (i = 0; i < len; i++)
11554         ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11555     Jim_SetResult(interp, objPtr);
11556     return JIM_OK;
11557 }
11558
11559 /* [rand] */
11560 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11561         Jim_Obj *const *argv)
11562 {
11563     jim_wide min = 0, max, len, maxMul;
11564
11565     if (argc < 1 || argc > 3) {
11566         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11567         return JIM_ERR;
11568     }
11569     if (argc == 1) {
11570         max = JIM_WIDE_MAX;
11571     } else if (argc == 2) {
11572         if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11573             return JIM_ERR;
11574     } else if (argc == 3) {
11575         if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11576             Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11577             return JIM_ERR;
11578     }
11579     len = max-min;
11580     if (len < 0) {
11581         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11582         return JIM_ERR;
11583     }
11584     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11585     while (1) {
11586         jim_wide r;
11587
11588         JimRandomBytes(interp, &r, sizeof(jim_wide));
11589         if (r < 0 || r >= maxMul) continue;
11590         r = (len == 0) ? 0 : r%len;
11591         Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11592         return JIM_OK;
11593     }
11594 }
11595
11596 /* [package] */
11597 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc, 
11598         Jim_Obj *const *argv)
11599 {
11600     int option;
11601     const char *options[] = {
11602         "require", "provide", NULL
11603     };
11604     enum {OPT_REQUIRE, OPT_PROVIDE};
11605
11606     if (argc < 2) {
11607         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11608         return JIM_ERR;
11609     }
11610     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11611                 JIM_ERRMSG) != JIM_OK)
11612         return JIM_ERR;
11613
11614     if (option == OPT_REQUIRE) {
11615         int exact = 0;
11616         const char *ver;
11617
11618         if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11619             exact = 1;
11620             argv++;
11621             argc--;
11622         }
11623         if (argc != 3 && argc != 4) {
11624             Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11625             return JIM_ERR;
11626         }
11627         ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11628                 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11629                 JIM_ERRMSG);
11630         if (ver == NULL)
11631             return JIM_ERR;
11632         Jim_SetResultString(interp, ver, -1);
11633     } else if (option == OPT_PROVIDE) {
11634         if (argc != 4) {
11635             Jim_WrongNumArgs(interp, 2, argv, "package version");
11636             return JIM_ERR;
11637         }
11638         return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11639                     Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11640     }
11641     return JIM_OK;
11642 }
11643
11644 static struct {
11645     const char *name;
11646     Jim_CmdProc cmdProc;
11647 } Jim_CoreCommandsTable[] = {
11648     {"set", Jim_SetCoreCommand},
11649     {"unset", Jim_UnsetCoreCommand},
11650     {"puts", Jim_PutsCoreCommand},
11651     {"+", Jim_AddCoreCommand},
11652     {"*", Jim_MulCoreCommand},
11653     {"-", Jim_SubCoreCommand},
11654     {"/", Jim_DivCoreCommand},
11655     {"incr", Jim_IncrCoreCommand},
11656     {"while", Jim_WhileCoreCommand},
11657     {"for", Jim_ForCoreCommand},
11658     {"foreach", Jim_ForeachCoreCommand},
11659     {"lmap", Jim_LmapCoreCommand},
11660     {"if", Jim_IfCoreCommand},
11661     {"switch", Jim_SwitchCoreCommand},
11662     {"list", Jim_ListCoreCommand},
11663     {"lindex", Jim_LindexCoreCommand},
11664     {"lset", Jim_LsetCoreCommand},
11665     {"llength", Jim_LlengthCoreCommand},
11666     {"lappend", Jim_LappendCoreCommand},
11667     {"linsert", Jim_LinsertCoreCommand},
11668     {"lsort", Jim_LsortCoreCommand},
11669     {"append", Jim_AppendCoreCommand},
11670     {"debug", Jim_DebugCoreCommand},
11671     {"eval", Jim_EvalCoreCommand},
11672     {"uplevel", Jim_UplevelCoreCommand},
11673     {"expr", Jim_ExprCoreCommand},
11674     {"break", Jim_BreakCoreCommand},
11675     {"continue", Jim_ContinueCoreCommand},
11676     {"proc", Jim_ProcCoreCommand},
11677     {"concat", Jim_ConcatCoreCommand},
11678     {"return", Jim_ReturnCoreCommand},
11679     {"upvar", Jim_UpvarCoreCommand},
11680     {"global", Jim_GlobalCoreCommand},
11681     {"string", Jim_StringCoreCommand},
11682     {"time", Jim_TimeCoreCommand},
11683     {"exit", Jim_ExitCoreCommand},
11684     {"catch", Jim_CatchCoreCommand},
11685     {"ref", Jim_RefCoreCommand},
11686     {"getref", Jim_GetrefCoreCommand},
11687     {"setref", Jim_SetrefCoreCommand},
11688     {"finalize", Jim_FinalizeCoreCommand},
11689     {"collect", Jim_CollectCoreCommand},
11690     {"rename", Jim_RenameCoreCommand},
11691     {"dict", Jim_DictCoreCommand},
11692     {"load", Jim_LoadCoreCommand},
11693     {"subst", Jim_SubstCoreCommand},
11694     {"info", Jim_InfoCoreCommand},
11695     {"split", Jim_SplitCoreCommand},
11696     {"join", Jim_JoinCoreCommand},
11697     {"format", Jim_FormatCoreCommand},
11698     {"scan", Jim_ScanCoreCommand},
11699     {"error", Jim_ErrorCoreCommand},
11700     {"lrange", Jim_LrangeCoreCommand},
11701     {"env", Jim_EnvCoreCommand},
11702     {"source", Jim_SourceCoreCommand},
11703     {"lreverse", Jim_LreverseCoreCommand},
11704     {"range", Jim_RangeCoreCommand},
11705     {"rand", Jim_RandCoreCommand},
11706     {"package", Jim_PackageCoreCommand},
11707     {"tailcall", Jim_TailcallCoreCommand},
11708     {NULL, NULL},
11709 };
11710
11711 /* Some Jim core command is actually a procedure written in Jim itself. */
11712 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11713 {
11714     Jim_Eval(interp, (char*)
11715 "proc lambda {arglist args} {\n"
11716 "    set name [ref {} function lambdaFinalizer]\n"
11717 "    uplevel 1 [list proc $name $arglist {expand}$args]\n"
11718 "    return $name\n"
11719 "}\n"
11720 "proc lambdaFinalizer {name val} {\n"
11721 "    rename $name {}\n"
11722 "}\n"
11723     );
11724 }
11725
11726 void Jim_RegisterCoreCommands(Jim_Interp *interp)
11727 {
11728     int i = 0;
11729
11730     while(Jim_CoreCommandsTable[i].name != NULL) {
11731         Jim_CreateCommand(interp, 
11732                 Jim_CoreCommandsTable[i].name,
11733                 Jim_CoreCommandsTable[i].cmdProc,
11734                 NULL, NULL);
11735         i++;
11736     }
11737     Jim_RegisterCoreProcedures(interp);
11738 }
11739
11740 /* -----------------------------------------------------------------------------
11741  * Interactive prompt
11742  * ---------------------------------------------------------------------------*/
11743 void Jim_PrintErrorMessage(Jim_Interp *interp)
11744 {
11745     int len, i;
11746
11747     fprintf(interp->stderr_, "Runtime error, file \"%s\", line %d:" JIM_NL,
11748             interp->errorFileName, interp->errorLine);
11749     fprintf(interp->stderr_, "    %s" JIM_NL,
11750             Jim_GetString(interp->result, NULL));
11751     Jim_ListLength(interp, interp->stackTrace, &len);
11752     for (i = 0; i < len; i+= 3) {
11753         Jim_Obj *objPtr;
11754         const char *proc, *file, *line;
11755
11756         Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
11757         proc = Jim_GetString(objPtr, NULL);
11758         Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
11759                 JIM_NONE);
11760         file = Jim_GetString(objPtr, NULL);
11761         Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
11762                 JIM_NONE);
11763         line = Jim_GetString(objPtr, NULL);
11764         fprintf(interp->stderr_,
11765                 "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
11766                 proc, file, line);
11767     }
11768 }
11769
11770 int Jim_InteractivePrompt(Jim_Interp *interp)
11771 {
11772     int retcode = JIM_OK;
11773     Jim_Obj *scriptObjPtr;
11774
11775     fprintf(interp->stdout_, "Welcome to Jim version %d.%d, "
11776            "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
11777            JIM_VERSION / 100, JIM_VERSION % 100);
11778      Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
11779     while (1) {
11780         char buf[1024];
11781         const char *result;
11782         const char *retcodestr[] = {
11783             "ok", "error", "return", "break", "continue", "eval", "exit"
11784         };
11785         int reslen;
11786
11787         if (retcode != 0) {
11788             if (retcode >= 2 && retcode <= 6)
11789                 fprintf(interp->stdout_, "[%s] . ", retcodestr[retcode]);
11790             else
11791                 fprintf(interp->stdout_, "[%d] . ", retcode);
11792         } else
11793             fprintf(interp->stdout_, ". ");
11794         fflush(interp->stdout_);
11795         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
11796         Jim_IncrRefCount(scriptObjPtr);
11797         while(1) {
11798             const char *str;
11799             char state;
11800             int len;
11801
11802             if (fgets(buf, 1024, interp->stdin_) == NULL) {
11803                 Jim_DecrRefCount(interp, scriptObjPtr);
11804                 goto out;
11805             }
11806             Jim_AppendString(interp, scriptObjPtr, buf, -1);
11807             str = Jim_GetString(scriptObjPtr, &len);
11808             if (Jim_ScriptIsComplete(str, len, &state))
11809                 break;
11810             fprintf(interp->stdout_, "%c> ", state);
11811             fflush(stdout);
11812         }
11813         retcode = Jim_EvalObj(interp, scriptObjPtr);
11814         Jim_DecrRefCount(interp, scriptObjPtr);
11815         result = Jim_GetString(Jim_GetResult(interp), &reslen);
11816         if (retcode == JIM_ERR) {
11817             Jim_PrintErrorMessage(interp);
11818         } else if (retcode == JIM_EXIT) {
11819             exit(Jim_GetExitCode(interp));
11820         } else {
11821             if (reslen) {
11822                 fwrite(result, 1, reslen, interp->stdout_);
11823                 fprintf(interp->stdout_, JIM_NL);
11824             }
11825         }
11826     }
11827 out:
11828     return 0;
11829 }