]> git.sur5r.net Git - openocd/blob - src/jim.c
4cf4f3dea6abb88dad406756cacdd0d9533d7dfa
[openocd] / src / jim.c
1 /* Jim - A small embeddable Tcl interpreter
2  * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
3  * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * A copy of the license is also included in the source distribution
12  * of Jim, as a TXT file name called LICENSE.
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #define __JIM_CORE__
22 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
23
24 #ifdef __ECOS
25 #include <pkgconf/jimtcl.h>
26 #endif
27 #ifndef JIM_ANSIC
28 #define JIM_DYNLIB      /* Dynamic library support for UNIX and WIN32 */
29 #endif /* JIM_ANSIC */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <ctype.h>
36 #include <limits.h>
37 #include <assert.h>
38 #include <errno.h>
39 #include <time.h>
40
41 /* Include the platform dependent libraries for
42  * dynamic loading of libraries. */
43 #ifdef JIM_DYNLIB
44 #if defined(_WIN32) || defined(WIN32)
45 #ifndef WIN32
46 #define WIN32 1
47 #endif
48 #define STRICT
49 #define WIN32_LEAN_AND_MEAN
50 #include <windows.h>
51 #if _MSC_VER >= 1000
52 #pragma warning(disable:4146)
53 #endif /* _MSC_VER */
54 #else
55 #include <dlfcn.h>
56 #endif /* WIN32 */
57 #endif /* JIM_DYNLIB */
58
59 #ifdef __ECOS
60 #include <cyg/jimtcl/jim.h>
61 #else
62 #include "jim.h"
63 #endif
64
65 #ifdef HAVE_BACKTRACE
66 #include <execinfo.h>
67 #endif
68
69 /* -----------------------------------------------------------------------------
70  * Global variables
71  * ---------------------------------------------------------------------------*/
72
73 /* A shared empty string for the objects string representation.
74  * Jim_InvalidateStringRep knows about it and don't try to free. */
75 static char *JimEmptyStringRep = (char*) "";
76
77 /* -----------------------------------------------------------------------------
78  * Required prototypes of not exported functions
79  * ---------------------------------------------------------------------------*/
80 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
81 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
82 static void JimRegisterCoreApi(Jim_Interp *interp);
83
84 static Jim_HashTableType JimVariablesHashTableType;
85
86 /* -----------------------------------------------------------------------------
87  * Utility functions
88  * ---------------------------------------------------------------------------*/
89
90 /*
91  * Convert a string to a jim_wide INTEGER.
92  * This function originates from BSD.
93  *
94  * Ignores `locale' stuff.  Assumes that the upper and lower case
95  * alphabets and digits are each contiguous.
96  */
97 #ifdef HAVE_LONG_LONG
98 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
99 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
100 {
101     register const char *s;
102     register unsigned jim_wide acc;
103     register unsigned char c;
104     register unsigned jim_wide qbase, cutoff;
105     register int neg, any, cutlim;
106
107     /*
108      * Skip white space and pick up leading +/- sign if any.
109      * If base is 0, allow 0x for hex and 0 for octal, else
110      * assume decimal; if base is already 16, allow 0x.
111      */
112     s = nptr;
113     do {
114         c = *s++;
115     } while (isspace(c));
116     if (c == '-') {
117         neg = 1;
118         c = *s++;
119     } else {
120         neg = 0;
121         if (c == '+')
122             c = *s++;
123     }
124     if ((base == 0 || base == 16) &&
125         c == '0' && (*s == 'x' || *s == 'X')) {
126         c = s[1];
127         s += 2;
128         base = 16;
129     }
130     if (base == 0)
131         base = c == '0' ? 8 : 10;
132
133     /*
134      * Compute the cutoff value between legal numbers and illegal
135      * numbers.  That is the largest legal value, divided by the
136      * base.  An input number that is greater than this value, if
137      * followed by a legal input character, is too big.  One that
138      * is equal to this value may be valid or not; the limit
139      * between valid and invalid numbers is then based on the last
140      * digit.  For instance, if the range for quads is
141      * [-9223372036854775808..9223372036854775807] and the input base
142      * is 10, cutoff will be set to 922337203685477580 and cutlim to
143      * either 7 (neg==0) or 8 (neg==1), meaning that if we have
144      * accumulated a value > 922337203685477580, or equal but the
145      * next digit is > 7 (or 8), the number is too big, and we will
146      * return a range error.
147      *
148      * Set any if any `digits' consumed; make it negative to indicate
149      * overflow.
150      */
151     qbase = (unsigned)base;
152     cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
153         : LLONG_MAX;
154     cutlim = (int)(cutoff % qbase);
155     cutoff /= qbase;
156     for (acc = 0, any = 0;; c = *s++) {
157         if (!JimIsAscii(c))
158             break;
159         if (isdigit(c))
160             c -= '0';
161         else if (isalpha(c))
162             c -= isupper(c) ? 'A' - 10 : 'a' - 10;
163         else
164             break;
165         if (c >= base)
166             break;
167         if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
168             any = -1;
169         else {
170             any = 1;
171             acc *= qbase;
172             acc += c;
173         }
174     }
175     if (any < 0) {
176         acc = neg ? LLONG_MIN : LLONG_MAX;
177         errno = ERANGE;
178     } else if (neg)
179         acc = -acc;
180     if (endptr != 0)
181         *endptr = (char *)(any ? s - 1 : nptr);
182     return (acc);
183 }
184 #endif
185
186 /* Glob-style pattern matching. */
187 static int JimStringMatch(const char *pattern, int patternLen,
188         const char *string, int stringLen, int nocase)
189 {
190     while(patternLen) {
191         switch(pattern[0]) {
192         case '*':
193             while (pattern[1] == '*') {
194                 pattern++;
195                 patternLen--;
196             }
197             if (patternLen == 1)
198                 return 1; /* match */
199             while(stringLen) {
200                 if (JimStringMatch(pattern+1, patternLen-1,
201                             string, stringLen, nocase))
202                     return 1; /* match */
203                 string++;
204                 stringLen--;
205             }
206             return 0; /* no match */
207             break;
208         case '?':
209             if (stringLen == 0)
210                 return 0; /* no match */
211             string++;
212             stringLen--;
213             break;
214         case '[':
215         {
216             int not, match;
217
218             pattern++;
219             patternLen--;
220             not = pattern[0] == '^';
221             if (not) {
222                 pattern++;
223                 patternLen--;
224             }
225             match = 0;
226             while(1) {
227                 if (pattern[0] == '\\') {
228                     pattern++;
229                     patternLen--;
230                     if (pattern[0] == string[0])
231                         match = 1;
232                 } else if (pattern[0] == ']') {
233                     break;
234                 } else if (patternLen == 0) {
235                     pattern--;
236                     patternLen++;
237                     break;
238                 } else if (pattern[1] == '-' && patternLen >= 3) {
239                     int start = pattern[0];
240                     int end = pattern[2];
241                     int c = string[0];
242                     if (start > end) {
243                         int t = start;
244                         start = end;
245                         end = t;
246                     }
247                     if (nocase) {
248                         start = tolower(start);
249                         end = tolower(end);
250                         c = tolower(c);
251                     }
252                     pattern += 2;
253                     patternLen -= 2;
254                     if (c >= start && c <= end)
255                         match = 1;
256                 } else {
257                     if (!nocase) {
258                         if (pattern[0] == string[0])
259                             match = 1;
260                     } else {
261                         if (tolower((int)pattern[0]) == tolower((int)string[0]))
262                             match = 1;
263                     }
264                 }
265                 pattern++;
266                 patternLen--;
267             }
268             if (not)
269                 match = !match;
270             if (!match)
271                 return 0; /* no match */
272             string++;
273             stringLen--;
274             break;
275         }
276         case '\\':
277             if (patternLen >= 2) {
278                 pattern++;
279                 patternLen--;
280             }
281             /* fall through */
282         default:
283             if (!nocase) {
284                 if (pattern[0] != string[0])
285                     return 0; /* no match */
286             } else {
287                 if (tolower((int)pattern[0]) != tolower((int)string[0]))
288                     return 0; /* no match */
289             }
290             string++;
291             stringLen--;
292             break;
293         }
294         pattern++;
295         patternLen--;
296         if (stringLen == 0) {
297             while(*pattern == '*') {
298                 pattern++;
299                 patternLen--;
300             }
301             break;
302         }
303     }
304     if (patternLen == 0 && stringLen == 0)
305         return 1;
306     return 0;
307 }
308
309 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
310         int nocase)
311 {
312     unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
313
314     if (nocase == 0) {
315         while(l1 && l2) {
316             if (*u1 != *u2)
317                 return (int)*u1-*u2;
318             u1++; u2++; l1--; l2--;
319         }
320         if (!l1 && !l2) return 0;
321         return l1-l2;
322     } else {
323         while(l1 && l2) {
324             if (tolower((int)*u1) != tolower((int)*u2))
325                 return tolower((int)*u1)-tolower((int)*u2);
326             u1++; u2++; l1--; l2--;
327         }
328         if (!l1 && !l2) return 0;
329         return l1-l2;
330     }
331 }
332
333 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
334  * The index of the first occurrence of s1 in s2 is returned. 
335  * If s1 is not found inside s2, -1 is returned. */
336 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
337 {
338     int i;
339
340     if (!l1 || !l2 || l1 > l2) return -1;
341     if (index < 0) index = 0;
342     s2 += index;
343     for (i = index; i <= l2-l1; i++) {
344         if (memcmp(s2, s1, l1) == 0)
345             return i;
346         s2++;
347     }
348     return -1;
349 }
350
351 int Jim_WideToString(char *buf, jim_wide wideValue)
352 {
353     const char *fmt = "%" JIM_WIDE_MODIFIER;
354     return sprintf(buf, fmt, wideValue);
355 }
356
357 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
358 {
359     char *endptr;
360
361 #ifdef HAVE_LONG_LONG
362     *widePtr = JimStrtoll(str, &endptr, base);
363 #else
364     *widePtr = strtol(str, &endptr, base);
365 #endif
366     if ((str[0] == '\0') || (str == endptr) )
367         return JIM_ERR;
368     if (endptr[0] != '\0') {
369         while(*endptr) {
370             if (!isspace((int)*endptr))
371                 return JIM_ERR;
372             endptr++;
373         }
374     }
375     return JIM_OK;
376 }
377
378 int Jim_StringToIndex(const char *str, int *intPtr)
379 {
380     char *endptr;
381
382     *intPtr = strtol(str, &endptr, 10);
383     if ( (str[0] == '\0') || (str == endptr) )
384         return JIM_ERR;
385     if (endptr[0] != '\0') {
386         while(*endptr) {
387             if (!isspace((int)*endptr))
388                 return JIM_ERR;
389             endptr++;
390         }
391     }
392     return JIM_OK;
393 }
394
395 /* The string representation of references has two features in order
396  * to make the GC faster. The first is that every reference starts
397  * with a non common character '~', in order to make the string matching
398  * fater. The second is that the reference string rep his 32 characters
399  * in length, this allows to avoid to check every object with a string
400  * repr < 32, and usually there are many of this objects. */
401
402 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
403
404 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
405 {
406     const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
407     sprintf(buf, fmt, refPtr->tag, id);
408     return JIM_REFERENCE_SPACE;
409 }
410
411 int Jim_DoubleToString(char *buf, double doubleValue)
412 {
413     char *s;
414     int len;
415
416     len = sprintf(buf, "%.17g", doubleValue);
417     s = buf;
418     while(*s) {
419         if (*s == '.') return len;
420         s++;
421     }
422     /* Add a final ".0" if it's a number. But not
423      * for NaN or InF */
424     if (isdigit((int)buf[0])
425         || ((buf[0] == '-' || buf[0] == '+')
426             && isdigit((int)buf[1]))) {
427         s[0] = '.';
428         s[1] = '0';
429         s[2] = '\0';
430         return len+2;
431     }
432     return len;
433 }
434
435 int Jim_StringToDouble(const char *str, double *doublePtr)
436 {
437     char *endptr;
438
439     *doublePtr = strtod(str, &endptr);
440     if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
441         return JIM_ERR;
442     return JIM_OK;
443 }
444
445 static jim_wide JimPowWide(jim_wide b, jim_wide e)
446 {
447     jim_wide i, res = 1;
448     if ((b==0 && e!=0) || (e<0)) return 0;
449     for(i=0; i<e; i++) {res *= b;}
450     return res;
451 }
452
453 /* -----------------------------------------------------------------------------
454  * Special functions
455  * ---------------------------------------------------------------------------*/
456
457 /* Note that 'interp' may be NULL if not available in the
458  * context of the panic. It's only useful to get the error
459  * file descriptor, it will default to stderr otherwise. */
460 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
461 {
462     va_list ap;
463
464     va_start(ap, fmt);
465         /* 
466          * Send it here first.. Assuming STDIO still works
467          */
468     fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
469     vfprintf(stderr, fmt, ap);
470     fprintf(stderr, JIM_NL JIM_NL);
471     va_end(ap);
472
473 #ifdef HAVE_BACKTRACE
474     {
475         void *array[40];
476         int size, i;
477         char **strings;
478
479         size = backtrace(array, 40);
480         strings = backtrace_symbols(array, size);
481         for (i = 0; i < size; i++)
482             fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
483         fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
484         fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
485     }
486 #endif
487         
488         /* This may actually crash... we do it last */
489         if( interp && interp->cookie_stderr ){
490                 Jim_fprintf(  interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
491                 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
492                 Jim_fprintf(  interp, interp->cookie_stderr, JIM_NL JIM_NL );
493         }
494     abort();
495 }
496
497 /* -----------------------------------------------------------------------------
498  * Memory allocation
499  * ---------------------------------------------------------------------------*/
500
501 /* Macro used for memory debugging.
502  * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
503  * and similary for Jim_Realloc and Jim_Free */
504 #if 0
505 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
506 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
507 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
508 #endif
509
510 void *Jim_Alloc(int size)
511 {
512         /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
513         if (size==0)
514                 size=1;
515     void *p = malloc(size);
516     if (p == NULL)
517         Jim_Panic(NULL,"malloc: Out of memory");
518     return p;
519 }
520
521 void Jim_Free(void *ptr) {
522     free(ptr);
523 }
524
525 void *Jim_Realloc(void *ptr, int size)
526 {
527         /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
528         if (size==0)
529                 size=1;
530     void *p = realloc(ptr, size);
531     if (p == NULL)
532         Jim_Panic(NULL,"realloc: Out of memory");
533     return p;
534 }
535
536 char *Jim_StrDup(const char *s)
537 {
538     int l = strlen(s);
539     char *copy = Jim_Alloc(l+1);
540
541     memcpy(copy, s, l+1);
542     return copy;
543 }
544
545 char *Jim_StrDupLen(const char *s, int l)
546 {
547     char *copy = Jim_Alloc(l+1);
548     
549     memcpy(copy, s, l+1);
550     copy[l] = 0;    /* Just to be sure, original could be substring */
551     return copy;
552 }
553
554 /* -----------------------------------------------------------------------------
555  * Time related functions
556  * ---------------------------------------------------------------------------*/
557 /* Returns microseconds of CPU used since start. */
558 static jim_wide JimClock(void)
559 {
560 #if (defined WIN32) && !(defined JIM_ANSIC)
561     LARGE_INTEGER t, f;
562     QueryPerformanceFrequency(&f);
563     QueryPerformanceCounter(&t);
564     return (long)((t.QuadPart * 1000000) / f.QuadPart);
565 #else /* !WIN32 */
566     clock_t clocks = clock();
567
568     return (long)(clocks*(1000000/CLOCKS_PER_SEC));
569 #endif /* WIN32 */
570 }
571
572 /* -----------------------------------------------------------------------------
573  * Hash Tables
574  * ---------------------------------------------------------------------------*/
575
576 /* -------------------------- private prototypes ---------------------------- */
577 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
578 static unsigned int JimHashTableNextPower(unsigned int size);
579 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
580
581 /* -------------------------- hash functions -------------------------------- */
582
583 /* Thomas Wang's 32 bit Mix Function */
584 unsigned int Jim_IntHashFunction(unsigned int key)
585 {
586     key += ~(key << 15);
587     key ^=  (key >> 10);
588     key +=  (key << 3);
589     key ^=  (key >> 6);
590     key += ~(key << 11);
591     key ^=  (key >> 16);
592     return key;
593 }
594
595 /* Identity hash function for integer keys */
596 unsigned int Jim_IdentityHashFunction(unsigned int key)
597 {
598     return key;
599 }
600
601 /* Generic hash function (we are using to multiply by 9 and add the byte
602  * as Tcl) */
603 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
604 {
605     unsigned int h = 0;
606     while(len--)
607         h += (h<<3)+*buf++;
608     return h;
609 }
610
611 /* ----------------------------- API implementation ------------------------- */
612 /* reset an hashtable already initialized with ht_init().
613  * NOTE: This function should only called by ht_destroy(). */
614 static void JimResetHashTable(Jim_HashTable *ht)
615 {
616     ht->table = NULL;
617     ht->size = 0;
618     ht->sizemask = 0;
619     ht->used = 0;
620     ht->collisions = 0;
621 }
622
623 /* Initialize the hash table */
624 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
625         void *privDataPtr)
626 {
627     JimResetHashTable(ht);
628     ht->type = type;
629     ht->privdata = privDataPtr;
630     return JIM_OK;
631 }
632
633 /* Resize the table to the minimal size that contains all the elements,
634  * but with the invariant of a USER/BUCKETS ration near to <= 1 */
635 int Jim_ResizeHashTable(Jim_HashTable *ht)
636 {
637     int minimal = ht->used;
638
639     if (minimal < JIM_HT_INITIAL_SIZE)
640         minimal = JIM_HT_INITIAL_SIZE;
641     return Jim_ExpandHashTable(ht, minimal);
642 }
643
644 /* Expand or create the hashtable */
645 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
646 {
647     Jim_HashTable n; /* the new hashtable */
648     unsigned int realsize = JimHashTableNextPower(size), i;
649
650     /* the size is invalid if it is smaller than the number of
651      * elements already inside the hashtable */
652     if (ht->used >= size)
653         return JIM_ERR;
654
655     Jim_InitHashTable(&n, ht->type, ht->privdata);
656     n.size = realsize;
657     n.sizemask = realsize-1;
658     n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
659
660     /* Initialize all the pointers to NULL */
661     memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
662
663     /* Copy all the elements from the old to the new table:
664      * note that if the old hash table is empty ht->size is zero,
665      * so Jim_ExpandHashTable just creates an hash table. */
666     n.used = ht->used;
667     for (i = 0; i < ht->size && ht->used > 0; i++) {
668         Jim_HashEntry *he, *nextHe;
669
670         if (ht->table[i] == NULL) continue;
671         
672         /* For each hash entry on this slot... */
673         he = ht->table[i];
674         while(he) {
675             unsigned int h;
676
677             nextHe = he->next;
678             /* Get the new element index */
679             h = Jim_HashKey(ht, he->key) & n.sizemask;
680             he->next = n.table[h];
681             n.table[h] = he;
682             ht->used--;
683             /* Pass to the next element */
684             he = nextHe;
685         }
686     }
687     assert(ht->used == 0);
688     Jim_Free(ht->table);
689
690     /* Remap the new hashtable in the old */
691     *ht = n;
692     return JIM_OK;
693 }
694
695 /* Add an element to the target hash table */
696 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
697 {
698     int index;
699     Jim_HashEntry *entry;
700
701     /* Get the index of the new element, or -1 if
702      * the element already exists. */
703     if ((index = JimInsertHashEntry(ht, key)) == -1)
704         return JIM_ERR;
705
706     /* Allocates the memory and stores key */
707     entry = Jim_Alloc(sizeof(*entry));
708     entry->next = ht->table[index];
709     ht->table[index] = entry;
710
711     /* Set the hash entry fields. */
712     Jim_SetHashKey(ht, entry, key);
713     Jim_SetHashVal(ht, entry, val);
714     ht->used++;
715     return JIM_OK;
716 }
717
718 /* Add an element, discarding the old if the key already exists */
719 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
720 {
721     Jim_HashEntry *entry;
722
723     /* Try to add the element. If the key
724      * does not exists Jim_AddHashEntry will suceed. */
725     if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
726         return JIM_OK;
727     /* It already exists, get the entry */
728     entry = Jim_FindHashEntry(ht, key);
729     /* Free the old value and set the new one */
730     Jim_FreeEntryVal(ht, entry);
731     Jim_SetHashVal(ht, entry, val);
732     return JIM_OK;
733 }
734
735 /* Search and remove an element */
736 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
737 {
738     unsigned int h;
739     Jim_HashEntry *he, *prevHe;
740
741     if (ht->size == 0)
742         return JIM_ERR;
743     h = Jim_HashKey(ht, key) & ht->sizemask;
744     he = ht->table[h];
745
746     prevHe = NULL;
747     while(he) {
748         if (Jim_CompareHashKeys(ht, key, he->key)) {
749             /* Unlink the element from the list */
750             if (prevHe)
751                 prevHe->next = he->next;
752             else
753                 ht->table[h] = he->next;
754             Jim_FreeEntryKey(ht, he);
755             Jim_FreeEntryVal(ht, he);
756             Jim_Free(he);
757             ht->used--;
758             return JIM_OK;
759         }
760         prevHe = he;
761         he = he->next;
762     }
763     return JIM_ERR; /* not found */
764 }
765
766 /* Destroy an entire hash table */
767 int Jim_FreeHashTable(Jim_HashTable *ht)
768 {
769     unsigned int i;
770
771     /* Free all the elements */
772     for (i = 0; i < ht->size && ht->used > 0; i++) {
773         Jim_HashEntry *he, *nextHe;
774
775         if ((he = ht->table[i]) == NULL) continue;
776         while(he) {
777             nextHe = he->next;
778             Jim_FreeEntryKey(ht, he);
779             Jim_FreeEntryVal(ht, he);
780             Jim_Free(he);
781             ht->used--;
782             he = nextHe;
783         }
784     }
785     /* Free the table and the allocated cache structure */
786     Jim_Free(ht->table);
787     /* Re-initialize the table */
788     JimResetHashTable(ht);
789     return JIM_OK; /* never fails */
790 }
791
792 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
793 {
794     Jim_HashEntry *he;
795     unsigned int h;
796
797     if (ht->size == 0) return NULL;
798     h = Jim_HashKey(ht, key) & ht->sizemask;
799     he = ht->table[h];
800     while(he) {
801         if (Jim_CompareHashKeys(ht, key, he->key))
802             return he;
803         he = he->next;
804     }
805     return NULL;
806 }
807
808 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
809 {
810     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
811
812     iter->ht = ht;
813     iter->index = -1;
814     iter->entry = NULL;
815     iter->nextEntry = NULL;
816     return iter;
817 }
818
819 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
820 {
821     while (1) {
822         if (iter->entry == NULL) {
823             iter->index++;
824             if (iter->index >=
825                     (signed)iter->ht->size) break;
826             iter->entry = iter->ht->table[iter->index];
827         } else {
828             iter->entry = iter->nextEntry;
829         }
830         if (iter->entry) {
831             /* We need to save the 'next' here, the iterator user
832              * may delete the entry we are returning. */
833             iter->nextEntry = iter->entry->next;
834             return iter->entry;
835         }
836     }
837     return NULL;
838 }
839
840 /* ------------------------- private functions ------------------------------ */
841
842 /* Expand the hash table if needed */
843 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
844 {
845     /* If the hash table is empty expand it to the intial size,
846      * if the table is "full" dobule its size. */
847     if (ht->size == 0)
848         return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
849     if (ht->size == ht->used)
850         return Jim_ExpandHashTable(ht, ht->size*2);
851     return JIM_OK;
852 }
853
854 /* Our hash table capability is a power of two */
855 static unsigned int JimHashTableNextPower(unsigned int size)
856 {
857     unsigned int i = JIM_HT_INITIAL_SIZE;
858
859     if (size >= 2147483648U)
860         return 2147483648U;
861     while(1) {
862         if (i >= size)
863             return i;
864         i *= 2;
865     }
866 }
867
868 /* Returns the index of a free slot that can be populated with
869  * an hash entry for the given 'key'.
870  * If the key already exists, -1 is returned. */
871 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
872 {
873     unsigned int h;
874     Jim_HashEntry *he;
875
876     /* Expand the hashtable if needed */
877     if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
878         return -1;
879     /* Compute the key hash value */
880     h = Jim_HashKey(ht, key) & ht->sizemask;
881     /* Search if this slot does not already contain the given key */
882     he = ht->table[h];
883     while(he) {
884         if (Jim_CompareHashKeys(ht, key, he->key))
885             return -1;
886         he = he->next;
887     }
888     return h;
889 }
890
891 /* ----------------------- StringCopy Hash Table Type ------------------------*/
892
893 static unsigned int JimStringCopyHTHashFunction(const void *key)
894 {
895     return Jim_GenHashFunction(key, strlen(key));
896 }
897
898 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
899 {
900     int len = strlen(key);
901     char *copy = Jim_Alloc(len+1);
902     JIM_NOTUSED(privdata);
903
904     memcpy(copy, key, len);
905     copy[len] = '\0';
906     return copy;
907 }
908
909 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
910 {
911     int len = strlen(val);
912     char *copy = Jim_Alloc(len+1);
913     JIM_NOTUSED(privdata);
914
915     memcpy(copy, val, len);
916     copy[len] = '\0';
917     return copy;
918 }
919
920 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
921         const void *key2)
922 {
923     JIM_NOTUSED(privdata);
924
925     return strcmp(key1, key2) == 0;
926 }
927
928 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
929 {
930     JIM_NOTUSED(privdata);
931
932     Jim_Free((void*)key); /* ATTENTION: const cast */
933 }
934
935 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
936 {
937     JIM_NOTUSED(privdata);
938
939     Jim_Free((void*)val); /* ATTENTION: const cast */
940 }
941
942 static Jim_HashTableType JimStringCopyHashTableType = {
943     JimStringCopyHTHashFunction,        /* hash function */
944     JimStringCopyHTKeyDup,              /* key dup */
945     NULL,                               /* val dup */
946     JimStringCopyHTKeyCompare,          /* key compare */
947     JimStringCopyHTKeyDestructor,       /* key destructor */
948     NULL                                /* val destructor */
949 };
950
951 /* This is like StringCopy but does not auto-duplicate the key.
952  * It's used for intepreter's shared strings. */
953 static Jim_HashTableType JimSharedStringsHashTableType = {
954     JimStringCopyHTHashFunction,        /* hash function */
955     NULL,                               /* key dup */
956     NULL,                               /* val dup */
957     JimStringCopyHTKeyCompare,          /* key compare */
958     JimStringCopyHTKeyDestructor,       /* key destructor */
959     NULL                                /* val destructor */
960 };
961
962 /* This is like StringCopy but also automatically handle dynamic
963  * allocated C strings as values. */
964 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
965     JimStringCopyHTHashFunction,        /* hash function */
966     JimStringCopyHTKeyDup,              /* key dup */
967     JimStringKeyValCopyHTValDup,        /* val dup */
968     JimStringCopyHTKeyCompare,          /* key compare */
969     JimStringCopyHTKeyDestructor,       /* key destructor */
970     JimStringKeyValCopyHTValDestructor, /* val destructor */
971 };
972
973 typedef struct AssocDataValue {
974     Jim_InterpDeleteProc *delProc;
975     void *data;
976 } AssocDataValue;
977
978 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
979 {
980     AssocDataValue *assocPtr = (AssocDataValue *)data;
981     if (assocPtr->delProc != NULL)
982         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
983     Jim_Free(data);
984 }
985
986 static Jim_HashTableType JimAssocDataHashTableType = {
987     JimStringCopyHTHashFunction,         /* hash function */
988     JimStringCopyHTKeyDup,               /* key dup */
989     NULL,                                /* val dup */
990     JimStringCopyHTKeyCompare,           /* key compare */
991     JimStringCopyHTKeyDestructor,        /* key destructor */
992     JimAssocDataHashTableValueDestructor /* val destructor */
993 };
994
995 /* -----------------------------------------------------------------------------
996  * Stack - This is a simple generic stack implementation. It is used for
997  * example in the 'expr' expression compiler.
998  * ---------------------------------------------------------------------------*/
999 void Jim_InitStack(Jim_Stack *stack)
1000 {
1001     stack->len = 0;
1002     stack->maxlen = 0;
1003     stack->vector = NULL;
1004 }
1005
1006 void Jim_FreeStack(Jim_Stack *stack)
1007 {
1008     Jim_Free(stack->vector);
1009 }
1010
1011 int Jim_StackLen(Jim_Stack *stack)
1012 {
1013     return stack->len;
1014 }
1015
1016 void Jim_StackPush(Jim_Stack *stack, void *element) {
1017     int neededLen = stack->len+1;
1018     if (neededLen > stack->maxlen) {
1019         stack->maxlen = neededLen*2;
1020         stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1021     }
1022     stack->vector[stack->len] = element;
1023     stack->len++;
1024 }
1025
1026 void *Jim_StackPop(Jim_Stack *stack)
1027 {
1028     if (stack->len == 0) return NULL;
1029     stack->len--;
1030     return stack->vector[stack->len];
1031 }
1032
1033 void *Jim_StackPeek(Jim_Stack *stack)
1034 {
1035     if (stack->len == 0) return NULL;
1036     return stack->vector[stack->len-1];
1037 }
1038
1039 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1040 {
1041     int i;
1042
1043     for (i = 0; i < stack->len; i++)
1044         freeFunc(stack->vector[i]);
1045 }
1046
1047 /* -----------------------------------------------------------------------------
1048  * Parser
1049  * ---------------------------------------------------------------------------*/
1050
1051 /* Token types */
1052 #define JIM_TT_NONE -1        /* No token returned */
1053 #define JIM_TT_STR 0        /* simple string */
1054 #define JIM_TT_ESC 1        /* string that needs escape chars conversion */
1055 #define JIM_TT_VAR 2        /* var substitution */
1056 #define JIM_TT_DICTSUGAR 3    /* Syntax sugar for [dict get], $foo(bar) */
1057 #define JIM_TT_CMD 4        /* command substitution */
1058 #define JIM_TT_SEP 5        /* word separator */
1059 #define JIM_TT_EOL 6        /* line separator */
1060
1061 /* Additional token types needed for expressions */
1062 #define JIM_TT_SUBEXPR_START 7
1063 #define JIM_TT_SUBEXPR_END 8
1064 #define JIM_TT_EXPR_NUMBER 9
1065 #define JIM_TT_EXPR_OPERATOR 10
1066
1067 /* Parser states */
1068 #define JIM_PS_DEF 0        /* Default state */
1069 #define JIM_PS_QUOTE 1        /* Inside "" */
1070
1071 /* Parser context structure. The same context is used both to parse
1072  * Tcl scripts and lists. */
1073 struct JimParserCtx {
1074     const char *prg;     /* Program text */
1075     const char *p;       /* Pointer to the point of the program we are parsing */
1076     int len;             /* Left length of 'prg' */
1077     int linenr;          /* Current line number */
1078     const char *tstart;
1079     const char *tend;    /* Returned token is at tstart-tend in 'prg'. */
1080     int tline;           /* Line number of the returned token */
1081     int tt;              /* Token type */
1082     int eof;             /* Non zero if EOF condition is true. */
1083     int state;           /* Parser state */
1084     int comment;         /* Non zero if the next chars may be a comment. */
1085 };
1086
1087 #define JimParserEof(c) ((c)->eof)
1088 #define JimParserTstart(c) ((c)->tstart)
1089 #define JimParserTend(c) ((c)->tend)
1090 #define JimParserTtype(c) ((c)->tt)
1091 #define JimParserTline(c) ((c)->tline)
1092
1093 static int JimParseScript(struct JimParserCtx *pc);
1094 static int JimParseSep(struct JimParserCtx *pc);
1095 static int JimParseEol(struct JimParserCtx *pc);
1096 static int JimParseCmd(struct JimParserCtx *pc);
1097 static int JimParseVar(struct JimParserCtx *pc);
1098 static int JimParseBrace(struct JimParserCtx *pc);
1099 static int JimParseStr(struct JimParserCtx *pc);
1100 static int JimParseComment(struct JimParserCtx *pc);
1101 static char *JimParserGetToken(struct JimParserCtx *pc,
1102         int *lenPtr, int *typePtr, int *linePtr);
1103
1104 /* Initialize a parser context.
1105  * 'prg' is a pointer to the program text, linenr is the line
1106  * number of the first line contained in the program. */
1107 void JimParserInit(struct JimParserCtx *pc, const char *prg, 
1108         int len, int linenr)
1109 {
1110     pc->prg = prg;
1111     pc->p = prg;
1112     pc->len = len;
1113     pc->tstart = NULL;
1114     pc->tend = NULL;
1115     pc->tline = 0;
1116     pc->tt = JIM_TT_NONE;
1117     pc->eof = 0;
1118     pc->state = JIM_PS_DEF;
1119     pc->linenr = linenr;
1120     pc->comment = 1;
1121 }
1122
1123 int JimParseScript(struct JimParserCtx *pc)
1124 {
1125     while(1) { /* the while is used to reiterate with continue if needed */
1126         if (!pc->len) {
1127             pc->tstart = pc->p;
1128             pc->tend = pc->p-1;
1129             pc->tline = pc->linenr;
1130             pc->tt = JIM_TT_EOL;
1131             pc->eof = 1;
1132             return JIM_OK;
1133         }
1134         switch(*(pc->p)) {
1135         case '\\':
1136             if (*(pc->p+1) == '\n')
1137                 return JimParseSep(pc);
1138             else {
1139                 pc->comment = 0;
1140                 return JimParseStr(pc);
1141             }
1142             break;
1143         case ' ':
1144         case '\t':
1145         case '\r':
1146             if (pc->state == JIM_PS_DEF)
1147                 return JimParseSep(pc);
1148             else {
1149                 pc->comment = 0;
1150                 return JimParseStr(pc);
1151             }
1152             break;
1153         case '\n':
1154         case ';':
1155             pc->comment = 1;
1156             if (pc->state == JIM_PS_DEF)
1157                 return JimParseEol(pc);
1158             else
1159                 return JimParseStr(pc);
1160             break;
1161         case '[':
1162             pc->comment = 0;
1163             return JimParseCmd(pc);
1164             break;
1165         case '$':
1166             pc->comment = 0;
1167             if (JimParseVar(pc) == JIM_ERR) {
1168                 pc->tstart = pc->tend = pc->p++; pc->len--;
1169                 pc->tline = pc->linenr;
1170                 pc->tt = JIM_TT_STR;
1171                 return JIM_OK;
1172             } else
1173                 return JIM_OK;
1174             break;
1175         case '#':
1176             if (pc->comment) {
1177                 JimParseComment(pc);
1178                 continue;
1179             } else {
1180                 return JimParseStr(pc);
1181             }
1182         default:
1183             pc->comment = 0;
1184             return JimParseStr(pc);
1185             break;
1186         }
1187         return JIM_OK;
1188     }
1189 }
1190
1191 int JimParseSep(struct JimParserCtx *pc)
1192 {
1193     pc->tstart = pc->p;
1194     pc->tline = pc->linenr;
1195     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1196            (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1197         if (*pc->p == '\\') {
1198             pc->p++; pc->len--;
1199             pc->linenr++;
1200         }
1201         pc->p++; pc->len--;
1202     }
1203     pc->tend = pc->p-1;
1204     pc->tt = JIM_TT_SEP;
1205     return JIM_OK;
1206 }
1207
1208 int JimParseEol(struct JimParserCtx *pc)
1209 {
1210     pc->tstart = pc->p;
1211     pc->tline = pc->linenr;
1212     while (*pc->p == ' ' || *pc->p == '\n' ||
1213            *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1214         if (*pc->p == '\n')
1215             pc->linenr++;
1216         pc->p++; pc->len--;
1217     }
1218     pc->tend = pc->p-1;
1219     pc->tt = JIM_TT_EOL;
1220     return JIM_OK;
1221 }
1222
1223 /* Todo. Don't stop if ']' appears inside {} or quoted.
1224  * Also should handle the case of puts [string length "]"] */
1225 int JimParseCmd(struct JimParserCtx *pc)
1226 {
1227     int level = 1;
1228     int blevel = 0;
1229
1230     pc->tstart = ++pc->p; pc->len--;
1231     pc->tline = pc->linenr;
1232     while (1) {
1233         if (pc->len == 0) {
1234             break;
1235         } else if (*pc->p == '[' && blevel == 0) {
1236             level++;
1237         } else if (*pc->p == ']' && blevel == 0) {
1238             level--;
1239             if (!level) break;
1240         } else if (*pc->p == '\\') {
1241             pc->p++; pc->len--;
1242         } else if (*pc->p == '{') {
1243             blevel++;
1244         } else if (*pc->p == '}') {
1245             if (blevel != 0)
1246                 blevel--;
1247         } else if (*pc->p == '\n')
1248             pc->linenr++;
1249         pc->p++; pc->len--;
1250     }
1251     pc->tend = pc->p-1;
1252     pc->tt = JIM_TT_CMD;
1253     if (*pc->p == ']') {
1254         pc->p++; pc->len--;
1255     }
1256     return JIM_OK;
1257 }
1258
1259 int JimParseVar(struct JimParserCtx *pc)
1260 {
1261     int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1262
1263     pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1264     pc->tline = pc->linenr;
1265     if (*pc->p == '{') {
1266         pc->tstart = ++pc->p; pc->len--;
1267         brace = 1;
1268     }
1269     if (brace) {
1270         while (!stop) {
1271             if (*pc->p == '}' || pc->len == 0) {
1272                 stop = 1;
1273                 if (pc->len == 0)
1274                     continue;
1275             }
1276             else if (*pc->p == '\n')
1277                 pc->linenr++;
1278             pc->p++; pc->len--;
1279         }
1280         if (pc->len == 0)
1281             pc->tend = pc->p-1;
1282         else
1283             pc->tend = pc->p-2;
1284     } else {
1285         while (!stop) {
1286             if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1287                 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1288                 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1289                 stop = 1;
1290             else {
1291                 pc->p++; pc->len--;
1292             }
1293         }
1294         /* Parse [dict get] syntax sugar. */
1295         if (*pc->p == '(') {
1296             while (*pc->p != ')' && pc->len) {
1297                 pc->p++; pc->len--;
1298                 if (*pc->p == '\\' && pc->len >= 2) {
1299                     pc->p += 2; pc->len -= 2;
1300                 }
1301             }
1302             if (*pc->p != '\0') {
1303                 pc->p++; pc->len--;
1304             }
1305             ttype = JIM_TT_DICTSUGAR;
1306         }
1307         pc->tend = pc->p-1;
1308     }
1309     /* Check if we parsed just the '$' character.
1310      * That's not a variable so an error is returned
1311      * to tell the state machine to consider this '$' just
1312      * a string. */
1313     if (pc->tstart == pc->p) {
1314         pc->p--; pc->len++;
1315         return JIM_ERR;
1316     }
1317     pc->tt = ttype;
1318     return JIM_OK;
1319 }
1320
1321 int JimParseBrace(struct JimParserCtx *pc)
1322 {
1323     int level = 1;
1324
1325     pc->tstart = ++pc->p; pc->len--;
1326     pc->tline = pc->linenr;
1327     while (1) {
1328         if (*pc->p == '\\' && pc->len >= 2) {
1329             pc->p++; pc->len--;
1330             if (*pc->p == '\n')
1331                 pc->linenr++;
1332         } else if (*pc->p == '{') {
1333             level++;
1334         } else if (pc->len == 0 || *pc->p == '}') {
1335             level--;
1336             if (pc->len == 0 || level == 0) {
1337                 pc->tend = pc->p-1;
1338                 if (pc->len != 0) {
1339                     pc->p++; pc->len--;
1340                 }
1341                 pc->tt = JIM_TT_STR;
1342                 return JIM_OK;
1343             }
1344         } else if (*pc->p == '\n') {
1345             pc->linenr++;
1346         }
1347         pc->p++; pc->len--;
1348     }
1349     return JIM_OK; /* unreached */
1350 }
1351
1352 int JimParseStr(struct JimParserCtx *pc)
1353 {
1354     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1355             pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1356     if (newword && *pc->p == '{') {
1357         return JimParseBrace(pc);
1358     } else if (newword && *pc->p == '"') {
1359         pc->state = JIM_PS_QUOTE;
1360         pc->p++; pc->len--;
1361     }
1362     pc->tstart = pc->p;
1363     pc->tline = pc->linenr;
1364     while (1) {
1365         if (pc->len == 0) {
1366             pc->tend = pc->p-1;
1367             pc->tt = JIM_TT_ESC;
1368             return JIM_OK;
1369         }
1370         switch(*pc->p) {
1371         case '\\':
1372             if (pc->state == JIM_PS_DEF &&
1373                 *(pc->p+1) == '\n') {
1374                 pc->tend = pc->p-1;
1375                 pc->tt = JIM_TT_ESC;
1376                 return JIM_OK;
1377             }
1378             if (pc->len >= 2) {
1379                 pc->p++; pc->len--;
1380             }
1381             break;
1382         case '$':
1383         case '[':
1384             pc->tend = pc->p-1;
1385             pc->tt = JIM_TT_ESC;
1386             return JIM_OK;
1387         case ' ':
1388         case '\t':
1389         case '\n':
1390         case '\r':
1391         case ';':
1392             if (pc->state == JIM_PS_DEF) {
1393                 pc->tend = pc->p-1;
1394                 pc->tt = JIM_TT_ESC;
1395                 return JIM_OK;
1396             } else if (*pc->p == '\n') {
1397                 pc->linenr++;
1398             }
1399             break;
1400         case '"':
1401             if (pc->state == JIM_PS_QUOTE) {
1402                 pc->tend = pc->p-1;
1403                 pc->tt = JIM_TT_ESC;
1404                 pc->p++; pc->len--;
1405                 pc->state = JIM_PS_DEF;
1406                 return JIM_OK;
1407             }
1408             break;
1409         }
1410         pc->p++; pc->len--;
1411     }
1412     return JIM_OK; /* unreached */
1413 }
1414
1415 int JimParseComment(struct JimParserCtx *pc)
1416 {
1417     while (*pc->p) {
1418         if (*pc->p == '\n') {
1419             pc->linenr++;
1420             if (*(pc->p-1) != '\\') {
1421                 pc->p++; pc->len--;
1422                 return JIM_OK;
1423             }
1424         }
1425         pc->p++; pc->len--;
1426     }
1427     return JIM_OK;
1428 }
1429
1430 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1431 static int xdigitval(int c)
1432 {
1433     if (c >= '0' && c <= '9') return c-'0';
1434     if (c >= 'a' && c <= 'f') return c-'a'+10;
1435     if (c >= 'A' && c <= 'F') return c-'A'+10;
1436     return -1;
1437 }
1438
1439 static int odigitval(int c)
1440 {
1441     if (c >= '0' && c <= '7') return c-'0';
1442     return -1;
1443 }
1444
1445 /* Perform Tcl escape substitution of 's', storing the result
1446  * string into 'dest'. The escaped string is guaranteed to
1447  * be the same length or shorted than the source string.
1448  * Slen is the length of the string at 's', if it's -1 the string
1449  * length will be calculated by the function.
1450  *
1451  * The function returns the length of the resulting string. */
1452 static int JimEscape(char *dest, const char *s, int slen)
1453 {
1454     char *p = dest;
1455     int i, len;
1456     
1457     if (slen == -1)
1458         slen = strlen(s);
1459
1460     for (i = 0; i < slen; i++) {
1461         switch(s[i]) {
1462         case '\\':
1463             switch(s[i+1]) {
1464             case 'a': *p++ = 0x7; i++; break;
1465             case 'b': *p++ = 0x8; i++; break;
1466             case 'f': *p++ = 0xc; i++; break;
1467             case 'n': *p++ = 0xa; i++; break;
1468             case 'r': *p++ = 0xd; i++; break;
1469             case 't': *p++ = 0x9; i++; break;
1470             case 'v': *p++ = 0xb; i++; break;
1471             case '\0': *p++ = '\\'; i++; break;
1472             case '\n': *p++ = ' '; i++; break;
1473             default:
1474                   if (s[i+1] == 'x') {
1475                     int val = 0;
1476                     int c = xdigitval(s[i+2]);
1477                     if (c == -1) {
1478                         *p++ = 'x';
1479                         i++;
1480                         break;
1481                     }
1482                     val = c;
1483                     c = xdigitval(s[i+3]);
1484                     if (c == -1) {
1485                         *p++ = val;
1486                         i += 2;
1487                         break;
1488                     }
1489                     val = (val*16)+c;
1490                     *p++ = val;
1491                     i += 3;
1492                     break;
1493                   } else if (s[i+1] >= '0' && s[i+1] <= '7')
1494                   {
1495                     int val = 0;
1496                     int c = odigitval(s[i+1]);
1497                     val = c;
1498                     c = odigitval(s[i+2]);
1499                     if (c == -1) {
1500                         *p++ = val;
1501                         i ++;
1502                         break;
1503                     }
1504                     val = (val*8)+c;
1505                     c = odigitval(s[i+3]);
1506                     if (c == -1) {
1507                         *p++ = val;
1508                         i += 2;
1509                         break;
1510                     }
1511                     val = (val*8)+c;
1512                     *p++ = val;
1513                     i += 3;
1514                   } else {
1515                     *p++ = s[i+1];
1516                     i++;
1517                   }
1518                   break;
1519             }
1520             break;
1521         default:
1522             *p++ = s[i];
1523             break;
1524         }
1525     }
1526     len = p-dest;
1527     *p++ = '\0';
1528     return len;
1529 }
1530
1531 /* Returns a dynamically allocated copy of the current token in the
1532  * parser context. The function perform conversion of escapes if
1533  * the token is of type JIM_TT_ESC.
1534  *
1535  * Note that after the conversion, tokens that are grouped with
1536  * braces in the source code, are always recognizable from the
1537  * identical string obtained in a different way from the type.
1538  *
1539  * For exmple the string:
1540  *
1541  * {expand}$a
1542  * 
1543  * will return as first token "expand", of type JIM_TT_STR
1544  *
1545  * While the string:
1546  *
1547  * expand$a
1548  *
1549  * will return as first token "expand", of type JIM_TT_ESC
1550  */
1551 char *JimParserGetToken(struct JimParserCtx *pc,
1552         int *lenPtr, int *typePtr, int *linePtr)
1553 {
1554     const char *start, *end;
1555     char *token;
1556     int len;
1557
1558     start = JimParserTstart(pc);
1559     end = JimParserTend(pc);
1560     if (start > end) {
1561         if (lenPtr) *lenPtr = 0;
1562         if (typePtr) *typePtr = JimParserTtype(pc);
1563         if (linePtr) *linePtr = JimParserTline(pc);
1564         token = Jim_Alloc(1);
1565         token[0] = '\0';
1566         return token;
1567     }
1568     len = (end-start)+1;
1569     token = Jim_Alloc(len+1);
1570     if (JimParserTtype(pc) != JIM_TT_ESC) {
1571         /* No escape conversion needed? Just copy it. */
1572         memcpy(token, start, len);
1573         token[len] = '\0';
1574     } else {
1575         /* Else convert the escape chars. */
1576         len = JimEscape(token, start, len);
1577     }
1578     if (lenPtr) *lenPtr = len;
1579     if (typePtr) *typePtr = JimParserTtype(pc);
1580     if (linePtr) *linePtr = JimParserTline(pc);
1581     return token;
1582 }
1583
1584 /* The following functin is not really part of the parsing engine of Jim,
1585  * but it somewhat related. Given an string and its length, it tries
1586  * to guess if the script is complete or there are instead " " or { }
1587  * open and not completed. This is useful for interactive shells
1588  * implementation and for [info complete].
1589  *
1590  * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1591  * '{' on scripts incomplete missing one or more '}' to be balanced.
1592  * '"' on scripts incomplete missing a '"' char.
1593  *
1594  * If the script is complete, 1 is returned, otherwise 0. */
1595 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1596 {
1597     int level = 0;
1598     int state = ' ';
1599
1600     while(len) {
1601         switch (*s) {
1602             case '\\':
1603                 if (len > 1)
1604                     s++;
1605                 break;
1606             case '"':
1607                 if (state == ' ') {
1608                     state = '"';
1609                 } else if (state == '"') {
1610                     state = ' ';
1611                 }
1612                 break;
1613             case '{':
1614                 if (state == '{') {
1615                     level++;
1616                 } else if (state == ' ') {
1617                     state = '{';
1618                     level++;
1619                 }
1620                 break;
1621             case '}':
1622                 if (state == '{') {
1623                     level--;
1624                     if (level == 0)
1625                         state = ' ';
1626                 }
1627                 break;
1628         }
1629         s++;
1630         len--;
1631     }
1632     if (stateCharPtr)
1633         *stateCharPtr = state;
1634     return state == ' ';
1635 }
1636
1637 /* -----------------------------------------------------------------------------
1638  * Tcl Lists parsing
1639  * ---------------------------------------------------------------------------*/
1640 static int JimParseListSep(struct JimParserCtx *pc);
1641 static int JimParseListStr(struct JimParserCtx *pc);
1642
1643 int JimParseList(struct JimParserCtx *pc)
1644 {
1645     if (pc->len == 0) {
1646         pc->tstart = pc->tend = pc->p;
1647         pc->tline = pc->linenr;
1648         pc->tt = JIM_TT_EOL;
1649         pc->eof = 1;
1650         return JIM_OK;
1651     }
1652     switch(*pc->p) {
1653     case ' ':
1654     case '\n':
1655     case '\t':
1656     case '\r':
1657         if (pc->state == JIM_PS_DEF)
1658             return JimParseListSep(pc);
1659         else
1660             return JimParseListStr(pc);
1661         break;
1662     default:
1663         return JimParseListStr(pc);
1664         break;
1665     }
1666     return JIM_OK;
1667 }
1668
1669 int JimParseListSep(struct JimParserCtx *pc)
1670 {
1671     pc->tstart = pc->p;
1672     pc->tline = pc->linenr;
1673     while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1674     {
1675         pc->p++; pc->len--;
1676     }
1677     pc->tend = pc->p-1;
1678     pc->tt = JIM_TT_SEP;
1679     return JIM_OK;
1680 }
1681
1682 int JimParseListStr(struct JimParserCtx *pc)
1683 {
1684     int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1685             pc->tt == JIM_TT_NONE);
1686     if (newword && *pc->p == '{') {
1687         return JimParseBrace(pc);
1688     } else if (newword && *pc->p == '"') {
1689         pc->state = JIM_PS_QUOTE;
1690         pc->p++; pc->len--;
1691     }
1692     pc->tstart = pc->p;
1693     pc->tline = pc->linenr;
1694     while (1) {
1695         if (pc->len == 0) {
1696             pc->tend = pc->p-1;
1697             pc->tt = JIM_TT_ESC;
1698             return JIM_OK;
1699         }
1700         switch(*pc->p) {
1701         case '\\':
1702             pc->p++; pc->len--;
1703             break;
1704         case ' ':
1705         case '\t':
1706         case '\n':
1707         case '\r':
1708             if (pc->state == JIM_PS_DEF) {
1709                 pc->tend = pc->p-1;
1710                 pc->tt = JIM_TT_ESC;
1711                 return JIM_OK;
1712             } else if (*pc->p == '\n') {
1713                 pc->linenr++;
1714             }
1715             break;
1716         case '"':
1717             if (pc->state == JIM_PS_QUOTE) {
1718                 pc->tend = pc->p-1;
1719                 pc->tt = JIM_TT_ESC;
1720                 pc->p++; pc->len--;
1721                 pc->state = JIM_PS_DEF;
1722                 return JIM_OK;
1723             }
1724             break;
1725         }
1726         pc->p++; pc->len--;
1727     }
1728     return JIM_OK; /* unreached */
1729 }
1730
1731 /* -----------------------------------------------------------------------------
1732  * Jim_Obj related functions
1733  * ---------------------------------------------------------------------------*/
1734
1735 /* Return a new initialized object. */
1736 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1737 {
1738     Jim_Obj *objPtr;
1739
1740     /* -- Check if there are objects in the free list -- */
1741     if (interp->freeList != NULL) {
1742         /* -- Unlink the object from the free list -- */
1743         objPtr = interp->freeList;
1744         interp->freeList = objPtr->nextObjPtr;
1745     } else {
1746         /* -- No ready to use objects: allocate a new one -- */
1747         objPtr = Jim_Alloc(sizeof(*objPtr));
1748     }
1749
1750     /* Object is returned with refCount of 0. Every
1751      * kind of GC implemented should take care to don't try
1752      * to scan objects with refCount == 0. */
1753     objPtr->refCount = 0;
1754     /* All the other fields are left not initialized to save time.
1755      * The caller will probably want set they to the right
1756      * value anyway. */
1757
1758     /* -- Put the object into the live list -- */
1759     objPtr->prevObjPtr = NULL;
1760     objPtr->nextObjPtr = interp->liveList;
1761     if (interp->liveList)
1762         interp->liveList->prevObjPtr = objPtr;
1763     interp->liveList = objPtr;
1764
1765     return objPtr;
1766 }
1767
1768 /* Free an object. Actually objects are never freed, but
1769  * just moved to the free objects list, where they will be
1770  * reused by Jim_NewObj(). */
1771 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1772 {
1773     /* Check if the object was already freed, panic. */
1774     if (objPtr->refCount != 0)  {
1775         Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1776                 objPtr->refCount);
1777     }
1778     /* Free the internal representation */
1779     Jim_FreeIntRep(interp, objPtr);
1780     /* Free the string representation */
1781     if (objPtr->bytes != NULL) {
1782         if (objPtr->bytes != JimEmptyStringRep)
1783             Jim_Free(objPtr->bytes);
1784     }
1785     /* Unlink the object from the live objects list */
1786     if (objPtr->prevObjPtr)
1787         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1788     if (objPtr->nextObjPtr)
1789         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1790     if (interp->liveList == objPtr)
1791         interp->liveList = objPtr->nextObjPtr;
1792     /* Link the object into the free objects list */
1793     objPtr->prevObjPtr = NULL;
1794     objPtr->nextObjPtr = interp->freeList;
1795     if (interp->freeList)
1796         interp->freeList->prevObjPtr = objPtr;
1797     interp->freeList = objPtr;
1798     objPtr->refCount = -1;
1799 }
1800
1801 /* Invalidate the string representation of an object. */
1802 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1803 {
1804     if (objPtr->bytes != NULL) {
1805         if (objPtr->bytes != JimEmptyStringRep)
1806             Jim_Free(objPtr->bytes);
1807     }
1808     objPtr->bytes = NULL;
1809 }
1810
1811 #define Jim_SetStringRep(o, b, l) \
1812     do { (o)->bytes = b; (o)->length = l; } while (0)
1813
1814 /* Set the initial string representation for an object.
1815  * Does not try to free an old one. */
1816 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1817 {
1818     if (length == 0) {
1819         objPtr->bytes = JimEmptyStringRep;
1820         objPtr->length = 0;
1821     } else {
1822         objPtr->bytes = Jim_Alloc(length+1);
1823         objPtr->length = length;
1824         memcpy(objPtr->bytes, bytes, length);
1825         objPtr->bytes[length] = '\0';
1826     }
1827 }
1828
1829 /* Duplicate an object. The returned object has refcount = 0. */
1830 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1831 {
1832     Jim_Obj *dupPtr;
1833
1834     dupPtr = Jim_NewObj(interp);
1835     if (objPtr->bytes == NULL) {
1836         /* Object does not have a valid string representation. */
1837         dupPtr->bytes = NULL;
1838     } else {
1839         Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1840     }
1841     if (objPtr->typePtr != NULL) {
1842         if (objPtr->typePtr->dupIntRepProc == NULL) {
1843             dupPtr->internalRep = objPtr->internalRep;
1844         } else {
1845             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1846         }
1847         dupPtr->typePtr = objPtr->typePtr;
1848     } else {
1849         dupPtr->typePtr = NULL;
1850     }
1851     return dupPtr;
1852 }
1853
1854 /* Return the string representation for objPtr. If the object
1855  * string representation is invalid, calls the method to create
1856  * a new one starting from the internal representation of the object. */
1857 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1858 {
1859     if (objPtr->bytes == NULL) {
1860         /* Invalid string repr. Generate it. */
1861         if (objPtr->typePtr->updateStringProc == NULL) {
1862             Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1863                 objPtr->typePtr->name);
1864         }
1865         objPtr->typePtr->updateStringProc(objPtr);
1866     }
1867     if (lenPtr)
1868         *lenPtr = objPtr->length;
1869     return objPtr->bytes;
1870 }
1871
1872 /* Just returns the length of the object's string rep */
1873 int Jim_Length(Jim_Obj *objPtr)
1874 {
1875     int len;
1876
1877     Jim_GetString(objPtr, &len);
1878     return len;
1879 }
1880
1881 /* -----------------------------------------------------------------------------
1882  * String Object
1883  * ---------------------------------------------------------------------------*/
1884 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1885 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1886
1887 static Jim_ObjType stringObjType = {
1888     "string",
1889     NULL,
1890     DupStringInternalRep,
1891     NULL,
1892     JIM_TYPE_REFERENCES,
1893 };
1894
1895 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1896 {
1897     JIM_NOTUSED(interp);
1898
1899     /* This is a bit subtle: the only caller of this function
1900      * should be Jim_DuplicateObj(), that will copy the
1901      * string representaion. After the copy, the duplicated
1902      * object will not have more room in teh buffer than
1903      * srcPtr->length bytes. So we just set it to length. */
1904     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1905 }
1906
1907 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1908 {
1909     /* Get a fresh string representation. */
1910     (void) Jim_GetString(objPtr, NULL);
1911     /* Free any other internal representation. */
1912     Jim_FreeIntRep(interp, objPtr);
1913     /* Set it as string, i.e. just set the maxLength field. */
1914     objPtr->typePtr = &stringObjType;
1915     objPtr->internalRep.strValue.maxLength = objPtr->length;
1916     return JIM_OK;
1917 }
1918
1919 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1920 {
1921     Jim_Obj *objPtr = Jim_NewObj(interp);
1922
1923     if (len == -1)
1924         len = strlen(s);
1925     /* Alloc/Set the string rep. */
1926     if (len == 0) {
1927         objPtr->bytes = JimEmptyStringRep;
1928         objPtr->length = 0;
1929     } else {
1930         objPtr->bytes = Jim_Alloc(len+1);
1931         objPtr->length = len;
1932         memcpy(objPtr->bytes, s, len);
1933         objPtr->bytes[len] = '\0';
1934     }
1935
1936     /* No typePtr field for the vanilla string object. */
1937     objPtr->typePtr = NULL;
1938     return objPtr;
1939 }
1940
1941 /* This version does not try to duplicate the 's' pointer, but
1942  * use it directly. */
1943 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
1944 {
1945     Jim_Obj *objPtr = Jim_NewObj(interp);
1946
1947     if (len == -1)
1948         len = strlen(s);
1949     Jim_SetStringRep(objPtr, s, len);
1950     objPtr->typePtr = NULL;
1951     return objPtr;
1952 }
1953
1954 /* Low-level string append. Use it only against objects
1955  * of type "string". */
1956 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
1957 {
1958     int needlen;
1959
1960     if (len == -1)
1961         len = strlen(str);
1962     needlen = objPtr->length + len;
1963     if (objPtr->internalRep.strValue.maxLength < needlen ||
1964         objPtr->internalRep.strValue.maxLength == 0) {
1965         if (objPtr->bytes == JimEmptyStringRep) {
1966             objPtr->bytes = Jim_Alloc((needlen*2)+1);
1967         } else {
1968             objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
1969         }
1970         objPtr->internalRep.strValue.maxLength = needlen*2;
1971     }
1972     memcpy(objPtr->bytes + objPtr->length, str, len);
1973     objPtr->bytes[objPtr->length+len] = '\0';
1974     objPtr->length += len;
1975 }
1976
1977 /* Low-level wrapper to append an object. */
1978 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
1979 {
1980     int len;
1981     const char *str;
1982
1983     str = Jim_GetString(appendObjPtr, &len);
1984     StringAppendString(objPtr, str, len);
1985 }
1986
1987 /* Higher level API to append strings to objects. */
1988 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
1989         int len)
1990 {
1991     if (Jim_IsShared(objPtr))
1992         Jim_Panic(interp,"Jim_AppendString called with shared object");
1993     if (objPtr->typePtr != &stringObjType)
1994         SetStringFromAny(interp, objPtr);
1995     StringAppendString(objPtr, str, len);
1996 }
1997
1998 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
1999         Jim_Obj *appendObjPtr)
2000 {
2001     int len;
2002     const char *str;
2003
2004     str = Jim_GetString(appendObjPtr, &len);
2005     Jim_AppendString(interp, objPtr, str, len);
2006 }
2007
2008 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2009 {
2010     va_list ap;
2011
2012     if (objPtr->typePtr != &stringObjType)
2013         SetStringFromAny(interp, objPtr);
2014     va_start(ap, objPtr);
2015     while (1) {
2016         char *s = va_arg(ap, char*);
2017
2018         if (s == NULL) break;
2019         Jim_AppendString(interp, objPtr, s, -1);
2020     }
2021     va_end(ap);
2022 }
2023
2024 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2025 {
2026     const char *aStr, *bStr;
2027     int aLen, bLen, i;
2028
2029     if (aObjPtr == bObjPtr) return 1;
2030     aStr = Jim_GetString(aObjPtr, &aLen);
2031     bStr = Jim_GetString(bObjPtr, &bLen);
2032     if (aLen != bLen) return 0;
2033     if (nocase == 0)
2034         return memcmp(aStr, bStr, aLen) == 0;
2035     for (i = 0; i < aLen; i++) {
2036         if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2037             return 0;
2038     }
2039     return 1;
2040 }
2041
2042 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2043         int nocase)
2044 {
2045     const char *pattern, *string;
2046     int patternLen, stringLen;
2047
2048     pattern = Jim_GetString(patternObjPtr, &patternLen);
2049     string = Jim_GetString(objPtr, &stringLen);
2050     return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2051 }
2052
2053 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2054         Jim_Obj *secondObjPtr, int nocase)
2055 {
2056     const char *s1, *s2;
2057     int l1, l2;
2058
2059     s1 = Jim_GetString(firstObjPtr, &l1);
2060     s2 = Jim_GetString(secondObjPtr, &l2);
2061     return JimStringCompare(s1, l1, s2, l2, nocase);
2062 }
2063
2064 /* Convert a range, as returned by Jim_GetRange(), into
2065  * an absolute index into an object of the specified length.
2066  * This function may return negative values, or values
2067  * bigger or equal to the length of the list if the index
2068  * is out of range. */
2069 static int JimRelToAbsIndex(int len, int index)
2070 {
2071     if (index < 0)
2072         return len + index;
2073     return index;
2074 }
2075
2076 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2077  * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2078  * for implementation of commands like [string range] and [lrange].
2079  *
2080  * The resulting range is guaranteed to address valid elements of
2081  * the structure. */
2082 static void JimRelToAbsRange(int len, int first, int last,
2083         int *firstPtr, int *lastPtr, int *rangeLenPtr)
2084 {
2085     int rangeLen;
2086
2087     if (first > last) {
2088         rangeLen = 0;
2089     } else {
2090         rangeLen = last-first+1;
2091         if (rangeLen) {
2092             if (first < 0) {
2093                 rangeLen += first;
2094                 first = 0;
2095             }
2096             if (last >= len) {
2097                 rangeLen -= (last-(len-1));
2098                 last = len-1;
2099             }
2100         }
2101     }
2102     if (rangeLen < 0) rangeLen = 0;
2103
2104     *firstPtr = first;
2105     *lastPtr = last;
2106     *rangeLenPtr = rangeLen;
2107 }
2108
2109 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2110         Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2111 {
2112     int first, last;
2113     const char *str;
2114     int len, rangeLen;
2115
2116     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2117         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2118         return NULL;
2119     str = Jim_GetString(strObjPtr, &len);
2120     first = JimRelToAbsIndex(len, first);
2121     last = JimRelToAbsIndex(len, last);
2122     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2123     return Jim_NewStringObj(interp, str+first, rangeLen);
2124 }
2125
2126 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2127 {
2128     char *buf = Jim_Alloc(strObjPtr->length+1);
2129     int i;
2130
2131     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2132     for (i = 0; i < strObjPtr->length; i++)
2133         buf[i] = tolower(buf[i]);
2134     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2135 }
2136
2137 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2138 {
2139     char *buf = Jim_Alloc(strObjPtr->length+1);
2140     int i;
2141
2142     memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2143     for (i = 0; i < strObjPtr->length; i++)
2144         buf[i] = toupper(buf[i]);
2145     return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2146 }
2147
2148 /* This is the core of the [format] command.
2149  * TODO: Lots of things work - via a hack
2150  *       However, no format item can be >= JIM_MAX_FMT 
2151  */
2152 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2153         int objc, Jim_Obj *const *objv)
2154 {
2155     const char *fmt, *_fmt;
2156     int fmtLen;
2157     Jim_Obj *resObjPtr;
2158     
2159
2160     fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2161         _fmt = fmt;
2162     resObjPtr = Jim_NewStringObj(interp, "", 0);
2163     while (fmtLen) {
2164         const char *p = fmt;
2165         char spec[2], c;
2166         jim_wide wideValue;
2167                 double doubleValue;
2168                 /* we cheat and use Sprintf()! */
2169 #define JIM_MAX_FMT 2048
2170                 char sprintf_buf[JIM_MAX_FMT];
2171                 char fmt_str[100];
2172                 char *cp;
2173                 int width;
2174                 int ljust;
2175                 int zpad;
2176                 int spad;
2177                 int altfm;
2178                 int forceplus;
2179
2180         while (*fmt != '%' && fmtLen) {
2181             fmt++; fmtLen--;
2182         }
2183         Jim_AppendString(interp, resObjPtr, p, fmt-p);
2184         if (fmtLen == 0)
2185             break;
2186         fmt++; fmtLen--; /* skip '%' */
2187                 zpad = 0;
2188                 spad = 0;
2189                 width = -1;
2190                 ljust = 0;
2191                 altfm = 0;
2192                 forceplus = 0;
2193     next_fmt:
2194                 if( fmtLen <= 0 ){
2195                         break;
2196                 }
2197                 switch( *fmt ){
2198                         /* terminals */
2199         case 'b': /* binary - not all printfs() do this */
2200                 case 's': /* string */
2201                 case 'i': /* integer */
2202                 case 'd': /* decimal */
2203                 case 'x': /* hex */
2204                 case 'X': /* CAP hex */
2205                 case 'c': /* char */
2206                 case 'o': /* octal */
2207                 case 'u': /* unsigned */
2208                 case 'f': /* float */
2209                         break;
2210                         
2211                         /* non-terminals */
2212                 case '0': /* zero pad */
2213                         zpad = 1;
2214                         *fmt++;  fmtLen--;
2215                         goto next_fmt;
2216                         break;
2217                 case '+':
2218                         forceplus = 1;
2219                         *fmt++;  fmtLen--;
2220                         goto next_fmt;
2221                         break;
2222                 case ' ': /* sign space */
2223                         spad = 1;
2224                         *fmt++;  fmtLen--;
2225                         goto next_fmt;
2226                         break;
2227                 case '-':
2228                         ljust = 1;
2229                         *fmt++;  fmtLen--;
2230                         goto next_fmt;
2231                         break;
2232                 case '#':
2233                         altfm = 1;
2234                         *fmt++; fmtLen--;
2235                         goto next_fmt;
2236                         
2237                 case '1':
2238                 case '2':
2239                 case '3':
2240                 case '4':
2241                 case '5':
2242                 case '6':
2243                 case '7':
2244                 case '8':
2245                 case '9':
2246                         width = 0;
2247                         while( isdigit(*fmt) && (fmtLen > 0) ){
2248                                 width = (width * 10) + (*fmt - '0');
2249                                 fmt++;  fmtLen--;
2250                         }
2251                         goto next_fmt;
2252                 case '*':
2253                         /* suck up the next item as an integer */
2254                         *fmt++;  fmtLen--;
2255                         objc--;
2256                         if( objc <= 0 ){
2257                                 goto not_enough_args;
2258                         }
2259                         if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2260                                 Jim_FreeNewObj(interp, resObjPtr );
2261                                 return NULL;
2262                         }
2263                         width = wideValue;
2264                         if( width < 0 ){
2265                                 ljust = 1;
2266                                 width = -width;
2267                         }
2268                         objv++;
2269                         goto next_fmt;
2270                         break;
2271                 }
2272                 
2273                 
2274                 if (*fmt != '%') {
2275             if (objc == 0) {
2276                         not_enough_args:
2277                 Jim_FreeNewObj(interp, resObjPtr);
2278                 Jim_SetResultString(interp,
2279                                                                         "not enough arguments for all format specifiers", -1);
2280                 return NULL;
2281             } else {
2282                 objc--;
2283             }
2284         }
2285                 
2286                 /*
2287                  * Create the formatter
2288                  * cause we cheat and use sprintf()
2289                  */
2290                 cp = fmt_str;
2291                 *cp++ = '%';
2292                 if( altfm ){
2293                         *cp++ = '#';
2294                 }
2295                 if( forceplus ){
2296                         *cp++ = '+';
2297                 } else if( spad ){
2298                         /* PLUS overrides */
2299                         *cp++ = ' ';
2300                 }
2301                 if( ljust ){
2302                         *cp++ = '-';
2303                 }
2304                 if( zpad  ){
2305                         *cp++ = '0';
2306                 }
2307                 if( width > 0 ){
2308                         sprintf( cp, "%d", width );
2309                         /* skip ahead */
2310                         cp = strchr(cp,0);
2311                 }
2312                 *cp = 0;
2313
2314                 /* here we do the work */
2315                 /* actually - we make sprintf() do it for us */
2316         switch(*fmt) {
2317         case 's':
2318                         *cp++ = 's';
2319                         *cp   = 0;
2320                         /* BUG: we do not handled embeded NULLs */
2321                         snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2322             break;
2323         case 'c':
2324                         *cp++ = 'c';
2325                         *cp   = 0;
2326             if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2327                 Jim_FreeNewObj(interp, resObjPtr);
2328                 return NULL;
2329             }
2330             c = (char) wideValue;
2331                         snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2332             break;
2333                 case 'f':
2334                 case 'F':
2335                 case 'g':
2336                 case 'G':
2337                 case 'e':
2338                 case 'E':
2339                         *cp++ = *fmt;
2340                         *cp   = 0;
2341                         if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2342                                 Jim_FreeNewObj( interp, resObjPtr );
2343                                 return NULL;
2344                         }
2345                         snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2346                         break;
2347         case 'b':
2348         case 'd':
2349                 case 'i':
2350                 case 'u':
2351                 case 'x':
2352                 case 'X':
2353                         /* jim widevaluse are 64bit */
2354                         if( sizeof(jim_wide) == sizeof(long long) ){
2355                                 *cp++ = 'l'; 
2356                                 *cp++ = 'l';
2357                         } else {
2358                                 *cp++ = 'l';
2359                         }
2360                         *cp++ = *fmt;
2361                         *cp   = 0;
2362             if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2363                 Jim_FreeNewObj(interp, resObjPtr);
2364                 return NULL;
2365             }
2366                         snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2367             break;
2368         case '%':
2369                         sprintf_buf[0] = '%';
2370                         sprintf_buf[1] = 0;
2371                         objv--; /* undo the objv++ below */
2372             break;
2373         default:
2374             spec[0] = *fmt; spec[1] = '\0';
2375             Jim_FreeNewObj(interp, resObjPtr);
2376             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2377             Jim_AppendStrings(interp, Jim_GetResult(interp),
2378                     "bad field specifier \"",  spec, "\"", NULL);
2379             return NULL;
2380         }
2381                 /* force terminate */
2382 #if 0
2383                 printf("FMT was: %s\n", fmt_str );
2384                 printf("RES was: |%s|\n", sprintf_buf );
2385 #endif
2386                 
2387                 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2388                 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2389                 /* next obj */
2390                 objv++;
2391         fmt++;
2392         fmtLen--;
2393     }
2394     return resObjPtr;
2395 }
2396
2397 /* -----------------------------------------------------------------------------
2398  * Compared String Object
2399  * ---------------------------------------------------------------------------*/
2400
2401 /* This is strange object that allows to compare a C literal string
2402  * with a Jim object in very short time if the same comparison is done
2403  * multiple times. For example every time the [if] command is executed,
2404  * Jim has to check if a given argument is "else". This comparions if
2405  * the code has no errors are true most of the times, so we can cache
2406  * inside the object the pointer of the string of the last matching
2407  * comparison. Because most C compilers perform literal sharing,
2408  * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2409  * this works pretty well even if comparisons are at different places
2410  * inside the C code. */
2411
2412 static Jim_ObjType comparedStringObjType = {
2413     "compared-string",
2414     NULL,
2415     NULL,
2416     NULL,
2417     JIM_TYPE_REFERENCES,
2418 };
2419
2420 /* The only way this object is exposed to the API is via the following
2421  * function. Returns true if the string and the object string repr.
2422  * are the same, otherwise zero is returned.
2423  *
2424  * Note: this isn't binary safe, but it hardly needs to be.*/
2425 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2426         const char *str)
2427 {
2428     if (objPtr->typePtr == &comparedStringObjType &&
2429         objPtr->internalRep.ptr == str)
2430         return 1;
2431     else {
2432         const char *objStr = Jim_GetString(objPtr, NULL);
2433         if (strcmp(str, objStr) != 0) return 0;
2434         if (objPtr->typePtr != &comparedStringObjType) {
2435             Jim_FreeIntRep(interp, objPtr);
2436             objPtr->typePtr = &comparedStringObjType;
2437         }
2438         objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2439         return 1;
2440     }
2441 }
2442
2443 int qsortCompareStringPointers(const void *a, const void *b)
2444 {
2445     char * const *sa = (char * const *)a;
2446     char * const *sb = (char * const *)b;
2447     return strcmp(*sa, *sb);
2448 }
2449
2450 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2451         const char **tablePtr, int *indexPtr, const char *name, int flags)
2452 {
2453     const char **entryPtr = NULL;
2454     char **tablePtrSorted;
2455     int i, count = 0;
2456
2457     *indexPtr = -1;
2458     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2459         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2460             *indexPtr = i;
2461             return JIM_OK;
2462         }
2463         count++; /* If nothing matches, this will reach the len of tablePtr */
2464     }
2465     if (flags & JIM_ERRMSG) {
2466         if (name == NULL)
2467             name = "option";
2468         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2469         Jim_AppendStrings(interp, Jim_GetResult(interp),
2470             "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2471             NULL);
2472         tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2473         memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2474         qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2475         for (i = 0; i < count; i++) {
2476             if (i+1 == count && count > 1)
2477                 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2478             Jim_AppendString(interp, Jim_GetResult(interp),
2479                     tablePtrSorted[i], -1);
2480             if (i+1 != count)
2481                 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2482         }
2483         Jim_Free(tablePtrSorted);
2484     }
2485     return JIM_ERR;
2486 }
2487
2488 /* -----------------------------------------------------------------------------
2489  * Source Object
2490  *
2491  * This object is just a string from the language point of view, but
2492  * in the internal representation it contains the filename and line number
2493  * where this given token was read. This information is used by
2494  * Jim_EvalObj() if the object passed happens to be of type "source".
2495  *
2496  * This allows to propagate the information about line numbers and file
2497  * names and give error messages with absolute line numbers.
2498  *
2499  * Note that this object uses shared strings for filenames, and the
2500  * pointer to the filename together with the line number is taken into
2501  * the space for the "inline" internal represenation of the Jim_Object,
2502  * so there is almost memory zero-overhead.
2503  *
2504  * Also the object will be converted to something else if the given
2505  * token it represents in the source file is not something to be
2506  * evaluated (not a script), and will be specialized in some other way,
2507  * so the time overhead is alzo null.
2508  * ---------------------------------------------------------------------------*/
2509
2510 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2511 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2512
2513 static Jim_ObjType sourceObjType = {
2514     "source",
2515     FreeSourceInternalRep,
2516     DupSourceInternalRep,
2517     NULL,
2518     JIM_TYPE_REFERENCES,
2519 };
2520
2521 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2522 {
2523     Jim_ReleaseSharedString(interp,
2524             objPtr->internalRep.sourceValue.fileName);
2525 }
2526
2527 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2528 {
2529     dupPtr->internalRep.sourceValue.fileName =
2530         Jim_GetSharedString(interp,
2531                 srcPtr->internalRep.sourceValue.fileName);
2532     dupPtr->internalRep.sourceValue.lineNumber =
2533         dupPtr->internalRep.sourceValue.lineNumber;
2534     dupPtr->typePtr = &sourceObjType;
2535 }
2536
2537 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2538         const char *fileName, int lineNumber)
2539 {
2540     if (Jim_IsShared(objPtr))
2541         Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2542     if (objPtr->typePtr != NULL)
2543         Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2544     objPtr->internalRep.sourceValue.fileName =
2545         Jim_GetSharedString(interp, fileName);
2546     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2547     objPtr->typePtr = &sourceObjType;
2548 }
2549
2550 /* -----------------------------------------------------------------------------
2551  * Script Object
2552  * ---------------------------------------------------------------------------*/
2553
2554 #define JIM_CMDSTRUCT_EXPAND -1
2555
2556 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2557 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2558 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2559
2560 static Jim_ObjType scriptObjType = {
2561     "script",
2562     FreeScriptInternalRep,
2563     DupScriptInternalRep,
2564     NULL,
2565     JIM_TYPE_REFERENCES,
2566 };
2567
2568 /* The ScriptToken structure represents every token into a scriptObj.
2569  * Every token contains an associated Jim_Obj that can be specialized
2570  * by commands operating on it. */
2571 typedef struct ScriptToken {
2572     int type;
2573     Jim_Obj *objPtr;
2574     int linenr;
2575 } ScriptToken;
2576
2577 /* This is the script object internal representation. An array of
2578  * ScriptToken structures, with an associated command structure array.
2579  * The command structure is a pre-computed representation of the
2580  * command length and arguments structure as a simple liner array
2581  * of integers.
2582  * 
2583  * For example the script:
2584  *
2585  * puts hello
2586  * set $i $x$y [foo]BAR
2587  *
2588  * will produce a ScriptObj with the following Tokens:
2589  *
2590  * ESC puts
2591  * SEP
2592  * ESC hello
2593  * EOL
2594  * ESC set
2595  * EOL
2596  * VAR i
2597  * SEP
2598  * VAR x
2599  * VAR y
2600  * SEP
2601  * CMD foo
2602  * ESC BAR
2603  * EOL
2604  *
2605  * This is a description of the tokens, separators, and of lines.
2606  * The command structure instead represents the number of arguments
2607  * of every command, followed by the tokens of which every argument
2608  * is composed. So for the example script, the cmdstruct array will
2609  * contain:
2610  *
2611  * 2 1 1 4 1 1 2 2
2612  *
2613  * Because "puts hello" has two args (2), composed of single tokens (1 1)
2614  * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2615  * composed of single tokens (1 1) and the last two of double tokens
2616  * (2 2).
2617  *
2618  * The precomputation of the command structure makes Jim_Eval() faster,
2619  * and simpler because there aren't dynamic lengths / allocations.
2620  *
2621  * -- {expand} handling --
2622  *
2623  * Expand is handled in a special way. When a command
2624  * contains at least an argument with the {expand} prefix,
2625  * the command structure presents a -1 before the integer
2626  * describing the number of arguments. This is used in order
2627  * to send the command exection to a different path in case
2628  * of {expand} and guarantee a fast path for the more common
2629  * case. Also, the integers describing the number of tokens
2630  * are expressed with negative sign, to allow for fast check
2631  * of what's an {expand}-prefixed argument and what not.
2632  *
2633  * For example the command:
2634  *
2635  * list {expand}{1 2}
2636  *
2637  * Will produce the following cmdstruct array:
2638  *
2639  * -1 2 1 -2
2640  *
2641  * -- the substFlags field of the structure --
2642  *
2643  * The scriptObj structure is used to represent both "script" objects
2644  * and "subst" objects. In the second case, the cmdStruct related
2645  * fields are not used at all, but there is an additional field used
2646  * that is 'substFlags': this represents the flags used to turn
2647  * the string into the intenral representation used to perform the
2648  * substitution. If this flags are not what the application requires
2649  * the scriptObj is created again. For example the script:
2650  *
2651  * subst -nocommands $string
2652  * subst -novariables $string
2653  *
2654  * Will recreate the internal representation of the $string object
2655  * two times.
2656  */
2657 typedef struct ScriptObj {
2658     int len; /* Length as number of tokens. */
2659     int commands; /* number of top-level commands in script. */
2660     ScriptToken *token; /* Tokens array. */
2661     int *cmdStruct; /* commands structure */
2662     int csLen; /* length of the cmdStruct array. */
2663     int substFlags; /* flags used for the compilation of "subst" objects */
2664     int inUse; /* Used to share a ScriptObj. Currently
2665               only used by Jim_EvalObj() as protection against
2666               shimmering of the currently evaluated object. */
2667     char *fileName;
2668 } ScriptObj;
2669
2670 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2671 {
2672     int i;
2673     struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2674
2675     script->inUse--;
2676     if (script->inUse != 0) return;
2677     for (i = 0; i < script->len; i++) {
2678         if (script->token[i].objPtr != NULL)
2679             Jim_DecrRefCount(interp, script->token[i].objPtr);
2680     }
2681     Jim_Free(script->token);
2682     Jim_Free(script->cmdStruct);
2683     Jim_Free(script->fileName);
2684     Jim_Free(script);
2685 }
2686
2687 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2688 {
2689     JIM_NOTUSED(interp);
2690     JIM_NOTUSED(srcPtr);
2691
2692     /* Just returns an simple string. */
2693     dupPtr->typePtr = NULL;
2694 }
2695
2696 /* Add a new token to the internal repr of a script object */
2697 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2698         char *strtoken, int len, int type, char *filename, int linenr)
2699 {
2700     int prevtype;
2701     struct ScriptToken *token;
2702
2703     prevtype = (script->len == 0) ? JIM_TT_EOL : \
2704         script->token[script->len-1].type;
2705     /* Skip tokens without meaning, like words separators
2706      * following a word separator or an end of command and
2707      * so on. */
2708     if (prevtype == JIM_TT_EOL) {
2709         if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2710             Jim_Free(strtoken);
2711             return;
2712         }
2713     } else if (prevtype == JIM_TT_SEP) {
2714         if (type == JIM_TT_SEP) {
2715             Jim_Free(strtoken);
2716             return;
2717         } else if (type == JIM_TT_EOL) {
2718             /* If an EOL is following by a SEP, drop the previous
2719              * separator. */
2720             script->len--;
2721             Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2722         }
2723     } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2724             type == JIM_TT_ESC && len == 0)
2725     {
2726         /* Don't add empty tokens used in interpolation */
2727         Jim_Free(strtoken);
2728         return;
2729     }
2730     /* Make space for a new istruction */
2731     script->len++;
2732     script->token = Jim_Realloc(script->token,
2733             sizeof(ScriptToken)*script->len);
2734     /* Initialize the new token */
2735     token = script->token+(script->len-1);
2736     token->type = type;
2737     /* Every object is intially as a string, but the
2738      * internal type may be specialized during execution of the
2739      * script. */
2740     token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2741     /* To add source info to SEP and EOL tokens is useless because
2742      * they will never by called as arguments of Jim_EvalObj(). */
2743     if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2744         JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2745     Jim_IncrRefCount(token->objPtr);
2746     token->linenr = linenr;
2747 }
2748
2749 /* Add an integer into the command structure field of the script object. */
2750 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2751 {
2752     script->csLen++;
2753     script->cmdStruct = Jim_Realloc(script->cmdStruct,
2754                     sizeof(int)*script->csLen);
2755     script->cmdStruct[script->csLen-1] = val;
2756 }
2757
2758 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2759  * of objPtr. Search nested script objects recursively. */
2760 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2761         ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2762 {
2763     int i;
2764
2765     for (i = 0; i < script->len; i++) {
2766         if (script->token[i].objPtr != objPtr &&
2767             Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2768             return script->token[i].objPtr;
2769         }
2770         /* Enter recursively on scripts only if the object
2771          * is not the same as the one we are searching for
2772          * shared occurrences. */
2773         if (script->token[i].objPtr->typePtr == &scriptObjType &&
2774             script->token[i].objPtr != objPtr) {
2775             Jim_Obj *foundObjPtr;
2776
2777             ScriptObj *subScript =
2778                 script->token[i].objPtr->internalRep.ptr;
2779             /* Don't recursively enter the script we are trying
2780              * to make shared to avoid circular references. */
2781             if (subScript == scriptBarrier) continue;
2782             if (subScript != script) {
2783                 foundObjPtr =
2784                     ScriptSearchLiteral(interp, subScript,
2785                             scriptBarrier, objPtr);
2786                 if (foundObjPtr != NULL)
2787                     return foundObjPtr;
2788             }
2789         }
2790     }
2791     return NULL;
2792 }
2793
2794 /* Share literals of a script recursively sharing sub-scripts literals. */
2795 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2796         ScriptObj *topLevelScript)
2797 {
2798     int i, j;
2799
2800     return;
2801     /* Try to share with toplevel object. */
2802     if (topLevelScript != NULL) {
2803         for (i = 0; i < script->len; i++) {
2804             Jim_Obj *foundObjPtr;
2805             char *str = script->token[i].objPtr->bytes;
2806
2807             if (script->token[i].objPtr->refCount != 1) continue;
2808             if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2809             if (strchr(str, ' ') || strchr(str, '\n')) continue;
2810             foundObjPtr = ScriptSearchLiteral(interp,
2811                     topLevelScript,
2812                     script, /* barrier */
2813                     script->token[i].objPtr);
2814             if (foundObjPtr != NULL) {
2815                 Jim_IncrRefCount(foundObjPtr);
2816                 Jim_DecrRefCount(interp,
2817                         script->token[i].objPtr);
2818                 script->token[i].objPtr = foundObjPtr;
2819             }
2820         }
2821     }
2822     /* Try to share locally */
2823     for (i = 0; i < script->len; i++) {
2824         char *str = script->token[i].objPtr->bytes;
2825
2826         if (script->token[i].objPtr->refCount != 1) continue;
2827         if (strchr(str, ' ') || strchr(str, '\n')) continue;
2828         for (j = 0; j < script->len; j++) {
2829             if (script->token[i].objPtr !=
2830                     script->token[j].objPtr &&
2831                 Jim_StringEqObj(script->token[i].objPtr,
2832                             script->token[j].objPtr, 0))
2833             {
2834                 Jim_IncrRefCount(script->token[j].objPtr);
2835                 Jim_DecrRefCount(interp,
2836                         script->token[i].objPtr);
2837                 script->token[i].objPtr =
2838                     script->token[j].objPtr;
2839             }
2840         }
2841     }
2842 }
2843
2844 /* This method takes the string representation of an object
2845  * as a Tcl script, and generates the pre-parsed internal representation
2846  * of the script. */
2847 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2848 {
2849     int scriptTextLen;
2850     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2851     struct JimParserCtx parser;
2852     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2853     ScriptToken *token;
2854     int args, tokens, start, end, i;
2855     int initialLineNumber;
2856     int propagateSourceInfo = 0;
2857
2858     script->len = 0;
2859     script->csLen = 0;
2860     script->commands = 0;
2861     script->token = NULL;
2862     script->cmdStruct = NULL;
2863     script->inUse = 1;
2864     /* Try to get information about filename / line number */
2865     if (objPtr->typePtr == &sourceObjType) {
2866         script->fileName =
2867             Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2868         initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2869         propagateSourceInfo = 1;
2870     } else {
2871         script->fileName = Jim_StrDup("?");
2872         initialLineNumber = 1;
2873     }
2874
2875     JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2876     while(!JimParserEof(&parser)) {
2877         char *token;
2878         int len, type, linenr;
2879
2880         JimParseScript(&parser);
2881         token = JimParserGetToken(&parser, &len, &type, &linenr);
2882         ScriptObjAddToken(interp, script, token, len, type,
2883                 propagateSourceInfo ? script->fileName : NULL,
2884                 linenr);
2885     }
2886     token = script->token;
2887
2888     /* Compute the command structure array
2889      * (see the ScriptObj struct definition for more info) */
2890     start = 0; /* Current command start token index */
2891     end = -1; /* Current command end token index */
2892     while (1) {
2893         int expand = 0; /* expand flag. set to 1 on {expand} form. */
2894         int interpolation = 0; /* set to 1 if there is at least one
2895                       argument of the command obtained via
2896                       interpolation of more tokens. */
2897         /* Search for the end of command, while
2898          * count the number of args. */
2899         start = ++end;
2900         if (start >= script->len) break;
2901         args = 1; /* Number of args in current command */
2902         while (token[end].type != JIM_TT_EOL) {
2903             if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2904                     token[end-1].type == JIM_TT_EOL)
2905             {
2906                 if (token[end].type == JIM_TT_STR &&
2907                     token[end+1].type != JIM_TT_SEP &&
2908                     token[end+1].type != JIM_TT_EOL &&
2909                     (!strcmp(token[end].objPtr->bytes, "expand") ||
2910                      !strcmp(token[end].objPtr->bytes, "*")))
2911                     expand++;
2912             }
2913             if (token[end].type == JIM_TT_SEP)
2914                 args++;
2915             end++;
2916         }
2917         interpolation = !((end-start+1) == args*2);
2918         /* Add the 'number of arguments' info into cmdstruct.
2919          * Negative value if there is list expansion involved. */
2920         if (expand)
2921             ScriptObjAddInt(script, -1);
2922         ScriptObjAddInt(script, args);
2923         /* Now add info about the number of tokens. */
2924         tokens = 0; /* Number of tokens in current argument. */
2925         expand = 0;
2926         for (i = start; i <= end; i++) {
2927             if (token[i].type == JIM_TT_SEP ||
2928                 token[i].type == JIM_TT_EOL)
2929             {
2930                 if (tokens == 1 && expand)
2931                     expand = 0;
2932                 ScriptObjAddInt(script,
2933                         expand ? -tokens : tokens);
2934
2935                 expand = 0;
2936                 tokens = 0;
2937                 continue;
2938             } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2939                    (!strcmp(token[i].objPtr->bytes, "expand") ||
2940                     !strcmp(token[i].objPtr->bytes, "*")))
2941             {
2942                 expand++;
2943             }
2944             tokens++;
2945         }
2946     }
2947     /* Perform literal sharing, but only for objects that appear
2948      * to be scripts written as literals inside the source code,
2949      * and not computed at runtime. Literal sharing is a costly
2950      * operation that should be done only against objects that
2951      * are likely to require compilation only the first time, and
2952      * then are executed multiple times. */
2953     if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2954         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2955         if (bodyObjPtr->typePtr == &scriptObjType) {
2956             ScriptObj *bodyScript =
2957                 bodyObjPtr->internalRep.ptr;
2958             ScriptShareLiterals(interp, script, bodyScript);
2959         }
2960     } else if (propagateSourceInfo) {
2961         ScriptShareLiterals(interp, script, NULL);
2962     }
2963     /* Free the old internal rep and set the new one. */
2964     Jim_FreeIntRep(interp, objPtr);
2965     Jim_SetIntRepPtr(objPtr, script);
2966     objPtr->typePtr = &scriptObjType;
2967     return JIM_OK;
2968 }
2969
2970 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
2971 {
2972     if (objPtr->typePtr != &scriptObjType) {
2973         SetScriptFromAny(interp, objPtr);
2974     }
2975     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
2976 }
2977
2978 /* -----------------------------------------------------------------------------
2979  * Commands
2980  * ---------------------------------------------------------------------------*/
2981
2982 /* Commands HashTable Type.
2983  *
2984  * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
2985 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
2986 {
2987     Jim_Cmd *cmdPtr = (void*) val;
2988
2989     if (cmdPtr->cmdProc == NULL) {
2990         Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2991         Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2992         if (cmdPtr->staticVars) {
2993             Jim_FreeHashTable(cmdPtr->staticVars);
2994             Jim_Free(cmdPtr->staticVars);
2995         }
2996     } else if (cmdPtr->delProc != NULL) {
2997             /* If it was a C coded command, call the delProc if any */
2998             cmdPtr->delProc(interp, cmdPtr->privData);
2999     }
3000     Jim_Free(val);
3001 }
3002
3003 static Jim_HashTableType JimCommandsHashTableType = {
3004     JimStringCopyHTHashFunction,        /* hash function */
3005     JimStringCopyHTKeyDup,        /* key dup */
3006     NULL,                    /* val dup */
3007     JimStringCopyHTKeyCompare,        /* key compare */
3008     JimStringCopyHTKeyDestructor,        /* key destructor */
3009     Jim_CommandsHT_ValDestructor        /* val destructor */
3010 };
3011
3012 /* ------------------------- Commands related functions --------------------- */
3013
3014 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3015         Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3016 {
3017     Jim_HashEntry *he;
3018     Jim_Cmd *cmdPtr;
3019
3020     he = Jim_FindHashEntry(&interp->commands, cmdName);
3021     if (he == NULL) { /* New command to create */
3022         cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3023         cmdPtr->cmdProc = cmdProc;
3024         cmdPtr->privData = privData;
3025         cmdPtr->delProc = delProc;
3026         Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3027     } else {
3028         Jim_InterpIncrProcEpoch(interp);
3029         /* Free the arglist/body objects if it was a Tcl procedure */
3030         cmdPtr = he->val;
3031         if (cmdPtr->cmdProc == NULL) {
3032             Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3033             Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3034             if (cmdPtr->staticVars) {
3035                 Jim_FreeHashTable(cmdPtr->staticVars);
3036                 Jim_Free(cmdPtr->staticVars);
3037             }
3038             cmdPtr->staticVars = NULL;
3039         } else if (cmdPtr->delProc != NULL) {
3040             /* If it was a C coded command, call the delProc if any */
3041             cmdPtr->delProc(interp, cmdPtr->privData);
3042         }
3043         cmdPtr->cmdProc = cmdProc;
3044         cmdPtr->privData = privData;
3045     }
3046     /* There is no need to increment the 'proc epoch' because
3047      * creation of a new procedure can never affect existing
3048      * cached commands. We don't do negative caching. */
3049     return JIM_OK;
3050 }
3051
3052 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3053         Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3054         int arityMin, int arityMax)
3055 {
3056     Jim_Cmd *cmdPtr;
3057
3058     cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3059     cmdPtr->cmdProc = NULL; /* Not a C coded command */
3060     cmdPtr->argListObjPtr = argListObjPtr;
3061     cmdPtr->bodyObjPtr = bodyObjPtr;
3062     Jim_IncrRefCount(argListObjPtr);
3063     Jim_IncrRefCount(bodyObjPtr);
3064     cmdPtr->arityMin = arityMin;
3065     cmdPtr->arityMax = arityMax;
3066     cmdPtr->staticVars = NULL;
3067    
3068     /* Create the statics hash table. */
3069     if (staticsListObjPtr) {
3070         int len, i;
3071
3072         Jim_ListLength(interp, staticsListObjPtr, &len);
3073         if (len != 0) {
3074             cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3075             Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
3076                     interp);
3077             for (i = 0; i < len; i++) {
3078                 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3079                 Jim_Var *varPtr;
3080                 int subLen;
3081
3082                 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3083                 /* Check if it's composed of two elements. */
3084                 Jim_ListLength(interp, objPtr, &subLen);
3085                 if (subLen == 1 || subLen == 2) {
3086                     /* Try to get the variable value from the current
3087                      * environment. */
3088                     Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3089                     if (subLen == 1) {
3090                         initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3091                                 JIM_NONE);
3092                         if (initObjPtr == NULL) {
3093                             Jim_SetResult(interp,
3094                                     Jim_NewEmptyStringObj(interp));
3095                             Jim_AppendStrings(interp, Jim_GetResult(interp),
3096                                 "variable for initialization of static \"",
3097                                 Jim_GetString(nameObjPtr, NULL),
3098                                 "\" not found in the local context",
3099                                 NULL);
3100                             goto err;
3101                         }
3102                     } else {
3103                         Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3104                     }
3105                     varPtr = Jim_Alloc(sizeof(*varPtr));
3106                     varPtr->objPtr = initObjPtr;
3107                     Jim_IncrRefCount(initObjPtr);
3108                     varPtr->linkFramePtr = NULL;
3109                     if (Jim_AddHashEntry(cmdPtr->staticVars,
3110                             Jim_GetString(nameObjPtr, NULL),
3111                             varPtr) != JIM_OK)
3112                     {
3113                         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3114                         Jim_AppendStrings(interp, Jim_GetResult(interp),
3115                             "static variable name \"",
3116                             Jim_GetString(objPtr, NULL), "\"",
3117                             " duplicated in statics list", NULL);
3118                         Jim_DecrRefCount(interp, initObjPtr);
3119                         Jim_Free(varPtr);
3120                         goto err;
3121                     }
3122                 } else {
3123                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3124                     Jim_AppendStrings(interp, Jim_GetResult(interp),
3125                         "too many fields in static specifier \"",
3126                         objPtr, "\"", NULL);
3127                     goto err;
3128                 }
3129             }
3130         }
3131     }
3132
3133     /* Add the new command */
3134
3135     /* it may already exist, so we try to delete the old one */
3136     if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3137         /* There was an old procedure with the same name, this requires
3138          * a 'proc epoch' update. */
3139         Jim_InterpIncrProcEpoch(interp);
3140     }
3141     /* If a procedure with the same name didn't existed there is no need
3142      * to increment the 'proc epoch' because creation of a new procedure
3143      * can never affect existing cached commands. We don't do
3144      * negative caching. */
3145     Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3146     return JIM_OK;
3147
3148 err:
3149     Jim_FreeHashTable(cmdPtr->staticVars);
3150     Jim_Free(cmdPtr->staticVars);
3151     Jim_DecrRefCount(interp, argListObjPtr);
3152     Jim_DecrRefCount(interp, bodyObjPtr);
3153     Jim_Free(cmdPtr);
3154     return JIM_ERR;
3155 }
3156
3157 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3158 {
3159     if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3160         return JIM_ERR;
3161     Jim_InterpIncrProcEpoch(interp);
3162     return JIM_OK;
3163 }
3164
3165 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, 
3166         const char *newName)
3167 {
3168     Jim_Cmd *cmdPtr;
3169     Jim_HashEntry *he;
3170     Jim_Cmd *copyCmdPtr;
3171
3172     if (newName[0] == '\0') /* Delete! */
3173         return Jim_DeleteCommand(interp, oldName);
3174     /* Rename */
3175     he = Jim_FindHashEntry(&interp->commands, oldName);
3176     if (he == NULL)
3177         return JIM_ERR; /* Invalid command name */
3178     cmdPtr = he->val;
3179     copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3180     *copyCmdPtr = *cmdPtr;
3181     /* In order to avoid that a procedure will get arglist/body/statics
3182      * freed by the hash table methods, fake a C-coded command
3183      * setting cmdPtr->cmdProc as not NULL */
3184     cmdPtr->cmdProc = (void*)1;
3185     /* Also make sure delProc is NULL. */
3186     cmdPtr->delProc = NULL;
3187     /* Destroy the old command, and make sure the new is freed
3188      * as well. */
3189     Jim_DeleteHashEntry(&interp->commands, oldName);
3190     Jim_DeleteHashEntry(&interp->commands, newName);
3191     /* Now the new command. We are sure it can't fail because
3192      * the target name was already freed. */
3193     Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3194     /* Increment the epoch */
3195     Jim_InterpIncrProcEpoch(interp);
3196     return JIM_OK;
3197 }
3198
3199 /* -----------------------------------------------------------------------------
3200  * Command object
3201  * ---------------------------------------------------------------------------*/
3202
3203 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3204
3205 static Jim_ObjType commandObjType = {
3206     "command",
3207     NULL,
3208     NULL,
3209     NULL,
3210     JIM_TYPE_REFERENCES,
3211 };
3212
3213 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3214 {
3215     Jim_HashEntry *he;
3216     const char *cmdName;
3217
3218     /* Get the string representation */
3219     cmdName = Jim_GetString(objPtr, NULL);
3220     /* Lookup this name into the commands hash table */
3221     he = Jim_FindHashEntry(&interp->commands, cmdName);
3222     if (he == NULL)
3223         return JIM_ERR;
3224
3225     /* Free the old internal repr and set the new one. */
3226     Jim_FreeIntRep(interp, objPtr);
3227     objPtr->typePtr = &commandObjType;
3228     objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3229     objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3230     return JIM_OK;
3231 }
3232
3233 /* This function returns the command structure for the command name
3234  * stored in objPtr. It tries to specialize the objPtr to contain
3235  * a cached info instead to perform the lookup into the hash table
3236  * every time. The information cached may not be uptodate, in such
3237  * a case the lookup is performed and the cache updated. */
3238 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3239 {
3240     if ((objPtr->typePtr != &commandObjType ||
3241         objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3242         SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3243         if (flags & JIM_ERRMSG) {
3244             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3245             Jim_AppendStrings(interp, Jim_GetResult(interp),
3246                 "invalid command name \"", objPtr->bytes, "\"",
3247                 NULL);
3248         }
3249         return NULL;
3250     }
3251     return objPtr->internalRep.cmdValue.cmdPtr;
3252 }
3253
3254 /* -----------------------------------------------------------------------------
3255  * Variables
3256  * ---------------------------------------------------------------------------*/
3257
3258 /* Variables HashTable Type.
3259  *
3260  * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3261 static void JimVariablesHTValDestructor(void *interp, void *val)
3262 {
3263     Jim_Var *varPtr = (void*) val;
3264
3265     Jim_DecrRefCount(interp, varPtr->objPtr);
3266     Jim_Free(val);
3267 }
3268
3269 static Jim_HashTableType JimVariablesHashTableType = {
3270     JimStringCopyHTHashFunction,        /* hash function */
3271     JimStringCopyHTKeyDup,              /* key dup */
3272     NULL,                               /* val dup */
3273     JimStringCopyHTKeyCompare,        /* key compare */
3274     JimStringCopyHTKeyDestructor,     /* key destructor */
3275     JimVariablesHTValDestructor       /* val destructor */
3276 };
3277
3278 /* -----------------------------------------------------------------------------
3279  * Variable object
3280  * ---------------------------------------------------------------------------*/
3281
3282 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3283
3284 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3285
3286 static Jim_ObjType variableObjType = {
3287     "variable",
3288     NULL,
3289     NULL,
3290     NULL,
3291     JIM_TYPE_REFERENCES,
3292 };
3293
3294 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3295  * is in the form "varname(key)". */
3296 static int Jim_NameIsDictSugar(const char *str, int len)
3297 {
3298     if (len == -1)
3299         len = strlen(str);
3300     if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3301         return 1;
3302     return 0;
3303 }
3304
3305 /* This method should be called only by the variable API.
3306  * It returns JIM_OK on success (variable already exists),
3307  * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3308  * a variable name, but syntax glue for [dict] i.e. the last
3309  * character is ')' */
3310 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3311 {
3312     Jim_HashEntry *he;
3313     const char *varName;
3314     int len;
3315
3316     /* Check if the object is already an uptodate variable */
3317     if (objPtr->typePtr == &variableObjType &&
3318         objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3319         return JIM_OK; /* nothing to do */
3320     /* Get the string representation */
3321     varName = Jim_GetString(objPtr, &len);
3322     /* Make sure it's not syntax glue to get/set dict. */
3323     if (Jim_NameIsDictSugar(varName, len))
3324             return JIM_DICT_SUGAR;
3325     /* Lookup this name into the variables hash table */
3326     he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3327     if (he == NULL) {
3328         /* Try with static vars. */
3329         if (interp->framePtr->staticVars == NULL)
3330             return JIM_ERR;
3331         if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3332             return JIM_ERR;
3333     }
3334     /* Free the old internal repr and set the new one. */
3335     Jim_FreeIntRep(interp, objPtr);
3336     objPtr->typePtr = &variableObjType;
3337     objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3338     objPtr->internalRep.varValue.varPtr = (void*)he->val;
3339     return JIM_OK;
3340 }
3341
3342 /* -------------------- Variables related functions ------------------------- */
3343 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3344         Jim_Obj *valObjPtr);
3345 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3346
3347 /* For now that's dummy. Variables lookup should be optimized
3348  * in many ways, with caching of lookups, and possibly with
3349  * a table of pre-allocated vars in every CallFrame for local vars.
3350  * All the caching should also have an 'epoch' mechanism similar
3351  * to the one used by Tcl for procedures lookup caching. */
3352
3353 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3354 {
3355     const char *name;
3356     Jim_Var *var;
3357     int err;
3358
3359     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3360         /* Check for [dict] syntax sugar. */
3361         if (err == JIM_DICT_SUGAR)
3362             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3363         /* New variable to create */
3364         name = Jim_GetString(nameObjPtr, NULL);
3365
3366         var = Jim_Alloc(sizeof(*var));
3367         var->objPtr = valObjPtr;
3368         Jim_IncrRefCount(valObjPtr);
3369         var->linkFramePtr = NULL;
3370         /* Insert the new variable */
3371         Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3372         /* Make the object int rep a variable */
3373         Jim_FreeIntRep(interp, nameObjPtr);
3374         nameObjPtr->typePtr = &variableObjType;
3375         nameObjPtr->internalRep.varValue.callFrameId =
3376             interp->framePtr->id;
3377         nameObjPtr->internalRep.varValue.varPtr = var;
3378     } else {
3379         var = nameObjPtr->internalRep.varValue.varPtr;
3380         if (var->linkFramePtr == NULL) {
3381             Jim_IncrRefCount(valObjPtr);
3382             Jim_DecrRefCount(interp, var->objPtr);
3383             var->objPtr = valObjPtr;
3384         } else { /* Else handle the link */
3385             Jim_CallFrame *savedCallFrame;
3386
3387             savedCallFrame = interp->framePtr;
3388             interp->framePtr = var->linkFramePtr;
3389             err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3390             interp->framePtr = savedCallFrame;
3391             if (err != JIM_OK)
3392                 return err;
3393         }
3394     }
3395     return JIM_OK;
3396 }
3397
3398 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3399 {
3400     Jim_Obj *nameObjPtr;
3401     int result;
3402
3403     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3404     Jim_IncrRefCount(nameObjPtr);
3405     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3406     Jim_DecrRefCount(interp, nameObjPtr);
3407     return result;
3408 }
3409
3410 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3411 {
3412     Jim_CallFrame *savedFramePtr;
3413     int result;
3414
3415     savedFramePtr = interp->framePtr;
3416     interp->framePtr = interp->topFramePtr;
3417     result = Jim_SetVariableStr(interp, name, objPtr);
3418     interp->framePtr = savedFramePtr;
3419     return result;
3420 }
3421
3422 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3423 {
3424     Jim_Obj *nameObjPtr, *valObjPtr;
3425     int result;
3426
3427     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3428     valObjPtr = Jim_NewStringObj(interp, val, -1);
3429     Jim_IncrRefCount(nameObjPtr);
3430     Jim_IncrRefCount(valObjPtr);
3431     result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3432     Jim_DecrRefCount(interp, nameObjPtr);
3433     Jim_DecrRefCount(interp, valObjPtr);
3434     return result;
3435 }
3436
3437 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3438         Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3439 {
3440     const char *varName;
3441     int len;
3442
3443     /* Check for cycles. */
3444     if (interp->framePtr == targetCallFrame) {
3445         Jim_Obj *objPtr = targetNameObjPtr;
3446         Jim_Var *varPtr;
3447         /* Cycles are only possible with 'uplevel 0' */
3448         while(1) {
3449             if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3450                 Jim_SetResultString(interp,
3451                     "can't upvar from variable to itself", -1);
3452                 return JIM_ERR;
3453             }
3454             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3455                 break;
3456             varPtr = objPtr->internalRep.varValue.varPtr;
3457             if (varPtr->linkFramePtr != targetCallFrame) break;
3458             objPtr = varPtr->objPtr;
3459         }
3460     }
3461     varName = Jim_GetString(nameObjPtr, &len);
3462     if (Jim_NameIsDictSugar(varName, len)) {
3463         Jim_SetResultString(interp,
3464             "Dict key syntax invalid as link source", -1);
3465         return JIM_ERR;
3466     }
3467     /* Perform the binding */
3468     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3469     /* We are now sure 'nameObjPtr' type is variableObjType */
3470     nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3471     return JIM_OK;
3472 }
3473
3474 /* Return the Jim_Obj pointer associated with a variable name,
3475  * or NULL if the variable was not found in the current context.
3476  * The same optimization discussed in the comment to the
3477  * 'SetVariable' function should apply here. */
3478 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3479 {
3480     int err;
3481
3482     /* All the rest is handled here */
3483     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3484         /* Check for [dict] syntax sugar. */
3485         if (err == JIM_DICT_SUGAR)
3486             return JimDictSugarGet(interp, nameObjPtr);
3487         if (flags & JIM_ERRMSG) {
3488             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3489             Jim_AppendStrings(interp, Jim_GetResult(interp),
3490                 "can't read \"", nameObjPtr->bytes,
3491                 "\": no such variable", NULL);
3492         }
3493         return NULL;
3494     } else {
3495         Jim_Var *varPtr;
3496         Jim_Obj *objPtr;
3497         Jim_CallFrame *savedCallFrame;
3498
3499         varPtr = nameObjPtr->internalRep.varValue.varPtr;
3500         if (varPtr->linkFramePtr == NULL)
3501             return varPtr->objPtr;
3502         /* The variable is a link? Resolve it. */
3503         savedCallFrame = interp->framePtr;
3504         interp->framePtr = varPtr->linkFramePtr;
3505         objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3506         if (objPtr == NULL && flags & JIM_ERRMSG) {
3507             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3508             Jim_AppendStrings(interp, Jim_GetResult(interp),
3509                 "can't read \"", nameObjPtr->bytes,
3510                 "\": no such variable", NULL);
3511         }
3512         interp->framePtr = savedCallFrame;
3513         return objPtr;
3514     }
3515 }
3516
3517 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3518         int flags)
3519 {
3520     Jim_CallFrame *savedFramePtr;
3521     Jim_Obj *objPtr;
3522
3523     savedFramePtr = interp->framePtr;
3524     interp->framePtr = interp->topFramePtr;
3525     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3526     interp->framePtr = savedFramePtr;
3527
3528     return objPtr;
3529 }
3530
3531 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3532 {
3533     Jim_Obj *nameObjPtr, *varObjPtr;
3534
3535     nameObjPtr = Jim_NewStringObj(interp, name, -1);
3536     Jim_IncrRefCount(nameObjPtr);
3537     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3538     Jim_DecrRefCount(interp, nameObjPtr);
3539     return varObjPtr;
3540 }
3541
3542 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3543         int flags)
3544 {
3545     Jim_CallFrame *savedFramePtr;
3546     Jim_Obj *objPtr;
3547
3548     savedFramePtr = interp->framePtr;
3549     interp->framePtr = interp->topFramePtr;
3550     objPtr = Jim_GetVariableStr(interp, name, flags);
3551     interp->framePtr = savedFramePtr;
3552
3553     return objPtr;
3554 }
3555
3556 /* Unset a variable.
3557  * Note: On success unset invalidates all the variable objects created
3558  * in the current call frame incrementing. */
3559 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3560 {
3561     const char *name;
3562     Jim_Var *varPtr;
3563     int err;
3564     
3565     if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3566         /* Check for [dict] syntax sugar. */
3567         if (err == JIM_DICT_SUGAR)
3568             return JimDictSugarSet(interp, nameObjPtr, NULL);
3569         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3570         Jim_AppendStrings(interp, Jim_GetResult(interp),
3571             "can't unset \"", nameObjPtr->bytes,
3572             "\": no such variable", NULL);
3573         return JIM_ERR; /* var not found */
3574     }
3575     varPtr = nameObjPtr->internalRep.varValue.varPtr;
3576     /* If it's a link call UnsetVariable recursively */
3577     if (varPtr->linkFramePtr) {
3578         int retval;
3579
3580         Jim_CallFrame *savedCallFrame;
3581
3582         savedCallFrame = interp->framePtr;
3583         interp->framePtr = varPtr->linkFramePtr;
3584         retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3585         interp->framePtr = savedCallFrame;
3586         if (retval != JIM_OK && flags & JIM_ERRMSG) {
3587             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3588             Jim_AppendStrings(interp, Jim_GetResult(interp),
3589                 "can't unset \"", nameObjPtr->bytes,
3590                 "\": no such variable", NULL);
3591         }
3592         return retval;
3593     } else {
3594         name = Jim_GetString(nameObjPtr, NULL);
3595         if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3596                 != JIM_OK) return JIM_ERR;
3597         /* Change the callframe id, invalidating var lookup caching */
3598         JimChangeCallFrameId(interp, interp->framePtr);
3599         return JIM_OK;
3600     }
3601 }
3602
3603 /* ----------  Dict syntax sugar (similar to array Tcl syntax) -------------- */
3604
3605 /* Given a variable name for [dict] operation syntax sugar,
3606  * this function returns two objects, the first with the name
3607  * of the variable to set, and the second with the rispective key.
3608  * For example "foo(bar)" will return objects with string repr. of
3609  * "foo" and "bar".
3610  *
3611  * The returned objects have refcount = 1. The function can't fail. */
3612 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3613         Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3614 {
3615     const char *str, *p;
3616     char *t;
3617     int len, keyLen, nameLen;
3618     Jim_Obj *varObjPtr, *keyObjPtr;
3619
3620     str = Jim_GetString(objPtr, &len);
3621     p = strchr(str, '(');
3622     p++;
3623     keyLen = len-((p-str)+1);
3624     nameLen = (p-str)-1;
3625     /* Create the objects with the variable name and key. */
3626     t = Jim_Alloc(nameLen+1);
3627     memcpy(t, str, nameLen);
3628     t[nameLen] = '\0';
3629     varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3630
3631     t = Jim_Alloc(keyLen+1);
3632     memcpy(t, p, keyLen);
3633     t[keyLen] = '\0';
3634     keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3635
3636     Jim_IncrRefCount(varObjPtr);
3637     Jim_IncrRefCount(keyObjPtr);
3638     *varPtrPtr = varObjPtr;
3639     *keyPtrPtr = keyObjPtr;
3640 }
3641
3642 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3643  * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3644 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3645         Jim_Obj *valObjPtr)
3646 {
3647     Jim_Obj *varObjPtr, *keyObjPtr;
3648     int err = JIM_OK;
3649
3650     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3651     err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3652             valObjPtr);
3653     Jim_DecrRefCount(interp, varObjPtr);
3654     Jim_DecrRefCount(interp, keyObjPtr);
3655     return err;
3656 }
3657
3658 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3659 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3660 {
3661     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3662
3663     JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3664     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3665     if (!dictObjPtr) {
3666         resObjPtr = NULL;
3667         goto err;
3668     }
3669     if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3670             != JIM_OK) {
3671         resObjPtr = NULL;
3672     }
3673 err:
3674     Jim_DecrRefCount(interp, varObjPtr);
3675     Jim_DecrRefCount(interp, keyObjPtr);
3676     return resObjPtr;
3677 }
3678
3679 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3680
3681 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3682 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3683         Jim_Obj *dupPtr);
3684
3685 static Jim_ObjType dictSubstObjType = {
3686     "dict-substitution",
3687     FreeDictSubstInternalRep,
3688     DupDictSubstInternalRep,
3689     NULL,
3690     JIM_TYPE_NONE,
3691 };
3692
3693 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3694 {
3695     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3696     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3697 }
3698
3699 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3700         Jim_Obj *dupPtr)
3701 {
3702     JIM_NOTUSED(interp);
3703
3704     dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3705         srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3706     dupPtr->internalRep.dictSubstValue.indexObjPtr =
3707         srcPtr->internalRep.dictSubstValue.indexObjPtr;
3708     dupPtr->typePtr = &dictSubstObjType;
3709 }
3710
3711 /* This function is used to expand [dict get] sugar in the form
3712  * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3713  * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3714  * object that is *guaranteed* to be in the form VARNAME(INDEX).
3715  * The 'index' part is [subst]ituted, and is used to lookup a key inside
3716  * the [dict]ionary contained in variable VARNAME. */
3717 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3718 {
3719     Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3720     Jim_Obj *substKeyObjPtr = NULL;
3721
3722     if (objPtr->typePtr != &dictSubstObjType) {
3723         JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3724         Jim_FreeIntRep(interp, objPtr);
3725         objPtr->typePtr = &dictSubstObjType;
3726         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3727         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3728     }
3729     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3730                 &substKeyObjPtr, JIM_NONE)
3731             != JIM_OK) {
3732         substKeyObjPtr = NULL;
3733         goto err;
3734     }
3735     Jim_IncrRefCount(substKeyObjPtr);
3736     dictObjPtr = Jim_GetVariable(interp,
3737             objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3738     if (!dictObjPtr) {
3739         resObjPtr = NULL;
3740         goto err;
3741     }
3742     if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3743             != JIM_OK) {
3744         resObjPtr = NULL;
3745         goto err;
3746     }
3747 err:
3748     if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3749     return resObjPtr;
3750 }
3751
3752 /* -----------------------------------------------------------------------------
3753  * CallFrame
3754  * ---------------------------------------------------------------------------*/
3755
3756 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3757 {
3758     Jim_CallFrame *cf;
3759     if (interp->freeFramesList) {
3760         cf = interp->freeFramesList;
3761         interp->freeFramesList = cf->nextFramePtr;
3762     } else {
3763         cf = Jim_Alloc(sizeof(*cf));
3764         cf->vars.table = NULL;
3765     }
3766
3767     cf->id = interp->callFrameEpoch++;
3768     cf->parentCallFrame = NULL;
3769     cf->argv = NULL;
3770     cf->argc = 0;
3771     cf->procArgsObjPtr = NULL;
3772     cf->procBodyObjPtr = NULL;
3773     cf->nextFramePtr = NULL;
3774     cf->staticVars = NULL;
3775     if (cf->vars.table == NULL)
3776         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3777     return cf;
3778 }
3779
3780 /* Used to invalidate every caching related to callframe stability. */
3781 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3782 {
3783     cf->id = interp->callFrameEpoch++;
3784 }
3785
3786 #define JIM_FCF_NONE 0 /* no flags */
3787 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3788 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3789         int flags)
3790 {
3791     if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3792     if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3793     if (!(flags & JIM_FCF_NOHT))
3794         Jim_FreeHashTable(&cf->vars);
3795     else {
3796         int i;
3797         Jim_HashEntry **table = cf->vars.table, *he;
3798
3799         for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3800             he = table[i];
3801             while (he != NULL) {
3802                 Jim_HashEntry *nextEntry = he->next;
3803                 Jim_Var *varPtr = (void*) he->val;
3804
3805                 Jim_DecrRefCount(interp, varPtr->objPtr);
3806                 Jim_Free(he->val);
3807                 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3808                 Jim_Free(he);
3809                 table[i] = NULL;
3810                 he = nextEntry;
3811             }
3812         }
3813         cf->vars.used = 0;
3814     }
3815     cf->nextFramePtr = interp->freeFramesList;
3816     interp->freeFramesList = cf;
3817 }
3818
3819 /* -----------------------------------------------------------------------------
3820  * References
3821  * ---------------------------------------------------------------------------*/
3822
3823 /* References HashTable Type.
3824  *
3825  * Keys are jim_wide integers, dynamically allocated for now but in the
3826  * future it's worth to cache this 8 bytes objects. Values are poitners
3827  * to Jim_References. */
3828 static void JimReferencesHTValDestructor(void *interp, void *val)
3829 {
3830     Jim_Reference *refPtr = (void*) val;
3831
3832     Jim_DecrRefCount(interp, refPtr->objPtr);
3833     if (refPtr->finalizerCmdNamePtr != NULL) {
3834         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3835     }
3836     Jim_Free(val);
3837 }
3838
3839 unsigned int JimReferencesHTHashFunction(const void *key)
3840 {
3841     /* Only the least significant bits are used. */
3842     const jim_wide *widePtr = key;
3843     unsigned int intValue = (unsigned int) *widePtr;
3844     return Jim_IntHashFunction(intValue);
3845 }
3846
3847 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3848 {
3849     /* Only the least significant bits are used. */
3850     const jim_wide *widePtr = key;
3851     unsigned int intValue = (unsigned int) *widePtr;
3852     return intValue; /* identity function. */
3853 }
3854
3855 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3856 {
3857     void *copy = Jim_Alloc(sizeof(jim_wide));
3858     JIM_NOTUSED(privdata);
3859
3860     memcpy(copy, key, sizeof(jim_wide));
3861     return copy;
3862 }
3863
3864 int JimReferencesHTKeyCompare(void *privdata, const void *key1, 
3865         const void *key2)
3866 {
3867     JIM_NOTUSED(privdata);
3868
3869     return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3870 }
3871
3872 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3873 {
3874     JIM_NOTUSED(privdata);
3875
3876     Jim_Free((void*)key);
3877 }
3878
3879 static Jim_HashTableType JimReferencesHashTableType = {
3880     JimReferencesHTHashFunction,    /* hash function */
3881     JimReferencesHTKeyDup,          /* key dup */
3882     NULL,                           /* val dup */
3883     JimReferencesHTKeyCompare,      /* key compare */
3884     JimReferencesHTKeyDestructor,   /* key destructor */
3885     JimReferencesHTValDestructor    /* val destructor */
3886 };
3887
3888 /* -----------------------------------------------------------------------------
3889  * Reference object type and References API
3890  * ---------------------------------------------------------------------------*/
3891
3892 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3893
3894 static Jim_ObjType referenceObjType = {
3895     "reference",
3896     NULL,
3897     NULL,
3898     UpdateStringOfReference,
3899     JIM_TYPE_REFERENCES,
3900 };
3901
3902 void UpdateStringOfReference(struct Jim_Obj *objPtr)
3903 {
3904     int len;
3905     char buf[JIM_REFERENCE_SPACE+1];
3906     Jim_Reference *refPtr;
3907
3908     refPtr = objPtr->internalRep.refValue.refPtr;
3909     len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3910     objPtr->bytes = Jim_Alloc(len+1);
3911     memcpy(objPtr->bytes, buf, len+1);
3912     objPtr->length = len;
3913 }
3914
3915 /* returns true if 'c' is a valid reference tag character.
3916  * i.e. inside the range [_a-zA-Z0-9] */
3917 static int isrefchar(int c)
3918 {
3919     if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3920         (c >= '0' && c <= '9')) return 1;
3921     return 0;
3922 }
3923
3924 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3925 {
3926     jim_wide wideValue;
3927     int i, len;
3928     const char *str, *start, *end;
3929     char refId[21];
3930     Jim_Reference *refPtr;
3931     Jim_HashEntry *he;
3932
3933     /* Get the string representation */
3934     str = Jim_GetString(objPtr, &len);
3935     /* Check if it looks like a reference */
3936     if (len < JIM_REFERENCE_SPACE) goto badformat;
3937     /* Trim spaces */
3938     start = str;
3939     end = str+len-1;
3940     while (*start == ' ') start++;
3941     while (*end == ' ' && end > start) end--;
3942     if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3943     /* <reference.<1234567>.%020> */
3944     if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3945     if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3946     /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3947     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3948         if (!isrefchar(start[12+i])) goto badformat;
3949     }
3950     /* Extract info from the refernece. */
3951     memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3952     refId[20] = '\0';
3953     /* Try to convert the ID into a jim_wide */
3954     if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3955     /* Check if the reference really exists! */
3956     he = Jim_FindHashEntry(&interp->references, &wideValue);
3957     if (he == NULL) {
3958         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3959         Jim_AppendStrings(interp, Jim_GetResult(interp),
3960                 "Invalid reference ID \"", str, "\"", NULL);
3961         return JIM_ERR;
3962     }
3963     refPtr = he->val;
3964     /* Free the old internal repr and set the new one. */
3965     Jim_FreeIntRep(interp, objPtr);
3966     objPtr->typePtr = &referenceObjType;
3967     objPtr->internalRep.refValue.id = wideValue;
3968     objPtr->internalRep.refValue.refPtr = refPtr;
3969     return JIM_OK;
3970
3971 badformat:
3972     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3973     Jim_AppendStrings(interp, Jim_GetResult(interp),
3974             "expected reference but got \"", str, "\"", NULL);
3975     return JIM_ERR;
3976 }
3977
3978 /* Returns a new reference pointing to objPtr, having cmdNamePtr
3979  * as finalizer command (or NULL if there is no finalizer).
3980  * The returned reference object has refcount = 0. */
3981 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
3982         Jim_Obj *cmdNamePtr)
3983 {
3984     struct Jim_Reference *refPtr;
3985     jim_wide wideValue = interp->referenceNextId;
3986     Jim_Obj *refObjPtr;
3987     const char *tag;
3988     int tagLen, i;
3989
3990     /* Perform the Garbage Collection if needed. */
3991     Jim_CollectIfNeeded(interp);
3992
3993     refPtr = Jim_Alloc(sizeof(*refPtr));
3994     refPtr->objPtr = objPtr;
3995     Jim_IncrRefCount(objPtr);
3996     refPtr->finalizerCmdNamePtr = cmdNamePtr;
3997     if (cmdNamePtr)
3998         Jim_IncrRefCount(cmdNamePtr);
3999     Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4000     refObjPtr = Jim_NewObj(interp);
4001     refObjPtr->typePtr = &referenceObjType;
4002     refObjPtr->bytes = NULL;
4003     refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4004     refObjPtr->internalRep.refValue.refPtr = refPtr;
4005     interp->referenceNextId++;
4006     /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4007      * that does not pass the 'isrefchar' test is replaced with '_' */
4008     tag = Jim_GetString(tagPtr, &tagLen);
4009     if (tagLen > JIM_REFERENCE_TAGLEN)
4010         tagLen = JIM_REFERENCE_TAGLEN;
4011     for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4012         if (i < tagLen)
4013             refPtr->tag[i] = tag[i];
4014         else
4015             refPtr->tag[i] = '_';
4016     }
4017     refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4018     return refObjPtr;
4019 }
4020
4021 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4022 {
4023     if (objPtr->typePtr != &referenceObjType &&
4024         SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4025         return NULL;
4026     return objPtr->internalRep.refValue.refPtr;
4027 }
4028
4029 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4030 {
4031     Jim_Reference *refPtr;
4032
4033     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4034         return JIM_ERR;
4035     Jim_IncrRefCount(cmdNamePtr);
4036     if (refPtr->finalizerCmdNamePtr)
4037         Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4038     refPtr->finalizerCmdNamePtr = cmdNamePtr;
4039     return JIM_OK;
4040 }
4041
4042 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4043 {
4044     Jim_Reference *refPtr;
4045
4046     if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4047         return JIM_ERR;
4048     *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4049     return JIM_OK;
4050 }
4051
4052 /* -----------------------------------------------------------------------------
4053  * References Garbage Collection
4054  * ---------------------------------------------------------------------------*/
4055
4056 /* This the hash table type for the "MARK" phase of the GC */
4057 static Jim_HashTableType JimRefMarkHashTableType = {
4058     JimReferencesHTHashFunction,    /* hash function */
4059     JimReferencesHTKeyDup,          /* key dup */
4060     NULL,                           /* val dup */
4061     JimReferencesHTKeyCompare,      /* key compare */
4062     JimReferencesHTKeyDestructor,   /* key destructor */
4063     NULL                            /* val destructor */
4064 };
4065
4066 /* #define JIM_DEBUG_GC 1 */
4067
4068 /* Performs the garbage collection. */
4069 int Jim_Collect(Jim_Interp *interp)
4070 {
4071     Jim_HashTable marks;
4072     Jim_HashTableIterator *htiter;
4073     Jim_HashEntry *he;
4074     Jim_Obj *objPtr;
4075     int collected = 0;
4076
4077     /* Avoid recursive calls */
4078     if (interp->lastCollectId == -1) {
4079         /* Jim_Collect() already running. Return just now. */
4080         return 0;
4081     }
4082     interp->lastCollectId = -1;
4083
4084     /* Mark all the references found into the 'mark' hash table.
4085      * The references are searched in every live object that
4086      * is of a type that can contain references. */
4087     Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4088     objPtr = interp->liveList;
4089     while(objPtr) {
4090         if (objPtr->typePtr == NULL ||
4091             objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4092             const char *str, *p;
4093             int len;
4094
4095             /* If the object is of type reference, to get the
4096              * Id is simple... */
4097             if (objPtr->typePtr == &referenceObjType) {
4098                 Jim_AddHashEntry(&marks,
4099                     &objPtr->internalRep.refValue.id, NULL);
4100 #ifdef JIM_DEBUG_GC
4101                 Jim_fprintf(interp,interp->cookie_stdout,
4102                     "MARK (reference): %d refcount: %d" JIM_NL, 
4103                     (int) objPtr->internalRep.refValue.id,
4104                     objPtr->refCount);
4105 #endif
4106                 objPtr = objPtr->nextObjPtr;
4107                 continue;
4108             }
4109             /* Get the string repr of the object we want
4110              * to scan for references. */
4111             p = str = Jim_GetString(objPtr, &len);
4112             /* Skip objects too little to contain references. */
4113             if (len < JIM_REFERENCE_SPACE) {
4114                 objPtr = objPtr->nextObjPtr;
4115                 continue;
4116             }
4117             /* Extract references from the object string repr. */
4118             while(1) {
4119                 int i;
4120                 jim_wide id;
4121                 char buf[21];
4122
4123                 if ((p = strstr(p, "<reference.<")) == NULL)
4124                     break;
4125                 /* Check if it's a valid reference. */
4126                 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4127                 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4128                 for (i = 21; i <= 40; i++)
4129                     if (!isdigit((int)p[i]))
4130                         break;
4131                 /* Get the ID */
4132                 memcpy(buf, p+21, 20);
4133                 buf[20] = '\0';
4134                 Jim_StringToWide(buf, &id, 10);
4135
4136                 /* Ok, a reference for the given ID
4137                  * was found. Mark it. */
4138                 Jim_AddHashEntry(&marks, &id, NULL);
4139 #ifdef JIM_DEBUG_GC
4140                 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4141 #endif
4142                 p += JIM_REFERENCE_SPACE;
4143             }
4144         }
4145         objPtr = objPtr->nextObjPtr;
4146     }
4147
4148     /* Run the references hash table to destroy every reference that
4149      * is not referenced outside (not present in the mark HT). */
4150     htiter = Jim_GetHashTableIterator(&interp->references);
4151     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4152         const jim_wide *refId;
4153         Jim_Reference *refPtr;
4154
4155         refId = he->key;
4156         /* Check if in the mark phase we encountered
4157          * this reference. */
4158         if (Jim_FindHashEntry(&marks, refId) == NULL) {
4159 #ifdef JIM_DEBUG_GC
4160             Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4161 #endif
4162             collected++;
4163             /* Drop the reference, but call the
4164              * finalizer first if registered. */
4165             refPtr = he->val;
4166             if (refPtr->finalizerCmdNamePtr) {
4167                 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4168                 Jim_Obj *objv[3], *oldResult;
4169
4170                 JimFormatReference(refstr, refPtr, *refId);
4171
4172                 objv[0] = refPtr->finalizerCmdNamePtr;
4173                 objv[1] = Jim_NewStringObjNoAlloc(interp,
4174                         refstr, 32);
4175                 objv[2] = refPtr->objPtr;
4176                 Jim_IncrRefCount(objv[0]);
4177                 Jim_IncrRefCount(objv[1]);
4178                 Jim_IncrRefCount(objv[2]);
4179
4180                 /* Drop the reference itself */
4181                 Jim_DeleteHashEntry(&interp->references, refId);
4182
4183                 /* Call the finalizer. Errors ignored. */
4184                 oldResult = interp->result;
4185                 Jim_IncrRefCount(oldResult);
4186                 Jim_EvalObjVector(interp, 3, objv);
4187                 Jim_SetResult(interp, oldResult);
4188                 Jim_DecrRefCount(interp, oldResult);
4189
4190                 Jim_DecrRefCount(interp, objv[0]);
4191                 Jim_DecrRefCount(interp, objv[1]);
4192                 Jim_DecrRefCount(interp, objv[2]);
4193             } else {
4194                 Jim_DeleteHashEntry(&interp->references, refId);
4195             }
4196         }
4197     }
4198     Jim_FreeHashTableIterator(htiter);
4199     Jim_FreeHashTable(&marks);
4200     interp->lastCollectId = interp->referenceNextId;
4201     interp->lastCollectTime = time(NULL);
4202     return collected;
4203 }
4204
4205 #define JIM_COLLECT_ID_PERIOD 5000
4206 #define JIM_COLLECT_TIME_PERIOD 300
4207
4208 void Jim_CollectIfNeeded(Jim_Interp *interp)
4209 {
4210     jim_wide elapsedId;
4211     int elapsedTime;
4212     
4213     elapsedId = interp->referenceNextId - interp->lastCollectId;
4214     elapsedTime = time(NULL) - interp->lastCollectTime;
4215
4216
4217     if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4218         elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4219         Jim_Collect(interp);
4220     }
4221 }
4222
4223 /* -----------------------------------------------------------------------------
4224  * Interpreter related functions
4225  * ---------------------------------------------------------------------------*/
4226
4227 Jim_Interp *Jim_CreateInterp(void)
4228 {
4229     Jim_Interp *i = Jim_Alloc(sizeof(*i));
4230     Jim_Obj *pathPtr;
4231
4232     i->errorLine = 0;
4233     i->errorFileName = Jim_StrDup("");
4234     i->numLevels = 0;
4235     i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4236     i->returnCode = JIM_OK;
4237     i->exitCode = 0;
4238     i->procEpoch = 0;
4239     i->callFrameEpoch = 0;
4240     i->liveList = i->freeList = NULL;
4241     i->scriptFileName = Jim_StrDup("");
4242     i->referenceNextId = 0;
4243     i->lastCollectId = 0;
4244     i->lastCollectTime = time(NULL);
4245     i->freeFramesList = NULL;
4246     i->prngState = NULL;
4247     i->evalRetcodeLevel = -1;
4248     i->cookie_stdin = stdin;
4249     i->cookie_stdout = stdout;
4250     i->cookie_stderr = stderr;
4251         i->cb_fwrite   = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4252         i->cb_fread    = ((size_t (*)(       void *, size_t, size_t, void *))(fread));
4253         i->cb_vfprintf = ((int    (*)( void *, const char *fmt, va_list ))(vfprintf));
4254         i->cb_fflush   = ((int    (*)( void *))(fflush));
4255         i->cb_fgets    = ((char * (*)( char *, int, void *))(fgets));
4256
4257     /* Note that we can create objects only after the
4258      * interpreter liveList and freeList pointers are
4259      * initialized to NULL. */
4260     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4261     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4262     Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4263             NULL);
4264     Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4265     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4266     Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4267     i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4268     i->emptyObj = Jim_NewEmptyStringObj(i);
4269     i->result = i->emptyObj;
4270     i->stackTrace = Jim_NewListObj(i, NULL, 0);
4271     i->unknown = Jim_NewStringObj(i, "unknown", -1);
4272     Jim_IncrRefCount(i->emptyObj);
4273     Jim_IncrRefCount(i->result);
4274     Jim_IncrRefCount(i->stackTrace);
4275     Jim_IncrRefCount(i->unknown);
4276
4277     /* Initialize key variables every interpreter should contain */
4278     pathPtr = Jim_NewStringObj(i, "./", -1);
4279     Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4280     Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4281
4282     /* Export the core API to extensions */
4283     JimRegisterCoreApi(i);
4284     return i;
4285 }
4286
4287 /* This is the only function Jim exports directly without
4288  * to use the STUB system. It is only used by embedders
4289  * in order to get an interpreter with the Jim API pointers
4290  * registered. */
4291 Jim_Interp *ExportedJimCreateInterp(void)
4292 {
4293     return Jim_CreateInterp();
4294 }
4295
4296 void Jim_FreeInterp(Jim_Interp *i)
4297 {
4298     Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4299     Jim_Obj *objPtr, *nextObjPtr;
4300
4301     Jim_DecrRefCount(i, i->emptyObj);
4302     Jim_DecrRefCount(i, i->result);
4303     Jim_DecrRefCount(i, i->stackTrace);
4304     Jim_DecrRefCount(i, i->unknown);
4305     Jim_Free((void*)i->errorFileName);
4306     Jim_Free((void*)i->scriptFileName);
4307     Jim_FreeHashTable(&i->commands);
4308     Jim_FreeHashTable(&i->references);
4309     Jim_FreeHashTable(&i->stub);
4310     Jim_FreeHashTable(&i->assocData);
4311     Jim_FreeHashTable(&i->packages);
4312     Jim_Free(i->prngState);
4313     /* Free the call frames list */
4314     while(cf) {
4315         prevcf = cf->parentCallFrame;
4316         JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4317         cf = prevcf;
4318     }
4319     /* Check that the live object list is empty, otherwise
4320      * there is a memory leak. */
4321     if (i->liveList != NULL) {
4322         Jim_Obj *objPtr = i->liveList;
4323     
4324         Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4325         Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4326         while(objPtr) {
4327             const char *type = objPtr->typePtr ?
4328                 objPtr->typePtr->name : "";
4329             Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4330                     objPtr, type,
4331                     objPtr->bytes ? objPtr->bytes
4332                     : "(null)", objPtr->refCount);
4333             if (objPtr->typePtr == &sourceObjType) {
4334                 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4335                 objPtr->internalRep.sourceValue.fileName,
4336                 objPtr->internalRep.sourceValue.lineNumber);
4337             }
4338             objPtr = objPtr->nextObjPtr;
4339         }
4340         Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4341         Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4342     }
4343     /* Free all the freed objects. */
4344     objPtr = i->freeList;
4345     while (objPtr) {
4346         nextObjPtr = objPtr->nextObjPtr;
4347         Jim_Free(objPtr);
4348         objPtr = nextObjPtr;
4349     }
4350     /* Free cached CallFrame structures */
4351     cf = i->freeFramesList;
4352     while(cf) {
4353         nextcf = cf->nextFramePtr;
4354         if (cf->vars.table != NULL)
4355             Jim_Free(cf->vars.table);
4356         Jim_Free(cf);
4357         cf = nextcf;
4358     }
4359     /* Free the sharedString hash table. Make sure to free it
4360      * after every other Jim_Object was freed. */
4361     Jim_FreeHashTable(&i->sharedStrings);
4362     /* Free the interpreter structure. */
4363     Jim_Free(i);
4364 }
4365
4366 /* Store the call frame relative to the level represented by
4367  * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4368  * level is assumed to be '1'.
4369  *
4370  * If a newLevelptr int pointer is specified, the function stores
4371  * the absolute level integer value of the new target callframe into
4372  * *newLevelPtr. (this is used to adjust interp->numLevels
4373  * in the implementation of [uplevel], so that [info level] will
4374  * return a correct information).
4375  *
4376  * This function accepts the 'level' argument in the form
4377  * of the commands [uplevel] and [upvar].
4378  *
4379  * For a function accepting a relative integer as level suitable
4380  * for implementation of [info level ?level?] check the
4381  * GetCallFrameByInteger() function. */
4382 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4383         Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4384 {
4385     long level;
4386     const char *str;
4387     Jim_CallFrame *framePtr;
4388
4389     if (newLevelPtr) *newLevelPtr = interp->numLevels;
4390     if (levelObjPtr) {
4391         str = Jim_GetString(levelObjPtr, NULL);
4392         if (str[0] == '#') {
4393             char *endptr;
4394             /* speedup for the toplevel (level #0) */
4395             if (str[1] == '0' && str[2] == '\0') {
4396                 if (newLevelPtr) *newLevelPtr = 0;
4397                 *framePtrPtr = interp->topFramePtr;
4398                 return JIM_OK;
4399             }
4400
4401             level = strtol(str+1, &endptr, 0);
4402             if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4403                 goto badlevel;
4404             /* An 'absolute' level is converted into the
4405              * 'number of levels to go back' format. */
4406             level = interp->numLevels - level;
4407             if (level < 0) goto badlevel;
4408         } else {
4409             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4410                 goto badlevel;
4411         }
4412     } else {
4413         str = "1"; /* Needed to format the error message. */
4414         level = 1;
4415     }
4416     /* Lookup */
4417     framePtr = interp->framePtr;
4418     if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4419     while (level--) {
4420         framePtr = framePtr->parentCallFrame;
4421         if (framePtr == NULL) goto badlevel;
4422     }
4423     *framePtrPtr = framePtr;
4424     return JIM_OK;
4425 badlevel:
4426     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4427     Jim_AppendStrings(interp, Jim_GetResult(interp),
4428             "bad level \"", str, "\"", NULL);
4429     return JIM_ERR;
4430 }
4431
4432 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4433  * as a relative integer like in the [info level ?level?] command. */
4434 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4435         Jim_CallFrame **framePtrPtr)
4436 {
4437     jim_wide level;
4438     jim_wide relLevel; /* level relative to the current one. */
4439     Jim_CallFrame *framePtr;
4440
4441     if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4442         goto badlevel;
4443     if (level > 0) {
4444         /* An 'absolute' level is converted into the
4445          * 'number of levels to go back' format. */
4446         relLevel = interp->numLevels - level;
4447     } else {
4448         relLevel = -level;
4449     }
4450     /* Lookup */
4451     framePtr = interp->framePtr;
4452     while (relLevel--) {
4453         framePtr = framePtr->parentCallFrame;
4454         if (framePtr == NULL) goto badlevel;
4455     }
4456     *framePtrPtr = framePtr;
4457     return JIM_OK;
4458 badlevel:
4459     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4460     Jim_AppendStrings(interp, Jim_GetResult(interp),
4461             "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4462     return JIM_ERR;
4463 }
4464
4465 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4466 {
4467     Jim_Free((void*)interp->errorFileName);
4468     interp->errorFileName = Jim_StrDup(filename);
4469 }
4470
4471 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4472 {
4473     interp->errorLine = linenr;
4474 }
4475
4476 static void JimResetStackTrace(Jim_Interp *interp)
4477 {
4478     Jim_DecrRefCount(interp, interp->stackTrace);
4479     interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4480     Jim_IncrRefCount(interp->stackTrace);
4481 }
4482
4483 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4484         const char *filename, int linenr)
4485 {
4486     if (Jim_IsShared(interp->stackTrace)) {
4487         interp->stackTrace =
4488             Jim_DuplicateObj(interp, interp->stackTrace);
4489         Jim_IncrRefCount(interp->stackTrace);
4490     }
4491     Jim_ListAppendElement(interp, interp->stackTrace,
4492             Jim_NewStringObj(interp, procname, -1));
4493     Jim_ListAppendElement(interp, interp->stackTrace,
4494             Jim_NewStringObj(interp, filename, -1));
4495     Jim_ListAppendElement(interp, interp->stackTrace,
4496             Jim_NewIntObj(interp, linenr));
4497 }
4498
4499 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4500 {
4501     AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4502     assocEntryPtr->delProc = delProc;
4503     assocEntryPtr->data = data;
4504     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4505 }
4506
4507 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4508 {
4509     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4510     if (entryPtr != NULL) {
4511         AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4512         return assocEntryPtr->data;
4513     }
4514     return NULL;
4515 }
4516
4517 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4518 {
4519     return Jim_DeleteHashEntry(&interp->assocData, key);
4520 }
4521
4522 int Jim_GetExitCode(Jim_Interp *interp) {
4523     return interp->exitCode;
4524 }
4525
4526 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4527 {
4528     if (fp != NULL) interp->cookie_stdin = fp;
4529     return interp->cookie_stdin;
4530 }
4531
4532 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4533 {
4534     if (fp != NULL) interp->cookie_stdout = fp;
4535     return interp->cookie_stdout;
4536 }
4537
4538 void *Jim_SetStderr(Jim_Interp *interp, void  *fp)
4539 {
4540     if (fp != NULL) interp->cookie_stderr = fp;
4541     return interp->cookie_stderr;
4542 }
4543
4544 /* -----------------------------------------------------------------------------
4545  * Shared strings.
4546  * Every interpreter has an hash table where to put shared dynamically
4547  * allocate strings that are likely to be used a lot of times.
4548  * For example, in the 'source' object type, there is a pointer to
4549  * the filename associated with that object. Every script has a lot
4550  * of this objects with the identical file name, so it is wise to share
4551  * this info.
4552  *
4553  * The API is trivial: Jim_GetSharedString(interp, "foobar")
4554  * returns the pointer to the shared string. Every time a reference
4555  * to the string is no longer used, the user should call
4556  * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4557  * a given string, it is removed from the hash table.
4558  * ---------------------------------------------------------------------------*/
4559 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4560 {
4561     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4562
4563     if (he == NULL) {
4564         char *strCopy = Jim_StrDup(str);
4565
4566         Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4567         return strCopy;
4568     } else {
4569         long refCount = (long) he->val;
4570
4571         refCount++;
4572         he->val = (void*) refCount;
4573         return he->key;
4574     }
4575 }
4576
4577 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4578 {
4579     long refCount;
4580     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4581
4582     if (he == NULL)
4583         Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4584               "unknown shared string '%s'", str);
4585     refCount = (long) he->val;
4586     refCount--;
4587     if (refCount == 0) {
4588         Jim_DeleteHashEntry(&interp->sharedStrings, str);
4589     } else {
4590         he->val = (void*) refCount;
4591     }
4592 }
4593
4594 /* -----------------------------------------------------------------------------
4595  * Integer object
4596  * ---------------------------------------------------------------------------*/
4597 #define JIM_INTEGER_SPACE 24
4598
4599 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4600 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4601
4602 static Jim_ObjType intObjType = {
4603     "int",
4604     NULL,
4605     NULL,
4606     UpdateStringOfInt,
4607     JIM_TYPE_NONE,
4608 };
4609
4610 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4611 {
4612     int len;
4613     char buf[JIM_INTEGER_SPACE+1];
4614
4615     len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4616     objPtr->bytes = Jim_Alloc(len+1);
4617     memcpy(objPtr->bytes, buf, len+1);
4618     objPtr->length = len;
4619 }
4620
4621 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4622 {
4623     jim_wide wideValue;
4624     const char *str;
4625
4626     /* Get the string representation */
4627     str = Jim_GetString(objPtr, NULL);
4628     /* Try to convert into a jim_wide */
4629     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4630         if (flags & JIM_ERRMSG) {
4631             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4632             Jim_AppendStrings(interp, Jim_GetResult(interp),
4633                     "expected integer but got \"", str, "\"", NULL);
4634         }
4635         return JIM_ERR;
4636     }
4637     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4638         errno == ERANGE) {
4639         Jim_SetResultString(interp,
4640             "Integer value too big to be represented", -1);
4641         return JIM_ERR;
4642     }
4643     /* Free the old internal repr and set the new one. */
4644     Jim_FreeIntRep(interp, objPtr);
4645     objPtr->typePtr = &intObjType;
4646     objPtr->internalRep.wideValue = wideValue;
4647     return JIM_OK;
4648 }
4649
4650 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4651 {
4652     if (objPtr->typePtr != &intObjType &&
4653         SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4654         return JIM_ERR;
4655     *widePtr = objPtr->internalRep.wideValue;
4656     return JIM_OK;
4657 }
4658
4659 /* Get a wide but does not set an error if the format is bad. */
4660 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4661         jim_wide *widePtr)
4662 {
4663     if (objPtr->typePtr != &intObjType &&
4664         SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4665         return JIM_ERR;
4666     *widePtr = objPtr->internalRep.wideValue;
4667     return JIM_OK;
4668 }
4669
4670 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4671 {
4672     jim_wide wideValue;
4673     int retval;
4674
4675     retval = Jim_GetWide(interp, objPtr, &wideValue);
4676     if (retval == JIM_OK) {
4677         *longPtr = (long) wideValue;
4678         return JIM_OK;
4679     }
4680     return JIM_ERR;
4681 }
4682
4683 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4684 {
4685     if (Jim_IsShared(objPtr))
4686         Jim_Panic(interp,"Jim_SetWide called with shared object");
4687     if (objPtr->typePtr != &intObjType) {
4688         Jim_FreeIntRep(interp, objPtr);
4689         objPtr->typePtr = &intObjType;
4690     }
4691     Jim_InvalidateStringRep(objPtr);
4692     objPtr->internalRep.wideValue = wideValue;
4693 }
4694
4695 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4696 {
4697     Jim_Obj *objPtr;
4698
4699     objPtr = Jim_NewObj(interp);
4700     objPtr->typePtr = &intObjType;
4701     objPtr->bytes = NULL;
4702     objPtr->internalRep.wideValue = wideValue;
4703     return objPtr;
4704 }
4705
4706 /* -----------------------------------------------------------------------------
4707  * Double object
4708  * ---------------------------------------------------------------------------*/
4709 #define JIM_DOUBLE_SPACE 30
4710
4711 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4712 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4713
4714 static Jim_ObjType doubleObjType = {
4715     "double",
4716     NULL,
4717     NULL,
4718     UpdateStringOfDouble,
4719     JIM_TYPE_NONE,
4720 };
4721
4722 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4723 {
4724     int len;
4725     char buf[JIM_DOUBLE_SPACE+1];
4726
4727     len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4728     objPtr->bytes = Jim_Alloc(len+1);
4729     memcpy(objPtr->bytes, buf, len+1);
4730     objPtr->length = len;
4731 }
4732
4733 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4734 {
4735     double doubleValue;
4736     const char *str;
4737
4738     /* Get the string representation */
4739     str = Jim_GetString(objPtr, NULL);
4740     /* Try to convert into a double */
4741     if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4742         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4743         Jim_AppendStrings(interp, Jim_GetResult(interp),
4744                 "expected number but got '", str, "'", NULL);
4745         return JIM_ERR;
4746     }
4747     /* Free the old internal repr and set the new one. */
4748     Jim_FreeIntRep(interp, objPtr);
4749     objPtr->typePtr = &doubleObjType;
4750     objPtr->internalRep.doubleValue = doubleValue;
4751     return JIM_OK;
4752 }
4753
4754 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4755 {
4756     if (objPtr->typePtr != &doubleObjType &&
4757         SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4758         return JIM_ERR;
4759     *doublePtr = objPtr->internalRep.doubleValue;
4760     return JIM_OK;
4761 }
4762
4763 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4764 {
4765     if (Jim_IsShared(objPtr))
4766         Jim_Panic(interp,"Jim_SetDouble called with shared object");
4767     if (objPtr->typePtr != &doubleObjType) {
4768         Jim_FreeIntRep(interp, objPtr);
4769         objPtr->typePtr = &doubleObjType;
4770     }
4771     Jim_InvalidateStringRep(objPtr);
4772     objPtr->internalRep.doubleValue = doubleValue;
4773 }
4774
4775 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4776 {
4777     Jim_Obj *objPtr;
4778
4779     objPtr = Jim_NewObj(interp);
4780     objPtr->typePtr = &doubleObjType;
4781     objPtr->bytes = NULL;
4782     objPtr->internalRep.doubleValue = doubleValue;
4783     return objPtr;
4784 }
4785
4786 /* -----------------------------------------------------------------------------
4787  * List object
4788  * ---------------------------------------------------------------------------*/
4789 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4790 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4791 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4792 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4793 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4794
4795 /* Note that while the elements of the list may contain references,
4796  * the list object itself can't. This basically means that the
4797  * list object string representation as a whole can't contain references
4798  * that are not presents in the single elements. */
4799 static Jim_ObjType listObjType = {
4800     "list",
4801     FreeListInternalRep,
4802     DupListInternalRep,
4803     UpdateStringOfList,
4804     JIM_TYPE_NONE,
4805 };
4806
4807 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4808 {
4809     int i;
4810
4811     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4812         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4813     }
4814     Jim_Free(objPtr->internalRep.listValue.ele);
4815 }
4816
4817 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4818 {
4819     int i;
4820     JIM_NOTUSED(interp);
4821
4822     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4823     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4824     dupPtr->internalRep.listValue.ele =
4825         Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4826     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4827             sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4828     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4829         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4830     }
4831     dupPtr->typePtr = &listObjType;
4832 }
4833
4834 /* The following function checks if a given string can be encoded
4835  * into a list element without any kind of quoting, surrounded by braces,
4836  * or using escapes to quote. */
4837 #define JIM_ELESTR_SIMPLE 0
4838 #define JIM_ELESTR_BRACE 1
4839 #define JIM_ELESTR_QUOTE 2
4840 static int ListElementQuotingType(const char *s, int len)
4841 {
4842     int i, level, trySimple = 1;
4843
4844     /* Try with the SIMPLE case */
4845     if (len == 0) return JIM_ELESTR_BRACE;
4846     if (s[0] == '"' || s[0] == '{') {
4847         trySimple = 0;
4848         goto testbrace;
4849     }
4850     for (i = 0; i < len; i++) {
4851         switch(s[i]) {
4852         case ' ':
4853         case '$':
4854         case '"':
4855         case '[':
4856         case ']':
4857         case ';':
4858         case '\\':
4859         case '\r':
4860         case '\n':
4861         case '\t':
4862         case '\f':
4863         case '\v':
4864             trySimple = 0;
4865         case '{':
4866         case '}':
4867             goto testbrace;
4868         }
4869     }
4870     return JIM_ELESTR_SIMPLE;
4871
4872 testbrace:
4873     /* Test if it's possible to do with braces */
4874     if (s[len-1] == '\\' ||
4875         s[len-1] == ']') return JIM_ELESTR_QUOTE;
4876     level = 0;
4877     for (i = 0; i < len; i++) {
4878         switch(s[i]) {
4879         case '{': level++; break;
4880         case '}': level--;
4881               if (level < 0) return JIM_ELESTR_QUOTE;
4882               break;
4883         case '\\':
4884               if (s[i+1] == '\n')
4885                   return JIM_ELESTR_QUOTE;
4886               else
4887                   if (s[i+1] != '\0') i++;
4888               break;
4889         }
4890     }
4891     if (level == 0) {
4892         if (!trySimple) return JIM_ELESTR_BRACE;
4893         for (i = 0; i < len; i++) {
4894             switch(s[i]) {
4895             case ' ':
4896             case '$':
4897             case '"':
4898             case '[':
4899             case ']':
4900             case ';':
4901             case '\\':
4902             case '\r':
4903             case '\n':
4904             case '\t':
4905             case '\f':
4906             case '\v':
4907                 return JIM_ELESTR_BRACE;
4908                 break;
4909             }
4910         }
4911         return JIM_ELESTR_SIMPLE;
4912     }
4913     return JIM_ELESTR_QUOTE;
4914 }
4915
4916 /* Returns the malloc-ed representation of a string
4917  * using backslash to quote special chars. */
4918 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4919 {
4920     char *q = Jim_Alloc(len*2+1), *p;
4921
4922     p = q;
4923     while(*s) {
4924         switch (*s) {
4925         case ' ':
4926         case '$':
4927         case '"':
4928         case '[':
4929         case ']':
4930         case '{':
4931         case '}':
4932         case ';':
4933         case '\\':
4934             *p++ = '\\';
4935             *p++ = *s++;
4936             break;
4937         case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4938         case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4939         case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4940         case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4941         case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4942         default:
4943             *p++ = *s++;
4944             break;
4945         }
4946     }
4947     *p = '\0';
4948     *qlenPtr = p-q;
4949     return q;
4950 }
4951
4952 void UpdateStringOfList(struct Jim_Obj *objPtr)
4953 {
4954     int i, bufLen, realLength;
4955     const char *strRep;
4956     char *p;
4957     int *quotingType;
4958     Jim_Obj **ele = objPtr->internalRep.listValue.ele;
4959
4960     /* (Over) Estimate the space needed. */
4961     quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
4962     bufLen = 0;
4963     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4964         int len;
4965
4966         strRep = Jim_GetString(ele[i], &len);
4967         quotingType[i] = ListElementQuotingType(strRep, len);
4968         switch (quotingType[i]) {
4969         case JIM_ELESTR_SIMPLE: bufLen += len; break;
4970         case JIM_ELESTR_BRACE: bufLen += len+2; break;
4971         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
4972         }
4973         bufLen++; /* elements separator. */
4974     }
4975     bufLen++;
4976
4977     /* Generate the string rep. */
4978     p = objPtr->bytes = Jim_Alloc(bufLen+1);
4979     realLength = 0;
4980     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4981         int len, qlen;
4982         const char *strRep = Jim_GetString(ele[i], &len);
4983         char *q;
4984
4985         switch(quotingType[i]) {
4986         case JIM_ELESTR_SIMPLE:
4987             memcpy(p, strRep, len);
4988             p += len;
4989             realLength += len;
4990             break;
4991         case JIM_ELESTR_BRACE:
4992             *p++ = '{';
4993             memcpy(p, strRep, len);
4994             p += len;
4995             *p++ = '}';
4996             realLength += len+2;
4997             break;
4998         case JIM_ELESTR_QUOTE:
4999             q = BackslashQuoteString(strRep, len, &qlen);
5000             memcpy(p, q, qlen);
5001             Jim_Free(q);
5002             p += qlen;
5003             realLength += qlen;
5004             break;
5005         }
5006         /* Add a separating space */
5007         if (i+1 != objPtr->internalRep.listValue.len) {
5008             *p++ = ' ';
5009             realLength ++;
5010         }
5011     }
5012     *p = '\0'; /* nul term. */
5013     objPtr->length = realLength;
5014     Jim_Free(quotingType);
5015 }
5016
5017 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5018 {
5019     struct JimParserCtx parser;
5020     const char *str;
5021     int strLen;
5022
5023     /* Get the string representation */
5024     str = Jim_GetString(objPtr, &strLen);
5025
5026     /* Free the old internal repr just now and initialize the
5027      * new one just now. The string->list conversion can't fail. */
5028     Jim_FreeIntRep(interp, objPtr);
5029     objPtr->typePtr = &listObjType;
5030     objPtr->internalRep.listValue.len = 0;
5031     objPtr->internalRep.listValue.maxLen = 0;
5032     objPtr->internalRep.listValue.ele = NULL;
5033
5034     /* Convert into a list */
5035     JimParserInit(&parser, str, strLen, 1);
5036     while(!JimParserEof(&parser)) {
5037         char *token;
5038         int tokenLen, type;
5039         Jim_Obj *elementPtr;
5040
5041         JimParseList(&parser);
5042         if (JimParserTtype(&parser) != JIM_TT_STR &&
5043             JimParserTtype(&parser) != JIM_TT_ESC)
5044             continue;
5045         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5046         elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5047         ListAppendElement(objPtr, elementPtr);
5048     }
5049     return JIM_OK;
5050 }
5051
5052 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, 
5053         int len)
5054 {
5055     Jim_Obj *objPtr;
5056     int i;
5057
5058     objPtr = Jim_NewObj(interp);
5059     objPtr->typePtr = &listObjType;
5060     objPtr->bytes = NULL;
5061     objPtr->internalRep.listValue.ele = NULL;
5062     objPtr->internalRep.listValue.len = 0;
5063     objPtr->internalRep.listValue.maxLen = 0;
5064     for (i = 0; i < len; i++) {
5065         ListAppendElement(objPtr, elements[i]);
5066     }
5067     return objPtr;
5068 }
5069
5070 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5071  * length of the vector. Note that the user of this function should make
5072  * sure that the list object can't shimmer while the vector returned
5073  * is in use, this vector is the one stored inside the internal representation
5074  * of the list object. This function is not exported, extensions should
5075  * always access to the List object elements using Jim_ListIndex(). */
5076 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5077         Jim_Obj ***listVec)
5078 {
5079     Jim_ListLength(interp, listObj, argc);
5080     assert(listObj->typePtr == &listObjType);
5081     *listVec = listObj->internalRep.listValue.ele;
5082 }
5083
5084 /* ListSortElements type values */
5085 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5086       JIM_LSORT_NOCASE_DECR};
5087
5088 /* Sort the internal rep of a list. */
5089 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5090 {
5091     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5092 }
5093
5094 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5095 {
5096     return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5097 }
5098
5099 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5100 {
5101     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5102 }
5103
5104 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5105 {
5106     return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5107 }
5108
5109 /* Sort a list *in place*. MUST be called with non-shared objects. */
5110 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5111 {
5112     typedef int (qsort_comparator)(const void *, const void *);
5113     int (*fn)(Jim_Obj**, Jim_Obj**);
5114     Jim_Obj **vector;
5115     int len;
5116
5117     if (Jim_IsShared(listObjPtr))
5118         Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5119     if (listObjPtr->typePtr != &listObjType)
5120         SetListFromAny(interp, listObjPtr);
5121
5122     vector = listObjPtr->internalRep.listValue.ele;
5123     len = listObjPtr->internalRep.listValue.len;
5124     switch (type) {
5125         case JIM_LSORT_ASCII: fn = ListSortString;  break;
5126         case JIM_LSORT_NOCASE: fn = ListSortStringNoCase;  break;
5127         case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr;  break;
5128         case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr;  break;
5129         default:
5130             fn = NULL; /* avoid warning */
5131             Jim_Panic(interp,"ListSort called with invalid sort type");
5132     }
5133     qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5134     Jim_InvalidateStringRep(listObjPtr);
5135 }
5136
5137 /* This is the low-level function to append an element to a list.
5138  * The higher-level Jim_ListAppendElement() performs shared object
5139  * check and invalidate the string repr. This version is used
5140  * in the internals of the List Object and is not exported.
5141  *
5142  * NOTE: this function can be called only against objects
5143  * with internal type of List. */
5144 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5145 {
5146     int requiredLen = listPtr->internalRep.listValue.len + 1;
5147
5148     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5149         int maxLen = requiredLen * 2;
5150
5151         listPtr->internalRep.listValue.ele =
5152             Jim_Realloc(listPtr->internalRep.listValue.ele,
5153                     sizeof(Jim_Obj*)*maxLen);
5154         listPtr->internalRep.listValue.maxLen = maxLen;
5155     }
5156     listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5157         objPtr;
5158     listPtr->internalRep.listValue.len ++;
5159     Jim_IncrRefCount(objPtr);
5160 }
5161
5162 /* This is the low-level function to insert elements into a list.
5163  * The higher-level Jim_ListInsertElements() performs shared object
5164  * check and invalidate the string repr. This version is used
5165  * in the internals of the List Object and is not exported.
5166  *
5167  * NOTE: this function can be called only against objects
5168  * with internal type of List. */
5169 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5170         Jim_Obj *const *elemVec)
5171 {
5172     int currentLen = listPtr->internalRep.listValue.len;
5173     int requiredLen = currentLen + elemc;
5174     int i;
5175     Jim_Obj **point;
5176
5177     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5178         int maxLen = requiredLen * 2;
5179
5180         listPtr->internalRep.listValue.ele =
5181             Jim_Realloc(listPtr->internalRep.listValue.ele,
5182                     sizeof(Jim_Obj*)*maxLen);
5183         listPtr->internalRep.listValue.maxLen = maxLen;
5184     }
5185     point = listPtr->internalRep.listValue.ele + index;
5186     memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5187     for (i=0; i < elemc; ++i) {
5188         point[i] = elemVec[i];
5189         Jim_IncrRefCount(point[i]);
5190     }
5191     listPtr->internalRep.listValue.len += elemc;
5192 }
5193
5194 /* Appends every element of appendListPtr into listPtr.
5195  * Both have to be of the list type. */
5196 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5197 {
5198     int i, oldLen = listPtr->internalRep.listValue.len;
5199     int appendLen = appendListPtr->internalRep.listValue.len;
5200     int requiredLen = oldLen + appendLen;
5201
5202     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5203         int maxLen = requiredLen * 2;
5204
5205         listPtr->internalRep.listValue.ele =
5206             Jim_Realloc(listPtr->internalRep.listValue.ele,
5207                     sizeof(Jim_Obj*)*maxLen);
5208         listPtr->internalRep.listValue.maxLen = maxLen;
5209     }
5210     for (i = 0; i < appendLen; i++) {
5211         Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5212         listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5213         Jim_IncrRefCount(objPtr);
5214     }
5215     listPtr->internalRep.listValue.len += appendLen;
5216 }
5217
5218 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5219 {
5220     if (Jim_IsShared(listPtr))
5221         Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5222     if (listPtr->typePtr != &listObjType)
5223         SetListFromAny(interp, listPtr);
5224     Jim_InvalidateStringRep(listPtr);
5225     ListAppendElement(listPtr, objPtr);
5226 }
5227
5228 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5229 {
5230     if (Jim_IsShared(listPtr))
5231         Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5232     if (listPtr->typePtr != &listObjType)
5233         SetListFromAny(interp, listPtr);
5234     Jim_InvalidateStringRep(listPtr);
5235     ListAppendList(listPtr, appendListPtr);
5236 }
5237
5238 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5239 {
5240     if (listPtr->typePtr != &listObjType)
5241         SetListFromAny(interp, listPtr);
5242     *intPtr = listPtr->internalRep.listValue.len;
5243 }
5244
5245 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5246         int objc, Jim_Obj *const *objVec)
5247 {
5248     if (Jim_IsShared(listPtr))
5249         Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5250     if (listPtr->typePtr != &listObjType)
5251         SetListFromAny(interp, listPtr);
5252     if (index >= 0 && index > listPtr->internalRep.listValue.len)
5253         index = listPtr->internalRep.listValue.len;
5254     else if (index < 0 ) 
5255         index = 0;
5256     Jim_InvalidateStringRep(listPtr);
5257     ListInsertElements(listPtr, index, objc, objVec);
5258 }
5259
5260 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5261         Jim_Obj **objPtrPtr, int flags)
5262 {
5263     if (listPtr->typePtr != &listObjType)
5264         SetListFromAny(interp, listPtr);
5265     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5266         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5267         if (flags & JIM_ERRMSG) {
5268             Jim_SetResultString(interp,
5269                 "list index out of range", -1);
5270         }
5271         return JIM_ERR;
5272     }
5273     if (index < 0)
5274         index = listPtr->internalRep.listValue.len+index;
5275     *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5276     return JIM_OK;
5277 }
5278
5279 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5280         Jim_Obj *newObjPtr, int flags)
5281 {
5282     if (listPtr->typePtr != &listObjType)
5283         SetListFromAny(interp, listPtr);
5284     if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5285         (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5286         if (flags & JIM_ERRMSG) {
5287             Jim_SetResultString(interp,
5288                 "list index out of range", -1);
5289         }
5290         return JIM_ERR;
5291     }
5292     if (index < 0)
5293         index = listPtr->internalRep.listValue.len+index;
5294     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5295     listPtr->internalRep.listValue.ele[index] = newObjPtr;
5296     Jim_IncrRefCount(newObjPtr);
5297     return JIM_OK;
5298 }
5299
5300 /* Modify the list stored into the variable named 'varNamePtr'
5301  * setting the element specified by the 'indexc' indexes objects in 'indexv',
5302  * with the new element 'newObjptr'. */
5303 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5304         Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5305 {
5306     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5307     int shared, i, index;
5308
5309     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5310     if (objPtr == NULL)
5311         return JIM_ERR;
5312     if ((shared = Jim_IsShared(objPtr)))
5313         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5314     for (i = 0; i < indexc-1; i++) {
5315         listObjPtr = objPtr;
5316         if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5317             goto err;
5318         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5319                     JIM_ERRMSG) != JIM_OK) {
5320             goto err;
5321         }
5322         if (Jim_IsShared(objPtr)) {
5323             objPtr = Jim_DuplicateObj(interp, objPtr);
5324             ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5325         }
5326         Jim_InvalidateStringRep(listObjPtr);
5327     }
5328     if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5329         goto err;
5330     if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5331         goto err;
5332     Jim_InvalidateStringRep(objPtr);
5333     Jim_InvalidateStringRep(varObjPtr);
5334     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5335         goto err;
5336     Jim_SetResult(interp, varObjPtr);
5337     return JIM_OK;
5338 err:
5339     if (shared) {
5340         Jim_FreeNewObj(interp, varObjPtr);
5341     }
5342     return JIM_ERR;
5343 }
5344
5345 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5346 {
5347     int i;
5348
5349     /* If all the objects in objv are lists without string rep.
5350      * it's possible to return a list as result, that's the
5351      * concatenation of all the lists. */
5352     for (i = 0; i < objc; i++) {
5353         if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5354             break;
5355     }
5356     if (i == objc) {
5357         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5358         for (i = 0; i < objc; i++)
5359             Jim_ListAppendList(interp, objPtr, objv[i]);
5360         return objPtr;
5361     } else {
5362         /* Else... we have to glue strings together */
5363         int len = 0, objLen;
5364         char *bytes, *p;
5365
5366         /* Compute the length */
5367         for (i = 0; i < objc; i++) {
5368             Jim_GetString(objv[i], &objLen);
5369             len += objLen;
5370         }
5371         if (objc) len += objc-1;
5372         /* Create the string rep, and a stinrg object holding it. */
5373         p = bytes = Jim_Alloc(len+1);
5374         for (i = 0; i < objc; i++) {
5375             const char *s = Jim_GetString(objv[i], &objLen);
5376             while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5377             {
5378                 s++; objLen--; len--;
5379             }
5380             while (objLen && (s[objLen-1] == ' ' ||
5381                 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5382                 objLen--; len--;
5383             }
5384             memcpy(p, s, objLen);
5385             p += objLen;
5386             if (objLen && i+1 != objc) {
5387                 *p++ = ' ';
5388             } else if (i+1 != objc) {
5389                 /* Drop the space calcuated for this
5390                  * element that is instead null. */
5391                 len--;
5392             }
5393         }
5394         *p = '\0';
5395         return Jim_NewStringObjNoAlloc(interp, bytes, len);
5396     }
5397 }
5398
5399 /* Returns a list composed of the elements in the specified range.
5400  * first and start are directly accepted as Jim_Objects and
5401  * processed for the end?-index? case. */
5402 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5403 {
5404     int first, last;
5405     int len, rangeLen;
5406
5407     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5408         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5409         return NULL;
5410     Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5411     first = JimRelToAbsIndex(len, first);
5412     last = JimRelToAbsIndex(len, last);
5413     JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5414     return Jim_NewListObj(interp,
5415             listObjPtr->internalRep.listValue.ele+first, rangeLen);
5416 }
5417
5418 /* -----------------------------------------------------------------------------
5419  * Dict object
5420  * ---------------------------------------------------------------------------*/
5421 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5422 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5423 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5424 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5425
5426 /* Dict HashTable Type.
5427  *
5428  * Keys and Values are Jim objects. */
5429
5430 unsigned int JimObjectHTHashFunction(const void *key)
5431 {
5432     const char *str;
5433     Jim_Obj *objPtr = (Jim_Obj*) key;
5434     int len, h;
5435
5436     str = Jim_GetString(objPtr, &len);
5437     h = Jim_GenHashFunction((unsigned char*)str, len);
5438     return h;
5439 }
5440
5441 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5442 {
5443     JIM_NOTUSED(privdata);
5444
5445     return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5446 }
5447
5448 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5449 {
5450     Jim_Obj *objPtr = val;
5451
5452     Jim_DecrRefCount(interp, objPtr);
5453 }
5454
5455 static Jim_HashTableType JimDictHashTableType = {
5456     JimObjectHTHashFunction,            /* hash function */
5457     NULL,                               /* key dup */
5458     NULL,                               /* val dup */
5459     JimObjectHTKeyCompare,              /* key compare */
5460     (void(*)(void*, const void*))       /* ATTENTION: const cast */
5461         JimObjectHTKeyValDestructor,    /* key destructor */
5462     JimObjectHTKeyValDestructor         /* val destructor */
5463 };
5464
5465 /* Note that while the elements of the dict may contain references,
5466  * the list object itself can't. This basically means that the
5467  * dict object string representation as a whole can't contain references
5468  * that are not presents in the single elements. */
5469 static Jim_ObjType dictObjType = {
5470     "dict",
5471     FreeDictInternalRep,
5472     DupDictInternalRep,
5473     UpdateStringOfDict,
5474     JIM_TYPE_NONE,
5475 };
5476
5477 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5478 {
5479     JIM_NOTUSED(interp);
5480
5481     Jim_FreeHashTable(objPtr->internalRep.ptr);
5482     Jim_Free(objPtr->internalRep.ptr);
5483 }
5484
5485 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5486 {
5487     Jim_HashTable *ht, *dupHt;
5488     Jim_HashTableIterator *htiter;
5489     Jim_HashEntry *he;
5490
5491     /* Create a new hash table */
5492     ht = srcPtr->internalRep.ptr;
5493     dupHt = Jim_Alloc(sizeof(*dupHt));
5494     Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5495     if (ht->size != 0)
5496         Jim_ExpandHashTable(dupHt, ht->size);
5497     /* Copy every element from the source to the dup hash table */
5498     htiter = Jim_GetHashTableIterator(ht);
5499     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5500         const Jim_Obj *keyObjPtr = he->key;
5501         Jim_Obj *valObjPtr = he->val;
5502
5503         Jim_IncrRefCount((Jim_Obj*)keyObjPtr);  /* ATTENTION: const cast */
5504         Jim_IncrRefCount(valObjPtr);
5505         Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5506     }
5507     Jim_FreeHashTableIterator(htiter);
5508
5509     dupPtr->internalRep.ptr = dupHt;
5510     dupPtr->typePtr = &dictObjType;
5511 }
5512
5513 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5514 {
5515     int i, bufLen, realLength;
5516     const char *strRep;
5517     char *p;
5518     int *quotingType, objc;
5519     Jim_HashTable *ht;
5520     Jim_HashTableIterator *htiter;
5521     Jim_HashEntry *he;
5522     Jim_Obj **objv;
5523
5524     /* Trun the hash table into a flat vector of Jim_Objects. */
5525     ht = objPtr->internalRep.ptr;
5526     objc = ht->used*2;
5527     objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5528     htiter = Jim_GetHashTableIterator(ht);
5529     i = 0;
5530     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5531         objv[i++] = (Jim_Obj*)he->key;  /* ATTENTION: const cast */
5532         objv[i++] = he->val;
5533     }
5534     Jim_FreeHashTableIterator(htiter);
5535     /* (Over) Estimate the space needed. */
5536     quotingType = Jim_Alloc(sizeof(int)*objc);
5537     bufLen = 0;
5538     for (i = 0; i < objc; i++) {
5539         int len;
5540
5541         strRep = Jim_GetString(objv[i], &len);
5542         quotingType[i] = ListElementQuotingType(strRep, len);
5543         switch (quotingType[i]) {
5544         case JIM_ELESTR_SIMPLE: bufLen += len; break;
5545         case JIM_ELESTR_BRACE: bufLen += len+2; break;
5546         case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5547         }
5548         bufLen++; /* elements separator. */
5549     }
5550     bufLen++;
5551
5552     /* Generate the string rep. */
5553     p = objPtr->bytes = Jim_Alloc(bufLen+1);
5554     realLength = 0;
5555     for (i = 0; i < objc; i++) {
5556         int len, qlen;
5557         const char *strRep = Jim_GetString(objv[i], &len);
5558         char *q;
5559
5560         switch(quotingType[i]) {
5561         case JIM_ELESTR_SIMPLE:
5562             memcpy(p, strRep, len);
5563             p += len;
5564             realLength += len;
5565             break;
5566         case JIM_ELESTR_BRACE:
5567             *p++ = '{';
5568             memcpy(p, strRep, len);
5569             p += len;
5570             *p++ = '}';
5571             realLength += len+2;
5572             break;
5573         case JIM_ELESTR_QUOTE:
5574             q = BackslashQuoteString(strRep, len, &qlen);
5575             memcpy(p, q, qlen);
5576             Jim_Free(q);
5577             p += qlen;
5578             realLength += qlen;
5579             break;
5580         }
5581         /* Add a separating space */
5582         if (i+1 != objc) {
5583             *p++ = ' ';
5584             realLength ++;
5585         }
5586     }
5587     *p = '\0'; /* nul term. */
5588     objPtr->length = realLength;
5589     Jim_Free(quotingType);
5590     Jim_Free(objv);
5591 }
5592
5593 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5594 {
5595     struct JimParserCtx parser;
5596     Jim_HashTable *ht;
5597     Jim_Obj *objv[2];
5598     const char *str;
5599     int i, strLen;
5600
5601     /* Get the string representation */
5602     str = Jim_GetString(objPtr, &strLen);
5603
5604     /* Free the old internal repr just now and initialize the
5605      * new one just now. The string->list conversion can't fail. */
5606     Jim_FreeIntRep(interp, objPtr);
5607     ht = Jim_Alloc(sizeof(*ht));
5608     Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5609     objPtr->typePtr = &dictObjType;
5610     objPtr->internalRep.ptr = ht;
5611
5612     /* Convert into a dict */
5613     JimParserInit(&parser, str, strLen, 1);
5614     i = 0;
5615     while(!JimParserEof(&parser)) {
5616         char *token;
5617         int tokenLen, type;
5618
5619         JimParseList(&parser);
5620         if (JimParserTtype(&parser) != JIM_TT_STR &&
5621             JimParserTtype(&parser) != JIM_TT_ESC)
5622             continue;
5623         token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5624         objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5625         if (i == 2) {
5626             i = 0;
5627             Jim_IncrRefCount(objv[0]);
5628             Jim_IncrRefCount(objv[1]);
5629             if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5630                 Jim_HashEntry *he;
5631                 he = Jim_FindHashEntry(ht, objv[0]);
5632                 Jim_DecrRefCount(interp, objv[0]);
5633                 /* ATTENTION: const cast */
5634                 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5635                 he->val = objv[1];
5636             }
5637         }
5638     }
5639     if (i) {
5640         Jim_FreeNewObj(interp, objv[0]);
5641         objPtr->typePtr = NULL;
5642         Jim_FreeHashTable(ht);
5643         Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5644         return JIM_ERR;
5645     }
5646     return JIM_OK;
5647 }
5648
5649 /* Dict object API */
5650
5651 /* Add an element to a dict. objPtr must be of the "dict" type.
5652  * The higer-level exported function is Jim_DictAddElement().
5653  * If an element with the specified key already exists, the value
5654  * associated is replaced with the new one.
5655  *
5656  * if valueObjPtr == NULL, the key is instead removed if it exists. */
5657 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5658         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5659 {
5660     Jim_HashTable *ht = objPtr->internalRep.ptr;
5661
5662     if (valueObjPtr == NULL) { /* unset */
5663         Jim_DeleteHashEntry(ht, keyObjPtr);
5664         return;
5665     }
5666     Jim_IncrRefCount(keyObjPtr);
5667     Jim_IncrRefCount(valueObjPtr);
5668     if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5669         Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5670         Jim_DecrRefCount(interp, keyObjPtr);
5671         /* ATTENTION: const cast */
5672         Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5673         he->val = valueObjPtr;
5674     }
5675 }
5676
5677 /* Add an element, higher-level interface for DictAddElement().
5678  * If valueObjPtr == NULL, the key is removed if it exists. */
5679 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5680         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5681 {
5682     if (Jim_IsShared(objPtr))
5683         Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5684     if (objPtr->typePtr != &dictObjType) {
5685         if (SetDictFromAny(interp, objPtr) != JIM_OK)
5686             return JIM_ERR;
5687     }
5688     DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5689     Jim_InvalidateStringRep(objPtr);
5690     return JIM_OK;
5691 }
5692
5693 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5694 {
5695     Jim_Obj *objPtr;
5696     int i;
5697
5698     if (len % 2)
5699         Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5700
5701     objPtr = Jim_NewObj(interp);
5702     objPtr->typePtr = &dictObjType;
5703     objPtr->bytes = NULL;
5704     objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5705     Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5706     for (i = 0; i < len; i += 2)
5707         DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5708     return objPtr;
5709 }
5710
5711 /* Return the value associated to the specified dict key */
5712 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5713         Jim_Obj **objPtrPtr, int flags)
5714 {
5715     Jim_HashEntry *he;
5716     Jim_HashTable *ht;
5717
5718     if (dictPtr->typePtr != &dictObjType) {
5719         if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5720             return JIM_ERR;
5721     }
5722     ht = dictPtr->internalRep.ptr;
5723     if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5724         if (flags & JIM_ERRMSG) {
5725             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5726             Jim_AppendStrings(interp, Jim_GetResult(interp),
5727                     "key \"", Jim_GetString(keyPtr, NULL),
5728                     "\" not found in dictionary", NULL);
5729         }
5730         return JIM_ERR;
5731     }
5732     *objPtrPtr = he->val;
5733     return JIM_OK;
5734 }
5735
5736 /* Return the value associated to the specified dict keys */
5737 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5738         Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5739 {
5740     Jim_Obj *objPtr;
5741     int i;
5742
5743     if (keyc == 0) {
5744         *objPtrPtr = dictPtr;
5745         return JIM_OK;
5746     }
5747
5748     for (i = 0; i < keyc; i++) {
5749         if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5750                 != JIM_OK)
5751             return JIM_ERR;
5752         dictPtr = objPtr;
5753     }
5754     *objPtrPtr = objPtr;
5755     return JIM_OK;
5756 }
5757
5758 /* Modify the dict stored into the variable named 'varNamePtr'
5759  * setting the element specified by the 'keyc' keys objects in 'keyv',
5760  * with the new value of the element 'newObjPtr'.
5761  *
5762  * If newObjPtr == NULL the operation is to remove the given key
5763  * from the dictionary. */
5764 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5765         Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5766 {
5767     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5768     int shared, i;
5769
5770     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5771     if (objPtr == NULL) {
5772         if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5773             return JIM_ERR;
5774         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5775         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5776             Jim_FreeNewObj(interp, varObjPtr);
5777             return JIM_ERR;
5778         }
5779     }
5780     if ((shared = Jim_IsShared(objPtr)))
5781         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5782     for (i = 0; i < keyc-1; i++) {
5783         dictObjPtr = objPtr;
5784
5785         /* Check if it's a valid dictionary */
5786         if (dictObjPtr->typePtr != &dictObjType) {
5787             if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5788                 goto err;
5789         }
5790         /* Check if the given key exists. */
5791         Jim_InvalidateStringRep(dictObjPtr);
5792         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5793             newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5794         {
5795             /* This key exists at the current level.
5796              * Make sure it's not shared!. */
5797             if (Jim_IsShared(objPtr)) {
5798                 objPtr = Jim_DuplicateObj(interp, objPtr);
5799                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5800             }
5801         } else {
5802             /* Key not found. If it's an [unset] operation
5803              * this is an error. Only the last key may not
5804              * exist. */
5805             if (newObjPtr == NULL)
5806                 goto err;
5807             /* Otherwise set an empty dictionary
5808              * as key's value. */
5809             objPtr = Jim_NewDictObj(interp, NULL, 0);
5810             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5811         }
5812     }
5813     if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5814             != JIM_OK)
5815         goto err;
5816     Jim_InvalidateStringRep(objPtr);
5817     Jim_InvalidateStringRep(varObjPtr);
5818     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5819         goto err;
5820     Jim_SetResult(interp, varObjPtr);
5821     return JIM_OK;
5822 err:
5823     if (shared) {
5824         Jim_FreeNewObj(interp, varObjPtr);
5825     }
5826     return JIM_ERR;
5827 }
5828
5829 /* -----------------------------------------------------------------------------
5830  * Index object
5831  * ---------------------------------------------------------------------------*/
5832 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5833 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5834
5835 static Jim_ObjType indexObjType = {
5836     "index",
5837     NULL,
5838     NULL,
5839     UpdateStringOfIndex,
5840     JIM_TYPE_NONE,
5841 };
5842
5843 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5844 {
5845     int len;
5846     char buf[JIM_INTEGER_SPACE+1];
5847
5848     if (objPtr->internalRep.indexValue >= 0)
5849         len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5850     else if (objPtr->internalRep.indexValue == -1)
5851         len = sprintf(buf, "end");
5852     else {
5853         len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5854     }
5855     objPtr->bytes = Jim_Alloc(len+1);
5856     memcpy(objPtr->bytes, buf, len+1);
5857     objPtr->length = len;
5858 }
5859
5860 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5861 {
5862     int index, end = 0;
5863     const char *str;
5864
5865     /* Get the string representation */
5866     str = Jim_GetString(objPtr, NULL);
5867     /* Try to convert into an index */
5868     if (!strcmp(str, "end")) {
5869         index = 0;
5870         end = 1;
5871     } else {
5872         if (!strncmp(str, "end-", 4)) {
5873             str += 4;
5874             end = 1;
5875         }
5876         if (Jim_StringToIndex(str, &index) != JIM_OK) {
5877             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5878             Jim_AppendStrings(interp, Jim_GetResult(interp),
5879                     "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5880                     "must be integer or end?-integer?", NULL);
5881             return JIM_ERR;
5882         }
5883     }
5884     if (end) {
5885         if (index < 0)
5886             index = INT_MAX;
5887         else
5888             index = -(index+1);
5889     } else if (!end && index < 0)
5890         index = -INT_MAX;
5891     /* Free the old internal repr and set the new one. */
5892     Jim_FreeIntRep(interp, objPtr);
5893     objPtr->typePtr = &indexObjType;
5894     objPtr->internalRep.indexValue = index;
5895     return JIM_OK;
5896 }
5897
5898 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5899 {
5900     /* Avoid shimmering if the object is an integer. */
5901     if (objPtr->typePtr == &intObjType) {
5902         jim_wide val = objPtr->internalRep.wideValue;
5903         if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5904             *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5905             return JIM_OK;
5906         }
5907     }
5908     if (objPtr->typePtr != &indexObjType &&
5909         SetIndexFromAny(interp, objPtr) == JIM_ERR)
5910         return JIM_ERR;
5911     *indexPtr = objPtr->internalRep.indexValue;
5912     return JIM_OK;
5913 }
5914
5915 /* -----------------------------------------------------------------------------
5916  * Return Code Object.
5917  * ---------------------------------------------------------------------------*/
5918
5919 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5920
5921 static Jim_ObjType returnCodeObjType = {
5922     "return-code",
5923     NULL,
5924     NULL,
5925     NULL,
5926     JIM_TYPE_NONE,
5927 };
5928
5929 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5930 {
5931     const char *str;
5932     int strLen, returnCode;
5933     jim_wide wideValue;
5934
5935     /* Get the string representation */
5936     str = Jim_GetString(objPtr, &strLen);
5937     /* Try to convert into an integer */
5938     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5939         returnCode = (int) wideValue;
5940     else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5941         returnCode = JIM_OK;
5942     else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5943         returnCode = JIM_ERR;
5944     else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5945         returnCode = JIM_RETURN;
5946     else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5947         returnCode = JIM_BREAK;
5948     else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5949         returnCode = JIM_CONTINUE;
5950     else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5951         returnCode = JIM_EVAL;
5952     else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5953         returnCode = JIM_EXIT;
5954     else {
5955         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5956         Jim_AppendStrings(interp, Jim_GetResult(interp),
5957                 "expected return code but got '", str, "'",
5958                 NULL);
5959         return JIM_ERR;
5960     }
5961     /* Free the old internal repr and set the new one. */
5962     Jim_FreeIntRep(interp, objPtr);
5963     objPtr->typePtr = &returnCodeObjType;
5964     objPtr->internalRep.returnCode = returnCode;
5965     return JIM_OK;
5966 }
5967
5968 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
5969 {
5970     if (objPtr->typePtr != &returnCodeObjType &&
5971         SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
5972         return JIM_ERR;
5973     *intPtr = objPtr->internalRep.returnCode;
5974     return JIM_OK;
5975 }
5976
5977 /* -----------------------------------------------------------------------------
5978  * Expression Parsing
5979  * ---------------------------------------------------------------------------*/
5980 static int JimParseExprOperator(struct JimParserCtx *pc);
5981 static int JimParseExprNumber(struct JimParserCtx *pc);
5982 static int JimParseExprIrrational(struct JimParserCtx *pc);
5983
5984 /* Exrp's Stack machine operators opcodes. */
5985
5986 /* Binary operators (numbers) */
5987 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
5988 #define JIM_EXPROP_MUL 0
5989 #define JIM_EXPROP_DIV 1
5990 #define JIM_EXPROP_MOD 2
5991 #define JIM_EXPROP_SUB 3
5992 #define JIM_EXPROP_ADD 4
5993 #define JIM_EXPROP_LSHIFT 5
5994 #define JIM_EXPROP_RSHIFT 6
5995 #define JIM_EXPROP_ROTL 7
5996 #define JIM_EXPROP_ROTR 8
5997 #define JIM_EXPROP_LT 9
5998 #define JIM_EXPROP_GT 10
5999 #define JIM_EXPROP_LTE 11
6000 #define JIM_EXPROP_GTE 12
6001 #define JIM_EXPROP_NUMEQ 13
6002 #define JIM_EXPROP_NUMNE 14
6003 #define JIM_EXPROP_BITAND 15
6004 #define JIM_EXPROP_BITXOR 16
6005 #define JIM_EXPROP_BITOR 17
6006 #define JIM_EXPROP_LOGICAND 18
6007 #define JIM_EXPROP_LOGICOR 19
6008 #define JIM_EXPROP_LOGICAND_LEFT 20
6009 #define JIM_EXPROP_LOGICOR_LEFT 21
6010 #define JIM_EXPROP_POW 22
6011 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6012
6013 /* Binary operators (strings) */
6014 #define JIM_EXPROP_STREQ 23
6015 #define JIM_EXPROP_STRNE 24
6016
6017 /* Unary operators (numbers) */
6018 #define JIM_EXPROP_NOT 25
6019 #define JIM_EXPROP_BITNOT 26
6020 #define JIM_EXPROP_UNARYMINUS 27
6021 #define JIM_EXPROP_UNARYPLUS 28
6022 #define JIM_EXPROP_LOGICAND_RIGHT 29
6023 #define JIM_EXPROP_LOGICOR_RIGHT 30
6024
6025 /* Ternary operators */
6026 #define JIM_EXPROP_TERNARY 31
6027
6028 /* Operands */
6029 #define JIM_EXPROP_NUMBER 32
6030 #define JIM_EXPROP_COMMAND 33
6031 #define JIM_EXPROP_VARIABLE 34
6032 #define JIM_EXPROP_DICTSUGAR 35
6033 #define JIM_EXPROP_SUBST 36
6034 #define JIM_EXPROP_STRING 37
6035
6036 /* Operators table */
6037 typedef struct Jim_ExprOperator {
6038     const char *name;
6039     int precedence;
6040     int arity;
6041     int opcode;
6042 } Jim_ExprOperator;
6043
6044 /* name - precedence - arity - opcode */
6045 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6046     {"!", 300, 1, JIM_EXPROP_NOT},
6047     {"~", 300, 1, JIM_EXPROP_BITNOT},
6048     {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6049     {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6050
6051     {"**", 250, 2, JIM_EXPROP_POW},
6052
6053     {"*", 200, 2, JIM_EXPROP_MUL},
6054     {"/", 200, 2, JIM_EXPROP_DIV},
6055     {"%", 200, 2, JIM_EXPROP_MOD},
6056
6057     {"-", 100, 2, JIM_EXPROP_SUB},
6058     {"+", 100, 2, JIM_EXPROP_ADD},
6059
6060     {"<<<", 90, 3, JIM_EXPROP_ROTL},
6061     {">>>", 90, 3, JIM_EXPROP_ROTR},
6062     {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6063     {">>", 90, 2, JIM_EXPROP_RSHIFT},
6064
6065     {"<",  80, 2, JIM_EXPROP_LT},
6066     {">",  80, 2, JIM_EXPROP_GT},
6067     {"<=", 80, 2, JIM_EXPROP_LTE},
6068     {">=", 80, 2, JIM_EXPROP_GTE},
6069
6070     {"==", 70, 2, JIM_EXPROP_NUMEQ},
6071     {"!=", 70, 2, JIM_EXPROP_NUMNE},
6072
6073     {"eq", 60, 2, JIM_EXPROP_STREQ},
6074     {"ne", 60, 2, JIM_EXPROP_STRNE},
6075
6076     {"&", 50, 2, JIM_EXPROP_BITAND},
6077     {"^", 49, 2, JIM_EXPROP_BITXOR},
6078     {"|", 48, 2, JIM_EXPROP_BITOR},
6079
6080     {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6081     {"||", 10, 2, JIM_EXPROP_LOGICOR},
6082
6083     {"?", 5, 3, JIM_EXPROP_TERNARY},
6084     /* private operators */
6085     {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6086     {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6087     {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6088     {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6089 };
6090
6091 #define JIM_EXPR_OPERATORS_NUM \
6092     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6093
6094 int JimParseExpression(struct JimParserCtx *pc)
6095 {
6096     /* Discard spaces and quoted newline */
6097     while(*(pc->p) == ' ' ||
6098           *(pc->p) == '\t' ||
6099           *(pc->p) == '\r' ||
6100           *(pc->p) == '\n' ||
6101             (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6102         pc->p++; pc->len--;
6103     }
6104
6105     if (pc->len == 0) {
6106         pc->tstart = pc->tend = pc->p;
6107         pc->tline = pc->linenr;
6108         pc->tt = JIM_TT_EOL;
6109         pc->eof = 1;
6110         return JIM_OK;
6111     }
6112     switch(*(pc->p)) {
6113     case '(':
6114         pc->tstart = pc->tend = pc->p;
6115         pc->tline = pc->linenr;
6116         pc->tt = JIM_TT_SUBEXPR_START;
6117         pc->p++; pc->len--;
6118         break;
6119     case ')':
6120         pc->tstart = pc->tend = pc->p;
6121         pc->tline = pc->linenr;
6122         pc->tt = JIM_TT_SUBEXPR_END;
6123         pc->p++; pc->len--;
6124         break;
6125     case '[':
6126         return JimParseCmd(pc);
6127         break;
6128     case '$':
6129         if (JimParseVar(pc) == JIM_ERR)
6130             return JimParseExprOperator(pc);
6131         else
6132             return JIM_OK;
6133         break;
6134     case '-':
6135         if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6136             isdigit((int)*(pc->p+1)))
6137             return JimParseExprNumber(pc);
6138         else
6139             return JimParseExprOperator(pc);
6140         break;
6141     case '0': case '1': case '2': case '3': case '4':
6142     case '5': case '6': case '7': case '8': case '9': case '.':
6143         return JimParseExprNumber(pc);
6144         break;
6145     case '"':
6146     case '{':
6147         /* Here it's possible to reuse the List String parsing. */
6148         pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6149         return JimParseListStr(pc);
6150         break;
6151     case 'N': case 'I':
6152     case 'n': case 'i':
6153         if (JimParseExprIrrational(pc) == JIM_ERR)
6154             return JimParseExprOperator(pc);
6155         break;
6156     default:
6157         return JimParseExprOperator(pc);
6158         break;
6159     }
6160     return JIM_OK;
6161 }
6162
6163 int JimParseExprNumber(struct JimParserCtx *pc)
6164 {
6165     int allowdot = 1;
6166     int allowhex = 0;
6167
6168     pc->tstart = pc->p;
6169     pc->tline = pc->linenr;
6170     if (*pc->p == '-') {
6171         pc->p++; pc->len--;
6172     }
6173     while (  isdigit((int)*pc->p) 
6174           || (allowhex && isxdigit((int)*pc->p) )
6175           || (allowdot && *pc->p == '.') 
6176           || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6177               (*pc->p == 'x' || *pc->p == 'X'))
6178           )
6179     {
6180         if ((*pc->p == 'x') || (*pc->p == 'X')) {
6181             allowhex = 1;
6182             allowdot = 0;
6183                 }
6184         if (*pc->p == '.')
6185             allowdot = 0;
6186         pc->p++; pc->len--;
6187         if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6188             pc->p += 2; pc->len -= 2;
6189         }
6190     }
6191     pc->tend = pc->p-1;
6192     pc->tt = JIM_TT_EXPR_NUMBER;
6193     return JIM_OK;
6194 }
6195
6196 int JimParseExprIrrational(struct JimParserCtx *pc)
6197 {
6198     const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6199     const char **token;
6200     for (token = Tokens; *token != NULL; token++) {
6201         int len = strlen(*token);
6202         if (strncmp(*token, pc->p, len) == 0) {
6203             pc->tstart = pc->p;
6204             pc->tend = pc->p + len - 1;
6205             pc->p += len; pc->len -= len;
6206             pc->tline = pc->linenr;
6207             pc->tt = JIM_TT_EXPR_NUMBER;
6208             return JIM_OK;
6209         }
6210     }
6211     return JIM_ERR;
6212 }
6213
6214 int JimParseExprOperator(struct JimParserCtx *pc)
6215 {
6216     int i;
6217     int bestIdx = -1, bestLen = 0;
6218
6219     /* Try to get the longest match. */
6220     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6221         const char *opname;
6222         int oplen;
6223
6224         opname = Jim_ExprOperators[i].name;
6225         if (opname == NULL) continue;
6226         oplen = strlen(opname);
6227
6228         if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6229             bestIdx = i;
6230             bestLen = oplen;
6231         }
6232     }
6233     if (bestIdx == -1) return JIM_ERR;
6234     pc->tstart = pc->p;
6235     pc->tend = pc->p + bestLen - 1;
6236     pc->p += bestLen; pc->len -= bestLen;
6237     pc->tline = pc->linenr;
6238     pc->tt = JIM_TT_EXPR_OPERATOR;
6239     return JIM_OK;
6240 }
6241
6242 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6243 {
6244     int i;
6245     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6246         if (Jim_ExprOperators[i].name &&
6247             strcmp(opname, Jim_ExprOperators[i].name) == 0)
6248             return &Jim_ExprOperators[i];
6249     return NULL;
6250 }
6251
6252 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6253 {
6254     int i;
6255     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6256         if (Jim_ExprOperators[i].opcode == opcode)
6257             return &Jim_ExprOperators[i];
6258     return NULL;
6259 }
6260
6261 /* -----------------------------------------------------------------------------
6262  * Expression Object
6263  * ---------------------------------------------------------------------------*/
6264 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6265 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6266 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6267
6268 static Jim_ObjType exprObjType = {
6269     "expression",
6270     FreeExprInternalRep,
6271     DupExprInternalRep,
6272     NULL,
6273     JIM_TYPE_REFERENCES,
6274 };
6275
6276 /* Expr bytecode structure */
6277 typedef struct ExprByteCode {
6278     int *opcode;        /* Integer array of opcodes. */
6279     Jim_Obj **obj;      /* Array of associated Jim Objects. */
6280     int len;            /* Bytecode length */
6281     int inUse;          /* Used for sharing. */
6282 } ExprByteCode;
6283
6284 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6285 {
6286     int i;
6287     ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6288
6289     expr->inUse--;
6290     if (expr->inUse != 0) return;
6291     for (i = 0; i < expr->len; i++)
6292         Jim_DecrRefCount(interp, expr->obj[i]);
6293     Jim_Free(expr->opcode);
6294     Jim_Free(expr->obj);
6295     Jim_Free(expr);
6296 }
6297
6298 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6299 {
6300     JIM_NOTUSED(interp);
6301     JIM_NOTUSED(srcPtr);
6302
6303     /* Just returns an simple string. */
6304     dupPtr->typePtr = NULL;
6305 }
6306
6307 /* Add a new instruction to an expression bytecode structure. */
6308 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6309         int opcode, char *str, int len)
6310 {
6311     expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6312     expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6313     expr->opcode[expr->len] = opcode;
6314     expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6315     Jim_IncrRefCount(expr->obj[expr->len]);
6316     expr->len++;
6317 }
6318
6319 /* Check if an expr program looks correct. */
6320 static int ExprCheckCorrectness(ExprByteCode *expr)
6321 {
6322     int i;
6323     int stacklen = 0;
6324
6325     /* Try to check if there are stack underflows,
6326      * and make sure at the end of the program there is
6327      * a single result on the stack. */
6328     for (i = 0; i < expr->len; i++) {
6329         switch(expr->opcode[i]) {
6330         case JIM_EXPROP_NUMBER:
6331         case JIM_EXPROP_STRING:
6332         case JIM_EXPROP_SUBST:
6333         case JIM_EXPROP_VARIABLE:
6334         case JIM_EXPROP_DICTSUGAR:
6335         case JIM_EXPROP_COMMAND:
6336             stacklen++;
6337             break;
6338         case JIM_EXPROP_NOT:
6339         case JIM_EXPROP_BITNOT:
6340         case JIM_EXPROP_UNARYMINUS:
6341         case JIM_EXPROP_UNARYPLUS:
6342             /* Unary operations */
6343             if (stacklen < 1) return JIM_ERR;
6344             break;
6345         case JIM_EXPROP_ADD:
6346         case JIM_EXPROP_SUB:
6347         case JIM_EXPROP_MUL:
6348         case JIM_EXPROP_DIV:
6349         case JIM_EXPROP_MOD:
6350         case JIM_EXPROP_LT:
6351         case JIM_EXPROP_GT:
6352         case JIM_EXPROP_LTE:
6353         case JIM_EXPROP_GTE:
6354         case JIM_EXPROP_ROTL:
6355         case JIM_EXPROP_ROTR:
6356         case JIM_EXPROP_LSHIFT:
6357         case JIM_EXPROP_RSHIFT:
6358         case JIM_EXPROP_NUMEQ:
6359         case JIM_EXPROP_NUMNE:
6360         case JIM_EXPROP_STREQ:
6361         case JIM_EXPROP_STRNE:
6362         case JIM_EXPROP_BITAND:
6363         case JIM_EXPROP_BITXOR:
6364         case JIM_EXPROP_BITOR:
6365         case JIM_EXPROP_LOGICAND:
6366         case JIM_EXPROP_LOGICOR:
6367         case JIM_EXPROP_POW:
6368             /* binary operations */
6369             if (stacklen < 2) return JIM_ERR;
6370             stacklen--;
6371             break;
6372         default:
6373             Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6374             break;
6375         }
6376     }
6377     if (stacklen != 1) return JIM_ERR;
6378     return JIM_OK;
6379 }
6380
6381 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6382         ScriptObj *topLevelScript)
6383 {
6384     int i;
6385
6386     return;
6387     for (i = 0; i < expr->len; i++) {
6388         Jim_Obj *foundObjPtr;
6389
6390         if (expr->obj[i] == NULL) continue;
6391         foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6392                 NULL, expr->obj[i]);
6393         if (foundObjPtr != NULL) {
6394             Jim_IncrRefCount(foundObjPtr);
6395             Jim_DecrRefCount(interp, expr->obj[i]);
6396             expr->obj[i] = foundObjPtr;
6397         }
6398     }
6399 }
6400
6401 /* This procedure converts every occurrence of || and && opereators
6402  * in lazy unary versions.
6403  *
6404  * a b || is converted into:
6405  *
6406  * a <offset> |L b |R
6407  *
6408  * a b && is converted into:
6409  *
6410  * a <offset> &L b &R
6411  *
6412  * "|L" checks if 'a' is true:
6413  *   1) if it is true pushes 1 and skips <offset> istructions to reach
6414  *      the opcode just after |R.
6415  *   2) if it is false does nothing.
6416  * "|R" checks if 'b' is true:
6417  *   1) if it is true pushes 1, otherwise pushes 0.
6418  *
6419  * "&L" checks if 'a' is true:
6420  *   1) if it is true does nothing.
6421  *   2) If it is false pushes 0 and skips <offset> istructions to reach
6422  *      the opcode just after &R
6423  * "&R" checks if 'a' is true:
6424  *      if it is true pushes 1, otherwise pushes 0.
6425  */
6426 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6427 {
6428     while (1) {
6429         int index = -1, leftindex, arity, i, offset;
6430         Jim_ExprOperator *op;
6431
6432         /* Search for || or && */
6433         for (i = 0; i < expr->len; i++) {
6434             if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6435                 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6436                 index = i;
6437                 break;
6438             }
6439         }
6440         if (index == -1) return;
6441         /* Search for the end of the first operator */
6442         leftindex = index-1;
6443         arity = 1;
6444         while(arity) {
6445             switch(expr->opcode[leftindex]) {
6446             case JIM_EXPROP_NUMBER:
6447             case JIM_EXPROP_COMMAND:
6448             case JIM_EXPROP_VARIABLE:
6449             case JIM_EXPROP_DICTSUGAR:
6450             case JIM_EXPROP_SUBST:
6451             case JIM_EXPROP_STRING:
6452                 break;
6453             default:
6454                 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6455                 if (op == NULL) {
6456                     Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6457                 }
6458                 arity += op->arity;
6459                 break;
6460             }
6461             arity--;
6462             leftindex--;
6463         }
6464         leftindex++;
6465         expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6466         expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6467         memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6468                 sizeof(int)*(expr->len-leftindex));
6469         memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6470                 sizeof(Jim_Obj*)*(expr->len-leftindex));
6471         expr->len += 2;
6472         index += 2;
6473         offset = (index-leftindex)-1;
6474         Jim_DecrRefCount(interp, expr->obj[index]);
6475         if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6476             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6477             expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6478             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6479             expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6480         } else {
6481             expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6482             expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6483             expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6484             expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6485         }
6486         expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6487         expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6488         Jim_IncrRefCount(expr->obj[index]);
6489         Jim_IncrRefCount(expr->obj[leftindex]);
6490         Jim_IncrRefCount(expr->obj[leftindex+1]);
6491     }
6492 }
6493
6494 /* This method takes the string representation of an expression
6495  * and generates a program for the Expr's stack-based VM. */
6496 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6497 {
6498     int exprTextLen;
6499     const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6500     struct JimParserCtx parser;
6501     int i, shareLiterals;
6502     ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6503     Jim_Stack stack;
6504     Jim_ExprOperator *op;
6505
6506     /* Perform literal sharing with the current procedure
6507      * running only if this expression appears to be not generated
6508      * at runtime. */
6509     shareLiterals = objPtr->typePtr == &sourceObjType;
6510
6511     expr->opcode = NULL;
6512     expr->obj = NULL;
6513     expr->len = 0;
6514     expr->inUse = 1;
6515
6516     Jim_InitStack(&stack);
6517     JimParserInit(&parser, exprText, exprTextLen, 1);
6518     while(!JimParserEof(&parser)) {
6519         char *token;
6520         int len, type;
6521
6522         if (JimParseExpression(&parser) != JIM_OK) {
6523             Jim_SetResultString(interp, "Syntax error in expression", -1);
6524             goto err;
6525         }
6526         token = JimParserGetToken(&parser, &len, &type, NULL);
6527         if (type == JIM_TT_EOL) {
6528             Jim_Free(token);
6529             break;
6530         }
6531         switch(type) {
6532         case JIM_TT_STR:
6533             ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6534             break;
6535         case JIM_TT_ESC:
6536             ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6537             break;
6538         case JIM_TT_VAR:
6539             ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6540             break;
6541         case JIM_TT_DICTSUGAR:
6542             ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6543             break;
6544         case JIM_TT_CMD:
6545             ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6546             break;
6547         case JIM_TT_EXPR_NUMBER:
6548             ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6549             break;
6550         case JIM_TT_EXPR_OPERATOR:
6551             op = JimExprOperatorInfo(token);
6552             while(1) {
6553                 Jim_ExprOperator *stackTopOp;
6554
6555                 if (Jim_StackPeek(&stack) != NULL) {
6556                     stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6557                 } else {
6558                     stackTopOp = NULL;
6559                 }
6560                 if (Jim_StackLen(&stack) && op->arity != 1 &&
6561                     stackTopOp && stackTopOp->precedence >= op->precedence)
6562                 {
6563                     ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6564                         Jim_StackPeek(&stack), -1);
6565                     Jim_StackPop(&stack);
6566                 } else {
6567                     break;
6568                 }
6569             }
6570             Jim_StackPush(&stack, token);
6571             break;
6572         case JIM_TT_SUBEXPR_START:
6573             Jim_StackPush(&stack, Jim_StrDup("("));
6574             Jim_Free(token);
6575             break;
6576         case JIM_TT_SUBEXPR_END:
6577             {
6578                 int found = 0;
6579                 while(Jim_StackLen(&stack)) {
6580                     char *opstr = Jim_StackPop(&stack);
6581                     if (!strcmp(opstr, "(")) {
6582                         Jim_Free(opstr);
6583                         found = 1;
6584                         break;
6585                     }
6586                     op = JimExprOperatorInfo(opstr);
6587                     ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6588                 }
6589                 if (!found) {
6590                     Jim_SetResultString(interp,
6591                         "Unexpected close parenthesis", -1);
6592                     goto err;
6593                 }
6594             }
6595             Jim_Free(token);
6596             break;
6597         default:
6598             Jim_Panic(interp,"Default reached in SetExprFromAny()");
6599             break;
6600         }
6601     }
6602     while (Jim_StackLen(&stack)) {
6603         char *opstr = Jim_StackPop(&stack);
6604         op = JimExprOperatorInfo(opstr);
6605         if (op == NULL && !strcmp(opstr, "(")) {
6606             Jim_Free(opstr);
6607             Jim_SetResultString(interp, "Missing close parenthesis", -1);
6608             goto err;
6609         }
6610         ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6611     }
6612     /* Check program correctness. */
6613     if (ExprCheckCorrectness(expr) != JIM_OK) {
6614         Jim_SetResultString(interp, "Invalid expression", -1);
6615         goto err;
6616     }
6617
6618     /* Free the stack used for the compilation. */
6619     Jim_FreeStackElements(&stack, Jim_Free);
6620     Jim_FreeStack(&stack);
6621
6622     /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6623     ExprMakeLazy(interp, expr);
6624
6625     /* Perform literal sharing */
6626     if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6627         Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6628         if (bodyObjPtr->typePtr == &scriptObjType) {
6629             ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6630             ExprShareLiterals(interp, expr, bodyScript);
6631         }
6632     }
6633
6634     /* Free the old internal rep and set the new one. */
6635     Jim_FreeIntRep(interp, objPtr);
6636     Jim_SetIntRepPtr(objPtr, expr);
6637     objPtr->typePtr = &exprObjType;
6638     return JIM_OK;
6639
6640 err:    /* we jump here on syntax/compile errors. */
6641     Jim_FreeStackElements(&stack, Jim_Free);
6642     Jim_FreeStack(&stack);
6643     Jim_Free(expr->opcode);
6644     for (i = 0; i < expr->len; i++) {
6645         Jim_DecrRefCount(interp,expr->obj[i]);
6646     }
6647     Jim_Free(expr->obj);
6648     Jim_Free(expr);
6649     return JIM_ERR;
6650 }
6651
6652 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6653 {
6654     if (objPtr->typePtr != &exprObjType) {
6655         if (SetExprFromAny(interp, objPtr) != JIM_OK)
6656             return NULL;
6657     }
6658     return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6659 }
6660
6661 /* -----------------------------------------------------------------------------
6662  * Expressions evaluation.
6663  * Jim uses a specialized stack-based virtual machine for expressions,
6664  * that takes advantage of the fact that expr's operators
6665  * can't be redefined.
6666  *
6667  * Jim_EvalExpression() uses the bytecode compiled by
6668  * SetExprFromAny() method of the "expression" object.
6669  *
6670  * On success a Tcl Object containing the result of the evaluation
6671  * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6672  * returned.
6673  * On error the function returns a retcode != to JIM_OK and set a suitable
6674  * error on the interp.
6675  * ---------------------------------------------------------------------------*/
6676 #define JIM_EE_STATICSTACK_LEN 10
6677
6678 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6679         Jim_Obj **exprResultPtrPtr)
6680 {
6681     ExprByteCode *expr;
6682     Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6683     int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6684
6685     Jim_IncrRefCount(exprObjPtr);
6686     expr = Jim_GetExpression(interp, exprObjPtr);
6687     if (!expr) {
6688         Jim_DecrRefCount(interp, exprObjPtr);
6689         return JIM_ERR; /* error in expression. */
6690     }
6691     /* In order to avoid that the internal repr gets freed due to
6692      * shimmering of the exprObjPtr's object, we make the internal rep
6693      * shared. */
6694     expr->inUse++;
6695
6696     /* The stack-based expr VM itself */
6697
6698     /* Stack allocation. Expr programs have the feature that
6699      * a program of length N can't require a stack longer than
6700      * N. */
6701     if (expr->len > JIM_EE_STATICSTACK_LEN)
6702         stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6703     else
6704         stack = staticStack;
6705
6706     /* Execute every istruction */
6707     for (i = 0; i < expr->len; i++) {
6708         Jim_Obj *A, *B, *objPtr;
6709         jim_wide wA, wB, wC;
6710         double dA, dB, dC;
6711         const char *sA, *sB;
6712         int Alen, Blen, retcode;
6713         int opcode = expr->opcode[i];
6714
6715         if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6716             stack[stacklen++] = expr->obj[i];
6717             Jim_IncrRefCount(expr->obj[i]);
6718         } else if (opcode == JIM_EXPROP_VARIABLE) {
6719             objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6720             if (objPtr == NULL) {
6721                 error = 1;
6722                 goto err;
6723             }
6724             stack[stacklen++] = objPtr;
6725             Jim_IncrRefCount(objPtr);
6726         } else if (opcode == JIM_EXPROP_SUBST) {
6727             if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6728                         &objPtr, JIM_NONE)) != JIM_OK)
6729             {
6730                 error = 1;
6731                 errRetCode = retcode;
6732                 goto err;
6733             }
6734             stack[stacklen++] = objPtr;
6735             Jim_IncrRefCount(objPtr);
6736         } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6737             objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6738             if (objPtr == NULL) {
6739                 error = 1;
6740                 goto err;
6741             }
6742             stack[stacklen++] = objPtr;
6743             Jim_IncrRefCount(objPtr);
6744         } else if (opcode == JIM_EXPROP_COMMAND) {
6745             if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6746                 error = 1;
6747                 errRetCode = retcode;
6748                 goto err;
6749             }
6750             stack[stacklen++] = interp->result;
6751             Jim_IncrRefCount(interp->result);
6752         } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6753                    opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6754         {
6755             /* Note that there isn't to increment the
6756              * refcount of objects. the references are moved
6757              * from stack to A and B. */
6758             B = stack[--stacklen];
6759             A = stack[--stacklen];
6760
6761             /* --- Integer --- */
6762             if ((A->typePtr == &doubleObjType && !A->bytes) ||
6763                 (B->typePtr == &doubleObjType && !B->bytes) ||
6764                 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6765                 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6766                 goto trydouble;
6767             }
6768             Jim_DecrRefCount(interp, A);
6769             Jim_DecrRefCount(interp, B);
6770             switch(expr->opcode[i]) {
6771             case JIM_EXPROP_ADD: wC = wA+wB; break;
6772             case JIM_EXPROP_SUB: wC = wA-wB; break;
6773             case JIM_EXPROP_MUL: wC = wA*wB; break;
6774             case JIM_EXPROP_LT: wC = wA<wB; break;
6775             case JIM_EXPROP_GT: wC = wA>wB; break;
6776             case JIM_EXPROP_LTE: wC = wA<=wB; break;
6777             case JIM_EXPROP_GTE: wC = wA>=wB; break;
6778             case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6779             case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6780             case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6781             case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6782             case JIM_EXPROP_BITAND: wC = wA&wB; break;
6783             case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6784             case JIM_EXPROP_BITOR: wC = wA|wB; break;
6785             case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6786             case JIM_EXPROP_LOGICAND_LEFT:
6787                 if (wA == 0) {
6788                     i += (int)wB;
6789                     wC = 0;
6790                 } else {
6791                     continue;
6792                 }
6793                 break;
6794             case JIM_EXPROP_LOGICOR_LEFT:
6795                 if (wA != 0) {
6796                     i += (int)wB;
6797                     wC = 1;
6798                 } else {
6799                     continue;
6800                 }
6801                 break;
6802             case JIM_EXPROP_DIV:
6803                 if (wB == 0) goto divbyzero;
6804                 wC = wA/wB;
6805                 break;
6806             case JIM_EXPROP_MOD:
6807                 if (wB == 0) goto divbyzero;
6808                 wC = wA%wB;
6809                 break;
6810             case JIM_EXPROP_ROTL: {
6811                 /* uint32_t would be better. But not everyone has inttypes.h?*/
6812                 unsigned long uA = (unsigned long)wA;
6813 #ifdef _MSC_VER
6814                 wC = _rotl(uA,(unsigned long)wB);
6815 #else
6816                 const unsigned int S = sizeof(unsigned long) * 8;
6817                 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6818 #endif
6819                 break;
6820             }
6821             case JIM_EXPROP_ROTR: {
6822                 unsigned long uA = (unsigned long)wA;
6823 #ifdef _MSC_VER
6824                 wC = _rotr(uA,(unsigned long)wB);
6825 #else
6826                 const unsigned int S = sizeof(unsigned long) * 8;
6827                 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6828 #endif
6829                 break;
6830             }
6831
6832             default:
6833                 wC = 0; /* avoid gcc warning */
6834                 break;
6835             }
6836             stack[stacklen] = Jim_NewIntObj(interp, wC);
6837             Jim_IncrRefCount(stack[stacklen]);
6838             stacklen++;
6839             continue;
6840 trydouble:
6841             /* --- Double --- */
6842             if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6843                 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6844                 Jim_DecrRefCount(interp, A);
6845                 Jim_DecrRefCount(interp, B);
6846                 error = 1;
6847                 goto err;
6848             }
6849             Jim_DecrRefCount(interp, A);
6850             Jim_DecrRefCount(interp, B);
6851             switch(expr->opcode[i]) {
6852             case JIM_EXPROP_ROTL:
6853             case JIM_EXPROP_ROTR:
6854             case JIM_EXPROP_LSHIFT:
6855             case JIM_EXPROP_RSHIFT:
6856             case JIM_EXPROP_BITAND:
6857             case JIM_EXPROP_BITXOR:
6858             case JIM_EXPROP_BITOR:
6859             case JIM_EXPROP_MOD:
6860             case JIM_EXPROP_POW:
6861                 Jim_SetResultString(interp,
6862                     "Got floating-point value where integer was expected", -1);
6863                 error = 1;
6864                 goto err;
6865                 break;
6866             case JIM_EXPROP_ADD: dC = dA+dB; break;
6867             case JIM_EXPROP_SUB: dC = dA-dB; break;
6868             case JIM_EXPROP_MUL: dC = dA*dB; break;
6869             case JIM_EXPROP_LT: dC = dA<dB; break;
6870             case JIM_EXPROP_GT: dC = dA>dB; break;
6871             case JIM_EXPROP_LTE: dC = dA<=dB; break;
6872             case JIM_EXPROP_GTE: dC = dA>=dB; break;
6873             case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6874             case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6875             case JIM_EXPROP_LOGICAND_LEFT:
6876                 if (dA == 0) {
6877                     i += (int)dB;
6878                     dC = 0;
6879                 } else {
6880                     continue;
6881                 }
6882                 break;
6883             case JIM_EXPROP_LOGICOR_LEFT:
6884                 if (dA != 0) {
6885                     i += (int)dB;
6886                     dC = 1;
6887                 } else {
6888                     continue;
6889                 }
6890                 break;
6891             case JIM_EXPROP_DIV:
6892                 if (dB == 0) goto divbyzero;
6893                 dC = dA/dB;
6894                 break;
6895             default:
6896                 dC = 0; /* avoid gcc warning */
6897                 break;
6898             }
6899             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6900             Jim_IncrRefCount(stack[stacklen]);
6901             stacklen++;
6902         } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6903             B = stack[--stacklen];
6904             A = stack[--stacklen];
6905             sA = Jim_GetString(A, &Alen);
6906             sB = Jim_GetString(B, &Blen);
6907             switch(expr->opcode[i]) {
6908             case JIM_EXPROP_STREQ:
6909                 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6910                     wC = 1;
6911                 else
6912                     wC = 0;
6913                 break;
6914             case JIM_EXPROP_STRNE:
6915                 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6916                     wC = 1;
6917                 else
6918                     wC = 0;
6919                 break;
6920             default:
6921                 wC = 0; /* avoid gcc warning */
6922                 break;
6923             }
6924             Jim_DecrRefCount(interp, A);
6925             Jim_DecrRefCount(interp, B);
6926             stack[stacklen] = Jim_NewIntObj(interp, wC);
6927             Jim_IncrRefCount(stack[stacklen]);
6928             stacklen++;
6929         } else if (opcode == JIM_EXPROP_NOT ||
6930                    opcode == JIM_EXPROP_BITNOT ||
6931                    opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6932                    opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6933             /* Note that there isn't to increment the
6934              * refcount of objects. the references are moved
6935              * from stack to A and B. */
6936             A = stack[--stacklen];
6937
6938             /* --- Integer --- */
6939             if ((A->typePtr == &doubleObjType && !A->bytes) ||
6940                 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6941                 goto trydouble_unary;
6942             }
6943             Jim_DecrRefCount(interp, A);
6944             switch(expr->opcode[i]) {
6945             case JIM_EXPROP_NOT: wC = !wA; break;
6946             case JIM_EXPROP_BITNOT: wC = ~wA; break;
6947             case JIM_EXPROP_LOGICAND_RIGHT:
6948             case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6949             default:
6950                 wC = 0; /* avoid gcc warning */
6951                 break;
6952             }
6953             stack[stacklen] = Jim_NewIntObj(interp, wC);
6954             Jim_IncrRefCount(stack[stacklen]);
6955             stacklen++;
6956             continue;
6957 trydouble_unary:
6958             /* --- Double --- */
6959             if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
6960                 Jim_DecrRefCount(interp, A);
6961                 error = 1;
6962                 goto err;
6963             }
6964             Jim_DecrRefCount(interp, A);
6965             switch(expr->opcode[i]) {
6966             case JIM_EXPROP_NOT: dC = !dA; break;
6967             case JIM_EXPROP_LOGICAND_RIGHT:
6968             case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
6969             case JIM_EXPROP_BITNOT:
6970                 Jim_SetResultString(interp,
6971                     "Got floating-point value where integer was expected", -1);
6972                 error = 1;
6973                 goto err;
6974                 break;
6975             default:
6976                 dC = 0; /* avoid gcc warning */
6977                 break;
6978             }
6979             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6980             Jim_IncrRefCount(stack[stacklen]);
6981             stacklen++;
6982         } else {
6983             Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
6984         }
6985     }
6986 err:
6987     /* There is no need to decerement the inUse field because
6988      * this reference is transfered back into the exprObjPtr. */
6989     Jim_FreeIntRep(interp, exprObjPtr);
6990     exprObjPtr->typePtr = &exprObjType;
6991     Jim_SetIntRepPtr(exprObjPtr, expr);
6992     Jim_DecrRefCount(interp, exprObjPtr);
6993     if (!error) {
6994         *exprResultPtrPtr = stack[0];
6995         Jim_IncrRefCount(stack[0]);
6996         errRetCode = JIM_OK;
6997     }
6998     for (i = 0; i < stacklen; i++) {
6999         Jim_DecrRefCount(interp, stack[i]);
7000     }
7001     if (stack != staticStack)
7002         Jim_Free(stack);
7003     return errRetCode;
7004 divbyzero:
7005     error = 1;
7006     Jim_SetResultString(interp, "Division by zero", -1);
7007     goto err;
7008 }
7009
7010 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7011 {
7012     int retcode;
7013     jim_wide wideValue;
7014     double doubleValue;
7015     Jim_Obj *exprResultPtr;
7016
7017     retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7018     if (retcode != JIM_OK)
7019         return retcode;
7020     if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7021         if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7022         {
7023             Jim_DecrRefCount(interp, exprResultPtr);
7024             return JIM_ERR;
7025         } else {
7026             Jim_DecrRefCount(interp, exprResultPtr);
7027             *boolPtr = doubleValue != 0;
7028             return JIM_OK;
7029         }
7030     }
7031     Jim_DecrRefCount(interp, exprResultPtr);
7032     *boolPtr = wideValue != 0;
7033     return JIM_OK;
7034 }
7035
7036 /* -----------------------------------------------------------------------------
7037  * ScanFormat String Object
7038  * ---------------------------------------------------------------------------*/
7039
7040 /* This Jim_Obj will held a parsed representation of a format string passed to
7041  * the Jim_ScanString command. For error diagnostics, the scanformat string has
7042  * to be parsed in its entirely first and then, if correct, can be used for
7043  * scanning. To avoid endless re-parsing, the parsed representation will be
7044  * stored in an internal representation and re-used for performance reason. */
7045  
7046 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7047  * scanformat string. This part will later be used to extract information
7048  * out from the string to be parsed by Jim_ScanString */
7049  
7050 typedef struct ScanFmtPartDescr {
7051     char type;         /* Type of conversion (e.g. c, d, f) */
7052     char modifier;     /* Modify type (e.g. l - long, h - short */
7053     size_t  width;     /* Maximal width of input to be converted */
7054     int  pos;          /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */ 
7055     char *arg;         /* Specification of a CHARSET conversion */
7056     char *prefix;      /* Prefix to be scanned literally before conversion */
7057 } ScanFmtPartDescr;
7058
7059 /* The ScanFmtStringObj will held the internal representation of a scanformat
7060  * string parsed and separated in part descriptions. Furthermore it contains
7061  * the original string representation of the scanformat string to allow for
7062  * fast update of the Jim_Obj's string representation part.
7063  *
7064  * As add-on the internal object representation add some scratch pad area
7065  * for usage by Jim_ScanString to avoid endless allocating and freeing of
7066  * memory for purpose of string scanning.
7067  *
7068  * The error member points to a static allocated string in case of a mal-
7069  * formed scanformat string or it contains '0' (NULL) in case of a valid
7070  * parse representation.
7071  *
7072  * The whole memory of the internal representation is allocated as a single
7073  * area of memory that will be internally separated. So freeing and duplicating
7074  * of such an object is cheap */
7075
7076 typedef struct ScanFmtStringObj {
7077     jim_wide        size;         /* Size of internal repr in bytes */
7078     char            *stringRep;   /* Original string representation */
7079     size_t          count;        /* Number of ScanFmtPartDescr contained */
7080     size_t          convCount;    /* Number of conversions that will assign */
7081     size_t          maxPos;       /* Max position index if XPG3 is used */
7082     const char      *error;       /* Ptr to error text (NULL if no error */
7083     char            *scratch;     /* Some scratch pad used by Jim_ScanString */
7084     ScanFmtPartDescr descr[1];    /* The vector of partial descriptions */
7085 } ScanFmtStringObj;
7086
7087
7088 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7089 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7090 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7091
7092 static Jim_ObjType scanFmtStringObjType = {
7093     "scanformatstring",
7094     FreeScanFmtInternalRep,
7095     DupScanFmtInternalRep,
7096     UpdateStringOfScanFmt,
7097     JIM_TYPE_NONE,
7098 };
7099
7100 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7101 {
7102     JIM_NOTUSED(interp);
7103     Jim_Free((char*)objPtr->internalRep.ptr);
7104     objPtr->internalRep.ptr = 0;
7105 }
7106
7107 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7108 {
7109     size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7110     ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7111
7112     JIM_NOTUSED(interp);
7113     memcpy(newVec, srcPtr->internalRep.ptr, size);
7114     dupPtr->internalRep.ptr = newVec;
7115     dupPtr->typePtr = &scanFmtStringObjType;
7116 }
7117
7118 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7119 {
7120     char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7121
7122     objPtr->bytes = Jim_StrDup(bytes);
7123     objPtr->length = strlen(bytes);
7124 }
7125
7126 /* SetScanFmtFromAny will parse a given string and create the internal
7127  * representation of the format specification. In case of an error
7128  * the error data member of the internal representation will be set
7129  * to an descriptive error text and the function will be left with
7130  * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7131  * specification */
7132
7133 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7134 {
7135     ScanFmtStringObj *fmtObj;
7136     char *buffer;
7137     int maxCount, i, approxSize, lastPos = -1;
7138     const char *fmt = objPtr->bytes;
7139     int maxFmtLen = objPtr->length;
7140     const char *fmtEnd = fmt + maxFmtLen;
7141     int curr;
7142
7143     Jim_FreeIntRep(interp, objPtr);
7144     /* Count how many conversions could take place maximally */
7145     for (i=0, maxCount=0; i < maxFmtLen; ++i)
7146         if (fmt[i] == '%')
7147             ++maxCount;
7148     /* Calculate an approximation of the memory necessary */
7149     approxSize = sizeof(ScanFmtStringObj)           /* Size of the container */
7150         + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7151         + maxFmtLen * sizeof(char) + 3 + 1          /* Scratch + "%n" + '\0' */
7152         + maxFmtLen * sizeof(char) + 1              /* Original stringrep */
7153         + maxFmtLen * sizeof(char)                  /* Arg for CHARSETs */
7154         + (maxCount +1) * sizeof(char)              /* '\0' for every partial */
7155         + 1;                                        /* safety byte */
7156     fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7157     memset(fmtObj, 0, approxSize);
7158     fmtObj->size = approxSize;
7159     fmtObj->maxPos = 0;
7160     fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7161     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7162     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7163     buffer = fmtObj->stringRep + maxFmtLen + 1;
7164     objPtr->internalRep.ptr = fmtObj;
7165     objPtr->typePtr = &scanFmtStringObjType;
7166     for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7167         int width=0, skip;
7168         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7169         fmtObj->count++;
7170         descr->width = 0;                   /* Assume width unspecified */ 
7171         /* Overread and store any "literal" prefix */
7172         if (*fmt != '%' || fmt[1] == '%') {
7173             descr->type = 0;
7174             descr->prefix = &buffer[i];
7175             for (; fmt < fmtEnd; ++fmt) {
7176                 if (*fmt == '%') {
7177                     if (fmt[1] != '%') break;
7178                     ++fmt;
7179                 }
7180                 buffer[i++] = *fmt;
7181             }
7182             buffer[i++] = 0;
7183         } 
7184         /* Skip the conversion introducing '%' sign */
7185         ++fmt;      
7186         /* End reached due to non-conversion literal only? */
7187         if (fmt >= fmtEnd)
7188             goto done;
7189         descr->pos = 0;                     /* Assume "natural" positioning */
7190         if (*fmt == '*') {
7191             descr->pos = -1;       /* Okay, conversion will not be assigned */
7192             ++fmt;
7193         } else
7194             fmtObj->convCount++;    /* Otherwise count as assign-conversion */
7195         /* Check if next token is a number (could be width or pos */
7196         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7197             fmt += skip;
7198             /* Was the number a XPG3 position specifier? */
7199             if (descr->pos != -1 && *fmt == '$') {
7200                 int prev;
7201                 ++fmt;
7202                 descr->pos = width;
7203                 width = 0;
7204                 /* Look if "natural" postioning and XPG3 one was mixed */
7205                 if ((lastPos == 0 && descr->pos > 0)
7206                         || (lastPos > 0 && descr->pos == 0)) {
7207                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7208                     return JIM_ERR;
7209                 }
7210                 /* Look if this position was already used */
7211                 for (prev=0; prev < curr; ++prev) {
7212                     if (fmtObj->descr[prev].pos == -1) continue;
7213                     if (fmtObj->descr[prev].pos == descr->pos) {
7214                         fmtObj->error = "same \"%n$\" conversion specifier "
7215                             "used more than once";
7216                         return JIM_ERR;
7217                     }
7218                 }
7219                 /* Try to find a width after the XPG3 specifier */
7220                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7221                     descr->width = width;
7222                     fmt += skip;
7223                 }
7224                 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7225                     fmtObj->maxPos = descr->pos;
7226             } else {
7227                 /* Number was not a XPG3, so it has to be a width */
7228                 descr->width = width;
7229             }
7230         }
7231         /* If positioning mode was undetermined yet, fix this */
7232         if (lastPos == -1)
7233             lastPos = descr->pos;
7234         /* Handle CHARSET conversion type ... */
7235         if (*fmt == '[') {
7236             int swapped = 1, beg = i, end, j;
7237             descr->type = '[';
7238             descr->arg = &buffer[i];
7239             ++fmt;
7240             if (*fmt == '^') buffer[i++] = *fmt++;
7241             if (*fmt == ']') buffer[i++] = *fmt++;
7242             while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7243             if (*fmt != ']') {
7244                 fmtObj->error = "unmatched [ in format string";
7245                 return JIM_ERR;
7246             } 
7247             end = i;
7248             buffer[i++] = 0;
7249             /* In case a range fence was given "backwards", swap it */
7250             while (swapped) {
7251                 swapped = 0;
7252                 for (j=beg+1; j < end-1; ++j) {
7253                     if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7254                         char tmp = buffer[j-1];
7255                         buffer[j-1] = buffer[j+1];
7256                         buffer[j+1] = tmp;
7257                         swapped = 1;
7258                     }
7259                 }
7260             }
7261         } else {
7262             /* Remember any valid modifier if given */
7263             if (strchr("hlL", *fmt) != 0)
7264                 descr->modifier = tolower((int)*fmt++);
7265             
7266             descr->type = *fmt;
7267             if (strchr("efgcsndoxui", *fmt) == 0) {
7268                 fmtObj->error = "bad scan conversion character";
7269                 return JIM_ERR;
7270             } else if (*fmt == 'c' && descr->width != 0) {
7271                 fmtObj->error = "field width may not be specified in %c "
7272                     "conversion";
7273                 return JIM_ERR;
7274             } else if (*fmt == 'u' && descr->modifier == 'l') {
7275                 fmtObj->error = "unsigned wide not supported";
7276                 return JIM_ERR;
7277             }
7278         }
7279         curr++;
7280     }
7281 done:
7282     if (fmtObj->convCount == 0) {
7283         fmtObj->error = "no any conversion specifier given";
7284         return JIM_ERR;
7285     }
7286     return JIM_OK;
7287 }
7288
7289 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7290
7291 #define FormatGetCnvCount(_fo_) \
7292     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7293 #define FormatGetMaxPos(_fo_) \
7294     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7295 #define FormatGetError(_fo_) \
7296     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7297
7298 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7299  * charsets ([a-z123]) within scanning. Later on perhaps a base for a 
7300  * bitvector implementation in Jim? */ 
7301
7302 static int JimTestBit(const char *bitvec, char ch)
7303 {
7304     div_t pos = div(ch-1, 8);
7305     return bitvec[pos.quot] & (1 << pos.rem);
7306 }
7307
7308 static void JimSetBit(char *bitvec, char ch)
7309 {
7310     div_t pos = div(ch-1, 8);
7311     bitvec[pos.quot] |= (1 << pos.rem);
7312 }
7313
7314 #if 0 /* currently not used */
7315 static void JimClearBit(char *bitvec, char ch)
7316 {
7317     div_t pos = div(ch-1, 8);
7318     bitvec[pos.quot] &= ~(1 << pos.rem);
7319 }
7320 #endif
7321
7322 /* JimScanAString is used to scan an unspecified string that ends with
7323  * next WS, or a string that is specified via a charset. The charset
7324  * is currently implemented in a way to only allow for usage with
7325  * ASCII. Whenever we will switch to UNICODE, another idea has to
7326  * be born :-/
7327  *
7328  * FIXME: Works only with ASCII */
7329
7330 static Jim_Obj *
7331 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7332 {
7333     size_t i;
7334     Jim_Obj *result;
7335     char charset[256/8+1];  /* A Charset may contain max 256 chars */
7336     char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7337
7338     /* First init charset to nothing or all, depending if a specified
7339      * or an unspecified string has to be parsed */
7340     memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7341     if (sdescr) {
7342         /* There was a set description given, that means we are parsing
7343          * a specified string. So we have to build a corresponding 
7344          * charset reflecting the description */
7345         int notFlag = 0;
7346         /* Should the set be negated at the end? */
7347         if (*sdescr == '^') {
7348             notFlag = 1;
7349             ++sdescr;
7350         }
7351         /* Here '-' is meant literally and not to define a range */
7352         if (*sdescr == '-') {
7353             JimSetBit(charset, '-');
7354             ++sdescr;
7355         }
7356         while (*sdescr) {
7357             if (sdescr[1] == '-' && sdescr[2] != 0) {
7358                 /* Handle range definitions */
7359                 int i;
7360                 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7361                     JimSetBit(charset, (char)i);
7362                 sdescr += 3;
7363             } else {
7364                 /* Handle verbatim character definitions */
7365                 JimSetBit(charset, *sdescr++);
7366             }
7367         }
7368         /* Negate the charset if there was a NOT given */
7369         for (i=0; notFlag && i < sizeof(charset); ++i)
7370             charset[i] = ~charset[i];
7371     } 
7372     /* And after all the mess above, the real work begin ... */
7373     while (str && *str) {
7374         if (!sdescr && isspace((int)*str))
7375             break; /* EOS via WS if unspecified */
7376         if (JimTestBit(charset, *str)) *buffer++ = *str++;
7377         else break;             /* EOS via mismatch if specified scanning */
7378     }
7379     *buffer = 0;                /* Close the string properly ... */
7380     result = Jim_NewStringObj(interp, anchor, -1);
7381     Jim_Free(anchor);           /* ... and free it afer usage */
7382     return result;
7383 }
7384
7385 /* ScanOneEntry will scan one entry out of the string passed as argument.
7386  * It use the sscanf() function for this task. After extracting and
7387  * converting of the value, the count of scanned characters will be
7388  * returned of -1 in case of no conversion tool place and string was
7389  * already scanned thru */
7390
7391 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7392         ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7393 {
7394 #   define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7395         ? sizeof(jim_wide)                             \
7396         : sizeof(double))
7397     char buffer[MAX_SIZE];
7398     char *value = buffer;
7399     const char *tok;
7400     const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7401     size_t sLen = strlen(&str[pos]), scanned = 0;
7402     size_t anchor = pos;
7403     int i;
7404
7405     /* First pessimiticly assume, we will not scan anything :-) */
7406     *valObjPtr = 0;
7407     if (descr->prefix) {
7408         /* There was a prefix given before the conversion, skip it and adjust
7409          * the string-to-be-parsed accordingly */
7410         for (i=0; str[pos] && descr->prefix[i]; ++i) {
7411             /* If prefix require, skip WS */
7412             if (isspace((int)descr->prefix[i]))
7413                 while (str[pos] && isspace((int)str[pos])) ++pos;
7414             else if (descr->prefix[i] != str[pos]) 
7415                 break;  /* Prefix do not match here, leave the loop */
7416             else
7417                 ++pos;  /* Prefix matched so far, next round */
7418         }
7419         if (str[pos] == 0)
7420             return -1;  /* All of str consumed: EOF condition */
7421         else if (descr->prefix[i] != 0)
7422             return 0;   /* Not whole prefix consumed, no conversion possible */
7423     }
7424     /* For all but following conversion, skip leading WS */
7425     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7426         while (isspace((int)str[pos])) ++pos;
7427     /* Determine how much skipped/scanned so far */
7428     scanned = pos - anchor;
7429     if (descr->type == 'n') {
7430         /* Return pseudo conversion means: how much scanned so far? */
7431         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7432     } else if (str[pos] == 0) {
7433         /* Cannot scan anything, as str is totally consumed */
7434         return -1;
7435     } else {
7436         /* Processing of conversions follows ... */
7437         if (descr->width > 0) {
7438             /* Do not try to scan as fas as possible but only the given width.
7439              * To ensure this, we copy the part that should be scanned. */
7440             size_t tLen = descr->width > sLen ? sLen : descr->width;
7441             tok = Jim_StrDupLen(&str[pos], tLen);
7442         } else {
7443             /* As no width was given, simply refer to the original string */
7444             tok = &str[pos];
7445         }
7446         switch (descr->type) {
7447             case 'c':
7448                 *valObjPtr = Jim_NewIntObj(interp, *tok);
7449                 scanned += 1;
7450                 break;
7451             case 'd': case 'o': case 'x': case 'u': case 'i': {
7452                 char *endp;  /* Position where the number finished */
7453                 int base = descr->type == 'o' ? 8
7454                     : descr->type == 'x' ? 16
7455                     : descr->type == 'i' ? 0
7456                     : 10;
7457                     
7458                 do {
7459                     /* Try to scan a number with the given base */
7460                     if (descr->modifier == 'l')
7461 #ifdef HAVE_LONG_LONG
7462                       *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7463 #else
7464                       *(jim_wide*)value = strtol(tok, &endp, base);
7465 #endif
7466                     else
7467                       if (descr->type == 'u')
7468                         *(long*)value = strtoul(tok, &endp, base);
7469                       else
7470                         *(long*)value = strtol(tok, &endp, base);
7471                     /* If scanning failed, and base was undetermined, simply
7472                      * put it to 10 and try once more. This should catch the
7473                      * case where %i begin to parse a number prefix (e.g. 
7474                      * '0x' but no further digits follows. This will be
7475                      * handled as a ZERO followed by a char 'x' by Tcl */
7476                     if (endp == tok && base == 0) base = 10;
7477                     else break;
7478                 } while (1);
7479                 if (endp != tok) {
7480                     /* There was some number sucessfully scanned! */
7481                     if (descr->modifier == 'l')
7482                         *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7483                     else
7484                         *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7485                     /* Adjust the number-of-chars scanned so far */
7486                     scanned += endp - tok;
7487                 } else {
7488                     /* Nothing was scanned. We have to determine if this
7489                      * happened due to e.g. prefix mismatch or input str
7490                      * exhausted */
7491                     scanned = *tok ? 0 : -1;
7492                 }
7493                 break;
7494             }
7495             case 's': case '[': {
7496                 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7497                 scanned += Jim_Length(*valObjPtr);
7498                 break;
7499             }
7500             case 'e': case 'f': case 'g': {
7501                 char *endp;
7502
7503                 *(double*)value = strtod(tok, &endp);
7504                 if (endp != tok) {
7505                     /* There was some number sucessfully scanned! */
7506                     *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7507                     /* Adjust the number-of-chars scanned so far */
7508                     scanned += endp - tok;
7509                 } else {
7510                     /* Nothing was scanned. We have to determine if this
7511                      * happened due to e.g. prefix mismatch or input str
7512                      * exhausted */
7513                     scanned = *tok ? 0 : -1;
7514                 }
7515                 break;
7516             }
7517         }
7518         /* If a substring was allocated (due to pre-defined width) do not
7519          * forget to free it */
7520         if (tok != &str[pos])
7521             Jim_Free((char*)tok);
7522     }
7523     return scanned;
7524 }
7525
7526 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7527  * string and returns all converted (and not ignored) values in a list back
7528  * to the caller. If an error occured, a NULL pointer will be returned */
7529
7530 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7531         Jim_Obj *fmtObjPtr, int flags)
7532 {
7533     size_t i, pos;
7534     int scanned = 1;
7535     const char *str = Jim_GetString(strObjPtr, 0);
7536     Jim_Obj *resultList = 0;
7537     Jim_Obj **resultVec;
7538     int resultc;
7539     Jim_Obj *emptyStr = 0;
7540     ScanFmtStringObj *fmtObj;
7541
7542     /* If format specification is not an object, convert it! */
7543     if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7544         SetScanFmtFromAny(interp, fmtObjPtr);
7545     fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7546     /* Check if format specification was valid */
7547     if (fmtObj->error != 0) {
7548         if (flags & JIM_ERRMSG)
7549             Jim_SetResultString(interp, fmtObj->error, -1);
7550         return 0;
7551     }
7552     /* Allocate a new "shared" empty string for all unassigned conversions */
7553     emptyStr = Jim_NewEmptyStringObj(interp);
7554     Jim_IncrRefCount(emptyStr);
7555     /* Create a list and fill it with empty strings up to max specified XPG3 */
7556     resultList = Jim_NewListObj(interp, 0, 0);
7557     if (fmtObj->maxPos > 0) {
7558         for (i=0; i < fmtObj->maxPos; ++i)
7559             Jim_ListAppendElement(interp, resultList, emptyStr);
7560         JimListGetElements(interp, resultList, &resultc, &resultVec);
7561     }
7562     /* Now handle every partial format description */
7563     for (i=0, pos=0; i < fmtObj->count; ++i) {
7564         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7565         Jim_Obj *value = 0;
7566         /* Only last type may be "literal" w/o conversion - skip it! */
7567         if (descr->type == 0) continue;
7568         /* As long as any conversion could be done, we will proceed */
7569         if (scanned > 0)
7570             scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7571         /* In case our first try results in EOF, we will leave */
7572         if (scanned == -1 && i == 0)
7573             goto eof;
7574         /* Advance next pos-to-be-scanned for the amount scanned already */
7575         pos += scanned;
7576         /* value == 0 means no conversion took place so take empty string */
7577         if (value == 0)
7578             value = Jim_NewEmptyStringObj(interp);
7579         /* If value is a non-assignable one, skip it */
7580         if (descr->pos == -1) {
7581             Jim_FreeNewObj(interp, value);
7582         } else if (descr->pos == 0)
7583             /* Otherwise append it to the result list if no XPG3 was given */
7584             Jim_ListAppendElement(interp, resultList, value);
7585         else if (resultVec[descr->pos-1] == emptyStr) {
7586             /* But due to given XPG3, put the value into the corr. slot */
7587             Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7588             Jim_IncrRefCount(value);
7589             resultVec[descr->pos-1] = value;
7590         } else {
7591             /* Otherwise, the slot was already used - free obj and ERROR */
7592             Jim_FreeNewObj(interp, value);
7593             goto err;
7594         }
7595     }
7596     Jim_DecrRefCount(interp, emptyStr);
7597     return resultList;
7598 eof:
7599     Jim_DecrRefCount(interp, emptyStr);
7600     Jim_FreeNewObj(interp, resultList);
7601     return (Jim_Obj*)EOF;
7602 err:
7603     Jim_DecrRefCount(interp, emptyStr);
7604     Jim_FreeNewObj(interp, resultList);
7605     return 0;
7606 }
7607
7608 /* -----------------------------------------------------------------------------
7609  * Pseudo Random Number Generation
7610  * ---------------------------------------------------------------------------*/
7611 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7612         int seedLen);
7613
7614 /* Initialize the sbox with the numbers from 0 to 255 */
7615 static void JimPrngInit(Jim_Interp *interp)
7616 {
7617     int i;
7618     unsigned int seed[256];
7619
7620     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7621     for (i = 0; i < 256; i++)
7622         seed[i] = (rand() ^ time(NULL) ^ clock());
7623     JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7624 }
7625
7626 /* Generates N bytes of random data */
7627 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7628 {
7629     Jim_PrngState *prng;
7630     unsigned char *destByte = (unsigned char*) dest;
7631     unsigned int si, sj, x;
7632
7633     /* initialization, only needed the first time */
7634     if (interp->prngState == NULL)
7635         JimPrngInit(interp);
7636     prng = interp->prngState;
7637     /* generates 'len' bytes of pseudo-random numbers */
7638     for (x = 0; x < len; x++) {
7639         prng->i = (prng->i+1) & 0xff;
7640         si = prng->sbox[prng->i];
7641         prng->j = (prng->j + si) & 0xff;
7642         sj = prng->sbox[prng->j];
7643         prng->sbox[prng->i] = sj;
7644         prng->sbox[prng->j] = si;
7645         *destByte++ = prng->sbox[(si+sj)&0xff];
7646     }
7647 }
7648
7649 /* Re-seed the generator with user-provided bytes */
7650 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7651         int seedLen)
7652 {
7653     int i;
7654     unsigned char buf[256];
7655     Jim_PrngState *prng;
7656
7657     /* initialization, only needed the first time */
7658     if (interp->prngState == NULL)
7659         JimPrngInit(interp);
7660     prng = interp->prngState;
7661
7662     /* Set the sbox[i] with i */
7663     for (i = 0; i < 256; i++)
7664         prng->sbox[i] = i;
7665     /* Now use the seed to perform a random permutation of the sbox */
7666     for (i = 0; i < seedLen; i++) {
7667         unsigned char t;
7668
7669         t = prng->sbox[i&0xFF];
7670         prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7671         prng->sbox[seed[i]] = t;
7672     }
7673     prng->i = prng->j = 0;
7674     /* discard the first 256 bytes of stream. */
7675     JimRandomBytes(interp, buf, 256);
7676 }
7677
7678 /* -----------------------------------------------------------------------------
7679  * Dynamic libraries support (WIN32 not supported)
7680  * ---------------------------------------------------------------------------*/
7681
7682 #ifdef JIM_DYNLIB
7683 #ifdef WIN32
7684 #define RTLD_LAZY 0
7685 void * dlopen(const char *path, int mode) 
7686 {
7687     JIM_NOTUSED(mode);
7688
7689     return (void *)LoadLibraryA(path);
7690 }
7691 int dlclose(void *handle)
7692 {
7693     FreeLibrary((HANDLE)handle);
7694     return 0;
7695 }
7696 void *dlsym(void *handle, const char *symbol)
7697 {
7698     return GetProcAddress((HMODULE)handle, symbol);
7699 }
7700 static char win32_dlerror_string[121];
7701 const char *dlerror()
7702 {
7703     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7704                    LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7705     return win32_dlerror_string;
7706 }
7707 #endif /* WIN32 */
7708
7709 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7710 {
7711     Jim_Obj *libPathObjPtr;
7712     int prefixc, i;
7713     void *handle;
7714     int (*onload)(Jim_Interp *interp);
7715
7716     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7717     if (libPathObjPtr == NULL) {
7718         prefixc = 0;
7719         libPathObjPtr = NULL;
7720     } else {
7721         Jim_IncrRefCount(libPathObjPtr);
7722         Jim_ListLength(interp, libPathObjPtr, &prefixc);
7723     }
7724
7725     for (i = -1; i < prefixc; i++) {
7726         if (i < 0) {
7727             handle = dlopen(pathName, RTLD_LAZY);
7728         } else {
7729             FILE *fp;
7730             char buf[JIM_PATH_LEN];
7731             const char *prefix;
7732             int prefixlen;
7733             Jim_Obj *prefixObjPtr;
7734             
7735             buf[0] = '\0';
7736             if (Jim_ListIndex(interp, libPathObjPtr, i,
7737                     &prefixObjPtr, JIM_NONE) != JIM_OK)
7738                 continue;
7739             prefix = Jim_GetString(prefixObjPtr, NULL);
7740             prefixlen = strlen(prefix);
7741             if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7742                 continue;
7743             if (prefixlen && prefix[prefixlen-1] == '/')
7744                 sprintf(buf, "%s%s", prefix, pathName);
7745             else
7746                 sprintf(buf, "%s/%s", prefix, pathName);
7747             printf("opening '%s'\n", buf);
7748             fp = fopen(buf, "r");
7749             if (fp == NULL)
7750                 continue;
7751             fclose(fp);
7752             handle = dlopen(buf, RTLD_LAZY);
7753             printf("got handle %p\n", handle);
7754         }
7755         if (handle == NULL) {
7756             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7757             Jim_AppendStrings(interp, Jim_GetResult(interp),
7758                 "error loading extension \"", pathName,
7759                 "\": ", dlerror(), NULL);
7760             if (i < 0)
7761                 continue;
7762             goto err;
7763         }
7764         if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7765             Jim_SetResultString(interp,
7766                     "No Jim_OnLoad symbol found on extension", -1);
7767             goto err;
7768         }
7769         if (onload(interp) == JIM_ERR) {
7770             dlclose(handle);
7771             goto err;
7772         }
7773         Jim_SetEmptyResult(interp);
7774         if (libPathObjPtr != NULL)
7775             Jim_DecrRefCount(interp, libPathObjPtr);
7776         return JIM_OK;
7777     }
7778 err:
7779     if (libPathObjPtr != NULL)
7780         Jim_DecrRefCount(interp, libPathObjPtr);
7781     return JIM_ERR;
7782 }
7783 #else /* JIM_DYNLIB */
7784 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7785 {
7786     JIM_NOTUSED(interp);
7787     JIM_NOTUSED(pathName);
7788
7789     Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7790     return JIM_ERR;
7791 }
7792 #endif/* JIM_DYNLIB */
7793
7794 /* -----------------------------------------------------------------------------
7795  * Packages handling
7796  * ---------------------------------------------------------------------------*/
7797
7798 #define JIM_PKG_ANY_VERSION -1
7799
7800 /* Convert a string of the type "1.2" into an integer.
7801  * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted 
7802  * to the integer with value 102 */
7803 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7804         int *intPtr, int flags)
7805 {
7806     char *copy;
7807     jim_wide major, minor;
7808     char *majorStr, *minorStr, *p;
7809
7810     if (v[0] == '\0') {
7811         *intPtr = JIM_PKG_ANY_VERSION;
7812         return JIM_OK;
7813     }
7814
7815     copy = Jim_StrDup(v);
7816     p = strchr(copy, '.');
7817     if (p == NULL) goto badfmt;
7818     *p = '\0';
7819     majorStr = copy;
7820     minorStr = p+1;
7821
7822     if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7823         Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7824         goto badfmt;
7825     *intPtr = (int)(major*100+minor);
7826     Jim_Free(copy);
7827     return JIM_OK;
7828
7829 badfmt:
7830     Jim_Free(copy);
7831     if (flags & JIM_ERRMSG) {
7832         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7833         Jim_AppendStrings(interp, Jim_GetResult(interp),
7834                 "invalid package version '", v, "'", NULL);
7835     }
7836     return JIM_ERR;
7837 }
7838
7839 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7840 static int JimPackageMatchVersion(int needed, int actual, int flags)
7841 {
7842     if (needed == JIM_PKG_ANY_VERSION) return 1;
7843     if (flags & JIM_MATCHVER_EXACT) {
7844         return needed == actual;
7845     } else {
7846         return needed/100 == actual/100 && (needed <= actual);
7847     }
7848 }
7849
7850 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7851         int flags)
7852 {
7853     int intVersion;
7854     /* Check if the version format is ok */
7855     if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7856         return JIM_ERR;
7857     /* If the package was already provided returns an error. */
7858     if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7859         if (flags & JIM_ERRMSG) {
7860             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7861             Jim_AppendStrings(interp, Jim_GetResult(interp),
7862                     "package '", name, "' was already provided", NULL);
7863         }
7864         return JIM_ERR;
7865     }
7866     Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7867     return JIM_OK;
7868 }
7869
7870 #ifndef JIM_ANSIC
7871
7872 #ifndef WIN32
7873 # include <sys/types.h>
7874 # include <dirent.h>
7875 #else
7876 # include <io.h>
7877 /* Posix dirent.h compatiblity layer for WIN32.
7878  * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7879  * Copyright Salvatore Sanfilippo ,2005.
7880  *
7881  * Permission to use, copy, modify, and distribute this software and its
7882  * documentation for any purpose is hereby granted without fee, provided
7883  * that this copyright and permissions notice appear in all copies and
7884  * derivatives.
7885  *
7886  * This software is supplied "as is" without express or implied warranty.
7887  * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7888  */
7889
7890 struct dirent {
7891     char *d_name;
7892 };
7893
7894 typedef struct DIR {
7895     long                handle; /* -1 for failed rewind */
7896     struct _finddata_t  info;
7897     struct dirent       result; /* d_name null iff first time */
7898     char                *name;  /* null-terminated char string */
7899 } DIR;
7900
7901 DIR *opendir(const char *name)
7902 {
7903     DIR *dir = 0;
7904
7905     if(name && name[0]) {
7906         size_t base_length = strlen(name);
7907         const char *all = /* search pattern must end with suitable wildcard */
7908             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7909
7910         if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7911            (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7912         {
7913             strcat(strcpy(dir->name, name), all);
7914
7915             if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7916                 dir->result.d_name = 0;
7917             else { /* rollback */
7918                 Jim_Free(dir->name);
7919                 Jim_Free(dir);
7920                 dir = 0;
7921             }
7922         } else { /* rollback */
7923             Jim_Free(dir);
7924             dir   = 0;
7925             errno = ENOMEM;
7926         }
7927     } else {
7928         errno = EINVAL;
7929     }
7930     return dir;
7931 }
7932
7933 int closedir(DIR *dir)
7934 {
7935     int result = -1;
7936
7937     if(dir) {
7938         if(dir->handle != -1)
7939             result = _findclose(dir->handle);
7940         Jim_Free(dir->name);
7941         Jim_Free(dir);
7942     }
7943     if(result == -1) /* map all errors to EBADF */
7944         errno = EBADF;
7945     return result;
7946 }
7947
7948 struct dirent *readdir(DIR *dir)
7949 {
7950     struct dirent *result = 0;
7951
7952     if(dir && dir->handle != -1) {
7953         if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7954             result         = &dir->result;
7955             result->d_name = dir->info.name;
7956         }
7957     } else {
7958         errno = EBADF;
7959     }
7960     return result;
7961 }
7962
7963 #endif /* WIN32 */
7964
7965 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7966         int prefixc, const char *pkgName, int pkgVer, int flags)
7967 {
7968     int bestVer = -1, i;
7969     int pkgNameLen = strlen(pkgName);
7970     char *bestPackage = NULL;
7971     struct dirent *de;
7972
7973     for (i = 0; i < prefixc; i++) {
7974         DIR *dir;
7975         char buf[JIM_PATH_LEN];
7976         int prefixLen;
7977
7978         if (prefixes[i] == NULL) continue;
7979         strncpy(buf, prefixes[i], JIM_PATH_LEN);
7980         buf[JIM_PATH_LEN-1] = '\0';
7981         prefixLen = strlen(buf);
7982         if (prefixLen && buf[prefixLen-1] == '/')
7983             buf[prefixLen-1] = '\0';
7984
7985         if ((dir = opendir(buf)) == NULL) continue;
7986         while ((de = readdir(dir)) != NULL) {
7987             char *fileName = de->d_name;
7988             int fileNameLen = strlen(fileName);
7989
7990             if (strncmp(fileName, "jim-", 4) == 0 &&
7991                 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
7992                 *(fileName+4+pkgNameLen) == '-' &&
7993                 fileNameLen > 4 && /* note that this is not really useful */
7994                 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
7995                  strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
7996                  strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
7997             {
7998                 char ver[6]; /* xx.yy<nulterm> */
7999                 char *p = strrchr(fileName, '.');
8000                 int verLen, fileVer;
8001
8002                 verLen = p - (fileName+4+pkgNameLen+1);
8003                 if (verLen < 3 || verLen > 5) continue;
8004                 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8005                 ver[verLen] = '\0';
8006                 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8007                         != JIM_OK) continue;
8008                 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8009                     (bestVer == -1 || bestVer < fileVer))
8010                 {
8011                     bestVer = fileVer;
8012                     Jim_Free(bestPackage);
8013                     bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8014                     sprintf(bestPackage, "%s/%s", buf, fileName);
8015                 }
8016             }
8017         }
8018         closedir(dir);
8019     }
8020     return bestPackage;
8021 }
8022
8023 #else /* JIM_ANSIC */
8024
8025 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8026         int prefixc, const char *pkgName, int pkgVer, int flags)
8027 {
8028     JIM_NOTUSED(interp);
8029     JIM_NOTUSED(prefixes);
8030     JIM_NOTUSED(prefixc);
8031     JIM_NOTUSED(pkgName);
8032     JIM_NOTUSED(pkgVer);
8033     JIM_NOTUSED(flags);
8034     return NULL;
8035 }
8036
8037 #endif /* JIM_ANSIC */
8038
8039 /* Search for a suitable package under every dir specified by jim_libpath
8040  * and load it if possible. If a suitable package was loaded with success
8041  * JIM_OK is returned, otherwise JIM_ERR is returned. */
8042 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8043         int flags)
8044 {
8045     Jim_Obj *libPathObjPtr;
8046     char **prefixes, *best;
8047     int prefixc, i, retCode = JIM_OK;
8048
8049     libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8050     if (libPathObjPtr == NULL) {
8051         prefixc = 0;
8052         libPathObjPtr = NULL;
8053     } else {
8054         Jim_IncrRefCount(libPathObjPtr);
8055         Jim_ListLength(interp, libPathObjPtr, &prefixc);
8056     }
8057
8058     prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8059     for (i = 0; i < prefixc; i++) {
8060             Jim_Obj *prefixObjPtr;
8061             if (Jim_ListIndex(interp, libPathObjPtr, i,
8062                     &prefixObjPtr, JIM_NONE) != JIM_OK)
8063             {
8064                 prefixes[i] = NULL;
8065                 continue;
8066             }
8067             prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8068     }
8069     /* Scan every directory to find the "best" package. */
8070     best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8071     if (best != NULL) {
8072         char *p = strrchr(best, '.');
8073         /* Try to load/source it */
8074         if (p && strcmp(p, ".tcl") == 0) {
8075             retCode = Jim_EvalFile(interp, best);
8076         } else {
8077             retCode = Jim_LoadLibrary(interp, best);
8078         }
8079     } else {
8080         retCode = JIM_ERR;
8081     }
8082     Jim_Free(best);
8083     for (i = 0; i < prefixc; i++)
8084         Jim_Free(prefixes[i]);
8085     Jim_Free(prefixes);
8086     if (libPathObjPtr)
8087         Jim_DecrRefCount(interp, libPathObjPtr);
8088     return retCode;
8089 }
8090
8091 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8092         const char *ver, int flags)
8093 {
8094     Jim_HashEntry *he;
8095     int requiredVer;
8096
8097     if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8098         return NULL;
8099     he = Jim_FindHashEntry(&interp->packages, name);
8100     if (he == NULL) {
8101         /* Try to load the package. */
8102         if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8103             he = Jim_FindHashEntry(&interp->packages, name);
8104             if (he == NULL) {
8105                 return "?";
8106             }
8107             return he->val;
8108         }
8109         /* No way... return an error. */
8110         if (flags & JIM_ERRMSG) {
8111             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8112             Jim_AppendStrings(interp, Jim_GetResult(interp),
8113                     "Can't find package '", name, "'", NULL);
8114         }
8115         return NULL;
8116     } else {
8117         int actualVer;
8118         if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8119                 != JIM_OK)
8120         {
8121             return NULL;
8122         }
8123         /* Check if version matches. */
8124         if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8125             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8126             Jim_AppendStrings(interp, Jim_GetResult(interp),
8127                     "Package '", name, "' already loaded, but with version ",
8128                     he->val, NULL);
8129             return NULL;
8130         }
8131         return he->val;
8132     }
8133 }
8134
8135 /* -----------------------------------------------------------------------------
8136  * Eval
8137  * ---------------------------------------------------------------------------*/
8138 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8139 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8140
8141 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8142         Jim_Obj *const *argv);
8143
8144 /* Handle calls to the [unknown] command */
8145 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8146 {
8147     Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8148     int retCode;
8149
8150     /* If the [unknown] command does not exists returns
8151      * just now */
8152     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8153         return JIM_ERR;
8154
8155     /* The object interp->unknown just contains
8156      * the "unknown" string, it is used in order to
8157      * avoid to lookup the unknown command every time
8158      * but instread to cache the result. */
8159     if (argc+1 <= JIM_EVAL_SARGV_LEN)
8160         v = sv;
8161     else
8162         v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8163     /* Make a copy of the arguments vector, but shifted on
8164      * the right of one position. The command name of the
8165      * command will be instead the first argument of the
8166      * [unknonw] call. */
8167     memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8168     v[0] = interp->unknown;
8169     /* Call it */
8170     retCode = Jim_EvalObjVector(interp, argc+1, v);
8171     /* Clean up */
8172     if (v != sv)
8173         Jim_Free(v);
8174     return retCode;
8175 }
8176
8177 /* Eval the object vector 'objv' composed of 'objc' elements.
8178  * Every element is used as single argument.
8179  * Jim_EvalObj() will call this function every time its object
8180  * argument is of "list" type, with no string representation.
8181  *
8182  * This is possible because the string representation of a
8183  * list object generated by the UpdateStringOfList is made
8184  * in a way that ensures that every list element is a different
8185  * command argument. */
8186 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8187 {
8188     int i, retcode;
8189     Jim_Cmd *cmdPtr;
8190
8191     /* Incr refcount of arguments. */
8192     for (i = 0; i < objc; i++)
8193         Jim_IncrRefCount(objv[i]);
8194     /* Command lookup */
8195     cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8196     if (cmdPtr == NULL) {
8197         retcode = JimUnknown(interp, objc, objv);
8198     } else {
8199         /* Call it -- Make sure result is an empty object. */
8200         Jim_SetEmptyResult(interp);
8201         if (cmdPtr->cmdProc) {
8202             interp->cmdPrivData = cmdPtr->privData;
8203             retcode = cmdPtr->cmdProc(interp, objc, objv);
8204         } else {
8205             retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8206             if (retcode == JIM_ERR) {
8207                 JimAppendStackTrace(interp,
8208                     Jim_GetString(objv[0], NULL), "?", 1);
8209             }
8210         }
8211     }
8212     /* Decr refcount of arguments and return the retcode */
8213     for (i = 0; i < objc; i++)
8214         Jim_DecrRefCount(interp, objv[i]);
8215     return retcode;
8216 }
8217
8218 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8219  * via *objPtrPtr. This function is only called by Jim_EvalObj().
8220  * The returned object has refcount = 0. */
8221 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8222         int tokens, Jim_Obj **objPtrPtr)
8223 {
8224     int totlen = 0, i, retcode;
8225     Jim_Obj **intv;
8226     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8227     Jim_Obj *objPtr;
8228     char *s;
8229
8230     if (tokens <= JIM_EVAL_SINTV_LEN)
8231         intv = sintv;
8232     else
8233         intv = Jim_Alloc(sizeof(Jim_Obj*)*
8234                 tokens);
8235     /* Compute every token forming the argument
8236      * in the intv objects vector. */
8237     for (i = 0; i < tokens; i++) {
8238         switch(token[i].type) {
8239         case JIM_TT_ESC:
8240         case JIM_TT_STR:
8241             intv[i] = token[i].objPtr;
8242             break;
8243         case JIM_TT_VAR:
8244             intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8245             if (!intv[i]) {
8246                 retcode = JIM_ERR;
8247                 goto err;
8248             }
8249             break;
8250         case JIM_TT_DICTSUGAR:
8251             intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8252             if (!intv[i]) {
8253                 retcode = JIM_ERR;
8254                 goto err;
8255             }
8256             break;
8257         case JIM_TT_CMD:
8258             retcode = Jim_EvalObj(interp, token[i].objPtr);
8259             if (retcode != JIM_OK)
8260                 goto err;
8261             intv[i] = Jim_GetResult(interp);
8262             break;
8263         default:
8264             Jim_Panic(interp,
8265               "default token type reached "
8266               "in Jim_InterpolateTokens().");
8267             break;
8268         }
8269         Jim_IncrRefCount(intv[i]);
8270         /* Make sure there is a valid
8271          * string rep, and add the string
8272          * length to the total legnth. */
8273         Jim_GetString(intv[i], NULL);
8274         totlen += intv[i]->length;
8275     }
8276     /* Concatenate every token in an unique
8277      * object. */
8278     objPtr = Jim_NewStringObjNoAlloc(interp,
8279             NULL, 0);
8280     s = objPtr->bytes = Jim_Alloc(totlen+1);
8281     objPtr->length = totlen;
8282     for (i = 0; i < tokens; i++) {
8283         memcpy(s, intv[i]->bytes, intv[i]->length);
8284         s += intv[i]->length;
8285         Jim_DecrRefCount(interp, intv[i]);
8286     }
8287     objPtr->bytes[totlen] = '\0';
8288     /* Free the intv vector if not static. */
8289     if (tokens > JIM_EVAL_SINTV_LEN)
8290         Jim_Free(intv);
8291     *objPtrPtr = objPtr;
8292     return JIM_OK;
8293 err:
8294     i--;
8295     for (; i >= 0; i--)
8296         Jim_DecrRefCount(interp, intv[i]);
8297     if (tokens > JIM_EVAL_SINTV_LEN)
8298         Jim_Free(intv);
8299     return retcode;
8300 }
8301
8302 /* Helper of Jim_EvalObj() to perform argument expansion.
8303  * Basically this function append an argument to 'argv'
8304  * (and increments argc by reference accordingly), performing
8305  * expansion of the list object if 'expand' is non-zero, or
8306  * just adding objPtr to argv if 'expand' is zero. */
8307 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8308         int *argcPtr, int expand, Jim_Obj *objPtr)
8309 {
8310     if (!expand) {
8311         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8312         /* refcount of objPtr not incremented because
8313          * we are actually transfering a reference from
8314          * the old 'argv' to the expanded one. */
8315         (*argv)[*argcPtr] = objPtr;
8316         (*argcPtr)++;
8317     } else {
8318         int len, i;
8319
8320         Jim_ListLength(interp, objPtr, &len);
8321         (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8322         for (i = 0; i < len; i++) {
8323             (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8324             Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8325             (*argcPtr)++;
8326         }
8327         /* The original object reference is no longer needed,
8328          * after the expansion it is no longer present on
8329          * the argument vector, but the single elements are
8330          * in its place. */
8331         Jim_DecrRefCount(interp, objPtr);
8332     }
8333 }
8334
8335 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8336 {
8337     int i, j = 0, len;
8338     ScriptObj *script;
8339     ScriptToken *token;
8340     int *cs; /* command structure array */
8341     int retcode = JIM_OK;
8342     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8343
8344     interp->errorFlag = 0;
8345
8346     /* If the object is of type "list" and there is no
8347      * string representation for this object, we can call
8348      * a specialized version of Jim_EvalObj() */
8349     if (scriptObjPtr->typePtr == &listObjType &&
8350         scriptObjPtr->internalRep.listValue.len &&
8351         scriptObjPtr->bytes == NULL) {
8352         Jim_IncrRefCount(scriptObjPtr);
8353         retcode = Jim_EvalObjVector(interp,
8354                 scriptObjPtr->internalRep.listValue.len,
8355                 scriptObjPtr->internalRep.listValue.ele);
8356         Jim_DecrRefCount(interp, scriptObjPtr);
8357         return retcode;
8358     }
8359
8360     Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8361     script = Jim_GetScript(interp, scriptObjPtr);
8362     /* Now we have to make sure the internal repr will not be
8363      * freed on shimmering.
8364      *
8365      * Think for example to this:
8366      *
8367      * set x {llength $x; ... some more code ...}; eval $x
8368      *
8369      * In order to preserve the internal rep, we increment the
8370      * inUse field of the script internal rep structure. */
8371     script->inUse++;
8372
8373     token = script->token;
8374     len = script->len;
8375     cs = script->cmdStruct;
8376     i = 0; /* 'i' is the current token index. */
8377
8378     /* Reset the interpreter result. This is useful to
8379      * return the emtpy result in the case of empty program. */
8380     Jim_SetEmptyResult(interp);
8381
8382     /* Execute every command sequentially, returns on
8383      * error (i.e. if a command does not return JIM_OK) */
8384     while (i < len) {
8385         int expand = 0;
8386         int argc = *cs++; /* Get the number of arguments */
8387         Jim_Cmd *cmd;
8388
8389         /* Set the expand flag if needed. */
8390         if (argc == -1) {
8391             expand++;
8392             argc = *cs++;
8393         }
8394         /* Allocate the arguments vector */
8395         if (argc <= JIM_EVAL_SARGV_LEN)
8396             argv = sargv;
8397         else
8398             argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8399         /* Populate the arguments objects. */
8400         for (j = 0; j < argc; j++) {
8401             int tokens = *cs++;
8402
8403             /* tokens is negative if expansion is needed.
8404              * for this argument. */
8405             if (tokens < 0) {
8406                 tokens = (-tokens)-1;
8407                 i++;
8408             }
8409             if (tokens == 1) {
8410                 /* Fast path if the token does not
8411                  * need interpolation */
8412                 switch(token[i].type) {
8413                 case JIM_TT_ESC:
8414                 case JIM_TT_STR:
8415                     argv[j] = token[i].objPtr;
8416                     break;
8417                 case JIM_TT_VAR:
8418                     tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8419                             JIM_ERRMSG);
8420                     if (!tmpObjPtr) {
8421                         retcode = JIM_ERR;
8422                         goto err;
8423                     }
8424                     argv[j] = tmpObjPtr;
8425                     break;
8426                 case JIM_TT_DICTSUGAR:
8427                     tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8428                     if (!tmpObjPtr) {
8429                         retcode = JIM_ERR;
8430                         goto err;
8431                     }
8432                     argv[j] = tmpObjPtr;
8433                     break;
8434                 case JIM_TT_CMD:
8435                     retcode = Jim_EvalObj(interp, token[i].objPtr);
8436                     if (retcode != JIM_OK)
8437                         goto err;
8438                     argv[j] = Jim_GetResult(interp);
8439                     break;
8440                 default:
8441                     Jim_Panic(interp,
8442                       "default token type reached "
8443                       "in Jim_EvalObj().");
8444                     break;
8445                 }
8446                 Jim_IncrRefCount(argv[j]);
8447                 i += 2;
8448             } else {
8449                 /* For interpolation we call an helper
8450                  * function doing the work for us. */
8451                 if ((retcode = Jim_InterpolateTokens(interp,
8452                         token+i, tokens, &tmpObjPtr)) != JIM_OK)
8453                 {
8454                     goto err;
8455                 }
8456                 argv[j] = tmpObjPtr;
8457                 Jim_IncrRefCount(argv[j]);
8458                 i += tokens+1;
8459             }
8460         }
8461         /* Handle {expand} expansion */
8462         if (expand) {
8463             int *ecs = cs - argc;
8464             int eargc = 0;
8465             Jim_Obj **eargv = NULL;
8466
8467             for (j = 0; j < argc; j++) {
8468                 Jim_ExpandArgument( interp, &eargv, &eargc,
8469                         ecs[j] < 0, argv[j]);
8470             }
8471             if (argv != sargv)
8472                 Jim_Free(argv);
8473             argc = eargc;
8474             argv = eargv;
8475             j = argc;
8476             if (argc == 0) {
8477                 /* Nothing to do with zero args. */
8478                 Jim_Free(eargv);
8479                 continue;
8480             }
8481         }
8482         /* Lookup the command to call */
8483         cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8484         if (cmd != NULL) {
8485             /* Call it -- Make sure result is an empty object. */
8486             Jim_SetEmptyResult(interp);
8487             if (cmd->cmdProc) {
8488                 interp->cmdPrivData = cmd->privData;
8489                 retcode = cmd->cmdProc(interp, argc, argv);
8490             } else {
8491                 retcode = JimCallProcedure(interp, cmd, argc, argv);
8492                 if (retcode == JIM_ERR) {
8493                     JimAppendStackTrace(interp,
8494                         Jim_GetString(argv[0], NULL), script->fileName,
8495                         token[i-argc*2].linenr);
8496                 }
8497             }
8498         } else {
8499             /* Call [unknown] */
8500             retcode = JimUnknown(interp, argc, argv);
8501         }
8502         if (retcode != JIM_OK) {
8503             i -= argc*2; /* point to the command name. */
8504             goto err;
8505         }
8506         /* Decrement the arguments count */
8507         for (j = 0; j < argc; j++) {
8508             Jim_DecrRefCount(interp, argv[j]);
8509         }
8510
8511         if (argv != sargv) {
8512             Jim_Free(argv);
8513             argv = NULL;
8514         }
8515     }
8516     /* Note that we don't have to decrement inUse, because the
8517      * following code transfers our use of the reference again to
8518      * the script object. */
8519     j = 0; /* on normal termination, the argv array is already
8520           Jim_DecrRefCount-ed. */
8521 err:
8522     /* Handle errors. */
8523     if (retcode == JIM_ERR && !interp->errorFlag) {
8524         interp->errorFlag = 1;
8525         JimSetErrorFileName(interp, script->fileName);
8526         JimSetErrorLineNumber(interp, token[i].linenr);
8527         JimResetStackTrace(interp);
8528     }
8529     Jim_FreeIntRep(interp, scriptObjPtr);
8530     scriptObjPtr->typePtr = &scriptObjType;
8531     Jim_SetIntRepPtr(scriptObjPtr, script);
8532     Jim_DecrRefCount(interp, scriptObjPtr);
8533     for (i = 0; i < j; i++) {
8534         Jim_DecrRefCount(interp, argv[i]);
8535     }
8536     if (argv != sargv)
8537         Jim_Free(argv);
8538     return retcode;
8539 }
8540
8541 /* Call a procedure implemented in Tcl.
8542  * It's possible to speed-up a lot this function, currently
8543  * the callframes are not cached, but allocated and
8544  * destroied every time. What is expecially costly is
8545  * to create/destroy the local vars hash table every time.
8546  *
8547  * This can be fixed just implementing callframes caching
8548  * in JimCreateCallFrame() and JimFreeCallFrame(). */
8549 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8550         Jim_Obj *const *argv)
8551 {
8552     int i, retcode;
8553     Jim_CallFrame *callFramePtr;
8554
8555     /* Check arity */
8556     if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8557         argc > cmd->arityMax)) {
8558         Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8559         Jim_AppendStrings(interp, objPtr,
8560             "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8561             (cmd->arityMin > 1) ? " " : "",
8562             Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8563         Jim_SetResult(interp, objPtr);
8564         return JIM_ERR;
8565     }
8566     /* Check if there are too nested calls */
8567     if (interp->numLevels == interp->maxNestingDepth) {
8568         Jim_SetResultString(interp,
8569             "Too many nested calls. Infinite recursion?", -1);
8570         return JIM_ERR;
8571     }
8572     /* Create a new callframe */
8573     callFramePtr = JimCreateCallFrame(interp);
8574     callFramePtr->parentCallFrame = interp->framePtr;
8575     callFramePtr->argv = argv;
8576     callFramePtr->argc = argc;
8577     callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8578     callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8579     callFramePtr->staticVars = cmd->staticVars;
8580     Jim_IncrRefCount(cmd->argListObjPtr);
8581     Jim_IncrRefCount(cmd->bodyObjPtr);
8582     interp->framePtr = callFramePtr;
8583     interp->numLevels ++;
8584     /* Set arguments */
8585     for (i = 0; i < cmd->arityMin-1; i++) {
8586         Jim_Obj *objPtr;
8587
8588         Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8589         Jim_SetVariable(interp, objPtr, argv[i+1]);
8590     }
8591     if (cmd->arityMax == -1) {
8592         Jim_Obj *listObjPtr, *objPtr;
8593
8594         listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8595                 argc-cmd->arityMin);
8596         Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8597         Jim_SetVariable(interp, objPtr, listObjPtr);
8598     }
8599     /* Eval the body */
8600     retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8601
8602     /* Destroy the callframe */
8603     interp->numLevels --;
8604     interp->framePtr = interp->framePtr->parentCallFrame;
8605     if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8606         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8607     } else {
8608         JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8609     }
8610     /* Handle the JIM_EVAL return code */
8611     if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8612         int savedLevel = interp->evalRetcodeLevel;
8613
8614         interp->evalRetcodeLevel = interp->numLevels;
8615         while (retcode == JIM_EVAL) {
8616             Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8617             Jim_IncrRefCount(resultScriptObjPtr);
8618             retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8619             Jim_DecrRefCount(interp, resultScriptObjPtr);
8620         }
8621         interp->evalRetcodeLevel = savedLevel;
8622     }
8623     /* Handle the JIM_RETURN return code */
8624     if (retcode == JIM_RETURN) {
8625         retcode = interp->returnCode;
8626         interp->returnCode = JIM_OK;
8627     }
8628     return retcode;
8629 }
8630
8631 int Jim_Eval(Jim_Interp *interp, const char *script)
8632 {
8633     Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8634     int retval;
8635
8636     Jim_IncrRefCount(scriptObjPtr);
8637     retval = Jim_EvalObj(interp, scriptObjPtr);
8638     Jim_DecrRefCount(interp, scriptObjPtr);
8639     return retval;
8640 }
8641
8642 /* Execute script in the scope of the global level */
8643 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8644 {
8645     Jim_CallFrame *savedFramePtr;
8646     int retval;
8647
8648     savedFramePtr = interp->framePtr;
8649     interp->framePtr = interp->topFramePtr;
8650     retval = Jim_Eval(interp, script);
8651     interp->framePtr = savedFramePtr;
8652     return retval;
8653 }
8654
8655 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8656 {
8657     Jim_CallFrame *savedFramePtr;
8658     int retval;
8659
8660     savedFramePtr = interp->framePtr;
8661     interp->framePtr = interp->topFramePtr;
8662     retval = Jim_EvalObj(interp, scriptObjPtr);
8663     interp->framePtr = savedFramePtr;
8664     /* Try to report the error (if any) via the bgerror proc */
8665     if (retval != JIM_OK) {
8666         Jim_Obj *objv[2];
8667
8668         objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8669         objv[1] = Jim_GetResult(interp);
8670         Jim_IncrRefCount(objv[0]);
8671         Jim_IncrRefCount(objv[1]);
8672         if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8673             /* Report the error to stderr. */
8674             Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8675             Jim_PrintErrorMessage(interp);
8676         }
8677         Jim_DecrRefCount(interp, objv[0]);
8678         Jim_DecrRefCount(interp, objv[1]);
8679     }
8680     return retval;
8681 }
8682
8683 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8684 {
8685     char *prg = NULL;
8686     FILE *fp;
8687     int nread, totread, maxlen, buflen;
8688     int retval;
8689     Jim_Obj *scriptObjPtr;
8690     
8691     if ((fp = fopen(filename, "r")) == NULL) {
8692         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8693         Jim_AppendStrings(interp, Jim_GetResult(interp),
8694             "Error loading script \"", filename, "\": ",
8695             strerror(errno), NULL);
8696         return JIM_ERR;
8697     }
8698     buflen = 1024;
8699     maxlen = totread = 0;
8700     while (1) {
8701         if (maxlen < totread+buflen+1) {
8702             maxlen = totread+buflen+1;
8703             prg = Jim_Realloc(prg, maxlen);
8704         }
8705                 /* do not use Jim_fread() - this is really a file */
8706         if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8707         totread += nread;
8708     }
8709     prg[totread] = '\0';
8710         /* do not use Jim_fclose() - this is really a file */
8711     fclose(fp);
8712
8713     scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8714     JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8715     Jim_IncrRefCount(scriptObjPtr);
8716     retval = Jim_EvalObj(interp, scriptObjPtr);
8717     Jim_DecrRefCount(interp, scriptObjPtr);
8718     return retval;
8719 }
8720
8721 /* -----------------------------------------------------------------------------
8722  * Subst
8723  * ---------------------------------------------------------------------------*/
8724 static int JimParseSubstStr(struct JimParserCtx *pc)
8725 {
8726     pc->tstart = pc->p;
8727     pc->tline = pc->linenr;
8728     while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8729         pc->p++; pc->len--;
8730     }
8731     pc->tend = pc->p-1;
8732     pc->tt = JIM_TT_ESC;
8733     return JIM_OK;
8734 }
8735
8736 static int JimParseSubst(struct JimParserCtx *pc, int flags)
8737 {
8738     int retval;
8739
8740     if (pc->len == 0) {
8741         pc->tstart = pc->tend = pc->p;
8742         pc->tline = pc->linenr;
8743         pc->tt = JIM_TT_EOL;
8744         pc->eof = 1;
8745         return JIM_OK;
8746     }
8747     switch(*pc->p) {
8748     case '[':
8749         retval = JimParseCmd(pc);
8750         if (flags & JIM_SUBST_NOCMD) {
8751             pc->tstart--;
8752             pc->tend++;
8753             pc->tt = (flags & JIM_SUBST_NOESC) ?
8754                 JIM_TT_STR : JIM_TT_ESC;
8755         }
8756         return retval;
8757         break;
8758     case '$':
8759         if (JimParseVar(pc) == JIM_ERR) {
8760             pc->tstart = pc->tend = pc->p++; pc->len--;
8761             pc->tline = pc->linenr;
8762             pc->tt = JIM_TT_STR;
8763         } else {
8764             if (flags & JIM_SUBST_NOVAR) {
8765                 pc->tstart--;
8766                 if (flags & JIM_SUBST_NOESC)
8767                     pc->tt = JIM_TT_STR;
8768                 else
8769                     pc->tt = JIM_TT_ESC;
8770                 if (*pc->tstart == '{') {
8771                     pc->tstart--;
8772                     if (*(pc->tend+1))
8773                         pc->tend++;
8774                 }
8775             }
8776         }
8777         break;
8778     default:
8779         retval = JimParseSubstStr(pc);
8780         if (flags & JIM_SUBST_NOESC)
8781             pc->tt = JIM_TT_STR;
8782         return retval;
8783         break;
8784     }
8785     return JIM_OK;
8786 }
8787
8788 /* The subst object type reuses most of the data structures and functions
8789  * of the script object. Script's data structures are a bit more complex
8790  * for what is needed for [subst]itution tasks, but the reuse helps to
8791  * deal with a single data structure at the cost of some more memory
8792  * usage for substitutions. */
8793 static Jim_ObjType substObjType = {
8794     "subst",
8795     FreeScriptInternalRep,
8796     DupScriptInternalRep,
8797     NULL,
8798     JIM_TYPE_REFERENCES,
8799 };
8800
8801 /* This method takes the string representation of an object
8802  * as a Tcl string where to perform [subst]itution, and generates
8803  * the pre-parsed internal representation. */
8804 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8805 {
8806     int scriptTextLen;
8807     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8808     struct JimParserCtx parser;
8809     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8810
8811     script->len = 0;
8812     script->csLen = 0;
8813     script->commands = 0;
8814     script->token = NULL;
8815     script->cmdStruct = NULL;
8816     script->inUse = 1;
8817     script->substFlags = flags;
8818     script->fileName = NULL;
8819
8820     JimParserInit(&parser, scriptText, scriptTextLen, 1);
8821     while(1) {
8822         char *token;
8823         int len, type, linenr;
8824
8825         JimParseSubst(&parser, flags);
8826         if (JimParserEof(&parser)) break;
8827         token = JimParserGetToken(&parser, &len, &type, &linenr);
8828         ScriptObjAddToken(interp, script, token, len, type,
8829                 NULL, linenr);
8830     }
8831     /* Free the old internal rep and set the new one. */
8832     Jim_FreeIntRep(interp, objPtr);
8833     Jim_SetIntRepPtr(objPtr, script);
8834     objPtr->typePtr = &scriptObjType;
8835     return JIM_OK;
8836 }
8837
8838 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8839 {
8840     struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8841
8842     if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8843         SetSubstFromAny(interp, objPtr, flags);
8844     return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8845 }
8846
8847 /* Performs commands,variables,blackslashes substitution,
8848  * storing the result object (with refcount 0) into
8849  * resObjPtrPtr. */
8850 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8851         Jim_Obj **resObjPtrPtr, int flags)
8852 {
8853     ScriptObj *script;
8854     ScriptToken *token;
8855     int i, len, retcode = JIM_OK;
8856     Jim_Obj *resObjPtr, *savedResultObjPtr;
8857
8858     script = Jim_GetSubst(interp, substObjPtr, flags);
8859 #ifdef JIM_OPTIMIZATION
8860     /* Fast path for a very common case with array-alike syntax,
8861      * that's: $foo($bar) */
8862     if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8863         Jim_Obj *varObjPtr = script->token[0].objPtr;
8864         
8865         Jim_IncrRefCount(varObjPtr);
8866         resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8867         if (resObjPtr == NULL) {
8868             Jim_DecrRefCount(interp, varObjPtr);
8869             return JIM_ERR;
8870         }
8871         Jim_DecrRefCount(interp, varObjPtr);
8872         *resObjPtrPtr = resObjPtr;
8873         return JIM_OK;
8874     }
8875 #endif
8876
8877     Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8878     /* In order to preserve the internal rep, we increment the
8879      * inUse field of the script internal rep structure. */
8880     script->inUse++;
8881
8882     token = script->token;
8883     len = script->len;
8884
8885     /* Save the interp old result, to set it again before
8886      * to return. */
8887     savedResultObjPtr = interp->result;
8888     Jim_IncrRefCount(savedResultObjPtr);
8889     
8890     /* Perform the substitution. Starts with an empty object
8891      * and adds every token (performing the appropriate
8892      * var/command/escape substitution). */
8893     resObjPtr = Jim_NewStringObj(interp, "", 0);
8894     for (i = 0; i < len; i++) {
8895         Jim_Obj *objPtr;
8896
8897         switch(token[i].type) {
8898         case JIM_TT_STR:
8899         case JIM_TT_ESC:
8900             Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8901             break;
8902         case JIM_TT_VAR:
8903             objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8904             if (objPtr == NULL) goto err;
8905             Jim_IncrRefCount(objPtr);
8906             Jim_AppendObj(interp, resObjPtr, objPtr);
8907             Jim_DecrRefCount(interp, objPtr);
8908             break;
8909         case JIM_TT_CMD:
8910             if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8911                 goto err;
8912             Jim_AppendObj(interp, resObjPtr, interp->result);
8913             break;
8914         default:
8915             Jim_Panic(interp,
8916               "default token type (%d) reached "
8917               "in Jim_SubstObj().", token[i].type);
8918             break;
8919         }
8920     }
8921 ok:
8922     if (retcode == JIM_OK)
8923         Jim_SetResult(interp, savedResultObjPtr);
8924     Jim_DecrRefCount(interp, savedResultObjPtr);
8925     /* Note that we don't have to decrement inUse, because the
8926      * following code transfers our use of the reference again to
8927      * the script object. */
8928     Jim_FreeIntRep(interp, substObjPtr);
8929     substObjPtr->typePtr = &scriptObjType;
8930     Jim_SetIntRepPtr(substObjPtr, script);
8931     Jim_DecrRefCount(interp, substObjPtr);
8932     *resObjPtrPtr = resObjPtr;
8933     return retcode;
8934 err:
8935     Jim_FreeNewObj(interp, resObjPtr);
8936     retcode = JIM_ERR;
8937     goto ok;
8938 }
8939
8940 /* -----------------------------------------------------------------------------
8941  * API Input/Export functions
8942  * ---------------------------------------------------------------------------*/
8943
8944 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8945 {
8946     Jim_HashEntry *he;
8947
8948     he = Jim_FindHashEntry(&interp->stub, funcname);
8949     if (!he)
8950         return JIM_ERR;
8951     memcpy(targetPtrPtr, &he->val, sizeof(void*));
8952     return JIM_OK;
8953 }
8954
8955 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
8956 {
8957     return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
8958 }
8959
8960 #define JIM_REGISTER_API(name) \
8961     Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
8962
8963 void JimRegisterCoreApi(Jim_Interp *interp)
8964 {
8965   interp->getApiFuncPtr = Jim_GetApi;
8966   JIM_REGISTER_API(Alloc);
8967   JIM_REGISTER_API(Free);
8968   JIM_REGISTER_API(Eval);
8969   JIM_REGISTER_API(EvalGlobal);
8970   JIM_REGISTER_API(EvalFile);
8971   JIM_REGISTER_API(EvalObj);
8972   JIM_REGISTER_API(EvalObjBackground);
8973   JIM_REGISTER_API(EvalObjVector);
8974   JIM_REGISTER_API(InitHashTable);
8975   JIM_REGISTER_API(ExpandHashTable);
8976   JIM_REGISTER_API(AddHashEntry);
8977   JIM_REGISTER_API(ReplaceHashEntry);
8978   JIM_REGISTER_API(DeleteHashEntry);
8979   JIM_REGISTER_API(FreeHashTable);
8980   JIM_REGISTER_API(FindHashEntry);
8981   JIM_REGISTER_API(ResizeHashTable);
8982   JIM_REGISTER_API(GetHashTableIterator);
8983   JIM_REGISTER_API(NextHashEntry);
8984   JIM_REGISTER_API(NewObj);
8985   JIM_REGISTER_API(FreeObj);
8986   JIM_REGISTER_API(InvalidateStringRep);
8987   JIM_REGISTER_API(InitStringRep);
8988   JIM_REGISTER_API(DuplicateObj);
8989   JIM_REGISTER_API(GetString);
8990   JIM_REGISTER_API(Length);
8991   JIM_REGISTER_API(InvalidateStringRep);
8992   JIM_REGISTER_API(NewStringObj);
8993   JIM_REGISTER_API(NewStringObjNoAlloc);
8994   JIM_REGISTER_API(AppendString);
8995   JIM_REGISTER_API(AppendObj);
8996   JIM_REGISTER_API(AppendStrings);
8997   JIM_REGISTER_API(StringEqObj);
8998   JIM_REGISTER_API(StringMatchObj);
8999   JIM_REGISTER_API(StringRangeObj);
9000   JIM_REGISTER_API(FormatString);
9001   JIM_REGISTER_API(CompareStringImmediate);
9002   JIM_REGISTER_API(NewReference);
9003   JIM_REGISTER_API(GetReference);
9004   JIM_REGISTER_API(SetFinalizer);
9005   JIM_REGISTER_API(GetFinalizer);
9006   JIM_REGISTER_API(CreateInterp);
9007   JIM_REGISTER_API(FreeInterp);
9008   JIM_REGISTER_API(GetExitCode);
9009   JIM_REGISTER_API(SetStdin);
9010   JIM_REGISTER_API(SetStdout);
9011   JIM_REGISTER_API(SetStderr);
9012   JIM_REGISTER_API(CreateCommand);
9013   JIM_REGISTER_API(CreateProcedure);
9014   JIM_REGISTER_API(DeleteCommand);
9015   JIM_REGISTER_API(RenameCommand);
9016   JIM_REGISTER_API(GetCommand);
9017   JIM_REGISTER_API(SetVariable);
9018   JIM_REGISTER_API(SetVariableStr);
9019   JIM_REGISTER_API(SetGlobalVariableStr);
9020   JIM_REGISTER_API(SetVariableStrWithStr);
9021   JIM_REGISTER_API(SetVariableLink);
9022   JIM_REGISTER_API(GetVariable);
9023   JIM_REGISTER_API(GetCallFrameByLevel);
9024   JIM_REGISTER_API(Collect);
9025   JIM_REGISTER_API(CollectIfNeeded);
9026   JIM_REGISTER_API(GetIndex);
9027   JIM_REGISTER_API(NewListObj);
9028   JIM_REGISTER_API(ListAppendElement);
9029   JIM_REGISTER_API(ListAppendList);
9030   JIM_REGISTER_API(ListLength);
9031   JIM_REGISTER_API(ListIndex);
9032   JIM_REGISTER_API(SetListIndex);
9033   JIM_REGISTER_API(ConcatObj);
9034   JIM_REGISTER_API(NewDictObj);
9035   JIM_REGISTER_API(DictKey);
9036   JIM_REGISTER_API(DictKeysVector);
9037   JIM_REGISTER_API(GetIndex);
9038   JIM_REGISTER_API(GetReturnCode);
9039   JIM_REGISTER_API(EvalExpression);
9040   JIM_REGISTER_API(GetBoolFromExpr);
9041   JIM_REGISTER_API(GetWide);
9042   JIM_REGISTER_API(GetLong);
9043   JIM_REGISTER_API(SetWide);
9044   JIM_REGISTER_API(NewIntObj);
9045   JIM_REGISTER_API(GetDouble);
9046   JIM_REGISTER_API(SetDouble);
9047   JIM_REGISTER_API(NewDoubleObj);
9048   JIM_REGISTER_API(WrongNumArgs);
9049   JIM_REGISTER_API(SetDictKeysVector);
9050   JIM_REGISTER_API(SubstObj);
9051   JIM_REGISTER_API(RegisterApi);
9052   JIM_REGISTER_API(PrintErrorMessage);
9053   JIM_REGISTER_API(InteractivePrompt);
9054   JIM_REGISTER_API(RegisterCoreCommands);
9055   JIM_REGISTER_API(GetSharedString);
9056   JIM_REGISTER_API(ReleaseSharedString);
9057   JIM_REGISTER_API(Panic);
9058   JIM_REGISTER_API(StrDup);
9059   JIM_REGISTER_API(UnsetVariable);
9060   JIM_REGISTER_API(GetVariableStr);
9061   JIM_REGISTER_API(GetGlobalVariable);
9062   JIM_REGISTER_API(GetGlobalVariableStr);
9063   JIM_REGISTER_API(GetAssocData);
9064   JIM_REGISTER_API(SetAssocData);
9065   JIM_REGISTER_API(DeleteAssocData);
9066   JIM_REGISTER_API(GetEnum);
9067   JIM_REGISTER_API(ScriptIsComplete);
9068   JIM_REGISTER_API(PackageRequire);
9069   JIM_REGISTER_API(PackageProvide);
9070   JIM_REGISTER_API(InitStack);
9071   JIM_REGISTER_API(FreeStack);
9072   JIM_REGISTER_API(StackLen);
9073   JIM_REGISTER_API(StackPush);
9074   JIM_REGISTER_API(StackPop);
9075   JIM_REGISTER_API(StackPeek);
9076   JIM_REGISTER_API(FreeStackElements);
9077 }
9078
9079 /* -----------------------------------------------------------------------------
9080  * Core commands utility functions
9081  * ---------------------------------------------------------------------------*/
9082 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, 
9083         const char *msg)
9084 {
9085     int i;
9086     Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9087
9088     Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9089     for (i = 0; i < argc; i++) {
9090         Jim_AppendObj(interp, objPtr, argv[i]);
9091         if (!(i+1 == argc && msg[0] == '\0'))
9092             Jim_AppendString(interp, objPtr, " ", 1);
9093     }
9094     Jim_AppendString(interp, objPtr, msg, -1);
9095     Jim_AppendString(interp, objPtr, "\"", 1);
9096     Jim_SetResult(interp, objPtr);
9097 }
9098
9099 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9100 {
9101     Jim_HashTableIterator *htiter;
9102     Jim_HashEntry *he;
9103     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9104     const char *pattern;
9105     int patternLen;
9106     
9107     pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9108     htiter = Jim_GetHashTableIterator(&interp->commands);
9109     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9110         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
9111                     strlen((const char*)he->key), 0))
9112             continue;
9113         Jim_ListAppendElement(interp, listObjPtr,
9114                 Jim_NewStringObj(interp, he->key, -1));
9115     }
9116     Jim_FreeHashTableIterator(htiter);
9117     return listObjPtr;
9118 }
9119
9120 #define JIM_VARLIST_GLOBALS 0
9121 #define JIM_VARLIST_LOCALS 1
9122 #define JIM_VARLIST_VARS 2
9123
9124 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9125         int mode)
9126 {
9127     Jim_HashTableIterator *htiter;
9128     Jim_HashEntry *he;
9129     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9130     const char *pattern;
9131     int patternLen;
9132     
9133     pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9134     if (mode == JIM_VARLIST_GLOBALS) {
9135         htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9136     } else {
9137         /* For [info locals], if we are at top level an emtpy list
9138          * is returned. I don't agree, but we aim at compatibility (SS) */
9139         if (mode == JIM_VARLIST_LOCALS &&
9140             interp->framePtr == interp->topFramePtr)
9141             return listObjPtr;
9142         htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9143     }
9144     while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9145         Jim_Var *varPtr = (Jim_Var*) he->val;
9146         if (mode == JIM_VARLIST_LOCALS) {
9147             if (varPtr->linkFramePtr != NULL)
9148                 continue;
9149         }
9150         if (pattern && !JimStringMatch(pattern, patternLen, he->key, 
9151                     strlen((const char*)he->key), 0))
9152             continue;
9153         Jim_ListAppendElement(interp, listObjPtr,
9154                 Jim_NewStringObj(interp, he->key, -1));
9155     }
9156     Jim_FreeHashTableIterator(htiter);
9157     return listObjPtr;
9158 }
9159
9160 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9161         Jim_Obj **objPtrPtr)
9162 {
9163     Jim_CallFrame *targetCallFrame;
9164
9165     if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9166             != JIM_OK)
9167         return JIM_ERR;
9168     /* No proc call at toplevel callframe */
9169     if (targetCallFrame == interp->topFramePtr) {
9170         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9171         Jim_AppendStrings(interp, Jim_GetResult(interp),
9172                 "bad level \"",
9173                 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9174         return JIM_ERR;
9175     }
9176     *objPtrPtr = Jim_NewListObj(interp,
9177             targetCallFrame->argv,
9178             targetCallFrame->argc);
9179     return JIM_OK;
9180 }
9181
9182 /* -----------------------------------------------------------------------------
9183  * Core commands
9184  * ---------------------------------------------------------------------------*/
9185
9186 /* fake [puts] -- not the real puts, just for debugging. */
9187 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9188         Jim_Obj *const *argv)
9189 {
9190     const char *str;
9191     int len, nonewline = 0;
9192     
9193     if (argc != 2 && argc != 3) {
9194         Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9195         return JIM_ERR;
9196     }
9197     if (argc == 3) {
9198         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9199         {
9200             Jim_SetResultString(interp, "The second argument must "
9201                     "be -nonewline", -1);
9202             return JIM_OK;
9203         } else {
9204             nonewline = 1;
9205             argv++;
9206         }
9207     }
9208     str = Jim_GetString(argv[1], &len);
9209     Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9210     if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9211     return JIM_OK;
9212 }
9213
9214 /* Helper for [+] and [*] */
9215 static int Jim_AddMulHelper(Jim_Interp *interp, int argc, 
9216         Jim_Obj *const *argv, int op)
9217 {
9218     jim_wide wideValue, res;
9219     double doubleValue, doubleRes;
9220     int i;
9221
9222     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9223     
9224     for (i = 1; i < argc; i++) {
9225         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9226             goto trydouble;
9227         if (op == JIM_EXPROP_ADD)
9228             res += wideValue;
9229         else
9230             res *= wideValue;
9231     }
9232     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9233     return JIM_OK;
9234 trydouble:
9235     doubleRes = (double) res;
9236     for (;i < argc; i++) {
9237         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9238             return JIM_ERR;
9239         if (op == JIM_EXPROP_ADD)
9240             doubleRes += doubleValue;
9241         else
9242             doubleRes *= doubleValue;
9243     }
9244     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9245     return JIM_OK;
9246 }
9247
9248 /* Helper for [-] and [/] */
9249 static int Jim_SubDivHelper(Jim_Interp *interp, int argc, 
9250         Jim_Obj *const *argv, int op)
9251 {
9252     jim_wide wideValue, res = 0;
9253     double doubleValue, doubleRes = 0;
9254     int i = 2;
9255
9256     if (argc < 2) {
9257         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9258         return JIM_ERR;
9259     } else if (argc == 2) {
9260         /* The arity = 2 case is different. For [- x] returns -x,
9261          * while [/ x] returns 1/x. */
9262         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9263             if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9264                     JIM_OK)
9265             {
9266                 return JIM_ERR;
9267             } else {
9268                 if (op == JIM_EXPROP_SUB)
9269                     doubleRes = -doubleValue;
9270                 else
9271                     doubleRes = 1.0/doubleValue;
9272                 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9273                             doubleRes));
9274                 return JIM_OK;
9275             }
9276         }
9277         if (op == JIM_EXPROP_SUB) {
9278             res = -wideValue;
9279             Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9280         } else {
9281             doubleRes = 1.0/wideValue;
9282             Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9283                         doubleRes));
9284         }
9285         return JIM_OK;
9286     } else {
9287         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9288             if (Jim_GetDouble(interp, argv[1], &doubleRes)
9289                     != JIM_OK) {
9290                 return JIM_ERR;
9291             } else {
9292                 goto trydouble;
9293             }
9294         }
9295     }
9296     for (i = 2; i < argc; i++) {
9297         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9298             doubleRes = (double) res;
9299             goto trydouble;
9300         }
9301         if (op == JIM_EXPROP_SUB)
9302             res -= wideValue;
9303         else
9304             res /= wideValue;
9305     }
9306     Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9307     return JIM_OK;
9308 trydouble:
9309     for (;i < argc; i++) {
9310         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9311             return JIM_ERR;
9312         if (op == JIM_EXPROP_SUB)
9313             doubleRes -= doubleValue;
9314         else
9315             doubleRes /= doubleValue;
9316     }
9317     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9318     return JIM_OK;
9319 }
9320
9321
9322 /* [+] */
9323 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9324         Jim_Obj *const *argv)
9325 {
9326     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9327 }
9328
9329 /* [*] */
9330 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9331         Jim_Obj *const *argv)
9332 {
9333     return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9334 }
9335
9336 /* [-] */
9337 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9338         Jim_Obj *const *argv)
9339 {
9340     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9341 }
9342
9343 /* [/] */
9344 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9345         Jim_Obj *const *argv)
9346 {
9347     return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9348 }
9349
9350 /* [set] */
9351 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9352         Jim_Obj *const *argv)
9353 {
9354     if (argc != 2 && argc != 3) {
9355         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9356         return JIM_ERR;
9357     }
9358     if (argc == 2) {
9359         Jim_Obj *objPtr;
9360         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9361         if (!objPtr)
9362             return JIM_ERR;
9363         Jim_SetResult(interp, objPtr);
9364         return JIM_OK;
9365     }
9366     /* argc == 3 case. */
9367     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9368         return JIM_ERR;
9369     Jim_SetResult(interp, argv[2]);
9370     return JIM_OK;
9371 }
9372
9373 /* [unset] */
9374 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, 
9375         Jim_Obj *const *argv)
9376 {
9377     int i;
9378
9379     if (argc < 2) {
9380         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9381         return JIM_ERR;
9382     }
9383     for (i = 1; i < argc; i++) {
9384         if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9385             return JIM_ERR;
9386     }
9387     return JIM_OK;
9388 }
9389
9390 /* [incr] */
9391 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, 
9392         Jim_Obj *const *argv)
9393 {
9394     jim_wide wideValue, increment = 1;
9395     Jim_Obj *intObjPtr;
9396
9397     if (argc != 2 && argc != 3) {
9398         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9399         return JIM_ERR;
9400     }
9401     if (argc == 3) {
9402         if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9403             return JIM_ERR;
9404     }
9405     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9406     if (!intObjPtr) return JIM_ERR;
9407     if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9408         return JIM_ERR;
9409     if (Jim_IsShared(intObjPtr)) {
9410         intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9411         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9412             Jim_FreeNewObj(interp, intObjPtr);
9413             return JIM_ERR;
9414         }
9415     } else {
9416         Jim_SetWide(interp, intObjPtr, wideValue+increment);
9417         /* The following step is required in order to invalidate the
9418          * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9419         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9420             return JIM_ERR;
9421         }
9422     }
9423     Jim_SetResult(interp, intObjPtr);
9424     return JIM_OK;
9425 }
9426
9427 /* [while] */
9428 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, 
9429         Jim_Obj *const *argv)
9430 {
9431     if (argc != 3) {
9432         Jim_WrongNumArgs(interp, 1, argv, "condition body");
9433         return JIM_ERR;
9434     }
9435     /* Try to run a specialized version of while if the expression
9436      * is in one of the following forms:
9437      *
9438      *   $a < CONST, $a < $b
9439      *   $a <= CONST, $a <= $b
9440      *   $a > CONST, $a > $b
9441      *   $a >= CONST, $a >= $b
9442      *   $a != CONST, $a != $b
9443      *   $a == CONST, $a == $b
9444      *   $a
9445      *   !$a
9446      *   CONST
9447      */
9448
9449 #ifdef JIM_OPTIMIZATION
9450     {
9451         ExprByteCode *expr;
9452         Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9453         int exprLen, retval;
9454
9455         /* STEP 1 -- Check if there are the conditions to run the specialized
9456          * version of while */
9457         
9458         if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9459         if (expr->len <= 0 || expr->len > 3) goto noopt;
9460         switch(expr->len) {
9461         case 1:
9462             if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9463                 expr->opcode[0] != JIM_EXPROP_NUMBER)
9464                 goto noopt;
9465             break;
9466         case 2:
9467             if (expr->opcode[1] != JIM_EXPROP_NOT ||
9468                 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9469                 goto noopt;
9470             break;
9471         case 3:
9472             if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9473                 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9474                  expr->opcode[1] != JIM_EXPROP_VARIABLE))
9475                 goto noopt;
9476             switch(expr->opcode[2]) {
9477             case JIM_EXPROP_LT:
9478             case JIM_EXPROP_LTE:
9479             case JIM_EXPROP_GT:
9480             case JIM_EXPROP_GTE:
9481             case JIM_EXPROP_NUMEQ:
9482             case JIM_EXPROP_NUMNE:
9483                 /* nothing to do */
9484                 break;
9485             default:
9486                 goto noopt;
9487             }
9488             break;
9489         default:
9490             Jim_Panic(interp,
9491                 "Unexpected default reached in Jim_WhileCoreCommand()");
9492             break;
9493         }
9494
9495         /* STEP 2 -- conditions meet. Initialization. Take different
9496          * branches for different expression lengths. */
9497         exprLen = expr->len;
9498
9499         if (exprLen == 1) {
9500             jim_wide wideValue;
9501
9502             if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9503                 varAObjPtr = expr->obj[0];
9504                 Jim_IncrRefCount(varAObjPtr);
9505             } else {
9506                 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9507                     goto noopt;
9508             }
9509             while (1) {
9510                 if (varAObjPtr) {
9511                     if (!(objPtr =
9512                                Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9513                         Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9514                     {
9515                         Jim_DecrRefCount(interp, varAObjPtr);
9516                         goto noopt;
9517                     }
9518                 }
9519                 if (!wideValue) break;
9520                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9521                     switch(retval) {
9522                     case JIM_BREAK:
9523                         if (varAObjPtr)
9524                             Jim_DecrRefCount(interp, varAObjPtr);
9525                         goto out;
9526                         break;
9527                     case JIM_CONTINUE:
9528                         continue;
9529                         break;
9530                     default:
9531                         if (varAObjPtr)
9532                             Jim_DecrRefCount(interp, varAObjPtr);
9533                         return retval;
9534                     }
9535                 }
9536             }
9537             if (varAObjPtr)
9538                 Jim_DecrRefCount(interp, varAObjPtr);
9539         } else if (exprLen == 3) {
9540             jim_wide wideValueA, wideValueB, cmpRes = 0;
9541             int cmpType = expr->opcode[2];
9542
9543             varAObjPtr = expr->obj[0];
9544             Jim_IncrRefCount(varAObjPtr);
9545             if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9546                 varBObjPtr = expr->obj[1];
9547                 Jim_IncrRefCount(varBObjPtr);
9548             } else {
9549                 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9550                     goto noopt;
9551             }
9552             while (1) {
9553                 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9554                     Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9555                 {
9556                     Jim_DecrRefCount(interp, varAObjPtr);
9557                     if (varBObjPtr)
9558                         Jim_DecrRefCount(interp, varBObjPtr);
9559                     goto noopt;
9560                 }
9561                 if (varBObjPtr) {
9562                     if (!(objPtr =
9563                                Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9564                         Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9565                     {
9566                         Jim_DecrRefCount(interp, varAObjPtr);
9567                         if (varBObjPtr)
9568                             Jim_DecrRefCount(interp, varBObjPtr);
9569                         goto noopt;
9570                     }
9571                 }
9572                 switch(cmpType) {
9573                 case JIM_EXPROP_LT:
9574                     cmpRes = wideValueA < wideValueB; break;
9575                 case JIM_EXPROP_LTE:
9576                     cmpRes = wideValueA <= wideValueB; break;
9577                 case JIM_EXPROP_GT:
9578                     cmpRes = wideValueA > wideValueB; break;
9579                 case JIM_EXPROP_GTE:
9580                     cmpRes = wideValueA >= wideValueB; break;
9581                 case JIM_EXPROP_NUMEQ:
9582                     cmpRes = wideValueA == wideValueB; break;
9583                 case JIM_EXPROP_NUMNE:
9584                     cmpRes = wideValueA != wideValueB; break;
9585                 }
9586                 if (!cmpRes) break;
9587                 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9588                     switch(retval) {
9589                     case JIM_BREAK:
9590                         Jim_DecrRefCount(interp, varAObjPtr);
9591                         if (varBObjPtr)
9592                             Jim_DecrRefCount(interp, varBObjPtr);
9593                         goto out;
9594                         break;
9595                     case JIM_CONTINUE:
9596                         continue;
9597                         break;
9598                     default:
9599                         Jim_DecrRefCount(interp, varAObjPtr);
9600                         if (varBObjPtr)
9601                             Jim_DecrRefCount(interp, varBObjPtr);
9602                         return retval;
9603                     }
9604                 }
9605             }
9606             Jim_DecrRefCount(interp, varAObjPtr);
9607             if (varBObjPtr)
9608                 Jim_DecrRefCount(interp, varBObjPtr);
9609         } else {
9610             /* TODO: case for len == 2 */
9611             goto noopt;
9612         }
9613         Jim_SetEmptyResult(interp);
9614         return JIM_OK;
9615     }
9616 noopt:
9617 #endif
9618
9619     /* The general purpose implementation of while starts here */
9620     while (1) {
9621         int boolean, retval;
9622
9623         if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9624                         &boolean)) != JIM_OK)
9625             return retval;
9626         if (!boolean) break;
9627         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9628             switch(retval) {
9629             case JIM_BREAK:
9630                 goto out;
9631                 break;
9632             case JIM_CONTINUE:
9633                 continue;
9634                 break;
9635             default:
9636                 return retval;
9637             }
9638         }
9639     }
9640 out:
9641     Jim_SetEmptyResult(interp);
9642     return JIM_OK;
9643 }
9644
9645 /* [for] */
9646 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, 
9647         Jim_Obj *const *argv)
9648 {
9649     int retval;
9650
9651     if (argc != 5) {
9652         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9653         return JIM_ERR;
9654     }
9655     /* Check if the for is on the form:
9656      *      for {set i CONST} {$i < CONST} {incr i}
9657      *      for {set i CONST} {$i < $j} {incr i}
9658      *      for {set i CONST} {$i <= CONST} {incr i}
9659      *      for {set i CONST} {$i <= $j} {incr i}
9660      * XXX: NOTE: if variable traces are implemented, this optimization
9661      * need to be modified to check for the proc epoch at every variable
9662      * update. */
9663 #ifdef JIM_OPTIMIZATION
9664     {
9665         ScriptObj *initScript, *incrScript;
9666         ExprByteCode *expr;
9667         jim_wide start, stop, currentVal;
9668         unsigned jim_wide procEpoch = interp->procEpoch;
9669         Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9670         int cmpType;
9671         struct Jim_Cmd *cmdPtr;
9672
9673         /* Do it only if there aren't shared arguments */
9674         if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9675             goto evalstart;
9676         initScript = Jim_GetScript(interp, argv[1]);
9677         expr = Jim_GetExpression(interp, argv[2]);
9678         incrScript = Jim_GetScript(interp, argv[3]);
9679
9680         /* Ensure proper lengths to start */
9681         if (initScript->len != 6) goto evalstart;
9682         if (incrScript->len != 4) goto evalstart;
9683         if (expr->len != 3) goto evalstart;
9684         /* Ensure proper token types. */
9685         if (initScript->token[2].type != JIM_TT_ESC ||
9686             initScript->token[4].type != JIM_TT_ESC ||
9687             incrScript->token[2].type != JIM_TT_ESC ||
9688             expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9689             (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9690              expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9691             (expr->opcode[2] != JIM_EXPROP_LT &&
9692              expr->opcode[2] != JIM_EXPROP_LTE))
9693             goto evalstart;
9694         cmpType = expr->opcode[2];
9695         /* Initialization command must be [set] */
9696         cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9697         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9698             goto evalstart;
9699         /* Update command must be incr */
9700         cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9701         if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9702             goto evalstart;
9703         /* set, incr, expression must be about the same variable */
9704         if (!Jim_StringEqObj(initScript->token[2].objPtr,
9705                             incrScript->token[2].objPtr, 0))
9706             goto evalstart;
9707         if (!Jim_StringEqObj(initScript->token[2].objPtr,
9708                             expr->obj[0], 0))
9709             goto evalstart;
9710         /* Check that the initialization and comparison are valid integers */
9711         if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9712             goto evalstart;
9713         if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9714             Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9715         {
9716             goto evalstart;
9717         }
9718
9719         /* Initialization */
9720         varNamePtr = expr->obj[0];
9721         if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9722             stopVarNamePtr = expr->obj[1];
9723             Jim_IncrRefCount(stopVarNamePtr);
9724         }
9725         Jim_IncrRefCount(varNamePtr);
9726
9727         /* --- OPTIMIZED FOR --- */
9728         /* Start to loop */
9729         objPtr = Jim_NewIntObj(interp, start);
9730         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9731             Jim_DecrRefCount(interp, varNamePtr);
9732             if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9733             Jim_FreeNewObj(interp, objPtr);
9734             goto evalstart;
9735         }
9736         while (1) {
9737             /* === Check condition === */
9738             /* Common code: */
9739             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9740             if (objPtr == NULL ||
9741                 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9742             {
9743                 Jim_DecrRefCount(interp, varNamePtr);
9744                 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9745                 goto testcond;
9746             }
9747             /* Immediate or Variable? get the 'stop' value if the latter. */
9748             if (stopVarNamePtr) {
9749                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9750                 if (objPtr == NULL ||
9751                     Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9752                 {
9753                     Jim_DecrRefCount(interp, varNamePtr);
9754                     Jim_DecrRefCount(interp, stopVarNamePtr);
9755                     goto testcond;
9756                 }
9757             }
9758             if (cmpType == JIM_EXPROP_LT) {
9759                 if (currentVal >= stop) break;
9760             } else {
9761                 if (currentVal > stop) break;
9762             }
9763             /* Eval body */
9764             if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9765                 switch(retval) {
9766                 case JIM_BREAK:
9767                     if (stopVarNamePtr)
9768                         Jim_DecrRefCount(interp, stopVarNamePtr);
9769                     Jim_DecrRefCount(interp, varNamePtr);
9770                     goto out;
9771                 case JIM_CONTINUE:
9772                     /* nothing to do */
9773                     break;
9774                 default:
9775                     if (stopVarNamePtr)
9776                         Jim_DecrRefCount(interp, stopVarNamePtr);
9777                     Jim_DecrRefCount(interp, varNamePtr);
9778                     return retval;
9779                 }
9780             }
9781             /* If there was a change in procedures/command continue
9782              * with the usual [for] command implementation */
9783             if (procEpoch != interp->procEpoch) {
9784                 if (stopVarNamePtr)
9785                     Jim_DecrRefCount(interp, stopVarNamePtr);
9786                 Jim_DecrRefCount(interp, varNamePtr);
9787                 goto evalnext;
9788             }
9789             /* Increment */
9790             objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9791             if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9792                 objPtr->internalRep.wideValue ++;
9793                 Jim_InvalidateStringRep(objPtr);
9794             } else {
9795                 Jim_Obj *auxObjPtr;
9796
9797                 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9798                     if (stopVarNamePtr)
9799                         Jim_DecrRefCount(interp, stopVarNamePtr);
9800                     Jim_DecrRefCount(interp, varNamePtr);
9801                     goto evalnext;
9802                 }
9803                 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9804                 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9805                     if (stopVarNamePtr)
9806                         Jim_DecrRefCount(interp, stopVarNamePtr);
9807                     Jim_DecrRefCount(interp, varNamePtr);
9808                     Jim_FreeNewObj(interp, auxObjPtr);
9809                     goto evalnext;
9810                 }
9811             }
9812         }
9813         if (stopVarNamePtr)
9814             Jim_DecrRefCount(interp, stopVarNamePtr);
9815         Jim_DecrRefCount(interp, varNamePtr);
9816         Jim_SetEmptyResult(interp);
9817         return JIM_OK;
9818     }
9819 #endif
9820 evalstart:
9821     /* Eval start */
9822     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9823         return retval;
9824     while (1) {
9825         int boolean;
9826 testcond:
9827         /* Test the condition */
9828         if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9829                 != JIM_OK)
9830             return retval;
9831         if (!boolean) break;
9832         /* Eval body */
9833         if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9834             switch(retval) {
9835             case JIM_BREAK:
9836                 goto out;
9837                 break;
9838             case JIM_CONTINUE:
9839                 /* Nothing to do */
9840                 break;
9841             default:
9842                 return retval;
9843             }
9844         }
9845 evalnext:
9846         /* Eval next */
9847         if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9848             switch(retval) {
9849             case JIM_BREAK:
9850                 goto out;
9851                 break;
9852             case JIM_CONTINUE:
9853                 continue;
9854                 break;
9855             default:
9856                 return retval;
9857             }
9858         }
9859     }
9860 out:
9861     Jim_SetEmptyResult(interp);
9862     return JIM_OK;
9863 }
9864
9865 /* foreach + lmap implementation. */
9866 static int JimForeachMapHelper(Jim_Interp *interp, int argc, 
9867         Jim_Obj *const *argv, int doMap)
9868 {
9869     int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9870     int nbrOfLoops = 0;
9871     Jim_Obj *emptyStr, *script, *mapRes = NULL;
9872
9873     if (argc < 4 || argc % 2 != 0) {
9874         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9875         return JIM_ERR;
9876     }
9877     if (doMap) {
9878         mapRes = Jim_NewListObj(interp, NULL, 0);
9879         Jim_IncrRefCount(mapRes);
9880     }
9881     emptyStr = Jim_NewEmptyStringObj(interp);
9882     Jim_IncrRefCount(emptyStr);
9883     script = argv[argc-1];            /* Last argument is a script */
9884     nbrOfLists = (argc - 1 - 1) / 2;  /* argc - 'foreach' - script */
9885     listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9886     listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9887     /* Initialize iterators and remember max nbr elements each list */
9888     memset(listsIdx, 0, nbrOfLists * sizeof(int));
9889     /* Remember lengths of all lists and calculate how much rounds to loop */
9890     for (i=0; i < nbrOfLists*2; i += 2) {
9891         div_t cnt;
9892         int count;
9893         Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9894         Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9895         if (listsEnd[i] == 0) {
9896             Jim_SetResultString(interp, "foreach varlist is empty", -1);
9897             goto err;
9898         }
9899         cnt = div(listsEnd[i+1], listsEnd[i]);
9900         count = cnt.quot + (cnt.rem ? 1 : 0);
9901         if (count > nbrOfLoops)
9902             nbrOfLoops = count;
9903     }
9904     for (; nbrOfLoops-- > 0; ) {
9905         for (i=0; i < nbrOfLists; ++i) {
9906             int varIdx = 0, var = i * 2;
9907             while (varIdx < listsEnd[var]) {
9908                 Jim_Obj *varName, *ele;
9909                 int lst = i * 2 + 1;
9910                 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9911                         != JIM_OK)
9912                         goto err;
9913                 if (listsIdx[i] < listsEnd[lst]) {
9914                     if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9915                         != JIM_OK)
9916                         goto err;
9917                     if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9918                         Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9919                         goto err;
9920                     }
9921                     ++listsIdx[i];  /* Remember next iterator of current list */ 
9922                 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9923                     Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9924                     goto err;
9925                 }
9926                 ++varIdx;  /* Next variable */
9927             }
9928         }
9929         switch (result = Jim_EvalObj(interp, script)) {
9930             case JIM_OK:
9931                 if (doMap)
9932                     Jim_ListAppendElement(interp, mapRes, interp->result);
9933                 break;
9934             case JIM_CONTINUE:
9935                 break;
9936             case JIM_BREAK:
9937                 goto out;
9938                 break;
9939             default:
9940                 goto err;
9941         }
9942     }
9943 out:
9944     result = JIM_OK;
9945     if (doMap)
9946         Jim_SetResult(interp, mapRes);
9947     else
9948         Jim_SetEmptyResult(interp);
9949 err:
9950     if (doMap)
9951         Jim_DecrRefCount(interp, mapRes);
9952     Jim_DecrRefCount(interp, emptyStr);
9953     Jim_Free(listsIdx);
9954     Jim_Free(listsEnd);
9955     return result;
9956 }
9957
9958 /* [foreach] */
9959 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, 
9960         Jim_Obj *const *argv)
9961 {
9962     return JimForeachMapHelper(interp, argc, argv, 0);
9963 }
9964
9965 /* [lmap] */
9966 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, 
9967         Jim_Obj *const *argv)
9968 {
9969     return JimForeachMapHelper(interp, argc, argv, 1);
9970 }
9971
9972 /* [if] */
9973 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, 
9974         Jim_Obj *const *argv)
9975 {
9976     int boolean, retval, current = 1, falsebody = 0;
9977     if (argc >= 3) {
9978         while (1) {
9979             /* Far not enough arguments given! */
9980             if (current >= argc) goto err;
9981             if ((retval = Jim_GetBoolFromExpr(interp,
9982                         argv[current++], &boolean))
9983                     != JIM_OK)
9984                 return retval;
9985             /* There lacks something, isn't it? */
9986             if (current >= argc) goto err;
9987             if (Jim_CompareStringImmediate(interp, argv[current],
9988                         "then")) current++;
9989             /* Tsk tsk, no then-clause? */
9990             if (current >= argc) goto err;
9991             if (boolean)
9992                 return Jim_EvalObj(interp, argv[current]);
9993              /* Ok: no else-clause follows */
9994             if (++current >= argc) return JIM_OK;
9995             falsebody = current++;
9996             if (Jim_CompareStringImmediate(interp, argv[falsebody],
9997                         "else")) {
9998                 /* IIICKS - else-clause isn't last cmd? */
9999                 if (current != argc-1) goto err;
10000                 return Jim_EvalObj(interp, argv[current]);
10001             } else if (Jim_CompareStringImmediate(interp,
10002                         argv[falsebody], "elseif"))
10003                 /* Ok: elseif follows meaning all the stuff
10004                  * again (how boring...) */
10005                 continue;
10006             /* OOPS - else-clause is not last cmd?*/
10007             else if (falsebody != argc-1)
10008                 goto err;
10009             return Jim_EvalObj(interp, argv[falsebody]);
10010         }
10011         return JIM_OK;
10012     }
10013 err:
10014     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10015     return JIM_ERR;
10016 }
10017
10018 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10019
10020 /* [switch] */
10021 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, 
10022         Jim_Obj *const *argv)
10023 {
10024     int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10025     Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10026     Jim_Obj *script = 0;
10027     if (argc < 3) goto wrongnumargs;
10028     for (opt=1; opt < argc; ++opt) {
10029         const char *option = Jim_GetString(argv[opt], 0);
10030         if (*option != '-') break;
10031         else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10032         else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10033         else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10034         else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10035         else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10036             if ((argc - opt) < 2) goto wrongnumargs;
10037             command = argv[++opt]; 
10038         } else {
10039             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10040             Jim_AppendStrings(interp, Jim_GetResult(interp),
10041                 "bad option \"", option, "\": must be -exact, -glob, "
10042                 "-regexp, -command procname or --", 0);
10043             goto err;            
10044         }
10045         if ((argc - opt) < 2) goto wrongnumargs;
10046     }
10047     strObj = argv[opt++];
10048     patCount = argc - opt;
10049     if (patCount == 1) {
10050         Jim_Obj **vector;
10051         JimListGetElements(interp, argv[opt], &patCount, &vector);
10052         caseList = vector;
10053     } else
10054         caseList = &argv[opt];
10055     if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10056     for (i=0; script == 0 && i < patCount; i += 2) {
10057         Jim_Obj *patObj = caseList[i];
10058         if (!Jim_CompareStringImmediate(interp, patObj, "default")
10059             || i < (patCount-2)) {
10060             switch (matchOpt) {
10061                 case SWITCH_EXACT:
10062                     if (Jim_StringEqObj(strObj, patObj, 0))
10063                         script = caseList[i+1];
10064                     break;
10065                 case SWITCH_GLOB:
10066                     if (Jim_StringMatchObj(patObj, strObj, 0))
10067                         script = caseList[i+1];
10068                     break;
10069                 case SWITCH_RE:
10070                     command = Jim_NewStringObj(interp, "regexp", -1);
10071                     /* Fall thru intentionally */
10072                 case SWITCH_CMD: {
10073                     Jim_Obj *parms[] = {command, patObj, strObj};
10074                     int rc = Jim_EvalObjVector(interp, 3, parms);
10075                     long matching;
10076                     /* After the execution of a command we need to
10077                      * make sure to reconvert the object into a list
10078                      * again. Only for the single-list style [switch]. */
10079                     if (argc-opt == 1) {
10080                         Jim_Obj **vector;
10081                         JimListGetElements(interp, argv[opt], &patCount,
10082                                 &vector);
10083                         caseList = vector;
10084                     }
10085                     /* command is here already decref'd */
10086                     if (rc != JIM_OK) {
10087                         retcode = rc;
10088                         goto err;
10089                     }
10090                     rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10091                     if (rc != JIM_OK) {
10092                         retcode = rc;
10093                         goto err;
10094                     }
10095                     if (matching)
10096                         script = caseList[i+1];
10097                     break;
10098                 }
10099                 default:
10100                     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10101                     Jim_AppendStrings(interp, Jim_GetResult(interp),
10102                         "internal error: no such option implemented", 0);
10103                     goto err;
10104             }
10105         } else {
10106           script = caseList[i+1];
10107         }
10108     }
10109     for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10110         i += 2)
10111         script = caseList[i+1];
10112     if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10113         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10114         Jim_AppendStrings(interp, Jim_GetResult(interp),
10115             "no body specified for pattern \"",
10116             Jim_GetString(caseList[i-2], 0), "\"", 0);
10117         goto err;
10118     }
10119     retcode = JIM_OK;
10120     Jim_SetEmptyResult(interp);
10121     if (script != 0)
10122         retcode = Jim_EvalObj(interp, script);
10123     return retcode;
10124 wrongnumargs:
10125     Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10126         "pattern body ... ?default body?   or   "
10127         "{pattern body ?pattern body ...?}");
10128 err:
10129     return retcode;        
10130 }
10131
10132 /* [list] */
10133 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, 
10134         Jim_Obj *const *argv)
10135 {
10136     Jim_Obj *listObjPtr;
10137
10138     listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10139     Jim_SetResult(interp, listObjPtr);
10140     return JIM_OK;
10141 }
10142
10143 /* [lindex] */
10144 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, 
10145         Jim_Obj *const *argv)
10146 {
10147     Jim_Obj *objPtr, *listObjPtr;
10148     int i;
10149     int index;
10150
10151     if (argc < 3) {
10152         Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10153         return JIM_ERR;
10154     }
10155     objPtr = argv[1];
10156     Jim_IncrRefCount(objPtr);
10157     for (i = 2; i < argc; i++) {
10158         listObjPtr = objPtr;
10159         if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10160             Jim_DecrRefCount(interp, listObjPtr);
10161             return JIM_ERR;
10162         }
10163         if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10164                     JIM_NONE) != JIM_OK) {
10165             /* Returns an empty object if the index
10166              * is out of range. */
10167             Jim_DecrRefCount(interp, listObjPtr);
10168             Jim_SetEmptyResult(interp);
10169             return JIM_OK;
10170         }
10171         Jim_IncrRefCount(objPtr);
10172         Jim_DecrRefCount(interp, listObjPtr);
10173     }
10174     Jim_SetResult(interp, objPtr);
10175     Jim_DecrRefCount(interp, objPtr);
10176     return JIM_OK;
10177 }
10178
10179 /* [llength] */
10180 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, 
10181         Jim_Obj *const *argv)
10182 {
10183     int len;
10184
10185     if (argc != 2) {
10186         Jim_WrongNumArgs(interp, 1, argv, "list");
10187         return JIM_ERR;
10188     }
10189     Jim_ListLength(interp, argv[1], &len);
10190     Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10191     return JIM_OK;
10192 }
10193
10194 /* [lappend] */
10195 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, 
10196         Jim_Obj *const *argv)
10197 {
10198     Jim_Obj *listObjPtr;
10199     int shared, i;
10200
10201     if (argc < 2) {
10202         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10203         return JIM_ERR;
10204     }
10205     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10206     if (!listObjPtr) {
10207         /* Create the list if it does not exists */
10208         listObjPtr = Jim_NewListObj(interp, NULL, 0);
10209         if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10210             Jim_FreeNewObj(interp, listObjPtr);
10211             return JIM_ERR;
10212         }
10213     }
10214     shared = Jim_IsShared(listObjPtr);
10215     if (shared)
10216         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10217     for (i = 2; i < argc; i++)
10218         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10219     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10220         if (shared)
10221             Jim_FreeNewObj(interp, listObjPtr);
10222         return JIM_ERR;
10223     }
10224     Jim_SetResult(interp, listObjPtr);
10225     return JIM_OK;
10226 }
10227
10228 /* [linsert] */
10229 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, 
10230         Jim_Obj *const *argv)
10231 {
10232     int index, len;
10233     Jim_Obj *listPtr;
10234
10235     if (argc < 4) {
10236         Jim_WrongNumArgs(interp, 1, argv, "list index element "
10237             "?element ...?");
10238         return JIM_ERR;
10239     }
10240     listPtr = argv[1];
10241     if (Jim_IsShared(listPtr))
10242         listPtr = Jim_DuplicateObj(interp, listPtr);
10243     if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10244         goto err;
10245     Jim_ListLength(interp, listPtr, &len);
10246     if (index >= len)
10247         index = len;
10248     else if (index < 0)
10249         index = len + index + 1;
10250     Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10251     Jim_SetResult(interp, listPtr);
10252     return JIM_OK;
10253 err:
10254     if (listPtr != argv[1]) {
10255         Jim_FreeNewObj(interp, listPtr);
10256     }
10257     return JIM_ERR;
10258 }
10259
10260 /* [lset] */
10261 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, 
10262         Jim_Obj *const *argv)
10263 {
10264     if (argc < 3) {
10265         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10266         return JIM_ERR;
10267     } else if (argc == 3) {
10268         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10269             return JIM_ERR;
10270         Jim_SetResult(interp, argv[2]);
10271         return JIM_OK;
10272     }
10273     if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10274             == JIM_ERR) return JIM_ERR;
10275     return JIM_OK;
10276 }
10277
10278 /* [lsort] */
10279 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10280 {
10281     const char *options[] = {
10282         "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10283     };
10284     enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10285     Jim_Obj *resObj;
10286     int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10287     int decreasing = 0;
10288
10289     if (argc < 2) {
10290         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10291         return JIM_ERR;
10292     }
10293     for (i = 1; i < (argc-1); i++) {
10294         int option;
10295
10296         if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10297                 != JIM_OK)
10298             return JIM_ERR;
10299         switch(option) {
10300         case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10301         case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10302         case OPT_INCREASING: decreasing = 0; break;
10303         case OPT_DECREASING: decreasing = 1; break;
10304         }
10305     }
10306     if (decreasing) {
10307         switch(lsortType) {
10308         case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10309         case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10310         }
10311     }
10312     resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10313     ListSortElements(interp, resObj, lsortType);
10314     Jim_SetResult(interp, resObj);
10315     return JIM_OK;
10316 }
10317
10318 /* [append] */
10319 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, 
10320         Jim_Obj *const *argv)
10321 {
10322     Jim_Obj *stringObjPtr;
10323     int shared, i;
10324
10325     if (argc < 2) {
10326         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10327         return JIM_ERR;
10328     }
10329     if (argc == 2) {
10330         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10331         if (!stringObjPtr) return JIM_ERR;
10332     } else {
10333         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10334         if (!stringObjPtr) {
10335             /* Create the string if it does not exists */
10336             stringObjPtr = Jim_NewEmptyStringObj(interp);
10337             if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10338                     != JIM_OK) {
10339                 Jim_FreeNewObj(interp, stringObjPtr);
10340                 return JIM_ERR;
10341             }
10342         }
10343     }
10344     shared = Jim_IsShared(stringObjPtr);
10345     if (shared)
10346         stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10347     for (i = 2; i < argc; i++)
10348         Jim_AppendObj(interp, stringObjPtr, argv[i]);
10349     if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10350         if (shared)
10351             Jim_FreeNewObj(interp, stringObjPtr);
10352         return JIM_ERR;
10353     }
10354     Jim_SetResult(interp, stringObjPtr);
10355     return JIM_OK;
10356 }
10357
10358 /* [debug] */
10359 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, 
10360         Jim_Obj *const *argv)
10361 {
10362     const char *options[] = {
10363         "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10364         "exprbc",
10365         NULL
10366     };
10367     enum {
10368         OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10369         OPT_EXPRLEN, OPT_EXPRBC
10370     };
10371     int option;
10372
10373     if (argc < 2) {
10374         Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10375         return JIM_ERR;
10376     }
10377     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10378                 JIM_ERRMSG) != JIM_OK)
10379         return JIM_ERR;
10380     if (option == OPT_REFCOUNT) {
10381         if (argc != 3) {
10382             Jim_WrongNumArgs(interp, 2, argv, "object");
10383             return JIM_ERR;
10384         }
10385         Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10386         return JIM_OK;
10387     } else if (option == OPT_OBJCOUNT) {
10388         int freeobj = 0, liveobj = 0;
10389         char buf[256];
10390         Jim_Obj *objPtr;
10391
10392         if (argc != 2) {
10393             Jim_WrongNumArgs(interp, 2, argv, "");
10394             return JIM_ERR;
10395         }
10396         /* Count the number of free objects. */
10397         objPtr = interp->freeList;
10398         while (objPtr) {
10399             freeobj++;
10400             objPtr = objPtr->nextObjPtr;
10401         }
10402         /* Count the number of live objects. */
10403         objPtr = interp->liveList;
10404         while (objPtr) {
10405             liveobj++;
10406             objPtr = objPtr->nextObjPtr;
10407         }
10408         /* Set the result string and return. */
10409         sprintf(buf, "free %d used %d", freeobj, liveobj);
10410         Jim_SetResultString(interp, buf, -1);
10411         return JIM_OK;
10412     } else if (option == OPT_OBJECTS) {
10413         Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10414         /* Count the number of live objects. */
10415         objPtr = interp->liveList;
10416         listObjPtr = Jim_NewListObj(interp, NULL, 0);
10417         while (objPtr) {
10418             char buf[128];
10419             const char *type = objPtr->typePtr ?
10420                 objPtr->typePtr->name : "";
10421             subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10422             sprintf(buf, "%p", objPtr);
10423             Jim_ListAppendElement(interp, subListObjPtr,
10424                 Jim_NewStringObj(interp, buf, -1));
10425             Jim_ListAppendElement(interp, subListObjPtr,
10426                 Jim_NewStringObj(interp, type, -1));
10427             Jim_ListAppendElement(interp, subListObjPtr,
10428                 Jim_NewIntObj(interp, objPtr->refCount));
10429             Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10430             Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10431             objPtr = objPtr->nextObjPtr;
10432         }
10433         Jim_SetResult(interp, listObjPtr);
10434         return JIM_OK;
10435     } else if (option == OPT_INVSTR) {
10436         Jim_Obj *objPtr;
10437
10438         if (argc != 3) {
10439             Jim_WrongNumArgs(interp, 2, argv, "object");
10440             return JIM_ERR;
10441         }
10442         objPtr = argv[2];
10443         if (objPtr->typePtr != NULL)
10444             Jim_InvalidateStringRep(objPtr);
10445         Jim_SetEmptyResult(interp);
10446         return JIM_OK;
10447     } else if (option == OPT_SCRIPTLEN) {
10448         ScriptObj *script;
10449         if (argc != 3) {
10450             Jim_WrongNumArgs(interp, 2, argv, "script");
10451             return JIM_ERR;
10452         }
10453         script = Jim_GetScript(interp, argv[2]);
10454         Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10455         return JIM_OK;
10456     } else if (option == OPT_EXPRLEN) {
10457         ExprByteCode *expr;
10458         if (argc != 3) {
10459             Jim_WrongNumArgs(interp, 2, argv, "expression");
10460             return JIM_ERR;
10461         }
10462         expr = Jim_GetExpression(interp, argv[2]);
10463         if (expr == NULL)
10464             return JIM_ERR;
10465         Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10466         return JIM_OK;
10467     } else if (option == OPT_EXPRBC) {
10468         Jim_Obj *objPtr;
10469         ExprByteCode *expr;
10470         int i;
10471
10472         if (argc != 3) {
10473             Jim_WrongNumArgs(interp, 2, argv, "expression");
10474             return JIM_ERR;
10475         }
10476         expr = Jim_GetExpression(interp, argv[2]);
10477         if (expr == NULL)
10478             return JIM_ERR;
10479         objPtr = Jim_NewListObj(interp, NULL, 0);
10480         for (i = 0; i < expr->len; i++) {
10481             const char *type;
10482             Jim_ExprOperator *op;
10483
10484             switch(expr->opcode[i]) {
10485             case JIM_EXPROP_NUMBER: type = "number"; break;
10486             case JIM_EXPROP_COMMAND: type = "command"; break;
10487             case JIM_EXPROP_VARIABLE: type = "variable"; break;
10488             case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10489             case JIM_EXPROP_SUBST: type = "subst"; break;
10490             case JIM_EXPROP_STRING: type = "string"; break;
10491             default:
10492                 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10493                 if (op == NULL) {
10494                     type = "private";
10495                 } else {
10496                     type = "operator";
10497                 }
10498                 break;
10499             }
10500             Jim_ListAppendElement(interp, objPtr,
10501                     Jim_NewStringObj(interp, type, -1));
10502             Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10503         }
10504         Jim_SetResult(interp, objPtr);
10505         return JIM_OK;
10506     } else {
10507         Jim_SetResultString(interp,
10508             "bad option. Valid options are refcount, "
10509             "objcount, objects, invstr", -1);
10510         return JIM_ERR;
10511     }
10512     return JIM_OK; /* unreached */
10513 }
10514
10515 /* [eval] */
10516 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, 
10517         Jim_Obj *const *argv)
10518 {
10519     if (argc == 2) {
10520         return Jim_EvalObj(interp, argv[1]);
10521     } else if (argc > 2) {
10522         Jim_Obj *objPtr;
10523         int retcode;
10524
10525         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10526         Jim_IncrRefCount(objPtr);
10527         retcode = Jim_EvalObj(interp, objPtr);
10528         Jim_DecrRefCount(interp, objPtr);
10529         return retcode;
10530     } else {
10531         Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10532         return JIM_ERR;
10533     }
10534 }
10535
10536 /* [uplevel] */
10537 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, 
10538         Jim_Obj *const *argv)
10539 {
10540     if (argc >= 2) {
10541         int retcode, newLevel, oldLevel;
10542         Jim_CallFrame *savedCallFrame, *targetCallFrame;
10543         Jim_Obj *objPtr;
10544         const char *str;
10545
10546         /* Save the old callframe pointer */
10547         savedCallFrame = interp->framePtr;
10548
10549         /* Lookup the target frame pointer */
10550         str = Jim_GetString(argv[1], NULL);
10551         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10552         {
10553             if (Jim_GetCallFrameByLevel(interp, argv[1],
10554                         &targetCallFrame,
10555                         &newLevel) != JIM_OK)
10556                 return JIM_ERR;
10557             argc--;
10558             argv++;
10559         } else {
10560             if (Jim_GetCallFrameByLevel(interp, NULL,
10561                         &targetCallFrame,
10562                         &newLevel) != JIM_OK)
10563                 return JIM_ERR;
10564         }
10565         if (argc < 2) {
10566             argc++;
10567             argv--;
10568             Jim_WrongNumArgs(interp, 1, argv,
10569                     "?level? command ?arg ...?");
10570             return JIM_ERR;
10571         }
10572         /* Eval the code in the target callframe. */
10573         interp->framePtr = targetCallFrame;
10574         oldLevel = interp->numLevels;
10575         interp->numLevels = newLevel;
10576         if (argc == 2) {
10577             retcode = Jim_EvalObj(interp, argv[1]);
10578         } else {
10579             objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10580             Jim_IncrRefCount(objPtr);
10581             retcode = Jim_EvalObj(interp, objPtr);
10582             Jim_DecrRefCount(interp, objPtr);
10583         }
10584         interp->numLevels = oldLevel;
10585         interp->framePtr = savedCallFrame;
10586         return retcode;
10587     } else {
10588         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10589         return JIM_ERR;
10590     }
10591 }
10592
10593 /* [expr] */
10594 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, 
10595         Jim_Obj *const *argv)
10596 {
10597     Jim_Obj *exprResultPtr;
10598     int retcode;
10599
10600     if (argc == 2) {
10601         retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10602     } else if (argc > 2) {
10603         Jim_Obj *objPtr;
10604
10605         objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10606         Jim_IncrRefCount(objPtr);
10607         retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10608         Jim_DecrRefCount(interp, objPtr);
10609     } else {
10610         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10611         return JIM_ERR;
10612     }
10613     if (retcode != JIM_OK) return retcode;
10614     Jim_SetResult(interp, exprResultPtr);
10615     Jim_DecrRefCount(interp, exprResultPtr);
10616     return JIM_OK;
10617 }
10618
10619 /* [break] */
10620 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, 
10621         Jim_Obj *const *argv)
10622 {
10623     if (argc != 1) {
10624         Jim_WrongNumArgs(interp, 1, argv, "");
10625         return JIM_ERR;
10626     }
10627     return JIM_BREAK;
10628 }
10629
10630 /* [continue] */
10631 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10632         Jim_Obj *const *argv)
10633 {
10634     if (argc != 1) {
10635         Jim_WrongNumArgs(interp, 1, argv, "");
10636         return JIM_ERR;
10637     }
10638     return JIM_CONTINUE;
10639 }
10640
10641 /* [return] */
10642 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, 
10643         Jim_Obj *const *argv)
10644 {
10645     if (argc == 1) {
10646         return JIM_RETURN;
10647     } else if (argc == 2) {
10648         Jim_SetResult(interp, argv[1]);
10649         interp->returnCode = JIM_OK;
10650         return JIM_RETURN;
10651     } else if (argc == 3 || argc == 4) {
10652         int returnCode;
10653         if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10654             return JIM_ERR;
10655         interp->returnCode = returnCode;
10656         if (argc == 4)
10657             Jim_SetResult(interp, argv[3]);
10658         return JIM_RETURN;
10659     } else {
10660         Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10661         return JIM_ERR;
10662     }
10663     return JIM_RETURN; /* unreached */
10664 }
10665
10666 /* [tailcall] */
10667 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10668         Jim_Obj *const *argv)
10669 {
10670     Jim_Obj *objPtr;
10671
10672     objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10673     Jim_SetResult(interp, objPtr);
10674     return JIM_EVAL;
10675 }
10676
10677 /* [proc] */
10678 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, 
10679         Jim_Obj *const *argv)
10680 {
10681     int argListLen;
10682     int arityMin, arityMax;
10683
10684     if (argc != 4 && argc != 5) {
10685         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10686         return JIM_ERR;
10687     }
10688     Jim_ListLength(interp, argv[2], &argListLen);
10689     arityMin = arityMax = argListLen+1;
10690     if (argListLen) {
10691         const char *str;
10692         int len;
10693         Jim_Obj *lastArgPtr;
10694         
10695         Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10696         str = Jim_GetString(lastArgPtr, &len);
10697         if (len == 4 && memcmp(str, "args", 4) == 0) {
10698             arityMin--;
10699             arityMax = -1;
10700         }
10701     }
10702     if (argc == 4) {
10703         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10704                 argv[2], NULL, argv[3], arityMin, arityMax);
10705     } else {
10706         return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10707                 argv[2], argv[3], argv[4], arityMin, arityMax);
10708     }
10709 }
10710
10711 /* [concat] */
10712 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, 
10713         Jim_Obj *const *argv)
10714 {
10715     Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10716     return JIM_OK;
10717 }
10718
10719 /* [upvar] */
10720 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, 
10721         Jim_Obj *const *argv)
10722 {
10723     const char *str;
10724     int i;
10725     Jim_CallFrame *targetCallFrame;
10726
10727     /* Lookup the target frame pointer */
10728     str = Jim_GetString(argv[1], NULL);
10729     if (argc > 3 && 
10730         ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10731     {
10732         if (Jim_GetCallFrameByLevel(interp, argv[1],
10733                     &targetCallFrame, NULL) != JIM_OK)
10734             return JIM_ERR;
10735         argc--;
10736         argv++;
10737     } else {
10738         if (Jim_GetCallFrameByLevel(interp, NULL,
10739                     &targetCallFrame, NULL) != JIM_OK)
10740             return JIM_ERR;
10741     }
10742     /* Check for arity */
10743     if (argc < 3 || ((argc-1)%2) != 0) {
10744         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10745         return JIM_ERR;
10746     }
10747     /* Now... for every other/local couple: */
10748     for (i = 1; i < argc; i += 2) {
10749         if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10750                 targetCallFrame) != JIM_OK) return JIM_ERR;
10751     }
10752     return JIM_OK;
10753 }
10754
10755 /* [global] */
10756 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, 
10757         Jim_Obj *const *argv)
10758 {
10759     int i;
10760
10761     if (argc < 2) {
10762         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10763         return JIM_ERR;
10764     }
10765     /* Link every var to the toplevel having the same name */
10766     if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10767     for (i = 1; i < argc; i++) {
10768         if (Jim_SetVariableLink(interp, argv[i], argv[i],
10769                 interp->topFramePtr) != JIM_OK) return JIM_ERR;
10770     }
10771     return JIM_OK;
10772 }
10773
10774 /* does the [string map] operation. On error NULL is returned,
10775  * otherwise a new string object with the result, having refcount = 0,
10776  * is returned. */
10777 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10778         Jim_Obj *objPtr, int nocase)
10779 {
10780     int numMaps;
10781     const char **key, *str, *noMatchStart = NULL;
10782     Jim_Obj **value;
10783     int *keyLen, strLen, i;
10784     Jim_Obj *resultObjPtr;
10785     
10786     Jim_ListLength(interp, mapListObjPtr, &numMaps);
10787     if (numMaps % 2) {
10788         Jim_SetResultString(interp,
10789                 "list must contain an even number of elements", -1);
10790         return NULL;
10791     }
10792     /* Initialization */
10793     numMaps /= 2;
10794     key = Jim_Alloc(sizeof(char*)*numMaps);
10795     keyLen = Jim_Alloc(sizeof(int)*numMaps);
10796     value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10797     resultObjPtr = Jim_NewStringObj(interp, "", 0);
10798     for (i = 0; i < numMaps; i++) {
10799         Jim_Obj *eleObjPtr;
10800
10801         Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10802         key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10803         Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10804         value[i] = eleObjPtr;
10805     }
10806     str = Jim_GetString(objPtr, &strLen);
10807     /* Map it */
10808     while(strLen) {
10809         for (i = 0; i < numMaps; i++) {
10810             if (strLen >= keyLen[i] && keyLen[i]) {
10811                 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10812                             nocase))
10813                 {
10814                     if (noMatchStart) {
10815                         Jim_AppendString(interp, resultObjPtr,
10816                                 noMatchStart, str-noMatchStart);
10817                         noMatchStart = NULL;
10818                     }
10819                     Jim_AppendObj(interp, resultObjPtr, value[i]);
10820                     str += keyLen[i];
10821                     strLen -= keyLen[i];
10822                     break;
10823                 }
10824             }
10825         }
10826         if (i == numMaps) { /* no match */
10827             if (noMatchStart == NULL)
10828                 noMatchStart = str;
10829             str ++;
10830             strLen --;
10831         }
10832     }
10833     if (noMatchStart) {
10834         Jim_AppendString(interp, resultObjPtr,
10835             noMatchStart, str-noMatchStart);
10836     }
10837     Jim_Free((void*)key);
10838     Jim_Free(keyLen);
10839     Jim_Free(value);
10840     return resultObjPtr;
10841 }
10842
10843 /* [string] */
10844 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, 
10845         Jim_Obj *const *argv)
10846 {
10847     int option;
10848     const char *options[] = {
10849         "length", "compare", "match", "equal", "range", "map", "repeat",
10850         "index", "first", "tolower", "toupper", NULL
10851     };
10852     enum {
10853         OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10854         OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10855     };
10856
10857     if (argc < 2) {
10858         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10859         return JIM_ERR;
10860     }
10861     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10862                 JIM_ERRMSG) != JIM_OK)
10863         return JIM_ERR;
10864
10865     if (option == OPT_LENGTH) {
10866         int len;
10867
10868         if (argc != 3) {
10869             Jim_WrongNumArgs(interp, 2, argv, "string");
10870             return JIM_ERR;
10871         }
10872         Jim_GetString(argv[2], &len);
10873         Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10874         return JIM_OK;
10875     } else if (option == OPT_COMPARE) {
10876         int nocase = 0;
10877         if ((argc != 4 && argc != 5) ||
10878             (argc == 5 && Jim_CompareStringImmediate(interp,
10879                 argv[2], "-nocase") == 0)) {
10880             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10881             return JIM_ERR;
10882         }
10883         if (argc == 5) {
10884             nocase = 1;
10885             argv++;
10886         }
10887         Jim_SetResult(interp, Jim_NewIntObj(interp,
10888                     Jim_StringCompareObj(argv[2],
10889                             argv[3], nocase)));
10890         return JIM_OK;
10891     } else if (option == OPT_MATCH) {
10892         int nocase = 0;
10893         if ((argc != 4 && argc != 5) ||
10894             (argc == 5 && Jim_CompareStringImmediate(interp,
10895                 argv[2], "-nocase") == 0)) {
10896             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10897                     "string");
10898             return JIM_ERR;
10899         }
10900         if (argc == 5) {
10901             nocase = 1;
10902             argv++;
10903         }
10904         Jim_SetResult(interp,
10905             Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10906                     argv[3], nocase)));
10907         return JIM_OK;
10908     } else if (option == OPT_EQUAL) {
10909         if (argc != 4) {
10910             Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10911             return JIM_ERR;
10912         }
10913         Jim_SetResult(interp,
10914             Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10915                     argv[3], 0)));
10916         return JIM_OK;
10917     } else if (option == OPT_RANGE) {
10918         Jim_Obj *objPtr;
10919
10920         if (argc != 5) {
10921             Jim_WrongNumArgs(interp, 2, argv, "string first last");
10922             return JIM_ERR;
10923         }
10924         objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10925         if (objPtr == NULL)
10926             return JIM_ERR;
10927         Jim_SetResult(interp, objPtr);
10928         return JIM_OK;
10929     } else if (option == OPT_MAP) {
10930         int nocase = 0;
10931         Jim_Obj *objPtr;
10932
10933         if ((argc != 4 && argc != 5) ||
10934             (argc == 5 && Jim_CompareStringImmediate(interp,
10935                 argv[2], "-nocase") == 0)) {
10936             Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10937                     "string");
10938             return JIM_ERR;
10939         }
10940         if (argc == 5) {
10941             nocase = 1;
10942             argv++;
10943         }
10944         objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10945         if (objPtr == NULL)
10946             return JIM_ERR;
10947         Jim_SetResult(interp, objPtr);
10948         return JIM_OK;
10949     } else if (option == OPT_REPEAT) {
10950         Jim_Obj *objPtr;
10951         jim_wide count;
10952
10953         if (argc != 4) {
10954             Jim_WrongNumArgs(interp, 2, argv, "string count");
10955             return JIM_ERR;
10956         }
10957         if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
10958             return JIM_ERR;
10959         objPtr = Jim_NewStringObj(interp, "", 0);
10960         while (count--) {
10961             Jim_AppendObj(interp, objPtr, argv[2]);
10962         }
10963         Jim_SetResult(interp, objPtr);
10964         return JIM_OK;
10965     } else if (option == OPT_INDEX) {
10966         int index, len;
10967         const char *str;
10968
10969         if (argc != 4) {
10970             Jim_WrongNumArgs(interp, 2, argv, "string index");
10971             return JIM_ERR;
10972         }
10973         if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
10974             return JIM_ERR;
10975         str = Jim_GetString(argv[2], &len);
10976         if (index != INT_MIN && index != INT_MAX)
10977             index = JimRelToAbsIndex(len, index);
10978         if (index < 0 || index >= len) {
10979             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10980             return JIM_OK;
10981         } else {
10982             Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
10983             return JIM_OK;
10984         }
10985     } else if (option == OPT_FIRST) {
10986         int index = 0, l1, l2;
10987         const char *s1, *s2;
10988
10989         if (argc != 4 && argc != 5) {
10990             Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
10991             return JIM_ERR;
10992         }
10993         s1 = Jim_GetString(argv[2], &l1);
10994         s2 = Jim_GetString(argv[3], &l2);
10995         if (argc == 5) {
10996             if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
10997                 return JIM_ERR;
10998             index = JimRelToAbsIndex(l2, index);
10999         }
11000         Jim_SetResult(interp, Jim_NewIntObj(interp,
11001                     JimStringFirst(s1, l1, s2, l2, index)));
11002         return JIM_OK;
11003     } else if (option == OPT_TOLOWER) {
11004         if (argc != 3) {
11005             Jim_WrongNumArgs(interp, 2, argv, "string");
11006             return JIM_ERR;
11007         }
11008         Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11009     } else if (option == OPT_TOUPPER) {
11010         if (argc != 3) {
11011             Jim_WrongNumArgs(interp, 2, argv, "string");
11012             return JIM_ERR;
11013         }
11014         Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11015     }
11016     return JIM_OK;
11017 }
11018
11019 /* [time] */
11020 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, 
11021         Jim_Obj *const *argv)
11022 {
11023     long i, count = 1;
11024     jim_wide start, elapsed;
11025     char buf [256];
11026     const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11027
11028     if (argc < 2) {
11029         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11030         return JIM_ERR;
11031     }
11032     if (argc == 3) {
11033         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11034             return JIM_ERR;
11035     }
11036     if (count < 0)
11037         return JIM_OK;
11038     i = count;
11039     start = JimClock();
11040     while (i-- > 0) {
11041         int retval;
11042
11043         if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11044             return retval;
11045     }
11046     elapsed = JimClock() - start;
11047     sprintf(buf, fmt, elapsed/count);
11048     Jim_SetResultString(interp, buf, -1);
11049     return JIM_OK;
11050 }
11051
11052 /* [exit] */
11053 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, 
11054         Jim_Obj *const *argv)
11055 {
11056     long exitCode = 0;
11057
11058     if (argc > 2) {
11059         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11060         return JIM_ERR;
11061     }
11062     if (argc == 2) {
11063         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11064             return JIM_ERR;
11065     }
11066     interp->exitCode = exitCode;
11067     return JIM_EXIT;
11068 }
11069
11070 /* [catch] */
11071 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, 
11072         Jim_Obj *const *argv)
11073 {
11074     int exitCode = 0;
11075
11076     if (argc != 2 && argc != 3) {
11077         Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11078         return JIM_ERR;
11079     }
11080     exitCode = Jim_EvalObj(interp, argv[1]);
11081     if (argc == 3) {
11082         if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11083                 != JIM_OK)
11084             return JIM_ERR;
11085     }
11086     Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11087     return JIM_OK;
11088 }
11089
11090 /* [ref] */
11091 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, 
11092         Jim_Obj *const *argv)
11093 {
11094     if (argc != 3 && argc != 4) {
11095         Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11096         return JIM_ERR;
11097     }
11098     if (argc == 3) {
11099         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11100     } else {
11101         Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11102                     argv[3]));
11103     }
11104     return JIM_OK;
11105 }
11106
11107 /* [getref] */
11108 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, 
11109         Jim_Obj *const *argv)
11110 {
11111     Jim_Reference *refPtr;
11112
11113     if (argc != 2) {
11114         Jim_WrongNumArgs(interp, 1, argv, "reference");
11115         return JIM_ERR;
11116     }
11117     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11118         return JIM_ERR;
11119     Jim_SetResult(interp, refPtr->objPtr);
11120     return JIM_OK;
11121 }
11122
11123 /* [setref] */
11124 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, 
11125         Jim_Obj *const *argv)
11126 {
11127     Jim_Reference *refPtr;
11128
11129     if (argc != 3) {
11130         Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11131         return JIM_ERR;
11132     }
11133     if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11134         return JIM_ERR;
11135     Jim_IncrRefCount(argv[2]);
11136     Jim_DecrRefCount(interp, refPtr->objPtr);
11137     refPtr->objPtr = argv[2];
11138     Jim_SetResult(interp, argv[2]);
11139     return JIM_OK;
11140 }
11141
11142 /* [collect] */
11143 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, 
11144         Jim_Obj *const *argv)
11145 {
11146     if (argc != 1) {
11147         Jim_WrongNumArgs(interp, 1, argv, "");
11148         return JIM_ERR;
11149     }
11150     Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11151     return JIM_OK;
11152 }
11153
11154 /* [finalize] reference ?newValue? */
11155 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, 
11156         Jim_Obj *const *argv)
11157 {
11158     if (argc != 2 && argc != 3) {
11159         Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11160         return JIM_ERR;
11161     }
11162     if (argc == 2) {
11163         Jim_Obj *cmdNamePtr;
11164
11165         if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11166             return JIM_ERR;
11167         if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11168             Jim_SetResult(interp, cmdNamePtr);
11169     } else {
11170         if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11171             return JIM_ERR;
11172         Jim_SetResult(interp, argv[2]);
11173     }
11174     return JIM_OK;
11175 }
11176
11177 /* TODO */
11178 /* [info references] (list of all the references/finalizers) */
11179
11180 /* [rename] */
11181 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, 
11182         Jim_Obj *const *argv)
11183 {
11184     const char *oldName, *newName;
11185
11186     if (argc != 3) {
11187         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11188         return JIM_ERR;
11189     }
11190     oldName = Jim_GetString(argv[1], NULL);
11191     newName = Jim_GetString(argv[2], NULL);
11192     if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11193         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11194         Jim_AppendStrings(interp, Jim_GetResult(interp),
11195             "can't rename \"", oldName, "\": ",
11196             "command doesn't exist", NULL);
11197         return JIM_ERR;
11198     }
11199     return JIM_OK;
11200 }
11201
11202 /* [dict] */
11203 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, 
11204         Jim_Obj *const *argv)
11205 {
11206     int option;
11207     const char *options[] = {
11208         "create", "get", "set", "unset", "exists", NULL
11209     };
11210     enum {
11211         OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11212     };
11213
11214     if (argc < 2) {
11215         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11216         return JIM_ERR;
11217     }
11218
11219     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11220                 JIM_ERRMSG) != JIM_OK)
11221         return JIM_ERR;
11222
11223     if (option == OPT_CREATE) {
11224         Jim_Obj *objPtr;
11225
11226         if (argc % 2) {
11227             Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11228             return JIM_ERR;
11229         }
11230         objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11231         Jim_SetResult(interp, objPtr);
11232         return JIM_OK;
11233     } else if (option == OPT_GET) {
11234         Jim_Obj *objPtr;
11235
11236         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11237                 JIM_ERRMSG) != JIM_OK)
11238             return JIM_ERR;
11239         Jim_SetResult(interp, objPtr);
11240         return JIM_OK;
11241     } else if (option == OPT_SET) {
11242         if (argc < 5) {
11243             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11244             return JIM_ERR;
11245         }
11246         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11247                     argv[argc-1]);
11248     } else if (option == OPT_UNSET) {
11249         if (argc < 4) {
11250             Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11251             return JIM_ERR;
11252         }
11253         return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11254                     NULL);
11255     } else if (option == OPT_EXIST) {
11256         Jim_Obj *objPtr;
11257         int exists;
11258
11259         if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11260                 JIM_ERRMSG) == JIM_OK)
11261             exists = 1;
11262         else
11263             exists = 0;
11264         Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11265         return JIM_OK;
11266     } else {
11267         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11268         Jim_AppendStrings(interp, Jim_GetResult(interp),
11269             "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11270             " must be create, get, set", NULL);
11271         return JIM_ERR;
11272     }
11273     return JIM_OK;
11274 }
11275
11276 /* [load] */
11277 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc, 
11278         Jim_Obj *const *argv)
11279 {
11280     if (argc < 2) {
11281         Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11282         return JIM_ERR;
11283     }
11284     return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11285 }
11286
11287 /* [subst] */
11288 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, 
11289         Jim_Obj *const *argv)
11290 {
11291     int i, flags = 0;
11292     Jim_Obj *objPtr;
11293
11294     if (argc < 2) {
11295         Jim_WrongNumArgs(interp, 1, argv,
11296             "?-nobackslashes? ?-nocommands? ?-novariables? string");
11297         return JIM_ERR;
11298     }
11299     i = argc-2;
11300     while(i--) {
11301         if (Jim_CompareStringImmediate(interp, argv[i+1],
11302                     "-nobackslashes"))
11303             flags |= JIM_SUBST_NOESC;
11304         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11305                     "-novariables"))
11306             flags |= JIM_SUBST_NOVAR;
11307         else if (Jim_CompareStringImmediate(interp, argv[i+1],
11308                     "-nocommands"))
11309             flags |= JIM_SUBST_NOCMD;
11310         else {
11311             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11312             Jim_AppendStrings(interp, Jim_GetResult(interp),
11313                 "bad option \"", Jim_GetString(argv[i+1], NULL),
11314                 "\": must be -nobackslashes, -nocommands, or "
11315                 "-novariables", NULL);
11316             return JIM_ERR;
11317         }
11318     }
11319     if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11320         return JIM_ERR;
11321     Jim_SetResult(interp, objPtr);
11322     return JIM_OK;
11323 }
11324
11325 /* [info] */
11326 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, 
11327         Jim_Obj *const *argv)
11328 {
11329     int cmd, result = JIM_OK;
11330     static const char *commands[] = {
11331         "body", "commands", "exists", "globals", "level", "locals",
11332         "vars", "version", "complete", "args", NULL
11333     };
11334     enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11335           INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11336     
11337     if (argc < 2) {
11338         Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11339         return JIM_ERR;
11340     }
11341     if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11342         != JIM_OK) {
11343         return JIM_ERR;
11344     }
11345     
11346     if (cmd == INFO_COMMANDS) {
11347         if (argc != 2 && argc != 3) {
11348             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11349             return JIM_ERR;
11350         }
11351         if (argc == 3)
11352             Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11353         else
11354             Jim_SetResult(interp, JimCommandsList(interp, NULL));
11355     } else if (cmd == INFO_EXISTS) {
11356         Jim_Obj *exists;
11357         if (argc != 3) {
11358             Jim_WrongNumArgs(interp, 2, argv, "varName");
11359             return JIM_ERR;
11360         }
11361         exists = Jim_GetVariable(interp, argv[2], 0);
11362         Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11363     } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11364         int mode;
11365         switch (cmd) {
11366             case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11367             case INFO_LOCALS:  mode = JIM_VARLIST_LOCALS; break;
11368             case INFO_VARS:    mode = JIM_VARLIST_VARS; break;
11369             default: mode = 0; /* avoid warning */; break;
11370         }
11371         if (argc != 2 && argc != 3) {
11372             Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11373             return JIM_ERR;
11374         }
11375         if (argc == 3)
11376             Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11377         else
11378             Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11379     } else if (cmd == INFO_LEVEL) {
11380         Jim_Obj *objPtr;
11381         switch (argc) {
11382             case 2:
11383                 Jim_SetResult(interp,
11384                               Jim_NewIntObj(interp, interp->numLevels));
11385                 break;
11386             case 3:
11387                 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11388                     return JIM_ERR;
11389                 Jim_SetResult(interp, objPtr);
11390                 break;
11391             default:
11392                 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11393                 return JIM_ERR;
11394         }
11395     } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11396         Jim_Cmd *cmdPtr;
11397
11398         if (argc != 3) {
11399             Jim_WrongNumArgs(interp, 2, argv, "procname");
11400             return JIM_ERR;
11401         }
11402         if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11403             return JIM_ERR;
11404         if (cmdPtr->cmdProc != NULL) {
11405             Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11406             Jim_AppendStrings(interp, Jim_GetResult(interp),
11407                 "command \"", Jim_GetString(argv[2], NULL),
11408                 "\" is not a procedure", NULL);
11409             return JIM_ERR;
11410         }
11411         if (cmd == INFO_BODY)
11412             Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11413         else
11414             Jim_SetResult(interp, cmdPtr->argListObjPtr);
11415     } else if (cmd == INFO_VERSION) {
11416         char buf[(JIM_INTEGER_SPACE * 2) + 1];
11417         sprintf(buf, "%d.%d", 
11418                 JIM_VERSION / 100, JIM_VERSION % 100);
11419         Jim_SetResultString(interp, buf, -1);
11420     } else if (cmd == INFO_COMPLETE) {
11421         const char *s;
11422         int len;
11423
11424         if (argc != 3) {
11425             Jim_WrongNumArgs(interp, 2, argv, "script");
11426             return JIM_ERR;
11427         }
11428         s = Jim_GetString(argv[2], &len);
11429         Jim_SetResult(interp,
11430                 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11431     }
11432     return result;
11433 }
11434
11435 /* [split] */
11436 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, 
11437         Jim_Obj *const *argv)
11438 {
11439     const char *str, *splitChars, *noMatchStart;
11440     int splitLen, strLen, i;
11441     Jim_Obj *resObjPtr;
11442
11443     if (argc != 2 && argc != 3) {
11444         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11445         return JIM_ERR;
11446     }
11447     /* Init */
11448     if (argc == 2) {
11449         splitChars = " \n\t\r";
11450         splitLen = 4;
11451     } else {
11452         splitChars = Jim_GetString(argv[2], &splitLen);
11453     }
11454     str = Jim_GetString(argv[1], &strLen);
11455     if (!strLen) return JIM_OK;
11456     noMatchStart = str;
11457     resObjPtr = Jim_NewListObj(interp, NULL, 0);
11458     /* Split */
11459     if (splitLen) {
11460         while (strLen) {
11461             for (i = 0; i < splitLen; i++) {
11462                 if (*str == splitChars[i]) {
11463                     Jim_Obj *objPtr;
11464
11465                     objPtr = Jim_NewStringObj(interp, noMatchStart,
11466                             (str-noMatchStart));
11467                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
11468                     noMatchStart = str+1;
11469                     break;
11470                 }
11471             }
11472             str ++;
11473             strLen --;
11474         }
11475         Jim_ListAppendElement(interp, resObjPtr,
11476                 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11477     } else {
11478         /* This handles the special case of splitchars eq {}. This
11479          * is trivial but we want to perform object sharing as Tcl does. */
11480         Jim_Obj *objCache[256];
11481         const unsigned char *u = (unsigned char*) str;
11482         memset(objCache, 0, sizeof(objCache));
11483         for (i = 0; i < strLen; i++) {
11484             int c = u[i];
11485             
11486             if (objCache[c] == NULL)
11487                 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11488             Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11489         }
11490     }
11491     Jim_SetResult(interp, resObjPtr);
11492     return JIM_OK;
11493 }
11494
11495 /* [join] */
11496 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, 
11497         Jim_Obj *const *argv)
11498 {
11499     const char *joinStr;
11500     int joinStrLen, i, listLen;
11501     Jim_Obj *resObjPtr;
11502
11503     if (argc != 2 && argc != 3) {
11504         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11505         return JIM_ERR;
11506     }
11507     /* Init */
11508     if (argc == 2) {
11509         joinStr = " ";
11510         joinStrLen = 1;
11511     } else {
11512         joinStr = Jim_GetString(argv[2], &joinStrLen);
11513     }
11514     Jim_ListLength(interp, argv[1], &listLen);
11515     resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11516     /* Split */
11517     for (i = 0; i < listLen; i++) {
11518         Jim_Obj *objPtr;
11519
11520         Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11521         Jim_AppendObj(interp, resObjPtr, objPtr);
11522         if (i+1 != listLen) {
11523             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11524         }
11525     }
11526     Jim_SetResult(interp, resObjPtr);
11527     return JIM_OK;
11528 }
11529
11530 /* [format] */
11531 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11532         Jim_Obj *const *argv)
11533 {
11534     Jim_Obj *objPtr;
11535
11536     if (argc < 2) {
11537         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11538         return JIM_ERR;
11539     }
11540     objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11541     if (objPtr == NULL)
11542         return JIM_ERR;
11543     Jim_SetResult(interp, objPtr);
11544     return JIM_OK;
11545 }
11546
11547 /* [scan] */
11548 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11549         Jim_Obj *const *argv)
11550 {
11551     Jim_Obj *listPtr, **outVec;
11552     int outc, i, count = 0;
11553
11554     if (argc < 3) {
11555         Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11556         return JIM_ERR;
11557     } 
11558     if (argv[2]->typePtr != &scanFmtStringObjType)
11559         SetScanFmtFromAny(interp, argv[2]);
11560     if (FormatGetError(argv[2]) != 0) {
11561         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11562         return JIM_ERR;
11563     }
11564     if (argc > 3) {
11565         int maxPos = FormatGetMaxPos(argv[2]);
11566         int count = FormatGetCnvCount(argv[2]);
11567         if (maxPos > argc-3) {
11568             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11569             return JIM_ERR;
11570         } else if (count != 0 && count < argc-3) {
11571             Jim_SetResultString(interp, "variable is not assigned by any "
11572                 "conversion specifiers", -1);
11573             return JIM_ERR;
11574         } else if (count > argc-3) {
11575             Jim_SetResultString(interp, "different numbers of variable names and "
11576                 "field specifiers", -1);
11577             return JIM_ERR;
11578         }
11579     } 
11580     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11581     if (listPtr == 0)
11582         return JIM_ERR;
11583     if (argc > 3) {
11584         int len = 0;
11585         if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11586             Jim_ListLength(interp, listPtr, &len);
11587         if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11588             Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11589             return JIM_OK;
11590         }
11591         JimListGetElements(interp, listPtr, &outc, &outVec);
11592         for (i = 0; i < outc; ++i) {
11593             if (Jim_Length(outVec[i]) > 0) {
11594                 ++count;
11595                 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11596                     goto err;
11597             }
11598         }
11599         Jim_FreeNewObj(interp, listPtr);
11600         Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11601     } else {
11602         if (listPtr == (Jim_Obj*)EOF) {
11603             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11604             return JIM_OK;
11605         }
11606         Jim_SetResult(interp, listPtr);
11607     }
11608     return JIM_OK;
11609 err:
11610     Jim_FreeNewObj(interp, listPtr);
11611     return JIM_ERR;
11612 }
11613
11614 /* [error] */
11615 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11616         Jim_Obj *const *argv)
11617 {
11618     if (argc != 2) {
11619         Jim_WrongNumArgs(interp, 1, argv, "message");
11620         return JIM_ERR;
11621     }
11622     Jim_SetResult(interp, argv[1]);
11623     return JIM_ERR;
11624 }
11625
11626 /* [lrange] */
11627 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11628         Jim_Obj *const *argv)
11629 {
11630     Jim_Obj *objPtr;
11631
11632     if (argc != 4) {
11633         Jim_WrongNumArgs(interp, 1, argv, "list first last");
11634         return JIM_ERR;
11635     }
11636     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11637         return JIM_ERR;
11638     Jim_SetResult(interp, objPtr);
11639     return JIM_OK;
11640 }
11641
11642 /* [env] */
11643 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11644         Jim_Obj *const *argv)
11645 {
11646     const char *key;
11647     char *val;
11648
11649     if (argc != 2) {
11650         Jim_WrongNumArgs(interp, 1, argv, "varName");
11651         return JIM_ERR;
11652     }
11653     key = Jim_GetString(argv[1], NULL);
11654     val = getenv(key);
11655     if (val == NULL) {
11656         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11657         Jim_AppendStrings(interp, Jim_GetResult(interp),
11658                 "environment variable \"",
11659                 key, "\" does not exist", NULL);
11660         return JIM_ERR;
11661     }
11662     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11663     return JIM_OK;
11664 }
11665
11666 /* [source] */
11667 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11668         Jim_Obj *const *argv)
11669 {
11670     int retval;
11671
11672     if (argc != 2) {
11673         Jim_WrongNumArgs(interp, 1, argv, "fileName");
11674         return JIM_ERR;
11675     }
11676     retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11677     if (retval == JIM_RETURN)
11678         return JIM_OK;
11679     return retval;
11680 }
11681
11682 /* [lreverse] */
11683 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11684         Jim_Obj *const *argv)
11685 {
11686     Jim_Obj *revObjPtr, **ele;
11687     int len;
11688
11689     if (argc != 2) {
11690         Jim_WrongNumArgs(interp, 1, argv, "list");
11691         return JIM_ERR;
11692     }
11693     JimListGetElements(interp, argv[1], &len, &ele);
11694     len--;
11695     revObjPtr = Jim_NewListObj(interp, NULL, 0);
11696     while (len >= 0)
11697         ListAppendElement(revObjPtr, ele[len--]);
11698     Jim_SetResult(interp, revObjPtr);
11699     return JIM_OK;
11700 }
11701
11702 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11703 {
11704     jim_wide len;
11705
11706     if (step == 0) return -1;
11707     if (start == end) return 0;
11708     else if (step > 0 && start > end) return -1;
11709     else if (step < 0 && end > start) return -1;
11710     len = end-start;
11711     if (len < 0) len = -len; /* abs(len) */
11712     if (step < 0) step = -step; /* abs(step) */
11713     len = 1 + ((len-1)/step);
11714     /* We can truncate safely to INT_MAX, the range command
11715      * will always return an error for a such long range
11716      * because Tcl lists can't be so long. */
11717     if (len > INT_MAX) len = INT_MAX;
11718     return (int)((len < 0) ? -1 : len);
11719 }
11720
11721 /* [range] */
11722 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11723         Jim_Obj *const *argv)
11724 {
11725     jim_wide start = 0, end, step = 1;
11726     int len, i;
11727     Jim_Obj *objPtr;
11728
11729     if (argc < 2 || argc > 4) {
11730         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11731         return JIM_ERR;
11732     }
11733     if (argc == 2) {
11734         if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11735             return JIM_ERR;
11736     } else {
11737         if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11738             Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11739             return JIM_ERR;
11740         if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11741             return JIM_ERR;
11742     }
11743     if ((len = JimRangeLen(start, end, step)) == -1) {
11744         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11745         return JIM_ERR;
11746     }
11747     objPtr = Jim_NewListObj(interp, NULL, 0);
11748     for (i = 0; i < len; i++)
11749         ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11750     Jim_SetResult(interp, objPtr);
11751     return JIM_OK;
11752 }
11753
11754 /* [rand] */
11755 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11756         Jim_Obj *const *argv)
11757 {
11758     jim_wide min = 0, max, len, maxMul;
11759
11760     if (argc < 1 || argc > 3) {
11761         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11762         return JIM_ERR;
11763     }
11764     if (argc == 1) {
11765         max = JIM_WIDE_MAX;
11766     } else if (argc == 2) {
11767         if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11768             return JIM_ERR;
11769     } else if (argc == 3) {
11770         if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11771             Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11772             return JIM_ERR;
11773     }
11774     len = max-min;
11775     if (len < 0) {
11776         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11777         return JIM_ERR;
11778     }
11779     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11780     while (1) {
11781         jim_wide r;
11782
11783         JimRandomBytes(interp, &r, sizeof(jim_wide));
11784         if (r < 0 || r >= maxMul) continue;
11785         r = (len == 0) ? 0 : r%len;
11786         Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11787         return JIM_OK;
11788     }
11789 }
11790
11791 /* [package] */
11792 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc, 
11793         Jim_Obj *const *argv)
11794 {
11795     int option;
11796     const char *options[] = {
11797         "require", "provide", NULL
11798     };
11799     enum {OPT_REQUIRE, OPT_PROVIDE};
11800
11801     if (argc < 2) {
11802         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11803         return JIM_ERR;
11804     }
11805     if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11806                 JIM_ERRMSG) != JIM_OK)
11807         return JIM_ERR;
11808
11809     if (option == OPT_REQUIRE) {
11810         int exact = 0;
11811         const char *ver;
11812
11813         if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11814             exact = 1;
11815             argv++;
11816             argc--;
11817         }
11818         if (argc != 3 && argc != 4) {
11819             Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11820             return JIM_ERR;
11821         }
11822         ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11823                 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11824                 JIM_ERRMSG);
11825         if (ver == NULL)
11826             return JIM_ERR;
11827         Jim_SetResultString(interp, ver, -1);
11828     } else if (option == OPT_PROVIDE) {
11829         if (argc != 4) {
11830             Jim_WrongNumArgs(interp, 2, argv, "package version");
11831             return JIM_ERR;
11832         }
11833         return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11834                     Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11835     }
11836     return JIM_OK;
11837 }
11838
11839 static struct {
11840     const char *name;
11841     Jim_CmdProc cmdProc;
11842 } Jim_CoreCommandsTable[] = {
11843     {"set", Jim_SetCoreCommand},
11844     {"unset", Jim_UnsetCoreCommand},
11845     {"puts", Jim_PutsCoreCommand},
11846     {"+", Jim_AddCoreCommand},
11847     {"*", Jim_MulCoreCommand},
11848     {"-", Jim_SubCoreCommand},
11849     {"/", Jim_DivCoreCommand},
11850     {"incr", Jim_IncrCoreCommand},
11851     {"while", Jim_WhileCoreCommand},
11852     {"for", Jim_ForCoreCommand},
11853     {"foreach", Jim_ForeachCoreCommand},
11854     {"lmap", Jim_LmapCoreCommand},
11855     {"if", Jim_IfCoreCommand},
11856     {"switch", Jim_SwitchCoreCommand},
11857     {"list", Jim_ListCoreCommand},
11858     {"lindex", Jim_LindexCoreCommand},
11859     {"lset", Jim_LsetCoreCommand},
11860     {"llength", Jim_LlengthCoreCommand},
11861     {"lappend", Jim_LappendCoreCommand},
11862     {"linsert", Jim_LinsertCoreCommand},
11863     {"lsort", Jim_LsortCoreCommand},
11864     {"append", Jim_AppendCoreCommand},
11865     {"debug", Jim_DebugCoreCommand},
11866     {"eval", Jim_EvalCoreCommand},
11867     {"uplevel", Jim_UplevelCoreCommand},
11868     {"expr", Jim_ExprCoreCommand},
11869     {"break", Jim_BreakCoreCommand},
11870     {"continue", Jim_ContinueCoreCommand},
11871     {"proc", Jim_ProcCoreCommand},
11872     {"concat", Jim_ConcatCoreCommand},
11873     {"return", Jim_ReturnCoreCommand},
11874     {"upvar", Jim_UpvarCoreCommand},
11875     {"global", Jim_GlobalCoreCommand},
11876     {"string", Jim_StringCoreCommand},
11877     {"time", Jim_TimeCoreCommand},
11878     {"exit", Jim_ExitCoreCommand},
11879     {"catch", Jim_CatchCoreCommand},
11880     {"ref", Jim_RefCoreCommand},
11881     {"getref", Jim_GetrefCoreCommand},
11882     {"setref", Jim_SetrefCoreCommand},
11883     {"finalize", Jim_FinalizeCoreCommand},
11884     {"collect", Jim_CollectCoreCommand},
11885     {"rename", Jim_RenameCoreCommand},
11886     {"dict", Jim_DictCoreCommand},
11887     {"load", Jim_LoadCoreCommand},
11888     {"subst", Jim_SubstCoreCommand},
11889     {"info", Jim_InfoCoreCommand},
11890     {"split", Jim_SplitCoreCommand},
11891     {"join", Jim_JoinCoreCommand},
11892     {"format", Jim_FormatCoreCommand},
11893     {"scan", Jim_ScanCoreCommand},
11894     {"error", Jim_ErrorCoreCommand},
11895     {"lrange", Jim_LrangeCoreCommand},
11896     {"env", Jim_EnvCoreCommand},
11897     {"source", Jim_SourceCoreCommand},
11898     {"lreverse", Jim_LreverseCoreCommand},
11899     {"range", Jim_RangeCoreCommand},
11900     {"rand", Jim_RandCoreCommand},
11901     {"package", Jim_PackageCoreCommand},
11902     {"tailcall", Jim_TailcallCoreCommand},
11903     {NULL, NULL},
11904 };
11905
11906 /* Some Jim core command is actually a procedure written in Jim itself. */
11907 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11908 {
11909     Jim_Eval(interp, (char*)
11910 "proc lambda {arglist args} {\n"
11911 "    set name [ref {} function lambdaFinalizer]\n"
11912 "    uplevel 1 [list proc $name $arglist {expand}$args]\n"
11913 "    return $name\n"
11914 "}\n"
11915 "proc lambdaFinalizer {name val} {\n"
11916 "    rename $name {}\n"
11917 "}\n"
11918     );
11919 }
11920
11921 void Jim_RegisterCoreCommands(Jim_Interp *interp)
11922 {
11923     int i = 0;
11924
11925     while(Jim_CoreCommandsTable[i].name != NULL) {
11926         Jim_CreateCommand(interp, 
11927                 Jim_CoreCommandsTable[i].name,
11928                 Jim_CoreCommandsTable[i].cmdProc,
11929                 NULL, NULL);
11930         i++;
11931     }
11932     Jim_RegisterCoreProcedures(interp);
11933 }
11934
11935 /* -----------------------------------------------------------------------------
11936  * Interactive prompt
11937  * ---------------------------------------------------------------------------*/
11938 void Jim_PrintErrorMessage(Jim_Interp *interp)
11939 {
11940     int len, i;
11941
11942     Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL,
11943                                 interp->errorFileName, interp->errorLine);
11944     Jim_fprintf(interp,interp->cookie_stderr, "    %s" JIM_NL,
11945             Jim_GetString(interp->result, NULL));
11946     Jim_ListLength(interp, interp->stackTrace, &len);
11947     for (i = 0; i < len; i+= 3) {
11948         Jim_Obj *objPtr;
11949         const char *proc, *file, *line;
11950
11951         Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
11952         proc = Jim_GetString(objPtr, NULL);
11953         Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
11954                 JIM_NONE);
11955         file = Jim_GetString(objPtr, NULL);
11956         Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
11957                 JIM_NONE);
11958         line = Jim_GetString(objPtr, NULL);
11959                 Jim_fprintf( interp, interp->cookie_stderr,
11960                 "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
11961                 proc, file, line);
11962     }
11963 }
11964
11965 int Jim_InteractivePrompt(Jim_Interp *interp)
11966 {
11967     int retcode = JIM_OK;
11968     Jim_Obj *scriptObjPtr;
11969
11970     Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
11971            "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
11972            JIM_VERSION / 100, JIM_VERSION % 100);
11973      Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
11974     while (1) {
11975         char buf[1024];
11976         const char *result;
11977         const char *retcodestr[] = {
11978             "ok", "error", "return", "break", "continue", "eval", "exit"
11979         };
11980         int reslen;
11981
11982         if (retcode != 0) {
11983             if (retcode >= 2 && retcode <= 6)
11984                 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
11985             else
11986                 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
11987         } else
11988             Jim_fprintf( interp, interp->cookie_stdout, ". ");
11989         Jim_fflush( interp, interp->cookie_stdout);
11990         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
11991         Jim_IncrRefCount(scriptObjPtr);
11992         while(1) {
11993             const char *str;
11994             char state;
11995             int len;
11996
11997             if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
11998                 Jim_DecrRefCount(interp, scriptObjPtr);
11999                 goto out;
12000             }
12001             Jim_AppendString(interp, scriptObjPtr, buf, -1);
12002             str = Jim_GetString(scriptObjPtr, &len);
12003             if (Jim_ScriptIsComplete(str, len, &state))
12004                 break;
12005             Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12006             Jim_fflush( interp, interp->cookie_stdout);
12007         }
12008         retcode = Jim_EvalObj(interp, scriptObjPtr);
12009         Jim_DecrRefCount(interp, scriptObjPtr);
12010         result = Jim_GetString(Jim_GetResult(interp), &reslen);
12011         if (retcode == JIM_ERR) {
12012             Jim_PrintErrorMessage(interp);
12013         } else if (retcode == JIM_EXIT) {
12014             exit(Jim_GetExitCode(interp));
12015         } else {
12016             if (reslen) {
12017                                 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12018                                 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12019             }
12020         }
12021     }
12022 out:
12023     return 0;
12024 }
12025
12026 /* -----------------------------------------------------------------------------
12027  * Jim's idea of STDIO..
12028  * ---------------------------------------------------------------------------*/
12029
12030 int
12031 Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12032 {
12033         int r;
12034
12035         va_list ap;
12036         va_start(ap,fmt);
12037         r = Jim_vfprintf( interp, cookie, fmt,ap );
12038         va_end(ap);
12039         return r;
12040 }
12041         
12042
12043 int 
12044 Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12045 {
12046         if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12047                 errno = ENOTSUP;
12048                 return -1;
12049         }
12050         return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12051 }
12052
12053 size_t
12054 Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12055 {
12056         if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12057                 errno = ENOTSUP;
12058                 return 0;
12059         }
12060         return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12061 }
12062
12063 size_t
12064 Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12065 {
12066         if( (interp == NULL) || (interp->cb_fread == NULL) ){
12067                 errno = ENOTSUP;
12068                 return 0;
12069         }
12070         return (*(interp->cb_fread))( ptr, size, n, cookie);
12071 }
12072
12073 int
12074 Jim_fflush( Jim_Interp *interp, void *cookie )
12075 {
12076         if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12077                 /* pretend all is well */
12078                 return 0;
12079         }
12080         return (*(interp->cb_fflush))( cookie );
12081 }
12082
12083 char *  
12084 Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12085 {
12086         if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12087                 errno = ENOTSUP;
12088                 return NULL;
12089         }
12090         return (*(interp->cb_fgets))( s, size, cookie );
12091 }
12092
12093         
12094
12095
12096
12097
12098 /*
12099  * Local Variables: **
12100  * tab-width: 4 **
12101  * c-basic-offset: 4 **
12102  * End: **
12103  */
12104