]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/athttpd/v2_0/src/jim.c
Cleanup CVS ipmorted branch
[karo-tx-redboot.git] / packages / net / athttpd / v2_0 / 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  * $Id: jim.c,v 1.1.1.1 2008/07/31 20:44:21 mmahesh Exp $
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * A copy of the license is also included in the source distribution
14  * of Jim, as a TXT file name called LICENSE.
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22
23 #define __JIM_CORE__
24 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
25
26 #include <pkgconf/athttpd.h>
27
28 #ifndef JIM_ANSIC
29 #define JIM_DYNLIB      /* Dynamic library support for UNIX and WIN32 */
30 #endif /* JIM_ANSIC */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <ctype.h>
37 #include <limits.h>
38 #include <assert.h>
39 #include <errno.h>
40 #include <time.h>
41
42 /* Include the platform dependent libraries for
43  * dynamic loading of libraries. */
44 #ifdef JIM_DYNLIB
45 #if defined(_WIN32) || defined(WIN32)
46 #ifndef WIN32
47 #define WIN32 1
48 #endif
49 #define STRICT
50 #define WIN32_LEAN_AND_MEAN
51 #include <windows.h>
52 #if _MSC_VER >= 1000
53 #pragma warning(disable:4146)
54 #endif /* _MSC_VER */
55 #else
56 #include <dlfcn.h>
57 #endif /* WIN32 */
58 #endif /* JIM_DYNLIB */
59
60 #include <cyg/athttpd/jim.h>
61
62 #ifdef HAVE_BACKTRACE
63 #include <execinfo.h>
64 #endif
65
66 /* -----------------------------------------------------------------------------
67  * Global variables
68  * ---------------------------------------------------------------------------*/
69
70 /* A shared empty string for the objects string representation.
71  * Jim_InvalidateStringRep knows about it and don't try to free. */
72 static char *JimEmptyStringRep = (char*) "";
73
74 /* -----------------------------------------------------------------------------
75  * Required prototypes of not exported functions
76  * ---------------------------------------------------------------------------*/
77 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
78 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
79 static void JimRegisterCoreApi(Jim_Interp *interp);
80
81 static Jim_HashTableType JimVariablesHashTableType;
82
83 /* -----------------------------------------------------------------------------
84  * Utility functions
85  * ---------------------------------------------------------------------------*/
86
87 /*
88  * Convert a string to a jim_wide INTEGER.
89  * This function originates from BSD.
90  *
91  * Ignores `locale' stuff.  Assumes that the upper and lower case
92  * alphabets and digits are each contiguous.
93  */
94 #ifdef HAVE_LONG_LONG
95 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
96 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
97 {
98     register const char *s;
99     register unsigned jim_wide acc;
100     register unsigned char c;
101     register unsigned jim_wide qbase, cutoff;
102     register int neg, any, cutlim;
103
104     /*
105      * Skip white space and pick up leading +/- sign if any.
106      * If base is 0, allow 0x for hex and 0 for octal, else
107      * assume decimal; if base is already 16, allow 0x.
108      */
109     s = nptr;
110     do {
111         c = *s++;
112     } while (isspace(c));
113     if (c == '-') {
114         neg = 1;
115         c = *s++;
116     } else {
117         neg = 0;
118         if (c == '+')
119             c = *s++;
120     }
121     if ((base == 0 || base == 16) &&
122         c == '0' && (*s == 'x' || *s == 'X')) {
123         c = s[1];
124         s += 2;
125         base = 16;
126     }
127     if (base == 0)
128         base = c == '0' ? 8 : 10;
129
130     /*
131      * Compute the cutoff value between legal numbers and illegal
132      * numbers.  That is the largest legal value, divided by the
133      * base.  An input number that is greater than this value, if
134      * followed by a legal input character, is too big.  One that
135      * is equal to this value may be valid or not; the limit
136      * between valid and invalid numbers is then based on the last
137      * digit.  For instance, if the range for quads is
138      * [-9223372036854775808..9223372036854775807] and the input base
139      * is 10, cutoff will be set to 922337203685477580 and cutlim to
140      * either 7 (neg==0) or 8 (neg==1), meaning that if we have
141      * accumulated a value > 922337203685477580, or equal but the
142      * next digit is > 7 (or 8), the number is too big, and we will
143      * return a range error.
144      *
145      * Set any if any `digits' consumed; make it negative to indicate
146      * overflow.
147      */
148     qbase = (unsigned)base;
149     cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
150         : LLONG_MAX;
151     cutlim = (int)(cutoff % qbase);
152     cutoff /= qbase;
153     for (acc = 0, any = 0;; c = *s++) {
154         if (!JimIsAscii(c))
155             break;
156         if (isdigit(c))
157             c -= '0';
158         else if (isalpha(c))
159             c -= isupper(c) ? 'A' - 10 : 'a' - 10;
160         else
161             break;
162         if (c >= base)
163             break;
164         if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
165             any = -1;
166         else {
167             any = 1;
168             acc *= qbase;
169             acc += c;
170         }
171     }
172     if (any < 0) {
173         acc = neg ? LLONG_MIN : LLONG_MAX;
174         errno = ERANGE;
175     } else if (neg)
176         acc = -acc;
177     if (endptr != 0)
178         *endptr = (char *)(any ? s - 1 : nptr);
179     return (acc);
180 }
181 #endif
182
183 /* Glob-style pattern matching. */
184 static int JimStringMatch(const char *pattern, int patternLen,
185         const char *string, int stringLen, int nocase)
186 {
187     while(patternLen) {
188         switch(pattern[0]) {
189         case '*':
190             while (pattern[1] == '*') {
191                 pattern++;
192                 patternLen--;
193             }
194             if (patternLen == 1)
195                 return 1; /* match */
196             while(stringLen) {
197                 if (JimStringMatch(pattern+1, patternLen-1,
198                             string, stringLen, nocase))
199                     return 1; /* match */
200                 string++;
201                 stringLen--;
202             }
203             return 0; /* no match */
204             break;
205         case '?':
206             if (stringLen == 0)
207                 return 0; /* no match */
208             string++;
209             stringLen--;
210             break;
211         case '[':
212         {
213             int not, match;
214
215             pattern++;
216             patternLen--;
217             not = pattern[0] == '^';
218             if (not) {
219                 pattern++;
220                 patternLen--;
221             }
222             match = 0;
223             while(1) {
224                 if (pattern[0] == '\\') {
225                     pattern++;
226                     patternLen--;
227                     if (pattern[0] == string[0])
228                         match = 1;
229                 } else if (pattern[0] == ']') {
230                     break;
231                 } else if (patternLen == 0) {
232                     pattern--;
233                     patternLen++;
234                     break;
235                 } else if (pattern[1] == '-' && patternLen >= 3) {
236                     int start = pattern[0];
237                     int end = pattern[2];
238                     int c = string[0];
239                     if (start > end) {
240                         int t = start;
241                         start = end;
242                         end = t;
243                     }
244                     if (nocase) {
245                         start = tolower(start);
246                         end = tolower(end);
247                         c = tolower(c);
248                     }
249                     pattern += 2;
250                     patternLen -= 2;
251                     if (c >= start && c <= end)
252                         match = 1;
253                 } else {
254                     if (!nocase) {
255                         if (pattern[0] == string[0])
256                             match = 1;
257                     } else {
258                         if (tolower((int)pattern[0]) == tolower((int)string[0]))
259                             match = 1;
260                     }
261                 }
262                 pattern++;
263                 patternLen--;
264             }
265             if (not)
266                 match = !match;
267             if (!match)
268                 return 0; /* no match */
269             string++;
270             stringLen--;
271             break;
272         }
273         case '\\':
274             if (patternLen >= 2) {
275                 pattern++;
276                 patternLen--;
277             }
278             /* fall through */
279         default:
280             if (!nocase) {
281                 if (pattern[0] != string[0])
282                     return 0; /* no match */
283             } else {
284                 if (tolower((int)pattern[0]) != tolower((int)string[0]))
285                     return 0; /* no match */
286             }
287             string++;
288             stringLen--;
289             break;
290         }
291         pattern++;
292         patternLen--;
293         if (stringLen == 0) {
294             while(*pattern == '*') {
295                 pattern++;
296                 patternLen--;
297             }
298             break;
299         }
300     }
301     if (patternLen == 0 && stringLen == 0)
302         return 1;
303     return 0;
304 }
305
306 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
307         int nocase)
308 {
309     unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
310
311     if (nocase == 0) {
312         while(l1 && l2) {
313             if (*u1 != *u2)
314                 return (int)*u1-*u2;
315             u1++; u2++; l1--; l2--;
316         }
317         if (!l1 && !l2) return 0;
318         return l1-l2;
319     } else {
320         while(l1 && l2) {
321             if (tolower((int)*u1) != tolower((int)*u2))
322                 return tolower((int)*u1)-tolower((int)*u2);
323             u1++; u2++; l1--; l2--;
324         }
325         if (!l1 && !l2) return 0;
326         return l1-l2;
327     }
328 }
329
330 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
331  * The index of the first occurrence of s1 in s2 is returned. 
332  * If s1 is not found inside s2, -1 is returned. */
333 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
334 {
335     int i;
336
337     if (!l1 || !l2 || l1 > l2) return -1;
338     if (index < 0) index = 0;
339     s2 += index;
340     for (i = index; i <= l2-l1; i++) {
341         if (memcmp(s2, s1, l1) == 0)
342             return i;
343         s2++;
344     }
345     return -1;
346 }
347
348 int Jim_WideToString(char *buf, jim_wide wideValue)
349 {
350     const char *fmt = "%" JIM_WIDE_MODIFIER;
351     return sprintf(buf, fmt, wideValue);
352 }
353
354 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
355 {
356     char *endptr;
357
358 #ifdef HAVE_LONG_LONG
359     *widePtr = JimStrtoll(str, &endptr, base);
360 #else
361     *widePtr = strtol(str, &endptr, base);
362 #endif
363     if (str[0] == '\0')
364         return JIM_ERR;
365     if (endptr[0] != '\0') {
366         while(*endptr) {
367             if (!isspace((int)*endptr))
368                 return JIM_ERR;
369             endptr++;
370         }
371     }
372     return JIM_OK;
373 }
374
375 int Jim_StringToIndex(const char *str, int *intPtr)
376 {
377     char *endptr;
378
379     *intPtr = strtol(str, &endptr, 10);
380     if (str[0] == '\0')
381         return JIM_ERR;
382     if (endptr[0] != '\0') {
383         while(*endptr) {
384             if (!isspace((int)*endptr))
385                 return JIM_ERR;
386             endptr++;
387         }
388     }
389     return JIM_OK;
390 }
391
392 /* The string representation of references has two features in order
393  * to make the GC faster. The first is that every reference starts
394  * with a non common character '~', in order to make the string matching
395  * fater. The second is that the reference string rep his 32 characters
396  * in length, this allows to avoid to check every object with a string
397  * repr < 32, and usually there are many of this objects. */
398
399 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
400
401 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
402 {
403     const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
404     sprintf(buf, fmt, refPtr->tag, id);
405     return JIM_REFERENCE_SPACE;
406 }
407
408 int Jim_DoubleToString(char *buf, double doubleValue)
409 {
410     char *s;
411     int len;
412
413     len = sprintf(buf, "%.17g", doubleValue);
414     s = buf;
415     while(*s) {
416         if (*s == '.') return len;
417         s++;
418     }
419     /* Add a final ".0" if it's a number. But not
420      * for NaN or InF */
421     if (isdigit((int)buf[0])
422         || ((buf[0] == '-' || buf[0] == '+')
423             && isdigit((int)buf[1]))) {
424         s[0] = '.';
425         s[1] = '0';
426         s[2] = '\0';
427         return len+2;
428     }
429     return len;
430 }
431
432 int Jim_StringToDouble(const char *str, double *doublePtr)
433 {
434     char *endptr;
435
436     *doublePtr = strtod(str, &endptr);
437     if (str[0] == '\0' || endptr[0] != '\0')
438         return JIM_ERR;
439     return JIM_OK;
440 }
441
442 static jim_wide JimPowWide(jim_wide b, jim_wide e)
443 {
444     jim_wide i, res = 1;
445     if ((b==0 && e!=0) || (e<0)) return 0;
446     for(i=0; i<e; i++) {res *= b;}
447     return res;
448 }
449
450 /* -----------------------------------------------------------------------------
451  * Special functions
452  * ---------------------------------------------------------------------------*/
453
454 /* Note that 'interp' may be NULL if not available in the
455  * context of the panic. It's only useful to get the error
456  * file descriptor, it will default to stderr otherwise. */
457 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
458 {
459     va_list ap;
460     FILE *fp = interp ? interp->stderr : stderr;
461
462     va_start(ap, fmt);
463     fprintf(fp, JIM_NL "JIM INTERPRETER PANIC: ");
464     vfprintf(fp, fmt, ap);
465     fprintf(fp, JIM_NL JIM_NL);
466     va_end(ap);
467 #ifdef HAVE_BACKTRACE
468     {
469         void *array[40];
470         int size, i;
471         char **strings;
472
473         size = backtrace(array, 40);
474         strings = backtrace_symbols(array, size);
475         for (i = 0; i < size; i++)
476             fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
477         fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
478         fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
479     }
480 #endif
481     abort();
482 }
483
484 /* -----------------------------------------------------------------------------
485  * Memory allocation
486  * ---------------------------------------------------------------------------*/
487
488 /* Macro used for memory debugging.
489  * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
490  * and similary for Jim_Realloc and Jim_Free */
491 #if 0
492 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
493 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
494 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
495 #endif
496
497 void *Jim_Alloc(int size)
498 {
499     void *p = malloc(size);
500     if (p == NULL)
501         Jim_Panic(NULL,"Out of memory");
502     return p;
503 }
504
505 void Jim_Free(void *ptr) {
506     free(ptr);
507 }
508
509 void *Jim_Realloc(void *ptr, int size)
510 {
511     void *p = realloc(ptr, size);
512     if (p == NULL)
513         Jim_Panic(NULL,"Out of memory");
514     return p;
515 }
516
517 char *Jim_StrDup(const char *s)
518 {
519     int l = strlen(s);
520     char *copy = Jim_Alloc(l+1);
521
522     memcpy(copy, s, l+1);
523     return copy;
524 }
525
526 char *Jim_StrDupLen(const char *s, int l)
527 {
528     char *copy = Jim_Alloc(l+1);
529     
530     memcpy(copy, s, l+1);
531     copy[l] = 0;    /* Just to be sure, original could be substring */
532     return copy;
533 }
534
535 /* -----------------------------------------------------------------------------
536  * Time related functions
537  * ---------------------------------------------------------------------------*/
538 /* Returns microseconds of CPU used since start. */
539 static jim_wide JimClock(void)
540 {
541 #if (defined WIN32) && !(defined JIM_ANSIC)
542     LARGE_INTEGER t, f;
543     QueryPerformanceFrequency(&f);
544     QueryPerformanceCounter(&t);
545     return (long)((t.QuadPart * 1000000) / f.QuadPart);
546 #else /* !WIN32 */
547     clock_t clocks = clock();
548
549     return (long)(clocks*(1000000/CLOCKS_PER_SEC));
550 #endif /* WIN32 */
551 }
552
553 /* -----------------------------------------------------------------------------
554  * Hash Tables
555  * ---------------------------------------------------------------------------*/
556
557 /* -------------------------- private prototypes ---------------------------- */
558 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
559 static unsigned int JimHashTableNextPower(unsigned int size);
560 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
561
562 /* -------------------------- hash functions -------------------------------- */
563
564 /* Thomas Wang's 32 bit Mix Function */
565 unsigned int Jim_IntHashFunction(unsigned int key)
566 {
567     key += ~(key << 15);
568     key ^=  (key >> 10);
569     key +=  (key << 3);
570     key ^=  (key >> 6);
571     key += ~(key << 11);
572     key ^=  (key >> 16);
573     return key;
574 }
575
576 /* Identity hash function for integer keys */
577 unsigned int Jim_IdentityHashFunction(unsigned int key)
578 {
579     return key;
580 }
581
582 /* Generic hash function (we are using to multiply by 9 and add the byte
583  * as Tcl) */
584 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
585 {
586     unsigned int h = 0;
587     while(len--)
588         h += (h<<3)+*buf++;
589     return h;
590 }
591
592 /* ----------------------------- API implementation ------------------------- */
593 /* reset an hashtable already initialized with ht_init().
594  * NOTE: This function should only called by ht_destroy(). */
595 static void JimResetHashTable(Jim_HashTable *ht)
596 {
597     ht->table = NULL;
598     ht->size = 0;
599     ht->sizemask = 0;
600     ht->used = 0;
601     ht->collisions = 0;
602 }
603
604 /* Initialize the hash table */
605 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
606         void *privDataPtr)
607 {
608     JimResetHashTable(ht);
609     ht->type = type;
610     ht->privdata = privDataPtr;
611     return JIM_OK;
612 }
613
614 /* Resize the table to the minimal size that contains all the elements,
615  * but with the invariant of a USER/BUCKETS ration near to <= 1 */
616 int Jim_ResizeHashTable(Jim_HashTable *ht)
617 {
618     int minimal = ht->used;
619
620     if (minimal < JIM_HT_INITIAL_SIZE)
621         minimal = JIM_HT_INITIAL_SIZE;
622     return Jim_ExpandHashTable(ht, minimal);
623 }
624
625 /* Expand or create the hashtable */
626 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
627 {
628     Jim_HashTable n; /* the new hashtable */
629     unsigned int realsize = JimHashTableNextPower(size), i;
630
631     /* the size is invalid if it is smaller than the number of
632      * elements already inside the hashtable */
633     if (ht->used >= size)
634         return JIM_ERR;
635
636     Jim_InitHashTable(&n, ht->type, ht->privdata);
637     n.size = realsize;
638     n.sizemask = realsize-1;
639     n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
640
641     /* Initialize all the pointers to NULL */
642     memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
643
644     /* Copy all the elements from the old to the new table:
645      * note that if the old hash table is empty ht->size is zero,
646      * so Jim_ExpandHashTable just creates an hash table. */
647     n.used = ht->used;
648     for (i = 0; i < ht->size && ht->used > 0; i++) {
649         Jim_HashEntry *he, *nextHe;
650
651         if (ht->table[i] == NULL) continue;
652         
653         /* For each hash entry on this slot... */
654         he = ht->table[i];
655         while(he) {
656             unsigned int h;
657
658             nextHe = he->next;
659             /* Get the new element index */
660             h = Jim_HashKey(ht, he->key) & n.sizemask;
661             he->next = n.table[h];
662             n.table[h] = he;
663             ht->used--;
664             /* Pass to the next element */
665             he = nextHe;
666         }
667     }
668     assert(ht->used == 0);
669     Jim_Free(ht->table);
670
671     /* Remap the new hashtable in the old */
672     *ht = n;
673     return JIM_OK;
674 }
675
676 /* Add an element to the target hash table */
677 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
678 {
679     int index;
680     Jim_HashEntry *entry;
681
682     /* Get the index of the new element, or -1 if
683      * the element already exists. */
684     if ((index = JimInsertHashEntry(ht, key)) == -1)
685         return JIM_ERR;
686
687     /* Allocates the memory and stores key */
688     entry = Jim_Alloc(sizeof(*entry));
689     entry->next = ht->table[index];
690     ht->table[index] = entry;
691
692     /* Set the hash entry fields. */
693     Jim_SetHashKey(ht, entry, key);
694     Jim_SetHashVal(ht, entry, val);
695     ht->used++;
696     return JIM_OK;
697 }
698
699 /* Add an element, discarding the old if the key already exists */
700 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
701 {
702     Jim_HashEntry *entry;
703
704     /* Try to add the element. If the key
705      * does not exists Jim_AddHashEntry will suceed. */
706     if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
707         return JIM_OK;
708     /* It already exists, get the entry */
709     entry = Jim_FindHashEntry(ht, key);
710     /* Free the old value and set the new one */
711     Jim_FreeEntryVal(ht, entry);
712     Jim_SetHashVal(ht, entry, val);
713     return JIM_OK;
714 }
715
716 /* Search and remove an element */
717 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
718 {
719     unsigned int h;
720     Jim_HashEntry *he, *prevHe;
721
722     if (ht->size == 0)
723         return JIM_ERR;
724     h = Jim_HashKey(ht, key) & ht->sizemask;
725     he = ht->table[h];
726
727     prevHe = NULL;
728     while(he) {
729         if (Jim_CompareHashKeys(ht, key, he->key)) {
730             /* Unlink the element from the list */
731             if (prevHe)
732                 prevHe->next = he->next;
733             else
734                 ht->table[h] = he->next;
735             Jim_FreeEntryKey(ht, he);
736             Jim_FreeEntryVal(ht, he);
737             Jim_Free(he);
738             ht->used--;
739             return JIM_OK;
740         }
741         prevHe = he;
742         he = he->next;
743     }
744     return JIM_ERR; /* not found */
745 }
746
747 /* Destroy an entire hash table */
748 int Jim_FreeHashTable(Jim_HashTable *ht)
749 {
750     unsigned int i;
751
752     /* Free all the elements */
753     for (i = 0; i < ht->size && ht->used > 0; i++) {
754         Jim_HashEntry *he, *nextHe;
755
756         if ((he = ht->table[i]) == NULL) continue;
757         while(he) {
758             nextHe = he->next;
759             Jim_FreeEntryKey(ht, he);
760             Jim_FreeEntryVal(ht, he);
761             Jim_Free(he);
762             ht->used--;
763             he = nextHe;
764         }
765     }
766     /* Free the table and the allocated cache structure */
767     Jim_Free(ht->table);
768     /* Re-initialize the table */
769     JimResetHashTable(ht);
770     return JIM_OK; /* never fails */
771 }
772
773 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
774 {
775     Jim_HashEntry *he;
776     unsigned int h;
777
778     if (ht->size == 0) return NULL;
779     h = Jim_HashKey(ht, key) & ht->sizemask;
780     he = ht->table[h];
781     while(he) {
782         if (Jim_CompareHashKeys(ht, key, he->key))
783             return he;
784         he = he->next;
785     }
786     return NULL;
787 }
788
789 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
790 {
791     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
792
793     iter->ht = ht;
794     iter->index = -1;
795     iter->entry = NULL;
796     iter->nextEntry = NULL;
797     return iter;
798 }
799
800 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
801 {
802     while (1) {
803         if (iter->entry == NULL) {
804             iter->index++;
805             if (iter->index >=
806                     (signed)iter->ht->size) break;
807             iter->entry = iter->ht->table[iter->index];
808         } else {
809             iter->entry = iter->nextEntry;
810         }
811         if (iter->entry) {
812             /* We need to save the 'next' here, the iterator user
813              * may delete the entry we are returning. */
814             iter->nextEntry = iter->entry->next;
815             return iter->entry;
816         }
817     }
818     return NULL;
819 }
820
821 /* ------------------------- private functions ------------------------------ */
822
823 /* Expand the hash table if needed */
824 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
825 {
826     /* If the hash table is empty expand it to the intial size,
827      * if the table is "full" dobule its size. */
828     if (ht->size == 0)
829         return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
830     if (ht->size == ht->used)
831         return Jim_ExpandHashTable(ht, ht->size*2);
832     return JIM_OK;
833 }
834
835 /* Our hash table capability is a power of two */
836 static unsigned int JimHashTableNextPower(unsigned int size)
837 {
838     unsigned int i = JIM_HT_INITIAL_SIZE;
839
840     if (size >= 2147483648U)
841         return 2147483648U;
842     while(1) {
843         if (i >= size)
844             return i;
845         i *= 2;
846     }
847 }
848
849 /* Returns the index of a free slot that can be populated with
850  * an hash entry for the given 'key'.
851  * If the key already exists, -1 is returned. */
852 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
853 {
854     unsigned int h;
855     Jim_HashEntry *he;
856
857     /* Expand the hashtable if needed */
858     if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
859         return -1;
860     /* Compute the key hash value */
861     h = Jim_HashKey(ht, key) & ht->sizemask;
862     /* Search if this slot does not already contain the given key */
863     he = ht->table[h];
864     while(he) {
865         if (Jim_CompareHashKeys(ht, key, he->key))
866             return -1;
867         he = he->next;
868     }
869     return h;
870 }
871
872 /* ----------------------- StringCopy Hash Table Type ------------------------*/
873
874 static unsigned int JimStringCopyHTHashFunction(const void *key)
875 {
876     return Jim_GenHashFunction(key, strlen(key));
877 }
878
879 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
880 {
881     int len = strlen(key);
882     char *copy = Jim_Alloc(len+1);
883     JIM_NOTUSED(privdata);
884
885     memcpy(copy, key, len);
886     copy[len] = '\0';
887     return copy;
888 }
889
890 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
891 {
892     int len = strlen(val);
893     char *copy = Jim_Alloc(len+1);
894     JIM_NOTUSED(privdata);
895
896     memcpy(copy, val, len);
897     copy[len] = '\0';
898     return copy;
899 }
900
901 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
902         const void *key2)
903 {
904     JIM_NOTUSED(privdata);
905
906     return strcmp(key1, key2) == 0;
907 }
908
909 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
910 {
911     JIM_NOTUSED(privdata);
912
913     Jim_Free((void*)key); /* ATTENTION: const cast */
914 }
915
916 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
917 {
918     JIM_NOTUSED(privdata);
919
920     Jim_Free((void*)val); /* ATTENTION: const cast */
921 }
922
923 static Jim_HashTableType JimStringCopyHashTableType = {
924     JimStringCopyHTHashFunction,        /* hash function */
925     JimStringCopyHTKeyDup,              /* key dup */
926     NULL,                               /* val dup */
927     JimStringCopyHTKeyCompare,          /* key compare */
928     JimStringCopyHTKeyDestructor,       /* key destructor */
929     NULL                                /* val destructor */
930 };
931
932 /* This is like StringCopy but does not auto-duplicate the key.
933  * It's used for intepreter's shared strings. */
934 static Jim_HashTableType JimSharedStringsHashTableType = {
935     JimStringCopyHTHashFunction,        /* hash function */
936     NULL,                               /* key dup */
937     NULL,                               /* val dup */
938     JimStringCopyHTKeyCompare,          /* key compare */
939     JimStringCopyHTKeyDestructor,       /* key destructor */
940     NULL                                /* val destructor */
941 };
942
943 /* This is like StringCopy but also automatically handle dynamic
944  * allocated C strings as values. */
945 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
946     JimStringCopyHTHashFunction,        /* hash function */
947     JimStringCopyHTKeyDup,              /* key dup */
948     JimStringKeyValCopyHTValDup,        /* val dup */
949     JimStringCopyHTKeyCompare,          /* key compare */
950     JimStringCopyHTKeyDestructor,       /* key destructor */
951     JimStringKeyValCopyHTValDestructor, /* val destructor */
952 };
953
954 typedef struct AssocDataValue {
955     Jim_InterpDeleteProc *delProc;
956     void *data;
957 } AssocDataValue;
958
959 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
960 {
961     AssocDataValue *assocPtr = (AssocDataValue *)data;
962     if (assocPtr->delProc != NULL)
963         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
964     Jim_Free(data);
965 }
966
967 static Jim_HashTableType JimAssocDataHashTableType = {
968     JimStringCopyHTHashFunction,         /* hash function */
969     JimStringCopyHTKeyDup,               /* key dup */
970     NULL,                                /* val dup */
971     JimStringCopyHTKeyCompare,           /* key compare */
972     JimStringCopyHTKeyDestructor,        /* key destructor */
973     JimAssocDataHashTableValueDestructor /* val destructor */
974 };
975
976 /* -----------------------------------------------------------------------------
977  * Stack - This is a simple generic stack implementation. It is used for
978  * example in the 'expr' expression compiler.
979  * ---------------------------------------------------------------------------*/
980 void Jim_InitStack(Jim_Stack *stack)
981 {
982     stack->len = 0;
983     stack->maxlen = 0;
984     stack->vector = NULL;
985 }
986
987 void Jim_FreeStack(Jim_Stack *stack)
988 {
989     Jim_Free(stack->vector);
990 }
991
992 int Jim_StackLen(Jim_Stack *stack)
993 {
994     return stack->len;
995 }
996
997 void Jim_StackPush(Jim_Stack *stack, void *element) {
998     int neededLen = stack->len+1;
999     if (neededLen > stack->maxlen) {
1000         stack->maxlen = neededLen*2;
1001         stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1002     }
1003     stack->vector[stack->len] = element;
1004     stack->len++;
1005 }
1006
1007 void *Jim_StackPop(Jim_Stack *stack)
1008 {
1009     if (stack->len == 0) return NULL;
1010     stack->len--;
1011     return stack->vector[stack->len];
1012 }
1013
1014 void *Jim_StackPeek(Jim_Stack *stack)
1015 {
1016     if (stack->len == 0) return NULL;
1017     return stack->vector[stack->len-1];
1018 }
1019
1020 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1021 {
1022     int i;
1023
1024     for (i = 0; i < stack->len; i++)
1025         freeFunc(stack->vector[i]);
1026 }
1027
1028 /* -----------------------------------------------------------------------------
1029  * Parser
1030  * ---------------------------------------------------------------------------*/
1031
1032 /* Token types */
1033 #define JIM_TT_NONE -1        /* No token returned */
1034 #define JIM_TT_STR 0        /* simple string */
1035 #define JIM_TT_ESC 1        /* string that needs escape chars conversion */
1036 #define JIM_TT_VAR 2        /* var substitution */
1037 #define JIM_TT_DICTSUGAR 3    /* Syntax sugar for [dict get], $foo(bar) */
1038 #define JIM_TT_CMD 4        /* command substitution */
1039 #define JIM_TT_SEP 5        /* word separator */
1040 #define JIM_TT_EOL 6        /* line separator */
1041
1042 /* Additional token types needed for expressions */
1043 #define JIM_TT_SUBEXPR_START 7
1044 #define JIM_TT_SUBEXPR_END 8
1045 #define JIM_TT_EXPR_NUMBER 9
1046 #define JIM_TT_EXPR_OPERATOR 10
1047
1048 /* Parser states */
1049 #define JIM_PS_DEF 0        /* Default state */
1050 #define JIM_PS_QUOTE 1        /* Inside "" */
1051
1052 /* Parser context structure. The same context is used both to parse
1053  * Tcl scripts and lists. */
1054 struct JimParserCtx {
1055     const char *prg;     /* Program text */
1056     const char *p;       /* Pointer to the point of the program we are parsing */
1057     int len;             /* Left length of 'prg' */
1058     int linenr;          /* Current line number */
1059     const char *tstart;
1060     const char *tend;    /* Returned token is at tstart-tend in 'prg'. */
1061     int tline;           /* Line number of the returned token */
1062     int tt;              /* Token type */
1063     int eof;             /* Non zero if EOF condition is true. */
1064     int state;           /* Parser state */
1065     int comment;         /* Non zero if the next chars may be a comment. */
1066 };
1067
1068 #define JimParserEof(c) ((c)->eof)
1069 #define JimParserTstart(c) ((c)->tstart)
1070 #define JimParserTend(c) ((c)->tend)
1071 #define JimParserTtype(c) ((c)->tt)
1072 #define JimParserTline(c) ((c)->tline)
1073
1074 static int JimParseScript(struct JimParserCtx *pc);
1075 static int JimParseSep(struct JimParserCtx *pc);
1076 static int JimParseEol(struct JimParserCtx *pc);
1077 static int JimParseCmd(struct JimParserCtx *pc);
1078 static int JimParseVar(struct JimParserCtx *pc);
1079 static int JimParseBrace(struct JimParserCtx *pc);
1080 static int JimParseStr(struct JimParserCtx *pc);
1081 static int JimParseComment(struct JimParserCtx *pc);
1082 static char *JimParserGetToken(struct JimParserCtx *pc,
1083         int *lenPtr, int *typePtr, int *linePtr);
1084
1085 /* Initialize a parser context.
1086  * 'prg' is a pointer to the program text, linenr is the line
1087  * number of the first line contained in the program. */
1088 void JimParserInit(struct JimParserCtx *pc, const char *prg, 
1089         int len, int linenr)
1090 {
1091     pc->prg = prg;
1092     pc->p = prg;
1093     pc->len = len;
1094     pc->tstart = NULL;
1095     pc->tend = NULL;
1096     pc->tline = 0;
1097     pc->tt = JIM_TT_NONE;
1098     pc->eof = 0;
1099     pc->state = JIM_PS_DEF;
1100     pc->linenr = linenr;
1101     pc->comment = 1;
1102 }
1103
1104 int JimParseScript(struct JimParserCtx *pc)
1105 {
1106     while(1) { /* the while is used to reiterate with continue if needed */
1107         if (!pc->len) {
1108             pc->tstart = pc->p;
1109             pc->tend = pc->p-1;
1110             pc->tline = pc->linenr;
1111             pc->tt = JIM_TT_EOL;
1112             pc->eof = 1;
1113             return JIM_OK;
1114         }
1115         switch(*(pc->p)) {
1116         case '\\':
1117             if (*(pc->p+1) == '\n')
1118                 return JimParseSep(pc);
1119             else {
1120                 pc->comment = 0;
1121                 return JimParseStr(pc);
1122             }
1123             break;
1124         case ' ':
1125         case '\t':
1126         case '\r':
1127             if (pc->state == JIM_PS_DEF)
1128                 return JimParseSep(pc);
1129             else {
1130                 pc->comment = 0;
1131                 return JimParseStr(pc);
1132             }
1133             break;
1134         case '\n':
1135         case ';':
1136             pc->comment = 1;
1137             if (pc->state == JIM_PS_DEF)
1138                 return JimParseEol(pc);
1139             else
1140                 return JimParseStr(pc);
1141             break;
1142         case '[':
1143             pc->comment = 0;
1144             return JimParseCmd(pc);
1145             break;
1146         case '$':
1147             pc->comment = 0;
1148             if (JimParseVar(pc) == JIM_ERR) {
1149                 pc->tstart = pc->tend = pc->p++; pc->len--;
1150                 pc->tline = pc->linenr;
1151                 pc->tt = JIM_TT_STR;
1152                 return JIM_OK;
1153             } else
1154                 return JIM_OK;
1155             break;
1156         case '#':
1157             if (pc->comment) {
1158                 JimParseComment(pc);
1159                 continue;
1160             } else {
1161                 return JimParseStr(pc);
1162             }
1163         default:
1164             pc->comment = 0;
1165             return JimParseStr(pc);
1166             break;
1167         }
1168         return JIM_OK;
1169     }
1170 }
1171
1172 int JimParseSep(struct JimParserCtx *pc)
1173 {
1174     pc->tstart = pc->p;
1175     pc->tline = pc->linenr;
1176     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1177            (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1178         if (*pc->p == '\\') {
1179             pc->p++; pc->len--;
1180             pc->linenr++;
1181         }
1182         pc->p++; pc->len--;
1183     }
1184     pc->tend = pc->p-1;
1185     pc->tt = JIM_TT_SEP;
1186     return JIM_OK;
1187 }
1188
1189 int JimParseEol(struct JimParserCtx *pc)
1190 {
1191     pc->tstart = pc->p;
1192     pc->tline = pc->linenr;
1193     while (*pc->p == ' ' || *pc->p == '\n' ||
1194            *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1195         if (*pc->p == '\n')
1196             pc->linenr++;
1197         pc->p++; pc->len--;
1198     }
1199     pc->tend = pc->p-1;
1200     pc->tt = JIM_TT_EOL;
1201     return JIM_OK;
1202 }
1203
1204 /* Todo. Don't stop if ']' appears inside {} or quoted.
1205  * Also should handle the case of puts [string length "]"] */
1206 int JimParseCmd(struct JimParserCtx *pc)
1207 {
1208     int level = 1;
1209     int blevel = 0;
1210
1211     pc->tstart = ++pc->p; pc->len--;
1212     pc->tline = pc->linenr;
1213     while (1) {
1214         if (pc->len == 0) {
1215             break;
1216         } else if (*pc->p == '[' && blevel == 0) {
1217             level++;
1218         } else if (*pc->p == ']' && blevel == 0) {
1219             level--;
1220             if (!level) break;
1221         } else if (*pc->p == '\\') {
1222             pc->p++; pc->len--;
1223         } else if (*pc->p == '{') {
1224             blevel++;
1225         } else if (*pc->p == '}') {
1226             if (blevel != 0)
1227                 blevel--;
1228         } else if (*pc->p == '\n')
1229             pc->linenr++;
1230         pc->p++; pc->len--;
1231     }
1232     pc->tend = pc->p-1;
1233     pc->tt = JIM_TT_CMD;
1234     if (*pc->p == ']') {
1235         pc->p++; pc->len--;
1236     }
1237     return JIM_OK;
1238 }
1239
1240 int JimParseVar(struct JimParserCtx *pc)
1241 {
1242     int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1243
1244     pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1245     pc->tline = pc->linenr;
1246     if (*pc->p == '{') {
1247         pc->tstart = ++pc->p; pc->len--;
1248         brace = 1;
1249     }
1250     if (brace) {
1251         while (!stop) {
1252             if (*pc->p == '}' || pc->len == 0) {
1253                 stop = 1;
1254                 if (pc->len == 0)
1255                     continue;
1256             }
1257             else if (*pc->p == '\n')
1258                 pc->linenr++;
1259             pc->p++; pc->len--;
1260         }
1261         if (pc->len == 0)
1262             pc->tend = pc->p-1;
1263         else
1264             pc->tend = pc->p-2;
1265     } else {
1266         while (!stop) {
1267             if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1268                 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1269                 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1270                 stop = 1;
1271             else {
1272                 pc->p++; pc->len--;
1273             }
1274         }
1275         /* Parse [dict get] syntax sugar. */
1276         if (*pc->p == '(') {
1277             while (*pc->p != ')' && pc->len) {
1278                 pc->p++; pc->len--;
1279                 if (*pc->p == '\\' && pc->len >= 2) {
1280                     pc->p += 2; pc->len -= 2;
1281                 }
1282             }
1283             if (*pc->p != '\0') {
1284                 pc->p++; pc->len--;
1285             }
1286             ttype = JIM_TT_DICTSUGAR;
1287         }
1288         pc->tend = pc->p-1;
1289     }
1290     /* Check if we parsed just the '$' character.
1291      * That's not a variable so an error is returned
1292      * to tell the state machine to consider this '$' just
1293      * a string. */
1294     if (pc->tstart == pc->p) {
1295         pc->p--; pc->len++;
1296         return JIM_ERR;
1297     }
1298     pc->tt = ttype;
1299     return JIM_OK;
1300 }
1301
1302 int JimParseBrace(struct JimParserCtx *pc)
1303 {
1304     int level = 1;
1305
1306     pc->tstart = ++pc->p; pc->len--;
1307     pc->tline = pc->linenr;
1308     while (1) {
1309         if (*pc->p == '\\' && pc->len >= 2) {
1310             pc->p++; pc->len--;
1311             if (*pc->p == '\n')
1312                 pc->linenr++;
1313         } else if (*pc->p == '{') {
1314             level++;
1315         } else if (pc->len == 0 || *pc->p == '}') {
1316             level--;
1317             if (pc->len == 0 || level == 0) {
1318                 pc->tend = pc->p-1;
1319                 if (pc->len != 0) {
1320                     pc->p++; pc->len--;
1321                 }
1322                 pc->tt = JIM_TT_STR;
1323                 return JIM_OK;
1324             }
1325         } else if (*pc->p == '\n') {
1326             pc->linenr++;
1327         }
1328         pc->p++; pc->len--;
1329     }
1330     return JIM_OK; /* unreached */
1331 }
1332
1333 int JimParseStr(struct JimParserCtx *pc)
1334 {
1335     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1336             pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1337     if (newword && *pc->p == '{') {
1338         return JimParseBrace(pc);
1339     } else if (newword && *pc->p == '"') {
1340         pc->state = JIM_PS_QUOTE;
1341         pc->p++; pc->len--;
1342     }
1343     pc->tstart = pc->p;
1344     pc->tline = pc->linenr;
1345     while (1) {
1346         if (pc->len == 0) {
1347             pc->tend = pc->p-1;
1348             pc->tt = JIM_TT_ESC;
1349             return JIM_OK;
1350         }
1351         switch(*pc->p) {
1352         case '\\':
1353             if (pc->state == JIM_PS_DEF &&
1354                 *(pc->p+1) == '\n') {
1355                 pc->tend = pc->p-1;
1356                 pc->tt = JIM_TT_ESC;
1357                 return JIM_OK;
1358             }
1359             if (pc->len >= 2) {
1360                 pc->p++; pc->len--;
1361             }
1362             break;
1363         case '$':
1364         case '[':
1365             pc->tend = pc->p-1;
1366             pc->tt = JIM_TT_ESC;
1367             return JIM_OK;
1368         case ' ':
1369         case '\t':
1370         case '\n':
1371         case '\r':
1372         case ';':
1373             if (pc->state == JIM_PS_DEF) {
1374                 pc->tend = pc->p-1;
1375                 pc->tt = JIM_TT_ESC;
1376                 return JIM_OK;
1377             } else if (*pc->p == '\n') {
1378                 pc->linenr++;
1379             }
1380             break;
1381         case '"':
1382             if (pc->state == JIM_PS_QUOTE) {
1383                 pc->tend = pc->p-1;
1384                 pc->tt = JIM_TT_ESC;
1385                 pc->p++; pc->len--;
1386                 pc->state = JIM_PS_DEF;
1387                 return JIM_OK;
1388             }
1389             break;
1390         }
1391         pc->p++; pc->len--;
1392     }
1393     return JIM_OK; /* unreached */
1394 }
1395
1396 int JimParseComment(struct JimParserCtx *pc)
1397 {
1398     while (*pc->p) {
1399         if (*pc->p == '\n') {
1400             pc->linenr++;
1401             if (*(pc->p-1) != '\\') {
1402                 pc->p++; pc->len--;
1403                 return JIM_OK;
1404             }
1405         }
1406         pc->p++; pc->len--;
1407     }
1408     return JIM_OK;
1409 }
1410
1411 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1412 static int xdigitval(int c)
1413 {
1414     if (c >= '0' && c <= '9') return c-'0';
1415     if (c >= 'a' && c <= 'f') return c-'a'+10;
1416     if (c >= 'A' && c <= 'F') return c-'A'+10;
1417     return -1;
1418 }
1419
1420 static int odigitval(int c)
1421 {
1422     if (c >= '0' && c <= '7') return c-'0';
1423     return -1;
1424 }
1425
1426 /* Perform Tcl escape substitution of 's', storing the result
1427  * string into 'dest'. The escaped string is guaranteed to
1428  * be the same length or shorted than the source string.
1429  * Slen is the length of the string at 's', if it's -1 the string
1430  * length will be calculated by the function.
1431  *
1432  * The function returns the length of the resulting string. */
1433 static int JimEscape(char *dest, const char *s, int slen)
1434 {
1435     char *p = dest;
1436     int i, len;
1437     
1438     if (slen == -1)
1439         slen = strlen(s);
1440
1441     for (i = 0; i < slen; i++) {
1442         switch(s[i]) {
1443         case '\\':
1444             switch(s[i+1]) {
1445             case 'a': *p++ = 0x7; i++; break;
1446             case 'b': *p++ = 0x8; i++; break;
1447             case 'f': *p++ = 0xc; i++; break;
1448             case 'n': *p++ = 0xa; i++; break;
1449             case 'r': *p++ = 0xd; i++; break;
1450             case 't': *p++ = 0x9; i++; break;
1451             case 'v': *p++ = 0xb; i++; break;
1452             case '\0': *p++ = '\\'; i++; break;
1453             case '\n': *p++ = ' '; i++; break;
1454             default:
1455                   if (s[i+1] == 'x') {
1456                     int val = 0;
1457                     int c = xdigitval(s[i+2]);
1458                     if (c == -1) {
1459                         *p++ = 'x';
1460                         i++;
1461                         break;
1462                     }
1463                     val = c;
1464                     c = xdigitval(s[i+3]);
1465                     if (c == -1) {
1466                         *p++ = val;
1467                         i += 2;
1468                         break;
1469                     }
1470                     val = (val*16)+c;
1471                     *p++ = val;
1472                     i += 3;
1473                     break;
1474                   } else if (s[i+1] >= '0' && s[i+1] <= '7')
1475                   {
1476                     int val = 0;
1477                     int c = odigitval(s[i+1]);
1478                     val = c;
1479                     c = odigitval(s[i+2]);
1480                     if (c == -1) {
1481                         *p++ = val;
1482                         i ++;
1483                         break;
1484                     }
1485                     val = (val*8)+c;
1486                     c = odigitval(s[i+3]);
1487                     if (c == -1) {
1488                         *p++ = val;
1489                         i += 2;
1490                         break;
1491                     }
1492                     val = (val*8)+c;
1493                     *p++ = val;
1494                     i += 3;
1495                   } else {
1496                     *p++ = s[i+1];
1497                     i++;
1498                   }
1499                   break;
1500             }
1501             break;
1502         default:
1503             *p++ = s[i];
1504             break;
1505         }
1506     }
1507     len = p-dest;
1508     *p++ = '\0';
1509     return len;
1510 }
1511
1512 /* Returns a dynamically allocated copy of the current token in the
1513  * parser context. The function perform conversion of escapes if
1514  * the token is of type JIM_TT_ESC.
1515  *
1516  * Note that after the conversion, tokens that are grouped with
1517  * braces in the source code, are always recognizable from the
1518  * identical string obtained in a different way from the type.
1519  *
1520  * For exmple the string:
1521  *
1522  * {expand}$a
1523  * 
1524  * will return as first token "expand", of type JIM_TT_STR
1525  *
1526  * While the string:
1527  *
1528  * expand$a
1529  *
1530  * will return as first token "expand", of type JIM_TT_ESC
1531  */
1532 char *JimParserGetToken(struct JimParserCtx *pc,
1533         int *lenPtr, int *typePtr, int *linePtr)
1534 {
1535     const char *start, *end;
1536     char *token;
1537     int len;
1538
1539     start = JimParserTstart(pc);
1540     end = JimParserTend(pc);
1541     if (start > end) {
1542         if (lenPtr) *lenPtr = 0;
1543         if (typePtr) *typePtr = JimParserTtype(pc);
1544         if (linePtr) *linePtr = JimParserTline(pc);
1545         token = Jim_Alloc(1);
1546         token[0] = '\0';
1547         return token;
1548     }
1549     len = (end-start)+1;
1550     token = Jim_Alloc(len+1);
1551     if (JimParserTtype(pc) != JIM_TT_ESC) {
1552         /* No escape conversion needed? Just copy it. */
1553         memcpy(token, start, len);
1554         token[len] = '\0';
1555     } else {
1556         /* Else convert the escape chars. */
1557         len = JimEscape(token, start, len);
1558     }
1559     if (lenPtr) *lenPtr = len;
1560     if (typePtr) *typePtr = JimParserTtype(pc);
1561     if (linePtr) *linePtr = JimParserTline(pc);
1562     return token;
1563 }
1564
1565 /* The following functin is not really part of the parsing engine of Jim,
1566  * but it somewhat related. Given an string and its length, it tries
1567  * to guess if the script is complete or there are instead " " or { }
1568  * open and not completed. This is useful for interactive shells
1569  * implementation and for [info complete].
1570  *
1571  * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1572  * '{' on scripts incomplete missing one or more '}' to be balanced.
1573  * '"' on scripts incomplete missing a '"' char.
1574  *
1575  * If the script is complete, 1 is returned, otherwise 0. */
1576 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1577 {
1578     int level = 0;
1579     int state = ' ';
1580
1581     while(len) {
1582         switch (*s) {
1583             case '\\':
1584                 if (len > 1)
1585                     s++;
1586                 break;
1587             case '"':
1588                 if (state == ' ') {
1589                     state = '"';
1590                 } else if (state == '"') {
1591                     state = ' ';
1592                 }
1593                 break;
1594             case '{':
1595                 if (state == '{') {
1596                     level++;
1597                 } else if (state == ' ') {
1598                     state = '{';
1599                     level++;
1600                 }
1601                 break;
1602             case '}':
1603                 if (state == '{') {
1604                     level--;
1605                     if (level == 0)
1606                         state = ' ';
1607                 }
1608                 break;
1609         }
1610         s++;
1611         len--;
1612     }
1613     if (stateCharPtr)
1614         *stateCharPtr = state;
1615     return state == ' ';
1616 }
1617
1618 /* -----------------------------------------------------------------------------
1619  * Tcl Lists parsing
1620  * ---------------------------------------------------------------------------*/
1621 static int JimParseListSep(struct JimParserCtx *pc);
1622 static int JimParseListStr(struct JimParserCtx *pc);
1623
1624 int JimParseList(struct JimParserCtx *pc)
1625 {
1626     if (pc->len == 0) {
1627         pc->tstart = pc->tend = pc->p;
1628         pc->tline = pc->linenr;
1629         pc->tt = JIM_TT_EOL;
1630         pc->eof = 1;
1631         return JIM_OK;
1632     }
1633     switch(*pc->p) {
1634     case ' ':
1635     case '\n':
1636     case '\t':
1637     case '\r':
1638         if (pc->state == JIM_PS_DEF)
1639             return JimParseListSep(pc);
1640         else
1641             return JimParseListStr(pc);
1642         break;
1643     default:
1644         return JimParseListStr(pc);
1645         break;
1646     }
1647     return JIM_OK;
1648 }
1649
1650 int JimParseListSep(struct JimParserCtx *pc)
1651 {
1652     pc->tstart = pc->p;
1653     pc->tline = pc->linenr;
1654     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1655     {
1656         pc->p++; pc->len--;
1657     }
1658     pc->tend = pc->p-1;
1659     pc->tt = JIM_TT_SEP;
1660     return JIM_OK;
1661 }
1662
1663 int JimParseListStr(struct JimParserCtx *pc)
1664 {
1665     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1666             pc->tt == JIM_TT_NONE);
1667     if (newword && *pc->p == '{') {
1668         return JimParseBrace(pc);
1669     } else if (newword && *pc->p == '"') {
1670         pc->state = JIM_PS_QUOTE;
1671         pc->p++; pc->len--;
1672     }
1673     pc->tstart = pc->p;
1674     pc->tline = pc->linenr;
1675     while (1) {
1676         if (pc->len == 0) {
1677             pc->tend = pc->p-1;
1678             pc->tt = JIM_TT_ESC;
1679             return JIM_OK;
1680         }
1681         switch(*pc->p) {
1682         case '\\':
1683             pc->p++; pc->len--;
1684             break;
1685         case ' ':
1686         case '\t':
1687         case '\n':
1688         case '\r':
1689             if (pc->state == JIM_PS_DEF) {
1690                 pc->tend = pc->p-1;
1691                 pc->tt = JIM_TT_ESC;
1692                 return JIM_OK;
1693             } else if (*pc->p == '\n') {
1694                 pc->linenr++;
1695             }
1696             break;
1697         case '"':
1698             if (pc->state == JIM_PS_QUOTE) {
1699                 pc->tend = pc->p-1;
1700                 pc->tt = JIM_TT_ESC;
1701                 pc->p++; pc->len--;
1702                 pc->state = JIM_PS_DEF;
1703                 return JIM_OK;
1704             }
1705             break;
1706         }
1707         pc->p++; pc->len--;
1708     }
1709     return JIM_OK; /* unreached */
1710 }
1711
1712 /* -----------------------------------------------------------------------------
1713  * Jim_Obj related functions
1714  * ---------------------------------------------------------------------------*/
1715
1716 /* Return a new initialized object. */
1717 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1718 {
1719     Jim_Obj *objPtr;
1720
1721     /* -- Check if there are objects in the free list -- */
1722     if (interp->freeList != NULL) {
1723         /* -- Unlink the object from the free list -- */
1724         objPtr = interp->freeList;
1725         interp->freeList = objPtr->nextObjPtr;
1726     } else {
1727         /* -- No ready to use objects: allocate a new one -- */
1728         objPtr = Jim_Alloc(sizeof(*objPtr));
1729     }
1730
1731     /* Object is returned with refCount of 0. Every
1732      * kind of GC implemented should take care to don't try
1733      * to scan objects with refCount == 0. */
1734     objPtr->refCount = 0;
1735     /* All the other fields are left not initialized to save time.
1736      * The caller will probably want set they to the right
1737      * value anyway. */
1738
1739     /* -- Put the object into the live list -- */
1740     objPtr->prevObjPtr = NULL;
1741     objPtr->nextObjPtr = interp->liveList;
1742     if (interp->liveList)
1743         interp->liveList->prevObjPtr = objPtr;
1744     interp->liveList = objPtr;
1745
1746     return objPtr;
1747 }
1748
1749 /* Free an object. Actually objects are never freed, but
1750  * just moved to the free objects list, where they will be
1751  * reused by Jim_NewObj(). */
1752 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1753 {
1754     /* Check if the object was already freed, panic. */
1755     if (objPtr->refCount != 0)  {
1756         Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1757                 objPtr->refCount);
1758     }
1759     /* Free the internal representation */
1760     Jim_FreeIntRep(interp, objPtr);
1761     /* Free the string representation */
1762     if (objPtr->bytes != NULL) {
1763         if (objPtr->bytes != JimEmptyStringRep)
1764             Jim_Free(objPtr->bytes);
1765     }
1766     /* Unlink the object from the live objects list */
1767     if (objPtr->prevObjPtr)
1768         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1769     if (objPtr->nextObjPtr)
1770         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1771     if (interp->liveList == objPtr)
1772         interp->liveList = objPtr->nextObjPtr;
1773     /* Link the object into the free objects list */
1774     objPtr->prevObjPtr = NULL;
1775     objPtr->nextObjPtr = interp->freeList;
1776     if (interp->freeList)
1777         interp->freeList->prevObjPtr = objPtr;
1778     interp->freeList = objPtr;
1779     objPtr->refCount = -1;
1780 }
1781
1782 /* Invalidate the string representation of an object. */
1783 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1784 {
1785     if (objPtr->bytes != NULL) {
1786         if (objPtr->bytes != JimEmptyStringRep)
1787             Jim_Free(objPtr->bytes);
1788     }
1789     objPtr->bytes = NULL;
1790 }
1791
1792 #define Jim_SetStringRep(o, b, l) \
1793     do { (o)->bytes = b; (o)->length = l; } while (0)
1794
1795 /* Set the initial string representation for an object.
1796  * Does not try to free an old one. */
1797 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1798 {
1799     if (length == 0) {
1800         objPtr->bytes = JimEmptyStringRep;
1801         objPtr->length = 0;
1802     } else {
1803         objPtr->bytes = Jim_Alloc(length+1);
1804         objPtr->length = length;
1805         memcpy(objPtr->bytes, bytes, length);
1806         objPtr->bytes[length] = '\0';
1807     }
1808 }
1809
1810 /* Duplicate an object. The returned object has refcount = 0. */
1811 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1812 {
1813     Jim_Obj *dupPtr;
1814
1815     dupPtr = Jim_NewObj(interp);
1816     if (objPtr->bytes == NULL) {
1817         /* Object does not have a valid string representation. */
1818         dupPtr->bytes = NULL;
1819     } else {
1820         Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1821     }
1822     if (objPtr->typePtr != NULL) {
1823         if (objPtr->typePtr->dupIntRepProc == NULL) {
1824             dupPtr->internalRep = objPtr->internalRep;
1825         } else {
1826             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1827         }
1828         dupPtr->typePtr = objPtr->typePtr;
1829     } else {
1830         dupPtr->typePtr = NULL;
1831     }
1832     return dupPtr;
1833 }
1834
1835 /* Return the string representation for objPtr. If the object
1836  * string representation is invalid, calls the method to create
1837  * a new one starting from the internal representation of the object. */
1838 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1839 {
1840     if (objPtr->bytes == NULL) {
1841         /* Invalid string repr. Generate it. */
1842         if (objPtr->typePtr->updateStringProc == NULL) {
1843             Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1844                 objPtr->typePtr->name);
1845         }
1846         objPtr->typePtr->updateStringProc(objPtr);
1847     }
1848     if (lenPtr)
1849         *lenPtr = objPtr->length;
1850     return objPtr->bytes;
1851 }
1852
1853 /* Just returns the length of the object's string rep */
1854 int Jim_Length(Jim_Obj *objPtr)
1855 {
1856     int len;
1857
1858     Jim_GetString(objPtr, &len);
1859     return len;
1860 }
1861
1862 /* -----------------------------------------------------------------------------
1863  * String Object
1864  * ---------------------------------------------------------------------------*/
1865 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1866 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1867
1868 static Jim_ObjType stringObjType = {
1869     "string",
1870     NULL,
1871     DupStringInternalRep,
1872     NULL,
1873     JIM_TYPE_REFERENCES,
1874 };
1875
1876 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1877 {
1878     JIM_NOTUSED(interp);
1879
1880     /* This is a bit subtle: the only caller of this function
1881      * should be Jim_DuplicateObj(), that will copy the
1882      * string representaion. After the copy, the duplicated
1883      * object will not have more room in teh buffer than
1884      * srcPtr->length bytes. So we just set it to length. */
1885     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1886 }
1887
1888 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1889 {
1890     /* Get a fresh string representation. */
1891     (void) Jim_GetString(objPtr, NULL);
1892     /* Free any other internal representation. */
1893     Jim_FreeIntRep(interp, objPtr);
1894     /* Set it as string, i.e. just set the maxLength field. */
1895     objPtr->typePtr = &stringObjType;
1896     objPtr->internalRep.strValue.maxLength = objPtr->length;
1897     return JIM_OK;
1898 }
1899
1900 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1901 {
1902     Jim_Obj *objPtr = Jim_NewObj(interp);
1903
1904     if (len == -1)
1905         len = strlen(s);
1906     /* Alloc/Set the string rep. */
1907     if (len == 0) {
1908         objPtr->bytes = JimEmptyStringRep;
1909         objPtr->length = 0;
1910     } else {
1911         objPtr->bytes = Jim_Alloc(len+1);
1912         objPtr->length = len;
1913         memcpy(objPtr->bytes, s, len);
1914         objPtr->bytes[len] = '\0';
1915     }
1916
1917     /* No typePtr field for the vanilla string object. */
1918     objPtr->typePtr = NULL;
1919     return objPtr;
1920 }
1921
1922 /* This version does not try to duplicate the 's' pointer, but
1923  * use it directly. */
1924 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
1925 {
1926     Jim_Obj *objPtr = Jim_NewObj(interp);
1927
1928     if (len == -1)
1929         len = strlen(s);
1930     Jim_SetStringRep(objPtr, s, len);
1931     objPtr->typePtr = NULL;
1932     return objPtr;
1933 }
1934
1935 /* Low-level string append. Use it only against objects
1936  * of type "string". */
1937 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
1938 {
1939     int needlen;
1940
1941     if (len == -1)
1942         len = strlen(str);
1943     needlen = objPtr->length + len;
1944     if (objPtr->internalRep.strValue.maxLength < needlen ||
1945         objPtr->internalRep.strValue.maxLength == 0) {
1946         if (objPtr->bytes == JimEmptyStringRep) {
1947             objPtr->bytes = Jim_Alloc((needlen*2)+1);
1948         } else {
1949             objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
1950         }
1951         objPtr->internalRep.strValue.maxLength = needlen*2;
1952     }
1953     memcpy(objPtr->bytes + objPtr->length, str, len);
1954     objPtr->bytes[objPtr->length+len] = '\0';
1955     objPtr->length += len;
1956 }
1957
1958 /* Low-level wrapper to append an object. */
1959 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
1960 {
1961     int len;
1962     const char *str;
1963
1964     str = Jim_GetString(appendObjPtr, &len);
1965     StringAppendString(objPtr, str, len);
1966 }
1967
1968 /* Higher level API to append strings to objects. */
1969 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
1970         int len)
1971 {
1972     if (Jim_IsShared(objPtr))
1973         Jim_Panic(interp,"Jim_AppendString called with shared object");
1974     if (objPtr->typePtr != &stringObjType)
1975         SetStringFromAny(interp, objPtr);
1976     StringAppendString(objPtr, str, len);
1977 }
1978
1979 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
1980         Jim_Obj *appendObjPtr)
1981 {
1982     int len;
1983     const char *str;
1984
1985     str = Jim_GetString(appendObjPtr, &len);
1986     Jim_AppendString(interp, objPtr, str, len);
1987 }
1988
1989 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
1990 {
1991     va_list ap;
1992
1993     if (objPtr->typePtr != &stringObjType)
1994         SetStringFromAny(interp, objPtr);
1995     va_start(ap, objPtr);
1996     while (1) {
1997         char *s = va_arg(ap, char*);
1998
1999         if (s == NULL) break;
2000         Jim_AppendString(interp, objPtr, s, -1);
2001     }
2002     va_end(ap);
2003 }
2004
2005 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2006 {
2007     const char *aStr, *bStr;
2008     int aLen, bLen, i;
2009
2010     if (aObjPtr == bObjPtr) return 1;
2011     aStr = Jim_GetString(aObjPtr, &aLen);
2012     bStr = Jim_GetString(bObjPtr, &bLen);
2013     if (aLen != bLen) return 0;
2014     if (nocase == 0)
2015         return memcmp(aStr, bStr, aLen) == 0;
2016     for (i = 0; i < aLen; i++) {
2017         if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2018             return 0;
2019     }
2020     return 1;
2021 }
2022
2023 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2024         int nocase)
2025 {
2026     const char *pattern, *string;
2027     int patternLen, stringLen;
2028
2029     pattern = Jim_GetString(patternObjPtr, &patternLen);
2030     string = Jim_GetString(objPtr, &stringLen);
2031     return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2032 }
2033
2034 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2035         Jim_Obj *secondObjPtr, int nocase)
2036 {
2037     const char *s1, *s2;
2038     int l1, l2;
2039
2040     s1 = Jim_GetString(firstObjPtr, &l1);
2041     s2 = Jim_GetString(secondObjPtr, &l2);
2042     return JimStringCompare(s1, l1, s2, l2, nocase);
2043 }
2044
2045 /* Convert a range, as returned by Jim_GetRange(), into
2046  * an absolute index into an object of the specified length.
2047  * This function may return negative values, or values
2048  * bigger or equal to the length of the list if the index
2049  * is out of range. */
2050 static int JimRelToAbsIndex(int len, int index)
2051 {
2052     if (index < 0)
2053         return len + index;
2054     return index;
2055 }
2056
2057 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2058  * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2059  * for implementation of commands like [string range] and [lrange].
2060  *
2061  * The resulting range is guaranteed to address valid elements of
2062  * the structure. */
2063 static void JimRelToAbsRange(int len, int first, int last,
2064         int *firstPtr, int *lastPtr, int *rangeLenPtr)
2065 {
2066     int rangeLen;
2067
2068     if (first > last) {
2069         rangeLen = 0;
2070     } else {
2071         rangeLen = last-first+1;
2072         if (rangeLen) {
2073             if (first < 0) {
2074                 rangeLen += first;
2075                 first = 0;
2076             }
2077             if (last >= len) {
2078                 rangeLen -= (last-(len-1));
2079                 last = len-1;
2080             }
2081         }
2082     }
2083     if (rangeLen < 0) rangeLen = 0;
2084
2085     *firstPtr = first;
2086     *lastPtr = last;
2087     *rangeLenPtr = rangeLen;
2088 }
2089
2090 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2091         Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2092 {
2093     int first, last;
2094     const char *str;
2095     int len, rangeLen;
2096
2097     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2098         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2099         return NULL;
2100     str = Jim_GetString(strObjPtr, &len);
2101     first = JimRelToAbsIndex(len, first);
2102     last = JimRelToAbsIndex(len, last);
2103     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2104     return Jim_NewStringObj(interp, str+first, rangeLen);
2105 }
2106
2107 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2108 {
2109     char *buf = Jim_Alloc(strObjPtr->length+1);
2110     int i;
2111
2112     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2113     for (i = 0; i < strObjPtr->length; i++)
2114         buf[i] = tolower(buf[i]);
2115     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2116 }
2117
2118 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2119 {
2120     char *buf = Jim_Alloc(strObjPtr->length+1);
2121     int i;
2122
2123     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2124     for (i = 0; i < strObjPtr->length; i++)
2125         buf[i] = toupper(buf[i]);
2126     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2127 }
2128
2129 /* This is the core of the [format] command.
2130  * TODO: Export it, make it real... for now only %s and %%
2131  * specifiers supported. */
2132 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2133         int objc, Jim_Obj *const *objv)
2134 {
2135     const char *fmt;
2136     int fmtLen;
2137     Jim_Obj *resObjPtr;
2138
2139     fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2140     resObjPtr = Jim_NewStringObj(interp, "", 0);
2141     while (fmtLen) {
2142         const char *p = fmt;
2143         char spec[2], c;
2144         jim_wide wideValue;
2145
2146         while (*fmt != '%' && fmtLen) {
2147             fmt++; fmtLen--;
2148         }
2149         Jim_AppendString(interp, resObjPtr, p, fmt-p);
2150         if (fmtLen == 0)
2151             break;
2152         fmt++; fmtLen--; /* skip '%' */
2153         if (*fmt != '%') {
2154             if (objc == 0) {
2155                 Jim_FreeNewObj(interp, resObjPtr);
2156                 Jim_SetResultString(interp,
2157                         "not enough arguments for all format specifiers", -1);
2158                 return NULL;
2159             } else {
2160                 objc--;
2161             }
2162         }
2163         switch(*fmt) {
2164         case 's':
2165             Jim_AppendObj(interp, resObjPtr, objv[0]);
2166             objv++;
2167             break;
2168         case 'c':
2169             if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2170                 Jim_FreeNewObj(interp, resObjPtr);
2171                 return NULL;
2172             }
2173             c = (char) wideValue;
2174             Jim_AppendString(interp, resObjPtr, &c, 1);
2175             break;
2176         case '%':
2177             Jim_AppendString(interp, resObjPtr, "%" , 1);
2178             break;
2179         default:
2180             spec[0] = *fmt; spec[1] = '\0';
2181             Jim_FreeNewObj(interp, resObjPtr);
2182             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2183             Jim_AppendStrings(interp, Jim_GetResult(interp),
2184                     "bad field specifier \"",  spec, "\"", NULL);
2185             return NULL;
2186         }
2187         fmt++;
2188         fmtLen--;
2189     }
2190     return resObjPtr;
2191 }
2192
2193 /* -----------------------------------------------------------------------------
2194  * Compared String Object
2195  * ---------------------------------------------------------------------------*/
2196
2197 /* This is strange object that allows to compare a C literal string
2198  * with a Jim object in very short time if the same comparison is done
2199  * multiple times. For example every time the [if] command is executed,
2200  * Jim has to check if a given argument is "else". This comparions if
2201  * the code has no errors are true most of the times, so we can cache
2202  * inside the object the pointer of the string of the last matching
2203  * comparison. Because most C compilers perform literal sharing,
2204  * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2205  * this works pretty well even if comparisons are at different places
2206  * inside the C code. */
2207
2208 static Jim_ObjType comparedStringObjType = {
2209     "compared-string",
2210     NULL,
2211     NULL,
2212     NULL,
2213     JIM_TYPE_REFERENCES,
2214 };
2215
2216 /* The only way this object is exposed to the API is via the following
2217  * function. Returns true if the string and the object string repr.
2218  * are the same, otherwise zero is returned.
2219  *
2220  * Note: this isn't binary safe, but it hardly needs to be.*/
2221 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2222         const char *str)
2223 {
2224     if (objPtr->typePtr == &comparedStringObjType &&
2225         objPtr->internalRep.ptr == str)
2226         return 1;
2227     else {
2228         const char *objStr = Jim_GetString(objPtr, NULL);
2229         if (strcmp(str, objStr) != 0) return 0;
2230         if (objPtr->typePtr != &comparedStringObjType) {
2231             Jim_FreeIntRep(interp, objPtr);
2232             objPtr->typePtr = &comparedStringObjType;
2233         }
2234         objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2235         return 1;
2236     }
2237 }
2238
2239 int qsortCompareStringPointers(const void *a, const void *b)
2240 {
2241     char * const *sa = (char * const *)a;
2242     char * const *sb = (char * const *)b;
2243     return strcmp(*sa, *sb);
2244 }
2245
2246 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2247         const char **tablePtr, int *indexPtr, const char *name, int flags)
2248 {
2249     const char **entryPtr = NULL;
2250     char **tablePtrSorted;
2251     int i, count = 0;
2252
2253     *indexPtr = -1;
2254     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2255         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2256             *indexPtr = i;
2257             return JIM_OK;
2258         }
2259         count++; /* If nothing matches, this will reach the len of tablePtr */
2260     }
2261     if (flags & JIM_ERRMSG) {
2262         if (name == NULL)
2263             name = "option";
2264         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2265         Jim_AppendStrings(interp, Jim_GetResult(interp),
2266             "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2267             NULL);
2268         tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2269         memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2270         qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2271         for (i = 0; i < count; i++) {
2272             if (i+1 == count && count > 1)
2273                 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2274             Jim_AppendString(interp, Jim_GetResult(interp),
2275                     tablePtrSorted[i], -1);
2276             if (i+1 != count)
2277                 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2278         }
2279         Jim_Free(tablePtrSorted);
2280     }
2281     return JIM_ERR;
2282 }
2283
2284 /* -----------------------------------------------------------------------------
2285  * Source Object
2286  *
2287  * This object is just a string from the language point of view, but
2288  * in the internal representation it contains the filename and line number
2289  * where this given token was read. This information is used by
2290  * Jim_EvalObj() if the object passed happens to be of type "source".
2291  *
2292  * This allows to propagate the information about line numbers and file
2293  * names and give error messages with absolute line numbers.
2294  *
2295  * Note that this object uses shared strings for filenames, and the
2296  * pointer to the filename together with the line number is taken into
2297  * the space for the "inline" internal represenation of the Jim_Object,
2298  * so there is almost memory zero-overhead.
2299  *
2300  * Also the object will be converted to something else if the given
2301  * token it represents in the source file is not something to be
2302  * evaluated (not a script), and will be specialized in some other way,
2303  * so the time overhead is alzo null.
2304  * ---------------------------------------------------------------------------*/
2305
2306 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2307 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2308
2309 static Jim_ObjType sourceObjType = {
2310     "source",
2311     FreeSourceInternalRep,
2312     DupSourceInternalRep,
2313     NULL,
2314     JIM_TYPE_REFERENCES,
2315 };
2316
2317 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2318 {
2319     Jim_ReleaseSharedString(interp,
2320             objPtr->internalRep.sourceValue.fileName);
2321 }
2322
2323 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2324 {
2325     dupPtr->internalRep.sourceValue.fileName =
2326         Jim_GetSharedString(interp,
2327                 srcPtr->internalRep.sourceValue.fileName);
2328     dupPtr->internalRep.sourceValue.lineNumber =
2329         dupPtr->internalRep.sourceValue.lineNumber;
2330     dupPtr->typePtr = &sourceObjType;
2331 }
2332
2333 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2334         const char *fileName, int lineNumber)
2335 {
2336     if (Jim_IsShared(objPtr))
2337         Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2338     if (objPtr->typePtr != NULL)
2339         Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2340     objPtr->internalRep.sourceValue.fileName =
2341         Jim_GetSharedString(interp, fileName);
2342     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2343     objPtr->typePtr = &sourceObjType;
2344 }
2345
2346 /* -----------------------------------------------------------------------------
2347  * Script Object
2348  * ---------------------------------------------------------------------------*/
2349
2350 #define JIM_CMDSTRUCT_EXPAND -1
2351
2352 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2353 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2354 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2355
2356 static Jim_ObjType scriptObjType = {
2357     "script",
2358     FreeScriptInternalRep,
2359     DupScriptInternalRep,
2360     NULL,
2361     JIM_TYPE_REFERENCES,
2362 };
2363
2364 /* The ScriptToken structure represents every token into a scriptObj.
2365  * Every token contains an associated Jim_Obj that can be specialized
2366  * by commands operating on it. */
2367 typedef struct ScriptToken {
2368     int type;
2369     Jim_Obj *objPtr;
2370     int linenr;
2371 } ScriptToken;
2372
2373 /* This is the script object internal representation. An array of
2374  * ScriptToken structures, with an associated command structure array.
2375  * The command structure is a pre-computed representation of the
2376  * command length and arguments structure as a simple liner array
2377  * of integers.
2378  * 
2379  * For example the script:
2380  *
2381  * puts hello
2382  * set $i $x$y [foo]BAR
2383  *
2384  * will produce a ScriptObj with the following Tokens:
2385  *
2386  * ESC puts
2387  * SEP
2388  * ESC hello
2389  * EOL
2390  * ESC set
2391  * EOL
2392  * VAR i
2393  * SEP
2394  * VAR x
2395  * VAR y
2396  * SEP
2397  * CMD foo
2398  * ESC BAR
2399  * EOL
2400  *
2401  * This is a description of the tokens, separators, and of lines.
2402  * The command structure instead represents the number of arguments
2403  * of every command, followed by the tokens of which every argument
2404  * is composed. So for the example script, the cmdstruct array will
2405  * contain:
2406  *
2407  * 2 1 1 4 1 1 2 2
2408  *
2409  * Because "puts hello" has two args (2), composed of single tokens (1 1)
2410  * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2411  * composed of single tokens (1 1) and the last two of double tokens
2412  * (2 2).
2413  *
2414  * The precomputation of the command structure makes Jim_Eval() faster,
2415  * and simpler because there aren't dynamic lengths / allocations.
2416  *
2417  * -- {expand} handling --
2418  *
2419  * Expand is handled in a special way. When a command
2420  * contains at least an argument with the {expand} prefix,
2421  * the command structure presents a -1 before the integer
2422  * describing the number of arguments. This is used in order
2423  * to send the command exection to a different path in case
2424  * of {expand} and guarantee a fast path for the more common
2425  * case. Also, the integers describing the number of tokens
2426  * are expressed with negative sign, to allow for fast check
2427  * of what's an {expand}-prefixed argument and what not.
2428  *
2429  * For example the command:
2430  *
2431  * list {expand}{1 2}
2432  *
2433  * Will produce the following cmdstruct array:
2434  *
2435  * -1 2 1 -2
2436  *
2437  * -- the substFlags field of the structure --
2438  *
2439  * The scriptObj structure is used to represent both "script" objects
2440  * and "subst" objects. In the second case, the cmdStruct related
2441  * fields are not used at all, but there is an additional field used
2442  * that is 'substFlags': this represents the flags used to turn
2443  * the string into the intenral representation used to perform the
2444  * substitution. If this flags are not what the application requires
2445  * the scriptObj is created again. For example the script:
2446  *
2447  * subst -nocommands $string
2448  * subst -novariables $string
2449  *
2450  * Will recreate the internal representation of the $string object
2451  * two times.
2452  */
2453 typedef struct ScriptObj {
2454     int len; /* Length as number of tokens. */
2455     int commands; /* number of top-level commands in script. */
2456     ScriptToken *token; /* Tokens array. */
2457     int *cmdStruct; /* commands structure */
2458     int csLen; /* length of the cmdStruct array. */
2459     int substFlags; /* flags used for the compilation of "subst" objects */
2460     int inUse; /* Used to share a ScriptObj. Currently
2461               only used by Jim_EvalObj() as protection against
2462               shimmering of the currently evaluated object. */
2463     char *fileName;
2464 } ScriptObj;
2465
2466 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2467 {
2468     int i;
2469     struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2470
2471     script->inUse--;
2472     if (script->inUse != 0) return;
2473     for (i = 0; i < script->len; i++) {
2474         if (script->token[i].objPtr != NULL)
2475             Jim_DecrRefCount(interp, script->token[i].objPtr);
2476     }
2477     Jim_Free(script->token);
2478     Jim_Free(script->cmdStruct);
2479     Jim_Free(script->fileName);
2480     Jim_Free(script);
2481 }
2482
2483 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2484 {
2485     JIM_NOTUSED(interp);
2486     JIM_NOTUSED(srcPtr);
2487
2488     /* Just returns an simple string. */
2489     dupPtr->typePtr = NULL;
2490 }
2491
2492 /* Add a new token to the internal repr of a script object */
2493 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2494         char *strtoken, int len, int type, char *filename, int linenr)
2495 {
2496     int prevtype;
2497     struct ScriptToken *token;
2498
2499     prevtype = (script->len == 0) ? JIM_TT_EOL : \
2500         script->token[script->len-1].type;
2501     /* Skip tokens without meaning, like words separators
2502      * following a word separator or an end of command and
2503      * so on. */
2504     if (prevtype == JIM_TT_EOL) {
2505         if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2506             Jim_Free(strtoken);
2507             return;
2508         }
2509     } else if (prevtype == JIM_TT_SEP) {
2510         if (type == JIM_TT_SEP) {
2511             Jim_Free(strtoken);
2512             return;
2513         } else if (type == JIM_TT_EOL) {
2514             /* If an EOL is following by a SEP, drop the previous
2515              * separator. */
2516             script->len--;
2517             Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2518         }
2519     } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2520             type == JIM_TT_ESC && len == 0)
2521     {
2522         /* Don't add empty tokens used in interpolation */
2523         Jim_Free(strtoken);
2524         return;
2525     }
2526     /* Make space for a new istruction */
2527     script->len++;
2528     script->token = Jim_Realloc(script->token,
2529             sizeof(ScriptToken)*script->len);
2530     /* Initialize the new token */
2531     token = script->token+(script->len-1);
2532     token->type = type;
2533     /* Every object is intially as a string, but the
2534      * internal type may be specialized during execution of the
2535      * script. */
2536     token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2537     /* To add source info to SEP and EOL tokens is useless because
2538      * they will never by called as arguments of Jim_EvalObj(). */
2539     if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2540         JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2541     Jim_IncrRefCount(token->objPtr);
2542     token->linenr = linenr;
2543 }
2544
2545 /* Add an integer into the command structure field of the script object. */
2546 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2547 {
2548     script->csLen++;
2549     script->cmdStruct = Jim_Realloc(script->cmdStruct,
2550                     sizeof(int)*script->csLen);
2551     script->cmdStruct[script->csLen-1] = val;
2552 }
2553
2554 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2555  * of objPtr. Search nested script objects recursively. */
2556 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2557         ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2558 {
2559     int i;
2560
2561     for (i = 0; i < script->len; i++) {
2562         if (script->token[i].objPtr != objPtr &&
2563             Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2564             return script->token[i].objPtr;
2565         }
2566         /* Enter recursively on scripts only if the object
2567          * is not the same as the one we are searching for
2568          * shared occurrences. */
2569         if (script->token[i].objPtr->typePtr == &scriptObjType &&
2570             script->token[i].objPtr != objPtr) {
2571             Jim_Obj *foundObjPtr;
2572
2573             ScriptObj *subScript =
2574                 script->token[i].objPtr->internalRep.ptr;
2575             /* Don't recursively enter the script we are trying
2576              * to make shared to avoid circular references. */
2577             if (subScript == scriptBarrier) continue;
2578             if (subScript != script) {
2579                 foundObjPtr =
2580                     ScriptSearchLiteral(interp, subScript,
2581                             scriptBarrier, objPtr);
2582                 if (foundObjPtr != NULL)
2583                     return foundObjPtr;
2584             }
2585         }
2586     }
2587     return NULL;
2588 }
2589
2590 /* Share literals of a script recursively sharing sub-scripts literals. */
2591 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2592         ScriptObj *topLevelScript)
2593 {
2594     int i, j;
2595
2596     return;
2597     /* Try to share with toplevel object. */
2598     if (topLevelScript != NULL) {
2599         for (i = 0; i < script->len; i++) {
2600             Jim_Obj *foundObjPtr;
2601             char *str = script->token[i].objPtr->bytes;
2602
2603             if (script->token[i].objPtr->refCount != 1) continue;
2604             if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2605             if (strchr(str, ' ') || strchr(str, '\n')) continue;
2606             foundObjPtr = ScriptSearchLiteral(interp,
2607                     topLevelScript,
2608                     script, /* barrier */
2609                     script->token[i].objPtr);
2610             if (foundObjPtr != NULL) {
2611                 Jim_IncrRefCount(foundObjPtr);
2612                 Jim_DecrRefCount(interp,
2613                         script->token[i].objPtr);
2614                 script->token[i].objPtr = foundObjPtr;
2615             }
2616         }
2617     }
2618     /* Try to share locally */
2619     for (i = 0; i < script->len; i++) {
2620         char *str = script->token[i].objPtr->bytes;
2621
2622         if (script->token[i].objPtr->refCount != 1) continue;
2623         if (strchr(str, ' ') || strchr(str, '\n')) continue;
2624         for (j = 0; j < script->len; j++) {
2625             if (script->token[i].objPtr !=
2626                     script->token[j].objPtr &&
2627                 Jim_StringEqObj(script->token[i].objPtr,
2628                             script->token[j].objPtr, 0))
2629             {
2630                 Jim_IncrRefCount(script->token[j].objPtr);
2631                 Jim_DecrRefCount(interp,
2632                         script->token[i].objPtr);
2633                 script->token[i].objPtr =
2634                     script->token[j].objPtr;
2635             }
2636         }
2637     }
2638 }
2639
2640 /* This method takes the string representation of an object
2641  * as a Tcl script, and generates the pre-parsed internal representation
2642  * of the script. */
2643 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2644 {
2645     int scriptTextLen;
2646     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2647     struct JimParserCtx parser;
2648     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2649     ScriptToken *token;
2650     int args, tokens, start, end, i;
2651     int initialLineNumber;
2652     int propagateSourceInfo = 0;
2653
2654     script->len = 0;
2655     script->csLen = 0;
2656     script->commands = 0;
2657     script->token = NULL;
2658     script->cmdStruct = NULL;
2659     script->inUse = 1;
2660     /* Try to get information about filename / line number */
2661     if (objPtr->typePtr == &sourceObjType) {
2662         script->fileName =
2663             Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2664         initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2665         propagateSourceInfo = 1;
2666     } else {
2667         script->fileName = Jim_StrDup("?");
2668         initialLineNumber = 1;
2669     }
2670
2671     JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2672     while(!JimParserEof(&parser)) {
2673         char *token;
2674         int len, type, linenr;
2675
2676         JimParseScript(&parser);
2677         token = JimParserGetToken(&parser, &len, &type, &linenr);
2678         ScriptObjAddToken(interp, script, token, len, type,
2679                 propagateSourceInfo ? script->fileName : NULL,
2680                 linenr);
2681     }
2682     token = script->token;
2683
2684     /* Compute the command structure array
2685      * (see the ScriptObj struct definition for more info) */
2686     start = 0; /* Current command start token index */
2687     end = -1; /* Current command end token index */
2688     while (1) {
2689         int expand = 0; /* expand flag. set to 1 on {expand} form. */
2690         int interpolation = 0; /* set to 1 if there is at least one
2691                       argument of the command obtained via
2692                       interpolation of more tokens. */
2693         /* Search for the end of command, while
2694          * count the number of args. */
2695         start = ++end;
2696         if (start >= script->len) break;
2697         args = 1; /* Number of args in current command */
2698         while (token[end].type != JIM_TT_EOL) {
2699             if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2700                     token[end-1].type == JIM_TT_EOL)
2701             {
2702                 if (token[end].type == JIM_TT_STR &&
2703                     token[end+1].type != JIM_TT_SEP &&
2704                     token[end+1].type != JIM_TT_EOL &&
2705                     (!strcmp(token[end].objPtr->bytes, "expand") ||
2706                      !strcmp(token[end].objPtr->bytes, "*")))
2707                     expand++;
2708             }
2709             if (token[end].type == JIM_TT_SEP)
2710                 args++;
2711             end++;
2712         }
2713         interpolation = !((end-start+1) == args*2);
2714         /* Add the 'number of arguments' info into cmdstruct.
2715          * Negative value if there is list expansion involved. */
2716         if (expand)
2717             ScriptObjAddInt(script, -1);
2718         ScriptObjAddInt(script, args);
2719         /* Now add info about the number of tokens. */
2720         tokens = 0; /* Number of tokens in current argument. */
2721         expand = 0;
2722         for (i = start; i <= end; i++) {
2723             if (token[i].type == JIM_TT_SEP ||
2724                 token[i].type == JIM_TT_EOL)
2725             {
2726                 if (tokens == 1 && expand)
2727                     expand = 0;
2728                 ScriptObjAddInt(script,
2729                         expand ? -tokens : tokens);
2730
2731                 expand = 0;
2732                 tokens = 0;
2733                 continue;
2734             } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2735                    (!strcmp(token[i].objPtr->bytes, "expand") ||
2736                     !strcmp(token[i].objPtr->bytes, "*")))
2737             {
2738                 expand++;
2739             }
2740             tokens++;
2741         }
2742     }
2743     /* Perform literal sharing, but only for objects that appear
2744      * to be scripts written as literals inside the source code,
2745      * and not computed at runtime. Literal sharing is a costly
2746      * operation that should be done only against objects that
2747      * are likely to require compilation only the first time, and
2748      * then are executed multiple times. */
2749     if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2750         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2751         if (bodyObjPtr->typePtr == &scriptObjType) {
2752             ScriptObj *bodyScript =
2753                 bodyObjPtr->internalRep.ptr;
2754             ScriptShareLiterals(interp, script, bodyScript);
2755         }
2756     } else if (propagateSourceInfo) {
2757         ScriptShareLiterals(interp, script, NULL);
2758     }
2759     /* Free the old internal rep and set the new one. */
2760     Jim_FreeIntRep(interp, objPtr);
2761     Jim_SetIntRepPtr(objPtr, script);
2762     objPtr->typePtr = &scriptObjType;
2763     return JIM_OK;
2764 }
2765
2766 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
2767 {
2768     if (objPtr->typePtr != &scriptObjType) {
2769         SetScriptFromAny(interp, objPtr);
2770     }
2771     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
2772 }
2773
2774 /* -----------------------------------------------------------------------------
2775  * Commands
2776  * ---------------------------------------------------------------------------*/
2777
2778 /* Commands HashTable Type.
2779  *
2780  * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
2781 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
2782 {
2783     Jim_Cmd *cmdPtr = (void*) val;
2784
2785     if (cmdPtr->cmdProc == NULL) {
2786         Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2787         Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2788         if (cmdPtr->staticVars) {
2789             Jim_FreeHashTable(cmdPtr->staticVars);
2790             Jim_Free(cmdPtr->staticVars);
2791         }
2792     } else if (cmdPtr->delProc != NULL) {
2793             /* If it was a C coded command, call the delProc if any */
2794             cmdPtr->delProc(interp, cmdPtr->privData);
2795     }
2796     Jim_Free(val);
2797 }
2798
2799 static Jim_HashTableType JimCommandsHashTableType = {
2800     JimStringCopyHTHashFunction,        /* hash function */
2801     JimStringCopyHTKeyDup,        /* key dup */
2802     NULL,                    /* val dup */
2803     JimStringCopyHTKeyCompare,        /* key compare */
2804     JimStringCopyHTKeyDestructor,        /* key destructor */
2805     Jim_CommandsHT_ValDestructor        /* val destructor */
2806 };
2807
2808 /* ------------------------- Commands related functions --------------------- */
2809
2810 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
2811         Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
2812 {
2813     Jim_HashEntry *he;
2814     Jim_Cmd *cmdPtr;
2815
2816     he = Jim_FindHashEntry(&interp->commands, cmdName);
2817     if (he == NULL) { /* New command to create */
2818         cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2819         cmdPtr->cmdProc = cmdProc;
2820         cmdPtr->privData = privData;
2821         cmdPtr->delProc = delProc;
2822         Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2823     } else {
2824         Jim_InterpIncrProcEpoch(interp);
2825         /* Free the arglist/body objects if it was a Tcl procedure */
2826         cmdPtr = he->val;
2827         if (cmdPtr->cmdProc == NULL) {
2828             Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2829             Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2830             if (cmdPtr->staticVars) {
2831                 Jim_FreeHashTable(cmdPtr->staticVars);
2832                 Jim_Free(cmdPtr->staticVars);
2833             }
2834             cmdPtr->staticVars = NULL;
2835         } else if (cmdPtr->delProc != NULL) {
2836             /* If it was a C coded command, call the delProc if any */
2837             cmdPtr->delProc(interp, cmdPtr->privData);
2838         }
2839         cmdPtr->cmdProc = cmdProc;
2840         cmdPtr->privData = privData;
2841     }
2842     /* There is no need to increment the 'proc epoch' because
2843      * creation of a new procedure can never affect existing
2844      * cached commands. We don't do negative caching. */
2845     return JIM_OK;
2846 }
2847
2848 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
2849         Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
2850         int arityMin, int arityMax)
2851 {
2852     Jim_Cmd *cmdPtr;
2853
2854     cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2855     cmdPtr->cmdProc = NULL; /* Not a C coded command */
2856     cmdPtr->argListObjPtr = argListObjPtr;
2857     cmdPtr->bodyObjPtr = bodyObjPtr;
2858     Jim_IncrRefCount(argListObjPtr);
2859     Jim_IncrRefCount(bodyObjPtr);
2860     cmdPtr->arityMin = arityMin;
2861     cmdPtr->arityMax = arityMax;
2862     cmdPtr->staticVars = NULL;
2863    
2864     /* Create the statics hash table. */
2865     if (staticsListObjPtr) {
2866         int len, i;
2867
2868         Jim_ListLength(interp, staticsListObjPtr, &len);
2869         if (len != 0) {
2870             cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
2871             Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
2872                     interp);
2873             for (i = 0; i < len; i++) {
2874                 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
2875                 Jim_Var *varPtr;
2876                 int subLen;
2877
2878                 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
2879                 /* Check if it's composed of two elements. */
2880                 Jim_ListLength(interp, objPtr, &subLen);
2881                 if (subLen == 1 || subLen == 2) {
2882                     /* Try to get the variable value from the current
2883                      * environment. */
2884                     Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
2885                     if (subLen == 1) {
2886                         initObjPtr = Jim_GetVariable(interp, nameObjPtr,
2887                                 JIM_NONE);
2888                         if (initObjPtr == NULL) {
2889                             Jim_SetResult(interp,
2890                                     Jim_NewEmptyStringObj(interp));
2891                             Jim_AppendStrings(interp, Jim_GetResult(interp),
2892                                 "variable for initialization of static \"",
2893                                 Jim_GetString(nameObjPtr, NULL),
2894                                 "\" not found in the local context",
2895                                 NULL);
2896                             goto err;
2897                         }
2898                     } else {
2899                         Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
2900                     }
2901                     varPtr = Jim_Alloc(sizeof(*varPtr));
2902                     varPtr->objPtr = initObjPtr;
2903                     Jim_IncrRefCount(initObjPtr);
2904                     varPtr->linkFramePtr = NULL;
2905                     if (Jim_AddHashEntry(cmdPtr->staticVars,
2906                             Jim_GetString(nameObjPtr, NULL),
2907                             varPtr) != JIM_OK)
2908                     {
2909                         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2910                         Jim_AppendStrings(interp, Jim_GetResult(interp),
2911                             "static variable name \"",
2912                             Jim_GetString(objPtr, NULL), "\"",
2913                             " duplicated in statics list", NULL);
2914                         Jim_DecrRefCount(interp, initObjPtr);
2915                         Jim_Free(varPtr);
2916                         goto err;
2917                     }
2918                 } else {
2919                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2920                     Jim_AppendStrings(interp, Jim_GetResult(interp),
2921                         "too many fields in static specifier \"",
2922                         objPtr, "\"", NULL);
2923                     goto err;
2924                 }
2925             }
2926         }
2927     }
2928
2929     /* Add the new command */
2930
2931     /* it may already exist, so we try to delete the old one */
2932     if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
2933         /* There was an old procedure with the same name, this requires
2934          * a 'proc epoch' update. */
2935         Jim_InterpIncrProcEpoch(interp);
2936     }
2937     /* If a procedure with the same name didn't existed there is no need
2938      * to increment the 'proc epoch' because creation of a new procedure
2939      * can never affect existing cached commands. We don't do
2940      * negative caching. */
2941     Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2942     return JIM_OK;
2943
2944 err:
2945     Jim_FreeHashTable(cmdPtr->staticVars);
2946     Jim_Free(cmdPtr->staticVars);
2947     Jim_DecrRefCount(interp, argListObjPtr);
2948     Jim_DecrRefCount(interp, bodyObjPtr);
2949     Jim_Free(cmdPtr);
2950     return JIM_ERR;
2951 }
2952
2953 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
2954 {
2955     if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
2956         return JIM_ERR;
2957     Jim_InterpIncrProcEpoch(interp);
2958     return JIM_OK;
2959 }
2960
2961 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, 
2962         const char *newName)
2963 {
2964     Jim_Cmd *cmdPtr;
2965     Jim_HashEntry *he;
2966     Jim_Cmd *copyCmdPtr;
2967
2968     if (newName[0] == '\0') /* Delete! */
2969         return Jim_DeleteCommand(interp, oldName);
2970     /* Rename */
2971     he = Jim_FindHashEntry(&interp->commands, oldName);
2972     if (he == NULL)
2973         return JIM_ERR; /* Invalid command name */
2974     cmdPtr = he->val;
2975     copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
2976     *copyCmdPtr = *cmdPtr;
2977     /* In order to avoid that a procedure will get arglist/body/statics
2978      * freed by the hash table methods, fake a C-coded command
2979      * setting cmdPtr->cmdProc as not NULL */
2980     cmdPtr->cmdProc = (void*)1;
2981     /* Also make sure delProc is NULL. */
2982     cmdPtr->delProc = NULL;
2983     /* Destroy the old command, and make sure the new is freed
2984      * as well. */
2985     Jim_DeleteHashEntry(&interp->commands, oldName);
2986     Jim_DeleteHashEntry(&interp->commands, newName);
2987     /* Now the new command. We are sure it can't fail because
2988      * the target name was already freed. */
2989     Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
2990     /* Increment the epoch */
2991     Jim_InterpIncrProcEpoch(interp);
2992     return JIM_OK;
2993 }
2994
2995 /* -----------------------------------------------------------------------------
2996  * Command object
2997  * ---------------------------------------------------------------------------*/
2998
2999 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3000
3001 static Jim_ObjType commandObjType = {
3002     "command",
3003     NULL,
3004     NULL,
3005     NULL,
3006     JIM_TYPE_REFERENCES,
3007 };
3008
3009 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3010 {
3011     Jim_HashEntry *he;
3012     const char *cmdName;
3013
3014     /* Get the string representation */
3015     cmdName = Jim_GetString(objPtr, NULL);
3016     /* Lookup this name into the commands hash table */
3017     he = Jim_FindHashEntry(&interp->commands, cmdName);
3018     if (he == NULL)
3019         return JIM_ERR;
3020
3021     /* Free the old internal repr and set the new one. */
3022     Jim_FreeIntRep(interp, objPtr);
3023     objPtr->typePtr = &commandObjType;
3024     objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3025     objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3026     return JIM_OK;
3027 }
3028
3029 /* This function returns the command structure for the command name
3030  * stored in objPtr. It tries to specialize the objPtr to contain
3031  * a cached info instead to perform the lookup into the hash table
3032  * every time. The information cached may not be uptodate, in such
3033  * a case the lookup is performed and the cache updated. */
3034 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3035 {
3036     if ((objPtr->typePtr != &commandObjType ||
3037         objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3038         SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3039         if (flags & JIM_ERRMSG) {
3040             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3041             Jim_AppendStrings(interp, Jim_GetResult(interp),
3042                 "invalid command name \"", objPtr->bytes, "\"",
3043                 NULL);
3044         }
3045         return NULL;
3046     }
3047     return objPtr->internalRep.cmdValue.cmdPtr;
3048 }
3049
3050 /* -----------------------------------------------------------------------------
3051  * Variables
3052  * ---------------------------------------------------------------------------*/
3053
3054 /* Variables HashTable Type.
3055  *
3056  * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3057 static void JimVariablesHTValDestructor(void *interp, void *val)
3058 {
3059     Jim_Var *varPtr = (void*) val;
3060
3061     Jim_DecrRefCount(interp, varPtr->objPtr);
3062     Jim_Free(val);
3063 }
3064
3065 static Jim_HashTableType JimVariablesHashTableType = {
3066     JimStringCopyHTHashFunction,        /* hash function */
3067     JimStringCopyHTKeyDup,              /* key dup */
3068     NULL,                               /* val dup */
3069     JimStringCopyHTKeyCompare,        /* key compare */
3070     JimStringCopyHTKeyDestructor,     /* key destructor */
3071     JimVariablesHTValDestructor       /* val destructor */
3072 };
3073
3074 /* -----------------------------------------------------------------------------
3075  * Variable object
3076  * ---------------------------------------------------------------------------*/
3077
3078 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3079
3080 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3081
3082 static Jim_ObjType variableObjType = {
3083     "variable",
3084     NULL,
3085     NULL,
3086     NULL,
3087     JIM_TYPE_REFERENCES,
3088 };
3089
3090 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3091  * is in the form "varname(key)". */
3092 static int Jim_NameIsDictSugar(const char *str, int len)
3093 {
3094     if (len == -1)
3095         len = strlen(str);
3096     if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3097         return 1;
3098     return 0;
3099 }
3100
3101 /* This method should be called only by the variable API.
3102  * It returns JIM_OK on success (variable already exists),
3103  * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3104  * a variable name, but syntax glue for [dict] i.e. the last
3105  * character is ')' */
3106 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3107 {
3108     Jim_HashEntry *he;
3109     const char *varName;
3110     int len;
3111
3112     /* Check if the object is already an uptodate variable */
3113     if (objPtr->typePtr == &variableObjType &&
3114         objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3115         return JIM_OK; /* nothing to do */
3116     /* Get the string representation */
3117     varName = Jim_GetString(objPtr, &len);
3118     /* Make sure it's not syntax glue to get/set dict. */
3119     if (Jim_NameIsDictSugar(varName, len))
3120             return JIM_DICT_SUGAR;
3121     /* Lookup this name into the variables hash table */
3122     he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3123     if (he == NULL) {
3124         /* Try with static vars. */
3125         if (interp->framePtr->staticVars == NULL)
3126             return JIM_ERR;
3127         if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3128             return JIM_ERR;
3129     }
3130     /* Free the old internal repr and set the new one. */
3131     Jim_FreeIntRep(interp, objPtr);
3132     objPtr->typePtr = &variableObjType;
3133     objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3134     objPtr->internalRep.varValue.varPtr = (void*)he->val;
3135     return JIM_OK;
3136 }
3137
3138 /* -------------------- Variables related functions ------------------------- */
3139 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3140         Jim_Obj *valObjPtr);
3141 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3142
3143 /* For now that's dummy. Variables lookup should be optimized
3144  * in many ways, with caching of lookups, and possibly with
3145  * a table of pre-allocated vars in every CallFrame for local vars.
3146  * All the caching should also have an 'epoch' mechanism similar
3147  * to the one used by Tcl for procedures lookup caching. */
3148
3149 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3150 {
3151     const char *name;
3152     Jim_Var *var;
3153     int err;
3154
3155     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3156         /* Check for [dict] syntax sugar. */
3157         if (err == JIM_DICT_SUGAR)
3158             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3159         /* New variable to create */
3160         name = Jim_GetString(nameObjPtr, NULL);
3161
3162         var = Jim_Alloc(sizeof(*var));
3163         var->objPtr = valObjPtr;
3164         Jim_IncrRefCount(valObjPtr);
3165         var->linkFramePtr = NULL;
3166         /* Insert the new variable */
3167         Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3168         /* Make the object int rep a variable */
3169         Jim_FreeIntRep(interp, nameObjPtr);
3170         nameObjPtr->typePtr = &variableObjType;
3171         nameObjPtr->internalRep.varValue.callFrameId =
3172             interp->framePtr->id;
3173         nameObjPtr->internalRep.varValue.varPtr = var;
3174     } else {
3175         var = nameObjPtr->internalRep.varValue.varPtr;
3176         if (var->linkFramePtr == NULL) {
3177             Jim_IncrRefCount(valObjPtr);
3178             Jim_DecrRefCount(interp, var->objPtr);
3179             var->objPtr = valObjPtr;
3180         } else { /* Else handle the link */
3181             Jim_CallFrame *savedCallFrame;
3182
3183             savedCallFrame = interp->framePtr;
3184             interp->framePtr = var->linkFramePtr;
3185             err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3186             interp->framePtr = savedCallFrame;
3187             if (err != JIM_OK)
3188                 return err;
3189         }
3190     }
3191     return JIM_OK;
3192 }
3193
3194 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3195 {
3196     Jim_Obj *nameObjPtr;
3197     int result;
3198
3199     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3200     Jim_IncrRefCount(nameObjPtr);
3201     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3202     Jim_DecrRefCount(interp, nameObjPtr);
3203     return result;
3204 }
3205
3206 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3207 {
3208     Jim_CallFrame *savedFramePtr;
3209     int result;
3210
3211     savedFramePtr = interp->framePtr;
3212     interp->framePtr = interp->topFramePtr;
3213     result = Jim_SetVariableStr(interp, name, objPtr);
3214     interp->framePtr = savedFramePtr;
3215     return result;
3216 }
3217
3218 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3219 {
3220     Jim_Obj *nameObjPtr, *valObjPtr;
3221     int result;
3222
3223     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3224     valObjPtr = Jim_NewStringObj(interp, val, -1);
3225     Jim_IncrRefCount(nameObjPtr);
3226     Jim_IncrRefCount(valObjPtr);
3227     result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3228     Jim_DecrRefCount(interp, nameObjPtr);
3229     Jim_DecrRefCount(interp, valObjPtr);
3230     return result;
3231 }
3232
3233 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3234         Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3235 {
3236     const char *varName;
3237     int len;
3238
3239     /* Check for cycles. */
3240     if (interp->framePtr == targetCallFrame) {
3241         Jim_Obj *objPtr = targetNameObjPtr;
3242         Jim_Var *varPtr;
3243         /* Cycles are only possible with 'uplevel 0' */
3244         while(1) {
3245             if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3246                 Jim_SetResultString(interp,
3247                     "can't upvar from variable to itself", -1);
3248                 return JIM_ERR;
3249             }
3250             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3251                 break;
3252             varPtr = objPtr->internalRep.varValue.varPtr;
3253             if (varPtr->linkFramePtr != targetCallFrame) break;
3254             objPtr = varPtr->objPtr;
3255         }
3256     }
3257     varName = Jim_GetString(nameObjPtr, &len);
3258     if (Jim_NameIsDictSugar(varName, len)) {
3259         Jim_SetResultString(interp,
3260             "Dict key syntax invalid as link source", -1);
3261         return JIM_ERR;
3262     }
3263     /* Perform the binding */
3264     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3265     /* We are now sure 'nameObjPtr' type is variableObjType */
3266     nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3267     return JIM_OK;
3268 }
3269
3270 /* Return the Jim_Obj pointer associated with a variable name,
3271  * or NULL if the variable was not found in the current context.
3272  * The same optimization discussed in the comment to the
3273  * 'SetVariable' function should apply here. */
3274 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3275 {
3276     int err;
3277
3278     /* All the rest is handled here */
3279     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3280         /* Check for [dict] syntax sugar. */
3281         if (err == JIM_DICT_SUGAR)
3282             return JimDictSugarGet(interp, nameObjPtr);
3283         if (flags & JIM_ERRMSG) {
3284             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3285             Jim_AppendStrings(interp, Jim_GetResult(interp),
3286                 "can't read \"", nameObjPtr->bytes,
3287                 "\": no such variable", NULL);
3288         }
3289         return NULL;
3290     } else {
3291         Jim_Var *varPtr;
3292         Jim_Obj *objPtr;
3293         Jim_CallFrame *savedCallFrame;
3294
3295         varPtr = nameObjPtr->internalRep.varValue.varPtr;
3296         if (varPtr->linkFramePtr == NULL)
3297             return varPtr->objPtr;
3298         /* The variable is a link? Resolve it. */
3299         savedCallFrame = interp->framePtr;
3300         interp->framePtr = varPtr->linkFramePtr;
3301         objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3302         if (objPtr == NULL && flags & JIM_ERRMSG) {
3303             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3304             Jim_AppendStrings(interp, Jim_GetResult(interp),
3305                 "can't read \"", nameObjPtr->bytes,
3306                 "\": no such variable", NULL);
3307         }
3308         interp->framePtr = savedCallFrame;
3309         return objPtr;
3310     }
3311 }
3312
3313 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3314         int flags)
3315 {
3316     Jim_CallFrame *savedFramePtr;
3317     Jim_Obj *objPtr;
3318
3319     savedFramePtr = interp->framePtr;
3320     interp->framePtr = interp->topFramePtr;
3321     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3322     interp->framePtr = savedFramePtr;
3323
3324     return objPtr;
3325 }
3326
3327 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3328 {
3329     Jim_Obj *nameObjPtr, *varObjPtr;
3330
3331     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3332     Jim_IncrRefCount(nameObjPtr);
3333     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3334     Jim_DecrRefCount(interp, nameObjPtr);
3335     return varObjPtr;
3336 }
3337
3338 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3339         int flags)
3340 {
3341     Jim_CallFrame *savedFramePtr;
3342     Jim_Obj *objPtr;
3343
3344     savedFramePtr = interp->framePtr;
3345     interp->framePtr = interp->topFramePtr;
3346     objPtr = Jim_GetVariableStr(interp, name, flags);
3347     interp->framePtr = savedFramePtr;
3348
3349     return objPtr;
3350 }
3351
3352 /* Unset a variable.
3353  * Note: On success unset invalidates all the variable objects created
3354  * in the current call frame incrementing. */
3355 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3356 {
3357     const char *name;
3358     Jim_Var *varPtr;
3359     int err;
3360     
3361     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3362         /* Check for [dict] syntax sugar. */
3363         if (err == JIM_DICT_SUGAR)
3364             return JimDictSugarSet(interp, nameObjPtr, NULL);
3365         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3366         Jim_AppendStrings(interp, Jim_GetResult(interp),
3367             "can't unset \"", nameObjPtr->bytes,
3368             "\": no such variable", NULL);
3369         return JIM_ERR; /* var not found */
3370     }
3371     varPtr = nameObjPtr->internalRep.varValue.varPtr;
3372     /* If it's a link call UnsetVariable recursively */
3373     if (varPtr->linkFramePtr) {
3374         int retval;
3375
3376         Jim_CallFrame *savedCallFrame;
3377
3378         savedCallFrame = interp->framePtr;
3379         interp->framePtr = varPtr->linkFramePtr;
3380         retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3381         interp->framePtr = savedCallFrame;
3382         if (retval != JIM_OK && flags & JIM_ERRMSG) {
3383             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3384             Jim_AppendStrings(interp, Jim_GetResult(interp),
3385                 "can't unset \"", nameObjPtr->bytes,
3386                 "\": no such variable", NULL);
3387         }
3388         return retval;
3389     } else {
3390         name = Jim_GetString(nameObjPtr, NULL);
3391         if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3392                 != JIM_OK) return JIM_ERR;
3393         /* Change the callframe id, invalidating var lookup caching */
3394         JimChangeCallFrameId(interp, interp->framePtr);
3395         return JIM_OK;
3396     }
3397 }
3398
3399 /* ----------  Dict syntax sugar (similar to array Tcl syntax) -------------- */
3400
3401 /* Given a variable name for [dict] operation syntax sugar,
3402  * this function returns two objects, the first with the name
3403  * of the variable to set, and the second with the rispective key.
3404  * For example "foo(bar)" will return objects with string repr. of
3405  * "foo" and "bar".
3406  *
3407  * The returned objects have refcount = 1. The function can't fail. */
3408 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3409         Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3410 {
3411     const char *str, *p;
3412     char *t;
3413     int len, keyLen, nameLen;
3414     Jim_Obj *varObjPtr, *keyObjPtr;
3415
3416     str = Jim_GetString(objPtr, &len);
3417     p = strchr(str, '(');
3418     p++;
3419     keyLen = len-((p-str)+1);
3420     nameLen = (p-str)-1;
3421     /* Create the objects with the variable name and key. */
3422     t = Jim_Alloc(nameLen+1);
3423     memcpy(t, str, nameLen);
3424     t[nameLen] = '\0';
3425     varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3426
3427     t = Jim_Alloc(keyLen+1);
3428     memcpy(t, p, keyLen);
3429     t[keyLen] = '\0';
3430     keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3431
3432     Jim_IncrRefCount(varObjPtr);
3433     Jim_IncrRefCount(keyObjPtr);
3434     *varPtrPtr = varObjPtr;
3435     *keyPtrPtr = keyObjPtr;
3436 }
3437
3438 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3439  * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3440 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3441         Jim_Obj *valObjPtr)
3442 {
3443     Jim_Obj *varObjPtr, *keyObjPtr;
3444     int err = JIM_OK;
3445
3446     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3447     err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3448             valObjPtr);
3449     Jim_DecrRefCount(interp, varObjPtr);
3450     Jim_DecrRefCount(interp, keyObjPtr);
3451     return err;
3452 }
3453
3454 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3455 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3456 {
3457     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3458
3459     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3460     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3461     if (!dictObjPtr) {
3462         resObjPtr = NULL;
3463         goto err;
3464     }
3465     if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3466             != JIM_OK) {
3467         resObjPtr = NULL;
3468     }
3469 err:
3470     Jim_DecrRefCount(interp, varObjPtr);
3471     Jim_DecrRefCount(interp, keyObjPtr);
3472     return resObjPtr;
3473 }
3474
3475 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3476
3477 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3478 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3479         Jim_Obj *dupPtr);
3480
3481 static Jim_ObjType dictSubstObjType = {
3482     "dict-substitution",
3483     FreeDictSubstInternalRep,
3484     DupDictSubstInternalRep,
3485     NULL,
3486     JIM_TYPE_NONE,
3487 };
3488
3489 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3490 {
3491     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3492     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3493 }
3494
3495 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3496         Jim_Obj *dupPtr)
3497 {
3498     JIM_NOTUSED(interp);
3499
3500     dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3501         srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3502     dupPtr->internalRep.dictSubstValue.indexObjPtr =
3503         srcPtr->internalRep.dictSubstValue.indexObjPtr;
3504     dupPtr->typePtr = &dictSubstObjType;
3505 }
3506
3507 /* This function is used to expand [dict get] sugar in the form
3508  * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3509  * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3510  * object that is *guaranteed* to be in the form VARNAME(INDEX).
3511  * The 'index' part is [subst]ituted, and is used to lookup a key inside
3512  * the [dict]ionary contained in variable VARNAME. */
3513 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3514 {
3515     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3516     Jim_Obj *substKeyObjPtr = NULL;
3517
3518     if (objPtr->typePtr != &dictSubstObjType) {
3519         JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3520         Jim_FreeIntRep(interp, objPtr);
3521         objPtr->typePtr = &dictSubstObjType;
3522         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3523         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3524     }
3525     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3526                 &substKeyObjPtr, JIM_NONE)
3527             != JIM_OK) {
3528         substKeyObjPtr = NULL;
3529         goto err;
3530     }
3531     Jim_IncrRefCount(substKeyObjPtr);
3532     dictObjPtr = Jim_GetVariable(interp,
3533             objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3534     if (!dictObjPtr) {
3535         resObjPtr = NULL;
3536         goto err;
3537     }
3538     if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3539             != JIM_OK) {
3540         resObjPtr = NULL;
3541         goto err;
3542     }
3543 err:
3544     if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3545     return resObjPtr;
3546 }
3547
3548 /* -----------------------------------------------------------------------------
3549  * CallFrame
3550  * ---------------------------------------------------------------------------*/
3551
3552 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3553 {
3554     Jim_CallFrame *cf;
3555     if (interp->freeFramesList) {
3556         cf = interp->freeFramesList;
3557         interp->freeFramesList = cf->nextFramePtr;
3558     } else {
3559         cf = Jim_Alloc(sizeof(*cf));
3560         cf->vars.table = NULL;
3561     }
3562
3563     cf->id = interp->callFrameEpoch++;
3564     cf->parentCallFrame = NULL;
3565     cf->argv = NULL;
3566     cf->argc = 0;
3567     cf->procArgsObjPtr = NULL;
3568     cf->procBodyObjPtr = NULL;
3569     cf->nextFramePtr = NULL;
3570     cf->staticVars = NULL;
3571     if (cf->vars.table == NULL)
3572         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3573     return cf;
3574 }
3575
3576 /* Used to invalidate every caching related to callframe stability. */
3577 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3578 {
3579     cf->id = interp->callFrameEpoch++;
3580 }
3581
3582 #define JIM_FCF_NONE 0 /* no flags */
3583 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3584 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3585         int flags)
3586 {
3587     if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3588     if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3589     if (!(flags & JIM_FCF_NOHT))
3590         Jim_FreeHashTable(&cf->vars);
3591     else {
3592         int i;
3593         Jim_HashEntry **table = cf->vars.table, *he;
3594
3595         for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3596             he = table[i];
3597             while (he != NULL) {
3598                 Jim_HashEntry *nextEntry = he->next;
3599                 Jim_Var *varPtr = (void*) he->val;
3600
3601                 Jim_DecrRefCount(interp, varPtr->objPtr);
3602                 Jim_Free(he->val);
3603                 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3604                 Jim_Free(he);
3605                 table[i] = NULL;
3606                 he = nextEntry;
3607             }
3608         }
3609         cf->vars.used = 0;
3610     }
3611     cf->nextFramePtr = interp->freeFramesList;
3612     interp->freeFramesList = cf;
3613 }
3614
3615 /* -----------------------------------------------------------------------------
3616  * References
3617  * ---------------------------------------------------------------------------*/
3618
3619 /* References HashTable Type.
3620  *
3621  * Keys are jim_wide integers, dynamically allocated for now but in the
3622  * future it's worth to cache this 8 bytes objects. Values are poitners
3623  * to Jim_References. */
3624 static void JimReferencesHTValDestructor(void *interp, void *val)
3625 {
3626     Jim_Reference *refPtr = (void*) val;
3627
3628     Jim_DecrRefCount(interp, refPtr->objPtr);
3629     if (refPtr->finalizerCmdNamePtr != NULL) {
3630         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3631     }
3632     Jim_Free(val);
3633 }
3634
3635 unsigned int JimReferencesHTHashFunction(const void *key)
3636 {
3637     /* Only the least significant bits are used. */
3638     const jim_wide *widePtr = key;
3639     unsigned int intValue = (unsigned int) *widePtr;
3640     return Jim_IntHashFunction(intValue);
3641 }
3642
3643 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3644 {
3645     /* Only the least significant bits are used. */
3646     const jim_wide *widePtr = key;
3647     unsigned int intValue = (unsigned int) *widePtr;
3648     return intValue; /* identity function. */
3649 }
3650
3651 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3652 {
3653     void *copy = Jim_Alloc(sizeof(jim_wide));
3654     JIM_NOTUSED(privdata);
3655
3656     memcpy(copy, key, sizeof(jim_wide));
3657     return copy;
3658 }
3659
3660 int JimReferencesHTKeyCompare(void *privdata, const void *key1, 
3661         const void *key2)
3662 {
3663     JIM_NOTUSED(privdata);
3664
3665     return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3666 }
3667
3668 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3669 {
3670     JIM_NOTUSED(privdata);
3671
3672     Jim_Free((void*)key);
3673 }
3674
3675 static Jim_HashTableType JimReferencesHashTableType = {
3676     JimReferencesHTHashFunction,    /* hash function */
3677     JimReferencesHTKeyDup,          /* key dup */
3678     NULL,                           /* val dup */
3679     JimReferencesHTKeyCompare,      /* key compare */
3680     JimReferencesHTKeyDestructor,   /* key destructor */
3681     JimReferencesHTValDestructor    /* val destructor */
3682 };
3683
3684 /* -----------------------------------------------------------------------------
3685  * Reference object type and References API
3686  * ---------------------------------------------------------------------------*/
3687
3688 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3689
3690 static Jim_ObjType referenceObjType = {
3691     "reference",
3692     NULL,
3693     NULL,
3694     UpdateStringOfReference,
3695     JIM_TYPE_REFERENCES,
3696 };
3697
3698 void UpdateStringOfReference(struct Jim_Obj *objPtr)
3699 {
3700     int len;
3701     char buf[JIM_REFERENCE_SPACE+1];
3702     Jim_Reference *refPtr;
3703
3704     refPtr = objPtr->internalRep.refValue.refPtr;
3705     len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3706     objPtr->bytes = Jim_Alloc(len+1);
3707     memcpy(objPtr->bytes, buf, len+1);
3708     objPtr->length = len;
3709 }
3710
3711 /* returns true if 'c' is a valid reference tag character.
3712  * i.e. inside the range [_a-zA-Z0-9] */
3713 static int isrefchar(int c)
3714 {
3715     if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3716         (c >= '0' && c <= '9')) return 1;
3717     return 0;
3718 }
3719
3720 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3721 {
3722     jim_wide wideValue;
3723     int i, len;
3724     const char *str, *start, *end;
3725     char refId[21];
3726     Jim_Reference *refPtr;
3727     Jim_HashEntry *he;
3728
3729     /* Get the string representation */
3730     str = Jim_GetString(objPtr, &len);
3731     /* Check if it looks like a reference */
3732     if (len < JIM_REFERENCE_SPACE) goto badformat;
3733     /* Trim spaces */
3734     start = str;
3735     end = str+len-1;
3736     while (*start == ' ') start++;
3737     while (*end == ' ' && end > start) end--;
3738     if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3739     /* <reference.<1234567>.%020> */
3740     if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3741     if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3742     /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3743     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3744         if (!isrefchar(start[12+i])) goto badformat;
3745     }
3746     /* Extract info from the refernece. */
3747     memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3748     refId[20] = '\0';
3749     /* Try to convert the ID into a jim_wide */
3750     if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3751     /* Check if the reference really exists! */
3752     he = Jim_FindHashEntry(&interp->references, &wideValue);
3753     if (he == NULL) {
3754         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3755         Jim_AppendStrings(interp, Jim_GetResult(interp),
3756                 "Invalid reference ID \"", str, "\"", NULL);
3757         return JIM_ERR;
3758     }
3759     refPtr = he->val;
3760     /* Free the old internal repr and set the new one. */
3761     Jim_FreeIntRep(interp, objPtr);
3762     objPtr->typePtr = &referenceObjType;
3763     objPtr->internalRep.refValue.id = wideValue;
3764     objPtr->internalRep.refValue.refPtr = refPtr;
3765     return JIM_OK;
3766
3767 badformat:
3768     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3769     Jim_AppendStrings(interp, Jim_GetResult(interp),
3770             "expected reference but got \"", str, "\"", NULL);
3771     return JIM_ERR;
3772 }
3773
3774 /* Returns a new reference pointing to objPtr, having cmdNamePtr
3775  * as finalizer command (or NULL if there is no finalizer).
3776  * The returned reference object has refcount = 0. */
3777 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
3778         Jim_Obj *cmdNamePtr)
3779 {
3780     struct Jim_Reference *refPtr;
3781     jim_wide wideValue = interp->referenceNextId;
3782     Jim_Obj *refObjPtr;
3783     const char *tag;
3784     int tagLen, i;
3785
3786     /* Perform the Garbage Collection if needed. */
3787     Jim_CollectIfNeeded(interp);
3788
3789     refPtr = Jim_Alloc(sizeof(*refPtr));
3790     refPtr->objPtr = objPtr;
3791     Jim_IncrRefCount(objPtr);
3792     refPtr->finalizerCmdNamePtr = cmdNamePtr;
3793     if (cmdNamePtr)
3794         Jim_IncrRefCount(cmdNamePtr);
3795     Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
3796     refObjPtr = Jim_NewObj(interp);
3797     refObjPtr->typePtr = &referenceObjType;
3798     refObjPtr->bytes = NULL;
3799     refObjPtr->internalRep.refValue.id = interp->referenceNextId;
3800     refObjPtr->internalRep.refValue.refPtr = refPtr;
3801     interp->referenceNextId++;
3802     /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
3803      * that does not pass the 'isrefchar' test is replaced with '_' */
3804     tag = Jim_GetString(tagPtr, &tagLen);
3805     if (tagLen > JIM_REFERENCE_TAGLEN)
3806         tagLen = JIM_REFERENCE_TAGLEN;
3807     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3808         if (i < tagLen)
3809             refPtr->tag[i] = tag[i];
3810         else
3811             refPtr->tag[i] = '_';
3812     }
3813     refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
3814     return refObjPtr;
3815 }
3816
3817 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
3818 {
3819     if (objPtr->typePtr != &referenceObjType &&
3820         SetReferenceFromAny(interp, objPtr) == JIM_ERR)
3821         return NULL;
3822     return objPtr->internalRep.refValue.refPtr;
3823 }
3824
3825 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
3826 {
3827     Jim_Reference *refPtr;
3828
3829     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3830         return JIM_ERR;
3831     Jim_IncrRefCount(cmdNamePtr);
3832     if (refPtr->finalizerCmdNamePtr)
3833         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3834     refPtr->finalizerCmdNamePtr = cmdNamePtr;
3835     return JIM_OK;
3836 }
3837
3838 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
3839 {
3840     Jim_Reference *refPtr;
3841
3842     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3843         return JIM_ERR;
3844     *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
3845     return JIM_OK;
3846 }
3847
3848 /* -----------------------------------------------------------------------------
3849  * References Garbage Collection
3850  * ---------------------------------------------------------------------------*/
3851
3852 /* This the hash table type for the "MARK" phase of the GC */
3853 static Jim_HashTableType JimRefMarkHashTableType = {
3854     JimReferencesHTHashFunction,    /* hash function */
3855     JimReferencesHTKeyDup,          /* key dup */
3856     NULL,                           /* val dup */
3857     JimReferencesHTKeyCompare,      /* key compare */
3858     JimReferencesHTKeyDestructor,   /* key destructor */
3859     NULL                            /* val destructor */
3860 };
3861
3862 /* #define JIM_DEBUG_GC 1 */
3863
3864 /* Performs the garbage collection. */
3865 int Jim_Collect(Jim_Interp *interp)
3866 {
3867     Jim_HashTable marks;
3868     Jim_HashTableIterator *htiter;
3869     Jim_HashEntry *he;
3870     Jim_Obj *objPtr;
3871     int collected = 0;
3872
3873     /* Avoid recursive calls */
3874     if (interp->lastCollectId == -1) {
3875         /* Jim_Collect() already running. Return just now. */
3876         return 0;
3877     }
3878     interp->lastCollectId = -1;
3879
3880     /* Mark all the references found into the 'mark' hash table.
3881      * The references are searched in every live object that
3882      * is of a type that can contain references. */
3883     Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
3884     objPtr = interp->liveList;
3885     while(objPtr) {
3886         if (objPtr->typePtr == NULL ||
3887             objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
3888             const char *str, *p;
3889             int len;
3890
3891             /* If the object is of type reference, to get the
3892              * Id is simple... */
3893             if (objPtr->typePtr == &referenceObjType) {
3894                 Jim_AddHashEntry(&marks,
3895                     &objPtr->internalRep.refValue.id, NULL);
3896 #ifdef JIM_DEBUG_GC
3897                 fprintf(interp->stdout,
3898                     "MARK (reference): %d refcount: %d" JIM_NL, 
3899                     (int) objPtr->internalRep.refValue.id,
3900                     objPtr->refCount);
3901 #endif
3902                 objPtr = objPtr->nextObjPtr;
3903                 continue;
3904             }
3905             /* Get the string repr of the object we want
3906              * to scan for references. */
3907             p = str = Jim_GetString(objPtr, &len);
3908             /* Skip objects too little to contain references. */
3909             if (len < JIM_REFERENCE_SPACE) {
3910                 objPtr = objPtr->nextObjPtr;
3911                 continue;
3912             }
3913             /* Extract references from the object string repr. */
3914             while(1) {
3915                 int i;
3916                 jim_wide id;
3917                 char buf[21];
3918
3919                 if ((p = strstr(p, "<reference.<")) == NULL)
3920                     break;
3921                 /* Check if it's a valid reference. */
3922                 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
3923                 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
3924                 for (i = 21; i <= 40; i++)
3925                     if (!isdigit((int)p[i]))
3926                         break;
3927                 /* Get the ID */
3928                 memcpy(buf, p+21, 20);
3929                 buf[20] = '\0';
3930                 Jim_StringToWide(buf, &id, 10);
3931
3932                 /* Ok, a reference for the given ID
3933                  * was found. Mark it. */
3934                 Jim_AddHashEntry(&marks, &id, NULL);
3935 #ifdef JIM_DEBUG_GC
3936                 fprintf(interp->stdout,"MARK: %d" JIM_NL, (int)id);
3937 #endif
3938                 p += JIM_REFERENCE_SPACE;
3939             }
3940         }
3941         objPtr = objPtr->nextObjPtr;
3942     }
3943
3944     /* Run the references hash table to destroy every reference that
3945      * is not referenced outside (not present in the mark HT). */
3946     htiter = Jim_GetHashTableIterator(&interp->references);
3947     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
3948         const jim_wide *refId;
3949         Jim_Reference *refPtr;
3950
3951         refId = he->key;
3952         /* Check if in the mark phase we encountered
3953          * this reference. */
3954         if (Jim_FindHashEntry(&marks, refId) == NULL) {
3955 #ifdef JIM_DEBUG_GC
3956             fprintf(interp->stdout,"COLLECTING %d" JIM_NL, (int)*refId);
3957 #endif
3958             collected++;
3959             /* Drop the reference, but call the
3960              * finalizer first if registered. */
3961             refPtr = he->val;
3962             if (refPtr->finalizerCmdNamePtr) {
3963                 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
3964                 Jim_Obj *objv[3], *oldResult;
3965
3966                 JimFormatReference(refstr, refPtr, *refId);
3967
3968                 objv[0] = refPtr->finalizerCmdNamePtr;
3969                 objv[1] = Jim_NewStringObjNoAlloc(interp,
3970                         refstr, 32);
3971                 objv[2] = refPtr->objPtr;
3972                 Jim_IncrRefCount(objv[0]);
3973                 Jim_IncrRefCount(objv[1]);
3974                 Jim_IncrRefCount(objv[2]);
3975
3976                 /* Drop the reference itself */
3977                 Jim_DeleteHashEntry(&interp->references, refId);
3978
3979                 /* Call the finalizer. Errors ignored. */
3980                 oldResult = interp->result;
3981                 Jim_IncrRefCount(oldResult);
3982                 Jim_EvalObjVector(interp, 3, objv);
3983                 Jim_SetResult(interp, oldResult);
3984                 Jim_DecrRefCount(interp, oldResult);
3985
3986                 Jim_DecrRefCount(interp, objv[0]);
3987                 Jim_DecrRefCount(interp, objv[1]);
3988                 Jim_DecrRefCount(interp, objv[2]);
3989             } else {
3990                 Jim_DeleteHashEntry(&interp->references, refId);
3991             }
3992         }
3993     }
3994     Jim_FreeHashTableIterator(htiter);
3995     Jim_FreeHashTable(&marks);
3996     interp->lastCollectId = interp->referenceNextId;
3997     interp->lastCollectTime = time(NULL);
3998     return collected;
3999 }
4000
4001 #define JIM_COLLECT_ID_PERIOD 5000
4002 #define JIM_COLLECT_TIME_PERIOD 300
4003
4004 void Jim_CollectIfNeeded(Jim_Interp *interp)
4005 {
4006     jim_wide elapsedId;
4007     int elapsedTime;
4008     
4009     elapsedId = interp->referenceNextId - interp->lastCollectId;
4010     elapsedTime = time(NULL) - interp->lastCollectTime;
4011
4012
4013     if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4014         elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4015         Jim_Collect(interp);
4016     }
4017 }
4018
4019 /* -----------------------------------------------------------------------------
4020  * Interpreter related functions
4021  * ---------------------------------------------------------------------------*/
4022
4023 Jim_Interp *Jim_CreateInterp(void)
4024 {
4025     Jim_Interp *i = Jim_Alloc(sizeof(*i));
4026     Jim_Obj *pathPtr;
4027
4028     i->errorLine = 0;
4029     i->errorFileName = Jim_StrDup("");
4030     i->numLevels = 0;
4031     i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4032     i->returnCode = JIM_OK;
4033     i->exitCode = 0;
4034     i->procEpoch = 0;
4035     i->callFrameEpoch = 0;
4036     i->liveList = i->freeList = NULL;
4037     i->scriptFileName = Jim_StrDup("");
4038     i->referenceNextId = 0;
4039     i->lastCollectId = 0;
4040     i->lastCollectTime = time(NULL);
4041     i->freeFramesList = NULL;
4042     i->prngState = NULL;
4043     i->evalRetcodeLevel = -1;
4044     i->stdin = stdin;
4045     i->stdout = stdout;
4046     i->stderr = stderr;
4047
4048     /* Note that we can create objects only after the
4049      * interpreter liveList and freeList pointers are
4050      * initialized to NULL. */
4051     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4052     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4053     Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4054             NULL);
4055     Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4056     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4057     Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4058     i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4059     i->emptyObj = Jim_NewEmptyStringObj(i);
4060     i->result = i->emptyObj;
4061     i->stackTrace = Jim_NewListObj(i, NULL, 0);
4062     i->unknown = Jim_NewStringObj(i, "unknown", -1);
4063     Jim_IncrRefCount(i->emptyObj);
4064     Jim_IncrRefCount(i->result);
4065     Jim_IncrRefCount(i->stackTrace);
4066     Jim_IncrRefCount(i->unknown);
4067
4068     /* Initialize key variables every interpreter should contain */
4069     pathPtr = Jim_NewStringObj(i, "./", -1);
4070     Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4071     Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4072
4073     /* Export the core API to extensions */
4074     JimRegisterCoreApi(i);
4075     return i;
4076 }
4077
4078 /* This is the only function Jim exports directly without
4079  * to use the STUB system. It is only used by embedders
4080  * in order to get an interpreter with the Jim API pointers
4081  * registered. */
4082 Jim_Interp *ExportedJimCreateInterp(void)
4083 {
4084     return Jim_CreateInterp();
4085 }
4086
4087 void Jim_FreeInterp(Jim_Interp *i)
4088 {
4089     Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4090     Jim_Obj *objPtr, *nextObjPtr;
4091
4092     Jim_DecrRefCount(i, i->emptyObj);
4093     Jim_DecrRefCount(i, i->result);
4094     Jim_DecrRefCount(i, i->stackTrace);
4095     Jim_DecrRefCount(i, i->unknown);
4096     Jim_Free((void*)i->errorFileName);
4097     Jim_Free((void*)i->scriptFileName);
4098     Jim_FreeHashTable(&i->commands);
4099     Jim_FreeHashTable(&i->references);
4100     Jim_FreeHashTable(&i->stub);
4101     Jim_FreeHashTable(&i->assocData);
4102     Jim_FreeHashTable(&i->packages);
4103     Jim_Free(i->prngState);
4104     /* Free the call frames list */
4105     while(cf) {
4106         prevcf = cf->parentCallFrame;
4107         JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4108         cf = prevcf;
4109     }
4110     /* Check that the live object list is empty, otherwise
4111      * there is a memory leak. */
4112     if (i->liveList != NULL) {
4113         Jim_Obj *objPtr = i->liveList;
4114     
4115         fprintf(i->stdout,JIM_NL "-------------------------------------" JIM_NL);
4116         fprintf(i->stdout,"Objects still in the free list:" JIM_NL);
4117         while(objPtr) {
4118             const char *type = objPtr->typePtr ?
4119                 objPtr->typePtr->name : "";
4120             fprintf(i->stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4121                     objPtr, type,
4122                     objPtr->bytes ? objPtr->bytes
4123                     : "(null)", objPtr->refCount);
4124             if (objPtr->typePtr == &sourceObjType) {
4125                 fprintf(i->stdout, "FILE %s LINE %d" JIM_NL,
4126                 objPtr->internalRep.sourceValue.fileName,
4127                 objPtr->internalRep.sourceValue.lineNumber);
4128             }
4129             objPtr = objPtr->nextObjPtr;
4130         }
4131         fprintf(stdout, "-------------------------------------" JIM_NL JIM_NL);
4132         Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4133     }
4134     /* Free all the freed objects. */
4135     objPtr = i->freeList;
4136     while (objPtr) {
4137         nextObjPtr = objPtr->nextObjPtr;
4138         Jim_Free(objPtr);
4139         objPtr = nextObjPtr;
4140     }
4141     /* Free cached CallFrame structures */
4142     cf = i->freeFramesList;
4143     while(cf) {
4144         nextcf = cf->nextFramePtr;
4145         if (cf->vars.table != NULL)
4146             Jim_Free(cf->vars.table);
4147         Jim_Free(cf);
4148         cf = nextcf;
4149     }
4150     /* Free the sharedString hash table. Make sure to free it
4151      * after every other Jim_Object was freed. */
4152     Jim_FreeHashTable(&i->sharedStrings);
4153     /* Free the interpreter structure. */
4154     Jim_Free(i);
4155 }
4156
4157 /* Store the call frame relative to the level represented by
4158  * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4159  * level is assumed to be '1'.
4160  *
4161  * If a newLevelptr int pointer is specified, the function stores
4162  * the absolute level integer value of the new target callframe into
4163  * *newLevelPtr. (this is used to adjust interp->numLevels
4164  * in the implementation of [uplevel], so that [info level] will
4165  * return a correct information).
4166  *
4167  * This function accepts the 'level' argument in the form
4168  * of the commands [uplevel] and [upvar].
4169  *
4170  * For a function accepting a relative integer as level suitable
4171  * for implementation of [info level ?level?] check the
4172  * GetCallFrameByInteger() function. */
4173 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4174         Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4175 {
4176     long level;
4177     const char *str;
4178     Jim_CallFrame *framePtr;
4179
4180     if (newLevelPtr) *newLevelPtr = interp->numLevels;
4181     if (levelObjPtr) {
4182         str = Jim_GetString(levelObjPtr, NULL);
4183         if (str[0] == '#') {
4184             char *endptr;
4185             /* speedup for the toplevel (level #0) */
4186             if (str[1] == '0' && str[2] == '\0') {
4187                 if (newLevelPtr) *newLevelPtr = 0;
4188                 *framePtrPtr = interp->topFramePtr;
4189                 return JIM_OK;
4190             }
4191
4192             level = strtol(str+1, &endptr, 0);
4193             if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4194                 goto badlevel;
4195             /* An 'absolute' level is converted into the
4196              * 'number of levels to go back' format. */
4197             level = interp->numLevels - level;
4198             if (level < 0) goto badlevel;
4199         } else {
4200             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4201                 goto badlevel;
4202         }
4203     } else {
4204         str = "1"; /* Needed to format the error message. */
4205         level = 1;
4206     }
4207     /* Lookup */
4208     framePtr = interp->framePtr;
4209     if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4210     while (level--) {
4211         framePtr = framePtr->parentCallFrame;
4212         if (framePtr == NULL) goto badlevel;
4213     }
4214     *framePtrPtr = framePtr;
4215     return JIM_OK;
4216 badlevel:
4217     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4218     Jim_AppendStrings(interp, Jim_GetResult(interp),
4219             "bad level \"", str, "\"", NULL);
4220     return JIM_ERR;
4221 }
4222
4223 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4224  * as a relative integer like in the [info level ?level?] command. */
4225 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4226         Jim_CallFrame **framePtrPtr)
4227 {
4228     jim_wide level;
4229     jim_wide relLevel; /* level relative to the current one. */
4230     Jim_CallFrame *framePtr;
4231
4232     if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4233         goto badlevel;
4234     if (level > 0) {
4235         /* An 'absolute' level is converted into the
4236          * 'number of levels to go back' format. */
4237         relLevel = interp->numLevels - level;
4238     } else {
4239         relLevel = -level;
4240     }
4241     /* Lookup */
4242     framePtr = interp->framePtr;
4243     while (relLevel--) {
4244         framePtr = framePtr->parentCallFrame;
4245         if (framePtr == NULL) goto badlevel;
4246     }
4247     *framePtrPtr = framePtr;
4248     return JIM_OK;
4249 badlevel:
4250     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4251     Jim_AppendStrings(interp, Jim_GetResult(interp),
4252             "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4253     return JIM_ERR;
4254 }
4255
4256 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4257 {
4258     Jim_Free((void*)interp->errorFileName);
4259     interp->errorFileName = Jim_StrDup(filename);
4260 }
4261
4262 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4263 {
4264     interp->errorLine = linenr;
4265 }
4266
4267 static void JimResetStackTrace(Jim_Interp *interp)
4268 {
4269     Jim_DecrRefCount(interp, interp->stackTrace);
4270     interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4271     Jim_IncrRefCount(interp->stackTrace);
4272 }
4273
4274 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4275         const char *filename, int linenr)
4276 {
4277     if (Jim_IsShared(interp->stackTrace)) {
4278         interp->stackTrace =
4279             Jim_DuplicateObj(interp, interp->stackTrace);
4280         Jim_IncrRefCount(interp->stackTrace);
4281     }
4282     Jim_ListAppendElement(interp, interp->stackTrace,
4283             Jim_NewStringObj(interp, procname, -1));
4284     Jim_ListAppendElement(interp, interp->stackTrace,
4285             Jim_NewStringObj(interp, filename, -1));
4286     Jim_ListAppendElement(interp, interp->stackTrace,
4287             Jim_NewIntObj(interp, linenr));
4288 }
4289
4290 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4291 {
4292     AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4293     assocEntryPtr->delProc = delProc;
4294     assocEntryPtr->data = data;
4295     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4296 }
4297
4298 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4299 {
4300     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4301     if (entryPtr != NULL) {
4302         AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4303         return assocEntryPtr->data;
4304     }
4305     return NULL;
4306 }
4307
4308 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4309 {
4310     return Jim_DeleteHashEntry(&interp->assocData, key);
4311 }
4312
4313 int Jim_GetExitCode(Jim_Interp *interp) {
4314     return interp->exitCode;
4315 }
4316
4317 FILE *Jim_SetStdin(Jim_Interp *interp, FILE *fp)
4318 {
4319     if (fp != NULL) interp->stdin = fp;
4320     return interp->stdin;
4321 }
4322
4323 FILE *Jim_SetStdout(Jim_Interp *interp, FILE *fp)
4324 {
4325     if (fp != NULL) interp->stdout = fp;
4326     return interp->stdout;
4327 }
4328
4329 FILE *Jim_SetStderr(Jim_Interp *interp, FILE *fp)
4330 {
4331     if (fp != NULL) interp->stderr = fp;
4332     return interp->stderr;
4333 }
4334
4335 /* -----------------------------------------------------------------------------
4336  * Shared strings.
4337  * Every interpreter has an hash table where to put shared dynamically
4338  * allocate strings that are likely to be used a lot of times.
4339  * For example, in the 'source' object type, there is a pointer to
4340  * the filename associated with that object. Every script has a lot
4341  * of this objects with the identical file name, so it is wise to share
4342  * this info.
4343  *
4344  * The API is trivial: Jim_GetSharedString(interp, "foobar")
4345  * returns the pointer to the shared string. Every time a reference
4346  * to the string is no longer used, the user should call
4347  * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4348  * a given string, it is removed from the hash table.
4349  * ---------------------------------------------------------------------------*/
4350 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4351 {
4352     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4353
4354     if (he == NULL) {
4355         char *strCopy = Jim_StrDup(str);
4356
4357         Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4358         return strCopy;
4359     } else {
4360         long refCount = (long) he->val;
4361
4362         refCount++;
4363         he->val = (void*) refCount;
4364         return he->key;
4365     }
4366 }
4367
4368 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4369 {
4370     long refCount;
4371     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4372
4373     if (he == NULL)
4374         Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4375               "unknown shared string '%s'", str);
4376     refCount = (long) he->val;
4377     refCount--;
4378     if (refCount == 0) {
4379         Jim_DeleteHashEntry(&interp->sharedStrings, str);
4380     } else {
4381         he->val = (void*) refCount;
4382     }
4383 }
4384
4385 /* -----------------------------------------------------------------------------
4386  * Integer object
4387  * ---------------------------------------------------------------------------*/
4388 #define JIM_INTEGER_SPACE 24
4389
4390 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4391 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4392
4393 static Jim_ObjType intObjType = {
4394     "int",
4395     NULL,
4396     NULL,
4397     UpdateStringOfInt,
4398     JIM_TYPE_NONE,
4399 };
4400
4401 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4402 {
4403     int len;
4404     char buf[JIM_INTEGER_SPACE+1];
4405
4406     len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4407     objPtr->bytes = Jim_Alloc(len+1);
4408     memcpy(objPtr->bytes, buf, len+1);
4409     objPtr->length = len;
4410 }
4411
4412 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4413 {
4414     jim_wide wideValue;
4415     const char *str;
4416
4417     /* Get the string representation */
4418     str = Jim_GetString(objPtr, NULL);
4419     /* Try to convert into a jim_wide */
4420     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4421         if (flags & JIM_ERRMSG) {
4422             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4423             Jim_AppendStrings(interp, Jim_GetResult(interp),
4424                     "expected integer but got \"", str, "\"", NULL);
4425         }
4426         return JIM_ERR;
4427     }
4428     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4429         errno == ERANGE) {
4430         Jim_SetResultString(interp,
4431             "Integer value too big to be represented", -1);
4432         return JIM_ERR;
4433     }
4434     /* Free the old internal repr and set the new one. */
4435     Jim_FreeIntRep(interp, objPtr);
4436     objPtr->typePtr = &intObjType;
4437     objPtr->internalRep.wideValue = wideValue;
4438     return JIM_OK;
4439 }
4440
4441 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4442 {
4443     if (objPtr->typePtr != &intObjType &&
4444         SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4445         return JIM_ERR;
4446     *widePtr = objPtr->internalRep.wideValue;
4447     return JIM_OK;
4448 }
4449
4450 /* Get a wide but does not set an error if the format is bad. */
4451 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4452         jim_wide *widePtr)
4453 {
4454     if (objPtr->typePtr != &intObjType &&
4455         SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4456         return JIM_ERR;
4457     *widePtr = objPtr->internalRep.wideValue;
4458     return JIM_OK;
4459 }
4460
4461 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4462 {
4463     jim_wide wideValue;
4464     int retval;
4465
4466     retval = Jim_GetWide(interp, objPtr, &wideValue);
4467     if (retval == JIM_OK) {
4468         *longPtr = (long) wideValue;
4469         return JIM_OK;
4470     }
4471     return JIM_ERR;
4472 }
4473
4474 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4475 {
4476     if (Jim_IsShared(objPtr))
4477         Jim_Panic(interp,"Jim_SetWide called with shared object");
4478     if (objPtr->typePtr != &intObjType) {
4479         Jim_FreeIntRep(interp, objPtr);
4480         objPtr->typePtr = &intObjType;
4481     }
4482     Jim_InvalidateStringRep(objPtr);
4483     objPtr->internalRep.wideValue = wideValue;
4484 }
4485
4486 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4487 {
4488     Jim_Obj *objPtr;
4489
4490     objPtr = Jim_NewObj(interp);
4491     objPtr->typePtr = &intObjType;
4492     objPtr->bytes = NULL;
4493     objPtr->internalRep.wideValue = wideValue;
4494     return objPtr;
4495 }
4496
4497 /* -----------------------------------------------------------------------------
4498  * Double object
4499  * ---------------------------------------------------------------------------*/
4500 #define JIM_DOUBLE_SPACE 30
4501
4502 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4503 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4504
4505 static Jim_ObjType doubleObjType = {
4506     "double",
4507     NULL,
4508     NULL,
4509     UpdateStringOfDouble,
4510     JIM_TYPE_NONE,
4511 };
4512
4513 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4514 {
4515     int len;
4516     char buf[JIM_DOUBLE_SPACE+1];
4517
4518     len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4519     objPtr->bytes = Jim_Alloc(len+1);
4520     memcpy(objPtr->bytes, buf, len+1);
4521     objPtr->length = len;
4522 }
4523
4524 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4525 {
4526     double doubleValue;
4527     const char *str;
4528
4529     /* Get the string representation */
4530     str = Jim_GetString(objPtr, NULL);
4531     /* Try to convert into a double */
4532     if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4533         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4534         Jim_AppendStrings(interp, Jim_GetResult(interp),
4535                 "expected number but got '", str, "'", NULL);
4536         return JIM_ERR;
4537     }
4538     /* Free the old internal repr and set the new one. */
4539     Jim_FreeIntRep(interp, objPtr);
4540     objPtr->typePtr = &doubleObjType;
4541     objPtr->internalRep.doubleValue = doubleValue;
4542     return JIM_OK;
4543 }
4544
4545 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4546 {
4547     if (objPtr->typePtr != &doubleObjType &&
4548         SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4549         return JIM_ERR;
4550     *doublePtr = objPtr->internalRep.doubleValue;
4551     return JIM_OK;
4552 }
4553
4554 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4555 {
4556     if (Jim_IsShared(objPtr))
4557         Jim_Panic(interp,"Jim_SetDouble called with shared object");
4558     if (objPtr->typePtr != &doubleObjType) {
4559         Jim_FreeIntRep(interp, objPtr);
4560         objPtr->typePtr = &doubleObjType;
4561     }
4562     Jim_InvalidateStringRep(objPtr);
4563     objPtr->internalRep.doubleValue = doubleValue;
4564 }
4565
4566 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4567 {
4568     Jim_Obj *objPtr;
4569
4570     objPtr = Jim_NewObj(interp);
4571     objPtr->typePtr = &doubleObjType;
4572     objPtr->bytes = NULL;
4573     objPtr->internalRep.doubleValue = doubleValue;
4574     return objPtr;
4575 }
4576
4577 /* -----------------------------------------------------------------------------
4578  * List object
4579  * ---------------------------------------------------------------------------*/
4580 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4581 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4582 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4583 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4584 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4585
4586 /* Note that while the elements of the list may contain references,
4587  * the list object itself can't. This basically means that the
4588  * list object string representation as a whole can't contain references
4589  * that are not presents in the single elements. */
4590 static Jim_ObjType listObjType = {
4591     "list",
4592     FreeListInternalRep,
4593     DupListInternalRep,
4594     UpdateStringOfList,
4595     JIM_TYPE_NONE,
4596 };
4597
4598 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4599 {
4600     int i;
4601
4602     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4603         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4604     }
4605     Jim_Free(objPtr->internalRep.listValue.ele);
4606 }
4607
4608 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4609 {
4610     int i;
4611     JIM_NOTUSED(interp);
4612
4613     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4614     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4615     dupPtr->internalRep.listValue.ele =
4616         Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4617     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4618             sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4619     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4620         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4621     }
4622     dupPtr->typePtr = &listObjType;
4623 }
4624
4625 /* The following function checks if a given string can be encoded
4626  * into a list element without any kind of quoting, surrounded by braces,
4627  * or using escapes to quote. */
4628 #define JIM_ELESTR_SIMPLE 0
4629 #define JIM_ELESTR_BRACE 1
4630 #define JIM_ELESTR_QUOTE 2
4631 static int ListElementQuotingType(const char *s, int len)
4632 {
4633     int i, level, trySimple = 1;
4634
4635     /* Try with the SIMPLE case */
4636     if (len == 0) return JIM_ELESTR_BRACE;
4637     if (s[0] == '"' || s[0] == '{') {
4638         trySimple = 0;
4639         goto testbrace;
4640     }
4641     for (i = 0; i < len; i++) {
4642         switch(s[i]) {
4643         case ' ':
4644         case '$':
4645         case '"':
4646         case '[':
4647         case ']':
4648         case ';':
4649         case '\\':
4650         case '\r':
4651         case '\n':
4652         case '\t':
4653         case '\f':
4654         case '\v':
4655             trySimple = 0;
4656         case '{':
4657         case '}':
4658             goto testbrace;
4659         }
4660     }
4661     return JIM_ELESTR_SIMPLE;
4662
4663 testbrace:
4664     /* Test if it's possible to do with braces */
4665     if (s[len-1] == '\\' ||
4666         s[len-1] == ']') return JIM_ELESTR_QUOTE;
4667     level = 0;
4668     for (i = 0; i < len; i++) {
4669         switch(s[i]) {
4670         case '{': level++; break;
4671         case '}': level--;
4672               if (level < 0) return JIM_ELESTR_QUOTE;
4673               break;
4674         case '\\':
4675               if (s[i+1] == '\n')
4676                   return JIM_ELESTR_QUOTE;
4677               else
4678                   if (s[i+1] != '\0') i++;
4679               break;
4680         }
4681     }
4682     if (level == 0) {
4683         if (!trySimple) return JIM_ELESTR_BRACE;
4684         for (i = 0; i < len; i++) {
4685             switch(s[i]) {
4686             case ' ':
4687             case '$':
4688             case '"':
4689             case '[':
4690             case ']':
4691             case ';':
4692             case '\\':
4693             case '\r':
4694             case '\n':
4695             case '\t':
4696             case '\f':
4697             case '\v':
4698                 return JIM_ELESTR_BRACE;
4699                 break;
4700             }
4701         }
4702         return JIM_ELESTR_SIMPLE;
4703     }
4704     return JIM_ELESTR_QUOTE;
4705 }
4706
4707 /* Returns the malloc-ed representation of a string
4708  * using backslash to quote special chars. */
4709 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4710 {
4711     char *q = Jim_Alloc(len*2+1), *p;
4712
4713     p = q;
4714     while(*s) {
4715         switch (*s) {
4716         case ' ':
4717         case '$':
4718         case '"':
4719         case '[':
4720         case ']':
4721         case '{':
4722         case '}':
4723         case ';':
4724         case '\\':
4725             *p++ = '\\';
4726             *p++ = *s++;
4727             break;
4728         case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4729         case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4730         case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4731         case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4732         case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4733         default:
4734             *p++ = *s++;
4735             break;
4736         }
4737     }
4738     *p = '\0';
4739     *qlenPtr = p-q;
4740     return q;
4741 }
4742
4743 void UpdateStringOfList(struct Jim_Obj *objPtr)
4744 {
4745     int i, bufLen, realLength;
4746     const char *strRep;
4747     char *p;
4748     int *quotingType;
4749     Jim_Obj **ele = objPtr->internalRep.listValue.ele;
4750
4751     /* (Over) Estimate the space needed. */
4752     quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
4753     bufLen = 0;
4754     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4755         int len;
4756
4757         strRep = Jim_GetString(ele[i], &len);
4758         quotingType[i] = ListElementQuotingType(strRep, len);
4759         switch (quotingType[i]) {
4760         case JIM_ELESTR_SIMPLE: bufLen += len; break;
4761         case JIM_ELESTR_BRACE: bufLen += len+2; break;
4762         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
4763         }
4764         bufLen++; /* elements separator. */
4765     }
4766     bufLen++;
4767
4768     /* Generate the string rep. */
4769     p = objPtr->bytes = Jim_Alloc(bufLen+1);
4770     realLength = 0;
4771     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4772         int len, qlen;
4773         const char *strRep = Jim_GetString(ele[i], &len);
4774         char *q;
4775
4776         switch(quotingType[i]) {
4777         case JIM_ELESTR_SIMPLE:
4778             memcpy(p, strRep, len);
4779             p += len;
4780             realLength += len;
4781             break;
4782         case JIM_ELESTR_BRACE:
4783             *p++ = '{';
4784             memcpy(p, strRep, len);
4785             p += len;
4786             *p++ = '}';
4787             realLength += len+2;
4788             break;
4789         case JIM_ELESTR_QUOTE:
4790             q = BackslashQuoteString(strRep, len, &qlen);
4791             memcpy(p, q, qlen);
4792             Jim_Free(q);
4793             p += qlen;
4794             realLength += qlen;
4795             break;
4796         }
4797         /* Add a separating space */
4798         if (i+1 != objPtr->internalRep.listValue.len) {
4799             *p++ = ' ';
4800             realLength ++;
4801         }
4802     }
4803     *p = '\0'; /* nul term. */
4804     objPtr->length = realLength;
4805     Jim_Free(quotingType);
4806 }
4807
4808 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
4809 {
4810     struct JimParserCtx parser;
4811     const char *str;
4812     int strLen;
4813
4814     /* Get the string representation */
4815     str = Jim_GetString(objPtr, &strLen);
4816
4817     /* Free the old internal repr just now and initialize the
4818      * new one just now. The string->list conversion can't fail. */
4819     Jim_FreeIntRep(interp, objPtr);
4820     objPtr->typePtr = &listObjType;
4821     objPtr->internalRep.listValue.len = 0;
4822     objPtr->internalRep.listValue.maxLen = 0;
4823     objPtr->internalRep.listValue.ele = NULL;
4824
4825     /* Convert into a list */
4826     JimParserInit(&parser, str, strLen, 1);
4827     while(!JimParserEof(&parser)) {
4828         char *token;
4829         int tokenLen, type;
4830         Jim_Obj *elementPtr;
4831
4832         JimParseList(&parser);
4833         if (JimParserTtype(&parser) != JIM_TT_STR &&
4834             JimParserTtype(&parser) != JIM_TT_ESC)
4835             continue;
4836         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
4837         elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
4838         ListAppendElement(objPtr, elementPtr);
4839     }
4840     return JIM_OK;
4841 }
4842
4843 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, 
4844         int len)
4845 {
4846     Jim_Obj *objPtr;
4847     int i;
4848
4849     objPtr = Jim_NewObj(interp);
4850     objPtr->typePtr = &listObjType;
4851     objPtr->bytes = NULL;
4852     objPtr->internalRep.listValue.ele = NULL;
4853     objPtr->internalRep.listValue.len = 0;
4854     objPtr->internalRep.listValue.maxLen = 0;
4855     for (i = 0; i < len; i++) {
4856         ListAppendElement(objPtr, elements[i]);
4857     }
4858     return objPtr;
4859 }
4860
4861 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
4862  * length of the vector. Note that the user of this function should make
4863  * sure that the list object can't shimmer while the vector returned
4864  * is in use, this vector is the one stored inside the internal representation
4865  * of the list object. This function is not exported, extensions should
4866  * always access to the List object elements using Jim_ListIndex(). */
4867 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
4868         Jim_Obj ***listVec)
4869 {
4870     Jim_ListLength(interp, listObj, argc);
4871     assert(listObj->typePtr == &listObjType);
4872     *listVec = listObj->internalRep.listValue.ele;
4873 }
4874
4875 /* ListSortElements type values */
4876 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
4877       JIM_LSORT_NOCASE_DECR};
4878
4879 /* Sort the internal rep of a list. */
4880 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4881 {
4882     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
4883 }
4884
4885 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4886 {
4887     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
4888 }
4889
4890 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4891 {
4892     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
4893 }
4894
4895 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4896 {
4897     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
4898 }
4899
4900 /* Sort a list *in place*. MUST be called with non-shared objects. */
4901 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
4902 {
4903     typedef int (qsort_comparator)(const void *, const void *);
4904     int (*fn)(Jim_Obj**, Jim_Obj**);
4905     Jim_Obj **vector;
4906     int len;
4907
4908     if (Jim_IsShared(listObjPtr))
4909         Jim_Panic(interp,"Jim_ListSortElements called with shared object");
4910     if (listObjPtr->typePtr != &listObjType)
4911         SetListFromAny(interp, listObjPtr);
4912
4913     vector = listObjPtr->internalRep.listValue.ele;
4914     len = listObjPtr->internalRep.listValue.len;
4915     switch (type) {
4916         case JIM_LSORT_ASCII: fn = ListSortString;  break;
4917         case JIM_LSORT_NOCASE: fn = ListSortStringNoCase;  break;
4918         case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr;  break;
4919         case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr;  break;
4920         default:
4921             fn = NULL; /* avoid warning */
4922             Jim_Panic(interp,"ListSort called with invalid sort type");
4923     }
4924     qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
4925     Jim_InvalidateStringRep(listObjPtr);
4926 }
4927
4928 /* This is the low-level function to append an element to a list.
4929  * The higher-level Jim_ListAppendElement() performs shared object
4930  * check and invalidate the string repr. This version is used
4931  * in the internals of the List Object and is not exported.
4932  *
4933  * NOTE: this function can be called only against objects
4934  * with internal type of List. */
4935 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
4936 {
4937     int requiredLen = listPtr->internalRep.listValue.len + 1;
4938
4939     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4940         int maxLen = requiredLen * 2;
4941
4942         listPtr->internalRep.listValue.ele =
4943             Jim_Realloc(listPtr->internalRep.listValue.ele,
4944                     sizeof(Jim_Obj*)*maxLen);
4945         listPtr->internalRep.listValue.maxLen = maxLen;
4946     }
4947     listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
4948         objPtr;
4949     listPtr->internalRep.listValue.len ++;
4950     Jim_IncrRefCount(objPtr);
4951 }
4952
4953 /* This is the low-level function to insert elements into a list.
4954  * The higher-level Jim_ListInsertElements() performs shared object
4955  * check and invalidate the string repr. This version is used
4956  * in the internals of the List Object and is not exported.
4957  *
4958  * NOTE: this function can be called only against objects
4959  * with internal type of List. */
4960 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
4961         Jim_Obj *const *elemVec)
4962 {
4963     int currentLen = listPtr->internalRep.listValue.len;
4964     int requiredLen = currentLen + elemc;
4965     int i;
4966     Jim_Obj **point;
4967
4968     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4969         int maxLen = requiredLen * 2;
4970
4971         listPtr->internalRep.listValue.ele =
4972             Jim_Realloc(listPtr->internalRep.listValue.ele,
4973                     sizeof(Jim_Obj*)*maxLen);
4974         listPtr->internalRep.listValue.maxLen = maxLen;
4975     }
4976     point = listPtr->internalRep.listValue.ele + index;
4977     memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
4978     for (i=0; i < elemc; ++i) {
4979         point[i] = elemVec[i];
4980         Jim_IncrRefCount(point[i]);
4981     }
4982     listPtr->internalRep.listValue.len += elemc;
4983 }
4984
4985 /* Appends every element of appendListPtr into listPtr.
4986  * Both have to be of the list type. */
4987 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
4988 {
4989     int i, oldLen = listPtr->internalRep.listValue.len;
4990     int appendLen = appendListPtr->internalRep.listValue.len;
4991     int requiredLen = oldLen + appendLen;
4992
4993     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4994         int maxLen = requiredLen * 2;
4995
4996         listPtr->internalRep.listValue.ele =
4997             Jim_Realloc(listPtr->internalRep.listValue.ele,
4998                     sizeof(Jim_Obj*)*maxLen);
4999         listPtr->internalRep.listValue.maxLen = maxLen;
5000     }
5001     for (i = 0; i < appendLen; i++) {
5002         Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5003         listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5004         Jim_IncrRefCount(objPtr);
5005     }
5006     listPtr->internalRep.listValue.len += appendLen;
5007 }
5008
5009 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5010 {
5011     if (Jim_IsShared(listPtr))
5012         Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5013     if (listPtr->typePtr != &listObjType)
5014         SetListFromAny(interp, listPtr);
5015     Jim_InvalidateStringRep(listPtr);
5016     ListAppendElement(listPtr, objPtr);
5017 }
5018
5019 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5020 {
5021     if (Jim_IsShared(listPtr))
5022         Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5023     if (listPtr->typePtr != &listObjType)
5024         SetListFromAny(interp, listPtr);
5025     Jim_InvalidateStringRep(listPtr);
5026     ListAppendList(listPtr, appendListPtr);
5027 }
5028
5029 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5030 {
5031     if (listPtr->typePtr != &listObjType)
5032         SetListFromAny(interp, listPtr);
5033     *intPtr = listPtr->internalRep.listValue.len;
5034 }
5035
5036 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5037         int objc, Jim_Obj *const *objVec)
5038 {
5039     if (Jim_IsShared(listPtr))
5040         Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5041     if (listPtr->typePtr != &listObjType)
5042         SetListFromAny(interp, listPtr);
5043     if (index >= 0 && index > listPtr->internalRep.listValue.len)
5044         index = listPtr->internalRep.listValue.len;
5045     else if (index < 0 ) 
5046         index = 0;
5047     Jim_InvalidateStringRep(listPtr);
5048     ListInsertElements(listPtr, index, objc, objVec);
5049 }
5050
5051 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5052         Jim_Obj **objPtrPtr, int flags)
5053 {
5054     if (listPtr->typePtr != &listObjType)
5055         SetListFromAny(interp, listPtr);
5056     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5057         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5058         if (flags & JIM_ERRMSG) {
5059             Jim_SetResultString(interp,
5060                 "list index out of range", -1);
5061         }
5062         return JIM_ERR;
5063     }
5064     if (index < 0)
5065         index = listPtr->internalRep.listValue.len+index;
5066     *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5067     return JIM_OK;
5068 }
5069
5070 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5071         Jim_Obj *newObjPtr, int flags)
5072 {
5073     if (listPtr->typePtr != &listObjType)
5074         SetListFromAny(interp, listPtr);
5075     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5076         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5077         if (flags & JIM_ERRMSG) {
5078             Jim_SetResultString(interp,
5079                 "list index out of range", -1);
5080         }
5081         return JIM_ERR;
5082     }
5083     if (index < 0)
5084         index = listPtr->internalRep.listValue.len+index;
5085     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5086     listPtr->internalRep.listValue.ele[index] = newObjPtr;
5087     Jim_IncrRefCount(newObjPtr);
5088     return JIM_OK;
5089 }
5090
5091 /* Modify the list stored into the variable named 'varNamePtr'
5092  * setting the element specified by the 'indexc' indexes objects in 'indexv',
5093  * with the new element 'newObjptr'. */
5094 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5095         Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5096 {
5097     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5098     int shared, i, index;
5099
5100     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5101     if (objPtr == NULL)
5102         return JIM_ERR;
5103     if ((shared = Jim_IsShared(objPtr)))
5104         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5105     for (i = 0; i < indexc-1; i++) {
5106         listObjPtr = objPtr;
5107         if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5108             goto err;
5109         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5110                     JIM_ERRMSG) != JIM_OK) {
5111             goto err;
5112         }
5113         if (Jim_IsShared(objPtr)) {
5114             objPtr = Jim_DuplicateObj(interp, objPtr);
5115             ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5116         }
5117         Jim_InvalidateStringRep(listObjPtr);
5118     }
5119     if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5120         goto err;
5121     if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5122         goto err;
5123     Jim_InvalidateStringRep(objPtr);
5124     Jim_InvalidateStringRep(varObjPtr);
5125     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5126         goto err;
5127     Jim_SetResult(interp, varObjPtr);
5128     return JIM_OK;
5129 err:
5130     if (shared) {
5131         Jim_FreeNewObj(interp, varObjPtr);
5132     }
5133     return JIM_ERR;
5134 }
5135
5136 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5137 {
5138     int i;
5139
5140     /* If all the objects in objv are lists without string rep.
5141      * it's possible to return a list as result, that's the
5142      * concatenation of all the lists. */
5143     for (i = 0; i < objc; i++) {
5144         if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5145             break;
5146     }
5147     if (i == objc) {
5148         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5149         for (i = 0; i < objc; i++)
5150             Jim_ListAppendList(interp, objPtr, objv[i]);
5151         return objPtr;
5152     } else {
5153         /* Else... we have to glue strings together */
5154         int len = 0, objLen;
5155         char *bytes, *p;
5156
5157         /* Compute the length */
5158         for (i = 0; i < objc; i++) {
5159             Jim_GetString(objv[i], &objLen);
5160             len += objLen;
5161         }
5162         if (objc) len += objc-1;
5163         /* Create the string rep, and a stinrg object holding it. */
5164         p = bytes = Jim_Alloc(len+1);
5165         for (i = 0; i < objc; i++) {
5166             const char *s = Jim_GetString(objv[i], &objLen);
5167             while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5168             {
5169                 s++; objLen--; len--;
5170             }
5171             while (objLen && (s[objLen-1] == ' ' ||
5172                 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5173                 objLen--; len--;
5174             }
5175             memcpy(p, s, objLen);
5176             p += objLen;
5177             if (objLen && i+1 != objc) {
5178                 *p++ = ' ';
5179             } else if (i+1 != objc) {
5180                 /* Drop the space calcuated for this
5181                  * element that is instead null. */
5182                 len--;
5183             }
5184         }
5185         *p = '\0';
5186         return Jim_NewStringObjNoAlloc(interp, bytes, len);
5187     }
5188 }
5189
5190 /* Returns a list composed of the elements in the specified range.
5191  * first and start are directly accepted as Jim_Objects and
5192  * processed for the end?-index? case. */
5193 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5194 {
5195     int first, last;
5196     int len, rangeLen;
5197
5198     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5199         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5200         return NULL;
5201     Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5202     first = JimRelToAbsIndex(len, first);
5203     last = JimRelToAbsIndex(len, last);
5204     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5205     return Jim_NewListObj(interp,
5206             listObjPtr->internalRep.listValue.ele+first, rangeLen);
5207 }
5208
5209 /* -----------------------------------------------------------------------------
5210  * Dict object
5211  * ---------------------------------------------------------------------------*/
5212 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5213 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5214 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5215 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5216
5217 /* Dict HashTable Type.
5218  *
5219  * Keys and Values are Jim objects. */
5220
5221 unsigned int JimObjectHTHashFunction(const void *key)
5222 {
5223     const char *str;
5224     Jim_Obj *objPtr = (Jim_Obj*) key;
5225     int len, h;
5226
5227     str = Jim_GetString(objPtr, &len);
5228     h = Jim_GenHashFunction((unsigned char*)str, len);
5229     return h;
5230 }
5231
5232 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5233 {
5234     JIM_NOTUSED(privdata);
5235
5236     return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5237 }
5238
5239 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5240 {
5241     Jim_Obj *objPtr = val;
5242
5243     Jim_DecrRefCount(interp, objPtr);
5244 }
5245
5246 static Jim_HashTableType JimDictHashTableType = {
5247     JimObjectHTHashFunction,            /* hash function */
5248     NULL,                               /* key dup */
5249     NULL,                               /* val dup */
5250     JimObjectHTKeyCompare,              /* key compare */
5251     (void(*)(void*, const void*))       /* ATTENTION: const cast */
5252         JimObjectHTKeyValDestructor,    /* key destructor */
5253     JimObjectHTKeyValDestructor         /* val destructor */
5254 };
5255
5256 /* Note that while the elements of the dict may contain references,
5257  * the list object itself can't. This basically means that the
5258  * dict object string representation as a whole can't contain references
5259  * that are not presents in the single elements. */
5260 static Jim_ObjType dictObjType = {
5261     "dict",
5262     FreeDictInternalRep,
5263     DupDictInternalRep,
5264     UpdateStringOfDict,
5265     JIM_TYPE_NONE,
5266 };
5267
5268 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5269 {
5270     JIM_NOTUSED(interp);
5271
5272     Jim_FreeHashTable(objPtr->internalRep.ptr);
5273     Jim_Free(objPtr->internalRep.ptr);
5274 }
5275
5276 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5277 {
5278     Jim_HashTable *ht, *dupHt;
5279     Jim_HashTableIterator *htiter;
5280     Jim_HashEntry *he;
5281
5282     /* Create a new hash table */
5283     ht = srcPtr->internalRep.ptr;
5284     dupHt = Jim_Alloc(sizeof(*dupHt));
5285     Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5286     if (ht->size != 0)
5287         Jim_ExpandHashTable(dupHt, ht->size);
5288     /* Copy every element from the source to the dup hash table */
5289     htiter = Jim_GetHashTableIterator(ht);
5290     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5291         const Jim_Obj *keyObjPtr = he->key;
5292         Jim_Obj *valObjPtr = he->val;
5293
5294         Jim_IncrRefCount((Jim_Obj*)keyObjPtr);  /* ATTENTION: const cast */
5295         Jim_IncrRefCount(valObjPtr);
5296         Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5297     }
5298     Jim_FreeHashTableIterator(htiter);
5299
5300     dupPtr->internalRep.ptr = dupHt;
5301     dupPtr->typePtr = &dictObjType;
5302 }
5303
5304 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5305 {
5306     int i, bufLen, realLength;
5307     const char *strRep;
5308     char *p;
5309     int *quotingType, objc;
5310     Jim_HashTable *ht;
5311     Jim_HashTableIterator *htiter;
5312     Jim_HashEntry *he;
5313     Jim_Obj **objv;
5314
5315     /* Trun the hash table into a flat vector of Jim_Objects. */
5316     ht = objPtr->internalRep.ptr;
5317     objc = ht->used*2;
5318     objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5319     htiter = Jim_GetHashTableIterator(ht);
5320     i = 0;
5321     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5322         objv[i++] = (Jim_Obj*)he->key;  /* ATTENTION: const cast */
5323         objv[i++] = he->val;
5324     }
5325     Jim_FreeHashTableIterator(htiter);
5326     /* (Over) Estimate the space needed. */
5327     quotingType = Jim_Alloc(sizeof(int)*objc);
5328     bufLen = 0;
5329     for (i = 0; i < objc; i++) {
5330         int len;
5331
5332         strRep = Jim_GetString(objv[i], &len);
5333         quotingType[i] = ListElementQuotingType(strRep, len);
5334         switch (quotingType[i]) {
5335         case JIM_ELESTR_SIMPLE: bufLen += len; break;
5336         case JIM_ELESTR_BRACE: bufLen += len+2; break;
5337         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5338         }
5339         bufLen++; /* elements separator. */
5340     }
5341     bufLen++;
5342
5343     /* Generate the string rep. */
5344     p = objPtr->bytes = Jim_Alloc(bufLen+1);
5345     realLength = 0;
5346     for (i = 0; i < objc; i++) {
5347         int len, qlen;
5348         const char *strRep = Jim_GetString(objv[i], &len);
5349         char *q;
5350
5351         switch(quotingType[i]) {
5352         case JIM_ELESTR_SIMPLE:
5353             memcpy(p, strRep, len);
5354             p += len;
5355             realLength += len;
5356             break;
5357         case JIM_ELESTR_BRACE:
5358             *p++ = '{';
5359             memcpy(p, strRep, len);
5360             p += len;
5361             *p++ = '}';
5362             realLength += len+2;
5363             break;
5364         case JIM_ELESTR_QUOTE:
5365             q = BackslashQuoteString(strRep, len, &qlen);
5366             memcpy(p, q, qlen);
5367             Jim_Free(q);
5368             p += qlen;
5369             realLength += qlen;
5370             break;
5371         }
5372         /* Add a separating space */
5373         if (i+1 != objc) {
5374             *p++ = ' ';
5375             realLength ++;
5376         }
5377     }
5378     *p = '\0'; /* nul term. */
5379     objPtr->length = realLength;
5380     Jim_Free(quotingType);
5381     Jim_Free(objv);
5382 }
5383
5384 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5385 {
5386     struct JimParserCtx parser;
5387     Jim_HashTable *ht;
5388     Jim_Obj *objv[2];
5389     const char *str;
5390     int i, strLen;
5391
5392     /* Get the string representation */
5393     str = Jim_GetString(objPtr, &strLen);
5394
5395     /* Free the old internal repr just now and initialize the
5396      * new one just now. The string->list conversion can't fail. */
5397     Jim_FreeIntRep(interp, objPtr);
5398     ht = Jim_Alloc(sizeof(*ht));
5399     Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5400     objPtr->typePtr = &dictObjType;
5401     objPtr->internalRep.ptr = ht;
5402
5403     /* Convert into a dict */
5404     JimParserInit(&parser, str, strLen, 1);
5405     i = 0;
5406     while(!JimParserEof(&parser)) {
5407         char *token;
5408         int tokenLen, type;
5409
5410         JimParseList(&parser);
5411         if (JimParserTtype(&parser) != JIM_TT_STR &&
5412             JimParserTtype(&parser) != JIM_TT_ESC)
5413             continue;
5414         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5415         objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5416         if (i == 2) {
5417             i = 0;
5418             Jim_IncrRefCount(objv[0]);
5419             Jim_IncrRefCount(objv[1]);
5420             if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5421                 Jim_HashEntry *he;
5422                 he = Jim_FindHashEntry(ht, objv[0]);
5423                 Jim_DecrRefCount(interp, objv[0]);
5424                 /* ATTENTION: const cast */
5425                 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5426                 he->val = objv[1];
5427             }
5428         }
5429     }
5430     if (i) {
5431         Jim_FreeNewObj(interp, objv[0]);
5432         objPtr->typePtr = NULL;
5433         Jim_FreeHashTable(ht);
5434         Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5435         return JIM_ERR;
5436     }
5437     return JIM_OK;
5438 }
5439
5440 /* Dict object API */
5441
5442 /* Add an element to a dict. objPtr must be of the "dict" type.
5443  * The higer-level exported function is Jim_DictAddElement().
5444  * If an element with the specified key already exists, the value
5445  * associated is replaced with the new one.
5446  *
5447  * if valueObjPtr == NULL, the key is instead removed if it exists. */
5448 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5449         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5450 {
5451     Jim_HashTable *ht = objPtr->internalRep.ptr;
5452
5453     if (valueObjPtr == NULL) { /* unset */
5454         Jim_DeleteHashEntry(ht, keyObjPtr);
5455         return;
5456     }
5457     Jim_IncrRefCount(keyObjPtr);
5458     Jim_IncrRefCount(valueObjPtr);
5459     if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5460         Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5461         Jim_DecrRefCount(interp, keyObjPtr);
5462         /* ATTENTION: const cast */
5463         Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5464         he->val = valueObjPtr;
5465     }
5466 }
5467
5468 /* Add an element, higher-level interface for DictAddElement().
5469  * If valueObjPtr == NULL, the key is removed if it exists. */
5470 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5471         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5472 {
5473     if (Jim_IsShared(objPtr))
5474         Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5475     if (objPtr->typePtr != &dictObjType) {
5476         if (SetDictFromAny(interp, objPtr) != JIM_OK)
5477             return JIM_ERR;
5478     }
5479     DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5480     Jim_InvalidateStringRep(objPtr);
5481     return JIM_OK;
5482 }
5483
5484 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5485 {
5486     Jim_Obj *objPtr;
5487     int i;
5488
5489     if (len % 2)
5490         Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5491
5492     objPtr = Jim_NewObj(interp);
5493     objPtr->typePtr = &dictObjType;
5494     objPtr->bytes = NULL;
5495     objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5496     Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5497     for (i = 0; i < len; i += 2)
5498         DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5499     return objPtr;
5500 }
5501
5502 /* Return the value associated to the specified dict key */
5503 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5504         Jim_Obj **objPtrPtr, int flags)
5505 {
5506     Jim_HashEntry *he;
5507     Jim_HashTable *ht;
5508
5509     if (dictPtr->typePtr != &dictObjType) {
5510         if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5511             return JIM_ERR;
5512     }
5513     ht = dictPtr->internalRep.ptr;
5514     if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5515         if (flags & JIM_ERRMSG) {
5516             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5517             Jim_AppendStrings(interp, Jim_GetResult(interp),
5518                     "key \"", Jim_GetString(keyPtr, NULL),
5519                     "\" not found in dictionary", NULL);
5520         }
5521         return JIM_ERR;
5522     }
5523     *objPtrPtr = he->val;
5524     return JIM_OK;
5525 }
5526
5527 /* Return the value associated to the specified dict keys */
5528 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5529         Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5530 {
5531     Jim_Obj *objPtr;
5532     int i;
5533
5534     if (keyc == 0) {
5535         *objPtrPtr = dictPtr;
5536         return JIM_OK;
5537     }
5538
5539     for (i = 0; i < keyc; i++) {
5540         if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5541                 != JIM_OK)
5542             return JIM_ERR;
5543         dictPtr = objPtr;
5544     }
5545     *objPtrPtr = objPtr;
5546     return JIM_OK;
5547 }
5548
5549 /* Modify the dict stored into the variable named 'varNamePtr'
5550  * setting the element specified by the 'keyc' keys objects in 'keyv',
5551  * with the new value of the element 'newObjPtr'.
5552  *
5553  * If newObjPtr == NULL the operation is to remove the given key
5554  * from the dictionary. */
5555 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5556         Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5557 {
5558     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5559     int shared, i;
5560
5561     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5562     if (objPtr == NULL) {
5563         if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5564             return JIM_ERR;
5565         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5566         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5567             Jim_FreeNewObj(interp, varObjPtr);
5568             return JIM_ERR;
5569         }
5570     }
5571     if ((shared = Jim_IsShared(objPtr)))
5572         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5573     for (i = 0; i < keyc-1; i++) {
5574         dictObjPtr = objPtr;
5575
5576         /* Check if it's a valid dictionary */
5577         if (dictObjPtr->typePtr != &dictObjType) {
5578             if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5579                 goto err;
5580         }
5581         /* Check if the given key exists. */
5582         Jim_InvalidateStringRep(dictObjPtr);
5583         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5584             newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5585         {
5586             /* This key exists at the current level.
5587              * Make sure it's not shared!. */
5588             if (Jim_IsShared(objPtr)) {
5589                 objPtr = Jim_DuplicateObj(interp, objPtr);
5590                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5591             }
5592         } else {
5593             /* Key not found. If it's an [unset] operation
5594              * this is an error. Only the last key may not
5595              * exist. */
5596             if (newObjPtr == NULL)
5597                 goto err;
5598             /* Otherwise set an empty dictionary
5599              * as key's value. */
5600             objPtr = Jim_NewDictObj(interp, NULL, 0);
5601             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5602         }
5603     }
5604     if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5605             != JIM_OK)
5606         goto err;
5607     Jim_InvalidateStringRep(objPtr);
5608     Jim_InvalidateStringRep(varObjPtr);
5609     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5610         goto err;
5611     Jim_SetResult(interp, varObjPtr);
5612     return JIM_OK;
5613 err:
5614     if (shared) {
5615         Jim_FreeNewObj(interp, varObjPtr);
5616     }
5617     return JIM_ERR;
5618 }
5619
5620 /* -----------------------------------------------------------------------------
5621  * Index object
5622  * ---------------------------------------------------------------------------*/
5623 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5624 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5625
5626 static Jim_ObjType indexObjType = {
5627     "index",
5628     NULL,
5629     NULL,
5630     UpdateStringOfIndex,
5631     JIM_TYPE_NONE,
5632 };
5633
5634 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5635 {
5636     int len;
5637     char buf[JIM_INTEGER_SPACE+1];
5638
5639     if (objPtr->internalRep.indexValue >= 0)
5640         len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5641     else if (objPtr->internalRep.indexValue == -1)
5642         len = sprintf(buf, "end");
5643     else {
5644         len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5645     }
5646     objPtr->bytes = Jim_Alloc(len+1);
5647     memcpy(objPtr->bytes, buf, len+1);
5648     objPtr->length = len;
5649 }
5650
5651 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5652 {
5653     int index, end = 0;
5654     const char *str;
5655
5656     /* Get the string representation */
5657     str = Jim_GetString(objPtr, NULL);
5658     /* Try to convert into an index */
5659     if (!strcmp(str, "end")) {
5660         index = 0;
5661         end = 1;
5662     } else {
5663         if (!strncmp(str, "end-", 4)) {
5664             str += 4;
5665             end = 1;
5666         }
5667         if (Jim_StringToIndex(str, &index) != JIM_OK) {
5668             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5669             Jim_AppendStrings(interp, Jim_GetResult(interp),
5670                     "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5671                     "must be integer or end?-integer?", NULL);
5672             return JIM_ERR;
5673         }
5674     }
5675     if (end) {
5676         if (index < 0)
5677             index = INT_MAX;
5678         else
5679             index = -(index+1);
5680     } else if (!end && index < 0)
5681         index = -INT_MAX;
5682     /* Free the old internal repr and set the new one. */
5683     Jim_FreeIntRep(interp, objPtr);
5684     objPtr->typePtr = &indexObjType;
5685     objPtr->internalRep.indexValue = index;
5686     return JIM_OK;
5687 }
5688
5689 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5690 {
5691     /* Avoid shimmering if the object is an integer. */
5692     if (objPtr->typePtr == &intObjType) {
5693         jim_wide val = objPtr->internalRep.wideValue;
5694         if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5695             *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5696             return JIM_OK;
5697         }
5698     }
5699     if (objPtr->typePtr != &indexObjType &&
5700         SetIndexFromAny(interp, objPtr) == JIM_ERR)
5701         return JIM_ERR;
5702     *indexPtr = objPtr->internalRep.indexValue;
5703     return JIM_OK;
5704 }
5705
5706 /* -----------------------------------------------------------------------------
5707  * Return Code Object.
5708  * ---------------------------------------------------------------------------*/
5709
5710 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5711
5712 static Jim_ObjType returnCodeObjType = {
5713     "return-code",
5714     NULL,
5715     NULL,
5716     NULL,
5717     JIM_TYPE_NONE,
5718 };
5719
5720 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5721 {
5722     const char *str;
5723     int strLen, returnCode;
5724     jim_wide wideValue;
5725
5726     /* Get the string representation */
5727     str = Jim_GetString(objPtr, &strLen);
5728     /* Try to convert into an integer */
5729     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5730         returnCode = (int) wideValue;
5731     else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5732         returnCode = JIM_OK;
5733     else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5734         returnCode = JIM_ERR;
5735     else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5736         returnCode = JIM_RETURN;
5737     else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5738         returnCode = JIM_BREAK;
5739     else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5740         returnCode = JIM_CONTINUE;
5741     else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5742         returnCode = JIM_EVAL;
5743     else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5744         returnCode = JIM_EXIT;
5745     else {
5746         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5747         Jim_AppendStrings(interp, Jim_GetResult(interp),
5748                 "expected return code but got '", str, "'",
5749                 NULL);
5750         return JIM_ERR;
5751     }
5752     /* Free the old internal repr and set the new one. */
5753     Jim_FreeIntRep(interp, objPtr);
5754     objPtr->typePtr = &returnCodeObjType;
5755     objPtr->internalRep.returnCode = returnCode;
5756     return JIM_OK;
5757 }
5758
5759 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
5760 {
5761     if (objPtr->typePtr != &returnCodeObjType &&
5762         SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
5763         return JIM_ERR;
5764     *intPtr = objPtr->internalRep.returnCode;
5765     return JIM_OK;
5766 }
5767
5768 /* -----------------------------------------------------------------------------
5769  * Expression Parsing
5770  * ---------------------------------------------------------------------------*/
5771 static int JimParseExprOperator(struct JimParserCtx *pc);
5772 static int JimParseExprNumber(struct JimParserCtx *pc);
5773 static int JimParseExprIrrational(struct JimParserCtx *pc);
5774
5775 /* Exrp's Stack machine operators opcodes. */
5776
5777 /* Binary operators (numbers) */
5778 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
5779 #define JIM_EXPROP_MUL 0
5780 #define JIM_EXPROP_DIV 1
5781 #define JIM_EXPROP_MOD 2
5782 #define JIM_EXPROP_SUB 3
5783 #define JIM_EXPROP_ADD 4
5784 #define JIM_EXPROP_LSHIFT 5
5785 #define JIM_EXPROP_RSHIFT 6
5786 #define JIM_EXPROP_ROTL 7
5787 #define JIM_EXPROP_ROTR 8
5788 #define JIM_EXPROP_LT 9
5789 #define JIM_EXPROP_GT 10
5790 #define JIM_EXPROP_LTE 11
5791 #define JIM_EXPROP_GTE 12
5792 #define JIM_EXPROP_NUMEQ 13
5793 #define JIM_EXPROP_NUMNE 14
5794 #define JIM_EXPROP_BITAND 15
5795 #define JIM_EXPROP_BITXOR 16
5796 #define JIM_EXPROP_BITOR 17
5797 #define JIM_EXPROP_LOGICAND 18
5798 #define JIM_EXPROP_LOGICOR 19
5799 #define JIM_EXPROP_LOGICAND_LEFT 20
5800 #define JIM_EXPROP_LOGICOR_LEFT 21
5801 #define JIM_EXPROP_POW 22
5802 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
5803
5804 /* Binary operators (strings) */
5805 #define JIM_EXPROP_STREQ 23
5806 #define JIM_EXPROP_STRNE 24
5807
5808 /* Unary operators (numbers) */
5809 #define JIM_EXPROP_NOT 25
5810 #define JIM_EXPROP_BITNOT 26
5811 #define JIM_EXPROP_UNARYMINUS 27
5812 #define JIM_EXPROP_UNARYPLUS 28
5813 #define JIM_EXPROP_LOGICAND_RIGHT 29
5814 #define JIM_EXPROP_LOGICOR_RIGHT 30
5815
5816 /* Ternary operators */
5817 #define JIM_EXPROP_TERNARY 31
5818
5819 /* Operands */
5820 #define JIM_EXPROP_NUMBER 32
5821 #define JIM_EXPROP_COMMAND 33
5822 #define JIM_EXPROP_VARIABLE 34
5823 #define JIM_EXPROP_DICTSUGAR 35
5824 #define JIM_EXPROP_SUBST 36
5825 #define JIM_EXPROP_STRING 37
5826
5827 /* Operators table */
5828 typedef struct Jim_ExprOperator {
5829     const char *name;
5830     int precedence;
5831     int arity;
5832     int opcode;
5833 } Jim_ExprOperator;
5834
5835 /* name - precedence - arity - opcode */
5836 static struct Jim_ExprOperator Jim_ExprOperators[] = {
5837     {"!", 300, 1, JIM_EXPROP_NOT},
5838     {"~", 300, 1, JIM_EXPROP_BITNOT},
5839     {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
5840     {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
5841
5842     {"**", 250, 2, JIM_EXPROP_POW},
5843
5844     {"*", 200, 2, JIM_EXPROP_MUL},
5845     {"/", 200, 2, JIM_EXPROP_DIV},
5846     {"%", 200, 2, JIM_EXPROP_MOD},
5847
5848     {"-", 100, 2, JIM_EXPROP_SUB},
5849     {"+", 100, 2, JIM_EXPROP_ADD},
5850
5851     {"<<<", 90, 3, JIM_EXPROP_ROTL},
5852     {">>>", 90, 3, JIM_EXPROP_ROTR},
5853     {"<<", 90, 2, JIM_EXPROP_LSHIFT},
5854     {">>", 90, 2, JIM_EXPROP_RSHIFT},
5855
5856     {"<",  80, 2, JIM_EXPROP_LT},
5857     {">",  80, 2, JIM_EXPROP_GT},
5858     {"<=", 80, 2, JIM_EXPROP_LTE},
5859     {">=", 80, 2, JIM_EXPROP_GTE},
5860
5861     {"==", 70, 2, JIM_EXPROP_NUMEQ},
5862     {"!=", 70, 2, JIM_EXPROP_NUMNE},
5863
5864     {"eq", 60, 2, JIM_EXPROP_STREQ},
5865     {"ne", 60, 2, JIM_EXPROP_STRNE},
5866
5867     {"&", 50, 2, JIM_EXPROP_BITAND},
5868     {"^", 49, 2, JIM_EXPROP_BITXOR},
5869     {"|", 48, 2, JIM_EXPROP_BITOR},
5870
5871     {"&&", 10, 2, JIM_EXPROP_LOGICAND},
5872     {"||", 10, 2, JIM_EXPROP_LOGICOR},
5873
5874     {"?", 5, 3, JIM_EXPROP_TERNARY},
5875     /* private operators */
5876     {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
5877     {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
5878     {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
5879     {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
5880 };
5881
5882 #define JIM_EXPR_OPERATORS_NUM \
5883     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
5884
5885 int JimParseExpression(struct JimParserCtx *pc)
5886 {
5887     /* Discard spaces and quoted newline */
5888     while(*(pc->p) == ' ' ||
5889           *(pc->p) == '\t' ||
5890           *(pc->p) == '\r' ||
5891           *(pc->p) == '\n' ||
5892             (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
5893         pc->p++; pc->len--;
5894     }
5895
5896     if (pc->len == 0) {
5897         pc->tstart = pc->tend = pc->p;
5898         pc->tline = pc->linenr;
5899         pc->tt = JIM_TT_EOL;
5900         pc->eof = 1;
5901         return JIM_OK;
5902     }
5903     switch(*(pc->p)) {
5904     case '(':
5905         pc->tstart = pc->tend = pc->p;
5906         pc->tline = pc->linenr;
5907         pc->tt = JIM_TT_SUBEXPR_START;
5908         pc->p++; pc->len--;
5909         break;
5910     case ')':
5911         pc->tstart = pc->tend = pc->p;
5912         pc->tline = pc->linenr;
5913         pc->tt = JIM_TT_SUBEXPR_END;
5914         pc->p++; pc->len--;
5915         break;
5916     case '[':
5917         return JimParseCmd(pc);
5918         break;
5919     case '$':
5920         if (JimParseVar(pc) == JIM_ERR)
5921             return JimParseExprOperator(pc);
5922         else
5923             return JIM_OK;
5924         break;
5925     case '-':
5926         if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
5927             isdigit((int)*(pc->p+1)))
5928             return JimParseExprNumber(pc);
5929         else
5930             return JimParseExprOperator(pc);
5931         break;
5932     case '0': case '1': case '2': case '3': case '4':
5933     case '5': case '6': case '7': case '8': case '9': case '.':
5934         return JimParseExprNumber(pc);
5935         break;
5936     case '"':
5937     case '{':
5938         /* Here it's possible to reuse the List String parsing. */
5939         pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
5940         return JimParseListStr(pc);
5941         break;
5942     case 'N': case 'I':
5943     case 'n': case 'i':
5944         if (JimParseExprIrrational(pc) == JIM_ERR)
5945             return JimParseExprOperator(pc);
5946         break;
5947     default:
5948         return JimParseExprOperator(pc);
5949         break;
5950     }
5951     return JIM_OK;
5952 }
5953
5954 int JimParseExprNumber(struct JimParserCtx *pc)
5955 {
5956     int allowdot = 1;
5957
5958     pc->tstart = pc->p;
5959     pc->tline = pc->linenr;
5960     if (*pc->p == '-') {
5961         pc->p++; pc->len--;
5962     }
5963     while (isdigit((int)*pc->p) || (allowdot && *pc->p == '.') ||
5964            (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
5965               (*pc->p == 'x' || *pc->p == 'X')))
5966     {
5967         if (*pc->p == '.')
5968             allowdot = 0;
5969         pc->p++; pc->len--;
5970         if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
5971             pc->p += 2; pc->len -= 2;
5972         }
5973     }
5974     pc->tend = pc->p-1;
5975     pc->tt = JIM_TT_EXPR_NUMBER;
5976     return JIM_OK;
5977 }
5978
5979 int JimParseExprIrrational(struct JimParserCtx *pc)
5980 {
5981     const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
5982     const char **token;
5983     for (token = Tokens; *token != NULL; token++) {
5984         int len = strlen(*token);
5985         if (strncmp(*token, pc->p, len) == 0) {
5986             pc->tstart = pc->p;
5987             pc->tend = pc->p + len - 1;
5988             pc->p += len; pc->len -= len;
5989             pc->tline = pc->linenr;
5990             pc->tt = JIM_TT_EXPR_NUMBER;
5991             return JIM_OK;
5992         }
5993     }
5994     return JIM_ERR;
5995 }
5996
5997 int JimParseExprOperator(struct JimParserCtx *pc)
5998 {
5999     int i;
6000     int bestIdx = -1, bestLen = 0;
6001
6002     /* Try to get the longest match. */
6003     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6004         const char *opname;
6005         int oplen;
6006
6007         opname = Jim_ExprOperators[i].name;
6008         if (opname == NULL) continue;
6009         oplen = strlen(opname);
6010
6011         if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6012             bestIdx = i;
6013             bestLen = oplen;
6014         }
6015     }
6016     if (bestIdx == -1) return JIM_ERR;
6017     pc->tstart = pc->p;
6018     pc->tend = pc->p + bestLen - 1;
6019     pc->p += bestLen; pc->len -= bestLen;
6020     pc->tline = pc->linenr;
6021     pc->tt = JIM_TT_EXPR_OPERATOR;
6022     return JIM_OK;
6023 }
6024
6025 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6026 {
6027     int i;
6028     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6029         if (Jim_ExprOperators[i].name &&
6030             strcmp(opname, Jim_ExprOperators[i].name) == 0)
6031             return &Jim_ExprOperators[i];
6032     return NULL;
6033 }
6034
6035 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6036 {
6037     int i;
6038     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6039         if (Jim_ExprOperators[i].opcode == opcode)
6040             return &Jim_ExprOperators[i];
6041     return NULL;
6042 }
6043
6044 /* -----------------------------------------------------------------------------
6045  * Expression Object
6046  * ---------------------------------------------------------------------------*/
6047 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6048 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6049 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6050
6051 static Jim_ObjType exprObjType = {
6052     "expression",
6053     FreeExprInternalRep,
6054     DupExprInternalRep,
6055     NULL,
6056     JIM_TYPE_REFERENCES,
6057 };
6058
6059 /* Expr bytecode structure */
6060 typedef struct ExprByteCode {
6061     int *opcode;        /* Integer array of opcodes. */
6062     Jim_Obj **obj;      /* Array of associated Jim Objects. */
6063     int len;            /* Bytecode length */
6064     int inUse;          /* Used for sharing. */
6065 } ExprByteCode;
6066
6067 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6068 {
6069     int i;
6070     ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6071
6072     expr->inUse--;
6073     if (expr->inUse != 0) return;
6074     for (i = 0; i < expr->len; i++)
6075         Jim_DecrRefCount(interp, expr->obj[i]);
6076     Jim_Free(expr->opcode);
6077     Jim_Free(expr->obj);
6078     Jim_Free(expr);
6079 }
6080
6081 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6082 {
6083     JIM_NOTUSED(interp);
6084     JIM_NOTUSED(srcPtr);
6085
6086     /* Just returns an simple string. */
6087     dupPtr->typePtr = NULL;
6088 }
6089
6090 /* Add a new instruction to an expression bytecode structure. */
6091 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6092         int opcode, char *str, int len)
6093 {
6094     expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6095     expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6096     expr->opcode[expr->len] = opcode;
6097     expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6098     Jim_IncrRefCount(expr->obj[expr->len]);
6099     expr->len++;
6100 }
6101
6102 /* Check if an expr program looks correct. */
6103 static int ExprCheckCorrectness(ExprByteCode *expr)
6104 {
6105     int i;
6106     int stacklen = 0;
6107
6108     /* Try to check if there are stack underflows,
6109      * and make sure at the end of the program there is
6110      * a single result on the stack. */
6111     for (i = 0; i < expr->len; i++) {
6112         switch(expr->opcode[i]) {
6113         case JIM_EXPROP_NUMBER:
6114         case JIM_EXPROP_STRING:
6115         case JIM_EXPROP_SUBST:
6116         case JIM_EXPROP_VARIABLE:
6117         case JIM_EXPROP_DICTSUGAR:
6118         case JIM_EXPROP_COMMAND:
6119             stacklen++;
6120             break;
6121         case JIM_EXPROP_NOT:
6122         case JIM_EXPROP_BITNOT:
6123         case JIM_EXPROP_UNARYMINUS:
6124         case JIM_EXPROP_UNARYPLUS:
6125             /* Unary operations */
6126             if (stacklen < 1) return JIM_ERR;
6127             break;
6128         case JIM_EXPROP_ADD:
6129         case JIM_EXPROP_SUB:
6130         case JIM_EXPROP_MUL:
6131         case JIM_EXPROP_DIV:
6132         case JIM_EXPROP_MOD:
6133         case JIM_EXPROP_LT:
6134         case JIM_EXPROP_GT:
6135         case JIM_EXPROP_LTE:
6136         case JIM_EXPROP_GTE:
6137         case JIM_EXPROP_ROTL:
6138         case JIM_EXPROP_ROTR:
6139         case JIM_EXPROP_LSHIFT:
6140         case JIM_EXPROP_RSHIFT:
6141         case JIM_EXPROP_NUMEQ:
6142         case JIM_EXPROP_NUMNE:
6143         case JIM_EXPROP_STREQ:
6144         case JIM_EXPROP_STRNE:
6145         case JIM_EXPROP_BITAND:
6146         case JIM_EXPROP_BITXOR:
6147         case JIM_EXPROP_BITOR:
6148         case JIM_EXPROP_LOGICAND:
6149         case JIM_EXPROP_LOGICOR:
6150         case JIM_EXPROP_POW:
6151             /* binary operations */
6152             if (stacklen < 2) return JIM_ERR;
6153             stacklen--;
6154             break;
6155         default:
6156             Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6157             break;
6158         }
6159     }
6160     if (stacklen != 1) return JIM_ERR;
6161     return JIM_OK;
6162 }
6163
6164 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6165         ScriptObj *topLevelScript)
6166 {
6167     int i;
6168
6169     return;
6170     for (i = 0; i < expr->len; i++) {
6171         Jim_Obj *foundObjPtr;
6172
6173         if (expr->obj[i] == NULL) continue;
6174         foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6175                 NULL, expr->obj[i]);
6176         if (foundObjPtr != NULL) {
6177             Jim_IncrRefCount(foundObjPtr);
6178             Jim_DecrRefCount(interp, expr->obj[i]);
6179             expr->obj[i] = foundObjPtr;
6180         }
6181     }
6182 }
6183
6184 /* This procedure converts every occurrence of || and && opereators
6185  * in lazy unary versions.
6186  *
6187  * a b || is converted into:
6188  *
6189  * a <offset> |L b |R
6190  *
6191  * a b && is converted into:
6192  *
6193  * a <offset> &L b &R
6194  *
6195  * "|L" checks if 'a' is true:
6196  *   1) if it is true pushes 1 and skips <offset> istructions to reach
6197  *      the opcode just after |R.
6198  *   2) if it is false does nothing.
6199  * "|R" checks if 'b' is true:
6200  *   1) if it is true pushes 1, otherwise pushes 0.
6201  *
6202  * "&L" checks if 'a' is true:
6203  *   1) if it is true does nothing.
6204  *   2) If it is false pushes 0 and skips <offset> istructions to reach
6205  *      the opcode just after &R
6206  * "&R" checks if 'a' is true:
6207  *      if it is true pushes 1, otherwise pushes 0.
6208  */
6209 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6210 {
6211     while (1) {
6212         int index = -1, leftindex, arity, i, offset;
6213         Jim_ExprOperator *op;
6214
6215         /* Search for || or && */
6216         for (i = 0; i < expr->len; i++) {
6217             if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6218                 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6219                 index = i;
6220                 break;
6221             }
6222         }
6223         if (index == -1) return;
6224         /* Search for the end of the first operator */
6225         leftindex = index-1;
6226         arity = 1;
6227         while(arity) {
6228             switch(expr->opcode[leftindex]) {
6229             case JIM_EXPROP_NUMBER:
6230             case JIM_EXPROP_COMMAND:
6231             case JIM_EXPROP_VARIABLE:
6232             case JIM_EXPROP_DICTSUGAR:
6233             case JIM_EXPROP_SUBST:
6234             case JIM_EXPROP_STRING:
6235                 break;
6236             default:
6237                 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6238                 if (op == NULL) {
6239                     Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6240                 }
6241                 arity += op->arity;
6242                 break;
6243             }
6244             arity--;
6245             leftindex--;
6246         }
6247         leftindex++;
6248         expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6249         expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6250         memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6251                 sizeof(int)*(expr->len-leftindex));
6252         memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6253                 sizeof(Jim_Obj*)*(expr->len-leftindex));
6254         expr->len += 2;
6255         index += 2;
6256         offset = (index-leftindex)-1;
6257         Jim_DecrRefCount(interp, expr->obj[index]);
6258         if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6259             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6260             expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6261             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6262             expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6263         } else {
6264             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6265             expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6266             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6267             expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6268         }
6269         expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6270         expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6271         Jim_IncrRefCount(expr->obj[index]);
6272         Jim_IncrRefCount(expr->obj[leftindex]);
6273         Jim_IncrRefCount(expr->obj[leftindex+1]);
6274     }
6275 }
6276
6277 /* This method takes the string representation of an expression
6278  * and generates a program for the Expr's stack-based VM. */
6279 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6280 {
6281     int exprTextLen;
6282     const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6283     struct JimParserCtx parser;
6284     int i, shareLiterals;
6285     ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6286     Jim_Stack stack;
6287     Jim_ExprOperator *op;
6288
6289     /* Perform literal sharing with the current procedure
6290      * running only if this expression appears to be not generated
6291      * at runtime. */
6292     shareLiterals = objPtr->typePtr == &sourceObjType;
6293
6294     expr->opcode = NULL;
6295     expr->obj = NULL;
6296     expr->len = 0;
6297     expr->inUse = 1;
6298
6299     Jim_InitStack(&stack);
6300     JimParserInit(&parser, exprText, exprTextLen, 1);
6301     while(!JimParserEof(&parser)) {
6302         char *token;
6303         int len, type;
6304
6305         if (JimParseExpression(&parser) != JIM_OK) {
6306             Jim_SetResultString(interp, "Syntax error in expression", -1);
6307             goto err;
6308         }
6309         token = JimParserGetToken(&parser, &len, &type, NULL);
6310         if (type == JIM_TT_EOL) {
6311             Jim_Free(token);
6312             break;
6313         }
6314         switch(type) {
6315         case JIM_TT_STR:
6316             ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6317             break;
6318         case JIM_TT_ESC:
6319             ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6320             break;
6321         case JIM_TT_VAR:
6322             ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6323             break;
6324         case JIM_TT_DICTSUGAR:
6325             ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6326             break;
6327         case JIM_TT_CMD:
6328             ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6329             break;
6330         case JIM_TT_EXPR_NUMBER:
6331             ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6332             break;
6333         case JIM_TT_EXPR_OPERATOR:
6334             op = JimExprOperatorInfo(token);
6335             while(1) {
6336                 Jim_ExprOperator *stackTopOp;
6337
6338                 if (Jim_StackPeek(&stack) != NULL) {
6339                     stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6340                 } else {
6341                     stackTopOp = NULL;
6342                 }
6343                 if (Jim_StackLen(&stack) && op->arity != 1 &&
6344                     stackTopOp && stackTopOp->precedence >= op->precedence)
6345                 {
6346                     ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6347                         Jim_StackPeek(&stack), -1);
6348                     Jim_StackPop(&stack);
6349                 } else {
6350                     break;
6351                 }
6352             }
6353             Jim_StackPush(&stack, token);
6354             break;
6355         case JIM_TT_SUBEXPR_START:
6356             Jim_StackPush(&stack, Jim_StrDup("("));
6357             Jim_Free(token);
6358             break;
6359         case JIM_TT_SUBEXPR_END:
6360             {
6361                 int found = 0;
6362                 while(Jim_StackLen(&stack)) {
6363                     char *opstr = Jim_StackPop(&stack);
6364                     if (!strcmp(opstr, "(")) {
6365                         Jim_Free(opstr);
6366                         found = 1;
6367                         break;
6368                     }
6369                     op = JimExprOperatorInfo(opstr);
6370                     ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6371                 }
6372                 if (!found) {
6373                     Jim_SetResultString(interp,
6374                         "Unexpected close parenthesis", -1);
6375                     goto err;
6376                 }
6377             }
6378             Jim_Free(token);
6379             break;
6380         default:
6381             Jim_Panic(interp,"Default reached in SetExprFromAny()");
6382             break;
6383         }
6384     }
6385     while (Jim_StackLen(&stack)) {
6386         char *opstr = Jim_StackPop(&stack);
6387         op = JimExprOperatorInfo(opstr);
6388         if (op == NULL && !strcmp(opstr, "(")) {
6389             Jim_Free(opstr);
6390             Jim_SetResultString(interp, "Missing close parenthesis", -1);
6391             goto err;
6392         }
6393         ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6394     }
6395     /* Check program correctness. */
6396     if (ExprCheckCorrectness(expr) != JIM_OK) {
6397         Jim_SetResultString(interp, "Invalid expression", -1);
6398         goto err;
6399     }
6400
6401     /* Free the stack used for the compilation. */
6402     Jim_FreeStackElements(&stack, Jim_Free);
6403     Jim_FreeStack(&stack);
6404
6405     /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6406     ExprMakeLazy(interp, expr);
6407
6408     /* Perform literal sharing */
6409     if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6410         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6411         if (bodyObjPtr->typePtr == &scriptObjType) {
6412             ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6413             ExprShareLiterals(interp, expr, bodyScript);
6414         }
6415     }
6416
6417     /* Free the old internal rep and set the new one. */
6418     Jim_FreeIntRep(interp, objPtr);
6419     Jim_SetIntRepPtr(objPtr, expr);
6420     objPtr->typePtr = &exprObjType;
6421     return JIM_OK;
6422
6423 err:    /* we jump here on syntax/compile errors. */
6424     Jim_FreeStackElements(&stack, Jim_Free);
6425     Jim_FreeStack(&stack);
6426     Jim_Free(expr->opcode);
6427     for (i = 0; i < expr->len; i++) {
6428         Jim_DecrRefCount(interp,expr->obj[i]);
6429     }
6430     Jim_Free(expr->obj);
6431     Jim_Free(expr);
6432     return JIM_ERR;
6433 }
6434
6435 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6436 {
6437     if (objPtr->typePtr != &exprObjType) {
6438         if (SetExprFromAny(interp, objPtr) != JIM_OK)
6439             return NULL;
6440     }
6441     return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6442 }
6443
6444 /* -----------------------------------------------------------------------------
6445  * Expressions evaluation.
6446  * Jim uses a specialized stack-based virtual machine for expressions,
6447  * that takes advantage of the fact that expr's operators
6448  * can't be redefined.
6449  *
6450  * Jim_EvalExpression() uses the bytecode compiled by
6451  * SetExprFromAny() method of the "expression" object.
6452  *
6453  * On success a Tcl Object containing the result of the evaluation
6454  * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6455  * returned.
6456  * On error the function returns a retcode != to JIM_OK and set a suitable
6457  * error on the interp.
6458  * ---------------------------------------------------------------------------*/
6459 #define JIM_EE_STATICSTACK_LEN 10
6460
6461 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6462         Jim_Obj **exprResultPtrPtr)
6463 {
6464     ExprByteCode *expr;
6465     Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6466     int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6467
6468     Jim_IncrRefCount(exprObjPtr);
6469     expr = Jim_GetExpression(interp, exprObjPtr);
6470     if (!expr) {
6471         Jim_DecrRefCount(interp, exprObjPtr);
6472         return JIM_ERR; /* error in expression. */
6473     }
6474     /* In order to avoid that the internal repr gets freed due to
6475      * shimmering of the exprObjPtr's object, we make the internal rep
6476      * shared. */
6477     expr->inUse++;
6478
6479     /* The stack-based expr VM itself */
6480
6481     /* Stack allocation. Expr programs have the feature that
6482      * a program of length N can't require a stack longer than
6483      * N. */
6484     if (expr->len > JIM_EE_STATICSTACK_LEN)
6485         stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6486     else
6487         stack = staticStack;
6488
6489     /* Execute every istruction */
6490     for (i = 0; i < expr->len; i++) {
6491         Jim_Obj *A, *B, *objPtr;
6492         jim_wide wA, wB, wC;
6493         double dA, dB, dC;
6494         const char *sA, *sB;
6495         int Alen, Blen, retcode;
6496         int opcode = expr->opcode[i];
6497
6498         if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6499             stack[stacklen++] = expr->obj[i];
6500             Jim_IncrRefCount(expr->obj[i]);
6501         } else if (opcode == JIM_EXPROP_VARIABLE) {
6502             objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6503             if (objPtr == NULL) {
6504                 error = 1;
6505                 goto err;
6506             }
6507             stack[stacklen++] = objPtr;
6508             Jim_IncrRefCount(objPtr);
6509         } else if (opcode == JIM_EXPROP_SUBST) {
6510             if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6511                         &objPtr, JIM_NONE)) != JIM_OK)
6512             {
6513                 error = 1;
6514                 errRetCode = retcode;
6515                 goto err;
6516             }
6517             stack[stacklen++] = objPtr;
6518             Jim_IncrRefCount(objPtr);
6519         } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6520             objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6521             if (objPtr == NULL) {
6522                 error = 1;
6523                 goto err;
6524             }
6525             stack[stacklen++] = objPtr;
6526             Jim_IncrRefCount(objPtr);
6527         } else if (opcode == JIM_EXPROP_COMMAND) {
6528             if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6529                 error = 1;
6530                 errRetCode = retcode;
6531                 goto err;
6532             }
6533             stack[stacklen++] = interp->result;
6534             Jim_IncrRefCount(interp->result);
6535         } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6536                    opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6537         {
6538             /* Note that there isn't to increment the
6539              * refcount of objects. the references are moved
6540              * from stack to A and B. */
6541             B = stack[--stacklen];
6542             A = stack[--stacklen];
6543
6544             /* --- Integer --- */
6545             if ((A->typePtr == &doubleObjType && !A->bytes) ||
6546                 (B->typePtr == &doubleObjType && !B->bytes) ||
6547                 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6548                 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6549                 goto trydouble;
6550             }
6551             Jim_DecrRefCount(interp, A);
6552             Jim_DecrRefCount(interp, B);
6553             switch(expr->opcode[i]) {
6554             case JIM_EXPROP_ADD: wC = wA+wB; break;
6555             case JIM_EXPROP_SUB: wC = wA-wB; break;
6556             case JIM_EXPROP_MUL: wC = wA*wB; break;
6557             case JIM_EXPROP_LT: wC = wA<wB; break;
6558             case JIM_EXPROP_GT: wC = wA>wB; break;
6559             case JIM_EXPROP_LTE: wC = wA<=wB; break;
6560             case JIM_EXPROP_GTE: wC = wA>=wB; break;
6561             case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6562             case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6563             case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6564             case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6565             case JIM_EXPROP_BITAND: wC = wA&wB; break;
6566             case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6567             case JIM_EXPROP_BITOR: wC = wA|wB; break;
6568             case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6569             case JIM_EXPROP_LOGICAND_LEFT:
6570                 if (wA == 0) {
6571                     i += (int)wB;
6572                     wC = 0;
6573                 } else {
6574                     continue;
6575                 }
6576                 break;
6577             case JIM_EXPROP_LOGICOR_LEFT:
6578                 if (wA != 0) {
6579                     i += (int)wB;
6580                     wC = 1;
6581                 } else {
6582                     continue;
6583                 }
6584                 break;
6585             case JIM_EXPROP_DIV:
6586                 if (wB == 0) goto divbyzero;
6587                 wC = wA/wB;
6588                 break;
6589             case JIM_EXPROP_MOD:
6590                 if (wB == 0) goto divbyzero;
6591                 wC = wA%wB;
6592                 break;
6593             case JIM_EXPROP_ROTL: {
6594                 /* uint32_t would be better. But not everyone has inttypes.h?*/
6595                 unsigned long uA = (unsigned long)wA;
6596 #ifdef _MSC_VER
6597                 wC = _rotl(uA,(unsigned long)wB);
6598 #else
6599                 const unsigned int S = sizeof(unsigned long) * 8;
6600                 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6601 #endif
6602                 break;
6603             }
6604             case JIM_EXPROP_ROTR: {
6605                 unsigned long uA = (unsigned long)wA;
6606 #ifdef _MSC_VER
6607                 wC = _rotr(uA,(unsigned long)wB);
6608 #else
6609                 const unsigned int S = sizeof(unsigned long) * 8;
6610                 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6611 #endif
6612                 break;
6613             }
6614
6615             default:
6616                 wC = 0; /* avoid gcc warning */
6617                 break;
6618             }
6619             stack[stacklen] = Jim_NewIntObj(interp, wC);
6620             Jim_IncrRefCount(stack[stacklen]);
6621             stacklen++;
6622             continue;
6623 trydouble:
6624             /* --- Double --- */
6625             if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6626                 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6627                 Jim_DecrRefCount(interp, A);
6628                 Jim_DecrRefCount(interp, B);
6629                 error = 1;
6630                 goto err;
6631             }
6632             Jim_DecrRefCount(interp, A);
6633             Jim_DecrRefCount(interp, B);
6634             switch(expr->opcode[i]) {
6635             case JIM_EXPROP_ROTL:
6636             case JIM_EXPROP_ROTR:
6637             case JIM_EXPROP_LSHIFT:
6638             case JIM_EXPROP_RSHIFT:
6639             case JIM_EXPROP_BITAND:
6640             case JIM_EXPROP_BITXOR:
6641             case JIM_EXPROP_BITOR:
6642             case JIM_EXPROP_MOD:
6643             case JIM_EXPROP_POW:
6644                 Jim_SetResultString(interp,
6645                     "Got floating-point value where integer was expected", -1);
6646                 error = 1;
6647                 goto err;
6648                 break;
6649             case JIM_EXPROP_ADD: dC = dA+dB; break;
6650             case JIM_EXPROP_SUB: dC = dA-dB; break;
6651             case JIM_EXPROP_MUL: dC = dA*dB; break;
6652             case JIM_EXPROP_LT: dC = dA<dB; break;
6653             case JIM_EXPROP_GT: dC = dA>dB; break;
6654             case JIM_EXPROP_LTE: dC = dA<=dB; break;
6655             case JIM_EXPROP_GTE: dC = dA>=dB; break;
6656             case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6657             case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6658             case JIM_EXPROP_LOGICAND_LEFT:
6659                 if (dA == 0) {
6660                     i += (int)dB;
6661                     dC = 0;
6662                 } else {
6663                     continue;
6664                 }
6665                 break;
6666             case JIM_EXPROP_LOGICOR_LEFT:
6667                 if (dA != 0) {
6668                     i += (int)dB;
6669                     dC = 1;
6670                 } else {
6671                     continue;
6672                 }
6673                 break;
6674             case JIM_EXPROP_DIV:
6675                 if (dB == 0) goto divbyzero;
6676                 dC = dA/dB;
6677                 break;
6678             default:
6679                 dC = 0; /* avoid gcc warning */
6680                 break;
6681             }
6682             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6683             Jim_IncrRefCount(stack[stacklen]);
6684             stacklen++;
6685         } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6686             B = stack[--stacklen];
6687             A = stack[--stacklen];
6688             sA = Jim_GetString(A, &Alen);
6689             sB = Jim_GetString(B, &Blen);
6690             switch(expr->opcode[i]) {
6691             case JIM_EXPROP_STREQ:
6692                 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6693                     wC = 1;
6694                 else
6695                     wC = 0;
6696                 break;
6697             case JIM_EXPROP_STRNE:
6698                 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6699                     wC = 1;
6700                 else
6701                     wC = 0;
6702                 break;
6703             default:
6704                 wC = 0; /* avoid gcc warning */
6705                 break;
6706             }
6707             Jim_DecrRefCount(interp, A);
6708             Jim_DecrRefCount(interp, B);
6709             stack[stacklen] = Jim_NewIntObj(interp, wC);
6710             Jim_IncrRefCount(stack[stacklen]);
6711             stacklen++;
6712         } else if (opcode == JIM_EXPROP_NOT ||
6713                    opcode == JIM_EXPROP_BITNOT ||
6714                    opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6715                    opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6716             /* Note that there isn't to increment the
6717              * refcount of objects. the references are moved
6718              * from stack to A and B. */
6719             A = stack[--stacklen];
6720
6721             /* --- Integer --- */
6722             if ((A->typePtr == &doubleObjType && !A->bytes) ||
6723                 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6724                 goto trydouble_unary;
6725             }
6726             Jim_DecrRefCount(interp, A);
6727             switch(expr->opcode[i]) {
6728             case JIM_EXPROP_NOT: wC = !wA; break;
6729             case JIM_EXPROP_BITNOT: wC = ~wA; break;
6730             case JIM_EXPROP_LOGICAND_RIGHT:
6731             case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6732             default:
6733                 wC = 0; /* avoid gcc warning */
6734                 break;
6735             }
6736             stack[stacklen] = Jim_NewIntObj(interp, wC);
6737             Jim_IncrRefCount(stack[stacklen]);
6738             stacklen++;
6739             continue;
6740 trydouble_unary:
6741             /* --- Double --- */
6742             if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
6743                 Jim_DecrRefCount(interp, A);
6744                 error = 1;
6745                 goto err;
6746             }
6747             Jim_DecrRefCount(interp, A);
6748             switch(expr->opcode[i]) {
6749             case JIM_EXPROP_NOT: dC = !dA; break;
6750             case JIM_EXPROP_LOGICAND_RIGHT:
6751             case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
6752             case JIM_EXPROP_BITNOT:
6753                 Jim_SetResultString(interp,
6754                     "Got floating-point value where integer was expected", -1);
6755                 error = 1;
6756                 goto err;
6757                 break;
6758             default:
6759                 dC = 0; /* avoid gcc warning */
6760                 break;
6761             }
6762             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6763             Jim_IncrRefCount(stack[stacklen]);
6764             stacklen++;
6765         } else {
6766             Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
6767         }
6768     }
6769 err:
6770     /* There is no need to decerement the inUse field because
6771      * this reference is transfered back into the exprObjPtr. */
6772     Jim_FreeIntRep(interp, exprObjPtr);
6773     exprObjPtr->typePtr = &exprObjType;
6774     Jim_SetIntRepPtr(exprObjPtr, expr);
6775     Jim_DecrRefCount(interp, exprObjPtr);
6776     if (!error) {
6777         *exprResultPtrPtr = stack[0];
6778         Jim_IncrRefCount(stack[0]);
6779         errRetCode = JIM_OK;
6780     }
6781     for (i = 0; i < stacklen; i++) {
6782         Jim_DecrRefCount(interp, stack[i]);
6783     }
6784     if (stack != staticStack)
6785         Jim_Free(stack);
6786     return errRetCode;
6787 divbyzero:
6788     error = 1;
6789     Jim_SetResultString(interp, "Division by zero", -1);
6790     goto err;
6791 }
6792
6793 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
6794 {
6795     int retcode;
6796     jim_wide wideValue;
6797     double doubleValue;
6798     Jim_Obj *exprResultPtr;
6799
6800     retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
6801     if (retcode != JIM_OK)
6802         return retcode;
6803     if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
6804         if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
6805         {
6806             Jim_DecrRefCount(interp, exprResultPtr);
6807             return JIM_ERR;
6808         } else {
6809             Jim_DecrRefCount(interp, exprResultPtr);
6810             *boolPtr = doubleValue != 0;
6811             return JIM_OK;
6812         }
6813     }
6814     Jim_DecrRefCount(interp, exprResultPtr);
6815     *boolPtr = wideValue != 0;
6816     return JIM_OK;
6817 }
6818
6819 /* -----------------------------------------------------------------------------
6820  * ScanFormat String Object
6821  * ---------------------------------------------------------------------------*/
6822
6823 /* This Jim_Obj will held a parsed representation of a format string passed to
6824  * the Jim_ScanString command. For error diagnostics, the scanformat string has
6825  * to be parsed in its entirely first and then, if correct, can be used for
6826  * scanning. To avoid endless re-parsing, the parsed representation will be
6827  * stored in an internal representation and re-used for performance reason. */
6828  
6829 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
6830  * scanformat string. This part will later be used to extract information
6831  * out from the string to be parsed by Jim_ScanString */
6832  
6833 typedef struct ScanFmtPartDescr {
6834     char type;         /* Type of conversion (e.g. c, d, f) */
6835     char modifier;     /* Modify type (e.g. l - long, h - short */
6836     size_t  width;     /* Maximal width of input to be converted */
6837     int  pos;          /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */ 
6838     char *arg;         /* Specification of a CHARSET conversion */
6839     char *prefix;      /* Prefix to be scanned literally before conversion */
6840 } ScanFmtPartDescr;
6841
6842 /* The ScanFmtStringObj will held the internal representation of a scanformat
6843  * string parsed and separated in part descriptions. Furthermore it contains
6844  * the original string representation of the scanformat string to allow for
6845  * fast update of the Jim_Obj's string representation part.
6846  *
6847  * As add-on the internal object representation add some scratch pad area
6848  * for usage by Jim_ScanString to avoid endless allocating and freeing of
6849  * memory for purpose of string scanning.
6850  *
6851  * The error member points to a static allocated string in case of a mal-
6852  * formed scanformat string or it contains '0' (NULL) in case of a valid
6853  * parse representation.
6854  *
6855  * The whole memory of the internal representation is allocated as a single
6856  * area of memory that will be internally separated. So freeing and duplicating
6857  * of such an object is cheap */
6858
6859 typedef struct ScanFmtStringObj {
6860     jim_wide        size;         /* Size of internal repr in bytes */
6861     char            *stringRep;   /* Original string representation */
6862     size_t          count;        /* Number of ScanFmtPartDescr contained */
6863     size_t          convCount;    /* Number of conversions that will assign */
6864     size_t          maxPos;       /* Max position index if XPG3 is used */
6865     const char      *error;       /* Ptr to error text (NULL if no error */
6866     char            *scratch;     /* Some scratch pad used by Jim_ScanString */
6867     ScanFmtPartDescr descr[1];    /* The vector of partial descriptions */
6868 } ScanFmtStringObj;
6869
6870
6871 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6872 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6873 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
6874
6875 static Jim_ObjType scanFmtStringObjType = {
6876     "scanformatstring",
6877     FreeScanFmtInternalRep,
6878     DupScanFmtInternalRep,
6879     UpdateStringOfScanFmt,
6880     JIM_TYPE_NONE,
6881 };
6882
6883 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6884 {
6885     JIM_NOTUSED(interp);
6886     Jim_Free((char*)objPtr->internalRep.ptr);
6887     objPtr->internalRep.ptr = 0;
6888 }
6889
6890 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6891 {
6892     size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
6893     ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
6894
6895     JIM_NOTUSED(interp);
6896     memcpy(newVec, srcPtr->internalRep.ptr, size);
6897     dupPtr->internalRep.ptr = newVec;
6898     dupPtr->typePtr = &scanFmtStringObjType;
6899 }
6900
6901 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
6902 {
6903     char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
6904
6905     objPtr->bytes = Jim_StrDup(bytes);
6906     objPtr->length = strlen(bytes);
6907 }
6908
6909 /* SetScanFmtFromAny will parse a given string and create the internal
6910  * representation of the format specification. In case of an error
6911  * the error data member of the internal representation will be set
6912  * to an descriptive error text and the function will be left with
6913  * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
6914  * specification */
6915
6916 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6917 {
6918     ScanFmtStringObj *fmtObj;
6919     char *buffer;
6920     int maxCount, i, approxSize, lastPos = -1;
6921     const char *fmt = objPtr->bytes;
6922     int maxFmtLen = objPtr->length;
6923     const char *fmtEnd = fmt + maxFmtLen;
6924     int curr;
6925
6926     Jim_FreeIntRep(interp, objPtr);
6927     /* Count how many conversions could take place maximally */
6928     for (i=0, maxCount=0; i < maxFmtLen; ++i)
6929         if (fmt[i] == '%')
6930             ++maxCount;
6931     /* Calculate an approximation of the memory necessary */
6932     approxSize = sizeof(ScanFmtStringObj)           /* Size of the container */
6933         + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
6934         + maxFmtLen * sizeof(char) + 3 + 1          /* Scratch + "%n" + '\0' */
6935         + maxFmtLen * sizeof(char) + 1              /* Original stringrep */
6936         + maxFmtLen * sizeof(char)                  /* Arg for CHARSETs */
6937         + (maxCount +1) * sizeof(char)              /* '\0' for every partial */
6938         + 1;                                        /* safety byte */
6939     fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
6940     memset(fmtObj, 0, approxSize);
6941     fmtObj->size = approxSize;
6942     fmtObj->maxPos = 0;
6943     fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
6944     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
6945     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
6946     buffer = fmtObj->stringRep + maxFmtLen + 1;
6947     objPtr->internalRep.ptr = fmtObj;
6948     objPtr->typePtr = &scanFmtStringObjType;
6949     for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
6950         int width=0, skip;
6951         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
6952         fmtObj->count++;
6953         descr->width = 0;                   /* Assume width unspecified */ 
6954         /* Overread and store any "literal" prefix */
6955         if (*fmt != '%' || fmt[1] == '%') {
6956             descr->type = 0;
6957             descr->prefix = &buffer[i];
6958             for (; fmt < fmtEnd; ++fmt) {
6959                 if (*fmt == '%') {
6960                     if (fmt[1] != '%') break;
6961                     ++fmt;
6962                 }
6963                 buffer[i++] = *fmt;
6964             }
6965             buffer[i++] = 0;
6966         } 
6967         /* Skip the conversion introducing '%' sign */
6968         ++fmt;      
6969         /* End reached due to non-conversion literal only? */
6970         if (fmt >= fmtEnd)
6971             goto done;
6972         descr->pos = 0;                     /* Assume "natural" positioning */
6973         if (*fmt == '*') {
6974             descr->pos = -1;       /* Okay, conversion will not be assigned */
6975             ++fmt;
6976         } else
6977             fmtObj->convCount++;    /* Otherwise count as assign-conversion */
6978         /* Check if next token is a number (could be width or pos */
6979         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
6980             fmt += skip;
6981             /* Was the number a XPG3 position specifier? */
6982             if (descr->pos != -1 && *fmt == '$') {
6983                 int prev;
6984                 ++fmt;
6985                 descr->pos = width;
6986                 width = 0;
6987                 /* Look if "natural" postioning and XPG3 one was mixed */
6988                 if ((lastPos == 0 && descr->pos > 0)
6989                         || (lastPos > 0 && descr->pos == 0)) {
6990                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
6991                     return JIM_ERR;
6992                 }
6993                 /* Look if this position was already used */
6994                 for (prev=0; prev < curr; ++prev) {
6995                     if (fmtObj->descr[prev].pos == -1) continue;
6996                     if (fmtObj->descr[prev].pos == descr->pos) {
6997                         fmtObj->error = "same \"%n$\" conversion specifier "
6998                             "used more than once";
6999                         return JIM_ERR;
7000                     }
7001                 }
7002                 /* Try to find a width after the XPG3 specifier */
7003                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7004                     descr->width = width;
7005                     fmt += skip;
7006                 }
7007                 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7008                     fmtObj->maxPos = descr->pos;
7009             } else {
7010                 /* Number was not a XPG3, so it has to be a width */
7011                 descr->width = width;
7012             }
7013         }
7014         /* If positioning mode was undetermined yet, fix this */
7015         if (lastPos == -1)
7016             lastPos = descr->pos;
7017         /* Handle CHARSET conversion type ... */
7018         if (*fmt == '[') {
7019             int swapped = 1, beg = i, end, j;
7020             descr->type = '[';
7021             descr->arg = &buffer[i];
7022             ++fmt;
7023             if (*fmt == '^') buffer[i++] = *fmt++;
7024             if (*fmt == ']') buffer[i++] = *fmt++;
7025             while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7026             if (*fmt != ']') {
7027                 fmtObj->error = "unmatched [ in format string";
7028                 return JIM_ERR;
7029             } 
7030             end = i;
7031             buffer[i++] = 0;
7032             /* In case a range fence was given "backwards", swap it */
7033             while (swapped) {
7034                 swapped = 0;
7035                 for (j=beg+1; j < end-1; ++j) {
7036                     if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7037                         char tmp = buffer[j-1];
7038                         buffer[j-1] = buffer[j+1];
7039                         buffer[j+1] = tmp;
7040                         swapped = 1;
7041                     }
7042                 }
7043             }
7044         } else {
7045             /* Remember any valid modifier if given */
7046             if (strchr("hlL", *fmt) != 0)
7047                 descr->modifier = tolower((int)*fmt++);
7048             
7049             descr->type = *fmt;
7050             if (strchr("efgcsndoxui", *fmt) == 0) {
7051                 fmtObj->error = "bad scan conversion character";
7052                 return JIM_ERR;
7053             } else if (*fmt == 'c' && descr->width != 0) {
7054                 fmtObj->error = "field width may not be specified in %c "
7055                     "conversion";
7056                 return JIM_ERR;
7057             } else if (*fmt == 'u' && descr->modifier == 'l') {
7058                 fmtObj->error = "unsigned wide not supported";
7059                 return JIM_ERR;
7060             }
7061         }
7062         curr++;
7063     }
7064 done:
7065     if (fmtObj->convCount == 0) {
7066         fmtObj->error = "no any conversion specifier given";
7067         return JIM_ERR;
7068     }
7069     return JIM_OK;
7070 }
7071
7072 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7073
7074 #define FormatGetCnvCount(_fo_) \
7075     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7076 #define FormatGetMaxPos(_fo_) \
7077     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7078 #define FormatGetError(_fo_) \
7079     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7080
7081 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7082  * charsets ([a-z123]) within scanning. Later on perhaps a base for a 
7083  * bitvector implementation in Jim? */ 
7084
7085 static int JimTestBit(const char *bitvec, char ch)
7086 {
7087     div_t pos = div(ch-1, 8);
7088     return bitvec[pos.quot] & (1 << pos.rem);
7089 }
7090
7091 static void JimSetBit(char *bitvec, char ch)
7092 {
7093     div_t pos = div(ch-1, 8);
7094     bitvec[pos.quot] |= (1 << pos.rem);
7095 }
7096
7097 #if 0 /* currently not used */
7098 static void JimClearBit(char *bitvec, char ch)
7099 {
7100     div_t pos = div(ch-1, 8);
7101     bitvec[pos.quot] &= ~(1 << pos.rem);
7102 }
7103 #endif
7104
7105 /* JimScanAString is used to scan an unspecified string that ends with
7106  * next WS, or a string that is specified via a charset. The charset
7107  * is currently implemented in a way to only allow for usage with
7108  * ASCII. Whenever we will switch to UNICODE, another idea has to
7109  * be born :-/
7110  *
7111  * FIXME: Works only with ASCII */
7112
7113 static Jim_Obj *
7114 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7115 {
7116     size_t i;
7117     Jim_Obj *result;
7118     char charset[256/8+1];  /* A Charset may contain max 256 chars */
7119     char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7120
7121     /* First init charset to nothing or all, depending if a specified
7122      * or an unspecified string has to be parsed */
7123     memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7124     if (sdescr) {
7125         /* There was a set description given, that means we are parsing
7126          * a specified string. So we have to build a corresponding 
7127          * charset reflecting the description */
7128         int notFlag = 0;
7129         /* Should the set be negated at the end? */
7130         if (*sdescr == '^') {
7131             notFlag = 1;
7132             ++sdescr;
7133         }
7134         /* Here '-' is meant literally and not to define a range */
7135         if (*sdescr == '-') {
7136             JimSetBit(charset, '-');
7137             ++sdescr;
7138         }
7139         while (*sdescr) {
7140             if (sdescr[1] == '-' && sdescr[2] != 0) {
7141                 /* Handle range definitions */
7142                 int i;
7143                 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7144                     JimSetBit(charset, i);
7145                 sdescr += 3;
7146             } else {
7147                 /* Handle verbatim character definitions */
7148                 JimSetBit(charset, *sdescr++);
7149             }
7150         }
7151         /* Negate the charset if there was a NOT given */
7152         for (i=0; notFlag && i < sizeof(charset); ++i)
7153             charset[i] = ~charset[i];
7154     } 
7155     /* And after all the mess above, the real work begin ... */
7156     while (str && *str) {
7157         if (!sdescr && isspace((int)*str))
7158             break; /* EOS via WS if unspecified */
7159         if (JimTestBit(charset, *str)) *buffer++ = *str++;
7160         else break;             /* EOS via mismatch if specified scanning */
7161     }
7162     *buffer = 0;                /* Close the string properly ... */
7163     result = Jim_NewStringObj(interp, anchor, -1);
7164     Jim_Free(anchor);           /* ... and free it afer usage */
7165     return result;
7166 }
7167
7168 /* ScanOneEntry will scan one entry out of the string passed as argument.
7169  * It use the sscanf() function for this task. After extracting and
7170  * converting of the value, the count of scanned characters will be
7171  * returned of -1 in case of no conversion tool place and string was
7172  * already scanned thru */
7173
7174 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7175         ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7176 {
7177 #   define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7178         ? sizeof(jim_wide)                             \
7179         : sizeof(double))
7180     char buffer[MAX_SIZE];
7181     char *value = buffer;
7182     const char *tok;
7183     const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7184     size_t sLen = strlen(&str[pos]), scanned = 0;
7185     size_t anchor = pos;
7186     int i;
7187
7188     /* First pessimiticly assume, we will not scan anything :-) */
7189     *valObjPtr = 0;
7190     if (descr->prefix) {
7191         /* There was a prefix given before the conversion, skip it and adjust
7192          * the string-to-be-parsed accordingly */
7193         for (i=0; str[pos] && descr->prefix[i]; ++i) {
7194             /* If prefix require, skip WS */
7195             if (isspace((int)descr->prefix[i]))
7196                 while (str[pos] && isspace((int)str[pos])) ++pos;
7197             else if (descr->prefix[i] != str[pos]) 
7198                 break;  /* Prefix do not match here, leave the loop */
7199             else
7200                 ++pos;  /* Prefix matched so far, next round */
7201         }
7202         if (str[pos] == 0)
7203             return -1;  /* All of str consumed: EOF condition */
7204         else if (descr->prefix[i] != 0)
7205             return 0;   /* Not whole prefix consumed, no conversion possible */
7206     }
7207     /* For all but following conversion, skip leading WS */
7208     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7209         while (isspace((int)str[pos])) ++pos;
7210     /* Determine how much skipped/scanned so far */
7211     scanned = pos - anchor;
7212     if (descr->type == 'n') {
7213         /* Return pseudo conversion means: how much scanned so far? */
7214         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7215     } else if (str[pos] == 0) {
7216         /* Cannot scan anything, as str is totally consumed */
7217         return -1;
7218     } else {
7219         /* Processing of conversions follows ... */
7220         if (descr->width > 0) {
7221             /* Do not try to scan as fas as possible but only the given width.
7222              * To ensure this, we copy the part that should be scanned. */
7223             size_t tLen = descr->width > sLen ? sLen : descr->width;
7224             tok = Jim_StrDupLen(&str[pos], tLen);
7225         } else {
7226             /* As no width was given, simply refer to the original string */
7227             tok = &str[pos];
7228         }
7229         switch (descr->type) {
7230             case 'c':
7231                 *valObjPtr = Jim_NewIntObj(interp, *tok);
7232                 scanned += 1;
7233                 break;
7234             case 'd': case 'o': case 'x': case 'u': case 'i': {
7235                 char *endp;  /* Position where the number finished */
7236                 int base = descr->type == 'o' ? 8
7237                     : descr->type == 'x' ? 16
7238                     : descr->type == 'i' ? 0
7239                     : 10;
7240                     
7241                 do {
7242                     /* Try to scan a number with the given base */
7243                     if (descr->modifier == 'l')
7244 #ifdef HAVE_LONG_LONG
7245                       *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7246 #else
7247                       *(jim_wide*)value = strtol(tok, &endp, base);
7248 #endif
7249                     else
7250                       if (descr->type == 'u')
7251                         *(long*)value = strtoul(tok, &endp, base);
7252                       else
7253                         *(long*)value = strtol(tok, &endp, base);
7254                     /* If scanning failed, and base was undetermined, simply
7255                      * put it to 10 and try once more. This should catch the
7256                      * case where %i begin to parse a number prefix (e.g. 
7257                      * '0x' but no further digits follows. This will be
7258                      * handled as a ZERO followed by a char 'x' by Tcl */
7259                     if (endp == tok && base == 0) base = 10;
7260                     else break;
7261                 } while (1);
7262                 if (endp != tok) {
7263                     /* There was some number sucessfully scanned! */
7264                     if (descr->modifier == 'l')
7265                         *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7266                     else
7267                         *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7268                     /* Adjust the number-of-chars scanned so far */
7269                     scanned += endp - tok;
7270                 } else {
7271                     /* Nothing was scanned. We have to determine if this
7272                      * happened due to e.g. prefix mismatch or input str
7273                      * exhausted */
7274                     scanned = *tok ? 0 : -1;
7275                 }
7276                 break;
7277             }
7278             case 's': case '[': {
7279                 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7280                 scanned += Jim_Length(*valObjPtr);
7281                 break;
7282             }
7283             case 'e': case 'f': case 'g': {
7284                 char *endp;
7285
7286                 *(double*)value = strtod(tok, &endp);
7287                 if (endp != tok) {
7288                     /* There was some number sucessfully scanned! */
7289                     *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7290                     /* Adjust the number-of-chars scanned so far */
7291                     scanned += endp - tok;
7292                 } else {
7293                     /* Nothing was scanned. We have to determine if this
7294                      * happened due to e.g. prefix mismatch or input str
7295                      * exhausted */
7296                     scanned = *tok ? 0 : -1;
7297                 }
7298                 break;
7299             }
7300         }
7301         /* If a substring was allocated (due to pre-defined width) do not
7302          * forget to free it */
7303         if (tok != &str[pos])
7304             Jim_Free((char*)tok);
7305     }
7306     return scanned;
7307 }
7308
7309 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7310  * string and returns all converted (and not ignored) values in a list back
7311  * to the caller. If an error occured, a NULL pointer will be returned */
7312
7313 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7314         Jim_Obj *fmtObjPtr, int flags)
7315 {
7316     size_t i, pos;
7317     int scanned = 1;
7318     const char *str = Jim_GetString(strObjPtr, 0);
7319     Jim_Obj *resultList = 0;
7320     Jim_Obj **resultVec;
7321     int resultc;
7322     Jim_Obj *emptyStr = 0;
7323     ScanFmtStringObj *fmtObj;
7324
7325     /* If format specification is not an object, convert it! */
7326     if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7327         SetScanFmtFromAny(interp, fmtObjPtr);
7328     fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7329     /* Check if format specification was valid */
7330     if (fmtObj->error != 0) {
7331         if (flags & JIM_ERRMSG)
7332             Jim_SetResultString(interp, fmtObj->error, -1);
7333         return 0;
7334     }
7335     /* Allocate a new "shared" empty string for all unassigned conversions */
7336     emptyStr = Jim_NewEmptyStringObj(interp);
7337     Jim_IncrRefCount(emptyStr);
7338     /* Create a list and fill it with empty strings up to max specified XPG3 */
7339     resultList = Jim_NewListObj(interp, 0, 0);
7340     if (fmtObj->maxPos > 0) {
7341         for (i=0; i < fmtObj->maxPos; ++i)
7342             Jim_ListAppendElement(interp, resultList, emptyStr);
7343         JimListGetElements(interp, resultList, &resultc, &resultVec);
7344     }
7345     /* Now handle every partial format description */
7346     for (i=0, pos=0; i < fmtObj->count; ++i) {
7347         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7348         Jim_Obj *value = 0;
7349         /* Only last type may be "literal" w/o conversion - skip it! */
7350         if (descr->type == 0) continue;
7351         /* As long as any conversion could be done, we will proceed */
7352         if (scanned > 0)
7353             scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7354         /* In case our first try results in EOF, we will leave */
7355         if (scanned == -1 && i == 0)
7356             goto eof;
7357         /* Advance next pos-to-be-scanned for the amount scanned already */
7358         pos += scanned;
7359         /* value == 0 means no conversion took place so take empty string */
7360         if (value == 0)
7361             value = Jim_NewEmptyStringObj(interp);
7362         /* If value is a non-assignable one, skip it */
7363         if (descr->pos == -1) {
7364             Jim_FreeNewObj(interp, value);
7365         } else if (descr->pos == 0)
7366             /* Otherwise append it to the result list if no XPG3 was given */
7367             Jim_ListAppendElement(interp, resultList, value);
7368         else if (resultVec[descr->pos-1] == emptyStr) {
7369             /* But due to given XPG3, put the value into the corr. slot */
7370             Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7371             Jim_IncrRefCount(value);
7372             resultVec[descr->pos-1] = value;
7373         } else {
7374             /* Otherwise, the slot was already used - free obj and ERROR */
7375             Jim_FreeNewObj(interp, value);
7376             goto err;
7377         }
7378     }
7379     Jim_DecrRefCount(interp, emptyStr);
7380     return resultList;
7381 eof:
7382     Jim_DecrRefCount(interp, emptyStr);
7383     Jim_FreeNewObj(interp, resultList);
7384     return (Jim_Obj*)EOF;
7385 err:
7386     Jim_DecrRefCount(interp, emptyStr);
7387     Jim_FreeNewObj(interp, resultList);
7388     return 0;
7389 }
7390
7391 /* -----------------------------------------------------------------------------
7392  * Pseudo Random Number Generation
7393  * ---------------------------------------------------------------------------*/
7394 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7395         int seedLen);
7396
7397 /* Initialize the sbox with the numbers from 0 to 255 */
7398 static void JimPrngInit(Jim_Interp *interp)
7399 {
7400     int i;
7401     unsigned int seed[256];
7402
7403     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7404     for (i = 0; i < 256; i++)
7405         seed[i] = (rand() ^ time(NULL) ^ clock());
7406     JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7407 }
7408
7409 /* Generates N bytes of random data */
7410 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7411 {
7412     Jim_PrngState *prng;
7413     unsigned char *destByte = (unsigned char*) dest;
7414     unsigned int si, sj, x;
7415
7416     /* initialization, only needed the first time */
7417     if (interp->prngState == NULL)
7418         JimPrngInit(interp);
7419     prng = interp->prngState;
7420     /* generates 'len' bytes of pseudo-random numbers */
7421     for (x = 0; x < len; x++) {
7422         prng->i = (prng->i+1) & 0xff;
7423         si = prng->sbox[prng->i];
7424         prng->j = (prng->j + si) & 0xff;
7425         sj = prng->sbox[prng->j];
7426         prng->sbox[prng->i] = sj;
7427         prng->sbox[prng->j] = si;
7428         *destByte++ = prng->sbox[(si+sj)&0xff];
7429     }
7430 }
7431
7432 /* Re-seed the generator with user-provided bytes */
7433 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7434         int seedLen)
7435 {
7436     int i;
7437     unsigned char buf[256];
7438     Jim_PrngState *prng;
7439
7440     /* initialization, only needed the first time */
7441     if (interp->prngState == NULL)
7442         JimPrngInit(interp);
7443     prng = interp->prngState;
7444
7445     /* Set the sbox[i] with i */
7446     for (i = 0; i < 256; i++)
7447         prng->sbox[i] = i;
7448     /* Now use the seed to perform a random permutation of the sbox */
7449     for (i = 0; i < seedLen; i++) {
7450         unsigned char t;
7451
7452         t = prng->sbox[i&0xFF];
7453         prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7454         prng->sbox[seed[i]] = t;
7455     }
7456     prng->i = prng->j = 0;
7457     /* discard the first 256 bytes of stream. */
7458     JimRandomBytes(interp, buf, 256);
7459 }
7460
7461 /* -----------------------------------------------------------------------------
7462  * Dynamic libraries support (WIN32 not supported)
7463  * ---------------------------------------------------------------------------*/
7464
7465 #ifdef JIM_DYNLIB
7466 #ifdef WIN32
7467 #define RTLD_LAZY 0
7468 void * dlopen(const char *path, int mode) 
7469 {
7470     JIM_NOTUSED(mode);
7471
7472     return (void *)LoadLibraryA(path);
7473 }
7474 int dlclose(void *handle)
7475 {
7476     FreeLibrary((HANDLE)handle);
7477     return 0;
7478 }
7479 void *dlsym(void *handle, const char *symbol)
7480 {
7481     return GetProcAddress((HMODULE)handle, symbol);
7482 }
7483 static char win32_dlerror_string[121];
7484 const char *dlerror()
7485 {
7486     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7487                    LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7488     return win32_dlerror_string;
7489 }
7490 #endif /* WIN32 */
7491
7492 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7493 {
7494     Jim_Obj *libPathObjPtr;
7495     int prefixc, i;
7496     void *handle;
7497     int (*onload)(Jim_Interp *interp);
7498
7499     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7500     if (libPathObjPtr == NULL) {
7501         prefixc = 0;
7502         libPathObjPtr = NULL;
7503     } else {
7504         Jim_IncrRefCount(libPathObjPtr);
7505         Jim_ListLength(interp, libPathObjPtr, &prefixc);
7506     }
7507
7508     for (i = -1; i < prefixc; i++) {
7509         if (i < 0) {
7510             handle = dlopen(pathName, RTLD_LAZY);
7511         } else {
7512             FILE *fp;
7513             char buf[JIM_PATH_LEN];
7514             const char *prefix;
7515             int prefixlen;
7516             Jim_Obj *prefixObjPtr;
7517             
7518             buf[0] = '\0';
7519             if (Jim_ListIndex(interp, libPathObjPtr, i,
7520                     &prefixObjPtr, JIM_NONE) != JIM_OK)
7521                 continue;
7522             prefix = Jim_GetString(prefixObjPtr, NULL);
7523             prefixlen = strlen(prefix);
7524             if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7525                 continue;
7526             if (prefixlen && prefix[prefixlen-1] == '/')
7527                 sprintf(buf, "%s%s", prefix, pathName);
7528             else
7529                 sprintf(buf, "%s/%s", prefix, pathName);
7530             fp = fopen(buf, "r");
7531             if (fp == NULL)
7532                 continue;
7533             fclose(fp);
7534             handle = dlopen(buf, RTLD_LAZY);
7535         }
7536         if (handle == NULL) {
7537             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7538             Jim_AppendStrings(interp, Jim_GetResult(interp),
7539                 "error loading extension \"", pathName,
7540                 "\": ", dlerror(), NULL);
7541             if (i < 0)
7542                 continue;
7543             goto err;
7544         }
7545         if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7546             Jim_SetResultString(interp,
7547                     "No Jim_OnLoad symbol found on extension", -1);
7548             goto err;
7549         }
7550         if (onload(interp) == JIM_ERR) {
7551             dlclose(handle);
7552             goto err;
7553         }
7554         Jim_SetEmptyResult(interp);
7555         if (libPathObjPtr != NULL)
7556             Jim_DecrRefCount(interp, libPathObjPtr);
7557         return JIM_OK;
7558     }
7559 err:
7560     if (libPathObjPtr != NULL)
7561         Jim_DecrRefCount(interp, libPathObjPtr);
7562     return JIM_ERR;
7563 }
7564 #else /* JIM_DYNLIB */
7565 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7566 {
7567     JIM_NOTUSED(interp);
7568     JIM_NOTUSED(pathName);
7569
7570     Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7571     return JIM_ERR;
7572 }
7573 #endif/* JIM_DYNLIB */
7574
7575 /* -----------------------------------------------------------------------------
7576  * Packages handling
7577  * ---------------------------------------------------------------------------*/
7578
7579 #define JIM_PKG_ANY_VERSION -1
7580
7581 /* Convert a string of the type "1.2" into an integer.
7582  * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted 
7583  * to the integer with value 102 */
7584 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7585         int *intPtr, int flags)
7586 {
7587     char *copy;
7588     jim_wide major, minor;
7589     char *majorStr, *minorStr, *p;
7590
7591     if (v[0] == '\0') {
7592         *intPtr = JIM_PKG_ANY_VERSION;
7593         return JIM_OK;
7594     }
7595
7596     copy = Jim_StrDup(v);
7597     p = strchr(copy, '.');
7598     if (p == NULL) goto badfmt;
7599     *p = '\0';
7600     majorStr = copy;
7601     minorStr = p+1;
7602
7603     if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7604         Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7605         goto badfmt;
7606     *intPtr = (int)(major*100+minor);
7607     Jim_Free(copy);
7608     return JIM_OK;
7609
7610 badfmt:
7611     Jim_Free(copy);
7612     if (flags & JIM_ERRMSG) {
7613         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7614         Jim_AppendStrings(interp, Jim_GetResult(interp),
7615                 "invalid package version '", v, "'", NULL);
7616     }
7617     return JIM_ERR;
7618 }
7619
7620 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7621 static int JimPackageMatchVersion(int needed, int actual, int flags)
7622 {
7623     if (needed == JIM_PKG_ANY_VERSION) return 1;
7624     if (flags & JIM_MATCHVER_EXACT) {
7625         return needed == actual;
7626     } else {
7627         return needed/100 == actual/100 && (needed <= actual);
7628     }
7629 }
7630
7631 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7632         int flags)
7633 {
7634     int intVersion;
7635     /* Check if the version format is ok */
7636     if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7637         return JIM_ERR;
7638     /* If the package was already provided returns an error. */
7639     if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7640         if (flags & JIM_ERRMSG) {
7641             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7642             Jim_AppendStrings(interp, Jim_GetResult(interp),
7643                     "package '", name, "' was already provided", NULL);
7644         }
7645         return JIM_ERR;
7646     }
7647     Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7648     return JIM_OK;
7649 }
7650
7651 #ifndef JIM_ANSIC
7652
7653 #ifndef WIN32
7654 # include <sys/types.h>
7655 # include <dirent.h>
7656 #else
7657 # include <io.h>
7658 /* Posix dirent.h compatiblity layer for WIN32.
7659  * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7660  * Copyright Salvatore Sanfilippo ,2005.
7661  *
7662  * Permission to use, copy, modify, and distribute this software and its
7663  * documentation for any purpose is hereby granted without fee, provided
7664  * that this copyright and permissions notice appear in all copies and
7665  * derivatives.
7666  *
7667  * This software is supplied "as is" without express or implied warranty.
7668  * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7669  */
7670
7671 struct dirent {
7672     char *d_name;
7673 };
7674
7675 typedef struct DIR {
7676     long                handle; /* -1 for failed rewind */
7677     struct _finddata_t  info;
7678     struct dirent       result; /* d_name null iff first time */
7679     char                *name;  /* null-terminated char string */
7680 } DIR;
7681
7682 DIR *opendir(const char *name)
7683 {
7684     DIR *dir = 0;
7685
7686     if(name && name[0]) {
7687         size_t base_length = strlen(name);
7688         const char *all = /* search pattern must end with suitable wildcard */
7689             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7690
7691         if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7692            (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7693         {
7694             strcat(strcpy(dir->name, name), all);
7695
7696             if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7697                 dir->result.d_name = 0;
7698             else { /* rollback */
7699                 Jim_Free(dir->name);
7700                 Jim_Free(dir);
7701                 dir = 0;
7702             }
7703         } else { /* rollback */
7704             Jim_Free(dir);
7705             dir   = 0;
7706             errno = ENOMEM;
7707         }
7708     } else {
7709         errno = EINVAL;
7710     }
7711     return dir;
7712 }
7713
7714 int closedir(DIR *dir)
7715 {
7716     int result = -1;
7717
7718     if(dir) {
7719         if(dir->handle != -1)
7720             result = _findclose(dir->handle);
7721         Jim_Free(dir->name);
7722         Jim_Free(dir);
7723     }
7724     if(result == -1) /* map all errors to EBADF */
7725         errno = EBADF;
7726     return result;
7727 }
7728
7729 struct dirent *readdir(DIR *dir)
7730 {
7731     struct dirent *result = 0;
7732
7733     if(dir && dir->handle != -1) {
7734         if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7735             result         = &dir->result;
7736             result->d_name = dir->info.name;
7737         }
7738     } else {
7739         errno = EBADF;
7740     }
7741     return result;
7742 }
7743
7744 #endif /* WIN32 */
7745
7746 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7747         int prefixc, const char *pkgName, int pkgVer, int flags)
7748 {
7749     int bestVer = -1, i;
7750     int pkgNameLen = strlen(pkgName);
7751     char *bestPackage = NULL;
7752     struct dirent *de;
7753
7754     for (i = 0; i < prefixc; i++) {
7755         DIR *dir;
7756         char buf[JIM_PATH_LEN];
7757         int prefixLen;
7758
7759         if (prefixes[i] == NULL) continue;
7760         strncpy(buf, prefixes[i], JIM_PATH_LEN);
7761         buf[JIM_PATH_LEN-1] = '\0';
7762         prefixLen = strlen(buf);
7763         if (prefixLen && buf[prefixLen-1] == '/')
7764             buf[prefixLen-1] = '\0';
7765
7766         if ((dir = opendir(buf)) == NULL) continue;
7767         while ((de = readdir(dir)) != NULL) {
7768             char *fileName = de->d_name;
7769             int fileNameLen = strlen(fileName);
7770
7771             if (strncmp(fileName, "jim-", 4) == 0 &&
7772                 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
7773                 *(fileName+4+pkgNameLen) == '-' &&
7774                 fileNameLen > 4 && /* note that this is not really useful */
7775                 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
7776                  strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
7777                  strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
7778             {
7779                 char ver[6]; /* xx.yy<nulterm> */
7780                 char *p = strrchr(fileName, '.');
7781                 int verLen, fileVer;
7782
7783                 verLen = p - (fileName+4+pkgNameLen+1);
7784                 if (verLen < 3 || verLen > 5) continue;
7785                 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
7786                 ver[verLen] = '\0';
7787                 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
7788                         != JIM_OK) continue;
7789                 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
7790                     (bestVer == -1 || bestVer < fileVer))
7791                 {
7792                     bestVer = fileVer;
7793                     Jim_Free(bestPackage);
7794                     bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
7795                     sprintf(bestPackage, "%s/%s", buf, fileName);
7796                 }
7797             }
7798         }
7799         closedir(dir);
7800     }
7801     return bestPackage;
7802 }
7803
7804 #else /* JIM_ANSIC */
7805
7806 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7807         int prefixc, const char *pkgName, int pkgVer, int flags)
7808 {
7809     JIM_NOTUSED(interp);
7810     JIM_NOTUSED(prefixes);
7811     JIM_NOTUSED(prefixc);
7812     JIM_NOTUSED(pkgName);
7813     JIM_NOTUSED(pkgVer);
7814     JIM_NOTUSED(flags);
7815     return NULL;
7816 }
7817
7818 #endif /* JIM_ANSIC */
7819
7820 /* Search for a suitable package under every dir specified by jim_libpath
7821  * and load it if possible. If a suitable package was loaded with success
7822  * JIM_OK is returned, otherwise JIM_ERR is returned. */
7823 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
7824         int flags)
7825 {
7826     Jim_Obj *libPathObjPtr;
7827     char **prefixes, *best;
7828     int prefixc, i, retCode = JIM_OK;
7829
7830     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7831     if (libPathObjPtr == NULL) {
7832         prefixc = 0;
7833         libPathObjPtr = NULL;
7834     } else {
7835         Jim_IncrRefCount(libPathObjPtr);
7836         Jim_ListLength(interp, libPathObjPtr, &prefixc);
7837     }
7838
7839     prefixes = Jim_Alloc(sizeof(char*)*prefixc);
7840     for (i = 0; i < prefixc; i++) {
7841             Jim_Obj *prefixObjPtr;
7842             if (Jim_ListIndex(interp, libPathObjPtr, i,
7843                     &prefixObjPtr, JIM_NONE) != JIM_OK)
7844             {
7845                 prefixes[i] = NULL;
7846                 continue;
7847             }
7848             prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
7849     }
7850     /* Scan every directory to find the "best" package. */
7851     best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
7852     if (best != NULL) {
7853         char *p = strrchr(best, '.');
7854         /* Try to load/source it */
7855         if (p && strcmp(p, ".tcl") == 0) {
7856             retCode = Jim_EvalFile(interp, best);
7857         } else {
7858             retCode = Jim_LoadLibrary(interp, best);
7859         }
7860     } else {
7861         retCode = JIM_ERR;
7862     }
7863     Jim_Free(best);
7864     for (i = 0; i < prefixc; i++)
7865         Jim_Free(prefixes[i]);
7866     Jim_Free(prefixes);
7867     if (libPathObjPtr)
7868         Jim_DecrRefCount(interp, libPathObjPtr);
7869     return retCode;
7870 }
7871
7872 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
7873         const char *ver, int flags)
7874 {
7875     Jim_HashEntry *he;
7876     int requiredVer;
7877
7878     if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
7879         return NULL;
7880     he = Jim_FindHashEntry(&interp->packages, name);
7881     if (he == NULL) {
7882         /* Try to load the package. */
7883         if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
7884             he = Jim_FindHashEntry(&interp->packages, name);
7885             if (he == NULL) {
7886                 return "?";
7887             }
7888             return he->val;
7889         }
7890         /* No way... return an error. */
7891         if (flags & JIM_ERRMSG) {
7892             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7893             Jim_AppendStrings(interp, Jim_GetResult(interp),
7894                     "Can't find package '", name, "'", NULL);
7895         }
7896         return NULL;
7897     } else {
7898         int actualVer;
7899         if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
7900                 != JIM_OK)
7901         {
7902             return NULL;
7903         }
7904         /* Check if version matches. */
7905         if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
7906             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7907             Jim_AppendStrings(interp, Jim_GetResult(interp),
7908                     "Package '", name, "' already loaded, but with version ",
7909                     he->val, NULL);
7910             return NULL;
7911         }
7912         return he->val;
7913     }
7914 }
7915
7916 /* -----------------------------------------------------------------------------
7917  * Eval
7918  * ---------------------------------------------------------------------------*/
7919 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
7920 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
7921
7922 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
7923         Jim_Obj *const *argv);
7924
7925 /* Handle calls to the [unknown] command */
7926 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
7927 {
7928     Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
7929     int retCode;
7930
7931     /* If the [unknown] command does not exists returns
7932      * just now */
7933     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
7934         return JIM_ERR;
7935
7936     /* The object interp->unknown just contains
7937      * the "unknown" string, it is used in order to
7938      * avoid to lookup the unknown command every time
7939      * but instread to cache the result. */
7940     if (argc+1 <= JIM_EVAL_SARGV_LEN)
7941         v = sv;
7942     else
7943         v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
7944     /* Make a copy of the arguments vector, but shifted on
7945      * the right of one position. The command name of the
7946      * command will be instead the first argument of the
7947      * [unknonw] call. */
7948     memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
7949     v[0] = interp->unknown;
7950     /* Call it */
7951     retCode = Jim_EvalObjVector(interp, argc+1, v);
7952     /* Clean up */
7953     if (v != sv)
7954         Jim_Free(v);
7955     return retCode;
7956 }
7957
7958 /* Eval the object vector 'objv' composed of 'objc' elements.
7959  * Every element is used as single argument.
7960  * Jim_EvalObj() will call this function every time its object
7961  * argument is of "list" type, with no string representation.
7962  *
7963  * This is possible because the string representation of a
7964  * list object generated by the UpdateStringOfList is made
7965  * in a way that ensures that every list element is a different
7966  * command argument. */
7967 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
7968 {
7969     int i, retcode;
7970     Jim_Cmd *cmdPtr;
7971
7972     /* Incr refcount of arguments. */
7973     for (i = 0; i < objc; i++)
7974         Jim_IncrRefCount(objv[i]);
7975     /* Command lookup */
7976     cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
7977     if (cmdPtr == NULL) {
7978         retcode = JimUnknown(interp, objc, objv);
7979     } else {
7980         /* Call it -- Make sure result is an empty object. */
7981         Jim_SetEmptyResult(interp);
7982         if (cmdPtr->cmdProc) {
7983             interp->cmdPrivData = cmdPtr->privData;
7984             retcode = cmdPtr->cmdProc(interp, objc, objv);
7985         } else {
7986             retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
7987             if (retcode == JIM_ERR) {
7988                 JimAppendStackTrace(interp,
7989                     Jim_GetString(objv[0], NULL), "?", 1);
7990             }
7991         }
7992     }
7993     /* Decr refcount of arguments and return the retcode */
7994     for (i = 0; i < objc; i++)
7995         Jim_DecrRefCount(interp, objv[i]);
7996     return retcode;
7997 }
7998
7999 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8000  * via *objPtrPtr. This function is only called by Jim_EvalObj().
8001  * The returned object has refcount = 0. */
8002 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8003         int tokens, Jim_Obj **objPtrPtr)
8004 {
8005     int totlen = 0, i, retcode;
8006     Jim_Obj **intv;
8007     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8008     Jim_Obj *objPtr;
8009     char *s;
8010
8011     if (tokens <= JIM_EVAL_SINTV_LEN)
8012         intv = sintv;
8013     else
8014         intv = Jim_Alloc(sizeof(Jim_Obj*)*
8015                 tokens);
8016     /* Compute every token forming the argument
8017      * in the intv objects vector. */
8018     for (i = 0; i < tokens; i++) {
8019         switch(token[i].type) {
8020         case JIM_TT_ESC:
8021         case JIM_TT_STR:
8022             intv[i] = token[i].objPtr;
8023             break;
8024         case JIM_TT_VAR:
8025             intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8026             if (!intv[i]) {
8027                 retcode = JIM_ERR;
8028                 goto err;
8029             }
8030             break;
8031         case JIM_TT_DICTSUGAR:
8032             intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8033             if (!intv[i]) {
8034                 retcode = JIM_ERR;
8035                 goto err;
8036             }
8037             break;
8038         case JIM_TT_CMD:
8039             retcode = Jim_EvalObj(interp, token[i].objPtr);
8040             if (retcode != JIM_OK)
8041                 goto err;
8042             intv[i] = Jim_GetResult(interp);
8043             break;
8044         default:
8045             Jim_Panic(interp,
8046               "default token type reached "
8047               "in Jim_InterpolateTokens().");
8048             break;
8049         }
8050         Jim_IncrRefCount(intv[i]);
8051         /* Make sure there is a valid
8052          * string rep, and add the string
8053          * length to the total legnth. */
8054         Jim_GetString(intv[i], NULL);
8055         totlen += intv[i]->length;
8056     }
8057     /* Concatenate every token in an unique
8058      * object. */
8059     objPtr = Jim_NewStringObjNoAlloc(interp,
8060             NULL, 0);
8061     s = objPtr->bytes = Jim_Alloc(totlen+1);
8062     objPtr->length = totlen;
8063     for (i = 0; i < tokens; i++) {
8064         memcpy(s, intv[i]->bytes, intv[i]->length);
8065         s += intv[i]->length;
8066         Jim_DecrRefCount(interp, intv[i]);
8067     }
8068     objPtr->bytes[totlen] = '\0';
8069     /* Free the intv vector if not static. */
8070     if (tokens > JIM_EVAL_SINTV_LEN)
8071         Jim_Free(intv);
8072     *objPtrPtr = objPtr;
8073     return JIM_OK;
8074 err:
8075     i--;
8076     for (; i >= 0; i--)
8077         Jim_DecrRefCount(interp, intv[i]);
8078     if (tokens > JIM_EVAL_SINTV_LEN)
8079         Jim_Free(intv);
8080     return retcode;
8081 }
8082
8083 /* Helper of Jim_EvalObj() to perform argument expansion.
8084  * Basically this function append an argument to 'argv'
8085  * (and increments argc by reference accordingly), performing
8086  * expansion of the list object if 'expand' is non-zero, or
8087  * just adding objPtr to argv if 'expand' is zero. */
8088 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8089         int *argcPtr, int expand, Jim_Obj *objPtr)
8090 {
8091     if (!expand) {
8092         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8093         /* refcount of objPtr not incremented because
8094          * we are actually transfering a reference from
8095          * the old 'argv' to the expanded one. */
8096         (*argv)[*argcPtr] = objPtr;
8097         (*argcPtr)++;
8098     } else {
8099         int len, i;
8100
8101         Jim_ListLength(interp, objPtr, &len);
8102         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8103         for (i = 0; i < len; i++) {
8104             (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8105             Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8106             (*argcPtr)++;
8107         }
8108         /* The original object reference is no longer needed,
8109          * after the expansion it is no longer present on
8110          * the argument vector, but the single elements are
8111          * in its place. */
8112         Jim_DecrRefCount(interp, objPtr);
8113     }
8114 }
8115
8116 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8117 {
8118     int i, j = 0, len;
8119     ScriptObj *script;
8120     ScriptToken *token;
8121     int *cs; /* command structure array */
8122     int retcode = JIM_OK;
8123     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8124
8125     interp->errorFlag = 0;
8126
8127     /* If the object is of type "list" and there is no
8128      * string representation for this object, we can call
8129      * a specialized version of Jim_EvalObj() */
8130     if (scriptObjPtr->typePtr == &listObjType &&
8131         scriptObjPtr->internalRep.listValue.len &&
8132         scriptObjPtr->bytes == NULL) {
8133         Jim_IncrRefCount(scriptObjPtr);
8134         retcode = Jim_EvalObjVector(interp,
8135                 scriptObjPtr->internalRep.listValue.len,
8136                 scriptObjPtr->internalRep.listValue.ele);
8137         Jim_DecrRefCount(interp, scriptObjPtr);
8138         return retcode;
8139     }
8140
8141     Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8142     script = Jim_GetScript(interp, scriptObjPtr);
8143     /* Now we have to make sure the internal repr will not be
8144      * freed on shimmering.
8145      *
8146      * Think for example to this:
8147      *
8148      * set x {llength $x; ... some more code ...}; eval $x
8149      *
8150      * In order to preserve the internal rep, we increment the
8151      * inUse field of the script internal rep structure. */
8152     script->inUse++;
8153
8154     token = script->token;
8155     len = script->len;
8156     cs = script->cmdStruct;
8157     i = 0; /* 'i' is the current token index. */
8158
8159     /* Reset the interpreter result. This is useful to
8160      * return the emtpy result in the case of empty program. */
8161     Jim_SetEmptyResult(interp);
8162
8163     /* Execute every command sequentially, returns on
8164      * error (i.e. if a command does not return JIM_OK) */
8165     while (i < len) {
8166         int expand = 0;
8167         int argc = *cs++; /* Get the number of arguments */
8168         Jim_Cmd *cmd;
8169
8170         /* Set the expand flag if needed. */
8171         if (argc == -1) {
8172             expand++;
8173             argc = *cs++;
8174         }
8175         /* Allocate the arguments vector */
8176         if (argc <= JIM_EVAL_SARGV_LEN)
8177             argv = sargv;
8178         else
8179             argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8180         /* Populate the arguments objects. */
8181         for (j = 0; j < argc; j++) {
8182             int tokens = *cs++;
8183
8184             /* tokens is negative if expansion is needed.
8185              * for this argument. */
8186             if (tokens < 0) {
8187                 tokens = (-tokens)-1;
8188                 i++;
8189             }
8190             if (tokens == 1) {
8191                 /* Fast path if the token does not
8192                  * need interpolation */
8193                 switch(token[i].type) {
8194                 case JIM_TT_ESC:
8195                 case JIM_TT_STR:
8196                     argv[j] = token[i].objPtr;
8197                     break;
8198                 case JIM_TT_VAR:
8199                     tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8200                             JIM_ERRMSG);
8201                     if (!tmpObjPtr) {
8202                         retcode = JIM_ERR;
8203                         goto err;
8204                     }
8205                     argv[j] = tmpObjPtr;
8206                     break;
8207                 case JIM_TT_DICTSUGAR:
8208                     tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8209                     if (!tmpObjPtr) {
8210                         retcode = JIM_ERR;
8211                         goto err;
8212                     }
8213                     argv[j] = tmpObjPtr;
8214                     break;
8215                 case JIM_TT_CMD:
8216                     retcode = Jim_EvalObj(interp, token[i].objPtr);
8217                     if (retcode != JIM_OK)
8218                         goto err;
8219                     argv[j] = Jim_GetResult(interp);
8220                     break;
8221                 default:
8222                     Jim_Panic(interp,
8223                       "default token type reached "
8224                       "in Jim_EvalObj().");
8225                     break;
8226                 }
8227                 Jim_IncrRefCount(argv[j]);
8228                 i += 2;
8229             } else {
8230                 /* For interpolation we call an helper
8231                  * function doing the work for us. */
8232                 if ((retcode = Jim_InterpolateTokens(interp,
8233                         token+i, tokens, &tmpObjPtr)) != JIM_OK)
8234                 {
8235                     goto err;
8236                 }
8237                 argv[j] = tmpObjPtr;
8238                 Jim_IncrRefCount(argv[j]);
8239                 i += tokens+1;
8240             }
8241         }
8242         /* Handle {expand} expansion */
8243         if (expand) {
8244             int *ecs = cs - argc;
8245             int eargc = 0;
8246             Jim_Obj **eargv = NULL;
8247
8248             for (j = 0; j < argc; j++) {
8249                 Jim_ExpandArgument( interp, &eargv, &eargc,
8250                         ecs[j] < 0, argv[j]);
8251             }
8252             if (argv != sargv)
8253                 Jim_Free(argv);
8254             argc = eargc;
8255             argv = eargv;
8256             j = argc;
8257             if (argc == 0) {
8258                 /* Nothing to do with zero args. */
8259                 Jim_Free(eargv);
8260                 continue;
8261             }
8262         }
8263         /* Lookup the command to call */
8264         cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8265         if (cmd != NULL) {
8266             /* Call it -- Make sure result is an empty object. */
8267             Jim_SetEmptyResult(interp);
8268             if (cmd->cmdProc) {
8269                 interp->cmdPrivData = cmd->privData;
8270                 retcode = cmd->cmdProc(interp, argc, argv);
8271             } else {
8272                 retcode = JimCallProcedure(interp, cmd, argc, argv);
8273                 if (retcode == JIM_ERR) {
8274                     JimAppendStackTrace(interp,
8275                         Jim_GetString(argv[0], NULL), script->fileName,
8276                         token[i-argc*2].linenr);
8277                 }
8278             }
8279         } else {
8280             /* Call [unknown] */
8281             retcode = JimUnknown(interp, argc, argv);
8282         }
8283         if (retcode != JIM_OK) {
8284             i -= argc*2; /* point to the command name. */
8285             goto err;
8286         }
8287         /* Decrement the arguments count */
8288         for (j = 0; j < argc; j++) {
8289             Jim_DecrRefCount(interp, argv[j]);
8290         }
8291
8292         if (argv != sargv) {
8293             Jim_Free(argv);
8294             argv = NULL;
8295         }
8296     }
8297     /* Note that we don't have to decrement inUse, because the
8298      * following code transfers our use of the reference again to
8299      * the script object. */
8300     j = 0; /* on normal termination, the argv array is already
8301           Jim_DecrRefCount-ed. */
8302 err:
8303     /* Handle errors. */
8304     if (retcode == JIM_ERR && !interp->errorFlag) {
8305         interp->errorFlag = 1;
8306         JimSetErrorFileName(interp, script->fileName);
8307         JimSetErrorLineNumber(interp, token[i].linenr);
8308         JimResetStackTrace(interp);
8309     }
8310     Jim_FreeIntRep(interp, scriptObjPtr);
8311     scriptObjPtr->typePtr = &scriptObjType;
8312     Jim_SetIntRepPtr(scriptObjPtr, script);
8313     Jim_DecrRefCount(interp, scriptObjPtr);
8314     for (i = 0; i < j; i++) {
8315         Jim_DecrRefCount(interp, argv[i]);
8316     }
8317     if (argv != sargv)
8318         Jim_Free(argv);
8319     return retcode;
8320 }
8321
8322 /* Call a procedure implemented in Tcl.
8323  * It's possible to speed-up a lot this function, currently
8324  * the callframes are not cached, but allocated and
8325  * destroied every time. What is expecially costly is
8326  * to create/destroy the local vars hash table every time.
8327  *
8328  * This can be fixed just implementing callframes caching
8329  * in JimCreateCallFrame() and JimFreeCallFrame(). */
8330 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8331         Jim_Obj *const *argv)
8332 {
8333     int i, retcode;
8334     Jim_CallFrame *callFramePtr;
8335
8336     /* Check arity */
8337     if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8338         argc > cmd->arityMax)) {
8339         Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8340         Jim_AppendStrings(interp, objPtr,
8341             "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8342             (cmd->arityMin > 1) ? " " : "",
8343             Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8344         Jim_SetResult(interp, objPtr);
8345         return JIM_ERR;
8346     }
8347     /* Check if there are too nested calls */
8348     if (interp->numLevels == interp->maxNestingDepth) {
8349         Jim_SetResultString(interp,
8350             "Too many nested calls. Infinite recursion?", -1);
8351         return JIM_ERR;
8352     }
8353     /* Create a new callframe */
8354     callFramePtr = JimCreateCallFrame(interp);
8355     callFramePtr->parentCallFrame = interp->framePtr;
8356     callFramePtr->argv = argv;
8357     callFramePtr->argc = argc;
8358     callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8359     callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8360     callFramePtr->staticVars = cmd->staticVars;
8361     Jim_IncrRefCount(cmd->argListObjPtr);
8362     Jim_IncrRefCount(cmd->bodyObjPtr);
8363     interp->framePtr = callFramePtr;
8364     interp->numLevels ++;
8365     /* Set arguments */
8366     for (i = 0; i < cmd->arityMin-1; i++) {
8367         Jim_Obj *objPtr;
8368
8369         Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8370         Jim_SetVariable(interp, objPtr, argv[i+1]);
8371     }
8372     if (cmd->arityMax == -1) {
8373         Jim_Obj *listObjPtr, *objPtr;
8374
8375         listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8376                 argc-cmd->arityMin);
8377         Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8378         Jim_SetVariable(interp, objPtr, listObjPtr);
8379     }
8380     /* Eval the body */
8381     retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8382
8383     /* Destroy the callframe */
8384     interp->numLevels --;
8385     interp->framePtr = interp->framePtr->parentCallFrame;
8386     if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8387         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8388     } else {
8389         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8390     }
8391     /* Handle the JIM_EVAL return code */
8392     if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8393         int savedLevel = interp->evalRetcodeLevel;
8394
8395         interp->evalRetcodeLevel = interp->numLevels;
8396         while (retcode == JIM_EVAL) {
8397             Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8398             Jim_IncrRefCount(resultScriptObjPtr);
8399             retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8400             Jim_DecrRefCount(interp, resultScriptObjPtr);
8401         }
8402         interp->evalRetcodeLevel = savedLevel;
8403     }
8404     /* Handle the JIM_RETURN return code */
8405     if (retcode == JIM_RETURN) {
8406         retcode = interp->returnCode;
8407         interp->returnCode = JIM_OK;
8408     }
8409     return retcode;
8410 }
8411
8412 int Jim_Eval(Jim_Interp *interp, const char *script)
8413 {
8414     Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8415     int retval;
8416
8417     Jim_IncrRefCount(scriptObjPtr);
8418     retval = Jim_EvalObj(interp, scriptObjPtr);
8419     Jim_DecrRefCount(interp, scriptObjPtr);
8420     return retval;
8421 }
8422
8423 /* Execute script in the scope of the global level */
8424 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8425 {
8426     Jim_CallFrame *savedFramePtr;
8427     int retval;
8428
8429     savedFramePtr = interp->framePtr;
8430     interp->framePtr = interp->topFramePtr;
8431     retval = Jim_Eval(interp, script);
8432     interp->framePtr = savedFramePtr;
8433     return retval;
8434 }
8435
8436 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8437 {
8438     Jim_CallFrame *savedFramePtr;
8439     int retval;
8440
8441     savedFramePtr = interp->framePtr;
8442     interp->framePtr = interp->topFramePtr;
8443     retval = Jim_EvalObj(interp, scriptObjPtr);
8444     interp->framePtr = savedFramePtr;
8445     /* Try to report the error (if any) via the bgerror proc */
8446     if (retval != JIM_OK) {
8447         Jim_Obj *objv[2];
8448
8449         objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8450         objv[1] = Jim_GetResult(interp);
8451         Jim_IncrRefCount(objv[0]);
8452         Jim_IncrRefCount(objv[1]);
8453         if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8454             /* Report the error to stderr. */
8455             fprintf(interp->stderr, "Background error:" JIM_NL);
8456             Jim_PrintErrorMessage(interp);
8457         }
8458         Jim_DecrRefCount(interp, objv[0]);
8459         Jim_DecrRefCount(interp, objv[1]);
8460     }
8461     return retval;
8462 }
8463
8464 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8465 {
8466     char *prg = NULL;
8467     FILE *fp;
8468     int nread, totread, maxlen, buflen;
8469     int retval;
8470     Jim_Obj *scriptObjPtr;
8471     
8472     if ((fp = fopen(filename, "r")) == NULL) {
8473         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8474         Jim_AppendStrings(interp, Jim_GetResult(interp),
8475             "Error loading script \"", filename, "\": ",
8476             strerror(errno), NULL);
8477         return JIM_ERR;
8478     }
8479     buflen = 1024;
8480     maxlen = totread = 0;
8481     while (1) {
8482         if (maxlen < totread+buflen+1) {
8483             maxlen = totread+buflen+1;
8484             prg = Jim_Realloc(prg, maxlen);
8485         }
8486         if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8487         totread += nread;
8488     }
8489     prg[totread] = '\0';
8490     fclose(fp);
8491
8492     scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8493     JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8494     Jim_IncrRefCount(scriptObjPtr);
8495     retval = Jim_EvalObj(interp, scriptObjPtr);
8496     Jim_DecrRefCount(interp, scriptObjPtr);
8497     return retval;
8498 }
8499
8500 /* -----------------------------------------------------------------------------
8501  * Subst
8502  * ---------------------------------------------------------------------------*/
8503 static int JimParseSubstStr(struct JimParserCtx *pc)
8504 {
8505     pc->tstart = pc->p;
8506     pc->tline = pc->linenr;
8507     while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8508         pc->p++; pc->len--;
8509     }
8510     pc->tend = pc->p-1;
8511     pc->tt = JIM_TT_ESC;
8512     return JIM_OK;
8513 }
8514
8515 static int JimParseSubst(struct JimParserCtx *pc, int flags)
8516 {
8517     int retval;
8518
8519     if (pc->len == 0) {
8520         pc->tstart = pc->tend = pc->p;
8521         pc->tline = pc->linenr;
8522         pc->tt = JIM_TT_EOL;
8523         pc->eof = 1;
8524         return JIM_OK;
8525     }
8526     switch(*pc->p) {
8527     case '[':
8528         retval = JimParseCmd(pc);
8529         if (flags & JIM_SUBST_NOCMD) {
8530             pc->tstart--;
8531             pc->tend++;
8532             pc->tt = (flags & JIM_SUBST_NOESC) ?
8533                 JIM_TT_STR : JIM_TT_ESC;
8534         }
8535         return retval;
8536         break;
8537     case '$':
8538         if (JimParseVar(pc) == JIM_ERR) {
8539             pc->tstart = pc->tend = pc->p++; pc->len--;
8540             pc->tline = pc->linenr;
8541             pc->tt = JIM_TT_STR;
8542         } else {
8543             if (flags & JIM_SUBST_NOVAR) {
8544                 pc->tstart--;
8545                 if (flags & JIM_SUBST_NOESC)
8546                     pc->tt = JIM_TT_STR;
8547                 else
8548                     pc->tt = JIM_TT_ESC;
8549                 if (*pc->tstart == '{') {
8550                     pc->tstart--;
8551                     if (*(pc->tend+1))
8552                         pc->tend++;
8553                 }
8554             }
8555         }
8556         break;
8557     default:
8558         retval = JimParseSubstStr(pc);
8559         if (flags & JIM_SUBST_NOESC)
8560             pc->tt = JIM_TT_STR;
8561         return retval;
8562         break;
8563     }
8564     return JIM_OK;
8565 }
8566
8567 /* The subst object type reuses most of the data structures and functions
8568  * of the script object. Script's data structures are a bit more complex
8569  * for what is needed for [subst]itution tasks, but the reuse helps to
8570  * deal with a single data structure at the cost of some more memory
8571  * usage for substitutions. */
8572 static Jim_ObjType substObjType = {
8573     "subst",
8574     FreeScriptInternalRep,
8575     DupScriptInternalRep,
8576     NULL,
8577     JIM_TYPE_REFERENCES,
8578 };
8579
8580 /* This method takes the string representation of an object
8581  * as a Tcl string where to perform [subst]itution, and generates
8582  * the pre-parsed internal representation. */
8583 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8584 {
8585     int scriptTextLen;
8586     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8587     struct JimParserCtx parser;
8588     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8589
8590     script->len = 0;
8591     script->csLen = 0;
8592     script->commands = 0;
8593     script->token = NULL;
8594     script->cmdStruct = NULL;
8595     script->inUse = 1;
8596     script->substFlags = flags;
8597     script->fileName = NULL;
8598
8599     JimParserInit(&parser, scriptText, scriptTextLen, 1);
8600     while(1) {
8601         char *token;
8602         int len, type, linenr;
8603
8604         JimParseSubst(&parser, flags);
8605         if (JimParserEof(&parser)) break;
8606         token = JimParserGetToken(&parser, &len, &type, &linenr);
8607         ScriptObjAddToken(interp, script, token, len, type,
8608                 NULL, linenr);
8609     }
8610     /* Free the old internal rep and set the new one. */
8611     Jim_FreeIntRep(interp, objPtr);
8612     Jim_SetIntRepPtr(objPtr, script);
8613     objPtr->typePtr = &scriptObjType;
8614     return JIM_OK;
8615 }
8616
8617 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8618 {
8619     struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8620
8621     if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8622         SetSubstFromAny(interp, objPtr, flags);
8623     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8624 }
8625
8626 /* Performs commands,variables,blackslashes substitution,
8627  * storing the result object (with refcount 0) into
8628  * resObjPtrPtr. */
8629 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8630         Jim_Obj **resObjPtrPtr, int flags)
8631 {
8632     ScriptObj *script;
8633     ScriptToken *token;
8634     int i, len, retcode = JIM_OK;
8635     Jim_Obj *resObjPtr, *savedResultObjPtr;
8636
8637     script = Jim_GetSubst(interp, substObjPtr, flags);
8638 #ifdef JIM_OPTIMIZATION
8639     /* Fast path for a very common case with array-alike syntax,
8640      * that's: $foo($bar) */
8641     if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8642         Jim_Obj *varObjPtr = script->token[0].objPtr;
8643         
8644         Jim_IncrRefCount(varObjPtr);
8645         resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8646         if (resObjPtr == NULL) {
8647             Jim_DecrRefCount(interp, varObjPtr);
8648             return JIM_ERR;
8649         }
8650         Jim_DecrRefCount(interp, varObjPtr);
8651         *resObjPtrPtr = resObjPtr;
8652         return JIM_OK;
8653     }
8654 #endif
8655
8656     Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8657     /* In order to preserve the internal rep, we increment the
8658      * inUse field of the script internal rep structure. */
8659     script->inUse++;
8660
8661     token = script->token;
8662     len = script->len;
8663
8664     /* Save the interp old result, to set it again before
8665      * to return. */
8666     savedResultObjPtr = interp->result;
8667     Jim_IncrRefCount(savedResultObjPtr);
8668     
8669     /* Perform the substitution. Starts with an empty object
8670      * and adds every token (performing the appropriate
8671      * var/command/escape substitution). */
8672     resObjPtr = Jim_NewStringObj(interp, "", 0);
8673     for (i = 0; i < len; i++) {
8674         Jim_Obj *objPtr;
8675
8676         switch(token[i].type) {
8677         case JIM_TT_STR:
8678         case JIM_TT_ESC:
8679             Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8680             break;
8681         case JIM_TT_VAR:
8682             objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8683             if (objPtr == NULL) goto err;
8684             Jim_IncrRefCount(objPtr);
8685             Jim_AppendObj(interp, resObjPtr, objPtr);
8686             Jim_DecrRefCount(interp, objPtr);
8687             break;
8688         case JIM_TT_CMD:
8689             if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8690                 goto err;
8691             Jim_AppendObj(interp, resObjPtr, interp->result);
8692             break;
8693         default:
8694             Jim_Panic(interp,
8695               "default token type (%d) reached "
8696               "in Jim_SubstObj().", token[i].type);
8697             break;
8698         }
8699     }
8700 ok:
8701     if (retcode == JIM_OK)
8702         Jim_SetResult(interp, savedResultObjPtr);
8703     Jim_DecrRefCount(interp, savedResultObjPtr);
8704     /* Note that we don't have to decrement inUse, because the
8705      * following code transfers our use of the reference again to
8706      * the script object. */
8707     Jim_FreeIntRep(interp, substObjPtr);
8708     substObjPtr->typePtr = &scriptObjType;
8709     Jim_SetIntRepPtr(substObjPtr, script);
8710     Jim_DecrRefCount(interp, substObjPtr);
8711     *resObjPtrPtr = resObjPtr;
8712     return retcode;
8713 err:
8714     Jim_FreeNewObj(interp, resObjPtr);
8715     retcode = JIM_ERR;
8716     goto ok;
8717 }
8718
8719 /* -----------------------------------------------------------------------------
8720  * API Input/Export functions
8721  * ---------------------------------------------------------------------------*/
8722
8723 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8724 {
8725     Jim_HashEntry *he;
8726
8727     he = Jim_FindHashEntry(&interp->stub, funcname);
8728     if (!he)
8729         return JIM_ERR;
8730     memcpy(targetPtrPtr, &he->val, sizeof(void*));
8731     return JIM_OK;
8732 }
8733
8734 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
8735 {
8736     return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
8737 }
8738
8739 #define JIM_REGISTER_API(name) \
8740     Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
8741
8742 void JimRegisterCoreApi(Jim_Interp *interp)
8743 {
8744   interp->getApiFuncPtr = Jim_GetApi;
8745   JIM_REGISTER_API(Alloc);
8746   JIM_REGISTER_API(Free);
8747   JIM_REGISTER_API(Eval);
8748   JIM_REGISTER_API(EvalGlobal);
8749   JIM_REGISTER_API(EvalFile);
8750   JIM_REGISTER_API(EvalObj);
8751   JIM_REGISTER_API(EvalObjBackground);
8752   JIM_REGISTER_API(EvalObjVector);
8753   JIM_REGISTER_API(InitHashTable);
8754   JIM_REGISTER_API(ExpandHashTable);
8755   JIM_REGISTER_API(AddHashEntry);
8756   JIM_REGISTER_API(ReplaceHashEntry);
8757   JIM_REGISTER_API(DeleteHashEntry);
8758   JIM_REGISTER_API(FreeHashTable);
8759   JIM_REGISTER_API(FindHashEntry);
8760   JIM_REGISTER_API(ResizeHashTable);
8761   JIM_REGISTER_API(GetHashTableIterator);
8762   JIM_REGISTER_API(NextHashEntry);
8763   JIM_REGISTER_API(NewObj);
8764   JIM_REGISTER_API(FreeObj);
8765   JIM_REGISTER_API(InvalidateStringRep);
8766   JIM_REGISTER_API(InitStringRep);
8767   JIM_REGISTER_API(DuplicateObj);
8768   JIM_REGISTER_API(GetString);
8769   JIM_REGISTER_API(Length);
8770   JIM_REGISTER_API(InvalidateStringRep);
8771   JIM_REGISTER_API(NewStringObj);
8772   JIM_REGISTER_API(NewStringObjNoAlloc);
8773   JIM_REGISTER_API(AppendString);
8774   JIM_REGISTER_API(AppendObj);
8775   JIM_REGISTER_API(AppendStrings);
8776   JIM_REGISTER_API(StringEqObj);
8777   JIM_REGISTER_API(StringMatchObj);
8778   JIM_REGISTER_API(StringRangeObj);
8779   JIM_REGISTER_API(FormatString);
8780   JIM_REGISTER_API(CompareStringImmediate);
8781   JIM_REGISTER_API(NewReference);
8782   JIM_REGISTER_API(GetReference);
8783   JIM_REGISTER_API(SetFinalizer);
8784   JIM_REGISTER_API(GetFinalizer);
8785   JIM_REGISTER_API(CreateInterp);
8786   JIM_REGISTER_API(FreeInterp);
8787   JIM_REGISTER_API(GetExitCode);
8788   JIM_REGISTER_API(SetStdin);
8789   JIM_REGISTER_API(SetStdout);
8790   JIM_REGISTER_API(SetStderr);
8791   JIM_REGISTER_API(CreateCommand);
8792   JIM_REGISTER_API(CreateProcedure);
8793   JIM_REGISTER_API(DeleteCommand);
8794   JIM_REGISTER_API(RenameCommand);
8795   JIM_REGISTER_API(GetCommand);
8796   JIM_REGISTER_API(SetVariable);
8797   JIM_REGISTER_API(SetVariableStr);
8798   JIM_REGISTER_API(SetGlobalVariableStr);
8799   JIM_REGISTER_API(SetVariableStrWithStr);
8800   JIM_REGISTER_API(SetVariableLink);
8801   JIM_REGISTER_API(GetVariable);
8802   JIM_REGISTER_API(GetCallFrameByLevel);
8803   JIM_REGISTER_API(Collect);
8804   JIM_REGISTER_API(CollectIfNeeded);
8805   JIM_REGISTER_API(GetIndex);
8806   JIM_REGISTER_API(NewListObj);
8807   JIM_REGISTER_API(ListAppendElement);
8808   JIM_REGISTER_API(ListAppendList);
8809   JIM_REGISTER_API(ListLength);
8810   JIM_REGISTER_API(ListIndex);
8811   JIM_REGISTER_API(SetListIndex);
8812   JIM_REGISTER_API(ConcatObj);
8813   JIM_REGISTER_API(NewDictObj);
8814   JIM_REGISTER_API(DictKey);
8815   JIM_REGISTER_API(DictKeysVector);
8816   JIM_REGISTER_API(GetIndex);
8817   JIM_REGISTER_API(GetReturnCode);
8818   JIM_REGISTER_API(EvalExpression);
8819   JIM_REGISTER_API(GetBoolFromExpr);
8820   JIM_REGISTER_API(GetWide);
8821   JIM_REGISTER_API(GetLong);
8822   JIM_REGISTER_API(SetWide);
8823   JIM_REGISTER_API(NewIntObj);
8824   JIM_REGISTER_API(GetDouble);
8825   JIM_REGISTER_API(SetDouble);
8826   JIM_REGISTER_API(NewDoubleObj);
8827   JIM_REGISTER_API(WrongNumArgs);
8828   JIM_REGISTER_API(SetDictKeysVector);
8829   JIM_REGISTER_API(SubstObj);
8830   JIM_REGISTER_API(RegisterApi);
8831   JIM_REGISTER_API(PrintErrorMessage);
8832   JIM_REGISTER_API(InteractivePrompt);
8833   JIM_REGISTER_API(RegisterCoreCommands);
8834   JIM_REGISTER_API(GetSharedString);
8835   JIM_REGISTER_API(ReleaseSharedString);
8836   JIM_REGISTER_API(Panic);
8837   JIM_REGISTER_API(StrDup);
8838   JIM_REGISTER_API(UnsetVariable);
8839   JIM_REGISTER_API(GetVariableStr);
8840   JIM_REGISTER_API(GetGlobalVariable);
8841   JIM_REGISTER_API(GetGlobalVariableStr);
8842   JIM_REGISTER_API(GetAssocData);
8843   JIM_REGISTER_API(SetAssocData);
8844   JIM_REGISTER_API(DeleteAssocData);
8845   JIM_REGISTER_API(GetEnum);
8846   JIM_REGISTER_API(ScriptIsComplete);
8847   JIM_REGISTER_API(PackageRequire);
8848   JIM_REGISTER_API(PackageProvide);
8849   JIM_REGISTER_API(InitStack);
8850   JIM_REGISTER_API(FreeStack);
8851   JIM_REGISTER_API(StackLen);
8852   JIM_REGISTER_API(StackPush);
8853   JIM_REGISTER_API(StackPop);
8854   JIM_REGISTER_API(StackPeek);
8855   JIM_REGISTER_API(FreeStackElements);
8856 }
8857
8858 /* -----------------------------------------------------------------------------
8859  * Core commands utility functions
8860  * ---------------------------------------------------------------------------*/
8861 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, 
8862         const char *msg)
8863 {
8864     int i;
8865     Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8866
8867     Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
8868     for (i = 0; i < argc; i++) {
8869         Jim_AppendObj(interp, objPtr, argv[i]);
8870         if (!(i+1 == argc && msg[0] == '\0'))
8871             Jim_AppendString(interp, objPtr, " ", 1);
8872     }
8873     Jim_AppendString(interp, objPtr, msg, -1);
8874     Jim_AppendString(interp, objPtr, "\"", 1);
8875     Jim_SetResult(interp, objPtr);
8876 }
8877
8878 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
8879 {
8880     Jim_HashTableIterator *htiter;
8881     Jim_HashEntry *he;
8882     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
8883     const char *pattern;
8884     int patternLen;
8885     
8886     pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
8887     htiter = Jim_GetHashTableIterator(&interp->commands);
8888     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8889         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
8890                     strlen((const char*)he->key), 0))
8891             continue;
8892         Jim_ListAppendElement(interp, listObjPtr,
8893                 Jim_NewStringObj(interp, he->key, -1));
8894     }
8895     Jim_FreeHashTableIterator(htiter);
8896     return listObjPtr;
8897 }
8898
8899 #define JIM_VARLIST_GLOBALS 0
8900 #define JIM_VARLIST_LOCALS 1
8901 #define JIM_VARLIST_VARS 2
8902
8903 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
8904         int mode)
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     if (mode == JIM_VARLIST_GLOBALS) {
8914         htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
8915     } else {
8916         /* For [info locals], if we are at top level an emtpy list
8917          * is returned. I don't agree, but we aim at compatibility (SS) */
8918         if (mode == JIM_VARLIST_LOCALS &&
8919             interp->framePtr == interp->topFramePtr)
8920             return listObjPtr;
8921         htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
8922     }
8923     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8924         Jim_Var *varPtr = (Jim_Var*) he->val;
8925         if (mode == JIM_VARLIST_LOCALS) {
8926             if (varPtr->linkFramePtr != NULL)
8927                 continue;
8928         }
8929         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
8930                     strlen((const char*)he->key), 0))
8931             continue;
8932         Jim_ListAppendElement(interp, listObjPtr,
8933                 Jim_NewStringObj(interp, he->key, -1));
8934     }
8935     Jim_FreeHashTableIterator(htiter);
8936     return listObjPtr;
8937 }
8938
8939 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
8940         Jim_Obj **objPtrPtr)
8941 {
8942     Jim_CallFrame *targetCallFrame;
8943
8944     if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
8945             != JIM_OK)
8946         return JIM_ERR;
8947     /* No proc call at toplevel callframe */
8948     if (targetCallFrame == interp->topFramePtr) {
8949         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8950         Jim_AppendStrings(interp, Jim_GetResult(interp),
8951                 "bad level \"",
8952                 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
8953         return JIM_ERR;
8954     }
8955     *objPtrPtr = Jim_NewListObj(interp,
8956             targetCallFrame->argv,
8957             targetCallFrame->argc);
8958     return JIM_OK;
8959 }
8960
8961 /* -----------------------------------------------------------------------------
8962  * Core commands
8963  * ---------------------------------------------------------------------------*/
8964
8965 /* fake [puts] -- not the real puts, just for debugging. */
8966 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
8967         Jim_Obj *const *argv)
8968 {
8969     const char *str;
8970     int len, nonewline = 0;
8971     
8972     if (argc != 2 && argc != 3) {
8973         Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
8974         return JIM_ERR;
8975     }
8976     if (argc == 3) {
8977         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
8978         {
8979             Jim_SetResultString(interp, "The second argument must "
8980                     "be -nonewline", -1);
8981             return JIM_OK;
8982         } else {
8983             nonewline = 1;
8984             argv++;
8985         }
8986     }
8987     str = Jim_GetString(argv[1], &len);
8988     fwrite(str, 1, len, interp->stdout);
8989     if (!nonewline) fprintf(interp->stdout, JIM_NL);
8990     return JIM_OK;
8991 }
8992
8993 /* Helper for [+] and [*] */
8994 static int Jim_AddMulHelper(Jim_Interp *interp, int argc, 
8995         Jim_Obj *const *argv, int op)
8996 {
8997     jim_wide wideValue, res;
8998     double doubleValue, doubleRes;
8999     int i;
9000
9001     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9002     
9003     for (i = 1; i < argc; i++) {
9004         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9005             goto trydouble;
9006         if (op == JIM_EXPROP_ADD)
9007             res += wideValue;
9008         else
9009             res *= wideValue;
9010     }
9011     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9012     return JIM_OK;
9013 trydouble:
9014     doubleRes = (double) res;
9015     for (;i < argc; i++) {
9016         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9017             return JIM_ERR;
9018         if (op == JIM_EXPROP_ADD)
9019             doubleRes += doubleValue;
9020         else
9021             doubleRes *= doubleValue;
9022     }
9023     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9024     return JIM_OK;
9025 }
9026
9027 /* Helper for [-] and [/] */
9028 static int Jim_SubDivHelper(Jim_Interp *interp, int argc, 
9029         Jim_Obj *const *argv, int op)
9030 {
9031     jim_wide wideValue, res = 0;
9032     double doubleValue, doubleRes = 0;
9033     int i = 2;
9034
9035     if (argc < 2) {
9036         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9037         return JIM_ERR;
9038     } else if (argc == 2) {
9039         /* The arity = 2 case is different. For [- x] returns -x,
9040          * while [/ x] returns 1/x. */
9041         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9042             if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9043                     JIM_OK)
9044             {
9045                 return JIM_ERR;
9046             } else {
9047                 if (op == JIM_EXPROP_SUB)
9048                     doubleRes = -doubleValue;
9049                 else
9050                     doubleRes = 1.0/doubleValue;
9051                 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9052                             doubleRes));
9053                 return JIM_OK;
9054             }
9055         }
9056         if (op == JIM_EXPROP_SUB) {
9057             res = -wideValue;
9058             Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9059         } else {
9060             doubleRes = 1.0/wideValue;
9061             Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9062                         doubleRes));
9063         }
9064         return JIM_OK;
9065     } else {
9066         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9067             if (Jim_GetDouble(interp, argv[1], &doubleRes)
9068                     != JIM_OK) {
9069                 return JIM_ERR;
9070             } else {
9071                 goto trydouble;
9072             }
9073         }
9074     }
9075     for (i = 2; i < argc; i++) {
9076         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9077             doubleRes = (double) res;
9078             goto trydouble;
9079         }
9080         if (op == JIM_EXPROP_SUB)
9081             res -= wideValue;
9082         else
9083             res /= wideValue;
9084     }
9085     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9086     return JIM_OK;
9087 trydouble:
9088     for (;i < argc; i++) {
9089         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9090             return JIM_ERR;
9091         if (op == JIM_EXPROP_SUB)
9092             doubleRes -= doubleValue;
9093         else
9094             doubleRes /= doubleValue;
9095     }
9096     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9097     return JIM_OK;
9098 }
9099
9100
9101 /* [+] */
9102 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9103         Jim_Obj *const *argv)
9104 {
9105     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9106 }
9107
9108 /* [*] */
9109 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9110         Jim_Obj *const *argv)
9111 {
9112     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9113 }
9114
9115 /* [-] */
9116 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9117         Jim_Obj *const *argv)
9118 {
9119     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9120 }
9121
9122 /* [/] */
9123 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9124         Jim_Obj *const *argv)
9125 {
9126     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9127 }
9128
9129 /* [set] */
9130 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9131         Jim_Obj *const *argv)
9132 {
9133     if (argc != 2 && argc != 3) {
9134         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9135         return JIM_ERR;
9136     }
9137     if (argc == 2) {
9138         Jim_Obj *objPtr;
9139         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9140         if (!objPtr)
9141             return JIM_ERR;
9142         Jim_SetResult(interp, objPtr);
9143         return JIM_OK;
9144     }
9145     /* argc == 3 case. */
9146     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9147         return JIM_ERR;
9148     Jim_SetResult(interp, argv[2]);
9149     return JIM_OK;
9150 }
9151
9152 /* [unset] */
9153 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, 
9154         Jim_Obj *const *argv)
9155 {
9156     int i;
9157
9158     if (argc < 2) {
9159         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9160         return JIM_ERR;
9161     }
9162     for (i = 1; i < argc; i++) {
9163         if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9164             return JIM_ERR;
9165     }
9166     return JIM_OK;
9167 }
9168
9169 /* [incr] */
9170 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, 
9171         Jim_Obj *const *argv)
9172 {
9173     jim_wide wideValue, increment = 1;
9174     Jim_Obj *intObjPtr;
9175
9176     if (argc != 2 && argc != 3) {
9177         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9178         return JIM_ERR;
9179     }
9180     if (argc == 3) {
9181         if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9182             return JIM_ERR;
9183     }
9184     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9185     if (!intObjPtr) return JIM_ERR;
9186     if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9187         return JIM_ERR;
9188     if (Jim_IsShared(intObjPtr)) {
9189         intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9190         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9191             Jim_FreeNewObj(interp, intObjPtr);
9192             return JIM_ERR;
9193         }
9194     } else {
9195         Jim_SetWide(interp, intObjPtr, wideValue+increment);
9196         /* The following step is required in order to invalidate the
9197          * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9198         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9199             return JIM_ERR;
9200         }
9201     }
9202     Jim_SetResult(interp, intObjPtr);
9203     return JIM_OK;
9204 }
9205
9206 /* [while] */
9207 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, 
9208         Jim_Obj *const *argv)
9209 {
9210     if (argc != 3) {
9211         Jim_WrongNumArgs(interp, 1, argv, "condition body");
9212         return JIM_ERR;
9213     }
9214     /* Try to run a specialized version of while if the expression
9215      * is in one of the following forms:
9216      *
9217      *   $a < CONST, $a < $b
9218      *   $a <= CONST, $a <= $b
9219      *   $a > CONST, $a > $b
9220      *   $a >= CONST, $a >= $b
9221      *   $a != CONST, $a != $b
9222      *   $a == CONST, $a == $b
9223      *   $a
9224      *   !$a
9225      *   CONST
9226      */
9227
9228 #ifdef JIM_OPTIMIZATION
9229     {
9230         ExprByteCode *expr;
9231         Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9232         int exprLen, retval;
9233
9234         /* STEP 1 -- Check if there are the conditions to run the specialized
9235          * version of while */
9236         
9237         if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9238         if (expr->len <= 0 || expr->len > 3) goto noopt;
9239         switch(expr->len) {
9240         case 1:
9241             if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9242                 expr->opcode[0] != JIM_EXPROP_NUMBER)
9243                 goto noopt;
9244             break;
9245         case 2:
9246             if (expr->opcode[1] != JIM_EXPROP_NOT ||
9247                 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9248                 goto noopt;
9249             break;
9250         case 3:
9251             if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9252                 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9253                  expr->opcode[1] != JIM_EXPROP_VARIABLE))
9254                 goto noopt;
9255             switch(expr->opcode[2]) {
9256             case JIM_EXPROP_LT:
9257             case JIM_EXPROP_LTE:
9258             case JIM_EXPROP_GT:
9259             case JIM_EXPROP_GTE:
9260             case JIM_EXPROP_NUMEQ:
9261             case JIM_EXPROP_NUMNE:
9262                 /* nothing to do */
9263                 break;
9264             default:
9265                 goto noopt;
9266             }
9267             break;
9268         default:
9269             Jim_Panic(interp,
9270                 "Unexpected default reached in Jim_WhileCoreCommand()");
9271             break;
9272         }
9273
9274         /* STEP 2 -- conditions meet. Initialization. Take different
9275          * branches for different expression lengths. */
9276         exprLen = expr->len;
9277
9278         if (exprLen == 1) {
9279             jim_wide wideValue;
9280
9281             if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9282                 varAObjPtr = expr->obj[0];
9283                 Jim_IncrRefCount(varAObjPtr);
9284             } else {
9285                 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9286                     goto noopt;
9287             }
9288             while (1) {
9289                 if (varAObjPtr) {
9290                     if (!(objPtr =
9291                                Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9292                         Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9293                     {
9294                         Jim_DecrRefCount(interp, varAObjPtr);
9295                         goto noopt;
9296                     }
9297                 }
9298                 if (!wideValue) break;
9299                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9300                     switch(retval) {
9301                     case JIM_BREAK:
9302                         if (varAObjPtr)
9303                             Jim_DecrRefCount(interp, varAObjPtr);
9304                         goto out;
9305                         break;
9306                     case JIM_CONTINUE:
9307                         continue;
9308                         break;
9309                     default:
9310                         if (varAObjPtr)
9311                             Jim_DecrRefCount(interp, varAObjPtr);
9312                         return retval;
9313                     }
9314                 }
9315             }
9316             if (varAObjPtr)
9317                 Jim_DecrRefCount(interp, varAObjPtr);
9318         } else if (exprLen == 3) {
9319             jim_wide wideValueA, wideValueB, cmpRes = 0;
9320             int cmpType = expr->opcode[2];
9321
9322             varAObjPtr = expr->obj[0];
9323             Jim_IncrRefCount(varAObjPtr);
9324             if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9325                 varBObjPtr = expr->obj[1];
9326                 Jim_IncrRefCount(varBObjPtr);
9327             } else {
9328                 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9329                     goto noopt;
9330             }
9331             while (1) {
9332                 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9333                     Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9334                 {
9335                     Jim_DecrRefCount(interp, varAObjPtr);
9336                     if (varBObjPtr)
9337                         Jim_DecrRefCount(interp, varBObjPtr);
9338                     goto noopt;
9339                 }
9340                 if (varBObjPtr) {
9341                     if (!(objPtr =
9342                                Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9343                         Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9344                     {
9345                         Jim_DecrRefCount(interp, varAObjPtr);
9346                         if (varBObjPtr)
9347                             Jim_DecrRefCount(interp, varBObjPtr);
9348                         goto noopt;
9349                     }
9350                 }
9351                 switch(cmpType) {
9352                 case JIM_EXPROP_LT:
9353                     cmpRes = wideValueA < wideValueB; break;
9354                 case JIM_EXPROP_LTE:
9355                     cmpRes = wideValueA <= wideValueB; break;
9356                 case JIM_EXPROP_GT:
9357                     cmpRes = wideValueA > wideValueB; break;
9358                 case JIM_EXPROP_GTE:
9359                     cmpRes = wideValueA >= wideValueB; break;
9360                 case JIM_EXPROP_NUMEQ:
9361                     cmpRes = wideValueA == wideValueB; break;
9362                 case JIM_EXPROP_NUMNE:
9363                     cmpRes = wideValueA != wideValueB; break;
9364                 }
9365                 if (!cmpRes) break;
9366                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9367                     switch(retval) {
9368                     case JIM_BREAK:
9369                         Jim_DecrRefCount(interp, varAObjPtr);
9370                         if (varBObjPtr)
9371                             Jim_DecrRefCount(interp, varBObjPtr);
9372                         goto out;
9373                         break;
9374                     case JIM_CONTINUE:
9375                         continue;
9376                         break;
9377                     default:
9378                         Jim_DecrRefCount(interp, varAObjPtr);
9379                         if (varBObjPtr)
9380                             Jim_DecrRefCount(interp, varBObjPtr);
9381                         return retval;
9382                     }
9383                 }
9384             }
9385             Jim_DecrRefCount(interp, varAObjPtr);
9386             if (varBObjPtr)
9387                 Jim_DecrRefCount(interp, varBObjPtr);
9388         } else {
9389             /* TODO: case for len == 2 */
9390             goto noopt;
9391         }
9392         Jim_SetEmptyResult(interp);
9393         return JIM_OK;
9394     }
9395 noopt:
9396 #endif
9397
9398     /* The general purpose implementation of while starts here */
9399     while (1) {
9400         int boolean, retval;
9401
9402         if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9403                         &boolean)) != JIM_OK)
9404             return retval;
9405         if (!boolean) break;
9406         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9407             switch(retval) {
9408             case JIM_BREAK:
9409                 goto out;
9410                 break;
9411             case JIM_CONTINUE:
9412                 continue;
9413                 break;
9414             default:
9415                 return retval;
9416             }
9417         }
9418     }
9419 out:
9420     Jim_SetEmptyResult(interp);
9421     return JIM_OK;
9422 }
9423
9424 /* [for] */
9425 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, 
9426         Jim_Obj *const *argv)
9427 {
9428     int retval;
9429
9430     if (argc != 5) {
9431         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9432         return JIM_ERR;
9433     }
9434     /* Check if the for is on the form:
9435      *      for {set i CONST} {$i < CONST} {incr i}
9436      *      for {set i CONST} {$i < $j} {incr i}
9437      *      for {set i CONST} {$i <= CONST} {incr i}
9438      *      for {set i CONST} {$i <= $j} {incr i}
9439      * XXX: NOTE: if variable traces are implemented, this optimization
9440      * need to be modified to check for the proc epoch at every variable
9441      * update. */
9442 #ifdef JIM_OPTIMIZATION
9443     {
9444         ScriptObj *initScript, *incrScript;
9445         ExprByteCode *expr;
9446         jim_wide start, stop, currentVal;
9447         unsigned jim_wide procEpoch = interp->procEpoch;
9448         Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9449         int cmpType;
9450         struct Jim_Cmd *cmdPtr;
9451
9452         /* Do it only if there aren't shared arguments */
9453         if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9454             goto evalstart;
9455         initScript = Jim_GetScript(interp, argv[1]);
9456         expr = Jim_GetExpression(interp, argv[2]);
9457         incrScript = Jim_GetScript(interp, argv[3]);
9458
9459         /* Ensure proper lengths to start */
9460         if (initScript->len != 6) goto evalstart;
9461         if (incrScript->len != 4) goto evalstart;
9462         if (expr->len != 3) goto evalstart;
9463         /* Ensure proper token types. */
9464         if (initScript->token[2].type != JIM_TT_ESC ||
9465             initScript->token[4].type != JIM_TT_ESC ||
9466             incrScript->token[2].type != JIM_TT_ESC ||
9467             expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9468             (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9469              expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9470             (expr->opcode[2] != JIM_EXPROP_LT &&
9471              expr->opcode[2] != JIM_EXPROP_LTE))
9472             goto evalstart;
9473         cmpType = expr->opcode[2];
9474         /* Initialization command must be [set] */
9475         cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9476         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9477             goto evalstart;
9478         /* Update command must be incr */
9479         cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9480         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9481             goto evalstart;
9482         /* set, incr, expression must be about the same variable */
9483         if (!Jim_StringEqObj(initScript->token[2].objPtr,
9484                             incrScript->token[2].objPtr, 0))
9485             goto evalstart;
9486         if (!Jim_StringEqObj(initScript->token[2].objPtr,
9487                             expr->obj[0], 0))
9488             goto evalstart;
9489         /* Check that the initialization and comparison are valid integers */
9490         if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9491             goto evalstart;
9492         if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9493             Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9494         {
9495             goto evalstart;
9496         }
9497
9498         /* Initialization */
9499         varNamePtr = expr->obj[0];
9500         if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9501             stopVarNamePtr = expr->obj[1];
9502             Jim_IncrRefCount(stopVarNamePtr);
9503         }
9504         Jim_IncrRefCount(varNamePtr);
9505
9506         /* --- OPTIMIZED FOR --- */
9507         /* Start to loop */
9508         objPtr = Jim_NewIntObj(interp, start);
9509         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9510             Jim_DecrRefCount(interp, varNamePtr);
9511             if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9512             Jim_FreeNewObj(interp, objPtr);
9513             goto evalstart;
9514         }
9515         while (1) {
9516             /* === Check condition === */
9517             /* Common code: */
9518             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9519             if (objPtr == NULL ||
9520                 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9521             {
9522                 Jim_DecrRefCount(interp, varNamePtr);
9523                 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9524                 goto testcond;
9525             }
9526             /* Immediate or Variable? get the 'stop' value if the latter. */
9527             if (stopVarNamePtr) {
9528                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9529                 if (objPtr == NULL ||
9530                     Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9531                 {
9532                     Jim_DecrRefCount(interp, varNamePtr);
9533                     Jim_DecrRefCount(interp, stopVarNamePtr);
9534                     goto testcond;
9535                 }
9536             }
9537             if (cmpType == JIM_EXPROP_LT) {
9538                 if (currentVal >= stop) break;
9539             } else {
9540                 if (currentVal > stop) break;
9541             }
9542             /* Eval body */
9543             if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9544                 switch(retval) {
9545                 case JIM_BREAK:
9546                     if (stopVarNamePtr)
9547                         Jim_DecrRefCount(interp, stopVarNamePtr);
9548                     Jim_DecrRefCount(interp, varNamePtr);
9549                     goto out;
9550                 case JIM_CONTINUE:
9551                     /* nothing to do */
9552                     break;
9553                 default:
9554                     if (stopVarNamePtr)
9555                         Jim_DecrRefCount(interp, stopVarNamePtr);
9556                     Jim_DecrRefCount(interp, varNamePtr);
9557                     return retval;
9558                 }
9559             }
9560             /* If there was a change in procedures/command continue
9561              * with the usual [for] command implementation */
9562             if (procEpoch != interp->procEpoch) {
9563                 if (stopVarNamePtr)
9564                     Jim_DecrRefCount(interp, stopVarNamePtr);
9565                 Jim_DecrRefCount(interp, varNamePtr);
9566                 goto evalnext;
9567             }
9568             /* Increment */
9569             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9570             if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9571                 objPtr->internalRep.wideValue ++;
9572                 Jim_InvalidateStringRep(objPtr);
9573             } else {
9574                 Jim_Obj *auxObjPtr;
9575
9576                 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9577                     if (stopVarNamePtr)
9578                         Jim_DecrRefCount(interp, stopVarNamePtr);
9579                     Jim_DecrRefCount(interp, varNamePtr);
9580                     goto evalnext;
9581                 }
9582                 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9583                 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9584                     if (stopVarNamePtr)
9585                         Jim_DecrRefCount(interp, stopVarNamePtr);
9586                     Jim_DecrRefCount(interp, varNamePtr);
9587                     Jim_FreeNewObj(interp, auxObjPtr);
9588                     goto evalnext;
9589                 }
9590             }
9591         }
9592         if (stopVarNamePtr)
9593             Jim_DecrRefCount(interp, stopVarNamePtr);
9594         Jim_DecrRefCount(interp, varNamePtr);
9595         Jim_SetEmptyResult(interp);
9596         return JIM_OK;
9597     }
9598 #endif
9599 evalstart:
9600     /* Eval start */
9601     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9602         return retval;
9603     while (1) {
9604         int boolean;
9605 testcond:
9606         /* Test the condition */
9607         if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9608                 != JIM_OK)
9609             return retval;
9610         if (!boolean) break;
9611         /* Eval body */
9612         if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9613             switch(retval) {
9614             case JIM_BREAK:
9615                 goto out;
9616                 break;
9617             case JIM_CONTINUE:
9618                 /* Nothing to do */
9619                 break;
9620             default:
9621                 return retval;
9622             }
9623         }
9624 evalnext:
9625         /* Eval next */
9626         if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9627             switch(retval) {
9628             case JIM_BREAK:
9629                 goto out;
9630                 break;
9631             case JIM_CONTINUE:
9632                 continue;
9633                 break;
9634             default:
9635                 return retval;
9636             }
9637         }
9638     }
9639 out:
9640     Jim_SetEmptyResult(interp);
9641     return JIM_OK;
9642 }
9643
9644 /* foreach + lmap implementation. */
9645 static int JimForeachMapHelper(Jim_Interp *interp, int argc, 
9646         Jim_Obj *const *argv, int doMap)
9647 {
9648     int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9649     int nbrOfLoops = 0;
9650     Jim_Obj *emptyStr, *script, *mapRes = NULL;
9651
9652     if (argc < 4 || argc % 2 != 0) {
9653         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9654         return JIM_ERR;
9655     }
9656     if (doMap) {
9657         mapRes = Jim_NewListObj(interp, NULL, 0);
9658         Jim_IncrRefCount(mapRes);
9659     }
9660     emptyStr = Jim_NewEmptyStringObj(interp);
9661     Jim_IncrRefCount(emptyStr);
9662     script = argv[argc-1];            /* Last argument is a script */
9663     nbrOfLists = (argc - 1 - 1) / 2;  /* argc - 'foreach' - script */
9664     listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9665     listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9666     /* Initialize iterators and remember max nbr elements each list */
9667     memset(listsIdx, 0, nbrOfLists * sizeof(int));
9668     /* Remember lengths of all lists and calculate how much rounds to loop */
9669     for (i=0; i < nbrOfLists*2; i += 2) {
9670         div_t cnt;
9671         int count;
9672         Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9673         Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9674         if (listsEnd[i] == 0) {
9675             Jim_SetResultString(interp, "foreach varlist is empty", -1);
9676             goto err;
9677         }
9678         cnt = div(listsEnd[i+1], listsEnd[i]);
9679         count = cnt.quot + (cnt.rem ? 1 : 0);
9680         if (count > nbrOfLoops)
9681             nbrOfLoops = count;
9682     }
9683     for (; nbrOfLoops-- > 0; ) {
9684         for (i=0; i < nbrOfLists; ++i) {
9685             int varIdx = 0, var = i * 2;
9686             while (varIdx < listsEnd[var]) {
9687                 Jim_Obj *varName, *ele;
9688                 int lst = i * 2 + 1;
9689                 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9690                         != JIM_OK)
9691                         goto err;
9692                 if (listsIdx[i] < listsEnd[lst]) {
9693                     if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9694                         != JIM_OK)
9695                         goto err;
9696                     if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9697                         Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9698                         goto err;
9699                     }
9700                     ++listsIdx[i];  /* Remember next iterator of current list */ 
9701                 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9702                     Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9703                     goto err;
9704                 }
9705                 ++varIdx;  /* Next variable */
9706             }
9707         }
9708         switch (result = Jim_EvalObj(interp, script)) {
9709             case JIM_OK:
9710                 if (doMap)
9711                     Jim_ListAppendElement(interp, mapRes, interp->result);
9712                 break;
9713             case JIM_CONTINUE:
9714                 break;
9715             case JIM_BREAK:
9716                 goto out;
9717                 break;
9718             default:
9719                 goto err;
9720         }
9721     }
9722 out:
9723     result = JIM_OK;
9724     if (doMap)
9725         Jim_SetResult(interp, mapRes);
9726     else
9727         Jim_SetEmptyResult(interp);
9728 err:
9729     if (doMap)
9730         Jim_DecrRefCount(interp, mapRes);
9731     Jim_DecrRefCount(interp, emptyStr);
9732     Jim_Free(listsIdx);
9733     Jim_Free(listsEnd);
9734     return result;
9735 }
9736
9737 /* [foreach] */
9738 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, 
9739         Jim_Obj *const *argv)
9740 {
9741     return JimForeachMapHelper(interp, argc, argv, 0);
9742 }
9743
9744 /* [lmap] */
9745 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, 
9746         Jim_Obj *const *argv)
9747 {
9748     return JimForeachMapHelper(interp, argc, argv, 1);
9749 }
9750
9751 /* [if] */
9752 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, 
9753         Jim_Obj *const *argv)
9754 {
9755     int boolean, retval, current = 1, falsebody = 0;
9756     if (argc >= 3) {
9757         while (1) {
9758             /* Far not enough arguments given! */
9759             if (current >= argc) goto err;
9760             if ((retval = Jim_GetBoolFromExpr(interp,
9761                         argv[current++], &boolean))
9762                     != JIM_OK)
9763                 return retval;
9764             /* There lacks something, isn't it? */
9765             if (current >= argc) goto err;
9766             if (Jim_CompareStringImmediate(interp, argv[current],
9767                         "then")) current++;
9768             /* Tsk tsk, no then-clause? */
9769             if (current >= argc) goto err;
9770             if (boolean)
9771                 return Jim_EvalObj(interp, argv[current]);
9772              /* Ok: no else-clause follows */
9773             if (++current >= argc) return JIM_OK;
9774             falsebody = current++;
9775             if (Jim_CompareStringImmediate(interp, argv[falsebody],
9776                         "else")) {
9777                 /* IIICKS - else-clause isn't last cmd? */
9778                 if (current != argc-1) goto err;
9779                 return Jim_EvalObj(interp, argv[current]);
9780             } else if (Jim_CompareStringImmediate(interp,
9781                         argv[falsebody], "elseif"))
9782                 /* Ok: elseif follows meaning all the stuff
9783                  * again (how boring...) */
9784                 continue;
9785             /* OOPS - else-clause is not last cmd?*/
9786             else if (falsebody != argc-1)
9787                 goto err;
9788             return Jim_EvalObj(interp, argv[falsebody]);
9789         }
9790         return JIM_OK;
9791     }
9792 err:
9793     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
9794     return JIM_ERR;
9795 }
9796
9797 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
9798
9799 /* [switch] */
9800 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, 
9801         Jim_Obj *const *argv)
9802 {
9803     int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
9804     Jim_Obj *command = 0, *const *caseList = 0, *strObj;
9805     Jim_Obj *script = 0;
9806     if (argc < 3) goto wrongnumargs;
9807     for (opt=1; opt < argc; ++opt) {
9808         const char *option = Jim_GetString(argv[opt], 0);
9809         if (*option != '-') break;
9810         else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
9811         else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
9812         else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
9813         else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
9814         else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
9815             if ((argc - opt) < 2) goto wrongnumargs;
9816             command = argv[++opt]; 
9817         } else {
9818             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9819             Jim_AppendStrings(interp, Jim_GetResult(interp),
9820                 "bad option \"", option, "\": must be -exact, -glob, "
9821                 "-regexp, -command procname or --", 0);
9822             goto err;            
9823         }
9824         if ((argc - opt) < 2) goto wrongnumargs;
9825     }
9826     strObj = argv[opt++];
9827     patCount = argc - opt;
9828     if (patCount == 1) {
9829         Jim_Obj **vector;
9830         JimListGetElements(interp, argv[opt], &patCount, &vector);
9831         caseList = vector;
9832     } else
9833         caseList = &argv[opt];
9834     if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
9835     for (i=0; script == 0 && i < patCount; i += 2) {
9836         Jim_Obj *patObj = caseList[i];
9837         if (!Jim_CompareStringImmediate(interp, patObj, "default")
9838             || i < (patCount-2)) {
9839             switch (matchOpt) {
9840                 case SWITCH_EXACT:
9841                     if (Jim_StringEqObj(strObj, patObj, 0))
9842                         script = caseList[i+1];
9843                     break;
9844                 case SWITCH_GLOB:
9845                     if (Jim_StringMatchObj(patObj, strObj, 0))
9846                         script = caseList[i+1];
9847                     break;
9848                 case SWITCH_RE:
9849                     command = Jim_NewStringObj(interp, "regexp", -1);
9850                     /* Fall thru intentionally */
9851                 case SWITCH_CMD: {
9852                     Jim_Obj *parms[] = {command, patObj, strObj};
9853                     int rc = Jim_EvalObjVector(interp, 3, parms);
9854                     long matching;
9855                     /* After the execution of a command we need to
9856                      * make sure to reconvert the object into a list
9857                      * again. Only for the single-list style [switch]. */
9858                     if (argc-opt == 1) {
9859                         Jim_Obj **vector;
9860                         JimListGetElements(interp, argv[opt], &patCount,
9861                                 &vector);
9862                         caseList = vector;
9863                     }
9864                     /* command is here already decref'd */
9865                     if (rc != JIM_OK) {
9866                         retcode = rc;
9867                         goto err;
9868                     }
9869                     rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
9870                     if (rc != JIM_OK) {
9871                         retcode = rc;
9872                         goto err;
9873                     }
9874                     if (matching)
9875                         script = caseList[i+1];
9876                     break;
9877                 }
9878                 default:
9879                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9880                     Jim_AppendStrings(interp, Jim_GetResult(interp),
9881                         "internal error: no such option implemented", 0);
9882                     goto err;
9883             }
9884         } else {
9885           script = caseList[i+1];
9886         }
9887     }
9888     for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
9889         i += 2)
9890         script = caseList[i+1];
9891     if (script && Jim_CompareStringImmediate(interp, script, "-")) {
9892         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9893         Jim_AppendStrings(interp, Jim_GetResult(interp),
9894             "no body specified for pattern \"",
9895             Jim_GetString(caseList[i-2], 0), "\"", 0);
9896         goto err;
9897     }
9898     retcode = JIM_OK;
9899     Jim_SetEmptyResult(interp);
9900     if (script != 0)
9901         retcode = Jim_EvalObj(interp, script);
9902     return retcode;
9903 wrongnumargs:
9904     Jim_WrongNumArgs(interp, 1, argv, "?options? string "
9905         "pattern body ... ?default body?   or   "
9906         "{pattern body ?pattern body ...?}");
9907 err:
9908     return retcode;        
9909 }
9910
9911 /* [list] */
9912 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, 
9913         Jim_Obj *const *argv)
9914 {
9915     Jim_Obj *listObjPtr;
9916
9917     listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
9918     Jim_SetResult(interp, listObjPtr);
9919     return JIM_OK;
9920 }
9921
9922 /* [lindex] */
9923 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, 
9924         Jim_Obj *const *argv)
9925 {
9926     Jim_Obj *objPtr, *listObjPtr;
9927     int i;
9928     int index;
9929
9930     if (argc < 3) {
9931         Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
9932         return JIM_ERR;
9933     }
9934     objPtr = argv[1];
9935     Jim_IncrRefCount(objPtr);
9936     for (i = 2; i < argc; i++) {
9937         listObjPtr = objPtr;
9938         if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
9939             Jim_DecrRefCount(interp, listObjPtr);
9940             return JIM_ERR;
9941         }
9942         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
9943                     JIM_NONE) != JIM_OK) {
9944             /* Returns an empty object if the index
9945              * is out of range. */
9946             Jim_DecrRefCount(interp, listObjPtr);
9947             Jim_SetEmptyResult(interp);
9948             return JIM_OK;
9949         }
9950         Jim_IncrRefCount(objPtr);
9951         Jim_DecrRefCount(interp, listObjPtr);
9952     }
9953     Jim_SetResult(interp, objPtr);
9954     Jim_DecrRefCount(interp, objPtr);
9955     return JIM_OK;
9956 }
9957
9958 /* [llength] */
9959 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, 
9960         Jim_Obj *const *argv)
9961 {
9962     int len;
9963
9964     if (argc != 2) {
9965         Jim_WrongNumArgs(interp, 1, argv, "list");
9966         return JIM_ERR;
9967     }
9968     Jim_ListLength(interp, argv[1], &len);
9969     Jim_SetResult(interp, Jim_NewIntObj(interp, len));
9970     return JIM_OK;
9971 }
9972
9973 /* [lappend] */
9974 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, 
9975         Jim_Obj *const *argv)
9976 {
9977     Jim_Obj *listObjPtr;
9978     int shared, i;
9979
9980     if (argc < 2) {
9981         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
9982         return JIM_ERR;
9983     }
9984     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
9985     if (!listObjPtr) {
9986         /* Create the list if it does not exists */
9987         listObjPtr = Jim_NewListObj(interp, NULL, 0);
9988         if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
9989             Jim_FreeNewObj(interp, listObjPtr);
9990             return JIM_ERR;
9991         }
9992     }
9993     shared = Jim_IsShared(listObjPtr);
9994     if (shared)
9995         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
9996     for (i = 2; i < argc; i++)
9997         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
9998     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
9999         if (shared)
10000             Jim_FreeNewObj(interp, listObjPtr);
10001         return JIM_ERR;
10002     }
10003     Jim_SetResult(interp, listObjPtr);
10004     return JIM_OK;
10005 }
10006
10007 /* [linsert] */
10008 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, 
10009         Jim_Obj *const *argv)
10010 {
10011     int index, len;
10012     Jim_Obj *listPtr;
10013
10014     if (argc < 4) {
10015         Jim_WrongNumArgs(interp, 1, argv, "list index element "
10016             "?element ...?");
10017         return JIM_ERR;
10018     }
10019     listPtr = argv[1];
10020     if (Jim_IsShared(listPtr))
10021         listPtr = Jim_DuplicateObj(interp, listPtr);
10022     if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10023         goto err;
10024     Jim_ListLength(interp, listPtr, &len);
10025     if (index >= len)
10026         index = len;
10027     else if (index < 0)
10028         index = len + index + 1;
10029     Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10030     Jim_SetResult(interp, listPtr);
10031     return JIM_OK;
10032 err:
10033     if (listPtr != argv[1]) {
10034         Jim_FreeNewObj(interp, listPtr);
10035     }
10036     return JIM_ERR;
10037 }
10038
10039 /* [lset] */
10040 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, 
10041         Jim_Obj *const *argv)
10042 {
10043     if (argc < 3) {
10044         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10045         return JIM_ERR;
10046     } else if (argc == 3) {
10047         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10048             return JIM_ERR;
10049         Jim_SetResult(interp, argv[2]);
10050         return JIM_OK;
10051     }
10052     if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10053             == JIM_ERR) return JIM_ERR;
10054     return JIM_OK;
10055 }
10056
10057 /* [lsort] */
10058 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10059 {
10060     const char *options[] = {
10061         "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10062     };
10063     enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10064     Jim_Obj *resObj;
10065     int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10066     int decreasing = 0;
10067
10068     if (argc < 2) {
10069         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10070         return JIM_ERR;
10071     }
10072     for (i = 1; i < (argc-1); i++) {
10073         int option;
10074
10075         if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10076                 != JIM_OK)
10077             return JIM_ERR;
10078         switch(option) {
10079         case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10080         case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10081         case OPT_INCREASING: decreasing = 0; break;
10082         case OPT_DECREASING: decreasing = 1; break;
10083         }
10084     }
10085     if (decreasing) {
10086         switch(lsortType) {
10087         case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10088         case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10089         }
10090     }
10091     resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10092     ListSortElements(interp, resObj, lsortType);
10093     Jim_SetResult(interp, resObj);
10094     return JIM_OK;
10095 }
10096
10097 /* [append] */
10098 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, 
10099         Jim_Obj *const *argv)
10100 {
10101     Jim_Obj *stringObjPtr;
10102     int shared, i;
10103
10104     if (argc < 2) {
10105         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10106         return JIM_ERR;
10107     }
10108     if (argc == 2) {
10109         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10110         if (!stringObjPtr) return JIM_ERR;
10111     } else {
10112         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10113         if (!stringObjPtr) {
10114             /* Create the string if it does not exists */
10115             stringObjPtr = Jim_NewEmptyStringObj(interp);
10116             if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10117                     != JIM_OK) {
10118                 Jim_FreeNewObj(interp, stringObjPtr);
10119                 return JIM_ERR;
10120             }
10121         }
10122     }
10123     shared = Jim_IsShared(stringObjPtr);
10124     if (shared)
10125         stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10126     for (i = 2; i < argc; i++)
10127         Jim_AppendObj(interp, stringObjPtr, argv[i]);
10128     if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10129         if (shared)
10130             Jim_FreeNewObj(interp, stringObjPtr);
10131         return JIM_ERR;
10132     }
10133     Jim_SetResult(interp, stringObjPtr);
10134     return JIM_OK;
10135 }
10136
10137 /* [debug] */
10138 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, 
10139         Jim_Obj *const *argv)
10140 {
10141     const char *options[] = {
10142         "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10143         "exprbc",
10144         NULL
10145     };
10146     enum {
10147         OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10148         OPT_EXPRLEN, OPT_EXPRBC
10149     };
10150     int option;
10151
10152     if (argc < 2) {
10153         Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10154         return JIM_ERR;
10155     }
10156     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10157                 JIM_ERRMSG) != JIM_OK)
10158         return JIM_ERR;
10159     if (option == OPT_REFCOUNT) {
10160         if (argc != 3) {
10161             Jim_WrongNumArgs(interp, 2, argv, "object");
10162             return JIM_ERR;
10163         }
10164         Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10165         return JIM_OK;
10166     } else if (option == OPT_OBJCOUNT) {
10167         int freeobj = 0, liveobj = 0;
10168         char buf[256];
10169         Jim_Obj *objPtr;
10170
10171         if (argc != 2) {
10172             Jim_WrongNumArgs(interp, 2, argv, "");
10173             return JIM_ERR;
10174         }
10175         /* Count the number of free objects. */
10176         objPtr = interp->freeList;
10177         while (objPtr) {
10178             freeobj++;
10179             objPtr = objPtr->nextObjPtr;
10180         }
10181         /* Count the number of live objects. */
10182         objPtr = interp->liveList;
10183         while (objPtr) {
10184             liveobj++;
10185             objPtr = objPtr->nextObjPtr;
10186         }
10187         /* Set the result string and return. */
10188         sprintf(buf, "free %d used %d", freeobj, liveobj);
10189         Jim_SetResultString(interp, buf, -1);
10190         return JIM_OK;
10191     } else if (option == OPT_OBJECTS) {
10192         Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10193         /* Count the number of live objects. */
10194         objPtr = interp->liveList;
10195         listObjPtr = Jim_NewListObj(interp, NULL, 0);
10196         while (objPtr) {
10197             char buf[128];
10198             const char *type = objPtr->typePtr ?
10199                 objPtr->typePtr->name : "";
10200             subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10201             sprintf(buf, "%p", objPtr);
10202             Jim_ListAppendElement(interp, subListObjPtr,
10203                 Jim_NewStringObj(interp, buf, -1));
10204             Jim_ListAppendElement(interp, subListObjPtr,
10205                 Jim_NewStringObj(interp, type, -1));
10206             Jim_ListAppendElement(interp, subListObjPtr,
10207                 Jim_NewIntObj(interp, objPtr->refCount));
10208             Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10209             Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10210             objPtr = objPtr->nextObjPtr;
10211         }
10212         Jim_SetResult(interp, listObjPtr);
10213         return JIM_OK;
10214     } else if (option == OPT_INVSTR) {
10215         Jim_Obj *objPtr;
10216
10217         if (argc != 3) {
10218             Jim_WrongNumArgs(interp, 2, argv, "object");
10219             return JIM_ERR;
10220         }
10221         objPtr = argv[2];
10222         if (objPtr->typePtr != NULL)
10223             Jim_InvalidateStringRep(objPtr);
10224         Jim_SetEmptyResult(interp);
10225         return JIM_OK;
10226     } else if (option == OPT_SCRIPTLEN) {
10227         ScriptObj *script;
10228         if (argc != 3) {
10229             Jim_WrongNumArgs(interp, 2, argv, "script");
10230             return JIM_ERR;
10231         }
10232         script = Jim_GetScript(interp, argv[2]);
10233         Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10234         return JIM_OK;
10235     } else if (option == OPT_EXPRLEN) {
10236         ExprByteCode *expr;
10237         if (argc != 3) {
10238             Jim_WrongNumArgs(interp, 2, argv, "expression");
10239             return JIM_ERR;
10240         }
10241         expr = Jim_GetExpression(interp, argv[2]);
10242         if (expr == NULL)
10243             return JIM_ERR;
10244         Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10245         return JIM_OK;
10246     } else if (option == OPT_EXPRBC) {
10247         Jim_Obj *objPtr;
10248         ExprByteCode *expr;
10249         int i;
10250
10251         if (argc != 3) {
10252             Jim_WrongNumArgs(interp, 2, argv, "expression");
10253             return JIM_ERR;
10254         }
10255         expr = Jim_GetExpression(interp, argv[2]);
10256         if (expr == NULL)
10257             return JIM_ERR;
10258         objPtr = Jim_NewListObj(interp, NULL, 0);
10259         for (i = 0; i < expr->len; i++) {
10260             const char *type;
10261             Jim_ExprOperator *op;
10262
10263             switch(expr->opcode[i]) {
10264             case JIM_EXPROP_NUMBER: type = "number"; break;
10265             case JIM_EXPROP_COMMAND: type = "command"; break;
10266             case JIM_EXPROP_VARIABLE: type = "variable"; break;
10267             case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10268             case JIM_EXPROP_SUBST: type = "subst"; break;
10269             case JIM_EXPROP_STRING: type = "string"; break;
10270             default:
10271                 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10272                 if (op == NULL) {
10273                     type = "private";
10274                 } else {
10275                     type = "operator";
10276                 }
10277                 break;
10278             }
10279             Jim_ListAppendElement(interp, objPtr,
10280                     Jim_NewStringObj(interp, type, -1));
10281             Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10282         }
10283         Jim_SetResult(interp, objPtr);
10284         return JIM_OK;
10285     } else {
10286         Jim_SetResultString(interp,
10287             "bad option. Valid options are refcount, "
10288             "objcount, objects, invstr", -1);
10289         return JIM_ERR;
10290     }
10291     return JIM_OK; /* unreached */
10292 }
10293
10294 /* [eval] */
10295 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, 
10296         Jim_Obj *const *argv)
10297 {
10298     if (argc == 2) {
10299         return Jim_EvalObj(interp, argv[1]);
10300     } else if (argc > 2) {
10301         Jim_Obj *objPtr;
10302         int retcode;
10303
10304         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10305         Jim_IncrRefCount(objPtr);
10306         retcode = Jim_EvalObj(interp, objPtr);
10307         Jim_DecrRefCount(interp, objPtr);
10308         return retcode;
10309     } else {
10310         Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10311         return JIM_ERR;
10312     }
10313 }
10314
10315 /* [uplevel] */
10316 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, 
10317         Jim_Obj *const *argv)
10318 {
10319     if (argc >= 2) {
10320         int retcode, newLevel, oldLevel;
10321         Jim_CallFrame *savedCallFrame, *targetCallFrame;
10322         Jim_Obj *objPtr;
10323         const char *str;
10324
10325         /* Save the old callframe pointer */
10326         savedCallFrame = interp->framePtr;
10327
10328         /* Lookup the target frame pointer */
10329         str = Jim_GetString(argv[1], NULL);
10330         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10331         {
10332             if (Jim_GetCallFrameByLevel(interp, argv[1],
10333                         &targetCallFrame,
10334                         &newLevel) != JIM_OK)
10335                 return JIM_ERR;
10336             argc--;
10337             argv++;
10338         } else {
10339             if (Jim_GetCallFrameByLevel(interp, NULL,
10340                         &targetCallFrame,
10341                         &newLevel) != JIM_OK)
10342                 return JIM_ERR;
10343         }
10344         if (argc < 2) {
10345             argc++;
10346             argv--;
10347             Jim_WrongNumArgs(interp, 1, argv,
10348                     "?level? command ?arg ...?");
10349             return JIM_ERR;
10350         }
10351         /* Eval the code in the target callframe. */
10352         interp->framePtr = targetCallFrame;
10353         oldLevel = interp->numLevels;
10354         interp->numLevels = newLevel;
10355         if (argc == 2) {
10356             retcode = Jim_EvalObj(interp, argv[1]);
10357         } else {
10358             objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10359             Jim_IncrRefCount(objPtr);
10360             retcode = Jim_EvalObj(interp, objPtr);
10361             Jim_DecrRefCount(interp, objPtr);
10362         }
10363         interp->numLevels = oldLevel;
10364         interp->framePtr = savedCallFrame;
10365         return retcode;
10366     } else {
10367         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10368         return JIM_ERR;
10369     }
10370 }
10371
10372 /* [expr] */
10373 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, 
10374         Jim_Obj *const *argv)
10375 {
10376     Jim_Obj *exprResultPtr;
10377     int retcode;
10378
10379     if (argc == 2) {
10380         retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10381     } else if (argc > 2) {
10382         Jim_Obj *objPtr;
10383
10384         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10385         Jim_IncrRefCount(objPtr);
10386         retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10387         Jim_DecrRefCount(interp, objPtr);
10388     } else {
10389         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10390         return JIM_ERR;
10391     }
10392     if (retcode != JIM_OK) return retcode;
10393     Jim_SetResult(interp, exprResultPtr);
10394     Jim_DecrRefCount(interp, exprResultPtr);
10395     return JIM_OK;
10396 }
10397
10398 /* [break] */
10399 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, 
10400         Jim_Obj *const *argv)
10401 {
10402     if (argc != 1) {
10403         Jim_WrongNumArgs(interp, 1, argv, "");
10404         return JIM_ERR;
10405     }
10406     return JIM_BREAK;
10407 }
10408
10409 /* [continue] */
10410 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10411         Jim_Obj *const *argv)
10412 {
10413     if (argc != 1) {
10414         Jim_WrongNumArgs(interp, 1, argv, "");
10415         return JIM_ERR;
10416     }
10417     return JIM_CONTINUE;
10418 }
10419
10420 /* [return] */
10421 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, 
10422         Jim_Obj *const *argv)
10423 {
10424     if (argc == 1) {
10425         return JIM_RETURN;
10426     } else if (argc == 2) {
10427         Jim_SetResult(interp, argv[1]);
10428         interp->returnCode = JIM_OK;
10429         return JIM_RETURN;
10430     } else if (argc == 3 || argc == 4) {
10431         int returnCode;
10432         if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10433             return JIM_ERR;
10434         interp->returnCode = returnCode;
10435         if (argc == 4)
10436             Jim_SetResult(interp, argv[3]);
10437         return JIM_RETURN;
10438     } else {
10439         Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10440         return JIM_ERR;
10441     }
10442     return JIM_RETURN; /* unreached */
10443 }
10444
10445 /* [tailcall] */
10446 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10447         Jim_Obj *const *argv)
10448 {
10449     Jim_Obj *objPtr;
10450
10451     objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10452     Jim_SetResult(interp, objPtr);
10453     return JIM_EVAL;
10454 }
10455
10456 /* [proc] */
10457 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, 
10458         Jim_Obj *const *argv)
10459 {
10460     int argListLen;
10461     int arityMin, arityMax;
10462
10463     if (argc != 4 && argc != 5) {
10464         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10465         return JIM_ERR;
10466     }
10467     Jim_ListLength(interp, argv[2], &argListLen);
10468     arityMin = arityMax = argListLen+1;
10469     if (argListLen) {
10470         const char *str;
10471         int len;
10472         Jim_Obj *lastArgPtr;
10473         
10474         Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10475         str = Jim_GetString(lastArgPtr, &len);
10476         if (len == 4 && memcmp(str, "args", 4) == 0) {
10477             arityMin--;
10478             arityMax = -1;
10479         }
10480     }
10481     if (argc == 4) {
10482         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10483                 argv[2], NULL, argv[3], arityMin, arityMax);
10484     } else {
10485         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10486                 argv[2], argv[3], argv[4], arityMin, arityMax);
10487     }
10488 }
10489
10490 /* [concat] */
10491 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, 
10492         Jim_Obj *const *argv)
10493 {
10494     Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10495     return JIM_OK;
10496 }
10497
10498 /* [upvar] */
10499 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, 
10500         Jim_Obj *const *argv)
10501 {
10502     const char *str;
10503     int i;
10504     Jim_CallFrame *targetCallFrame;
10505
10506     /* Lookup the target frame pointer */
10507     str = Jim_GetString(argv[1], NULL);
10508     if (argc > 3 && 
10509         ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10510     {
10511         if (Jim_GetCallFrameByLevel(interp, argv[1],
10512                     &targetCallFrame, NULL) != JIM_OK)
10513             return JIM_ERR;
10514         argc--;
10515         argv++;
10516     } else {
10517         if (Jim_GetCallFrameByLevel(interp, NULL,
10518                     &targetCallFrame, NULL) != JIM_OK)
10519             return JIM_ERR;
10520     }
10521     /* Check for arity */
10522     if (argc < 3 || ((argc-1)%2) != 0) {
10523         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10524         return JIM_ERR;
10525     }
10526     /* Now... for every other/local couple: */
10527     for (i = 1; i < argc; i += 2) {
10528         if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10529                 targetCallFrame) != JIM_OK) return JIM_ERR;
10530     }
10531     return JIM_OK;
10532 }
10533
10534 /* [global] */
10535 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, 
10536         Jim_Obj *const *argv)
10537 {
10538     int i;
10539
10540     if (argc < 2) {
10541         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10542         return JIM_ERR;
10543     }
10544     /* Link every var to the toplevel having the same name */
10545     if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10546     for (i = 1; i < argc; i++) {
10547         if (Jim_SetVariableLink(interp, argv[i], argv[i],
10548                 interp->topFramePtr) != JIM_OK) return JIM_ERR;
10549     }
10550     return JIM_OK;
10551 }
10552
10553 /* does the [string map] operation. On error NULL is returned,
10554  * otherwise a new string object with the result, having refcount = 0,
10555  * is returned. */
10556 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10557         Jim_Obj *objPtr, int nocase)
10558 {
10559     int numMaps;
10560     const char **key, *str, *noMatchStart = NULL;
10561     Jim_Obj **value;
10562     int *keyLen, strLen, i;
10563     Jim_Obj *resultObjPtr;
10564     
10565     Jim_ListLength(interp, mapListObjPtr, &numMaps);
10566     if (numMaps % 2) {
10567         Jim_SetResultString(interp,
10568                 "list must contain an even number of elements", -1);
10569         return NULL;
10570     }
10571     /* Initialization */
10572     numMaps /= 2;
10573     key = Jim_Alloc(sizeof(char*)*numMaps);
10574     keyLen = Jim_Alloc(sizeof(int)*numMaps);
10575     value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10576     resultObjPtr = Jim_NewStringObj(interp, "", 0);
10577     for (i = 0; i < numMaps; i++) {
10578         Jim_Obj *eleObjPtr;
10579
10580         Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10581         key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10582         Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10583         value[i] = eleObjPtr;
10584     }
10585     str = Jim_GetString(objPtr, &strLen);
10586     /* Map it */
10587     while(strLen) {
10588         for (i = 0; i < numMaps; i++) {
10589             if (strLen >= keyLen[i] && keyLen[i]) {
10590                 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10591                             nocase))
10592                 {
10593                     if (noMatchStart) {
10594                         Jim_AppendString(interp, resultObjPtr,
10595                                 noMatchStart, str-noMatchStart);
10596                         noMatchStart = NULL;
10597                     }
10598                     Jim_AppendObj(interp, resultObjPtr, value[i]);
10599                     str += keyLen[i];
10600                     strLen -= keyLen[i];
10601                     break;
10602                 }
10603             }
10604         }
10605         if (i == numMaps) { /* no match */
10606             if (noMatchStart == NULL)
10607                 noMatchStart = str;
10608             str ++;
10609             strLen --;
10610         }
10611     }
10612     if (noMatchStart) {
10613         Jim_AppendString(interp, resultObjPtr,
10614             noMatchStart, str-noMatchStart);
10615     }
10616     Jim_Free((void*)key);
10617     Jim_Free(keyLen);
10618     Jim_Free(value);
10619     return resultObjPtr;
10620 }
10621
10622 /* [string] */
10623 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, 
10624         Jim_Obj *const *argv)
10625 {
10626     int option;
10627     const char *options[] = {
10628         "length", "compare", "match", "equal", "range", "map", "repeat",
10629         "index", "first", "tolower", "toupper", NULL
10630     };
10631     enum {
10632         OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10633         OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10634     };
10635
10636     if (argc < 2) {
10637         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10638         return JIM_ERR;
10639     }
10640     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10641                 JIM_ERRMSG) != JIM_OK)
10642         return JIM_ERR;
10643
10644     if (option == OPT_LENGTH) {
10645         int len;
10646
10647         if (argc != 3) {
10648             Jim_WrongNumArgs(interp, 2, argv, "string");
10649             return JIM_ERR;
10650         }
10651         Jim_GetString(argv[2], &len);
10652         Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10653         return JIM_OK;
10654     } else if (option == OPT_COMPARE) {
10655         int nocase = 0;
10656         if ((argc != 4 && argc != 5) ||
10657             (argc == 5 && Jim_CompareStringImmediate(interp,
10658                 argv[2], "-nocase") == 0)) {
10659             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10660             return JIM_ERR;
10661         }
10662         if (argc == 5) {
10663             nocase = 1;
10664             argv++;
10665         }
10666         Jim_SetResult(interp, Jim_NewIntObj(interp,
10667                     Jim_StringCompareObj(argv[2],
10668                             argv[3], nocase)));
10669         return JIM_OK;
10670     } else if (option == OPT_MATCH) {
10671         int nocase = 0;
10672         if ((argc != 4 && argc != 5) ||
10673             (argc == 5 && Jim_CompareStringImmediate(interp,
10674                 argv[2], "-nocase") == 0)) {
10675             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10676                     "string");
10677             return JIM_ERR;
10678         }
10679         if (argc == 5) {
10680             nocase = 1;
10681             argv++;
10682         }
10683         Jim_SetResult(interp,
10684             Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10685                     argv[3], nocase)));
10686         return JIM_OK;
10687     } else if (option == OPT_EQUAL) {
10688         if (argc != 4) {
10689             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10690             return JIM_ERR;
10691         }
10692         Jim_SetResult(interp,
10693             Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10694                     argv[3], 0)));
10695         return JIM_OK;
10696     } else if (option == OPT_RANGE) {
10697         Jim_Obj *objPtr;
10698
10699         if (argc != 5) {
10700             Jim_WrongNumArgs(interp, 2, argv, "string first last");
10701             return JIM_ERR;
10702         }
10703         objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10704         if (objPtr == NULL)
10705             return JIM_ERR;
10706         Jim_SetResult(interp, objPtr);
10707         return JIM_OK;
10708     } else if (option == OPT_MAP) {
10709         int nocase = 0;
10710         Jim_Obj *objPtr;
10711
10712         if ((argc != 4 && argc != 5) ||
10713             (argc == 5 && Jim_CompareStringImmediate(interp,
10714                 argv[2], "-nocase") == 0)) {
10715             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10716                     "string");
10717             return JIM_ERR;
10718         }
10719         if (argc == 5) {
10720             nocase = 1;
10721             argv++;
10722         }
10723         objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10724         if (objPtr == NULL)
10725             return JIM_ERR;
10726         Jim_SetResult(interp, objPtr);
10727         return JIM_OK;
10728     } else if (option == OPT_REPEAT) {
10729         Jim_Obj *objPtr;
10730         jim_wide count;
10731
10732         if (argc != 4) {
10733             Jim_WrongNumArgs(interp, 2, argv, "string count");
10734             return JIM_ERR;
10735         }
10736         if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
10737             return JIM_ERR;
10738         objPtr = Jim_NewStringObj(interp, "", 0);
10739         while (count--) {
10740             Jim_AppendObj(interp, objPtr, argv[2]);
10741         }
10742         Jim_SetResult(interp, objPtr);
10743         return JIM_OK;
10744     } else if (option == OPT_INDEX) {
10745         int index, len;
10746         const char *str;
10747
10748         if (argc != 4) {
10749             Jim_WrongNumArgs(interp, 2, argv, "string index");
10750             return JIM_ERR;
10751         }
10752         if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
10753             return JIM_ERR;
10754         str = Jim_GetString(argv[2], &len);
10755         if (index != INT_MIN && index != INT_MAX)
10756             index = JimRelToAbsIndex(len, index);
10757         if (index < 0 || index >= len) {
10758             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10759             return JIM_OK;
10760         } else {
10761             Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
10762             return JIM_OK;
10763         }
10764     } else if (option == OPT_FIRST) {
10765         int index = 0, l1, l2;
10766         const char *s1, *s2;
10767
10768         if (argc != 4 && argc != 5) {
10769             Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
10770             return JIM_ERR;
10771         }
10772         s1 = Jim_GetString(argv[2], &l1);
10773         s2 = Jim_GetString(argv[3], &l2);
10774         if (argc == 5) {
10775             if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
10776                 return JIM_ERR;
10777             index = JimRelToAbsIndex(l2, index);
10778         }
10779         Jim_SetResult(interp, Jim_NewIntObj(interp,
10780                     JimStringFirst(s1, l1, s2, l2, index)));
10781         return JIM_OK;
10782     } else if (option == OPT_TOLOWER) {
10783         if (argc != 3) {
10784             Jim_WrongNumArgs(interp, 2, argv, "string");
10785             return JIM_ERR;
10786         }
10787         Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
10788     } else if (option == OPT_TOUPPER) {
10789         if (argc != 3) {
10790             Jim_WrongNumArgs(interp, 2, argv, "string");
10791             return JIM_ERR;
10792         }
10793         Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
10794     }
10795     return JIM_OK;
10796 }
10797
10798 /* [time] */
10799 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, 
10800         Jim_Obj *const *argv)
10801 {
10802     long i, count = 1;
10803     jim_wide start, elapsed;
10804     char buf [256];
10805     const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
10806
10807     if (argc < 2) {
10808         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
10809         return JIM_ERR;
10810     }
10811     if (argc == 3) {
10812         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
10813             return JIM_ERR;
10814     }
10815     if (count < 0)
10816         return JIM_OK;
10817     i = count;
10818     start = JimClock();
10819     while (i-- > 0) {
10820         int retval;
10821
10822         if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10823             return retval;
10824     }
10825     elapsed = JimClock() - start;
10826     sprintf(buf, fmt, elapsed/count);
10827     Jim_SetResultString(interp, buf, -1);
10828     return JIM_OK;
10829 }
10830
10831 /* [exit] */
10832 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, 
10833         Jim_Obj *const *argv)
10834 {
10835     long exitCode = 0;
10836
10837     if (argc > 2) {
10838         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
10839         return JIM_ERR;
10840     }
10841     if (argc == 2) {
10842         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
10843             return JIM_ERR;
10844     }
10845     interp->exitCode = exitCode;
10846     return JIM_EXIT;
10847 }
10848
10849 /* [catch] */
10850 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, 
10851         Jim_Obj *const *argv)
10852 {
10853     int exitCode = 0;
10854
10855     if (argc != 2 && argc != 3) {
10856         Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
10857         return JIM_ERR;
10858     }
10859     exitCode = Jim_EvalObj(interp, argv[1]);
10860     if (argc == 3) {
10861         if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
10862                 != JIM_OK)
10863             return JIM_ERR;
10864     }
10865     Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
10866     return JIM_OK;
10867 }
10868
10869 /* [ref] */
10870 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, 
10871         Jim_Obj *const *argv)
10872 {
10873     if (argc != 3 && argc != 4) {
10874         Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
10875         return JIM_ERR;
10876     }
10877     if (argc == 3) {
10878         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
10879     } else {
10880         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
10881                     argv[3]));
10882     }
10883     return JIM_OK;
10884 }
10885
10886 /* [getref] */
10887 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, 
10888         Jim_Obj *const *argv)
10889 {
10890     Jim_Reference *refPtr;
10891
10892     if (argc != 2) {
10893         Jim_WrongNumArgs(interp, 1, argv, "reference");
10894         return JIM_ERR;
10895     }
10896     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10897         return JIM_ERR;
10898     Jim_SetResult(interp, refPtr->objPtr);
10899     return JIM_OK;
10900 }
10901
10902 /* [setref] */
10903 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, 
10904         Jim_Obj *const *argv)
10905 {
10906     Jim_Reference *refPtr;
10907
10908     if (argc != 3) {
10909         Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
10910         return JIM_ERR;
10911     }
10912     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10913         return JIM_ERR;
10914     Jim_IncrRefCount(argv[2]);
10915     Jim_DecrRefCount(interp, refPtr->objPtr);
10916     refPtr->objPtr = argv[2];
10917     Jim_SetResult(interp, argv[2]);
10918     return JIM_OK;
10919 }
10920
10921 /* [collect] */
10922 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, 
10923         Jim_Obj *const *argv)
10924 {
10925     if (argc != 1) {
10926         Jim_WrongNumArgs(interp, 1, argv, "");
10927         return JIM_ERR;
10928     }
10929     Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
10930     return JIM_OK;
10931 }
10932
10933 /* [finalize] reference ?newValue? */
10934 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, 
10935         Jim_Obj *const *argv)
10936 {
10937     if (argc != 2 && argc != 3) {
10938         Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
10939         return JIM_ERR;
10940     }
10941     if (argc == 2) {
10942         Jim_Obj *cmdNamePtr;
10943
10944         if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
10945             return JIM_ERR;
10946         if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
10947             Jim_SetResult(interp, cmdNamePtr);
10948     } else {
10949         if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
10950             return JIM_ERR;
10951         Jim_SetResult(interp, argv[2]);
10952     }
10953     return JIM_OK;
10954 }
10955
10956 /* TODO */
10957 /* [info references] (list of all the references/finalizers) */
10958
10959 /* [rename] */
10960 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, 
10961         Jim_Obj *const *argv)
10962 {
10963     const char *oldName, *newName;
10964
10965     if (argc != 3) {
10966         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
10967         return JIM_ERR;
10968     }
10969     oldName = Jim_GetString(argv[1], NULL);
10970     newName = Jim_GetString(argv[2], NULL);
10971     if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
10972         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10973         Jim_AppendStrings(interp, Jim_GetResult(interp),
10974             "can't rename \"", oldName, "\": ",
10975             "command doesn't exist", NULL);
10976         return JIM_ERR;
10977     }
10978     return JIM_OK;
10979 }
10980
10981 /* [dict] */
10982 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, 
10983         Jim_Obj *const *argv)
10984 {
10985     int option;
10986     const char *options[] = {
10987         "create", "get", "set", "unset", "exists", NULL
10988     };
10989     enum {
10990         OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
10991     };
10992
10993     if (argc < 2) {
10994         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10995         return JIM_ERR;
10996     }
10997
10998     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10999                 JIM_ERRMSG) != JIM_OK)
11000         return JIM_ERR;
11001
11002     if (option == OPT_CREATE) {
11003         Jim_Obj *objPtr;
11004
11005         if (argc % 2) {
11006             Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11007             return JIM_ERR;
11008         }
11009         objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11010         Jim_SetResult(interp, objPtr);
11011         return JIM_OK;
11012     } else if (option == OPT_GET) {
11013         Jim_Obj *objPtr;
11014
11015         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11016                 JIM_ERRMSG) != JIM_OK)
11017             return JIM_ERR;
11018         Jim_SetResult(interp, objPtr);
11019         return JIM_OK;
11020     } else if (option == OPT_SET) {
11021         if (argc < 5) {
11022             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11023             return JIM_ERR;
11024         }
11025         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11026                     argv[argc-1]);
11027     } else if (option == OPT_UNSET) {
11028         if (argc < 4) {
11029             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11030             return JIM_ERR;
11031         }
11032         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11033                     NULL);
11034     } else if (option == OPT_EXIST) {
11035         Jim_Obj *objPtr;
11036         int exists;
11037
11038         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11039                 JIM_ERRMSG) == JIM_OK)
11040             exists = 1;
11041         else
11042             exists = 0;
11043         Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11044         return JIM_OK;
11045     } else {
11046         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11047         Jim_AppendStrings(interp, Jim_GetResult(interp),
11048             "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11049             " must be create, get, set", NULL);
11050         return JIM_ERR;
11051     }
11052     return JIM_OK;
11053 }
11054
11055 /* [load] */
11056 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc, 
11057         Jim_Obj *const *argv)
11058 {
11059     if (argc < 2) {
11060         Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11061         return JIM_ERR;
11062     }
11063     return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11064 }
11065
11066 /* [subst] */
11067 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, 
11068         Jim_Obj *const *argv)
11069 {
11070     int i, flags = 0;
11071     Jim_Obj *objPtr;
11072
11073     if (argc < 2) {
11074         Jim_WrongNumArgs(interp, 1, argv,
11075             "?-nobackslashes? ?-nocommands? ?-novariables? string");
11076         return JIM_ERR;
11077     }
11078     i = argc-2;
11079     while(i--) {
11080         if (Jim_CompareStringImmediate(interp, argv[i+1],
11081                     "-nobackslashes"))
11082             flags |= JIM_SUBST_NOESC;
11083         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11084                     "-novariables"))
11085             flags |= JIM_SUBST_NOVAR;
11086         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11087                     "-nocommands"))
11088             flags |= JIM_SUBST_NOCMD;
11089         else {
11090             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11091             Jim_AppendStrings(interp, Jim_GetResult(interp),
11092                 "bad option \"", Jim_GetString(argv[i+1], NULL),
11093                 "\": must be -nobackslashes, -nocommands, or "
11094                 "-novariables", NULL);
11095             return JIM_ERR;
11096         }
11097     }
11098     if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11099         return JIM_ERR;
11100     Jim_SetResult(interp, objPtr);
11101     return JIM_OK;
11102 }
11103
11104 /* [info] */
11105 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, 
11106         Jim_Obj *const *argv)
11107 {
11108     int cmd, result = JIM_OK;
11109     static const char *commands[] = {
11110         "body", "commands", "exists", "globals", "level", "locals",
11111         "vars", "version", "complete", "args", NULL
11112     };
11113     enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11114           INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11115     
11116     if (argc < 2) {
11117         Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11118         return JIM_ERR;
11119     }
11120     if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11121         != JIM_OK) {
11122         return JIM_ERR;
11123     }
11124     
11125     if (cmd == INFO_COMMANDS) {
11126         if (argc != 2 && argc != 3) {
11127             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11128             return JIM_ERR;
11129         }
11130         if (argc == 3)
11131             Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11132         else
11133             Jim_SetResult(interp, JimCommandsList(interp, NULL));
11134     } else if (cmd == INFO_EXISTS) {
11135         Jim_Obj *exists;
11136         if (argc != 3) {
11137             Jim_WrongNumArgs(interp, 2, argv, "varName");
11138             return JIM_ERR;
11139         }
11140         exists = Jim_GetVariable(interp, argv[2], 0);
11141         Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11142     } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11143         int mode;
11144         switch (cmd) {
11145             case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11146             case INFO_LOCALS:  mode = JIM_VARLIST_LOCALS; break;
11147             case INFO_VARS:    mode = JIM_VARLIST_VARS; break;
11148             default: mode = 0; /* avoid warning */; break;
11149         }
11150         if (argc != 2 && argc != 3) {
11151             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11152             return JIM_ERR;
11153         }
11154         if (argc == 3)
11155             Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11156         else
11157             Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11158     } else if (cmd == INFO_LEVEL) {
11159         Jim_Obj *objPtr;
11160         switch (argc) {
11161             case 2:
11162                 Jim_SetResult(interp,
11163                               Jim_NewIntObj(interp, interp->numLevels));
11164                 break;
11165             case 3:
11166                 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11167                     return JIM_ERR;
11168                 Jim_SetResult(interp, objPtr);
11169                 break;
11170             default:
11171                 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11172                 return JIM_ERR;
11173         }
11174     } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11175         Jim_Cmd *cmdPtr;
11176
11177         if (argc != 3) {
11178             Jim_WrongNumArgs(interp, 2, argv, "procname");
11179             return JIM_ERR;
11180         }
11181         if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11182             return JIM_ERR;
11183         if (cmdPtr->cmdProc != NULL) {
11184             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11185             Jim_AppendStrings(interp, Jim_GetResult(interp),
11186                 "command \"", Jim_GetString(argv[2], NULL),
11187                 "\" is not a procedure", NULL);
11188             return JIM_ERR;
11189         }
11190         if (cmd == INFO_BODY)
11191             Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11192         else
11193             Jim_SetResult(interp, cmdPtr->argListObjPtr);
11194     } else if (cmd == INFO_VERSION) {
11195         char buf[(JIM_INTEGER_SPACE * 2) + 1];
11196         sprintf(buf, "%d.%d", 
11197                 JIM_VERSION / 100, JIM_VERSION % 100);
11198         Jim_SetResultString(interp, buf, -1);
11199     } else if (cmd == INFO_COMPLETE) {
11200         const char *s;
11201         int len;
11202
11203         if (argc != 3) {
11204             Jim_WrongNumArgs(interp, 2, argv, "script");
11205             return JIM_ERR;
11206         }
11207         s = Jim_GetString(argv[2], &len);
11208         Jim_SetResult(interp,
11209                 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11210     }
11211     return result;
11212 }
11213
11214 /* [split] */
11215 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, 
11216         Jim_Obj *const *argv)
11217 {
11218     const char *str, *splitChars, *noMatchStart;
11219     int splitLen, strLen, i;
11220     Jim_Obj *resObjPtr;
11221
11222     if (argc != 2 && argc != 3) {
11223         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11224         return JIM_ERR;
11225     }
11226     /* Init */
11227     if (argc == 2) {
11228         splitChars = " \n\t\r";
11229         splitLen = 4;
11230     } else {
11231         splitChars = Jim_GetString(argv[2], &splitLen);
11232     }
11233     str = Jim_GetString(argv[1], &strLen);
11234     if (!strLen) return JIM_OK;
11235     noMatchStart = str;
11236     resObjPtr = Jim_NewListObj(interp, NULL, 0);
11237     /* Split */
11238     if (splitLen) {
11239         while (strLen) {
11240             for (i = 0; i < splitLen; i++) {
11241                 if (*str == splitChars[i]) {
11242                     Jim_Obj *objPtr;
11243
11244                     objPtr = Jim_NewStringObj(interp, noMatchStart,
11245                             (str-noMatchStart));
11246                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
11247                     noMatchStart = str+1;
11248                     break;
11249                 }
11250             }
11251             str ++;
11252             strLen --;
11253         }
11254         Jim_ListAppendElement(interp, resObjPtr,
11255                 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11256     } else {
11257         /* This handles the special case of splitchars eq {}. This
11258          * is trivial but we want to perform object sharing as Tcl does. */
11259         Jim_Obj *objCache[256];
11260         const unsigned char *u = (unsigned char*) str;
11261         memset(objCache, 0, sizeof(objCache));
11262         for (i = 0; i < strLen; i++) {
11263             int c = u[i];
11264             
11265             if (objCache[c] == NULL)
11266                 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11267             Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11268         }
11269     }
11270     Jim_SetResult(interp, resObjPtr);
11271     return JIM_OK;
11272 }
11273
11274 /* [join] */
11275 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, 
11276         Jim_Obj *const *argv)
11277 {
11278     const char *joinStr;
11279     int joinStrLen, i, listLen;
11280     Jim_Obj *resObjPtr;
11281
11282     if (argc != 2 && argc != 3) {
11283         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11284         return JIM_ERR;
11285     }
11286     /* Init */
11287     if (argc == 2) {
11288         joinStr = " ";
11289         joinStrLen = 1;
11290     } else {
11291         joinStr = Jim_GetString(argv[2], &joinStrLen);
11292     }
11293     Jim_ListLength(interp, argv[1], &listLen);
11294     resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11295     /* Split */
11296     for (i = 0; i < listLen; i++) {
11297         Jim_Obj *objPtr;
11298
11299         Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11300         Jim_AppendObj(interp, resObjPtr, objPtr);
11301         if (i+1 != listLen) {
11302             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11303         }
11304     }
11305     Jim_SetResult(interp, resObjPtr);
11306     return JIM_OK;
11307 }
11308
11309 /* [format] */
11310 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11311         Jim_Obj *const *argv)
11312 {
11313     Jim_Obj *objPtr;
11314
11315     if (argc < 2) {
11316         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11317         return JIM_ERR;
11318     }
11319     objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11320     if (objPtr == NULL)
11321         return JIM_ERR;
11322     Jim_SetResult(interp, objPtr);
11323     return JIM_OK;
11324 }
11325
11326 /* [scan] */
11327 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11328         Jim_Obj *const *argv)
11329 {
11330     Jim_Obj *listPtr, **outVec;
11331     int outc, i, count = 0;
11332
11333     if (argc < 3) {
11334         Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11335         return JIM_ERR;
11336     } 
11337     if (argv[2]->typePtr != &scanFmtStringObjType)
11338         SetScanFmtFromAny(interp, argv[2]);
11339     if (FormatGetError(argv[2]) != 0) {
11340         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11341         return JIM_ERR;
11342     }
11343     if (argc > 3) {
11344         int maxPos = FormatGetMaxPos(argv[2]);
11345         int count = FormatGetCnvCount(argv[2]);
11346         if (maxPos > argc-3) {
11347             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11348             return JIM_ERR;
11349         } else if (count != 0 && count < argc-3) {
11350             Jim_SetResultString(interp, "variable is not assigned by any "
11351                 "conversion specifiers", -1);
11352             return JIM_ERR;
11353         } else if (count > argc-3) {
11354             Jim_SetResultString(interp, "different numbers of variable names and "
11355                 "field specifiers", -1);
11356             return JIM_ERR;
11357         }
11358     } 
11359     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11360     if (listPtr == 0)
11361         return JIM_ERR;
11362     if (argc > 3) {
11363         int len = 0;
11364         if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11365             Jim_ListLength(interp, listPtr, &len);
11366         if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11367             Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11368             return JIM_OK;
11369         }
11370         JimListGetElements(interp, listPtr, &outc, &outVec);
11371         for (i = 0; i < outc; ++i) {
11372             if (Jim_Length(outVec[i]) > 0) {
11373                 ++count;
11374                 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11375                     goto err;
11376             }
11377         }
11378         Jim_FreeNewObj(interp, listPtr);
11379         Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11380     } else {
11381         if (listPtr == (Jim_Obj*)EOF) {
11382             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11383             return JIM_OK;
11384         }
11385         Jim_SetResult(interp, listPtr);
11386     }
11387     return JIM_OK;
11388 err:
11389     Jim_FreeNewObj(interp, listPtr);
11390     return JIM_ERR;
11391 }
11392
11393 /* [error] */
11394 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11395         Jim_Obj *const *argv)
11396 {
11397     if (argc != 2) {
11398         Jim_WrongNumArgs(interp, 1, argv, "message");
11399         return JIM_ERR;
11400     }
11401     Jim_SetResult(interp, argv[1]);
11402     return JIM_ERR;
11403 }
11404
11405 /* [lrange] */
11406 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11407         Jim_Obj *const *argv)
11408 {
11409     Jim_Obj *objPtr;
11410
11411     if (argc != 4) {
11412         Jim_WrongNumArgs(interp, 1, argv, "list first last");
11413         return JIM_ERR;
11414     }
11415     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11416         return JIM_ERR;
11417     Jim_SetResult(interp, objPtr);
11418     return JIM_OK;
11419 }
11420
11421 /* [env] */
11422 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11423         Jim_Obj *const *argv)
11424 {
11425     const char *key;
11426     char *val;
11427
11428     if (argc != 2) {
11429         Jim_WrongNumArgs(interp, 1, argv, "varName");
11430         return JIM_ERR;
11431     }
11432     key = Jim_GetString(argv[1], NULL);
11433     val = getenv(key);
11434     if (val == NULL) {
11435         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11436         Jim_AppendStrings(interp, Jim_GetResult(interp),
11437                 "environment variable \"",
11438                 key, "\" does not exist", NULL);
11439         return JIM_ERR;
11440     }
11441     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11442     return JIM_OK;
11443 }
11444
11445 /* [source] */
11446 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11447         Jim_Obj *const *argv)
11448 {
11449     int retval;
11450
11451     if (argc != 2) {
11452         Jim_WrongNumArgs(interp, 1, argv, "fileName");
11453         return JIM_ERR;
11454     }
11455     retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11456     if (retval == JIM_RETURN)
11457         return JIM_OK;
11458     return retval;
11459 }
11460
11461 /* [lreverse] */
11462 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11463         Jim_Obj *const *argv)
11464 {
11465     Jim_Obj *revObjPtr, **ele;
11466     int len;
11467
11468     if (argc != 2) {
11469         Jim_WrongNumArgs(interp, 1, argv, "list");
11470         return JIM_ERR;
11471     }
11472     JimListGetElements(interp, argv[1], &len, &ele);
11473     len--;
11474     revObjPtr = Jim_NewListObj(interp, NULL, 0);
11475     while (len >= 0)
11476         ListAppendElement(revObjPtr, ele[len--]);
11477     Jim_SetResult(interp, revObjPtr);
11478     return JIM_OK;
11479 }
11480
11481 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11482 {
11483     jim_wide len;
11484
11485     if (step == 0) return -1;
11486     if (start == end) return 0;
11487     else if (step > 0 && start > end) return -1;
11488     else if (step < 0 && end > start) return -1;
11489     len = end-start;
11490     if (len < 0) len = -len; /* abs(len) */
11491     if (step < 0) step = -step; /* abs(step) */
11492     len = 1 + ((len-1)/step);
11493     /* We can truncate safely to INT_MAX, the range command
11494      * will always return an error for a such long range
11495      * because Tcl lists can't be so long. */
11496     if (len > INT_MAX) len = INT_MAX;
11497     return (int)((len < 0) ? -1 : len);
11498 }
11499
11500 /* [range] */
11501 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11502         Jim_Obj *const *argv)
11503 {
11504     jim_wide start = 0, end, step = 1;
11505     int len, i;
11506     Jim_Obj *objPtr;
11507
11508     if (argc < 2 || argc > 4) {
11509         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11510         return JIM_ERR;
11511     }
11512     if (argc == 2) {
11513         if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11514             return JIM_ERR;
11515     } else {
11516         if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11517             Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11518             return JIM_ERR;
11519         if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11520             return JIM_ERR;
11521     }
11522     if ((len = JimRangeLen(start, end, step)) == -1) {
11523         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11524         return JIM_ERR;
11525     }
11526     objPtr = Jim_NewListObj(interp, NULL, 0);
11527     for (i = 0; i < len; i++)
11528         ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11529     Jim_SetResult(interp, objPtr);
11530     return JIM_OK;
11531 }
11532
11533 /* [rand] */
11534 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11535         Jim_Obj *const *argv)
11536 {
11537     jim_wide min = 0, max, len, maxMul;
11538
11539     if (argc < 1 || argc > 3) {
11540         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11541         return JIM_ERR;
11542     }
11543     if (argc == 1) {
11544         max = JIM_WIDE_MAX;
11545     } else if (argc == 2) {
11546         if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11547             return JIM_ERR;
11548     } else if (argc == 3) {
11549         if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11550             Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11551             return JIM_ERR;
11552     }
11553     len = max-min;
11554     if (len < 0) {
11555         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11556         return JIM_ERR;
11557     }
11558     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11559     while (1) {
11560         jim_wide r;
11561
11562         JimRandomBytes(interp, &r, sizeof(jim_wide));
11563         if (r < 0 || r >= maxMul) continue;
11564         r = (len == 0) ? 0 : r%len;
11565         Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11566         return JIM_OK;
11567     }
11568 }
11569
11570 /* [package] */
11571 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc, 
11572         Jim_Obj *const *argv)
11573 {
11574     int option;
11575     const char *options[] = {
11576         "require", "provide", NULL
11577     };
11578     enum {OPT_REQUIRE, OPT_PROVIDE};
11579
11580     if (argc < 2) {
11581         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11582         return JIM_ERR;
11583     }
11584     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11585                 JIM_ERRMSG) != JIM_OK)
11586         return JIM_ERR;
11587
11588     if (option == OPT_REQUIRE) {
11589         int exact = 0;
11590         const char *ver;
11591
11592         if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11593             exact = 1;
11594             argv++;
11595             argc--;
11596         }
11597         if (argc != 3 && argc != 4) {
11598             Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11599             return JIM_ERR;
11600         }
11601         ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11602                 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11603                 JIM_ERRMSG);
11604         if (ver == NULL)
11605             return JIM_ERR;
11606         Jim_SetResultString(interp, ver, -1);
11607     } else if (option == OPT_PROVIDE) {
11608         if (argc != 4) {
11609             Jim_WrongNumArgs(interp, 2, argv, "package version");
11610             return JIM_ERR;
11611         }
11612         return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11613                     Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11614     }
11615     return JIM_OK;
11616 }
11617
11618 static struct {
11619     const char *name;
11620     Jim_CmdProc cmdProc;
11621 } Jim_CoreCommandsTable[] = {
11622     {"set", Jim_SetCoreCommand},
11623     {"unset", Jim_UnsetCoreCommand},
11624     {"puts", Jim_PutsCoreCommand},
11625     {"+", Jim_AddCoreCommand},
11626     {"*", Jim_MulCoreCommand},
11627     {"-", Jim_SubCoreCommand},
11628     {"/", Jim_DivCoreCommand},
11629     {"incr", Jim_IncrCoreCommand},
11630     {"while", Jim_WhileCoreCommand},
11631     {"for", Jim_ForCoreCommand},
11632     {"foreach", Jim_ForeachCoreCommand},
11633     {"lmap", Jim_LmapCoreCommand},
11634     {"if", Jim_IfCoreCommand},
11635     {"switch", Jim_SwitchCoreCommand},
11636     {"list", Jim_ListCoreCommand},
11637     {"lindex", Jim_LindexCoreCommand},
11638     {"lset", Jim_LsetCoreCommand},
11639     {"llength", Jim_LlengthCoreCommand},
11640     {"lappend", Jim_LappendCoreCommand},
11641     {"linsert", Jim_LinsertCoreCommand},
11642     {"lsort", Jim_LsortCoreCommand},
11643     {"append", Jim_AppendCoreCommand},
11644     {"debug", Jim_DebugCoreCommand},
11645     {"eval", Jim_EvalCoreCommand},
11646     {"uplevel", Jim_UplevelCoreCommand},
11647     {"expr", Jim_ExprCoreCommand},
11648     {"break", Jim_BreakCoreCommand},
11649     {"continue", Jim_ContinueCoreCommand},
11650     {"proc", Jim_ProcCoreCommand},
11651     {"concat", Jim_ConcatCoreCommand},
11652     {"return", Jim_ReturnCoreCommand},
11653     {"upvar", Jim_UpvarCoreCommand},
11654     {"global", Jim_GlobalCoreCommand},
11655     {"string", Jim_StringCoreCommand},
11656     {"time", Jim_TimeCoreCommand},
11657     {"exit", Jim_ExitCoreCommand},
11658     {"catch", Jim_CatchCoreCommand},
11659     {"ref", Jim_RefCoreCommand},
11660     {"getref", Jim_GetrefCoreCommand},
11661     {"setref", Jim_SetrefCoreCommand},
11662     {"finalize", Jim_FinalizeCoreCommand},
11663     {"collect", Jim_CollectCoreCommand},
11664     {"rename", Jim_RenameCoreCommand},
11665     {"dict", Jim_DictCoreCommand},
11666     {"load", Jim_LoadCoreCommand},
11667     {"subst", Jim_SubstCoreCommand},
11668     {"info", Jim_InfoCoreCommand},
11669     {"split", Jim_SplitCoreCommand},
11670     {"join", Jim_JoinCoreCommand},
11671     {"format", Jim_FormatCoreCommand},
11672     {"scan", Jim_ScanCoreCommand},
11673     {"error", Jim_ErrorCoreCommand},
11674     {"lrange", Jim_LrangeCoreCommand},
11675     {"env", Jim_EnvCoreCommand},
11676     {"source", Jim_SourceCoreCommand},
11677     {"lreverse", Jim_LreverseCoreCommand},
11678     {"range", Jim_RangeCoreCommand},
11679     {"rand", Jim_RandCoreCommand},
11680     {"package", Jim_PackageCoreCommand},
11681     {"tailcall", Jim_TailcallCoreCommand},
11682     {NULL, NULL},
11683 };
11684
11685 /* Some Jim core command is actually a procedure written in Jim itself. */
11686 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11687 {
11688     Jim_Eval(interp, (char*)
11689 "proc lambda {arglist args} {\n"
11690 "    set name [ref {} function lambdaFinalizer]\n"
11691 "    uplevel 1 [list proc $name $arglist {expand}$args]\n"
11692 "    return $name\n"
11693 "}\n"
11694 "proc lambdaFinalizer {name val} {\n"
11695 "    rename $name {}\n"
11696 "}\n"
11697     );
11698 }
11699
11700 void Jim_RegisterCoreCommands(Jim_Interp *interp)
11701 {
11702     int i = 0;
11703
11704     while(Jim_CoreCommandsTable[i].name != NULL) {
11705         Jim_CreateCommand(interp, 
11706                 Jim_CoreCommandsTable[i].name,
11707                 Jim_CoreCommandsTable[i].cmdProc,
11708                 NULL, NULL);
11709         i++;
11710     }
11711     Jim_RegisterCoreProcedures(interp);
11712 }
11713
11714 /* -----------------------------------------------------------------------------
11715  * Interactive prompt
11716  * ---------------------------------------------------------------------------*/
11717 void Jim_PrintErrorMessage(Jim_Interp *interp)
11718 {
11719     int len, i;
11720
11721     fprintf(interp->stderr, "Runtime error, file \"%s\", line %d:" JIM_NL,
11722             interp->errorFileName, interp->errorLine);
11723     fprintf(interp->stderr, "    %s" JIM_NL,
11724             Jim_GetString(interp->result, NULL));
11725     Jim_ListLength(interp, interp->stackTrace, &len);
11726     for (i = 0; i < len; i+= 3) {
11727         Jim_Obj *objPtr;
11728         const char *proc, *file, *line;
11729
11730         Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
11731         proc = Jim_GetString(objPtr, NULL);
11732         Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
11733                 JIM_NONE);
11734         file = Jim_GetString(objPtr, NULL);
11735         Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
11736                 JIM_NONE);
11737         line = Jim_GetString(objPtr, NULL);
11738         fprintf(interp->stderr,
11739                 "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
11740                 proc, file, line);
11741     }
11742 }
11743
11744 int Jim_InteractivePrompt(Jim_Interp *interp)
11745 {
11746     int retcode = JIM_OK;
11747     Jim_Obj *scriptObjPtr;
11748
11749     fprintf(interp->stdout, "Welcome to Jim version %d.%d, "
11750            "Copyright (c) 2005 Salvatore Sanfilippo" JIM_NL,
11751            JIM_VERSION / 100, JIM_VERSION % 100);
11752     fprintf(interp->stdout,
11753             "CVS ID: $Id: jim.c,v 1.1.1.1 2008/07/31 20:44:21 mmahesh Exp $"
11754             JIM_NL);
11755     Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
11756     while (1) {
11757         char buf[1024];
11758         const char *result;
11759         const char *retcodestr[] = {
11760             "ok", "error", "return", "break", "continue", "eval", "exit"
11761         };
11762         int reslen;
11763
11764         if (retcode != 0) {
11765             if (retcode >= 2 && retcode <= 6)
11766                 fprintf(interp->stdout, "[%s] . ", retcodestr[retcode]);
11767             else
11768                 fprintf(interp->stdout, "[%d] . ", retcode);
11769         } else
11770             fprintf(interp->stdout, ". ");
11771         fflush(interp->stdout);
11772         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
11773         Jim_IncrRefCount(scriptObjPtr);
11774         while(1) {
11775             const char *str;
11776             char state;
11777             int len;
11778
11779             if (fgets(buf, 1024, interp->stdin) == NULL) {
11780                 Jim_DecrRefCount(interp, scriptObjPtr);
11781                 goto out;
11782             }
11783             Jim_AppendString(interp, scriptObjPtr, buf, -1);
11784             str = Jim_GetString(scriptObjPtr, &len);
11785             if (Jim_ScriptIsComplete(str, len, &state))
11786                 break;
11787             fprintf(interp->stdout, "%c> ", state);
11788             fflush(stdout);
11789         }
11790         retcode = Jim_EvalObj(interp, scriptObjPtr);
11791         Jim_DecrRefCount(interp, scriptObjPtr);
11792         result = Jim_GetString(Jim_GetResult(interp), &reslen);
11793         if (retcode == JIM_ERR) {
11794             Jim_PrintErrorMessage(interp);
11795         } else if (retcode == JIM_EXIT) {
11796             exit(Jim_GetExitCode(interp));
11797         } else {
11798             if (reslen) {
11799                 fwrite(result, 1, reslen, interp->stdout);
11800                 fprintf(interp->stdout, JIM_NL);
11801             }
11802         }
11803     }
11804 out:
11805     return 0;
11806 }