Update jim helper files to use proper configure script support:
[openocd.git] / src / helper / jim.c
1 /* Jim - A small embeddable Tcl interpreter
2 *
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
6 * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
7 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
10 * Copyright 2008 Steve Bennett <steveb@workware.net.au>
11 *
12 * The FreeBSD license
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
26 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 *
38 * The views and conclusions contained in the software and documentation
39 * are those of the authors and should not be interpreted as representing
40 * official policies, either expressed or implied, of the Jim Tcl Project.
41 **/
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #define __JIM_CORE__
47 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
48
49 #ifdef __ECOS
50 #include <pkgconf/jimtcl.h>
51 #endif
52 #ifndef JIM_ANSIC
53 #define JIM_DYNLIB /* Dynamic library support for UNIX and WIN32 */
54 #endif /* JIM_ANSIC */
55
56 #ifndef _GNU_SOURCE
57 #define _GNU_SOURCE /* for vasprintf() */
58 #endif
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <stdarg.h>
63 #include <ctype.h>
64 #include <limits.h>
65 #include <assert.h>
66 #include <errno.h>
67 #include <time.h>
68 #if defined(WIN32)
69 /* sys/time - need is different */
70 #else
71 #include <sys/time.h> // for gettimeofday()
72 #endif
73
74 #include "replacements.h"
75
76 /* Include the platform dependent libraries for
77 * dynamic loading of libraries. */
78 #ifdef JIM_DYNLIB
79 #if defined(_WIN32) || defined(WIN32)
80 #ifndef WIN32
81 #define WIN32 1
82 #endif
83 #ifndef STRICT
84 #define STRICT
85 #endif
86 #define WIN32_LEAN_AND_MEAN
87 #include <windows.h>
88 #if _MSC_VER >= 1000
89 #pragma warning(disable:4146)
90 #endif /* _MSC_VER */
91 #else
92 #include <dlfcn.h>
93 #endif /* WIN32 */
94 #endif /* JIM_DYNLIB */
95
96 #ifdef HAVE_UNISTD_H
97 #include <unistd.h>
98 #endif
99
100 #ifdef __ECOS
101 #include <cyg/jimtcl/jim.h>
102 #else
103 #include "jim.h"
104 #endif
105
106 #ifdef HAVE_BACKTRACE
107 #include <execinfo.h>
108 #endif
109
110 /* -----------------------------------------------------------------------------
111 * Global variables
112 * ---------------------------------------------------------------------------*/
113
114 /* A shared empty string for the objects string representation.
115 * Jim_InvalidateStringRep knows about it and don't try to free. */
116 static char *JimEmptyStringRep = (char*) "";
117
118 /* -----------------------------------------------------------------------------
119 * Required prototypes of not exported functions
120 * ---------------------------------------------------------------------------*/
121 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
122 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
123 static void JimRegisterCoreApi(Jim_Interp *interp);
124
125 static Jim_HashTableType *getJimVariablesHashTableType(void);
126
127 /* -----------------------------------------------------------------------------
128 * Utility functions
129 * ---------------------------------------------------------------------------*/
130
131 static char *
132 jim_vasprintf( const char *fmt, va_list ap )
133 {
134 #ifndef HAVE_VASPRINTF
135 /* yucky way */
136 static char buf[2048];
137 vsnprintf( buf, sizeof(buf), fmt, ap );
138 /* garentee termination */
139 buf[sizeof(buf)-1] = 0;
140 #else
141 char *buf;
142 int result;
143 result = vasprintf( &buf, fmt, ap );
144 if (result < 0) exit(-1);
145 #endif
146 return buf;
147 }
148
149 static void
150 jim_vasprintf_done( void *buf )
151 {
152 #ifndef HAVE_VASPRINTF
153 (void)(buf);
154 #else
155 free(buf);
156 #endif
157 }
158
159
160 /*
161 * Convert a string to a jim_wide INTEGER.
162 * This function originates from BSD.
163 *
164 * Ignores `locale' stuff. Assumes that the upper and lower case
165 * alphabets and digits are each contiguous.
166 */
167 #ifdef HAVE_LONG_LONG_INT
168 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
169 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
170 {
171 register const char *s;
172 register unsigned jim_wide acc;
173 register unsigned char c;
174 register unsigned jim_wide qbase, cutoff;
175 register int neg, any, cutlim;
176
177 /*
178 * Skip white space and pick up leading +/- sign if any.
179 * If base is 0, allow 0x for hex and 0 for octal, else
180 * assume decimal; if base is already 16, allow 0x.
181 */
182 s = nptr;
183 do {
184 c = *s++;
185 } while (isspace(c));
186 if (c == '-') {
187 neg = 1;
188 c = *s++;
189 } else {
190 neg = 0;
191 if (c == '+')
192 c = *s++;
193 }
194 if ((base == 0 || base == 16) &&
195 c == '0' && (*s == 'x' || *s == 'X')) {
196 c = s[1];
197 s += 2;
198 base = 16;
199 }
200 if (base == 0)
201 base = c == '0' ? 8 : 10;
202
203 /*
204 * Compute the cutoff value between legal numbers and illegal
205 * numbers. That is the largest legal value, divided by the
206 * base. An input number that is greater than this value, if
207 * followed by a legal input character, is too big. One that
208 * is equal to this value may be valid or not; the limit
209 * between valid and invalid numbers is then based on the last
210 * digit. For instance, if the range for quads is
211 * [-9223372036854775808..9223372036854775807] and the input base
212 * is 10, cutoff will be set to 922337203685477580 and cutlim to
213 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
214 * accumulated a value > 922337203685477580, or equal but the
215 * next digit is > 7 (or 8), the number is too big, and we will
216 * return a range error.
217 *
218 * Set any if any `digits' consumed; make it negative to indicate
219 * overflow.
220 */
221 qbase = (unsigned)base;
222 cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
223 : LLONG_MAX;
224 cutlim = (int)(cutoff % qbase);
225 cutoff /= qbase;
226 for (acc = 0, any = 0;; c = *s++) {
227 if (!JimIsAscii(c))
228 break;
229 if (isdigit(c))
230 c -= '0';
231 else if (isalpha(c))
232 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
233 else
234 break;
235 if (c >= base)
236 break;
237 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
238 any = -1;
239 else {
240 any = 1;
241 acc *= qbase;
242 acc += c;
243 }
244 }
245 if (any < 0) {
246 acc = neg ? LLONG_MIN : LLONG_MAX;
247 errno = ERANGE;
248 } else if (neg)
249 acc = -acc;
250 if (endptr != 0)
251 *endptr = (char *)(any ? s - 1 : nptr);
252 return (acc);
253 }
254 #endif
255
256 /* Glob-style pattern matching. */
257 static int JimStringMatch(const char *pattern, int patternLen,
258 const char *string, int stringLen, int nocase)
259 {
260 while(patternLen) {
261 switch(pattern[0]) {
262 case '*':
263 while (pattern[1] == '*') {
264 pattern++;
265 patternLen--;
266 }
267 if (patternLen == 1)
268 return 1; /* match */
269 while(stringLen) {
270 if (JimStringMatch(pattern+1, patternLen-1,
271 string, stringLen, nocase))
272 return 1; /* match */
273 string++;
274 stringLen--;
275 }
276 return 0; /* no match */
277 break;
278 case '?':
279 if (stringLen == 0)
280 return 0; /* no match */
281 string++;
282 stringLen--;
283 break;
284 case '[':
285 {
286 int not, match;
287
288 pattern++;
289 patternLen--;
290 not = pattern[0] == '^';
291 if (not) {
292 pattern++;
293 patternLen--;
294 }
295 match = 0;
296 while(1) {
297 if (pattern[0] == '\\') {
298 pattern++;
299 patternLen--;
300 if (pattern[0] == string[0])
301 match = 1;
302 } else if (pattern[0] == ']') {
303 break;
304 } else if (patternLen == 0) {
305 pattern--;
306 patternLen++;
307 break;
308 } else if (pattern[1] == '-' && patternLen >= 3) {
309 int start = pattern[0];
310 int end = pattern[2];
311 int c = string[0];
312 if (start > end) {
313 int t = start;
314 start = end;
315 end = t;
316 }
317 if (nocase) {
318 start = tolower(start);
319 end = tolower(end);
320 c = tolower(c);
321 }
322 pattern += 2;
323 patternLen -= 2;
324 if (c >= start && c <= end)
325 match = 1;
326 } else {
327 if (!nocase) {
328 if (pattern[0] == string[0])
329 match = 1;
330 } else {
331 if (tolower((int)pattern[0]) == tolower((int)string[0]))
332 match = 1;
333 }
334 }
335 pattern++;
336 patternLen--;
337 }
338 if (not)
339 match = !match;
340 if (!match)
341 return 0; /* no match */
342 string++;
343 stringLen--;
344 break;
345 }
346 case '\\':
347 if (patternLen >= 2) {
348 pattern++;
349 patternLen--;
350 }
351 /* fall through */
352 default:
353 if (!nocase) {
354 if (pattern[0] != string[0])
355 return 0; /* no match */
356 } else {
357 if (tolower((int)pattern[0]) != tolower((int)string[0]))
358 return 0; /* no match */
359 }
360 string++;
361 stringLen--;
362 break;
363 }
364 pattern++;
365 patternLen--;
366 if (stringLen == 0) {
367 while(*pattern == '*') {
368 pattern++;
369 patternLen--;
370 }
371 break;
372 }
373 }
374 if (patternLen == 0 && stringLen == 0)
375 return 1;
376 return 0;
377 }
378
379 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
380 int nocase)
381 {
382 unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
383
384 if (nocase == 0) {
385 while(l1 && l2) {
386 if (*u1 != *u2)
387 return (int)*u1-*u2;
388 u1++; u2++; l1--; l2--;
389 }
390 if (!l1 && !l2) return 0;
391 return l1-l2;
392 } else {
393 while(l1 && l2) {
394 if (tolower((int)*u1) != tolower((int)*u2))
395 return tolower((int)*u1)-tolower((int)*u2);
396 u1++; u2++; l1--; l2--;
397 }
398 if (!l1 && !l2) return 0;
399 return l1-l2;
400 }
401 }
402
403 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
404 * The index of the first occurrence of s1 in s2 is returned.
405 * If s1 is not found inside s2, -1 is returned. */
406 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
407 {
408 int i;
409
410 if (!l1 || !l2 || l1 > l2) return -1;
411 if (index < 0) index = 0;
412 s2 += index;
413 for (i = index; i <= l2-l1; i++) {
414 if (memcmp(s2, s1, l1) == 0)
415 return i;
416 s2++;
417 }
418 return -1;
419 }
420
421 int Jim_WideToString(char *buf, jim_wide wideValue)
422 {
423 const char *fmt = "%" JIM_WIDE_MODIFIER;
424 return sprintf(buf, fmt, wideValue);
425 }
426
427 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
428 {
429 char *endptr;
430
431 #ifdef HAVE_LONG_LONG_INT
432 *widePtr = JimStrtoll(str, &endptr, base);
433 #else
434 *widePtr = strtol(str, &endptr, base);
435 #endif
436 if ((str[0] == '\0') || (str == endptr) )
437 return JIM_ERR;
438 if (endptr[0] != '\0') {
439 while(*endptr) {
440 if (!isspace((int)*endptr))
441 return JIM_ERR;
442 endptr++;
443 }
444 }
445 return JIM_OK;
446 }
447
448 int Jim_StringToIndex(const char *str, int *intPtr)
449 {
450 char *endptr;
451
452 *intPtr = strtol(str, &endptr, 10);
453 if ( (str[0] == '\0') || (str == endptr) )
454 return JIM_ERR;
455 if (endptr[0] != '\0') {
456 while(*endptr) {
457 if (!isspace((int)*endptr))
458 return JIM_ERR;
459 endptr++;
460 }
461 }
462 return JIM_OK;
463 }
464
465 /* The string representation of references has two features in order
466 * to make the GC faster. The first is that every reference starts
467 * with a non common character '~', in order to make the string matching
468 * fater. The second is that the reference string rep his 32 characters
469 * in length, this allows to avoid to check every object with a string
470 * repr < 32, and usually there are many of this objects. */
471
472 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
473
474 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
475 {
476 const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
477 sprintf(buf, fmt, refPtr->tag, id);
478 return JIM_REFERENCE_SPACE;
479 }
480
481 int Jim_DoubleToString(char *buf, double doubleValue)
482 {
483 char *s;
484 int len;
485
486 len = sprintf(buf, "%.17g", doubleValue);
487 s = buf;
488 while(*s) {
489 if (*s == '.') return len;
490 s++;
491 }
492 /* Add a final ".0" if it's a number. But not
493 * for NaN or InF */
494 if (isdigit((int)buf[0])
495 || ((buf[0] == '-' || buf[0] == '+')
496 && isdigit((int)buf[1]))) {
497 s[0] = '.';
498 s[1] = '0';
499 s[2] = '\0';
500 return len+2;
501 }
502 return len;
503 }
504
505 int Jim_StringToDouble(const char *str, double *doublePtr)
506 {
507 char *endptr;
508
509 *doublePtr = strtod(str, &endptr);
510 if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
511 return JIM_ERR;
512 return JIM_OK;
513 }
514
515 static jim_wide JimPowWide(jim_wide b, jim_wide e)
516 {
517 jim_wide i, res = 1;
518 if ((b==0 && e!=0) || (e<0)) return 0;
519 for(i=0; i<e; i++) {res *= b;}
520 return res;
521 }
522
523 /* -----------------------------------------------------------------------------
524 * Special functions
525 * ---------------------------------------------------------------------------*/
526
527 /* Note that 'interp' may be NULL if not available in the
528 * context of the panic. It's only useful to get the error
529 * file descriptor, it will default to stderr otherwise. */
530 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
531 {
532 va_list ap;
533
534 va_start(ap, fmt);
535 /*
536 * Send it here first.. Assuming STDIO still works
537 */
538 fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
539 vfprintf(stderr, fmt, ap);
540 fprintf(stderr, JIM_NL JIM_NL);
541 va_end(ap);
542
543 #ifdef HAVE_BACKTRACE
544 {
545 void *array[40];
546 int size, i;
547 char **strings;
548
549 size = backtrace(array, 40);
550 strings = backtrace_symbols(array, size);
551 for (i = 0; i < size; i++)
552 fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
553 fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
554 fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
555 }
556 #endif
557
558 /* This may actually crash... we do it last */
559 if( interp && interp->cookie_stderr ){
560 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
561 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
562 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL JIM_NL );
563 }
564 abort();
565 }
566
567 /* -----------------------------------------------------------------------------
568 * Memory allocation
569 * ---------------------------------------------------------------------------*/
570
571 /* Macro used for memory debugging.
572 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
573 * and similary for Jim_Realloc and Jim_Free */
574 #if 0
575 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
576 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
577 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
578 #endif
579
580 void *Jim_Alloc(int size)
581 {
582 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
583 if (size==0)
584 size=1;
585 void *p = malloc(size);
586 if (p == NULL)
587 Jim_Panic(NULL,"malloc: Out of memory");
588 return p;
589 }
590
591 void Jim_Free(void *ptr) {
592 free(ptr);
593 }
594
595 void *Jim_Realloc(void *ptr, int size)
596 {
597 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
598 if (size==0)
599 size=1;
600 void *p = realloc(ptr, size);
601 if (p == NULL)
602 Jim_Panic(NULL,"realloc: Out of memory");
603 return p;
604 }
605
606 char *Jim_StrDup(const char *s)
607 {
608 int l = strlen(s);
609 char *copy = Jim_Alloc(l+1);
610
611 memcpy(copy, s, l+1);
612 return copy;
613 }
614
615 char *Jim_StrDupLen(const char *s, int l)
616 {
617 char *copy = Jim_Alloc(l+1);
618
619 memcpy(copy, s, l+1);
620 copy[l] = 0; /* Just to be sure, original could be substring */
621 return copy;
622 }
623
624 /* -----------------------------------------------------------------------------
625 * Time related functions
626 * ---------------------------------------------------------------------------*/
627 /* Returns microseconds of CPU used since start. */
628 static jim_wide JimClock(void)
629 {
630 #if (defined WIN32) && !(defined JIM_ANSIC)
631 LARGE_INTEGER t, f;
632 QueryPerformanceFrequency(&f);
633 QueryPerformanceCounter(&t);
634 return (long)((t.QuadPart * 1000000) / f.QuadPart);
635 #else /* !WIN32 */
636 clock_t clocks = clock();
637
638 return (long)(clocks*(1000000/CLOCKS_PER_SEC));
639 #endif /* WIN32 */
640 }
641
642 /* -----------------------------------------------------------------------------
643 * Hash Tables
644 * ---------------------------------------------------------------------------*/
645
646 /* -------------------------- private prototypes ---------------------------- */
647 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
648 static unsigned int JimHashTableNextPower(unsigned int size);
649 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
650
651 /* -------------------------- hash functions -------------------------------- */
652
653 /* Thomas Wang's 32 bit Mix Function */
654 unsigned int Jim_IntHashFunction(unsigned int key)
655 {
656 key += ~(key << 15);
657 key ^= (key >> 10);
658 key += (key << 3);
659 key ^= (key >> 6);
660 key += ~(key << 11);
661 key ^= (key >> 16);
662 return key;
663 }
664
665 /* Identity hash function for integer keys */
666 unsigned int Jim_IdentityHashFunction(unsigned int key)
667 {
668 return key;
669 }
670
671 /* Generic hash function (we are using to multiply by 9 and add the byte
672 * as Tcl) */
673 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
674 {
675 unsigned int h = 0;
676 while(len--)
677 h += (h<<3)+*buf++;
678 return h;
679 }
680
681 /* ----------------------------- API implementation ------------------------- */
682 /* reset an hashtable already initialized with ht_init().
683 * NOTE: This function should only called by ht_destroy(). */
684 static void JimResetHashTable(Jim_HashTable *ht)
685 {
686 ht->table = NULL;
687 ht->size = 0;
688 ht->sizemask = 0;
689 ht->used = 0;
690 ht->collisions = 0;
691 }
692
693 /* Initialize the hash table */
694 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
695 void *privDataPtr)
696 {
697 JimResetHashTable(ht);
698 ht->type = type;
699 ht->privdata = privDataPtr;
700 return JIM_OK;
701 }
702
703 /* Resize the table to the minimal size that contains all the elements,
704 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
705 int Jim_ResizeHashTable(Jim_HashTable *ht)
706 {
707 int minimal = ht->used;
708
709 if (minimal < JIM_HT_INITIAL_SIZE)
710 minimal = JIM_HT_INITIAL_SIZE;
711 return Jim_ExpandHashTable(ht, minimal);
712 }
713
714 /* Expand or create the hashtable */
715 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
716 {
717 Jim_HashTable n; /* the new hashtable */
718 unsigned int realsize = JimHashTableNextPower(size), i;
719
720 /* the size is invalid if it is smaller than the number of
721 * elements already inside the hashtable */
722 if (ht->used >= size)
723 return JIM_ERR;
724
725 Jim_InitHashTable(&n, ht->type, ht->privdata);
726 n.size = realsize;
727 n.sizemask = realsize-1;
728 n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
729
730 /* Initialize all the pointers to NULL */
731 memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
732
733 /* Copy all the elements from the old to the new table:
734 * note that if the old hash table is empty ht->size is zero,
735 * so Jim_ExpandHashTable just creates an hash table. */
736 n.used = ht->used;
737 for (i = 0; i < ht->size && ht->used > 0; i++) {
738 Jim_HashEntry *he, *nextHe;
739
740 if (ht->table[i] == NULL) continue;
741
742 /* For each hash entry on this slot... */
743 he = ht->table[i];
744 while(he) {
745 unsigned int h;
746
747 nextHe = he->next;
748 /* Get the new element index */
749 h = Jim_HashKey(ht, he->key) & n.sizemask;
750 he->next = n.table[h];
751 n.table[h] = he;
752 ht->used--;
753 /* Pass to the next element */
754 he = nextHe;
755 }
756 }
757 assert(ht->used == 0);
758 Jim_Free(ht->table);
759
760 /* Remap the new hashtable in the old */
761 *ht = n;
762 return JIM_OK;
763 }
764
765 /* Add an element to the target hash table */
766 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
767 {
768 int index;
769 Jim_HashEntry *entry;
770
771 /* Get the index of the new element, or -1 if
772 * the element already exists. */
773 if ((index = JimInsertHashEntry(ht, key)) == -1)
774 return JIM_ERR;
775
776 /* Allocates the memory and stores key */
777 entry = Jim_Alloc(sizeof(*entry));
778 entry->next = ht->table[index];
779 ht->table[index] = entry;
780
781 /* Set the hash entry fields. */
782 Jim_SetHashKey(ht, entry, key);
783 Jim_SetHashVal(ht, entry, val);
784 ht->used++;
785 return JIM_OK;
786 }
787
788 /* Add an element, discarding the old if the key already exists */
789 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
790 {
791 Jim_HashEntry *entry;
792
793 /* Try to add the element. If the key
794 * does not exists Jim_AddHashEntry will suceed. */
795 if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
796 return JIM_OK;
797 /* It already exists, get the entry */
798 entry = Jim_FindHashEntry(ht, key);
799 /* Free the old value and set the new one */
800 Jim_FreeEntryVal(ht, entry);
801 Jim_SetHashVal(ht, entry, val);
802 return JIM_OK;
803 }
804
805 /* Search and remove an element */
806 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
807 {
808 unsigned int h;
809 Jim_HashEntry *he, *prevHe;
810
811 if (ht->size == 0)
812 return JIM_ERR;
813 h = Jim_HashKey(ht, key) & ht->sizemask;
814 he = ht->table[h];
815
816 prevHe = NULL;
817 while(he) {
818 if (Jim_CompareHashKeys(ht, key, he->key)) {
819 /* Unlink the element from the list */
820 if (prevHe)
821 prevHe->next = he->next;
822 else
823 ht->table[h] = he->next;
824 Jim_FreeEntryKey(ht, he);
825 Jim_FreeEntryVal(ht, he);
826 Jim_Free(he);
827 ht->used--;
828 return JIM_OK;
829 }
830 prevHe = he;
831 he = he->next;
832 }
833 return JIM_ERR; /* not found */
834 }
835
836 /* Destroy an entire hash table */
837 int Jim_FreeHashTable(Jim_HashTable *ht)
838 {
839 unsigned int i;
840
841 /* Free all the elements */
842 for (i = 0; i < ht->size && ht->used > 0; i++) {
843 Jim_HashEntry *he, *nextHe;
844
845 if ((he = ht->table[i]) == NULL) continue;
846 while(he) {
847 nextHe = he->next;
848 Jim_FreeEntryKey(ht, he);
849 Jim_FreeEntryVal(ht, he);
850 Jim_Free(he);
851 ht->used--;
852 he = nextHe;
853 }
854 }
855 /* Free the table and the allocated cache structure */
856 Jim_Free(ht->table);
857 /* Re-initialize the table */
858 JimResetHashTable(ht);
859 return JIM_OK; /* never fails */
860 }
861
862 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
863 {
864 Jim_HashEntry *he;
865 unsigned int h;
866
867 if (ht->size == 0) return NULL;
868 h = Jim_HashKey(ht, key) & ht->sizemask;
869 he = ht->table[h];
870 while(he) {
871 if (Jim_CompareHashKeys(ht, key, he->key))
872 return he;
873 he = he->next;
874 }
875 return NULL;
876 }
877
878 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
879 {
880 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
881
882 iter->ht = ht;
883 iter->index = -1;
884 iter->entry = NULL;
885 iter->nextEntry = NULL;
886 return iter;
887 }
888
889 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
890 {
891 while (1) {
892 if (iter->entry == NULL) {
893 iter->index++;
894 if (iter->index >=
895 (signed)iter->ht->size) break;
896 iter->entry = iter->ht->table[iter->index];
897 } else {
898 iter->entry = iter->nextEntry;
899 }
900 if (iter->entry) {
901 /* We need to save the 'next' here, the iterator user
902 * may delete the entry we are returning. */
903 iter->nextEntry = iter->entry->next;
904 return iter->entry;
905 }
906 }
907 return NULL;
908 }
909
910 /* ------------------------- private functions ------------------------------ */
911
912 /* Expand the hash table if needed */
913 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
914 {
915 /* If the hash table is empty expand it to the intial size,
916 * if the table is "full" dobule its size. */
917 if (ht->size == 0)
918 return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
919 if (ht->size == ht->used)
920 return Jim_ExpandHashTable(ht, ht->size*2);
921 return JIM_OK;
922 }
923
924 /* Our hash table capability is a power of two */
925 static unsigned int JimHashTableNextPower(unsigned int size)
926 {
927 unsigned int i = JIM_HT_INITIAL_SIZE;
928
929 if (size >= 2147483648U)
930 return 2147483648U;
931 while(1) {
932 if (i >= size)
933 return i;
934 i *= 2;
935 }
936 }
937
938 /* Returns the index of a free slot that can be populated with
939 * an hash entry for the given 'key'.
940 * If the key already exists, -1 is returned. */
941 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
942 {
943 unsigned int h;
944 Jim_HashEntry *he;
945
946 /* Expand the hashtable if needed */
947 if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
948 return -1;
949 /* Compute the key hash value */
950 h = Jim_HashKey(ht, key) & ht->sizemask;
951 /* Search if this slot does not already contain the given key */
952 he = ht->table[h];
953 while(he) {
954 if (Jim_CompareHashKeys(ht, key, he->key))
955 return -1;
956 he = he->next;
957 }
958 return h;
959 }
960
961 /* ----------------------- StringCopy Hash Table Type ------------------------*/
962
963 static unsigned int JimStringCopyHTHashFunction(const void *key)
964 {
965 return Jim_GenHashFunction(key, strlen(key));
966 }
967
968 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
969 {
970 int len = strlen(key);
971 char *copy = Jim_Alloc(len+1);
972 JIM_NOTUSED(privdata);
973
974 memcpy(copy, key, len);
975 copy[len] = '\0';
976 return copy;
977 }
978
979 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
980 {
981 int len = strlen(val);
982 char *copy = Jim_Alloc(len+1);
983 JIM_NOTUSED(privdata);
984
985 memcpy(copy, val, len);
986 copy[len] = '\0';
987 return copy;
988 }
989
990 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
991 const void *key2)
992 {
993 JIM_NOTUSED(privdata);
994
995 return strcmp(key1, key2) == 0;
996 }
997
998 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
999 {
1000 JIM_NOTUSED(privdata);
1001
1002 Jim_Free((void*)key); /* ATTENTION: const cast */
1003 }
1004
1005 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
1006 {
1007 JIM_NOTUSED(privdata);
1008
1009 Jim_Free((void*)val); /* ATTENTION: const cast */
1010 }
1011
1012 static Jim_HashTableType JimStringCopyHashTableType = {
1013 JimStringCopyHTHashFunction, /* hash function */
1014 JimStringCopyHTKeyDup, /* key dup */
1015 NULL, /* val dup */
1016 JimStringCopyHTKeyCompare, /* key compare */
1017 JimStringCopyHTKeyDestructor, /* key destructor */
1018 NULL /* val destructor */
1019 };
1020
1021 /* This is like StringCopy but does not auto-duplicate the key.
1022 * It's used for intepreter's shared strings. */
1023 static Jim_HashTableType JimSharedStringsHashTableType = {
1024 JimStringCopyHTHashFunction, /* hash function */
1025 NULL, /* key dup */
1026 NULL, /* val dup */
1027 JimStringCopyHTKeyCompare, /* key compare */
1028 JimStringCopyHTKeyDestructor, /* key destructor */
1029 NULL /* val destructor */
1030 };
1031
1032 /* This is like StringCopy but also automatically handle dynamic
1033 * allocated C strings as values. */
1034 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
1035 JimStringCopyHTHashFunction, /* hash function */
1036 JimStringCopyHTKeyDup, /* key dup */
1037 JimStringKeyValCopyHTValDup, /* val dup */
1038 JimStringCopyHTKeyCompare, /* key compare */
1039 JimStringCopyHTKeyDestructor, /* key destructor */
1040 JimStringKeyValCopyHTValDestructor, /* val destructor */
1041 };
1042
1043 typedef struct AssocDataValue {
1044 Jim_InterpDeleteProc *delProc;
1045 void *data;
1046 } AssocDataValue;
1047
1048 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
1049 {
1050 AssocDataValue *assocPtr = (AssocDataValue *)data;
1051 if (assocPtr->delProc != NULL)
1052 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
1053 Jim_Free(data);
1054 }
1055
1056 static Jim_HashTableType JimAssocDataHashTableType = {
1057 JimStringCopyHTHashFunction, /* hash function */
1058 JimStringCopyHTKeyDup, /* key dup */
1059 NULL, /* val dup */
1060 JimStringCopyHTKeyCompare, /* key compare */
1061 JimStringCopyHTKeyDestructor, /* key destructor */
1062 JimAssocDataHashTableValueDestructor /* val destructor */
1063 };
1064
1065 /* -----------------------------------------------------------------------------
1066 * Stack - This is a simple generic stack implementation. It is used for
1067 * example in the 'expr' expression compiler.
1068 * ---------------------------------------------------------------------------*/
1069 void Jim_InitStack(Jim_Stack *stack)
1070 {
1071 stack->len = 0;
1072 stack->maxlen = 0;
1073 stack->vector = NULL;
1074 }
1075
1076 void Jim_FreeStack(Jim_Stack *stack)
1077 {
1078 Jim_Free(stack->vector);
1079 }
1080
1081 int Jim_StackLen(Jim_Stack *stack)
1082 {
1083 return stack->len;
1084 }
1085
1086 void Jim_StackPush(Jim_Stack *stack, void *element) {
1087 int neededLen = stack->len+1;
1088 if (neededLen > stack->maxlen) {
1089 stack->maxlen = neededLen*2;
1090 stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1091 }
1092 stack->vector[stack->len] = element;
1093 stack->len++;
1094 }
1095
1096 void *Jim_StackPop(Jim_Stack *stack)
1097 {
1098 if (stack->len == 0) return NULL;
1099 stack->len--;
1100 return stack->vector[stack->len];
1101 }
1102
1103 void *Jim_StackPeek(Jim_Stack *stack)
1104 {
1105 if (stack->len == 0) return NULL;
1106 return stack->vector[stack->len-1];
1107 }
1108
1109 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1110 {
1111 int i;
1112
1113 for (i = 0; i < stack->len; i++)
1114 freeFunc(stack->vector[i]);
1115 }
1116
1117 /* -----------------------------------------------------------------------------
1118 * Parser
1119 * ---------------------------------------------------------------------------*/
1120
1121 /* Token types */
1122 #define JIM_TT_NONE -1 /* No token returned */
1123 #define JIM_TT_STR 0 /* simple string */
1124 #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
1125 #define JIM_TT_VAR 2 /* var substitution */
1126 #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
1127 #define JIM_TT_CMD 4 /* command substitution */
1128 #define JIM_TT_SEP 5 /* word separator */
1129 #define JIM_TT_EOL 6 /* line separator */
1130
1131 /* Additional token types needed for expressions */
1132 #define JIM_TT_SUBEXPR_START 7
1133 #define JIM_TT_SUBEXPR_END 8
1134 #define JIM_TT_EXPR_NUMBER 9
1135 #define JIM_TT_EXPR_OPERATOR 10
1136
1137 /* Parser states */
1138 #define JIM_PS_DEF 0 /* Default state */
1139 #define JIM_PS_QUOTE 1 /* Inside "" */
1140
1141 /* Parser context structure. The same context is used both to parse
1142 * Tcl scripts and lists. */
1143 struct JimParserCtx {
1144 const char *prg; /* Program text */
1145 const char *p; /* Pointer to the point of the program we are parsing */
1146 int len; /* Left length of 'prg' */
1147 int linenr; /* Current line number */
1148 const char *tstart;
1149 const char *tend; /* Returned token is at tstart-tend in 'prg'. */
1150 int tline; /* Line number of the returned token */
1151 int tt; /* Token type */
1152 int eof; /* Non zero if EOF condition is true. */
1153 int state; /* Parser state */
1154 int comment; /* Non zero if the next chars may be a comment. */
1155 };
1156
1157 #define JimParserEof(c) ((c)->eof)
1158 #define JimParserTstart(c) ((c)->tstart)
1159 #define JimParserTend(c) ((c)->tend)
1160 #define JimParserTtype(c) ((c)->tt)
1161 #define JimParserTline(c) ((c)->tline)
1162
1163 static int JimParseScript(struct JimParserCtx *pc);
1164 static int JimParseSep(struct JimParserCtx *pc);
1165 static int JimParseEol(struct JimParserCtx *pc);
1166 static int JimParseCmd(struct JimParserCtx *pc);
1167 static int JimParseVar(struct JimParserCtx *pc);
1168 static int JimParseBrace(struct JimParserCtx *pc);
1169 static int JimParseStr(struct JimParserCtx *pc);
1170 static int JimParseComment(struct JimParserCtx *pc);
1171 static char *JimParserGetToken(struct JimParserCtx *pc,
1172 int *lenPtr, int *typePtr, int *linePtr);
1173
1174 /* Initialize a parser context.
1175 * 'prg' is a pointer to the program text, linenr is the line
1176 * number of the first line contained in the program. */
1177 void JimParserInit(struct JimParserCtx *pc, const char *prg,
1178 int len, int linenr)
1179 {
1180 pc->prg = prg;
1181 pc->p = prg;
1182 pc->len = len;
1183 pc->tstart = NULL;
1184 pc->tend = NULL;
1185 pc->tline = 0;
1186 pc->tt = JIM_TT_NONE;
1187 pc->eof = 0;
1188 pc->state = JIM_PS_DEF;
1189 pc->linenr = linenr;
1190 pc->comment = 1;
1191 }
1192
1193 int JimParseScript(struct JimParserCtx *pc)
1194 {
1195 while(1) { /* the while is used to reiterate with continue if needed */
1196 if (!pc->len) {
1197 pc->tstart = pc->p;
1198 pc->tend = pc->p-1;
1199 pc->tline = pc->linenr;
1200 pc->tt = JIM_TT_EOL;
1201 pc->eof = 1;
1202 return JIM_OK;
1203 }
1204 switch(*(pc->p)) {
1205 case '\\':
1206 if (*(pc->p+1) == '\n')
1207 return JimParseSep(pc);
1208 else {
1209 pc->comment = 0;
1210 return JimParseStr(pc);
1211 }
1212 break;
1213 case ' ':
1214 case '\t':
1215 case '\r':
1216 if (pc->state == JIM_PS_DEF)
1217 return JimParseSep(pc);
1218 else {
1219 pc->comment = 0;
1220 return JimParseStr(pc);
1221 }
1222 break;
1223 case '\n':
1224 case ';':
1225 pc->comment = 1;
1226 if (pc->state == JIM_PS_DEF)
1227 return JimParseEol(pc);
1228 else
1229 return JimParseStr(pc);
1230 break;
1231 case '[':
1232 pc->comment = 0;
1233 return JimParseCmd(pc);
1234 break;
1235 case '$':
1236 pc->comment = 0;
1237 if (JimParseVar(pc) == JIM_ERR) {
1238 pc->tstart = pc->tend = pc->p++; pc->len--;
1239 pc->tline = pc->linenr;
1240 pc->tt = JIM_TT_STR;
1241 return JIM_OK;
1242 } else
1243 return JIM_OK;
1244 break;
1245 case '#':
1246 if (pc->comment) {
1247 JimParseComment(pc);
1248 continue;
1249 } else {
1250 return JimParseStr(pc);
1251 }
1252 default:
1253 pc->comment = 0;
1254 return JimParseStr(pc);
1255 break;
1256 }
1257 return JIM_OK;
1258 }
1259 }
1260
1261 int JimParseSep(struct JimParserCtx *pc)
1262 {
1263 pc->tstart = pc->p;
1264 pc->tline = pc->linenr;
1265 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1266 (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1267 if (*pc->p == '\\') {
1268 pc->p++; pc->len--;
1269 pc->linenr++;
1270 }
1271 pc->p++; pc->len--;
1272 }
1273 pc->tend = pc->p-1;
1274 pc->tt = JIM_TT_SEP;
1275 return JIM_OK;
1276 }
1277
1278 int JimParseEol(struct JimParserCtx *pc)
1279 {
1280 pc->tstart = pc->p;
1281 pc->tline = pc->linenr;
1282 while (*pc->p == ' ' || *pc->p == '\n' ||
1283 *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1284 if (*pc->p == '\n')
1285 pc->linenr++;
1286 pc->p++; pc->len--;
1287 }
1288 pc->tend = pc->p-1;
1289 pc->tt = JIM_TT_EOL;
1290 return JIM_OK;
1291 }
1292
1293 /* Todo. Don't stop if ']' appears inside {} or quoted.
1294 * Also should handle the case of puts [string length "]"] */
1295 int JimParseCmd(struct JimParserCtx *pc)
1296 {
1297 int level = 1;
1298 int blevel = 0;
1299
1300 pc->tstart = ++pc->p; pc->len--;
1301 pc->tline = pc->linenr;
1302 while (1) {
1303 if (pc->len == 0) {
1304 break;
1305 } else if (*pc->p == '[' && blevel == 0) {
1306 level++;
1307 } else if (*pc->p == ']' && blevel == 0) {
1308 level--;
1309 if (!level) break;
1310 } else if (*pc->p == '\\') {
1311 pc->p++; pc->len--;
1312 } else if (*pc->p == '{') {
1313 blevel++;
1314 } else if (*pc->p == '}') {
1315 if (blevel != 0)
1316 blevel--;
1317 } else if (*pc->p == '\n')
1318 pc->linenr++;
1319 pc->p++; pc->len--;
1320 }
1321 pc->tend = pc->p-1;
1322 pc->tt = JIM_TT_CMD;
1323 if (*pc->p == ']') {
1324 pc->p++; pc->len--;
1325 }
1326 return JIM_OK;
1327 }
1328
1329 int JimParseVar(struct JimParserCtx *pc)
1330 {
1331 int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1332
1333 pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1334 pc->tline = pc->linenr;
1335 if (*pc->p == '{') {
1336 pc->tstart = ++pc->p; pc->len--;
1337 brace = 1;
1338 }
1339 if (brace) {
1340 while (!stop) {
1341 if (*pc->p == '}' || pc->len == 0) {
1342 pc->tend = pc->p-1;
1343 stop = 1;
1344 if (pc->len == 0)
1345 break;
1346 }
1347 else if (*pc->p == '\n')
1348 pc->linenr++;
1349 pc->p++; pc->len--;
1350 }
1351 } else {
1352 /* Include leading colons */
1353 while (*pc->p == ':') {
1354 pc->p++;
1355 pc->len--;
1356 }
1357 while (!stop) {
1358 if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1359 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1360 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1361 stop = 1;
1362 else {
1363 pc->p++; pc->len--;
1364 }
1365 }
1366 /* Parse [dict get] syntax sugar. */
1367 if (*pc->p == '(') {
1368 while (*pc->p != ')' && pc->len) {
1369 pc->p++; pc->len--;
1370 if (*pc->p == '\\' && pc->len >= 2) {
1371 pc->p += 2; pc->len -= 2;
1372 }
1373 }
1374 if (*pc->p != '\0') {
1375 pc->p++; pc->len--;
1376 }
1377 ttype = JIM_TT_DICTSUGAR;
1378 }
1379 pc->tend = pc->p-1;
1380 }
1381 /* Check if we parsed just the '$' character.
1382 * That's not a variable so an error is returned
1383 * to tell the state machine to consider this '$' just
1384 * a string. */
1385 if (pc->tstart == pc->p) {
1386 pc->p--; pc->len++;
1387 return JIM_ERR;
1388 }
1389 pc->tt = ttype;
1390 return JIM_OK;
1391 }
1392
1393 int JimParseBrace(struct JimParserCtx *pc)
1394 {
1395 int level = 1;
1396
1397 pc->tstart = ++pc->p; pc->len--;
1398 pc->tline = pc->linenr;
1399 while (1) {
1400 if (*pc->p == '\\' && pc->len >= 2) {
1401 pc->p++; pc->len--;
1402 if (*pc->p == '\n')
1403 pc->linenr++;
1404 } else if (*pc->p == '{') {
1405 level++;
1406 } else if (pc->len == 0 || *pc->p == '}') {
1407 level--;
1408 if (pc->len == 0 || level == 0) {
1409 pc->tend = pc->p-1;
1410 if (pc->len != 0) {
1411 pc->p++; pc->len--;
1412 }
1413 pc->tt = JIM_TT_STR;
1414 return JIM_OK;
1415 }
1416 } else if (*pc->p == '\n') {
1417 pc->linenr++;
1418 }
1419 pc->p++; pc->len--;
1420 }
1421 return JIM_OK; /* unreached */
1422 }
1423
1424 int JimParseStr(struct JimParserCtx *pc)
1425 {
1426 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1427 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1428 if (newword && *pc->p == '{') {
1429 return JimParseBrace(pc);
1430 } else if (newword && *pc->p == '"') {
1431 pc->state = JIM_PS_QUOTE;
1432 pc->p++; pc->len--;
1433 }
1434 pc->tstart = pc->p;
1435 pc->tline = pc->linenr;
1436 while (1) {
1437 if (pc->len == 0) {
1438 pc->tend = pc->p-1;
1439 pc->tt = JIM_TT_ESC;
1440 return JIM_OK;
1441 }
1442 switch(*pc->p) {
1443 case '\\':
1444 if (pc->state == JIM_PS_DEF &&
1445 *(pc->p+1) == '\n') {
1446 pc->tend = pc->p-1;
1447 pc->tt = JIM_TT_ESC;
1448 return JIM_OK;
1449 }
1450 if (pc->len >= 2) {
1451 pc->p++; pc->len--;
1452 }
1453 break;
1454 case '$':
1455 case '[':
1456 pc->tend = pc->p-1;
1457 pc->tt = JIM_TT_ESC;
1458 return JIM_OK;
1459 case ' ':
1460 case '\t':
1461 case '\n':
1462 case '\r':
1463 case ';':
1464 if (pc->state == JIM_PS_DEF) {
1465 pc->tend = pc->p-1;
1466 pc->tt = JIM_TT_ESC;
1467 return JIM_OK;
1468 } else if (*pc->p == '\n') {
1469 pc->linenr++;
1470 }
1471 break;
1472 case '"':
1473 if (pc->state == JIM_PS_QUOTE) {
1474 pc->tend = pc->p-1;
1475 pc->tt = JIM_TT_ESC;
1476 pc->p++; pc->len--;
1477 pc->state = JIM_PS_DEF;
1478 return JIM_OK;
1479 }
1480 break;
1481 }
1482 pc->p++; pc->len--;
1483 }
1484 return JIM_OK; /* unreached */
1485 }
1486
1487 int JimParseComment(struct JimParserCtx *pc)
1488 {
1489 while (*pc->p) {
1490 if (*pc->p == '\n') {
1491 pc->linenr++;
1492 if (*(pc->p-1) != '\\') {
1493 pc->p++; pc->len--;
1494 return JIM_OK;
1495 }
1496 }
1497 pc->p++; pc->len--;
1498 }
1499 return JIM_OK;
1500 }
1501
1502 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1503 static int xdigitval(int c)
1504 {
1505 if (c >= '0' && c <= '9') return c-'0';
1506 if (c >= 'a' && c <= 'f') return c-'a'+10;
1507 if (c >= 'A' && c <= 'F') return c-'A'+10;
1508 return -1;
1509 }
1510
1511 static int odigitval(int c)
1512 {
1513 if (c >= '0' && c <= '7') return c-'0';
1514 return -1;
1515 }
1516
1517 /* Perform Tcl escape substitution of 's', storing the result
1518 * string into 'dest'. The escaped string is guaranteed to
1519 * be the same length or shorted than the source string.
1520 * Slen is the length of the string at 's', if it's -1 the string
1521 * length will be calculated by the function.
1522 *
1523 * The function returns the length of the resulting string. */
1524 static int JimEscape(char *dest, const char *s, int slen)
1525 {
1526 char *p = dest;
1527 int i, len;
1528
1529 if (slen == -1)
1530 slen = strlen(s);
1531
1532 for (i = 0; i < slen; i++) {
1533 switch(s[i]) {
1534 case '\\':
1535 switch(s[i+1]) {
1536 case 'a': *p++ = 0x7; i++; break;
1537 case 'b': *p++ = 0x8; i++; break;
1538 case 'f': *p++ = 0xc; i++; break;
1539 case 'n': *p++ = 0xa; i++; break;
1540 case 'r': *p++ = 0xd; i++; break;
1541 case 't': *p++ = 0x9; i++; break;
1542 case 'v': *p++ = 0xb; i++; break;
1543 case '\0': *p++ = '\\'; i++; break;
1544 case '\n': *p++ = ' '; i++; break;
1545 default:
1546 if (s[i+1] == 'x') {
1547 int val = 0;
1548 int c = xdigitval(s[i+2]);
1549 if (c == -1) {
1550 *p++ = 'x';
1551 i++;
1552 break;
1553 }
1554 val = c;
1555 c = xdigitval(s[i+3]);
1556 if (c == -1) {
1557 *p++ = val;
1558 i += 2;
1559 break;
1560 }
1561 val = (val*16)+c;
1562 *p++ = val;
1563 i += 3;
1564 break;
1565 } else if (s[i+1] >= '0' && s[i+1] <= '7')
1566 {
1567 int val = 0;
1568 int c = odigitval(s[i+1]);
1569 val = c;
1570 c = odigitval(s[i+2]);
1571 if (c == -1) {
1572 *p++ = val;
1573 i ++;
1574 break;
1575 }
1576 val = (val*8)+c;
1577 c = odigitval(s[i+3]);
1578 if (c == -1) {
1579 *p++ = val;
1580 i += 2;
1581 break;
1582 }
1583 val = (val*8)+c;
1584 *p++ = val;
1585 i += 3;
1586 } else {
1587 *p++ = s[i+1];
1588 i++;
1589 }
1590 break;
1591 }
1592 break;
1593 default:
1594 *p++ = s[i];
1595 break;
1596 }
1597 }
1598 len = p-dest;
1599 *p++ = '\0';
1600 return len;
1601 }
1602
1603 /* Returns a dynamically allocated copy of the current token in the
1604 * parser context. The function perform conversion of escapes if
1605 * the token is of type JIM_TT_ESC.
1606 *
1607 * Note that after the conversion, tokens that are grouped with
1608 * braces in the source code, are always recognizable from the
1609 * identical string obtained in a different way from the type.
1610 *
1611 * For exmple the string:
1612 *
1613 * {expand}$a
1614 *
1615 * will return as first token "expand", of type JIM_TT_STR
1616 *
1617 * While the string:
1618 *
1619 * expand$a
1620 *
1621 * will return as first token "expand", of type JIM_TT_ESC
1622 */
1623 char *JimParserGetToken(struct JimParserCtx *pc,
1624 int *lenPtr, int *typePtr, int *linePtr)
1625 {
1626 const char *start, *end;
1627 char *token;
1628 int len;
1629
1630 start = JimParserTstart(pc);
1631 end = JimParserTend(pc);
1632 if (start > end) {
1633 if (lenPtr) *lenPtr = 0;
1634 if (typePtr) *typePtr = JimParserTtype(pc);
1635 if (linePtr) *linePtr = JimParserTline(pc);
1636 token = Jim_Alloc(1);
1637 token[0] = '\0';
1638 return token;
1639 }
1640 len = (end-start)+1;
1641 token = Jim_Alloc(len+1);
1642 if (JimParserTtype(pc) != JIM_TT_ESC) {
1643 /* No escape conversion needed? Just copy it. */
1644 memcpy(token, start, len);
1645 token[len] = '\0';
1646 } else {
1647 /* Else convert the escape chars. */
1648 len = JimEscape(token, start, len);
1649 }
1650 if (lenPtr) *lenPtr = len;
1651 if (typePtr) *typePtr = JimParserTtype(pc);
1652 if (linePtr) *linePtr = JimParserTline(pc);
1653 return token;
1654 }
1655
1656 /* The following functin is not really part of the parsing engine of Jim,
1657 * but it somewhat related. Given an string and its length, it tries
1658 * to guess if the script is complete or there are instead " " or { }
1659 * open and not completed. This is useful for interactive shells
1660 * implementation and for [info complete].
1661 *
1662 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1663 * '{' on scripts incomplete missing one or more '}' to be balanced.
1664 * '"' on scripts incomplete missing a '"' char.
1665 *
1666 * If the script is complete, 1 is returned, otherwise 0. */
1667 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1668 {
1669 int level = 0;
1670 int state = ' ';
1671
1672 while(len) {
1673 switch (*s) {
1674 case '\\':
1675 if (len > 1)
1676 s++;
1677 break;
1678 case '"':
1679 if (state == ' ') {
1680 state = '"';
1681 } else if (state == '"') {
1682 state = ' ';
1683 }
1684 break;
1685 case '{':
1686 if (state == '{') {
1687 level++;
1688 } else if (state == ' ') {
1689 state = '{';
1690 level++;
1691 }
1692 break;
1693 case '}':
1694 if (state == '{') {
1695 level--;
1696 if (level == 0)
1697 state = ' ';
1698 }
1699 break;
1700 }
1701 s++;
1702 len--;
1703 }
1704 if (stateCharPtr)
1705 *stateCharPtr = state;
1706 return state == ' ';
1707 }
1708
1709 /* -----------------------------------------------------------------------------
1710 * Tcl Lists parsing
1711 * ---------------------------------------------------------------------------*/
1712 static int JimParseListSep(struct JimParserCtx *pc);
1713 static int JimParseListStr(struct JimParserCtx *pc);
1714
1715 int JimParseList(struct JimParserCtx *pc)
1716 {
1717 if (pc->len == 0) {
1718 pc->tstart = pc->tend = pc->p;
1719 pc->tline = pc->linenr;
1720 pc->tt = JIM_TT_EOL;
1721 pc->eof = 1;
1722 return JIM_OK;
1723 }
1724 switch(*pc->p) {
1725 case ' ':
1726 case '\n':
1727 case '\t':
1728 case '\r':
1729 if (pc->state == JIM_PS_DEF)
1730 return JimParseListSep(pc);
1731 else
1732 return JimParseListStr(pc);
1733 break;
1734 default:
1735 return JimParseListStr(pc);
1736 break;
1737 }
1738 return JIM_OK;
1739 }
1740
1741 int JimParseListSep(struct JimParserCtx *pc)
1742 {
1743 pc->tstart = pc->p;
1744 pc->tline = pc->linenr;
1745 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1746 {
1747 pc->p++; pc->len--;
1748 }
1749 pc->tend = pc->p-1;
1750 pc->tt = JIM_TT_SEP;
1751 return JIM_OK;
1752 }
1753
1754 int JimParseListStr(struct JimParserCtx *pc)
1755 {
1756 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1757 pc->tt == JIM_TT_NONE);
1758 if (newword && *pc->p == '{') {
1759 return JimParseBrace(pc);
1760 } else if (newword && *pc->p == '"') {
1761 pc->state = JIM_PS_QUOTE;
1762 pc->p++; pc->len--;
1763 }
1764 pc->tstart = pc->p;
1765 pc->tline = pc->linenr;
1766 while (1) {
1767 if (pc->len == 0) {
1768 pc->tend = pc->p-1;
1769 pc->tt = JIM_TT_ESC;
1770 return JIM_OK;
1771 }
1772 switch(*pc->p) {
1773 case '\\':
1774 pc->p++; pc->len--;
1775 break;
1776 case ' ':
1777 case '\t':
1778 case '\n':
1779 case '\r':
1780 if (pc->state == JIM_PS_DEF) {
1781 pc->tend = pc->p-1;
1782 pc->tt = JIM_TT_ESC;
1783 return JIM_OK;
1784 } else if (*pc->p == '\n') {
1785 pc->linenr++;
1786 }
1787 break;
1788 case '"':
1789 if (pc->state == JIM_PS_QUOTE) {
1790 pc->tend = pc->p-1;
1791 pc->tt = JIM_TT_ESC;
1792 pc->p++; pc->len--;
1793 pc->state = JIM_PS_DEF;
1794 return JIM_OK;
1795 }
1796 break;
1797 }
1798 pc->p++; pc->len--;
1799 }
1800 return JIM_OK; /* unreached */
1801 }
1802
1803 /* -----------------------------------------------------------------------------
1804 * Jim_Obj related functions
1805 * ---------------------------------------------------------------------------*/
1806
1807 /* Return a new initialized object. */
1808 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1809 {
1810 Jim_Obj *objPtr;
1811
1812 /* -- Check if there are objects in the free list -- */
1813 if (interp->freeList != NULL) {
1814 /* -- Unlink the object from the free list -- */
1815 objPtr = interp->freeList;
1816 interp->freeList = objPtr->nextObjPtr;
1817 } else {
1818 /* -- No ready to use objects: allocate a new one -- */
1819 objPtr = Jim_Alloc(sizeof(*objPtr));
1820 }
1821
1822 /* Object is returned with refCount of 0. Every
1823 * kind of GC implemented should take care to don't try
1824 * to scan objects with refCount == 0. */
1825 objPtr->refCount = 0;
1826 /* All the other fields are left not initialized to save time.
1827 * The caller will probably want set they to the right
1828 * value anyway. */
1829
1830 /* -- Put the object into the live list -- */
1831 objPtr->prevObjPtr = NULL;
1832 objPtr->nextObjPtr = interp->liveList;
1833 if (interp->liveList)
1834 interp->liveList->prevObjPtr = objPtr;
1835 interp->liveList = objPtr;
1836
1837 return objPtr;
1838 }
1839
1840 /* Free an object. Actually objects are never freed, but
1841 * just moved to the free objects list, where they will be
1842 * reused by Jim_NewObj(). */
1843 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1844 {
1845 /* Check if the object was already freed, panic. */
1846 if (objPtr->refCount != 0) {
1847 Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1848 objPtr->refCount);
1849 }
1850 /* Free the internal representation */
1851 Jim_FreeIntRep(interp, objPtr);
1852 /* Free the string representation */
1853 if (objPtr->bytes != NULL) {
1854 if (objPtr->bytes != JimEmptyStringRep)
1855 Jim_Free(objPtr->bytes);
1856 }
1857 /* Unlink the object from the live objects list */
1858 if (objPtr->prevObjPtr)
1859 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1860 if (objPtr->nextObjPtr)
1861 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1862 if (interp->liveList == objPtr)
1863 interp->liveList = objPtr->nextObjPtr;
1864 /* Link the object into the free objects list */
1865 objPtr->prevObjPtr = NULL;
1866 objPtr->nextObjPtr = interp->freeList;
1867 if (interp->freeList)
1868 interp->freeList->prevObjPtr = objPtr;
1869 interp->freeList = objPtr;
1870 objPtr->refCount = -1;
1871 }
1872
1873 /* Invalidate the string representation of an object. */
1874 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1875 {
1876 if (objPtr->bytes != NULL) {
1877 if (objPtr->bytes != JimEmptyStringRep)
1878 Jim_Free(objPtr->bytes);
1879 }
1880 objPtr->bytes = NULL;
1881 }
1882
1883 #define Jim_SetStringRep(o, b, l) \
1884 do { (o)->bytes = b; (o)->length = l; } while (0)
1885
1886 /* Set the initial string representation for an object.
1887 * Does not try to free an old one. */
1888 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1889 {
1890 if (length == 0) {
1891 objPtr->bytes = JimEmptyStringRep;
1892 objPtr->length = 0;
1893 } else {
1894 objPtr->bytes = Jim_Alloc(length+1);
1895 objPtr->length = length;
1896 memcpy(objPtr->bytes, bytes, length);
1897 objPtr->bytes[length] = '\0';
1898 }
1899 }
1900
1901 /* Duplicate an object. The returned object has refcount = 0. */
1902 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1903 {
1904 Jim_Obj *dupPtr;
1905
1906 dupPtr = Jim_NewObj(interp);
1907 if (objPtr->bytes == NULL) {
1908 /* Object does not have a valid string representation. */
1909 dupPtr->bytes = NULL;
1910 } else {
1911 Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1912 }
1913 if (objPtr->typePtr != NULL) {
1914 if (objPtr->typePtr->dupIntRepProc == NULL) {
1915 dupPtr->internalRep = objPtr->internalRep;
1916 } else {
1917 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1918 }
1919 dupPtr->typePtr = objPtr->typePtr;
1920 } else {
1921 dupPtr->typePtr = NULL;
1922 }
1923 return dupPtr;
1924 }
1925
1926 /* Return the string representation for objPtr. If the object
1927 * string representation is invalid, calls the method to create
1928 * a new one starting from the internal representation of the object. */
1929 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1930 {
1931 if (objPtr->bytes == NULL) {
1932 /* Invalid string repr. Generate it. */
1933 if (objPtr->typePtr->updateStringProc == NULL) {
1934 Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1935 objPtr->typePtr->name);
1936 }
1937 objPtr->typePtr->updateStringProc(objPtr);
1938 }
1939 if (lenPtr)
1940 *lenPtr = objPtr->length;
1941 return objPtr->bytes;
1942 }
1943
1944 /* Just returns the length of the object's string rep */
1945 int Jim_Length(Jim_Obj *objPtr)
1946 {
1947 int len;
1948
1949 Jim_GetString(objPtr, &len);
1950 return len;
1951 }
1952
1953 /* -----------------------------------------------------------------------------
1954 * String Object
1955 * ---------------------------------------------------------------------------*/
1956 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1957 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1958
1959 static Jim_ObjType stringObjType = {
1960 "string",
1961 NULL,
1962 DupStringInternalRep,
1963 NULL,
1964 JIM_TYPE_REFERENCES,
1965 };
1966
1967 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1968 {
1969 JIM_NOTUSED(interp);
1970
1971 /* This is a bit subtle: the only caller of this function
1972 * should be Jim_DuplicateObj(), that will copy the
1973 * string representaion. After the copy, the duplicated
1974 * object will not have more room in teh buffer than
1975 * srcPtr->length bytes. So we just set it to length. */
1976 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1977 }
1978
1979 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1980 {
1981 /* Get a fresh string representation. */
1982 (void) Jim_GetString(objPtr, NULL);
1983 /* Free any other internal representation. */
1984 Jim_FreeIntRep(interp, objPtr);
1985 /* Set it as string, i.e. just set the maxLength field. */
1986 objPtr->typePtr = &stringObjType;
1987 objPtr->internalRep.strValue.maxLength = objPtr->length;
1988 return JIM_OK;
1989 }
1990
1991 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1992 {
1993 Jim_Obj *objPtr = Jim_NewObj(interp);
1994
1995 if (len == -1)
1996 len = strlen(s);
1997 /* Alloc/Set the string rep. */
1998 if (len == 0) {
1999 objPtr->bytes = JimEmptyStringRep;
2000 objPtr->length = 0;
2001 } else {
2002 objPtr->bytes = Jim_Alloc(len+1);
2003 objPtr->length = len;
2004 memcpy(objPtr->bytes, s, len);
2005 objPtr->bytes[len] = '\0';
2006 }
2007
2008 /* No typePtr field for the vanilla string object. */
2009 objPtr->typePtr = NULL;
2010 return objPtr;
2011 }
2012
2013 /* This version does not try to duplicate the 's' pointer, but
2014 * use it directly. */
2015 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
2016 {
2017 Jim_Obj *objPtr = Jim_NewObj(interp);
2018
2019 if (len == -1)
2020 len = strlen(s);
2021 Jim_SetStringRep(objPtr, s, len);
2022 objPtr->typePtr = NULL;
2023 return objPtr;
2024 }
2025
2026 /* Low-level string append. Use it only against objects
2027 * of type "string". */
2028 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
2029 {
2030 int needlen;
2031
2032 if (len == -1)
2033 len = strlen(str);
2034 needlen = objPtr->length + len;
2035 if (objPtr->internalRep.strValue.maxLength < needlen ||
2036 objPtr->internalRep.strValue.maxLength == 0) {
2037 if (objPtr->bytes == JimEmptyStringRep) {
2038 objPtr->bytes = Jim_Alloc((needlen*2)+1);
2039 } else {
2040 objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
2041 }
2042 objPtr->internalRep.strValue.maxLength = needlen*2;
2043 }
2044 memcpy(objPtr->bytes + objPtr->length, str, len);
2045 objPtr->bytes[objPtr->length+len] = '\0';
2046 objPtr->length += len;
2047 }
2048
2049 /* Low-level wrapper to append an object. */
2050 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
2051 {
2052 int len;
2053 const char *str;
2054
2055 str = Jim_GetString(appendObjPtr, &len);
2056 StringAppendString(objPtr, str, len);
2057 }
2058
2059 /* Higher level API to append strings to objects. */
2060 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
2061 int len)
2062 {
2063 if (Jim_IsShared(objPtr))
2064 Jim_Panic(interp,"Jim_AppendString called with shared object");
2065 if (objPtr->typePtr != &stringObjType)
2066 SetStringFromAny(interp, objPtr);
2067 StringAppendString(objPtr, str, len);
2068 }
2069
2070 void Jim_AppendString_sprintf( Jim_Interp *interp, Jim_Obj *objPtr, const char *fmt, ... )
2071 {
2072 char *buf;
2073 va_list ap;
2074
2075 va_start( ap, fmt );
2076 buf = jim_vasprintf( fmt, ap );
2077 va_end(ap);
2078
2079 if( buf ){
2080 Jim_AppendString( interp, objPtr, buf, -1 );
2081 jim_vasprintf_done(buf);
2082 }
2083 }
2084
2085
2086 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
2087 Jim_Obj *appendObjPtr)
2088 {
2089 int len;
2090 const char *str;
2091
2092 str = Jim_GetString(appendObjPtr, &len);
2093 Jim_AppendString(interp, objPtr, str, len);
2094 }
2095
2096 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2097 {
2098 va_list ap;
2099
2100 if (objPtr->typePtr != &stringObjType)
2101 SetStringFromAny(interp, objPtr);
2102 va_start(ap, objPtr);
2103 while (1) {
2104 char *s = va_arg(ap, char*);
2105
2106 if (s == NULL) break;
2107 Jim_AppendString(interp, objPtr, s, -1);
2108 }
2109 va_end(ap);
2110 }
2111
2112 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2113 {
2114 const char *aStr, *bStr;
2115 int aLen, bLen, i;
2116
2117 if (aObjPtr == bObjPtr) return 1;
2118 aStr = Jim_GetString(aObjPtr, &aLen);
2119 bStr = Jim_GetString(bObjPtr, &bLen);
2120 if (aLen != bLen) return 0;
2121 if (nocase == 0)
2122 return memcmp(aStr, bStr, aLen) == 0;
2123 for (i = 0; i < aLen; i++) {
2124 if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2125 return 0;
2126 }
2127 return 1;
2128 }
2129
2130 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2131 int nocase)
2132 {
2133 const char *pattern, *string;
2134 int patternLen, stringLen;
2135
2136 pattern = Jim_GetString(patternObjPtr, &patternLen);
2137 string = Jim_GetString(objPtr, &stringLen);
2138 return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2139 }
2140
2141 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2142 Jim_Obj *secondObjPtr, int nocase)
2143 {
2144 const char *s1, *s2;
2145 int l1, l2;
2146
2147 s1 = Jim_GetString(firstObjPtr, &l1);
2148 s2 = Jim_GetString(secondObjPtr, &l2);
2149 return JimStringCompare(s1, l1, s2, l2, nocase);
2150 }
2151
2152 /* Convert a range, as returned by Jim_GetRange(), into
2153 * an absolute index into an object of the specified length.
2154 * This function may return negative values, or values
2155 * bigger or equal to the length of the list if the index
2156 * is out of range. */
2157 static int JimRelToAbsIndex(int len, int index)
2158 {
2159 if (index < 0)
2160 return len + index;
2161 return index;
2162 }
2163
2164 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2165 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2166 * for implementation of commands like [string range] and [lrange].
2167 *
2168 * The resulting range is guaranteed to address valid elements of
2169 * the structure. */
2170 static void JimRelToAbsRange(int len, int first, int last,
2171 int *firstPtr, int *lastPtr, int *rangeLenPtr)
2172 {
2173 int rangeLen;
2174
2175 if (first > last) {
2176 rangeLen = 0;
2177 } else {
2178 rangeLen = last-first+1;
2179 if (rangeLen) {
2180 if (first < 0) {
2181 rangeLen += first;
2182 first = 0;
2183 }
2184 if (last >= len) {
2185 rangeLen -= (last-(len-1));
2186 last = len-1;
2187 }
2188 }
2189 }
2190 if (rangeLen < 0) rangeLen = 0;
2191
2192 *firstPtr = first;
2193 *lastPtr = last;
2194 *rangeLenPtr = rangeLen;
2195 }
2196
2197 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2198 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2199 {
2200 int first, last;
2201 const char *str;
2202 int len, rangeLen;
2203
2204 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2205 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2206 return NULL;
2207 str = Jim_GetString(strObjPtr, &len);
2208 first = JimRelToAbsIndex(len, first);
2209 last = JimRelToAbsIndex(len, last);
2210 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2211 return Jim_NewStringObj(interp, str+first, rangeLen);
2212 }
2213
2214 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2215 {
2216 char *buf;
2217 int i;
2218 if (strObjPtr->typePtr != &stringObjType) {
2219 SetStringFromAny(interp, strObjPtr);
2220 }
2221
2222 buf = Jim_Alloc(strObjPtr->length+1);
2223
2224 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2225 for (i = 0; i < strObjPtr->length; i++)
2226 buf[i] = tolower(buf[i]);
2227 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2228 }
2229
2230 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2231 {
2232 char *buf;
2233 int i;
2234 if (strObjPtr->typePtr != &stringObjType) {
2235 SetStringFromAny(interp, strObjPtr);
2236 }
2237
2238 buf = Jim_Alloc(strObjPtr->length+1);
2239
2240 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2241 for (i = 0; i < strObjPtr->length; i++)
2242 buf[i] = toupper(buf[i]);
2243 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2244 }
2245
2246 /* This is the core of the [format] command.
2247 * TODO: Lots of things work - via a hack
2248 * However, no format item can be >= JIM_MAX_FMT
2249 */
2250 #define JIM_MAX_FMT 2048
2251 static Jim_Obj *Jim_FormatString_Inner(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2252 int objc, Jim_Obj *const *objv, char *sprintf_buf)
2253 {
2254 const char *fmt, *_fmt;
2255 int fmtLen;
2256 Jim_Obj *resObjPtr;
2257
2258
2259 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2260 _fmt = fmt;
2261 resObjPtr = Jim_NewStringObj(interp, "", 0);
2262 while (fmtLen) {
2263 const char *p = fmt;
2264 char spec[2], c;
2265 jim_wide wideValue;
2266 double doubleValue;
2267 /* we cheat and use Sprintf()! */
2268 char fmt_str[100];
2269 char *cp;
2270 int width;
2271 int ljust;
2272 int zpad;
2273 int spad;
2274 int altfm;
2275 int forceplus;
2276 int prec;
2277 int inprec;
2278 int haveprec;
2279 int accum;
2280
2281 while (*fmt != '%' && fmtLen) {
2282 fmt++; fmtLen--;
2283 }
2284 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2285 if (fmtLen == 0)
2286 break;
2287 fmt++; fmtLen--; /* skip '%' */
2288 zpad = 0;
2289 spad = 0;
2290 width = -1;
2291 ljust = 0;
2292 altfm = 0;
2293 forceplus = 0;
2294 inprec = 0;
2295 haveprec = 0;
2296 prec = -1; /* not found yet */
2297 next_fmt:
2298 if( fmtLen <= 0 ){
2299 break;
2300 }
2301 switch( *fmt ){
2302 /* terminals */
2303 case 'b': /* binary - not all printfs() do this */
2304 case 's': /* string */
2305 case 'i': /* integer */
2306 case 'd': /* decimal */
2307 case 'x': /* hex */
2308 case 'X': /* CAP hex */
2309 case 'c': /* char */
2310 case 'o': /* octal */
2311 case 'u': /* unsigned */
2312 case 'f': /* float */
2313 break;
2314
2315 /* non-terminals */
2316 case '0': /* zero pad */
2317 zpad = 1;
2318 fmt++; fmtLen--;
2319 goto next_fmt;
2320 break;
2321 case '+':
2322 forceplus = 1;
2323 fmt++; fmtLen--;
2324 goto next_fmt;
2325 break;
2326 case ' ': /* sign space */
2327 spad = 1;
2328 fmt++; fmtLen--;
2329 goto next_fmt;
2330 break;
2331 case '-':
2332 ljust = 1;
2333 fmt++; fmtLen--;
2334 goto next_fmt;
2335 break;
2336 case '#':
2337 altfm = 1;
2338 fmt++; fmtLen--;
2339 goto next_fmt;
2340
2341 case '.':
2342 inprec = 1;
2343 fmt++; fmtLen--;
2344 goto next_fmt;
2345 break;
2346 case '1':
2347 case '2':
2348 case '3':
2349 case '4':
2350 case '5':
2351 case '6':
2352 case '7':
2353 case '8':
2354 case '9':
2355 accum = 0;
2356 while( isdigit(*fmt) && (fmtLen > 0) ){
2357 accum = (accum * 10) + (*fmt - '0');
2358 fmt++; fmtLen--;
2359 }
2360 if( inprec ){
2361 haveprec = 1;
2362 prec = accum;
2363 } else {
2364 width = accum;
2365 }
2366 goto next_fmt;
2367 case '*':
2368 /* suck up the next item as an integer */
2369 fmt++; fmtLen--;
2370 objc--;
2371 if( objc <= 0 ){
2372 goto not_enough_args;
2373 }
2374 if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2375 Jim_FreeNewObj(interp, resObjPtr );
2376 return NULL;
2377 }
2378 if( inprec ){
2379 haveprec = 1;
2380 prec = wideValue;
2381 if( prec < 0 ){
2382 /* man 3 printf says */
2383 /* if prec is negative, it is zero */
2384 prec = 0;
2385 }
2386 } else {
2387 width = wideValue;
2388 if( width < 0 ){
2389 ljust = 1;
2390 width = -width;
2391 }
2392 }
2393 objv++;
2394 goto next_fmt;
2395 break;
2396 }
2397
2398
2399 if (*fmt != '%') {
2400 if (objc == 0) {
2401 not_enough_args:
2402 Jim_FreeNewObj(interp, resObjPtr);
2403 Jim_SetResultString(interp,
2404 "not enough arguments for all format specifiers", -1);
2405 return NULL;
2406 } else {
2407 objc--;
2408 }
2409 }
2410
2411 /*
2412 * Create the formatter
2413 * cause we cheat and use sprintf()
2414 */
2415 cp = fmt_str;
2416 *cp++ = '%';
2417 if( altfm ){
2418 *cp++ = '#';
2419 }
2420 if( forceplus ){
2421 *cp++ = '+';
2422 } else if( spad ){
2423 /* PLUS overrides */
2424 *cp++ = ' ';
2425 }
2426 if( ljust ){
2427 *cp++ = '-';
2428 }
2429 if( zpad ){
2430 *cp++ = '0';
2431 }
2432 if( width > 0 ){
2433 sprintf( cp, "%d", width );
2434 /* skip ahead */
2435 cp = strchr(cp,0);
2436 }
2437 /* did we find a period? */
2438 if( inprec ){
2439 /* then add it */
2440 *cp++ = '.';
2441 /* did something occur after the period? */
2442 if( haveprec ){
2443 sprintf( cp, "%d", prec );
2444 }
2445 cp = strchr(cp,0);
2446 }
2447 *cp = 0;
2448
2449 /* here we do the work */
2450 /* actually - we make sprintf() do it for us */
2451 switch(*fmt) {
2452 case 's':
2453 *cp++ = 's';
2454 *cp = 0;
2455 /* BUG: we do not handled embeded NULLs */
2456 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2457 break;
2458 case 'c':
2459 *cp++ = 'c';
2460 *cp = 0;
2461 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2462 Jim_FreeNewObj(interp, resObjPtr);
2463 return NULL;
2464 }
2465 c = (char) wideValue;
2466 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2467 break;
2468 case 'f':
2469 case 'F':
2470 case 'g':
2471 case 'G':
2472 case 'e':
2473 case 'E':
2474 *cp++ = *fmt;
2475 *cp = 0;
2476 if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2477 Jim_FreeNewObj( interp, resObjPtr );
2478 return NULL;
2479 }
2480 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2481 break;
2482 case 'b':
2483 case 'd':
2484 case 'o':
2485 case 'i':
2486 case 'u':
2487 case 'x':
2488 case 'X':
2489 /* jim widevaluse are 64bit */
2490 if( sizeof(jim_wide) == sizeof(long long) ){
2491 *cp++ = 'l';
2492 *cp++ = 'l';
2493 } else {
2494 *cp++ = 'l';
2495 }
2496 *cp++ = *fmt;
2497 *cp = 0;
2498 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2499 Jim_FreeNewObj(interp, resObjPtr);
2500 return NULL;
2501 }
2502 snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2503 break;
2504 case '%':
2505 sprintf_buf[0] = '%';
2506 sprintf_buf[1] = 0;
2507 objv--; /* undo the objv++ below */
2508 break;
2509 default:
2510 spec[0] = *fmt; spec[1] = '\0';
2511 Jim_FreeNewObj(interp, resObjPtr);
2512 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2513 Jim_AppendStrings(interp, Jim_GetResult(interp),
2514 "bad field specifier \"", spec, "\"", NULL);
2515 return NULL;
2516 }
2517 /* force terminate */
2518 #if 0
2519 printf("FMT was: %s\n", fmt_str );
2520 printf("RES was: |%s|\n", sprintf_buf );
2521 #endif
2522
2523 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2524 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2525 /* next obj */
2526 objv++;
2527 fmt++;
2528 fmtLen--;
2529 }
2530 return resObjPtr;
2531 }
2532
2533 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2534 int objc, Jim_Obj *const *objv)
2535 {
2536 char *sprintf_buf=malloc(JIM_MAX_FMT);
2537 Jim_Obj *t=Jim_FormatString_Inner(interp, fmtObjPtr, objc, objv, sprintf_buf);
2538 free(sprintf_buf);
2539 return t;
2540 }
2541
2542 /* -----------------------------------------------------------------------------
2543 * Compared String Object
2544 * ---------------------------------------------------------------------------*/
2545
2546 /* This is strange object that allows to compare a C literal string
2547 * with a Jim object in very short time if the same comparison is done
2548 * multiple times. For example every time the [if] command is executed,
2549 * Jim has to check if a given argument is "else". This comparions if
2550 * the code has no errors are true most of the times, so we can cache
2551 * inside the object the pointer of the string of the last matching
2552 * comparison. Because most C compilers perform literal sharing,
2553 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2554 * this works pretty well even if comparisons are at different places
2555 * inside the C code. */
2556
2557 static Jim_ObjType comparedStringObjType = {
2558 "compared-string",
2559 NULL,
2560 NULL,
2561 NULL,
2562 JIM_TYPE_REFERENCES,
2563 };
2564
2565 /* The only way this object is exposed to the API is via the following
2566 * function. Returns true if the string and the object string repr.
2567 * are the same, otherwise zero is returned.
2568 *
2569 * Note: this isn't binary safe, but it hardly needs to be.*/
2570 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2571 const char *str)
2572 {
2573 if (objPtr->typePtr == &comparedStringObjType &&
2574 objPtr->internalRep.ptr == str)
2575 return 1;
2576 else {
2577 const char *objStr = Jim_GetString(objPtr, NULL);
2578 if (strcmp(str, objStr) != 0) return 0;
2579 if (objPtr->typePtr != &comparedStringObjType) {
2580 Jim_FreeIntRep(interp, objPtr);
2581 objPtr->typePtr = &comparedStringObjType;
2582 }
2583 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2584 return 1;
2585 }
2586 }
2587
2588 int qsortCompareStringPointers(const void *a, const void *b)
2589 {
2590 char * const *sa = (char * const *)a;
2591 char * const *sb = (char * const *)b;
2592 return strcmp(*sa, *sb);
2593 }
2594
2595 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2596 const char * const *tablePtr, int *indexPtr, const char *name, int flags)
2597 {
2598 const char * const *entryPtr = NULL;
2599 char **tablePtrSorted;
2600 int i, count = 0;
2601
2602 *indexPtr = -1;
2603 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2604 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2605 *indexPtr = i;
2606 return JIM_OK;
2607 }
2608 count++; /* If nothing matches, this will reach the len of tablePtr */
2609 }
2610 if (flags & JIM_ERRMSG) {
2611 if (name == NULL)
2612 name = "option";
2613 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2614 Jim_AppendStrings(interp, Jim_GetResult(interp),
2615 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2616 NULL);
2617 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2618 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2619 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2620 for (i = 0; i < count; i++) {
2621 if (i+1 == count && count > 1)
2622 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2623 Jim_AppendString(interp, Jim_GetResult(interp),
2624 tablePtrSorted[i], -1);
2625 if (i+1 != count)
2626 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2627 }
2628 Jim_Free(tablePtrSorted);
2629 }
2630 return JIM_ERR;
2631 }
2632
2633 int Jim_GetNvp(Jim_Interp *interp,
2634 Jim_Obj *objPtr,
2635 const Jim_Nvp *nvp_table,
2636 const Jim_Nvp ** result)
2637 {
2638 Jim_Nvp *n;
2639 int e;
2640
2641 e = Jim_Nvp_name2value_obj( interp, nvp_table, objPtr, &n );
2642 if( e == JIM_ERR ){
2643 return e;
2644 }
2645
2646 /* Success? found? */
2647 if( n->name ){
2648 /* remove const */
2649 *result = (Jim_Nvp *)n;
2650 return JIM_OK;
2651 } else {
2652 return JIM_ERR;
2653 }
2654 }
2655
2656 /* -----------------------------------------------------------------------------
2657 * Source Object
2658 *
2659 * This object is just a string from the language point of view, but
2660 * in the internal representation it contains the filename and line number
2661 * where this given token was read. This information is used by
2662 * Jim_EvalObj() if the object passed happens to be of type "source".
2663 *
2664 * This allows to propagate the information about line numbers and file
2665 * names and give error messages with absolute line numbers.
2666 *
2667 * Note that this object uses shared strings for filenames, and the
2668 * pointer to the filename together with the line number is taken into
2669 * the space for the "inline" internal represenation of the Jim_Object,
2670 * so there is almost memory zero-overhead.
2671 *
2672 * Also the object will be converted to something else if the given
2673 * token it represents in the source file is not something to be
2674 * evaluated (not a script), and will be specialized in some other way,
2675 * so the time overhead is alzo null.
2676 * ---------------------------------------------------------------------------*/
2677
2678 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2679 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2680
2681 static Jim_ObjType sourceObjType = {
2682 "source",
2683 FreeSourceInternalRep,
2684 DupSourceInternalRep,
2685 NULL,
2686 JIM_TYPE_REFERENCES,
2687 };
2688
2689 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2690 {
2691 Jim_ReleaseSharedString(interp,
2692 objPtr->internalRep.sourceValue.fileName);
2693 }
2694
2695 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2696 {
2697 dupPtr->internalRep.sourceValue.fileName =
2698 Jim_GetSharedString(interp,
2699 srcPtr->internalRep.sourceValue.fileName);
2700 dupPtr->internalRep.sourceValue.lineNumber =
2701 dupPtr->internalRep.sourceValue.lineNumber;
2702 dupPtr->typePtr = &sourceObjType;
2703 }
2704
2705 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2706 const char *fileName, int lineNumber)
2707 {
2708 if (Jim_IsShared(objPtr))
2709 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2710 if (objPtr->typePtr != NULL)
2711 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2712 objPtr->internalRep.sourceValue.fileName =
2713 Jim_GetSharedString(interp, fileName);
2714 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2715 objPtr->typePtr = &sourceObjType;
2716 }
2717
2718 /* -----------------------------------------------------------------------------
2719 * Script Object
2720 * ---------------------------------------------------------------------------*/
2721
2722 #define JIM_CMDSTRUCT_EXPAND -1
2723
2724 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2725 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2726 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2727
2728 static Jim_ObjType scriptObjType = {
2729 "script",
2730 FreeScriptInternalRep,
2731 DupScriptInternalRep,
2732 NULL,
2733 JIM_TYPE_REFERENCES,
2734 };
2735
2736 /* The ScriptToken structure represents every token into a scriptObj.
2737 * Every token contains an associated Jim_Obj that can be specialized
2738 * by commands operating on it. */
2739 typedef struct ScriptToken {
2740 int type;
2741 Jim_Obj *objPtr;
2742 int linenr;
2743 } ScriptToken;
2744
2745 /* This is the script object internal representation. An array of
2746 * ScriptToken structures, with an associated command structure array.
2747 * The command structure is a pre-computed representation of the
2748 * command length and arguments structure as a simple liner array
2749 * of integers.
2750 *
2751 * For example the script:
2752 *
2753 * puts hello
2754 * set $i $x$y [foo]BAR
2755 *
2756 * will produce a ScriptObj with the following Tokens:
2757 *
2758 * ESC puts
2759 * SEP
2760 * ESC hello
2761 * EOL
2762 * ESC set
2763 * EOL
2764 * VAR i
2765 * SEP
2766 * VAR x
2767 * VAR y
2768 * SEP
2769 * CMD foo
2770 * ESC BAR
2771 * EOL
2772 *
2773 * This is a description of the tokens, separators, and of lines.
2774 * The command structure instead represents the number of arguments
2775 * of every command, followed by the tokens of which every argument
2776 * is composed. So for the example script, the cmdstruct array will
2777 * contain:
2778 *
2779 * 2 1 1 4 1 1 2 2
2780 *
2781 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2782 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2783 * composed of single tokens (1 1) and the last two of double tokens
2784 * (2 2).
2785 *
2786 * The precomputation of the command structure makes Jim_Eval() faster,
2787 * and simpler because there aren't dynamic lengths / allocations.
2788 *
2789 * -- {expand} handling --
2790 *
2791 * Expand is handled in a special way. When a command
2792 * contains at least an argument with the {expand} prefix,
2793 * the command structure presents a -1 before the integer
2794 * describing the number of arguments. This is used in order
2795 * to send the command exection to a different path in case
2796 * of {expand} and guarantee a fast path for the more common
2797 * case. Also, the integers describing the number of tokens
2798 * are expressed with negative sign, to allow for fast check
2799 * of what's an {expand}-prefixed argument and what not.
2800 *
2801 * For example the command:
2802 *
2803 * list {expand}{1 2}
2804 *
2805 * Will produce the following cmdstruct array:
2806 *
2807 * -1 2 1 -2
2808 *
2809 * -- the substFlags field of the structure --
2810 *
2811 * The scriptObj structure is used to represent both "script" objects
2812 * and "subst" objects. In the second case, the cmdStruct related
2813 * fields are not used at all, but there is an additional field used
2814 * that is 'substFlags': this represents the flags used to turn
2815 * the string into the intenral representation used to perform the
2816 * substitution. If this flags are not what the application requires
2817 * the scriptObj is created again. For example the script:
2818 *
2819 * subst -nocommands $string
2820 * subst -novariables $string
2821 *
2822 * Will recreate the internal representation of the $string object
2823 * two times.
2824 */
2825 typedef struct ScriptObj {
2826 int len; /* Length as number of tokens. */
2827 int commands; /* number of top-level commands in script. */
2828 ScriptToken *token; /* Tokens array. */
2829 int *cmdStruct; /* commands structure */
2830 int csLen; /* length of the cmdStruct array. */
2831 int substFlags; /* flags used for the compilation of "subst" objects */
2832 int inUse; /* Used to share a ScriptObj. Currently
2833 only used by Jim_EvalObj() as protection against
2834 shimmering of the currently evaluated object. */
2835 char *fileName;
2836 } ScriptObj;
2837
2838 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2839 {
2840 int i;
2841 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2842
2843 script->inUse--;
2844 if (script->inUse != 0) return;
2845 for (i = 0; i < script->len; i++) {
2846 if (script->token[i].objPtr != NULL)
2847 Jim_DecrRefCount(interp, script->token[i].objPtr);
2848 }
2849 Jim_Free(script->token);
2850 Jim_Free(script->cmdStruct);
2851 Jim_Free(script->fileName);
2852 Jim_Free(script);
2853 }
2854
2855 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2856 {
2857 JIM_NOTUSED(interp);
2858 JIM_NOTUSED(srcPtr);
2859
2860 /* Just returns an simple string. */
2861 dupPtr->typePtr = NULL;
2862 }
2863
2864 /* Add a new token to the internal repr of a script object */
2865 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2866 char *strtoken, int len, int type, char *filename, int linenr)
2867 {
2868 int prevtype;
2869 struct ScriptToken *token;
2870
2871 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2872 script->token[script->len-1].type;
2873 /* Skip tokens without meaning, like words separators
2874 * following a word separator or an end of command and
2875 * so on. */
2876 if (prevtype == JIM_TT_EOL) {
2877 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2878 Jim_Free(strtoken);
2879 return;
2880 }
2881 } else if (prevtype == JIM_TT_SEP) {
2882 if (type == JIM_TT_SEP) {
2883 Jim_Free(strtoken);
2884 return;
2885 } else if (type == JIM_TT_EOL) {
2886 /* If an EOL is following by a SEP, drop the previous
2887 * separator. */
2888 script->len--;
2889 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2890 }
2891 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2892 type == JIM_TT_ESC && len == 0)
2893 {
2894 /* Don't add empty tokens used in interpolation */
2895 Jim_Free(strtoken);
2896 return;
2897 }
2898 /* Make space for a new istruction */
2899 script->len++;
2900 script->token = Jim_Realloc(script->token,
2901 sizeof(ScriptToken)*script->len);
2902 /* Initialize the new token */
2903 token = script->token+(script->len-1);
2904 token->type = type;
2905 /* Every object is intially as a string, but the
2906 * internal type may be specialized during execution of the
2907 * script. */
2908 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2909 /* To add source info to SEP and EOL tokens is useless because
2910 * they will never by called as arguments of Jim_EvalObj(). */
2911 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2912 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2913 Jim_IncrRefCount(token->objPtr);
2914 token->linenr = linenr;
2915 }
2916
2917 /* Add an integer into the command structure field of the script object. */
2918 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2919 {
2920 script->csLen++;
2921 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2922 sizeof(int)*script->csLen);
2923 script->cmdStruct[script->csLen-1] = val;
2924 }
2925
2926 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2927 * of objPtr. Search nested script objects recursively. */
2928 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2929 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2930 {
2931 int i;
2932
2933 for (i = 0; i < script->len; i++) {
2934 if (script->token[i].objPtr != objPtr &&
2935 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2936 return script->token[i].objPtr;
2937 }
2938 /* Enter recursively on scripts only if the object
2939 * is not the same as the one we are searching for
2940 * shared occurrences. */
2941 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2942 script->token[i].objPtr != objPtr) {
2943 Jim_Obj *foundObjPtr;
2944
2945 ScriptObj *subScript =
2946 script->token[i].objPtr->internalRep.ptr;
2947 /* Don't recursively enter the script we are trying
2948 * to make shared to avoid circular references. */
2949 if (subScript == scriptBarrier) continue;
2950 if (subScript != script) {
2951 foundObjPtr =
2952 ScriptSearchLiteral(interp, subScript,
2953 scriptBarrier, objPtr);
2954 if (foundObjPtr != NULL)
2955 return foundObjPtr;
2956 }
2957 }
2958 }
2959 return NULL;
2960 }
2961
2962 /* Share literals of a script recursively sharing sub-scripts literals. */
2963 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2964 ScriptObj *topLevelScript)
2965 {
2966 int i, j;
2967
2968 return;
2969 /* Try to share with toplevel object. */
2970 if (topLevelScript != NULL) {
2971 for (i = 0; i < script->len; i++) {
2972 Jim_Obj *foundObjPtr;
2973 char *str = script->token[i].objPtr->bytes;
2974
2975 if (script->token[i].objPtr->refCount != 1) continue;
2976 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2977 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2978 foundObjPtr = ScriptSearchLiteral(interp,
2979 topLevelScript,
2980 script, /* barrier */
2981 script->token[i].objPtr);
2982 if (foundObjPtr != NULL) {
2983 Jim_IncrRefCount(foundObjPtr);
2984 Jim_DecrRefCount(interp,
2985 script->token[i].objPtr);
2986 script->token[i].objPtr = foundObjPtr;
2987 }
2988 }
2989 }
2990 /* Try to share locally */
2991 for (i = 0; i < script->len; i++) {
2992 char *str = script->token[i].objPtr->bytes;
2993
2994 if (script->token[i].objPtr->refCount != 1) continue;
2995 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2996 for (j = 0; j < script->len; j++) {
2997 if (script->token[i].objPtr !=
2998 script->token[j].objPtr &&
2999 Jim_StringEqObj(script->token[i].objPtr,
3000 script->token[j].objPtr, 0))
3001 {
3002 Jim_IncrRefCount(script->token[j].objPtr);
3003 Jim_DecrRefCount(interp,
3004 script->token[i].objPtr);
3005 script->token[i].objPtr =
3006 script->token[j].objPtr;
3007 }
3008 }
3009 }
3010 }
3011
3012 /* This method takes the string representation of an object
3013 * as a Tcl script, and generates the pre-parsed internal representation
3014 * of the script. */
3015 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3016 {
3017 int scriptTextLen;
3018 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
3019 struct JimParserCtx parser;
3020 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
3021 ScriptToken *token;
3022 int args, tokens, start, end, i;
3023 int initialLineNumber;
3024 int propagateSourceInfo = 0;
3025
3026 script->len = 0;
3027 script->csLen = 0;
3028 script->commands = 0;
3029 script->token = NULL;
3030 script->cmdStruct = NULL;
3031 script->inUse = 1;
3032 /* Try to get information about filename / line number */
3033 if (objPtr->typePtr == &sourceObjType) {
3034 script->fileName =
3035 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
3036 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
3037 propagateSourceInfo = 1;
3038 } else {
3039 script->fileName = Jim_StrDup("");
3040 initialLineNumber = 1;
3041 }
3042
3043 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
3044 while(!JimParserEof(&parser)) {
3045 char *token;
3046 int len, type, linenr;
3047
3048 JimParseScript(&parser);
3049 token = JimParserGetToken(&parser, &len, &type, &linenr);
3050 ScriptObjAddToken(interp, script, token, len, type,
3051 propagateSourceInfo ? script->fileName : NULL,
3052 linenr);
3053 }
3054 token = script->token;
3055
3056 /* Compute the command structure array
3057 * (see the ScriptObj struct definition for more info) */
3058 start = 0; /* Current command start token index */
3059 end = -1; /* Current command end token index */
3060 while (1) {
3061 int expand = 0; /* expand flag. set to 1 on {expand} form. */
3062 int interpolation = 0; /* set to 1 if there is at least one
3063 argument of the command obtained via
3064 interpolation of more tokens. */
3065 /* Search for the end of command, while
3066 * count the number of args. */
3067 start = ++end;
3068 if (start >= script->len) break;
3069 args = 1; /* Number of args in current command */
3070 while (token[end].type != JIM_TT_EOL) {
3071 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
3072 token[end-1].type == JIM_TT_EOL)
3073 {
3074 if (token[end].type == JIM_TT_STR &&
3075 token[end+1].type != JIM_TT_SEP &&
3076 token[end+1].type != JIM_TT_EOL &&
3077 (!strcmp(token[end].objPtr->bytes, "expand") ||
3078 !strcmp(token[end].objPtr->bytes, "*")))
3079 expand++;
3080 }
3081 if (token[end].type == JIM_TT_SEP)
3082 args++;
3083 end++;
3084 }
3085 interpolation = !((end-start+1) == args*2);
3086 /* Add the 'number of arguments' info into cmdstruct.
3087 * Negative value if there is list expansion involved. */
3088 if (expand)
3089 ScriptObjAddInt(script, -1);
3090 ScriptObjAddInt(script, args);
3091 /* Now add info about the number of tokens. */
3092 tokens = 0; /* Number of tokens in current argument. */
3093 expand = 0;
3094 for (i = start; i <= end; i++) {
3095 if (token[i].type == JIM_TT_SEP ||
3096 token[i].type == JIM_TT_EOL)
3097 {
3098 if (tokens == 1 && expand)
3099 expand = 0;
3100 ScriptObjAddInt(script,
3101 expand ? -tokens : tokens);
3102
3103 expand = 0;
3104 tokens = 0;
3105 continue;
3106 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
3107 (!strcmp(token[i].objPtr->bytes, "expand") ||
3108 !strcmp(token[i].objPtr->bytes, "*")))
3109 {
3110 expand++;
3111 }
3112 tokens++;
3113 }
3114 }
3115 /* Perform literal sharing, but only for objects that appear
3116 * to be scripts written as literals inside the source code,
3117 * and not computed at runtime. Literal sharing is a costly
3118 * operation that should be done only against objects that
3119 * are likely to require compilation only the first time, and
3120 * then are executed multiple times. */
3121 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
3122 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
3123 if (bodyObjPtr->typePtr == &scriptObjType) {
3124 ScriptObj *bodyScript =
3125 bodyObjPtr->internalRep.ptr;
3126 ScriptShareLiterals(interp, script, bodyScript);
3127 }
3128 } else if (propagateSourceInfo) {
3129 ScriptShareLiterals(interp, script, NULL);
3130 }
3131 /* Free the old internal rep and set the new one. */
3132 Jim_FreeIntRep(interp, objPtr);
3133 Jim_SetIntRepPtr(objPtr, script);
3134 objPtr->typePtr = &scriptObjType;
3135 return JIM_OK;
3136 }
3137
3138 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
3139 {
3140 if (objPtr->typePtr != &scriptObjType) {
3141 SetScriptFromAny(interp, objPtr);
3142 }
3143 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
3144 }
3145
3146 /* -----------------------------------------------------------------------------
3147 * Commands
3148 * ---------------------------------------------------------------------------*/
3149
3150 /* Commands HashTable Type.
3151 *
3152 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
3153 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
3154 {
3155 Jim_Cmd *cmdPtr = (void*) val;
3156
3157 if (cmdPtr->cmdProc == NULL) {
3158 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3159 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3160 if (cmdPtr->staticVars) {
3161 Jim_FreeHashTable(cmdPtr->staticVars);
3162 Jim_Free(cmdPtr->staticVars);
3163 }
3164 } else if (cmdPtr->delProc != NULL) {
3165 /* If it was a C coded command, call the delProc if any */
3166 cmdPtr->delProc(interp, cmdPtr->privData);
3167 }
3168 Jim_Free(val);
3169 }
3170
3171 static Jim_HashTableType JimCommandsHashTableType = {
3172 JimStringCopyHTHashFunction, /* hash function */
3173 JimStringCopyHTKeyDup, /* key dup */
3174 NULL, /* val dup */
3175 JimStringCopyHTKeyCompare, /* key compare */
3176 JimStringCopyHTKeyDestructor, /* key destructor */
3177 Jim_CommandsHT_ValDestructor /* val destructor */
3178 };
3179
3180 /* ------------------------- Commands related functions --------------------- */
3181
3182 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3183 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3184 {
3185 Jim_HashEntry *he;
3186 Jim_Cmd *cmdPtr;
3187
3188 he = Jim_FindHashEntry(&interp->commands, cmdName);
3189 if (he == NULL) { /* New command to create */
3190 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3191 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3192 } else {
3193 Jim_InterpIncrProcEpoch(interp);
3194 /* Free the arglist/body objects if it was a Tcl procedure */
3195 cmdPtr = he->val;
3196 if (cmdPtr->cmdProc == NULL) {
3197 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3198 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3199 if (cmdPtr->staticVars) {
3200 Jim_FreeHashTable(cmdPtr->staticVars);
3201 Jim_Free(cmdPtr->staticVars);
3202 }
3203 cmdPtr->staticVars = NULL;
3204 } else if (cmdPtr->delProc != NULL) {
3205 /* If it was a C coded command, call the delProc if any */
3206 cmdPtr->delProc(interp, cmdPtr->privData);
3207 }
3208 }
3209
3210 /* Store the new details for this proc */
3211 cmdPtr->delProc = delProc;
3212 cmdPtr->cmdProc = cmdProc;
3213 cmdPtr->privData = privData;
3214
3215 /* There is no need to increment the 'proc epoch' because
3216 * creation of a new procedure can never affect existing
3217 * cached commands. We don't do negative caching. */
3218 return JIM_OK;
3219 }
3220
3221 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3222 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3223 int arityMin, int arityMax)
3224 {
3225 Jim_Cmd *cmdPtr;
3226
3227 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3228 cmdPtr->cmdProc = NULL; /* Not a C coded command */
3229 cmdPtr->argListObjPtr = argListObjPtr;
3230 cmdPtr->bodyObjPtr = bodyObjPtr;
3231 Jim_IncrRefCount(argListObjPtr);
3232 Jim_IncrRefCount(bodyObjPtr);
3233 cmdPtr->arityMin = arityMin;
3234 cmdPtr->arityMax = arityMax;
3235 cmdPtr->staticVars = NULL;
3236
3237 /* Create the statics hash table. */
3238 if (staticsListObjPtr) {
3239 int len, i;
3240
3241 Jim_ListLength(interp, staticsListObjPtr, &len);
3242 if (len != 0) {
3243 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3244 Jim_InitHashTable(cmdPtr->staticVars, getJimVariablesHashTableType(),
3245 interp);
3246 for (i = 0; i < len; i++) {
3247 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3248 Jim_Var *varPtr;
3249 int subLen;
3250
3251 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3252 /* Check if it's composed of two elements. */
3253 Jim_ListLength(interp, objPtr, &subLen);
3254 if (subLen == 1 || subLen == 2) {
3255 /* Try to get the variable value from the current
3256 * environment. */
3257 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3258 if (subLen == 1) {
3259 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3260 JIM_NONE);
3261 if (initObjPtr == NULL) {
3262 Jim_SetResult(interp,
3263 Jim_NewEmptyStringObj(interp));
3264 Jim_AppendStrings(interp, Jim_GetResult(interp),
3265 "variable for initialization of static \"",
3266 Jim_GetString(nameObjPtr, NULL),
3267 "\" not found in the local context",
3268 NULL);
3269 goto err;
3270 }
3271 } else {
3272 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3273 }
3274 varPtr = Jim_Alloc(sizeof(*varPtr));
3275 varPtr->objPtr = initObjPtr;
3276 Jim_IncrRefCount(initObjPtr);
3277 varPtr->linkFramePtr = NULL;
3278 if (Jim_AddHashEntry(cmdPtr->staticVars,
3279 Jim_GetString(nameObjPtr, NULL),
3280 varPtr) != JIM_OK)
3281 {
3282 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3283 Jim_AppendStrings(interp, Jim_GetResult(interp),
3284 "static variable name \"",
3285 Jim_GetString(objPtr, NULL), "\"",
3286 " duplicated in statics list", NULL);
3287 Jim_DecrRefCount(interp, initObjPtr);
3288 Jim_Free(varPtr);
3289 goto err;
3290 }
3291 } else {
3292 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3293 Jim_AppendStrings(interp, Jim_GetResult(interp),
3294 "too many fields in static specifier \"",
3295 objPtr, "\"", NULL);
3296 goto err;
3297 }
3298 }
3299 }
3300 }
3301
3302 /* Add the new command */
3303
3304 /* it may already exist, so we try to delete the old one */
3305 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3306 /* There was an old procedure with the same name, this requires
3307 * a 'proc epoch' update. */
3308 Jim_InterpIncrProcEpoch(interp);
3309 }
3310 /* If a procedure with the same name didn't existed there is no need
3311 * to increment the 'proc epoch' because creation of a new procedure
3312 * can never affect existing cached commands. We don't do
3313 * negative caching. */
3314 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3315 return JIM_OK;
3316
3317 err:
3318 Jim_FreeHashTable(cmdPtr->staticVars);
3319 Jim_Free(cmdPtr->staticVars);
3320 Jim_DecrRefCount(interp, argListObjPtr);
3321 Jim_DecrRefCount(interp, bodyObjPtr);
3322 Jim_Free(cmdPtr);
3323 return JIM_ERR;
3324 }
3325
3326 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3327 {
3328 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3329 return JIM_ERR;
3330 Jim_InterpIncrProcEpoch(interp);
3331 return JIM_OK;
3332 }
3333
3334 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
3335 const char *newName)
3336 {
3337 Jim_Cmd *cmdPtr;
3338 Jim_HashEntry *he;
3339 Jim_Cmd *copyCmdPtr;
3340
3341 if (newName[0] == '\0') /* Delete! */
3342 return Jim_DeleteCommand(interp, oldName);
3343 /* Rename */
3344 he = Jim_FindHashEntry(&interp->commands, oldName);
3345 if (he == NULL)
3346 return JIM_ERR; /* Invalid command name */
3347 cmdPtr = he->val;
3348 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3349 *copyCmdPtr = *cmdPtr;
3350 /* In order to avoid that a procedure will get arglist/body/statics
3351 * freed by the hash table methods, fake a C-coded command
3352 * setting cmdPtr->cmdProc as not NULL */
3353 cmdPtr->cmdProc = (void*)1;
3354 /* Also make sure delProc is NULL. */
3355 cmdPtr->delProc = NULL;
3356 /* Destroy the old command, and make sure the new is freed
3357 * as well. */
3358 Jim_DeleteHashEntry(&interp->commands, oldName);
3359 Jim_DeleteHashEntry(&interp->commands, newName);
3360 /* Now the new command. We are sure it can't fail because
3361 * the target name was already freed. */
3362 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3363 /* Increment the epoch */
3364 Jim_InterpIncrProcEpoch(interp);
3365 return JIM_OK;
3366 }
3367
3368 /* -----------------------------------------------------------------------------
3369 * Command object
3370 * ---------------------------------------------------------------------------*/
3371
3372 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3373
3374 static Jim_ObjType commandObjType = {
3375 "command",
3376 NULL,
3377 NULL,
3378 NULL,
3379 JIM_TYPE_REFERENCES,
3380 };
3381
3382 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3383 {
3384 Jim_HashEntry *he;
3385 const char *cmdName;
3386
3387 /* Get the string representation */
3388 cmdName = Jim_GetString(objPtr, NULL);
3389 /* Lookup this name into the commands hash table */
3390 he = Jim_FindHashEntry(&interp->commands, cmdName);
3391 if (he == NULL)
3392 return JIM_ERR;
3393
3394 /* Free the old internal repr and set the new one. */
3395 Jim_FreeIntRep(interp, objPtr);
3396 objPtr->typePtr = &commandObjType;
3397 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3398 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3399 return JIM_OK;
3400 }
3401
3402 /* This function returns the command structure for the command name
3403 * stored in objPtr. It tries to specialize the objPtr to contain
3404 * a cached info instead to perform the lookup into the hash table
3405 * every time. The information cached may not be uptodate, in such
3406 * a case the lookup is performed and the cache updated. */
3407 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3408 {
3409 if ((objPtr->typePtr != &commandObjType ||
3410 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3411 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3412 if (flags & JIM_ERRMSG) {
3413 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3414 Jim_AppendStrings(interp, Jim_GetResult(interp),
3415 "invalid command name \"", objPtr->bytes, "\"",
3416 NULL);
3417 }
3418 return NULL;
3419 }
3420 return objPtr->internalRep.cmdValue.cmdPtr;
3421 }
3422
3423 /* -----------------------------------------------------------------------------
3424 * Variables
3425 * ---------------------------------------------------------------------------*/
3426
3427 /* Variables HashTable Type.
3428 *
3429 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3430 static void JimVariablesHTValDestructor(void *interp, void *val)
3431 {
3432 Jim_Var *varPtr = (void*) val;
3433
3434 Jim_DecrRefCount(interp, varPtr->objPtr);
3435 Jim_Free(val);
3436 }
3437
3438 static Jim_HashTableType JimVariablesHashTableType = {
3439 JimStringCopyHTHashFunction, /* hash function */
3440 JimStringCopyHTKeyDup, /* key dup */
3441 NULL, /* val dup */
3442 JimStringCopyHTKeyCompare, /* key compare */
3443 JimStringCopyHTKeyDestructor, /* key destructor */
3444 JimVariablesHTValDestructor /* val destructor */
3445 };
3446
3447 static Jim_HashTableType *getJimVariablesHashTableType(void)
3448 {
3449 return &JimVariablesHashTableType;
3450 }
3451
3452 /* -----------------------------------------------------------------------------
3453 * Variable object
3454 * ---------------------------------------------------------------------------*/
3455
3456 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3457
3458 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3459
3460 static Jim_ObjType variableObjType = {
3461 "variable",
3462 NULL,
3463 NULL,
3464 NULL,
3465 JIM_TYPE_REFERENCES,
3466 };
3467
3468 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3469 * is in the form "varname(key)". */
3470 static int Jim_NameIsDictSugar(const char *str, int len)
3471 {
3472 if (len == -1)
3473 len = strlen(str);
3474 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3475 return 1;
3476 return 0;
3477 }
3478
3479 /* This method should be called only by the variable API.
3480 * It returns JIM_OK on success (variable already exists),
3481 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3482 * a variable name, but syntax glue for [dict] i.e. the last
3483 * character is ')' */
3484 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3485 {
3486 Jim_HashEntry *he;
3487 const char *varName;
3488 int len;
3489
3490 /* Check if the object is already an uptodate variable */
3491 if (objPtr->typePtr == &variableObjType &&
3492 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3493 return JIM_OK; /* nothing to do */
3494 /* Get the string representation */
3495 varName = Jim_GetString(objPtr, &len);
3496 /* Make sure it's not syntax glue to get/set dict. */
3497 if (Jim_NameIsDictSugar(varName, len))
3498 return JIM_DICT_SUGAR;
3499 if (varName[0] == ':' && varName[1] == ':') {
3500 he = Jim_FindHashEntry(&interp->topFramePtr->vars, varName + 2);
3501 if (he == NULL) {
3502 return JIM_ERR;
3503 }
3504 }
3505 else {
3506 /* Lookup this name into the variables hash table */
3507 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3508 if (he == NULL) {
3509 /* Try with static vars. */
3510 if (interp->framePtr->staticVars == NULL)
3511 return JIM_ERR;
3512 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3513 return JIM_ERR;
3514 }
3515 }
3516 /* Free the old internal repr and set the new one. */
3517 Jim_FreeIntRep(interp, objPtr);
3518 objPtr->typePtr = &variableObjType;
3519 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3520 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3521 return JIM_OK;
3522 }
3523
3524 /* -------------------- Variables related functions ------------------------- */
3525 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3526 Jim_Obj *valObjPtr);
3527 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3528
3529 /* For now that's dummy. Variables lookup should be optimized
3530 * in many ways, with caching of lookups, and possibly with
3531 * a table of pre-allocated vars in every CallFrame for local vars.
3532 * All the caching should also have an 'epoch' mechanism similar
3533 * to the one used by Tcl for procedures lookup caching. */
3534
3535 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3536 {
3537 const char *name;
3538 Jim_Var *var;
3539 int err;
3540
3541 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3542 /* Check for [dict] syntax sugar. */
3543 if (err == JIM_DICT_SUGAR)
3544 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3545 /* New variable to create */
3546 name = Jim_GetString(nameObjPtr, NULL);
3547
3548 var = Jim_Alloc(sizeof(*var));
3549 var->objPtr = valObjPtr;
3550 Jim_IncrRefCount(valObjPtr);
3551 var->linkFramePtr = NULL;
3552 /* Insert the new variable */
3553 if (name[0] == ':' && name[1] == ':') {
3554 /* Into to the top evel frame */
3555 Jim_AddHashEntry(&interp->topFramePtr->vars, name + 2, var);
3556 }
3557 else {
3558 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3559 }
3560 /* Make the object int rep a variable */
3561 Jim_FreeIntRep(interp, nameObjPtr);
3562 nameObjPtr->typePtr = &variableObjType;
3563 nameObjPtr->internalRep.varValue.callFrameId =
3564 interp->framePtr->id;
3565 nameObjPtr->internalRep.varValue.varPtr = var;
3566 } else {
3567 var = nameObjPtr->internalRep.varValue.varPtr;
3568 if (var->linkFramePtr == NULL) {
3569 Jim_IncrRefCount(valObjPtr);
3570 Jim_DecrRefCount(interp, var->objPtr);
3571 var->objPtr = valObjPtr;
3572 } else { /* Else handle the link */
3573 Jim_CallFrame *savedCallFrame;
3574
3575 savedCallFrame = interp->framePtr;
3576 interp->framePtr = var->linkFramePtr;
3577 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3578 interp->framePtr = savedCallFrame;
3579 if (err != JIM_OK)
3580 return err;
3581 }
3582 }
3583 return JIM_OK;
3584 }
3585
3586 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3587 {
3588 Jim_Obj *nameObjPtr;
3589 int result;
3590
3591 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3592 Jim_IncrRefCount(nameObjPtr);
3593 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3594 Jim_DecrRefCount(interp, nameObjPtr);
3595 return result;
3596 }
3597
3598 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3599 {
3600 Jim_CallFrame *savedFramePtr;
3601 int result;
3602
3603 savedFramePtr = interp->framePtr;
3604 interp->framePtr = interp->topFramePtr;
3605 result = Jim_SetVariableStr(interp, name, objPtr);
3606 interp->framePtr = savedFramePtr;
3607 return result;
3608 }
3609
3610 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3611 {
3612 Jim_Obj *nameObjPtr, *valObjPtr;
3613 int result;
3614
3615 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3616 valObjPtr = Jim_NewStringObj(interp, val, -1);
3617 Jim_IncrRefCount(nameObjPtr);
3618 Jim_IncrRefCount(valObjPtr);
3619 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3620 Jim_DecrRefCount(interp, nameObjPtr);
3621 Jim_DecrRefCount(interp, valObjPtr);
3622 return result;
3623 }
3624
3625 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3626 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3627 {
3628 const char *varName;
3629 int len;
3630
3631 /* Check for cycles. */
3632 if (interp->framePtr == targetCallFrame) {
3633 Jim_Obj *objPtr = targetNameObjPtr;
3634 Jim_Var *varPtr;
3635 /* Cycles are only possible with 'uplevel 0' */
3636 while(1) {
3637 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3638 Jim_SetResultString(interp,
3639 "can't upvar from variable to itself", -1);
3640 return JIM_ERR;
3641 }
3642 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3643 break;
3644 varPtr = objPtr->internalRep.varValue.varPtr;
3645 if (varPtr->linkFramePtr != targetCallFrame) break;
3646 objPtr = varPtr->objPtr;
3647 }
3648 }
3649 varName = Jim_GetString(nameObjPtr, &len);
3650 if (Jim_NameIsDictSugar(varName, len)) {
3651 Jim_SetResultString(interp,
3652 "Dict key syntax invalid as link source", -1);
3653 return JIM_ERR;
3654 }
3655 /* Perform the binding */
3656 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3657 /* We are now sure 'nameObjPtr' type is variableObjType */
3658 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3659 return JIM_OK;
3660 }
3661
3662 /* Return the Jim_Obj pointer associated with a variable name,
3663 * or NULL if the variable was not found in the current context.
3664 * The same optimization discussed in the comment to the
3665 * 'SetVariable' function should apply here. */
3666 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3667 {
3668 int err;
3669
3670 /* All the rest is handled here */
3671 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3672 /* Check for [dict] syntax sugar. */
3673 if (err == JIM_DICT_SUGAR)
3674 return JimDictSugarGet(interp, nameObjPtr);
3675 if (flags & JIM_ERRMSG) {
3676 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3677 Jim_AppendStrings(interp, Jim_GetResult(interp),
3678 "can't read \"", nameObjPtr->bytes,
3679 "\": no such variable", NULL);
3680 }
3681 return NULL;
3682 } else {
3683 Jim_Var *varPtr;
3684 Jim_Obj *objPtr;
3685 Jim_CallFrame *savedCallFrame;
3686
3687 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3688 if (varPtr->linkFramePtr == NULL)
3689 return varPtr->objPtr;
3690 /* The variable is a link? Resolve it. */
3691 savedCallFrame = interp->framePtr;
3692 interp->framePtr = varPtr->linkFramePtr;
3693 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3694 if (objPtr == NULL && flags & JIM_ERRMSG) {
3695 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3696 Jim_AppendStrings(interp, Jim_GetResult(interp),
3697 "can't read \"", nameObjPtr->bytes,
3698 "\": no such variable", NULL);
3699 }
3700 interp->framePtr = savedCallFrame;
3701 return objPtr;
3702 }
3703 }
3704
3705 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3706 int flags)
3707 {
3708 Jim_CallFrame *savedFramePtr;
3709 Jim_Obj *objPtr;
3710
3711 savedFramePtr = interp->framePtr;
3712 interp->framePtr = interp->topFramePtr;
3713 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3714 interp->framePtr = savedFramePtr;
3715
3716 return objPtr;
3717 }
3718
3719 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3720 {
3721 Jim_Obj *nameObjPtr, *varObjPtr;
3722
3723 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3724 Jim_IncrRefCount(nameObjPtr);
3725 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3726 Jim_DecrRefCount(interp, nameObjPtr);
3727 return varObjPtr;
3728 }
3729
3730 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3731 int flags)
3732 {
3733 Jim_CallFrame *savedFramePtr;
3734 Jim_Obj *objPtr;
3735
3736 savedFramePtr = interp->framePtr;
3737 interp->framePtr = interp->topFramePtr;
3738 objPtr = Jim_GetVariableStr(interp, name, flags);
3739 interp->framePtr = savedFramePtr;
3740
3741 return objPtr;
3742 }
3743
3744 /* Unset a variable.
3745 * Note: On success unset invalidates all the variable objects created
3746 * in the current call frame incrementing. */
3747 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3748 {
3749 const char *name;
3750 Jim_Var *varPtr;
3751 int err;
3752
3753 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3754 /* Check for [dict] syntax sugar. */
3755 if (err == JIM_DICT_SUGAR)
3756 return JimDictSugarSet(interp, nameObjPtr, NULL);
3757 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3758 Jim_AppendStrings(interp, Jim_GetResult(interp),
3759 "can't unset \"", nameObjPtr->bytes,
3760 "\": no such variable", NULL);
3761 return JIM_ERR; /* var not found */
3762 }
3763 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3764 /* If it's a link call UnsetVariable recursively */
3765 if (varPtr->linkFramePtr) {
3766 int retval;
3767
3768 Jim_CallFrame *savedCallFrame;
3769
3770 savedCallFrame = interp->framePtr;
3771 interp->framePtr = varPtr->linkFramePtr;
3772 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3773 interp->framePtr = savedCallFrame;
3774 if (retval != JIM_OK && flags & JIM_ERRMSG) {
3775 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3776 Jim_AppendStrings(interp, Jim_GetResult(interp),
3777 "can't unset \"", nameObjPtr->bytes,
3778 "\": no such variable", NULL);
3779 }
3780 return retval;
3781 } else {
3782 name = Jim_GetString(nameObjPtr, NULL);
3783 if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3784 != JIM_OK) return JIM_ERR;
3785 /* Change the callframe id, invalidating var lookup caching */
3786 JimChangeCallFrameId(interp, interp->framePtr);
3787 return JIM_OK;
3788 }
3789 }
3790
3791 /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
3792
3793 /* Given a variable name for [dict] operation syntax sugar,
3794 * this function returns two objects, the first with the name
3795 * of the variable to set, and the second with the rispective key.
3796 * For example "foo(bar)" will return objects with string repr. of
3797 * "foo" and "bar".
3798 *
3799 * The returned objects have refcount = 1. The function can't fail. */
3800 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3801 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3802 {
3803 const char *str, *p;
3804 char *t;
3805 int len, keyLen, nameLen;
3806 Jim_Obj *varObjPtr, *keyObjPtr;
3807
3808 str = Jim_GetString(objPtr, &len);
3809 p = strchr(str, '(');
3810 p++;
3811 keyLen = len-((p-str)+1);
3812 nameLen = (p-str)-1;
3813 /* Create the objects with the variable name and key. */
3814 t = Jim_Alloc(nameLen+1);
3815 memcpy(t, str, nameLen);
3816 t[nameLen] = '\0';
3817 varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3818
3819 t = Jim_Alloc(keyLen+1);
3820 memcpy(t, p, keyLen);
3821 t[keyLen] = '\0';
3822 keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3823
3824 Jim_IncrRefCount(varObjPtr);
3825 Jim_IncrRefCount(keyObjPtr);
3826 *varPtrPtr = varObjPtr;
3827 *keyPtrPtr = keyObjPtr;
3828 }
3829
3830 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3831 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3832 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3833 Jim_Obj *valObjPtr)
3834 {
3835 Jim_Obj *varObjPtr, *keyObjPtr;
3836 int err = JIM_OK;
3837
3838 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3839 err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3840 valObjPtr);
3841 Jim_DecrRefCount(interp, varObjPtr);
3842 Jim_DecrRefCount(interp, keyObjPtr);
3843 return err;
3844 }
3845
3846 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3847 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3848 {
3849 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3850
3851 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3852 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3853 if (!dictObjPtr) {
3854 resObjPtr = NULL;
3855 goto err;
3856 }
3857 if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3858 != JIM_OK) {
3859 resObjPtr = NULL;
3860 }
3861 err:
3862 Jim_DecrRefCount(interp, varObjPtr);
3863 Jim_DecrRefCount(interp, keyObjPtr);
3864 return resObjPtr;
3865 }
3866
3867 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3868
3869 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3870 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3871 Jim_Obj *dupPtr);
3872
3873 static Jim_ObjType dictSubstObjType = {
3874 "dict-substitution",
3875 FreeDictSubstInternalRep,
3876 DupDictSubstInternalRep,
3877 NULL,
3878 JIM_TYPE_NONE,
3879 };
3880
3881 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3882 {
3883 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3884 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3885 }
3886
3887 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3888 Jim_Obj *dupPtr)
3889 {
3890 JIM_NOTUSED(interp);
3891
3892 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3893 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3894 dupPtr->internalRep.dictSubstValue.indexObjPtr =
3895 srcPtr->internalRep.dictSubstValue.indexObjPtr;
3896 dupPtr->typePtr = &dictSubstObjType;
3897 }
3898
3899 /* This function is used to expand [dict get] sugar in the form
3900 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3901 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3902 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3903 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3904 * the [dict]ionary contained in variable VARNAME. */
3905 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3906 {
3907 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3908 Jim_Obj *substKeyObjPtr = NULL;
3909
3910 if (objPtr->typePtr != &dictSubstObjType) {
3911 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3912 Jim_FreeIntRep(interp, objPtr);
3913 objPtr->typePtr = &dictSubstObjType;
3914 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3915 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3916 }
3917 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3918 &substKeyObjPtr, JIM_NONE)
3919 != JIM_OK) {
3920 substKeyObjPtr = NULL;
3921 goto err;
3922 }
3923 Jim_IncrRefCount(substKeyObjPtr);
3924 dictObjPtr = Jim_GetVariable(interp,
3925 objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3926 if (!dictObjPtr) {
3927 resObjPtr = NULL;
3928 goto err;
3929 }
3930 if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3931 != JIM_OK) {
3932 resObjPtr = NULL;
3933 goto err;
3934 }
3935 err:
3936 if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3937 return resObjPtr;
3938 }
3939
3940 /* -----------------------------------------------------------------------------
3941 * CallFrame
3942 * ---------------------------------------------------------------------------*/
3943
3944 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3945 {
3946 Jim_CallFrame *cf;
3947 if (interp->freeFramesList) {
3948 cf = interp->freeFramesList;
3949 interp->freeFramesList = cf->nextFramePtr;
3950 } else {
3951 cf = Jim_Alloc(sizeof(*cf));
3952 cf->vars.table = NULL;
3953 }
3954
3955 cf->id = interp->callFrameEpoch++;
3956 cf->parentCallFrame = NULL;
3957 cf->argv = NULL;
3958 cf->argc = 0;
3959 cf->procArgsObjPtr = NULL;
3960 cf->procBodyObjPtr = NULL;
3961 cf->nextFramePtr = NULL;
3962 cf->staticVars = NULL;
3963 if (cf->vars.table == NULL)
3964 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3965 return cf;
3966 }
3967
3968 /* Used to invalidate every caching related to callframe stability. */
3969 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3970 {
3971 cf->id = interp->callFrameEpoch++;
3972 }
3973
3974 #define JIM_FCF_NONE 0 /* no flags */
3975 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3976 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3977 int flags)
3978 {
3979 if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3980 if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3981 if (!(flags & JIM_FCF_NOHT))
3982 Jim_FreeHashTable(&cf->vars);
3983 else {
3984 int i;
3985 Jim_HashEntry **table = cf->vars.table, *he;
3986
3987 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3988 he = table[i];
3989 while (he != NULL) {
3990 Jim_HashEntry *nextEntry = he->next;
3991 Jim_Var *varPtr = (void*) he->val;
3992
3993 Jim_DecrRefCount(interp, varPtr->objPtr);
3994 Jim_Free(he->val);
3995 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3996 Jim_Free(he);
3997 table[i] = NULL;
3998 he = nextEntry;
3999 }
4000 }
4001 cf->vars.used = 0;
4002 }
4003 cf->nextFramePtr = interp->freeFramesList;
4004 interp->freeFramesList = cf;
4005 }
4006
4007 /* -----------------------------------------------------------------------------
4008 * References
4009 * ---------------------------------------------------------------------------*/
4010
4011 /* References HashTable Type.
4012 *
4013 * Keys are jim_wide integers, dynamically allocated for now but in the
4014 * future it's worth to cache this 8 bytes objects. Values are poitners
4015 * to Jim_References. */
4016 static void JimReferencesHTValDestructor(void *interp, void *val)
4017 {
4018 Jim_Reference *refPtr = (void*) val;
4019
4020 Jim_DecrRefCount(interp, refPtr->objPtr);
4021 if (refPtr->finalizerCmdNamePtr != NULL) {
4022 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4023 }
4024 Jim_Free(val);
4025 }
4026
4027 unsigned int JimReferencesHTHashFunction(const void *key)
4028 {
4029 /* Only the least significant bits are used. */
4030 const jim_wide *widePtr = key;
4031 unsigned int intValue = (unsigned int) *widePtr;
4032 return Jim_IntHashFunction(intValue);
4033 }
4034
4035 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
4036 {
4037 /* Only the least significant bits are used. */
4038 const jim_wide *widePtr = key;
4039 unsigned int intValue = (unsigned int) *widePtr;
4040 return intValue; /* identity function. */
4041 }
4042
4043 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
4044 {
4045 void *copy = Jim_Alloc(sizeof(jim_wide));
4046 JIM_NOTUSED(privdata);
4047
4048 memcpy(copy, key, sizeof(jim_wide));
4049 return copy;
4050 }
4051
4052 int JimReferencesHTKeyCompare(void *privdata, const void *key1,
4053 const void *key2)
4054 {
4055 JIM_NOTUSED(privdata);
4056
4057 return memcmp(key1, key2, sizeof(jim_wide)) == 0;
4058 }
4059
4060 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
4061 {
4062 JIM_NOTUSED(privdata);
4063
4064 Jim_Free((void*)key);
4065 }
4066
4067 static Jim_HashTableType JimReferencesHashTableType = {
4068 JimReferencesHTHashFunction, /* hash function */
4069 JimReferencesHTKeyDup, /* key dup */
4070 NULL, /* val dup */
4071 JimReferencesHTKeyCompare, /* key compare */
4072 JimReferencesHTKeyDestructor, /* key destructor */
4073 JimReferencesHTValDestructor /* val destructor */
4074 };
4075
4076 /* -----------------------------------------------------------------------------
4077 * Reference object type and References API
4078 * ---------------------------------------------------------------------------*/
4079
4080 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
4081
4082 static Jim_ObjType referenceObjType = {
4083 "reference",
4084 NULL,
4085 NULL,
4086 UpdateStringOfReference,
4087 JIM_TYPE_REFERENCES,
4088 };
4089
4090 void UpdateStringOfReference(struct Jim_Obj *objPtr)
4091 {
4092 int len;
4093 char buf[JIM_REFERENCE_SPACE+1];
4094 Jim_Reference *refPtr;
4095
4096 refPtr = objPtr->internalRep.refValue.refPtr;
4097 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
4098 objPtr->bytes = Jim_Alloc(len+1);
4099 memcpy(objPtr->bytes, buf, len+1);
4100 objPtr->length = len;
4101 }
4102
4103 /* returns true if 'c' is a valid reference tag character.
4104 * i.e. inside the range [_a-zA-Z0-9] */
4105 static int isrefchar(int c)
4106 {
4107 if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
4108 (c >= '0' && c <= '9')) return 1;
4109 return 0;
4110 }
4111
4112 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4113 {
4114 jim_wide wideValue;
4115 int i, len;
4116 const char *str, *start, *end;
4117 char refId[21];
4118 Jim_Reference *refPtr;
4119 Jim_HashEntry *he;
4120
4121 /* Get the string representation */
4122 str = Jim_GetString(objPtr, &len);
4123 /* Check if it looks like a reference */
4124 if (len < JIM_REFERENCE_SPACE) goto badformat;
4125 /* Trim spaces */
4126 start = str;
4127 end = str+len-1;
4128 while (*start == ' ') start++;
4129 while (*end == ' ' && end > start) end--;
4130 if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
4131 /* <reference.<1234567>.%020> */
4132 if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
4133 if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
4134 /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
4135 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4136 if (!isrefchar(start[12+i])) goto badformat;
4137 }
4138 /* Extract info from the refernece. */
4139 memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
4140 refId[20] = '\0';
4141 /* Try to convert the ID into a jim_wide */
4142 if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
4143 /* Check if the reference really exists! */
4144 he = Jim_FindHashEntry(&interp->references, &wideValue);
4145 if (he == NULL) {
4146 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4147 Jim_AppendStrings(interp, Jim_GetResult(interp),
4148 "Invalid reference ID \"", str, "\"", NULL);
4149 return JIM_ERR;
4150 }
4151 refPtr = he->val;
4152 /* Free the old internal repr and set the new one. */
4153 Jim_FreeIntRep(interp, objPtr);
4154 objPtr->typePtr = &referenceObjType;
4155 objPtr->internalRep.refValue.id = wideValue;
4156 objPtr->internalRep.refValue.refPtr = refPtr;
4157 return JIM_OK;
4158
4159 badformat:
4160 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4161 Jim_AppendStrings(interp, Jim_GetResult(interp),
4162 "expected reference but got \"", str, "\"", NULL);
4163 return JIM_ERR;
4164 }
4165
4166 /* Returns a new reference pointing to objPtr, having cmdNamePtr
4167 * as finalizer command (or NULL if there is no finalizer).
4168 * The returned reference object has refcount = 0. */
4169 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
4170 Jim_Obj *cmdNamePtr)
4171 {
4172 struct Jim_Reference *refPtr;
4173 jim_wide wideValue = interp->referenceNextId;
4174 Jim_Obj *refObjPtr;
4175 const char *tag;
4176 int tagLen, i;
4177
4178 /* Perform the Garbage Collection if needed. */
4179 Jim_CollectIfNeeded(interp);
4180
4181 refPtr = Jim_Alloc(sizeof(*refPtr));
4182 refPtr->objPtr = objPtr;
4183 Jim_IncrRefCount(objPtr);
4184 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4185 if (cmdNamePtr)
4186 Jim_IncrRefCount(cmdNamePtr);
4187 Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4188 refObjPtr = Jim_NewObj(interp);
4189 refObjPtr->typePtr = &referenceObjType;
4190 refObjPtr->bytes = NULL;
4191 refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4192 refObjPtr->internalRep.refValue.refPtr = refPtr;
4193 interp->referenceNextId++;
4194 /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4195 * that does not pass the 'isrefchar' test is replaced with '_' */
4196 tag = Jim_GetString(tagPtr, &tagLen);
4197 if (tagLen > JIM_REFERENCE_TAGLEN)
4198 tagLen = JIM_REFERENCE_TAGLEN;
4199 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4200 if (i < tagLen)
4201 refPtr->tag[i] = tag[i];
4202 else
4203 refPtr->tag[i] = '_';
4204 }
4205 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4206 return refObjPtr;
4207 }
4208
4209 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4210 {
4211 if (objPtr->typePtr != &referenceObjType &&
4212 SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4213 return NULL;
4214 return objPtr->internalRep.refValue.refPtr;
4215 }
4216
4217 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4218 {
4219 Jim_Reference *refPtr;
4220
4221 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4222 return JIM_ERR;
4223 Jim_IncrRefCount(cmdNamePtr);
4224 if (refPtr->finalizerCmdNamePtr)
4225 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4226 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4227 return JIM_OK;
4228 }
4229
4230 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4231 {
4232 Jim_Reference *refPtr;
4233
4234 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4235 return JIM_ERR;
4236 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4237 return JIM_OK;
4238 }
4239
4240 /* -----------------------------------------------------------------------------
4241 * References Garbage Collection
4242 * ---------------------------------------------------------------------------*/
4243
4244 /* This the hash table type for the "MARK" phase of the GC */
4245 static Jim_HashTableType JimRefMarkHashTableType = {
4246 JimReferencesHTHashFunction, /* hash function */
4247 JimReferencesHTKeyDup, /* key dup */
4248 NULL, /* val dup */
4249 JimReferencesHTKeyCompare, /* key compare */
4250 JimReferencesHTKeyDestructor, /* key destructor */
4251 NULL /* val destructor */
4252 };
4253
4254 /* #define JIM_DEBUG_GC 1 */
4255
4256 /* Performs the garbage collection. */
4257 int Jim_Collect(Jim_Interp *interp)
4258 {
4259 Jim_HashTable marks;
4260 Jim_HashTableIterator *htiter;
4261 Jim_HashEntry *he;
4262 Jim_Obj *objPtr;
4263 int collected = 0;
4264
4265 /* Avoid recursive calls */
4266 if (interp->lastCollectId == -1) {
4267 /* Jim_Collect() already running. Return just now. */
4268 return 0;
4269 }
4270 interp->lastCollectId = -1;
4271
4272 /* Mark all the references found into the 'mark' hash table.
4273 * The references are searched in every live object that
4274 * is of a type that can contain references. */
4275 Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4276 objPtr = interp->liveList;
4277 while(objPtr) {
4278 if (objPtr->typePtr == NULL ||
4279 objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4280 const char *str, *p;
4281 int len;
4282
4283 /* If the object is of type reference, to get the
4284 * Id is simple... */
4285 if (objPtr->typePtr == &referenceObjType) {
4286 Jim_AddHashEntry(&marks,
4287 &objPtr->internalRep.refValue.id, NULL);
4288 #ifdef JIM_DEBUG_GC
4289 Jim_fprintf(interp,interp->cookie_stdout,
4290 "MARK (reference): %d refcount: %d" JIM_NL,
4291 (int) objPtr->internalRep.refValue.id,
4292 objPtr->refCount);
4293 #endif
4294 objPtr = objPtr->nextObjPtr;
4295 continue;
4296 }
4297 /* Get the string repr of the object we want
4298 * to scan for references. */
4299 p = str = Jim_GetString(objPtr, &len);
4300 /* Skip objects too little to contain references. */
4301 if (len < JIM_REFERENCE_SPACE) {
4302 objPtr = objPtr->nextObjPtr;
4303 continue;
4304 }
4305 /* Extract references from the object string repr. */
4306 while(1) {
4307 int i;
4308 jim_wide id;
4309 char buf[21];
4310
4311 if ((p = strstr(p, "<reference.<")) == NULL)
4312 break;
4313 /* Check if it's a valid reference. */
4314 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4315 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4316 for (i = 21; i <= 40; i++)
4317 if (!isdigit((int)p[i]))
4318 break;
4319 /* Get the ID */
4320 memcpy(buf, p+21, 20);
4321 buf[20] = '\0';
4322 Jim_StringToWide(buf, &id, 10);
4323
4324 /* Ok, a reference for the given ID
4325 * was found. Mark it. */
4326 Jim_AddHashEntry(&marks, &id, NULL);
4327 #ifdef JIM_DEBUG_GC
4328 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4329 #endif
4330 p += JIM_REFERENCE_SPACE;
4331 }
4332 }
4333 objPtr = objPtr->nextObjPtr;
4334 }
4335
4336 /* Run the references hash table to destroy every reference that
4337 * is not referenced outside (not present in the mark HT). */
4338 htiter = Jim_GetHashTableIterator(&interp->references);
4339 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4340 const jim_wide *refId;
4341 Jim_Reference *refPtr;
4342
4343 refId = he->key;
4344 /* Check if in the mark phase we encountered
4345 * this reference. */
4346 if (Jim_FindHashEntry(&marks, refId) == NULL) {
4347 #ifdef JIM_DEBUG_GC
4348 Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4349 #endif
4350 collected++;
4351 /* Drop the reference, but call the
4352 * finalizer first if registered. */
4353 refPtr = he->val;
4354 if (refPtr->finalizerCmdNamePtr) {
4355 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4356 Jim_Obj *objv[3], *oldResult;
4357
4358 JimFormatReference(refstr, refPtr, *refId);
4359
4360 objv[0] = refPtr->finalizerCmdNamePtr;
4361 objv[1] = Jim_NewStringObjNoAlloc(interp,
4362 refstr, 32);
4363 objv[2] = refPtr->objPtr;
4364 Jim_IncrRefCount(objv[0]);
4365 Jim_IncrRefCount(objv[1]);
4366 Jim_IncrRefCount(objv[2]);
4367
4368 /* Drop the reference itself */
4369 Jim_DeleteHashEntry(&interp->references, refId);
4370
4371 /* Call the finalizer. Errors ignored. */
4372 oldResult = interp->result;
4373 Jim_IncrRefCount(oldResult);
4374 Jim_EvalObjVector(interp, 3, objv);
4375 Jim_SetResult(interp, oldResult);
4376 Jim_DecrRefCount(interp, oldResult);
4377
4378 Jim_DecrRefCount(interp, objv[0]);
4379 Jim_DecrRefCount(interp, objv[1]);
4380 Jim_DecrRefCount(interp, objv[2]);
4381 } else {
4382 Jim_DeleteHashEntry(&interp->references, refId);
4383 }
4384 }
4385 }
4386 Jim_FreeHashTableIterator(htiter);
4387 Jim_FreeHashTable(&marks);
4388 interp->lastCollectId = interp->referenceNextId;
4389 interp->lastCollectTime = time(NULL);
4390 return collected;
4391 }
4392
4393 #define JIM_COLLECT_ID_PERIOD 5000
4394 #define JIM_COLLECT_TIME_PERIOD 300
4395
4396 void Jim_CollectIfNeeded(Jim_Interp *interp)
4397 {
4398 jim_wide elapsedId;
4399 int elapsedTime;
4400
4401 elapsedId = interp->referenceNextId - interp->lastCollectId;
4402 elapsedTime = time(NULL) - interp->lastCollectTime;
4403
4404
4405 if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4406 elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4407 Jim_Collect(interp);
4408 }
4409 }
4410
4411 /* -----------------------------------------------------------------------------
4412 * Interpreter related functions
4413 * ---------------------------------------------------------------------------*/
4414
4415 Jim_Interp *Jim_CreateInterp(void)
4416 {
4417 Jim_Interp *i = Jim_Alloc(sizeof(*i));
4418 Jim_Obj *pathPtr;
4419
4420 i->errorLine = 0;
4421 i->errorFileName = Jim_StrDup("");
4422 i->numLevels = 0;
4423 i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4424 i->returnCode = JIM_OK;
4425 i->exitCode = 0;
4426 i->procEpoch = 0;
4427 i->callFrameEpoch = 0;
4428 i->liveList = i->freeList = NULL;
4429 i->scriptFileName = Jim_StrDup("");
4430 i->referenceNextId = 0;
4431 i->lastCollectId = 0;
4432 i->lastCollectTime = time(NULL);
4433 i->freeFramesList = NULL;
4434 i->prngState = NULL;
4435 i->evalRetcodeLevel = -1;
4436 i->cookie_stdin = stdin;
4437 i->cookie_stdout = stdout;
4438 i->cookie_stderr = stderr;
4439 i->cb_fwrite = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4440 i->cb_fread = ((size_t (*)( void *, size_t, size_t, void *))(fread));
4441 i->cb_vfprintf = ((int (*)( void *, const char *fmt, va_list ))(vfprintf));
4442 i->cb_fflush = ((int (*)( void *))(fflush));
4443 i->cb_fgets = ((char * (*)( char *, int, void *))(fgets));
4444
4445 /* Note that we can create objects only after the
4446 * interpreter liveList and freeList pointers are
4447 * initialized to NULL. */
4448 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4449 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4450 Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4451 NULL);
4452 Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4453 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4454 Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4455 i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4456 i->emptyObj = Jim_NewEmptyStringObj(i);
4457 i->result = i->emptyObj;
4458 i->stackTrace = Jim_NewListObj(i, NULL, 0);
4459 i->unknown = Jim_NewStringObj(i, "unknown", -1);
4460 i->unknown_called = 0;
4461 Jim_IncrRefCount(i->emptyObj);
4462 Jim_IncrRefCount(i->result);
4463 Jim_IncrRefCount(i->stackTrace);
4464 Jim_IncrRefCount(i->unknown);
4465
4466 /* Initialize key variables every interpreter should contain */
4467 pathPtr = Jim_NewStringObj(i, "./", -1);
4468 Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4469 Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4470
4471 /* Export the core API to extensions */
4472 JimRegisterCoreApi(i);
4473 return i;
4474 }
4475
4476 /* This is the only function Jim exports directly without
4477 * to use the STUB system. It is only used by embedders
4478 * in order to get an interpreter with the Jim API pointers
4479 * registered. */
4480 Jim_Interp *ExportedJimCreateInterp(void)
4481 {
4482 return Jim_CreateInterp();
4483 }
4484
4485 void Jim_FreeInterp(Jim_Interp *i)
4486 {
4487 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4488 Jim_Obj *objPtr, *nextObjPtr;
4489
4490 Jim_DecrRefCount(i, i->emptyObj);
4491 Jim_DecrRefCount(i, i->result);
4492 Jim_DecrRefCount(i, i->stackTrace);
4493 Jim_DecrRefCount(i, i->unknown);
4494 Jim_Free((void*)i->errorFileName);
4495 Jim_Free((void*)i->scriptFileName);
4496 Jim_FreeHashTable(&i->commands);
4497 Jim_FreeHashTable(&i->references);
4498 Jim_FreeHashTable(&i->stub);
4499 Jim_FreeHashTable(&i->assocData);
4500 Jim_FreeHashTable(&i->packages);
4501 Jim_Free(i->prngState);
4502 /* Free the call frames list */
4503 while(cf) {
4504 prevcf = cf->parentCallFrame;
4505 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4506 cf = prevcf;
4507 }
4508 /* Check that the live object list is empty, otherwise
4509 * there is a memory leak. */
4510 if (i->liveList != NULL) {
4511 Jim_Obj *objPtr = i->liveList;
4512
4513 Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4514 Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4515 while(objPtr) {
4516 const char *type = objPtr->typePtr ?
4517 objPtr->typePtr->name : "";
4518 Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4519 objPtr, type,
4520 objPtr->bytes ? objPtr->bytes
4521 : "(null)", objPtr->refCount);
4522 if (objPtr->typePtr == &sourceObjType) {
4523 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4524 objPtr->internalRep.sourceValue.fileName,
4525 objPtr->internalRep.sourceValue.lineNumber);
4526 }
4527 objPtr = objPtr->nextObjPtr;
4528 }
4529 Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4530 Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4531 }
4532 /* Free all the freed objects. */
4533 objPtr = i->freeList;
4534 while (objPtr) {
4535 nextObjPtr = objPtr->nextObjPtr;
4536 Jim_Free(objPtr);
4537 objPtr = nextObjPtr;
4538 }
4539 /* Free cached CallFrame structures */
4540 cf = i->freeFramesList;
4541 while(cf) {
4542 nextcf = cf->nextFramePtr;
4543 if (cf->vars.table != NULL)
4544 Jim_Free(cf->vars.table);
4545 Jim_Free(cf);
4546 cf = nextcf;
4547 }
4548 /* Free the sharedString hash table. Make sure to free it
4549 * after every other Jim_Object was freed. */
4550 Jim_FreeHashTable(&i->sharedStrings);
4551 /* Free the interpreter structure. */
4552 Jim_Free(i);
4553 }
4554
4555 /* Store the call frame relative to the level represented by
4556 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4557 * level is assumed to be '1'.
4558 *
4559 * If a newLevelptr int pointer is specified, the function stores
4560 * the absolute level integer value of the new target callframe into
4561 * *newLevelPtr. (this is used to adjust interp->numLevels
4562 * in the implementation of [uplevel], so that [info level] will
4563 * return a correct information).
4564 *
4565 * This function accepts the 'level' argument in the form
4566 * of the commands [uplevel] and [upvar].
4567 *
4568 * For a function accepting a relative integer as level suitable
4569 * for implementation of [info level ?level?] check the
4570 * GetCallFrameByInteger() function. */
4571 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4572 Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4573 {
4574 long level;
4575 const char *str;
4576 Jim_CallFrame *framePtr;
4577
4578 if (newLevelPtr) *newLevelPtr = interp->numLevels;
4579 if (levelObjPtr) {
4580 str = Jim_GetString(levelObjPtr, NULL);
4581 if (str[0] == '#') {
4582 char *endptr;
4583 /* speedup for the toplevel (level #0) */
4584 if (str[1] == '0' && str[2] == '\0') {
4585 if (newLevelPtr) *newLevelPtr = 0;
4586 *framePtrPtr = interp->topFramePtr;
4587 return JIM_OK;
4588 }
4589
4590 level = strtol(str+1, &endptr, 0);
4591 if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4592 goto badlevel;
4593 /* An 'absolute' level is converted into the
4594 * 'number of levels to go back' format. */
4595 level = interp->numLevels - level;
4596 if (level < 0) goto badlevel;
4597 } else {
4598 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4599 goto badlevel;
4600 }
4601 } else {
4602 str = "1"; /* Needed to format the error message. */
4603 level = 1;
4604 }
4605 /* Lookup */
4606 framePtr = interp->framePtr;
4607 if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4608 while (level--) {
4609 framePtr = framePtr->parentCallFrame;
4610 if (framePtr == NULL) goto badlevel;
4611 }
4612 *framePtrPtr = framePtr;
4613 return JIM_OK;
4614 badlevel:
4615 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4616 Jim_AppendStrings(interp, Jim_GetResult(interp),
4617 "bad level \"", str, "\"", NULL);
4618 return JIM_ERR;
4619 }
4620
4621 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4622 * as a relative integer like in the [info level ?level?] command. */
4623 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4624 Jim_CallFrame **framePtrPtr)
4625 {
4626 jim_wide level;
4627 jim_wide relLevel; /* level relative to the current one. */
4628 Jim_CallFrame *framePtr;
4629
4630 if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4631 goto badlevel;
4632 if (level > 0) {
4633 /* An 'absolute' level is converted into the
4634 * 'number of levels to go back' format. */
4635 relLevel = interp->numLevels - level;
4636 } else {
4637 relLevel = -level;
4638 }
4639 /* Lookup */
4640 framePtr = interp->framePtr;
4641 while (relLevel--) {
4642 framePtr = framePtr->parentCallFrame;
4643 if (framePtr == NULL) goto badlevel;
4644 }
4645 *framePtrPtr = framePtr;
4646 return JIM_OK;
4647 badlevel:
4648 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4649 Jim_AppendStrings(interp, Jim_GetResult(interp),
4650 "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4651 return JIM_ERR;
4652 }
4653
4654 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4655 {
4656 Jim_Free((void*)interp->errorFileName);
4657 interp->errorFileName = Jim_StrDup(filename);
4658 }
4659
4660 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4661 {
4662 interp->errorLine = linenr;
4663 }
4664
4665 static void JimResetStackTrace(Jim_Interp *interp)
4666 {
4667 Jim_DecrRefCount(interp, interp->stackTrace);
4668 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4669 Jim_IncrRefCount(interp->stackTrace);
4670 }
4671
4672 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4673 const char *filename, int linenr)
4674 {
4675 /* No need to add this dummy entry to the stack trace */
4676 if (strcmp(procname, "unknown") == 0) {
4677 return;
4678 }
4679
4680 if (Jim_IsShared(interp->stackTrace)) {
4681 interp->stackTrace =
4682 Jim_DuplicateObj(interp, interp->stackTrace);
4683 Jim_IncrRefCount(interp->stackTrace);
4684 }
4685 Jim_ListAppendElement(interp, interp->stackTrace,
4686 Jim_NewStringObj(interp, procname, -1));
4687 Jim_ListAppendElement(interp, interp->stackTrace,
4688 Jim_NewStringObj(interp, filename, -1));
4689 Jim_ListAppendElement(interp, interp->stackTrace,
4690 Jim_NewIntObj(interp, linenr));
4691 }
4692
4693 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4694 {
4695 AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4696 assocEntryPtr->delProc = delProc;
4697 assocEntryPtr->data = data;
4698 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4699 }
4700
4701 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4702 {
4703 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4704 if (entryPtr != NULL) {
4705 AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4706 return assocEntryPtr->data;
4707 }
4708 return NULL;
4709 }
4710
4711 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4712 {
4713 return Jim_DeleteHashEntry(&interp->assocData, key);
4714 }
4715
4716 int Jim_GetExitCode(Jim_Interp *interp) {
4717 return interp->exitCode;
4718 }
4719
4720 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4721 {
4722 if (fp != NULL) interp->cookie_stdin = fp;
4723 return interp->cookie_stdin;
4724 }
4725
4726 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4727 {
4728 if (fp != NULL) interp->cookie_stdout = fp;
4729 return interp->cookie_stdout;
4730 }
4731
4732 void *Jim_SetStderr(Jim_Interp *interp, void *fp)
4733 {
4734 if (fp != NULL) interp->cookie_stderr = fp;
4735 return interp->cookie_stderr;
4736 }
4737
4738 /* -----------------------------------------------------------------------------
4739 * Shared strings.
4740 * Every interpreter has an hash table where to put shared dynamically
4741 * allocate strings that are likely to be used a lot of times.
4742 * For example, in the 'source' object type, there is a pointer to
4743 * the filename associated with that object. Every script has a lot
4744 * of this objects with the identical file name, so it is wise to share
4745 * this info.
4746 *
4747 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4748 * returns the pointer to the shared string. Every time a reference
4749 * to the string is no longer used, the user should call
4750 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4751 * a given string, it is removed from the hash table.
4752 * ---------------------------------------------------------------------------*/
4753 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4754 {
4755 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4756
4757 if (he == NULL) {
4758 char *strCopy = Jim_StrDup(str);
4759
4760 Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4761 return strCopy;
4762 } else {
4763 long refCount = (long) he->val;
4764
4765 refCount++;
4766 he->val = (void*) refCount;
4767 return he->key;
4768 }
4769 }
4770
4771 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4772 {
4773 long refCount;
4774 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4775
4776 if (he == NULL)
4777 Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4778 "unknown shared string '%s'", str);
4779 refCount = (long) he->val;
4780 refCount--;
4781 if (refCount == 0) {
4782 Jim_DeleteHashEntry(&interp->sharedStrings, str);
4783 } else {
4784 he->val = (void*) refCount;
4785 }
4786 }
4787
4788 /* -----------------------------------------------------------------------------
4789 * Integer object
4790 * ---------------------------------------------------------------------------*/
4791 #define JIM_INTEGER_SPACE 24
4792
4793 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4794 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4795
4796 static Jim_ObjType intObjType = {
4797 "int",
4798 NULL,
4799 NULL,
4800 UpdateStringOfInt,
4801 JIM_TYPE_NONE,
4802 };
4803
4804 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4805 {
4806 int len;
4807 char buf[JIM_INTEGER_SPACE+1];
4808
4809 len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4810 objPtr->bytes = Jim_Alloc(len+1);
4811 memcpy(objPtr->bytes, buf, len+1);
4812 objPtr->length = len;
4813 }
4814
4815 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4816 {
4817 jim_wide wideValue;
4818 const char *str;
4819
4820 /* Get the string representation */
4821 str = Jim_GetString(objPtr, NULL);
4822 /* Try to convert into a jim_wide */
4823 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4824 if (flags & JIM_ERRMSG) {
4825 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4826 Jim_AppendStrings(interp, Jim_GetResult(interp),
4827 "expected integer but got \"", str, "\"", NULL);
4828 }
4829 return JIM_ERR;
4830 }
4831 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4832 errno == ERANGE) {
4833 Jim_SetResultString(interp,
4834 "Integer value too big to be represented", -1);
4835 return JIM_ERR;
4836 }
4837 /* Free the old internal repr and set the new one. */
4838 Jim_FreeIntRep(interp, objPtr);
4839 objPtr->typePtr = &intObjType;
4840 objPtr->internalRep.wideValue = wideValue;
4841 return JIM_OK;
4842 }
4843
4844 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4845 {
4846 if (objPtr->typePtr != &intObjType &&
4847 SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4848 return JIM_ERR;
4849 *widePtr = objPtr->internalRep.wideValue;
4850 return JIM_OK;
4851 }
4852
4853 /* Get a wide but does not set an error if the format is bad. */
4854 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4855 jim_wide *widePtr)
4856 {
4857 if (objPtr->typePtr != &intObjType &&
4858 SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4859 return JIM_ERR;
4860 *widePtr = objPtr->internalRep.wideValue;
4861 return JIM_OK;
4862 }
4863
4864 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4865 {
4866 jim_wide wideValue;
4867 int retval;
4868
4869 retval = Jim_GetWide(interp, objPtr, &wideValue);
4870 if (retval == JIM_OK) {
4871 *longPtr = (long) wideValue;
4872 return JIM_OK;
4873 }
4874 return JIM_ERR;
4875 }
4876
4877 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4878 {
4879 if (Jim_IsShared(objPtr))
4880 Jim_Panic(interp,"Jim_SetWide called with shared object");
4881 if (objPtr->typePtr != &intObjType) {
4882 Jim_FreeIntRep(interp, objPtr);
4883 objPtr->typePtr = &intObjType;
4884 }
4885 Jim_InvalidateStringRep(objPtr);
4886 objPtr->internalRep.wideValue = wideValue;
4887 }
4888
4889 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4890 {
4891 Jim_Obj *objPtr;
4892
4893 objPtr = Jim_NewObj(interp);
4894 objPtr->typePtr = &intObjType;
4895 objPtr->bytes = NULL;
4896 objPtr->internalRep.wideValue = wideValue;
4897 return objPtr;
4898 }
4899
4900 /* -----------------------------------------------------------------------------
4901 * Double object
4902 * ---------------------------------------------------------------------------*/
4903 #define JIM_DOUBLE_SPACE 30
4904
4905 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4906 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4907
4908 static Jim_ObjType doubleObjType = {
4909 "double",
4910 NULL,
4911 NULL,
4912 UpdateStringOfDouble,
4913 JIM_TYPE_NONE,
4914 };
4915
4916 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4917 {
4918 int len;
4919 char buf[JIM_DOUBLE_SPACE+1];
4920
4921 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4922 objPtr->bytes = Jim_Alloc(len+1);
4923 memcpy(objPtr->bytes, buf, len+1);
4924 objPtr->length = len;
4925 }
4926
4927 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4928 {
4929 double doubleValue;
4930 const char *str;
4931
4932 /* Get the string representation */
4933 str = Jim_GetString(objPtr, NULL);
4934 /* Try to convert into a double */
4935 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4936 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4937 Jim_AppendStrings(interp, Jim_GetResult(interp),
4938 "expected number but got '", str, "'", NULL);
4939 return JIM_ERR;
4940 }
4941 /* Free the old internal repr and set the new one. */
4942 Jim_FreeIntRep(interp, objPtr);
4943 objPtr->typePtr = &doubleObjType;
4944 objPtr->internalRep.doubleValue = doubleValue;
4945 return JIM_OK;
4946 }
4947
4948 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4949 {
4950 if (objPtr->typePtr != &doubleObjType &&
4951 SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4952 return JIM_ERR;
4953 *doublePtr = objPtr->internalRep.doubleValue;
4954 return JIM_OK;
4955 }
4956
4957 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4958 {
4959 if (Jim_IsShared(objPtr))
4960 Jim_Panic(interp,"Jim_SetDouble called with shared object");
4961 if (objPtr->typePtr != &doubleObjType) {
4962 Jim_FreeIntRep(interp, objPtr);
4963 objPtr->typePtr = &doubleObjType;
4964 }
4965 Jim_InvalidateStringRep(objPtr);
4966 objPtr->internalRep.doubleValue = doubleValue;
4967 }
4968
4969 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4970 {
4971 Jim_Obj *objPtr;
4972
4973 objPtr = Jim_NewObj(interp);
4974 objPtr->typePtr = &doubleObjType;
4975 objPtr->bytes = NULL;
4976 objPtr->internalRep.doubleValue = doubleValue;
4977 return objPtr;
4978 }
4979
4980 /* -----------------------------------------------------------------------------
4981 * List object
4982 * ---------------------------------------------------------------------------*/
4983 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4984 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4985 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4986 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4987 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4988
4989 /* Note that while the elements of the list may contain references,
4990 * the list object itself can't. This basically means that the
4991 * list object string representation as a whole can't contain references
4992 * that are not presents in the single elements. */
4993 static Jim_ObjType listObjType = {
4994 "list",
4995 FreeListInternalRep,
4996 DupListInternalRep,
4997 UpdateStringOfList,
4998 JIM_TYPE_NONE,
4999 };
5000
5001 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5002 {
5003 int i;
5004
5005 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5006 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
5007 }
5008 Jim_Free(objPtr->internalRep.listValue.ele);
5009 }
5010
5011 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5012 {
5013 int i;
5014 JIM_NOTUSED(interp);
5015
5016 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
5017 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
5018 dupPtr->internalRep.listValue.ele =
5019 Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
5020 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
5021 sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
5022 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
5023 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
5024 }
5025 dupPtr->typePtr = &listObjType;
5026 }
5027
5028 /* The following function checks if a given string can be encoded
5029 * into a list element without any kind of quoting, surrounded by braces,
5030 * or using escapes to quote. */
5031 #define JIM_ELESTR_SIMPLE 0
5032 #define JIM_ELESTR_BRACE 1
5033 #define JIM_ELESTR_QUOTE 2
5034 static int ListElementQuotingType(const char *s, int len)
5035 {
5036 int i, level, trySimple = 1;
5037
5038 /* Try with the SIMPLE case */
5039 if (len == 0) return JIM_ELESTR_BRACE;
5040 if (s[0] == '"' || s[0] == '{') {
5041 trySimple = 0;
5042 goto testbrace;
5043 }
5044 for (i = 0; i < len; i++) {
5045 switch(s[i]) {
5046 case ' ':
5047 case '$':
5048 case '"':
5049 case '[':
5050 case ']':
5051 case ';':
5052 case '\\':
5053 case '\r':
5054 case '\n':
5055 case '\t':
5056 case '\f':
5057 case '\v':
5058 trySimple = 0;
5059 case '{':
5060 case '}':
5061 goto testbrace;
5062 }
5063 }
5064 return JIM_ELESTR_SIMPLE;
5065
5066 testbrace:
5067 /* Test if it's possible to do with braces */
5068 if (s[len-1] == '\\' ||
5069 s[len-1] == ']') return JIM_ELESTR_QUOTE;
5070 level = 0;
5071 for (i = 0; i < len; i++) {
5072 switch(s[i]) {
5073 case '{': level++; break;
5074 case '}': level--;
5075 if (level < 0) return JIM_ELESTR_QUOTE;
5076 break;
5077 case '\\':
5078 if (s[i+1] == '\n')
5079 return JIM_ELESTR_QUOTE;
5080 else
5081 if (s[i+1] != '\0') i++;
5082 break;
5083 }
5084 }
5085 if (level == 0) {
5086 if (!trySimple) return JIM_ELESTR_BRACE;
5087 for (i = 0; i < len; i++) {
5088 switch(s[i]) {
5089 case ' ':
5090 case '$':
5091 case '"':
5092 case '[':
5093 case ']':
5094 case ';':
5095 case '\\':
5096 case '\r':
5097 case '\n':
5098 case '\t':
5099 case '\f':
5100 case '\v':
5101 return JIM_ELESTR_BRACE;
5102 break;
5103 }
5104 }
5105 return JIM_ELESTR_SIMPLE;
5106 }
5107 return JIM_ELESTR_QUOTE;
5108 }
5109
5110 /* Returns the malloc-ed representation of a string
5111 * using backslash to quote special chars. */
5112 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
5113 {
5114 char *q = Jim_Alloc(len*2+1), *p;
5115
5116 p = q;
5117 while(*s) {
5118 switch (*s) {
5119 case ' ':
5120 case '$':
5121 case '"':
5122 case '[':
5123 case ']':
5124 case '{':
5125 case '}':
5126 case ';':
5127 case '\\':
5128 *p++ = '\\';
5129 *p++ = *s++;
5130 break;
5131 case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
5132 case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
5133 case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
5134 case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
5135 case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
5136 default:
5137 *p++ = *s++;
5138 break;
5139 }
5140 }
5141 *p = '\0';
5142 *qlenPtr = p-q;
5143 return q;
5144 }
5145
5146 void UpdateStringOfList(struct Jim_Obj *objPtr)
5147 {
5148 int i, bufLen, realLength;
5149 const char *strRep;
5150 char *p;
5151 int *quotingType;
5152 Jim_Obj **ele = objPtr->internalRep.listValue.ele;
5153
5154 /* (Over) Estimate the space needed. */
5155 quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
5156 bufLen = 0;
5157 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5158 int len;
5159
5160 strRep = Jim_GetString(ele[i], &len);
5161 quotingType[i] = ListElementQuotingType(strRep, len);
5162 switch (quotingType[i]) {
5163 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5164 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5165 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5166 }
5167 bufLen++; /* elements separator. */
5168 }
5169 bufLen++;
5170
5171 /* Generate the string rep. */
5172 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5173 realLength = 0;
5174 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5175 int len, qlen;
5176 const char *strRep = Jim_GetString(ele[i], &len);
5177 char *q;
5178
5179 switch(quotingType[i]) {
5180 case JIM_ELESTR_SIMPLE:
5181 memcpy(p, strRep, len);
5182 p += len;
5183 realLength += len;
5184 break;
5185 case JIM_ELESTR_BRACE:
5186 *p++ = '{';
5187 memcpy(p, strRep, len);
5188 p += len;
5189 *p++ = '}';
5190 realLength += len+2;
5191 break;
5192 case JIM_ELESTR_QUOTE:
5193 q = BackslashQuoteString(strRep, len, &qlen);
5194 memcpy(p, q, qlen);
5195 Jim_Free(q);
5196 p += qlen;
5197 realLength += qlen;
5198 break;
5199 }
5200 /* Add a separating space */
5201 if (i+1 != objPtr->internalRep.listValue.len) {
5202 *p++ = ' ';
5203 realLength ++;
5204 }
5205 }
5206 *p = '\0'; /* nul term. */
5207 objPtr->length = realLength;
5208 Jim_Free(quotingType);
5209 }
5210
5211 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5212 {
5213 struct JimParserCtx parser;
5214 const char *str;
5215 int strLen;
5216
5217 /* Get the string representation */
5218 str = Jim_GetString(objPtr, &strLen);
5219
5220 /* Free the old internal repr just now and initialize the
5221 * new one just now. The string->list conversion can't fail. */
5222 Jim_FreeIntRep(interp, objPtr);
5223 objPtr->typePtr = &listObjType;
5224 objPtr->internalRep.listValue.len = 0;
5225 objPtr->internalRep.listValue.maxLen = 0;
5226 objPtr->internalRep.listValue.ele = NULL;
5227
5228 /* Convert into a list */
5229 JimParserInit(&parser, str, strLen, 1);
5230 while(!JimParserEof(&parser)) {
5231 char *token;
5232 int tokenLen, type;
5233 Jim_Obj *elementPtr;
5234
5235 JimParseList(&parser);
5236 if (JimParserTtype(&parser) != JIM_TT_STR &&
5237 JimParserTtype(&parser) != JIM_TT_ESC)
5238 continue;
5239 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5240 elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5241 ListAppendElement(objPtr, elementPtr);
5242 }
5243 return JIM_OK;
5244 }
5245
5246 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
5247 int len)
5248 {
5249 Jim_Obj *objPtr;
5250 int i;
5251
5252 objPtr = Jim_NewObj(interp);
5253 objPtr->typePtr = &listObjType;
5254 objPtr->bytes = NULL;
5255 objPtr->internalRep.listValue.ele = NULL;
5256 objPtr->internalRep.listValue.len = 0;
5257 objPtr->internalRep.listValue.maxLen = 0;
5258 for (i = 0; i < len; i++) {
5259 ListAppendElement(objPtr, elements[i]);
5260 }
5261 return objPtr;
5262 }
5263
5264 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5265 * length of the vector. Note that the user of this function should make
5266 * sure that the list object can't shimmer while the vector returned
5267 * is in use, this vector is the one stored inside the internal representation
5268 * of the list object. This function is not exported, extensions should
5269 * always access to the List object elements using Jim_ListIndex(). */
5270 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5271 Jim_Obj ***listVec)
5272 {
5273 Jim_ListLength(interp, listObj, argc);
5274 assert(listObj->typePtr == &listObjType);
5275 *listVec = listObj->internalRep.listValue.ele;
5276 }
5277
5278 /* ListSortElements type values */
5279 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5280 JIM_LSORT_NOCASE_DECR};
5281
5282 /* Sort the internal rep of a list. */
5283 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5284 {
5285 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5286 }
5287
5288 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5289 {
5290 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5291 }
5292
5293 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5294 {
5295 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5296 }
5297
5298 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5299 {
5300 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5301 }
5302
5303 /* Sort a list *in place*. MUST be called with non-shared objects. */
5304 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5305 {
5306 typedef int (qsort_comparator)(const void *, const void *);
5307 int (*fn)(Jim_Obj**, Jim_Obj**);
5308 Jim_Obj **vector;
5309 int len;
5310
5311 if (Jim_IsShared(listObjPtr))
5312 Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5313 if (listObjPtr->typePtr != &listObjType)
5314 SetListFromAny(interp, listObjPtr);
5315
5316 vector = listObjPtr->internalRep.listValue.ele;
5317 len = listObjPtr->internalRep.listValue.len;
5318 switch (type) {
5319 case JIM_LSORT_ASCII: fn = ListSortString; break;
5320 case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
5321 case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
5322 case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
5323 default:
5324 fn = NULL; /* avoid warning */
5325 Jim_Panic(interp,"ListSort called with invalid sort type");
5326 }
5327 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5328 Jim_InvalidateStringRep(listObjPtr);
5329 }
5330
5331 /* This is the low-level function to append an element to a list.
5332 * The higher-level Jim_ListAppendElement() performs shared object
5333 * check and invalidate the string repr. This version is used
5334 * in the internals of the List Object and is not exported.
5335 *
5336 * NOTE: this function can be called only against objects
5337 * with internal type of List. */
5338 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5339 {
5340 int requiredLen = listPtr->internalRep.listValue.len + 1;
5341
5342 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5343 int maxLen = requiredLen * 2;
5344
5345 listPtr->internalRep.listValue.ele =
5346 Jim_Realloc(listPtr->internalRep.listValue.ele,
5347 sizeof(Jim_Obj*)*maxLen);
5348 listPtr->internalRep.listValue.maxLen = maxLen;
5349 }
5350 listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5351 objPtr;
5352 listPtr->internalRep.listValue.len ++;
5353 Jim_IncrRefCount(objPtr);
5354 }
5355
5356 /* This is the low-level function to insert elements into a list.
5357 * The higher-level Jim_ListInsertElements() performs shared object
5358 * check and invalidate the string repr. This version is used
5359 * in the internals of the List Object and is not exported.
5360 *
5361 * NOTE: this function can be called only against objects
5362 * with internal type of List. */
5363 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5364 Jim_Obj *const *elemVec)
5365 {
5366 int currentLen = listPtr->internalRep.listValue.len;
5367 int requiredLen = currentLen + elemc;
5368 int i;
5369 Jim_Obj **point;
5370
5371 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5372 int maxLen = requiredLen * 2;
5373
5374 listPtr->internalRep.listValue.ele =
5375 Jim_Realloc(listPtr->internalRep.listValue.ele,
5376 sizeof(Jim_Obj*)*maxLen);
5377 listPtr->internalRep.listValue.maxLen = maxLen;
5378 }
5379 point = listPtr->internalRep.listValue.ele + index;
5380 memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5381 for (i=0; i < elemc; ++i) {
5382 point[i] = elemVec[i];
5383 Jim_IncrRefCount(point[i]);
5384 }
5385 listPtr->internalRep.listValue.len += elemc;
5386 }
5387
5388 /* Appends every element of appendListPtr into listPtr.
5389 * Both have to be of the list type. */
5390 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5391 {
5392 int i, oldLen = listPtr->internalRep.listValue.len;
5393 int appendLen = appendListPtr->internalRep.listValue.len;
5394 int requiredLen = oldLen + appendLen;
5395
5396 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5397 int maxLen = requiredLen * 2;
5398
5399 listPtr->internalRep.listValue.ele =
5400 Jim_Realloc(listPtr->internalRep.listValue.ele,
5401 sizeof(Jim_Obj*)*maxLen);
5402 listPtr->internalRep.listValue.maxLen = maxLen;
5403 }
5404 for (i = 0; i < appendLen; i++) {
5405 Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5406 listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5407 Jim_IncrRefCount(objPtr);
5408 }
5409 listPtr->internalRep.listValue.len += appendLen;
5410 }
5411
5412 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5413 {
5414 if (Jim_IsShared(listPtr))
5415 Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5416 if (listPtr->typePtr != &listObjType)
5417 SetListFromAny(interp, listPtr);
5418 Jim_InvalidateStringRep(listPtr);
5419 ListAppendElement(listPtr, objPtr);
5420 }
5421
5422 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5423 {
5424 if (Jim_IsShared(listPtr))
5425 Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5426 if (listPtr->typePtr != &listObjType)
5427 SetListFromAny(interp, listPtr);
5428 Jim_InvalidateStringRep(listPtr);
5429 ListAppendList(listPtr, appendListPtr);
5430 }
5431
5432 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5433 {
5434 if (listPtr->typePtr != &listObjType)
5435 SetListFromAny(interp, listPtr);
5436 *intPtr = listPtr->internalRep.listValue.len;
5437 }
5438
5439 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5440 int objc, Jim_Obj *const *objVec)
5441 {
5442 if (Jim_IsShared(listPtr))
5443 Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5444 if (listPtr->typePtr != &listObjType)
5445 SetListFromAny(interp, listPtr);
5446 if (index >= 0 && index > listPtr->internalRep.listValue.len)
5447 index = listPtr->internalRep.listValue.len;
5448 else if (index < 0 )
5449 index = 0;
5450 Jim_InvalidateStringRep(listPtr);
5451 ListInsertElements(listPtr, index, objc, objVec);
5452 }
5453
5454 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5455 Jim_Obj **objPtrPtr, int flags)
5456 {
5457 if (listPtr->typePtr != &listObjType)
5458 SetListFromAny(interp, listPtr);
5459 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5460 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5461 if (flags & JIM_ERRMSG) {
5462 Jim_SetResultString(interp,
5463 "list index out of range", -1);
5464 }
5465 return JIM_ERR;
5466 }
5467 if (index < 0)
5468 index = listPtr->internalRep.listValue.len+index;
5469 *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5470 return JIM_OK;
5471 }
5472
5473 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5474 Jim_Obj *newObjPtr, int flags)
5475 {
5476 if (listPtr->typePtr != &listObjType)
5477 SetListFromAny(interp, listPtr);
5478 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5479 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5480 if (flags & JIM_ERRMSG) {
5481 Jim_SetResultString(interp,
5482 "list index out of range", -1);
5483 }
5484 return JIM_ERR;
5485 }
5486 if (index < 0)
5487 index = listPtr->internalRep.listValue.len+index;
5488 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5489 listPtr->internalRep.listValue.ele[index] = newObjPtr;
5490 Jim_IncrRefCount(newObjPtr);
5491 return JIM_OK;
5492 }
5493
5494 /* Modify the list stored into the variable named 'varNamePtr'
5495 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5496 * with the new element 'newObjptr'. */
5497 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5498 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5499 {
5500 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5501 int shared, i, index;
5502
5503 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5504 if (objPtr == NULL)
5505 return JIM_ERR;
5506 if ((shared = Jim_IsShared(objPtr)))
5507 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5508 for (i = 0; i < indexc-1; i++) {
5509 listObjPtr = objPtr;
5510 if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5511 goto err;
5512 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5513 JIM_ERRMSG) != JIM_OK) {
5514 goto err;
5515 }
5516 if (Jim_IsShared(objPtr)) {
5517 objPtr = Jim_DuplicateObj(interp, objPtr);
5518 ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5519 }
5520 Jim_InvalidateStringRep(listObjPtr);
5521 }
5522 if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5523 goto err;
5524 if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5525 goto err;
5526 Jim_InvalidateStringRep(objPtr);
5527 Jim_InvalidateStringRep(varObjPtr);
5528 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5529 goto err;
5530 Jim_SetResult(interp, varObjPtr);
5531 return JIM_OK;
5532 err:
5533 if (shared) {
5534 Jim_FreeNewObj(interp, varObjPtr);
5535 }
5536 return JIM_ERR;
5537 }
5538
5539 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5540 {
5541 int i;
5542
5543 /* If all the objects in objv are lists without string rep.
5544 * it's possible to return a list as result, that's the
5545 * concatenation of all the lists. */
5546 for (i = 0; i < objc; i++) {
5547 if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5548 break;
5549 }
5550 if (i == objc) {
5551 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5552 for (i = 0; i < objc; i++)
5553 Jim_ListAppendList(interp, objPtr, objv[i]);
5554 return objPtr;
5555 } else {
5556 /* Else... we have to glue strings together */
5557 int len = 0, objLen;
5558 char *bytes, *p;
5559
5560 /* Compute the length */
5561 for (i = 0; i < objc; i++) {
5562 Jim_GetString(objv[i], &objLen);
5563 len += objLen;
5564 }
5565 if (objc) len += objc-1;
5566 /* Create the string rep, and a stinrg object holding it. */
5567 p = bytes = Jim_Alloc(len+1);
5568 for (i = 0; i < objc; i++) {
5569 const char *s = Jim_GetString(objv[i], &objLen);
5570 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5571 {
5572 s++; objLen--; len--;
5573 }
5574 while (objLen && (s[objLen-1] == ' ' ||
5575 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5576 objLen--; len--;
5577 }
5578 memcpy(p, s, objLen);
5579 p += objLen;
5580 if (objLen && i+1 != objc) {
5581 *p++ = ' ';
5582 } else if (i+1 != objc) {
5583 /* Drop the space calcuated for this
5584 * element that is instead null. */
5585 len--;
5586 }
5587 }
5588 *p = '\0';
5589 return Jim_NewStringObjNoAlloc(interp, bytes, len);
5590 }
5591 }
5592
5593 /* Returns a list composed of the elements in the specified range.
5594 * first and start are directly accepted as Jim_Objects and
5595 * processed for the end?-index? case. */
5596 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5597 {
5598 int first, last;
5599 int len, rangeLen;
5600
5601 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5602 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5603 return NULL;
5604 Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5605 first = JimRelToAbsIndex(len, first);
5606 last = JimRelToAbsIndex(len, last);
5607 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5608 return Jim_NewListObj(interp,
5609 listObjPtr->internalRep.listValue.ele+first, rangeLen);
5610 }
5611
5612 /* -----------------------------------------------------------------------------
5613 * Dict object
5614 * ---------------------------------------------------------------------------*/
5615 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5616 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5617 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5618 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5619
5620 /* Dict HashTable Type.
5621 *
5622 * Keys and Values are Jim objects. */
5623
5624 unsigned int JimObjectHTHashFunction(const void *key)
5625 {
5626 const char *str;
5627 Jim_Obj *objPtr = (Jim_Obj*) key;
5628 int len, h;
5629
5630 str = Jim_GetString(objPtr, &len);
5631 h = Jim_GenHashFunction((unsigned char*)str, len);
5632 return h;
5633 }
5634
5635 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5636 {
5637 JIM_NOTUSED(privdata);
5638
5639 return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5640 }
5641
5642 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5643 {
5644 Jim_Obj *objPtr = val;
5645
5646 Jim_DecrRefCount(interp, objPtr);
5647 }
5648
5649 static Jim_HashTableType JimDictHashTableType = {
5650 JimObjectHTHashFunction, /* hash function */
5651 NULL, /* key dup */
5652 NULL, /* val dup */
5653 JimObjectHTKeyCompare, /* key compare */
5654 (void(*)(void*, const void*)) /* ATTENTION: const cast */
5655 JimObjectHTKeyValDestructor, /* key destructor */
5656 JimObjectHTKeyValDestructor /* val destructor */
5657 };
5658
5659 /* Note that while the elements of the dict may contain references,
5660 * the list object itself can't. This basically means that the
5661 * dict object string representation as a whole can't contain references
5662 * that are not presents in the single elements. */
5663 static Jim_ObjType dictObjType = {
5664 "dict",
5665 FreeDictInternalRep,
5666 DupDictInternalRep,
5667 UpdateStringOfDict,
5668 JIM_TYPE_NONE,
5669 };
5670
5671 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5672 {
5673 JIM_NOTUSED(interp);
5674
5675 Jim_FreeHashTable(objPtr->internalRep.ptr);
5676 Jim_Free(objPtr->internalRep.ptr);
5677 }
5678
5679 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5680 {
5681 Jim_HashTable *ht, *dupHt;
5682 Jim_HashTableIterator *htiter;
5683 Jim_HashEntry *he;
5684
5685 /* Create a new hash table */
5686 ht = srcPtr->internalRep.ptr;
5687 dupHt = Jim_Alloc(sizeof(*dupHt));
5688 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5689 if (ht->size != 0)
5690 Jim_ExpandHashTable(dupHt, ht->size);
5691 /* Copy every element from the source to the dup hash table */
5692 htiter = Jim_GetHashTableIterator(ht);
5693 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5694 const Jim_Obj *keyObjPtr = he->key;
5695 Jim_Obj *valObjPtr = he->val;
5696
5697 Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
5698 Jim_IncrRefCount(valObjPtr);
5699 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5700 }
5701 Jim_FreeHashTableIterator(htiter);
5702
5703 dupPtr->internalRep.ptr = dupHt;
5704 dupPtr->typePtr = &dictObjType;
5705 }
5706
5707 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5708 {
5709 int i, bufLen, realLength;
5710 const char *strRep;
5711 char *p;
5712 int *quotingType, objc;
5713 Jim_HashTable *ht;
5714 Jim_HashTableIterator *htiter;
5715 Jim_HashEntry *he;
5716 Jim_Obj **objv;
5717
5718 /* Trun the hash table into a flat vector of Jim_Objects. */
5719 ht = objPtr->internalRep.ptr;
5720 objc = ht->used*2;
5721 objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5722 htiter = Jim_GetHashTableIterator(ht);
5723 i = 0;
5724 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5725 objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
5726 objv[i++] = he->val;
5727 }
5728 Jim_FreeHashTableIterator(htiter);
5729 /* (Over) Estimate the space needed. */
5730 quotingType = Jim_Alloc(sizeof(int)*objc);
5731 bufLen = 0;
5732 for (i = 0; i < objc; i++) {
5733 int len;
5734
5735 strRep = Jim_GetString(objv[i], &len);
5736 quotingType[i] = ListElementQuotingType(strRep, len);
5737 switch (quotingType[i]) {
5738 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5739 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5740 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5741 }
5742 bufLen++; /* elements separator. */
5743 }
5744 bufLen++;
5745
5746 /* Generate the string rep. */
5747 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5748 realLength = 0;
5749 for (i = 0; i < objc; i++) {
5750 int len, qlen;
5751 const char *strRep = Jim_GetString(objv[i], &len);
5752 char *q;
5753
5754 switch(quotingType[i]) {
5755 case JIM_ELESTR_SIMPLE:
5756 memcpy(p, strRep, len);
5757 p += len;
5758 realLength += len;
5759 break;
5760 case JIM_ELESTR_BRACE:
5761 *p++ = '{';
5762 memcpy(p, strRep, len);
5763 p += len;
5764 *p++ = '}';
5765 realLength += len+2;
5766 break;
5767 case JIM_ELESTR_QUOTE:
5768 q = BackslashQuoteString(strRep, len, &qlen);
5769 memcpy(p, q, qlen);
5770 Jim_Free(q);
5771 p += qlen;
5772 realLength += qlen;
5773 break;
5774 }
5775 /* Add a separating space */
5776 if (i+1 != objc) {
5777 *p++ = ' ';
5778 realLength ++;
5779 }
5780 }
5781 *p = '\0'; /* nul term. */
5782 objPtr->length = realLength;
5783 Jim_Free(quotingType);
5784 Jim_Free(objv);
5785 }
5786
5787 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5788 {
5789 struct JimParserCtx parser;
5790 Jim_HashTable *ht;
5791 Jim_Obj *objv[2];
5792 const char *str;
5793 int i, strLen;
5794
5795 /* Get the string representation */
5796 str = Jim_GetString(objPtr, &strLen);
5797
5798 /* Free the old internal repr just now and initialize the
5799 * new one just now. The string->list conversion can't fail. */
5800 Jim_FreeIntRep(interp, objPtr);
5801 ht = Jim_Alloc(sizeof(*ht));
5802 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5803 objPtr->typePtr = &dictObjType;
5804 objPtr->internalRep.ptr = ht;
5805
5806 /* Convert into a dict */
5807 JimParserInit(&parser, str, strLen, 1);
5808 i = 0;
5809 while(!JimParserEof(&parser)) {
5810 char *token;
5811 int tokenLen, type;
5812
5813 JimParseList(&parser);
5814 if (JimParserTtype(&parser) != JIM_TT_STR &&
5815 JimParserTtype(&parser) != JIM_TT_ESC)
5816 continue;
5817 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5818 objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5819 if (i == 2) {
5820 i = 0;
5821 Jim_IncrRefCount(objv[0]);
5822 Jim_IncrRefCount(objv[1]);
5823 if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5824 Jim_HashEntry *he;
5825 he = Jim_FindHashEntry(ht, objv[0]);
5826 Jim_DecrRefCount(interp, objv[0]);
5827 /* ATTENTION: const cast */
5828 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5829 he->val = objv[1];
5830 }
5831 }
5832 }
5833 if (i) {
5834 Jim_FreeNewObj(interp, objv[0]);
5835 objPtr->typePtr = NULL;
5836 Jim_FreeHashTable(ht);
5837 Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5838 return JIM_ERR;
5839 }
5840 return JIM_OK;
5841 }
5842
5843 /* Dict object API */
5844
5845 /* Add an element to a dict. objPtr must be of the "dict" type.
5846 * The higer-level exported function is Jim_DictAddElement().
5847 * If an element with the specified key already exists, the value
5848 * associated is replaced with the new one.
5849 *
5850 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5851 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5852 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5853 {
5854 Jim_HashTable *ht = objPtr->internalRep.ptr;
5855
5856 if (valueObjPtr == NULL) { /* unset */
5857 Jim_DeleteHashEntry(ht, keyObjPtr);
5858 return;
5859 }
5860 Jim_IncrRefCount(keyObjPtr);
5861 Jim_IncrRefCount(valueObjPtr);
5862 if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5863 Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5864 Jim_DecrRefCount(interp, keyObjPtr);
5865 /* ATTENTION: const cast */
5866 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5867 he->val = valueObjPtr;
5868 }
5869 }
5870
5871 /* Add an element, higher-level interface for DictAddElement().
5872 * If valueObjPtr == NULL, the key is removed if it exists. */
5873 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5874 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5875 {
5876 if (Jim_IsShared(objPtr))
5877 Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5878 if (objPtr->typePtr != &dictObjType) {
5879 if (SetDictFromAny(interp, objPtr) != JIM_OK)
5880 return JIM_ERR;
5881 }
5882 DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5883 Jim_InvalidateStringRep(objPtr);
5884 return JIM_OK;
5885 }
5886
5887 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5888 {
5889 Jim_Obj *objPtr;
5890 int i;
5891
5892 if (len % 2)
5893 Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5894
5895 objPtr = Jim_NewObj(interp);
5896 objPtr->typePtr = &dictObjType;
5897 objPtr->bytes = NULL;
5898 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5899 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5900 for (i = 0; i < len; i += 2)
5901 DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5902 return objPtr;
5903 }
5904
5905 /* Return the value associated to the specified dict key */
5906 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5907 Jim_Obj **objPtrPtr, int flags)
5908 {
5909 Jim_HashEntry *he;
5910 Jim_HashTable *ht;
5911
5912 if (dictPtr->typePtr != &dictObjType) {
5913 if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5914 return JIM_ERR;
5915 }
5916 ht = dictPtr->internalRep.ptr;
5917 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5918 if (flags & JIM_ERRMSG) {
5919 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5920 Jim_AppendStrings(interp, Jim_GetResult(interp),
5921 "key \"", Jim_GetString(keyPtr, NULL),
5922 "\" not found in dictionary", NULL);
5923 }
5924 return JIM_ERR;
5925 }
5926 *objPtrPtr = he->val;
5927 return JIM_OK;
5928 }
5929
5930 /* Return the value associated to the specified dict keys */
5931 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5932 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5933 {
5934 Jim_Obj *objPtr;
5935 int i;
5936
5937 if (keyc == 0) {
5938 *objPtrPtr = dictPtr;
5939 return JIM_OK;
5940 }
5941
5942 for (i = 0; i < keyc; i++) {
5943 if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5944 != JIM_OK)
5945 return JIM_ERR;
5946 dictPtr = objPtr;
5947 }
5948 *objPtrPtr = objPtr;
5949 return JIM_OK;
5950 }
5951
5952 /* Modify the dict stored into the variable named 'varNamePtr'
5953 * setting the element specified by the 'keyc' keys objects in 'keyv',
5954 * with the new value of the element 'newObjPtr'.
5955 *
5956 * If newObjPtr == NULL the operation is to remove the given key
5957 * from the dictionary. */
5958 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5959 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5960 {
5961 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5962 int shared, i;
5963
5964 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5965 if (objPtr == NULL) {
5966 if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5967 return JIM_ERR;
5968 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5969 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5970 Jim_FreeNewObj(interp, varObjPtr);
5971 return JIM_ERR;
5972 }
5973 }
5974 if ((shared = Jim_IsShared(objPtr)))
5975 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5976 for (i = 0; i < keyc-1; i++) {
5977 dictObjPtr = objPtr;
5978
5979 /* Check if it's a valid dictionary */
5980 if (dictObjPtr->typePtr != &dictObjType) {
5981 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5982 goto err;
5983 }
5984 /* Check if the given key exists. */
5985 Jim_InvalidateStringRep(dictObjPtr);
5986 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5987 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5988 {
5989 /* This key exists at the current level.
5990 * Make sure it's not shared!. */
5991 if (Jim_IsShared(objPtr)) {
5992 objPtr = Jim_DuplicateObj(interp, objPtr);
5993 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5994 }
5995 } else {
5996 /* Key not found. If it's an [unset] operation
5997 * this is an error. Only the last key may not
5998 * exist. */
5999 if (newObjPtr == NULL)
6000 goto err;
6001 /* Otherwise set an empty dictionary
6002 * as key's value. */
6003 objPtr = Jim_NewDictObj(interp, NULL, 0);
6004 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
6005 }
6006 }
6007 if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
6008 != JIM_OK)
6009 goto err;
6010 Jim_InvalidateStringRep(objPtr);
6011 Jim_InvalidateStringRep(varObjPtr);
6012 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
6013 goto err;
6014 Jim_SetResult(interp, varObjPtr);
6015 return JIM_OK;
6016 err:
6017 if (shared) {
6018 Jim_FreeNewObj(interp, varObjPtr);
6019 }
6020 return JIM_ERR;
6021 }
6022
6023 /* -----------------------------------------------------------------------------
6024 * Index object
6025 * ---------------------------------------------------------------------------*/
6026 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
6027 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6028
6029 static Jim_ObjType indexObjType = {
6030 "index",
6031 NULL,
6032 NULL,
6033 UpdateStringOfIndex,
6034 JIM_TYPE_NONE,
6035 };
6036
6037 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
6038 {
6039 int len;
6040 char buf[JIM_INTEGER_SPACE+1];
6041
6042 if (objPtr->internalRep.indexValue >= 0)
6043 len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
6044 else if (objPtr->internalRep.indexValue == -1)
6045 len = sprintf(buf, "end");
6046 else {
6047 len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
6048 }
6049 objPtr->bytes = Jim_Alloc(len+1);
6050 memcpy(objPtr->bytes, buf, len+1);
6051 objPtr->length = len;
6052 }
6053
6054 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6055 {
6056 int index, end = 0;
6057 const char *str;
6058
6059 /* Get the string representation */
6060 str = Jim_GetString(objPtr, NULL);
6061 /* Try to convert into an index */
6062 if (!strcmp(str, "end")) {
6063 index = 0;
6064 end = 1;
6065 } else {
6066 if (!strncmp(str, "end-", 4)) {
6067 str += 4;
6068 end = 1;
6069 }
6070 if (Jim_StringToIndex(str, &index) != JIM_OK) {
6071 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6072 Jim_AppendStrings(interp, Jim_GetResult(interp),
6073 "bad index \"", Jim_GetString(objPtr, NULL), "\": "
6074 "must be integer or end?-integer?", NULL);
6075 return JIM_ERR;
6076 }
6077 }
6078 if (end) {
6079 if (index < 0)
6080 index = INT_MAX;
6081 else
6082 index = -(index+1);
6083 } else if (!end && index < 0)
6084 index = -INT_MAX;
6085 /* Free the old internal repr and set the new one. */
6086 Jim_FreeIntRep(interp, objPtr);
6087 objPtr->typePtr = &indexObjType;
6088 objPtr->internalRep.indexValue = index;
6089 return JIM_OK;
6090 }
6091
6092 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
6093 {
6094 /* Avoid shimmering if the object is an integer. */
6095 if (objPtr->typePtr == &intObjType) {
6096 jim_wide val = objPtr->internalRep.wideValue;
6097 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
6098 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
6099 return JIM_OK;
6100 }
6101 }
6102 if (objPtr->typePtr != &indexObjType &&
6103 SetIndexFromAny(interp, objPtr) == JIM_ERR)
6104 return JIM_ERR;
6105 *indexPtr = objPtr->internalRep.indexValue;
6106 return JIM_OK;
6107 }
6108
6109 /* -----------------------------------------------------------------------------
6110 * Return Code Object.
6111 * ---------------------------------------------------------------------------*/
6112
6113 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6114
6115 static Jim_ObjType returnCodeObjType = {
6116 "return-code",
6117 NULL,
6118 NULL,
6119 NULL,
6120 JIM_TYPE_NONE,
6121 };
6122
6123 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6124 {
6125 const char *str;
6126 int strLen, returnCode;
6127 jim_wide wideValue;
6128
6129 /* Get the string representation */
6130 str = Jim_GetString(objPtr, &strLen);
6131 /* Try to convert into an integer */
6132 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
6133 returnCode = (int) wideValue;
6134 else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
6135 returnCode = JIM_OK;
6136 else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
6137 returnCode = JIM_ERR;
6138 else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
6139 returnCode = JIM_RETURN;
6140 else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
6141 returnCode = JIM_BREAK;
6142 else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
6143 returnCode = JIM_CONTINUE;
6144 else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
6145 returnCode = JIM_EVAL;
6146 else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
6147 returnCode = JIM_EXIT;
6148 else {
6149 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6150 Jim_AppendStrings(interp, Jim_GetResult(interp),
6151 "expected return code but got '", str, "'",
6152 NULL);
6153 return JIM_ERR;
6154 }
6155 /* Free the old internal repr and set the new one. */
6156 Jim_FreeIntRep(interp, objPtr);
6157 objPtr->typePtr = &returnCodeObjType;
6158 objPtr->internalRep.returnCode = returnCode;
6159 return JIM_OK;
6160 }
6161
6162 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
6163 {
6164 if (objPtr->typePtr != &returnCodeObjType &&
6165 SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
6166 return JIM_ERR;
6167 *intPtr = objPtr->internalRep.returnCode;
6168 return JIM_OK;
6169 }
6170
6171 /* -----------------------------------------------------------------------------
6172 * Expression Parsing
6173 * ---------------------------------------------------------------------------*/
6174 static int JimParseExprOperator(struct JimParserCtx *pc);
6175 static int JimParseExprNumber(struct JimParserCtx *pc);
6176 static int JimParseExprIrrational(struct JimParserCtx *pc);
6177
6178 /* Exrp's Stack machine operators opcodes. */
6179
6180 /* Binary operators (numbers) */
6181 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
6182 #define JIM_EXPROP_MUL 0
6183 #define JIM_EXPROP_DIV 1
6184 #define JIM_EXPROP_MOD 2
6185 #define JIM_EXPROP_SUB 3
6186 #define JIM_EXPROP_ADD 4
6187 #define JIM_EXPROP_LSHIFT 5
6188 #define JIM_EXPROP_RSHIFT 6
6189 #define JIM_EXPROP_ROTL 7
6190 #define JIM_EXPROP_ROTR 8
6191 #define JIM_EXPROP_LT 9
6192 #define JIM_EXPROP_GT 10
6193 #define JIM_EXPROP_LTE 11
6194 #define JIM_EXPROP_GTE 12
6195 #define JIM_EXPROP_NUMEQ 13
6196 #define JIM_EXPROP_NUMNE 14
6197 #define JIM_EXPROP_BITAND 15
6198 #define JIM_EXPROP_BITXOR 16
6199 #define JIM_EXPROP_BITOR 17
6200 #define JIM_EXPROP_LOGICAND 18
6201 #define JIM_EXPROP_LOGICOR 19
6202 #define JIM_EXPROP_LOGICAND_LEFT 20
6203 #define JIM_EXPROP_LOGICOR_LEFT 21
6204 #define JIM_EXPROP_POW 22
6205 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6206
6207 /* Binary operators (strings) */
6208 #define JIM_EXPROP_STREQ 23
6209 #define JIM_EXPROP_STRNE 24
6210
6211 /* Unary operators (numbers) */
6212 #define JIM_EXPROP_NOT 25
6213 #define JIM_EXPROP_BITNOT 26
6214 #define JIM_EXPROP_UNARYMINUS 27
6215 #define JIM_EXPROP_UNARYPLUS 28
6216 #define JIM_EXPROP_LOGICAND_RIGHT 29
6217 #define JIM_EXPROP_LOGICOR_RIGHT 30
6218
6219 /* Ternary operators */
6220 #define JIM_EXPROP_TERNARY 31
6221
6222 /* Operands */
6223 #define JIM_EXPROP_NUMBER 32
6224 #define JIM_EXPROP_COMMAND 33
6225 #define JIM_EXPROP_VARIABLE 34
6226 #define JIM_EXPROP_DICTSUGAR 35
6227 #define JIM_EXPROP_SUBST 36
6228 #define JIM_EXPROP_STRING 37
6229
6230 /* Operators table */
6231 typedef struct Jim_ExprOperator {
6232 const char *name;
6233 int precedence;
6234 int arity;
6235 int opcode;
6236 } Jim_ExprOperator;
6237
6238 /* name - precedence - arity - opcode */
6239 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6240 {"!", 300, 1, JIM_EXPROP_NOT},
6241 {"~", 300, 1, JIM_EXPROP_BITNOT},
6242 {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6243 {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6244
6245 {"**", 250, 2, JIM_EXPROP_POW},
6246
6247 {"*", 200, 2, JIM_EXPROP_MUL},
6248 {"/", 200, 2, JIM_EXPROP_DIV},
6249 {"%", 200, 2, JIM_EXPROP_MOD},
6250
6251 {"-", 100, 2, JIM_EXPROP_SUB},
6252 {"+", 100, 2, JIM_EXPROP_ADD},
6253
6254 {"<<<", 90, 3, JIM_EXPROP_ROTL},
6255 {">>>", 90, 3, JIM_EXPROP_ROTR},
6256 {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6257 {">>", 90, 2, JIM_EXPROP_RSHIFT},
6258
6259 {"<", 80, 2, JIM_EXPROP_LT},
6260 {">", 80, 2, JIM_EXPROP_GT},
6261 {"<=", 80, 2, JIM_EXPROP_LTE},
6262 {">=", 80, 2, JIM_EXPROP_GTE},
6263
6264 {"==", 70, 2, JIM_EXPROP_NUMEQ},
6265 {"!=", 70, 2, JIM_EXPROP_NUMNE},
6266
6267 {"eq", 60, 2, JIM_EXPROP_STREQ},
6268 {"ne", 60, 2, JIM_EXPROP_STRNE},
6269
6270 {"&", 50, 2, JIM_EXPROP_BITAND},
6271 {"^", 49, 2, JIM_EXPROP_BITXOR},
6272 {"|", 48, 2, JIM_EXPROP_BITOR},
6273
6274 {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6275 {"||", 10, 2, JIM_EXPROP_LOGICOR},
6276
6277 {"?", 5, 3, JIM_EXPROP_TERNARY},
6278 /* private operators */
6279 {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6280 {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6281 {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6282 {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6283 };
6284
6285 #define JIM_EXPR_OPERATORS_NUM \
6286 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6287
6288 int JimParseExpression(struct JimParserCtx *pc)
6289 {
6290 /* Discard spaces and quoted newline */
6291 while(*(pc->p) == ' ' ||
6292 *(pc->p) == '\t' ||
6293 *(pc->p) == '\r' ||
6294 *(pc->p) == '\n' ||
6295 (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6296 pc->p++; pc->len--;
6297 }
6298
6299 if (pc->len == 0) {
6300 pc->tstart = pc->tend = pc->p;
6301 pc->tline = pc->linenr;
6302 pc->tt = JIM_TT_EOL;
6303 pc->eof = 1;
6304 return JIM_OK;
6305 }
6306 switch(*(pc->p)) {
6307 case '(':
6308 pc->tstart = pc->tend = pc->p;
6309 pc->tline = pc->linenr;
6310 pc->tt = JIM_TT_SUBEXPR_START;
6311 pc->p++; pc->len--;
6312 break;
6313 case ')':
6314 pc->tstart = pc->tend = pc->p;
6315 pc->tline = pc->linenr;
6316 pc->tt = JIM_TT_SUBEXPR_END;
6317 pc->p++; pc->len--;
6318 break;
6319 case '[':
6320 return JimParseCmd(pc);
6321 break;
6322 case '$':
6323 if (JimParseVar(pc) == JIM_ERR)
6324 return JimParseExprOperator(pc);
6325 else
6326 return JIM_OK;
6327 break;
6328 case '-':
6329 if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6330 isdigit((int)*(pc->p+1)))
6331 return JimParseExprNumber(pc);
6332 else
6333 return JimParseExprOperator(pc);
6334 break;
6335 case '0': case '1': case '2': case '3': case '4':
6336 case '5': case '6': case '7': case '8': case '9': case '.':
6337 return JimParseExprNumber(pc);
6338 break;
6339 case '"':
6340 case '{':
6341 /* Here it's possible to reuse the List String parsing. */
6342 pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6343 return JimParseListStr(pc);
6344 break;
6345 case 'N': case 'I':
6346 case 'n': case 'i':
6347 if (JimParseExprIrrational(pc) == JIM_ERR)
6348 return JimParseExprOperator(pc);
6349 break;
6350 default:
6351 return JimParseExprOperator(pc);
6352 break;
6353 }
6354 return JIM_OK;
6355 }
6356
6357 int JimParseExprNumber(struct JimParserCtx *pc)
6358 {
6359 int allowdot = 1;
6360 int allowhex = 0;
6361
6362 pc->tstart = pc->p;
6363 pc->tline = pc->linenr;
6364 if (*pc->p == '-') {
6365 pc->p++; pc->len--;
6366 }
6367 while ( isdigit((int)*pc->p)
6368 || (allowhex && isxdigit((int)*pc->p) )
6369 || (allowdot && *pc->p == '.')
6370 || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6371 (*pc->p == 'x' || *pc->p == 'X'))
6372 )
6373 {
6374 if ((*pc->p == 'x') || (*pc->p == 'X')) {
6375 allowhex = 1;
6376 allowdot = 0;
6377 }
6378 if (*pc->p == '.')
6379 allowdot = 0;
6380 pc->p++; pc->len--;
6381 if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6382 pc->p += 2; pc->len -= 2;
6383 }
6384 }
6385 pc->tend = pc->p-1;
6386 pc->tt = JIM_TT_EXPR_NUMBER;
6387 return JIM_OK;
6388 }
6389
6390 int JimParseExprIrrational(struct JimParserCtx *pc)
6391 {
6392 const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6393 const char **token;
6394 for (token = Tokens; *token != NULL; token++) {
6395 int len = strlen(*token);
6396 if (strncmp(*token, pc->p, len) == 0) {
6397 pc->tstart = pc->p;
6398 pc->tend = pc->p + len - 1;
6399 pc->p += len; pc->len -= len;
6400 pc->tline = pc->linenr;
6401 pc->tt = JIM_TT_EXPR_NUMBER;
6402 return JIM_OK;
6403 }
6404 }
6405 return JIM_ERR;
6406 }
6407
6408 int JimParseExprOperator(struct JimParserCtx *pc)
6409 {
6410 int i;
6411 int bestIdx = -1, bestLen = 0;
6412
6413 /* Try to get the longest match. */
6414 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6415 const char *opname;
6416 int oplen;
6417
6418 opname = Jim_ExprOperators[i].name;
6419 if (opname == NULL) continue;
6420 oplen = strlen(opname);
6421
6422 if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6423 bestIdx = i;
6424 bestLen = oplen;
6425 }
6426 }
6427 if (bestIdx == -1) return JIM_ERR;
6428 pc->tstart = pc->p;
6429 pc->tend = pc->p + bestLen - 1;
6430 pc->p += bestLen; pc->len -= bestLen;
6431 pc->tline = pc->linenr;
6432 pc->tt = JIM_TT_EXPR_OPERATOR;
6433 return JIM_OK;
6434 }
6435
6436 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6437 {
6438 int i;
6439 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6440 if (Jim_ExprOperators[i].name &&
6441 strcmp(opname, Jim_ExprOperators[i].name) == 0)
6442 return &Jim_ExprOperators[i];
6443 return NULL;
6444 }
6445
6446 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6447 {
6448 int i;
6449 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6450 if (Jim_ExprOperators[i].opcode == opcode)
6451 return &Jim_ExprOperators[i];
6452 return NULL;
6453 }
6454
6455 /* -----------------------------------------------------------------------------
6456 * Expression Object
6457 * ---------------------------------------------------------------------------*/
6458 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6459 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6460 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6461
6462 static Jim_ObjType exprObjType = {
6463 "expression",
6464 FreeExprInternalRep,
6465 DupExprInternalRep,
6466 NULL,
6467 JIM_TYPE_REFERENCES,
6468 };
6469
6470 /* Expr bytecode structure */
6471 typedef struct ExprByteCode {
6472 int *opcode; /* Integer array of opcodes. */
6473 Jim_Obj **obj; /* Array of associated Jim Objects. */
6474 int len; /* Bytecode length */
6475 int inUse; /* Used for sharing. */
6476 } ExprByteCode;
6477
6478 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6479 {
6480 int i;
6481 ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6482
6483 expr->inUse--;
6484 if (expr->inUse != 0) return;
6485 for (i = 0; i < expr->len; i++)
6486 Jim_DecrRefCount(interp, expr->obj[i]);
6487 Jim_Free(expr->opcode);
6488 Jim_Free(expr->obj);
6489 Jim_Free(expr);
6490 }
6491
6492 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6493 {
6494 JIM_NOTUSED(interp);
6495 JIM_NOTUSED(srcPtr);
6496
6497 /* Just returns an simple string. */
6498 dupPtr->typePtr = NULL;
6499 }
6500
6501 /* Add a new instruction to an expression bytecode structure. */
6502 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6503 int opcode, char *str, int len)
6504 {
6505 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6506 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6507 expr->opcode[expr->len] = opcode;
6508 expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6509 Jim_IncrRefCount(expr->obj[expr->len]);
6510 expr->len++;
6511 }
6512
6513 /* Check if an expr program looks correct. */
6514 static int ExprCheckCorrectness(ExprByteCode *expr)
6515 {
6516 int i;
6517 int stacklen = 0;
6518
6519 /* Try to check if there are stack underflows,
6520 * and make sure at the end of the program there is
6521 * a single result on the stack. */
6522 for (i = 0; i < expr->len; i++) {
6523 switch(expr->opcode[i]) {
6524 case JIM_EXPROP_NUMBER:
6525 case JIM_EXPROP_STRING:
6526 case JIM_EXPROP_SUBST:
6527 case JIM_EXPROP_VARIABLE:
6528 case JIM_EXPROP_DICTSUGAR:
6529 case JIM_EXPROP_COMMAND:
6530 stacklen++;
6531 break;
6532 case JIM_EXPROP_NOT:
6533 case JIM_EXPROP_BITNOT:
6534 case JIM_EXPROP_UNARYMINUS:
6535 case JIM_EXPROP_UNARYPLUS:
6536 /* Unary operations */
6537 if (stacklen < 1) return JIM_ERR;
6538 break;
6539 case JIM_EXPROP_ADD:
6540 case JIM_EXPROP_SUB:
6541 case JIM_EXPROP_MUL:
6542 case JIM_EXPROP_DIV:
6543 case JIM_EXPROP_MOD:
6544 case JIM_EXPROP_LT:
6545 case JIM_EXPROP_GT:
6546 case JIM_EXPROP_LTE:
6547 case JIM_EXPROP_GTE:
6548 case JIM_EXPROP_ROTL:
6549 case JIM_EXPROP_ROTR:
6550 case JIM_EXPROP_LSHIFT:
6551 case JIM_EXPROP_RSHIFT:
6552 case JIM_EXPROP_NUMEQ:
6553 case JIM_EXPROP_NUMNE:
6554 case JIM_EXPROP_STREQ:
6555 case JIM_EXPROP_STRNE:
6556 case JIM_EXPROP_BITAND:
6557 case JIM_EXPROP_BITXOR:
6558 case JIM_EXPROP_BITOR:
6559 case JIM_EXPROP_LOGICAND:
6560 case JIM_EXPROP_LOGICOR:
6561 case JIM_EXPROP_POW:
6562 /* binary operations */
6563 if (stacklen < 2) return JIM_ERR;
6564 stacklen--;
6565 break;
6566 default:
6567 Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6568 break;
6569 }
6570 }
6571 if (stacklen != 1) return JIM_ERR;
6572 return JIM_OK;
6573 }
6574
6575 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6576 ScriptObj *topLevelScript)
6577 {
6578 int i;
6579
6580 return;
6581 for (i = 0; i < expr->len; i++) {
6582 Jim_Obj *foundObjPtr;
6583
6584 if (expr->obj[i] == NULL) continue;
6585 foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6586 NULL, expr->obj[i]);
6587 if (foundObjPtr != NULL) {
6588 Jim_IncrRefCount(foundObjPtr);
6589 Jim_DecrRefCount(interp, expr->obj[i]);
6590 expr->obj[i] = foundObjPtr;
6591 }
6592 }
6593 }
6594
6595 /* This procedure converts every occurrence of || and && opereators
6596 * in lazy unary versions.
6597 *
6598 * a b || is converted into:
6599 *
6600 * a <offset> |L b |R
6601 *
6602 * a b && is converted into:
6603 *
6604 * a <offset> &L b &R
6605 *
6606 * "|L" checks if 'a' is true:
6607 * 1) if it is true pushes 1 and skips <offset> istructions to reach
6608 * the opcode just after |R.
6609 * 2) if it is false does nothing.
6610 * "|R" checks if 'b' is true:
6611 * 1) if it is true pushes 1, otherwise pushes 0.
6612 *
6613 * "&L" checks if 'a' is true:
6614 * 1) if it is true does nothing.
6615 * 2) If it is false pushes 0 and skips <offset> istructions to reach
6616 * the opcode just after &R
6617 * "&R" checks if 'a' is true:
6618 * if it is true pushes 1, otherwise pushes 0.
6619 */
6620 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6621 {
6622 while (1) {
6623 int index = -1, leftindex, arity, i, offset;
6624 Jim_ExprOperator *op;
6625
6626 /* Search for || or && */
6627 for (i = 0; i < expr->len; i++) {
6628 if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6629 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6630 index = i;
6631 break;
6632 }
6633 }
6634 if (index == -1) return;
6635 /* Search for the end of the first operator */
6636 leftindex = index-1;
6637 arity = 1;
6638 while(arity) {
6639 switch(expr->opcode[leftindex]) {
6640 case JIM_EXPROP_NUMBER:
6641 case JIM_EXPROP_COMMAND:
6642 case JIM_EXPROP_VARIABLE:
6643 case JIM_EXPROP_DICTSUGAR:
6644 case JIM_EXPROP_SUBST:
6645 case JIM_EXPROP_STRING:
6646 break;
6647 default:
6648 op = JimExprOperatorInfoByOpcode(expr->opcode[leftindex]);
6649 if (op == NULL) {
6650 Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6651 }
6652 arity += op->arity;
6653 break;
6654 }
6655 arity--;
6656 leftindex--;
6657 }
6658 leftindex++;
6659 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6660 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6661 memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6662 sizeof(int)*(expr->len-leftindex));
6663 memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6664 sizeof(Jim_Obj*)*(expr->len-leftindex));
6665 expr->len += 2;
6666 index += 2;
6667 offset = (index-leftindex)-1;
6668 Jim_DecrRefCount(interp, expr->obj[index]);
6669 if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6670 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6671 expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6672 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6673 expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6674 } else {
6675 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6676 expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6677 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6678 expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6679 }
6680 expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6681 expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6682 Jim_IncrRefCount(expr->obj[index]);
6683 Jim_IncrRefCount(expr->obj[leftindex]);
6684 Jim_IncrRefCount(expr->obj[leftindex+1]);
6685 }
6686 }
6687
6688 /* This method takes the string representation of an expression
6689 * and generates a program for the Expr's stack-based VM. */
6690 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6691 {
6692 int exprTextLen;
6693 const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6694 struct JimParserCtx parser;
6695 int i, shareLiterals;
6696 ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6697 Jim_Stack stack;
6698 Jim_ExprOperator *op;
6699
6700 /* Perform literal sharing with the current procedure
6701 * running only if this expression appears to be not generated
6702 * at runtime. */
6703 shareLiterals = objPtr->typePtr == &sourceObjType;
6704
6705 expr->opcode = NULL;
6706 expr->obj = NULL;
6707 expr->len = 0;
6708 expr->inUse = 1;
6709
6710 Jim_InitStack(&stack);
6711 JimParserInit(&parser, exprText, exprTextLen, 1);
6712 while(!JimParserEof(&parser)) {
6713 char *token;
6714 int len, type;
6715
6716 if (JimParseExpression(&parser) != JIM_OK) {
6717 Jim_SetResultString(interp, "Syntax error in expression", -1);
6718 goto err;
6719 }
6720 token = JimParserGetToken(&parser, &len, &type, NULL);
6721 if (type == JIM_TT_EOL) {
6722 Jim_Free(token);
6723 break;
6724 }
6725 switch(type) {
6726 case JIM_TT_STR:
6727 ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6728 break;
6729 case JIM_TT_ESC:
6730 ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6731 break;
6732 case JIM_TT_VAR:
6733 ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6734 break;
6735 case JIM_TT_DICTSUGAR:
6736 ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6737 break;
6738 case JIM_TT_CMD:
6739 ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6740 break;
6741 case JIM_TT_EXPR_NUMBER:
6742 ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6743 break;
6744 case JIM_TT_EXPR_OPERATOR:
6745 op = JimExprOperatorInfo(token);
6746 while(1) {
6747 Jim_ExprOperator *stackTopOp;
6748
6749 if (Jim_StackPeek(&stack) != NULL) {
6750 stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6751 } else {
6752 stackTopOp = NULL;
6753 }
6754 if (Jim_StackLen(&stack) && op->arity != 1 &&
6755 stackTopOp && stackTopOp->precedence >= op->precedence)
6756 {
6757 ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6758 Jim_StackPeek(&stack), -1);
6759 Jim_StackPop(&stack);
6760 } else {
6761 break;
6762 }
6763 }
6764 Jim_StackPush(&stack, token);
6765 break;
6766 case JIM_TT_SUBEXPR_START:
6767 Jim_StackPush(&stack, Jim_StrDup("("));
6768 Jim_Free(token);
6769 break;
6770 case JIM_TT_SUBEXPR_END:
6771 {
6772 int found = 0;
6773 while(Jim_StackLen(&stack)) {
6774 char *opstr = Jim_StackPop(&stack);
6775 if (!strcmp(opstr, "(")) {
6776 Jim_Free(opstr);
6777 found = 1;
6778 break;
6779 }
6780 op = JimExprOperatorInfo(opstr);
6781 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6782 }
6783 if (!found) {
6784 Jim_SetResultString(interp,
6785 "Unexpected close parenthesis", -1);
6786 goto err;
6787 }
6788 }
6789 Jim_Free(token);
6790 break;
6791 default:
6792 Jim_Panic(interp,"Default reached in SetExprFromAny()");
6793 break;
6794 }
6795 }
6796 while (Jim_StackLen(&stack)) {
6797 char *opstr = Jim_StackPop(&stack);
6798 op = JimExprOperatorInfo(opstr);
6799 if (op == NULL && !strcmp(opstr, "(")) {
6800 Jim_Free(opstr);
6801 Jim_SetResultString(interp, "Missing close parenthesis", -1);
6802 goto err;
6803 }
6804 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6805 }
6806 /* Check program correctness. */
6807 if (ExprCheckCorrectness(expr) != JIM_OK) {
6808 Jim_SetResultString(interp, "Invalid expression", -1);
6809 goto err;
6810 }
6811
6812 /* Free the stack used for the compilation. */
6813 Jim_FreeStackElements(&stack, Jim_Free);
6814 Jim_FreeStack(&stack);
6815
6816 /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6817 ExprMakeLazy(interp, expr);
6818
6819 /* Perform literal sharing */
6820 if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6821 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6822 if (bodyObjPtr->typePtr == &scriptObjType) {
6823 ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6824 ExprShareLiterals(interp, expr, bodyScript);
6825 }
6826 }
6827
6828 /* Free the old internal rep and set the new one. */
6829 Jim_FreeIntRep(interp, objPtr);
6830 Jim_SetIntRepPtr(objPtr, expr);
6831 objPtr->typePtr = &exprObjType;
6832 return JIM_OK;
6833
6834 err: /* we jump here on syntax/compile errors. */
6835 Jim_FreeStackElements(&stack, Jim_Free);
6836 Jim_FreeStack(&stack);
6837 Jim_Free(expr->opcode);
6838 for (i = 0; i < expr->len; i++) {
6839 Jim_DecrRefCount(interp,expr->obj[i]);
6840 }
6841 Jim_Free(expr->obj);
6842 Jim_Free(expr);
6843 return JIM_ERR;
6844 }
6845
6846 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6847 {
6848 if (objPtr->typePtr != &exprObjType) {
6849 if (SetExprFromAny(interp, objPtr) != JIM_OK)
6850 return NULL;
6851 }
6852 return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6853 }
6854
6855 /* -----------------------------------------------------------------------------
6856 * Expressions evaluation.
6857 * Jim uses a specialized stack-based virtual machine for expressions,
6858 * that takes advantage of the fact that expr's operators
6859 * can't be redefined.
6860 *
6861 * Jim_EvalExpression() uses the bytecode compiled by
6862 * SetExprFromAny() method of the "expression" object.
6863 *
6864 * On success a Tcl Object containing the result of the evaluation
6865 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6866 * returned.
6867 * On error the function returns a retcode != to JIM_OK and set a suitable
6868 * error on the interp.
6869 * ---------------------------------------------------------------------------*/
6870 #define JIM_EE_STATICSTACK_LEN 10
6871
6872 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6873 Jim_Obj **exprResultPtrPtr)
6874 {
6875 ExprByteCode *expr;
6876 Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6877 int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6878
6879 Jim_IncrRefCount(exprObjPtr);
6880 expr = Jim_GetExpression(interp, exprObjPtr);
6881 if (!expr) {
6882 Jim_DecrRefCount(interp, exprObjPtr);
6883 return JIM_ERR; /* error in expression. */
6884 }
6885 /* In order to avoid that the internal repr gets freed due to
6886 * shimmering of the exprObjPtr's object, we make the internal rep
6887 * shared. */
6888 expr->inUse++;
6889
6890 /* The stack-based expr VM itself */
6891
6892 /* Stack allocation. Expr programs have the feature that
6893 * a program of length N can't require a stack longer than
6894 * N. */
6895 if (expr->len > JIM_EE_STATICSTACK_LEN)
6896 stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6897 else
6898 stack = staticStack;
6899
6900 /* Execute every istruction */
6901 for (i = 0; i < expr->len; i++) {
6902 Jim_Obj *A, *B, *objPtr;
6903 jim_wide wA, wB, wC;
6904 double dA, dB, dC;
6905 const char *sA, *sB;
6906 int Alen, Blen, retcode;
6907 int opcode = expr->opcode[i];
6908
6909 if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6910 stack[stacklen++] = expr->obj[i];
6911 Jim_IncrRefCount(expr->obj[i]);
6912 } else if (opcode == JIM_EXPROP_VARIABLE) {
6913 objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6914 if (objPtr == NULL) {
6915 error = 1;
6916 goto err;
6917 }
6918 stack[stacklen++] = objPtr;
6919 Jim_IncrRefCount(objPtr);
6920 } else if (opcode == JIM_EXPROP_SUBST) {
6921 if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6922 &objPtr, JIM_NONE)) != JIM_OK)
6923 {
6924 error = 1;
6925 errRetCode = retcode;
6926 goto err;
6927 }
6928 stack[stacklen++] = objPtr;
6929 Jim_IncrRefCount(objPtr);
6930 } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6931 objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6932 if (objPtr == NULL) {
6933 error = 1;
6934 goto err;
6935 }
6936 stack[stacklen++] = objPtr;
6937 Jim_IncrRefCount(objPtr);
6938 } else if (opcode == JIM_EXPROP_COMMAND) {
6939 if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6940 error = 1;
6941 errRetCode = retcode;
6942 goto err;
6943 }
6944 stack[stacklen++] = interp->result;
6945 Jim_IncrRefCount(interp->result);
6946 } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6947 opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6948 {
6949 /* Note that there isn't to increment the
6950 * refcount of objects. the references are moved
6951 * from stack to A and B. */
6952 B = stack[--stacklen];
6953 A = stack[--stacklen];
6954
6955 /* --- Integer --- */
6956 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6957 (B->typePtr == &doubleObjType && !B->bytes) ||
6958 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6959 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6960 goto trydouble;
6961 }
6962 Jim_DecrRefCount(interp, A);
6963 Jim_DecrRefCount(interp, B);
6964 switch(expr->opcode[i]) {
6965 case JIM_EXPROP_ADD: wC = wA+wB; break;
6966 case JIM_EXPROP_SUB: wC = wA-wB; break;
6967 case JIM_EXPROP_MUL: wC = wA*wB; break;
6968 case JIM_EXPROP_LT: wC = wA<wB; break;
6969 case JIM_EXPROP_GT: wC = wA>wB; break;
6970 case JIM_EXPROP_LTE: wC = wA<=wB; break;
6971 case JIM_EXPROP_GTE: wC = wA>=wB; break;
6972 case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6973 case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6974 case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6975 case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6976 case JIM_EXPROP_BITAND: wC = wA&wB; break;
6977 case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6978 case JIM_EXPROP_BITOR: wC = wA|wB; break;
6979 case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6980 case JIM_EXPROP_LOGICAND_LEFT:
6981 if (wA == 0) {
6982 i += (int)wB;
6983 wC = 0;
6984 } else {
6985 continue;
6986 }
6987 break;
6988 case JIM_EXPROP_LOGICOR_LEFT:
6989 if (wA != 0) {
6990 i += (int)wB;
6991 wC = 1;
6992 } else {
6993 continue;
6994 }
6995 break;
6996 case JIM_EXPROP_DIV:
6997 if (wB == 0) goto divbyzero;
6998 wC = wA/wB;
6999 break;
7000 case JIM_EXPROP_MOD:
7001 if (wB == 0) goto divbyzero;
7002 wC = wA%wB;
7003 break;
7004 case JIM_EXPROP_ROTL: {
7005 /* uint32_t would be better. But not everyone has inttypes.h?*/
7006 unsigned long uA = (unsigned long)wA;
7007 #ifdef _MSC_VER
7008 wC = _rotl(uA,(unsigned long)wB);
7009 #else
7010 const unsigned int S = sizeof(unsigned long) * 8;
7011 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
7012 #endif
7013 break;
7014 }
7015 case JIM_EXPROP_ROTR: {
7016 unsigned long uA = (unsigned long)wA;
7017 #ifdef _MSC_VER
7018 wC = _rotr(uA,(unsigned long)wB);
7019 #else
7020 const unsigned int S = sizeof(unsigned long) * 8;
7021 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
7022 #endif
7023 break;
7024 }
7025
7026 default:
7027 wC = 0; /* avoid gcc warning */
7028 break;
7029 }
7030 stack[stacklen] = Jim_NewIntObj(interp, wC);
7031 Jim_IncrRefCount(stack[stacklen]);
7032 stacklen++;
7033 continue;
7034 trydouble:
7035 /* --- Double --- */
7036 if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
7037 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
7038
7039 /* Hmmm! For compatibility, maybe convert != and == into ne and eq */
7040 if (expr->opcode[i] == JIM_EXPROP_NUMNE) {
7041 opcode = JIM_EXPROP_STRNE;
7042 goto retry_as_string;
7043 }
7044 else if (expr->opcode[i] == JIM_EXPROP_NUMEQ) {
7045 opcode = JIM_EXPROP_STREQ;
7046 goto retry_as_string;
7047 }
7048 Jim_DecrRefCount(interp, A);
7049 Jim_DecrRefCount(interp, B);
7050 error = 1;
7051 goto err;
7052 }
7053 Jim_DecrRefCount(interp, A);
7054 Jim_DecrRefCount(interp, B);
7055 switch(expr->opcode[i]) {
7056 case JIM_EXPROP_ROTL:
7057 case JIM_EXPROP_ROTR:
7058 case JIM_EXPROP_LSHIFT:
7059 case JIM_EXPROP_RSHIFT:
7060 case JIM_EXPROP_BITAND:
7061 case JIM_EXPROP_BITXOR:
7062 case JIM_EXPROP_BITOR:
7063 case JIM_EXPROP_MOD:
7064 case JIM_EXPROP_POW:
7065 Jim_SetResultString(interp,
7066 "Got floating-point value where integer was expected", -1);
7067 error = 1;
7068 goto err;
7069 break;
7070 case JIM_EXPROP_ADD: dC = dA+dB; break;
7071 case JIM_EXPROP_SUB: dC = dA-dB; break;
7072 case JIM_EXPROP_MUL: dC = dA*dB; break;
7073 case JIM_EXPROP_LT: dC = dA<dB; break;
7074 case JIM_EXPROP_GT: dC = dA>dB; break;
7075 case JIM_EXPROP_LTE: dC = dA<=dB; break;
7076 case JIM_EXPROP_GTE: dC = dA>=dB; break;
7077 case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
7078 case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
7079 case JIM_EXPROP_LOGICAND_LEFT:
7080 if (dA == 0) {
7081 i += (int)dB;
7082 dC = 0;
7083 } else {
7084 continue;
7085 }
7086 break;
7087 case JIM_EXPROP_LOGICOR_LEFT:
7088 if (dA != 0) {
7089 i += (int)dB;
7090 dC = 1;
7091 } else {
7092 continue;
7093 }
7094 break;
7095 case JIM_EXPROP_DIV:
7096 if (dB == 0) goto divbyzero;
7097 dC = dA/dB;
7098 break;
7099 default:
7100 dC = 0; /* avoid gcc warning */
7101 break;
7102 }
7103 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7104 Jim_IncrRefCount(stack[stacklen]);
7105 stacklen++;
7106 } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
7107 B = stack[--stacklen];
7108 A = stack[--stacklen];
7109 retry_as_string:
7110 sA = Jim_GetString(A, &Alen);
7111 sB = Jim_GetString(B, &Blen);
7112 switch(opcode) {
7113 case JIM_EXPROP_STREQ:
7114 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
7115 wC = 1;
7116 else
7117 wC = 0;
7118 break;
7119 case JIM_EXPROP_STRNE:
7120 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
7121 wC = 1;
7122 else
7123 wC = 0;
7124 break;
7125 default:
7126 wC = 0; /* avoid gcc warning */
7127 break;
7128 }
7129 Jim_DecrRefCount(interp, A);
7130 Jim_DecrRefCount(interp, B);
7131 stack[stacklen] = Jim_NewIntObj(interp, wC);
7132 Jim_IncrRefCount(stack[stacklen]);
7133 stacklen++;
7134 } else if (opcode == JIM_EXPROP_NOT ||
7135 opcode == JIM_EXPROP_BITNOT ||
7136 opcode == JIM_EXPROP_LOGICAND_RIGHT ||
7137 opcode == JIM_EXPROP_LOGICOR_RIGHT) {
7138 /* Note that there isn't to increment the
7139 * refcount of objects. the references are moved
7140 * from stack to A and B. */
7141 A = stack[--stacklen];
7142
7143 /* --- Integer --- */
7144 if ((A->typePtr == &doubleObjType && !A->bytes) ||
7145 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
7146 goto trydouble_unary;
7147 }
7148 Jim_DecrRefCount(interp, A);
7149 switch(expr->opcode[i]) {
7150 case JIM_EXPROP_NOT: wC = !wA; break;
7151 case JIM_EXPROP_BITNOT: wC = ~wA; break;
7152 case JIM_EXPROP_LOGICAND_RIGHT:
7153 case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
7154 default:
7155 wC = 0; /* avoid gcc warning */
7156 break;
7157 }
7158 stack[stacklen] = Jim_NewIntObj(interp, wC);
7159 Jim_IncrRefCount(stack[stacklen]);
7160 stacklen++;
7161 continue;
7162 trydouble_unary:
7163 /* --- Double --- */
7164 if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
7165 Jim_DecrRefCount(interp, A);
7166 error = 1;
7167 goto err;
7168 }
7169 Jim_DecrRefCount(interp, A);
7170 switch(expr->opcode[i]) {
7171 case JIM_EXPROP_NOT: dC = !dA; break;
7172 case JIM_EXPROP_LOGICAND_RIGHT:
7173 case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
7174 case JIM_EXPROP_BITNOT:
7175 Jim_SetResultString(interp,
7176 "Got floating-point value where integer was expected", -1);
7177 error = 1;
7178 goto err;
7179 break;
7180 default:
7181 dC = 0; /* avoid gcc warning */
7182 break;
7183 }
7184 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7185 Jim_IncrRefCount(stack[stacklen]);
7186 stacklen++;
7187 } else {
7188 Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
7189 }
7190 }
7191 err:
7192 /* There is no need to decerement the inUse field because
7193 * this reference is transfered back into the exprObjPtr. */
7194 Jim_FreeIntRep(interp, exprObjPtr);
7195 exprObjPtr->typePtr = &exprObjType;
7196 Jim_SetIntRepPtr(exprObjPtr, expr);
7197 Jim_DecrRefCount(interp, exprObjPtr);
7198 if (!error) {
7199 *exprResultPtrPtr = stack[0];
7200 Jim_IncrRefCount(stack[0]);
7201 errRetCode = JIM_OK;
7202 }
7203 for (i = 0; i < stacklen; i++) {
7204 Jim_DecrRefCount(interp, stack[i]);
7205 }
7206 if (stack != staticStack)
7207 Jim_Free(stack);
7208 return errRetCode;
7209 divbyzero:
7210 error = 1;
7211 Jim_SetResultString(interp, "Division by zero", -1);
7212 goto err;
7213 }
7214
7215 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7216 {
7217 int retcode;
7218 jim_wide wideValue;
7219 double doubleValue;
7220 Jim_Obj *exprResultPtr;
7221
7222 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7223 if (retcode != JIM_OK)
7224 return retcode;
7225 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7226 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7227 {
7228 Jim_DecrRefCount(interp, exprResultPtr);
7229 return JIM_ERR;
7230 } else {
7231 Jim_DecrRefCount(interp, exprResultPtr);
7232 *boolPtr = doubleValue != 0;
7233 return JIM_OK;
7234 }
7235 }
7236 Jim_DecrRefCount(interp, exprResultPtr);
7237 *boolPtr = wideValue != 0;
7238 return JIM_OK;
7239 }
7240
7241 /* -----------------------------------------------------------------------------
7242 * ScanFormat String Object
7243 * ---------------------------------------------------------------------------*/
7244
7245 /* This Jim_Obj will held a parsed representation of a format string passed to
7246 * the Jim_ScanString command. For error diagnostics, the scanformat string has
7247 * to be parsed in its entirely first and then, if correct, can be used for
7248 * scanning. To avoid endless re-parsing, the parsed representation will be
7249 * stored in an internal representation and re-used for performance reason. */
7250
7251 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7252 * scanformat string. This part will later be used to extract information
7253 * out from the string to be parsed by Jim_ScanString */
7254
7255 typedef struct ScanFmtPartDescr {
7256 char type; /* Type of conversion (e.g. c, d, f) */
7257 char modifier; /* Modify type (e.g. l - long, h - short */
7258 size_t width; /* Maximal width of input to be converted */
7259 int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
7260 char *arg; /* Specification of a CHARSET conversion */
7261 char *prefix; /* Prefix to be scanned literally before conversion */
7262 } ScanFmtPartDescr;
7263
7264 /* The ScanFmtStringObj will held the internal representation of a scanformat
7265 * string parsed and separated in part descriptions. Furthermore it contains
7266 * the original string representation of the scanformat string to allow for
7267 * fast update of the Jim_Obj's string representation part.
7268 *
7269 * As add-on the internal object representation add some scratch pad area
7270 * for usage by Jim_ScanString to avoid endless allocating and freeing of
7271 * memory for purpose of string scanning.
7272 *
7273 * The error member points to a static allocated string in case of a mal-
7274 * formed scanformat string or it contains '0' (NULL) in case of a valid
7275 * parse representation.
7276 *
7277 * The whole memory of the internal representation is allocated as a single
7278 * area of memory that will be internally separated. So freeing and duplicating
7279 * of such an object is cheap */
7280
7281 typedef struct ScanFmtStringObj {
7282 jim_wide size; /* Size of internal repr in bytes */
7283 char *stringRep; /* Original string representation */
7284 size_t count; /* Number of ScanFmtPartDescr contained */
7285 size_t convCount; /* Number of conversions that will assign */
7286 size_t maxPos; /* Max position index if XPG3 is used */
7287 const char *error; /* Ptr to error text (NULL if no error */
7288 char *scratch; /* Some scratch pad used by Jim_ScanString */
7289 ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
7290 } ScanFmtStringObj;
7291
7292
7293 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7294 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7295 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7296
7297 static Jim_ObjType scanFmtStringObjType = {
7298 "scanformatstring",
7299 FreeScanFmtInternalRep,
7300 DupScanFmtInternalRep,
7301 UpdateStringOfScanFmt,
7302 JIM_TYPE_NONE,
7303 };
7304
7305 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7306 {
7307 JIM_NOTUSED(interp);
7308 Jim_Free((char*)objPtr->internalRep.ptr);
7309 objPtr->internalRep.ptr = 0;
7310 }
7311
7312 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7313 {
7314 size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7315 ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7316
7317 JIM_NOTUSED(interp);
7318 memcpy(newVec, srcPtr->internalRep.ptr, size);
7319 dupPtr->internalRep.ptr = newVec;
7320 dupPtr->typePtr = &scanFmtStringObjType;
7321 }
7322
7323 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7324 {
7325 char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7326
7327 objPtr->bytes = Jim_StrDup(bytes);
7328 objPtr->length = strlen(bytes);
7329 }
7330
7331 /* SetScanFmtFromAny will parse a given string and create the internal
7332 * representation of the format specification. In case of an error
7333 * the error data member of the internal representation will be set
7334 * to an descriptive error text and the function will be left with
7335 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7336 * specification */
7337
7338 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7339 {
7340 ScanFmtStringObj *fmtObj;
7341 char *buffer;
7342 int maxCount, i, approxSize, lastPos = -1;
7343 const char *fmt = objPtr->bytes;
7344 int maxFmtLen = objPtr->length;
7345 const char *fmtEnd = fmt + maxFmtLen;
7346 int curr;
7347
7348 Jim_FreeIntRep(interp, objPtr);
7349 /* Count how many conversions could take place maximally */
7350 for (i=0, maxCount=0; i < maxFmtLen; ++i)
7351 if (fmt[i] == '%')
7352 ++maxCount;
7353 /* Calculate an approximation of the memory necessary */
7354 approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
7355 + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7356 + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
7357 + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
7358 + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
7359 + (maxCount +1) * sizeof(char) /* '\0' for every partial */
7360 + 1; /* safety byte */
7361 fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7362 memset(fmtObj, 0, approxSize);
7363 fmtObj->size = approxSize;
7364 fmtObj->maxPos = 0;
7365 fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7366 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7367 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7368 buffer = fmtObj->stringRep + maxFmtLen + 1;
7369 objPtr->internalRep.ptr = fmtObj;
7370 objPtr->typePtr = &scanFmtStringObjType;
7371 for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7372 int width=0, skip;
7373 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7374 fmtObj->count++;
7375 descr->width = 0; /* Assume width unspecified */
7376 /* Overread and store any "literal" prefix */
7377 if (*fmt != '%' || fmt[1] == '%') {
7378 descr->type = 0;
7379 descr->prefix = &buffer[i];
7380 for (; fmt < fmtEnd; ++fmt) {
7381 if (*fmt == '%') {
7382 if (fmt[1] != '%') break;
7383 ++fmt;
7384 }
7385 buffer[i++] = *fmt;
7386 }
7387 buffer[i++] = 0;
7388 }
7389 /* Skip the conversion introducing '%' sign */
7390 ++fmt;
7391 /* End reached due to non-conversion literal only? */
7392 if (fmt >= fmtEnd)
7393 goto done;
7394 descr->pos = 0; /* Assume "natural" positioning */
7395 if (*fmt == '*') {
7396 descr->pos = -1; /* Okay, conversion will not be assigned */
7397 ++fmt;
7398 } else
7399 fmtObj->convCount++; /* Otherwise count as assign-conversion */
7400 /* Check if next token is a number (could be width or pos */
7401 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7402 fmt += skip;
7403 /* Was the number a XPG3 position specifier? */
7404 if (descr->pos != -1 && *fmt == '$') {
7405 int prev;
7406 ++fmt;
7407 descr->pos = width;
7408 width = 0;
7409 /* Look if "natural" postioning and XPG3 one was mixed */
7410 if ((lastPos == 0 && descr->pos > 0)
7411 || (lastPos > 0 && descr->pos == 0)) {
7412 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7413 return JIM_ERR;
7414 }
7415 /* Look if this position was already used */
7416 for (prev=0; prev < curr; ++prev) {
7417 if (fmtObj->descr[prev].pos == -1) continue;
7418 if (fmtObj->descr[prev].pos == descr->pos) {
7419 fmtObj->error = "same \"%n$\" conversion specifier "
7420 "used more than once";
7421 return JIM_ERR;
7422 }
7423 }
7424 /* Try to find a width after the XPG3 specifier */
7425 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7426 descr->width = width;
7427 fmt += skip;
7428 }
7429 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7430 fmtObj->maxPos = descr->pos;
7431 } else {
7432 /* Number was not a XPG3, so it has to be a width */
7433 descr->width = width;
7434 }
7435 }
7436 /* If positioning mode was undetermined yet, fix this */
7437 if (lastPos == -1)
7438 lastPos = descr->pos;
7439 /* Handle CHARSET conversion type ... */
7440 if (*fmt == '[') {
7441 int swapped = 1, beg = i, end, j;
7442 descr->type = '[';
7443 descr->arg = &buffer[i];
7444 ++fmt;
7445 if (*fmt == '^') buffer[i++] = *fmt++;
7446 if (*fmt == ']') buffer[i++] = *fmt++;
7447 while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7448 if (*fmt != ']') {
7449 fmtObj->error = "unmatched [ in format string";
7450 return JIM_ERR;
7451 }
7452 end = i;
7453 buffer[i++] = 0;
7454 /* In case a range fence was given "backwards", swap it */
7455 while (swapped) {
7456 swapped = 0;
7457 for (j=beg+1; j < end-1; ++j) {
7458 if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7459 char tmp = buffer[j-1];
7460 buffer[j-1] = buffer[j+1];
7461 buffer[j+1] = tmp;
7462 swapped = 1;
7463 }
7464 }
7465 }
7466 } else {
7467 /* Remember any valid modifier if given */
7468 if (strchr("hlL", *fmt) != 0)
7469 descr->modifier = tolower((int)*fmt++);
7470
7471 descr->type = *fmt;
7472 if (strchr("efgcsndoxui", *fmt) == 0) {
7473 fmtObj->error = "bad scan conversion character";
7474 return JIM_ERR;
7475 } else if (*fmt == 'c' && descr->width != 0) {
7476 fmtObj->error = "field width may not be specified in %c "
7477 "conversion";
7478 return JIM_ERR;
7479 } else if (*fmt == 'u' && descr->modifier == 'l') {
7480 fmtObj->error = "unsigned wide not supported";
7481 return JIM_ERR;
7482 }
7483 }
7484 curr++;
7485 }
7486 done:
7487 if (fmtObj->convCount == 0) {
7488 fmtObj->error = "no any conversion specifier given";
7489 return JIM_ERR;
7490 }
7491 return JIM_OK;
7492 }
7493
7494 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7495
7496 #define FormatGetCnvCount(_fo_) \
7497 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7498 #define FormatGetMaxPos(_fo_) \
7499 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7500 #define FormatGetError(_fo_) \
7501 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7502
7503 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7504 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7505 * bitvector implementation in Jim? */
7506
7507 static int JimTestBit(const char *bitvec, char ch)
7508 {
7509 div_t pos = div(ch-1, 8);
7510 return bitvec[pos.quot] & (1 << pos.rem);
7511 }
7512
7513 static void JimSetBit(char *bitvec, char ch)
7514 {
7515 div_t pos = div(ch-1, 8);
7516 bitvec[pos.quot] |= (1 << pos.rem);
7517 }
7518
7519 #if 0 /* currently not used */
7520 static void JimClearBit(char *bitvec, char ch)
7521 {
7522 div_t pos = div(ch-1, 8);
7523 bitvec[pos.quot] &= ~(1 << pos.rem);
7524 }
7525 #endif
7526
7527 /* JimScanAString is used to scan an unspecified string that ends with
7528 * next WS, or a string that is specified via a charset. The charset
7529 * is currently implemented in a way to only allow for usage with
7530 * ASCII. Whenever we will switch to UNICODE, another idea has to
7531 * be born :-/
7532 *
7533 * FIXME: Works only with ASCII */
7534
7535 static Jim_Obj *
7536 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7537 {
7538 size_t i;
7539 Jim_Obj *result;
7540 char charset[256/8+1]; /* A Charset may contain max 256 chars */
7541 char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7542
7543 /* First init charset to nothing or all, depending if a specified
7544 * or an unspecified string has to be parsed */
7545 memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7546 if (sdescr) {
7547 /* There was a set description given, that means we are parsing
7548 * a specified string. So we have to build a corresponding
7549 * charset reflecting the description */
7550 int notFlag = 0;
7551 /* Should the set be negated at the end? */
7552 if (*sdescr == '^') {
7553 notFlag = 1;
7554 ++sdescr;
7555 }
7556 /* Here '-' is meant literally and not to define a range */
7557 if (*sdescr == '-') {
7558 JimSetBit(charset, '-');
7559 ++sdescr;
7560 }
7561 while (*sdescr) {
7562 if (sdescr[1] == '-' && sdescr[2] != 0) {
7563 /* Handle range definitions */
7564 int i;
7565 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7566 JimSetBit(charset, (char)i);
7567 sdescr += 3;
7568 } else {
7569 /* Handle verbatim character definitions */
7570 JimSetBit(charset, *sdescr++);
7571 }
7572 }
7573 /* Negate the charset if there was a NOT given */
7574 for (i=0; notFlag && i < sizeof(charset); ++i)
7575 charset[i] = ~charset[i];
7576 }
7577 /* And after all the mess above, the real work begin ... */
7578 while (str && *str) {
7579 if (!sdescr && isspace((int)*str))
7580 break; /* EOS via WS if unspecified */
7581 if (JimTestBit(charset, *str)) *buffer++ = *str++;
7582 else break; /* EOS via mismatch if specified scanning */
7583 }
7584 *buffer = 0; /* Close the string properly ... */
7585 result = Jim_NewStringObj(interp, anchor, -1);
7586 Jim_Free(anchor); /* ... and free it afer usage */
7587 return result;
7588 }
7589
7590 /* ScanOneEntry will scan one entry out of the string passed as argument.
7591 * It use the sscanf() function for this task. After extracting and
7592 * converting of the value, the count of scanned characters will be
7593 * returned of -1 in case of no conversion tool place and string was
7594 * already scanned thru */
7595
7596 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7597 ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7598 {
7599 # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7600 ? sizeof(jim_wide) \
7601 : sizeof(double))
7602 char buffer[MAX_SIZE];
7603 char *value = buffer;
7604 const char *tok;
7605 const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7606 size_t sLen = strlen(&str[pos]), scanned = 0;
7607 size_t anchor = pos;
7608 int i;
7609
7610 /* First pessimiticly assume, we will not scan anything :-) */
7611 *valObjPtr = 0;
7612 if (descr->prefix) {
7613 /* There was a prefix given before the conversion, skip it and adjust
7614 * the string-to-be-parsed accordingly */
7615 for (i=0; str[pos] && descr->prefix[i]; ++i) {
7616 /* If prefix require, skip WS */
7617 if (isspace((int)descr->prefix[i]))
7618 while (str[pos] && isspace((int)str[pos])) ++pos;
7619 else if (descr->prefix[i] != str[pos])
7620 break; /* Prefix do not match here, leave the loop */
7621 else
7622 ++pos; /* Prefix matched so far, next round */
7623 }
7624 if (str[pos] == 0)
7625 return -1; /* All of str consumed: EOF condition */
7626 else if (descr->prefix[i] != 0)
7627 return 0; /* Not whole prefix consumed, no conversion possible */
7628 }
7629 /* For all but following conversion, skip leading WS */
7630 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7631 while (isspace((int)str[pos])) ++pos;
7632 /* Determine how much skipped/scanned so far */
7633 scanned = pos - anchor;
7634 if (descr->type == 'n') {
7635 /* Return pseudo conversion means: how much scanned so far? */
7636 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7637 } else if (str[pos] == 0) {
7638 /* Cannot scan anything, as str is totally consumed */
7639 return -1;
7640 } else {
7641 /* Processing of conversions follows ... */
7642 if (descr->width > 0) {
7643 /* Do not try to scan as fas as possible but only the given width.
7644 * To ensure this, we copy the part that should be scanned. */
7645 size_t tLen = descr->width > sLen ? sLen : descr->width;
7646 tok = Jim_StrDupLen(&str[pos], tLen);
7647 } else {
7648 /* As no width was given, simply refer to the original string */
7649 tok = &str[pos];
7650 }
7651 switch (descr->type) {
7652 case 'c':
7653 *valObjPtr = Jim_NewIntObj(interp, *tok);
7654 scanned += 1;
7655 break;
7656 case 'd': case 'o': case 'x': case 'u': case 'i': {
7657 jim_wide jwvalue;
7658 long lvalue;
7659 char *endp; /* Position where the number finished */
7660 int base = descr->type == 'o' ? 8
7661 : descr->type == 'x' ? 16
7662 : descr->type == 'i' ? 0
7663 : 10;
7664
7665 do {
7666 /* Try to scan a number with the given base */
7667 if (descr->modifier == 'l')
7668 {
7669 #ifdef HAVE_LONG_LONG_INT
7670 jwvalue = JimStrtoll(tok, &endp, base),
7671 #else
7672 jwvalue = strtol(tok, &endp, base),
7673 #endif
7674 memcpy(value, &jwvalue, sizeof(jim_wide));
7675 }
7676 else
7677 {
7678 if (descr->type == 'u')
7679 lvalue = strtoul(tok, &endp, base);
7680 else
7681 lvalue = strtol(tok, &endp, base);
7682 memcpy(value, &lvalue, sizeof(lvalue));
7683 }
7684 /* If scanning failed, and base was undetermined, simply
7685 * put it to 10 and try once more. This should catch the
7686 * case where %i begin to parse a number prefix (e.g.
7687 * '0x' but no further digits follows. This will be
7688 * handled as a ZERO followed by a char 'x' by Tcl */
7689 if (endp == tok && base == 0) base = 10;
7690 else break;
7691 } while (1);
7692 if (endp != tok) {
7693 /* There was some number sucessfully scanned! */
7694 if (descr->modifier == 'l')
7695 *valObjPtr = Jim_NewIntObj(interp, jwvalue);
7696 else
7697 *valObjPtr = Jim_NewIntObj(interp, lvalue);
7698 /* Adjust the number-of-chars scanned so far */
7699 scanned += endp - tok;
7700 } else {
7701 /* Nothing was scanned. We have to determine if this
7702 * happened due to e.g. prefix mismatch or input str
7703 * exhausted */
7704 scanned = *tok ? 0 : -1;
7705 }
7706 break;
7707 }
7708 case 's': case '[': {
7709 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7710 scanned += Jim_Length(*valObjPtr);
7711 break;
7712 }
7713 case 'e': case 'f': case 'g': {
7714 char *endp;
7715
7716 double dvalue = strtod(tok, &endp);
7717 memcpy(value, &dvalue, sizeof(double));
7718 if (endp != tok) {
7719 /* There was some number sucessfully scanned! */
7720 *valObjPtr = Jim_NewDoubleObj(interp, dvalue);
7721 /* Adjust the number-of-chars scanned so far */
7722 scanned += endp - tok;
7723 } else {
7724 /* Nothing was scanned. We have to determine if this
7725 * happened due to e.g. prefix mismatch or input str
7726 * exhausted */
7727 scanned = *tok ? 0 : -1;
7728 }
7729 break;
7730 }
7731 }
7732 /* If a substring was allocated (due to pre-defined width) do not
7733 * forget to free it */
7734 if (tok != &str[pos])
7735 Jim_Free((char*)tok);
7736 }
7737 return scanned;
7738 }
7739
7740 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7741 * string and returns all converted (and not ignored) values in a list back
7742 * to the caller. If an error occured, a NULL pointer will be returned */
7743
7744 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7745 Jim_Obj *fmtObjPtr, int flags)
7746 {
7747 size_t i, pos;
7748 int scanned = 1;
7749 const char *str = Jim_GetString(strObjPtr, 0);
7750 Jim_Obj *resultList = 0;
7751 Jim_Obj **resultVec;
7752 int resultc;
7753 Jim_Obj *emptyStr = 0;
7754 ScanFmtStringObj *fmtObj;
7755
7756 /* If format specification is not an object, convert it! */
7757 if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7758 SetScanFmtFromAny(interp, fmtObjPtr);
7759 fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7760 /* Check if format specification was valid */
7761 if (fmtObj->error != 0) {
7762 if (flags & JIM_ERRMSG)
7763 Jim_SetResultString(interp, fmtObj->error, -1);
7764 return 0;
7765 }
7766 /* Allocate a new "shared" empty string for all unassigned conversions */
7767 emptyStr = Jim_NewEmptyStringObj(interp);
7768 Jim_IncrRefCount(emptyStr);
7769 /* Create a list and fill it with empty strings up to max specified XPG3 */
7770 resultList = Jim_NewListObj(interp, 0, 0);
7771 if (fmtObj->maxPos > 0) {
7772 for (i=0; i < fmtObj->maxPos; ++i)
7773 Jim_ListAppendElement(interp, resultList, emptyStr);
7774 JimListGetElements(interp, resultList, &resultc, &resultVec);
7775 }
7776 /* Now handle every partial format description */
7777 for (i=0, pos=0; i < fmtObj->count; ++i) {
7778 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7779 Jim_Obj *value = 0;
7780 /* Only last type may be "literal" w/o conversion - skip it! */
7781 if (descr->type == 0) continue;
7782 /* As long as any conversion could be done, we will proceed */
7783 if (scanned > 0)
7784 scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7785 /* In case our first try results in EOF, we will leave */
7786 if (scanned == -1 && i == 0)
7787 goto eof;
7788 /* Advance next pos-to-be-scanned for the amount scanned already */
7789 pos += scanned;
7790 /* value == 0 means no conversion took place so take empty string */
7791 if (value == 0)
7792 value = Jim_NewEmptyStringObj(interp);
7793 /* If value is a non-assignable one, skip it */
7794 if (descr->pos == -1) {
7795 Jim_FreeNewObj(interp, value);
7796 } else if (descr->pos == 0)
7797 /* Otherwise append it to the result list if no XPG3 was given */
7798 Jim_ListAppendElement(interp, resultList, value);
7799 else if (resultVec[descr->pos-1] == emptyStr) {
7800 /* But due to given XPG3, put the value into the corr. slot */
7801 Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7802 Jim_IncrRefCount(value);
7803 resultVec[descr->pos-1] = value;
7804 } else {
7805 /* Otherwise, the slot was already used - free obj and ERROR */
7806 Jim_FreeNewObj(interp, value);
7807 goto err;
7808 }
7809 }
7810 Jim_DecrRefCount(interp, emptyStr);
7811 return resultList;
7812 eof:
7813 Jim_DecrRefCount(interp, emptyStr);
7814 Jim_FreeNewObj(interp, resultList);
7815 return (Jim_Obj*)EOF;
7816 err:
7817 Jim_DecrRefCount(interp, emptyStr);
7818 Jim_FreeNewObj(interp, resultList);
7819 return 0;
7820 }
7821
7822 /* -----------------------------------------------------------------------------
7823 * Pseudo Random Number Generation
7824 * ---------------------------------------------------------------------------*/
7825 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7826 int seedLen);
7827
7828 /* Initialize the sbox with the numbers from 0 to 255 */
7829 static void JimPrngInit(Jim_Interp *interp)
7830 {
7831 int i;
7832 unsigned int seed[256];
7833
7834 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7835 for (i = 0; i < 256; i++)
7836 seed[i] = (rand() ^ time(NULL) ^ clock());
7837 JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7838 }
7839
7840 /* Generates N bytes of random data */
7841 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7842 {
7843 Jim_PrngState *prng;
7844 unsigned char *destByte = (unsigned char*) dest;
7845 unsigned int si, sj, x;
7846
7847 /* initialization, only needed the first time */
7848 if (interp->prngState == NULL)
7849 JimPrngInit(interp);
7850 prng = interp->prngState;
7851 /* generates 'len' bytes of pseudo-random numbers */
7852 for (x = 0; x < len; x++) {
7853 prng->i = (prng->i+1) & 0xff;
7854 si = prng->sbox[prng->i];
7855 prng->j = (prng->j + si) & 0xff;
7856 sj = prng->sbox[prng->j];
7857 prng->sbox[prng->i] = sj;
7858 prng->sbox[prng->j] = si;
7859 *destByte++ = prng->sbox[(si+sj)&0xff];
7860 }
7861 }
7862
7863 /* Re-seed the generator with user-provided bytes */
7864 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7865 int seedLen)
7866 {
7867 int i;
7868 unsigned char buf[256];
7869 Jim_PrngState *prng;
7870
7871 /* initialization, only needed the first time */
7872 if (interp->prngState == NULL)
7873 JimPrngInit(interp);
7874 prng = interp->prngState;
7875
7876 /* Set the sbox[i] with i */
7877 for (i = 0; i < 256; i++)
7878 prng->sbox[i] = i;
7879 /* Now use the seed to perform a random permutation of the sbox */
7880 for (i = 0; i < seedLen; i++) {
7881 unsigned char t;
7882
7883 t = prng->sbox[i&0xFF];
7884 prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7885 prng->sbox[seed[i]] = t;
7886 }
7887 prng->i = prng->j = 0;
7888 /* discard the first 256 bytes of stream. */
7889 JimRandomBytes(interp, buf, 256);
7890 }
7891
7892 /* -----------------------------------------------------------------------------
7893 * Dynamic libraries support (WIN32 not supported)
7894 * ---------------------------------------------------------------------------*/
7895
7896 #ifdef JIM_DYNLIB
7897 #ifdef WIN32
7898 #define RTLD_LAZY 0
7899 void * dlopen(const char *path, int mode)
7900 {
7901 JIM_NOTUSED(mode);
7902
7903 return (void *)LoadLibraryA(path);
7904 }
7905 int dlclose(void *handle)
7906 {
7907 FreeLibrary((HANDLE)handle);
7908 return 0;
7909 }
7910 void *dlsym(void *handle, const char *symbol)
7911 {
7912 return GetProcAddress((HMODULE)handle, symbol);
7913 }
7914 static char win32_dlerror_string[121];
7915 const char *dlerror(void)
7916 {
7917 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7918 LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7919 return win32_dlerror_string;
7920 }
7921 #endif /* WIN32 */
7922
7923 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7924 {
7925 Jim_Obj *libPathObjPtr;
7926 int prefixc, i;
7927 void *handle;
7928 int (*onload)(Jim_Interp *interp);
7929
7930 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7931 if (libPathObjPtr == NULL) {
7932 prefixc = 0;
7933 libPathObjPtr = NULL;
7934 } else {
7935 Jim_IncrRefCount(libPathObjPtr);
7936 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7937 }
7938
7939 for (i = -1; i < prefixc; i++) {
7940 if (i < 0) {
7941 handle = dlopen(pathName, RTLD_LAZY);
7942 } else {
7943 FILE *fp;
7944 char buf[JIM_PATH_LEN];
7945 const char *prefix;
7946 int prefixlen;
7947 Jim_Obj *prefixObjPtr;
7948
7949 buf[0] = '\0';
7950 if (Jim_ListIndex(interp, libPathObjPtr, i,
7951 &prefixObjPtr, JIM_NONE) != JIM_OK)
7952 continue;
7953 prefix = Jim_GetString(prefixObjPtr, &prefixlen);
7954 if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7955 continue;
7956 if (*pathName == '/') {
7957 strcpy(buf, pathName);
7958 }
7959 else if (prefixlen && prefix[prefixlen-1] == '/')
7960 sprintf(buf, "%s%s", prefix, pathName);
7961 else
7962 sprintf(buf, "%s/%s", prefix, pathName);
7963 fp = fopen(buf, "r");
7964 if (fp == NULL)
7965 continue;
7966 fclose(fp);
7967 handle = dlopen(buf, RTLD_LAZY);
7968 }
7969 if (handle == NULL) {
7970 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7971 Jim_AppendStrings(interp, Jim_GetResult(interp),
7972 "error loading extension \"", pathName,
7973 "\": ", dlerror(), NULL);
7974 if (i < 0)
7975 continue;
7976 goto err;
7977 }
7978 if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7979 Jim_SetResultString(interp,
7980 "No Jim_OnLoad symbol found on extension", -1);
7981 goto err;
7982 }
7983 if (onload(interp) == JIM_ERR) {
7984 dlclose(handle);
7985 goto err;
7986 }
7987 Jim_SetEmptyResult(interp);
7988 if (libPathObjPtr != NULL)
7989 Jim_DecrRefCount(interp, libPathObjPtr);
7990 return JIM_OK;
7991 }
7992 err:
7993 if (libPathObjPtr != NULL)
7994 Jim_DecrRefCount(interp, libPathObjPtr);
7995 return JIM_ERR;
7996 }
7997 #else /* JIM_DYNLIB */
7998 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7999 {
8000 JIM_NOTUSED(interp);
8001 JIM_NOTUSED(pathName);
8002
8003 Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
8004 return JIM_ERR;
8005 }
8006 #endif/* JIM_DYNLIB */
8007
8008 /* -----------------------------------------------------------------------------
8009 * Packages handling
8010 * ---------------------------------------------------------------------------*/
8011
8012 #define JIM_PKG_ANY_VERSION -1
8013
8014 /* Convert a string of the type "1.2" into an integer.
8015 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
8016 * to the integer with value 102 */
8017 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
8018 int *intPtr, int flags)
8019 {
8020 char *copy;
8021 jim_wide major, minor;
8022 char *majorStr, *minorStr, *p;
8023
8024 if (v[0] == '\0') {
8025 *intPtr = JIM_PKG_ANY_VERSION;
8026 return JIM_OK;
8027 }
8028
8029 copy = Jim_StrDup(v);
8030 p = strchr(copy, '.');
8031 if (p == NULL) goto badfmt;
8032 *p = '\0';
8033 majorStr = copy;
8034 minorStr = p+1;
8035
8036 if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
8037 Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
8038 goto badfmt;
8039 *intPtr = (int)(major*100+minor);
8040 Jim_Free(copy);
8041 return JIM_OK;
8042
8043 badfmt:
8044 Jim_Free(copy);
8045 if (flags & JIM_ERRMSG) {
8046 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8047 Jim_AppendStrings(interp, Jim_GetResult(interp),
8048 "invalid package version '", v, "'", NULL);
8049 }
8050 return JIM_ERR;
8051 }
8052
8053 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
8054 static int JimPackageMatchVersion(int needed, int actual, int flags)
8055 {
8056 if (needed == JIM_PKG_ANY_VERSION) return 1;
8057 if (flags & JIM_MATCHVER_EXACT) {
8058 return needed == actual;
8059 } else {
8060 return needed/100 == actual/100 && (needed <= actual);
8061 }
8062 }
8063
8064 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
8065 int flags)
8066 {
8067 int intVersion;
8068 /* Check if the version format is ok */
8069 if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
8070 return JIM_ERR;
8071 /* If the package was already provided returns an error. */
8072 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
8073 if (flags & JIM_ERRMSG) {
8074 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8075 Jim_AppendStrings(interp, Jim_GetResult(interp),
8076 "package '", name, "' was already provided", NULL);
8077 }
8078 return JIM_ERR;
8079 }
8080 Jim_AddHashEntry(&interp->packages, name, (char*) ver);
8081 return JIM_OK;
8082 }
8083
8084 #ifndef JIM_ANSIC
8085
8086 #ifndef WIN32
8087 # include <sys/types.h>
8088 # include <dirent.h>
8089 #else
8090 # include <io.h>
8091 /* Posix dirent.h compatiblity layer for WIN32.
8092 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
8093 * Copyright Salvatore Sanfilippo ,2005.
8094 *
8095 * Permission to use, copy, modify, and distribute this software and its
8096 * documentation for any purpose is hereby granted without fee, provided
8097 * that this copyright and permissions notice appear in all copies and
8098 * derivatives.
8099 *
8100 * This software is supplied "as is" without express or implied warranty.
8101 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
8102 */
8103
8104 struct dirent {
8105 char *d_name;
8106 };
8107
8108 typedef struct DIR {
8109 long handle; /* -1 for failed rewind */
8110 struct _finddata_t info;
8111 struct dirent result; /* d_name null iff first time */
8112 char *name; /* null-terminated char string */
8113 } DIR;
8114
8115 DIR *opendir(const char *name)
8116 {
8117 DIR *dir = 0;
8118
8119 if(name && name[0]) {
8120 size_t base_length = strlen(name);
8121 const char *all = /* search pattern must end with suitable wildcard */
8122 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
8123
8124 if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
8125 (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
8126 {
8127 strcat(strcpy(dir->name, name), all);
8128
8129 if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
8130 dir->result.d_name = 0;
8131 else { /* rollback */
8132 Jim_Free(dir->name);
8133 Jim_Free(dir);
8134 dir = 0;
8135 }
8136 } else { /* rollback */
8137 Jim_Free(dir);
8138 dir = 0;
8139 errno = ENOMEM;
8140 }
8141 } else {
8142 errno = EINVAL;
8143 }
8144 return dir;
8145 }
8146
8147 int closedir(DIR *dir)
8148 {
8149 int result = -1;
8150
8151 if(dir) {
8152 if(dir->handle != -1)
8153 result = _findclose(dir->handle);
8154 Jim_Free(dir->name);
8155 Jim_Free(dir);
8156 }
8157 if(result == -1) /* map all errors to EBADF */
8158 errno = EBADF;
8159 return result;
8160 }
8161
8162 struct dirent *readdir(DIR *dir)
8163 {
8164 struct dirent *result = 0;
8165
8166 if(dir && dir->handle != -1) {
8167 if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
8168 result = &dir->result;
8169 result->d_name = dir->info.name;
8170 }
8171 } else {
8172 errno = EBADF;
8173 }
8174 return result;
8175 }
8176
8177 #endif /* WIN32 */
8178
8179 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8180 int prefixc, const char *pkgName, int pkgVer, int flags)
8181 {
8182 int bestVer = -1, i;
8183 int pkgNameLen = strlen(pkgName);
8184 char *bestPackage = NULL;
8185 struct dirent *de;
8186
8187 for (i = 0; i < prefixc; i++) {
8188 DIR *dir;
8189 char buf[JIM_PATH_LEN];
8190 int prefixLen;
8191
8192 if (prefixes[i] == NULL) continue;
8193 strncpy(buf, prefixes[i], JIM_PATH_LEN);
8194 buf[JIM_PATH_LEN-1] = '\0';
8195 prefixLen = strlen(buf);
8196 if (prefixLen && buf[prefixLen-1] == '/')
8197 buf[prefixLen-1] = '\0';
8198
8199 if ((dir = opendir(buf)) == NULL) continue;
8200 while ((de = readdir(dir)) != NULL) {
8201 char *fileName = de->d_name;
8202 int fileNameLen = strlen(fileName);
8203
8204 if (strncmp(fileName, "jim-", 4) == 0 &&
8205 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
8206 *(fileName+4+pkgNameLen) == '-' &&
8207 fileNameLen > 4 && /* note that this is not really useful */
8208 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
8209 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
8210 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
8211 {
8212 char ver[6]; /* xx.yy<nulterm> */
8213 char *p = strrchr(fileName, '.');
8214 int verLen, fileVer;
8215
8216 verLen = p - (fileName+4+pkgNameLen+1);
8217 if (verLen < 3 || verLen > 5) continue;
8218 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8219 ver[verLen] = '\0';
8220 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8221 != JIM_OK) continue;
8222 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8223 (bestVer == -1 || bestVer < fileVer))
8224 {
8225 bestVer = fileVer;
8226 Jim_Free(bestPackage);
8227 bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8228 sprintf(bestPackage, "%s/%s", buf, fileName);
8229 }
8230 }
8231 }
8232 closedir(dir);
8233 }
8234 return bestPackage;
8235 }
8236
8237 #else /* JIM_ANSIC */
8238
8239 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8240 int prefixc, const char *pkgName, int pkgVer, int flags)
8241 {
8242 JIM_NOTUSED(interp);
8243 JIM_NOTUSED(prefixes);
8244 JIM_NOTUSED(prefixc);
8245 JIM_NOTUSED(pkgName);
8246 JIM_NOTUSED(pkgVer);
8247 JIM_NOTUSED(flags);
8248 return NULL;
8249 }
8250
8251 #endif /* JIM_ANSIC */
8252
8253 /* Search for a suitable package under every dir specified by jim_libpath
8254 * and load it if possible. If a suitable package was loaded with success
8255 * JIM_OK is returned, otherwise JIM_ERR is returned. */
8256 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8257 int flags)
8258 {
8259 Jim_Obj *libPathObjPtr;
8260 char **prefixes, *best;
8261 int prefixc, i, retCode = JIM_OK;
8262
8263 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8264 if (libPathObjPtr == NULL) {
8265 prefixc = 0;
8266 libPathObjPtr = NULL;
8267 } else {
8268 Jim_IncrRefCount(libPathObjPtr);
8269 Jim_ListLength(interp, libPathObjPtr, &prefixc);
8270 }
8271
8272 prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8273 for (i = 0; i < prefixc; i++) {
8274 Jim_Obj *prefixObjPtr;
8275 if (Jim_ListIndex(interp, libPathObjPtr, i,
8276 &prefixObjPtr, JIM_NONE) != JIM_OK)
8277 {
8278 prefixes[i] = NULL;
8279 continue;
8280 }
8281 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8282 }
8283 /* Scan every directory to find the "best" package. */
8284 best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8285 if (best != NULL) {
8286 char *p = strrchr(best, '.');
8287 /* Try to load/source it */
8288 if (p && strcmp(p, ".tcl") == 0) {
8289 retCode = Jim_EvalFile(interp, best);
8290 } else {
8291 retCode = Jim_LoadLibrary(interp, best);
8292 }
8293 } else {
8294 retCode = JIM_ERR;
8295 }
8296 Jim_Free(best);
8297 for (i = 0; i < prefixc; i++)
8298 Jim_Free(prefixes[i]);
8299 Jim_Free(prefixes);
8300 if (libPathObjPtr)
8301 Jim_DecrRefCount(interp, libPathObjPtr);
8302 return retCode;
8303 }
8304
8305 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8306 const char *ver, int flags)
8307 {
8308 Jim_HashEntry *he;
8309 int requiredVer;
8310
8311 /* Start with an empty error string */
8312 Jim_SetResultString(interp, "", 0);
8313
8314 if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8315 return NULL;
8316 he = Jim_FindHashEntry(&interp->packages, name);
8317 if (he == NULL) {
8318 /* Try to load the package. */
8319 if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8320 he = Jim_FindHashEntry(&interp->packages, name);
8321 if (he == NULL) {
8322 return "?";
8323 }
8324 return he->val;
8325 }
8326 /* No way... return an error. */
8327 if (flags & JIM_ERRMSG) {
8328 int len;
8329 Jim_GetString(Jim_GetResult(interp), &len);
8330 Jim_AppendStrings(interp, Jim_GetResult(interp), len ? "\n" : "",
8331 "Can't find package '", name, "'", NULL);
8332 }
8333 return NULL;
8334 } else {
8335 int actualVer;
8336 if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8337 != JIM_OK)
8338 {
8339 return NULL;
8340 }
8341 /* Check if version matches. */
8342 if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8343 Jim_AppendStrings(interp, Jim_GetResult(interp),
8344 "Package '", name, "' already loaded, but with version ",
8345 he->val, NULL);
8346 return NULL;
8347 }
8348 return he->val;
8349 }
8350 }
8351
8352 /* -----------------------------------------------------------------------------
8353 * Eval
8354 * ---------------------------------------------------------------------------*/
8355 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8356 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8357
8358 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8359 Jim_Obj *const *argv);
8360
8361 /* Handle calls to the [unknown] command */
8362 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8363 {
8364 Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8365 int retCode;
8366
8367 /* If JimUnknown() is recursively called (e.g. error in the unknown proc,
8368 * done here
8369 */
8370 if (interp->unknown_called) {
8371 return JIM_ERR;
8372 }
8373
8374 /* If the [unknown] command does not exists returns
8375 * just now */
8376 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8377 return JIM_ERR;
8378
8379 /* The object interp->unknown just contains
8380 * the "unknown" string, it is used in order to
8381 * avoid to lookup the unknown command every time
8382 * but instread to cache the result. */
8383 if (argc+1 <= JIM_EVAL_SARGV_LEN)
8384 v = sv;
8385 else
8386 v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8387 /* Make a copy of the arguments vector, but shifted on
8388 * the right of one position. The command name of the
8389 * command will be instead the first argument of the
8390 * [unknonw] call. */
8391 memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8392 v[0] = interp->unknown;
8393 /* Call it */
8394 interp->unknown_called++;
8395 retCode = Jim_EvalObjVector(interp, argc+1, v);
8396 interp->unknown_called--;
8397
8398 /* Clean up */
8399 if (v != sv)
8400 Jim_Free(v);
8401 return retCode;
8402 }
8403
8404 /* Eval the object vector 'objv' composed of 'objc' elements.
8405 * Every element is used as single argument.
8406 * Jim_EvalObj() will call this function every time its object
8407 * argument is of "list" type, with no string representation.
8408 *
8409 * This is possible because the string representation of a
8410 * list object generated by the UpdateStringOfList is made
8411 * in a way that ensures that every list element is a different
8412 * command argument. */
8413 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8414 {
8415 int i, retcode;
8416 Jim_Cmd *cmdPtr;
8417
8418 /* Incr refcount of arguments. */
8419 for (i = 0; i < objc; i++)
8420 Jim_IncrRefCount(objv[i]);
8421 /* Command lookup */
8422 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8423 if (cmdPtr == NULL) {
8424 retcode = JimUnknown(interp, objc, objv);
8425 } else {
8426 /* Call it -- Make sure result is an empty object. */
8427 Jim_SetEmptyResult(interp);
8428 if (cmdPtr->cmdProc) {
8429 interp->cmdPrivData = cmdPtr->privData;
8430 retcode = cmdPtr->cmdProc(interp, objc, objv);
8431 if (retcode == JIM_ERR_ADDSTACK) {
8432 //JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8433 retcode = JIM_ERR;
8434 }
8435 } else {
8436 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8437 if (retcode == JIM_ERR) {
8438 JimAppendStackTrace(interp,
8439 Jim_GetString(objv[0], NULL), "", 1);
8440 }
8441 }
8442 }
8443 /* Decr refcount of arguments and return the retcode */
8444 for (i = 0; i < objc; i++)
8445 Jim_DecrRefCount(interp, objv[i]);
8446 return retcode;
8447 }
8448
8449 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8450 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8451 * The returned object has refcount = 0. */
8452 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8453 int tokens, Jim_Obj **objPtrPtr)
8454 {
8455 int totlen = 0, i, retcode;
8456 Jim_Obj **intv;
8457 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8458 Jim_Obj *objPtr;
8459 char *s;
8460
8461 if (tokens <= JIM_EVAL_SINTV_LEN)
8462 intv = sintv;
8463 else
8464 intv = Jim_Alloc(sizeof(Jim_Obj*)*
8465 tokens);
8466 /* Compute every token forming the argument
8467 * in the intv objects vector. */
8468 for (i = 0; i < tokens; i++) {
8469 switch(token[i].type) {
8470 case JIM_TT_ESC:
8471 case JIM_TT_STR:
8472 intv[i] = token[i].objPtr;
8473 break;
8474 case JIM_TT_VAR:
8475 intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8476 if (!intv[i]) {
8477 retcode = JIM_ERR;
8478 goto err;
8479 }
8480 break;
8481 case JIM_TT_DICTSUGAR:
8482 intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8483 if (!intv[i]) {
8484 retcode = JIM_ERR;
8485 goto err;
8486 }
8487 break;
8488 case JIM_TT_CMD:
8489 retcode = Jim_EvalObj(interp, token[i].objPtr);
8490 if (retcode != JIM_OK)
8491 goto err;
8492 intv[i] = Jim_GetResult(interp);
8493 break;
8494 default:
8495 Jim_Panic(interp,
8496 "default token type reached "
8497 "in Jim_InterpolateTokens().");
8498 break;
8499 }
8500 Jim_IncrRefCount(intv[i]);
8501 /* Make sure there is a valid
8502 * string rep, and add the string
8503 * length to the total legnth. */
8504 Jim_GetString(intv[i], NULL);
8505 totlen += intv[i]->length;
8506 }
8507 /* Concatenate every token in an unique
8508 * object. */
8509 objPtr = Jim_NewStringObjNoAlloc(interp,
8510 NULL, 0);
8511 s = objPtr->bytes = Jim_Alloc(totlen+1);
8512 objPtr->length = totlen;
8513 for (i = 0; i < tokens; i++) {
8514 memcpy(s, intv[i]->bytes, intv[i]->length);
8515 s += intv[i]->length;
8516 Jim_DecrRefCount(interp, intv[i]);
8517 }
8518 objPtr->bytes[totlen] = '\0';
8519 /* Free the intv vector if not static. */
8520 if (tokens > JIM_EVAL_SINTV_LEN)
8521 Jim_Free(intv);
8522 *objPtrPtr = objPtr;
8523 return JIM_OK;
8524 err:
8525 i--;
8526 for (; i >= 0; i--)
8527 Jim_DecrRefCount(interp, intv[i]);
8528 if (tokens > JIM_EVAL_SINTV_LEN)
8529 Jim_Free(intv);
8530 return retcode;
8531 }
8532
8533 /* Helper of Jim_EvalObj() to perform argument expansion.
8534 * Basically this function append an argument to 'argv'
8535 * (and increments argc by reference accordingly), performing
8536 * expansion of the list object if 'expand' is non-zero, or
8537 * just adding objPtr to argv if 'expand' is zero. */
8538 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8539 int *argcPtr, int expand, Jim_Obj *objPtr)
8540 {
8541 if (!expand) {
8542 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8543 /* refcount of objPtr not incremented because
8544 * we are actually transfering a reference from
8545 * the old 'argv' to the expanded one. */
8546 (*argv)[*argcPtr] = objPtr;
8547 (*argcPtr)++;
8548 } else {
8549 int len, i;
8550
8551 Jim_ListLength(interp, objPtr, &len);
8552 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8553 for (i = 0; i < len; i++) {
8554 (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8555 Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8556 (*argcPtr)++;
8557 }
8558 /* The original object reference is no longer needed,
8559 * after the expansion it is no longer present on
8560 * the argument vector, but the single elements are
8561 * in its place. */
8562 Jim_DecrRefCount(interp, objPtr);
8563 }
8564 }
8565
8566 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8567 {
8568 int i, j = 0, len;
8569 ScriptObj *script;
8570 ScriptToken *token;
8571 int *cs; /* command structure array */
8572 int retcode = JIM_OK;
8573 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8574
8575 interp->errorFlag = 0;
8576
8577 /* If the object is of type "list" and there is no
8578 * string representation for this object, we can call
8579 * a specialized version of Jim_EvalObj() */
8580 if (scriptObjPtr->typePtr == &listObjType &&
8581 scriptObjPtr->internalRep.listValue.len &&
8582 scriptObjPtr->bytes == NULL) {
8583 Jim_IncrRefCount(scriptObjPtr);
8584 retcode = Jim_EvalObjVector(interp,
8585 scriptObjPtr->internalRep.listValue.len,
8586 scriptObjPtr->internalRep.listValue.ele);
8587 Jim_DecrRefCount(interp, scriptObjPtr);
8588 return retcode;
8589 }
8590
8591 Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8592 script = Jim_GetScript(interp, scriptObjPtr);
8593 /* Now we have to make sure the internal repr will not be
8594 * freed on shimmering.
8595 *
8596 * Think for example to this:
8597 *
8598 * set x {llength $x; ... some more code ...}; eval $x
8599 *
8600 * In order to preserve the internal rep, we increment the
8601 * inUse field of the script internal rep structure. */
8602 script->inUse++;
8603
8604 token = script->token;
8605 len = script->len;
8606 cs = script->cmdStruct;
8607 i = 0; /* 'i' is the current token index. */
8608
8609 /* Reset the interpreter result. This is useful to
8610 * return the emtpy result in the case of empty program. */
8611 Jim_SetEmptyResult(interp);
8612
8613 /* Execute every command sequentially, returns on
8614 * error (i.e. if a command does not return JIM_OK) */
8615 while (i < len) {
8616 int expand = 0;
8617 int argc = *cs++; /* Get the number of arguments */
8618 Jim_Cmd *cmd;
8619
8620 /* Set the expand flag if needed. */
8621 if (argc == -1) {
8622 expand++;
8623 argc = *cs++;
8624 }
8625 /* Allocate the arguments vector */
8626 if (argc <= JIM_EVAL_SARGV_LEN)
8627 argv = sargv;
8628 else
8629 argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8630 /* Populate the arguments objects. */
8631 for (j = 0; j < argc; j++) {
8632 int tokens = *cs++;
8633
8634 /* tokens is negative if expansion is needed.
8635 * for this argument. */
8636 if (tokens < 0) {
8637 tokens = (-tokens)-1;
8638 i++;
8639 }
8640 if (tokens == 1) {
8641 /* Fast path if the token does not
8642 * need interpolation */
8643 switch(token[i].type) {
8644 case JIM_TT_ESC:
8645 case JIM_TT_STR:
8646 argv[j] = token[i].objPtr;
8647 break;
8648 case JIM_TT_VAR:
8649 tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8650 JIM_ERRMSG);
8651 if (!tmpObjPtr) {
8652 retcode = JIM_ERR;
8653 goto err;
8654 }
8655 argv[j] = tmpObjPtr;
8656 break;
8657 case JIM_TT_DICTSUGAR:
8658 tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8659 if (!tmpObjPtr) {
8660 retcode = JIM_ERR;
8661 goto err;
8662 }
8663 argv[j] = tmpObjPtr;
8664 break;
8665 case JIM_TT_CMD:
8666 retcode = Jim_EvalObj(interp, token[i].objPtr);
8667 if (retcode != JIM_OK)
8668 goto err;
8669 argv[j] = Jim_GetResult(interp);
8670 break;
8671 default:
8672 Jim_Panic(interp,
8673 "default token type reached "
8674 "in Jim_EvalObj().");
8675 break;
8676 }
8677 Jim_IncrRefCount(argv[j]);
8678 i += 2;
8679 } else {
8680 /* For interpolation we call an helper
8681 * function doing the work for us. */
8682 if ((retcode = Jim_InterpolateTokens(interp,
8683 token+i, tokens, &tmpObjPtr)) != JIM_OK)
8684 {
8685 goto err;
8686 }
8687 argv[j] = tmpObjPtr;
8688 Jim_IncrRefCount(argv[j]);
8689 i += tokens+1;
8690 }
8691 }
8692 /* Handle {expand} expansion */
8693 if (expand) {
8694 int *ecs = cs - argc;
8695 int eargc = 0;
8696 Jim_Obj **eargv = NULL;
8697
8698 for (j = 0; j < argc; j++) {
8699 Jim_ExpandArgument( interp, &eargv, &eargc,
8700 ecs[j] < 0, argv[j]);
8701 }
8702 if (argv != sargv)
8703 Jim_Free(argv);
8704 argc = eargc;
8705 argv = eargv;
8706 j = argc;
8707 if (argc == 0) {
8708 /* Nothing to do with zero args. */
8709 Jim_Free(eargv);
8710 continue;
8711 }
8712 }
8713 /* Lookup the command to call */
8714 cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8715 if (cmd != NULL) {
8716 /* Call it -- Make sure result is an empty object. */
8717 Jim_SetEmptyResult(interp);
8718 if (cmd->cmdProc) {
8719 interp->cmdPrivData = cmd->privData;
8720 retcode = cmd->cmdProc(interp, argc, argv);
8721 if ((retcode == JIM_ERR)||(retcode == JIM_ERR_ADDSTACK)) {
8722 JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8723 retcode = JIM_ERR;
8724 }
8725 } else {
8726 retcode = JimCallProcedure(interp, cmd, argc, argv);
8727 if (retcode == JIM_ERR) {
8728 JimAppendStackTrace(interp,
8729 Jim_GetString(argv[0], NULL), script->fileName,
8730 token[i-argc*2].linenr);
8731 }
8732 }
8733 } else {
8734 /* Call [unknown] */
8735 retcode = JimUnknown(interp, argc, argv);
8736 if (retcode == JIM_ERR) {
8737 JimAppendStackTrace(interp,
8738 "", script->fileName,
8739 token[i-argc*2].linenr);
8740 }
8741 }
8742 if (retcode != JIM_OK) {
8743 i -= argc*2; /* point to the command name. */
8744 goto err;
8745 }
8746 /* Decrement the arguments count */
8747 for (j = 0; j < argc; j++) {
8748 Jim_DecrRefCount(interp, argv[j]);
8749 }
8750
8751 if (argv != sargv) {
8752 Jim_Free(argv);
8753 argv = NULL;
8754 }
8755 }
8756 /* Note that we don't have to decrement inUse, because the
8757 * following code transfers our use of the reference again to
8758 * the script object. */
8759 j = 0; /* on normal termination, the argv array is already
8760 Jim_DecrRefCount-ed. */
8761 err:
8762 /* Handle errors. */
8763 if (retcode == JIM_ERR && !interp->errorFlag) {
8764 interp->errorFlag = 1;
8765 JimSetErrorFileName(interp, script->fileName);
8766 JimSetErrorLineNumber(interp, token[i].linenr);
8767 JimResetStackTrace(interp);
8768 }
8769 Jim_FreeIntRep(interp, scriptObjPtr);
8770 scriptObjPtr->typePtr = &scriptObjType;
8771 Jim_SetIntRepPtr(scriptObjPtr, script);
8772 Jim_DecrRefCount(interp, scriptObjPtr);
8773 for (i = 0; i < j; i++) {
8774 Jim_DecrRefCount(interp, argv[i]);
8775 }
8776 if (argv != sargv)
8777 Jim_Free(argv);
8778 return retcode;
8779 }
8780
8781 /* Call a procedure implemented in Tcl.
8782 * It's possible to speed-up a lot this function, currently
8783 * the callframes are not cached, but allocated and
8784 * destroied every time. What is expecially costly is
8785 * to create/destroy the local vars hash table every time.
8786 *
8787 * This can be fixed just implementing callframes caching
8788 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8789 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8790 Jim_Obj *const *argv)
8791 {
8792 int i, retcode;
8793 Jim_CallFrame *callFramePtr;
8794 int num_args;
8795
8796 /* Check arity */
8797 if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8798 argc > cmd->arityMax)) {
8799 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8800 Jim_AppendStrings(interp, objPtr,
8801 "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8802 (cmd->arityMin > 1) ? " " : "",
8803 Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8804 Jim_SetResult(interp, objPtr);
8805 return JIM_ERR;
8806 }
8807 /* Check if there are too nested calls */
8808 if (interp->numLevels == interp->maxNestingDepth) {
8809 Jim_SetResultString(interp,
8810 "Too many nested calls. Infinite recursion?", -1);
8811 return JIM_ERR;
8812 }
8813 /* Create a new callframe */
8814 callFramePtr = JimCreateCallFrame(interp);
8815 callFramePtr->parentCallFrame = interp->framePtr;
8816 callFramePtr->argv = argv;
8817 callFramePtr->argc = argc;
8818 callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8819 callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8820 callFramePtr->staticVars = cmd->staticVars;
8821 Jim_IncrRefCount(cmd->argListObjPtr);
8822 Jim_IncrRefCount(cmd->bodyObjPtr);
8823 interp->framePtr = callFramePtr;
8824 interp->numLevels ++;
8825
8826 /* Set arguments */
8827 Jim_ListLength(interp, cmd->argListObjPtr, &num_args);
8828
8829 /* If last argument is 'args', don't set it here */
8830 if (cmd->arityMax == -1) {
8831 num_args--;
8832 }
8833
8834 for (i = 0; i < num_args; i++) {
8835 Jim_Obj *argObjPtr;
8836 Jim_Obj *nameObjPtr;
8837 Jim_Obj *valueObjPtr;
8838
8839 Jim_ListIndex(interp, cmd->argListObjPtr, i, &argObjPtr, JIM_NONE);
8840 if (i + 1 >= cmd->arityMin) {
8841 /* The name is the first element of the list */
8842 Jim_ListIndex(interp, argObjPtr, 0, &nameObjPtr, JIM_NONE);
8843 }
8844 else {
8845 /* The element arg is the name */
8846 nameObjPtr = argObjPtr;
8847 }
8848
8849 if (i + 1 >= argc) {
8850 /* No more values, so use default */
8851 /* The value is the second element of the list */
8852 Jim_ListIndex(interp, argObjPtr, 1, &valueObjPtr, JIM_NONE);
8853 }
8854 else {
8855 valueObjPtr = argv[i+1];
8856 }
8857 Jim_SetVariable(interp, nameObjPtr, valueObjPtr);
8858 }
8859 /* Set optional arguments */
8860 if (cmd->arityMax == -1) {
8861 Jim_Obj *listObjPtr, *objPtr;
8862
8863 i++;
8864 listObjPtr = Jim_NewListObj(interp, argv+i, argc-i);
8865 Jim_ListIndex(interp, cmd->argListObjPtr, num_args, &objPtr, JIM_NONE);
8866 Jim_SetVariable(interp, objPtr, listObjPtr);
8867 }
8868 /* Eval the body */
8869 retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8870
8871 /* Destroy the callframe */
8872 interp->numLevels --;
8873 interp->framePtr = interp->framePtr->parentCallFrame;
8874 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8875 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8876 } else {
8877 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8878 }
8879 /* Handle the JIM_EVAL return code */
8880 if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8881 int savedLevel = interp->evalRetcodeLevel;
8882
8883 interp->evalRetcodeLevel = interp->numLevels;
8884 while (retcode == JIM_EVAL) {
8885 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8886 Jim_IncrRefCount(resultScriptObjPtr);
8887 retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8888 Jim_DecrRefCount(interp, resultScriptObjPtr);
8889 }
8890 interp->evalRetcodeLevel = savedLevel;
8891 }
8892 /* Handle the JIM_RETURN return code */
8893 if (retcode == JIM_RETURN) {
8894 retcode = interp->returnCode;
8895 interp->returnCode = JIM_OK;
8896 }
8897 return retcode;
8898 }
8899
8900 int Jim_Eval_Named(Jim_Interp *interp, const char *script, const char *filename, int lineno)
8901 {
8902 int retval;
8903 Jim_Obj *scriptObjPtr;
8904
8905 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8906 Jim_IncrRefCount(scriptObjPtr);
8907
8908
8909 if( filename ){
8910 JimSetSourceInfo( interp, scriptObjPtr, filename, lineno );
8911 }
8912
8913 retval = Jim_EvalObj(interp, scriptObjPtr);
8914 Jim_DecrRefCount(interp, scriptObjPtr);
8915 return retval;
8916 }
8917
8918 int Jim_Eval(Jim_Interp *interp, const char *script)
8919 {
8920 return Jim_Eval_Named( interp, script, NULL, 0 );
8921 }
8922
8923
8924
8925 /* Execute script in the scope of the global level */
8926 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8927 {
8928 Jim_CallFrame *savedFramePtr;
8929 int retval;
8930
8931 savedFramePtr = interp->framePtr;
8932 interp->framePtr = interp->topFramePtr;
8933 retval = Jim_Eval(interp, script);
8934 interp->framePtr = savedFramePtr;
8935 return retval;
8936 }
8937
8938 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8939 {
8940 Jim_CallFrame *savedFramePtr;
8941 int retval;
8942
8943 savedFramePtr = interp->framePtr;
8944 interp->framePtr = interp->topFramePtr;
8945 retval = Jim_EvalObj(interp, scriptObjPtr);
8946 interp->framePtr = savedFramePtr;
8947 /* Try to report the error (if any) via the bgerror proc */
8948 if (retval != JIM_OK) {
8949 Jim_Obj *objv[2];
8950
8951 objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8952 objv[1] = Jim_GetResult(interp);
8953 Jim_IncrRefCount(objv[0]);
8954 Jim_IncrRefCount(objv[1]);
8955 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8956 /* Report the error to stderr. */
8957 Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8958 Jim_PrintErrorMessage(interp);
8959 }
8960 Jim_DecrRefCount(interp, objv[0]);
8961 Jim_DecrRefCount(interp, objv[1]);
8962 }
8963 return retval;
8964 }
8965
8966 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8967 {
8968 char *prg = NULL;
8969 FILE *fp;
8970 int nread, totread, maxlen, buflen;
8971 int retval;
8972 Jim_Obj *scriptObjPtr;
8973
8974 if ((fp = fopen(filename, "r")) == NULL) {
8975 const int cwd_len=2048;
8976 char *cwd=malloc(cwd_len);
8977 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8978 if (!getcwd( cwd, cwd_len )) strcpy(cwd, "unknown");
8979 Jim_AppendStrings(interp, Jim_GetResult(interp),
8980 "Error loading script \"", filename, "\"",
8981 " cwd: ", cwd,
8982 " err: ", strerror(errno), NULL);
8983 free(cwd);
8984 return JIM_ERR;
8985 }
8986 buflen = 1024;
8987 maxlen = totread = 0;
8988 while (1) {
8989 if (maxlen < totread+buflen+1) {
8990 maxlen = totread+buflen+1;
8991 prg = Jim_Realloc(prg, maxlen);
8992 }
8993 /* do not use Jim_fread() - this is really a file */
8994 if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8995 totread += nread;
8996 }
8997 prg[totread] = '\0';
8998 /* do not use Jim_fclose() - this is really a file */
8999 fclose(fp);
9000
9001 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
9002 JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
9003 Jim_IncrRefCount(scriptObjPtr);
9004 retval = Jim_EvalObj(interp, scriptObjPtr);
9005 Jim_DecrRefCount(interp, scriptObjPtr);
9006 return retval;
9007 }
9008
9009 /* -----------------------------------------------------------------------------
9010 * Subst
9011 * ---------------------------------------------------------------------------*/
9012 static int JimParseSubstStr(struct JimParserCtx *pc)
9013 {
9014 pc->tstart = pc->p;
9015 pc->tline = pc->linenr;
9016 while (*pc->p && *pc->p != '$' && *pc->p != '[') {
9017 pc->p++; pc->len--;
9018 }
9019 pc->tend = pc->p-1;
9020 pc->tt = JIM_TT_ESC;
9021 return JIM_OK;
9022 }
9023
9024 static int JimParseSubst(struct JimParserCtx *pc, int flags)
9025 {
9026 int retval;
9027
9028 if (pc->len == 0) {
9029 pc->tstart = pc->tend = pc->p;
9030 pc->tline = pc->linenr;
9031 pc->tt = JIM_TT_EOL;
9032 pc->eof = 1;
9033 return JIM_OK;
9034 }
9035 switch(*pc->p) {
9036 case '[':
9037 retval = JimParseCmd(pc);
9038 if (flags & JIM_SUBST_NOCMD) {
9039 pc->tstart--;
9040 pc->tend++;
9041 pc->tt = (flags & JIM_SUBST_NOESC) ?
9042 JIM_TT_STR : JIM_TT_ESC;
9043 }
9044 return retval;
9045 break;
9046 case '$':
9047 if (JimParseVar(pc) == JIM_ERR) {
9048 pc->tstart = pc->tend = pc->p++; pc->len--;
9049 pc->tline = pc->linenr;
9050 pc->tt = JIM_TT_STR;
9051 } else {
9052 if (flags & JIM_SUBST_NOVAR) {
9053 pc->tstart--;
9054 if (flags & JIM_SUBST_NOESC)
9055 pc->tt = JIM_TT_STR;
9056 else
9057 pc->tt = JIM_TT_ESC;
9058 if (*pc->tstart == '{') {
9059 pc->tstart--;
9060 if (*(pc->tend+1))
9061 pc->tend++;
9062 }
9063 }
9064 }
9065 break;
9066 default:
9067 retval = JimParseSubstStr(pc);
9068 if (flags & JIM_SUBST_NOESC)
9069 pc->tt = JIM_TT_STR;
9070 return retval;
9071 break;
9072 }
9073 return JIM_OK;
9074 }
9075
9076 /* The subst object type reuses most of the data structures and functions
9077 * of the script object. Script's data structures are a bit more complex
9078 * for what is needed for [subst]itution tasks, but the reuse helps to
9079 * deal with a single data structure at the cost of some more memory
9080 * usage for substitutions. */
9081 static Jim_ObjType substObjType = {
9082 "subst",
9083 FreeScriptInternalRep,
9084 DupScriptInternalRep,
9085 NULL,
9086 JIM_TYPE_REFERENCES,
9087 };
9088
9089 /* This method takes the string representation of an object
9090 * as a Tcl string where to perform [subst]itution, and generates
9091 * the pre-parsed internal representation. */
9092 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
9093 {
9094 int scriptTextLen;
9095 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9096 struct JimParserCtx parser;
9097 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
9098
9099 script->len = 0;
9100 script->csLen = 0;
9101 script->commands = 0;
9102 script->token = NULL;
9103 script->cmdStruct = NULL;
9104 script->inUse = 1;
9105 script->substFlags = flags;
9106 script->fileName = NULL;
9107
9108 JimParserInit(&parser, scriptText, scriptTextLen, 1);
9109 while(1) {
9110 char *token;
9111 int len, type, linenr;
9112
9113 JimParseSubst(&parser, flags);
9114 if (JimParserEof(&parser)) break;
9115 token = JimParserGetToken(&parser, &len, &type, &linenr);
9116 ScriptObjAddToken(interp, script, token, len, type,
9117 NULL, linenr);
9118 }
9119 /* Free the old internal rep and set the new one. */
9120 Jim_FreeIntRep(interp, objPtr);
9121 Jim_SetIntRepPtr(objPtr, script);
9122 objPtr->typePtr = &scriptObjType;
9123 return JIM_OK;
9124 }
9125
9126 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9127 {
9128 struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
9129
9130 if (objPtr->typePtr != &substObjType || script->substFlags != flags)
9131 SetSubstFromAny(interp, objPtr, flags);
9132 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
9133 }
9134
9135 /* Performs commands,variables,blackslashes substitution,
9136 * storing the result object (with refcount 0) into
9137 * resObjPtrPtr. */
9138 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
9139 Jim_Obj **resObjPtrPtr, int flags)
9140 {
9141 ScriptObj *script;
9142 ScriptToken *token;
9143 int i, len, retcode = JIM_OK;
9144 Jim_Obj *resObjPtr, *savedResultObjPtr;
9145
9146 script = Jim_GetSubst(interp, substObjPtr, flags);
9147 #ifdef JIM_OPTIMIZATION
9148 /* Fast path for a very common case with array-alike syntax,
9149 * that's: $foo($bar) */
9150 if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
9151 Jim_Obj *varObjPtr = script->token[0].objPtr;
9152
9153 Jim_IncrRefCount(varObjPtr);
9154 resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
9155 if (resObjPtr == NULL) {
9156 Jim_DecrRefCount(interp, varObjPtr);
9157 return JIM_ERR;
9158 }
9159 Jim_DecrRefCount(interp, varObjPtr);
9160 *resObjPtrPtr = resObjPtr;
9161 return JIM_OK;
9162 }
9163 #endif
9164
9165 Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
9166 /* In order to preserve the internal rep, we increment the
9167 * inUse field of the script internal rep structure. */
9168 script->inUse++;
9169
9170 token = script->token;
9171 len = script->len;
9172
9173 /* Save the interp old result, to set it again before
9174 * to return. */
9175 savedResultObjPtr = interp->result;
9176 Jim_IncrRefCount(savedResultObjPtr);
9177
9178 /* Perform the substitution. Starts with an empty object
9179 * and adds every token (performing the appropriate
9180 * var/command/escape substitution). */
9181 resObjPtr = Jim_NewStringObj(interp, "", 0);
9182 for (i = 0; i < len; i++) {
9183 Jim_Obj *objPtr;
9184
9185 switch(token[i].type) {
9186 case JIM_TT_STR:
9187 case JIM_TT_ESC:
9188 Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
9189 break;
9190 case JIM_TT_VAR:
9191 objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
9192 if (objPtr == NULL) goto err;
9193 Jim_IncrRefCount(objPtr);
9194 Jim_AppendObj(interp, resObjPtr, objPtr);
9195 Jim_DecrRefCount(interp, objPtr);
9196 break;
9197 case JIM_TT_DICTSUGAR:
9198 objPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
9199 if (!objPtr) {
9200 retcode = JIM_ERR;
9201 goto err;
9202 }
9203 break;
9204 case JIM_TT_CMD:
9205 if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
9206 goto err;
9207 Jim_AppendObj(interp, resObjPtr, interp->result);
9208 break;
9209 default:
9210 Jim_Panic(interp,
9211 "default token type (%d) reached "
9212 "in Jim_SubstObj().", token[i].type);
9213 break;
9214 }
9215 }
9216 ok:
9217 if (retcode == JIM_OK)
9218 Jim_SetResult(interp, savedResultObjPtr);
9219 Jim_DecrRefCount(interp, savedResultObjPtr);
9220 /* Note that we don't have to decrement inUse, because the
9221 * following code transfers our use of the reference again to
9222 * the script object. */
9223 Jim_FreeIntRep(interp, substObjPtr);
9224 substObjPtr->typePtr = &scriptObjType;
9225 Jim_SetIntRepPtr(substObjPtr, script);
9226 Jim_DecrRefCount(interp, substObjPtr);
9227 *resObjPtrPtr = resObjPtr;
9228 return retcode;
9229 err:
9230 Jim_FreeNewObj(interp, resObjPtr);
9231 retcode = JIM_ERR;
9232 goto ok;
9233 }
9234
9235 /* -----------------------------------------------------------------------------
9236 * API Input/Export functions
9237 * ---------------------------------------------------------------------------*/
9238
9239 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
9240 {
9241 Jim_HashEntry *he;
9242
9243 he = Jim_FindHashEntry(&interp->stub, funcname);
9244 if (!he)
9245 return JIM_ERR;
9246 memcpy(targetPtrPtr, &he->val, sizeof(void*));
9247 return JIM_OK;
9248 }
9249
9250 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
9251 {
9252 return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
9253 }
9254
9255 #define JIM_REGISTER_API(name) \
9256 Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
9257
9258 void JimRegisterCoreApi(Jim_Interp *interp)
9259 {
9260 interp->getApiFuncPtr = Jim_GetApi;
9261 JIM_REGISTER_API(Alloc);
9262 JIM_REGISTER_API(Free);
9263 JIM_REGISTER_API(Eval);
9264 JIM_REGISTER_API(Eval_Named);
9265 JIM_REGISTER_API(EvalGlobal);
9266 JIM_REGISTER_API(EvalFile);
9267 JIM_REGISTER_API(EvalObj);
9268 JIM_REGISTER_API(EvalObjBackground);
9269 JIM_REGISTER_API(EvalObjVector);
9270 JIM_REGISTER_API(InitHashTable);
9271 JIM_REGISTER_API(ExpandHashTable);
9272 JIM_REGISTER_API(AddHashEntry);
9273 JIM_REGISTER_API(ReplaceHashEntry);
9274 JIM_REGISTER_API(DeleteHashEntry);
9275 JIM_REGISTER_API(FreeHashTable);
9276 JIM_REGISTER_API(FindHashEntry);
9277 JIM_REGISTER_API(ResizeHashTable);
9278 JIM_REGISTER_API(GetHashTableIterator);
9279 JIM_REGISTER_API(NextHashEntry);
9280 JIM_REGISTER_API(NewObj);
9281 JIM_REGISTER_API(FreeObj);
9282 JIM_REGISTER_API(InvalidateStringRep);
9283 JIM_REGISTER_API(InitStringRep);
9284 JIM_REGISTER_API(DuplicateObj);
9285 JIM_REGISTER_API(GetString);
9286 JIM_REGISTER_API(Length);
9287 JIM_REGISTER_API(InvalidateStringRep);
9288 JIM_REGISTER_API(NewStringObj);
9289 JIM_REGISTER_API(NewStringObjNoAlloc);
9290 JIM_REGISTER_API(AppendString);
9291 JIM_REGISTER_API(AppendString_sprintf);
9292 JIM_REGISTER_API(AppendObj);
9293 JIM_REGISTER_API(AppendStrings);
9294 JIM_REGISTER_API(StringEqObj);
9295 JIM_REGISTER_API(StringMatchObj);
9296 JIM_REGISTER_API(StringRangeObj);
9297 JIM_REGISTER_API(FormatString);
9298 JIM_REGISTER_API(CompareStringImmediate);
9299 JIM_REGISTER_API(NewReference);
9300 JIM_REGISTER_API(GetReference);
9301 JIM_REGISTER_API(SetFinalizer);
9302 JIM_REGISTER_API(GetFinalizer);
9303 JIM_REGISTER_API(CreateInterp);
9304 JIM_REGISTER_API(FreeInterp);
9305 JIM_REGISTER_API(GetExitCode);
9306 JIM_REGISTER_API(SetStdin);
9307 JIM_REGISTER_API(SetStdout);
9308 JIM_REGISTER_API(SetStderr);
9309 JIM_REGISTER_API(CreateCommand);
9310 JIM_REGISTER_API(CreateProcedure);
9311 JIM_REGISTER_API(DeleteCommand);
9312 JIM_REGISTER_API(RenameCommand);
9313 JIM_REGISTER_API(GetCommand);
9314 JIM_REGISTER_API(SetVariable);
9315 JIM_REGISTER_API(SetVariableStr);
9316 JIM_REGISTER_API(SetGlobalVariableStr);
9317 JIM_REGISTER_API(SetVariableStrWithStr);
9318 JIM_REGISTER_API(SetVariableLink);
9319 JIM_REGISTER_API(GetVariable);
9320 JIM_REGISTER_API(GetCallFrameByLevel);
9321 JIM_REGISTER_API(Collect);
9322 JIM_REGISTER_API(CollectIfNeeded);
9323 JIM_REGISTER_API(GetIndex);
9324 JIM_REGISTER_API(NewListObj);
9325 JIM_REGISTER_API(ListAppendElement);
9326 JIM_REGISTER_API(ListAppendList);
9327 JIM_REGISTER_API(ListLength);
9328 JIM_REGISTER_API(ListIndex);
9329 JIM_REGISTER_API(SetListIndex);
9330 JIM_REGISTER_API(ConcatObj);
9331 JIM_REGISTER_API(NewDictObj);
9332 JIM_REGISTER_API(DictKey);
9333 JIM_REGISTER_API(DictKeysVector);
9334 JIM_REGISTER_API(GetIndex);
9335 JIM_REGISTER_API(GetReturnCode);
9336 JIM_REGISTER_API(EvalExpression);
9337 JIM_REGISTER_API(GetBoolFromExpr);
9338 JIM_REGISTER_API(GetWide);
9339 JIM_REGISTER_API(GetLong);
9340 JIM_REGISTER_API(SetWide);
9341 JIM_REGISTER_API(NewIntObj);
9342 JIM_REGISTER_API(GetDouble);
9343 JIM_REGISTER_API(SetDouble);
9344 JIM_REGISTER_API(NewDoubleObj);
9345 JIM_REGISTER_API(WrongNumArgs);
9346 JIM_REGISTER_API(SetDictKeysVector);
9347 JIM_REGISTER_API(SubstObj);
9348 JIM_REGISTER_API(RegisterApi);
9349 JIM_REGISTER_API(PrintErrorMessage);
9350 JIM_REGISTER_API(InteractivePrompt);
9351 JIM_REGISTER_API(RegisterCoreCommands);
9352 JIM_REGISTER_API(GetSharedString);
9353 JIM_REGISTER_API(ReleaseSharedString);
9354 JIM_REGISTER_API(Panic);
9355 JIM_REGISTER_API(StrDup);
9356 JIM_REGISTER_API(UnsetVariable);
9357 JIM_REGISTER_API(GetVariableStr);
9358 JIM_REGISTER_API(GetGlobalVariable);
9359 JIM_REGISTER_API(GetGlobalVariableStr);
9360 JIM_REGISTER_API(GetAssocData);
9361 JIM_REGISTER_API(SetAssocData);
9362 JIM_REGISTER_API(DeleteAssocData);
9363 JIM_REGISTER_API(GetEnum);
9364 JIM_REGISTER_API(ScriptIsComplete);
9365 JIM_REGISTER_API(PackageRequire);
9366 JIM_REGISTER_API(PackageProvide);
9367 JIM_REGISTER_API(InitStack);
9368 JIM_REGISTER_API(FreeStack);
9369 JIM_REGISTER_API(StackLen);
9370 JIM_REGISTER_API(StackPush);
9371 JIM_REGISTER_API(StackPop);
9372 JIM_REGISTER_API(StackPeek);
9373 JIM_REGISTER_API(FreeStackElements);
9374 JIM_REGISTER_API(fprintf );
9375 JIM_REGISTER_API(vfprintf );
9376 JIM_REGISTER_API(fwrite );
9377 JIM_REGISTER_API(fread );
9378 JIM_REGISTER_API(fflush );
9379 JIM_REGISTER_API(fgets );
9380 JIM_REGISTER_API(GetNvp);
9381 JIM_REGISTER_API(Nvp_name2value);
9382 JIM_REGISTER_API(Nvp_name2value_simple);
9383 JIM_REGISTER_API(Nvp_name2value_obj);
9384 JIM_REGISTER_API(Nvp_name2value_nocase);
9385 JIM_REGISTER_API(Nvp_name2value_obj_nocase);
9386
9387 JIM_REGISTER_API(Nvp_value2name);
9388 JIM_REGISTER_API(Nvp_value2name_simple);
9389 JIM_REGISTER_API(Nvp_value2name_obj);
9390
9391 JIM_REGISTER_API(GetOpt_Setup);
9392 JIM_REGISTER_API(GetOpt_Debug);
9393 JIM_REGISTER_API(GetOpt_Obj);
9394 JIM_REGISTER_API(GetOpt_String);
9395 JIM_REGISTER_API(GetOpt_Double);
9396 JIM_REGISTER_API(GetOpt_Wide);
9397 JIM_REGISTER_API(GetOpt_Nvp);
9398 JIM_REGISTER_API(GetOpt_NvpUnknown);
9399 JIM_REGISTER_API(GetOpt_Enum);
9400
9401 JIM_REGISTER_API(Debug_ArgvString);
9402 JIM_REGISTER_API(SetResult_sprintf);
9403 JIM_REGISTER_API(SetResult_NvpUnknown);
9404
9405 }
9406
9407 /* -----------------------------------------------------------------------------
9408 * Core commands utility functions
9409 * ---------------------------------------------------------------------------*/
9410 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
9411 const char *msg)
9412 {
9413 int i;
9414 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9415
9416 Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9417 for (i = 0; i < argc; i++) {
9418 Jim_AppendObj(interp, objPtr, argv[i]);
9419 if (!(i+1 == argc && msg[0] == '\0'))
9420 Jim_AppendString(interp, objPtr, " ", 1);
9421 }
9422 Jim_AppendString(interp, objPtr, msg, -1);
9423 Jim_AppendString(interp, objPtr, "\"", 1);
9424 Jim_SetResult(interp, objPtr);
9425 }
9426
9427 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9428 {
9429 Jim_HashTableIterator *htiter;
9430 Jim_HashEntry *he;
9431 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9432 const char *pattern;
9433 int patternLen;
9434
9435 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9436 htiter = Jim_GetHashTableIterator(&interp->commands);
9437 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9438 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9439 strlen((const char*)he->key), 0))
9440 continue;
9441 Jim_ListAppendElement(interp, listObjPtr,
9442 Jim_NewStringObj(interp, he->key, -1));
9443 }
9444 Jim_FreeHashTableIterator(htiter);
9445 return listObjPtr;
9446 }
9447
9448 #define JIM_VARLIST_GLOBALS 0
9449 #define JIM_VARLIST_LOCALS 1
9450 #define JIM_VARLIST_VARS 2
9451
9452 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9453 int mode)
9454 {
9455 Jim_HashTableIterator *htiter;
9456 Jim_HashEntry *he;
9457 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9458 const char *pattern;
9459 int patternLen;
9460
9461 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9462 if (mode == JIM_VARLIST_GLOBALS) {
9463 htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9464 } else {
9465 /* For [info locals], if we are at top level an emtpy list
9466 * is returned. I don't agree, but we aim at compatibility (SS) */
9467 if (mode == JIM_VARLIST_LOCALS &&
9468 interp->framePtr == interp->topFramePtr)
9469 return listObjPtr;
9470 htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9471 }
9472 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9473 Jim_Var *varPtr = (Jim_Var*) he->val;
9474 if (mode == JIM_VARLIST_LOCALS) {
9475 if (varPtr->linkFramePtr != NULL)
9476 continue;
9477 }
9478 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9479 strlen((const char*)he->key), 0))
9480 continue;
9481 Jim_ListAppendElement(interp, listObjPtr,
9482 Jim_NewStringObj(interp, he->key, -1));
9483 }
9484 Jim_FreeHashTableIterator(htiter);
9485 return listObjPtr;
9486 }
9487
9488 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9489 Jim_Obj **objPtrPtr)
9490 {
9491 Jim_CallFrame *targetCallFrame;
9492
9493 if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9494 != JIM_OK)
9495 return JIM_ERR;
9496 /* No proc call at toplevel callframe */
9497 if (targetCallFrame == interp->topFramePtr) {
9498 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9499 Jim_AppendStrings(interp, Jim_GetResult(interp),
9500 "bad level \"",
9501 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9502 return JIM_ERR;
9503 }
9504 *objPtrPtr = Jim_NewListObj(interp,
9505 targetCallFrame->argv,
9506 targetCallFrame->argc);
9507 return JIM_OK;
9508 }
9509
9510 /* -----------------------------------------------------------------------------
9511 * Core commands
9512 * ---------------------------------------------------------------------------*/
9513
9514 /* fake [puts] -- not the real puts, just for debugging. */
9515 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9516 Jim_Obj *const *argv)
9517 {
9518 const char *str;
9519 int len, nonewline = 0;
9520
9521 if (argc != 2 && argc != 3) {
9522 Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9523 return JIM_ERR;
9524 }
9525 if (argc == 3) {
9526 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9527 {
9528 Jim_SetResultString(interp, "The second argument must "
9529 "be -nonewline", -1);
9530 return JIM_OK;
9531 } else {
9532 nonewline = 1;
9533 argv++;
9534 }
9535 }
9536 str = Jim_GetString(argv[1], &len);
9537 Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9538 if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9539 return JIM_OK;
9540 }
9541
9542 /* Helper for [+] and [*] */
9543 static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
9544 Jim_Obj *const *argv, int op)
9545 {
9546 jim_wide wideValue, res;
9547 double doubleValue, doubleRes;
9548 int i;
9549
9550 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9551
9552 for (i = 1; i < argc; i++) {
9553 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9554 goto trydouble;
9555 if (op == JIM_EXPROP_ADD)
9556 res += wideValue;
9557 else
9558 res *= wideValue;
9559 }
9560 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9561 return JIM_OK;
9562 trydouble:
9563 doubleRes = (double) res;
9564 for (;i < argc; i++) {
9565 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9566 return JIM_ERR;
9567 if (op == JIM_EXPROP_ADD)
9568 doubleRes += doubleValue;
9569 else
9570 doubleRes *= doubleValue;
9571 }
9572 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9573 return JIM_OK;
9574 }
9575
9576 /* Helper for [-] and [/] */
9577 static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9578 Jim_Obj *const *argv, int op)
9579 {
9580 jim_wide wideValue, res = 0;
9581 double doubleValue, doubleRes = 0;
9582 int i = 2;
9583
9584 if (argc < 2) {
9585 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9586 return JIM_ERR;
9587 } else if (argc == 2) {
9588 /* The arity = 2 case is different. For [- x] returns -x,
9589 * while [/ x] returns 1/x. */
9590 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9591 if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9592 JIM_OK)
9593 {
9594 return JIM_ERR;
9595 } else {
9596 if (op == JIM_EXPROP_SUB)
9597 doubleRes = -doubleValue;
9598 else
9599 doubleRes = 1.0/doubleValue;
9600 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9601 doubleRes));
9602 return JIM_OK;
9603 }
9604 }
9605 if (op == JIM_EXPROP_SUB) {
9606 res = -wideValue;
9607 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9608 } else {
9609 doubleRes = 1.0/wideValue;
9610 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9611 doubleRes));
9612 }
9613 return JIM_OK;
9614 } else {
9615 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9616 if (Jim_GetDouble(interp, argv[1], &doubleRes)
9617 != JIM_OK) {
9618 return JIM_ERR;
9619 } else {
9620 goto trydouble;
9621 }
9622 }
9623 }
9624 for (i = 2; i < argc; i++) {
9625 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9626 doubleRes = (double) res;
9627 goto trydouble;
9628 }
9629 if (op == JIM_EXPROP_SUB)
9630 res -= wideValue;
9631 else
9632 res /= wideValue;
9633 }
9634 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9635 return JIM_OK;
9636 trydouble:
9637 for (;i < argc; i++) {
9638 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9639 return JIM_ERR;
9640 if (op == JIM_EXPROP_SUB)
9641 doubleRes -= doubleValue;
9642 else
9643 doubleRes /= doubleValue;
9644 }
9645 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9646 return JIM_OK;
9647 }
9648
9649
9650 /* [+] */
9651 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9652 Jim_Obj *const *argv)
9653 {
9654 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9655 }
9656
9657 /* [*] */
9658 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9659 Jim_Obj *const *argv)
9660 {
9661 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9662 }
9663
9664 /* [-] */
9665 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9666 Jim_Obj *const *argv)
9667 {
9668 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9669 }
9670
9671 /* [/] */
9672 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9673 Jim_Obj *const *argv)
9674 {
9675 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9676 }
9677
9678 /* [set] */
9679 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9680 Jim_Obj *const *argv)
9681 {
9682 if (argc != 2 && argc != 3) {
9683 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9684 return JIM_ERR;
9685 }
9686 if (argc == 2) {
9687 Jim_Obj *objPtr;
9688 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9689 if (!objPtr)
9690 return JIM_ERR;
9691 Jim_SetResult(interp, objPtr);
9692 return JIM_OK;
9693 }
9694 /* argc == 3 case. */
9695 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9696 return JIM_ERR;
9697 Jim_SetResult(interp, argv[2]);
9698 return JIM_OK;
9699 }
9700
9701 /* [unset] */
9702 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9703 Jim_Obj *const *argv)
9704 {
9705 int i;
9706
9707 if (argc < 2) {
9708 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9709 return JIM_ERR;
9710 }
9711 for (i = 1; i < argc; i++) {
9712 if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9713 return JIM_ERR;
9714 }
9715 return JIM_OK;
9716 }
9717
9718 /* [incr] */
9719 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9720 Jim_Obj *const *argv)
9721 {
9722 jim_wide wideValue, increment = 1;
9723 Jim_Obj *intObjPtr;
9724
9725 if (argc != 2 && argc != 3) {
9726 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9727 return JIM_ERR;
9728 }
9729 if (argc == 3) {
9730 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9731 return JIM_ERR;
9732 }
9733 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9734 if (!intObjPtr) return JIM_ERR;
9735 if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9736 return JIM_ERR;
9737 if (Jim_IsShared(intObjPtr)) {
9738 intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9739 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9740 Jim_FreeNewObj(interp, intObjPtr);
9741 return JIM_ERR;
9742 }
9743 } else {
9744 Jim_SetWide(interp, intObjPtr, wideValue+increment);
9745 /* The following step is required in order to invalidate the
9746 * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9747 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9748 return JIM_ERR;
9749 }
9750 }
9751 Jim_SetResult(interp, intObjPtr);
9752 return JIM_OK;
9753 }
9754
9755 /* [while] */
9756 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9757 Jim_Obj *const *argv)
9758 {
9759 if (argc != 3) {
9760 Jim_WrongNumArgs(interp, 1, argv, "condition body");
9761 return JIM_ERR;
9762 }
9763 /* Try to run a specialized version of while if the expression
9764 * is in one of the following forms:
9765 *
9766 * $a < CONST, $a < $b
9767 * $a <= CONST, $a <= $b
9768 * $a > CONST, $a > $b
9769 * $a >= CONST, $a >= $b
9770 * $a != CONST, $a != $b
9771 * $a == CONST, $a == $b
9772 * $a
9773 * !$a
9774 * CONST
9775 */
9776
9777 #ifdef JIM_OPTIMIZATION
9778 {
9779 ExprByteCode *expr;
9780 Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9781 int exprLen, retval;
9782
9783 /* STEP 1 -- Check if there are the conditions to run the specialized
9784 * version of while */
9785
9786 if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9787 if (expr->len <= 0 || expr->len > 3) goto noopt;
9788 switch(expr->len) {
9789 case 1:
9790 if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9791 expr->opcode[0] != JIM_EXPROP_NUMBER)
9792 goto noopt;
9793 break;
9794 case 2:
9795 if (expr->opcode[1] != JIM_EXPROP_NOT ||
9796 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9797 goto noopt;
9798 break;
9799 case 3:
9800 if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9801 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9802 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9803 goto noopt;
9804 switch(expr->opcode[2]) {
9805 case JIM_EXPROP_LT:
9806 case JIM_EXPROP_LTE:
9807 case JIM_EXPROP_GT:
9808 case JIM_EXPROP_GTE:
9809 case JIM_EXPROP_NUMEQ:
9810 case JIM_EXPROP_NUMNE:
9811 /* nothing to do */
9812 break;
9813 default:
9814 goto noopt;
9815 }
9816 break;
9817 default:
9818 Jim_Panic(interp,
9819 "Unexpected default reached in Jim_WhileCoreCommand()");
9820 break;
9821 }
9822
9823 /* STEP 2 -- conditions meet. Initialization. Take different
9824 * branches for different expression lengths. */
9825 exprLen = expr->len;
9826
9827 if (exprLen == 1) {
9828 jim_wide wideValue;
9829
9830 if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9831 varAObjPtr = expr->obj[0];
9832 Jim_IncrRefCount(varAObjPtr);
9833 } else {
9834 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9835 goto noopt;
9836 }
9837 while (1) {
9838 if (varAObjPtr) {
9839 if (!(objPtr =
9840 Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9841 Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9842 {
9843 Jim_DecrRefCount(interp, varAObjPtr);
9844 goto noopt;
9845 }
9846 }
9847 if (!wideValue) break;
9848 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9849 switch(retval) {
9850 case JIM_BREAK:
9851 if (varAObjPtr)
9852 Jim_DecrRefCount(interp, varAObjPtr);
9853 goto out;
9854 break;
9855 case JIM_CONTINUE:
9856 continue;
9857 break;
9858 default:
9859 if (varAObjPtr)
9860 Jim_DecrRefCount(interp, varAObjPtr);
9861 return retval;
9862 }
9863 }
9864 }
9865 if (varAObjPtr)
9866 Jim_DecrRefCount(interp, varAObjPtr);
9867 } else if (exprLen == 3) {
9868 jim_wide wideValueA, wideValueB, cmpRes = 0;
9869 int cmpType = expr->opcode[2];
9870
9871 varAObjPtr = expr->obj[0];
9872 Jim_IncrRefCount(varAObjPtr);
9873 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9874 varBObjPtr = expr->obj[1];
9875 Jim_IncrRefCount(varBObjPtr);
9876 } else {
9877 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9878 goto noopt;
9879 }
9880 while (1) {
9881 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9882 Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9883 {
9884 Jim_DecrRefCount(interp, varAObjPtr);
9885 if (varBObjPtr)
9886 Jim_DecrRefCount(interp, varBObjPtr);
9887 goto noopt;
9888 }
9889 if (varBObjPtr) {
9890 if (!(objPtr =
9891 Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9892 Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9893 {
9894 Jim_DecrRefCount(interp, varAObjPtr);
9895 if (varBObjPtr)
9896 Jim_DecrRefCount(interp, varBObjPtr);
9897 goto noopt;
9898 }
9899 }
9900 switch(cmpType) {
9901 case JIM_EXPROP_LT:
9902 cmpRes = wideValueA < wideValueB; break;
9903 case JIM_EXPROP_LTE:
9904 cmpRes = wideValueA <= wideValueB; break;
9905 case JIM_EXPROP_GT:
9906 cmpRes = wideValueA > wideValueB; break;
9907 case JIM_EXPROP_GTE:
9908 cmpRes = wideValueA >= wideValueB; break;
9909 case JIM_EXPROP_NUMEQ:
9910 cmpRes = wideValueA == wideValueB; break;
9911 case JIM_EXPROP_NUMNE:
9912 cmpRes = wideValueA != wideValueB; break;
9913 }
9914 if (!cmpRes) break;
9915 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9916 switch(retval) {
9917 case JIM_BREAK:
9918 Jim_DecrRefCount(interp, varAObjPtr);
9919 if (varBObjPtr)
9920 Jim_DecrRefCount(interp, varBObjPtr);
9921 goto out;
9922 break;
9923 case JIM_CONTINUE:
9924 continue;
9925 break;
9926 default:
9927 Jim_DecrRefCount(interp, varAObjPtr);
9928 if (varBObjPtr)
9929 Jim_DecrRefCount(interp, varBObjPtr);
9930 return retval;
9931 }
9932 }
9933 }
9934 Jim_DecrRefCount(interp, varAObjPtr);
9935 if (varBObjPtr)
9936 Jim_DecrRefCount(interp, varBObjPtr);
9937 } else {
9938 /* TODO: case for len == 2 */
9939 goto noopt;
9940 }
9941 Jim_SetEmptyResult(interp);
9942 return JIM_OK;
9943 }
9944 noopt:
9945 #endif
9946
9947 /* The general purpose implementation of while starts here */
9948 while (1) {
9949 int boolean, retval;
9950
9951 if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9952 &boolean)) != JIM_OK)
9953 return retval;
9954 if (!boolean) break;
9955 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9956 switch(retval) {
9957 case JIM_BREAK:
9958 goto out;
9959 break;
9960 case JIM_CONTINUE:
9961 continue;
9962 break;
9963 default:
9964 return retval;
9965 }
9966 }
9967 }
9968 out:
9969 Jim_SetEmptyResult(interp);
9970 return JIM_OK;
9971 }
9972
9973 /* [for] */
9974 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9975 Jim_Obj *const *argv)
9976 {
9977 int retval;
9978
9979 if (argc != 5) {
9980 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9981 return JIM_ERR;
9982 }
9983 /* Check if the for is on the form:
9984 * for {set i CONST} {$i < CONST} {incr i}
9985 * for {set i CONST} {$i < $j} {incr i}
9986 * for {set i CONST} {$i <= CONST} {incr i}
9987 * for {set i CONST} {$i <= $j} {incr i}
9988 * XXX: NOTE: if variable traces are implemented, this optimization
9989 * need to be modified to check for the proc epoch at every variable
9990 * update. */
9991 #ifdef JIM_OPTIMIZATION
9992 {
9993 ScriptObj *initScript, *incrScript;
9994 ExprByteCode *expr;
9995 jim_wide start, stop, currentVal;
9996 unsigned jim_wide procEpoch = interp->procEpoch;
9997 Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9998 int cmpType;
9999 struct Jim_Cmd *cmdPtr;
10000
10001 /* Do it only if there aren't shared arguments */
10002 if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
10003 goto evalstart;
10004 initScript = Jim_GetScript(interp, argv[1]);
10005 expr = Jim_GetExpression(interp, argv[2]);
10006 incrScript = Jim_GetScript(interp, argv[3]);
10007
10008 /* Ensure proper lengths to start */
10009 if (initScript->len != 6) goto evalstart;
10010 if (incrScript->len != 4) goto evalstart;
10011 if (expr->len != 3) goto evalstart;
10012 /* Ensure proper token types. */
10013 if (initScript->token[2].type != JIM_TT_ESC ||
10014 initScript->token[4].type != JIM_TT_ESC ||
10015 incrScript->token[2].type != JIM_TT_ESC ||
10016 expr->opcode[0] != JIM_EXPROP_VARIABLE ||
10017 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
10018 expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
10019 (expr->opcode[2] != JIM_EXPROP_LT &&
10020 expr->opcode[2] != JIM_EXPROP_LTE))
10021 goto evalstart;
10022 cmpType = expr->opcode[2];
10023 /* Initialization command must be [set] */
10024 cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
10025 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
10026 goto evalstart;
10027 /* Update command must be incr */
10028 cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
10029 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
10030 goto evalstart;
10031 /* set, incr, expression must be about the same variable */
10032 if (!Jim_StringEqObj(initScript->token[2].objPtr,
10033 incrScript->token[2].objPtr, 0))
10034 goto evalstart;
10035 if (!Jim_StringEqObj(initScript->token[2].objPtr,
10036 expr->obj[0], 0))
10037 goto evalstart;
10038 /* Check that the initialization and comparison are valid integers */
10039 if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
10040 goto evalstart;
10041 if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
10042 Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
10043 {
10044 goto evalstart;
10045 }
10046
10047 /* Initialization */
10048 varNamePtr = expr->obj[0];
10049 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
10050 stopVarNamePtr = expr->obj[1];
10051 Jim_IncrRefCount(stopVarNamePtr);
10052 }
10053 Jim_IncrRefCount(varNamePtr);
10054
10055 /* --- OPTIMIZED FOR --- */
10056 /* Start to loop */
10057 objPtr = Jim_NewIntObj(interp, start);
10058 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
10059 Jim_DecrRefCount(interp, varNamePtr);
10060 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10061 Jim_FreeNewObj(interp, objPtr);
10062 goto evalstart;
10063 }
10064 while (1) {
10065 /* === Check condition === */
10066 /* Common code: */
10067 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
10068 if (objPtr == NULL ||
10069 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
10070 {
10071 Jim_DecrRefCount(interp, varNamePtr);
10072 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10073 goto testcond;
10074 }
10075 /* Immediate or Variable? get the 'stop' value if the latter. */
10076 if (stopVarNamePtr) {
10077 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
10078 if (objPtr == NULL ||
10079 Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
10080 {
10081 Jim_DecrRefCount(interp, varNamePtr);
10082 Jim_DecrRefCount(interp, stopVarNamePtr);
10083 goto testcond;
10084 }
10085 }
10086 if (cmpType == JIM_EXPROP_LT) {
10087 if (currentVal >= stop) break;
10088 } else {
10089 if (currentVal > stop) break;
10090 }
10091 /* Eval body */
10092 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10093 switch(retval) {
10094 case JIM_BREAK:
10095 if (stopVarNamePtr)
10096 Jim_DecrRefCount(interp, stopVarNamePtr);
10097 Jim_DecrRefCount(interp, varNamePtr);
10098 goto out;
10099 case JIM_CONTINUE:
10100 /* nothing to do */
10101 break;
10102 default:
10103 if (stopVarNamePtr)
10104 Jim_DecrRefCount(interp, stopVarNamePtr);
10105 Jim_DecrRefCount(interp, varNamePtr);
10106 return retval;
10107 }
10108 }
10109 /* If there was a change in procedures/command continue
10110 * with the usual [for] command implementation */
10111 if (procEpoch != interp->procEpoch) {
10112 if (stopVarNamePtr)
10113 Jim_DecrRefCount(interp, stopVarNamePtr);
10114 Jim_DecrRefCount(interp, varNamePtr);
10115 goto evalnext;
10116 }
10117 /* Increment */
10118 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
10119 if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
10120 objPtr->internalRep.wideValue ++;
10121 Jim_InvalidateStringRep(objPtr);
10122 } else {
10123 Jim_Obj *auxObjPtr;
10124
10125 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
10126 if (stopVarNamePtr)
10127 Jim_DecrRefCount(interp, stopVarNamePtr);
10128 Jim_DecrRefCount(interp, varNamePtr);
10129 goto evalnext;
10130 }
10131 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
10132 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
10133 if (stopVarNamePtr)
10134 Jim_DecrRefCount(interp, stopVarNamePtr);
10135 Jim_DecrRefCount(interp, varNamePtr);
10136 Jim_FreeNewObj(interp, auxObjPtr);
10137 goto evalnext;
10138 }
10139 }
10140 }
10141 if (stopVarNamePtr)
10142 Jim_DecrRefCount(interp, stopVarNamePtr);
10143 Jim_DecrRefCount(interp, varNamePtr);
10144 Jim_SetEmptyResult(interp);
10145 return JIM_OK;
10146 }
10147 #endif
10148 evalstart:
10149 /* Eval start */
10150 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10151 return retval;
10152 while (1) {
10153 int boolean;
10154 testcond:
10155 /* Test the condition */
10156 if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
10157 != JIM_OK)
10158 return retval;
10159 if (!boolean) break;
10160 /* Eval body */
10161 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10162 switch(retval) {
10163 case JIM_BREAK:
10164 goto out;
10165 break;
10166 case JIM_CONTINUE:
10167 /* Nothing to do */
10168 break;
10169 default:
10170 return retval;
10171 }
10172 }
10173 evalnext:
10174 /* Eval next */
10175 if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
10176 switch(retval) {
10177 case JIM_BREAK:
10178 goto out;
10179 break;
10180 case JIM_CONTINUE:
10181 continue;
10182 break;
10183 default:
10184 return retval;
10185 }
10186 }
10187 }
10188 out:
10189 Jim_SetEmptyResult(interp);
10190 return JIM_OK;
10191 }
10192
10193 /* foreach + lmap implementation. */
10194 static int JimForeachMapHelper(Jim_Interp *interp, int argc,
10195 Jim_Obj *const *argv, int doMap)
10196 {
10197 int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
10198 int nbrOfLoops = 0;
10199 Jim_Obj *emptyStr, *script, *mapRes = NULL;
10200
10201 if (argc < 4 || argc % 2 != 0) {
10202 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
10203 return JIM_ERR;
10204 }
10205 if (doMap) {
10206 mapRes = Jim_NewListObj(interp, NULL, 0);
10207 Jim_IncrRefCount(mapRes);
10208 }
10209 emptyStr = Jim_NewEmptyStringObj(interp);
10210 Jim_IncrRefCount(emptyStr);
10211 script = argv[argc-1]; /* Last argument is a script */
10212 nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
10213 listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
10214 listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
10215 /* Initialize iterators and remember max nbr elements each list */
10216 memset(listsIdx, 0, nbrOfLists * sizeof(int));
10217 /* Remember lengths of all lists and calculate how much rounds to loop */
10218 for (i=0; i < nbrOfLists*2; i += 2) {
10219 div_t cnt;
10220 int count;
10221 Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
10222 Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
10223 if (listsEnd[i] == 0) {
10224 Jim_SetResultString(interp, "foreach varlist is empty", -1);
10225 goto err;
10226 }
10227 cnt = div(listsEnd[i+1], listsEnd[i]);
10228 count = cnt.quot + (cnt.rem ? 1 : 0);
10229 if (count > nbrOfLoops)
10230 nbrOfLoops = count;
10231 }
10232 for (; nbrOfLoops-- > 0; ) {
10233 for (i=0; i < nbrOfLists; ++i) {
10234 int varIdx = 0, var = i * 2;
10235 while (varIdx < listsEnd[var]) {
10236 Jim_Obj *varName, *ele;
10237 int lst = i * 2 + 1;
10238 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
10239 != JIM_OK)
10240 goto err;
10241 if (listsIdx[i] < listsEnd[lst]) {
10242 if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
10243 != JIM_OK)
10244 goto err;
10245 if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
10246 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10247 goto err;
10248 }
10249 ++listsIdx[i]; /* Remember next iterator of current list */
10250 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
10251 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10252 goto err;
10253 }
10254 ++varIdx; /* Next variable */
10255 }
10256 }
10257 switch (result = Jim_EvalObj(interp, script)) {
10258 case JIM_OK:
10259 if (doMap)
10260 Jim_ListAppendElement(interp, mapRes, interp->result);
10261 break;
10262 case JIM_CONTINUE:
10263 break;
10264 case JIM_BREAK:
10265 goto out;
10266 break;
10267 default:
10268 goto err;
10269 }
10270 }
10271 out:
10272 result = JIM_OK;
10273 if (doMap)
10274 Jim_SetResult(interp, mapRes);
10275 else
10276 Jim_SetEmptyResult(interp);
10277 err:
10278 if (doMap)
10279 Jim_DecrRefCount(interp, mapRes);
10280 Jim_DecrRefCount(interp, emptyStr);
10281 Jim_Free(listsIdx);
10282 Jim_Free(listsEnd);
10283 return result;
10284 }
10285
10286 /* [foreach] */
10287 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
10288 Jim_Obj *const *argv)
10289 {
10290 return JimForeachMapHelper(interp, argc, argv, 0);
10291 }
10292
10293 /* [lmap] */
10294 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
10295 Jim_Obj *const *argv)
10296 {
10297 return JimForeachMapHelper(interp, argc, argv, 1);
10298 }
10299
10300 /* [if] */
10301 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
10302 Jim_Obj *const *argv)
10303 {
10304 int boolean, retval, current = 1, falsebody = 0;
10305 if (argc >= 3) {
10306 while (1) {
10307 /* Far not enough arguments given! */
10308 if (current >= argc) goto err;
10309 if ((retval = Jim_GetBoolFromExpr(interp,
10310 argv[current++], &boolean))
10311 != JIM_OK)
10312 return retval;
10313 /* There lacks something, isn't it? */
10314 if (current >= argc) goto err;
10315 if (Jim_CompareStringImmediate(interp, argv[current],
10316 "then")) current++;
10317 /* Tsk tsk, no then-clause? */
10318 if (current >= argc) goto err;
10319 if (boolean)
10320 return Jim_EvalObj(interp, argv[current]);
10321 /* Ok: no else-clause follows */
10322 if (++current >= argc) {
10323 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10324 return JIM_OK;
10325 }
10326 falsebody = current++;
10327 if (Jim_CompareStringImmediate(interp, argv[falsebody],
10328 "else")) {
10329 /* IIICKS - else-clause isn't last cmd? */
10330 if (current != argc-1) goto err;
10331 return Jim_EvalObj(interp, argv[current]);
10332 } else if (Jim_CompareStringImmediate(interp,
10333 argv[falsebody], "elseif"))
10334 /* Ok: elseif follows meaning all the stuff
10335 * again (how boring...) */
10336 continue;
10337 /* OOPS - else-clause is not last cmd?*/
10338 else if (falsebody != argc-1)
10339 goto err;
10340 return Jim_EvalObj(interp, argv[falsebody]);
10341 }
10342 return JIM_OK;
10343 }
10344 err:
10345 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10346 return JIM_ERR;
10347 }
10348
10349 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10350
10351 /* [switch] */
10352 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
10353 Jim_Obj *const *argv)
10354 {
10355 int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10356 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10357 Jim_Obj *script = 0;
10358 if (argc < 3) goto wrongnumargs;
10359 for (opt=1; opt < argc; ++opt) {
10360 const char *option = Jim_GetString(argv[opt], 0);
10361 if (*option != '-') break;
10362 else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10363 else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10364 else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10365 else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10366 else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10367 if ((argc - opt) < 2) goto wrongnumargs;
10368 command = argv[++opt];
10369 } else {
10370 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10371 Jim_AppendStrings(interp, Jim_GetResult(interp),
10372 "bad option \"", option, "\": must be -exact, -glob, "
10373 "-regexp, -command procname or --", 0);
10374 goto err;
10375 }
10376 if ((argc - opt) < 2) goto wrongnumargs;
10377 }
10378 strObj = argv[opt++];
10379 patCount = argc - opt;
10380 if (patCount == 1) {
10381 Jim_Obj **vector;
10382 JimListGetElements(interp, argv[opt], &patCount, &vector);
10383 caseList = vector;
10384 } else
10385 caseList = &argv[opt];
10386 if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10387 for (i=0; script == 0 && i < patCount; i += 2) {
10388 Jim_Obj *patObj = caseList[i];
10389 if (!Jim_CompareStringImmediate(interp, patObj, "default")
10390 || i < (patCount-2)) {
10391 switch (matchOpt) {
10392 case SWITCH_EXACT:
10393 if (Jim_StringEqObj(strObj, patObj, 0))
10394 script = caseList[i+1];
10395 break;
10396 case SWITCH_GLOB:
10397 if (Jim_StringMatchObj(patObj, strObj, 0))
10398 script = caseList[i+1];
10399 break;
10400 case SWITCH_RE:
10401 command = Jim_NewStringObj(interp, "regexp", -1);
10402 /* Fall thru intentionally */
10403 case SWITCH_CMD: {
10404 Jim_Obj *parms[] = {command, patObj, strObj};
10405 int rc = Jim_EvalObjVector(interp, 3, parms);
10406 long matching;
10407 /* After the execution of a command we need to
10408 * make sure to reconvert the object into a list
10409 * again. Only for the single-list style [switch]. */
10410 if (argc-opt == 1) {
10411 Jim_Obj **vector;
10412 JimListGetElements(interp, argv[opt], &patCount,
10413 &vector);
10414 caseList = vector;
10415 }
10416 /* command is here already decref'd */
10417 if (rc != JIM_OK) {
10418 retcode = rc;
10419 goto err;
10420 }
10421 rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10422 if (rc != JIM_OK) {
10423 retcode = rc;
10424 goto err;
10425 }
10426 if (matching)
10427 script = caseList[i+1];
10428 break;
10429 }
10430 default:
10431 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10432 Jim_AppendStrings(interp, Jim_GetResult(interp),
10433 "internal error: no such option implemented", 0);
10434 goto err;
10435 }
10436 } else {
10437 script = caseList[i+1];
10438 }
10439 }
10440 for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10441 i += 2)
10442 script = caseList[i+1];
10443 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10444 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10445 Jim_AppendStrings(interp, Jim_GetResult(interp),
10446 "no body specified for pattern \"",
10447 Jim_GetString(caseList[i-2], 0), "\"", 0);
10448 goto err;
10449 }
10450 retcode = JIM_OK;
10451 Jim_SetEmptyResult(interp);
10452 if (script != 0)
10453 retcode = Jim_EvalObj(interp, script);
10454 return retcode;
10455 wrongnumargs:
10456 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10457 "pattern body ... ?default body? or "
10458 "{pattern body ?pattern body ...?}");
10459 err:
10460 return retcode;
10461 }
10462
10463 /* [list] */
10464 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
10465 Jim_Obj *const *argv)
10466 {
10467 Jim_Obj *listObjPtr;
10468
10469 listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10470 Jim_SetResult(interp, listObjPtr);
10471 return JIM_OK;
10472 }
10473
10474 /* [lindex] */
10475 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
10476 Jim_Obj *const *argv)
10477 {
10478 Jim_Obj *objPtr, *listObjPtr;
10479 int i;
10480 int index;
10481
10482 if (argc < 3) {
10483 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10484 return JIM_ERR;
10485 }
10486 objPtr = argv[1];
10487 Jim_IncrRefCount(objPtr);
10488 for (i = 2; i < argc; i++) {
10489 listObjPtr = objPtr;
10490 if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10491 Jim_DecrRefCount(interp, listObjPtr);
10492 return JIM_ERR;
10493 }
10494 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10495 JIM_NONE) != JIM_OK) {
10496 /* Returns an empty object if the index
10497 * is out of range. */
10498 Jim_DecrRefCount(interp, listObjPtr);
10499 Jim_SetEmptyResult(interp);
10500 return JIM_OK;
10501 }
10502 Jim_IncrRefCount(objPtr);
10503 Jim_DecrRefCount(interp, listObjPtr);
10504 }
10505 Jim_SetResult(interp, objPtr);
10506 Jim_DecrRefCount(interp, objPtr);
10507 return JIM_OK;
10508 }
10509
10510 /* [llength] */
10511 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
10512 Jim_Obj *const *argv)
10513 {
10514 int len;
10515
10516 if (argc != 2) {
10517 Jim_WrongNumArgs(interp, 1, argv, "list");
10518 return JIM_ERR;
10519 }
10520 Jim_ListLength(interp, argv[1], &len);
10521 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10522 return JIM_OK;
10523 }
10524
10525 /* [lappend] */
10526 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
10527 Jim_Obj *const *argv)
10528 {
10529 Jim_Obj *listObjPtr;
10530 int shared, i;
10531
10532 if (argc < 2) {
10533 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10534 return JIM_ERR;
10535 }
10536 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10537 if (!listObjPtr) {
10538 /* Create the list if it does not exists */
10539 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10540 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10541 Jim_FreeNewObj(interp, listObjPtr);
10542 return JIM_ERR;
10543 }
10544 }
10545 shared = Jim_IsShared(listObjPtr);
10546 if (shared)
10547 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10548 for (i = 2; i < argc; i++)
10549 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10550 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10551 if (shared)
10552 Jim_FreeNewObj(interp, listObjPtr);
10553 return JIM_ERR;
10554 }
10555 Jim_SetResult(interp, listObjPtr);
10556 return JIM_OK;
10557 }
10558
10559 /* [linsert] */
10560 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10561 Jim_Obj *const *argv)
10562 {
10563 int index, len;
10564 Jim_Obj *listPtr;
10565
10566 if (argc < 4) {
10567 Jim_WrongNumArgs(interp, 1, argv, "list index element "
10568 "?element ...?");
10569 return JIM_ERR;
10570 }
10571 listPtr = argv[1];
10572 if (Jim_IsShared(listPtr))
10573 listPtr = Jim_DuplicateObj(interp, listPtr);
10574 if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10575 goto err;
10576 Jim_ListLength(interp, listPtr, &len);
10577 if (index >= len)
10578 index = len;
10579 else if (index < 0)
10580 index = len + index + 1;
10581 Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10582 Jim_SetResult(interp, listPtr);
10583 return JIM_OK;
10584 err:
10585 if (listPtr != argv[1]) {
10586 Jim_FreeNewObj(interp, listPtr);
10587 }
10588 return JIM_ERR;
10589 }
10590
10591 /* [lset] */
10592 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10593 Jim_Obj *const *argv)
10594 {
10595 if (argc < 3) {
10596 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10597 return JIM_ERR;
10598 } else if (argc == 3) {
10599 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10600 return JIM_ERR;
10601 Jim_SetResult(interp, argv[2]);
10602 return JIM_OK;
10603 }
10604 if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10605 == JIM_ERR) return JIM_ERR;
10606 return JIM_OK;
10607 }
10608
10609 /* [lsort] */
10610 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10611 {
10612 const char *options[] = {
10613 "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10614 };
10615 enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10616 Jim_Obj *resObj;
10617 int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10618 int decreasing = 0;
10619
10620 if (argc < 2) {
10621 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10622 return JIM_ERR;
10623 }
10624 for (i = 1; i < (argc-1); i++) {
10625 int option;
10626
10627 if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10628 != JIM_OK)
10629 return JIM_ERR;
10630 switch(option) {
10631 case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10632 case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10633 case OPT_INCREASING: decreasing = 0; break;
10634 case OPT_DECREASING: decreasing = 1; break;
10635 }
10636 }
10637 if (decreasing) {
10638 switch(lsortType) {
10639 case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10640 case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10641 }
10642 }
10643 resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10644 ListSortElements(interp, resObj, lsortType);
10645 Jim_SetResult(interp, resObj);
10646 return JIM_OK;
10647 }
10648
10649 /* [append] */
10650 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10651 Jim_Obj *const *argv)
10652 {
10653 Jim_Obj *stringObjPtr;
10654 int shared, i;
10655
10656 if (argc < 2) {
10657 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10658 return JIM_ERR;
10659 }
10660 if (argc == 2) {
10661 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10662 if (!stringObjPtr) return JIM_ERR;
10663 } else {
10664 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10665 if (!stringObjPtr) {
10666 /* Create the string if it does not exists */
10667 stringObjPtr = Jim_NewEmptyStringObj(interp);
10668 if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10669 != JIM_OK) {
10670 Jim_FreeNewObj(interp, stringObjPtr);
10671 return JIM_ERR;
10672 }
10673 }
10674 }
10675 shared = Jim_IsShared(stringObjPtr);
10676 if (shared)
10677 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10678 for (i = 2; i < argc; i++)
10679 Jim_AppendObj(interp, stringObjPtr, argv[i]);
10680 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10681 if (shared)
10682 Jim_FreeNewObj(interp, stringObjPtr);
10683 return JIM_ERR;
10684 }
10685 Jim_SetResult(interp, stringObjPtr);
10686 return JIM_OK;
10687 }
10688
10689 /* [debug] */
10690 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10691 Jim_Obj *const *argv)
10692 {
10693 const char *options[] = {
10694 "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10695 "exprbc",
10696 NULL
10697 };
10698 enum {
10699 OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10700 OPT_EXPRLEN, OPT_EXPRBC
10701 };
10702 int option;
10703
10704 if (argc < 2) {
10705 Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10706 return JIM_ERR;
10707 }
10708 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10709 JIM_ERRMSG) != JIM_OK)
10710 return JIM_ERR;
10711 if (option == OPT_REFCOUNT) {
10712 if (argc != 3) {
10713 Jim_WrongNumArgs(interp, 2, argv, "object");
10714 return JIM_ERR;
10715 }
10716 Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10717 return JIM_OK;
10718 } else if (option == OPT_OBJCOUNT) {
10719 int freeobj = 0, liveobj = 0;
10720 char buf[256];
10721 Jim_Obj *objPtr;
10722
10723 if (argc != 2) {
10724 Jim_WrongNumArgs(interp, 2, argv, "");
10725 return JIM_ERR;
10726 }
10727 /* Count the number of free objects. */
10728 objPtr = interp->freeList;
10729 while (objPtr) {
10730 freeobj++;
10731 objPtr = objPtr->nextObjPtr;
10732 }
10733 /* Count the number of live objects. */
10734 objPtr = interp->liveList;
10735 while (objPtr) {
10736 liveobj++;
10737 objPtr = objPtr->nextObjPtr;
10738 }
10739 /* Set the result string and return. */
10740 sprintf(buf, "free %d used %d", freeobj, liveobj);
10741 Jim_SetResultString(interp, buf, -1);
10742 return JIM_OK;
10743 } else if (option == OPT_OBJECTS) {
10744 Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10745 /* Count the number of live objects. */
10746 objPtr = interp->liveList;
10747 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10748 while (objPtr) {
10749 char buf[128];
10750 const char *type = objPtr->typePtr ?
10751 objPtr->typePtr->name : "";
10752 subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10753 sprintf(buf, "%p", objPtr);
10754 Jim_ListAppendElement(interp, subListObjPtr,
10755 Jim_NewStringObj(interp, buf, -1));
10756 Jim_ListAppendElement(interp, subListObjPtr,
10757 Jim_NewStringObj(interp, type, -1));
10758 Jim_ListAppendElement(interp, subListObjPtr,
10759 Jim_NewIntObj(interp, objPtr->refCount));
10760 Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10761 Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10762 objPtr = objPtr->nextObjPtr;
10763 }
10764 Jim_SetResult(interp, listObjPtr);
10765 return JIM_OK;
10766 } else if (option == OPT_INVSTR) {
10767 Jim_Obj *objPtr;
10768
10769 if (argc != 3) {
10770 Jim_WrongNumArgs(interp, 2, argv, "object");
10771 return JIM_ERR;
10772 }
10773 objPtr = argv[2];
10774 if (objPtr->typePtr != NULL)
10775 Jim_InvalidateStringRep(objPtr);
10776 Jim_SetEmptyResult(interp);
10777 return JIM_OK;
10778 } else if (option == OPT_SCRIPTLEN) {
10779 ScriptObj *script;
10780 if (argc != 3) {
10781 Jim_WrongNumArgs(interp, 2, argv, "script");
10782 return JIM_ERR;
10783 }
10784 script = Jim_GetScript(interp, argv[2]);
10785 Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10786 return JIM_OK;
10787 } else if (option == OPT_EXPRLEN) {
10788 ExprByteCode *expr;
10789 if (argc != 3) {
10790 Jim_WrongNumArgs(interp, 2, argv, "expression");
10791 return JIM_ERR;
10792 }
10793 expr = Jim_GetExpression(interp, argv[2]);
10794 if (expr == NULL)
10795 return JIM_ERR;
10796 Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10797 return JIM_OK;
10798 } else if (option == OPT_EXPRBC) {
10799 Jim_Obj *objPtr;
10800 ExprByteCode *expr;
10801 int i;
10802
10803 if (argc != 3) {
10804 Jim_WrongNumArgs(interp, 2, argv, "expression");
10805 return JIM_ERR;
10806 }
10807 expr = Jim_GetExpression(interp, argv[2]);
10808 if (expr == NULL)
10809 return JIM_ERR;
10810 objPtr = Jim_NewListObj(interp, NULL, 0);
10811 for (i = 0; i < expr->len; i++) {
10812 const char *type;
10813 Jim_ExprOperator *op;
10814
10815 switch(expr->opcode[i]) {
10816 case JIM_EXPROP_NUMBER: type = "number"; break;
10817 case JIM_EXPROP_COMMAND: type = "command"; break;
10818 case JIM_EXPROP_VARIABLE: type = "variable"; break;
10819 case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10820 case JIM_EXPROP_SUBST: type = "subst"; break;
10821 case JIM_EXPROP_STRING: type = "string"; break;
10822 default:
10823 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10824 if (op == NULL) {
10825 type = "private";
10826 } else {
10827 type = "operator";
10828 }
10829 break;
10830 }
10831 Jim_ListAppendElement(interp, objPtr,
10832 Jim_NewStringObj(interp, type, -1));
10833 Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10834 }
10835 Jim_SetResult(interp, objPtr);
10836 return JIM_OK;
10837 } else {
10838 Jim_SetResultString(interp,
10839 "bad option. Valid options are refcount, "
10840 "objcount, objects, invstr", -1);
10841 return JIM_ERR;
10842 }
10843 return JIM_OK; /* unreached */
10844 }
10845
10846 /* [eval] */
10847 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10848 Jim_Obj *const *argv)
10849 {
10850 if (argc == 2) {
10851 return Jim_EvalObj(interp, argv[1]);
10852 } else if (argc > 2) {
10853 Jim_Obj *objPtr;
10854 int retcode;
10855
10856 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10857 Jim_IncrRefCount(objPtr);
10858 retcode = Jim_EvalObj(interp, objPtr);
10859 Jim_DecrRefCount(interp, objPtr);
10860 return retcode;
10861 } else {
10862 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10863 return JIM_ERR;
10864 }
10865 }
10866
10867 /* [uplevel] */
10868 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10869 Jim_Obj *const *argv)
10870 {
10871 if (argc >= 2) {
10872 int retcode, newLevel, oldLevel;
10873 Jim_CallFrame *savedCallFrame, *targetCallFrame;
10874 Jim_Obj *objPtr;
10875 const char *str;
10876
10877 /* Save the old callframe pointer */
10878 savedCallFrame = interp->framePtr;
10879
10880 /* Lookup the target frame pointer */
10881 str = Jim_GetString(argv[1], NULL);
10882 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10883 {
10884 if (Jim_GetCallFrameByLevel(interp, argv[1],
10885 &targetCallFrame,
10886 &newLevel) != JIM_OK)
10887 return JIM_ERR;
10888 argc--;
10889 argv++;
10890 } else {
10891 if (Jim_GetCallFrameByLevel(interp, NULL,
10892 &targetCallFrame,
10893 &newLevel) != JIM_OK)
10894 return JIM_ERR;
10895 }
10896 if (argc < 2) {
10897 argc++;
10898 argv--;
10899 Jim_WrongNumArgs(interp, 1, argv,
10900 "?level? command ?arg ...?");
10901 return JIM_ERR;
10902 }
10903 /* Eval the code in the target callframe. */
10904 interp->framePtr = targetCallFrame;
10905 oldLevel = interp->numLevels;
10906 interp->numLevels = newLevel;
10907 if (argc == 2) {
10908 retcode = Jim_EvalObj(interp, argv[1]);
10909 } else {
10910 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10911 Jim_IncrRefCount(objPtr);
10912 retcode = Jim_EvalObj(interp, objPtr);
10913 Jim_DecrRefCount(interp, objPtr);
10914 }
10915 interp->numLevels = oldLevel;
10916 interp->framePtr = savedCallFrame;
10917 return retcode;
10918 } else {
10919 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10920 return JIM_ERR;
10921 }
10922 }
10923
10924 /* [expr] */
10925 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10926 Jim_Obj *const *argv)
10927 {
10928 Jim_Obj *exprResultPtr;
10929 int retcode;
10930
10931 if (argc == 2) {
10932 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10933 } else if (argc > 2) {
10934 Jim_Obj *objPtr;
10935
10936 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10937 Jim_IncrRefCount(objPtr);
10938 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10939 Jim_DecrRefCount(interp, objPtr);
10940 } else {
10941 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10942 return JIM_ERR;
10943 }
10944 if (retcode != JIM_OK) return retcode;
10945 Jim_SetResult(interp, exprResultPtr);
10946 Jim_DecrRefCount(interp, exprResultPtr);
10947 return JIM_OK;
10948 }
10949
10950 /* [break] */
10951 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10952 Jim_Obj *const *argv)
10953 {
10954 if (argc != 1) {
10955 Jim_WrongNumArgs(interp, 1, argv, "");
10956 return JIM_ERR;
10957 }
10958 return JIM_BREAK;
10959 }
10960
10961 /* [continue] */
10962 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10963 Jim_Obj *const *argv)
10964 {
10965 if (argc != 1) {
10966 Jim_WrongNumArgs(interp, 1, argv, "");
10967 return JIM_ERR;
10968 }
10969 return JIM_CONTINUE;
10970 }
10971
10972 /* [return] */
10973 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10974 Jim_Obj *const *argv)
10975 {
10976 if (argc == 1) {
10977 return JIM_RETURN;
10978 } else if (argc == 2) {
10979 Jim_SetResult(interp, argv[1]);
10980 interp->returnCode = JIM_OK;
10981 return JIM_RETURN;
10982 } else if (argc == 3 || argc == 4) {
10983 int returnCode;
10984 if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10985 return JIM_ERR;
10986 interp->returnCode = returnCode;
10987 if (argc == 4)
10988 Jim_SetResult(interp, argv[3]);
10989 return JIM_RETURN;
10990 } else {
10991 Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10992 return JIM_ERR;
10993 }
10994 return JIM_RETURN; /* unreached */
10995 }
10996
10997 /* [tailcall] */
10998 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10999 Jim_Obj *const *argv)
11000 {
11001 Jim_Obj *objPtr;
11002
11003 objPtr = Jim_NewListObj(interp, argv+1, argc-1);
11004 Jim_SetResult(interp, objPtr);
11005 return JIM_EVAL;
11006 }
11007
11008 /* [proc] */
11009 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
11010 Jim_Obj *const *argv)
11011 {
11012 int argListLen;
11013 int arityMin, arityMax;
11014
11015 if (argc != 4 && argc != 5) {
11016 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
11017 return JIM_ERR;
11018 }
11019 Jim_ListLength(interp, argv[2], &argListLen);
11020 arityMin = arityMax = argListLen+1;
11021
11022 if (argListLen) {
11023 const char *str;
11024 int len;
11025 Jim_Obj *argPtr;
11026
11027 /* Check for 'args' and adjust arityMin and arityMax if necessary */
11028 Jim_ListIndex(interp, argv[2], argListLen-1, &argPtr, JIM_NONE);
11029 str = Jim_GetString(argPtr, &len);
11030 if (len == 4 && memcmp(str, "args", 4) == 0) {
11031 arityMin--;
11032 arityMax = -1;
11033 }
11034
11035 /* Check for default arguments and reduce arityMin if necessary */
11036 while (arityMin > 1) {
11037 int len;
11038 Jim_ListIndex(interp, argv[2], arityMin - 2, &argPtr, JIM_NONE);
11039 Jim_ListLength(interp, argPtr, &len);
11040 if (len != 2) {
11041 /* No default argument */
11042 break;
11043 }
11044 arityMin--;
11045 }
11046 }
11047 if (argc == 4) {
11048 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11049 argv[2], NULL, argv[3], arityMin, arityMax);
11050 } else {
11051 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11052 argv[2], argv[3], argv[4], arityMin, arityMax);
11053 }
11054 }
11055
11056 /* [concat] */
11057 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
11058 Jim_Obj *const *argv)
11059 {
11060 Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
11061 return JIM_OK;
11062 }
11063
11064 /* [upvar] */
11065 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
11066 Jim_Obj *const *argv)
11067 {
11068 const char *str;
11069 int i;
11070 Jim_CallFrame *targetCallFrame;
11071
11072 /* Lookup the target frame pointer */
11073 str = Jim_GetString(argv[1], NULL);
11074 if (argc > 3 &&
11075 ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
11076 {
11077 if (Jim_GetCallFrameByLevel(interp, argv[1],
11078 &targetCallFrame, NULL) != JIM_OK)
11079 return JIM_ERR;
11080 argc--;
11081 argv++;
11082 } else {
11083 if (Jim_GetCallFrameByLevel(interp, NULL,
11084 &targetCallFrame, NULL) != JIM_OK)
11085 return JIM_ERR;
11086 }
11087 /* Check for arity */
11088 if (argc < 3 || ((argc-1)%2) != 0) {
11089 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
11090 return JIM_ERR;
11091 }
11092 /* Now... for every other/local couple: */
11093 for (i = 1; i < argc; i += 2) {
11094 if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
11095 targetCallFrame) != JIM_OK) return JIM_ERR;
11096 }
11097 return JIM_OK;
11098 }
11099
11100 /* [global] */
11101 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
11102 Jim_Obj *const *argv)
11103 {
11104 int i;
11105
11106 if (argc < 2) {
11107 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
11108 return JIM_ERR;
11109 }
11110 /* Link every var to the toplevel having the same name */
11111 if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
11112 for (i = 1; i < argc; i++) {
11113 if (Jim_SetVariableLink(interp, argv[i], argv[i],
11114 interp->topFramePtr) != JIM_OK) return JIM_ERR;
11115 }
11116 return JIM_OK;
11117 }
11118
11119 /* does the [string map] operation. On error NULL is returned,
11120 * otherwise a new string object with the result, having refcount = 0,
11121 * is returned. */
11122 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
11123 Jim_Obj *objPtr, int nocase)
11124 {
11125 int numMaps;
11126 const char **key, *str, *noMatchStart = NULL;
11127 Jim_Obj **value;
11128 int *keyLen, strLen, i;
11129 Jim_Obj *resultObjPtr;
11130
11131 Jim_ListLength(interp, mapListObjPtr, &numMaps);
11132 if (numMaps % 2) {
11133 Jim_SetResultString(interp,
11134 "list must contain an even number of elements", -1);
11135 return NULL;
11136 }
11137 /* Initialization */
11138 numMaps /= 2;
11139 key = Jim_Alloc(sizeof(char*)*numMaps);
11140 keyLen = Jim_Alloc(sizeof(int)*numMaps);
11141 value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
11142 resultObjPtr = Jim_NewStringObj(interp, "", 0);
11143 for (i = 0; i < numMaps; i++) {
11144 Jim_Obj *eleObjPtr;
11145
11146 Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
11147 key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
11148 Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
11149 value[i] = eleObjPtr;
11150 }
11151 str = Jim_GetString(objPtr, &strLen);
11152 /* Map it */
11153 while(strLen) {
11154 for (i = 0; i < numMaps; i++) {
11155 if (strLen >= keyLen[i] && keyLen[i]) {
11156 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
11157 nocase))
11158 {
11159 if (noMatchStart) {
11160 Jim_AppendString(interp, resultObjPtr,
11161 noMatchStart, str-noMatchStart);
11162 noMatchStart = NULL;
11163 }
11164 Jim_AppendObj(interp, resultObjPtr, value[i]);
11165 str += keyLen[i];
11166 strLen -= keyLen[i];
11167 break;
11168 }
11169 }
11170 }
11171 if (i == numMaps) { /* no match */
11172 if (noMatchStart == NULL)
11173 noMatchStart = str;
11174 str ++;
11175 strLen --;
11176 }
11177 }
11178 if (noMatchStart) {
11179 Jim_AppendString(interp, resultObjPtr,
11180 noMatchStart, str-noMatchStart);
11181 }
11182 Jim_Free((void*)key);
11183 Jim_Free(keyLen);
11184 Jim_Free(value);
11185 return resultObjPtr;
11186 }
11187
11188 /* [string] */
11189 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
11190 Jim_Obj *const *argv)
11191 {
11192 int option;
11193 const char *options[] = {
11194 "length", "compare", "match", "equal", "range", "map", "repeat",
11195 "index", "first", "tolower", "toupper", NULL
11196 };
11197 enum {
11198 OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
11199 OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
11200 };
11201
11202 if (argc < 2) {
11203 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11204 return JIM_ERR;
11205 }
11206 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11207 JIM_ERRMSG) != JIM_OK)
11208 return JIM_ERR;
11209
11210 if (option == OPT_LENGTH) {
11211 int len;
11212
11213 if (argc != 3) {
11214 Jim_WrongNumArgs(interp, 2, argv, "string");
11215 return JIM_ERR;
11216 }
11217 Jim_GetString(argv[2], &len);
11218 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
11219 return JIM_OK;
11220 } else if (option == OPT_COMPARE) {
11221 int nocase = 0;
11222 if ((argc != 4 && argc != 5) ||
11223 (argc == 5 && Jim_CompareStringImmediate(interp,
11224 argv[2], "-nocase") == 0)) {
11225 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11226 return JIM_ERR;
11227 }
11228 if (argc == 5) {
11229 nocase = 1;
11230 argv++;
11231 }
11232 Jim_SetResult(interp, Jim_NewIntObj(interp,
11233 Jim_StringCompareObj(argv[2],
11234 argv[3], nocase)));
11235 return JIM_OK;
11236 } else if (option == OPT_MATCH) {
11237 int nocase = 0;
11238 if ((argc != 4 && argc != 5) ||
11239 (argc == 5 && Jim_CompareStringImmediate(interp,
11240 argv[2], "-nocase") == 0)) {
11241 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
11242 "string");
11243 return JIM_ERR;
11244 }
11245 if (argc == 5) {
11246 nocase = 1;
11247 argv++;
11248 }
11249 Jim_SetResult(interp,
11250 Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
11251 argv[3], nocase)));
11252 return JIM_OK;
11253 } else if (option == OPT_EQUAL) {
11254 if (argc != 4) {
11255 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11256 return JIM_ERR;
11257 }
11258 Jim_SetResult(interp,
11259 Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
11260 argv[3], 0)));
11261 return JIM_OK;
11262 } else if (option == OPT_RANGE) {
11263 Jim_Obj *objPtr;
11264
11265 if (argc != 5) {
11266 Jim_WrongNumArgs(interp, 2, argv, "string first last");
11267 return JIM_ERR;
11268 }
11269 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
11270 if (objPtr == NULL)
11271 return JIM_ERR;
11272 Jim_SetResult(interp, objPtr);
11273 return JIM_OK;
11274 } else if (option == OPT_MAP) {
11275 int nocase = 0;
11276 Jim_Obj *objPtr;
11277
11278 if ((argc != 4 && argc != 5) ||
11279 (argc == 5 && Jim_CompareStringImmediate(interp,
11280 argv[2], "-nocase") == 0)) {
11281 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
11282 "string");
11283 return JIM_ERR;
11284 }
11285 if (argc == 5) {
11286 nocase = 1;
11287 argv++;
11288 }
11289 objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
11290 if (objPtr == NULL)
11291 return JIM_ERR;
11292 Jim_SetResult(interp, objPtr);
11293 return JIM_OK;
11294 } else if (option == OPT_REPEAT) {
11295 Jim_Obj *objPtr;
11296 jim_wide count;
11297
11298 if (argc != 4) {
11299 Jim_WrongNumArgs(interp, 2, argv, "string count");
11300 return JIM_ERR;
11301 }
11302 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
11303 return JIM_ERR;
11304 objPtr = Jim_NewStringObj(interp, "", 0);
11305 while (count--) {
11306 Jim_AppendObj(interp, objPtr, argv[2]);
11307 }
11308 Jim_SetResult(interp, objPtr);
11309 return JIM_OK;
11310 } else if (option == OPT_INDEX) {
11311 int index, len;
11312 const char *str;
11313
11314 if (argc != 4) {
11315 Jim_WrongNumArgs(interp, 2, argv, "string index");
11316 return JIM_ERR;
11317 }
11318 if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
11319 return JIM_ERR;
11320 str = Jim_GetString(argv[2], &len);
11321 if (index != INT_MIN && index != INT_MAX)
11322 index = JimRelToAbsIndex(len, index);
11323 if (index < 0 || index >= len) {
11324 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11325 return JIM_OK;
11326 } else {
11327 Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
11328 return JIM_OK;
11329 }
11330 } else if (option == OPT_FIRST) {
11331 int index = 0, l1, l2;
11332 const char *s1, *s2;
11333
11334 if (argc != 4 && argc != 5) {
11335 Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
11336 return JIM_ERR;
11337 }
11338 s1 = Jim_GetString(argv[2], &l1);
11339 s2 = Jim_GetString(argv[3], &l2);
11340 if (argc == 5) {
11341 if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
11342 return JIM_ERR;
11343 index = JimRelToAbsIndex(l2, index);
11344 }
11345 Jim_SetResult(interp, Jim_NewIntObj(interp,
11346 JimStringFirst(s1, l1, s2, l2, index)));
11347 return JIM_OK;
11348 } else if (option == OPT_TOLOWER) {
11349 if (argc != 3) {
11350 Jim_WrongNumArgs(interp, 2, argv, "string");
11351 return JIM_ERR;
11352 }
11353 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11354 } else if (option == OPT_TOUPPER) {
11355 if (argc != 3) {
11356 Jim_WrongNumArgs(interp, 2, argv, "string");
11357 return JIM_ERR;
11358 }
11359 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11360 }
11361 return JIM_OK;
11362 }
11363
11364 /* [time] */
11365 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
11366 Jim_Obj *const *argv)
11367 {
11368 long i, count = 1;
11369 jim_wide start, elapsed;
11370 char buf [256];
11371 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11372
11373 if (argc < 2) {
11374 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11375 return JIM_ERR;
11376 }
11377 if (argc == 3) {
11378 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11379 return JIM_ERR;
11380 }
11381 if (count < 0)
11382 return JIM_OK;
11383 i = count;
11384 start = JimClock();
11385 while (i-- > 0) {
11386 int retval;
11387
11388 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11389 return retval;
11390 }
11391 elapsed = JimClock() - start;
11392 sprintf(buf, fmt, elapsed/count);
11393 Jim_SetResultString(interp, buf, -1);
11394 return JIM_OK;
11395 }
11396
11397 /* [exit] */
11398 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
11399 Jim_Obj *const *argv)
11400 {
11401 long exitCode = 0;
11402
11403 if (argc > 2) {
11404 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11405 return JIM_ERR;
11406 }
11407 if (argc == 2) {
11408 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11409 return JIM_ERR;
11410 }
11411 interp->exitCode = exitCode;
11412 return JIM_EXIT;
11413 }
11414
11415 /* [catch] */
11416 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
11417 Jim_Obj *const *argv)
11418 {
11419 int exitCode = 0;
11420
11421 if (argc != 2 && argc != 3) {
11422 Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11423 return JIM_ERR;
11424 }
11425 exitCode = Jim_EvalObj(interp, argv[1]);
11426 if (argc == 3) {
11427 if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11428 != JIM_OK)
11429 return JIM_ERR;
11430 }
11431 Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11432 return JIM_OK;
11433 }
11434
11435 /* [ref] */
11436 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
11437 Jim_Obj *const *argv)
11438 {
11439 if (argc != 3 && argc != 4) {
11440 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11441 return JIM_ERR;
11442 }
11443 if (argc == 3) {
11444 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11445 } else {
11446 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11447 argv[3]));
11448 }
11449 return JIM_OK;
11450 }
11451
11452 /* [getref] */
11453 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
11454 Jim_Obj *const *argv)
11455 {
11456 Jim_Reference *refPtr;
11457
11458 if (argc != 2) {
11459 Jim_WrongNumArgs(interp, 1, argv, "reference");
11460 return JIM_ERR;
11461 }
11462 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11463 return JIM_ERR;
11464 Jim_SetResult(interp, refPtr->objPtr);
11465 return JIM_OK;
11466 }
11467
11468 /* [setref] */
11469 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
11470 Jim_Obj *const *argv)
11471 {
11472 Jim_Reference *refPtr;
11473
11474 if (argc != 3) {
11475 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11476 return JIM_ERR;
11477 }
11478 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11479 return JIM_ERR;
11480 Jim_IncrRefCount(argv[2]);
11481 Jim_DecrRefCount(interp, refPtr->objPtr);
11482 refPtr->objPtr = argv[2];
11483 Jim_SetResult(interp, argv[2]);
11484 return JIM_OK;
11485 }
11486
11487 /* [collect] */
11488 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
11489 Jim_Obj *const *argv)
11490 {
11491 if (argc != 1) {
11492 Jim_WrongNumArgs(interp, 1, argv, "");
11493 return JIM_ERR;
11494 }
11495 Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11496 return JIM_OK;
11497 }
11498
11499 /* [finalize] reference ?newValue? */
11500 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
11501 Jim_Obj *const *argv)
11502 {
11503 if (argc != 2 && argc != 3) {
11504 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11505 return JIM_ERR;
11506 }
11507 if (argc == 2) {
11508 Jim_Obj *cmdNamePtr;
11509
11510 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11511 return JIM_ERR;
11512 if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11513 Jim_SetResult(interp, cmdNamePtr);
11514 } else {
11515 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11516 return JIM_ERR;
11517 Jim_SetResult(interp, argv[2]);
11518 }
11519 return JIM_OK;
11520 }
11521
11522 /* TODO */
11523 /* [info references] (list of all the references/finalizers) */
11524
11525 /* [rename] */
11526 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
11527 Jim_Obj *const *argv)
11528 {
11529 const char *oldName, *newName;
11530
11531 if (argc != 3) {
11532 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11533 return JIM_ERR;
11534 }
11535 oldName = Jim_GetString(argv[1], NULL);
11536 newName = Jim_GetString(argv[2], NULL);
11537 if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11538 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11539 Jim_AppendStrings(interp, Jim_GetResult(interp),
11540 "can't rename \"", oldName, "\": ",
11541 "command doesn't exist", NULL);
11542 return JIM_ERR;
11543 }
11544 return JIM_OK;
11545 }
11546
11547 /* [dict] */
11548 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
11549 Jim_Obj *const *argv)
11550 {
11551 int option;
11552 const char *options[] = {
11553 "create", "get", "set", "unset", "exists", NULL
11554 };
11555 enum {
11556 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11557 };
11558
11559 if (argc < 2) {
11560 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11561 return JIM_ERR;
11562 }
11563
11564 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11565 JIM_ERRMSG) != JIM_OK)
11566 return JIM_ERR;
11567
11568 if (option == OPT_CREATE) {
11569 Jim_Obj *objPtr;
11570
11571 if (argc % 2) {
11572 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11573 return JIM_ERR;
11574 }
11575 objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11576 Jim_SetResult(interp, objPtr);
11577 return JIM_OK;
11578 } else if (option == OPT_GET) {
11579 Jim_Obj *objPtr;
11580
11581 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11582 JIM_ERRMSG) != JIM_OK)
11583 return JIM_ERR;
11584 Jim_SetResult(interp, objPtr);
11585 return JIM_OK;
11586 } else if (option == OPT_SET) {
11587 if (argc < 5) {
11588 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11589 return JIM_ERR;
11590 }
11591 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11592 argv[argc-1]);
11593 } else if (option == OPT_UNSET) {
11594 if (argc < 4) {
11595 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11596 return JIM_ERR;
11597 }
11598 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11599 NULL);
11600 } else if (option == OPT_EXIST) {
11601 Jim_Obj *objPtr;
11602 int exists;
11603
11604 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11605 JIM_ERRMSG) == JIM_OK)
11606 exists = 1;
11607 else
11608 exists = 0;
11609 Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11610 return JIM_OK;
11611 } else {
11612 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11613 Jim_AppendStrings(interp, Jim_GetResult(interp),
11614 "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11615 " must be create, get, set", NULL);
11616 return JIM_ERR;
11617 }
11618 return JIM_OK;
11619 }
11620
11621 /* [load] */
11622 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11623 Jim_Obj *const *argv)
11624 {
11625 if (argc < 2) {
11626 Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11627 return JIM_ERR;
11628 }
11629 return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11630 }
11631
11632 /* [subst] */
11633 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11634 Jim_Obj *const *argv)
11635 {
11636 int i, flags = 0;
11637 Jim_Obj *objPtr;
11638
11639 if (argc < 2) {
11640 Jim_WrongNumArgs(interp, 1, argv,
11641 "?-nobackslashes? ?-nocommands? ?-novariables? string");
11642 return JIM_ERR;
11643 }
11644 i = argc-2;
11645 while(i--) {
11646 if (Jim_CompareStringImmediate(interp, argv[i+1],
11647 "-nobackslashes"))
11648 flags |= JIM_SUBST_NOESC;
11649 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11650 "-novariables"))
11651 flags |= JIM_SUBST_NOVAR;
11652 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11653 "-nocommands"))
11654 flags |= JIM_SUBST_NOCMD;
11655 else {
11656 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11657 Jim_AppendStrings(interp, Jim_GetResult(interp),
11658 "bad option \"", Jim_GetString(argv[i+1], NULL),
11659 "\": must be -nobackslashes, -nocommands, or "
11660 "-novariables", NULL);
11661 return JIM_ERR;
11662 }
11663 }
11664 if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11665 return JIM_ERR;
11666 Jim_SetResult(interp, objPtr);
11667 return JIM_OK;
11668 }
11669
11670 /* [info] */
11671 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11672 Jim_Obj *const *argv)
11673 {
11674 int cmd, result = JIM_OK;
11675 static const char *commands[] = {
11676 "body", "commands", "exists", "globals", "level", "locals",
11677 "vars", "version", "complete", "args", "hostname", NULL
11678 };
11679 enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11680 INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS, INFO_HOSTNAME};
11681
11682 if (argc < 2) {
11683 Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11684 return JIM_ERR;
11685 }
11686 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11687 != JIM_OK) {
11688 return JIM_ERR;
11689 }
11690
11691 if (cmd == INFO_COMMANDS) {
11692 if (argc != 2 && argc != 3) {
11693 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11694 return JIM_ERR;
11695 }
11696 if (argc == 3)
11697 Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11698 else
11699 Jim_SetResult(interp, JimCommandsList(interp, NULL));
11700 } else if (cmd == INFO_EXISTS) {
11701 Jim_Obj *exists;
11702 if (argc != 3) {
11703 Jim_WrongNumArgs(interp, 2, argv, "varName");
11704 return JIM_ERR;
11705 }
11706 exists = Jim_GetVariable(interp, argv[2], 0);
11707 Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11708 } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11709 int mode;
11710 switch (cmd) {
11711 case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11712 case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
11713 case INFO_VARS: mode = JIM_VARLIST_VARS; break;
11714 default: mode = 0; /* avoid warning */; break;
11715 }
11716 if (argc != 2 && argc != 3) {
11717 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11718 return JIM_ERR;
11719 }
11720 if (argc == 3)
11721 Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11722 else
11723 Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11724 } else if (cmd == INFO_LEVEL) {
11725 Jim_Obj *objPtr;
11726 switch (argc) {
11727 case 2:
11728 Jim_SetResult(interp,
11729 Jim_NewIntObj(interp, interp->numLevels));
11730 break;
11731 case 3:
11732 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11733 return JIM_ERR;
11734 Jim_SetResult(interp, objPtr);
11735 break;
11736 default:
11737 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11738 return JIM_ERR;
11739 }
11740 } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11741 Jim_Cmd *cmdPtr;
11742
11743 if (argc != 3) {
11744 Jim_WrongNumArgs(interp, 2, argv, "procname");
11745 return JIM_ERR;
11746 }
11747 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11748 return JIM_ERR;
11749 if (cmdPtr->cmdProc != NULL) {
11750 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11751 Jim_AppendStrings(interp, Jim_GetResult(interp),
11752 "command \"", Jim_GetString(argv[2], NULL),
11753 "\" is not a procedure", NULL);
11754 return JIM_ERR;
11755 }
11756 if (cmd == INFO_BODY)
11757 Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11758 else
11759 Jim_SetResult(interp, cmdPtr->argListObjPtr);
11760 } else if (cmd == INFO_VERSION) {
11761 char buf[(JIM_INTEGER_SPACE * 2) + 1];
11762 sprintf(buf, "%d.%d",
11763 JIM_VERSION / 100, JIM_VERSION % 100);
11764 Jim_SetResultString(interp, buf, -1);
11765 } else if (cmd == INFO_COMPLETE) {
11766 const char *s;
11767 int len;
11768
11769 if (argc != 3) {
11770 Jim_WrongNumArgs(interp, 2, argv, "script");
11771 return JIM_ERR;
11772 }
11773 s = Jim_GetString(argv[2], &len);
11774 Jim_SetResult(interp,
11775 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11776 } else if (cmd == INFO_HOSTNAME) {
11777 /* Redirect to os.hostname if it exists */
11778 Jim_Obj *command = Jim_NewStringObj(interp, "os.gethostname", -1);
11779 result = Jim_EvalObjVector(interp, 1, &command);
11780 }
11781 return result;
11782 }
11783
11784 /* [split] */
11785 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11786 Jim_Obj *const *argv)
11787 {
11788 const char *str, *splitChars, *noMatchStart;
11789 int splitLen, strLen, i;
11790 Jim_Obj *resObjPtr;
11791
11792 if (argc != 2 && argc != 3) {
11793 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11794 return JIM_ERR;
11795 }
11796 /* Init */
11797 if (argc == 2) {
11798 splitChars = " \n\t\r";
11799 splitLen = 4;
11800 } else {
11801 splitChars = Jim_GetString(argv[2], &splitLen);
11802 }
11803 str = Jim_GetString(argv[1], &strLen);
11804 if (!strLen) return JIM_OK;
11805 noMatchStart = str;
11806 resObjPtr = Jim_NewListObj(interp, NULL, 0);
11807 /* Split */
11808 if (splitLen) {
11809 while (strLen) {
11810 for (i = 0; i < splitLen; i++) {
11811 if (*str == splitChars[i]) {
11812 Jim_Obj *objPtr;
11813
11814 objPtr = Jim_NewStringObj(interp, noMatchStart,
11815 (str-noMatchStart));
11816 Jim_ListAppendElement(interp, resObjPtr, objPtr);
11817 noMatchStart = str+1;
11818 break;
11819 }
11820 }
11821 str ++;
11822 strLen --;
11823 }
11824 Jim_ListAppendElement(interp, resObjPtr,
11825 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11826 } else {
11827 /* This handles the special case of splitchars eq {}. This
11828 * is trivial but we want to perform object sharing as Tcl does. */
11829 Jim_Obj *objCache[256];
11830 const unsigned char *u = (unsigned char*) str;
11831 memset(objCache, 0, sizeof(objCache));
11832 for (i = 0; i < strLen; i++) {
11833 int c = u[i];
11834
11835 if (objCache[c] == NULL)
11836 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11837 Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11838 }
11839 }
11840 Jim_SetResult(interp, resObjPtr);
11841 return JIM_OK;
11842 }
11843
11844 /* [join] */
11845 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11846 Jim_Obj *const *argv)
11847 {
11848 const char *joinStr;
11849 int joinStrLen, i, listLen;
11850 Jim_Obj *resObjPtr;
11851
11852 if (argc != 2 && argc != 3) {
11853 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11854 return JIM_ERR;
11855 }
11856 /* Init */
11857 if (argc == 2) {
11858 joinStr = " ";
11859 joinStrLen = 1;
11860 } else {
11861 joinStr = Jim_GetString(argv[2], &joinStrLen);
11862 }
11863 Jim_ListLength(interp, argv[1], &listLen);
11864 resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11865 /* Split */
11866 for (i = 0; i < listLen; i++) {
11867 Jim_Obj *objPtr;
11868
11869 Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11870 Jim_AppendObj(interp, resObjPtr, objPtr);
11871 if (i+1 != listLen) {
11872 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11873 }
11874 }
11875 Jim_SetResult(interp, resObjPtr);
11876 return JIM_OK;
11877 }
11878
11879 /* [format] */
11880 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11881 Jim_Obj *const *argv)
11882 {
11883 Jim_Obj *objPtr;
11884
11885 if (argc < 2) {
11886 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11887 return JIM_ERR;
11888 }
11889 objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11890 if (objPtr == NULL)
11891 return JIM_ERR;
11892 Jim_SetResult(interp, objPtr);
11893 return JIM_OK;
11894 }
11895
11896 /* [scan] */
11897 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11898 Jim_Obj *const *argv)
11899 {
11900 Jim_Obj *listPtr, **outVec;
11901 int outc, i, count = 0;
11902
11903 if (argc < 3) {
11904 Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11905 return JIM_ERR;
11906 }
11907 if (argv[2]->typePtr != &scanFmtStringObjType)
11908 SetScanFmtFromAny(interp, argv[2]);
11909 if (FormatGetError(argv[2]) != 0) {
11910 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11911 return JIM_ERR;
11912 }
11913 if (argc > 3) {
11914 int maxPos = FormatGetMaxPos(argv[2]);
11915 int count = FormatGetCnvCount(argv[2]);
11916 if (maxPos > argc-3) {
11917 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11918 return JIM_ERR;
11919 } else if (count != 0 && count < argc-3) {
11920 Jim_SetResultString(interp, "variable is not assigned by any "
11921 "conversion specifiers", -1);
11922 return JIM_ERR;
11923 } else if (count > argc-3) {
11924 Jim_SetResultString(interp, "different numbers of variable names and "
11925 "field specifiers", -1);
11926 return JIM_ERR;
11927 }
11928 }
11929 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11930 if (listPtr == 0)
11931 return JIM_ERR;
11932 if (argc > 3) {
11933 int len = 0;
11934 if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11935 Jim_ListLength(interp, listPtr, &len);
11936 if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11937 Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11938 return JIM_OK;
11939 }
11940 JimListGetElements(interp, listPtr, &outc, &outVec);
11941 for (i = 0; i < outc; ++i) {
11942 if (Jim_Length(outVec[i]) > 0) {
11943 ++count;
11944 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11945 goto err;
11946 }
11947 }
11948 Jim_FreeNewObj(interp, listPtr);
11949 Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11950 } else {
11951 if (listPtr == (Jim_Obj*)EOF) {
11952 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11953 return JIM_OK;
11954 }
11955 Jim_SetResult(interp, listPtr);
11956 }
11957 return JIM_OK;
11958 err:
11959 Jim_FreeNewObj(interp, listPtr);
11960 return JIM_ERR;
11961 }
11962
11963 /* [error] */
11964 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11965 Jim_Obj *const *argv)
11966 {
11967 if (argc != 2) {
11968 Jim_WrongNumArgs(interp, 1, argv, "message");
11969 return JIM_ERR;
11970 }
11971 Jim_SetResult(interp, argv[1]);
11972 return JIM_ERR;
11973 }
11974
11975 /* [lrange] */
11976 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11977 Jim_Obj *const *argv)
11978 {
11979 Jim_Obj *objPtr;
11980
11981 if (argc != 4) {
11982 Jim_WrongNumArgs(interp, 1, argv, "list first last");
11983 return JIM_ERR;
11984 }
11985 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11986 return JIM_ERR;
11987 Jim_SetResult(interp, objPtr);
11988 return JIM_OK;
11989 }
11990
11991 /* [env] */
11992 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11993 Jim_Obj *const *argv)
11994 {
11995 const char *key;
11996 char *val;
11997
11998 if (argc == 1) {
11999
12000 #ifdef NEED_ENVIRON_EXTERN
12001 extern char **environ;
12002 #endif
12003
12004 int i;
12005 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
12006
12007 for (i = 0; environ[i]; i++) {
12008 const char *equals = strchr(environ[i], '=');
12009 if (equals) {
12010 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, environ[i], equals - environ[i]));
12011 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
12012 }
12013 }
12014
12015 Jim_SetResult(interp, listObjPtr);
12016 return JIM_OK;
12017 }
12018
12019 if (argc != 2) {
12020 Jim_WrongNumArgs(interp, 1, argv, "varName");
12021 return JIM_ERR;
12022 }
12023 key = Jim_GetString(argv[1], NULL);
12024 val = getenv(key);
12025 if (val == NULL) {
12026 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
12027 Jim_AppendStrings(interp, Jim_GetResult(interp),
12028 "environment variable \"",
12029 key, "\" does not exist", NULL);
12030 return JIM_ERR;
12031 }
12032 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
12033 return JIM_OK;
12034 }
12035
12036 /* [source] */
12037 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
12038 Jim_Obj *const *argv)
12039 {
12040 int retval;
12041
12042 if (argc != 2) {
12043 Jim_WrongNumArgs(interp, 1, argv, "fileName");
12044 return JIM_ERR;
12045 }
12046 retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
12047 if (retval == JIM_ERR) {
12048 return JIM_ERR_ADDSTACK;
12049 }
12050 if (retval == JIM_RETURN)
12051 return JIM_OK;
12052 return retval;
12053 }
12054
12055 /* [lreverse] */
12056 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
12057 Jim_Obj *const *argv)
12058 {
12059 Jim_Obj *revObjPtr, **ele;
12060 int len;
12061
12062 if (argc != 2) {
12063 Jim_WrongNumArgs(interp, 1, argv, "list");
12064 return JIM_ERR;
12065 }
12066 JimListGetElements(interp, argv[1], &len, &ele);
12067 len--;
12068 revObjPtr = Jim_NewListObj(interp, NULL, 0);
12069 while (len >= 0)
12070 ListAppendElement(revObjPtr, ele[len--]);
12071 Jim_SetResult(interp, revObjPtr);
12072 return JIM_OK;
12073 }
12074
12075 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
12076 {
12077 jim_wide len;
12078
12079 if (step == 0) return -1;
12080 if (start == end) return 0;
12081 else if (step > 0 && start > end) return -1;
12082 else if (step < 0 && end > start) return -1;
12083 len = end-start;
12084 if (len < 0) len = -len; /* abs(len) */
12085 if (step < 0) step = -step; /* abs(step) */
12086 len = 1 + ((len-1)/step);
12087 /* We can truncate safely to INT_MAX, the range command
12088 * will always return an error for a such long range
12089 * because Tcl lists can't be so long. */
12090 if (len > INT_MAX) len = INT_MAX;
12091 return (int)((len < 0) ? -1 : len);
12092 }
12093
12094 /* [range] */
12095 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
12096 Jim_Obj *const *argv)
12097 {
12098 jim_wide start = 0, end, step = 1;
12099 int len, i;
12100 Jim_Obj *objPtr;
12101
12102 if (argc < 2 || argc > 4) {
12103 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
12104 return JIM_ERR;
12105 }
12106 if (argc == 2) {
12107 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
12108 return JIM_ERR;
12109 } else {
12110 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
12111 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
12112 return JIM_ERR;
12113 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
12114 return JIM_ERR;
12115 }
12116 if ((len = JimRangeLen(start, end, step)) == -1) {
12117 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
12118 return JIM_ERR;
12119 }
12120 objPtr = Jim_NewListObj(interp, NULL, 0);
12121 for (i = 0; i < len; i++)
12122 ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
12123 Jim_SetResult(interp, objPtr);
12124 return JIM_OK;
12125 }
12126
12127 /* [rand] */
12128 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
12129 Jim_Obj *const *argv)
12130 {
12131 jim_wide min = 0, max, len, maxMul;
12132
12133 if (argc < 1 || argc > 3) {
12134 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
12135 return JIM_ERR;
12136 }
12137 if (argc == 1) {
12138 max = JIM_WIDE_MAX;
12139 } else if (argc == 2) {
12140 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
12141 return JIM_ERR;
12142 } else if (argc == 3) {
12143 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
12144 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
12145 return JIM_ERR;
12146 }
12147 len = max-min;
12148 if (len < 0) {
12149 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
12150 return JIM_ERR;
12151 }
12152 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
12153 while (1) {
12154 jim_wide r;
12155
12156 JimRandomBytes(interp, &r, sizeof(jim_wide));
12157 if (r < 0 || r >= maxMul) continue;
12158 r = (len == 0) ? 0 : r%len;
12159 Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
12160 return JIM_OK;
12161 }
12162 }
12163
12164 /* [package] */
12165 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
12166 Jim_Obj *const *argv)
12167 {
12168 int option;
12169 const char *options[] = {
12170 "require", "provide", NULL
12171 };
12172 enum {OPT_REQUIRE, OPT_PROVIDE};
12173
12174 if (argc < 2) {
12175 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
12176 return JIM_ERR;
12177 }
12178 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
12179 JIM_ERRMSG) != JIM_OK)
12180 return JIM_ERR;
12181
12182 if (option == OPT_REQUIRE) {
12183 int exact = 0;
12184 const char *ver;
12185
12186 if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
12187 exact = 1;
12188 argv++;
12189 argc--;
12190 }
12191 if (argc != 3 && argc != 4) {
12192 Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
12193 return JIM_ERR;
12194 }
12195 ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
12196 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
12197 JIM_ERRMSG);
12198 if (ver == NULL)
12199 return JIM_ERR_ADDSTACK;
12200 Jim_SetResultString(interp, ver, -1);
12201 } else if (option == OPT_PROVIDE) {
12202 if (argc != 4) {
12203 Jim_WrongNumArgs(interp, 2, argv, "package version");
12204 return JIM_ERR;
12205 }
12206 return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
12207 Jim_GetString(argv[3], NULL), JIM_ERRMSG);
12208 }
12209 return JIM_OK;
12210 }
12211
12212 static struct {
12213 const char *name;
12214 Jim_CmdProc cmdProc;
12215 } Jim_CoreCommandsTable[] = {
12216 {"set", Jim_SetCoreCommand},
12217 {"unset", Jim_UnsetCoreCommand},
12218 {"puts", Jim_PutsCoreCommand},
12219 {"+", Jim_AddCoreCommand},
12220 {"*", Jim_MulCoreCommand},
12221 {"-", Jim_SubCoreCommand},
12222 {"/", Jim_DivCoreCommand},
12223 {"incr", Jim_IncrCoreCommand},
12224 {"while", Jim_WhileCoreCommand},
12225 {"for", Jim_ForCoreCommand},
12226 {"foreach", Jim_ForeachCoreCommand},
12227 {"lmap", Jim_LmapCoreCommand},
12228 {"if", Jim_IfCoreCommand},
12229 {"switch", Jim_SwitchCoreCommand},
12230 {"list", Jim_ListCoreCommand},
12231 {"lindex", Jim_LindexCoreCommand},
12232 {"lset", Jim_LsetCoreCommand},
12233 {"llength", Jim_LlengthCoreCommand},
12234 {"lappend", Jim_LappendCoreCommand},
12235 {"linsert", Jim_LinsertCoreCommand},
12236 {"lsort", Jim_LsortCoreCommand},
12237 {"append", Jim_AppendCoreCommand},
12238 {"debug", Jim_DebugCoreCommand},
12239 {"eval", Jim_EvalCoreCommand},
12240 {"uplevel", Jim_UplevelCoreCommand},
12241 {"expr", Jim_ExprCoreCommand},
12242 {"break", Jim_BreakCoreCommand},
12243 {"continue", Jim_ContinueCoreCommand},
12244 {"proc", Jim_ProcCoreCommand},
12245 {"concat", Jim_ConcatCoreCommand},
12246 {"return", Jim_ReturnCoreCommand},
12247 {"upvar", Jim_UpvarCoreCommand},
12248 {"global", Jim_GlobalCoreCommand},
12249 {"string", Jim_StringCoreCommand},
12250 {"time", Jim_TimeCoreCommand},
12251 {"exit", Jim_ExitCoreCommand},
12252 {"catch", Jim_CatchCoreCommand},
12253 {"ref", Jim_RefCoreCommand},
12254 {"getref", Jim_GetrefCoreCommand},
12255 {"setref", Jim_SetrefCoreCommand},
12256 {"finalize", Jim_FinalizeCoreCommand},
12257 {"collect", Jim_CollectCoreCommand},
12258 {"rename", Jim_RenameCoreCommand},
12259 {"dict", Jim_DictCoreCommand},
12260 {"load", Jim_LoadCoreCommand},
12261 {"subst", Jim_SubstCoreCommand},
12262 {"info", Jim_InfoCoreCommand},
12263 {"split", Jim_SplitCoreCommand},
12264 {"join", Jim_JoinCoreCommand},
12265 {"format", Jim_FormatCoreCommand},
12266 {"scan", Jim_ScanCoreCommand},
12267 {"error", Jim_ErrorCoreCommand},
12268 {"lrange", Jim_LrangeCoreCommand},
12269 {"env", Jim_EnvCoreCommand},
12270 {"source", Jim_SourceCoreCommand},
12271 {"lreverse", Jim_LreverseCoreCommand},
12272 {"range", Jim_RangeCoreCommand},
12273 {"rand", Jim_RandCoreCommand},
12274 {"package", Jim_PackageCoreCommand},
12275 {"tailcall", Jim_TailcallCoreCommand},
12276 {NULL, NULL},
12277 };
12278
12279 /* Some Jim core command is actually a procedure written in Jim itself. */
12280 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
12281 {
12282 Jim_Eval(interp, (char*)
12283 "proc lambda {arglist args} {\n"
12284 " set name [ref {} function lambdaFinalizer]\n"
12285 " uplevel 1 [list proc $name $arglist {expand}$args]\n"
12286 " return $name\n"
12287 "}\n"
12288 "proc lambdaFinalizer {name val} {\n"
12289 " rename $name {}\n"
12290 "}\n"
12291 );
12292 }
12293
12294 void Jim_RegisterCoreCommands(Jim_Interp *interp)
12295 {
12296 int i = 0;
12297
12298 while(Jim_CoreCommandsTable[i].name != NULL) {
12299 Jim_CreateCommand(interp,
12300 Jim_CoreCommandsTable[i].name,
12301 Jim_CoreCommandsTable[i].cmdProc,
12302 NULL, NULL);
12303 i++;
12304 }
12305 Jim_RegisterCoreProcedures(interp);
12306 }
12307
12308 /* -----------------------------------------------------------------------------
12309 * Interactive prompt
12310 * ---------------------------------------------------------------------------*/
12311 void Jim_PrintErrorMessage(Jim_Interp *interp)
12312 {
12313 int len, i;
12314
12315 if (*interp->errorFileName) {
12316 Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL " ",
12317 interp->errorFileName, interp->errorLine);
12318 }
12319 Jim_fprintf(interp,interp->cookie_stderr, "%s" JIM_NL,
12320 Jim_GetString(interp->result, NULL));
12321 Jim_ListLength(interp, interp->stackTrace, &len);
12322 for (i = len-3; i >= 0; i-= 3) {
12323 Jim_Obj *objPtr;
12324 const char *proc, *file, *line;
12325
12326 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
12327 proc = Jim_GetString(objPtr, NULL);
12328 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
12329 JIM_NONE);
12330 file = Jim_GetString(objPtr, NULL);
12331 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
12332 JIM_NONE);
12333 line = Jim_GetString(objPtr, NULL);
12334 if (*proc) {
12335 Jim_fprintf( interp, interp->cookie_stderr,
12336 "in procedure '%s' ", proc);
12337 }
12338 if (*file) {
12339 Jim_fprintf( interp, interp->cookie_stderr,
12340 "called at file \"%s\", line %s",
12341 file, line);
12342 }
12343 if (*file || *proc) {
12344 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL);
12345 }
12346 }
12347 }
12348
12349 int Jim_InteractivePrompt(Jim_Interp *interp)
12350 {
12351 int retcode = JIM_OK;
12352 Jim_Obj *scriptObjPtr;
12353
12354 Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
12355 "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
12356 JIM_VERSION / 100, JIM_VERSION % 100);
12357 Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
12358 while (1) {
12359 char buf[1024];
12360 const char *result;
12361 const char *retcodestr[] = {
12362 "ok", "error", "return", "break", "continue", "eval", "exit"
12363 };
12364 int reslen;
12365
12366 if (retcode != 0) {
12367 if (retcode >= 2 && retcode <= 6)
12368 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
12369 else
12370 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
12371 } else
12372 Jim_fprintf( interp, interp->cookie_stdout, ". ");
12373 Jim_fflush( interp, interp->cookie_stdout);
12374 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
12375 Jim_IncrRefCount(scriptObjPtr);
12376 while(1) {
12377 const char *str;
12378 char state;
12379 int len;
12380
12381 if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
12382 Jim_DecrRefCount(interp, scriptObjPtr);
12383 goto out;
12384 }
12385 Jim_AppendString(interp, scriptObjPtr, buf, -1);
12386 str = Jim_GetString(scriptObjPtr, &len);
12387 if (Jim_ScriptIsComplete(str, len, &state))
12388 break;
12389 Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12390 Jim_fflush( interp, interp->cookie_stdout);
12391 }
12392 retcode = Jim_EvalObj(interp, scriptObjPtr);
12393 Jim_DecrRefCount(interp, scriptObjPtr);
12394 result = Jim_GetString(Jim_GetResult(interp), &reslen);
12395 if (retcode == JIM_ERR) {
12396 Jim_PrintErrorMessage(interp);
12397 } else if (retcode == JIM_EXIT) {
12398 exit(Jim_GetExitCode(interp));
12399 } else {
12400 if (reslen) {
12401 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12402 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12403 }
12404 }
12405 }
12406 out:
12407 return 0;
12408 }
12409
12410 /* -----------------------------------------------------------------------------
12411 * Jim's idea of STDIO..
12412 * ---------------------------------------------------------------------------*/
12413
12414 int Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12415 {
12416 int r;
12417
12418 va_list ap;
12419 va_start(ap,fmt);
12420 r = Jim_vfprintf( interp, cookie, fmt,ap );
12421 va_end(ap);
12422 return r;
12423 }
12424
12425 int Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12426 {
12427 if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12428 errno = ENOTSUP;
12429 return -1;
12430 }
12431 return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12432 }
12433
12434 size_t Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12435 {
12436 if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12437 errno = ENOTSUP;
12438 return 0;
12439 }
12440 return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12441 }
12442
12443 size_t Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12444 {
12445 if( (interp == NULL) || (interp->cb_fread == NULL) ){
12446 errno = ENOTSUP;
12447 return 0;
12448 }
12449 return (*(interp->cb_fread))( ptr, size, n, cookie);
12450 }
12451
12452 int Jim_fflush( Jim_Interp *interp, void *cookie )
12453 {
12454 if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12455 /* pretend all is well */
12456 return 0;
12457 }
12458 return (*(interp->cb_fflush))( cookie );
12459 }
12460
12461 char* Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12462 {
12463 if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12464 errno = ENOTSUP;
12465 return NULL;
12466 }
12467 return (*(interp->cb_fgets))( s, size, cookie );
12468 }
12469 Jim_Nvp *
12470 Jim_Nvp_name2value_simple( const Jim_Nvp *p, const char *name )
12471 {
12472 while( p->name ){
12473 if( 0 == strcmp( name, p->name ) ){
12474 break;
12475 }
12476 p++;
12477 }
12478 return ((Jim_Nvp *)(p));
12479 }
12480
12481 Jim_Nvp *
12482 Jim_Nvp_name2value_nocase_simple( const Jim_Nvp *p, const char *name )
12483 {
12484 while( p->name ){
12485 if( 0 == strcasecmp( name, p->name ) ){
12486 break;
12487 }
12488 p++;
12489 }
12490 return ((Jim_Nvp *)(p));
12491 }
12492
12493 int
12494 Jim_Nvp_name2value_obj( Jim_Interp *interp,
12495 const Jim_Nvp *p,
12496 Jim_Obj *o,
12497 Jim_Nvp **result )
12498 {
12499 return Jim_Nvp_name2value( interp, p, Jim_GetString( o, NULL ), result );
12500 }
12501
12502
12503 int
12504 Jim_Nvp_name2value( Jim_Interp *interp,
12505 const Jim_Nvp *_p,
12506 const char *name,
12507 Jim_Nvp **result)
12508 {
12509 const Jim_Nvp *p;
12510
12511 p = Jim_Nvp_name2value_simple( _p, name );
12512
12513 /* result */
12514 if( result ){
12515 *result = (Jim_Nvp *)(p);
12516 }
12517
12518 /* found? */
12519 if( p->name ){
12520 return JIM_OK;
12521 } else {
12522 return JIM_ERR;
12523 }
12524 }
12525
12526 int
12527 Jim_Nvp_name2value_obj_nocase( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **puthere )
12528 {
12529 return Jim_Nvp_name2value_nocase( interp, p, Jim_GetString( o, NULL ), puthere );
12530 }
12531
12532 int
12533 Jim_Nvp_name2value_nocase( Jim_Interp *interp, const Jim_Nvp *_p, const char *name, Jim_Nvp **puthere )
12534 {
12535 const Jim_Nvp *p;
12536
12537 p = Jim_Nvp_name2value_nocase_simple( _p, name );
12538
12539 if( puthere ){
12540 *puthere = (Jim_Nvp *)(p);
12541 }
12542 /* found */
12543 if( p->name ){
12544 return JIM_OK;
12545 } else {
12546 return JIM_ERR;
12547 }
12548 }
12549
12550
12551 int
12552 Jim_Nvp_value2name_obj( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **result )
12553 {
12554 int e;;
12555 jim_wide w;
12556
12557 e = Jim_GetWide( interp, o, &w );
12558 if( e != JIM_OK ){
12559 return e;
12560 }
12561
12562 return Jim_Nvp_value2name( interp, p, w, result );
12563 }
12564
12565 Jim_Nvp *
12566 Jim_Nvp_value2name_simple( const Jim_Nvp *p, int value )
12567 {
12568 while( p->name ){
12569 if( value == p->value ){
12570 break;
12571 }
12572 p++;
12573 }
12574 return ((Jim_Nvp *)(p));
12575 }
12576
12577
12578 int
12579 Jim_Nvp_value2name( Jim_Interp *interp, const Jim_Nvp *_p, int value, Jim_Nvp **result )
12580 {
12581 const Jim_Nvp *p;
12582
12583 p = Jim_Nvp_value2name_simple( _p, value );
12584
12585 if( result ){
12586 *result = (Jim_Nvp *)(p);
12587 }
12588
12589 if( p->name ){
12590 return JIM_OK;
12591 } else {
12592 return JIM_ERR;
12593 }
12594 }
12595
12596
12597 int
12598 Jim_GetOpt_Setup( Jim_GetOptInfo *p, Jim_Interp *interp, int argc, Jim_Obj * const * argv)
12599 {
12600 memset( p, 0, sizeof(*p) );
12601 p->interp = interp;
12602 p->argc = argc;
12603 p->argv = argv;
12604
12605 return JIM_OK;
12606 }
12607
12608 void
12609 Jim_GetOpt_Debug( Jim_GetOptInfo *p )
12610 {
12611 int x;
12612
12613 Jim_fprintf( p->interp, p->interp->cookie_stderr, "---args---\n");
12614 for( x = 0 ; x < p->argc ; x++ ){
12615 Jim_fprintf( p->interp, p->interp->cookie_stderr,
12616 "%2d) %s\n",
12617 x,
12618 Jim_GetString( p->argv[x], NULL ) );
12619 }
12620 Jim_fprintf( p->interp, p->interp->cookie_stderr, "-------\n");
12621 }
12622
12623
12624 int
12625 Jim_GetOpt_Obj( Jim_GetOptInfo *goi, Jim_Obj **puthere )
12626 {
12627 Jim_Obj *o;
12628
12629 o = NULL; // failure
12630 if( goi->argc ){
12631 // success
12632 o = goi->argv[0];
12633 goi->argc -= 1;
12634 goi->argv += 1;
12635 }
12636 if( puthere ){
12637 *puthere = o;
12638 }
12639 if( o != NULL ){
12640 return JIM_OK;
12641 } else {
12642 return JIM_ERR;
12643 }
12644 }
12645
12646 int
12647 Jim_GetOpt_String( Jim_GetOptInfo *goi, char **puthere, int *len )
12648 {
12649 int r;
12650 Jim_Obj *o;
12651 const char *cp;
12652
12653
12654 r = Jim_GetOpt_Obj( goi, &o );
12655 if( r == JIM_OK ){
12656 cp = Jim_GetString( o, len );
12657 if( puthere ){
12658 /* remove const */
12659 *puthere = (char *)(cp);
12660 }
12661 }
12662 return r;
12663 }
12664
12665 int
12666 Jim_GetOpt_Double( Jim_GetOptInfo *goi, double *puthere )
12667 {
12668 int r;
12669 Jim_Obj *o;
12670 double _safe;
12671
12672 if( puthere == NULL ){
12673 puthere = &_safe;
12674 }
12675
12676 r = Jim_GetOpt_Obj( goi, &o );
12677 if( r == JIM_OK ){
12678 r = Jim_GetDouble( goi->interp, o, puthere );
12679 if( r != JIM_OK ){
12680 Jim_SetResult_sprintf( goi->interp,
12681 "not a number: %s",
12682 Jim_GetString( o, NULL ) );
12683 }
12684 }
12685 return r;
12686 }
12687
12688 int
12689 Jim_GetOpt_Wide( Jim_GetOptInfo *goi, jim_wide *puthere )
12690 {
12691 int r;
12692 Jim_Obj *o;
12693 jim_wide _safe;
12694
12695 if( puthere == NULL ){
12696 puthere = &_safe;
12697 }
12698
12699 r = Jim_GetOpt_Obj( goi, &o );
12700 if( r == JIM_OK ){
12701 r = Jim_GetWide( goi->interp, o, puthere );
12702 }
12703 return r;
12704 }
12705
12706 int Jim_GetOpt_Nvp( Jim_GetOptInfo *goi,
12707 const Jim_Nvp *nvp,
12708 Jim_Nvp **puthere)
12709 {
12710 Jim_Nvp *_safe;
12711 Jim_Obj *o;
12712 int e;
12713
12714 if( puthere == NULL ){
12715 puthere = &_safe;
12716 }
12717
12718 e = Jim_GetOpt_Obj( goi, &o );
12719 if( e == JIM_OK ){
12720 e = Jim_Nvp_name2value_obj( goi->interp,
12721 nvp,
12722 o,
12723 puthere );
12724 }
12725
12726 return e;
12727 }
12728
12729 void
12730 Jim_GetOpt_NvpUnknown( Jim_GetOptInfo *goi,
12731 const Jim_Nvp *nvptable,
12732 int hadprefix )
12733 {
12734 if( hadprefix ){
12735 Jim_SetResult_NvpUnknown( goi->interp,
12736 goi->argv[-2],
12737 goi->argv[-1],
12738 nvptable );
12739 } else {
12740 Jim_SetResult_NvpUnknown( goi->interp,
12741 NULL,
12742 goi->argv[-1],
12743 nvptable );
12744 }
12745 }
12746
12747
12748 int
12749 Jim_GetOpt_Enum( Jim_GetOptInfo *goi,
12750 const char * const * lookup,
12751 int *puthere)
12752 {
12753 int _safe;
12754 Jim_Obj *o;
12755 int e;
12756
12757 if( puthere == NULL ){
12758 puthere = &_safe;
12759 }
12760 e = Jim_GetOpt_Obj( goi, &o );
12761 if( e == JIM_OK ){
12762 e = Jim_GetEnum( goi->interp,
12763 o,
12764 lookup,
12765 puthere,
12766 "option",
12767 JIM_ERRMSG );
12768 }
12769 return e;
12770 }
12771
12772
12773
12774 int
12775 Jim_SetResult_sprintf( Jim_Interp *interp, const char *fmt,... )
12776 {
12777 va_list ap;
12778 char *buf;
12779
12780 va_start(ap,fmt);
12781 buf = jim_vasprintf( fmt, ap );
12782 va_end(ap);
12783 if( buf ){
12784 Jim_SetResultString( interp, buf, -1 );
12785 jim_vasprintf_done(buf);
12786 }
12787 return JIM_OK;
12788 }
12789
12790
12791 void
12792 Jim_SetResult_NvpUnknown( Jim_Interp *interp,
12793 Jim_Obj *param_name,
12794 Jim_Obj *param_value,
12795 const Jim_Nvp *nvp )
12796 {
12797 if( param_name ){
12798 Jim_SetResult_sprintf( interp,
12799 "%s: Unknown: %s, try one of: ",
12800 Jim_GetString( param_name, NULL ),
12801 Jim_GetString( param_value, NULL ) );
12802 } else {
12803 Jim_SetResult_sprintf( interp,
12804 "Unknown param: %s, try one of: ",
12805 Jim_GetString( param_value, NULL ) );
12806 }
12807 while( nvp->name ){
12808 const char *a;
12809 const char *b;
12810
12811 if( (nvp+1)->name ){
12812 a = nvp->name;
12813 b = ", ";
12814 } else {
12815 a = "or ";
12816 b = nvp->name;
12817 }
12818 Jim_AppendStrings( interp,
12819 Jim_GetResult(interp),
12820 a, b, NULL );
12821 nvp++;
12822 }
12823 }
12824
12825
12826 static Jim_Obj *debug_string_obj;
12827
12828 const char *
12829 Jim_Debug_ArgvString( Jim_Interp *interp, int argc, Jim_Obj *const *argv )
12830 {
12831 int x;
12832
12833 if( debug_string_obj ){
12834 Jim_FreeObj( interp, debug_string_obj );
12835 }
12836
12837 debug_string_obj = Jim_NewEmptyStringObj( interp );
12838 for( x = 0 ; x < argc ; x++ ){
12839 Jim_AppendStrings( interp,
12840 debug_string_obj,
12841 Jim_GetString( argv[x], NULL ),
12842 " ",
12843 NULL );
12844 }
12845
12846 return Jim_GetString( debug_string_obj, NULL );
12847 }
12848
12849
12850
12851 /*
12852 * Local Variables: ***
12853 * c-basic-offset: 4 ***
12854 * tab-width: 4 ***
12855 * End: ***
12856 */

Linking to existing account procedure

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

SSH host keys fingerprints

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