]> git.sur5r.net Git - openocd/blob - src/helper/jim.c
Update jim helper files to use proper configure script support:
[openocd] / src / helper / jim.c
1 /* Jim - A small embeddable Tcl interpreter
2  *
3  * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4  * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5  * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net> 
6  * Copyright 2008 oharboe - Ã˜yvind Harboe - oyvind.harboe@zylin.com
7  * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8  * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9  * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
10  * Copyright 2008 Steve Bennett <steveb@workware.net.au>
11  * 
12  * The FreeBSD license
13  * 
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above
21  *    copyright notice, this list of conditions and the following
22  *    disclaimer in the documentation and/or other materials
23  *    provided with the distribution.
24  * 
25  * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
26  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
28  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37  * 
38  * The views and conclusions contained in the software and documentation
39  * are those of the authors and should not be interpreted as representing
40  * official policies, either expressed or implied, of the Jim Tcl Project.
41  **/
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #define __JIM_CORE__
47 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
48
49 #ifdef __ECOS
50 #include <pkgconf/jimtcl.h>
51 #endif
52 #ifndef JIM_ANSIC
53 #define JIM_DYNLIB      /* Dynamic library support for UNIX and WIN32 */
54 #endif /* JIM_ANSIC */
55
56 #ifndef _GNU_SOURCE
57 #define _GNU_SOURCE     /* for vasprintf() */
58 #endif
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <stdarg.h>
63 #include <ctype.h>
64 #include <limits.h>
65 #include <assert.h>
66 #include <errno.h>
67 #include <time.h>
68 #if defined(WIN32)
69 /* sys/time - need is different */
70 #else
71 #include <sys/time.h> // for gettimeofday()
72 #endif
73
74 #include "replacements.h"
75
76 /* Include the platform dependent libraries for
77  * dynamic loading of libraries. */
78 #ifdef JIM_DYNLIB
79 #if defined(_WIN32) || defined(WIN32)
80 #ifndef WIN32
81 #define WIN32 1
82 #endif
83 #ifndef STRICT
84 #define STRICT
85 #endif
86 #define WIN32_LEAN_AND_MEAN
87 #include <windows.h>
88 #if _MSC_VER >= 1000
89 #pragma warning(disable:4146)
90 #endif /* _MSC_VER */
91 #else
92 #include <dlfcn.h>
93 #endif /* WIN32 */
94 #endif /* JIM_DYNLIB */
95
96 #ifdef HAVE_UNISTD_H
97 #include <unistd.h>
98 #endif
99
100 #ifdef __ECOS
101 #include <cyg/jimtcl/jim.h>
102 #else
103 #include "jim.h"
104 #endif
105
106 #ifdef HAVE_BACKTRACE
107 #include <execinfo.h>
108 #endif
109
110 /* -----------------------------------------------------------------------------
111  * Global variables
112  * ---------------------------------------------------------------------------*/
113
114 /* A shared empty string for the objects string representation.
115  * Jim_InvalidateStringRep knows about it and don't try to free. */
116 static char *JimEmptyStringRep = (char*) "";
117
118 /* -----------------------------------------------------------------------------
119  * Required prototypes of not exported functions
120  * ---------------------------------------------------------------------------*/
121 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
122 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
123 static void JimRegisterCoreApi(Jim_Interp *interp);
124
125 static Jim_HashTableType *getJimVariablesHashTableType(void);
126
127 /* -----------------------------------------------------------------------------
128  * Utility functions
129  * ---------------------------------------------------------------------------*/
130
131 static char *
132 jim_vasprintf( const char *fmt, va_list ap )
133 {
134 #ifndef HAVE_VASPRINTF
135         /* yucky way */
136 static char buf[2048];
137         vsnprintf( buf, sizeof(buf), fmt, ap );
138         /* garentee termination */
139         buf[sizeof(buf)-1] = 0;
140 #else
141         char *buf;
142         int result;
143         result = vasprintf( &buf, fmt, ap );
144         if (result < 0) exit(-1);
145 #endif
146         return buf;
147 }
148
149 static void
150 jim_vasprintf_done( void *buf )
151 {
152 #ifndef HAVE_VASPRINTF
153         (void)(buf);
154 #else
155         free(buf);
156 #endif
157 }
158         
159
160 /*
161  * Convert a string to a jim_wide INTEGER.
162  * This function originates from BSD.
163  *
164  * Ignores `locale' stuff.  Assumes that the upper and lower case
165  * alphabets and digits are each contiguous.
166  */
167 #ifdef HAVE_LONG_LONG_INT
168 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
169 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
170 {
171     register const char *s;
172     register unsigned jim_wide acc;
173     register unsigned char c;
174     register unsigned jim_wide qbase, cutoff;
175     register int neg, any, cutlim;
176
177     /*
178      * Skip white space and pick up leading +/- sign if any.
179      * If base is 0, allow 0x for hex and 0 for octal, else
180      * assume decimal; if base is already 16, allow 0x.
181      */
182     s = nptr;
183     do {
184         c = *s++;
185     } while (isspace(c));
186     if (c == '-') {
187         neg = 1;
188         c = *s++;
189     } else {
190         neg = 0;
191         if (c == '+')
192             c = *s++;
193     }
194     if ((base == 0 || base == 16) &&
195         c == '0' && (*s == 'x' || *s == 'X')) {
196         c = s[1];
197         s += 2;
198         base = 16;
199     }
200     if (base == 0)
201         base = c == '0' ? 8 : 10;
202
203     /*
204      * Compute the cutoff value between legal numbers and illegal
205      * numbers.  That is the largest legal value, divided by the
206      * base.  An input number that is greater than this value, if
207      * followed by a legal input character, is too big.  One that
208      * is equal to this value may be valid or not; the limit
209      * between valid and invalid numbers is then based on the last
210      * digit.  For instance, if the range for quads is
211      * [-9223372036854775808..9223372036854775807] and the input base
212      * is 10, cutoff will be set to 922337203685477580 and cutlim to
213      * either 7 (neg==0) or 8 (neg==1), meaning that if we have
214      * accumulated a value > 922337203685477580, or equal but the
215      * next digit is > 7 (or 8), the number is too big, and we will
216      * return a range error.
217      *
218      * Set any if any `digits' consumed; make it negative to indicate
219      * overflow.
220      */
221     qbase = (unsigned)base;
222     cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
223         : LLONG_MAX;
224     cutlim = (int)(cutoff % qbase);
225     cutoff /= qbase;
226     for (acc = 0, any = 0;; c = *s++) {
227         if (!JimIsAscii(c))
228             break;
229         if (isdigit(c))
230             c -= '0';
231         else if (isalpha(c))
232             c -= isupper(c) ? 'A' - 10 : 'a' - 10;
233         else
234             break;
235         if (c >= base)
236             break;
237         if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
238             any = -1;
239         else {
240             any = 1;
241             acc *= qbase;
242             acc += c;
243         }
244     }
245     if (any < 0) {
246         acc = neg ? LLONG_MIN : LLONG_MAX;
247         errno = ERANGE;
248     } else if (neg)
249         acc = -acc;
250     if (endptr != 0)
251         *endptr = (char *)(any ? s - 1 : nptr);
252     return (acc);
253 }
254 #endif
255
256 /* Glob-style pattern matching. */
257 static int JimStringMatch(const char *pattern, int patternLen,
258         const char *string, int stringLen, int nocase)
259 {
260     while(patternLen) {
261         switch(pattern[0]) {
262         case '*':
263             while (pattern[1] == '*') {
264                 pattern++;
265                 patternLen--;
266             }
267             if (patternLen == 1)
268                 return 1; /* match */
269             while(stringLen) {
270                 if (JimStringMatch(pattern+1, patternLen-1,
271                             string, stringLen, nocase))
272                     return 1; /* match */
273                 string++;
274                 stringLen--;
275             }
276             return 0; /* no match */
277             break;
278         case '?':
279             if (stringLen == 0)
280                 return 0; /* no match */
281             string++;
282             stringLen--;
283             break;
284         case '[':
285         {
286             int not, match;
287
288             pattern++;
289             patternLen--;
290             not = pattern[0] == '^';
291             if (not) {
292                 pattern++;
293                 patternLen--;
294             }
295             match = 0;
296             while(1) {
297                 if (pattern[0] == '\\') {
298                     pattern++;
299                     patternLen--;
300                     if (pattern[0] == string[0])
301                         match = 1;
302                 } else if (pattern[0] == ']') {
303                     break;
304                 } else if (patternLen == 0) {
305                     pattern--;
306                     patternLen++;
307                     break;
308                 } else if (pattern[1] == '-' && patternLen >= 3) {
309                     int start = pattern[0];
310                     int end = pattern[2];
311                     int c = string[0];
312                     if (start > end) {
313                         int t = start;
314                         start = end;
315                         end = t;
316                     }
317                     if (nocase) {
318                         start = tolower(start);
319                         end = tolower(end);
320                         c = tolower(c);
321                     }
322                     pattern += 2;
323                     patternLen -= 2;
324                     if (c >= start && c <= end)
325                         match = 1;
326                 } else {
327                     if (!nocase) {
328                         if (pattern[0] == string[0])
329                             match = 1;
330                     } else {
331                         if (tolower((int)pattern[0]) == tolower((int)string[0]))
332                             match = 1;
333                     }
334                 }
335                 pattern++;
336                 patternLen--;
337             }
338             if (not)
339                 match = !match;
340             if (!match)
341                 return 0; /* no match */
342             string++;
343             stringLen--;
344             break;
345         }
346         case '\\':
347             if (patternLen >= 2) {
348                 pattern++;
349                 patternLen--;
350             }
351             /* fall through */
352         default:
353             if (!nocase) {
354                 if (pattern[0] != string[0])
355                     return 0; /* no match */
356             } else {
357                 if (tolower((int)pattern[0]) != tolower((int)string[0]))
358                     return 0; /* no match */
359             }
360             string++;
361             stringLen--;
362             break;
363         }
364         pattern++;
365         patternLen--;
366         if (stringLen == 0) {
367             while(*pattern == '*') {
368                 pattern++;
369                 patternLen--;
370             }
371             break;
372         }
373     }
374     if (patternLen == 0 && stringLen == 0)
375         return 1;
376     return 0;
377 }
378
379 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
380         int nocase)
381 {
382     unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
383
384     if (nocase == 0) {
385         while(l1 && l2) {
386             if (*u1 != *u2)
387                 return (int)*u1-*u2;
388             u1++; u2++; l1--; l2--;
389         }
390         if (!l1 && !l2) return 0;
391         return l1-l2;
392     } else {
393         while(l1 && l2) {
394             if (tolower((int)*u1) != tolower((int)*u2))
395                 return tolower((int)*u1)-tolower((int)*u2);
396             u1++; u2++; l1--; l2--;
397         }
398         if (!l1 && !l2) return 0;
399         return l1-l2;
400     }
401 }
402
403 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
404  * The index of the first occurrence of s1 in s2 is returned. 
405  * If s1 is not found inside s2, -1 is returned. */
406 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
407 {
408     int i;
409
410     if (!l1 || !l2 || l1 > l2) return -1;
411     if (index < 0) index = 0;
412     s2 += index;
413     for (i = index; i <= l2-l1; i++) {
414         if (memcmp(s2, s1, l1) == 0)
415             return i;
416         s2++;
417     }
418     return -1;
419 }
420
421 int Jim_WideToString(char *buf, jim_wide wideValue)
422 {
423     const char *fmt = "%" JIM_WIDE_MODIFIER;
424     return sprintf(buf, fmt, wideValue);
425 }
426
427 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
428 {
429     char *endptr;
430
431 #ifdef HAVE_LONG_LONG_INT
432     *widePtr = JimStrtoll(str, &endptr, base);
433 #else
434     *widePtr = strtol(str, &endptr, base);
435 #endif
436     if ((str[0] == '\0') || (str == endptr) )
437         return JIM_ERR;
438     if (endptr[0] != '\0') {
439         while(*endptr) {
440             if (!isspace((int)*endptr))
441                 return JIM_ERR;
442             endptr++;
443         }
444     }
445     return JIM_OK;
446 }
447
448 int Jim_StringToIndex(const char *str, int *intPtr)
449 {
450     char *endptr;
451
452     *intPtr = strtol(str, &endptr, 10);
453     if ( (str[0] == '\0') || (str == endptr) )
454         return JIM_ERR;
455     if (endptr[0] != '\0') {
456         while(*endptr) {
457             if (!isspace((int)*endptr))
458                 return JIM_ERR;
459             endptr++;
460         }
461     }
462     return JIM_OK;
463 }
464
465 /* The string representation of references has two features in order
466  * to make the GC faster. The first is that every reference starts
467  * with a non common character '~', in order to make the string matching
468  * fater. The second is that the reference string rep his 32 characters
469  * in length, this allows to avoid to check every object with a string
470  * repr < 32, and usually there are many of this objects. */
471
472 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
473
474 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
475 {
476     const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
477     sprintf(buf, fmt, refPtr->tag, id);
478     return JIM_REFERENCE_SPACE;
479 }
480
481 int Jim_DoubleToString(char *buf, double doubleValue)
482 {
483     char *s;
484     int len;
485
486     len = sprintf(buf, "%.17g", doubleValue);
487     s = buf;
488     while(*s) {
489         if (*s == '.') return len;
490         s++;
491     }
492     /* Add a final ".0" if it's a number. But not
493      * for NaN or InF */
494     if (isdigit((int)buf[0])
495         || ((buf[0] == '-' || buf[0] == '+')
496             && isdigit((int)buf[1]))) {
497         s[0] = '.';
498         s[1] = '0';
499         s[2] = '\0';
500         return len+2;
501     }
502     return len;
503 }
504
505 int Jim_StringToDouble(const char *str, double *doublePtr)
506 {
507     char *endptr;
508
509     *doublePtr = strtod(str, &endptr);
510     if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
511         return JIM_ERR;
512     return JIM_OK;
513 }
514
515 static jim_wide JimPowWide(jim_wide b, jim_wide e)
516 {
517     jim_wide i, res = 1;
518     if ((b==0 && e!=0) || (e<0)) return 0;
519     for(i=0; i<e; i++) {res *= b;}
520     return res;
521 }
522
523 /* -----------------------------------------------------------------------------
524  * Special functions
525  * ---------------------------------------------------------------------------*/
526
527 /* Note that 'interp' may be NULL if not available in the
528  * context of the panic. It's only useful to get the error
529  * file descriptor, it will default to stderr otherwise. */
530 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
531 {
532     va_list ap;
533
534     va_start(ap, fmt);
535         /* 
536          * Send it here first.. Assuming STDIO still works
537          */
538     fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
539     vfprintf(stderr, fmt, ap);
540     fprintf(stderr, JIM_NL JIM_NL);
541     va_end(ap);
542
543 #ifdef HAVE_BACKTRACE
544     {
545         void *array[40];
546         int size, i;
547         char **strings;
548
549         size = backtrace(array, 40);
550         strings = backtrace_symbols(array, size);
551         for (i = 0; i < size; i++)
552             fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
553         fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
554         fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
555     }
556 #endif
557         
558         /* This may actually crash... we do it last */
559         if( interp && interp->cookie_stderr ){
560                 Jim_fprintf(  interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
561                 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
562                 Jim_fprintf(  interp, interp->cookie_stderr, JIM_NL JIM_NL );
563         }
564     abort();
565 }
566
567 /* -----------------------------------------------------------------------------
568  * Memory allocation
569  * ---------------------------------------------------------------------------*/
570
571 /* Macro used for memory debugging.
572  * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
573  * and similary for Jim_Realloc and Jim_Free */
574 #if 0
575 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
576 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
577 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
578 #endif
579
580 void *Jim_Alloc(int size)
581 {
582         /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
583         if (size==0)
584                 size=1;
585     void *p = malloc(size);
586     if (p == NULL)
587         Jim_Panic(NULL,"malloc: Out of memory");
588     return p;
589 }
590
591 void Jim_Free(void *ptr) {
592     free(ptr);
593 }
594
595 void *Jim_Realloc(void *ptr, int size)
596 {
597         /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
598         if (size==0)
599                 size=1;
600     void *p = realloc(ptr, size);
601     if (p == NULL)
602         Jim_Panic(NULL,"realloc: Out of memory");
603     return p;
604 }
605
606 char *Jim_StrDup(const char *s)
607 {
608     int l = strlen(s);
609     char *copy = Jim_Alloc(l+1);
610
611     memcpy(copy, s, l+1);
612     return copy;
613 }
614
615 char *Jim_StrDupLen(const char *s, int l)
616 {
617     char *copy = Jim_Alloc(l+1);
618     
619     memcpy(copy, s, l+1);
620     copy[l] = 0;    /* Just to be sure, original could be substring */
621     return copy;
622 }
623
624 /* -----------------------------------------------------------------------------
625  * Time related functions
626  * ---------------------------------------------------------------------------*/
627 /* Returns microseconds of CPU used since start. */
628 static jim_wide JimClock(void)
629 {
630 #if (defined WIN32) && !(defined JIM_ANSIC)
631     LARGE_INTEGER t, f;
632     QueryPerformanceFrequency(&f);
633     QueryPerformanceCounter(&t);
634     return (long)((t.QuadPart * 1000000) / f.QuadPart);
635 #else /* !WIN32 */
636     clock_t clocks = clock();
637
638     return (long)(clocks*(1000000/CLOCKS_PER_SEC));
639 #endif /* WIN32 */
640 }
641
642 /* -----------------------------------------------------------------------------
643  * Hash Tables
644  * ---------------------------------------------------------------------------*/
645
646 /* -------------------------- private prototypes ---------------------------- */
647 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
648 static unsigned int JimHashTableNextPower(unsigned int size);
649 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
650
651 /* -------------------------- hash functions -------------------------------- */
652
653 /* Thomas Wang's 32 bit Mix Function */
654 unsigned int Jim_IntHashFunction(unsigned int key)
655 {
656     key += ~(key << 15);
657     key ^=  (key >> 10);
658     key +=  (key << 3);
659     key ^=  (key >> 6);
660     key += ~(key << 11);
661     key ^=  (key >> 16);
662     return key;
663 }
664
665 /* Identity hash function for integer keys */
666 unsigned int Jim_IdentityHashFunction(unsigned int key)
667 {
668     return key;
669 }
670
671 /* Generic hash function (we are using to multiply by 9 and add the byte
672  * as Tcl) */
673 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
674 {
675     unsigned int h = 0;
676     while(len--)
677         h += (h<<3)+*buf++;
678     return h;
679 }
680
681 /* ----------------------------- API implementation ------------------------- */
682 /* reset an hashtable already initialized with ht_init().
683  * NOTE: This function should only called by ht_destroy(). */
684 static void JimResetHashTable(Jim_HashTable *ht)
685 {
686     ht->table = NULL;
687     ht->size = 0;
688     ht->sizemask = 0;
689     ht->used = 0;
690     ht->collisions = 0;
691 }
692
693 /* Initialize the hash table */
694 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
695         void *privDataPtr)
696 {
697     JimResetHashTable(ht);
698     ht->type = type;
699     ht->privdata = privDataPtr;
700     return JIM_OK;
701 }
702
703 /* Resize the table to the minimal size that contains all the elements,
704  * but with the invariant of a USER/BUCKETS ration near to <= 1 */
705 int Jim_ResizeHashTable(Jim_HashTable *ht)
706 {
707     int minimal = ht->used;
708
709     if (minimal < JIM_HT_INITIAL_SIZE)
710         minimal = JIM_HT_INITIAL_SIZE;
711     return Jim_ExpandHashTable(ht, minimal);
712 }
713
714 /* Expand or create the hashtable */
715 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
716 {
717     Jim_HashTable n; /* the new hashtable */
718     unsigned int realsize = JimHashTableNextPower(size), i;
719
720     /* the size is invalid if it is smaller than the number of
721      * elements already inside the hashtable */
722     if (ht->used >= size)
723         return JIM_ERR;
724
725     Jim_InitHashTable(&n, ht->type, ht->privdata);
726     n.size = realsize;
727     n.sizemask = realsize-1;
728     n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
729
730     /* Initialize all the pointers to NULL */
731     memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
732
733     /* Copy all the elements from the old to the new table:
734      * note that if the old hash table is empty ht->size is zero,
735      * so Jim_ExpandHashTable just creates an hash table. */
736     n.used = ht->used;
737     for (i = 0; i < ht->size && ht->used > 0; i++) {
738         Jim_HashEntry *he, *nextHe;
739
740         if (ht->table[i] == NULL) continue;
741         
742         /* For each hash entry on this slot... */
743         he = ht->table[i];
744         while(he) {
745             unsigned int h;
746
747             nextHe = he->next;
748             /* Get the new element index */
749             h = Jim_HashKey(ht, he->key) & n.sizemask;
750             he->next = n.table[h];
751             n.table[h] = he;
752             ht->used--;
753             /* Pass to the next element */
754             he = nextHe;
755         }
756     }
757     assert(ht->used == 0);
758     Jim_Free(ht->table);
759
760     /* Remap the new hashtable in the old */
761     *ht = n;
762     return JIM_OK;
763 }
764
765 /* Add an element to the target hash table */
766 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
767 {
768     int index;
769     Jim_HashEntry *entry;
770
771     /* Get the index of the new element, or -1 if
772      * the element already exists. */
773     if ((index = JimInsertHashEntry(ht, key)) == -1)
774         return JIM_ERR;
775
776     /* Allocates the memory and stores key */
777     entry = Jim_Alloc(sizeof(*entry));
778     entry->next = ht->table[index];
779     ht->table[index] = entry;
780
781     /* Set the hash entry fields. */
782     Jim_SetHashKey(ht, entry, key);
783     Jim_SetHashVal(ht, entry, val);
784     ht->used++;
785     return JIM_OK;
786 }
787
788 /* Add an element, discarding the old if the key already exists */
789 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
790 {
791     Jim_HashEntry *entry;
792
793     /* Try to add the element. If the key
794      * does not exists Jim_AddHashEntry will suceed. */
795     if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
796         return JIM_OK;
797     /* It already exists, get the entry */
798     entry = Jim_FindHashEntry(ht, key);
799     /* Free the old value and set the new one */
800     Jim_FreeEntryVal(ht, entry);
801     Jim_SetHashVal(ht, entry, val);
802     return JIM_OK;
803 }
804
805 /* Search and remove an element */
806 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
807 {
808     unsigned int h;
809     Jim_HashEntry *he, *prevHe;
810
811     if (ht->size == 0)
812         return JIM_ERR;
813     h = Jim_HashKey(ht, key) & ht->sizemask;
814     he = ht->table[h];
815
816     prevHe = NULL;
817     while(he) {
818         if (Jim_CompareHashKeys(ht, key, he->key)) {
819             /* Unlink the element from the list */
820             if (prevHe)
821                 prevHe->next = he->next;
822             else
823                 ht->table[h] = he->next;
824             Jim_FreeEntryKey(ht, he);
825             Jim_FreeEntryVal(ht, he);
826             Jim_Free(he);
827             ht->used--;
828             return JIM_OK;
829         }
830         prevHe = he;
831         he = he->next;
832     }
833     return JIM_ERR; /* not found */
834 }
835
836 /* Destroy an entire hash table */
837 int Jim_FreeHashTable(Jim_HashTable *ht)
838 {
839     unsigned int i;
840
841     /* Free all the elements */
842     for (i = 0; i < ht->size && ht->used > 0; i++) {
843         Jim_HashEntry *he, *nextHe;
844
845         if ((he = ht->table[i]) == NULL) continue;
846         while(he) {
847             nextHe = he->next;
848             Jim_FreeEntryKey(ht, he);
849             Jim_FreeEntryVal(ht, he);
850             Jim_Free(he);
851             ht->used--;
852             he = nextHe;
853         }
854     }
855     /* Free the table and the allocated cache structure */
856     Jim_Free(ht->table);
857     /* Re-initialize the table */
858     JimResetHashTable(ht);
859     return JIM_OK; /* never fails */
860 }
861
862 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
863 {
864     Jim_HashEntry *he;
865     unsigned int h;
866
867     if (ht->size == 0) return NULL;
868     h = Jim_HashKey(ht, key) & ht->sizemask;
869     he = ht->table[h];
870     while(he) {
871         if (Jim_CompareHashKeys(ht, key, he->key))
872             return he;
873         he = he->next;
874     }
875     return NULL;
876 }
877
878 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
879 {
880     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
881
882     iter->ht = ht;
883     iter->index = -1;
884     iter->entry = NULL;
885     iter->nextEntry = NULL;
886     return iter;
887 }
888
889 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
890 {
891     while (1) {
892         if (iter->entry == NULL) {
893             iter->index++;
894             if (iter->index >=
895                     (signed)iter->ht->size) break;
896             iter->entry = iter->ht->table[iter->index];
897         } else {
898             iter->entry = iter->nextEntry;
899         }
900         if (iter->entry) {
901             /* We need to save the 'next' here, the iterator user
902              * may delete the entry we are returning. */
903             iter->nextEntry = iter->entry->next;
904             return iter->entry;
905         }
906     }
907     return NULL;
908 }
909
910 /* ------------------------- private functions ------------------------------ */
911
912 /* Expand the hash table if needed */
913 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
914 {
915     /* If the hash table is empty expand it to the intial size,
916      * if the table is "full" dobule its size. */
917     if (ht->size == 0)
918         return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
919     if (ht->size == ht->used)
920         return Jim_ExpandHashTable(ht, ht->size*2);
921     return JIM_OK;
922 }
923
924 /* Our hash table capability is a power of two */
925 static unsigned int JimHashTableNextPower(unsigned int size)
926 {
927     unsigned int i = JIM_HT_INITIAL_SIZE;
928
929     if (size >= 2147483648U)
930         return 2147483648U;
931     while(1) {
932         if (i >= size)
933             return i;
934         i *= 2;
935     }
936 }
937
938 /* Returns the index of a free slot that can be populated with
939  * an hash entry for the given 'key'.
940  * If the key already exists, -1 is returned. */
941 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
942 {
943     unsigned int h;
944     Jim_HashEntry *he;
945
946     /* Expand the hashtable if needed */
947     if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
948         return -1;
949     /* Compute the key hash value */
950     h = Jim_HashKey(ht, key) & ht->sizemask;
951     /* Search if this slot does not already contain the given key */
952     he = ht->table[h];
953     while(he) {
954         if (Jim_CompareHashKeys(ht, key, he->key))
955             return -1;
956         he = he->next;
957     }
958     return h;
959 }
960
961 /* ----------------------- StringCopy Hash Table Type ------------------------*/
962
963 static unsigned int JimStringCopyHTHashFunction(const void *key)
964 {
965     return Jim_GenHashFunction(key, strlen(key));
966 }
967
968 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
969 {
970     int len = strlen(key);
971     char *copy = Jim_Alloc(len+1);
972     JIM_NOTUSED(privdata);
973
974     memcpy(copy, key, len);
975     copy[len] = '\0';
976     return copy;
977 }
978
979 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
980 {
981     int len = strlen(val);
982     char *copy = Jim_Alloc(len+1);
983     JIM_NOTUSED(privdata);
984
985     memcpy(copy, val, len);
986     copy[len] = '\0';
987     return copy;
988 }
989
990 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
991         const void *key2)
992 {
993     JIM_NOTUSED(privdata);
994
995     return strcmp(key1, key2) == 0;
996 }
997
998 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
999 {
1000     JIM_NOTUSED(privdata);
1001
1002     Jim_Free((void*)key); /* ATTENTION: const cast */
1003 }
1004
1005 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
1006 {
1007     JIM_NOTUSED(privdata);
1008
1009     Jim_Free((void*)val); /* ATTENTION: const cast */
1010 }
1011
1012 static Jim_HashTableType JimStringCopyHashTableType = {
1013     JimStringCopyHTHashFunction,        /* hash function */
1014     JimStringCopyHTKeyDup,              /* key dup */
1015     NULL,                               /* val dup */
1016     JimStringCopyHTKeyCompare,          /* key compare */
1017     JimStringCopyHTKeyDestructor,       /* key destructor */
1018     NULL                                /* val destructor */
1019 };
1020
1021 /* This is like StringCopy but does not auto-duplicate the key.
1022  * It's used for intepreter's shared strings. */
1023 static Jim_HashTableType JimSharedStringsHashTableType = {
1024     JimStringCopyHTHashFunction,        /* hash function */
1025     NULL,                               /* key dup */
1026     NULL,                               /* val dup */
1027     JimStringCopyHTKeyCompare,          /* key compare */
1028     JimStringCopyHTKeyDestructor,       /* key destructor */
1029     NULL                                /* val destructor */
1030 };
1031
1032 /* This is like StringCopy but also automatically handle dynamic
1033  * allocated C strings as values. */
1034 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
1035     JimStringCopyHTHashFunction,        /* hash function */
1036     JimStringCopyHTKeyDup,              /* key dup */
1037     JimStringKeyValCopyHTValDup,        /* val dup */
1038     JimStringCopyHTKeyCompare,          /* key compare */
1039     JimStringCopyHTKeyDestructor,       /* key destructor */
1040     JimStringKeyValCopyHTValDestructor, /* val destructor */
1041 };
1042
1043 typedef struct AssocDataValue {
1044     Jim_InterpDeleteProc *delProc;
1045     void *data;
1046 } AssocDataValue;
1047
1048 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
1049 {
1050     AssocDataValue *assocPtr = (AssocDataValue *)data;
1051     if (assocPtr->delProc != NULL)
1052         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
1053     Jim_Free(data);
1054 }
1055
1056 static Jim_HashTableType JimAssocDataHashTableType = {
1057     JimStringCopyHTHashFunction,         /* hash function */
1058     JimStringCopyHTKeyDup,               /* key dup */
1059     NULL,                                /* val dup */
1060     JimStringCopyHTKeyCompare,           /* key compare */
1061     JimStringCopyHTKeyDestructor,        /* key destructor */
1062     JimAssocDataHashTableValueDestructor /* val destructor */
1063 };
1064
1065 /* -----------------------------------------------------------------------------
1066  * Stack - This is a simple generic stack implementation. It is used for
1067  * example in the 'expr' expression compiler.
1068  * ---------------------------------------------------------------------------*/
1069 void Jim_InitStack(Jim_Stack *stack)
1070 {
1071     stack->len = 0;
1072     stack->maxlen = 0;
1073     stack->vector = NULL;
1074 }
1075
1076 void Jim_FreeStack(Jim_Stack *stack)
1077 {
1078     Jim_Free(stack->vector);
1079 }
1080
1081 int Jim_StackLen(Jim_Stack *stack)
1082 {
1083     return stack->len;
1084 }
1085
1086 void Jim_StackPush(Jim_Stack *stack, void *element) {
1087     int neededLen = stack->len+1;
1088     if (neededLen > stack->maxlen) {
1089         stack->maxlen = neededLen*2;
1090         stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1091     }
1092     stack->vector[stack->len] = element;
1093     stack->len++;
1094 }
1095
1096 void *Jim_StackPop(Jim_Stack *stack)
1097 {
1098     if (stack->len == 0) return NULL;
1099     stack->len--;
1100     return stack->vector[stack->len];
1101 }
1102
1103 void *Jim_StackPeek(Jim_Stack *stack)
1104 {
1105     if (stack->len == 0) return NULL;
1106     return stack->vector[stack->len-1];
1107 }
1108
1109 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1110 {
1111     int i;
1112
1113     for (i = 0; i < stack->len; i++)
1114         freeFunc(stack->vector[i]);
1115 }
1116
1117 /* -----------------------------------------------------------------------------
1118  * Parser
1119  * ---------------------------------------------------------------------------*/
1120
1121 /* Token types */
1122 #define JIM_TT_NONE -1        /* No token returned */
1123 #define JIM_TT_STR 0        /* simple string */
1124 #define JIM_TT_ESC 1        /* string that needs escape chars conversion */
1125 #define JIM_TT_VAR 2        /* var substitution */
1126 #define JIM_TT_DICTSUGAR 3    /* Syntax sugar for [dict get], $foo(bar) */
1127 #define JIM_TT_CMD 4        /* command substitution */
1128 #define JIM_TT_SEP 5        /* word separator */
1129 #define JIM_TT_EOL 6        /* line separator */
1130
1131 /* Additional token types needed for expressions */
1132 #define JIM_TT_SUBEXPR_START 7
1133 #define JIM_TT_SUBEXPR_END 8
1134 #define JIM_TT_EXPR_NUMBER 9
1135 #define JIM_TT_EXPR_OPERATOR 10
1136
1137 /* Parser states */
1138 #define JIM_PS_DEF 0        /* Default state */
1139 #define JIM_PS_QUOTE 1        /* Inside "" */
1140
1141 /* Parser context structure. The same context is used both to parse
1142  * Tcl scripts and lists. */
1143 struct JimParserCtx {
1144     const char *prg;     /* Program text */
1145     const char *p;       /* Pointer to the point of the program we are parsing */
1146     int len;             /* Left length of 'prg' */
1147     int linenr;          /* Current line number */
1148     const char *tstart;
1149     const char *tend;    /* Returned token is at tstart-tend in 'prg'. */
1150     int tline;           /* Line number of the returned token */
1151     int tt;              /* Token type */
1152     int eof;             /* Non zero if EOF condition is true. */
1153     int state;           /* Parser state */
1154     int comment;         /* Non zero if the next chars may be a comment. */
1155 };
1156
1157 #define JimParserEof(c) ((c)->eof)
1158 #define JimParserTstart(c) ((c)->tstart)
1159 #define JimParserTend(c) ((c)->tend)
1160 #define JimParserTtype(c) ((c)->tt)
1161 #define JimParserTline(c) ((c)->tline)
1162
1163 static int JimParseScript(struct JimParserCtx *pc);
1164 static int JimParseSep(struct JimParserCtx *pc);
1165 static int JimParseEol(struct JimParserCtx *pc);
1166 static int JimParseCmd(struct JimParserCtx *pc);
1167 static int JimParseVar(struct JimParserCtx *pc);
1168 static int JimParseBrace(struct JimParserCtx *pc);
1169 static int JimParseStr(struct JimParserCtx *pc);
1170 static int JimParseComment(struct JimParserCtx *pc);
1171 static char *JimParserGetToken(struct JimParserCtx *pc,
1172         int *lenPtr, int *typePtr, int *linePtr);
1173
1174 /* Initialize a parser context.
1175  * 'prg' is a pointer to the program text, linenr is the line
1176  * number of the first line contained in the program. */
1177 void JimParserInit(struct JimParserCtx *pc, const char *prg, 
1178         int len, int linenr)
1179 {
1180     pc->prg = prg;
1181     pc->p = prg;
1182     pc->len = len;
1183     pc->tstart = NULL;
1184     pc->tend = NULL;
1185     pc->tline = 0;
1186     pc->tt = JIM_TT_NONE;
1187     pc->eof = 0;
1188     pc->state = JIM_PS_DEF;
1189     pc->linenr = linenr;
1190     pc->comment = 1;
1191 }
1192
1193 int JimParseScript(struct JimParserCtx *pc)
1194 {
1195     while(1) { /* the while is used to reiterate with continue if needed */
1196         if (!pc->len) {
1197             pc->tstart = pc->p;
1198             pc->tend = pc->p-1;
1199             pc->tline = pc->linenr;
1200             pc->tt = JIM_TT_EOL;
1201             pc->eof = 1;
1202             return JIM_OK;
1203         }
1204         switch(*(pc->p)) {
1205         case '\\':
1206             if (*(pc->p+1) == '\n')
1207                 return JimParseSep(pc);
1208             else {
1209                 pc->comment = 0;
1210                 return JimParseStr(pc);
1211             }
1212             break;
1213         case ' ':
1214         case '\t':
1215         case '\r':
1216             if (pc->state == JIM_PS_DEF)
1217                 return JimParseSep(pc);
1218             else {
1219                 pc->comment = 0;
1220                 return JimParseStr(pc);
1221             }
1222             break;
1223         case '\n':
1224         case ';':
1225             pc->comment = 1;
1226             if (pc->state == JIM_PS_DEF)
1227                 return JimParseEol(pc);
1228             else
1229                 return JimParseStr(pc);
1230             break;
1231         case '[':
1232             pc->comment = 0;
1233             return JimParseCmd(pc);
1234             break;
1235         case '$':
1236             pc->comment = 0;
1237             if (JimParseVar(pc) == JIM_ERR) {
1238                 pc->tstart = pc->tend = pc->p++; pc->len--;
1239                 pc->tline = pc->linenr;
1240                 pc->tt = JIM_TT_STR;
1241                 return JIM_OK;
1242             } else
1243                 return JIM_OK;
1244             break;
1245         case '#':
1246             if (pc->comment) {
1247                 JimParseComment(pc);
1248                 continue;
1249             } else {
1250                 return JimParseStr(pc);
1251             }
1252         default:
1253             pc->comment = 0;
1254             return JimParseStr(pc);
1255             break;
1256         }
1257         return JIM_OK;
1258     }
1259 }
1260
1261 int JimParseSep(struct JimParserCtx *pc)
1262 {
1263     pc->tstart = pc->p;
1264     pc->tline = pc->linenr;
1265     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1266            (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1267         if (*pc->p == '\\') {
1268             pc->p++; pc->len--;
1269             pc->linenr++;
1270         }
1271         pc->p++; pc->len--;
1272     }
1273     pc->tend = pc->p-1;
1274     pc->tt = JIM_TT_SEP;
1275     return JIM_OK;
1276 }
1277
1278 int JimParseEol(struct JimParserCtx *pc)
1279 {
1280     pc->tstart = pc->p;
1281     pc->tline = pc->linenr;
1282     while (*pc->p == ' ' || *pc->p == '\n' ||
1283            *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1284         if (*pc->p == '\n')
1285             pc->linenr++;
1286         pc->p++; pc->len--;
1287     }
1288     pc->tend = pc->p-1;
1289     pc->tt = JIM_TT_EOL;
1290     return JIM_OK;
1291 }
1292
1293 /* Todo. Don't stop if ']' appears inside {} or quoted.
1294  * Also should handle the case of puts [string length "]"] */
1295 int JimParseCmd(struct JimParserCtx *pc)
1296 {
1297     int level = 1;
1298     int blevel = 0;
1299
1300     pc->tstart = ++pc->p; pc->len--;
1301     pc->tline = pc->linenr;
1302     while (1) {
1303         if (pc->len == 0) {
1304             break;
1305         } else if (*pc->p == '[' && blevel == 0) {
1306             level++;
1307         } else if (*pc->p == ']' && blevel == 0) {
1308             level--;
1309             if (!level) break;
1310         } else if (*pc->p == '\\') {
1311             pc->p++; pc->len--;
1312         } else if (*pc->p == '{') {
1313             blevel++;
1314         } else if (*pc->p == '}') {
1315             if (blevel != 0)
1316                 blevel--;
1317         } else if (*pc->p == '\n')
1318             pc->linenr++;
1319         pc->p++; pc->len--;
1320     }
1321     pc->tend = pc->p-1;
1322     pc->tt = JIM_TT_CMD;
1323     if (*pc->p == ']') {
1324         pc->p++; pc->len--;
1325     }
1326     return JIM_OK;
1327 }
1328
1329 int JimParseVar(struct JimParserCtx *pc)
1330 {
1331     int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1332
1333     pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1334     pc->tline = pc->linenr;
1335     if (*pc->p == '{') {
1336         pc->tstart = ++pc->p; pc->len--;
1337         brace = 1;
1338     }
1339     if (brace) {
1340         while (!stop) {
1341             if (*pc->p == '}' || pc->len == 0) {
1342                 pc->tend = pc->p-1;
1343                 stop = 1;
1344                 if (pc->len == 0)
1345                     break;
1346             }
1347             else if (*pc->p == '\n')
1348                 pc->linenr++;
1349             pc->p++; pc->len--;
1350         }
1351     } else {
1352         /* Include leading colons */
1353         while (*pc->p == ':') {
1354             pc->p++;
1355             pc->len--;
1356         }
1357         while (!stop) {
1358             if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1359                 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1360                 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1361                 stop = 1;
1362             else {
1363                 pc->p++; pc->len--;
1364             }
1365         }
1366         /* Parse [dict get] syntax sugar. */
1367         if (*pc->p == '(') {
1368             while (*pc->p != ')' && pc->len) {
1369                 pc->p++; pc->len--;
1370                 if (*pc->p == '\\' && pc->len >= 2) {
1371                     pc->p += 2; pc->len -= 2;
1372                 }
1373             }
1374             if (*pc->p != '\0') {
1375                 pc->p++; pc->len--;
1376             }
1377             ttype = JIM_TT_DICTSUGAR;
1378         }
1379         pc->tend = pc->p-1;
1380     }
1381     /* Check if we parsed just the '$' character.
1382      * That's not a variable so an error is returned
1383      * to tell the state machine to consider this '$' just
1384      * a string. */
1385     if (pc->tstart == pc->p) {
1386         pc->p--; pc->len++;
1387         return JIM_ERR;
1388     }
1389     pc->tt = ttype;
1390     return JIM_OK;
1391 }
1392
1393 int JimParseBrace(struct JimParserCtx *pc)
1394 {
1395     int level = 1;
1396
1397     pc->tstart = ++pc->p; pc->len--;
1398     pc->tline = pc->linenr;
1399     while (1) {
1400         if (*pc->p == '\\' && pc->len >= 2) {
1401             pc->p++; pc->len--;
1402             if (*pc->p == '\n')
1403                 pc->linenr++;
1404         } else if (*pc->p == '{') {
1405             level++;
1406         } else if (pc->len == 0 || *pc->p == '}') {
1407             level--;
1408             if (pc->len == 0 || level == 0) {
1409                 pc->tend = pc->p-1;
1410                 if (pc->len != 0) {
1411                     pc->p++; pc->len--;
1412                 }
1413                 pc->tt = JIM_TT_STR;
1414                 return JIM_OK;
1415             }
1416         } else if (*pc->p == '\n') {
1417             pc->linenr++;
1418         }
1419         pc->p++; pc->len--;
1420     }
1421     return JIM_OK; /* unreached */
1422 }
1423
1424 int JimParseStr(struct JimParserCtx *pc)
1425 {
1426     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1427             pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1428     if (newword && *pc->p == '{') {
1429         return JimParseBrace(pc);
1430     } else if (newword && *pc->p == '"') {
1431         pc->state = JIM_PS_QUOTE;
1432         pc->p++; pc->len--;
1433     }
1434     pc->tstart = pc->p;
1435     pc->tline = pc->linenr;
1436     while (1) {
1437         if (pc->len == 0) {
1438             pc->tend = pc->p-1;
1439             pc->tt = JIM_TT_ESC;
1440             return JIM_OK;
1441         }
1442         switch(*pc->p) {
1443         case '\\':
1444             if (pc->state == JIM_PS_DEF &&
1445                 *(pc->p+1) == '\n') {
1446                 pc->tend = pc->p-1;
1447                 pc->tt = JIM_TT_ESC;
1448                 return JIM_OK;
1449             }
1450             if (pc->len >= 2) {
1451                 pc->p++; pc->len--;
1452             }
1453             break;
1454         case '$':
1455         case '[':
1456             pc->tend = pc->p-1;
1457             pc->tt = JIM_TT_ESC;
1458             return JIM_OK;
1459         case ' ':
1460         case '\t':
1461         case '\n':
1462         case '\r':
1463         case ';':
1464             if (pc->state == JIM_PS_DEF) {
1465                 pc->tend = pc->p-1;
1466                 pc->tt = JIM_TT_ESC;
1467                 return JIM_OK;
1468             } else if (*pc->p == '\n') {
1469                 pc->linenr++;
1470             }
1471             break;
1472         case '"':
1473             if (pc->state == JIM_PS_QUOTE) {
1474                 pc->tend = pc->p-1;
1475                 pc->tt = JIM_TT_ESC;
1476                 pc->p++; pc->len--;
1477                 pc->state = JIM_PS_DEF;
1478                 return JIM_OK;
1479             }
1480             break;
1481         }
1482         pc->p++; pc->len--;
1483     }
1484     return JIM_OK; /* unreached */
1485 }
1486
1487 int JimParseComment(struct JimParserCtx *pc)
1488 {
1489     while (*pc->p) {
1490         if (*pc->p == '\n') {
1491             pc->linenr++;
1492             if (*(pc->p-1) != '\\') {
1493                 pc->p++; pc->len--;
1494                 return JIM_OK;
1495             }
1496         }
1497         pc->p++; pc->len--;
1498     }
1499     return JIM_OK;
1500 }
1501
1502 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1503 static int xdigitval(int c)
1504 {
1505     if (c >= '0' && c <= '9') return c-'0';
1506     if (c >= 'a' && c <= 'f') return c-'a'+10;
1507     if (c >= 'A' && c <= 'F') return c-'A'+10;
1508     return -1;
1509 }
1510
1511 static int odigitval(int c)
1512 {
1513     if (c >= '0' && c <= '7') return c-'0';
1514     return -1;
1515 }
1516
1517 /* Perform Tcl escape substitution of 's', storing the result
1518  * string into 'dest'. The escaped string is guaranteed to
1519  * be the same length or shorted than the source string.
1520  * Slen is the length of the string at 's', if it's -1 the string
1521  * length will be calculated by the function.
1522  *
1523  * The function returns the length of the resulting string. */
1524 static int JimEscape(char *dest, const char *s, int slen)
1525 {
1526     char *p = dest;
1527     int i, len;
1528     
1529     if (slen == -1)
1530         slen = strlen(s);
1531
1532     for (i = 0; i < slen; i++) {
1533         switch(s[i]) {
1534         case '\\':
1535             switch(s[i+1]) {
1536             case 'a': *p++ = 0x7; i++; break;
1537             case 'b': *p++ = 0x8; i++; break;
1538             case 'f': *p++ = 0xc; i++; break;
1539             case 'n': *p++ = 0xa; i++; break;
1540             case 'r': *p++ = 0xd; i++; break;
1541             case 't': *p++ = 0x9; i++; break;
1542             case 'v': *p++ = 0xb; i++; break;
1543             case '\0': *p++ = '\\'; i++; break;
1544             case '\n': *p++ = ' '; i++; break;
1545             default:
1546                   if (s[i+1] == 'x') {
1547                     int val = 0;
1548                     int c = xdigitval(s[i+2]);
1549                     if (c == -1) {
1550                         *p++ = 'x';
1551                         i++;
1552                         break;
1553                     }
1554                     val = c;
1555                     c = xdigitval(s[i+3]);
1556                     if (c == -1) {
1557                         *p++ = val;
1558                         i += 2;
1559                         break;
1560                     }
1561                     val = (val*16)+c;
1562                     *p++ = val;
1563                     i += 3;
1564                     break;
1565                   } else if (s[i+1] >= '0' && s[i+1] <= '7')
1566                   {
1567                     int val = 0;
1568                     int c = odigitval(s[i+1]);
1569                     val = c;
1570                     c = odigitval(s[i+2]);
1571                     if (c == -1) {
1572                         *p++ = val;
1573                         i ++;
1574                         break;
1575                     }
1576                     val = (val*8)+c;
1577                     c = odigitval(s[i+3]);
1578                     if (c == -1) {
1579                         *p++ = val;
1580                         i += 2;
1581                         break;
1582                     }
1583                     val = (val*8)+c;
1584                     *p++ = val;
1585                     i += 3;
1586                   } else {
1587                     *p++ = s[i+1];
1588                     i++;
1589                   }
1590                   break;
1591             }
1592             break;
1593         default:
1594             *p++ = s[i];
1595             break;
1596         }
1597     }
1598     len = p-dest;
1599     *p++ = '\0';
1600     return len;
1601 }
1602
1603 /* Returns a dynamically allocated copy of the current token in the
1604  * parser context. The function perform conversion of escapes if
1605  * the token is of type JIM_TT_ESC.
1606  *
1607  * Note that after the conversion, tokens that are grouped with
1608  * braces in the source code, are always recognizable from the
1609  * identical string obtained in a different way from the type.
1610  *
1611  * For exmple the string:
1612  *
1613  * {expand}$a
1614  * 
1615  * will return as first token "expand", of type JIM_TT_STR
1616  *
1617  * While the string:
1618  *
1619  * expand$a
1620  *
1621  * will return as first token "expand", of type JIM_TT_ESC
1622  */
1623 char *JimParserGetToken(struct JimParserCtx *pc,
1624         int *lenPtr, int *typePtr, int *linePtr)
1625 {
1626     const char *start, *end;
1627     char *token;
1628     int len;
1629
1630     start = JimParserTstart(pc);
1631     end = JimParserTend(pc);
1632     if (start > end) {
1633         if (lenPtr) *lenPtr = 0;
1634         if (typePtr) *typePtr = JimParserTtype(pc);
1635         if (linePtr) *linePtr = JimParserTline(pc);
1636         token = Jim_Alloc(1);
1637         token[0] = '\0';
1638         return token;
1639     }
1640     len = (end-start)+1;
1641     token = Jim_Alloc(len+1);
1642     if (JimParserTtype(pc) != JIM_TT_ESC) {
1643         /* No escape conversion needed? Just copy it. */
1644         memcpy(token, start, len);
1645         token[len] = '\0';
1646     } else {
1647         /* Else convert the escape chars. */
1648         len = JimEscape(token, start, len);
1649     }
1650     if (lenPtr) *lenPtr = len;
1651     if (typePtr) *typePtr = JimParserTtype(pc);
1652     if (linePtr) *linePtr = JimParserTline(pc);
1653     return token;
1654 }
1655
1656 /* The following functin is not really part of the parsing engine of Jim,
1657  * but it somewhat related. Given an string and its length, it tries
1658  * to guess if the script is complete or there are instead " " or { }
1659  * open and not completed. This is useful for interactive shells
1660  * implementation and for [info complete].
1661  *
1662  * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1663  * '{' on scripts incomplete missing one or more '}' to be balanced.
1664  * '"' on scripts incomplete missing a '"' char.
1665  *
1666  * If the script is complete, 1 is returned, otherwise 0. */
1667 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1668 {
1669     int level = 0;
1670     int state = ' ';
1671
1672     while(len) {
1673         switch (*s) {
1674             case '\\':
1675                 if (len > 1)
1676                     s++;
1677                 break;
1678             case '"':
1679                 if (state == ' ') {
1680                     state = '"';
1681                 } else if (state == '"') {
1682                     state = ' ';
1683                 }
1684                 break;
1685             case '{':
1686                 if (state == '{') {
1687                     level++;
1688                 } else if (state == ' ') {
1689                     state = '{';
1690                     level++;
1691                 }
1692                 break;
1693             case '}':
1694                 if (state == '{') {
1695                     level--;
1696                     if (level == 0)
1697                         state = ' ';
1698                 }
1699                 break;
1700         }
1701         s++;
1702         len--;
1703     }
1704     if (stateCharPtr)
1705         *stateCharPtr = state;
1706     return state == ' ';
1707 }
1708
1709 /* -----------------------------------------------------------------------------
1710  * Tcl Lists parsing
1711  * ---------------------------------------------------------------------------*/
1712 static int JimParseListSep(struct JimParserCtx *pc);
1713 static int JimParseListStr(struct JimParserCtx *pc);
1714
1715 int JimParseList(struct JimParserCtx *pc)
1716 {
1717     if (pc->len == 0) {
1718         pc->tstart = pc->tend = pc->p;
1719         pc->tline = pc->linenr;
1720         pc->tt = JIM_TT_EOL;
1721         pc->eof = 1;
1722         return JIM_OK;
1723     }
1724     switch(*pc->p) {
1725     case ' ':
1726     case '\n':
1727     case '\t':
1728     case '\r':
1729         if (pc->state == JIM_PS_DEF)
1730             return JimParseListSep(pc);
1731         else
1732             return JimParseListStr(pc);
1733         break;
1734     default:
1735         return JimParseListStr(pc);
1736         break;
1737     }
1738     return JIM_OK;
1739 }
1740
1741 int JimParseListSep(struct JimParserCtx *pc)
1742 {
1743     pc->tstart = pc->p;
1744     pc->tline = pc->linenr;
1745     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1746     {
1747         pc->p++; pc->len--;
1748     }
1749     pc->tend = pc->p-1;
1750     pc->tt = JIM_TT_SEP;
1751     return JIM_OK;
1752 }
1753
1754 int JimParseListStr(struct JimParserCtx *pc)
1755 {
1756     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1757             pc->tt == JIM_TT_NONE);
1758     if (newword && *pc->p == '{') {
1759         return JimParseBrace(pc);
1760     } else if (newword && *pc->p == '"') {
1761         pc->state = JIM_PS_QUOTE;
1762         pc->p++; pc->len--;
1763     }
1764     pc->tstart = pc->p;
1765     pc->tline = pc->linenr;
1766     while (1) {
1767         if (pc->len == 0) {
1768             pc->tend = pc->p-1;
1769             pc->tt = JIM_TT_ESC;
1770             return JIM_OK;
1771         }
1772         switch(*pc->p) {
1773         case '\\':
1774             pc->p++; pc->len--;
1775             break;
1776         case ' ':
1777         case '\t':
1778         case '\n':
1779         case '\r':
1780             if (pc->state == JIM_PS_DEF) {
1781                 pc->tend = pc->p-1;
1782                 pc->tt = JIM_TT_ESC;
1783                 return JIM_OK;
1784             } else if (*pc->p == '\n') {
1785                 pc->linenr++;
1786             }
1787             break;
1788         case '"':
1789             if (pc->state == JIM_PS_QUOTE) {
1790                 pc->tend = pc->p-1;
1791                 pc->tt = JIM_TT_ESC;
1792                 pc->p++; pc->len--;
1793                 pc->state = JIM_PS_DEF;
1794                 return JIM_OK;
1795             }
1796             break;
1797         }
1798         pc->p++; pc->len--;
1799     }
1800     return JIM_OK; /* unreached */
1801 }
1802
1803 /* -----------------------------------------------------------------------------
1804  * Jim_Obj related functions
1805  * ---------------------------------------------------------------------------*/
1806
1807 /* Return a new initialized object. */
1808 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1809 {
1810     Jim_Obj *objPtr;
1811
1812     /* -- Check if there are objects in the free list -- */
1813     if (interp->freeList != NULL) {
1814         /* -- Unlink the object from the free list -- */
1815         objPtr = interp->freeList;
1816         interp->freeList = objPtr->nextObjPtr;
1817     } else {
1818         /* -- No ready to use objects: allocate a new one -- */
1819         objPtr = Jim_Alloc(sizeof(*objPtr));
1820     }
1821
1822     /* Object is returned with refCount of 0. Every
1823      * kind of GC implemented should take care to don't try
1824      * to scan objects with refCount == 0. */
1825     objPtr->refCount = 0;
1826     /* All the other fields are left not initialized to save time.
1827      * The caller will probably want set they to the right
1828      * value anyway. */
1829
1830     /* -- Put the object into the live list -- */
1831     objPtr->prevObjPtr = NULL;
1832     objPtr->nextObjPtr = interp->liveList;
1833     if (interp->liveList)
1834         interp->liveList->prevObjPtr = objPtr;
1835     interp->liveList = objPtr;
1836
1837     return objPtr;
1838 }
1839
1840 /* Free an object. Actually objects are never freed, but
1841  * just moved to the free objects list, where they will be
1842  * reused by Jim_NewObj(). */
1843 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1844 {
1845     /* Check if the object was already freed, panic. */
1846     if (objPtr->refCount != 0)  {
1847         Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1848                 objPtr->refCount);
1849     }
1850     /* Free the internal representation */
1851     Jim_FreeIntRep(interp, objPtr);
1852     /* Free the string representation */
1853     if (objPtr->bytes != NULL) {
1854         if (objPtr->bytes != JimEmptyStringRep)
1855             Jim_Free(objPtr->bytes);
1856     }
1857     /* Unlink the object from the live objects list */
1858     if (objPtr->prevObjPtr)
1859         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1860     if (objPtr->nextObjPtr)
1861         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1862     if (interp->liveList == objPtr)
1863         interp->liveList = objPtr->nextObjPtr;
1864     /* Link the object into the free objects list */
1865     objPtr->prevObjPtr = NULL;
1866     objPtr->nextObjPtr = interp->freeList;
1867     if (interp->freeList)
1868         interp->freeList->prevObjPtr = objPtr;
1869     interp->freeList = objPtr;
1870     objPtr->refCount = -1;
1871 }
1872
1873 /* Invalidate the string representation of an object. */
1874 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1875 {
1876     if (objPtr->bytes != NULL) {
1877         if (objPtr->bytes != JimEmptyStringRep)
1878             Jim_Free(objPtr->bytes);
1879     }
1880     objPtr->bytes = NULL;
1881 }
1882
1883 #define Jim_SetStringRep(o, b, l) \
1884     do { (o)->bytes = b; (o)->length = l; } while (0)
1885
1886 /* Set the initial string representation for an object.
1887  * Does not try to free an old one. */
1888 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1889 {
1890     if (length == 0) {
1891         objPtr->bytes = JimEmptyStringRep;
1892         objPtr->length = 0;
1893     } else {
1894         objPtr->bytes = Jim_Alloc(length+1);
1895         objPtr->length = length;
1896         memcpy(objPtr->bytes, bytes, length);
1897         objPtr->bytes[length] = '\0';
1898     }
1899 }
1900
1901 /* Duplicate an object. The returned object has refcount = 0. */
1902 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1903 {
1904     Jim_Obj *dupPtr;
1905
1906     dupPtr = Jim_NewObj(interp);
1907     if (objPtr->bytes == NULL) {
1908         /* Object does not have a valid string representation. */
1909         dupPtr->bytes = NULL;
1910     } else {
1911         Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1912     }
1913     if (objPtr->typePtr != NULL) {
1914         if (objPtr->typePtr->dupIntRepProc == NULL) {
1915             dupPtr->internalRep = objPtr->internalRep;
1916         } else {
1917             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1918         }
1919         dupPtr->typePtr = objPtr->typePtr;
1920     } else {
1921         dupPtr->typePtr = NULL;
1922     }
1923     return dupPtr;
1924 }
1925
1926 /* Return the string representation for objPtr. If the object
1927  * string representation is invalid, calls the method to create
1928  * a new one starting from the internal representation of the object. */
1929 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1930 {
1931     if (objPtr->bytes == NULL) {
1932         /* Invalid string repr. Generate it. */
1933         if (objPtr->typePtr->updateStringProc == NULL) {
1934             Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1935                 objPtr->typePtr->name);
1936         }
1937         objPtr->typePtr->updateStringProc(objPtr);
1938     }
1939     if (lenPtr)
1940         *lenPtr = objPtr->length;
1941     return objPtr->bytes;
1942 }
1943
1944 /* Just returns the length of the object's string rep */
1945 int Jim_Length(Jim_Obj *objPtr)
1946 {
1947     int len;
1948
1949     Jim_GetString(objPtr, &len);
1950     return len;
1951 }
1952
1953 /* -----------------------------------------------------------------------------
1954  * String Object
1955  * ---------------------------------------------------------------------------*/
1956 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1957 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1958
1959 static Jim_ObjType stringObjType = {
1960     "string",
1961     NULL,
1962     DupStringInternalRep,
1963     NULL,
1964     JIM_TYPE_REFERENCES,
1965 };
1966
1967 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1968 {
1969     JIM_NOTUSED(interp);
1970
1971     /* This is a bit subtle: the only caller of this function
1972      * should be Jim_DuplicateObj(), that will copy the
1973      * string representaion. After the copy, the duplicated
1974      * object will not have more room in teh buffer than
1975      * srcPtr->length bytes. So we just set it to length. */
1976     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1977 }
1978
1979 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1980 {
1981     /* Get a fresh string representation. */
1982     (void) Jim_GetString(objPtr, NULL);
1983     /* Free any other internal representation. */
1984     Jim_FreeIntRep(interp, objPtr);
1985     /* Set it as string, i.e. just set the maxLength field. */
1986     objPtr->typePtr = &stringObjType;
1987     objPtr->internalRep.strValue.maxLength = objPtr->length;
1988     return JIM_OK;
1989 }
1990
1991 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1992 {
1993     Jim_Obj *objPtr = Jim_NewObj(interp);
1994
1995     if (len == -1)
1996         len = strlen(s);
1997     /* Alloc/Set the string rep. */
1998     if (len == 0) {
1999         objPtr->bytes = JimEmptyStringRep;
2000         objPtr->length = 0;
2001     } else {
2002         objPtr->bytes = Jim_Alloc(len+1);
2003         objPtr->length = len;
2004         memcpy(objPtr->bytes, s, len);
2005         objPtr->bytes[len] = '\0';
2006     }
2007
2008     /* No typePtr field for the vanilla string object. */
2009     objPtr->typePtr = NULL;
2010     return objPtr;
2011 }
2012
2013 /* This version does not try to duplicate the 's' pointer, but
2014  * use it directly. */
2015 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
2016 {
2017     Jim_Obj *objPtr = Jim_NewObj(interp);
2018
2019     if (len == -1)
2020         len = strlen(s);
2021     Jim_SetStringRep(objPtr, s, len);
2022     objPtr->typePtr = NULL;
2023     return objPtr;
2024 }
2025
2026 /* Low-level string append. Use it only against objects
2027  * of type "string". */
2028 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
2029 {
2030     int needlen;
2031
2032     if (len == -1)
2033         len = strlen(str);
2034     needlen = objPtr->length + len;
2035     if (objPtr->internalRep.strValue.maxLength < needlen ||
2036         objPtr->internalRep.strValue.maxLength == 0) {
2037         if (objPtr->bytes == JimEmptyStringRep) {
2038             objPtr->bytes = Jim_Alloc((needlen*2)+1);
2039         } else {
2040             objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
2041         }
2042         objPtr->internalRep.strValue.maxLength = needlen*2;
2043     }
2044     memcpy(objPtr->bytes + objPtr->length, str, len);
2045     objPtr->bytes[objPtr->length+len] = '\0';
2046     objPtr->length += len;
2047 }
2048
2049 /* Low-level wrapper to append an object. */
2050 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
2051 {
2052     int len;
2053     const char *str;
2054
2055     str = Jim_GetString(appendObjPtr, &len);
2056     StringAppendString(objPtr, str, len);
2057 }
2058
2059 /* Higher level API to append strings to objects. */
2060 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
2061         int len)
2062 {
2063     if (Jim_IsShared(objPtr))
2064         Jim_Panic(interp,"Jim_AppendString called with shared object");
2065     if (objPtr->typePtr != &stringObjType)
2066         SetStringFromAny(interp, objPtr);
2067     StringAppendString(objPtr, str, len);
2068 }
2069
2070 void Jim_AppendString_sprintf( Jim_Interp *interp, Jim_Obj *objPtr, const char *fmt, ... )
2071 {
2072         char *buf;
2073         va_list ap;
2074
2075         va_start( ap, fmt );
2076         buf = jim_vasprintf( fmt, ap );
2077         va_end(ap);
2078
2079         if( buf ){
2080                 Jim_AppendString( interp, objPtr, buf, -1 );
2081                 jim_vasprintf_done(buf);
2082         }
2083 }
2084
2085
2086 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
2087         Jim_Obj *appendObjPtr)
2088 {
2089     int len;
2090     const char *str;
2091
2092     str = Jim_GetString(appendObjPtr, &len);
2093     Jim_AppendString(interp, objPtr, str, len);
2094 }
2095
2096 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2097 {
2098     va_list ap;
2099
2100     if (objPtr->typePtr != &stringObjType)
2101         SetStringFromAny(interp, objPtr);
2102     va_start(ap, objPtr);
2103     while (1) {
2104         char *s = va_arg(ap, char*);
2105
2106         if (s == NULL) break;
2107         Jim_AppendString(interp, objPtr, s, -1);
2108     }
2109     va_end(ap);
2110 }
2111
2112 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2113 {
2114     const char *aStr, *bStr;
2115     int aLen, bLen, i;
2116
2117     if (aObjPtr == bObjPtr) return 1;
2118     aStr = Jim_GetString(aObjPtr, &aLen);
2119     bStr = Jim_GetString(bObjPtr, &bLen);
2120     if (aLen != bLen) return 0;
2121     if (nocase == 0)
2122         return memcmp(aStr, bStr, aLen) == 0;
2123     for (i = 0; i < aLen; i++) {
2124         if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2125             return 0;
2126     }
2127     return 1;
2128 }
2129
2130 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2131         int nocase)
2132 {
2133     const char *pattern, *string;
2134     int patternLen, stringLen;
2135
2136     pattern = Jim_GetString(patternObjPtr, &patternLen);
2137     string = Jim_GetString(objPtr, &stringLen);
2138     return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2139 }
2140
2141 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2142         Jim_Obj *secondObjPtr, int nocase)
2143 {
2144     const char *s1, *s2;
2145     int l1, l2;
2146
2147     s1 = Jim_GetString(firstObjPtr, &l1);
2148     s2 = Jim_GetString(secondObjPtr, &l2);
2149     return JimStringCompare(s1, l1, s2, l2, nocase);
2150 }
2151
2152 /* Convert a range, as returned by Jim_GetRange(), into
2153  * an absolute index into an object of the specified length.
2154  * This function may return negative values, or values
2155  * bigger or equal to the length of the list if the index
2156  * is out of range. */
2157 static int JimRelToAbsIndex(int len, int index)
2158 {
2159     if (index < 0)
2160         return len + index;
2161     return index;
2162 }
2163
2164 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2165  * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2166  * for implementation of commands like [string range] and [lrange].
2167  *
2168  * The resulting range is guaranteed to address valid elements of
2169  * the structure. */
2170 static void JimRelToAbsRange(int len, int first, int last,
2171         int *firstPtr, int *lastPtr, int *rangeLenPtr)
2172 {
2173     int rangeLen;
2174
2175     if (first > last) {
2176         rangeLen = 0;
2177     } else {
2178         rangeLen = last-first+1;
2179         if (rangeLen) {
2180             if (first < 0) {
2181                 rangeLen += first;
2182                 first = 0;
2183             }
2184             if (last >= len) {
2185                 rangeLen -= (last-(len-1));
2186                 last = len-1;
2187             }
2188         }
2189     }
2190     if (rangeLen < 0) rangeLen = 0;
2191
2192     *firstPtr = first;
2193     *lastPtr = last;
2194     *rangeLenPtr = rangeLen;
2195 }
2196
2197 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2198         Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2199 {
2200     int first, last;
2201     const char *str;
2202     int len, rangeLen;
2203
2204     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2205         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2206         return NULL;
2207     str = Jim_GetString(strObjPtr, &len);
2208     first = JimRelToAbsIndex(len, first);
2209     last = JimRelToAbsIndex(len, last);
2210     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2211     return Jim_NewStringObj(interp, str+first, rangeLen);
2212 }
2213
2214 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2215 {
2216     char *buf;
2217     int i;
2218     if (strObjPtr->typePtr != &stringObjType) {
2219         SetStringFromAny(interp, strObjPtr);
2220     }
2221
2222     buf = Jim_Alloc(strObjPtr->length+1);
2223
2224     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2225     for (i = 0; i < strObjPtr->length; i++)
2226         buf[i] = tolower(buf[i]);
2227     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2228 }
2229
2230 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2231 {
2232     char *buf;
2233     int i;
2234     if (strObjPtr->typePtr != &stringObjType) {
2235         SetStringFromAny(interp, strObjPtr);
2236     }
2237
2238     buf = Jim_Alloc(strObjPtr->length+1);
2239
2240     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2241     for (i = 0; i < strObjPtr->length; i++)
2242         buf[i] = toupper(buf[i]);
2243     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2244 }
2245
2246 /* This is the core of the [format] command.
2247  * TODO: Lots of things work - via a hack
2248  *       However, no format item can be >= JIM_MAX_FMT 
2249  */
2250 #define JIM_MAX_FMT 2048
2251 static Jim_Obj *Jim_FormatString_Inner(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2252         int objc, Jim_Obj *const *objv, char *sprintf_buf)
2253 {
2254     const char *fmt, *_fmt;
2255     int fmtLen;
2256     Jim_Obj *resObjPtr;
2257     
2258
2259     fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2260         _fmt = fmt;
2261     resObjPtr = Jim_NewStringObj(interp, "", 0);
2262     while (fmtLen) {
2263         const char *p = fmt;
2264         char spec[2], c;
2265         jim_wide wideValue;
2266                 double doubleValue;
2267                 /* we cheat and use Sprintf()! */
2268                 char fmt_str[100];
2269                 char *cp;
2270                 int width;
2271                 int ljust;
2272                 int zpad;
2273                 int spad;
2274                 int altfm;
2275                 int forceplus;
2276                 int prec;
2277                 int inprec;
2278                 int haveprec;
2279                 int accum;
2280
2281         while (*fmt != '%' && fmtLen) {
2282             fmt++; fmtLen--;
2283         }
2284         Jim_AppendString(interp, resObjPtr, p, fmt-p);
2285         if (fmtLen == 0)
2286             break;
2287         fmt++; fmtLen--; /* skip '%' */
2288                 zpad = 0;
2289                 spad = 0;
2290                 width = -1;
2291                 ljust = 0;
2292                 altfm = 0;
2293                 forceplus = 0;
2294                 inprec = 0;
2295                 haveprec = 0;
2296                 prec = -1; /* not found yet */
2297     next_fmt:
2298                 if( fmtLen <= 0 ){
2299                         break;
2300                 }
2301                 switch( *fmt ){
2302                         /* terminals */
2303         case 'b': /* binary - not all printfs() do this */
2304                 case 's': /* string */
2305                 case 'i': /* integer */
2306                 case 'd': /* decimal */
2307                 case 'x': /* hex */
2308                 case 'X': /* CAP hex */
2309                 case 'c': /* char */
2310                 case 'o': /* octal */
2311                 case 'u': /* unsigned */
2312                 case 'f': /* float */
2313                         break;
2314                         
2315                         /* non-terminals */
2316                 case '0': /* zero pad */
2317                         zpad = 1;
2318                         fmt++;  fmtLen--;
2319                         goto next_fmt;
2320                         break;
2321                 case '+':
2322                         forceplus = 1;
2323                         fmt++;  fmtLen--;
2324                         goto next_fmt;
2325                         break;
2326                 case ' ': /* sign space */
2327                         spad = 1;
2328                         fmt++;  fmtLen--;
2329                         goto next_fmt;
2330                         break;
2331                 case '-':
2332                         ljust = 1;
2333                         fmt++;  fmtLen--;
2334                         goto next_fmt;
2335                         break;
2336                 case '#':
2337                         altfm = 1;
2338                         fmt++; fmtLen--;
2339                         goto next_fmt;
2340                         
2341                 case '.':
2342                         inprec = 1;
2343                         fmt++; fmtLen--;
2344                         goto next_fmt;
2345                         break;
2346                 case '1':
2347                 case '2':
2348                 case '3':
2349                 case '4':
2350                 case '5':
2351                 case '6':
2352                 case '7':
2353                 case '8':
2354                 case '9':
2355                         accum = 0;
2356                         while( isdigit(*fmt) && (fmtLen > 0) ){
2357                                 accum = (accum * 10) + (*fmt - '0');
2358                                 fmt++;  fmtLen--;
2359                         }
2360                         if( inprec ){
2361                                 haveprec = 1;
2362                                 prec = accum;
2363                         } else {
2364                                 width = accum;
2365                         }
2366                         goto next_fmt;
2367                 case '*':
2368                         /* suck up the next item as an integer */
2369                         fmt++;  fmtLen--;
2370                         objc--;
2371                         if( objc <= 0 ){
2372                                 goto not_enough_args;
2373                         }
2374                         if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2375                                 Jim_FreeNewObj(interp, resObjPtr );
2376                                 return NULL;
2377                         }
2378                         if( inprec ){
2379                                 haveprec = 1;
2380                                 prec = wideValue;
2381                                 if( prec < 0 ){
2382                                         /* man 3 printf says */
2383                                         /* if prec is negative, it is zero */
2384                                         prec = 0;
2385                                 }
2386                         } else {
2387                         width = wideValue;
2388                         if( width < 0 ){
2389                                 ljust = 1;
2390                                 width = -width;
2391                         }
2392                         }
2393                         objv++;
2394                         goto next_fmt;
2395                         break;
2396                 }
2397                 
2398                 
2399                 if (*fmt != '%') {
2400             if (objc == 0) {
2401                         not_enough_args:
2402                 Jim_FreeNewObj(interp, resObjPtr);
2403                 Jim_SetResultString(interp,
2404                                                                         "not enough arguments for all format specifiers", -1);
2405                 return NULL;
2406             } else {
2407                 objc--;
2408             }
2409         }
2410                 
2411                 /*
2412                  * Create the formatter
2413                  * cause we cheat and use sprintf()
2414                  */
2415                 cp = fmt_str;
2416                 *cp++ = '%';
2417                 if( altfm ){
2418                         *cp++ = '#';
2419                 }
2420                 if( forceplus ){
2421                         *cp++ = '+';
2422                 } else if( spad ){
2423                         /* PLUS overrides */
2424                         *cp++ = ' ';
2425                 }
2426                 if( ljust ){
2427                         *cp++ = '-';
2428                 }
2429                 if( zpad  ){
2430                         *cp++ = '0';
2431                 }
2432                 if( width > 0 ){
2433                         sprintf( cp, "%d", width );
2434                         /* skip ahead */
2435                         cp = strchr(cp,0);
2436                 }
2437                 /* did we find a period? */
2438                 if( inprec ){
2439                         /* then add it */
2440                         *cp++ = '.';
2441                         /* did something occur after the period? */
2442                         if( haveprec ){
2443                                 sprintf( cp, "%d", prec );
2444                         }
2445                         cp = strchr(cp,0);
2446                 }
2447                 *cp = 0;
2448
2449                 /* here we do the work */
2450                 /* actually - we make sprintf() do it for us */
2451         switch(*fmt) {
2452         case 's':
2453                         *cp++ = 's';
2454                         *cp   = 0;
2455                         /* BUG: we do not handled embeded NULLs */
2456                         snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2457             break;
2458         case 'c':
2459                         *cp++ = 'c';
2460                         *cp   = 0;
2461             if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2462                 Jim_FreeNewObj(interp, resObjPtr);
2463                 return NULL;
2464             }
2465             c = (char) wideValue;
2466                         snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2467             break;
2468                 case 'f':
2469                 case 'F':
2470                 case 'g':
2471                 case 'G':
2472                 case 'e':
2473                 case 'E':
2474                         *cp++ = *fmt;
2475                         *cp   = 0;
2476                         if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2477                                 Jim_FreeNewObj( interp, resObjPtr );
2478                                 return NULL;
2479                         }
2480                         snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2481                         break;
2482         case 'b':
2483         case 'd':
2484         case 'o':
2485                 case 'i':
2486                 case 'u':
2487                 case 'x':
2488                 case 'X':
2489                         /* jim widevaluse are 64bit */
2490                         if( sizeof(jim_wide) == sizeof(long long) ){
2491                                 *cp++ = 'l'; 
2492                                 *cp++ = 'l';
2493                         } else {
2494                                 *cp++ = 'l';
2495                         }
2496                         *cp++ = *fmt;
2497                         *cp   = 0;
2498             if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2499                 Jim_FreeNewObj(interp, resObjPtr);
2500                 return NULL;
2501             }
2502                         snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2503             break;
2504         case '%':
2505                         sprintf_buf[0] = '%';
2506                         sprintf_buf[1] = 0;
2507                         objv--; /* undo the objv++ below */
2508             break;
2509         default:
2510             spec[0] = *fmt; spec[1] = '\0';
2511             Jim_FreeNewObj(interp, resObjPtr);
2512             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2513             Jim_AppendStrings(interp, Jim_GetResult(interp),
2514                     "bad field specifier \"",  spec, "\"", NULL);
2515             return NULL;
2516         }
2517                 /* force terminate */
2518 #if 0
2519                 printf("FMT was: %s\n", fmt_str );
2520                 printf("RES was: |%s|\n", sprintf_buf );
2521 #endif
2522                 
2523                 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2524                 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2525                 /* next obj */
2526                 objv++;
2527         fmt++;
2528         fmtLen--;
2529     }
2530     return resObjPtr;
2531 }
2532
2533 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2534         int objc, Jim_Obj *const *objv)
2535 {
2536         char *sprintf_buf=malloc(JIM_MAX_FMT);
2537         Jim_Obj *t=Jim_FormatString_Inner(interp, fmtObjPtr, objc, objv, sprintf_buf);
2538         free(sprintf_buf);
2539         return t; 
2540 }
2541
2542 /* -----------------------------------------------------------------------------
2543  * Compared String Object
2544  * ---------------------------------------------------------------------------*/
2545
2546 /* This is strange object that allows to compare a C literal string
2547  * with a Jim object in very short time if the same comparison is done
2548  * multiple times. For example every time the [if] command is executed,
2549  * Jim has to check if a given argument is "else". This comparions if
2550  * the code has no errors are true most of the times, so we can cache
2551  * inside the object the pointer of the string of the last matching
2552  * comparison. Because most C compilers perform literal sharing,
2553  * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2554  * this works pretty well even if comparisons are at different places
2555  * inside the C code. */
2556
2557 static Jim_ObjType comparedStringObjType = {
2558     "compared-string",
2559     NULL,
2560     NULL,
2561     NULL,
2562     JIM_TYPE_REFERENCES,
2563 };
2564
2565 /* The only way this object is exposed to the API is via the following
2566  * function. Returns true if the string and the object string repr.
2567  * are the same, otherwise zero is returned.
2568  *
2569  * Note: this isn't binary safe, but it hardly needs to be.*/
2570 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2571         const char *str)
2572 {
2573     if (objPtr->typePtr == &comparedStringObjType &&
2574         objPtr->internalRep.ptr == str)
2575         return 1;
2576     else {
2577         const char *objStr = Jim_GetString(objPtr, NULL);
2578         if (strcmp(str, objStr) != 0) return 0;
2579         if (objPtr->typePtr != &comparedStringObjType) {
2580             Jim_FreeIntRep(interp, objPtr);
2581             objPtr->typePtr = &comparedStringObjType;
2582         }
2583         objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2584         return 1;
2585     }
2586 }
2587
2588 int qsortCompareStringPointers(const void *a, const void *b)
2589 {
2590     char * const *sa = (char * const *)a;
2591     char * const *sb = (char * const *)b;
2592     return strcmp(*sa, *sb);
2593 }
2594
2595 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2596         const char * const *tablePtr, int *indexPtr, const char *name, int flags)
2597 {
2598     const char * const *entryPtr = NULL;
2599     char **tablePtrSorted;
2600     int i, count = 0;
2601
2602     *indexPtr = -1;
2603     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2604         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2605             *indexPtr = i;
2606             return JIM_OK;
2607         }
2608         count++; /* If nothing matches, this will reach the len of tablePtr */
2609     }
2610     if (flags & JIM_ERRMSG) {
2611         if (name == NULL)
2612             name = "option";
2613         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2614         Jim_AppendStrings(interp, Jim_GetResult(interp),
2615             "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2616             NULL);
2617         tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2618         memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2619         qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2620         for (i = 0; i < count; i++) {
2621             if (i+1 == count && count > 1)
2622                 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2623             Jim_AppendString(interp, Jim_GetResult(interp),
2624                     tablePtrSorted[i], -1);
2625             if (i+1 != count)
2626                 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2627         }
2628         Jim_Free(tablePtrSorted);
2629     }
2630     return JIM_ERR;
2631 }
2632
2633 int Jim_GetNvp(Jim_Interp *interp, 
2634                            Jim_Obj *objPtr,
2635                            const Jim_Nvp *nvp_table, 
2636                            const Jim_Nvp ** result)
2637 {
2638         Jim_Nvp *n;
2639         int e;
2640
2641         e = Jim_Nvp_name2value_obj( interp, nvp_table, objPtr, &n );
2642         if( e == JIM_ERR ){
2643                 return e;
2644         }
2645
2646         /* Success? found? */
2647         if( n->name ){
2648                 /* remove const */
2649                 *result = (Jim_Nvp *)n;
2650                 return JIM_OK;
2651         } else {
2652                 return JIM_ERR;
2653         }
2654 }
2655
2656 /* -----------------------------------------------------------------------------
2657  * Source Object
2658  *
2659  * This object is just a string from the language point of view, but
2660  * in the internal representation it contains the filename and line number
2661  * where this given token was read. This information is used by
2662  * Jim_EvalObj() if the object passed happens to be of type "source".
2663  *
2664  * This allows to propagate the information about line numbers and file
2665  * names and give error messages with absolute line numbers.
2666  *
2667  * Note that this object uses shared strings for filenames, and the
2668  * pointer to the filename together with the line number is taken into
2669  * the space for the "inline" internal represenation of the Jim_Object,
2670  * so there is almost memory zero-overhead.
2671  *
2672  * Also the object will be converted to something else if the given
2673  * token it represents in the source file is not something to be
2674  * evaluated (not a script), and will be specialized in some other way,
2675  * so the time overhead is alzo null.
2676  * ---------------------------------------------------------------------------*/
2677
2678 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2679 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2680
2681 static Jim_ObjType sourceObjType = {
2682     "source",
2683     FreeSourceInternalRep,
2684     DupSourceInternalRep,
2685     NULL,
2686     JIM_TYPE_REFERENCES,
2687 };
2688
2689 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2690 {
2691     Jim_ReleaseSharedString(interp,
2692             objPtr->internalRep.sourceValue.fileName);
2693 }
2694
2695 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2696 {
2697     dupPtr->internalRep.sourceValue.fileName =
2698         Jim_GetSharedString(interp,
2699                 srcPtr->internalRep.sourceValue.fileName);
2700     dupPtr->internalRep.sourceValue.lineNumber =
2701         dupPtr->internalRep.sourceValue.lineNumber;
2702     dupPtr->typePtr = &sourceObjType;
2703 }
2704
2705 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2706         const char *fileName, int lineNumber)
2707 {
2708     if (Jim_IsShared(objPtr))
2709         Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2710     if (objPtr->typePtr != NULL)
2711         Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2712     objPtr->internalRep.sourceValue.fileName =
2713         Jim_GetSharedString(interp, fileName);
2714     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2715     objPtr->typePtr = &sourceObjType;
2716 }
2717
2718 /* -----------------------------------------------------------------------------
2719  * Script Object
2720  * ---------------------------------------------------------------------------*/
2721
2722 #define JIM_CMDSTRUCT_EXPAND -1
2723
2724 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2725 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2726 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2727
2728 static Jim_ObjType scriptObjType = {
2729     "script",
2730     FreeScriptInternalRep,
2731     DupScriptInternalRep,
2732     NULL,
2733     JIM_TYPE_REFERENCES,
2734 };
2735
2736 /* The ScriptToken structure represents every token into a scriptObj.
2737  * Every token contains an associated Jim_Obj that can be specialized
2738  * by commands operating on it. */
2739 typedef struct ScriptToken {
2740     int type;
2741     Jim_Obj *objPtr;
2742     int linenr;
2743 } ScriptToken;
2744
2745 /* This is the script object internal representation. An array of
2746  * ScriptToken structures, with an associated command structure array.
2747  * The command structure is a pre-computed representation of the
2748  * command length and arguments structure as a simple liner array
2749  * of integers.
2750  * 
2751  * For example the script:
2752  *
2753  * puts hello
2754  * set $i $x$y [foo]BAR
2755  *
2756  * will produce a ScriptObj with the following Tokens:
2757  *
2758  * ESC puts
2759  * SEP
2760  * ESC hello
2761  * EOL
2762  * ESC set
2763  * EOL
2764  * VAR i
2765  * SEP
2766  * VAR x
2767  * VAR y
2768  * SEP
2769  * CMD foo
2770  * ESC BAR
2771  * EOL
2772  *
2773  * This is a description of the tokens, separators, and of lines.
2774  * The command structure instead represents the number of arguments
2775  * of every command, followed by the tokens of which every argument
2776  * is composed. So for the example script, the cmdstruct array will
2777  * contain:
2778  *
2779  * 2 1 1 4 1 1 2 2
2780  *
2781  * Because "puts hello" has two args (2), composed of single tokens (1 1)
2782  * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2783  * composed of single tokens (1 1) and the last two of double tokens
2784  * (2 2).
2785  *
2786  * The precomputation of the command structure makes Jim_Eval() faster,
2787  * and simpler because there aren't dynamic lengths / allocations.
2788  *
2789  * -- {expand} handling --
2790  *
2791  * Expand is handled in a special way. When a command
2792  * contains at least an argument with the {expand} prefix,
2793  * the command structure presents a -1 before the integer
2794  * describing the number of arguments. This is used in order
2795  * to send the command exection to a different path in case
2796  * of {expand} and guarantee a fast path for the more common
2797  * case. Also, the integers describing the number of tokens
2798  * are expressed with negative sign, to allow for fast check
2799  * of what's an {expand}-prefixed argument and what not.
2800  *
2801  * For example the command:
2802  *
2803  * list {expand}{1 2}
2804  *
2805  * Will produce the following cmdstruct array:
2806  *
2807  * -1 2 1 -2
2808  *
2809  * -- the substFlags field of the structure --
2810  *
2811  * The scriptObj structure is used to represent both "script" objects
2812  * and "subst" objects. In the second case, the cmdStruct related
2813  * fields are not used at all, but there is an additional field used
2814  * that is 'substFlags': this represents the flags used to turn
2815  * the string into the intenral representation used to perform the
2816  * substitution. If this flags are not what the application requires
2817  * the scriptObj is created again. For example the script:
2818  *
2819  * subst -nocommands $string
2820  * subst -novariables $string
2821  *
2822  * Will recreate the internal representation of the $string object
2823  * two times.
2824  */
2825 typedef struct ScriptObj {
2826     int len; /* Length as number of tokens. */
2827     int commands; /* number of top-level commands in script. */
2828     ScriptToken *token; /* Tokens array. */
2829     int *cmdStruct; /* commands structure */
2830     int csLen; /* length of the cmdStruct array. */
2831     int substFlags; /* flags used for the compilation of "subst" objects */
2832     int inUse; /* Used to share a ScriptObj. Currently
2833               only used by Jim_EvalObj() as protection against
2834               shimmering of the currently evaluated object. */
2835     char *fileName;
2836 } ScriptObj;
2837
2838 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2839 {
2840     int i;
2841     struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2842
2843     script->inUse--;
2844     if (script->inUse != 0) return;
2845     for (i = 0; i < script->len; i++) {
2846         if (script->token[i].objPtr != NULL)
2847             Jim_DecrRefCount(interp, script->token[i].objPtr);
2848     }
2849     Jim_Free(script->token);
2850     Jim_Free(script->cmdStruct);
2851     Jim_Free(script->fileName);
2852     Jim_Free(script);
2853 }
2854
2855 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2856 {
2857     JIM_NOTUSED(interp);
2858     JIM_NOTUSED(srcPtr);
2859
2860     /* Just returns an simple string. */
2861     dupPtr->typePtr = NULL;
2862 }
2863
2864 /* Add a new token to the internal repr of a script object */
2865 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2866         char *strtoken, int len, int type, char *filename, int linenr)
2867 {
2868     int prevtype;
2869     struct ScriptToken *token;
2870
2871     prevtype = (script->len == 0) ? JIM_TT_EOL : \
2872         script->token[script->len-1].type;
2873     /* Skip tokens without meaning, like words separators
2874      * following a word separator or an end of command and
2875      * so on. */
2876     if (prevtype == JIM_TT_EOL) {
2877         if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2878             Jim_Free(strtoken);
2879             return;
2880         }
2881     } else if (prevtype == JIM_TT_SEP) {
2882         if (type == JIM_TT_SEP) {
2883             Jim_Free(strtoken);
2884             return;
2885         } else if (type == JIM_TT_EOL) {
2886             /* If an EOL is following by a SEP, drop the previous
2887              * separator. */
2888             script->len--;
2889             Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2890         }
2891     } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2892             type == JIM_TT_ESC && len == 0)
2893     {
2894         /* Don't add empty tokens used in interpolation */
2895         Jim_Free(strtoken);
2896         return;
2897     }
2898     /* Make space for a new istruction */
2899     script->len++;
2900     script->token = Jim_Realloc(script->token,
2901             sizeof(ScriptToken)*script->len);
2902     /* Initialize the new token */
2903     token = script->token+(script->len-1);
2904     token->type = type;
2905     /* Every object is intially as a string, but the
2906      * internal type may be specialized during execution of the
2907      * script. */
2908     token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2909     /* To add source info to SEP and EOL tokens is useless because
2910      * they will never by called as arguments of Jim_EvalObj(). */
2911     if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2912         JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2913     Jim_IncrRefCount(token->objPtr);
2914     token->linenr = linenr;
2915 }
2916
2917 /* Add an integer into the command structure field of the script object. */
2918 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2919 {
2920     script->csLen++;
2921     script->cmdStruct = Jim_Realloc(script->cmdStruct,
2922                     sizeof(int)*script->csLen);
2923     script->cmdStruct[script->csLen-1] = val;
2924 }
2925
2926 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2927  * of objPtr. Search nested script objects recursively. */
2928 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2929         ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2930 {
2931     int i;
2932
2933     for (i = 0; i < script->len; i++) {
2934         if (script->token[i].objPtr != objPtr &&
2935             Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2936             return script->token[i].objPtr;
2937         }
2938         /* Enter recursively on scripts only if the object
2939          * is not the same as the one we are searching for
2940          * shared occurrences. */
2941         if (script->token[i].objPtr->typePtr == &scriptObjType &&
2942             script->token[i].objPtr != objPtr) {
2943             Jim_Obj *foundObjPtr;
2944
2945             ScriptObj *subScript =
2946                 script->token[i].objPtr->internalRep.ptr;
2947             /* Don't recursively enter the script we are trying
2948              * to make shared to avoid circular references. */
2949             if (subScript == scriptBarrier) continue;
2950             if (subScript != script) {
2951                 foundObjPtr =
2952                     ScriptSearchLiteral(interp, subScript,
2953                             scriptBarrier, objPtr);
2954                 if (foundObjPtr != NULL)
2955                     return foundObjPtr;
2956             }
2957         }
2958     }
2959     return NULL;
2960 }
2961
2962 /* Share literals of a script recursively sharing sub-scripts literals. */
2963 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2964         ScriptObj *topLevelScript)
2965 {
2966     int i, j;
2967
2968     return;
2969     /* Try to share with toplevel object. */
2970     if (topLevelScript != NULL) {
2971         for (i = 0; i < script->len; i++) {
2972             Jim_Obj *foundObjPtr;
2973             char *str = script->token[i].objPtr->bytes;
2974
2975             if (script->token[i].objPtr->refCount != 1) continue;
2976             if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2977             if (strchr(str, ' ') || strchr(str, '\n')) continue;
2978             foundObjPtr = ScriptSearchLiteral(interp,
2979                     topLevelScript,
2980                     script, /* barrier */
2981                     script->token[i].objPtr);
2982             if (foundObjPtr != NULL) {
2983                 Jim_IncrRefCount(foundObjPtr);
2984                 Jim_DecrRefCount(interp,
2985                         script->token[i].objPtr);
2986                 script->token[i].objPtr = foundObjPtr;
2987             }
2988         }
2989     }
2990     /* Try to share locally */
2991     for (i = 0; i < script->len; i++) {
2992         char *str = script->token[i].objPtr->bytes;
2993
2994         if (script->token[i].objPtr->refCount != 1) continue;
2995         if (strchr(str, ' ') || strchr(str, '\n')) continue;
2996         for (j = 0; j < script->len; j++) {
2997             if (script->token[i].objPtr !=
2998                     script->token[j].objPtr &&
2999                 Jim_StringEqObj(script->token[i].objPtr,
3000                             script->token[j].objPtr, 0))
3001             {
3002                 Jim_IncrRefCount(script->token[j].objPtr);
3003                 Jim_DecrRefCount(interp,
3004                         script->token[i].objPtr);
3005                 script->token[i].objPtr =
3006                     script->token[j].objPtr;
3007             }
3008         }
3009     }
3010 }
3011
3012 /* This method takes the string representation of an object
3013  * as a Tcl script, and generates the pre-parsed internal representation
3014  * of the script. */
3015 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3016 {
3017     int scriptTextLen;
3018     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
3019     struct JimParserCtx parser;
3020     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
3021     ScriptToken *token;
3022     int args, tokens, start, end, i;
3023     int initialLineNumber;
3024     int propagateSourceInfo = 0;
3025
3026     script->len = 0;
3027     script->csLen = 0;
3028     script->commands = 0;
3029     script->token = NULL;
3030     script->cmdStruct = NULL;
3031     script->inUse = 1;
3032     /* Try to get information about filename / line number */
3033     if (objPtr->typePtr == &sourceObjType) {
3034         script->fileName =
3035             Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
3036         initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
3037         propagateSourceInfo = 1;
3038     } else {
3039         script->fileName = Jim_StrDup("");
3040         initialLineNumber = 1;
3041     }
3042
3043     JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
3044     while(!JimParserEof(&parser)) {
3045         char *token;
3046         int len, type, linenr;
3047
3048         JimParseScript(&parser);
3049         token = JimParserGetToken(&parser, &len, &type, &linenr);
3050         ScriptObjAddToken(interp, script, token, len, type,
3051                 propagateSourceInfo ? script->fileName : NULL,
3052                 linenr);
3053     }
3054     token = script->token;
3055
3056     /* Compute the command structure array
3057      * (see the ScriptObj struct definition for more info) */
3058     start = 0; /* Current command start token index */
3059     end = -1; /* Current command end token index */
3060     while (1) {
3061         int expand = 0; /* expand flag. set to 1 on {expand} form. */
3062         int interpolation = 0; /* set to 1 if there is at least one
3063                       argument of the command obtained via
3064                       interpolation of more tokens. */
3065         /* Search for the end of command, while
3066          * count the number of args. */
3067         start = ++end;
3068         if (start >= script->len) break;
3069         args = 1; /* Number of args in current command */
3070         while (token[end].type != JIM_TT_EOL) {
3071             if (end == 0 || token[end-1].type == JIM_TT_SEP ||
3072                     token[end-1].type == JIM_TT_EOL)
3073             {
3074                 if (token[end].type == JIM_TT_STR &&
3075                     token[end+1].type != JIM_TT_SEP &&
3076                     token[end+1].type != JIM_TT_EOL &&
3077                     (!strcmp(token[end].objPtr->bytes, "expand") ||
3078                      !strcmp(token[end].objPtr->bytes, "*")))
3079                     expand++;
3080             }
3081             if (token[end].type == JIM_TT_SEP)
3082                 args++;
3083             end++;
3084         }
3085         interpolation = !((end-start+1) == args*2);
3086         /* Add the 'number of arguments' info into cmdstruct.
3087          * Negative value if there is list expansion involved. */
3088         if (expand)
3089             ScriptObjAddInt(script, -1);
3090         ScriptObjAddInt(script, args);
3091         /* Now add info about the number of tokens. */
3092         tokens = 0; /* Number of tokens in current argument. */
3093         expand = 0;
3094         for (i = start; i <= end; i++) {
3095             if (token[i].type == JIM_TT_SEP ||
3096                 token[i].type == JIM_TT_EOL)
3097             {
3098                 if (tokens == 1 && expand)
3099                     expand = 0;
3100                 ScriptObjAddInt(script,
3101                         expand ? -tokens : tokens);
3102
3103                 expand = 0;
3104                 tokens = 0;
3105                 continue;
3106             } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
3107                    (!strcmp(token[i].objPtr->bytes, "expand") ||
3108                     !strcmp(token[i].objPtr->bytes, "*")))
3109             {
3110                 expand++;
3111             }
3112             tokens++;
3113         }
3114     }
3115     /* Perform literal sharing, but only for objects that appear
3116      * to be scripts written as literals inside the source code,
3117      * and not computed at runtime. Literal sharing is a costly
3118      * operation that should be done only against objects that
3119      * are likely to require compilation only the first time, and
3120      * then are executed multiple times. */
3121     if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
3122         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
3123         if (bodyObjPtr->typePtr == &scriptObjType) {
3124             ScriptObj *bodyScript =
3125                 bodyObjPtr->internalRep.ptr;
3126             ScriptShareLiterals(interp, script, bodyScript);
3127         }
3128     } else if (propagateSourceInfo) {
3129         ScriptShareLiterals(interp, script, NULL);
3130     }
3131     /* Free the old internal rep and set the new one. */
3132     Jim_FreeIntRep(interp, objPtr);
3133     Jim_SetIntRepPtr(objPtr, script);
3134     objPtr->typePtr = &scriptObjType;
3135     return JIM_OK;
3136 }
3137
3138 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
3139 {
3140     if (objPtr->typePtr != &scriptObjType) {
3141         SetScriptFromAny(interp, objPtr);
3142     }
3143     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
3144 }
3145
3146 /* -----------------------------------------------------------------------------
3147  * Commands
3148  * ---------------------------------------------------------------------------*/
3149
3150 /* Commands HashTable Type.
3151  *
3152  * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
3153 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
3154 {
3155     Jim_Cmd *cmdPtr = (void*) val;
3156
3157     if (cmdPtr->cmdProc == NULL) {
3158         Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3159         Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3160         if (cmdPtr->staticVars) {
3161             Jim_FreeHashTable(cmdPtr->staticVars);
3162             Jim_Free(cmdPtr->staticVars);
3163         }
3164     } else if (cmdPtr->delProc != NULL) {
3165             /* If it was a C coded command, call the delProc if any */
3166             cmdPtr->delProc(interp, cmdPtr->privData);
3167     }
3168     Jim_Free(val);
3169 }
3170
3171 static Jim_HashTableType JimCommandsHashTableType = {
3172     JimStringCopyHTHashFunction,        /* hash function */
3173     JimStringCopyHTKeyDup,        /* key dup */
3174     NULL,                    /* val dup */
3175     JimStringCopyHTKeyCompare,        /* key compare */
3176     JimStringCopyHTKeyDestructor,        /* key destructor */
3177     Jim_CommandsHT_ValDestructor        /* val destructor */
3178 };
3179
3180 /* ------------------------- Commands related functions --------------------- */
3181
3182 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3183         Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3184 {
3185     Jim_HashEntry *he;
3186     Jim_Cmd *cmdPtr;
3187
3188     he = Jim_FindHashEntry(&interp->commands, cmdName);
3189     if (he == NULL) { /* New command to create */
3190         cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3191         Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3192     } else {
3193         Jim_InterpIncrProcEpoch(interp);
3194         /* Free the arglist/body objects if it was a Tcl procedure */
3195         cmdPtr = he->val;
3196         if (cmdPtr->cmdProc == NULL) {
3197             Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3198             Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3199             if (cmdPtr->staticVars) {
3200                 Jim_FreeHashTable(cmdPtr->staticVars);
3201                 Jim_Free(cmdPtr->staticVars);
3202             }
3203             cmdPtr->staticVars = NULL;
3204         } else if (cmdPtr->delProc != NULL) {
3205             /* If it was a C coded command, call the delProc if any */
3206             cmdPtr->delProc(interp, cmdPtr->privData);
3207         }
3208     }
3209
3210     /* Store the new details for this proc */
3211     cmdPtr->delProc = delProc;
3212     cmdPtr->cmdProc = cmdProc;
3213     cmdPtr->privData = privData;
3214
3215     /* There is no need to increment the 'proc epoch' because
3216      * creation of a new procedure can never affect existing
3217      * cached commands. We don't do negative caching. */
3218     return JIM_OK;
3219 }
3220
3221 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3222         Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3223         int arityMin, int arityMax)
3224 {
3225     Jim_Cmd *cmdPtr;
3226
3227     cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3228     cmdPtr->cmdProc = NULL; /* Not a C coded command */
3229     cmdPtr->argListObjPtr = argListObjPtr;
3230     cmdPtr->bodyObjPtr = bodyObjPtr;
3231     Jim_IncrRefCount(argListObjPtr);
3232     Jim_IncrRefCount(bodyObjPtr);
3233     cmdPtr->arityMin = arityMin;
3234     cmdPtr->arityMax = arityMax;
3235     cmdPtr->staticVars = NULL;
3236    
3237     /* Create the statics hash table. */
3238     if (staticsListObjPtr) {
3239         int len, i;
3240
3241         Jim_ListLength(interp, staticsListObjPtr, &len);
3242         if (len != 0) {
3243             cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3244             Jim_InitHashTable(cmdPtr->staticVars, getJimVariablesHashTableType(),
3245                     interp);
3246             for (i = 0; i < len; i++) {
3247                 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3248                 Jim_Var *varPtr;
3249                 int subLen;
3250
3251                 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3252                 /* Check if it's composed of two elements. */
3253                 Jim_ListLength(interp, objPtr, &subLen);
3254                 if (subLen == 1 || subLen == 2) {
3255                     /* Try to get the variable value from the current
3256                      * environment. */
3257                     Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3258                     if (subLen == 1) {
3259                         initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3260                                 JIM_NONE);
3261                         if (initObjPtr == NULL) {
3262                             Jim_SetResult(interp,
3263                                     Jim_NewEmptyStringObj(interp));
3264                             Jim_AppendStrings(interp, Jim_GetResult(interp),
3265                                 "variable for initialization of static \"",
3266                                 Jim_GetString(nameObjPtr, NULL),
3267                                 "\" not found in the local context",
3268                                 NULL);
3269                             goto err;
3270                         }
3271                     } else {
3272                         Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3273                     }
3274                     varPtr = Jim_Alloc(sizeof(*varPtr));
3275                     varPtr->objPtr = initObjPtr;
3276                     Jim_IncrRefCount(initObjPtr);
3277                     varPtr->linkFramePtr = NULL;
3278                     if (Jim_AddHashEntry(cmdPtr->staticVars,
3279                             Jim_GetString(nameObjPtr, NULL),
3280                             varPtr) != JIM_OK)
3281                     {
3282                         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3283                         Jim_AppendStrings(interp, Jim_GetResult(interp),
3284                             "static variable name \"",
3285                             Jim_GetString(objPtr, NULL), "\"",
3286                             " duplicated in statics list", NULL);
3287                         Jim_DecrRefCount(interp, initObjPtr);
3288                         Jim_Free(varPtr);
3289                         goto err;
3290                     }
3291                 } else {
3292                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3293                     Jim_AppendStrings(interp, Jim_GetResult(interp),
3294                         "too many fields in static specifier \"",
3295                         objPtr, "\"", NULL);
3296                     goto err;
3297                 }
3298             }
3299         }
3300     }
3301
3302     /* Add the new command */
3303
3304     /* it may already exist, so we try to delete the old one */
3305     if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3306         /* There was an old procedure with the same name, this requires
3307          * a 'proc epoch' update. */
3308         Jim_InterpIncrProcEpoch(interp);
3309     }
3310     /* If a procedure with the same name didn't existed there is no need
3311      * to increment the 'proc epoch' because creation of a new procedure
3312      * can never affect existing cached commands. We don't do
3313      * negative caching. */
3314     Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3315     return JIM_OK;
3316
3317 err:
3318     Jim_FreeHashTable(cmdPtr->staticVars);
3319     Jim_Free(cmdPtr->staticVars);
3320     Jim_DecrRefCount(interp, argListObjPtr);
3321     Jim_DecrRefCount(interp, bodyObjPtr);
3322     Jim_Free(cmdPtr);
3323     return JIM_ERR;
3324 }
3325
3326 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3327 {
3328     if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3329         return JIM_ERR;
3330     Jim_InterpIncrProcEpoch(interp);
3331     return JIM_OK;
3332 }
3333
3334 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, 
3335         const char *newName)
3336 {
3337     Jim_Cmd *cmdPtr;
3338     Jim_HashEntry *he;
3339     Jim_Cmd *copyCmdPtr;
3340
3341     if (newName[0] == '\0') /* Delete! */
3342         return Jim_DeleteCommand(interp, oldName);
3343     /* Rename */
3344     he = Jim_FindHashEntry(&interp->commands, oldName);
3345     if (he == NULL)
3346         return JIM_ERR; /* Invalid command name */
3347     cmdPtr = he->val;
3348     copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3349     *copyCmdPtr = *cmdPtr;
3350     /* In order to avoid that a procedure will get arglist/body/statics
3351      * freed by the hash table methods, fake a C-coded command
3352      * setting cmdPtr->cmdProc as not NULL */
3353     cmdPtr->cmdProc = (void*)1;
3354     /* Also make sure delProc is NULL. */
3355     cmdPtr->delProc = NULL;
3356     /* Destroy the old command, and make sure the new is freed
3357      * as well. */
3358     Jim_DeleteHashEntry(&interp->commands, oldName);
3359     Jim_DeleteHashEntry(&interp->commands, newName);
3360     /* Now the new command. We are sure it can't fail because
3361      * the target name was already freed. */
3362     Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3363     /* Increment the epoch */
3364     Jim_InterpIncrProcEpoch(interp);
3365     return JIM_OK;
3366 }
3367
3368 /* -----------------------------------------------------------------------------
3369  * Command object
3370  * ---------------------------------------------------------------------------*/
3371
3372 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3373
3374 static Jim_ObjType commandObjType = {
3375     "command",
3376     NULL,
3377     NULL,
3378     NULL,
3379     JIM_TYPE_REFERENCES,
3380 };
3381
3382 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3383 {
3384     Jim_HashEntry *he;
3385     const char *cmdName;
3386
3387     /* Get the string representation */
3388     cmdName = Jim_GetString(objPtr, NULL);
3389     /* Lookup this name into the commands hash table */
3390     he = Jim_FindHashEntry(&interp->commands, cmdName);
3391     if (he == NULL)
3392         return JIM_ERR;
3393
3394     /* Free the old internal repr and set the new one. */
3395     Jim_FreeIntRep(interp, objPtr);
3396     objPtr->typePtr = &commandObjType;
3397     objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3398     objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3399     return JIM_OK;
3400 }
3401
3402 /* This function returns the command structure for the command name
3403  * stored in objPtr. It tries to specialize the objPtr to contain
3404  * a cached info instead to perform the lookup into the hash table
3405  * every time. The information cached may not be uptodate, in such
3406  * a case the lookup is performed and the cache updated. */
3407 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3408 {
3409     if ((objPtr->typePtr != &commandObjType ||
3410         objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3411         SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3412         if (flags & JIM_ERRMSG) {
3413             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3414             Jim_AppendStrings(interp, Jim_GetResult(interp),
3415                 "invalid command name \"", objPtr->bytes, "\"",
3416                 NULL);
3417         }
3418         return NULL;
3419     }
3420     return objPtr->internalRep.cmdValue.cmdPtr;
3421 }
3422
3423 /* -----------------------------------------------------------------------------
3424  * Variables
3425  * ---------------------------------------------------------------------------*/
3426
3427 /* Variables HashTable Type.
3428  *
3429  * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3430 static void JimVariablesHTValDestructor(void *interp, void *val)
3431 {
3432     Jim_Var *varPtr = (void*) val;
3433
3434     Jim_DecrRefCount(interp, varPtr->objPtr);
3435     Jim_Free(val);
3436 }
3437
3438 static Jim_HashTableType JimVariablesHashTableType = {
3439     JimStringCopyHTHashFunction,        /* hash function */
3440     JimStringCopyHTKeyDup,              /* key dup */
3441     NULL,                               /* val dup */
3442     JimStringCopyHTKeyCompare,        /* key compare */
3443     JimStringCopyHTKeyDestructor,     /* key destructor */
3444     JimVariablesHTValDestructor       /* val destructor */
3445 };
3446
3447 static Jim_HashTableType *getJimVariablesHashTableType(void)
3448 {
3449         return &JimVariablesHashTableType;
3450 }
3451
3452 /* -----------------------------------------------------------------------------
3453  * Variable object
3454  * ---------------------------------------------------------------------------*/
3455
3456 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3457
3458 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3459
3460 static Jim_ObjType variableObjType = {
3461     "variable",
3462     NULL,
3463     NULL,
3464     NULL,
3465     JIM_TYPE_REFERENCES,
3466 };
3467
3468 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3469  * is in the form "varname(key)". */
3470 static int Jim_NameIsDictSugar(const char *str, int len)
3471 {
3472     if (len == -1)
3473         len = strlen(str);
3474     if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3475         return 1;
3476     return 0;
3477 }
3478
3479 /* This method should be called only by the variable API.
3480  * It returns JIM_OK on success (variable already exists),
3481  * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3482  * a variable name, but syntax glue for [dict] i.e. the last
3483  * character is ')' */
3484 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3485 {
3486     Jim_HashEntry *he;
3487     const char *varName;
3488     int len;
3489
3490     /* Check if the object is already an uptodate variable */
3491     if (objPtr->typePtr == &variableObjType &&
3492         objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3493         return JIM_OK; /* nothing to do */
3494     /* Get the string representation */
3495     varName = Jim_GetString(objPtr, &len);
3496     /* Make sure it's not syntax glue to get/set dict. */
3497     if (Jim_NameIsDictSugar(varName, len))
3498             return JIM_DICT_SUGAR;
3499     if (varName[0] == ':' && varName[1] == ':') {
3500         he = Jim_FindHashEntry(&interp->topFramePtr->vars, varName + 2);
3501         if (he == NULL) {
3502             return JIM_ERR;
3503         }
3504     }
3505     else {
3506         /* Lookup this name into the variables hash table */
3507         he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3508         if (he == NULL) {
3509             /* Try with static vars. */
3510             if (interp->framePtr->staticVars == NULL)
3511                 return JIM_ERR;
3512             if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3513                 return JIM_ERR;
3514         }
3515     }
3516     /* Free the old internal repr and set the new one. */
3517     Jim_FreeIntRep(interp, objPtr);
3518     objPtr->typePtr = &variableObjType;
3519     objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3520     objPtr->internalRep.varValue.varPtr = (void*)he->val;
3521     return JIM_OK;
3522 }
3523
3524 /* -------------------- Variables related functions ------------------------- */
3525 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3526         Jim_Obj *valObjPtr);
3527 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3528
3529 /* For now that's dummy. Variables lookup should be optimized
3530  * in many ways, with caching of lookups, and possibly with
3531  * a table of pre-allocated vars in every CallFrame for local vars.
3532  * All the caching should also have an 'epoch' mechanism similar
3533  * to the one used by Tcl for procedures lookup caching. */
3534
3535 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3536 {
3537     const char *name;
3538     Jim_Var *var;
3539     int err;
3540
3541     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3542         /* Check for [dict] syntax sugar. */
3543         if (err == JIM_DICT_SUGAR)
3544             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3545         /* New variable to create */
3546         name = Jim_GetString(nameObjPtr, NULL);
3547
3548         var = Jim_Alloc(sizeof(*var));
3549         var->objPtr = valObjPtr;
3550         Jim_IncrRefCount(valObjPtr);
3551         var->linkFramePtr = NULL;
3552         /* Insert the new variable */
3553         if (name[0] == ':' && name[1] == ':') {
3554             /* Into to the top evel frame */
3555             Jim_AddHashEntry(&interp->topFramePtr->vars, name + 2, var);
3556         }
3557         else {
3558             Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3559         }
3560         /* Make the object int rep a variable */
3561         Jim_FreeIntRep(interp, nameObjPtr);
3562         nameObjPtr->typePtr = &variableObjType;
3563         nameObjPtr->internalRep.varValue.callFrameId =
3564             interp->framePtr->id;
3565         nameObjPtr->internalRep.varValue.varPtr = var;
3566     } else {
3567         var = nameObjPtr->internalRep.varValue.varPtr;
3568         if (var->linkFramePtr == NULL) {
3569             Jim_IncrRefCount(valObjPtr);
3570             Jim_DecrRefCount(interp, var->objPtr);
3571             var->objPtr = valObjPtr;
3572         } else { /* Else handle the link */
3573             Jim_CallFrame *savedCallFrame;
3574
3575             savedCallFrame = interp->framePtr;
3576             interp->framePtr = var->linkFramePtr;
3577             err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3578             interp->framePtr = savedCallFrame;
3579             if (err != JIM_OK)
3580                 return err;
3581         }
3582     }
3583     return JIM_OK;
3584 }
3585
3586 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3587 {
3588     Jim_Obj *nameObjPtr;
3589     int result;
3590
3591     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3592     Jim_IncrRefCount(nameObjPtr);
3593     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3594     Jim_DecrRefCount(interp, nameObjPtr);
3595     return result;
3596 }
3597
3598 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3599 {
3600     Jim_CallFrame *savedFramePtr;
3601     int result;
3602
3603     savedFramePtr = interp->framePtr;
3604     interp->framePtr = interp->topFramePtr;
3605     result = Jim_SetVariableStr(interp, name, objPtr);
3606     interp->framePtr = savedFramePtr;
3607     return result;
3608 }
3609
3610 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3611 {
3612     Jim_Obj *nameObjPtr, *valObjPtr;
3613     int result;
3614
3615     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3616     valObjPtr = Jim_NewStringObj(interp, val, -1);
3617     Jim_IncrRefCount(nameObjPtr);
3618     Jim_IncrRefCount(valObjPtr);
3619     result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3620     Jim_DecrRefCount(interp, nameObjPtr);
3621     Jim_DecrRefCount(interp, valObjPtr);
3622     return result;
3623 }
3624
3625 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3626         Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3627 {
3628     const char *varName;
3629     int len;
3630
3631     /* Check for cycles. */
3632     if (interp->framePtr == targetCallFrame) {
3633         Jim_Obj *objPtr = targetNameObjPtr;
3634         Jim_Var *varPtr;
3635         /* Cycles are only possible with 'uplevel 0' */
3636         while(1) {
3637             if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3638                 Jim_SetResultString(interp,
3639                     "can't upvar from variable to itself", -1);
3640                 return JIM_ERR;
3641             }
3642             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3643                 break;
3644             varPtr = objPtr->internalRep.varValue.varPtr;
3645             if (varPtr->linkFramePtr != targetCallFrame) break;
3646             objPtr = varPtr->objPtr;
3647         }
3648     }
3649     varName = Jim_GetString(nameObjPtr, &len);
3650     if (Jim_NameIsDictSugar(varName, len)) {
3651         Jim_SetResultString(interp,
3652             "Dict key syntax invalid as link source", -1);
3653         return JIM_ERR;
3654     }
3655     /* Perform the binding */
3656     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3657     /* We are now sure 'nameObjPtr' type is variableObjType */
3658     nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3659     return JIM_OK;
3660 }
3661
3662 /* Return the Jim_Obj pointer associated with a variable name,
3663  * or NULL if the variable was not found in the current context.
3664  * The same optimization discussed in the comment to the
3665  * 'SetVariable' function should apply here. */
3666 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3667 {
3668     int err;
3669
3670     /* All the rest is handled here */
3671     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3672         /* Check for [dict] syntax sugar. */
3673         if (err == JIM_DICT_SUGAR)
3674             return JimDictSugarGet(interp, nameObjPtr);
3675         if (flags & JIM_ERRMSG) {
3676             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3677             Jim_AppendStrings(interp, Jim_GetResult(interp),
3678                 "can't read \"", nameObjPtr->bytes,
3679                 "\": no such variable", NULL);
3680         }
3681         return NULL;
3682     } else {
3683         Jim_Var *varPtr;
3684         Jim_Obj *objPtr;
3685         Jim_CallFrame *savedCallFrame;
3686
3687         varPtr = nameObjPtr->internalRep.varValue.varPtr;
3688         if (varPtr->linkFramePtr == NULL)
3689             return varPtr->objPtr;
3690         /* The variable is a link? Resolve it. */
3691         savedCallFrame = interp->framePtr;
3692         interp->framePtr = varPtr->linkFramePtr;
3693         objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3694         if (objPtr == NULL && flags & JIM_ERRMSG) {
3695             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3696             Jim_AppendStrings(interp, Jim_GetResult(interp),
3697                 "can't read \"", nameObjPtr->bytes,
3698                 "\": no such variable", NULL);
3699         }
3700         interp->framePtr = savedCallFrame;
3701         return objPtr;
3702     }
3703 }
3704
3705 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3706         int flags)
3707 {
3708     Jim_CallFrame *savedFramePtr;
3709     Jim_Obj *objPtr;
3710
3711     savedFramePtr = interp->framePtr;
3712     interp->framePtr = interp->topFramePtr;
3713     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3714     interp->framePtr = savedFramePtr;
3715
3716     return objPtr;
3717 }
3718
3719 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3720 {
3721     Jim_Obj *nameObjPtr, *varObjPtr;
3722
3723     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3724     Jim_IncrRefCount(nameObjPtr);
3725     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3726     Jim_DecrRefCount(interp, nameObjPtr);
3727     return varObjPtr;
3728 }
3729
3730 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3731         int flags)
3732 {
3733     Jim_CallFrame *savedFramePtr;
3734     Jim_Obj *objPtr;
3735
3736     savedFramePtr = interp->framePtr;
3737     interp->framePtr = interp->topFramePtr;
3738     objPtr = Jim_GetVariableStr(interp, name, flags);
3739     interp->framePtr = savedFramePtr;
3740
3741     return objPtr;
3742 }
3743
3744 /* Unset a variable.
3745  * Note: On success unset invalidates all the variable objects created
3746  * in the current call frame incrementing. */
3747 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3748 {
3749     const char *name;
3750     Jim_Var *varPtr;
3751     int err;
3752     
3753     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3754         /* Check for [dict] syntax sugar. */
3755         if (err == JIM_DICT_SUGAR)
3756             return JimDictSugarSet(interp, nameObjPtr, NULL);
3757         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3758         Jim_AppendStrings(interp, Jim_GetResult(interp),
3759             "can't unset \"", nameObjPtr->bytes,
3760             "\": no such variable", NULL);
3761         return JIM_ERR; /* var not found */
3762     }
3763     varPtr = nameObjPtr->internalRep.varValue.varPtr;
3764     /* If it's a link call UnsetVariable recursively */
3765     if (varPtr->linkFramePtr) {
3766         int retval;
3767
3768         Jim_CallFrame *savedCallFrame;
3769
3770         savedCallFrame = interp->framePtr;
3771         interp->framePtr = varPtr->linkFramePtr;
3772         retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3773         interp->framePtr = savedCallFrame;
3774         if (retval != JIM_OK && flags & JIM_ERRMSG) {
3775             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3776             Jim_AppendStrings(interp, Jim_GetResult(interp),
3777                 "can't unset \"", nameObjPtr->bytes,
3778                 "\": no such variable", NULL);
3779         }
3780         return retval;
3781     } else {
3782         name = Jim_GetString(nameObjPtr, NULL);
3783         if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3784                 != JIM_OK) return JIM_ERR;
3785         /* Change the callframe id, invalidating var lookup caching */
3786         JimChangeCallFrameId(interp, interp->framePtr);
3787         return JIM_OK;
3788     }
3789 }
3790
3791 /* ----------  Dict syntax sugar (similar to array Tcl syntax) -------------- */
3792
3793 /* Given a variable name for [dict] operation syntax sugar,
3794  * this function returns two objects, the first with the name
3795  * of the variable to set, and the second with the rispective key.
3796  * For example "foo(bar)" will return objects with string repr. of
3797  * "foo" and "bar".
3798  *
3799  * The returned objects have refcount = 1. The function can't fail. */
3800 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3801         Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3802 {
3803     const char *str, *p;
3804     char *t;
3805     int len, keyLen, nameLen;
3806     Jim_Obj *varObjPtr, *keyObjPtr;
3807
3808     str = Jim_GetString(objPtr, &len);
3809     p = strchr(str, '(');
3810     p++;
3811     keyLen = len-((p-str)+1);
3812     nameLen = (p-str)-1;
3813     /* Create the objects with the variable name and key. */
3814     t = Jim_Alloc(nameLen+1);
3815     memcpy(t, str, nameLen);
3816     t[nameLen] = '\0';
3817     varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3818
3819     t = Jim_Alloc(keyLen+1);
3820     memcpy(t, p, keyLen);
3821     t[keyLen] = '\0';
3822     keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3823
3824     Jim_IncrRefCount(varObjPtr);
3825     Jim_IncrRefCount(keyObjPtr);
3826     *varPtrPtr = varObjPtr;
3827     *keyPtrPtr = keyObjPtr;
3828 }
3829
3830 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3831  * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3832 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3833         Jim_Obj *valObjPtr)
3834 {
3835     Jim_Obj *varObjPtr, *keyObjPtr;
3836     int err = JIM_OK;
3837
3838     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3839     err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3840             valObjPtr);
3841     Jim_DecrRefCount(interp, varObjPtr);
3842     Jim_DecrRefCount(interp, keyObjPtr);
3843     return err;
3844 }
3845
3846 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3847 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3848 {
3849     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3850
3851     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3852     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3853     if (!dictObjPtr) {
3854         resObjPtr = NULL;
3855         goto err;
3856     }
3857     if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3858             != JIM_OK) {
3859         resObjPtr = NULL;
3860     }
3861 err:
3862     Jim_DecrRefCount(interp, varObjPtr);
3863     Jim_DecrRefCount(interp, keyObjPtr);
3864     return resObjPtr;
3865 }
3866
3867 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3868
3869 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3870 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3871         Jim_Obj *dupPtr);
3872
3873 static Jim_ObjType dictSubstObjType = {
3874     "dict-substitution",
3875     FreeDictSubstInternalRep,
3876     DupDictSubstInternalRep,
3877     NULL,
3878     JIM_TYPE_NONE,
3879 };
3880
3881 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3882 {
3883     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3884     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3885 }
3886
3887 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3888         Jim_Obj *dupPtr)
3889 {
3890     JIM_NOTUSED(interp);
3891
3892     dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3893         srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3894     dupPtr->internalRep.dictSubstValue.indexObjPtr =
3895         srcPtr->internalRep.dictSubstValue.indexObjPtr;
3896     dupPtr->typePtr = &dictSubstObjType;
3897 }
3898
3899 /* This function is used to expand [dict get] sugar in the form
3900  * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3901  * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3902  * object that is *guaranteed* to be in the form VARNAME(INDEX).
3903  * The 'index' part is [subst]ituted, and is used to lookup a key inside
3904  * the [dict]ionary contained in variable VARNAME. */
3905 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3906 {
3907     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3908     Jim_Obj *substKeyObjPtr = NULL;
3909
3910     if (objPtr->typePtr != &dictSubstObjType) {
3911         JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3912         Jim_FreeIntRep(interp, objPtr);
3913         objPtr->typePtr = &dictSubstObjType;
3914         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3915         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3916     }
3917     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3918                 &substKeyObjPtr, JIM_NONE)
3919             != JIM_OK) {
3920         substKeyObjPtr = NULL;
3921         goto err;
3922     }
3923     Jim_IncrRefCount(substKeyObjPtr);
3924     dictObjPtr = Jim_GetVariable(interp,
3925             objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3926     if (!dictObjPtr) {
3927         resObjPtr = NULL;
3928         goto err;
3929     }
3930     if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3931             != JIM_OK) {
3932         resObjPtr = NULL;
3933         goto err;
3934     }
3935 err:
3936     if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3937     return resObjPtr;
3938 }
3939
3940 /* -----------------------------------------------------------------------------
3941  * CallFrame
3942  * ---------------------------------------------------------------------------*/
3943
3944 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3945 {
3946     Jim_CallFrame *cf;
3947     if (interp->freeFramesList) {
3948         cf = interp->freeFramesList;
3949         interp->freeFramesList = cf->nextFramePtr;
3950     } else {
3951         cf = Jim_Alloc(sizeof(*cf));
3952         cf->vars.table = NULL;
3953     }
3954
3955     cf->id = interp->callFrameEpoch++;
3956     cf->parentCallFrame = NULL;
3957     cf->argv = NULL;
3958     cf->argc = 0;
3959     cf->procArgsObjPtr = NULL;
3960     cf->procBodyObjPtr = NULL;
3961     cf->nextFramePtr = NULL;
3962     cf->staticVars = NULL;
3963     if (cf->vars.table == NULL)
3964         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3965     return cf;
3966 }
3967
3968 /* Used to invalidate every caching related to callframe stability. */
3969 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3970 {
3971     cf->id = interp->callFrameEpoch++;
3972 }
3973
3974 #define JIM_FCF_NONE 0 /* no flags */
3975 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3976 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3977         int flags)
3978 {
3979     if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3980     if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3981     if (!(flags & JIM_FCF_NOHT))
3982         Jim_FreeHashTable(&cf->vars);
3983     else {
3984         int i;
3985         Jim_HashEntry **table = cf->vars.table, *he;
3986
3987         for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3988             he = table[i];
3989             while (he != NULL) {
3990                 Jim_HashEntry *nextEntry = he->next;
3991                 Jim_Var *varPtr = (void*) he->val;
3992
3993                 Jim_DecrRefCount(interp, varPtr->objPtr);
3994                 Jim_Free(he->val);
3995                 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3996                 Jim_Free(he);
3997                 table[i] = NULL;
3998                 he = nextEntry;
3999             }
4000         }
4001         cf->vars.used = 0;
4002     }
4003     cf->nextFramePtr = interp->freeFramesList;
4004     interp->freeFramesList = cf;
4005 }
4006
4007 /* -----------------------------------------------------------------------------
4008  * References
4009  * ---------------------------------------------------------------------------*/
4010
4011 /* References HashTable Type.
4012  *
4013  * Keys are jim_wide integers, dynamically allocated for now but in the
4014  * future it's worth to cache this 8 bytes objects. Values are poitners
4015  * to Jim_References. */
4016 static void JimReferencesHTValDestructor(void *interp, void *val)
4017 {
4018     Jim_Reference *refPtr = (void*) val;
4019
4020     Jim_DecrRefCount(interp, refPtr->objPtr);
4021     if (refPtr->finalizerCmdNamePtr != NULL) {
4022         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4023     }
4024     Jim_Free(val);
4025 }
4026
4027 unsigned int JimReferencesHTHashFunction(const void *key)
4028 {
4029     /* Only the least significant bits are used. */
4030     const jim_wide *widePtr = key;
4031     unsigned int intValue = (unsigned int) *widePtr;
4032     return Jim_IntHashFunction(intValue);
4033 }
4034
4035 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
4036 {
4037     /* Only the least significant bits are used. */
4038     const jim_wide *widePtr = key;
4039     unsigned int intValue = (unsigned int) *widePtr;
4040     return intValue; /* identity function. */
4041 }
4042
4043 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
4044 {
4045     void *copy = Jim_Alloc(sizeof(jim_wide));
4046     JIM_NOTUSED(privdata);
4047
4048     memcpy(copy, key, sizeof(jim_wide));
4049     return copy;
4050 }
4051
4052 int JimReferencesHTKeyCompare(void *privdata, const void *key1, 
4053         const void *key2)
4054 {
4055     JIM_NOTUSED(privdata);
4056
4057     return memcmp(key1, key2, sizeof(jim_wide)) == 0;
4058 }
4059
4060 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
4061 {
4062     JIM_NOTUSED(privdata);
4063
4064     Jim_Free((void*)key);
4065 }
4066
4067 static Jim_HashTableType JimReferencesHashTableType = {
4068     JimReferencesHTHashFunction,    /* hash function */
4069     JimReferencesHTKeyDup,          /* key dup */
4070     NULL,                           /* val dup */
4071     JimReferencesHTKeyCompare,      /* key compare */
4072     JimReferencesHTKeyDestructor,   /* key destructor */
4073     JimReferencesHTValDestructor    /* val destructor */
4074 };
4075
4076 /* -----------------------------------------------------------------------------
4077  * Reference object type and References API
4078  * ---------------------------------------------------------------------------*/
4079
4080 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
4081
4082 static Jim_ObjType referenceObjType = {
4083     "reference",
4084     NULL,
4085     NULL,
4086     UpdateStringOfReference,
4087     JIM_TYPE_REFERENCES,
4088 };
4089
4090 void UpdateStringOfReference(struct Jim_Obj *objPtr)
4091 {
4092     int len;
4093     char buf[JIM_REFERENCE_SPACE+1];
4094     Jim_Reference *refPtr;
4095
4096     refPtr = objPtr->internalRep.refValue.refPtr;
4097     len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
4098     objPtr->bytes = Jim_Alloc(len+1);
4099     memcpy(objPtr->bytes, buf, len+1);
4100     objPtr->length = len;
4101 }
4102
4103 /* returns true if 'c' is a valid reference tag character.
4104  * i.e. inside the range [_a-zA-Z0-9] */
4105 static int isrefchar(int c)
4106 {
4107     if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
4108         (c >= '0' && c <= '9')) return 1;
4109     return 0;
4110 }
4111
4112 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4113 {
4114     jim_wide wideValue;
4115     int i, len;
4116     const char *str, *start, *end;
4117     char refId[21];
4118     Jim_Reference *refPtr;
4119     Jim_HashEntry *he;
4120
4121     /* Get the string representation */
4122     str = Jim_GetString(objPtr, &len);
4123     /* Check if it looks like a reference */
4124     if (len < JIM_REFERENCE_SPACE) goto badformat;
4125     /* Trim spaces */
4126     start = str;
4127     end = str+len-1;
4128     while (*start == ' ') start++;
4129     while (*end == ' ' && end > start) end--;
4130     if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
4131     /* <reference.<1234567>.%020> */
4132     if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
4133     if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
4134     /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
4135     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4136         if (!isrefchar(start[12+i])) goto badformat;
4137     }
4138     /* Extract info from the refernece. */
4139     memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
4140     refId[20] = '\0';
4141     /* Try to convert the ID into a jim_wide */
4142     if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
4143     /* Check if the reference really exists! */
4144     he = Jim_FindHashEntry(&interp->references, &wideValue);
4145     if (he == NULL) {
4146         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4147         Jim_AppendStrings(interp, Jim_GetResult(interp),
4148                 "Invalid reference ID \"", str, "\"", NULL);
4149         return JIM_ERR;
4150     }
4151     refPtr = he->val;
4152     /* Free the old internal repr and set the new one. */
4153     Jim_FreeIntRep(interp, objPtr);
4154     objPtr->typePtr = &referenceObjType;
4155     objPtr->internalRep.refValue.id = wideValue;
4156     objPtr->internalRep.refValue.refPtr = refPtr;
4157     return JIM_OK;
4158
4159 badformat:
4160     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4161     Jim_AppendStrings(interp, Jim_GetResult(interp),
4162             "expected reference but got \"", str, "\"", NULL);
4163     return JIM_ERR;
4164 }
4165
4166 /* Returns a new reference pointing to objPtr, having cmdNamePtr
4167  * as finalizer command (or NULL if there is no finalizer).
4168  * The returned reference object has refcount = 0. */
4169 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
4170         Jim_Obj *cmdNamePtr)
4171 {
4172     struct Jim_Reference *refPtr;
4173     jim_wide wideValue = interp->referenceNextId;
4174     Jim_Obj *refObjPtr;
4175     const char *tag;
4176     int tagLen, i;
4177
4178     /* Perform the Garbage Collection if needed. */
4179     Jim_CollectIfNeeded(interp);
4180
4181     refPtr = Jim_Alloc(sizeof(*refPtr));
4182     refPtr->objPtr = objPtr;
4183     Jim_IncrRefCount(objPtr);
4184     refPtr->finalizerCmdNamePtr = cmdNamePtr;
4185     if (cmdNamePtr)
4186         Jim_IncrRefCount(cmdNamePtr);
4187     Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4188     refObjPtr = Jim_NewObj(interp);
4189     refObjPtr->typePtr = &referenceObjType;
4190     refObjPtr->bytes = NULL;
4191     refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4192     refObjPtr->internalRep.refValue.refPtr = refPtr;
4193     interp->referenceNextId++;
4194     /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4195      * that does not pass the 'isrefchar' test is replaced with '_' */
4196     tag = Jim_GetString(tagPtr, &tagLen);
4197     if (tagLen > JIM_REFERENCE_TAGLEN)
4198         tagLen = JIM_REFERENCE_TAGLEN;
4199     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4200         if (i < tagLen)
4201             refPtr->tag[i] = tag[i];
4202         else
4203             refPtr->tag[i] = '_';
4204     }
4205     refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4206     return refObjPtr;
4207 }
4208
4209 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4210 {
4211     if (objPtr->typePtr != &referenceObjType &&
4212         SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4213         return NULL;
4214     return objPtr->internalRep.refValue.refPtr;
4215 }
4216
4217 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4218 {
4219     Jim_Reference *refPtr;
4220
4221     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4222         return JIM_ERR;
4223     Jim_IncrRefCount(cmdNamePtr);
4224     if (refPtr->finalizerCmdNamePtr)
4225         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4226     refPtr->finalizerCmdNamePtr = cmdNamePtr;
4227     return JIM_OK;
4228 }
4229
4230 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4231 {
4232     Jim_Reference *refPtr;
4233
4234     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4235         return JIM_ERR;
4236     *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4237     return JIM_OK;
4238 }
4239
4240 /* -----------------------------------------------------------------------------
4241  * References Garbage Collection
4242  * ---------------------------------------------------------------------------*/
4243
4244 /* This the hash table type for the "MARK" phase of the GC */
4245 static Jim_HashTableType JimRefMarkHashTableType = {
4246     JimReferencesHTHashFunction,    /* hash function */
4247     JimReferencesHTKeyDup,          /* key dup */
4248     NULL,                           /* val dup */
4249     JimReferencesHTKeyCompare,      /* key compare */
4250     JimReferencesHTKeyDestructor,   /* key destructor */
4251     NULL                            /* val destructor */
4252 };
4253
4254 /* #define JIM_DEBUG_GC 1 */
4255
4256 /* Performs the garbage collection. */
4257 int Jim_Collect(Jim_Interp *interp)
4258 {
4259     Jim_HashTable marks;
4260     Jim_HashTableIterator *htiter;
4261     Jim_HashEntry *he;
4262     Jim_Obj *objPtr;
4263     int collected = 0;
4264
4265     /* Avoid recursive calls */
4266     if (interp->lastCollectId == -1) {
4267         /* Jim_Collect() already running. Return just now. */
4268         return 0;
4269     }
4270     interp->lastCollectId = -1;
4271
4272     /* Mark all the references found into the 'mark' hash table.
4273      * The references are searched in every live object that
4274      * is of a type that can contain references. */
4275     Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4276     objPtr = interp->liveList;
4277     while(objPtr) {
4278         if (objPtr->typePtr == NULL ||
4279             objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4280             const char *str, *p;
4281             int len;
4282
4283             /* If the object is of type reference, to get the
4284              * Id is simple... */
4285             if (objPtr->typePtr == &referenceObjType) {
4286                 Jim_AddHashEntry(&marks,
4287                     &objPtr->internalRep.refValue.id, NULL);
4288 #ifdef JIM_DEBUG_GC
4289                 Jim_fprintf(interp,interp->cookie_stdout,
4290                     "MARK (reference): %d refcount: %d" JIM_NL, 
4291                     (int) objPtr->internalRep.refValue.id,
4292                     objPtr->refCount);
4293 #endif
4294                 objPtr = objPtr->nextObjPtr;
4295                 continue;
4296             }
4297             /* Get the string repr of the object we want
4298              * to scan for references. */
4299             p = str = Jim_GetString(objPtr, &len);
4300             /* Skip objects too little to contain references. */
4301             if (len < JIM_REFERENCE_SPACE) {
4302                 objPtr = objPtr->nextObjPtr;
4303                 continue;
4304             }
4305             /* Extract references from the object string repr. */
4306             while(1) {
4307                 int i;
4308                 jim_wide id;
4309                 char buf[21];
4310
4311                 if ((p = strstr(p, "<reference.<")) == NULL)
4312                     break;
4313                 /* Check if it's a valid reference. */
4314                 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4315                 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4316                 for (i = 21; i <= 40; i++)
4317                     if (!isdigit((int)p[i]))
4318                         break;
4319                 /* Get the ID */
4320                 memcpy(buf, p+21, 20);
4321                 buf[20] = '\0';
4322                 Jim_StringToWide(buf, &id, 10);
4323
4324                 /* Ok, a reference for the given ID
4325                  * was found. Mark it. */
4326                 Jim_AddHashEntry(&marks, &id, NULL);
4327 #ifdef JIM_DEBUG_GC
4328                 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4329 #endif
4330                 p += JIM_REFERENCE_SPACE;
4331             }
4332         }
4333         objPtr = objPtr->nextObjPtr;
4334     }
4335
4336     /* Run the references hash table to destroy every reference that
4337      * is not referenced outside (not present in the mark HT). */
4338     htiter = Jim_GetHashTableIterator(&interp->references);
4339     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4340         const jim_wide *refId;
4341         Jim_Reference *refPtr;
4342
4343         refId = he->key;
4344         /* Check if in the mark phase we encountered
4345          * this reference. */
4346         if (Jim_FindHashEntry(&marks, refId) == NULL) {
4347 #ifdef JIM_DEBUG_GC
4348             Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4349 #endif
4350             collected++;
4351             /* Drop the reference, but call the
4352              * finalizer first if registered. */
4353             refPtr = he->val;
4354             if (refPtr->finalizerCmdNamePtr) {
4355                 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4356                 Jim_Obj *objv[3], *oldResult;
4357
4358                 JimFormatReference(refstr, refPtr, *refId);
4359
4360                 objv[0] = refPtr->finalizerCmdNamePtr;
4361                 objv[1] = Jim_NewStringObjNoAlloc(interp,
4362                         refstr, 32);
4363                 objv[2] = refPtr->objPtr;
4364                 Jim_IncrRefCount(objv[0]);
4365                 Jim_IncrRefCount(objv[1]);
4366                 Jim_IncrRefCount(objv[2]);
4367
4368                 /* Drop the reference itself */
4369                 Jim_DeleteHashEntry(&interp->references, refId);
4370
4371                 /* Call the finalizer. Errors ignored. */
4372                 oldResult = interp->result;
4373                 Jim_IncrRefCount(oldResult);
4374                 Jim_EvalObjVector(interp, 3, objv);
4375                 Jim_SetResult(interp, oldResult);
4376                 Jim_DecrRefCount(interp, oldResult);
4377
4378                 Jim_DecrRefCount(interp, objv[0]);
4379                 Jim_DecrRefCount(interp, objv[1]);
4380                 Jim_DecrRefCount(interp, objv[2]);
4381             } else {
4382                 Jim_DeleteHashEntry(&interp->references, refId);
4383             }
4384         }
4385     }
4386     Jim_FreeHashTableIterator(htiter);
4387     Jim_FreeHashTable(&marks);
4388     interp->lastCollectId = interp->referenceNextId;
4389     interp->lastCollectTime = time(NULL);
4390     return collected;
4391 }
4392
4393 #define JIM_COLLECT_ID_PERIOD 5000
4394 #define JIM_COLLECT_TIME_PERIOD 300
4395
4396 void Jim_CollectIfNeeded(Jim_Interp *interp)
4397 {
4398     jim_wide elapsedId;
4399     int elapsedTime;
4400     
4401     elapsedId = interp->referenceNextId - interp->lastCollectId;
4402     elapsedTime = time(NULL) - interp->lastCollectTime;
4403
4404
4405     if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4406         elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4407         Jim_Collect(interp);
4408     }
4409 }
4410
4411 /* -----------------------------------------------------------------------------
4412  * Interpreter related functions
4413  * ---------------------------------------------------------------------------*/
4414
4415 Jim_Interp *Jim_CreateInterp(void)
4416 {
4417     Jim_Interp *i = Jim_Alloc(sizeof(*i));
4418     Jim_Obj *pathPtr;
4419
4420     i->errorLine = 0;
4421     i->errorFileName = Jim_StrDup("");
4422     i->numLevels = 0;
4423     i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4424     i->returnCode = JIM_OK;
4425     i->exitCode = 0;
4426     i->procEpoch = 0;
4427     i->callFrameEpoch = 0;
4428     i->liveList = i->freeList = NULL;
4429     i->scriptFileName = Jim_StrDup("");
4430     i->referenceNextId = 0;
4431     i->lastCollectId = 0;
4432     i->lastCollectTime = time(NULL);
4433     i->freeFramesList = NULL;
4434     i->prngState = NULL;
4435     i->evalRetcodeLevel = -1;
4436     i->cookie_stdin = stdin;
4437     i->cookie_stdout = stdout;
4438     i->cookie_stderr = stderr;
4439         i->cb_fwrite   = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4440         i->cb_fread    = ((size_t (*)(       void *, size_t, size_t, void *))(fread));
4441         i->cb_vfprintf = ((int    (*)( void *, const char *fmt, va_list ))(vfprintf));
4442         i->cb_fflush   = ((int    (*)( void *))(fflush));
4443         i->cb_fgets    = ((char * (*)( char *, int, void *))(fgets));
4444
4445     /* Note that we can create objects only after the
4446      * interpreter liveList and freeList pointers are
4447      * initialized to NULL. */
4448     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4449     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4450     Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4451             NULL);
4452     Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4453     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4454     Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4455     i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4456     i->emptyObj = Jim_NewEmptyStringObj(i);
4457     i->result = i->emptyObj;
4458     i->stackTrace = Jim_NewListObj(i, NULL, 0);
4459     i->unknown = Jim_NewStringObj(i, "unknown", -1);
4460     i->unknown_called = 0;
4461     Jim_IncrRefCount(i->emptyObj);
4462     Jim_IncrRefCount(i->result);
4463     Jim_IncrRefCount(i->stackTrace);
4464     Jim_IncrRefCount(i->unknown);
4465
4466     /* Initialize key variables every interpreter should contain */
4467     pathPtr = Jim_NewStringObj(i, "./", -1);
4468     Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4469     Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4470
4471     /* Export the core API to extensions */
4472     JimRegisterCoreApi(i);
4473     return i;
4474 }
4475
4476 /* This is the only function Jim exports directly without
4477  * to use the STUB system. It is only used by embedders
4478  * in order to get an interpreter with the Jim API pointers
4479  * registered. */
4480 Jim_Interp *ExportedJimCreateInterp(void)
4481 {
4482     return Jim_CreateInterp();
4483 }
4484
4485 void Jim_FreeInterp(Jim_Interp *i)
4486 {
4487     Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4488     Jim_Obj *objPtr, *nextObjPtr;
4489
4490     Jim_DecrRefCount(i, i->emptyObj);
4491     Jim_DecrRefCount(i, i->result);
4492     Jim_DecrRefCount(i, i->stackTrace);
4493     Jim_DecrRefCount(i, i->unknown);
4494     Jim_Free((void*)i->errorFileName);
4495     Jim_Free((void*)i->scriptFileName);
4496     Jim_FreeHashTable(&i->commands);
4497     Jim_FreeHashTable(&i->references);
4498     Jim_FreeHashTable(&i->stub);
4499     Jim_FreeHashTable(&i->assocData);
4500     Jim_FreeHashTable(&i->packages);
4501     Jim_Free(i->prngState);
4502     /* Free the call frames list */
4503     while(cf) {
4504         prevcf = cf->parentCallFrame;
4505         JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4506         cf = prevcf;
4507     }
4508     /* Check that the live object list is empty, otherwise
4509      * there is a memory leak. */
4510     if (i->liveList != NULL) {
4511         Jim_Obj *objPtr = i->liveList;
4512     
4513         Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4514         Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4515         while(objPtr) {
4516             const char *type = objPtr->typePtr ?
4517                 objPtr->typePtr->name : "";
4518             Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4519                     objPtr, type,
4520                     objPtr->bytes ? objPtr->bytes
4521                     : "(null)", objPtr->refCount);
4522             if (objPtr->typePtr == &sourceObjType) {
4523                 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4524                 objPtr->internalRep.sourceValue.fileName,
4525                 objPtr->internalRep.sourceValue.lineNumber);
4526             }
4527             objPtr = objPtr->nextObjPtr;
4528         }
4529         Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4530         Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4531     }
4532     /* Free all the freed objects. */
4533     objPtr = i->freeList;
4534     while (objPtr) {
4535         nextObjPtr = objPtr->nextObjPtr;
4536         Jim_Free(objPtr);
4537         objPtr = nextObjPtr;
4538     }
4539     /* Free cached CallFrame structures */
4540     cf = i->freeFramesList;
4541     while(cf) {
4542         nextcf = cf->nextFramePtr;
4543         if (cf->vars.table != NULL)
4544             Jim_Free(cf->vars.table);
4545         Jim_Free(cf);
4546         cf = nextcf;
4547     }
4548     /* Free the sharedString hash table. Make sure to free it
4549      * after every other Jim_Object was freed. */
4550     Jim_FreeHashTable(&i->sharedStrings);
4551     /* Free the interpreter structure. */
4552     Jim_Free(i);
4553 }
4554
4555 /* Store the call frame relative to the level represented by
4556  * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4557  * level is assumed to be '1'.
4558  *
4559  * If a newLevelptr int pointer is specified, the function stores
4560  * the absolute level integer value of the new target callframe into
4561  * *newLevelPtr. (this is used to adjust interp->numLevels
4562  * in the implementation of [uplevel], so that [info level] will
4563  * return a correct information).
4564  *
4565  * This function accepts the 'level' argument in the form
4566  * of the commands [uplevel] and [upvar].
4567  *
4568  * For a function accepting a relative integer as level suitable
4569  * for implementation of [info level ?level?] check the
4570  * GetCallFrameByInteger() function. */
4571 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4572         Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4573 {
4574     long level;
4575     const char *str;
4576     Jim_CallFrame *framePtr;
4577
4578     if (newLevelPtr) *newLevelPtr = interp->numLevels;
4579     if (levelObjPtr) {
4580         str = Jim_GetString(levelObjPtr, NULL);
4581         if (str[0] == '#') {
4582             char *endptr;
4583             /* speedup for the toplevel (level #0) */
4584             if (str[1] == '0' && str[2] == '\0') {
4585                 if (newLevelPtr) *newLevelPtr = 0;
4586                 *framePtrPtr = interp->topFramePtr;
4587                 return JIM_OK;
4588             }
4589
4590             level = strtol(str+1, &endptr, 0);
4591             if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4592                 goto badlevel;
4593             /* An 'absolute' level is converted into the
4594              * 'number of levels to go back' format. */
4595             level = interp->numLevels - level;
4596             if (level < 0) goto badlevel;
4597         } else {
4598             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4599                 goto badlevel;
4600         }
4601     } else {
4602         str = "1"; /* Needed to format the error message. */
4603         level = 1;
4604     }
4605     /* Lookup */
4606     framePtr = interp->framePtr;
4607     if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4608     while (level--) {
4609         framePtr = framePtr->parentCallFrame;
4610         if (framePtr == NULL) goto badlevel;
4611     }
4612     *framePtrPtr = framePtr;
4613     return JIM_OK;
4614 badlevel:
4615     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4616     Jim_AppendStrings(interp, Jim_GetResult(interp),
4617             "bad level \"", str, "\"", NULL);
4618     return JIM_ERR;
4619 }
4620
4621 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4622  * as a relative integer like in the [info level ?level?] command. */
4623 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4624         Jim_CallFrame **framePtrPtr)
4625 {
4626     jim_wide level;
4627     jim_wide relLevel; /* level relative to the current one. */
4628     Jim_CallFrame *framePtr;
4629
4630     if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4631         goto badlevel;
4632     if (level > 0) {
4633         /* An 'absolute' level is converted into the
4634          * 'number of levels to go back' format. */
4635         relLevel = interp->numLevels - level;
4636     } else {
4637         relLevel = -level;
4638     }
4639     /* Lookup */
4640     framePtr = interp->framePtr;
4641     while (relLevel--) {
4642         framePtr = framePtr->parentCallFrame;
4643         if (framePtr == NULL) goto badlevel;
4644     }
4645     *framePtrPtr = framePtr;
4646     return JIM_OK;
4647 badlevel:
4648     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4649     Jim_AppendStrings(interp, Jim_GetResult(interp),
4650             "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4651     return JIM_ERR;
4652 }
4653
4654 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4655 {
4656     Jim_Free((void*)interp->errorFileName);
4657     interp->errorFileName = Jim_StrDup(filename);
4658 }
4659
4660 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4661 {
4662     interp->errorLine = linenr;
4663 }
4664
4665 static void JimResetStackTrace(Jim_Interp *interp)
4666 {
4667     Jim_DecrRefCount(interp, interp->stackTrace);
4668     interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4669     Jim_IncrRefCount(interp->stackTrace);
4670 }
4671
4672 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4673         const char *filename, int linenr)
4674 {
4675     /* No need to add this dummy entry to the stack trace */
4676     if (strcmp(procname, "unknown") == 0) {
4677         return;
4678     }
4679
4680     if (Jim_IsShared(interp->stackTrace)) {
4681         interp->stackTrace =
4682             Jim_DuplicateObj(interp, interp->stackTrace);
4683         Jim_IncrRefCount(interp->stackTrace);
4684     }
4685     Jim_ListAppendElement(interp, interp->stackTrace,
4686             Jim_NewStringObj(interp, procname, -1));
4687     Jim_ListAppendElement(interp, interp->stackTrace,
4688             Jim_NewStringObj(interp, filename, -1));
4689     Jim_ListAppendElement(interp, interp->stackTrace,
4690             Jim_NewIntObj(interp, linenr));
4691 }
4692
4693 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4694 {
4695     AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4696     assocEntryPtr->delProc = delProc;
4697     assocEntryPtr->data = data;
4698     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4699 }
4700
4701 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4702 {
4703     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4704     if (entryPtr != NULL) {
4705         AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4706         return assocEntryPtr->data;
4707     }
4708     return NULL;
4709 }
4710
4711 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4712 {
4713     return Jim_DeleteHashEntry(&interp->assocData, key);
4714 }
4715
4716 int Jim_GetExitCode(Jim_Interp *interp) {
4717     return interp->exitCode;
4718 }
4719
4720 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4721 {
4722     if (fp != NULL) interp->cookie_stdin = fp;
4723     return interp->cookie_stdin;
4724 }
4725
4726 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4727 {
4728     if (fp != NULL) interp->cookie_stdout = fp;
4729     return interp->cookie_stdout;
4730 }
4731
4732 void *Jim_SetStderr(Jim_Interp *interp, void  *fp)
4733 {
4734     if (fp != NULL) interp->cookie_stderr = fp;
4735     return interp->cookie_stderr;
4736 }
4737
4738 /* -----------------------------------------------------------------------------
4739  * Shared strings.
4740  * Every interpreter has an hash table where to put shared dynamically
4741  * allocate strings that are likely to be used a lot of times.
4742  * For example, in the 'source' object type, there is a pointer to
4743  * the filename associated with that object. Every script has a lot
4744  * of this objects with the identical file name, so it is wise to share
4745  * this info.
4746  *
4747  * The API is trivial: Jim_GetSharedString(interp, "foobar")
4748  * returns the pointer to the shared string. Every time a reference
4749  * to the string is no longer used, the user should call
4750  * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4751  * a given string, it is removed from the hash table.
4752  * ---------------------------------------------------------------------------*/
4753 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4754 {
4755     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4756
4757     if (he == NULL) {
4758         char *strCopy = Jim_StrDup(str);
4759
4760         Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4761         return strCopy;
4762     } else {
4763         long refCount = (long) he->val;
4764
4765         refCount++;
4766         he->val = (void*) refCount;
4767         return he->key;
4768     }
4769 }
4770
4771 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4772 {
4773     long refCount;
4774     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4775
4776     if (he == NULL)
4777         Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4778               "unknown shared string '%s'", str);
4779     refCount = (long) he->val;
4780     refCount--;
4781     if (refCount == 0) {
4782         Jim_DeleteHashEntry(&interp->sharedStrings, str);
4783     } else {
4784         he->val = (void*) refCount;
4785     }
4786 }
4787
4788 /* -----------------------------------------------------------------------------
4789  * Integer object
4790  * ---------------------------------------------------------------------------*/
4791 #define JIM_INTEGER_SPACE 24
4792
4793 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4794 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4795
4796 static Jim_ObjType intObjType = {
4797     "int",
4798     NULL,
4799     NULL,
4800     UpdateStringOfInt,
4801     JIM_TYPE_NONE,
4802 };
4803
4804 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4805 {
4806     int len;
4807     char buf[JIM_INTEGER_SPACE+1];
4808
4809     len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4810     objPtr->bytes = Jim_Alloc(len+1);
4811     memcpy(objPtr->bytes, buf, len+1);
4812     objPtr->length = len;
4813 }
4814
4815 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4816 {
4817     jim_wide wideValue;
4818     const char *str;
4819
4820     /* Get the string representation */
4821     str = Jim_GetString(objPtr, NULL);
4822     /* Try to convert into a jim_wide */
4823     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4824         if (flags & JIM_ERRMSG) {
4825             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4826             Jim_AppendStrings(interp, Jim_GetResult(interp),
4827                     "expected integer but got \"", str, "\"", NULL);
4828         }
4829         return JIM_ERR;
4830     }
4831     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4832         errno == ERANGE) {
4833         Jim_SetResultString(interp,
4834             "Integer value too big to be represented", -1);
4835         return JIM_ERR;
4836     }
4837     /* Free the old internal repr and set the new one. */
4838     Jim_FreeIntRep(interp, objPtr);
4839     objPtr->typePtr = &intObjType;
4840     objPtr->internalRep.wideValue = wideValue;
4841     return JIM_OK;
4842 }
4843
4844 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4845 {
4846     if (objPtr->typePtr != &intObjType &&
4847         SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4848         return JIM_ERR;
4849     *widePtr = objPtr->internalRep.wideValue;
4850     return JIM_OK;
4851 }
4852
4853 /* Get a wide but does not set an error if the format is bad. */
4854 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4855         jim_wide *widePtr)
4856 {
4857     if (objPtr->typePtr != &intObjType &&
4858         SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4859         return JIM_ERR;
4860     *widePtr = objPtr->internalRep.wideValue;
4861     return JIM_OK;
4862 }
4863
4864 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4865 {
4866     jim_wide wideValue;
4867     int retval;
4868
4869     retval = Jim_GetWide(interp, objPtr, &wideValue);
4870     if (retval == JIM_OK) {
4871         *longPtr = (long) wideValue;
4872         return JIM_OK;
4873     }
4874     return JIM_ERR;
4875 }
4876
4877 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4878 {
4879     if (Jim_IsShared(objPtr))
4880         Jim_Panic(interp,"Jim_SetWide called with shared object");
4881     if (objPtr->typePtr != &intObjType) {
4882         Jim_FreeIntRep(interp, objPtr);
4883         objPtr->typePtr = &intObjType;
4884     }
4885     Jim_InvalidateStringRep(objPtr);
4886     objPtr->internalRep.wideValue = wideValue;
4887 }
4888
4889 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4890 {
4891     Jim_Obj *objPtr;
4892
4893     objPtr = Jim_NewObj(interp);
4894     objPtr->typePtr = &intObjType;
4895     objPtr->bytes = NULL;
4896     objPtr->internalRep.wideValue = wideValue;
4897     return objPtr;
4898 }
4899
4900 /* -----------------------------------------------------------------------------
4901  * Double object
4902  * ---------------------------------------------------------------------------*/
4903 #define JIM_DOUBLE_SPACE 30
4904
4905 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4906 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4907
4908 static Jim_ObjType doubleObjType = {
4909     "double",
4910     NULL,
4911     NULL,
4912     UpdateStringOfDouble,
4913     JIM_TYPE_NONE,
4914 };
4915
4916 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4917 {
4918     int len;
4919     char buf[JIM_DOUBLE_SPACE+1];
4920
4921     len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4922     objPtr->bytes = Jim_Alloc(len+1);
4923     memcpy(objPtr->bytes, buf, len+1);
4924     objPtr->length = len;
4925 }
4926
4927 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4928 {
4929     double doubleValue;
4930     const char *str;
4931
4932     /* Get the string representation */
4933     str = Jim_GetString(objPtr, NULL);
4934     /* Try to convert into a double */
4935     if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4936         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4937         Jim_AppendStrings(interp, Jim_GetResult(interp),
4938                 "expected number but got '", str, "'", NULL);
4939         return JIM_ERR;
4940     }
4941     /* Free the old internal repr and set the new one. */
4942     Jim_FreeIntRep(interp, objPtr);
4943     objPtr->typePtr = &doubleObjType;
4944     objPtr->internalRep.doubleValue = doubleValue;
4945     return JIM_OK;
4946 }
4947
4948 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4949 {
4950     if (objPtr->typePtr != &doubleObjType &&
4951         SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4952         return JIM_ERR;
4953     *doublePtr = objPtr->internalRep.doubleValue;
4954     return JIM_OK;
4955 }
4956
4957 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4958 {
4959     if (Jim_IsShared(objPtr))
4960         Jim_Panic(interp,"Jim_SetDouble called with shared object");
4961     if (objPtr->typePtr != &doubleObjType) {
4962         Jim_FreeIntRep(interp, objPtr);
4963         objPtr->typePtr = &doubleObjType;
4964     }
4965     Jim_InvalidateStringRep(objPtr);
4966     objPtr->internalRep.doubleValue = doubleValue;
4967 }
4968
4969 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4970 {
4971     Jim_Obj *objPtr;
4972
4973     objPtr = Jim_NewObj(interp);
4974     objPtr->typePtr = &doubleObjType;
4975     objPtr->bytes = NULL;
4976     objPtr->internalRep.doubleValue = doubleValue;
4977     return objPtr;
4978 }
4979
4980 /* -----------------------------------------------------------------------------
4981  * List object
4982  * ---------------------------------------------------------------------------*/
4983 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4984 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4985 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4986 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4987 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4988
4989 /* Note that while the elements of the list may contain references,
4990  * the list object itself can't. This basically means that the
4991  * list object string representation as a whole can't contain references
4992  * that are not presents in the single elements. */
4993 static Jim_ObjType listObjType = {
4994     "list",
4995     FreeListInternalRep,
4996     DupListInternalRep,
4997     UpdateStringOfList,
4998     JIM_TYPE_NONE,
4999 };
5000
5001 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5002 {
5003     int i;
5004
5005     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5006         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
5007     }
5008     Jim_Free(objPtr->internalRep.listValue.ele);
5009 }
5010
5011 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5012 {
5013     int i;
5014     JIM_NOTUSED(interp);
5015
5016     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
5017     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
5018     dupPtr->internalRep.listValue.ele =
5019         Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
5020     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
5021             sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
5022     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
5023         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
5024     }
5025     dupPtr->typePtr = &listObjType;
5026 }
5027
5028 /* The following function checks if a given string can be encoded
5029  * into a list element without any kind of quoting, surrounded by braces,
5030  * or using escapes to quote. */
5031 #define JIM_ELESTR_SIMPLE 0
5032 #define JIM_ELESTR_BRACE 1
5033 #define JIM_ELESTR_QUOTE 2
5034 static int ListElementQuotingType(const char *s, int len)
5035 {
5036     int i, level, trySimple = 1;
5037
5038     /* Try with the SIMPLE case */
5039     if (len == 0) return JIM_ELESTR_BRACE;
5040     if (s[0] == '"' || s[0] == '{') {
5041         trySimple = 0;
5042         goto testbrace;
5043     }
5044     for (i = 0; i < len; i++) {
5045         switch(s[i]) {
5046         case ' ':
5047         case '$':
5048         case '"':
5049         case '[':
5050         case ']':
5051         case ';':
5052         case '\\':
5053         case '\r':
5054         case '\n':
5055         case '\t':
5056         case '\f':
5057         case '\v':
5058             trySimple = 0;
5059         case '{':
5060         case '}':
5061             goto testbrace;
5062         }
5063     }
5064     return JIM_ELESTR_SIMPLE;
5065
5066 testbrace:
5067     /* Test if it's possible to do with braces */
5068     if (s[len-1] == '\\' ||
5069         s[len-1] == ']') return JIM_ELESTR_QUOTE;
5070     level = 0;
5071     for (i = 0; i < len; i++) {
5072         switch(s[i]) {
5073         case '{': level++; break;
5074         case '}': level--;
5075               if (level < 0) return JIM_ELESTR_QUOTE;
5076               break;
5077         case '\\':
5078               if (s[i+1] == '\n')
5079                   return JIM_ELESTR_QUOTE;
5080               else
5081                   if (s[i+1] != '\0') i++;
5082               break;
5083         }
5084     }
5085     if (level == 0) {
5086         if (!trySimple) return JIM_ELESTR_BRACE;
5087         for (i = 0; i < len; i++) {
5088             switch(s[i]) {
5089             case ' ':
5090             case '$':
5091             case '"':
5092             case '[':
5093             case ']':
5094             case ';':
5095             case '\\':
5096             case '\r':
5097             case '\n':
5098             case '\t':
5099             case '\f':
5100             case '\v':
5101                 return JIM_ELESTR_BRACE;
5102                 break;
5103             }
5104         }
5105         return JIM_ELESTR_SIMPLE;
5106     }
5107     return JIM_ELESTR_QUOTE;
5108 }
5109
5110 /* Returns the malloc-ed representation of a string
5111  * using backslash to quote special chars. */
5112 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
5113 {
5114     char *q = Jim_Alloc(len*2+1), *p;
5115
5116     p = q;
5117     while(*s) {
5118         switch (*s) {
5119         case ' ':
5120         case '$':
5121         case '"':
5122         case '[':
5123         case ']':
5124         case '{':
5125         case '}':
5126         case ';':
5127         case '\\':
5128             *p++ = '\\';
5129             *p++ = *s++;
5130             break;
5131         case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
5132         case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
5133         case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
5134         case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
5135         case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
5136         default:
5137             *p++ = *s++;
5138             break;
5139         }
5140     }
5141     *p = '\0';
5142     *qlenPtr = p-q;
5143     return q;
5144 }
5145
5146 void UpdateStringOfList(struct Jim_Obj *objPtr)
5147 {
5148     int i, bufLen, realLength;
5149     const char *strRep;
5150     char *p;
5151     int *quotingType;
5152     Jim_Obj **ele = objPtr->internalRep.listValue.ele;
5153
5154     /* (Over) Estimate the space needed. */
5155     quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
5156     bufLen = 0;
5157     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5158         int len;
5159
5160         strRep = Jim_GetString(ele[i], &len);
5161         quotingType[i] = ListElementQuotingType(strRep, len);
5162         switch (quotingType[i]) {
5163         case JIM_ELESTR_SIMPLE: bufLen += len; break;
5164         case JIM_ELESTR_BRACE: bufLen += len+2; break;
5165         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5166         }
5167         bufLen++; /* elements separator. */
5168     }
5169     bufLen++;
5170
5171     /* Generate the string rep. */
5172     p = objPtr->bytes = Jim_Alloc(bufLen+1);
5173     realLength = 0;
5174     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5175         int len, qlen;
5176         const char *strRep = Jim_GetString(ele[i], &len);
5177         char *q;
5178
5179         switch(quotingType[i]) {
5180         case JIM_ELESTR_SIMPLE:
5181             memcpy(p, strRep, len);
5182             p += len;
5183             realLength += len;
5184             break;
5185         case JIM_ELESTR_BRACE:
5186             *p++ = '{';
5187             memcpy(p, strRep, len);
5188             p += len;
5189             *p++ = '}';
5190             realLength += len+2;
5191             break;
5192         case JIM_ELESTR_QUOTE:
5193             q = BackslashQuoteString(strRep, len, &qlen);
5194             memcpy(p, q, qlen);
5195             Jim_Free(q);
5196             p += qlen;
5197             realLength += qlen;
5198             break;
5199         }
5200         /* Add a separating space */
5201         if (i+1 != objPtr->internalRep.listValue.len) {
5202             *p++ = ' ';
5203             realLength ++;
5204         }
5205     }
5206     *p = '\0'; /* nul term. */
5207     objPtr->length = realLength;
5208     Jim_Free(quotingType);
5209 }
5210
5211 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5212 {
5213     struct JimParserCtx parser;
5214     const char *str;
5215     int strLen;
5216
5217     /* Get the string representation */
5218     str = Jim_GetString(objPtr, &strLen);
5219
5220     /* Free the old internal repr just now and initialize the
5221      * new one just now. The string->list conversion can't fail. */
5222     Jim_FreeIntRep(interp, objPtr);
5223     objPtr->typePtr = &listObjType;
5224     objPtr->internalRep.listValue.len = 0;
5225     objPtr->internalRep.listValue.maxLen = 0;
5226     objPtr->internalRep.listValue.ele = NULL;
5227
5228     /* Convert into a list */
5229     JimParserInit(&parser, str, strLen, 1);
5230     while(!JimParserEof(&parser)) {
5231         char *token;
5232         int tokenLen, type;
5233         Jim_Obj *elementPtr;
5234
5235         JimParseList(&parser);
5236         if (JimParserTtype(&parser) != JIM_TT_STR &&
5237             JimParserTtype(&parser) != JIM_TT_ESC)
5238             continue;
5239         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5240         elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5241         ListAppendElement(objPtr, elementPtr);
5242     }
5243     return JIM_OK;
5244 }
5245
5246 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, 
5247         int len)
5248 {
5249     Jim_Obj *objPtr;
5250     int i;
5251
5252     objPtr = Jim_NewObj(interp);
5253     objPtr->typePtr = &listObjType;
5254     objPtr->bytes = NULL;
5255     objPtr->internalRep.listValue.ele = NULL;
5256     objPtr->internalRep.listValue.len = 0;
5257     objPtr->internalRep.listValue.maxLen = 0;
5258     for (i = 0; i < len; i++) {
5259         ListAppendElement(objPtr, elements[i]);
5260     }
5261     return objPtr;
5262 }
5263
5264 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5265  * length of the vector. Note that the user of this function should make
5266  * sure that the list object can't shimmer while the vector returned
5267  * is in use, this vector is the one stored inside the internal representation
5268  * of the list object. This function is not exported, extensions should
5269  * always access to the List object elements using Jim_ListIndex(). */
5270 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5271         Jim_Obj ***listVec)
5272 {
5273     Jim_ListLength(interp, listObj, argc);
5274     assert(listObj->typePtr == &listObjType);
5275     *listVec = listObj->internalRep.listValue.ele;
5276 }
5277
5278 /* ListSortElements type values */
5279 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5280       JIM_LSORT_NOCASE_DECR};
5281
5282 /* Sort the internal rep of a list. */
5283 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5284 {
5285     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5286 }
5287
5288 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5289 {
5290     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5291 }
5292
5293 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5294 {
5295     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5296 }
5297
5298 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5299 {
5300     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5301 }
5302
5303 /* Sort a list *in place*. MUST be called with non-shared objects. */
5304 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5305 {
5306     typedef int (qsort_comparator)(const void *, const void *);
5307     int (*fn)(Jim_Obj**, Jim_Obj**);
5308     Jim_Obj **vector;
5309     int len;
5310
5311     if (Jim_IsShared(listObjPtr))
5312         Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5313     if (listObjPtr->typePtr != &listObjType)
5314         SetListFromAny(interp, listObjPtr);
5315
5316     vector = listObjPtr->internalRep.listValue.ele;
5317     len = listObjPtr->internalRep.listValue.len;
5318     switch (type) {
5319         case JIM_LSORT_ASCII: fn = ListSortString;  break;
5320         case JIM_LSORT_NOCASE: fn = ListSortStringNoCase;  break;
5321         case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr;  break;
5322         case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr;  break;
5323         default:
5324             fn = NULL; /* avoid warning */
5325             Jim_Panic(interp,"ListSort called with invalid sort type");
5326     }
5327     qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5328     Jim_InvalidateStringRep(listObjPtr);
5329 }
5330
5331 /* This is the low-level function to append an element to a list.
5332  * The higher-level Jim_ListAppendElement() performs shared object
5333  * check and invalidate the string repr. This version is used
5334  * in the internals of the List Object and is not exported.
5335  *
5336  * NOTE: this function can be called only against objects
5337  * with internal type of List. */
5338 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5339 {
5340     int requiredLen = listPtr->internalRep.listValue.len + 1;
5341
5342     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5343         int maxLen = requiredLen * 2;
5344
5345         listPtr->internalRep.listValue.ele =
5346             Jim_Realloc(listPtr->internalRep.listValue.ele,
5347                     sizeof(Jim_Obj*)*maxLen);
5348         listPtr->internalRep.listValue.maxLen = maxLen;
5349     }
5350     listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5351         objPtr;
5352     listPtr->internalRep.listValue.len ++;
5353     Jim_IncrRefCount(objPtr);
5354 }
5355
5356 /* This is the low-level function to insert elements into a list.
5357  * The higher-level Jim_ListInsertElements() performs shared object
5358  * check and invalidate the string repr. This version is used
5359  * in the internals of the List Object and is not exported.
5360  *
5361  * NOTE: this function can be called only against objects
5362  * with internal type of List. */
5363 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5364         Jim_Obj *const *elemVec)
5365 {
5366     int currentLen = listPtr->internalRep.listValue.len;
5367     int requiredLen = currentLen + elemc;
5368     int i;
5369     Jim_Obj **point;
5370
5371     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5372         int maxLen = requiredLen * 2;
5373
5374         listPtr->internalRep.listValue.ele =
5375             Jim_Realloc(listPtr->internalRep.listValue.ele,
5376                     sizeof(Jim_Obj*)*maxLen);
5377         listPtr->internalRep.listValue.maxLen = maxLen;
5378     }
5379     point = listPtr->internalRep.listValue.ele + index;
5380     memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5381     for (i=0; i < elemc; ++i) {
5382         point[i] = elemVec[i];
5383         Jim_IncrRefCount(point[i]);
5384     }
5385     listPtr->internalRep.listValue.len += elemc;
5386 }
5387
5388 /* Appends every element of appendListPtr into listPtr.
5389  * Both have to be of the list type. */
5390 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5391 {
5392     int i, oldLen = listPtr->internalRep.listValue.len;
5393     int appendLen = appendListPtr->internalRep.listValue.len;
5394     int requiredLen = oldLen + appendLen;
5395
5396     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5397         int maxLen = requiredLen * 2;
5398
5399         listPtr->internalRep.listValue.ele =
5400             Jim_Realloc(listPtr->internalRep.listValue.ele,
5401                     sizeof(Jim_Obj*)*maxLen);
5402         listPtr->internalRep.listValue.maxLen = maxLen;
5403     }
5404     for (i = 0; i < appendLen; i++) {
5405         Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5406         listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5407         Jim_IncrRefCount(objPtr);
5408     }
5409     listPtr->internalRep.listValue.len += appendLen;
5410 }
5411
5412 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5413 {
5414     if (Jim_IsShared(listPtr))
5415         Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5416     if (listPtr->typePtr != &listObjType)
5417         SetListFromAny(interp, listPtr);
5418     Jim_InvalidateStringRep(listPtr);
5419     ListAppendElement(listPtr, objPtr);
5420 }
5421
5422 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5423 {
5424     if (Jim_IsShared(listPtr))
5425         Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5426     if (listPtr->typePtr != &listObjType)
5427         SetListFromAny(interp, listPtr);
5428     Jim_InvalidateStringRep(listPtr);
5429     ListAppendList(listPtr, appendListPtr);
5430 }
5431
5432 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5433 {
5434     if (listPtr->typePtr != &listObjType)
5435         SetListFromAny(interp, listPtr);
5436     *intPtr = listPtr->internalRep.listValue.len;
5437 }
5438
5439 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5440         int objc, Jim_Obj *const *objVec)
5441 {
5442     if (Jim_IsShared(listPtr))
5443         Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5444     if (listPtr->typePtr != &listObjType)
5445         SetListFromAny(interp, listPtr);
5446     if (index >= 0 && index > listPtr->internalRep.listValue.len)
5447         index = listPtr->internalRep.listValue.len;
5448     else if (index < 0 ) 
5449         index = 0;
5450     Jim_InvalidateStringRep(listPtr);
5451     ListInsertElements(listPtr, index, objc, objVec);
5452 }
5453
5454 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5455         Jim_Obj **objPtrPtr, int flags)
5456 {
5457     if (listPtr->typePtr != &listObjType)
5458         SetListFromAny(interp, listPtr);
5459     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5460         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5461         if (flags & JIM_ERRMSG) {
5462             Jim_SetResultString(interp,
5463                 "list index out of range", -1);
5464         }
5465         return JIM_ERR;
5466     }
5467     if (index < 0)
5468         index = listPtr->internalRep.listValue.len+index;
5469     *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5470     return JIM_OK;
5471 }
5472
5473 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5474         Jim_Obj *newObjPtr, int flags)
5475 {
5476     if (listPtr->typePtr != &listObjType)
5477         SetListFromAny(interp, listPtr);
5478     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5479         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5480         if (flags & JIM_ERRMSG) {
5481             Jim_SetResultString(interp,
5482                 "list index out of range", -1);
5483         }
5484         return JIM_ERR;
5485     }
5486     if (index < 0)
5487         index = listPtr->internalRep.listValue.len+index;
5488     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5489     listPtr->internalRep.listValue.ele[index] = newObjPtr;
5490     Jim_IncrRefCount(newObjPtr);
5491     return JIM_OK;
5492 }
5493
5494 /* Modify the list stored into the variable named 'varNamePtr'
5495  * setting the element specified by the 'indexc' indexes objects in 'indexv',
5496  * with the new element 'newObjptr'. */
5497 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5498         Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5499 {
5500     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5501     int shared, i, index;
5502
5503     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5504     if (objPtr == NULL)
5505         return JIM_ERR;
5506     if ((shared = Jim_IsShared(objPtr)))
5507         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5508     for (i = 0; i < indexc-1; i++) {
5509         listObjPtr = objPtr;
5510         if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5511             goto err;
5512         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5513                     JIM_ERRMSG) != JIM_OK) {
5514             goto err;
5515         }
5516         if (Jim_IsShared(objPtr)) {
5517             objPtr = Jim_DuplicateObj(interp, objPtr);
5518             ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5519         }
5520         Jim_InvalidateStringRep(listObjPtr);
5521     }
5522     if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5523         goto err;
5524     if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5525         goto err;
5526     Jim_InvalidateStringRep(objPtr);
5527     Jim_InvalidateStringRep(varObjPtr);
5528     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5529         goto err;
5530     Jim_SetResult(interp, varObjPtr);
5531     return JIM_OK;
5532 err:
5533     if (shared) {
5534         Jim_FreeNewObj(interp, varObjPtr);
5535     }
5536     return JIM_ERR;
5537 }
5538
5539 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5540 {
5541     int i;
5542
5543     /* If all the objects in objv are lists without string rep.
5544      * it's possible to return a list as result, that's the
5545      * concatenation of all the lists. */
5546     for (i = 0; i < objc; i++) {
5547         if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5548             break;
5549     }
5550     if (i == objc) {
5551         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5552         for (i = 0; i < objc; i++)
5553             Jim_ListAppendList(interp, objPtr, objv[i]);
5554         return objPtr;
5555     } else {
5556         /* Else... we have to glue strings together */
5557         int len = 0, objLen;
5558         char *bytes, *p;
5559
5560         /* Compute the length */
5561         for (i = 0; i < objc; i++) {
5562             Jim_GetString(objv[i], &objLen);
5563             len += objLen;
5564         }
5565         if (objc) len += objc-1;
5566         /* Create the string rep, and a stinrg object holding it. */
5567         p = bytes = Jim_Alloc(len+1);
5568         for (i = 0; i < objc; i++) {
5569             const char *s = Jim_GetString(objv[i], &objLen);
5570             while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5571             {
5572                 s++; objLen--; len--;
5573             }
5574             while (objLen && (s[objLen-1] == ' ' ||
5575                 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5576                 objLen--; len--;
5577             }
5578             memcpy(p, s, objLen);
5579             p += objLen;
5580             if (objLen && i+1 != objc) {
5581                 *p++ = ' ';
5582             } else if (i+1 != objc) {
5583                 /* Drop the space calcuated for this
5584                  * element that is instead null. */
5585                 len--;
5586             }
5587         }
5588         *p = '\0';
5589         return Jim_NewStringObjNoAlloc(interp, bytes, len);
5590     }
5591 }
5592
5593 /* Returns a list composed of the elements in the specified range.
5594  * first and start are directly accepted as Jim_Objects and
5595  * processed for the end?-index? case. */
5596 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5597 {
5598     int first, last;
5599     int len, rangeLen;
5600
5601     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5602         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5603         return NULL;
5604     Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5605     first = JimRelToAbsIndex(len, first);
5606     last = JimRelToAbsIndex(len, last);
5607     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5608     return Jim_NewListObj(interp,
5609             listObjPtr->internalRep.listValue.ele+first, rangeLen);
5610 }
5611
5612 /* -----------------------------------------------------------------------------
5613  * Dict object
5614  * ---------------------------------------------------------------------------*/
5615 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5616 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5617 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5618 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5619
5620 /* Dict HashTable Type.
5621  *
5622  * Keys and Values are Jim objects. */
5623
5624 unsigned int JimObjectHTHashFunction(const void *key)
5625 {
5626     const char *str;
5627     Jim_Obj *objPtr = (Jim_Obj*) key;
5628     int len, h;
5629
5630     str = Jim_GetString(objPtr, &len);
5631     h = Jim_GenHashFunction((unsigned char*)str, len);
5632     return h;
5633 }
5634
5635 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5636 {
5637     JIM_NOTUSED(privdata);
5638
5639     return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5640 }
5641
5642 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5643 {
5644     Jim_Obj *objPtr = val;
5645
5646     Jim_DecrRefCount(interp, objPtr);
5647 }
5648
5649 static Jim_HashTableType JimDictHashTableType = {
5650     JimObjectHTHashFunction,            /* hash function */
5651     NULL,                               /* key dup */
5652     NULL,                               /* val dup */
5653     JimObjectHTKeyCompare,              /* key compare */
5654     (void(*)(void*, const void*))       /* ATTENTION: const cast */
5655         JimObjectHTKeyValDestructor,    /* key destructor */
5656     JimObjectHTKeyValDestructor         /* val destructor */
5657 };
5658
5659 /* Note that while the elements of the dict may contain references,
5660  * the list object itself can't. This basically means that the
5661  * dict object string representation as a whole can't contain references
5662  * that are not presents in the single elements. */
5663 static Jim_ObjType dictObjType = {
5664     "dict",
5665     FreeDictInternalRep,
5666     DupDictInternalRep,
5667     UpdateStringOfDict,
5668     JIM_TYPE_NONE,
5669 };
5670
5671 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5672 {
5673     JIM_NOTUSED(interp);
5674
5675     Jim_FreeHashTable(objPtr->internalRep.ptr);
5676     Jim_Free(objPtr->internalRep.ptr);
5677 }
5678
5679 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5680 {
5681     Jim_HashTable *ht, *dupHt;
5682     Jim_HashTableIterator *htiter;
5683     Jim_HashEntry *he;
5684
5685     /* Create a new hash table */
5686     ht = srcPtr->internalRep.ptr;
5687     dupHt = Jim_Alloc(sizeof(*dupHt));
5688     Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5689     if (ht->size != 0)
5690         Jim_ExpandHashTable(dupHt, ht->size);
5691     /* Copy every element from the source to the dup hash table */
5692     htiter = Jim_GetHashTableIterator(ht);
5693     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5694         const Jim_Obj *keyObjPtr = he->key;
5695         Jim_Obj *valObjPtr = he->val;
5696
5697         Jim_IncrRefCount((Jim_Obj*)keyObjPtr);  /* ATTENTION: const cast */
5698         Jim_IncrRefCount(valObjPtr);
5699         Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5700     }
5701     Jim_FreeHashTableIterator(htiter);
5702
5703     dupPtr->internalRep.ptr = dupHt;
5704     dupPtr->typePtr = &dictObjType;
5705 }
5706
5707 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5708 {
5709     int i, bufLen, realLength;
5710     const char *strRep;
5711     char *p;
5712     int *quotingType, objc;
5713     Jim_HashTable *ht;
5714     Jim_HashTableIterator *htiter;
5715     Jim_HashEntry *he;
5716     Jim_Obj **objv;
5717
5718     /* Trun the hash table into a flat vector of Jim_Objects. */
5719     ht = objPtr->internalRep.ptr;
5720     objc = ht->used*2;
5721     objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5722     htiter = Jim_GetHashTableIterator(ht);
5723     i = 0;
5724     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5725         objv[i++] = (Jim_Obj*)he->key;  /* ATTENTION: const cast */
5726         objv[i++] = he->val;
5727     }
5728     Jim_FreeHashTableIterator(htiter);
5729     /* (Over) Estimate the space needed. */
5730     quotingType = Jim_Alloc(sizeof(int)*objc);
5731     bufLen = 0;
5732     for (i = 0; i < objc; i++) {
5733         int len;
5734
5735         strRep = Jim_GetString(objv[i], &len);
5736         quotingType[i] = ListElementQuotingType(strRep, len);
5737         switch (quotingType[i]) {
5738         case JIM_ELESTR_SIMPLE: bufLen += len; break;
5739         case JIM_ELESTR_BRACE: bufLen += len+2; break;
5740         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5741         }
5742         bufLen++; /* elements separator. */
5743     }
5744     bufLen++;
5745
5746     /* Generate the string rep. */
5747     p = objPtr->bytes = Jim_Alloc(bufLen+1);
5748     realLength = 0;
5749     for (i = 0; i < objc; i++) {
5750         int len, qlen;
5751         const char *strRep = Jim_GetString(objv[i], &len);
5752         char *q;
5753
5754         switch(quotingType[i]) {
5755         case JIM_ELESTR_SIMPLE:
5756             memcpy(p, strRep, len);
5757             p += len;
5758             realLength += len;
5759             break;
5760         case JIM_ELESTR_BRACE:
5761             *p++ = '{';
5762             memcpy(p, strRep, len);
5763             p += len;
5764             *p++ = '}';
5765             realLength += len+2;
5766             break;
5767         case JIM_ELESTR_QUOTE:
5768             q = BackslashQuoteString(strRep, len, &qlen);
5769             memcpy(p, q, qlen);
5770             Jim_Free(q);
5771             p += qlen;
5772             realLength += qlen;
5773             break;
5774         }
5775         /* Add a separating space */
5776         if (i+1 != objc) {
5777             *p++ = ' ';
5778             realLength ++;
5779         }
5780     }
5781     *p = '\0'; /* nul term. */
5782     objPtr->length = realLength;
5783     Jim_Free(quotingType);
5784     Jim_Free(objv);
5785 }
5786
5787 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5788 {
5789     struct JimParserCtx parser;
5790     Jim_HashTable *ht;
5791     Jim_Obj *objv[2];
5792     const char *str;
5793     int i, strLen;
5794
5795     /* Get the string representation */
5796     str = Jim_GetString(objPtr, &strLen);
5797
5798     /* Free the old internal repr just now and initialize the
5799      * new one just now. The string->list conversion can't fail. */
5800     Jim_FreeIntRep(interp, objPtr);
5801     ht = Jim_Alloc(sizeof(*ht));
5802     Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5803     objPtr->typePtr = &dictObjType;
5804     objPtr->internalRep.ptr = ht;
5805
5806     /* Convert into a dict */
5807     JimParserInit(&parser, str, strLen, 1);
5808     i = 0;
5809     while(!JimParserEof(&parser)) {
5810         char *token;
5811         int tokenLen, type;
5812
5813         JimParseList(&parser);
5814         if (JimParserTtype(&parser) != JIM_TT_STR &&
5815             JimParserTtype(&parser) != JIM_TT_ESC)
5816             continue;
5817         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5818         objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5819         if (i == 2) {
5820             i = 0;
5821             Jim_IncrRefCount(objv[0]);
5822             Jim_IncrRefCount(objv[1]);
5823             if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5824                 Jim_HashEntry *he;
5825                 he = Jim_FindHashEntry(ht, objv[0]);
5826                 Jim_DecrRefCount(interp, objv[0]);
5827                 /* ATTENTION: const cast */
5828                 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5829                 he->val = objv[1];
5830             }
5831         }
5832     }
5833     if (i) {
5834         Jim_FreeNewObj(interp, objv[0]);
5835         objPtr->typePtr = NULL;
5836         Jim_FreeHashTable(ht);
5837         Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5838         return JIM_ERR;
5839     }
5840     return JIM_OK;
5841 }
5842
5843 /* Dict object API */
5844
5845 /* Add an element to a dict. objPtr must be of the "dict" type.
5846  * The higer-level exported function is Jim_DictAddElement().
5847  * If an element with the specified key already exists, the value
5848  * associated is replaced with the new one.
5849  *
5850  * if valueObjPtr == NULL, the key is instead removed if it exists. */
5851 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5852         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5853 {
5854     Jim_HashTable *ht = objPtr->internalRep.ptr;
5855
5856     if (valueObjPtr == NULL) { /* unset */
5857         Jim_DeleteHashEntry(ht, keyObjPtr);
5858         return;
5859     }
5860     Jim_IncrRefCount(keyObjPtr);
5861     Jim_IncrRefCount(valueObjPtr);
5862     if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5863         Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5864         Jim_DecrRefCount(interp, keyObjPtr);
5865         /* ATTENTION: const cast */
5866         Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5867         he->val = valueObjPtr;
5868     }
5869 }
5870
5871 /* Add an element, higher-level interface for DictAddElement().
5872  * If valueObjPtr == NULL, the key is removed if it exists. */
5873 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5874         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5875 {
5876     if (Jim_IsShared(objPtr))
5877         Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5878     if (objPtr->typePtr != &dictObjType) {
5879         if (SetDictFromAny(interp, objPtr) != JIM_OK)
5880             return JIM_ERR;
5881     }
5882     DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5883     Jim_InvalidateStringRep(objPtr);
5884     return JIM_OK;
5885 }
5886
5887 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5888 {
5889     Jim_Obj *objPtr;
5890     int i;
5891
5892     if (len % 2)
5893         Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5894
5895     objPtr = Jim_NewObj(interp);
5896     objPtr->typePtr = &dictObjType;
5897     objPtr->bytes = NULL;
5898     objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5899     Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5900     for (i = 0; i < len; i += 2)
5901         DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5902     return objPtr;
5903 }
5904
5905 /* Return the value associated to the specified dict key */
5906 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5907         Jim_Obj **objPtrPtr, int flags)
5908 {
5909     Jim_HashEntry *he;
5910     Jim_HashTable *ht;
5911
5912     if (dictPtr->typePtr != &dictObjType) {
5913         if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5914             return JIM_ERR;
5915     }
5916     ht = dictPtr->internalRep.ptr;
5917     if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5918         if (flags & JIM_ERRMSG) {
5919             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5920             Jim_AppendStrings(interp, Jim_GetResult(interp),
5921                     "key \"", Jim_GetString(keyPtr, NULL),
5922                     "\" not found in dictionary", NULL);
5923         }
5924         return JIM_ERR;
5925     }
5926     *objPtrPtr = he->val;
5927     return JIM_OK;
5928 }
5929
5930 /* Return the value associated to the specified dict keys */
5931 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5932         Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5933 {
5934     Jim_Obj *objPtr;
5935     int i;
5936
5937     if (keyc == 0) {
5938         *objPtrPtr = dictPtr;
5939         return JIM_OK;
5940     }
5941
5942     for (i = 0; i < keyc; i++) {
5943         if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5944                 != JIM_OK)
5945             return JIM_ERR;
5946         dictPtr = objPtr;
5947     }
5948     *objPtrPtr = objPtr;
5949     return JIM_OK;
5950 }
5951
5952 /* Modify the dict stored into the variable named 'varNamePtr'
5953  * setting the element specified by the 'keyc' keys objects in 'keyv',
5954  * with the new value of the element 'newObjPtr'.
5955  *
5956  * If newObjPtr == NULL the operation is to remove the given key
5957  * from the dictionary. */
5958 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5959         Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5960 {
5961     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5962     int shared, i;
5963
5964     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5965     if (objPtr == NULL) {
5966         if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5967             return JIM_ERR;
5968         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5969         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5970             Jim_FreeNewObj(interp, varObjPtr);
5971             return JIM_ERR;
5972         }
5973     }
5974     if ((shared = Jim_IsShared(objPtr)))
5975         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5976     for (i = 0; i < keyc-1; i++) {
5977         dictObjPtr = objPtr;
5978
5979         /* Check if it's a valid dictionary */
5980         if (dictObjPtr->typePtr != &dictObjType) {
5981             if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5982                 goto err;
5983         }
5984         /* Check if the given key exists. */
5985         Jim_InvalidateStringRep(dictObjPtr);
5986         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5987             newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5988         {
5989             /* This key exists at the current level.
5990              * Make sure it's not shared!. */
5991             if (Jim_IsShared(objPtr)) {
5992                 objPtr = Jim_DuplicateObj(interp, objPtr);
5993                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5994             }
5995         } else {
5996             /* Key not found. If it's an [unset] operation
5997              * this is an error. Only the last key may not
5998              * exist. */
5999             if (newObjPtr == NULL)
6000                 goto err;
6001             /* Otherwise set an empty dictionary
6002              * as key's value. */
6003             objPtr = Jim_NewDictObj(interp, NULL, 0);
6004             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
6005         }
6006     }
6007     if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
6008             != JIM_OK)
6009         goto err;
6010     Jim_InvalidateStringRep(objPtr);
6011     Jim_InvalidateStringRep(varObjPtr);
6012     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
6013         goto err;
6014     Jim_SetResult(interp, varObjPtr);
6015     return JIM_OK;
6016 err:
6017     if (shared) {
6018         Jim_FreeNewObj(interp, varObjPtr);
6019     }
6020     return JIM_ERR;
6021 }
6022
6023 /* -----------------------------------------------------------------------------
6024  * Index object
6025  * ---------------------------------------------------------------------------*/
6026 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
6027 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6028
6029 static Jim_ObjType indexObjType = {
6030     "index",
6031     NULL,
6032     NULL,
6033     UpdateStringOfIndex,
6034     JIM_TYPE_NONE,
6035 };
6036
6037 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
6038 {
6039     int len;
6040     char buf[JIM_INTEGER_SPACE+1];
6041
6042     if (objPtr->internalRep.indexValue >= 0)
6043         len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
6044     else if (objPtr->internalRep.indexValue == -1)
6045         len = sprintf(buf, "end");
6046     else {
6047         len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
6048     }
6049     objPtr->bytes = Jim_Alloc(len+1);
6050     memcpy(objPtr->bytes, buf, len+1);
6051     objPtr->length = len;
6052 }
6053
6054 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6055 {
6056     int index, end = 0;
6057     const char *str;
6058
6059     /* Get the string representation */
6060     str = Jim_GetString(objPtr, NULL);
6061     /* Try to convert into an index */
6062     if (!strcmp(str, "end")) {
6063         index = 0;
6064         end = 1;
6065     } else {
6066         if (!strncmp(str, "end-", 4)) {
6067             str += 4;
6068             end = 1;
6069         }
6070         if (Jim_StringToIndex(str, &index) != JIM_OK) {
6071             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6072             Jim_AppendStrings(interp, Jim_GetResult(interp),
6073                     "bad index \"", Jim_GetString(objPtr, NULL), "\": "
6074                     "must be integer or end?-integer?", NULL);
6075             return JIM_ERR;
6076         }
6077     }
6078     if (end) {
6079         if (index < 0)
6080             index = INT_MAX;
6081         else
6082             index = -(index+1);
6083     } else if (!end && index < 0)
6084         index = -INT_MAX;
6085     /* Free the old internal repr and set the new one. */
6086     Jim_FreeIntRep(interp, objPtr);
6087     objPtr->typePtr = &indexObjType;
6088     objPtr->internalRep.indexValue = index;
6089     return JIM_OK;
6090 }
6091
6092 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
6093 {
6094     /* Avoid shimmering if the object is an integer. */
6095     if (objPtr->typePtr == &intObjType) {
6096         jim_wide val = objPtr->internalRep.wideValue;
6097         if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
6098             *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
6099             return JIM_OK;
6100         }
6101     }
6102     if (objPtr->typePtr != &indexObjType &&
6103         SetIndexFromAny(interp, objPtr) == JIM_ERR)
6104         return JIM_ERR;
6105     *indexPtr = objPtr->internalRep.indexValue;
6106     return JIM_OK;
6107 }
6108
6109 /* -----------------------------------------------------------------------------
6110  * Return Code Object.
6111  * ---------------------------------------------------------------------------*/
6112
6113 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6114
6115 static Jim_ObjType returnCodeObjType = {
6116     "return-code",
6117     NULL,
6118     NULL,
6119     NULL,
6120     JIM_TYPE_NONE,
6121 };
6122
6123 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6124 {
6125     const char *str;
6126     int strLen, returnCode;
6127     jim_wide wideValue;
6128
6129     /* Get the string representation */
6130     str = Jim_GetString(objPtr, &strLen);
6131     /* Try to convert into an integer */
6132     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
6133         returnCode = (int) wideValue;
6134     else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
6135         returnCode = JIM_OK;
6136     else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
6137         returnCode = JIM_ERR;
6138     else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
6139         returnCode = JIM_RETURN;
6140     else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
6141         returnCode = JIM_BREAK;
6142     else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
6143         returnCode = JIM_CONTINUE;
6144     else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
6145         returnCode = JIM_EVAL;
6146     else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
6147         returnCode = JIM_EXIT;
6148     else {
6149         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6150         Jim_AppendStrings(interp, Jim_GetResult(interp),
6151                 "expected return code but got '", str, "'",
6152                 NULL);
6153         return JIM_ERR;
6154     }
6155     /* Free the old internal repr and set the new one. */
6156     Jim_FreeIntRep(interp, objPtr);
6157     objPtr->typePtr = &returnCodeObjType;
6158     objPtr->internalRep.returnCode = returnCode;
6159     return JIM_OK;
6160 }
6161
6162 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
6163 {
6164     if (objPtr->typePtr != &returnCodeObjType &&
6165         SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
6166         return JIM_ERR;
6167     *intPtr = objPtr->internalRep.returnCode;
6168     return JIM_OK;
6169 }
6170
6171 /* -----------------------------------------------------------------------------
6172  * Expression Parsing
6173  * ---------------------------------------------------------------------------*/
6174 static int JimParseExprOperator(struct JimParserCtx *pc);
6175 static int JimParseExprNumber(struct JimParserCtx *pc);
6176 static int JimParseExprIrrational(struct JimParserCtx *pc);
6177
6178 /* Exrp's Stack machine operators opcodes. */
6179
6180 /* Binary operators (numbers) */
6181 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
6182 #define JIM_EXPROP_MUL 0
6183 #define JIM_EXPROP_DIV 1
6184 #define JIM_EXPROP_MOD 2
6185 #define JIM_EXPROP_SUB 3
6186 #define JIM_EXPROP_ADD 4
6187 #define JIM_EXPROP_LSHIFT 5
6188 #define JIM_EXPROP_RSHIFT 6
6189 #define JIM_EXPROP_ROTL 7
6190 #define JIM_EXPROP_ROTR 8
6191 #define JIM_EXPROP_LT 9
6192 #define JIM_EXPROP_GT 10
6193 #define JIM_EXPROP_LTE 11
6194 #define JIM_EXPROP_GTE 12
6195 #define JIM_EXPROP_NUMEQ 13
6196 #define JIM_EXPROP_NUMNE 14
6197 #define JIM_EXPROP_BITAND 15
6198 #define JIM_EXPROP_BITXOR 16
6199 #define JIM_EXPROP_BITOR 17
6200 #define JIM_EXPROP_LOGICAND 18
6201 #define JIM_EXPROP_LOGICOR 19
6202 #define JIM_EXPROP_LOGICAND_LEFT 20
6203 #define JIM_EXPROP_LOGICOR_LEFT 21
6204 #define JIM_EXPROP_POW 22
6205 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6206
6207 /* Binary operators (strings) */
6208 #define JIM_EXPROP_STREQ 23
6209 #define JIM_EXPROP_STRNE 24
6210
6211 /* Unary operators (numbers) */
6212 #define JIM_EXPROP_NOT 25
6213 #define JIM_EXPROP_BITNOT 26
6214 #define JIM_EXPROP_UNARYMINUS 27
6215 #define JIM_EXPROP_UNARYPLUS 28
6216 #define JIM_EXPROP_LOGICAND_RIGHT 29
6217 #define JIM_EXPROP_LOGICOR_RIGHT 30
6218
6219 /* Ternary operators */
6220 #define JIM_EXPROP_TERNARY 31
6221
6222 /* Operands */
6223 #define JIM_EXPROP_NUMBER 32
6224 #define JIM_EXPROP_COMMAND 33
6225 #define JIM_EXPROP_VARIABLE 34
6226 #define JIM_EXPROP_DICTSUGAR 35
6227 #define JIM_EXPROP_SUBST 36
6228 #define JIM_EXPROP_STRING 37
6229
6230 /* Operators table */
6231 typedef struct Jim_ExprOperator {
6232     const char *name;
6233     int precedence;
6234     int arity;
6235     int opcode;
6236 } Jim_ExprOperator;
6237
6238 /* name - precedence - arity - opcode */
6239 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6240     {"!", 300, 1, JIM_EXPROP_NOT},
6241     {"~", 300, 1, JIM_EXPROP_BITNOT},
6242     {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6243     {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6244
6245     {"**", 250, 2, JIM_EXPROP_POW},
6246
6247     {"*", 200, 2, JIM_EXPROP_MUL},
6248     {"/", 200, 2, JIM_EXPROP_DIV},
6249     {"%", 200, 2, JIM_EXPROP_MOD},
6250
6251     {"-", 100, 2, JIM_EXPROP_SUB},
6252     {"+", 100, 2, JIM_EXPROP_ADD},
6253
6254     {"<<<", 90, 3, JIM_EXPROP_ROTL},
6255     {">>>", 90, 3, JIM_EXPROP_ROTR},
6256     {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6257     {">>", 90, 2, JIM_EXPROP_RSHIFT},
6258
6259     {"<",  80, 2, JIM_EXPROP_LT},
6260     {">",  80, 2, JIM_EXPROP_GT},
6261     {"<=", 80, 2, JIM_EXPROP_LTE},
6262     {">=", 80, 2, JIM_EXPROP_GTE},
6263
6264     {"==", 70, 2, JIM_EXPROP_NUMEQ},
6265     {"!=", 70, 2, JIM_EXPROP_NUMNE},
6266
6267     {"eq", 60, 2, JIM_EXPROP_STREQ},
6268     {"ne", 60, 2, JIM_EXPROP_STRNE},
6269
6270     {"&", 50, 2, JIM_EXPROP_BITAND},
6271     {"^", 49, 2, JIM_EXPROP_BITXOR},
6272     {"|", 48, 2, JIM_EXPROP_BITOR},
6273
6274     {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6275     {"||", 10, 2, JIM_EXPROP_LOGICOR},
6276
6277     {"?", 5, 3, JIM_EXPROP_TERNARY},
6278     /* private operators */
6279     {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6280     {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6281     {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6282     {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6283 };
6284
6285 #define JIM_EXPR_OPERATORS_NUM \
6286     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6287
6288 int JimParseExpression(struct JimParserCtx *pc)
6289 {
6290     /* Discard spaces and quoted newline */
6291     while(*(pc->p) == ' ' ||
6292           *(pc->p) == '\t' ||
6293           *(pc->p) == '\r' ||
6294           *(pc->p) == '\n' ||
6295             (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6296         pc->p++; pc->len--;
6297     }
6298
6299     if (pc->len == 0) {
6300         pc->tstart = pc->tend = pc->p;
6301         pc->tline = pc->linenr;
6302         pc->tt = JIM_TT_EOL;
6303         pc->eof = 1;
6304         return JIM_OK;
6305     }
6306     switch(*(pc->p)) {
6307     case '(':
6308         pc->tstart = pc->tend = pc->p;
6309         pc->tline = pc->linenr;
6310         pc->tt = JIM_TT_SUBEXPR_START;
6311         pc->p++; pc->len--;
6312         break;
6313     case ')':
6314         pc->tstart = pc->tend = pc->p;
6315         pc->tline = pc->linenr;
6316         pc->tt = JIM_TT_SUBEXPR_END;
6317         pc->p++; pc->len--;
6318         break;
6319     case '[':
6320         return JimParseCmd(pc);
6321         break;
6322     case '$':
6323         if (JimParseVar(pc) == JIM_ERR)
6324             return JimParseExprOperator(pc);
6325         else
6326             return JIM_OK;
6327         break;
6328     case '-':
6329         if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6330             isdigit((int)*(pc->p+1)))
6331             return JimParseExprNumber(pc);
6332         else
6333             return JimParseExprOperator(pc);
6334         break;
6335     case '0': case '1': case '2': case '3': case '4':
6336     case '5': case '6': case '7': case '8': case '9': case '.':
6337         return JimParseExprNumber(pc);
6338         break;
6339     case '"':
6340     case '{':
6341         /* Here it's possible to reuse the List String parsing. */
6342         pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6343         return JimParseListStr(pc);
6344         break;
6345     case 'N': case 'I':
6346     case 'n': case 'i':
6347         if (JimParseExprIrrational(pc) == JIM_ERR)
6348             return JimParseExprOperator(pc);
6349         break;
6350     default:
6351         return JimParseExprOperator(pc);
6352         break;
6353     }
6354     return JIM_OK;
6355 }
6356
6357 int JimParseExprNumber(struct JimParserCtx *pc)
6358 {
6359     int allowdot = 1;
6360     int allowhex = 0;
6361
6362     pc->tstart = pc->p;
6363     pc->tline = pc->linenr;
6364     if (*pc->p == '-') {
6365         pc->p++; pc->len--;
6366     }
6367     while (  isdigit((int)*pc->p) 
6368           || (allowhex && isxdigit((int)*pc->p) )
6369           || (allowdot && *pc->p == '.') 
6370           || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6371               (*pc->p == 'x' || *pc->p == 'X'))
6372           )
6373     {
6374         if ((*pc->p == 'x') || (*pc->p == 'X')) {
6375             allowhex = 1;
6376             allowdot = 0;
6377                 }
6378         if (*pc->p == '.')
6379             allowdot = 0;
6380         pc->p++; pc->len--;
6381         if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6382             pc->p += 2; pc->len -= 2;
6383         }
6384     }
6385     pc->tend = pc->p-1;
6386     pc->tt = JIM_TT_EXPR_NUMBER;
6387     return JIM_OK;
6388 }
6389
6390 int JimParseExprIrrational(struct JimParserCtx *pc)
6391 {
6392     const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6393     const char **token;
6394     for (token = Tokens; *token != NULL; token++) {
6395         int len = strlen(*token);
6396         if (strncmp(*token, pc->p, len) == 0) {
6397             pc->tstart = pc->p;
6398             pc->tend = pc->p + len - 1;
6399             pc->p += len; pc->len -= len;
6400             pc->tline = pc->linenr;
6401             pc->tt = JIM_TT_EXPR_NUMBER;
6402             return JIM_OK;
6403         }
6404     }
6405     return JIM_ERR;
6406 }
6407
6408 int JimParseExprOperator(struct JimParserCtx *pc)
6409 {
6410     int i;
6411     int bestIdx = -1, bestLen = 0;
6412
6413     /* Try to get the longest match. */
6414     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6415         const char *opname;
6416         int oplen;
6417
6418         opname = Jim_ExprOperators[i].name;
6419         if (opname == NULL) continue;
6420         oplen = strlen(opname);
6421
6422         if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6423             bestIdx = i;
6424             bestLen = oplen;
6425         }
6426     }
6427     if (bestIdx == -1) return JIM_ERR;
6428     pc->tstart = pc->p;
6429     pc->tend = pc->p + bestLen - 1;
6430     pc->p += bestLen; pc->len -= bestLen;
6431     pc->tline = pc->linenr;
6432     pc->tt = JIM_TT_EXPR_OPERATOR;
6433     return JIM_OK;
6434 }
6435
6436 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6437 {
6438     int i;
6439     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6440         if (Jim_ExprOperators[i].name &&
6441             strcmp(opname, Jim_ExprOperators[i].name) == 0)
6442             return &Jim_ExprOperators[i];
6443     return NULL;
6444 }
6445
6446 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6447 {
6448     int i;
6449     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6450         if (Jim_ExprOperators[i].opcode == opcode)
6451             return &Jim_ExprOperators[i];
6452     return NULL;
6453 }
6454
6455 /* -----------------------------------------------------------------------------
6456  * Expression Object
6457  * ---------------------------------------------------------------------------*/
6458 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6459 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6460 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6461
6462 static Jim_ObjType exprObjType = {
6463     "expression",
6464     FreeExprInternalRep,
6465     DupExprInternalRep,
6466     NULL,
6467     JIM_TYPE_REFERENCES,
6468 };
6469
6470 /* Expr bytecode structure */
6471 typedef struct ExprByteCode {
6472     int *opcode;        /* Integer array of opcodes. */
6473     Jim_Obj **obj;      /* Array of associated Jim Objects. */
6474     int len;            /* Bytecode length */
6475     int inUse;          /* Used for sharing. */
6476 } ExprByteCode;
6477
6478 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6479 {
6480     int i;
6481     ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6482
6483     expr->inUse--;
6484     if (expr->inUse != 0) return;
6485     for (i = 0; i < expr->len; i++)
6486         Jim_DecrRefCount(interp, expr->obj[i]);
6487     Jim_Free(expr->opcode);
6488     Jim_Free(expr->obj);
6489     Jim_Free(expr);
6490 }
6491
6492 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6493 {
6494     JIM_NOTUSED(interp);
6495     JIM_NOTUSED(srcPtr);
6496
6497     /* Just returns an simple string. */
6498     dupPtr->typePtr = NULL;
6499 }
6500
6501 /* Add a new instruction to an expression bytecode structure. */
6502 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6503         int opcode, char *str, int len)
6504 {
6505     expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6506     expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6507     expr->opcode[expr->len] = opcode;
6508     expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6509     Jim_IncrRefCount(expr->obj[expr->len]);
6510     expr->len++;
6511 }
6512
6513 /* Check if an expr program looks correct. */
6514 static int ExprCheckCorrectness(ExprByteCode *expr)
6515 {
6516     int i;
6517     int stacklen = 0;
6518
6519     /* Try to check if there are stack underflows,
6520      * and make sure at the end of the program there is
6521      * a single result on the stack. */
6522     for (i = 0; i < expr->len; i++) {
6523         switch(expr->opcode[i]) {
6524         case JIM_EXPROP_NUMBER:
6525         case JIM_EXPROP_STRING:
6526         case JIM_EXPROP_SUBST:
6527         case JIM_EXPROP_VARIABLE:
6528         case JIM_EXPROP_DICTSUGAR:
6529         case JIM_EXPROP_COMMAND:
6530             stacklen++;
6531             break;
6532         case JIM_EXPROP_NOT:
6533         case JIM_EXPROP_BITNOT:
6534         case JIM_EXPROP_UNARYMINUS:
6535         case JIM_EXPROP_UNARYPLUS:
6536             /* Unary operations */
6537             if (stacklen < 1) return JIM_ERR;
6538             break;
6539         case JIM_EXPROP_ADD:
6540         case JIM_EXPROP_SUB:
6541         case JIM_EXPROP_MUL:
6542         case JIM_EXPROP_DIV:
6543         case JIM_EXPROP_MOD:
6544         case JIM_EXPROP_LT:
6545         case JIM_EXPROP_GT:
6546         case JIM_EXPROP_LTE:
6547         case JIM_EXPROP_GTE:
6548         case JIM_EXPROP_ROTL:
6549         case JIM_EXPROP_ROTR:
6550         case JIM_EXPROP_LSHIFT:
6551         case JIM_EXPROP_RSHIFT:
6552         case JIM_EXPROP_NUMEQ:
6553         case JIM_EXPROP_NUMNE:
6554         case JIM_EXPROP_STREQ:
6555         case JIM_EXPROP_STRNE:
6556         case JIM_EXPROP_BITAND:
6557         case JIM_EXPROP_BITXOR:
6558         case JIM_EXPROP_BITOR:
6559         case JIM_EXPROP_LOGICAND:
6560         case JIM_EXPROP_LOGICOR:
6561         case JIM_EXPROP_POW:
6562             /* binary operations */
6563             if (stacklen < 2) return JIM_ERR;
6564             stacklen--;
6565             break;
6566         default:
6567             Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6568             break;
6569         }
6570     }
6571     if (stacklen != 1) return JIM_ERR;
6572     return JIM_OK;
6573 }
6574
6575 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6576         ScriptObj *topLevelScript)
6577 {
6578     int i;
6579
6580     return;
6581     for (i = 0; i < expr->len; i++) {
6582         Jim_Obj *foundObjPtr;
6583
6584         if (expr->obj[i] == NULL) continue;
6585         foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6586                 NULL, expr->obj[i]);
6587         if (foundObjPtr != NULL) {
6588             Jim_IncrRefCount(foundObjPtr);
6589             Jim_DecrRefCount(interp, expr->obj[i]);
6590             expr->obj[i] = foundObjPtr;
6591         }
6592     }
6593 }
6594
6595 /* This procedure converts every occurrence of || and && opereators
6596  * in lazy unary versions.
6597  *
6598  * a b || is converted into:
6599  *
6600  * a <offset> |L b |R
6601  *
6602  * a b && is converted into:
6603  *
6604  * a <offset> &L b &R
6605  *
6606  * "|L" checks if 'a' is true:
6607  *   1) if it is true pushes 1 and skips <offset> istructions to reach
6608  *      the opcode just after |R.
6609  *   2) if it is false does nothing.
6610  * "|R" checks if 'b' is true:
6611  *   1) if it is true pushes 1, otherwise pushes 0.
6612  *
6613  * "&L" checks if 'a' is true:
6614  *   1) if it is true does nothing.
6615  *   2) If it is false pushes 0 and skips <offset> istructions to reach
6616  *      the opcode just after &R
6617  * "&R" checks if 'a' is true:
6618  *      if it is true pushes 1, otherwise pushes 0.
6619  */
6620 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6621 {
6622     while (1) {
6623         int index = -1, leftindex, arity, i, offset;
6624         Jim_ExprOperator *op;
6625
6626         /* Search for || or && */
6627         for (i = 0; i < expr->len; i++) {
6628             if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6629                 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6630                 index = i;
6631                 break;
6632             }
6633         }
6634         if (index == -1) return;
6635         /* Search for the end of the first operator */
6636         leftindex = index-1;
6637         arity = 1;
6638         while(arity) {
6639             switch(expr->opcode[leftindex]) {
6640             case JIM_EXPROP_NUMBER:
6641             case JIM_EXPROP_COMMAND:
6642             case JIM_EXPROP_VARIABLE:
6643             case JIM_EXPROP_DICTSUGAR:
6644             case JIM_EXPROP_SUBST:
6645             case JIM_EXPROP_STRING:
6646                 break;
6647             default:
6648                 op = JimExprOperatorInfoByOpcode(expr->opcode[leftindex]);
6649                 if (op == NULL) {
6650                     Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6651                 }
6652                 arity += op->arity;
6653                 break;
6654             }
6655             arity--;
6656             leftindex--;
6657         }
6658         leftindex++;
6659         expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6660         expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6661         memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6662                 sizeof(int)*(expr->len-leftindex));
6663         memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6664                 sizeof(Jim_Obj*)*(expr->len-leftindex));
6665         expr->len += 2;
6666         index += 2;
6667         offset = (index-leftindex)-1;
6668         Jim_DecrRefCount(interp, expr->obj[index]);
6669         if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6670             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6671             expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6672             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6673             expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6674         } else {
6675             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6676             expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6677             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6678             expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6679         }
6680         expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6681         expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6682         Jim_IncrRefCount(expr->obj[index]);
6683         Jim_IncrRefCount(expr->obj[leftindex]);
6684         Jim_IncrRefCount(expr->obj[leftindex+1]);
6685     }
6686 }
6687
6688 /* This method takes the string representation of an expression
6689  * and generates a program for the Expr's stack-based VM. */
6690 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6691 {
6692     int exprTextLen;
6693     const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6694     struct JimParserCtx parser;
6695     int i, shareLiterals;
6696     ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6697     Jim_Stack stack;
6698     Jim_ExprOperator *op;
6699
6700     /* Perform literal sharing with the current procedure
6701      * running only if this expression appears to be not generated
6702      * at runtime. */
6703     shareLiterals = objPtr->typePtr == &sourceObjType;
6704
6705     expr->opcode = NULL;
6706     expr->obj = NULL;
6707     expr->len = 0;
6708     expr->inUse = 1;
6709
6710     Jim_InitStack(&stack);
6711     JimParserInit(&parser, exprText, exprTextLen, 1);
6712     while(!JimParserEof(&parser)) {
6713         char *token;
6714         int len, type;
6715
6716         if (JimParseExpression(&parser) != JIM_OK) {
6717             Jim_SetResultString(interp, "Syntax error in expression", -1);
6718             goto err;
6719         }
6720         token = JimParserGetToken(&parser, &len, &type, NULL);
6721         if (type == JIM_TT_EOL) {
6722             Jim_Free(token);
6723             break;
6724         }
6725         switch(type) {
6726         case JIM_TT_STR:
6727             ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6728             break;
6729         case JIM_TT_ESC:
6730             ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6731             break;
6732         case JIM_TT_VAR:
6733             ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6734             break;
6735         case JIM_TT_DICTSUGAR:
6736             ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6737             break;
6738         case JIM_TT_CMD:
6739             ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6740             break;
6741         case JIM_TT_EXPR_NUMBER:
6742             ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6743             break;
6744         case JIM_TT_EXPR_OPERATOR:
6745             op = JimExprOperatorInfo(token);
6746             while(1) {
6747                 Jim_ExprOperator *stackTopOp;
6748
6749                 if (Jim_StackPeek(&stack) != NULL) {
6750                     stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6751                 } else {
6752                     stackTopOp = NULL;
6753                 }
6754                 if (Jim_StackLen(&stack) && op->arity != 1 &&
6755                     stackTopOp && stackTopOp->precedence >= op->precedence)
6756                 {
6757                     ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6758                         Jim_StackPeek(&stack), -1);
6759                     Jim_StackPop(&stack);
6760                 } else {
6761                     break;
6762                 }
6763             }
6764             Jim_StackPush(&stack, token);
6765             break;
6766         case JIM_TT_SUBEXPR_START:
6767             Jim_StackPush(&stack, Jim_StrDup("("));
6768             Jim_Free(token);
6769             break;
6770         case JIM_TT_SUBEXPR_END:
6771             {
6772                 int found = 0;
6773                 while(Jim_StackLen(&stack)) {
6774                     char *opstr = Jim_StackPop(&stack);
6775                     if (!strcmp(opstr, "(")) {
6776                         Jim_Free(opstr);
6777                         found = 1;
6778                         break;
6779                     }
6780                     op = JimExprOperatorInfo(opstr);
6781                     ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6782                 }
6783                 if (!found) {
6784                     Jim_SetResultString(interp,
6785                         "Unexpected close parenthesis", -1);
6786                     goto err;
6787                 }
6788             }
6789             Jim_Free(token);
6790             break;
6791         default:
6792             Jim_Panic(interp,"Default reached in SetExprFromAny()");
6793             break;
6794         }
6795     }
6796     while (Jim_StackLen(&stack)) {
6797         char *opstr = Jim_StackPop(&stack);
6798         op = JimExprOperatorInfo(opstr);
6799         if (op == NULL && !strcmp(opstr, "(")) {
6800             Jim_Free(opstr);
6801             Jim_SetResultString(interp, "Missing close parenthesis", -1);
6802             goto err;
6803         }
6804         ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6805     }
6806     /* Check program correctness. */
6807     if (ExprCheckCorrectness(expr) != JIM_OK) {
6808         Jim_SetResultString(interp, "Invalid expression", -1);
6809         goto err;
6810     }
6811
6812     /* Free the stack used for the compilation. */
6813     Jim_FreeStackElements(&stack, Jim_Free);
6814     Jim_FreeStack(&stack);
6815
6816     /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6817     ExprMakeLazy(interp, expr);
6818
6819     /* Perform literal sharing */
6820     if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6821         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6822         if (bodyObjPtr->typePtr == &scriptObjType) {
6823             ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6824             ExprShareLiterals(interp, expr, bodyScript);
6825         }
6826     }
6827
6828     /* Free the old internal rep and set the new one. */
6829     Jim_FreeIntRep(interp, objPtr);
6830     Jim_SetIntRepPtr(objPtr, expr);
6831     objPtr->typePtr = &exprObjType;
6832     return JIM_OK;
6833
6834 err:    /* we jump here on syntax/compile errors. */
6835     Jim_FreeStackElements(&stack, Jim_Free);
6836     Jim_FreeStack(&stack);
6837     Jim_Free(expr->opcode);
6838     for (i = 0; i < expr->len; i++) {
6839         Jim_DecrRefCount(interp,expr->obj[i]);
6840     }
6841     Jim_Free(expr->obj);
6842     Jim_Free(expr);
6843     return JIM_ERR;
6844 }
6845
6846 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6847 {
6848     if (objPtr->typePtr != &exprObjType) {
6849         if (SetExprFromAny(interp, objPtr) != JIM_OK)
6850             return NULL;
6851     }
6852     return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6853 }
6854
6855 /* -----------------------------------------------------------------------------
6856  * Expressions evaluation.
6857  * Jim uses a specialized stack-based virtual machine for expressions,
6858  * that takes advantage of the fact that expr's operators
6859  * can't be redefined.
6860  *
6861  * Jim_EvalExpression() uses the bytecode compiled by
6862  * SetExprFromAny() method of the "expression" object.
6863  *
6864  * On success a Tcl Object containing the result of the evaluation
6865  * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6866  * returned.
6867  * On error the function returns a retcode != to JIM_OK and set a suitable
6868  * error on the interp.
6869  * ---------------------------------------------------------------------------*/
6870 #define JIM_EE_STATICSTACK_LEN 10
6871
6872 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6873         Jim_Obj **exprResultPtrPtr)
6874 {
6875     ExprByteCode *expr;
6876     Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6877     int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6878
6879     Jim_IncrRefCount(exprObjPtr);
6880     expr = Jim_GetExpression(interp, exprObjPtr);
6881     if (!expr) {
6882         Jim_DecrRefCount(interp, exprObjPtr);
6883         return JIM_ERR; /* error in expression. */
6884     }
6885     /* In order to avoid that the internal repr gets freed due to
6886      * shimmering of the exprObjPtr's object, we make the internal rep
6887      * shared. */
6888     expr->inUse++;
6889
6890     /* The stack-based expr VM itself */
6891
6892     /* Stack allocation. Expr programs have the feature that
6893      * a program of length N can't require a stack longer than
6894      * N. */
6895     if (expr->len > JIM_EE_STATICSTACK_LEN)
6896         stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6897     else
6898         stack = staticStack;
6899
6900     /* Execute every istruction */
6901     for (i = 0; i < expr->len; i++) {
6902         Jim_Obj *A, *B, *objPtr;
6903         jim_wide wA, wB, wC;
6904         double dA, dB, dC;
6905         const char *sA, *sB;
6906         int Alen, Blen, retcode;
6907         int opcode = expr->opcode[i];
6908
6909         if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6910             stack[stacklen++] = expr->obj[i];
6911             Jim_IncrRefCount(expr->obj[i]);
6912         } else if (opcode == JIM_EXPROP_VARIABLE) {
6913             objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6914             if (objPtr == NULL) {
6915                 error = 1;
6916                 goto err;
6917             }
6918             stack[stacklen++] = objPtr;
6919             Jim_IncrRefCount(objPtr);
6920         } else if (opcode == JIM_EXPROP_SUBST) {
6921             if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6922                         &objPtr, JIM_NONE)) != JIM_OK)
6923             {
6924                 error = 1;
6925                 errRetCode = retcode;
6926                 goto err;
6927             }
6928             stack[stacklen++] = objPtr;
6929             Jim_IncrRefCount(objPtr);
6930         } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6931             objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6932             if (objPtr == NULL) {
6933                 error = 1;
6934                 goto err;
6935             }
6936             stack[stacklen++] = objPtr;
6937             Jim_IncrRefCount(objPtr);
6938         } else if (opcode == JIM_EXPROP_COMMAND) {
6939             if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6940                 error = 1;
6941                 errRetCode = retcode;
6942                 goto err;
6943             }
6944             stack[stacklen++] = interp->result;
6945             Jim_IncrRefCount(interp->result);
6946         } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6947                    opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6948         {
6949             /* Note that there isn't to increment the
6950              * refcount of objects. the references are moved
6951              * from stack to A and B. */
6952             B = stack[--stacklen];
6953             A = stack[--stacklen];
6954
6955             /* --- Integer --- */
6956             if ((A->typePtr == &doubleObjType && !A->bytes) ||
6957                 (B->typePtr == &doubleObjType && !B->bytes) ||
6958                 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6959                 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6960                 goto trydouble;
6961             }
6962             Jim_DecrRefCount(interp, A);
6963             Jim_DecrRefCount(interp, B);
6964             switch(expr->opcode[i]) {
6965             case JIM_EXPROP_ADD: wC = wA+wB; break;
6966             case JIM_EXPROP_SUB: wC = wA-wB; break;
6967             case JIM_EXPROP_MUL: wC = wA*wB; break;
6968             case JIM_EXPROP_LT: wC = wA<wB; break;
6969             case JIM_EXPROP_GT: wC = wA>wB; break;
6970             case JIM_EXPROP_LTE: wC = wA<=wB; break;
6971             case JIM_EXPROP_GTE: wC = wA>=wB; break;
6972             case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6973             case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6974             case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6975             case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6976             case JIM_EXPROP_BITAND: wC = wA&wB; break;
6977             case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6978             case JIM_EXPROP_BITOR: wC = wA|wB; break;
6979             case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6980             case JIM_EXPROP_LOGICAND_LEFT:
6981                 if (wA == 0) {
6982                     i += (int)wB;
6983                     wC = 0;
6984                 } else {
6985                     continue;
6986                 }
6987                 break;
6988             case JIM_EXPROP_LOGICOR_LEFT:
6989                 if (wA != 0) {
6990                     i += (int)wB;
6991                     wC = 1;
6992                 } else {
6993                     continue;
6994                 }
6995                 break;
6996             case JIM_EXPROP_DIV:
6997                 if (wB == 0) goto divbyzero;
6998                 wC = wA/wB;
6999                 break;
7000             case JIM_EXPROP_MOD:
7001                 if (wB == 0) goto divbyzero;
7002                 wC = wA%wB;
7003                 break;
7004             case JIM_EXPROP_ROTL: {
7005                 /* uint32_t would be better. But not everyone has inttypes.h?*/
7006                 unsigned long uA = (unsigned long)wA;
7007 #ifdef _MSC_VER
7008                 wC = _rotl(uA,(unsigned long)wB);
7009 #else
7010                 const unsigned int S = sizeof(unsigned long) * 8;
7011                 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
7012 #endif
7013                 break;
7014             }
7015             case JIM_EXPROP_ROTR: {
7016                 unsigned long uA = (unsigned long)wA;
7017 #ifdef _MSC_VER
7018                 wC = _rotr(uA,(unsigned long)wB);
7019 #else
7020                 const unsigned int S = sizeof(unsigned long) * 8;
7021                 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
7022 #endif
7023                 break;
7024             }
7025
7026             default:
7027                 wC = 0; /* avoid gcc warning */
7028                 break;
7029             }
7030             stack[stacklen] = Jim_NewIntObj(interp, wC);
7031             Jim_IncrRefCount(stack[stacklen]);
7032             stacklen++;
7033             continue;
7034 trydouble:
7035             /* --- Double --- */
7036             if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
7037                 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
7038
7039                 /* Hmmm! For compatibility, maybe convert != and == into ne and eq */
7040                 if (expr->opcode[i] == JIM_EXPROP_NUMNE) {
7041                     opcode = JIM_EXPROP_STRNE;
7042                     goto retry_as_string;
7043                 }
7044                 else if (expr->opcode[i] == JIM_EXPROP_NUMEQ) {
7045                     opcode = JIM_EXPROP_STREQ;
7046                     goto retry_as_string;
7047                 }
7048                 Jim_DecrRefCount(interp, A);
7049                 Jim_DecrRefCount(interp, B);
7050                 error = 1;
7051                 goto err;
7052             }
7053             Jim_DecrRefCount(interp, A);
7054             Jim_DecrRefCount(interp, B);
7055             switch(expr->opcode[i]) {
7056             case JIM_EXPROP_ROTL:
7057             case JIM_EXPROP_ROTR:
7058             case JIM_EXPROP_LSHIFT:
7059             case JIM_EXPROP_RSHIFT:
7060             case JIM_EXPROP_BITAND:
7061             case JIM_EXPROP_BITXOR:
7062             case JIM_EXPROP_BITOR:
7063             case JIM_EXPROP_MOD:
7064             case JIM_EXPROP_POW:
7065                 Jim_SetResultString(interp,
7066                     "Got floating-point value where integer was expected", -1);
7067                 error = 1;
7068                 goto err;
7069                 break;
7070             case JIM_EXPROP_ADD: dC = dA+dB; break;
7071             case JIM_EXPROP_SUB: dC = dA-dB; break;
7072             case JIM_EXPROP_MUL: dC = dA*dB; break;
7073             case JIM_EXPROP_LT: dC = dA<dB; break;
7074             case JIM_EXPROP_GT: dC = dA>dB; break;
7075             case JIM_EXPROP_LTE: dC = dA<=dB; break;
7076             case JIM_EXPROP_GTE: dC = dA>=dB; break;
7077             case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
7078             case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
7079             case JIM_EXPROP_LOGICAND_LEFT:
7080                 if (dA == 0) {
7081                     i += (int)dB;
7082                     dC = 0;
7083                 } else {
7084                     continue;
7085                 }
7086                 break;
7087             case JIM_EXPROP_LOGICOR_LEFT:
7088                 if (dA != 0) {
7089                     i += (int)dB;
7090                     dC = 1;
7091                 } else {
7092                     continue;
7093                 }
7094                 break;
7095             case JIM_EXPROP_DIV:
7096                 if (dB == 0) goto divbyzero;
7097                 dC = dA/dB;
7098                 break;
7099             default:
7100                 dC = 0; /* avoid gcc warning */
7101                 break;
7102             }
7103             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7104             Jim_IncrRefCount(stack[stacklen]);
7105             stacklen++;
7106         } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
7107             B = stack[--stacklen];
7108             A = stack[--stacklen];
7109 retry_as_string:
7110             sA = Jim_GetString(A, &Alen);
7111             sB = Jim_GetString(B, &Blen);
7112             switch(opcode) {
7113             case JIM_EXPROP_STREQ:
7114                 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
7115                     wC = 1;
7116                 else
7117                     wC = 0;
7118                 break;
7119             case JIM_EXPROP_STRNE:
7120                 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
7121                     wC = 1;
7122                 else
7123                     wC = 0;
7124                 break;
7125             default:
7126                 wC = 0; /* avoid gcc warning */
7127                 break;
7128             }
7129             Jim_DecrRefCount(interp, A);
7130             Jim_DecrRefCount(interp, B);
7131             stack[stacklen] = Jim_NewIntObj(interp, wC);
7132             Jim_IncrRefCount(stack[stacklen]);
7133             stacklen++;
7134         } else if (opcode == JIM_EXPROP_NOT ||
7135                    opcode == JIM_EXPROP_BITNOT ||
7136                    opcode == JIM_EXPROP_LOGICAND_RIGHT ||
7137                    opcode == JIM_EXPROP_LOGICOR_RIGHT) {
7138             /* Note that there isn't to increment the
7139              * refcount of objects. the references are moved
7140              * from stack to A and B. */
7141             A = stack[--stacklen];
7142
7143             /* --- Integer --- */
7144             if ((A->typePtr == &doubleObjType && !A->bytes) ||
7145                 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
7146                 goto trydouble_unary;
7147             }
7148             Jim_DecrRefCount(interp, A);
7149             switch(expr->opcode[i]) {
7150             case JIM_EXPROP_NOT: wC = !wA; break;
7151             case JIM_EXPROP_BITNOT: wC = ~wA; break;
7152             case JIM_EXPROP_LOGICAND_RIGHT:
7153             case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
7154             default:
7155                 wC = 0; /* avoid gcc warning */
7156                 break;
7157             }
7158             stack[stacklen] = Jim_NewIntObj(interp, wC);
7159             Jim_IncrRefCount(stack[stacklen]);
7160             stacklen++;
7161             continue;
7162 trydouble_unary:
7163             /* --- Double --- */
7164             if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
7165                 Jim_DecrRefCount(interp, A);
7166                 error = 1;
7167                 goto err;
7168             }
7169             Jim_DecrRefCount(interp, A);
7170             switch(expr->opcode[i]) {
7171             case JIM_EXPROP_NOT: dC = !dA; break;
7172             case JIM_EXPROP_LOGICAND_RIGHT:
7173             case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
7174             case JIM_EXPROP_BITNOT:
7175                 Jim_SetResultString(interp,
7176                     "Got floating-point value where integer was expected", -1);
7177                 error = 1;
7178                 goto err;
7179                 break;
7180             default:
7181                 dC = 0; /* avoid gcc warning */
7182                 break;
7183             }
7184             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7185             Jim_IncrRefCount(stack[stacklen]);
7186             stacklen++;
7187         } else {
7188             Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
7189         }
7190     }
7191 err:
7192     /* There is no need to decerement the inUse field because
7193      * this reference is transfered back into the exprObjPtr. */
7194     Jim_FreeIntRep(interp, exprObjPtr);
7195     exprObjPtr->typePtr = &exprObjType;
7196     Jim_SetIntRepPtr(exprObjPtr, expr);
7197     Jim_DecrRefCount(interp, exprObjPtr);
7198     if (!error) {
7199         *exprResultPtrPtr = stack[0];
7200         Jim_IncrRefCount(stack[0]);
7201         errRetCode = JIM_OK;
7202     }
7203     for (i = 0; i < stacklen; i++) {
7204         Jim_DecrRefCount(interp, stack[i]);
7205     }
7206     if (stack != staticStack)
7207         Jim_Free(stack);
7208     return errRetCode;
7209 divbyzero:
7210     error = 1;
7211     Jim_SetResultString(interp, "Division by zero", -1);
7212     goto err;
7213 }
7214
7215 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7216 {
7217     int retcode;
7218     jim_wide wideValue;
7219     double doubleValue;
7220     Jim_Obj *exprResultPtr;
7221
7222     retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7223     if (retcode != JIM_OK)
7224         return retcode;
7225     if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7226         if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7227         {
7228             Jim_DecrRefCount(interp, exprResultPtr);
7229             return JIM_ERR;
7230         } else {
7231             Jim_DecrRefCount(interp, exprResultPtr);
7232             *boolPtr = doubleValue != 0;
7233             return JIM_OK;
7234         }
7235     }
7236     Jim_DecrRefCount(interp, exprResultPtr);
7237     *boolPtr = wideValue != 0;
7238     return JIM_OK;
7239 }
7240
7241 /* -----------------------------------------------------------------------------
7242  * ScanFormat String Object
7243  * ---------------------------------------------------------------------------*/
7244
7245 /* This Jim_Obj will held a parsed representation of a format string passed to
7246  * the Jim_ScanString command. For error diagnostics, the scanformat string has
7247  * to be parsed in its entirely first and then, if correct, can be used for
7248  * scanning. To avoid endless re-parsing, the parsed representation will be
7249  * stored in an internal representation and re-used for performance reason. */
7250  
7251 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7252  * scanformat string. This part will later be used to extract information
7253  * out from the string to be parsed by Jim_ScanString */
7254  
7255 typedef struct ScanFmtPartDescr {
7256     char type;         /* Type of conversion (e.g. c, d, f) */
7257     char modifier;     /* Modify type (e.g. l - long, h - short */
7258     size_t  width;     /* Maximal width of input to be converted */
7259     int  pos;          /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */ 
7260     char *arg;         /* Specification of a CHARSET conversion */
7261     char *prefix;      /* Prefix to be scanned literally before conversion */
7262 } ScanFmtPartDescr;
7263
7264 /* The ScanFmtStringObj will held the internal representation of a scanformat
7265  * string parsed and separated in part descriptions. Furthermore it contains
7266  * the original string representation of the scanformat string to allow for
7267  * fast update of the Jim_Obj's string representation part.
7268  *
7269  * As add-on the internal object representation add some scratch pad area
7270  * for usage by Jim_ScanString to avoid endless allocating and freeing of
7271  * memory for purpose of string scanning.
7272  *
7273  * The error member points to a static allocated string in case of a mal-
7274  * formed scanformat string or it contains '0' (NULL) in case of a valid
7275  * parse representation.
7276  *
7277  * The whole memory of the internal representation is allocated as a single
7278  * area of memory that will be internally separated. So freeing and duplicating
7279  * of such an object is cheap */
7280
7281 typedef struct ScanFmtStringObj {
7282     jim_wide        size;         /* Size of internal repr in bytes */
7283     char            *stringRep;   /* Original string representation */
7284     size_t          count;        /* Number of ScanFmtPartDescr contained */
7285     size_t          convCount;    /* Number of conversions that will assign */
7286     size_t          maxPos;       /* Max position index if XPG3 is used */
7287     const char      *error;       /* Ptr to error text (NULL if no error */
7288     char            *scratch;     /* Some scratch pad used by Jim_ScanString */
7289     ScanFmtPartDescr descr[1];    /* The vector of partial descriptions */
7290 } ScanFmtStringObj;
7291
7292
7293 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7294 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7295 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7296
7297 static Jim_ObjType scanFmtStringObjType = {
7298     "scanformatstring",
7299     FreeScanFmtInternalRep,
7300     DupScanFmtInternalRep,
7301     UpdateStringOfScanFmt,
7302     JIM_TYPE_NONE,
7303 };
7304
7305 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7306 {
7307     JIM_NOTUSED(interp);
7308     Jim_Free((char*)objPtr->internalRep.ptr);
7309     objPtr->internalRep.ptr = 0;
7310 }
7311
7312 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7313 {
7314     size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7315     ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7316
7317     JIM_NOTUSED(interp);
7318     memcpy(newVec, srcPtr->internalRep.ptr, size);
7319     dupPtr->internalRep.ptr = newVec;
7320     dupPtr->typePtr = &scanFmtStringObjType;
7321 }
7322
7323 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7324 {
7325     char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7326
7327     objPtr->bytes = Jim_StrDup(bytes);
7328     objPtr->length = strlen(bytes);
7329 }
7330
7331 /* SetScanFmtFromAny will parse a given string and create the internal
7332  * representation of the format specification. In case of an error
7333  * the error data member of the internal representation will be set
7334  * to an descriptive error text and the function will be left with
7335  * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7336  * specification */
7337
7338 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7339 {
7340     ScanFmtStringObj *fmtObj;
7341     char *buffer;
7342     int maxCount, i, approxSize, lastPos = -1;
7343     const char *fmt = objPtr->bytes;
7344     int maxFmtLen = objPtr->length;
7345     const char *fmtEnd = fmt + maxFmtLen;
7346     int curr;
7347
7348     Jim_FreeIntRep(interp, objPtr);
7349     /* Count how many conversions could take place maximally */
7350     for (i=0, maxCount=0; i < maxFmtLen; ++i)
7351         if (fmt[i] == '%')
7352             ++maxCount;
7353     /* Calculate an approximation of the memory necessary */
7354     approxSize = sizeof(ScanFmtStringObj)           /* Size of the container */
7355         + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7356         + maxFmtLen * sizeof(char) + 3 + 1          /* Scratch + "%n" + '\0' */
7357         + maxFmtLen * sizeof(char) + 1              /* Original stringrep */
7358         + maxFmtLen * sizeof(char)                  /* Arg for CHARSETs */
7359         + (maxCount +1) * sizeof(char)              /* '\0' for every partial */
7360         + 1;                                        /* safety byte */
7361     fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7362     memset(fmtObj, 0, approxSize);
7363     fmtObj->size = approxSize;
7364     fmtObj->maxPos = 0;
7365     fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7366     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7367     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7368     buffer = fmtObj->stringRep + maxFmtLen + 1;
7369     objPtr->internalRep.ptr = fmtObj;
7370     objPtr->typePtr = &scanFmtStringObjType;
7371     for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7372         int width=0, skip;
7373         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7374         fmtObj->count++;
7375         descr->width = 0;                   /* Assume width unspecified */ 
7376         /* Overread and store any "literal" prefix */
7377         if (*fmt != '%' || fmt[1] == '%') {
7378             descr->type = 0;
7379             descr->prefix = &buffer[i];
7380             for (; fmt < fmtEnd; ++fmt) {
7381                 if (*fmt == '%') {
7382                     if (fmt[1] != '%') break;
7383                     ++fmt;
7384                 }
7385                 buffer[i++] = *fmt;
7386             }
7387             buffer[i++] = 0;
7388         } 
7389         /* Skip the conversion introducing '%' sign */
7390         ++fmt;      
7391         /* End reached due to non-conversion literal only? */
7392         if (fmt >= fmtEnd)
7393             goto done;
7394         descr->pos = 0;                     /* Assume "natural" positioning */
7395         if (*fmt == '*') {
7396             descr->pos = -1;       /* Okay, conversion will not be assigned */
7397             ++fmt;
7398         } else
7399             fmtObj->convCount++;    /* Otherwise count as assign-conversion */
7400         /* Check if next token is a number (could be width or pos */
7401         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7402             fmt += skip;
7403             /* Was the number a XPG3 position specifier? */
7404             if (descr->pos != -1 && *fmt == '$') {
7405                 int prev;
7406                 ++fmt;
7407                 descr->pos = width;
7408                 width = 0;
7409                 /* Look if "natural" postioning and XPG3 one was mixed */
7410                 if ((lastPos == 0 && descr->pos > 0)
7411                         || (lastPos > 0 && descr->pos == 0)) {
7412                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7413                     return JIM_ERR;
7414                 }
7415                 /* Look if this position was already used */
7416                 for (prev=0; prev < curr; ++prev) {
7417                     if (fmtObj->descr[prev].pos == -1) continue;
7418                     if (fmtObj->descr[prev].pos == descr->pos) {
7419                         fmtObj->error = "same \"%n$\" conversion specifier "
7420                             "used more than once";
7421                         return JIM_ERR;
7422                     }
7423                 }
7424                 /* Try to find a width after the XPG3 specifier */
7425                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7426                     descr->width = width;
7427                     fmt += skip;
7428                 }
7429                 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7430                     fmtObj->maxPos = descr->pos;
7431             } else {
7432                 /* Number was not a XPG3, so it has to be a width */
7433                 descr->width = width;
7434             }
7435         }
7436         /* If positioning mode was undetermined yet, fix this */
7437         if (lastPos == -1)
7438             lastPos = descr->pos;
7439         /* Handle CHARSET conversion type ... */
7440         if (*fmt == '[') {
7441             int swapped = 1, beg = i, end, j;
7442             descr->type = '[';
7443             descr->arg = &buffer[i];
7444             ++fmt;
7445             if (*fmt == '^') buffer[i++] = *fmt++;
7446             if (*fmt == ']') buffer[i++] = *fmt++;
7447             while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7448             if (*fmt != ']') {
7449                 fmtObj->error = "unmatched [ in format string";
7450                 return JIM_ERR;
7451             } 
7452             end = i;
7453             buffer[i++] = 0;
7454             /* In case a range fence was given "backwards", swap it */
7455             while (swapped) {
7456                 swapped = 0;
7457                 for (j=beg+1; j < end-1; ++j) {
7458                     if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7459                         char tmp = buffer[j-1];
7460                         buffer[j-1] = buffer[j+1];
7461                         buffer[j+1] = tmp;
7462                         swapped = 1;
7463                     }
7464                 }
7465             }
7466         } else {
7467             /* Remember any valid modifier if given */
7468             if (strchr("hlL", *fmt) != 0)
7469                 descr->modifier = tolower((int)*fmt++);
7470             
7471             descr->type = *fmt;
7472             if (strchr("efgcsndoxui", *fmt) == 0) {
7473                 fmtObj->error = "bad scan conversion character";
7474                 return JIM_ERR;
7475             } else if (*fmt == 'c' && descr->width != 0) {
7476                 fmtObj->error = "field width may not be specified in %c "
7477                     "conversion";
7478                 return JIM_ERR;
7479             } else if (*fmt == 'u' && descr->modifier == 'l') {
7480                 fmtObj->error = "unsigned wide not supported";
7481                 return JIM_ERR;
7482             }
7483         }
7484         curr++;
7485     }
7486 done:
7487     if (fmtObj->convCount == 0) {
7488         fmtObj->error = "no any conversion specifier given";
7489         return JIM_ERR;
7490     }
7491     return JIM_OK;
7492 }
7493
7494 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7495
7496 #define FormatGetCnvCount(_fo_) \
7497     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7498 #define FormatGetMaxPos(_fo_) \
7499     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7500 #define FormatGetError(_fo_) \
7501     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7502
7503 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7504  * charsets ([a-z123]) within scanning. Later on perhaps a base for a 
7505  * bitvector implementation in Jim? */ 
7506
7507 static int JimTestBit(const char *bitvec, char ch)
7508 {
7509     div_t pos = div(ch-1, 8);
7510     return bitvec[pos.quot] & (1 << pos.rem);
7511 }
7512
7513 static void JimSetBit(char *bitvec, char ch)
7514 {
7515     div_t pos = div(ch-1, 8);
7516     bitvec[pos.quot] |= (1 << pos.rem);
7517 }
7518
7519 #if 0 /* currently not used */
7520 static void JimClearBit(char *bitvec, char ch)
7521 {
7522     div_t pos = div(ch-1, 8);
7523     bitvec[pos.quot] &= ~(1 << pos.rem);
7524 }
7525 #endif
7526
7527 /* JimScanAString is used to scan an unspecified string that ends with
7528  * next WS, or a string that is specified via a charset. The charset
7529  * is currently implemented in a way to only allow for usage with
7530  * ASCII. Whenever we will switch to UNICODE, another idea has to
7531  * be born :-/
7532  *
7533  * FIXME: Works only with ASCII */
7534
7535 static Jim_Obj *
7536 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7537 {
7538     size_t i;
7539     Jim_Obj *result;
7540     char charset[256/8+1];  /* A Charset may contain max 256 chars */
7541     char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7542
7543     /* First init charset to nothing or all, depending if a specified
7544      * or an unspecified string has to be parsed */
7545     memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7546     if (sdescr) {
7547         /* There was a set description given, that means we are parsing
7548          * a specified string. So we have to build a corresponding 
7549          * charset reflecting the description */
7550         int notFlag = 0;
7551         /* Should the set be negated at the end? */
7552         if (*sdescr == '^') {
7553             notFlag = 1;
7554             ++sdescr;
7555         }
7556         /* Here '-' is meant literally and not to define a range */
7557         if (*sdescr == '-') {
7558             JimSetBit(charset, '-');
7559             ++sdescr;
7560         }
7561         while (*sdescr) {
7562             if (sdescr[1] == '-' && sdescr[2] != 0) {
7563                 /* Handle range definitions */
7564                 int i;
7565                 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7566                     JimSetBit(charset, (char)i);
7567                 sdescr += 3;
7568             } else {
7569                 /* Handle verbatim character definitions */
7570                 JimSetBit(charset, *sdescr++);
7571             }
7572         }
7573         /* Negate the charset if there was a NOT given */
7574         for (i=0; notFlag && i < sizeof(charset); ++i)
7575             charset[i] = ~charset[i];
7576     } 
7577     /* And after all the mess above, the real work begin ... */
7578     while (str && *str) {
7579         if (!sdescr && isspace((int)*str))
7580             break; /* EOS via WS if unspecified */
7581         if (JimTestBit(charset, *str)) *buffer++ = *str++;
7582         else break;             /* EOS via mismatch if specified scanning */
7583     }
7584     *buffer = 0;                /* Close the string properly ... */
7585     result = Jim_NewStringObj(interp, anchor, -1);
7586     Jim_Free(anchor);           /* ... and free it afer usage */
7587     return result;
7588 }
7589
7590 /* ScanOneEntry will scan one entry out of the string passed as argument.
7591  * It use the sscanf() function for this task. After extracting and
7592  * converting of the value, the count of scanned characters will be
7593  * returned of -1 in case of no conversion tool place and string was
7594  * already scanned thru */
7595
7596 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7597         ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7598 {
7599 #   define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7600         ? sizeof(jim_wide)                             \
7601         : sizeof(double))
7602     char buffer[MAX_SIZE];
7603     char *value = buffer;
7604     const char *tok;
7605     const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7606     size_t sLen = strlen(&str[pos]), scanned = 0;
7607     size_t anchor = pos;
7608     int i;
7609
7610     /* First pessimiticly assume, we will not scan anything :-) */
7611     *valObjPtr = 0;
7612     if (descr->prefix) {
7613         /* There was a prefix given before the conversion, skip it and adjust
7614          * the string-to-be-parsed accordingly */
7615         for (i=0; str[pos] && descr->prefix[i]; ++i) {
7616             /* If prefix require, skip WS */
7617             if (isspace((int)descr->prefix[i]))
7618                 while (str[pos] && isspace((int)str[pos])) ++pos;
7619             else if (descr->prefix[i] != str[pos]) 
7620                 break;  /* Prefix do not match here, leave the loop */
7621             else
7622                 ++pos;  /* Prefix matched so far, next round */
7623         }
7624         if (str[pos] == 0)
7625             return -1;  /* All of str consumed: EOF condition */
7626         else if (descr->prefix[i] != 0)
7627             return 0;   /* Not whole prefix consumed, no conversion possible */
7628     }
7629     /* For all but following conversion, skip leading WS */
7630     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7631         while (isspace((int)str[pos])) ++pos;
7632     /* Determine how much skipped/scanned so far */
7633     scanned = pos - anchor;
7634     if (descr->type == 'n') {
7635         /* Return pseudo conversion means: how much scanned so far? */
7636         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7637     } else if (str[pos] == 0) {
7638         /* Cannot scan anything, as str is totally consumed */
7639         return -1;
7640     } else {
7641         /* Processing of conversions follows ... */
7642         if (descr->width > 0) {
7643             /* Do not try to scan as fas as possible but only the given width.
7644              * To ensure this, we copy the part that should be scanned. */
7645             size_t tLen = descr->width > sLen ? sLen : descr->width;
7646             tok = Jim_StrDupLen(&str[pos], tLen);
7647         } else {
7648             /* As no width was given, simply refer to the original string */
7649             tok = &str[pos];
7650         }
7651         switch (descr->type) {
7652             case 'c':
7653                 *valObjPtr = Jim_NewIntObj(interp, *tok);
7654                 scanned += 1;
7655                 break;
7656             case 'd': case 'o': case 'x': case 'u': case 'i': {
7657                 jim_wide jwvalue;
7658                 long lvalue;
7659                 char *endp;  /* Position where the number finished */
7660                 int base = descr->type == 'o' ? 8
7661                     : descr->type == 'x' ? 16
7662                     : descr->type == 'i' ? 0
7663                     : 10;
7664                     
7665                 do {
7666                     /* Try to scan a number with the given base */
7667                     if (descr->modifier == 'l')
7668                     {
7669 #ifdef HAVE_LONG_LONG_INT
7670                         jwvalue = JimStrtoll(tok, &endp, base),
7671 #else
7672                         jwvalue = strtol(tok, &endp, base),
7673 #endif
7674                         memcpy(value, &jwvalue, sizeof(jim_wide));
7675                     }
7676                     else
7677                     {
7678                       if (descr->type == 'u')
7679                         lvalue = strtoul(tok, &endp, base);
7680                       else
7681                         lvalue = strtol(tok, &endp, base);
7682                       memcpy(value, &lvalue, sizeof(lvalue));
7683                     }
7684                     /* If scanning failed, and base was undetermined, simply
7685                      * put it to 10 and try once more. This should catch the
7686                      * case where %i begin to parse a number prefix (e.g. 
7687                      * '0x' but no further digits follows. This will be
7688                      * handled as a ZERO followed by a char 'x' by Tcl */
7689                     if (endp == tok && base == 0) base = 10;
7690                     else break;
7691                 } while (1);
7692                 if (endp != tok) {
7693                     /* There was some number sucessfully scanned! */
7694                     if (descr->modifier == 'l')
7695                         *valObjPtr = Jim_NewIntObj(interp, jwvalue);
7696                     else
7697                         *valObjPtr = Jim_NewIntObj(interp, lvalue);
7698                     /* Adjust the number-of-chars scanned so far */
7699                     scanned += endp - tok;
7700                 } else {
7701                     /* Nothing was scanned. We have to determine if this
7702                      * happened due to e.g. prefix mismatch or input str
7703                      * exhausted */
7704                     scanned = *tok ? 0 : -1;
7705                 }
7706                 break;
7707             }
7708             case 's': case '[': {
7709                 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7710                 scanned += Jim_Length(*valObjPtr);
7711                 break;
7712             }
7713             case 'e': case 'f': case 'g': {
7714                 char *endp;
7715
7716                 double dvalue = strtod(tok, &endp);
7717                 memcpy(value, &dvalue, sizeof(double));
7718                 if (endp != tok) {
7719                     /* There was some number sucessfully scanned! */
7720                     *valObjPtr = Jim_NewDoubleObj(interp, dvalue);
7721                     /* Adjust the number-of-chars scanned so far */
7722                     scanned += endp - tok;
7723                 } else {
7724                     /* Nothing was scanned. We have to determine if this
7725                      * happened due to e.g. prefix mismatch or input str
7726                      * exhausted */
7727                     scanned = *tok ? 0 : -1;
7728                 }
7729                 break;
7730             }
7731         }
7732         /* If a substring was allocated (due to pre-defined width) do not
7733          * forget to free it */
7734         if (tok != &str[pos])
7735             Jim_Free((char*)tok);
7736     }
7737     return scanned;
7738 }
7739
7740 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7741  * string and returns all converted (and not ignored) values in a list back
7742  * to the caller. If an error occured, a NULL pointer will be returned */
7743
7744 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7745         Jim_Obj *fmtObjPtr, int flags)
7746 {
7747     size_t i, pos;
7748     int scanned = 1;
7749     const char *str = Jim_GetString(strObjPtr, 0);
7750     Jim_Obj *resultList = 0;
7751     Jim_Obj **resultVec;
7752     int resultc;
7753     Jim_Obj *emptyStr = 0;
7754     ScanFmtStringObj *fmtObj;
7755
7756     /* If format specification is not an object, convert it! */
7757     if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7758         SetScanFmtFromAny(interp, fmtObjPtr);
7759     fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7760     /* Check if format specification was valid */
7761     if (fmtObj->error != 0) {
7762         if (flags & JIM_ERRMSG)
7763             Jim_SetResultString(interp, fmtObj->error, -1);
7764         return 0;
7765     }
7766     /* Allocate a new "shared" empty string for all unassigned conversions */
7767     emptyStr = Jim_NewEmptyStringObj(interp);
7768     Jim_IncrRefCount(emptyStr);
7769     /* Create a list and fill it with empty strings up to max specified XPG3 */
7770     resultList = Jim_NewListObj(interp, 0, 0);
7771     if (fmtObj->maxPos > 0) {
7772         for (i=0; i < fmtObj->maxPos; ++i)
7773             Jim_ListAppendElement(interp, resultList, emptyStr);
7774         JimListGetElements(interp, resultList, &resultc, &resultVec);
7775     }
7776     /* Now handle every partial format description */
7777     for (i=0, pos=0; i < fmtObj->count; ++i) {
7778         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7779         Jim_Obj *value = 0;
7780         /* Only last type may be "literal" w/o conversion - skip it! */
7781         if (descr->type == 0) continue;
7782         /* As long as any conversion could be done, we will proceed */
7783         if (scanned > 0)
7784             scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7785         /* In case our first try results in EOF, we will leave */
7786         if (scanned == -1 && i == 0)
7787             goto eof;
7788         /* Advance next pos-to-be-scanned for the amount scanned already */
7789         pos += scanned;
7790         /* value == 0 means no conversion took place so take empty string */
7791         if (value == 0)
7792             value = Jim_NewEmptyStringObj(interp);
7793         /* If value is a non-assignable one, skip it */
7794         if (descr->pos == -1) {
7795             Jim_FreeNewObj(interp, value);
7796         } else if (descr->pos == 0)
7797             /* Otherwise append it to the result list if no XPG3 was given */
7798             Jim_ListAppendElement(interp, resultList, value);
7799         else if (resultVec[descr->pos-1] == emptyStr) {
7800             /* But due to given XPG3, put the value into the corr. slot */
7801             Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7802             Jim_IncrRefCount(value);
7803             resultVec[descr->pos-1] = value;
7804         } else {
7805             /* Otherwise, the slot was already used - free obj and ERROR */
7806             Jim_FreeNewObj(interp, value);
7807             goto err;
7808         }
7809     }
7810     Jim_DecrRefCount(interp, emptyStr);
7811     return resultList;
7812 eof:
7813     Jim_DecrRefCount(interp, emptyStr);
7814     Jim_FreeNewObj(interp, resultList);
7815     return (Jim_Obj*)EOF;
7816 err:
7817     Jim_DecrRefCount(interp, emptyStr);
7818     Jim_FreeNewObj(interp, resultList);
7819     return 0;
7820 }
7821
7822 /* -----------------------------------------------------------------------------
7823  * Pseudo Random Number Generation
7824  * ---------------------------------------------------------------------------*/
7825 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7826         int seedLen);
7827
7828 /* Initialize the sbox with the numbers from 0 to 255 */
7829 static void JimPrngInit(Jim_Interp *interp)
7830 {
7831     int i;
7832     unsigned int seed[256];
7833
7834     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7835     for (i = 0; i < 256; i++)
7836         seed[i] = (rand() ^ time(NULL) ^ clock());
7837     JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7838 }
7839
7840 /* Generates N bytes of random data */
7841 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7842 {
7843     Jim_PrngState *prng;
7844     unsigned char *destByte = (unsigned char*) dest;
7845     unsigned int si, sj, x;
7846
7847     /* initialization, only needed the first time */
7848     if (interp->prngState == NULL)
7849         JimPrngInit(interp);
7850     prng = interp->prngState;
7851     /* generates 'len' bytes of pseudo-random numbers */
7852     for (x = 0; x < len; x++) {
7853         prng->i = (prng->i+1) & 0xff;
7854         si = prng->sbox[prng->i];
7855         prng->j = (prng->j + si) & 0xff;
7856         sj = prng->sbox[prng->j];
7857         prng->sbox[prng->i] = sj;
7858         prng->sbox[prng->j] = si;
7859         *destByte++ = prng->sbox[(si+sj)&0xff];
7860     }
7861 }
7862
7863 /* Re-seed the generator with user-provided bytes */
7864 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7865         int seedLen)
7866 {
7867     int i;
7868     unsigned char buf[256];
7869     Jim_PrngState *prng;
7870
7871     /* initialization, only needed the first time */
7872     if (interp->prngState == NULL)
7873         JimPrngInit(interp);
7874     prng = interp->prngState;
7875
7876     /* Set the sbox[i] with i */
7877     for (i = 0; i < 256; i++)
7878         prng->sbox[i] = i;
7879     /* Now use the seed to perform a random permutation of the sbox */
7880     for (i = 0; i < seedLen; i++) {
7881         unsigned char t;
7882
7883         t = prng->sbox[i&0xFF];
7884         prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7885         prng->sbox[seed[i]] = t;
7886     }
7887     prng->i = prng->j = 0;
7888     /* discard the first 256 bytes of stream. */
7889     JimRandomBytes(interp, buf, 256);
7890 }
7891
7892 /* -----------------------------------------------------------------------------
7893  * Dynamic libraries support (WIN32 not supported)
7894  * ---------------------------------------------------------------------------*/
7895
7896 #ifdef JIM_DYNLIB
7897 #ifdef WIN32
7898 #define RTLD_LAZY 0
7899 void * dlopen(const char *path, int mode) 
7900 {
7901     JIM_NOTUSED(mode);
7902
7903     return (void *)LoadLibraryA(path);
7904 }
7905 int dlclose(void *handle)
7906 {
7907     FreeLibrary((HANDLE)handle);
7908     return 0;
7909 }
7910 void *dlsym(void *handle, const char *symbol)
7911 {
7912     return GetProcAddress((HMODULE)handle, symbol);
7913 }
7914 static char win32_dlerror_string[121];
7915 const char *dlerror(void)
7916 {
7917     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7918                    LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7919     return win32_dlerror_string;
7920 }
7921 #endif /* WIN32 */
7922
7923 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7924 {
7925     Jim_Obj *libPathObjPtr;
7926     int prefixc, i;
7927     void *handle;
7928     int (*onload)(Jim_Interp *interp);
7929
7930     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7931     if (libPathObjPtr == NULL) {
7932         prefixc = 0;
7933         libPathObjPtr = NULL;
7934     } else {
7935         Jim_IncrRefCount(libPathObjPtr);
7936         Jim_ListLength(interp, libPathObjPtr, &prefixc);
7937     }
7938
7939     for (i = -1; i < prefixc; i++) {
7940         if (i < 0) {
7941             handle = dlopen(pathName, RTLD_LAZY);
7942         } else {
7943             FILE *fp;
7944             char buf[JIM_PATH_LEN];
7945             const char *prefix;
7946             int prefixlen;
7947             Jim_Obj *prefixObjPtr;
7948             
7949             buf[0] = '\0';
7950             if (Jim_ListIndex(interp, libPathObjPtr, i,
7951                     &prefixObjPtr, JIM_NONE) != JIM_OK)
7952                 continue;
7953             prefix = Jim_GetString(prefixObjPtr, &prefixlen);
7954             if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7955                 continue;
7956             if (*pathName == '/') {
7957                 strcpy(buf, pathName);
7958             }    
7959             else if (prefixlen && prefix[prefixlen-1] == '/')
7960                 sprintf(buf, "%s%s", prefix, pathName);
7961             else
7962                 sprintf(buf, "%s/%s", prefix, pathName);
7963             fp = fopen(buf, "r");
7964             if (fp == NULL)
7965                 continue;
7966             fclose(fp);
7967             handle = dlopen(buf, RTLD_LAZY);
7968         }
7969         if (handle == NULL) {
7970             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7971             Jim_AppendStrings(interp, Jim_GetResult(interp),
7972                 "error loading extension \"", pathName,
7973                 "\": ", dlerror(), NULL);
7974             if (i < 0)
7975                 continue;
7976             goto err;
7977         }
7978         if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7979             Jim_SetResultString(interp,
7980                     "No Jim_OnLoad symbol found on extension", -1);
7981             goto err;
7982         }
7983         if (onload(interp) == JIM_ERR) {
7984             dlclose(handle);
7985             goto err;
7986         }
7987         Jim_SetEmptyResult(interp);
7988         if (libPathObjPtr != NULL)
7989             Jim_DecrRefCount(interp, libPathObjPtr);
7990         return JIM_OK;
7991     }
7992 err:
7993     if (libPathObjPtr != NULL)
7994         Jim_DecrRefCount(interp, libPathObjPtr);
7995     return JIM_ERR;
7996 }
7997 #else /* JIM_DYNLIB */
7998 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7999 {
8000     JIM_NOTUSED(interp);
8001     JIM_NOTUSED(pathName);
8002
8003     Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
8004     return JIM_ERR;
8005 }
8006 #endif/* JIM_DYNLIB */
8007
8008 /* -----------------------------------------------------------------------------
8009  * Packages handling
8010  * ---------------------------------------------------------------------------*/
8011
8012 #define JIM_PKG_ANY_VERSION -1
8013
8014 /* Convert a string of the type "1.2" into an integer.
8015  * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted 
8016  * to the integer with value 102 */
8017 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
8018         int *intPtr, int flags)
8019 {
8020     char *copy;
8021     jim_wide major, minor;
8022     char *majorStr, *minorStr, *p;
8023
8024     if (v[0] == '\0') {
8025         *intPtr = JIM_PKG_ANY_VERSION;
8026         return JIM_OK;
8027     }
8028
8029     copy = Jim_StrDup(v);
8030     p = strchr(copy, '.');
8031     if (p == NULL) goto badfmt;
8032     *p = '\0';
8033     majorStr = copy;
8034     minorStr = p+1;
8035
8036     if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
8037         Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
8038         goto badfmt;
8039     *intPtr = (int)(major*100+minor);
8040     Jim_Free(copy);
8041     return JIM_OK;
8042
8043 badfmt:
8044     Jim_Free(copy);
8045     if (flags & JIM_ERRMSG) {
8046         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8047         Jim_AppendStrings(interp, Jim_GetResult(interp),
8048                 "invalid package version '", v, "'", NULL);
8049     }
8050     return JIM_ERR;
8051 }
8052
8053 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
8054 static int JimPackageMatchVersion(int needed, int actual, int flags)
8055 {
8056     if (needed == JIM_PKG_ANY_VERSION) return 1;
8057     if (flags & JIM_MATCHVER_EXACT) {
8058         return needed == actual;
8059     } else {
8060         return needed/100 == actual/100 && (needed <= actual);
8061     }
8062 }
8063
8064 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
8065         int flags)
8066 {
8067     int intVersion;
8068     /* Check if the version format is ok */
8069     if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
8070         return JIM_ERR;
8071     /* If the package was already provided returns an error. */
8072     if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
8073         if (flags & JIM_ERRMSG) {
8074             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8075             Jim_AppendStrings(interp, Jim_GetResult(interp),
8076                     "package '", name, "' was already provided", NULL);
8077         }
8078         return JIM_ERR;
8079     }
8080     Jim_AddHashEntry(&interp->packages, name, (char*) ver);
8081     return JIM_OK;
8082 }
8083
8084 #ifndef JIM_ANSIC
8085
8086 #ifndef WIN32
8087 # include <sys/types.h>
8088 # include <dirent.h>
8089 #else
8090 # include <io.h>
8091 /* Posix dirent.h compatiblity layer for WIN32.
8092  * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
8093  * Copyright Salvatore Sanfilippo ,2005.
8094  *
8095  * Permission to use, copy, modify, and distribute this software and its
8096  * documentation for any purpose is hereby granted without fee, provided
8097  * that this copyright and permissions notice appear in all copies and
8098  * derivatives.
8099  *
8100  * This software is supplied "as is" without express or implied warranty.
8101  * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
8102  */
8103
8104 struct dirent {
8105     char *d_name;
8106 };
8107
8108 typedef struct DIR {
8109     long                handle; /* -1 for failed rewind */
8110     struct _finddata_t  info;
8111     struct dirent       result; /* d_name null iff first time */
8112     char                *name;  /* null-terminated char string */
8113 } DIR;
8114
8115 DIR *opendir(const char *name)
8116 {
8117     DIR *dir = 0;
8118
8119     if(name && name[0]) {
8120         size_t base_length = strlen(name);
8121         const char *all = /* search pattern must end with suitable wildcard */
8122             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
8123
8124         if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
8125            (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
8126         {
8127             strcat(strcpy(dir->name, name), all);
8128
8129             if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
8130                 dir->result.d_name = 0;
8131             else { /* rollback */
8132                 Jim_Free(dir->name);
8133                 Jim_Free(dir);
8134                 dir = 0;
8135             }
8136         } else { /* rollback */
8137             Jim_Free(dir);
8138             dir   = 0;
8139             errno = ENOMEM;
8140         }
8141     } else {
8142         errno = EINVAL;
8143     }
8144     return dir;
8145 }
8146
8147 int closedir(DIR *dir)
8148 {
8149     int result = -1;
8150
8151     if(dir) {
8152         if(dir->handle != -1)
8153             result = _findclose(dir->handle);
8154         Jim_Free(dir->name);
8155         Jim_Free(dir);
8156     }
8157     if(result == -1) /* map all errors to EBADF */
8158         errno = EBADF;
8159     return result;
8160 }
8161
8162 struct dirent *readdir(DIR *dir)
8163 {
8164     struct dirent *result = 0;
8165
8166     if(dir && dir->handle != -1) {
8167         if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
8168             result         = &dir->result;
8169             result->d_name = dir->info.name;
8170         }
8171     } else {
8172         errno = EBADF;
8173     }
8174     return result;
8175 }
8176
8177 #endif /* WIN32 */
8178
8179 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8180         int prefixc, const char *pkgName, int pkgVer, int flags)
8181 {
8182     int bestVer = -1, i;
8183     int pkgNameLen = strlen(pkgName);
8184     char *bestPackage = NULL;
8185     struct dirent *de;
8186
8187     for (i = 0; i < prefixc; i++) {
8188         DIR *dir;
8189         char buf[JIM_PATH_LEN];
8190         int prefixLen;
8191
8192         if (prefixes[i] == NULL) continue;
8193         strncpy(buf, prefixes[i], JIM_PATH_LEN);
8194         buf[JIM_PATH_LEN-1] = '\0';
8195         prefixLen = strlen(buf);
8196         if (prefixLen && buf[prefixLen-1] == '/')
8197             buf[prefixLen-1] = '\0';
8198
8199         if ((dir = opendir(buf)) == NULL) continue;
8200         while ((de = readdir(dir)) != NULL) {
8201             char *fileName = de->d_name;
8202             int fileNameLen = strlen(fileName);
8203
8204             if (strncmp(fileName, "jim-", 4) == 0 &&
8205                 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
8206                 *(fileName+4+pkgNameLen) == '-' &&
8207                 fileNameLen > 4 && /* note that this is not really useful */
8208                 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
8209                  strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
8210                  strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
8211             {
8212                 char ver[6]; /* xx.yy<nulterm> */
8213                 char *p = strrchr(fileName, '.');
8214                 int verLen, fileVer;
8215
8216                 verLen = p - (fileName+4+pkgNameLen+1);
8217                 if (verLen < 3 || verLen > 5) continue;
8218                 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8219                 ver[verLen] = '\0';
8220                 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8221                         != JIM_OK) continue;
8222                 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8223                     (bestVer == -1 || bestVer < fileVer))
8224                 {
8225                     bestVer = fileVer;
8226                     Jim_Free(bestPackage);
8227                     bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8228                     sprintf(bestPackage, "%s/%s", buf, fileName);
8229                 }
8230             }
8231         }
8232         closedir(dir);
8233     }
8234     return bestPackage;
8235 }
8236
8237 #else /* JIM_ANSIC */
8238
8239 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8240         int prefixc, const char *pkgName, int pkgVer, int flags)
8241 {
8242     JIM_NOTUSED(interp);
8243     JIM_NOTUSED(prefixes);
8244     JIM_NOTUSED(prefixc);
8245     JIM_NOTUSED(pkgName);
8246     JIM_NOTUSED(pkgVer);
8247     JIM_NOTUSED(flags);
8248     return NULL;
8249 }
8250
8251 #endif /* JIM_ANSIC */
8252
8253 /* Search for a suitable package under every dir specified by jim_libpath
8254  * and load it if possible. If a suitable package was loaded with success
8255  * JIM_OK is returned, otherwise JIM_ERR is returned. */
8256 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8257         int flags)
8258 {
8259     Jim_Obj *libPathObjPtr;
8260     char **prefixes, *best;
8261     int prefixc, i, retCode = JIM_OK;
8262
8263     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8264     if (libPathObjPtr == NULL) {
8265         prefixc = 0;
8266         libPathObjPtr = NULL;
8267     } else {
8268         Jim_IncrRefCount(libPathObjPtr);
8269         Jim_ListLength(interp, libPathObjPtr, &prefixc);
8270     }
8271
8272     prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8273     for (i = 0; i < prefixc; i++) {
8274             Jim_Obj *prefixObjPtr;
8275             if (Jim_ListIndex(interp, libPathObjPtr, i,
8276                     &prefixObjPtr, JIM_NONE) != JIM_OK)
8277             {
8278                 prefixes[i] = NULL;
8279                 continue;
8280             }
8281             prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8282     }
8283     /* Scan every directory to find the "best" package. */
8284     best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8285     if (best != NULL) {
8286         char *p = strrchr(best, '.');
8287         /* Try to load/source it */
8288         if (p && strcmp(p, ".tcl") == 0) {
8289             retCode = Jim_EvalFile(interp, best);
8290         } else {
8291             retCode = Jim_LoadLibrary(interp, best);
8292         }
8293     } else {
8294         retCode = JIM_ERR;
8295     }
8296     Jim_Free(best);
8297     for (i = 0; i < prefixc; i++)
8298         Jim_Free(prefixes[i]);
8299     Jim_Free(prefixes);
8300     if (libPathObjPtr)
8301         Jim_DecrRefCount(interp, libPathObjPtr);
8302     return retCode;
8303 }
8304
8305 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8306         const char *ver, int flags)
8307 {
8308     Jim_HashEntry *he;
8309     int requiredVer;
8310
8311     /* Start with an empty error string */
8312     Jim_SetResultString(interp, "", 0);
8313
8314     if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8315         return NULL;
8316     he = Jim_FindHashEntry(&interp->packages, name);
8317     if (he == NULL) {
8318         /* Try to load the package. */
8319         if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8320             he = Jim_FindHashEntry(&interp->packages, name);
8321             if (he == NULL) {
8322                 return "?";
8323             }
8324             return he->val;
8325         }
8326         /* No way... return an error. */
8327         if (flags & JIM_ERRMSG) {
8328             int len;
8329             Jim_GetString(Jim_GetResult(interp), &len);
8330             Jim_AppendStrings(interp, Jim_GetResult(interp), len ? "\n" : "",
8331                     "Can't find package '", name, "'", NULL);
8332         }
8333         return NULL;
8334     } else {
8335         int actualVer;
8336         if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8337                 != JIM_OK)
8338         {
8339             return NULL;
8340         }
8341         /* Check if version matches. */
8342         if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8343             Jim_AppendStrings(interp, Jim_GetResult(interp),
8344                     "Package '", name, "' already loaded, but with version ",
8345                     he->val, NULL);
8346             return NULL;
8347         }
8348         return he->val;
8349     }
8350 }
8351
8352 /* -----------------------------------------------------------------------------
8353  * Eval
8354  * ---------------------------------------------------------------------------*/
8355 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8356 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8357
8358 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8359         Jim_Obj *const *argv);
8360
8361 /* Handle calls to the [unknown] command */
8362 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8363 {
8364     Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8365     int retCode;
8366
8367     /* If JimUnknown() is recursively called (e.g. error in the unknown proc,
8368      * done here
8369      */
8370     if (interp->unknown_called) {
8371         return JIM_ERR;
8372     }
8373
8374     /* If the [unknown] command does not exists returns
8375      * just now */
8376     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8377         return JIM_ERR;
8378
8379     /* The object interp->unknown just contains
8380      * the "unknown" string, it is used in order to
8381      * avoid to lookup the unknown command every time
8382      * but instread to cache the result. */
8383     if (argc+1 <= JIM_EVAL_SARGV_LEN)
8384         v = sv;
8385     else
8386         v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8387     /* Make a copy of the arguments vector, but shifted on
8388      * the right of one position. The command name of the
8389      * command will be instead the first argument of the
8390      * [unknonw] call. */
8391     memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8392     v[0] = interp->unknown;
8393     /* Call it */
8394     interp->unknown_called++;
8395     retCode = Jim_EvalObjVector(interp, argc+1, v);
8396     interp->unknown_called--;
8397
8398     /* Clean up */
8399     if (v != sv)
8400         Jim_Free(v);
8401     return retCode;
8402 }
8403
8404 /* Eval the object vector 'objv' composed of 'objc' elements.
8405  * Every element is used as single argument.
8406  * Jim_EvalObj() will call this function every time its object
8407  * argument is of "list" type, with no string representation.
8408  *
8409  * This is possible because the string representation of a
8410  * list object generated by the UpdateStringOfList is made
8411  * in a way that ensures that every list element is a different
8412  * command argument. */
8413 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8414 {
8415     int i, retcode;
8416     Jim_Cmd *cmdPtr;
8417
8418     /* Incr refcount of arguments. */
8419     for (i = 0; i < objc; i++)
8420         Jim_IncrRefCount(objv[i]);
8421     /* Command lookup */
8422     cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8423     if (cmdPtr == NULL) {
8424         retcode = JimUnknown(interp, objc, objv);
8425     } else {
8426         /* Call it -- Make sure result is an empty object. */
8427         Jim_SetEmptyResult(interp);
8428         if (cmdPtr->cmdProc) {
8429             interp->cmdPrivData = cmdPtr->privData;
8430             retcode = cmdPtr->cmdProc(interp, objc, objv);
8431             if (retcode == JIM_ERR_ADDSTACK) {
8432                 //JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8433                 retcode = JIM_ERR;
8434             }
8435         } else {
8436             retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8437             if (retcode == JIM_ERR) {
8438                 JimAppendStackTrace(interp,
8439                     Jim_GetString(objv[0], NULL), "", 1);
8440             }
8441         }
8442     }
8443     /* Decr refcount of arguments and return the retcode */
8444     for (i = 0; i < objc; i++)
8445         Jim_DecrRefCount(interp, objv[i]);
8446     return retcode;
8447 }
8448
8449 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8450  * via *objPtrPtr. This function is only called by Jim_EvalObj().
8451  * The returned object has refcount = 0. */
8452 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8453         int tokens, Jim_Obj **objPtrPtr)
8454 {
8455     int totlen = 0, i, retcode;
8456     Jim_Obj **intv;
8457     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8458     Jim_Obj *objPtr;
8459     char *s;
8460
8461     if (tokens <= JIM_EVAL_SINTV_LEN)
8462         intv = sintv;
8463     else
8464         intv = Jim_Alloc(sizeof(Jim_Obj*)*
8465                 tokens);
8466     /* Compute every token forming the argument
8467      * in the intv objects vector. */
8468     for (i = 0; i < tokens; i++) {
8469         switch(token[i].type) {
8470         case JIM_TT_ESC:
8471         case JIM_TT_STR:
8472             intv[i] = token[i].objPtr;
8473             break;
8474         case JIM_TT_VAR:
8475             intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8476             if (!intv[i]) {
8477                 retcode = JIM_ERR;
8478                 goto err;
8479             }
8480             break;
8481         case JIM_TT_DICTSUGAR:
8482             intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8483             if (!intv[i]) {
8484                 retcode = JIM_ERR;
8485                 goto err;
8486             }
8487             break;
8488         case JIM_TT_CMD:
8489             retcode = Jim_EvalObj(interp, token[i].objPtr);
8490             if (retcode != JIM_OK)
8491                 goto err;
8492             intv[i] = Jim_GetResult(interp);
8493             break;
8494         default:
8495             Jim_Panic(interp,
8496               "default token type reached "
8497               "in Jim_InterpolateTokens().");
8498             break;
8499         }
8500         Jim_IncrRefCount(intv[i]);
8501         /* Make sure there is a valid
8502          * string rep, and add the string
8503          * length to the total legnth. */
8504         Jim_GetString(intv[i], NULL);
8505         totlen += intv[i]->length;
8506     }
8507     /* Concatenate every token in an unique
8508      * object. */
8509     objPtr = Jim_NewStringObjNoAlloc(interp,
8510             NULL, 0);
8511     s = objPtr->bytes = Jim_Alloc(totlen+1);
8512     objPtr->length = totlen;
8513     for (i = 0; i < tokens; i++) {
8514         memcpy(s, intv[i]->bytes, intv[i]->length);
8515         s += intv[i]->length;
8516         Jim_DecrRefCount(interp, intv[i]);
8517     }
8518     objPtr->bytes[totlen] = '\0';
8519     /* Free the intv vector if not static. */
8520     if (tokens > JIM_EVAL_SINTV_LEN)
8521         Jim_Free(intv);
8522     *objPtrPtr = objPtr;
8523     return JIM_OK;
8524 err:
8525     i--;
8526     for (; i >= 0; i--)
8527         Jim_DecrRefCount(interp, intv[i]);
8528     if (tokens > JIM_EVAL_SINTV_LEN)
8529         Jim_Free(intv);
8530     return retcode;
8531 }
8532
8533 /* Helper of Jim_EvalObj() to perform argument expansion.
8534  * Basically this function append an argument to 'argv'
8535  * (and increments argc by reference accordingly), performing
8536  * expansion of the list object if 'expand' is non-zero, or
8537  * just adding objPtr to argv if 'expand' is zero. */
8538 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8539         int *argcPtr, int expand, Jim_Obj *objPtr)
8540 {
8541     if (!expand) {
8542         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8543         /* refcount of objPtr not incremented because
8544          * we are actually transfering a reference from
8545          * the old 'argv' to the expanded one. */
8546         (*argv)[*argcPtr] = objPtr;
8547         (*argcPtr)++;
8548     } else {
8549         int len, i;
8550
8551         Jim_ListLength(interp, objPtr, &len);
8552         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8553         for (i = 0; i < len; i++) {
8554             (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8555             Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8556             (*argcPtr)++;
8557         }
8558         /* The original object reference is no longer needed,
8559          * after the expansion it is no longer present on
8560          * the argument vector, but the single elements are
8561          * in its place. */
8562         Jim_DecrRefCount(interp, objPtr);
8563     }
8564 }
8565
8566 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8567 {
8568     int i, j = 0, len;
8569     ScriptObj *script;
8570     ScriptToken *token;
8571     int *cs; /* command structure array */
8572     int retcode = JIM_OK;
8573     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8574
8575     interp->errorFlag = 0;
8576
8577     /* If the object is of type "list" and there is no
8578      * string representation for this object, we can call
8579      * a specialized version of Jim_EvalObj() */
8580     if (scriptObjPtr->typePtr == &listObjType &&
8581         scriptObjPtr->internalRep.listValue.len &&
8582         scriptObjPtr->bytes == NULL) {
8583         Jim_IncrRefCount(scriptObjPtr);
8584         retcode = Jim_EvalObjVector(interp,
8585                 scriptObjPtr->internalRep.listValue.len,
8586                 scriptObjPtr->internalRep.listValue.ele);
8587         Jim_DecrRefCount(interp, scriptObjPtr);
8588         return retcode;
8589     }
8590
8591     Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8592     script = Jim_GetScript(interp, scriptObjPtr);
8593     /* Now we have to make sure the internal repr will not be
8594      * freed on shimmering.
8595      *
8596      * Think for example to this:
8597      *
8598      * set x {llength $x; ... some more code ...}; eval $x
8599      *
8600      * In order to preserve the internal rep, we increment the
8601      * inUse field of the script internal rep structure. */
8602     script->inUse++;
8603
8604     token = script->token;
8605     len = script->len;
8606     cs = script->cmdStruct;
8607     i = 0; /* 'i' is the current token index. */
8608
8609     /* Reset the interpreter result. This is useful to
8610      * return the emtpy result in the case of empty program. */
8611     Jim_SetEmptyResult(interp);
8612
8613     /* Execute every command sequentially, returns on
8614      * error (i.e. if a command does not return JIM_OK) */
8615     while (i < len) {
8616         int expand = 0;
8617         int argc = *cs++; /* Get the number of arguments */
8618         Jim_Cmd *cmd;
8619
8620         /* Set the expand flag if needed. */
8621         if (argc == -1) {
8622             expand++;
8623             argc = *cs++;
8624         }
8625         /* Allocate the arguments vector */
8626         if (argc <= JIM_EVAL_SARGV_LEN)
8627             argv = sargv;
8628         else
8629             argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8630         /* Populate the arguments objects. */
8631         for (j = 0; j < argc; j++) {
8632             int tokens = *cs++;
8633
8634             /* tokens is negative if expansion is needed.
8635              * for this argument. */
8636             if (tokens < 0) {
8637                 tokens = (-tokens)-1;
8638                 i++;
8639             }
8640             if (tokens == 1) {
8641                 /* Fast path if the token does not
8642                  * need interpolation */
8643                 switch(token[i].type) {
8644                 case JIM_TT_ESC:
8645                 case JIM_TT_STR:
8646                     argv[j] = token[i].objPtr;
8647                     break;
8648                 case JIM_TT_VAR:
8649                     tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8650                             JIM_ERRMSG);
8651                     if (!tmpObjPtr) {
8652                         retcode = JIM_ERR;
8653                         goto err;
8654                     }
8655                     argv[j] = tmpObjPtr;
8656                     break;
8657                 case JIM_TT_DICTSUGAR:
8658                     tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8659                     if (!tmpObjPtr) {
8660                         retcode = JIM_ERR;
8661                         goto err;
8662                     }
8663                     argv[j] = tmpObjPtr;
8664                     break;
8665                 case JIM_TT_CMD:
8666                     retcode = Jim_EvalObj(interp, token[i].objPtr);
8667                     if (retcode != JIM_OK)
8668                         goto err;
8669                     argv[j] = Jim_GetResult(interp);
8670                     break;
8671                 default:
8672                     Jim_Panic(interp,
8673                       "default token type reached "
8674                       "in Jim_EvalObj().");
8675                     break;
8676                 }
8677                 Jim_IncrRefCount(argv[j]);
8678                 i += 2;
8679             } else {
8680                 /* For interpolation we call an helper
8681                  * function doing the work for us. */
8682                 if ((retcode = Jim_InterpolateTokens(interp,
8683                         token+i, tokens, &tmpObjPtr)) != JIM_OK)
8684                 {
8685                     goto err;
8686                 }
8687                 argv[j] = tmpObjPtr;
8688                 Jim_IncrRefCount(argv[j]);
8689                 i += tokens+1;
8690             }
8691         }
8692         /* Handle {expand} expansion */
8693         if (expand) {
8694             int *ecs = cs - argc;
8695             int eargc = 0;
8696             Jim_Obj **eargv = NULL;
8697
8698             for (j = 0; j < argc; j++) {
8699                 Jim_ExpandArgument( interp, &eargv, &eargc,
8700                         ecs[j] < 0, argv[j]);
8701             }
8702             if (argv != sargv)
8703                 Jim_Free(argv);
8704             argc = eargc;
8705             argv = eargv;
8706             j = argc;
8707             if (argc == 0) {
8708                 /* Nothing to do with zero args. */
8709                 Jim_Free(eargv);
8710                 continue;
8711             }
8712         }
8713         /* Lookup the command to call */
8714         cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8715         if (cmd != NULL) {
8716             /* Call it -- Make sure result is an empty object. */
8717             Jim_SetEmptyResult(interp);
8718             if (cmd->cmdProc) {
8719                 interp->cmdPrivData = cmd->privData;
8720                 retcode = cmd->cmdProc(interp, argc, argv);
8721                 if ((retcode == JIM_ERR)||(retcode == JIM_ERR_ADDSTACK)) {
8722                     JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8723                     retcode = JIM_ERR;
8724                 }
8725             } else {
8726                 retcode = JimCallProcedure(interp, cmd, argc, argv);
8727                 if (retcode == JIM_ERR) {
8728                     JimAppendStackTrace(interp,
8729                         Jim_GetString(argv[0], NULL), script->fileName,
8730                         token[i-argc*2].linenr);
8731                 }
8732             }
8733         } else {
8734             /* Call [unknown] */
8735             retcode = JimUnknown(interp, argc, argv);
8736             if (retcode == JIM_ERR) {
8737                 JimAppendStackTrace(interp,
8738                     "", script->fileName,
8739                     token[i-argc*2].linenr);
8740             }
8741         }
8742         if (retcode != JIM_OK) {
8743             i -= argc*2; /* point to the command name. */
8744             goto err;
8745         }
8746         /* Decrement the arguments count */
8747         for (j = 0; j < argc; j++) {
8748             Jim_DecrRefCount(interp, argv[j]);
8749         }
8750
8751         if (argv != sargv) {
8752             Jim_Free(argv);
8753             argv = NULL;
8754         }
8755     }
8756     /* Note that we don't have to decrement inUse, because the
8757      * following code transfers our use of the reference again to
8758      * the script object. */
8759     j = 0; /* on normal termination, the argv array is already
8760           Jim_DecrRefCount-ed. */
8761 err:
8762     /* Handle errors. */
8763     if (retcode == JIM_ERR && !interp->errorFlag) {
8764         interp->errorFlag = 1;
8765         JimSetErrorFileName(interp, script->fileName);
8766         JimSetErrorLineNumber(interp, token[i].linenr);
8767         JimResetStackTrace(interp);
8768     }
8769     Jim_FreeIntRep(interp, scriptObjPtr);
8770     scriptObjPtr->typePtr = &scriptObjType;
8771     Jim_SetIntRepPtr(scriptObjPtr, script);
8772     Jim_DecrRefCount(interp, scriptObjPtr);
8773     for (i = 0; i < j; i++) {
8774         Jim_DecrRefCount(interp, argv[i]);
8775     }
8776     if (argv != sargv)
8777         Jim_Free(argv);
8778     return retcode;
8779 }
8780
8781 /* Call a procedure implemented in Tcl.
8782  * It's possible to speed-up a lot this function, currently
8783  * the callframes are not cached, but allocated and
8784  * destroied every time. What is expecially costly is
8785  * to create/destroy the local vars hash table every time.
8786  *
8787  * This can be fixed just implementing callframes caching
8788  * in JimCreateCallFrame() and JimFreeCallFrame(). */
8789 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8790         Jim_Obj *const *argv)
8791 {
8792     int i, retcode;
8793     Jim_CallFrame *callFramePtr;
8794     int num_args;
8795
8796     /* Check arity */
8797     if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8798         argc > cmd->arityMax)) {
8799         Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8800         Jim_AppendStrings(interp, objPtr,
8801             "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8802             (cmd->arityMin > 1) ? " " : "",
8803             Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8804         Jim_SetResult(interp, objPtr);
8805         return JIM_ERR;
8806     }
8807     /* Check if there are too nested calls */
8808     if (interp->numLevels == interp->maxNestingDepth) {
8809         Jim_SetResultString(interp,
8810             "Too many nested calls. Infinite recursion?", -1);
8811         return JIM_ERR;
8812     }
8813     /* Create a new callframe */
8814     callFramePtr = JimCreateCallFrame(interp);
8815     callFramePtr->parentCallFrame = interp->framePtr;
8816     callFramePtr->argv = argv;
8817     callFramePtr->argc = argc;
8818     callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8819     callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8820     callFramePtr->staticVars = cmd->staticVars;
8821     Jim_IncrRefCount(cmd->argListObjPtr);
8822     Jim_IncrRefCount(cmd->bodyObjPtr);
8823     interp->framePtr = callFramePtr;
8824     interp->numLevels ++;
8825
8826     /* Set arguments */
8827     Jim_ListLength(interp, cmd->argListObjPtr, &num_args);
8828
8829     /* If last argument is 'args', don't set it here */
8830     if (cmd->arityMax == -1) {
8831         num_args--;
8832     }
8833
8834     for (i = 0; i < num_args; i++) {
8835         Jim_Obj *argObjPtr;
8836         Jim_Obj *nameObjPtr;
8837         Jim_Obj *valueObjPtr;
8838
8839         Jim_ListIndex(interp, cmd->argListObjPtr, i, &argObjPtr, JIM_NONE);
8840         if (i + 1 >= cmd->arityMin) {
8841             /* The name is the first element of the list */
8842             Jim_ListIndex(interp, argObjPtr, 0, &nameObjPtr, JIM_NONE);
8843         }
8844         else {
8845             /* The element arg is the name */
8846             nameObjPtr = argObjPtr;
8847         }
8848
8849         if (i + 1 >= argc) {
8850             /* No more values, so use default */
8851             /* The value is the second element of the list */
8852             Jim_ListIndex(interp, argObjPtr, 1, &valueObjPtr, JIM_NONE);
8853         }
8854         else {
8855             valueObjPtr = argv[i+1];
8856         }
8857         Jim_SetVariable(interp, nameObjPtr, valueObjPtr);
8858     }
8859     /* Set optional arguments */
8860     if (cmd->arityMax == -1) {
8861         Jim_Obj *listObjPtr, *objPtr;
8862
8863         i++;
8864         listObjPtr = Jim_NewListObj(interp, argv+i, argc-i);
8865         Jim_ListIndex(interp, cmd->argListObjPtr, num_args, &objPtr, JIM_NONE);
8866         Jim_SetVariable(interp, objPtr, listObjPtr);
8867     }
8868     /* Eval the body */
8869     retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8870
8871     /* Destroy the callframe */
8872     interp->numLevels --;
8873     interp->framePtr = interp->framePtr->parentCallFrame;
8874     if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8875         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8876     } else {
8877         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8878     }
8879     /* Handle the JIM_EVAL return code */
8880     if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8881         int savedLevel = interp->evalRetcodeLevel;
8882
8883         interp->evalRetcodeLevel = interp->numLevels;
8884         while (retcode == JIM_EVAL) {
8885             Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8886             Jim_IncrRefCount(resultScriptObjPtr);
8887             retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8888             Jim_DecrRefCount(interp, resultScriptObjPtr);
8889         }
8890         interp->evalRetcodeLevel = savedLevel;
8891     }
8892     /* Handle the JIM_RETURN return code */
8893     if (retcode == JIM_RETURN) {
8894         retcode = interp->returnCode;
8895         interp->returnCode = JIM_OK;
8896     }
8897     return retcode;
8898 }
8899
8900 int Jim_Eval_Named(Jim_Interp *interp, const char *script, const char *filename, int lineno)
8901 {
8902     int retval;
8903     Jim_Obj *scriptObjPtr;
8904
8905         scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8906     Jim_IncrRefCount(scriptObjPtr);
8907
8908
8909         if( filename ){
8910                 JimSetSourceInfo( interp, scriptObjPtr, filename, lineno );
8911         }
8912
8913     retval = Jim_EvalObj(interp, scriptObjPtr);
8914     Jim_DecrRefCount(interp, scriptObjPtr);
8915     return retval;
8916 }
8917
8918 int Jim_Eval(Jim_Interp *interp, const char *script)
8919 {
8920         return Jim_Eval_Named( interp, script, NULL, 0 );
8921 }
8922
8923
8924
8925 /* Execute script in the scope of the global level */
8926 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8927 {
8928     Jim_CallFrame *savedFramePtr;
8929     int retval;
8930
8931     savedFramePtr = interp->framePtr;
8932     interp->framePtr = interp->topFramePtr;
8933     retval = Jim_Eval(interp, script);
8934     interp->framePtr = savedFramePtr;
8935     return retval;
8936 }
8937
8938 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8939 {
8940     Jim_CallFrame *savedFramePtr;
8941     int retval;
8942
8943     savedFramePtr = interp->framePtr;
8944     interp->framePtr = interp->topFramePtr;
8945     retval = Jim_EvalObj(interp, scriptObjPtr);
8946     interp->framePtr = savedFramePtr;
8947     /* Try to report the error (if any) via the bgerror proc */
8948     if (retval != JIM_OK) {
8949         Jim_Obj *objv[2];
8950
8951         objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8952         objv[1] = Jim_GetResult(interp);
8953         Jim_IncrRefCount(objv[0]);
8954         Jim_IncrRefCount(objv[1]);
8955         if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8956             /* Report the error to stderr. */
8957             Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8958             Jim_PrintErrorMessage(interp);
8959         }
8960         Jim_DecrRefCount(interp, objv[0]);
8961         Jim_DecrRefCount(interp, objv[1]);
8962     }
8963     return retval;
8964 }
8965
8966 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8967 {
8968     char *prg = NULL;
8969     FILE *fp;
8970     int nread, totread, maxlen, buflen;
8971     int retval;
8972     Jim_Obj *scriptObjPtr;
8973     
8974     if ((fp = fopen(filename, "r")) == NULL) {
8975         const int cwd_len=2048;
8976                 char *cwd=malloc(cwd_len);
8977         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8978         if (!getcwd( cwd, cwd_len )) strcpy(cwd, "unknown");
8979         Jim_AppendStrings(interp, Jim_GetResult(interp),
8980         "Error loading script \"", filename, "\"",
8981             " cwd: ", cwd,
8982             " err: ", strerror(errno), NULL);
8983             free(cwd);
8984         return JIM_ERR;
8985     }
8986     buflen = 1024;
8987     maxlen = totread = 0;
8988     while (1) {
8989         if (maxlen < totread+buflen+1) {
8990             maxlen = totread+buflen+1;
8991             prg = Jim_Realloc(prg, maxlen);
8992         }
8993                 /* do not use Jim_fread() - this is really a file */
8994         if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8995         totread += nread;
8996     }
8997     prg[totread] = '\0';
8998         /* do not use Jim_fclose() - this is really a file */
8999     fclose(fp);
9000
9001     scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
9002     JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
9003     Jim_IncrRefCount(scriptObjPtr);
9004     retval = Jim_EvalObj(interp, scriptObjPtr);
9005     Jim_DecrRefCount(interp, scriptObjPtr);
9006     return retval;
9007 }
9008
9009 /* -----------------------------------------------------------------------------
9010  * Subst
9011  * ---------------------------------------------------------------------------*/
9012 static int JimParseSubstStr(struct JimParserCtx *pc)
9013 {
9014     pc->tstart = pc->p;
9015     pc->tline = pc->linenr;
9016     while (*pc->p && *pc->p != '$' && *pc->p != '[') {
9017         pc->p++; pc->len--;
9018     }
9019     pc->tend = pc->p-1;
9020     pc->tt = JIM_TT_ESC;
9021     return JIM_OK;
9022 }
9023
9024 static int JimParseSubst(struct JimParserCtx *pc, int flags)
9025 {
9026     int retval;
9027
9028     if (pc->len == 0) {
9029         pc->tstart = pc->tend = pc->p;
9030         pc->tline = pc->linenr;
9031         pc->tt = JIM_TT_EOL;
9032         pc->eof = 1;
9033         return JIM_OK;
9034     }
9035     switch(*pc->p) {
9036     case '[':
9037         retval = JimParseCmd(pc);
9038         if (flags & JIM_SUBST_NOCMD) {
9039             pc->tstart--;
9040             pc->tend++;
9041             pc->tt = (flags & JIM_SUBST_NOESC) ?
9042                 JIM_TT_STR : JIM_TT_ESC;
9043         }
9044         return retval;
9045         break;
9046     case '$':
9047         if (JimParseVar(pc) == JIM_ERR) {
9048             pc->tstart = pc->tend = pc->p++; pc->len--;
9049             pc->tline = pc->linenr;
9050             pc->tt = JIM_TT_STR;
9051         } else {
9052             if (flags & JIM_SUBST_NOVAR) {
9053                 pc->tstart--;
9054                 if (flags & JIM_SUBST_NOESC)
9055                     pc->tt = JIM_TT_STR;
9056                 else
9057                     pc->tt = JIM_TT_ESC;
9058                 if (*pc->tstart == '{') {
9059                     pc->tstart--;
9060                     if (*(pc->tend+1))
9061                         pc->tend++;
9062                 }
9063             }
9064         }
9065         break;
9066     default:
9067         retval = JimParseSubstStr(pc);
9068         if (flags & JIM_SUBST_NOESC)
9069             pc->tt = JIM_TT_STR;
9070         return retval;
9071         break;
9072     }
9073     return JIM_OK;
9074 }
9075
9076 /* The subst object type reuses most of the data structures and functions
9077  * of the script object. Script's data structures are a bit more complex
9078  * for what is needed for [subst]itution tasks, but the reuse helps to
9079  * deal with a single data structure at the cost of some more memory
9080  * usage for substitutions. */
9081 static Jim_ObjType substObjType = {
9082     "subst",
9083     FreeScriptInternalRep,
9084     DupScriptInternalRep,
9085     NULL,
9086     JIM_TYPE_REFERENCES,
9087 };
9088
9089 /* This method takes the string representation of an object
9090  * as a Tcl string where to perform [subst]itution, and generates
9091  * the pre-parsed internal representation. */
9092 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
9093 {
9094     int scriptTextLen;
9095     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9096     struct JimParserCtx parser;
9097     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
9098
9099     script->len = 0;
9100     script->csLen = 0;
9101     script->commands = 0;
9102     script->token = NULL;
9103     script->cmdStruct = NULL;
9104     script->inUse = 1;
9105     script->substFlags = flags;
9106     script->fileName = NULL;
9107
9108     JimParserInit(&parser, scriptText, scriptTextLen, 1);
9109     while(1) {
9110         char *token;
9111         int len, type, linenr;
9112
9113         JimParseSubst(&parser, flags);
9114         if (JimParserEof(&parser)) break;
9115         token = JimParserGetToken(&parser, &len, &type, &linenr);
9116         ScriptObjAddToken(interp, script, token, len, type,
9117                 NULL, linenr);
9118     }
9119     /* Free the old internal rep and set the new one. */
9120     Jim_FreeIntRep(interp, objPtr);
9121     Jim_SetIntRepPtr(objPtr, script);
9122     objPtr->typePtr = &scriptObjType;
9123     return JIM_OK;
9124 }
9125
9126 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9127 {
9128     struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
9129
9130     if (objPtr->typePtr != &substObjType || script->substFlags != flags)
9131         SetSubstFromAny(interp, objPtr, flags);
9132     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
9133 }
9134
9135 /* Performs commands,variables,blackslashes substitution,
9136  * storing the result object (with refcount 0) into
9137  * resObjPtrPtr. */
9138 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
9139         Jim_Obj **resObjPtrPtr, int flags)
9140 {
9141     ScriptObj *script;
9142     ScriptToken *token;
9143     int i, len, retcode = JIM_OK;
9144     Jim_Obj *resObjPtr, *savedResultObjPtr;
9145
9146     script = Jim_GetSubst(interp, substObjPtr, flags);
9147 #ifdef JIM_OPTIMIZATION
9148     /* Fast path for a very common case with array-alike syntax,
9149      * that's: $foo($bar) */
9150     if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
9151         Jim_Obj *varObjPtr = script->token[0].objPtr;
9152         
9153         Jim_IncrRefCount(varObjPtr);
9154         resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
9155         if (resObjPtr == NULL) {
9156             Jim_DecrRefCount(interp, varObjPtr);
9157             return JIM_ERR;
9158         }
9159         Jim_DecrRefCount(interp, varObjPtr);
9160         *resObjPtrPtr = resObjPtr;
9161         return JIM_OK;
9162     }
9163 #endif
9164
9165     Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
9166     /* In order to preserve the internal rep, we increment the
9167      * inUse field of the script internal rep structure. */
9168     script->inUse++;
9169
9170     token = script->token;
9171     len = script->len;
9172
9173     /* Save the interp old result, to set it again before
9174      * to return. */
9175     savedResultObjPtr = interp->result;
9176     Jim_IncrRefCount(savedResultObjPtr);
9177     
9178     /* Perform the substitution. Starts with an empty object
9179      * and adds every token (performing the appropriate
9180      * var/command/escape substitution). */
9181     resObjPtr = Jim_NewStringObj(interp, "", 0);
9182     for (i = 0; i < len; i++) {
9183         Jim_Obj *objPtr;
9184
9185         switch(token[i].type) {
9186         case JIM_TT_STR:
9187         case JIM_TT_ESC:
9188             Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
9189             break;
9190         case JIM_TT_VAR:
9191             objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
9192             if (objPtr == NULL) goto err;
9193             Jim_IncrRefCount(objPtr);
9194             Jim_AppendObj(interp, resObjPtr, objPtr);
9195             Jim_DecrRefCount(interp, objPtr);
9196             break;
9197         case JIM_TT_DICTSUGAR:
9198             objPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
9199             if (!objPtr) {
9200                 retcode = JIM_ERR;
9201                 goto err;
9202             }
9203             break;
9204         case JIM_TT_CMD:
9205             if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
9206                 goto err;
9207             Jim_AppendObj(interp, resObjPtr, interp->result);
9208             break;
9209         default:
9210             Jim_Panic(interp,
9211               "default token type (%d) reached "
9212               "in Jim_SubstObj().", token[i].type);
9213             break;
9214         }
9215     }
9216 ok:
9217     if (retcode == JIM_OK)
9218         Jim_SetResult(interp, savedResultObjPtr);
9219     Jim_DecrRefCount(interp, savedResultObjPtr);
9220     /* Note that we don't have to decrement inUse, because the
9221      * following code transfers our use of the reference again to
9222      * the script object. */
9223     Jim_FreeIntRep(interp, substObjPtr);
9224     substObjPtr->typePtr = &scriptObjType;
9225     Jim_SetIntRepPtr(substObjPtr, script);
9226     Jim_DecrRefCount(interp, substObjPtr);
9227     *resObjPtrPtr = resObjPtr;
9228     return retcode;
9229 err:
9230     Jim_FreeNewObj(interp, resObjPtr);
9231     retcode = JIM_ERR;
9232     goto ok;
9233 }
9234
9235 /* -----------------------------------------------------------------------------
9236  * API Input/Export functions
9237  * ---------------------------------------------------------------------------*/
9238
9239 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
9240 {
9241     Jim_HashEntry *he;
9242
9243     he = Jim_FindHashEntry(&interp->stub, funcname);
9244     if (!he)
9245         return JIM_ERR;
9246     memcpy(targetPtrPtr, &he->val, sizeof(void*));
9247     return JIM_OK;
9248 }
9249
9250 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
9251 {
9252     return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
9253 }
9254
9255 #define JIM_REGISTER_API(name) \
9256     Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
9257
9258 void JimRegisterCoreApi(Jim_Interp *interp)
9259 {
9260   interp->getApiFuncPtr = Jim_GetApi;
9261   JIM_REGISTER_API(Alloc);
9262   JIM_REGISTER_API(Free);
9263   JIM_REGISTER_API(Eval);
9264   JIM_REGISTER_API(Eval_Named);
9265   JIM_REGISTER_API(EvalGlobal);
9266   JIM_REGISTER_API(EvalFile);
9267   JIM_REGISTER_API(EvalObj);
9268   JIM_REGISTER_API(EvalObjBackground);
9269   JIM_REGISTER_API(EvalObjVector);
9270   JIM_REGISTER_API(InitHashTable);
9271   JIM_REGISTER_API(ExpandHashTable);
9272   JIM_REGISTER_API(AddHashEntry);
9273   JIM_REGISTER_API(ReplaceHashEntry);
9274   JIM_REGISTER_API(DeleteHashEntry);
9275   JIM_REGISTER_API(FreeHashTable);
9276   JIM_REGISTER_API(FindHashEntry);
9277   JIM_REGISTER_API(ResizeHashTable);
9278   JIM_REGISTER_API(GetHashTableIterator);
9279   JIM_REGISTER_API(NextHashEntry);
9280   JIM_REGISTER_API(NewObj);
9281   JIM_REGISTER_API(FreeObj);
9282   JIM_REGISTER_API(InvalidateStringRep);
9283   JIM_REGISTER_API(InitStringRep);
9284   JIM_REGISTER_API(DuplicateObj);
9285   JIM_REGISTER_API(GetString);
9286   JIM_REGISTER_API(Length);
9287   JIM_REGISTER_API(InvalidateStringRep);
9288   JIM_REGISTER_API(NewStringObj);
9289   JIM_REGISTER_API(NewStringObjNoAlloc);
9290   JIM_REGISTER_API(AppendString);
9291   JIM_REGISTER_API(AppendString_sprintf);
9292   JIM_REGISTER_API(AppendObj);
9293   JIM_REGISTER_API(AppendStrings);
9294   JIM_REGISTER_API(StringEqObj);
9295   JIM_REGISTER_API(StringMatchObj);
9296   JIM_REGISTER_API(StringRangeObj);
9297   JIM_REGISTER_API(FormatString);
9298   JIM_REGISTER_API(CompareStringImmediate);
9299   JIM_REGISTER_API(NewReference);
9300   JIM_REGISTER_API(GetReference);
9301   JIM_REGISTER_API(SetFinalizer);
9302   JIM_REGISTER_API(GetFinalizer);
9303   JIM_REGISTER_API(CreateInterp);
9304   JIM_REGISTER_API(FreeInterp);
9305   JIM_REGISTER_API(GetExitCode);
9306   JIM_REGISTER_API(SetStdin);
9307   JIM_REGISTER_API(SetStdout);
9308   JIM_REGISTER_API(SetStderr);
9309   JIM_REGISTER_API(CreateCommand);
9310   JIM_REGISTER_API(CreateProcedure);
9311   JIM_REGISTER_API(DeleteCommand);
9312   JIM_REGISTER_API(RenameCommand);
9313   JIM_REGISTER_API(GetCommand);
9314   JIM_REGISTER_API(SetVariable);
9315   JIM_REGISTER_API(SetVariableStr);
9316   JIM_REGISTER_API(SetGlobalVariableStr);
9317   JIM_REGISTER_API(SetVariableStrWithStr);
9318   JIM_REGISTER_API(SetVariableLink);
9319   JIM_REGISTER_API(GetVariable);
9320   JIM_REGISTER_API(GetCallFrameByLevel);
9321   JIM_REGISTER_API(Collect);
9322   JIM_REGISTER_API(CollectIfNeeded);
9323   JIM_REGISTER_API(GetIndex);
9324   JIM_REGISTER_API(NewListObj);
9325   JIM_REGISTER_API(ListAppendElement);
9326   JIM_REGISTER_API(ListAppendList);
9327   JIM_REGISTER_API(ListLength);
9328   JIM_REGISTER_API(ListIndex);
9329   JIM_REGISTER_API(SetListIndex);
9330   JIM_REGISTER_API(ConcatObj);
9331   JIM_REGISTER_API(NewDictObj);
9332   JIM_REGISTER_API(DictKey);
9333   JIM_REGISTER_API(DictKeysVector);
9334   JIM_REGISTER_API(GetIndex);
9335   JIM_REGISTER_API(GetReturnCode);
9336   JIM_REGISTER_API(EvalExpression);
9337   JIM_REGISTER_API(GetBoolFromExpr);
9338   JIM_REGISTER_API(GetWide);
9339   JIM_REGISTER_API(GetLong);
9340   JIM_REGISTER_API(SetWide);
9341   JIM_REGISTER_API(NewIntObj);
9342   JIM_REGISTER_API(GetDouble);
9343   JIM_REGISTER_API(SetDouble);
9344   JIM_REGISTER_API(NewDoubleObj);
9345   JIM_REGISTER_API(WrongNumArgs);
9346   JIM_REGISTER_API(SetDictKeysVector);
9347   JIM_REGISTER_API(SubstObj);
9348   JIM_REGISTER_API(RegisterApi);
9349   JIM_REGISTER_API(PrintErrorMessage);
9350   JIM_REGISTER_API(InteractivePrompt);
9351   JIM_REGISTER_API(RegisterCoreCommands);
9352   JIM_REGISTER_API(GetSharedString);
9353   JIM_REGISTER_API(ReleaseSharedString);
9354   JIM_REGISTER_API(Panic);
9355   JIM_REGISTER_API(StrDup);
9356   JIM_REGISTER_API(UnsetVariable);
9357   JIM_REGISTER_API(GetVariableStr);
9358   JIM_REGISTER_API(GetGlobalVariable);
9359   JIM_REGISTER_API(GetGlobalVariableStr);
9360   JIM_REGISTER_API(GetAssocData);
9361   JIM_REGISTER_API(SetAssocData);
9362   JIM_REGISTER_API(DeleteAssocData);
9363   JIM_REGISTER_API(GetEnum);
9364   JIM_REGISTER_API(ScriptIsComplete);
9365   JIM_REGISTER_API(PackageRequire);
9366   JIM_REGISTER_API(PackageProvide);
9367   JIM_REGISTER_API(InitStack);
9368   JIM_REGISTER_API(FreeStack);
9369   JIM_REGISTER_API(StackLen);
9370   JIM_REGISTER_API(StackPush);
9371   JIM_REGISTER_API(StackPop);
9372   JIM_REGISTER_API(StackPeek);
9373   JIM_REGISTER_API(FreeStackElements);
9374   JIM_REGISTER_API(fprintf  );
9375   JIM_REGISTER_API(vfprintf );
9376   JIM_REGISTER_API(fwrite   );
9377   JIM_REGISTER_API(fread    );
9378   JIM_REGISTER_API(fflush   );
9379   JIM_REGISTER_API(fgets    );
9380   JIM_REGISTER_API(GetNvp);
9381   JIM_REGISTER_API(Nvp_name2value);
9382   JIM_REGISTER_API(Nvp_name2value_simple);
9383   JIM_REGISTER_API(Nvp_name2value_obj);
9384   JIM_REGISTER_API(Nvp_name2value_nocase);
9385   JIM_REGISTER_API(Nvp_name2value_obj_nocase);
9386
9387   JIM_REGISTER_API(Nvp_value2name);
9388   JIM_REGISTER_API(Nvp_value2name_simple);
9389   JIM_REGISTER_API(Nvp_value2name_obj);
9390
9391   JIM_REGISTER_API(GetOpt_Setup);
9392   JIM_REGISTER_API(GetOpt_Debug);
9393   JIM_REGISTER_API(GetOpt_Obj);
9394   JIM_REGISTER_API(GetOpt_String);
9395   JIM_REGISTER_API(GetOpt_Double);
9396   JIM_REGISTER_API(GetOpt_Wide);
9397   JIM_REGISTER_API(GetOpt_Nvp);
9398   JIM_REGISTER_API(GetOpt_NvpUnknown);
9399   JIM_REGISTER_API(GetOpt_Enum);
9400   
9401   JIM_REGISTER_API(Debug_ArgvString);
9402   JIM_REGISTER_API(SetResult_sprintf);
9403   JIM_REGISTER_API(SetResult_NvpUnknown);
9404
9405 }
9406
9407 /* -----------------------------------------------------------------------------
9408  * Core commands utility functions
9409  * ---------------------------------------------------------------------------*/
9410 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, 
9411         const char *msg)
9412 {
9413     int i;
9414     Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9415
9416     Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9417     for (i = 0; i < argc; i++) {
9418         Jim_AppendObj(interp, objPtr, argv[i]);
9419         if (!(i+1 == argc && msg[0] == '\0'))
9420             Jim_AppendString(interp, objPtr, " ", 1);
9421     }
9422     Jim_AppendString(interp, objPtr, msg, -1);
9423     Jim_AppendString(interp, objPtr, "\"", 1);
9424     Jim_SetResult(interp, objPtr);
9425 }
9426
9427 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9428 {
9429     Jim_HashTableIterator *htiter;
9430     Jim_HashEntry *he;
9431     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9432     const char *pattern;
9433     int patternLen;
9434     
9435     pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9436     htiter = Jim_GetHashTableIterator(&interp->commands);
9437     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9438         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
9439                     strlen((const char*)he->key), 0))
9440             continue;
9441         Jim_ListAppendElement(interp, listObjPtr,
9442                 Jim_NewStringObj(interp, he->key, -1));
9443     }
9444     Jim_FreeHashTableIterator(htiter);
9445     return listObjPtr;
9446 }
9447
9448 #define JIM_VARLIST_GLOBALS 0
9449 #define JIM_VARLIST_LOCALS 1
9450 #define JIM_VARLIST_VARS 2
9451
9452 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9453         int mode)
9454 {
9455     Jim_HashTableIterator *htiter;
9456     Jim_HashEntry *he;
9457     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9458     const char *pattern;
9459     int patternLen;
9460     
9461     pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9462     if (mode == JIM_VARLIST_GLOBALS) {
9463         htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9464     } else {
9465         /* For [info locals], if we are at top level an emtpy list
9466          * is returned. I don't agree, but we aim at compatibility (SS) */
9467         if (mode == JIM_VARLIST_LOCALS &&
9468             interp->framePtr == interp->topFramePtr)
9469             return listObjPtr;
9470         htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9471     }
9472     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9473         Jim_Var *varPtr = (Jim_Var*) he->val;
9474         if (mode == JIM_VARLIST_LOCALS) {
9475             if (varPtr->linkFramePtr != NULL)
9476                 continue;
9477         }
9478         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
9479                     strlen((const char*)he->key), 0))
9480             continue;
9481         Jim_ListAppendElement(interp, listObjPtr,
9482                 Jim_NewStringObj(interp, he->key, -1));
9483     }
9484     Jim_FreeHashTableIterator(htiter);
9485     return listObjPtr;
9486 }
9487
9488 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9489         Jim_Obj **objPtrPtr)
9490 {
9491     Jim_CallFrame *targetCallFrame;
9492
9493     if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9494             != JIM_OK)
9495         return JIM_ERR;
9496     /* No proc call at toplevel callframe */
9497     if (targetCallFrame == interp->topFramePtr) {
9498         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9499         Jim_AppendStrings(interp, Jim_GetResult(interp),
9500                 "bad level \"",
9501                 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9502         return JIM_ERR;
9503     }
9504     *objPtrPtr = Jim_NewListObj(interp,
9505             targetCallFrame->argv,
9506             targetCallFrame->argc);
9507     return JIM_OK;
9508 }
9509
9510 /* -----------------------------------------------------------------------------
9511  * Core commands
9512  * ---------------------------------------------------------------------------*/
9513
9514 /* fake [puts] -- not the real puts, just for debugging. */
9515 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9516         Jim_Obj *const *argv)
9517 {
9518     const char *str;
9519     int len, nonewline = 0;
9520     
9521     if (argc != 2 && argc != 3) {
9522         Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9523         return JIM_ERR;
9524     }
9525     if (argc == 3) {
9526         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9527         {
9528             Jim_SetResultString(interp, "The second argument must "
9529                     "be -nonewline", -1);
9530             return JIM_OK;
9531         } else {
9532             nonewline = 1;
9533             argv++;
9534         }
9535     }
9536     str = Jim_GetString(argv[1], &len);
9537     Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9538     if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9539     return JIM_OK;
9540 }
9541
9542 /* Helper for [+] and [*] */
9543 static int Jim_AddMulHelper(Jim_Interp *interp, int argc, 
9544         Jim_Obj *const *argv, int op)
9545 {
9546     jim_wide wideValue, res;
9547     double doubleValue, doubleRes;
9548     int i;
9549
9550     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9551     
9552     for (i = 1; i < argc; i++) {
9553         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9554             goto trydouble;
9555         if (op == JIM_EXPROP_ADD)
9556             res += wideValue;
9557         else
9558             res *= wideValue;
9559     }
9560     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9561     return JIM_OK;
9562 trydouble:
9563     doubleRes = (double) res;
9564     for (;i < argc; i++) {
9565         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9566             return JIM_ERR;
9567         if (op == JIM_EXPROP_ADD)
9568             doubleRes += doubleValue;
9569         else
9570             doubleRes *= doubleValue;
9571     }
9572     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9573     return JIM_OK;
9574 }
9575
9576 /* Helper for [-] and [/] */
9577 static int Jim_SubDivHelper(Jim_Interp *interp, int argc, 
9578         Jim_Obj *const *argv, int op)
9579 {
9580     jim_wide wideValue, res = 0;
9581     double doubleValue, doubleRes = 0;
9582     int i = 2;
9583
9584     if (argc < 2) {
9585         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9586         return JIM_ERR;
9587     } else if (argc == 2) {
9588         /* The arity = 2 case is different. For [- x] returns -x,
9589          * while [/ x] returns 1/x. */
9590         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9591             if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9592                     JIM_OK)
9593             {
9594                 return JIM_ERR;
9595             } else {
9596                 if (op == JIM_EXPROP_SUB)
9597                     doubleRes = -doubleValue;
9598                 else
9599                     doubleRes = 1.0/doubleValue;
9600                 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9601                             doubleRes));
9602                 return JIM_OK;
9603             }
9604         }
9605         if (op == JIM_EXPROP_SUB) {
9606             res = -wideValue;
9607             Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9608         } else {
9609             doubleRes = 1.0/wideValue;
9610             Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9611                         doubleRes));
9612         }
9613         return JIM_OK;
9614     } else {
9615         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9616             if (Jim_GetDouble(interp, argv[1], &doubleRes)
9617                     != JIM_OK) {
9618                 return JIM_ERR;
9619             } else {
9620                 goto trydouble;
9621             }
9622         }
9623     }
9624     for (i = 2; i < argc; i++) {
9625         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9626             doubleRes = (double) res;
9627             goto trydouble;
9628         }
9629         if (op == JIM_EXPROP_SUB)
9630             res -= wideValue;
9631         else
9632             res /= wideValue;
9633     }
9634     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9635     return JIM_OK;
9636 trydouble:
9637     for (;i < argc; i++) {
9638         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9639             return JIM_ERR;
9640         if (op == JIM_EXPROP_SUB)
9641             doubleRes -= doubleValue;
9642         else
9643             doubleRes /= doubleValue;
9644     }
9645     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9646     return JIM_OK;
9647 }
9648
9649
9650 /* [+] */
9651 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9652         Jim_Obj *const *argv)
9653 {
9654     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9655 }
9656
9657 /* [*] */
9658 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9659         Jim_Obj *const *argv)
9660 {
9661     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9662 }
9663
9664 /* [-] */
9665 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9666         Jim_Obj *const *argv)
9667 {
9668     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9669 }
9670
9671 /* [/] */
9672 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9673         Jim_Obj *const *argv)
9674 {
9675     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9676 }
9677
9678 /* [set] */
9679 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9680         Jim_Obj *const *argv)
9681 {
9682     if (argc != 2 && argc != 3) {
9683         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9684         return JIM_ERR;
9685     }
9686     if (argc == 2) {
9687         Jim_Obj *objPtr;
9688         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9689         if (!objPtr)
9690             return JIM_ERR;
9691         Jim_SetResult(interp, objPtr);
9692         return JIM_OK;
9693     }
9694     /* argc == 3 case. */
9695     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9696         return JIM_ERR;
9697     Jim_SetResult(interp, argv[2]);
9698     return JIM_OK;
9699 }
9700
9701 /* [unset] */
9702 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, 
9703         Jim_Obj *const *argv)
9704 {
9705     int i;
9706
9707     if (argc < 2) {
9708         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9709         return JIM_ERR;
9710     }
9711     for (i = 1; i < argc; i++) {
9712         if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9713             return JIM_ERR;
9714     }
9715     return JIM_OK;
9716 }
9717
9718 /* [incr] */
9719 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, 
9720         Jim_Obj *const *argv)
9721 {
9722     jim_wide wideValue, increment = 1;
9723     Jim_Obj *intObjPtr;
9724
9725     if (argc != 2 && argc != 3) {
9726         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9727         return JIM_ERR;
9728     }
9729     if (argc == 3) {
9730         if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9731             return JIM_ERR;
9732     }
9733     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9734     if (!intObjPtr) return JIM_ERR;
9735     if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9736         return JIM_ERR;
9737     if (Jim_IsShared(intObjPtr)) {
9738         intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9739         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9740             Jim_FreeNewObj(interp, intObjPtr);
9741             return JIM_ERR;
9742         }
9743     } else {
9744         Jim_SetWide(interp, intObjPtr, wideValue+increment);
9745         /* The following step is required in order to invalidate the
9746          * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9747         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9748             return JIM_ERR;
9749         }
9750     }
9751     Jim_SetResult(interp, intObjPtr);
9752     return JIM_OK;
9753 }
9754
9755 /* [while] */
9756 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, 
9757         Jim_Obj *const *argv)
9758 {
9759     if (argc != 3) {
9760         Jim_WrongNumArgs(interp, 1, argv, "condition body");
9761         return JIM_ERR;
9762     }
9763     /* Try to run a specialized version of while if the expression
9764      * is in one of the following forms:
9765      *
9766      *   $a < CONST, $a < $b
9767      *   $a <= CONST, $a <= $b
9768      *   $a > CONST, $a > $b
9769      *   $a >= CONST, $a >= $b
9770      *   $a != CONST, $a != $b
9771      *   $a == CONST, $a == $b
9772      *   $a
9773      *   !$a
9774      *   CONST
9775      */
9776
9777 #ifdef JIM_OPTIMIZATION
9778     {
9779         ExprByteCode *expr;
9780         Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9781         int exprLen, retval;
9782
9783         /* STEP 1 -- Check if there are the conditions to run the specialized
9784          * version of while */
9785         
9786         if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9787         if (expr->len <= 0 || expr->len > 3) goto noopt;
9788         switch(expr->len) {
9789         case 1:
9790             if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9791                 expr->opcode[0] != JIM_EXPROP_NUMBER)
9792                 goto noopt;
9793             break;
9794         case 2:
9795             if (expr->opcode[1] != JIM_EXPROP_NOT ||
9796                 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9797                 goto noopt;
9798             break;
9799         case 3:
9800             if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9801                 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9802                  expr->opcode[1] != JIM_EXPROP_VARIABLE))
9803                 goto noopt;
9804             switch(expr->opcode[2]) {
9805             case JIM_EXPROP_LT:
9806             case JIM_EXPROP_LTE:
9807             case JIM_EXPROP_GT:
9808             case JIM_EXPROP_GTE:
9809             case JIM_EXPROP_NUMEQ:
9810             case JIM_EXPROP_NUMNE:
9811                 /* nothing to do */
9812                 break;
9813             default:
9814                 goto noopt;
9815             }
9816             break;
9817         default:
9818             Jim_Panic(interp,
9819                 "Unexpected default reached in Jim_WhileCoreCommand()");
9820             break;
9821         }
9822
9823         /* STEP 2 -- conditions meet. Initialization. Take different
9824          * branches for different expression lengths. */
9825         exprLen = expr->len;
9826
9827         if (exprLen == 1) {
9828             jim_wide wideValue;
9829
9830             if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9831                 varAObjPtr = expr->obj[0];
9832                 Jim_IncrRefCount(varAObjPtr);
9833             } else {
9834                 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9835                     goto noopt;
9836             }
9837             while (1) {
9838                 if (varAObjPtr) {
9839                     if (!(objPtr =
9840                                Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9841                         Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9842                     {
9843                         Jim_DecrRefCount(interp, varAObjPtr);
9844                         goto noopt;
9845                     }
9846                 }
9847                 if (!wideValue) break;
9848                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9849                     switch(retval) {
9850                     case JIM_BREAK:
9851                         if (varAObjPtr)
9852                             Jim_DecrRefCount(interp, varAObjPtr);
9853                         goto out;
9854                         break;
9855                     case JIM_CONTINUE:
9856                         continue;
9857                         break;
9858                     default:
9859                         if (varAObjPtr)
9860                             Jim_DecrRefCount(interp, varAObjPtr);
9861                         return retval;
9862                     }
9863                 }
9864             }
9865             if (varAObjPtr)
9866                 Jim_DecrRefCount(interp, varAObjPtr);
9867         } else if (exprLen == 3) {
9868             jim_wide wideValueA, wideValueB, cmpRes = 0;
9869             int cmpType = expr->opcode[2];
9870
9871             varAObjPtr = expr->obj[0];
9872             Jim_IncrRefCount(varAObjPtr);
9873             if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9874                 varBObjPtr = expr->obj[1];
9875                 Jim_IncrRefCount(varBObjPtr);
9876             } else {
9877                 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9878                     goto noopt;
9879             }
9880             while (1) {
9881                 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9882                     Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9883                 {
9884                     Jim_DecrRefCount(interp, varAObjPtr);
9885                     if (varBObjPtr)
9886                         Jim_DecrRefCount(interp, varBObjPtr);
9887                     goto noopt;
9888                 }
9889                 if (varBObjPtr) {
9890                     if (!(objPtr =
9891                                Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9892                         Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9893                     {
9894                         Jim_DecrRefCount(interp, varAObjPtr);
9895                         if (varBObjPtr)
9896                             Jim_DecrRefCount(interp, varBObjPtr);
9897                         goto noopt;
9898                     }
9899                 }
9900                 switch(cmpType) {
9901                 case JIM_EXPROP_LT:
9902                     cmpRes = wideValueA < wideValueB; break;
9903                 case JIM_EXPROP_LTE:
9904                     cmpRes = wideValueA <= wideValueB; break;
9905                 case JIM_EXPROP_GT:
9906                     cmpRes = wideValueA > wideValueB; break;
9907                 case JIM_EXPROP_GTE:
9908                     cmpRes = wideValueA >= wideValueB; break;
9909                 case JIM_EXPROP_NUMEQ:
9910                     cmpRes = wideValueA == wideValueB; break;
9911                 case JIM_EXPROP_NUMNE:
9912                     cmpRes = wideValueA != wideValueB; break;
9913                 }
9914                 if (!cmpRes) break;
9915                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9916                     switch(retval) {
9917                     case JIM_BREAK:
9918                         Jim_DecrRefCount(interp, varAObjPtr);
9919                         if (varBObjPtr)
9920                             Jim_DecrRefCount(interp, varBObjPtr);
9921                         goto out;
9922                         break;
9923                     case JIM_CONTINUE:
9924                         continue;
9925                         break;
9926                     default:
9927                         Jim_DecrRefCount(interp, varAObjPtr);
9928                         if (varBObjPtr)
9929                             Jim_DecrRefCount(interp, varBObjPtr);
9930                         return retval;
9931                     }
9932                 }
9933             }
9934             Jim_DecrRefCount(interp, varAObjPtr);
9935             if (varBObjPtr)
9936                 Jim_DecrRefCount(interp, varBObjPtr);
9937         } else {
9938             /* TODO: case for len == 2 */
9939             goto noopt;
9940         }
9941         Jim_SetEmptyResult(interp);
9942         return JIM_OK;
9943     }
9944 noopt:
9945 #endif
9946
9947     /* The general purpose implementation of while starts here */
9948     while (1) {
9949         int boolean, retval;
9950
9951         if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9952                         &boolean)) != JIM_OK)
9953             return retval;
9954         if (!boolean) break;
9955         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9956             switch(retval) {
9957             case JIM_BREAK:
9958                 goto out;
9959                 break;
9960             case JIM_CONTINUE:
9961                 continue;
9962                 break;
9963             default:
9964                 return retval;
9965             }
9966         }
9967     }
9968 out:
9969     Jim_SetEmptyResult(interp);
9970     return JIM_OK;
9971 }
9972
9973 /* [for] */
9974 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, 
9975         Jim_Obj *const *argv)
9976 {
9977     int retval;
9978
9979     if (argc != 5) {
9980         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9981         return JIM_ERR;
9982     }
9983     /* Check if the for is on the form:
9984      *      for {set i CONST} {$i < CONST} {incr i}
9985      *      for {set i CONST} {$i < $j} {incr i}
9986      *      for {set i CONST} {$i <= CONST} {incr i}
9987      *      for {set i CONST} {$i <= $j} {incr i}
9988      * XXX: NOTE: if variable traces are implemented, this optimization
9989      * need to be modified to check for the proc epoch at every variable
9990      * update. */
9991 #ifdef JIM_OPTIMIZATION
9992     {
9993         ScriptObj *initScript, *incrScript;
9994         ExprByteCode *expr;
9995         jim_wide start, stop, currentVal;
9996         unsigned jim_wide procEpoch = interp->procEpoch;
9997         Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9998         int cmpType;
9999         struct Jim_Cmd *cmdPtr;
10000
10001         /* Do it only if there aren't shared arguments */
10002         if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
10003             goto evalstart;
10004         initScript = Jim_GetScript(interp, argv[1]);
10005         expr = Jim_GetExpression(interp, argv[2]);
10006         incrScript = Jim_GetScript(interp, argv[3]);
10007
10008         /* Ensure proper lengths to start */
10009         if (initScript->len != 6) goto evalstart;
10010         if (incrScript->len != 4) goto evalstart;
10011         if (expr->len != 3) goto evalstart;
10012         /* Ensure proper token types. */
10013         if (initScript->token[2].type != JIM_TT_ESC ||
10014             initScript->token[4].type != JIM_TT_ESC ||
10015             incrScript->token[2].type != JIM_TT_ESC ||
10016             expr->opcode[0] != JIM_EXPROP_VARIABLE ||
10017             (expr->opcode[1] != JIM_EXPROP_NUMBER &&
10018              expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
10019             (expr->opcode[2] != JIM_EXPROP_LT &&
10020              expr->opcode[2] != JIM_EXPROP_LTE))
10021             goto evalstart;
10022         cmpType = expr->opcode[2];
10023         /* Initialization command must be [set] */
10024         cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
10025         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
10026             goto evalstart;
10027         /* Update command must be incr */
10028         cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
10029         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
10030             goto evalstart;
10031         /* set, incr, expression must be about the same variable */
10032         if (!Jim_StringEqObj(initScript->token[2].objPtr,
10033                             incrScript->token[2].objPtr, 0))
10034             goto evalstart;
10035         if (!Jim_StringEqObj(initScript->token[2].objPtr,
10036                             expr->obj[0], 0))
10037             goto evalstart;
10038         /* Check that the initialization and comparison are valid integers */
10039         if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
10040             goto evalstart;
10041         if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
10042             Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
10043         {
10044             goto evalstart;
10045         }
10046
10047         /* Initialization */
10048         varNamePtr = expr->obj[0];
10049         if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
10050             stopVarNamePtr = expr->obj[1];
10051             Jim_IncrRefCount(stopVarNamePtr);
10052         }
10053         Jim_IncrRefCount(varNamePtr);
10054
10055         /* --- OPTIMIZED FOR --- */
10056         /* Start to loop */
10057         objPtr = Jim_NewIntObj(interp, start);
10058         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
10059             Jim_DecrRefCount(interp, varNamePtr);
10060             if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10061             Jim_FreeNewObj(interp, objPtr);
10062             goto evalstart;
10063         }
10064         while (1) {
10065             /* === Check condition === */
10066             /* Common code: */
10067             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
10068             if (objPtr == NULL ||
10069                 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
10070             {
10071                 Jim_DecrRefCount(interp, varNamePtr);
10072                 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10073                 goto testcond;
10074             }
10075             /* Immediate or Variable? get the 'stop' value if the latter. */
10076             if (stopVarNamePtr) {
10077                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
10078                 if (objPtr == NULL ||
10079                     Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
10080                 {
10081                     Jim_DecrRefCount(interp, varNamePtr);
10082                     Jim_DecrRefCount(interp, stopVarNamePtr);
10083                     goto testcond;
10084                 }
10085             }
10086             if (cmpType == JIM_EXPROP_LT) {
10087                 if (currentVal >= stop) break;
10088             } else {
10089                 if (currentVal > stop) break;
10090             }
10091             /* Eval body */
10092             if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10093                 switch(retval) {
10094                 case JIM_BREAK:
10095                     if (stopVarNamePtr)
10096                         Jim_DecrRefCount(interp, stopVarNamePtr);
10097                     Jim_DecrRefCount(interp, varNamePtr);
10098                     goto out;
10099                 case JIM_CONTINUE:
10100                     /* nothing to do */
10101                     break;
10102                 default:
10103                     if (stopVarNamePtr)
10104                         Jim_DecrRefCount(interp, stopVarNamePtr);
10105                     Jim_DecrRefCount(interp, varNamePtr);
10106                     return retval;
10107                 }
10108             }
10109             /* If there was a change in procedures/command continue
10110              * with the usual [for] command implementation */
10111             if (procEpoch != interp->procEpoch) {
10112                 if (stopVarNamePtr)
10113                     Jim_DecrRefCount(interp, stopVarNamePtr);
10114                 Jim_DecrRefCount(interp, varNamePtr);
10115                 goto evalnext;
10116             }
10117             /* Increment */
10118             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
10119             if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
10120                 objPtr->internalRep.wideValue ++;
10121                 Jim_InvalidateStringRep(objPtr);
10122             } else {
10123                 Jim_Obj *auxObjPtr;
10124
10125                 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
10126                     if (stopVarNamePtr)
10127                         Jim_DecrRefCount(interp, stopVarNamePtr);
10128                     Jim_DecrRefCount(interp, varNamePtr);
10129                     goto evalnext;
10130                 }
10131                 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
10132                 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
10133                     if (stopVarNamePtr)
10134                         Jim_DecrRefCount(interp, stopVarNamePtr);
10135                     Jim_DecrRefCount(interp, varNamePtr);
10136                     Jim_FreeNewObj(interp, auxObjPtr);
10137                     goto evalnext;
10138                 }
10139             }
10140         }
10141         if (stopVarNamePtr)
10142             Jim_DecrRefCount(interp, stopVarNamePtr);
10143         Jim_DecrRefCount(interp, varNamePtr);
10144         Jim_SetEmptyResult(interp);
10145         return JIM_OK;
10146     }
10147 #endif
10148 evalstart:
10149     /* Eval start */
10150     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10151         return retval;
10152     while (1) {
10153         int boolean;
10154 testcond:
10155         /* Test the condition */
10156         if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
10157                 != JIM_OK)
10158             return retval;
10159         if (!boolean) break;
10160         /* Eval body */
10161         if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10162             switch(retval) {
10163             case JIM_BREAK:
10164                 goto out;
10165                 break;
10166             case JIM_CONTINUE:
10167                 /* Nothing to do */
10168                 break;
10169             default:
10170                 return retval;
10171             }
10172         }
10173 evalnext:
10174         /* Eval next */
10175         if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
10176             switch(retval) {
10177             case JIM_BREAK:
10178                 goto out;
10179                 break;
10180             case JIM_CONTINUE:
10181                 continue;
10182                 break;
10183             default:
10184                 return retval;
10185             }
10186         }
10187     }
10188 out:
10189     Jim_SetEmptyResult(interp);
10190     return JIM_OK;
10191 }
10192
10193 /* foreach + lmap implementation. */
10194 static int JimForeachMapHelper(Jim_Interp *interp, int argc, 
10195         Jim_Obj *const *argv, int doMap)
10196 {
10197     int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
10198     int nbrOfLoops = 0;
10199     Jim_Obj *emptyStr, *script, *mapRes = NULL;
10200
10201     if (argc < 4 || argc % 2 != 0) {
10202         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
10203         return JIM_ERR;
10204     }
10205     if (doMap) {
10206         mapRes = Jim_NewListObj(interp, NULL, 0);
10207         Jim_IncrRefCount(mapRes);
10208     }
10209     emptyStr = Jim_NewEmptyStringObj(interp);
10210     Jim_IncrRefCount(emptyStr);
10211     script = argv[argc-1];            /* Last argument is a script */
10212     nbrOfLists = (argc - 1 - 1) / 2;  /* argc - 'foreach' - script */
10213     listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
10214     listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
10215     /* Initialize iterators and remember max nbr elements each list */
10216     memset(listsIdx, 0, nbrOfLists * sizeof(int));
10217     /* Remember lengths of all lists and calculate how much rounds to loop */
10218     for (i=0; i < nbrOfLists*2; i += 2) {
10219         div_t cnt;
10220         int count;
10221         Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
10222         Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
10223         if (listsEnd[i] == 0) {
10224             Jim_SetResultString(interp, "foreach varlist is empty", -1);
10225             goto err;
10226         }
10227         cnt = div(listsEnd[i+1], listsEnd[i]);
10228         count = cnt.quot + (cnt.rem ? 1 : 0);
10229         if (count > nbrOfLoops)
10230             nbrOfLoops = count;
10231     }
10232     for (; nbrOfLoops-- > 0; ) {
10233         for (i=0; i < nbrOfLists; ++i) {
10234             int varIdx = 0, var = i * 2;
10235             while (varIdx < listsEnd[var]) {
10236                 Jim_Obj *varName, *ele;
10237                 int lst = i * 2 + 1;
10238                 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
10239                         != JIM_OK)
10240                         goto err;
10241                 if (listsIdx[i] < listsEnd[lst]) {
10242                     if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
10243                         != JIM_OK)
10244                         goto err;
10245                     if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
10246                         Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10247                         goto err;
10248                     }
10249                     ++listsIdx[i];  /* Remember next iterator of current list */ 
10250                 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
10251                     Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10252                     goto err;
10253                 }
10254                 ++varIdx;  /* Next variable */
10255             }
10256         }
10257         switch (result = Jim_EvalObj(interp, script)) {
10258             case JIM_OK:
10259                 if (doMap)
10260                     Jim_ListAppendElement(interp, mapRes, interp->result);
10261                 break;
10262             case JIM_CONTINUE:
10263                 break;
10264             case JIM_BREAK:
10265                 goto out;
10266                 break;
10267             default:
10268                 goto err;
10269         }
10270     }
10271 out:
10272     result = JIM_OK;
10273     if (doMap)
10274         Jim_SetResult(interp, mapRes);
10275     else
10276         Jim_SetEmptyResult(interp);
10277 err:
10278     if (doMap)
10279         Jim_DecrRefCount(interp, mapRes);
10280     Jim_DecrRefCount(interp, emptyStr);
10281     Jim_Free(listsIdx);
10282     Jim_Free(listsEnd);
10283     return result;
10284 }
10285
10286 /* [foreach] */
10287 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, 
10288         Jim_Obj *const *argv)
10289 {
10290     return JimForeachMapHelper(interp, argc, argv, 0);
10291 }
10292
10293 /* [lmap] */
10294 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, 
10295         Jim_Obj *const *argv)
10296 {
10297     return JimForeachMapHelper(interp, argc, argv, 1);
10298 }
10299
10300 /* [if] */
10301 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, 
10302         Jim_Obj *const *argv)
10303 {
10304     int boolean, retval, current = 1, falsebody = 0;
10305     if (argc >= 3) {
10306         while (1) {
10307             /* Far not enough arguments given! */
10308             if (current >= argc) goto err;
10309             if ((retval = Jim_GetBoolFromExpr(interp,
10310                         argv[current++], &boolean))
10311                     != JIM_OK)
10312                 return retval;
10313             /* There lacks something, isn't it? */
10314             if (current >= argc) goto err;
10315             if (Jim_CompareStringImmediate(interp, argv[current],
10316                         "then")) current++;
10317             /* Tsk tsk, no then-clause? */
10318             if (current >= argc) goto err;
10319             if (boolean)
10320                 return Jim_EvalObj(interp, argv[current]);
10321              /* Ok: no else-clause follows */
10322             if (++current >= argc) {
10323                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));                   
10324                 return JIM_OK;
10325             }
10326             falsebody = current++;
10327             if (Jim_CompareStringImmediate(interp, argv[falsebody],
10328                         "else")) {
10329                 /* IIICKS - else-clause isn't last cmd? */
10330                 if (current != argc-1) goto err;
10331                 return Jim_EvalObj(interp, argv[current]);
10332             } else if (Jim_CompareStringImmediate(interp,
10333                         argv[falsebody], "elseif"))
10334                 /* Ok: elseif follows meaning all the stuff
10335                  * again (how boring...) */
10336                 continue;
10337             /* OOPS - else-clause is not last cmd?*/
10338             else if (falsebody != argc-1)
10339                 goto err;
10340             return Jim_EvalObj(interp, argv[falsebody]);
10341         }
10342         return JIM_OK;
10343     }
10344 err:
10345     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10346     return JIM_ERR;
10347 }
10348
10349 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10350
10351 /* [switch] */
10352 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, 
10353         Jim_Obj *const *argv)
10354 {
10355     int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10356     Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10357     Jim_Obj *script = 0;
10358     if (argc < 3) goto wrongnumargs;
10359     for (opt=1; opt < argc; ++opt) {
10360         const char *option = Jim_GetString(argv[opt], 0);
10361         if (*option != '-') break;
10362         else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10363         else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10364         else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10365         else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10366         else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10367             if ((argc - opt) < 2) goto wrongnumargs;
10368             command = argv[++opt]; 
10369         } else {
10370             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10371             Jim_AppendStrings(interp, Jim_GetResult(interp),
10372                 "bad option \"", option, "\": must be -exact, -glob, "
10373                 "-regexp, -command procname or --", 0);
10374             goto err;            
10375         }
10376         if ((argc - opt) < 2) goto wrongnumargs;
10377     }
10378     strObj = argv[opt++];
10379     patCount = argc - opt;
10380     if (patCount == 1) {
10381         Jim_Obj **vector;
10382         JimListGetElements(interp, argv[opt], &patCount, &vector);
10383         caseList = vector;
10384     } else
10385         caseList = &argv[opt];
10386     if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10387     for (i=0; script == 0 && i < patCount; i += 2) {
10388         Jim_Obj *patObj = caseList[i];
10389         if (!Jim_CompareStringImmediate(interp, patObj, "default")
10390             || i < (patCount-2)) {
10391             switch (matchOpt) {
10392                 case SWITCH_EXACT:
10393                     if (Jim_StringEqObj(strObj, patObj, 0))
10394                         script = caseList[i+1];
10395                     break;
10396                 case SWITCH_GLOB:
10397                     if (Jim_StringMatchObj(patObj, strObj, 0))
10398                         script = caseList[i+1];
10399                     break;
10400                 case SWITCH_RE:
10401                     command = Jim_NewStringObj(interp, "regexp", -1);
10402                     /* Fall thru intentionally */
10403                 case SWITCH_CMD: {
10404                     Jim_Obj *parms[] = {command, patObj, strObj};
10405                     int rc = Jim_EvalObjVector(interp, 3, parms);
10406                     long matching;
10407                     /* After the execution of a command we need to
10408                      * make sure to reconvert the object into a list
10409                      * again. Only for the single-list style [switch]. */
10410                     if (argc-opt == 1) {
10411                         Jim_Obj **vector;
10412                         JimListGetElements(interp, argv[opt], &patCount,
10413                                 &vector);
10414                         caseList = vector;
10415                     }
10416                     /* command is here already decref'd */
10417                     if (rc != JIM_OK) {
10418                         retcode = rc;
10419                         goto err;
10420                     }
10421                     rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10422                     if (rc != JIM_OK) {
10423                         retcode = rc;
10424                         goto err;
10425                     }
10426                     if (matching)
10427                         script = caseList[i+1];
10428                     break;
10429                 }
10430                 default:
10431                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10432                     Jim_AppendStrings(interp, Jim_GetResult(interp),
10433                         "internal error: no such option implemented", 0);
10434                     goto err;
10435             }
10436         } else {
10437           script = caseList[i+1];
10438         }
10439     }
10440     for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10441         i += 2)
10442         script = caseList[i+1];
10443     if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10444         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10445         Jim_AppendStrings(interp, Jim_GetResult(interp),
10446             "no body specified for pattern \"",
10447             Jim_GetString(caseList[i-2], 0), "\"", 0);
10448         goto err;
10449     }
10450     retcode = JIM_OK;
10451     Jim_SetEmptyResult(interp);
10452     if (script != 0)
10453         retcode = Jim_EvalObj(interp, script);
10454     return retcode;
10455 wrongnumargs:
10456     Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10457         "pattern body ... ?default body?   or   "
10458         "{pattern body ?pattern body ...?}");
10459 err:
10460     return retcode;        
10461 }
10462
10463 /* [list] */
10464 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, 
10465         Jim_Obj *const *argv)
10466 {
10467     Jim_Obj *listObjPtr;
10468
10469     listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10470     Jim_SetResult(interp, listObjPtr);
10471     return JIM_OK;
10472 }
10473
10474 /* [lindex] */
10475 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, 
10476         Jim_Obj *const *argv)
10477 {
10478     Jim_Obj *objPtr, *listObjPtr;
10479     int i;
10480     int index;
10481
10482     if (argc < 3) {
10483         Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10484         return JIM_ERR;
10485     }
10486     objPtr = argv[1];
10487     Jim_IncrRefCount(objPtr);
10488     for (i = 2; i < argc; i++) {
10489         listObjPtr = objPtr;
10490         if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10491             Jim_DecrRefCount(interp, listObjPtr);
10492             return JIM_ERR;
10493         }
10494         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10495                     JIM_NONE) != JIM_OK) {
10496             /* Returns an empty object if the index
10497              * is out of range. */
10498             Jim_DecrRefCount(interp, listObjPtr);
10499             Jim_SetEmptyResult(interp);
10500             return JIM_OK;
10501         }
10502         Jim_IncrRefCount(objPtr);
10503         Jim_DecrRefCount(interp, listObjPtr);
10504     }
10505     Jim_SetResult(interp, objPtr);
10506     Jim_DecrRefCount(interp, objPtr);
10507     return JIM_OK;
10508 }
10509
10510 /* [llength] */
10511 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, 
10512         Jim_Obj *const *argv)
10513 {
10514     int len;
10515
10516     if (argc != 2) {
10517         Jim_WrongNumArgs(interp, 1, argv, "list");
10518         return JIM_ERR;
10519     }
10520     Jim_ListLength(interp, argv[1], &len);
10521     Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10522     return JIM_OK;
10523 }
10524
10525 /* [lappend] */
10526 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, 
10527         Jim_Obj *const *argv)
10528 {
10529     Jim_Obj *listObjPtr;
10530     int shared, i;
10531
10532     if (argc < 2) {
10533         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10534         return JIM_ERR;
10535     }
10536     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10537     if (!listObjPtr) {
10538         /* Create the list if it does not exists */
10539         listObjPtr = Jim_NewListObj(interp, NULL, 0);
10540         if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10541             Jim_FreeNewObj(interp, listObjPtr);
10542             return JIM_ERR;
10543         }
10544     }
10545     shared = Jim_IsShared(listObjPtr);
10546     if (shared)
10547         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10548     for (i = 2; i < argc; i++)
10549         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10550     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10551         if (shared)
10552             Jim_FreeNewObj(interp, listObjPtr);
10553         return JIM_ERR;
10554     }
10555     Jim_SetResult(interp, listObjPtr);
10556     return JIM_OK;
10557 }
10558
10559 /* [linsert] */
10560 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, 
10561         Jim_Obj *const *argv)
10562 {
10563     int index, len;
10564     Jim_Obj *listPtr;
10565
10566     if (argc < 4) {
10567         Jim_WrongNumArgs(interp, 1, argv, "list index element "
10568             "?element ...?");
10569         return JIM_ERR;
10570     }
10571     listPtr = argv[1];
10572     if (Jim_IsShared(listPtr))
10573         listPtr = Jim_DuplicateObj(interp, listPtr);
10574     if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10575         goto err;
10576     Jim_ListLength(interp, listPtr, &len);
10577     if (index >= len)
10578         index = len;
10579     else if (index < 0)
10580         index = len + index + 1;
10581     Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10582     Jim_SetResult(interp, listPtr);
10583     return JIM_OK;
10584 err:
10585     if (listPtr != argv[1]) {
10586         Jim_FreeNewObj(interp, listPtr);
10587     }
10588     return JIM_ERR;
10589 }
10590
10591 /* [lset] */
10592 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, 
10593         Jim_Obj *const *argv)
10594 {
10595     if (argc < 3) {
10596         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10597         return JIM_ERR;
10598     } else if (argc == 3) {
10599         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10600             return JIM_ERR;
10601         Jim_SetResult(interp, argv[2]);
10602         return JIM_OK;
10603     }
10604     if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10605             == JIM_ERR) return JIM_ERR;
10606     return JIM_OK;
10607 }
10608
10609 /* [lsort] */
10610 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10611 {
10612     const char *options[] = {
10613         "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10614     };
10615     enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10616     Jim_Obj *resObj;
10617     int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10618     int decreasing = 0;
10619
10620     if (argc < 2) {
10621         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10622         return JIM_ERR;
10623     }
10624     for (i = 1; i < (argc-1); i++) {
10625         int option;
10626
10627         if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10628                 != JIM_OK)
10629             return JIM_ERR;
10630         switch(option) {
10631         case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10632         case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10633         case OPT_INCREASING: decreasing = 0; break;
10634         case OPT_DECREASING: decreasing = 1; break;
10635         }
10636     }
10637     if (decreasing) {
10638         switch(lsortType) {
10639         case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10640         case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10641         }
10642     }
10643     resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10644     ListSortElements(interp, resObj, lsortType);
10645     Jim_SetResult(interp, resObj);
10646     return JIM_OK;
10647 }
10648
10649 /* [append] */
10650 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, 
10651         Jim_Obj *const *argv)
10652 {
10653     Jim_Obj *stringObjPtr;
10654     int shared, i;
10655
10656     if (argc < 2) {
10657         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10658         return JIM_ERR;
10659     }
10660     if (argc == 2) {
10661         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10662         if (!stringObjPtr) return JIM_ERR;
10663     } else {
10664         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10665         if (!stringObjPtr) {
10666             /* Create the string if it does not exists */
10667             stringObjPtr = Jim_NewEmptyStringObj(interp);
10668             if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10669                     != JIM_OK) {
10670                 Jim_FreeNewObj(interp, stringObjPtr);
10671                 return JIM_ERR;
10672             }
10673         }
10674     }
10675     shared = Jim_IsShared(stringObjPtr);
10676     if (shared)
10677         stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10678     for (i = 2; i < argc; i++)
10679         Jim_AppendObj(interp, stringObjPtr, argv[i]);
10680     if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10681         if (shared)
10682             Jim_FreeNewObj(interp, stringObjPtr);
10683         return JIM_ERR;
10684     }
10685     Jim_SetResult(interp, stringObjPtr);
10686     return JIM_OK;
10687 }
10688
10689 /* [debug] */
10690 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, 
10691         Jim_Obj *const *argv)
10692 {
10693     const char *options[] = {
10694         "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10695         "exprbc",
10696         NULL
10697     };
10698     enum {
10699         OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10700         OPT_EXPRLEN, OPT_EXPRBC
10701     };
10702     int option;
10703
10704     if (argc < 2) {
10705         Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10706         return JIM_ERR;
10707     }
10708     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10709                 JIM_ERRMSG) != JIM_OK)
10710         return JIM_ERR;
10711     if (option == OPT_REFCOUNT) {
10712         if (argc != 3) {
10713             Jim_WrongNumArgs(interp, 2, argv, "object");
10714             return JIM_ERR;
10715         }
10716         Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10717         return JIM_OK;
10718     } else if (option == OPT_OBJCOUNT) {
10719         int freeobj = 0, liveobj = 0;
10720         char buf[256];
10721         Jim_Obj *objPtr;
10722
10723         if (argc != 2) {
10724             Jim_WrongNumArgs(interp, 2, argv, "");
10725             return JIM_ERR;
10726         }
10727         /* Count the number of free objects. */
10728         objPtr = interp->freeList;
10729         while (objPtr) {
10730             freeobj++;
10731             objPtr = objPtr->nextObjPtr;
10732         }
10733         /* Count the number of live objects. */
10734         objPtr = interp->liveList;
10735         while (objPtr) {
10736             liveobj++;
10737             objPtr = objPtr->nextObjPtr;
10738         }
10739         /* Set the result string and return. */
10740         sprintf(buf, "free %d used %d", freeobj, liveobj);
10741         Jim_SetResultString(interp, buf, -1);
10742         return JIM_OK;
10743     } else if (option == OPT_OBJECTS) {
10744         Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10745         /* Count the number of live objects. */
10746         objPtr = interp->liveList;
10747         listObjPtr = Jim_NewListObj(interp, NULL, 0);
10748         while (objPtr) {
10749             char buf[128];
10750             const char *type = objPtr->typePtr ?
10751                 objPtr->typePtr->name : "";
10752             subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10753             sprintf(buf, "%p", objPtr);
10754             Jim_ListAppendElement(interp, subListObjPtr,
10755                 Jim_NewStringObj(interp, buf, -1));
10756             Jim_ListAppendElement(interp, subListObjPtr,
10757                 Jim_NewStringObj(interp, type, -1));
10758             Jim_ListAppendElement(interp, subListObjPtr,
10759                 Jim_NewIntObj(interp, objPtr->refCount));
10760             Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10761             Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10762             objPtr = objPtr->nextObjPtr;
10763         }
10764         Jim_SetResult(interp, listObjPtr);
10765         return JIM_OK;
10766     } else if (option == OPT_INVSTR) {
10767         Jim_Obj *objPtr;
10768
10769         if (argc != 3) {
10770             Jim_WrongNumArgs(interp, 2, argv, "object");
10771             return JIM_ERR;
10772         }
10773         objPtr = argv[2];
10774         if (objPtr->typePtr != NULL)
10775             Jim_InvalidateStringRep(objPtr);
10776         Jim_SetEmptyResult(interp);
10777         return JIM_OK;
10778     } else if (option == OPT_SCRIPTLEN) {
10779         ScriptObj *script;
10780         if (argc != 3) {
10781             Jim_WrongNumArgs(interp, 2, argv, "script");
10782             return JIM_ERR;
10783         }
10784         script = Jim_GetScript(interp, argv[2]);
10785         Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10786         return JIM_OK;
10787     } else if (option == OPT_EXPRLEN) {
10788         ExprByteCode *expr;
10789         if (argc != 3) {
10790             Jim_WrongNumArgs(interp, 2, argv, "expression");
10791             return JIM_ERR;
10792         }
10793         expr = Jim_GetExpression(interp, argv[2]);
10794         if (expr == NULL)
10795             return JIM_ERR;
10796         Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10797         return JIM_OK;
10798     } else if (option == OPT_EXPRBC) {
10799         Jim_Obj *objPtr;
10800         ExprByteCode *expr;
10801         int i;
10802
10803         if (argc != 3) {
10804             Jim_WrongNumArgs(interp, 2, argv, "expression");
10805             return JIM_ERR;
10806         }
10807         expr = Jim_GetExpression(interp, argv[2]);
10808         if (expr == NULL)
10809             return JIM_ERR;
10810         objPtr = Jim_NewListObj(interp, NULL, 0);
10811         for (i = 0; i < expr->len; i++) {
10812             const char *type;
10813             Jim_ExprOperator *op;
10814
10815             switch(expr->opcode[i]) {
10816             case JIM_EXPROP_NUMBER: type = "number"; break;
10817             case JIM_EXPROP_COMMAND: type = "command"; break;
10818             case JIM_EXPROP_VARIABLE: type = "variable"; break;
10819             case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10820             case JIM_EXPROP_SUBST: type = "subst"; break;
10821             case JIM_EXPROP_STRING: type = "string"; break;
10822             default:
10823                 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10824                 if (op == NULL) {
10825                     type = "private";
10826                 } else {
10827                     type = "operator";
10828                 }
10829                 break;
10830             }
10831             Jim_ListAppendElement(interp, objPtr,
10832                     Jim_NewStringObj(interp, type, -1));
10833             Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10834         }
10835         Jim_SetResult(interp, objPtr);
10836         return JIM_OK;
10837     } else {
10838         Jim_SetResultString(interp,
10839             "bad option. Valid options are refcount, "
10840             "objcount, objects, invstr", -1);
10841         return JIM_ERR;
10842     }
10843     return JIM_OK; /* unreached */
10844 }
10845
10846 /* [eval] */
10847 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, 
10848         Jim_Obj *const *argv)
10849 {
10850     if (argc == 2) {
10851         return Jim_EvalObj(interp, argv[1]);
10852     } else if (argc > 2) {
10853         Jim_Obj *objPtr;
10854         int retcode;
10855
10856         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10857         Jim_IncrRefCount(objPtr);
10858         retcode = Jim_EvalObj(interp, objPtr);
10859         Jim_DecrRefCount(interp, objPtr);
10860         return retcode;
10861     } else {
10862         Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10863         return JIM_ERR;
10864     }
10865 }
10866
10867 /* [uplevel] */
10868 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, 
10869         Jim_Obj *const *argv)
10870 {
10871     if (argc >= 2) {
10872         int retcode, newLevel, oldLevel;
10873         Jim_CallFrame *savedCallFrame, *targetCallFrame;
10874         Jim_Obj *objPtr;
10875         const char *str;
10876
10877         /* Save the old callframe pointer */
10878         savedCallFrame = interp->framePtr;
10879
10880         /* Lookup the target frame pointer */
10881         str = Jim_GetString(argv[1], NULL);
10882         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10883         {
10884             if (Jim_GetCallFrameByLevel(interp, argv[1],
10885                         &targetCallFrame,
10886                         &newLevel) != JIM_OK)
10887                 return JIM_ERR;
10888             argc--;
10889             argv++;
10890         } else {
10891             if (Jim_GetCallFrameByLevel(interp, NULL,
10892                         &targetCallFrame,
10893                         &newLevel) != JIM_OK)
10894                 return JIM_ERR;
10895         }
10896         if (argc < 2) {
10897             argc++;
10898             argv--;
10899             Jim_WrongNumArgs(interp, 1, argv,
10900                     "?level? command ?arg ...?");
10901             return JIM_ERR;
10902         }
10903         /* Eval the code in the target callframe. */
10904         interp->framePtr = targetCallFrame;
10905         oldLevel = interp->numLevels;
10906         interp->numLevels = newLevel;
10907         if (argc == 2) {
10908             retcode = Jim_EvalObj(interp, argv[1]);
10909         } else {
10910             objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10911             Jim_IncrRefCount(objPtr);
10912             retcode = Jim_EvalObj(interp, objPtr);
10913             Jim_DecrRefCount(interp, objPtr);
10914         }
10915         interp->numLevels = oldLevel;
10916         interp->framePtr = savedCallFrame;
10917         return retcode;
10918     } else {
10919         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10920         return JIM_ERR;
10921     }
10922 }
10923
10924 /* [expr] */
10925 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, 
10926         Jim_Obj *const *argv)
10927 {
10928     Jim_Obj *exprResultPtr;
10929     int retcode;
10930
10931     if (argc == 2) {
10932         retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10933     } else if (argc > 2) {
10934         Jim_Obj *objPtr;
10935
10936         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10937         Jim_IncrRefCount(objPtr);
10938         retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10939         Jim_DecrRefCount(interp, objPtr);
10940     } else {
10941         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10942         return JIM_ERR;
10943     }
10944     if (retcode != JIM_OK) return retcode;
10945     Jim_SetResult(interp, exprResultPtr);
10946     Jim_DecrRefCount(interp, exprResultPtr);
10947     return JIM_OK;
10948 }
10949
10950 /* [break] */
10951 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, 
10952         Jim_Obj *const *argv)
10953 {
10954     if (argc != 1) {
10955         Jim_WrongNumArgs(interp, 1, argv, "");
10956         return JIM_ERR;
10957     }
10958     return JIM_BREAK;
10959 }
10960
10961 /* [continue] */
10962 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10963         Jim_Obj *const *argv)
10964 {
10965     if (argc != 1) {
10966         Jim_WrongNumArgs(interp, 1, argv, "");
10967         return JIM_ERR;
10968     }
10969     return JIM_CONTINUE;
10970 }
10971
10972 /* [return] */
10973 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, 
10974         Jim_Obj *const *argv)
10975 {
10976     if (argc == 1) {
10977         return JIM_RETURN;
10978     } else if (argc == 2) {
10979         Jim_SetResult(interp, argv[1]);
10980         interp->returnCode = JIM_OK;
10981         return JIM_RETURN;
10982     } else if (argc == 3 || argc == 4) {
10983         int returnCode;
10984         if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10985             return JIM_ERR;
10986         interp->returnCode = returnCode;
10987         if (argc == 4)
10988             Jim_SetResult(interp, argv[3]);
10989         return JIM_RETURN;
10990     } else {
10991         Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10992         return JIM_ERR;
10993     }
10994     return JIM_RETURN; /* unreached */
10995 }
10996
10997 /* [tailcall] */
10998 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10999         Jim_Obj *const *argv)
11000 {
11001     Jim_Obj *objPtr;
11002
11003     objPtr = Jim_NewListObj(interp, argv+1, argc-1);
11004     Jim_SetResult(interp, objPtr);
11005     return JIM_EVAL;
11006 }
11007
11008 /* [proc] */
11009 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, 
11010         Jim_Obj *const *argv)
11011 {
11012     int argListLen;
11013     int arityMin, arityMax;
11014
11015     if (argc != 4 && argc != 5) {
11016         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
11017         return JIM_ERR;
11018     }
11019     Jim_ListLength(interp, argv[2], &argListLen);
11020     arityMin = arityMax = argListLen+1;
11021
11022     if (argListLen) {
11023         const char *str;
11024         int len;
11025         Jim_Obj *argPtr;
11026         
11027         /* Check for 'args' and adjust arityMin and arityMax if necessary */
11028         Jim_ListIndex(interp, argv[2], argListLen-1, &argPtr, JIM_NONE);
11029         str = Jim_GetString(argPtr, &len);
11030         if (len == 4 && memcmp(str, "args", 4) == 0) {
11031             arityMin--;
11032             arityMax = -1;
11033         }
11034
11035         /* Check for default arguments and reduce arityMin if necessary */
11036         while (arityMin > 1) {
11037             int len;
11038             Jim_ListIndex(interp, argv[2], arityMin - 2, &argPtr, JIM_NONE);
11039             Jim_ListLength(interp, argPtr, &len);
11040             if (len != 2) {
11041                 /* No default argument */
11042                 break;
11043             }
11044             arityMin--;
11045         }
11046     }
11047     if (argc == 4) {
11048         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11049                 argv[2], NULL, argv[3], arityMin, arityMax);
11050     } else {
11051         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11052                 argv[2], argv[3], argv[4], arityMin, arityMax);
11053     }
11054 }
11055
11056 /* [concat] */
11057 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, 
11058         Jim_Obj *const *argv)
11059 {
11060     Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
11061     return JIM_OK;
11062 }
11063
11064 /* [upvar] */
11065 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, 
11066         Jim_Obj *const *argv)
11067 {
11068     const char *str;
11069     int i;
11070     Jim_CallFrame *targetCallFrame;
11071
11072     /* Lookup the target frame pointer */
11073     str = Jim_GetString(argv[1], NULL);
11074     if (argc > 3 && 
11075         ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
11076     {
11077         if (Jim_GetCallFrameByLevel(interp, argv[1],
11078                     &targetCallFrame, NULL) != JIM_OK)
11079             return JIM_ERR;
11080         argc--;
11081         argv++;
11082     } else {
11083         if (Jim_GetCallFrameByLevel(interp, NULL,
11084                     &targetCallFrame, NULL) != JIM_OK)
11085             return JIM_ERR;
11086     }
11087     /* Check for arity */
11088     if (argc < 3 || ((argc-1)%2) != 0) {
11089         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
11090         return JIM_ERR;
11091     }
11092     /* Now... for every other/local couple: */
11093     for (i = 1; i < argc; i += 2) {
11094         if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
11095                 targetCallFrame) != JIM_OK) return JIM_ERR;
11096     }
11097     return JIM_OK;
11098 }
11099
11100 /* [global] */
11101 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, 
11102         Jim_Obj *const *argv)
11103 {
11104     int i;
11105
11106     if (argc < 2) {
11107         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
11108         return JIM_ERR;
11109     }
11110     /* Link every var to the toplevel having the same name */
11111     if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
11112     for (i = 1; i < argc; i++) {
11113         if (Jim_SetVariableLink(interp, argv[i], argv[i],
11114                 interp->topFramePtr) != JIM_OK) return JIM_ERR;
11115     }
11116     return JIM_OK;
11117 }
11118
11119 /* does the [string map] operation. On error NULL is returned,
11120  * otherwise a new string object with the result, having refcount = 0,
11121  * is returned. */
11122 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
11123         Jim_Obj *objPtr, int nocase)
11124 {
11125     int numMaps;
11126     const char **key, *str, *noMatchStart = NULL;
11127     Jim_Obj **value;
11128     int *keyLen, strLen, i;
11129     Jim_Obj *resultObjPtr;
11130     
11131     Jim_ListLength(interp, mapListObjPtr, &numMaps);
11132     if (numMaps % 2) {
11133         Jim_SetResultString(interp,
11134                 "list must contain an even number of elements", -1);
11135         return NULL;
11136     }
11137     /* Initialization */
11138     numMaps /= 2;
11139     key = Jim_Alloc(sizeof(char*)*numMaps);
11140     keyLen = Jim_Alloc(sizeof(int)*numMaps);
11141     value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
11142     resultObjPtr = Jim_NewStringObj(interp, "", 0);
11143     for (i = 0; i < numMaps; i++) {
11144         Jim_Obj *eleObjPtr;
11145
11146         Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
11147         key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
11148         Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
11149         value[i] = eleObjPtr;
11150     }
11151     str = Jim_GetString(objPtr, &strLen);
11152     /* Map it */
11153     while(strLen) {
11154         for (i = 0; i < numMaps; i++) {
11155             if (strLen >= keyLen[i] && keyLen[i]) {
11156                 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
11157                             nocase))
11158                 {
11159                     if (noMatchStart) {
11160                         Jim_AppendString(interp, resultObjPtr,
11161                                 noMatchStart, str-noMatchStart);
11162                         noMatchStart = NULL;
11163                     }
11164                     Jim_AppendObj(interp, resultObjPtr, value[i]);
11165                     str += keyLen[i];
11166                     strLen -= keyLen[i];
11167                     break;
11168                 }
11169             }
11170         }
11171         if (i == numMaps) { /* no match */
11172             if (noMatchStart == NULL)
11173                 noMatchStart = str;
11174             str ++;
11175             strLen --;
11176         }
11177     }
11178     if (noMatchStart) {
11179         Jim_AppendString(interp, resultObjPtr,
11180             noMatchStart, str-noMatchStart);
11181     }
11182     Jim_Free((void*)key);
11183     Jim_Free(keyLen);
11184     Jim_Free(value);
11185     return resultObjPtr;
11186 }
11187
11188 /* [string] */
11189 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, 
11190         Jim_Obj *const *argv)
11191 {
11192     int option;
11193     const char *options[] = {
11194         "length", "compare", "match", "equal", "range", "map", "repeat",
11195         "index", "first", "tolower", "toupper", NULL
11196     };
11197     enum {
11198         OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
11199         OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
11200     };
11201
11202     if (argc < 2) {
11203         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11204         return JIM_ERR;
11205     }
11206     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11207                 JIM_ERRMSG) != JIM_OK)
11208         return JIM_ERR;
11209
11210     if (option == OPT_LENGTH) {
11211         int len;
11212
11213         if (argc != 3) {
11214             Jim_WrongNumArgs(interp, 2, argv, "string");
11215             return JIM_ERR;
11216         }
11217         Jim_GetString(argv[2], &len);
11218         Jim_SetResult(interp, Jim_NewIntObj(interp, len));
11219         return JIM_OK;
11220     } else if (option == OPT_COMPARE) {
11221         int nocase = 0;
11222         if ((argc != 4 && argc != 5) ||
11223             (argc == 5 && Jim_CompareStringImmediate(interp,
11224                 argv[2], "-nocase") == 0)) {
11225             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11226             return JIM_ERR;
11227         }
11228         if (argc == 5) {
11229             nocase = 1;
11230             argv++;
11231         }
11232         Jim_SetResult(interp, Jim_NewIntObj(interp,
11233                     Jim_StringCompareObj(argv[2],
11234                             argv[3], nocase)));
11235         return JIM_OK;
11236     } else if (option == OPT_MATCH) {
11237         int nocase = 0;
11238         if ((argc != 4 && argc != 5) ||
11239             (argc == 5 && Jim_CompareStringImmediate(interp,
11240                 argv[2], "-nocase") == 0)) {
11241             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
11242                     "string");
11243             return JIM_ERR;
11244         }
11245         if (argc == 5) {
11246             nocase = 1;
11247             argv++;
11248         }
11249         Jim_SetResult(interp,
11250             Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
11251                     argv[3], nocase)));
11252         return JIM_OK;
11253     } else if (option == OPT_EQUAL) {
11254         if (argc != 4) {
11255             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11256             return JIM_ERR;
11257         }
11258         Jim_SetResult(interp,
11259             Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
11260                     argv[3], 0)));
11261         return JIM_OK;
11262     } else if (option == OPT_RANGE) {
11263         Jim_Obj *objPtr;
11264
11265         if (argc != 5) {
11266             Jim_WrongNumArgs(interp, 2, argv, "string first last");
11267             return JIM_ERR;
11268         }
11269         objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
11270         if (objPtr == NULL)
11271             return JIM_ERR;
11272         Jim_SetResult(interp, objPtr);
11273         return JIM_OK;
11274     } else if (option == OPT_MAP) {
11275         int nocase = 0;
11276         Jim_Obj *objPtr;
11277
11278         if ((argc != 4 && argc != 5) ||
11279             (argc == 5 && Jim_CompareStringImmediate(interp,
11280                 argv[2], "-nocase") == 0)) {
11281             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
11282                     "string");
11283             return JIM_ERR;
11284         }
11285         if (argc == 5) {
11286             nocase = 1;
11287             argv++;
11288         }
11289         objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
11290         if (objPtr == NULL)
11291             return JIM_ERR;
11292         Jim_SetResult(interp, objPtr);
11293         return JIM_OK;
11294     } else if (option == OPT_REPEAT) {
11295         Jim_Obj *objPtr;
11296         jim_wide count;
11297
11298         if (argc != 4) {
11299             Jim_WrongNumArgs(interp, 2, argv, "string count");
11300             return JIM_ERR;
11301         }
11302         if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
11303             return JIM_ERR;
11304         objPtr = Jim_NewStringObj(interp, "", 0);
11305         while (count--) {
11306             Jim_AppendObj(interp, objPtr, argv[2]);
11307         }
11308         Jim_SetResult(interp, objPtr);
11309         return JIM_OK;
11310     } else if (option == OPT_INDEX) {
11311         int index, len;
11312         const char *str;
11313
11314         if (argc != 4) {
11315             Jim_WrongNumArgs(interp, 2, argv, "string index");
11316             return JIM_ERR;
11317         }
11318         if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
11319             return JIM_ERR;
11320         str = Jim_GetString(argv[2], &len);
11321         if (index != INT_MIN && index != INT_MAX)
11322             index = JimRelToAbsIndex(len, index);
11323         if (index < 0 || index >= len) {
11324             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11325             return JIM_OK;
11326         } else {
11327             Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
11328             return JIM_OK;
11329         }
11330     } else if (option == OPT_FIRST) {
11331         int index = 0, l1, l2;
11332         const char *s1, *s2;
11333
11334         if (argc != 4 && argc != 5) {
11335             Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
11336             return JIM_ERR;
11337         }
11338         s1 = Jim_GetString(argv[2], &l1);
11339         s2 = Jim_GetString(argv[3], &l2);
11340         if (argc == 5) {
11341             if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
11342                 return JIM_ERR;
11343             index = JimRelToAbsIndex(l2, index);
11344         }
11345         Jim_SetResult(interp, Jim_NewIntObj(interp,
11346                     JimStringFirst(s1, l1, s2, l2, index)));
11347         return JIM_OK;
11348     } else if (option == OPT_TOLOWER) {
11349         if (argc != 3) {
11350             Jim_WrongNumArgs(interp, 2, argv, "string");
11351             return JIM_ERR;
11352         }
11353         Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11354     } else if (option == OPT_TOUPPER) {
11355         if (argc != 3) {
11356             Jim_WrongNumArgs(interp, 2, argv, "string");
11357             return JIM_ERR;
11358         }
11359         Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11360     }
11361     return JIM_OK;
11362 }
11363
11364 /* [time] */
11365 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, 
11366         Jim_Obj *const *argv)
11367 {
11368     long i, count = 1;
11369     jim_wide start, elapsed;
11370     char buf [256];
11371     const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11372
11373     if (argc < 2) {
11374         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11375         return JIM_ERR;
11376     }
11377     if (argc == 3) {
11378         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11379             return JIM_ERR;
11380     }
11381     if (count < 0)
11382         return JIM_OK;
11383     i = count;
11384     start = JimClock();
11385     while (i-- > 0) {
11386         int retval;
11387
11388         if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11389             return retval;
11390     }
11391     elapsed = JimClock() - start;
11392     sprintf(buf, fmt, elapsed/count);
11393     Jim_SetResultString(interp, buf, -1);
11394     return JIM_OK;
11395 }
11396
11397 /* [exit] */
11398 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, 
11399         Jim_Obj *const *argv)
11400 {
11401     long exitCode = 0;
11402
11403     if (argc > 2) {
11404         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11405         return JIM_ERR;
11406     }
11407     if (argc == 2) {
11408         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11409             return JIM_ERR;
11410     }
11411     interp->exitCode = exitCode;
11412     return JIM_EXIT;
11413 }
11414
11415 /* [catch] */
11416 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, 
11417         Jim_Obj *const *argv)
11418 {
11419     int exitCode = 0;
11420
11421     if (argc != 2 && argc != 3) {
11422         Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11423         return JIM_ERR;
11424     }
11425     exitCode = Jim_EvalObj(interp, argv[1]);
11426     if (argc == 3) {
11427         if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11428                 != JIM_OK)
11429             return JIM_ERR;
11430     }
11431     Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11432     return JIM_OK;
11433 }
11434
11435 /* [ref] */
11436 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, 
11437         Jim_Obj *const *argv)
11438 {
11439     if (argc != 3 && argc != 4) {
11440         Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11441         return JIM_ERR;
11442     }
11443     if (argc == 3) {
11444         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11445     } else {
11446         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11447                     argv[3]));
11448     }
11449     return JIM_OK;
11450 }
11451
11452 /* [getref] */
11453 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, 
11454         Jim_Obj *const *argv)
11455 {
11456     Jim_Reference *refPtr;
11457
11458     if (argc != 2) {
11459         Jim_WrongNumArgs(interp, 1, argv, "reference");
11460         return JIM_ERR;
11461     }
11462     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11463         return JIM_ERR;
11464     Jim_SetResult(interp, refPtr->objPtr);
11465     return JIM_OK;
11466 }
11467
11468 /* [setref] */
11469 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, 
11470         Jim_Obj *const *argv)
11471 {
11472     Jim_Reference *refPtr;
11473
11474     if (argc != 3) {
11475         Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11476         return JIM_ERR;
11477     }
11478     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11479         return JIM_ERR;
11480     Jim_IncrRefCount(argv[2]);
11481     Jim_DecrRefCount(interp, refPtr->objPtr);
11482     refPtr->objPtr = argv[2];
11483     Jim_SetResult(interp, argv[2]);
11484     return JIM_OK;
11485 }
11486
11487 /* [collect] */
11488 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, 
11489         Jim_Obj *const *argv)
11490 {
11491     if (argc != 1) {
11492         Jim_WrongNumArgs(interp, 1, argv, "");
11493         return JIM_ERR;
11494     }
11495     Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11496     return JIM_OK;
11497 }
11498
11499 /* [finalize] reference ?newValue? */
11500 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, 
11501         Jim_Obj *const *argv)
11502 {
11503     if (argc != 2 && argc != 3) {
11504         Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11505         return JIM_ERR;
11506     }
11507     if (argc == 2) {
11508         Jim_Obj *cmdNamePtr;
11509
11510         if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11511             return JIM_ERR;
11512         if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11513             Jim_SetResult(interp, cmdNamePtr);
11514     } else {
11515         if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11516             return JIM_ERR;
11517         Jim_SetResult(interp, argv[2]);
11518     }
11519     return JIM_OK;
11520 }
11521
11522 /* TODO */
11523 /* [info references] (list of all the references/finalizers) */
11524
11525 /* [rename] */
11526 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, 
11527         Jim_Obj *const *argv)
11528 {
11529     const char *oldName, *newName;
11530
11531     if (argc != 3) {
11532         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11533         return JIM_ERR;
11534     }
11535     oldName = Jim_GetString(argv[1], NULL);
11536     newName = Jim_GetString(argv[2], NULL);
11537     if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11538         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11539         Jim_AppendStrings(interp, Jim_GetResult(interp),
11540             "can't rename \"", oldName, "\": ",
11541             "command doesn't exist", NULL);
11542         return JIM_ERR;
11543     }
11544     return JIM_OK;
11545 }
11546
11547 /* [dict] */
11548 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, 
11549         Jim_Obj *const *argv)
11550 {
11551     int option;
11552     const char *options[] = {
11553         "create", "get", "set", "unset", "exists", NULL
11554     };
11555     enum {
11556         OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11557     };
11558
11559     if (argc < 2) {
11560         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11561         return JIM_ERR;
11562     }
11563
11564     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11565                 JIM_ERRMSG) != JIM_OK)
11566         return JIM_ERR;
11567
11568     if (option == OPT_CREATE) {
11569         Jim_Obj *objPtr;
11570
11571         if (argc % 2) {
11572             Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11573             return JIM_ERR;
11574         }
11575         objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11576         Jim_SetResult(interp, objPtr);
11577         return JIM_OK;
11578     } else if (option == OPT_GET) {
11579         Jim_Obj *objPtr;
11580
11581         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11582                 JIM_ERRMSG) != JIM_OK)
11583             return JIM_ERR;
11584         Jim_SetResult(interp, objPtr);
11585         return JIM_OK;
11586     } else if (option == OPT_SET) {
11587         if (argc < 5) {
11588             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11589             return JIM_ERR;
11590         }
11591         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11592                     argv[argc-1]);
11593     } else if (option == OPT_UNSET) {
11594         if (argc < 4) {
11595             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11596             return JIM_ERR;
11597         }
11598         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11599                     NULL);
11600     } else if (option == OPT_EXIST) {
11601         Jim_Obj *objPtr;
11602         int exists;
11603
11604         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11605                 JIM_ERRMSG) == JIM_OK)
11606             exists = 1;
11607         else
11608             exists = 0;
11609         Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11610         return JIM_OK;
11611     } else {
11612         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11613         Jim_AppendStrings(interp, Jim_GetResult(interp),
11614             "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11615             " must be create, get, set", NULL);
11616         return JIM_ERR;
11617     }
11618     return JIM_OK;
11619 }
11620
11621 /* [load] */
11622 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc, 
11623         Jim_Obj *const *argv)
11624 {
11625     if (argc < 2) {
11626         Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11627         return JIM_ERR;
11628     }
11629     return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11630 }
11631
11632 /* [subst] */
11633 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, 
11634         Jim_Obj *const *argv)
11635 {
11636     int i, flags = 0;
11637     Jim_Obj *objPtr;
11638
11639     if (argc < 2) {
11640         Jim_WrongNumArgs(interp, 1, argv,
11641             "?-nobackslashes? ?-nocommands? ?-novariables? string");
11642         return JIM_ERR;
11643     }
11644     i = argc-2;
11645     while(i--) {
11646         if (Jim_CompareStringImmediate(interp, argv[i+1],
11647                     "-nobackslashes"))
11648             flags |= JIM_SUBST_NOESC;
11649         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11650                     "-novariables"))
11651             flags |= JIM_SUBST_NOVAR;
11652         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11653                     "-nocommands"))
11654             flags |= JIM_SUBST_NOCMD;
11655         else {
11656             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11657             Jim_AppendStrings(interp, Jim_GetResult(interp),
11658                 "bad option \"", Jim_GetString(argv[i+1], NULL),
11659                 "\": must be -nobackslashes, -nocommands, or "
11660                 "-novariables", NULL);
11661             return JIM_ERR;
11662         }
11663     }
11664     if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11665         return JIM_ERR;
11666     Jim_SetResult(interp, objPtr);
11667     return JIM_OK;
11668 }
11669
11670 /* [info] */
11671 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, 
11672         Jim_Obj *const *argv)
11673 {
11674     int cmd, result = JIM_OK;
11675     static const char *commands[] = {
11676         "body", "commands", "exists", "globals", "level", "locals",
11677         "vars", "version", "complete", "args", "hostname", NULL
11678     };
11679     enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11680           INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS, INFO_HOSTNAME};
11681     
11682     if (argc < 2) {
11683         Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11684         return JIM_ERR;
11685     }
11686     if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11687         != JIM_OK) {
11688         return JIM_ERR;
11689     }
11690     
11691     if (cmd == INFO_COMMANDS) {
11692         if (argc != 2 && argc != 3) {
11693             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11694             return JIM_ERR;
11695         }
11696         if (argc == 3)
11697             Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11698         else
11699             Jim_SetResult(interp, JimCommandsList(interp, NULL));
11700     } else if (cmd == INFO_EXISTS) {
11701         Jim_Obj *exists;
11702         if (argc != 3) {
11703             Jim_WrongNumArgs(interp, 2, argv, "varName");
11704             return JIM_ERR;
11705         }
11706         exists = Jim_GetVariable(interp, argv[2], 0);
11707         Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11708     } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11709         int mode;
11710         switch (cmd) {
11711             case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11712             case INFO_LOCALS:  mode = JIM_VARLIST_LOCALS; break;
11713             case INFO_VARS:    mode = JIM_VARLIST_VARS; break;
11714             default: mode = 0; /* avoid warning */; break;
11715         }
11716         if (argc != 2 && argc != 3) {
11717             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11718             return JIM_ERR;
11719         }
11720         if (argc == 3)
11721             Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11722         else
11723             Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11724     } else if (cmd == INFO_LEVEL) {
11725         Jim_Obj *objPtr;
11726         switch (argc) {
11727             case 2:
11728                 Jim_SetResult(interp,
11729                               Jim_NewIntObj(interp, interp->numLevels));
11730                 break;
11731             case 3:
11732                 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11733                     return JIM_ERR;
11734                 Jim_SetResult(interp, objPtr);
11735                 break;
11736             default:
11737                 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11738                 return JIM_ERR;
11739         }
11740     } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11741         Jim_Cmd *cmdPtr;
11742
11743         if (argc != 3) {
11744             Jim_WrongNumArgs(interp, 2, argv, "procname");
11745             return JIM_ERR;
11746         }
11747         if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11748             return JIM_ERR;
11749         if (cmdPtr->cmdProc != NULL) {
11750             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11751             Jim_AppendStrings(interp, Jim_GetResult(interp),
11752                 "command \"", Jim_GetString(argv[2], NULL),
11753                 "\" is not a procedure", NULL);
11754             return JIM_ERR;
11755         }
11756         if (cmd == INFO_BODY)
11757             Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11758         else
11759             Jim_SetResult(interp, cmdPtr->argListObjPtr);
11760     } else if (cmd == INFO_VERSION) {
11761         char buf[(JIM_INTEGER_SPACE * 2) + 1];
11762         sprintf(buf, "%d.%d", 
11763                 JIM_VERSION / 100, JIM_VERSION % 100);
11764         Jim_SetResultString(interp, buf, -1);
11765     } else if (cmd == INFO_COMPLETE) {
11766         const char *s;
11767         int len;
11768
11769         if (argc != 3) {
11770             Jim_WrongNumArgs(interp, 2, argv, "script");
11771             return JIM_ERR;
11772         }
11773         s = Jim_GetString(argv[2], &len);
11774         Jim_SetResult(interp,
11775                 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11776     } else if (cmd == INFO_HOSTNAME) {
11777         /* Redirect to os.hostname if it exists */
11778         Jim_Obj *command = Jim_NewStringObj(interp, "os.gethostname", -1);
11779         result = Jim_EvalObjVector(interp, 1, &command);
11780     }
11781     return result;
11782 }
11783
11784 /* [split] */
11785 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, 
11786         Jim_Obj *const *argv)
11787 {
11788     const char *str, *splitChars, *noMatchStart;
11789     int splitLen, strLen, i;
11790     Jim_Obj *resObjPtr;
11791
11792     if (argc != 2 && argc != 3) {
11793         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11794         return JIM_ERR;
11795     }
11796     /* Init */
11797     if (argc == 2) {
11798         splitChars = " \n\t\r";
11799         splitLen = 4;
11800     } else {
11801         splitChars = Jim_GetString(argv[2], &splitLen);
11802     }
11803     str = Jim_GetString(argv[1], &strLen);
11804     if (!strLen) return JIM_OK;
11805     noMatchStart = str;
11806     resObjPtr = Jim_NewListObj(interp, NULL, 0);
11807     /* Split */
11808     if (splitLen) {
11809         while (strLen) {
11810             for (i = 0; i < splitLen; i++) {
11811                 if (*str == splitChars[i]) {
11812                     Jim_Obj *objPtr;
11813
11814                     objPtr = Jim_NewStringObj(interp, noMatchStart,
11815                             (str-noMatchStart));
11816                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
11817                     noMatchStart = str+1;
11818                     break;
11819                 }
11820             }
11821             str ++;
11822             strLen --;
11823         }
11824         Jim_ListAppendElement(interp, resObjPtr,
11825                 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11826     } else {
11827         /* This handles the special case of splitchars eq {}. This
11828          * is trivial but we want to perform object sharing as Tcl does. */
11829         Jim_Obj *objCache[256];
11830         const unsigned char *u = (unsigned char*) str;
11831         memset(objCache, 0, sizeof(objCache));
11832         for (i = 0; i < strLen; i++) {
11833             int c = u[i];
11834             
11835             if (objCache[c] == NULL)
11836                 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11837             Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11838         }
11839     }
11840     Jim_SetResult(interp, resObjPtr);
11841     return JIM_OK;
11842 }
11843
11844 /* [join] */
11845 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, 
11846         Jim_Obj *const *argv)
11847 {
11848     const char *joinStr;
11849     int joinStrLen, i, listLen;
11850     Jim_Obj *resObjPtr;
11851
11852     if (argc != 2 && argc != 3) {
11853         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11854         return JIM_ERR;
11855     }
11856     /* Init */
11857     if (argc == 2) {
11858         joinStr = " ";
11859         joinStrLen = 1;
11860     } else {
11861         joinStr = Jim_GetString(argv[2], &joinStrLen);
11862     }
11863     Jim_ListLength(interp, argv[1], &listLen);
11864     resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11865     /* Split */
11866     for (i = 0; i < listLen; i++) {
11867         Jim_Obj *objPtr;
11868
11869         Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11870         Jim_AppendObj(interp, resObjPtr, objPtr);
11871         if (i+1 != listLen) {
11872             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11873         }
11874     }
11875     Jim_SetResult(interp, resObjPtr);
11876     return JIM_OK;
11877 }
11878
11879 /* [format] */
11880 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11881         Jim_Obj *const *argv)
11882 {
11883     Jim_Obj *objPtr;
11884
11885     if (argc < 2) {
11886         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11887         return JIM_ERR;
11888     }
11889     objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11890     if (objPtr == NULL)
11891         return JIM_ERR;
11892     Jim_SetResult(interp, objPtr);
11893     return JIM_OK;
11894 }
11895
11896 /* [scan] */
11897 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11898         Jim_Obj *const *argv)
11899 {
11900     Jim_Obj *listPtr, **outVec;
11901     int outc, i, count = 0;
11902
11903     if (argc < 3) {
11904         Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11905         return JIM_ERR;
11906     } 
11907     if (argv[2]->typePtr != &scanFmtStringObjType)
11908         SetScanFmtFromAny(interp, argv[2]);
11909     if (FormatGetError(argv[2]) != 0) {
11910         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11911         return JIM_ERR;
11912     }
11913     if (argc > 3) {
11914         int maxPos = FormatGetMaxPos(argv[2]);
11915         int count = FormatGetCnvCount(argv[2]);
11916         if (maxPos > argc-3) {
11917             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11918             return JIM_ERR;
11919         } else if (count != 0 && count < argc-3) {
11920             Jim_SetResultString(interp, "variable is not assigned by any "
11921                 "conversion specifiers", -1);
11922             return JIM_ERR;
11923         } else if (count > argc-3) {
11924             Jim_SetResultString(interp, "different numbers of variable names and "
11925                 "field specifiers", -1);
11926             return JIM_ERR;
11927         }
11928     } 
11929     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11930     if (listPtr == 0)
11931         return JIM_ERR;
11932     if (argc > 3) {
11933         int len = 0;
11934         if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11935             Jim_ListLength(interp, listPtr, &len);
11936         if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11937             Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11938             return JIM_OK;
11939         }
11940         JimListGetElements(interp, listPtr, &outc, &outVec);
11941         for (i = 0; i < outc; ++i) {
11942             if (Jim_Length(outVec[i]) > 0) {
11943                 ++count;
11944                 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11945                     goto err;
11946             }
11947         }
11948         Jim_FreeNewObj(interp, listPtr);
11949         Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11950     } else {
11951         if (listPtr == (Jim_Obj*)EOF) {
11952             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11953             return JIM_OK;
11954         }
11955         Jim_SetResult(interp, listPtr);
11956     }
11957     return JIM_OK;
11958 err:
11959     Jim_FreeNewObj(interp, listPtr);
11960     return JIM_ERR;
11961 }
11962
11963 /* [error] */
11964 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11965         Jim_Obj *const *argv)
11966 {
11967     if (argc != 2) {
11968         Jim_WrongNumArgs(interp, 1, argv, "message");
11969         return JIM_ERR;
11970     }
11971     Jim_SetResult(interp, argv[1]);
11972     return JIM_ERR;
11973 }
11974
11975 /* [lrange] */
11976 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11977         Jim_Obj *const *argv)
11978 {
11979     Jim_Obj *objPtr;
11980
11981     if (argc != 4) {
11982         Jim_WrongNumArgs(interp, 1, argv, "list first last");
11983         return JIM_ERR;
11984     }
11985     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11986         return JIM_ERR;
11987     Jim_SetResult(interp, objPtr);
11988     return JIM_OK;
11989 }
11990
11991 /* [env] */
11992 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11993         Jim_Obj *const *argv)
11994 {
11995     const char *key;
11996     char *val;
11997
11998     if (argc == 1) {
11999
12000 #ifdef NEED_ENVIRON_EXTERN
12001         extern char **environ;
12002 #endif
12003
12004         int i;
12005         Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
12006
12007         for (i = 0; environ[i]; i++) {
12008             const char *equals = strchr(environ[i], '=');
12009             if (equals) {
12010                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, environ[i], equals - environ[i]));
12011                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
12012             }
12013         }
12014
12015         Jim_SetResult(interp, listObjPtr);
12016         return JIM_OK;
12017     }
12018
12019     if (argc != 2) {
12020         Jim_WrongNumArgs(interp, 1, argv, "varName");
12021         return JIM_ERR;
12022     }
12023     key = Jim_GetString(argv[1], NULL);
12024     val = getenv(key);
12025     if (val == NULL) {
12026         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
12027         Jim_AppendStrings(interp, Jim_GetResult(interp),
12028                 "environment variable \"",
12029                 key, "\" does not exist", NULL);
12030         return JIM_ERR;
12031     }
12032     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
12033     return JIM_OK;
12034 }
12035
12036 /* [source] */
12037 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
12038         Jim_Obj *const *argv)
12039 {
12040     int retval;
12041
12042     if (argc != 2) {
12043         Jim_WrongNumArgs(interp, 1, argv, "fileName");
12044         return JIM_ERR;
12045     }
12046     retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
12047     if (retval == JIM_ERR) {
12048         return JIM_ERR_ADDSTACK;
12049     }
12050     if (retval == JIM_RETURN)
12051         return JIM_OK;
12052     return retval;
12053 }
12054
12055 /* [lreverse] */
12056 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
12057         Jim_Obj *const *argv)
12058 {
12059     Jim_Obj *revObjPtr, **ele;
12060     int len;
12061
12062     if (argc != 2) {
12063         Jim_WrongNumArgs(interp, 1, argv, "list");
12064         return JIM_ERR;
12065     }
12066     JimListGetElements(interp, argv[1], &len, &ele);
12067     len--;
12068     revObjPtr = Jim_NewListObj(interp, NULL, 0);
12069     while (len >= 0)
12070         ListAppendElement(revObjPtr, ele[len--]);
12071     Jim_SetResult(interp, revObjPtr);
12072     return JIM_OK;
12073 }
12074
12075 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
12076 {
12077     jim_wide len;
12078
12079     if (step == 0) return -1;
12080     if (start == end) return 0;
12081     else if (step > 0 && start > end) return -1;
12082     else if (step < 0 && end > start) return -1;
12083     len = end-start;
12084     if (len < 0) len = -len; /* abs(len) */
12085     if (step < 0) step = -step; /* abs(step) */
12086     len = 1 + ((len-1)/step);
12087     /* We can truncate safely to INT_MAX, the range command
12088      * will always return an error for a such long range
12089      * because Tcl lists can't be so long. */
12090     if (len > INT_MAX) len = INT_MAX;
12091     return (int)((len < 0) ? -1 : len);
12092 }
12093
12094 /* [range] */
12095 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
12096         Jim_Obj *const *argv)
12097 {
12098     jim_wide start = 0, end, step = 1;
12099     int len, i;
12100     Jim_Obj *objPtr;
12101
12102     if (argc < 2 || argc > 4) {
12103         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
12104         return JIM_ERR;
12105     }
12106     if (argc == 2) {
12107         if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
12108             return JIM_ERR;
12109     } else {
12110         if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
12111             Jim_GetWide(interp, argv[2], &end) != JIM_OK)
12112             return JIM_ERR;
12113         if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
12114             return JIM_ERR;
12115     }
12116     if ((len = JimRangeLen(start, end, step)) == -1) {
12117         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
12118         return JIM_ERR;
12119     }
12120     objPtr = Jim_NewListObj(interp, NULL, 0);
12121     for (i = 0; i < len; i++)
12122         ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
12123     Jim_SetResult(interp, objPtr);
12124     return JIM_OK;
12125 }
12126
12127 /* [rand] */
12128 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
12129         Jim_Obj *const *argv)
12130 {
12131     jim_wide min = 0, max, len, maxMul;
12132
12133     if (argc < 1 || argc > 3) {
12134         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
12135         return JIM_ERR;
12136     }
12137     if (argc == 1) {
12138         max = JIM_WIDE_MAX;
12139     } else if (argc == 2) {
12140         if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
12141             return JIM_ERR;
12142     } else if (argc == 3) {
12143         if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
12144             Jim_GetWide(interp, argv[2], &max) != JIM_OK)
12145             return JIM_ERR;
12146     }
12147     len = max-min;
12148     if (len < 0) {
12149         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
12150         return JIM_ERR;
12151     }
12152     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
12153     while (1) {
12154         jim_wide r;
12155
12156         JimRandomBytes(interp, &r, sizeof(jim_wide));
12157         if (r < 0 || r >= maxMul) continue;
12158         r = (len == 0) ? 0 : r%len;
12159         Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
12160         return JIM_OK;
12161     }
12162 }
12163
12164 /* [package] */
12165 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc, 
12166         Jim_Obj *const *argv)
12167 {
12168     int option;
12169     const char *options[] = {
12170         "require", "provide", NULL
12171     };
12172     enum {OPT_REQUIRE, OPT_PROVIDE};
12173
12174     if (argc < 2) {
12175         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
12176         return JIM_ERR;
12177     }
12178     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
12179                 JIM_ERRMSG) != JIM_OK)
12180         return JIM_ERR;
12181
12182     if (option == OPT_REQUIRE) {
12183         int exact = 0;
12184         const char *ver;
12185
12186         if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
12187             exact = 1;
12188             argv++;
12189             argc--;
12190         }
12191         if (argc != 3 && argc != 4) {
12192             Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
12193             return JIM_ERR;
12194         }
12195         ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
12196                 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
12197                 JIM_ERRMSG);
12198         if (ver == NULL)
12199             return JIM_ERR_ADDSTACK;
12200         Jim_SetResultString(interp, ver, -1);
12201     } else if (option == OPT_PROVIDE) {
12202         if (argc != 4) {
12203             Jim_WrongNumArgs(interp, 2, argv, "package version");
12204             return JIM_ERR;
12205         }
12206         return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
12207                     Jim_GetString(argv[3], NULL), JIM_ERRMSG);
12208     }
12209     return JIM_OK;
12210 }
12211
12212 static struct {
12213     const char *name;
12214     Jim_CmdProc cmdProc;
12215 } Jim_CoreCommandsTable[] = {
12216     {"set", Jim_SetCoreCommand},
12217     {"unset", Jim_UnsetCoreCommand},
12218     {"puts", Jim_PutsCoreCommand},
12219     {"+", Jim_AddCoreCommand},
12220     {"*", Jim_MulCoreCommand},
12221     {"-", Jim_SubCoreCommand},
12222     {"/", Jim_DivCoreCommand},
12223     {"incr", Jim_IncrCoreCommand},
12224     {"while", Jim_WhileCoreCommand},
12225     {"for", Jim_ForCoreCommand},
12226     {"foreach", Jim_ForeachCoreCommand},
12227     {"lmap", Jim_LmapCoreCommand},
12228     {"if", Jim_IfCoreCommand},
12229     {"switch", Jim_SwitchCoreCommand},
12230     {"list", Jim_ListCoreCommand},
12231     {"lindex", Jim_LindexCoreCommand},
12232     {"lset", Jim_LsetCoreCommand},
12233     {"llength", Jim_LlengthCoreCommand},
12234     {"lappend", Jim_LappendCoreCommand},
12235     {"linsert", Jim_LinsertCoreCommand},
12236     {"lsort", Jim_LsortCoreCommand},
12237     {"append", Jim_AppendCoreCommand},
12238     {"debug", Jim_DebugCoreCommand},
12239     {"eval", Jim_EvalCoreCommand},
12240     {"uplevel", Jim_UplevelCoreCommand},
12241     {"expr", Jim_ExprCoreCommand},
12242     {"break", Jim_BreakCoreCommand},
12243     {"continue", Jim_ContinueCoreCommand},
12244     {"proc", Jim_ProcCoreCommand},
12245     {"concat", Jim_ConcatCoreCommand},
12246     {"return", Jim_ReturnCoreCommand},
12247     {"upvar", Jim_UpvarCoreCommand},
12248     {"global", Jim_GlobalCoreCommand},
12249     {"string", Jim_StringCoreCommand},
12250     {"time", Jim_TimeCoreCommand},
12251     {"exit", Jim_ExitCoreCommand},
12252     {"catch", Jim_CatchCoreCommand},
12253     {"ref", Jim_RefCoreCommand},
12254     {"getref", Jim_GetrefCoreCommand},
12255     {"setref", Jim_SetrefCoreCommand},
12256     {"finalize", Jim_FinalizeCoreCommand},
12257     {"collect", Jim_CollectCoreCommand},
12258     {"rename", Jim_RenameCoreCommand},
12259     {"dict", Jim_DictCoreCommand},
12260     {"load", Jim_LoadCoreCommand},
12261     {"subst", Jim_SubstCoreCommand},
12262     {"info", Jim_InfoCoreCommand},
12263     {"split", Jim_SplitCoreCommand},
12264     {"join", Jim_JoinCoreCommand},
12265     {"format", Jim_FormatCoreCommand},
12266     {"scan", Jim_ScanCoreCommand},
12267     {"error", Jim_ErrorCoreCommand},
12268     {"lrange", Jim_LrangeCoreCommand},
12269     {"env", Jim_EnvCoreCommand},
12270     {"source", Jim_SourceCoreCommand},
12271     {"lreverse", Jim_LreverseCoreCommand},
12272     {"range", Jim_RangeCoreCommand},
12273     {"rand", Jim_RandCoreCommand},
12274     {"package", Jim_PackageCoreCommand},
12275     {"tailcall", Jim_TailcallCoreCommand},
12276     {NULL, NULL},
12277 };
12278
12279 /* Some Jim core command is actually a procedure written in Jim itself. */
12280 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
12281 {
12282     Jim_Eval(interp, (char*)
12283 "proc lambda {arglist args} {\n"
12284 "    set name [ref {} function lambdaFinalizer]\n"
12285 "    uplevel 1 [list proc $name $arglist {expand}$args]\n"
12286 "    return $name\n"
12287 "}\n"
12288 "proc lambdaFinalizer {name val} {\n"
12289 "    rename $name {}\n"
12290 "}\n"
12291     );
12292 }
12293
12294 void Jim_RegisterCoreCommands(Jim_Interp *interp)
12295 {
12296     int i = 0;
12297
12298     while(Jim_CoreCommandsTable[i].name != NULL) {
12299         Jim_CreateCommand(interp, 
12300                 Jim_CoreCommandsTable[i].name,
12301                 Jim_CoreCommandsTable[i].cmdProc,
12302                 NULL, NULL);
12303         i++;
12304     }
12305     Jim_RegisterCoreProcedures(interp);
12306 }
12307
12308 /* -----------------------------------------------------------------------------
12309  * Interactive prompt
12310  * ---------------------------------------------------------------------------*/
12311 void Jim_PrintErrorMessage(Jim_Interp *interp)
12312 {
12313     int len, i;
12314
12315     if (*interp->errorFileName) {
12316         Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL "    ",
12317                                     interp->errorFileName, interp->errorLine);
12318     }
12319     Jim_fprintf(interp,interp->cookie_stderr, "%s" JIM_NL,
12320             Jim_GetString(interp->result, NULL));
12321     Jim_ListLength(interp, interp->stackTrace, &len);
12322     for (i = len-3; i >= 0; i-= 3) {
12323         Jim_Obj *objPtr;
12324         const char *proc, *file, *line;
12325
12326         Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
12327         proc = Jim_GetString(objPtr, NULL);
12328         Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
12329                 JIM_NONE);
12330         file = Jim_GetString(objPtr, NULL);
12331         Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
12332                 JIM_NONE);
12333         line = Jim_GetString(objPtr, NULL);
12334         if (*proc) {
12335             Jim_fprintf( interp, interp->cookie_stderr,
12336                     "in procedure '%s' ", proc);
12337         }
12338         if (*file) {
12339             Jim_fprintf( interp, interp->cookie_stderr,
12340                     "called at file \"%s\", line %s",
12341                     file, line);
12342         }
12343         if (*file || *proc) {
12344             Jim_fprintf( interp, interp->cookie_stderr, JIM_NL);
12345         }
12346     }
12347 }
12348
12349 int Jim_InteractivePrompt(Jim_Interp *interp)
12350 {
12351     int retcode = JIM_OK;
12352     Jim_Obj *scriptObjPtr;
12353
12354     Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
12355            "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
12356            JIM_VERSION / 100, JIM_VERSION % 100);
12357      Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
12358     while (1) {
12359         char buf[1024];
12360         const char *result;
12361         const char *retcodestr[] = {
12362             "ok", "error", "return", "break", "continue", "eval", "exit"
12363         };
12364         int reslen;
12365
12366         if (retcode != 0) {
12367             if (retcode >= 2 && retcode <= 6)
12368                 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
12369             else
12370                 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
12371         } else
12372             Jim_fprintf( interp, interp->cookie_stdout, ". ");
12373         Jim_fflush( interp, interp->cookie_stdout);
12374         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
12375         Jim_IncrRefCount(scriptObjPtr);
12376         while(1) {
12377             const char *str;
12378             char state;
12379             int len;
12380
12381             if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
12382                 Jim_DecrRefCount(interp, scriptObjPtr);
12383                 goto out;
12384             }
12385             Jim_AppendString(interp, scriptObjPtr, buf, -1);
12386             str = Jim_GetString(scriptObjPtr, &len);
12387             if (Jim_ScriptIsComplete(str, len, &state))
12388                 break;
12389             Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12390             Jim_fflush( interp, interp->cookie_stdout);
12391         }
12392         retcode = Jim_EvalObj(interp, scriptObjPtr);
12393         Jim_DecrRefCount(interp, scriptObjPtr);
12394         result = Jim_GetString(Jim_GetResult(interp), &reslen);
12395         if (retcode == JIM_ERR) {
12396             Jim_PrintErrorMessage(interp);
12397         } else if (retcode == JIM_EXIT) {
12398             exit(Jim_GetExitCode(interp));
12399         } else {
12400             if (reslen) {
12401                                 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12402                                 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12403             }
12404         }
12405     }
12406 out:
12407     return 0;
12408 }
12409
12410 /* -----------------------------------------------------------------------------
12411  * Jim's idea of STDIO..
12412  * ---------------------------------------------------------------------------*/
12413
12414 int Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12415 {
12416         int r;
12417
12418         va_list ap;
12419         va_start(ap,fmt);
12420         r = Jim_vfprintf( interp, cookie, fmt,ap );
12421         va_end(ap);
12422         return r;
12423 }
12424
12425 int Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12426 {
12427         if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12428                 errno = ENOTSUP;
12429                 return -1;
12430         }
12431         return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12432 }
12433
12434 size_t Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12435 {
12436         if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12437                 errno = ENOTSUP;
12438                 return 0;
12439         }
12440         return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12441 }
12442
12443 size_t Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12444 {
12445         if( (interp == NULL) || (interp->cb_fread == NULL) ){
12446                 errno = ENOTSUP;
12447                 return 0;
12448         }
12449         return (*(interp->cb_fread))( ptr, size, n, cookie);
12450 }
12451
12452 int Jim_fflush( Jim_Interp *interp, void *cookie )
12453 {
12454         if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12455                 /* pretend all is well */
12456                 return 0;
12457         }
12458         return (*(interp->cb_fflush))( cookie );
12459 }
12460
12461 char* Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12462 {
12463         if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12464                 errno = ENOTSUP;
12465                 return NULL;
12466         }
12467         return (*(interp->cb_fgets))( s, size, cookie );
12468 }
12469 Jim_Nvp *
12470 Jim_Nvp_name2value_simple( const Jim_Nvp *p, const char *name )
12471 {
12472         while( p->name ){
12473                 if( 0 == strcmp( name, p->name ) ){
12474                         break;
12475                 }
12476                 p++;
12477         }
12478         return ((Jim_Nvp *)(p));
12479 }
12480
12481 Jim_Nvp *
12482 Jim_Nvp_name2value_nocase_simple( const Jim_Nvp *p, const char *name )
12483 {
12484         while( p->name ){
12485                 if( 0 == strcasecmp( name, p->name ) ){
12486                         break;
12487                 }
12488                 p++;
12489         }
12490         return ((Jim_Nvp *)(p));
12491 }
12492
12493 int
12494 Jim_Nvp_name2value_obj( Jim_Interp *interp, 
12495                                                 const Jim_Nvp *p, 
12496                                                 Jim_Obj *o, 
12497                                                 Jim_Nvp **result )
12498 {
12499         return Jim_Nvp_name2value( interp, p, Jim_GetString( o, NULL ), result );
12500 }
12501         
12502
12503 int 
12504 Jim_Nvp_name2value( Jim_Interp *interp, 
12505                                         const Jim_Nvp *_p, 
12506                                         const char *name, 
12507                                         Jim_Nvp **result)
12508 {
12509         const Jim_Nvp *p;
12510
12511         p = Jim_Nvp_name2value_simple( _p, name );
12512
12513         /* result */
12514         if( result ){
12515                 *result = (Jim_Nvp *)(p);
12516         }
12517         
12518         /* found? */
12519         if( p->name ){
12520                 return JIM_OK;
12521         } else {
12522                 return JIM_ERR;
12523         }
12524 }
12525
12526 int
12527 Jim_Nvp_name2value_obj_nocase( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **puthere )
12528 {
12529         return Jim_Nvp_name2value_nocase( interp, p, Jim_GetString( o, NULL ), puthere );
12530 }
12531
12532 int
12533 Jim_Nvp_name2value_nocase( Jim_Interp *interp, const Jim_Nvp *_p, const char *name, Jim_Nvp **puthere )
12534 {
12535         const Jim_Nvp *p;
12536
12537         p = Jim_Nvp_name2value_nocase_simple( _p, name );
12538
12539         if( puthere ){
12540                 *puthere = (Jim_Nvp *)(p);
12541         }
12542         /* found */
12543         if( p->name ){
12544                 return JIM_OK;
12545         } else {
12546                 return JIM_ERR;
12547         }
12548 }
12549
12550
12551 int 
12552 Jim_Nvp_value2name_obj( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **result )
12553 {
12554         int e;;
12555         jim_wide w;
12556
12557         e = Jim_GetWide( interp, o, &w );
12558         if( e != JIM_OK ){
12559                 return e;
12560         }
12561
12562         return Jim_Nvp_value2name( interp, p, w, result );
12563 }
12564
12565 Jim_Nvp *
12566 Jim_Nvp_value2name_simple( const Jim_Nvp *p, int value )
12567 {
12568         while( p->name ){
12569                 if( value == p->value ){
12570                         break;
12571                 }
12572                 p++;
12573         }
12574         return ((Jim_Nvp *)(p));
12575 }
12576
12577
12578 int 
12579 Jim_Nvp_value2name( Jim_Interp *interp, const Jim_Nvp *_p, int value, Jim_Nvp **result )
12580 {
12581         const Jim_Nvp *p;
12582
12583         p = Jim_Nvp_value2name_simple( _p, value );
12584
12585         if( result ){
12586                 *result = (Jim_Nvp *)(p);
12587         }
12588
12589         if( p->name ){
12590                 return JIM_OK;
12591         } else {
12592                 return JIM_ERR;
12593         }
12594 }
12595
12596
12597 int
12598 Jim_GetOpt_Setup( Jim_GetOptInfo *p, Jim_Interp *interp, int argc, Jim_Obj * const *  argv)
12599 {
12600         memset( p, 0, sizeof(*p) );
12601         p->interp = interp;
12602         p->argc   = argc;
12603         p->argv   = argv;
12604
12605         return JIM_OK;
12606 }
12607
12608 void
12609 Jim_GetOpt_Debug( Jim_GetOptInfo *p )
12610 {
12611         int x;
12612
12613         Jim_fprintf( p->interp, p->interp->cookie_stderr, "---args---\n");
12614         for( x = 0 ; x < p->argc ; x++ ){
12615                 Jim_fprintf( p->interp, p->interp->cookie_stderr, 
12616                                          "%2d) %s\n", 
12617                                          x, 
12618                                          Jim_GetString( p->argv[x], NULL ) );
12619         }
12620         Jim_fprintf( p->interp, p->interp->cookie_stderr, "-------\n");
12621 }
12622
12623
12624 int
12625 Jim_GetOpt_Obj( Jim_GetOptInfo *goi, Jim_Obj **puthere )
12626 {
12627         Jim_Obj *o;
12628         
12629         o = NULL; // failure 
12630         if( goi->argc ){
12631                 // success 
12632                 o = goi->argv[0];
12633                 goi->argc -= 1;
12634                 goi->argv += 1;
12635         }
12636         if( puthere ){
12637                 *puthere = o;
12638         }
12639         if( o != NULL ){
12640                 return JIM_OK;
12641         } else {
12642                 return JIM_ERR;
12643         }
12644 }
12645
12646 int
12647 Jim_GetOpt_String( Jim_GetOptInfo *goi, char **puthere, int *len )
12648 {
12649         int r;
12650         Jim_Obj *o;
12651         const char *cp;
12652
12653
12654         r = Jim_GetOpt_Obj( goi, &o );
12655         if( r == JIM_OK ){
12656                 cp = Jim_GetString( o, len );
12657                 if( puthere ){
12658                         /* remove const */
12659                         *puthere = (char *)(cp);
12660                 }
12661         }
12662         return r;
12663 }
12664
12665 int
12666 Jim_GetOpt_Double( Jim_GetOptInfo *goi, double *puthere )
12667 {
12668         int r;
12669         Jim_Obj *o;
12670         double _safe;
12671         
12672         if( puthere == NULL ){
12673                 puthere = &_safe;
12674         }
12675
12676         r = Jim_GetOpt_Obj( goi, &o );
12677         if( r == JIM_OK ){
12678                 r = Jim_GetDouble( goi->interp, o, puthere );
12679                 if( r != JIM_OK ){
12680                         Jim_SetResult_sprintf( goi->interp,
12681                                                                    "not a number: %s", 
12682                                                                    Jim_GetString( o, NULL ) );
12683                 }
12684         }
12685         return r;
12686 }
12687
12688 int
12689 Jim_GetOpt_Wide( Jim_GetOptInfo *goi, jim_wide *puthere )
12690 {
12691         int r;
12692         Jim_Obj *o;
12693         jim_wide _safe;
12694
12695         if( puthere == NULL ){
12696                 puthere = &_safe;
12697         }
12698
12699         r = Jim_GetOpt_Obj( goi, &o );
12700         if( r == JIM_OK ){
12701                 r = Jim_GetWide( goi->interp, o, puthere );
12702         }
12703         return r;
12704 }
12705
12706 int Jim_GetOpt_Nvp( Jim_GetOptInfo *goi, 
12707                                         const Jim_Nvp *nvp, 
12708                                         Jim_Nvp **puthere)
12709 {
12710         Jim_Nvp *_safe;
12711         Jim_Obj *o;
12712         int e;
12713
12714         if( puthere == NULL ){
12715                 puthere = &_safe;
12716         }
12717
12718         e = Jim_GetOpt_Obj( goi, &o );
12719         if( e == JIM_OK ){
12720                 e = Jim_Nvp_name2value_obj( goi->interp,
12721                                                                         nvp, 
12722                                                                         o,
12723                                                                         puthere );
12724         }
12725
12726         return e;
12727 }
12728
12729 void
12730 Jim_GetOpt_NvpUnknown( Jim_GetOptInfo *goi,
12731                                            const Jim_Nvp *nvptable,
12732                                            int hadprefix )
12733 {
12734         if( hadprefix ){
12735                 Jim_SetResult_NvpUnknown( goi->interp,
12736                                                                   goi->argv[-2],
12737                                                                   goi->argv[-1],
12738                                                                   nvptable );
12739         } else {
12740                 Jim_SetResult_NvpUnknown( goi->interp,
12741                                                                   NULL,
12742                                                                   goi->argv[-1],
12743                                                                   nvptable );
12744         }
12745 }
12746                                            
12747
12748 int 
12749 Jim_GetOpt_Enum( Jim_GetOptInfo *goi,
12750                                  const char * const *  lookup,
12751                                  int *puthere)
12752 {
12753         int _safe;
12754         Jim_Obj *o;
12755         int e;
12756
12757         if( puthere == NULL ){
12758                 puthere = &_safe;
12759         }
12760         e = Jim_GetOpt_Obj( goi, &o );
12761         if( e == JIM_OK ){
12762                 e = Jim_GetEnum( goi->interp,
12763                                                  o,
12764                                                  lookup,
12765                                                  puthere,
12766                                                  "option",
12767                                                  JIM_ERRMSG );
12768         }
12769         return e;
12770 }
12771         
12772
12773
12774 int
12775 Jim_SetResult_sprintf( Jim_Interp *interp, const char *fmt,... )
12776 {
12777         va_list ap;
12778         char *buf;
12779
12780         va_start(ap,fmt);
12781         buf = jim_vasprintf( fmt, ap );
12782         va_end(ap);
12783         if( buf ){
12784                 Jim_SetResultString( interp, buf, -1 );
12785                 jim_vasprintf_done(buf);
12786         }
12787         return JIM_OK;
12788 }
12789         
12790
12791 void
12792 Jim_SetResult_NvpUnknown( Jim_Interp *interp, 
12793                                                   Jim_Obj *param_name,
12794                                                   Jim_Obj *param_value,
12795                                                   const Jim_Nvp *nvp )
12796 {
12797         if( param_name ){
12798                 Jim_SetResult_sprintf( interp,
12799                                                            "%s: Unknown: %s, try one of: ",
12800                                                            Jim_GetString( param_name, NULL ),
12801                                                            Jim_GetString( param_value, NULL ) );
12802         } else {
12803                 Jim_SetResult_sprintf( interp,
12804                                                            "Unknown param: %s, try one of: ",
12805                                                            Jim_GetString( param_value, NULL ) );
12806         }
12807         while( nvp->name ){
12808                 const char *a;
12809                 const char *b;
12810
12811                 if( (nvp+1)->name ){
12812                         a = nvp->name;
12813                         b = ", ";
12814                 } else {
12815                         a = "or ";
12816                         b = nvp->name;
12817                 }
12818                 Jim_AppendStrings( interp,
12819                                                    Jim_GetResult(interp),
12820                                                    a, b, NULL );
12821                 nvp++;
12822         }
12823 }
12824                                                            
12825
12826 static Jim_Obj *debug_string_obj;
12827
12828 const char *
12829 Jim_Debug_ArgvString( Jim_Interp *interp, int argc, Jim_Obj *const *argv )
12830 {
12831         int x;
12832
12833         if( debug_string_obj ){
12834                 Jim_FreeObj( interp, debug_string_obj );
12835         }
12836
12837         debug_string_obj = Jim_NewEmptyStringObj( interp );
12838         for( x = 0 ; x < argc ; x++ ){
12839                 Jim_AppendStrings( interp,
12840                                                    debug_string_obj,
12841                                                    Jim_GetString( argv[x], NULL ),
12842                                                    " ",
12843                                                    NULL );
12844         }
12845
12846         return Jim_GetString( debug_string_obj, NULL );
12847 }
12848
12849         
12850
12851 /*
12852  * Local Variables: ***
12853  * c-basic-offset: 4 ***
12854  * tab-width: 4 ***
12855  * End: ***
12856  */