Remove redundant declarations to allow building with -Wredundant-decls.
[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 #define __JIM_CORE__
43 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
44
45 #ifdef __ECOS
46 #include <pkgconf/jimtcl.h>
47 #endif
48 #ifndef JIM_ANSIC
49 #define JIM_DYNLIB /* Dynamic library support for UNIX and WIN32 */
50 #endif /* JIM_ANSIC */
51
52 #ifndef _GNU_SOURCE
53 #define _GNU_SOURCE /* for vasprintf() */
54 #endif
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <stdarg.h>
59 #include <ctype.h>
60 #include <limits.h>
61 #include <assert.h>
62 #include <errno.h>
63 #include <time.h>
64 #if defined(WIN32)
65 /* sys/time - need is different */
66 #else
67 #include <sys/time.h> // for gettimeofday()
68 #endif
69
70 #include "replacements.h"
71
72 /* Include the platform dependent libraries for
73 * dynamic loading of libraries. */
74 #ifdef JIM_DYNLIB
75 #if defined(_WIN32) || defined(WIN32)
76 #ifndef WIN32
77 #define WIN32 1
78 #endif
79 #ifndef STRICT
80 #define STRICT
81 #endif
82 #define WIN32_LEAN_AND_MEAN
83 #include <windows.h>
84 #if _MSC_VER >= 1000
85 #pragma warning(disable:4146)
86 #endif /* _MSC_VER */
87 #else
88 #include <dlfcn.h>
89 #endif /* WIN32 */
90 #endif /* JIM_DYNLIB */
91
92 #ifndef WIN32
93 #include <unistd.h>
94 #endif
95
96 #ifdef __ECOS
97 #include <cyg/jimtcl/jim.h>
98 #else
99 #include "jim.h"
100 #endif
101
102 #ifdef HAVE_BACKTRACE
103 #include <execinfo.h>
104 #endif
105
106 /* -----------------------------------------------------------------------------
107 * Global variables
108 * ---------------------------------------------------------------------------*/
109
110 /* A shared empty string for the objects string representation.
111 * Jim_InvalidateStringRep knows about it and don't try to free. */
112 static char *JimEmptyStringRep = (char*) "";
113
114 /* -----------------------------------------------------------------------------
115 * Required prototypes of not exported functions
116 * ---------------------------------------------------------------------------*/
117 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
118 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
119 static void JimRegisterCoreApi(Jim_Interp *interp);
120
121 static Jim_HashTableType JimVariablesHashTableType;
122
123 /* -----------------------------------------------------------------------------
124 * Utility functions
125 * ---------------------------------------------------------------------------*/
126
127 static char *
128 jim_vasprintf( const char *fmt, va_list ap )
129 {
130 #ifndef HAVE_VASPRINTF
131 /* yucky way */
132 static char buf[2048];
133 vsnprintf( buf, sizeof(buf), fmt, ap );
134 /* garentee termination */
135 buf[sizeof(buf)-1] = 0;
136 #else
137 char *buf;
138 int result;
139 result = vasprintf( &buf, fmt, ap );
140 if (result < 0) exit(-1);
141 #endif
142 return buf;
143 }
144
145 static void
146 jim_vasprintf_done( void *buf )
147 {
148 #ifndef HAVE_VASPRINTF
149 (void)(buf);
150 #else
151 free(buf);
152 #endif
153 }
154
155
156 /*
157 * Convert a string to a jim_wide INTEGER.
158 * This function originates from BSD.
159 *
160 * Ignores `locale' stuff. Assumes that the upper and lower case
161 * alphabets and digits are each contiguous.
162 */
163 #ifdef HAVE_LONG_LONG
164 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
165 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
166 {
167 register const char *s;
168 register unsigned jim_wide acc;
169 register unsigned char c;
170 register unsigned jim_wide qbase, cutoff;
171 register int neg, any, cutlim;
172
173 /*
174 * Skip white space and pick up leading +/- sign if any.
175 * If base is 0, allow 0x for hex and 0 for octal, else
176 * assume decimal; if base is already 16, allow 0x.
177 */
178 s = nptr;
179 do {
180 c = *s++;
181 } while (isspace(c));
182 if (c == '-') {
183 neg = 1;
184 c = *s++;
185 } else {
186 neg = 0;
187 if (c == '+')
188 c = *s++;
189 }
190 if ((base == 0 || base == 16) &&
191 c == '0' && (*s == 'x' || *s == 'X')) {
192 c = s[1];
193 s += 2;
194 base = 16;
195 }
196 if (base == 0)
197 base = c == '0' ? 8 : 10;
198
199 /*
200 * Compute the cutoff value between legal numbers and illegal
201 * numbers. That is the largest legal value, divided by the
202 * base. An input number that is greater than this value, if
203 * followed by a legal input character, is too big. One that
204 * is equal to this value may be valid or not; the limit
205 * between valid and invalid numbers is then based on the last
206 * digit. For instance, if the range for quads is
207 * [-9223372036854775808..9223372036854775807] and the input base
208 * is 10, cutoff will be set to 922337203685477580 and cutlim to
209 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
210 * accumulated a value > 922337203685477580, or equal but the
211 * next digit is > 7 (or 8), the number is too big, and we will
212 * return a range error.
213 *
214 * Set any if any `digits' consumed; make it negative to indicate
215 * overflow.
216 */
217 qbase = (unsigned)base;
218 cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
219 : LLONG_MAX;
220 cutlim = (int)(cutoff % qbase);
221 cutoff /= qbase;
222 for (acc = 0, any = 0;; c = *s++) {
223 if (!JimIsAscii(c))
224 break;
225 if (isdigit(c))
226 c -= '0';
227 else if (isalpha(c))
228 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
229 else
230 break;
231 if (c >= base)
232 break;
233 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
234 any = -1;
235 else {
236 any = 1;
237 acc *= qbase;
238 acc += c;
239 }
240 }
241 if (any < 0) {
242 acc = neg ? LLONG_MIN : LLONG_MAX;
243 errno = ERANGE;
244 } else if (neg)
245 acc = -acc;
246 if (endptr != 0)
247 *endptr = (char *)(any ? s - 1 : nptr);
248 return (acc);
249 }
250 #endif
251
252 /* Glob-style pattern matching. */
253 static int JimStringMatch(const char *pattern, int patternLen,
254 const char *string, int stringLen, int nocase)
255 {
256 while(patternLen) {
257 switch(pattern[0]) {
258 case '*':
259 while (pattern[1] == '*') {
260 pattern++;
261 patternLen--;
262 }
263 if (patternLen == 1)
264 return 1; /* match */
265 while(stringLen) {
266 if (JimStringMatch(pattern+1, patternLen-1,
267 string, stringLen, nocase))
268 return 1; /* match */
269 string++;
270 stringLen--;
271 }
272 return 0; /* no match */
273 break;
274 case '?':
275 if (stringLen == 0)
276 return 0; /* no match */
277 string++;
278 stringLen--;
279 break;
280 case '[':
281 {
282 int not, match;
283
284 pattern++;
285 patternLen--;
286 not = pattern[0] == '^';
287 if (not) {
288 pattern++;
289 patternLen--;
290 }
291 match = 0;
292 while(1) {
293 if (pattern[0] == '\\') {
294 pattern++;
295 patternLen--;
296 if (pattern[0] == string[0])
297 match = 1;
298 } else if (pattern[0] == ']') {
299 break;
300 } else if (patternLen == 0) {
301 pattern--;
302 patternLen++;
303 break;
304 } else if (pattern[1] == '-' && patternLen >= 3) {
305 int start = pattern[0];
306 int end = pattern[2];
307 int c = string[0];
308 if (start > end) {
309 int t = start;
310 start = end;
311 end = t;
312 }
313 if (nocase) {
314 start = tolower(start);
315 end = tolower(end);
316 c = tolower(c);
317 }
318 pattern += 2;
319 patternLen -= 2;
320 if (c >= start && c <= end)
321 match = 1;
322 } else {
323 if (!nocase) {
324 if (pattern[0] == string[0])
325 match = 1;
326 } else {
327 if (tolower((int)pattern[0]) == tolower((int)string[0]))
328 match = 1;
329 }
330 }
331 pattern++;
332 patternLen--;
333 }
334 if (not)
335 match = !match;
336 if (!match)
337 return 0; /* no match */
338 string++;
339 stringLen--;
340 break;
341 }
342 case '\\':
343 if (patternLen >= 2) {
344 pattern++;
345 patternLen--;
346 }
347 /* fall through */
348 default:
349 if (!nocase) {
350 if (pattern[0] != string[0])
351 return 0; /* no match */
352 } else {
353 if (tolower((int)pattern[0]) != tolower((int)string[0]))
354 return 0; /* no match */
355 }
356 string++;
357 stringLen--;
358 break;
359 }
360 pattern++;
361 patternLen--;
362 if (stringLen == 0) {
363 while(*pattern == '*') {
364 pattern++;
365 patternLen--;
366 }
367 break;
368 }
369 }
370 if (patternLen == 0 && stringLen == 0)
371 return 1;
372 return 0;
373 }
374
375 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
376 int nocase)
377 {
378 unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
379
380 if (nocase == 0) {
381 while(l1 && l2) {
382 if (*u1 != *u2)
383 return (int)*u1-*u2;
384 u1++; u2++; l1--; l2--;
385 }
386 if (!l1 && !l2) return 0;
387 return l1-l2;
388 } else {
389 while(l1 && l2) {
390 if (tolower((int)*u1) != tolower((int)*u2))
391 return tolower((int)*u1)-tolower((int)*u2);
392 u1++; u2++; l1--; l2--;
393 }
394 if (!l1 && !l2) return 0;
395 return l1-l2;
396 }
397 }
398
399 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
400 * The index of the first occurrence of s1 in s2 is returned.
401 * If s1 is not found inside s2, -1 is returned. */
402 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
403 {
404 int i;
405
406 if (!l1 || !l2 || l1 > l2) return -1;
407 if (index < 0) index = 0;
408 s2 += index;
409 for (i = index; i <= l2-l1; i++) {
410 if (memcmp(s2, s1, l1) == 0)
411 return i;
412 s2++;
413 }
414 return -1;
415 }
416
417 int Jim_WideToString(char *buf, jim_wide wideValue)
418 {
419 const char *fmt = "%" JIM_WIDE_MODIFIER;
420 return sprintf(buf, fmt, wideValue);
421 }
422
423 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
424 {
425 char *endptr;
426
427 #ifdef HAVE_LONG_LONG
428 *widePtr = JimStrtoll(str, &endptr, base);
429 #else
430 *widePtr = strtol(str, &endptr, base);
431 #endif
432 if ((str[0] == '\0') || (str == endptr) )
433 return JIM_ERR;
434 if (endptr[0] != '\0') {
435 while(*endptr) {
436 if (!isspace((int)*endptr))
437 return JIM_ERR;
438 endptr++;
439 }
440 }
441 return JIM_OK;
442 }
443
444 int Jim_StringToIndex(const char *str, int *intPtr)
445 {
446 char *endptr;
447
448 *intPtr = strtol(str, &endptr, 10);
449 if ( (str[0] == '\0') || (str == endptr) )
450 return JIM_ERR;
451 if (endptr[0] != '\0') {
452 while(*endptr) {
453 if (!isspace((int)*endptr))
454 return JIM_ERR;
455 endptr++;
456 }
457 }
458 return JIM_OK;
459 }
460
461 /* The string representation of references has two features in order
462 * to make the GC faster. The first is that every reference starts
463 * with a non common character '~', in order to make the string matching
464 * fater. The second is that the reference string rep his 32 characters
465 * in length, this allows to avoid to check every object with a string
466 * repr < 32, and usually there are many of this objects. */
467
468 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
469
470 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
471 {
472 const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
473 sprintf(buf, fmt, refPtr->tag, id);
474 return JIM_REFERENCE_SPACE;
475 }
476
477 int Jim_DoubleToString(char *buf, double doubleValue)
478 {
479 char *s;
480 int len;
481
482 len = sprintf(buf, "%.17g", doubleValue);
483 s = buf;
484 while(*s) {
485 if (*s == '.') return len;
486 s++;
487 }
488 /* Add a final ".0" if it's a number. But not
489 * for NaN or InF */
490 if (isdigit((int)buf[0])
491 || ((buf[0] == '-' || buf[0] == '+')
492 && isdigit((int)buf[1]))) {
493 s[0] = '.';
494 s[1] = '0';
495 s[2] = '\0';
496 return len+2;
497 }
498 return len;
499 }
500
501 int Jim_StringToDouble(const char *str, double *doublePtr)
502 {
503 char *endptr;
504
505 *doublePtr = strtod(str, &endptr);
506 if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
507 return JIM_ERR;
508 return JIM_OK;
509 }
510
511 static jim_wide JimPowWide(jim_wide b, jim_wide e)
512 {
513 jim_wide i, res = 1;
514 if ((b==0 && e!=0) || (e<0)) return 0;
515 for(i=0; i<e; i++) {res *= b;}
516 return res;
517 }
518
519 /* -----------------------------------------------------------------------------
520 * Special functions
521 * ---------------------------------------------------------------------------*/
522
523 /* Note that 'interp' may be NULL if not available in the
524 * context of the panic. It's only useful to get the error
525 * file descriptor, it will default to stderr otherwise. */
526 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
527 {
528 va_list ap;
529
530 va_start(ap, fmt);
531 /*
532 * Send it here first.. Assuming STDIO still works
533 */
534 fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
535 vfprintf(stderr, fmt, ap);
536 fprintf(stderr, JIM_NL JIM_NL);
537 va_end(ap);
538
539 #ifdef HAVE_BACKTRACE
540 {
541 void *array[40];
542 int size, i;
543 char **strings;
544
545 size = backtrace(array, 40);
546 strings = backtrace_symbols(array, size);
547 for (i = 0; i < size; i++)
548 fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
549 fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
550 fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
551 }
552 #endif
553
554 /* This may actually crash... we do it last */
555 if( interp && interp->cookie_stderr ){
556 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
557 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
558 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL JIM_NL );
559 }
560 abort();
561 }
562
563 /* -----------------------------------------------------------------------------
564 * Memory allocation
565 * ---------------------------------------------------------------------------*/
566
567 /* Macro used for memory debugging.
568 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
569 * and similary for Jim_Realloc and Jim_Free */
570 #if 0
571 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
572 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
573 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
574 #endif
575
576 void *Jim_Alloc(int size)
577 {
578 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
579 if (size==0)
580 size=1;
581 void *p = malloc(size);
582 if (p == NULL)
583 Jim_Panic(NULL,"malloc: Out of memory");
584 return p;
585 }
586
587 void Jim_Free(void *ptr) {
588 free(ptr);
589 }
590
591 void *Jim_Realloc(void *ptr, int size)
592 {
593 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
594 if (size==0)
595 size=1;
596 void *p = realloc(ptr, size);
597 if (p == NULL)
598 Jim_Panic(NULL,"realloc: Out of memory");
599 return p;
600 }
601
602 char *Jim_StrDup(const char *s)
603 {
604 int l = strlen(s);
605 char *copy = Jim_Alloc(l+1);
606
607 memcpy(copy, s, l+1);
608 return copy;
609 }
610
611 char *Jim_StrDupLen(const char *s, int l)
612 {
613 char *copy = Jim_Alloc(l+1);
614
615 memcpy(copy, s, l+1);
616 copy[l] = 0; /* Just to be sure, original could be substring */
617 return copy;
618 }
619
620 /* -----------------------------------------------------------------------------
621 * Time related functions
622 * ---------------------------------------------------------------------------*/
623 /* Returns microseconds of CPU used since start. */
624 static jim_wide JimClock(void)
625 {
626 #if (defined WIN32) && !(defined JIM_ANSIC)
627 LARGE_INTEGER t, f;
628 QueryPerformanceFrequency(&f);
629 QueryPerformanceCounter(&t);
630 return (long)((t.QuadPart * 1000000) / f.QuadPart);
631 #else /* !WIN32 */
632 clock_t clocks = clock();
633
634 return (long)(clocks*(1000000/CLOCKS_PER_SEC));
635 #endif /* WIN32 */
636 }
637
638 /* -----------------------------------------------------------------------------
639 * Hash Tables
640 * ---------------------------------------------------------------------------*/
641
642 /* -------------------------- private prototypes ---------------------------- */
643 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
644 static unsigned int JimHashTableNextPower(unsigned int size);
645 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
646
647 /* -------------------------- hash functions -------------------------------- */
648
649 /* Thomas Wang's 32 bit Mix Function */
650 unsigned int Jim_IntHashFunction(unsigned int key)
651 {
652 key += ~(key << 15);
653 key ^= (key >> 10);
654 key += (key << 3);
655 key ^= (key >> 6);
656 key += ~(key << 11);
657 key ^= (key >> 16);
658 return key;
659 }
660
661 /* Identity hash function for integer keys */
662 unsigned int Jim_IdentityHashFunction(unsigned int key)
663 {
664 return key;
665 }
666
667 /* Generic hash function (we are using to multiply by 9 and add the byte
668 * as Tcl) */
669 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
670 {
671 unsigned int h = 0;
672 while(len--)
673 h += (h<<3)+*buf++;
674 return h;
675 }
676
677 /* ----------------------------- API implementation ------------------------- */
678 /* reset an hashtable already initialized with ht_init().
679 * NOTE: This function should only called by ht_destroy(). */
680 static void JimResetHashTable(Jim_HashTable *ht)
681 {
682 ht->table = NULL;
683 ht->size = 0;
684 ht->sizemask = 0;
685 ht->used = 0;
686 ht->collisions = 0;
687 }
688
689 /* Initialize the hash table */
690 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
691 void *privDataPtr)
692 {
693 JimResetHashTable(ht);
694 ht->type = type;
695 ht->privdata = privDataPtr;
696 return JIM_OK;
697 }
698
699 /* Resize the table to the minimal size that contains all the elements,
700 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
701 int Jim_ResizeHashTable(Jim_HashTable *ht)
702 {
703 int minimal = ht->used;
704
705 if (minimal < JIM_HT_INITIAL_SIZE)
706 minimal = JIM_HT_INITIAL_SIZE;
707 return Jim_ExpandHashTable(ht, minimal);
708 }
709
710 /* Expand or create the hashtable */
711 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
712 {
713 Jim_HashTable n; /* the new hashtable */
714 unsigned int realsize = JimHashTableNextPower(size), i;
715
716 /* the size is invalid if it is smaller than the number of
717 * elements already inside the hashtable */
718 if (ht->used >= size)
719 return JIM_ERR;
720
721 Jim_InitHashTable(&n, ht->type, ht->privdata);
722 n.size = realsize;
723 n.sizemask = realsize-1;
724 n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
725
726 /* Initialize all the pointers to NULL */
727 memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
728
729 /* Copy all the elements from the old to the new table:
730 * note that if the old hash table is empty ht->size is zero,
731 * so Jim_ExpandHashTable just creates an hash table. */
732 n.used = ht->used;
733 for (i = 0; i < ht->size && ht->used > 0; i++) {
734 Jim_HashEntry *he, *nextHe;
735
736 if (ht->table[i] == NULL) continue;
737
738 /* For each hash entry on this slot... */
739 he = ht->table[i];
740 while(he) {
741 unsigned int h;
742
743 nextHe = he->next;
744 /* Get the new element index */
745 h = Jim_HashKey(ht, he->key) & n.sizemask;
746 he->next = n.table[h];
747 n.table[h] = he;
748 ht->used--;
749 /* Pass to the next element */
750 he = nextHe;
751 }
752 }
753 assert(ht->used == 0);
754 Jim_Free(ht->table);
755
756 /* Remap the new hashtable in the old */
757 *ht = n;
758 return JIM_OK;
759 }
760
761 /* Add an element to the target hash table */
762 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
763 {
764 int index;
765 Jim_HashEntry *entry;
766
767 /* Get the index of the new element, or -1 if
768 * the element already exists. */
769 if ((index = JimInsertHashEntry(ht, key)) == -1)
770 return JIM_ERR;
771
772 /* Allocates the memory and stores key */
773 entry = Jim_Alloc(sizeof(*entry));
774 entry->next = ht->table[index];
775 ht->table[index] = entry;
776
777 /* Set the hash entry fields. */
778 Jim_SetHashKey(ht, entry, key);
779 Jim_SetHashVal(ht, entry, val);
780 ht->used++;
781 return JIM_OK;
782 }
783
784 /* Add an element, discarding the old if the key already exists */
785 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
786 {
787 Jim_HashEntry *entry;
788
789 /* Try to add the element. If the key
790 * does not exists Jim_AddHashEntry will suceed. */
791 if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
792 return JIM_OK;
793 /* It already exists, get the entry */
794 entry = Jim_FindHashEntry(ht, key);
795 /* Free the old value and set the new one */
796 Jim_FreeEntryVal(ht, entry);
797 Jim_SetHashVal(ht, entry, val);
798 return JIM_OK;
799 }
800
801 /* Search and remove an element */
802 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
803 {
804 unsigned int h;
805 Jim_HashEntry *he, *prevHe;
806
807 if (ht->size == 0)
808 return JIM_ERR;
809 h = Jim_HashKey(ht, key) & ht->sizemask;
810 he = ht->table[h];
811
812 prevHe = NULL;
813 while(he) {
814 if (Jim_CompareHashKeys(ht, key, he->key)) {
815 /* Unlink the element from the list */
816 if (prevHe)
817 prevHe->next = he->next;
818 else
819 ht->table[h] = he->next;
820 Jim_FreeEntryKey(ht, he);
821 Jim_FreeEntryVal(ht, he);
822 Jim_Free(he);
823 ht->used--;
824 return JIM_OK;
825 }
826 prevHe = he;
827 he = he->next;
828 }
829 return JIM_ERR; /* not found */
830 }
831
832 /* Destroy an entire hash table */
833 int Jim_FreeHashTable(Jim_HashTable *ht)
834 {
835 unsigned int i;
836
837 /* Free all the elements */
838 for (i = 0; i < ht->size && ht->used > 0; i++) {
839 Jim_HashEntry *he, *nextHe;
840
841 if ((he = ht->table[i]) == NULL) continue;
842 while(he) {
843 nextHe = he->next;
844 Jim_FreeEntryKey(ht, he);
845 Jim_FreeEntryVal(ht, he);
846 Jim_Free(he);
847 ht->used--;
848 he = nextHe;
849 }
850 }
851 /* Free the table and the allocated cache structure */
852 Jim_Free(ht->table);
853 /* Re-initialize the table */
854 JimResetHashTable(ht);
855 return JIM_OK; /* never fails */
856 }
857
858 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
859 {
860 Jim_HashEntry *he;
861 unsigned int h;
862
863 if (ht->size == 0) return NULL;
864 h = Jim_HashKey(ht, key) & ht->sizemask;
865 he = ht->table[h];
866 while(he) {
867 if (Jim_CompareHashKeys(ht, key, he->key))
868 return he;
869 he = he->next;
870 }
871 return NULL;
872 }
873
874 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
875 {
876 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
877
878 iter->ht = ht;
879 iter->index = -1;
880 iter->entry = NULL;
881 iter->nextEntry = NULL;
882 return iter;
883 }
884
885 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
886 {
887 while (1) {
888 if (iter->entry == NULL) {
889 iter->index++;
890 if (iter->index >=
891 (signed)iter->ht->size) break;
892 iter->entry = iter->ht->table[iter->index];
893 } else {
894 iter->entry = iter->nextEntry;
895 }
896 if (iter->entry) {
897 /* We need to save the 'next' here, the iterator user
898 * may delete the entry we are returning. */
899 iter->nextEntry = iter->entry->next;
900 return iter->entry;
901 }
902 }
903 return NULL;
904 }
905
906 /* ------------------------- private functions ------------------------------ */
907
908 /* Expand the hash table if needed */
909 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
910 {
911 /* If the hash table is empty expand it to the intial size,
912 * if the table is "full" dobule its size. */
913 if (ht->size == 0)
914 return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
915 if (ht->size == ht->used)
916 return Jim_ExpandHashTable(ht, ht->size*2);
917 return JIM_OK;
918 }
919
920 /* Our hash table capability is a power of two */
921 static unsigned int JimHashTableNextPower(unsigned int size)
922 {
923 unsigned int i = JIM_HT_INITIAL_SIZE;
924
925 if (size >= 2147483648U)
926 return 2147483648U;
927 while(1) {
928 if (i >= size)
929 return i;
930 i *= 2;
931 }
932 }
933
934 /* Returns the index of a free slot that can be populated with
935 * an hash entry for the given 'key'.
936 * If the key already exists, -1 is returned. */
937 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
938 {
939 unsigned int h;
940 Jim_HashEntry *he;
941
942 /* Expand the hashtable if needed */
943 if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
944 return -1;
945 /* Compute the key hash value */
946 h = Jim_HashKey(ht, key) & ht->sizemask;
947 /* Search if this slot does not already contain the given key */
948 he = ht->table[h];
949 while(he) {
950 if (Jim_CompareHashKeys(ht, key, he->key))
951 return -1;
952 he = he->next;
953 }
954 return h;
955 }
956
957 /* ----------------------- StringCopy Hash Table Type ------------------------*/
958
959 static unsigned int JimStringCopyHTHashFunction(const void *key)
960 {
961 return Jim_GenHashFunction(key, strlen(key));
962 }
963
964 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
965 {
966 int len = strlen(key);
967 char *copy = Jim_Alloc(len+1);
968 JIM_NOTUSED(privdata);
969
970 memcpy(copy, key, len);
971 copy[len] = '\0';
972 return copy;
973 }
974
975 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
976 {
977 int len = strlen(val);
978 char *copy = Jim_Alloc(len+1);
979 JIM_NOTUSED(privdata);
980
981 memcpy(copy, val, len);
982 copy[len] = '\0';
983 return copy;
984 }
985
986 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
987 const void *key2)
988 {
989 JIM_NOTUSED(privdata);
990
991 return strcmp(key1, key2) == 0;
992 }
993
994 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
995 {
996 JIM_NOTUSED(privdata);
997
998 Jim_Free((void*)key); /* ATTENTION: const cast */
999 }
1000
1001 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
1002 {
1003 JIM_NOTUSED(privdata);
1004
1005 Jim_Free((void*)val); /* ATTENTION: const cast */
1006 }
1007
1008 static Jim_HashTableType JimStringCopyHashTableType = {
1009 JimStringCopyHTHashFunction, /* hash function */
1010 JimStringCopyHTKeyDup, /* key dup */
1011 NULL, /* val dup */
1012 JimStringCopyHTKeyCompare, /* key compare */
1013 JimStringCopyHTKeyDestructor, /* key destructor */
1014 NULL /* val destructor */
1015 };
1016
1017 /* This is like StringCopy but does not auto-duplicate the key.
1018 * It's used for intepreter's shared strings. */
1019 static Jim_HashTableType JimSharedStringsHashTableType = {
1020 JimStringCopyHTHashFunction, /* hash function */
1021 NULL, /* key dup */
1022 NULL, /* val dup */
1023 JimStringCopyHTKeyCompare, /* key compare */
1024 JimStringCopyHTKeyDestructor, /* key destructor */
1025 NULL /* val destructor */
1026 };
1027
1028 /* This is like StringCopy but also automatically handle dynamic
1029 * allocated C strings as values. */
1030 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
1031 JimStringCopyHTHashFunction, /* hash function */
1032 JimStringCopyHTKeyDup, /* key dup */
1033 JimStringKeyValCopyHTValDup, /* val dup */
1034 JimStringCopyHTKeyCompare, /* key compare */
1035 JimStringCopyHTKeyDestructor, /* key destructor */
1036 JimStringKeyValCopyHTValDestructor, /* val destructor */
1037 };
1038
1039 typedef struct AssocDataValue {
1040 Jim_InterpDeleteProc *delProc;
1041 void *data;
1042 } AssocDataValue;
1043
1044 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
1045 {
1046 AssocDataValue *assocPtr = (AssocDataValue *)data;
1047 if (assocPtr->delProc != NULL)
1048 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
1049 Jim_Free(data);
1050 }
1051
1052 static Jim_HashTableType JimAssocDataHashTableType = {
1053 JimStringCopyHTHashFunction, /* hash function */
1054 JimStringCopyHTKeyDup, /* key dup */
1055 NULL, /* val dup */
1056 JimStringCopyHTKeyCompare, /* key compare */
1057 JimStringCopyHTKeyDestructor, /* key destructor */
1058 JimAssocDataHashTableValueDestructor /* val destructor */
1059 };
1060
1061 /* -----------------------------------------------------------------------------
1062 * Stack - This is a simple generic stack implementation. It is used for
1063 * example in the 'expr' expression compiler.
1064 * ---------------------------------------------------------------------------*/
1065 void Jim_InitStack(Jim_Stack *stack)
1066 {
1067 stack->len = 0;
1068 stack->maxlen = 0;
1069 stack->vector = NULL;
1070 }
1071
1072 void Jim_FreeStack(Jim_Stack *stack)
1073 {
1074 Jim_Free(stack->vector);
1075 }
1076
1077 int Jim_StackLen(Jim_Stack *stack)
1078 {
1079 return stack->len;
1080 }
1081
1082 void Jim_StackPush(Jim_Stack *stack, void *element) {
1083 int neededLen = stack->len+1;
1084 if (neededLen > stack->maxlen) {
1085 stack->maxlen = neededLen*2;
1086 stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1087 }
1088 stack->vector[stack->len] = element;
1089 stack->len++;
1090 }
1091
1092 void *Jim_StackPop(Jim_Stack *stack)
1093 {
1094 if (stack->len == 0) return NULL;
1095 stack->len--;
1096 return stack->vector[stack->len];
1097 }
1098
1099 void *Jim_StackPeek(Jim_Stack *stack)
1100 {
1101 if (stack->len == 0) return NULL;
1102 return stack->vector[stack->len-1];
1103 }
1104
1105 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1106 {
1107 int i;
1108
1109 for (i = 0; i < stack->len; i++)
1110 freeFunc(stack->vector[i]);
1111 }
1112
1113 /* -----------------------------------------------------------------------------
1114 * Parser
1115 * ---------------------------------------------------------------------------*/
1116
1117 /* Token types */
1118 #define JIM_TT_NONE -1 /* No token returned */
1119 #define JIM_TT_STR 0 /* simple string */
1120 #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
1121 #define JIM_TT_VAR 2 /* var substitution */
1122 #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
1123 #define JIM_TT_CMD 4 /* command substitution */
1124 #define JIM_TT_SEP 5 /* word separator */
1125 #define JIM_TT_EOL 6 /* line separator */
1126
1127 /* Additional token types needed for expressions */
1128 #define JIM_TT_SUBEXPR_START 7
1129 #define JIM_TT_SUBEXPR_END 8
1130 #define JIM_TT_EXPR_NUMBER 9
1131 #define JIM_TT_EXPR_OPERATOR 10
1132
1133 /* Parser states */
1134 #define JIM_PS_DEF 0 /* Default state */
1135 #define JIM_PS_QUOTE 1 /* Inside "" */
1136
1137 /* Parser context structure. The same context is used both to parse
1138 * Tcl scripts and lists. */
1139 struct JimParserCtx {
1140 const char *prg; /* Program text */
1141 const char *p; /* Pointer to the point of the program we are parsing */
1142 int len; /* Left length of 'prg' */
1143 int linenr; /* Current line number */
1144 const char *tstart;
1145 const char *tend; /* Returned token is at tstart-tend in 'prg'. */
1146 int tline; /* Line number of the returned token */
1147 int tt; /* Token type */
1148 int eof; /* Non zero if EOF condition is true. */
1149 int state; /* Parser state */
1150 int comment; /* Non zero if the next chars may be a comment. */
1151 };
1152
1153 #define JimParserEof(c) ((c)->eof)
1154 #define JimParserTstart(c) ((c)->tstart)
1155 #define JimParserTend(c) ((c)->tend)
1156 #define JimParserTtype(c) ((c)->tt)
1157 #define JimParserTline(c) ((c)->tline)
1158
1159 static int JimParseScript(struct JimParserCtx *pc);
1160 static int JimParseSep(struct JimParserCtx *pc);
1161 static int JimParseEol(struct JimParserCtx *pc);
1162 static int JimParseCmd(struct JimParserCtx *pc);
1163 static int JimParseVar(struct JimParserCtx *pc);
1164 static int JimParseBrace(struct JimParserCtx *pc);
1165 static int JimParseStr(struct JimParserCtx *pc);
1166 static int JimParseComment(struct JimParserCtx *pc);
1167 static char *JimParserGetToken(struct JimParserCtx *pc,
1168 int *lenPtr, int *typePtr, int *linePtr);
1169
1170 /* Initialize a parser context.
1171 * 'prg' is a pointer to the program text, linenr is the line
1172 * number of the first line contained in the program. */
1173 void JimParserInit(struct JimParserCtx *pc, const char *prg,
1174 int len, int linenr)
1175 {
1176 pc->prg = prg;
1177 pc->p = prg;
1178 pc->len = len;
1179 pc->tstart = NULL;
1180 pc->tend = NULL;
1181 pc->tline = 0;
1182 pc->tt = JIM_TT_NONE;
1183 pc->eof = 0;
1184 pc->state = JIM_PS_DEF;
1185 pc->linenr = linenr;
1186 pc->comment = 1;
1187 }
1188
1189 int JimParseScript(struct JimParserCtx *pc)
1190 {
1191 while(1) { /* the while is used to reiterate with continue if needed */
1192 if (!pc->len) {
1193 pc->tstart = pc->p;
1194 pc->tend = pc->p-1;
1195 pc->tline = pc->linenr;
1196 pc->tt = JIM_TT_EOL;
1197 pc->eof = 1;
1198 return JIM_OK;
1199 }
1200 switch(*(pc->p)) {
1201 case '\\':
1202 if (*(pc->p+1) == '\n')
1203 return JimParseSep(pc);
1204 else {
1205 pc->comment = 0;
1206 return JimParseStr(pc);
1207 }
1208 break;
1209 case ' ':
1210 case '\t':
1211 case '\r':
1212 if (pc->state == JIM_PS_DEF)
1213 return JimParseSep(pc);
1214 else {
1215 pc->comment = 0;
1216 return JimParseStr(pc);
1217 }
1218 break;
1219 case '\n':
1220 case ';':
1221 pc->comment = 1;
1222 if (pc->state == JIM_PS_DEF)
1223 return JimParseEol(pc);
1224 else
1225 return JimParseStr(pc);
1226 break;
1227 case '[':
1228 pc->comment = 0;
1229 return JimParseCmd(pc);
1230 break;
1231 case '$':
1232 pc->comment = 0;
1233 if (JimParseVar(pc) == JIM_ERR) {
1234 pc->tstart = pc->tend = pc->p++; pc->len--;
1235 pc->tline = pc->linenr;
1236 pc->tt = JIM_TT_STR;
1237 return JIM_OK;
1238 } else
1239 return JIM_OK;
1240 break;
1241 case '#':
1242 if (pc->comment) {
1243 JimParseComment(pc);
1244 continue;
1245 } else {
1246 return JimParseStr(pc);
1247 }
1248 default:
1249 pc->comment = 0;
1250 return JimParseStr(pc);
1251 break;
1252 }
1253 return JIM_OK;
1254 }
1255 }
1256
1257 int JimParseSep(struct JimParserCtx *pc)
1258 {
1259 pc->tstart = pc->p;
1260 pc->tline = pc->linenr;
1261 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1262 (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1263 if (*pc->p == '\\') {
1264 pc->p++; pc->len--;
1265 pc->linenr++;
1266 }
1267 pc->p++; pc->len--;
1268 }
1269 pc->tend = pc->p-1;
1270 pc->tt = JIM_TT_SEP;
1271 return JIM_OK;
1272 }
1273
1274 int JimParseEol(struct JimParserCtx *pc)
1275 {
1276 pc->tstart = pc->p;
1277 pc->tline = pc->linenr;
1278 while (*pc->p == ' ' || *pc->p == '\n' ||
1279 *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1280 if (*pc->p == '\n')
1281 pc->linenr++;
1282 pc->p++; pc->len--;
1283 }
1284 pc->tend = pc->p-1;
1285 pc->tt = JIM_TT_EOL;
1286 return JIM_OK;
1287 }
1288
1289 /* Todo. Don't stop if ']' appears inside {} or quoted.
1290 * Also should handle the case of puts [string length "]"] */
1291 int JimParseCmd(struct JimParserCtx *pc)
1292 {
1293 int level = 1;
1294 int blevel = 0;
1295
1296 pc->tstart = ++pc->p; pc->len--;
1297 pc->tline = pc->linenr;
1298 while (1) {
1299 if (pc->len == 0) {
1300 break;
1301 } else if (*pc->p == '[' && blevel == 0) {
1302 level++;
1303 } else if (*pc->p == ']' && blevel == 0) {
1304 level--;
1305 if (!level) break;
1306 } else if (*pc->p == '\\') {
1307 pc->p++; pc->len--;
1308 } else if (*pc->p == '{') {
1309 blevel++;
1310 } else if (*pc->p == '}') {
1311 if (blevel != 0)
1312 blevel--;
1313 } else if (*pc->p == '\n')
1314 pc->linenr++;
1315 pc->p++; pc->len--;
1316 }
1317 pc->tend = pc->p-1;
1318 pc->tt = JIM_TT_CMD;
1319 if (*pc->p == ']') {
1320 pc->p++; pc->len--;
1321 }
1322 return JIM_OK;
1323 }
1324
1325 int JimParseVar(struct JimParserCtx *pc)
1326 {
1327 int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1328
1329 pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1330 pc->tline = pc->linenr;
1331 if (*pc->p == '{') {
1332 pc->tstart = ++pc->p; pc->len--;
1333 brace = 1;
1334 }
1335 if (brace) {
1336 while (!stop) {
1337 if (*pc->p == '}' || pc->len == 0) {
1338 pc->tend = pc->p-1;
1339 stop = 1;
1340 if (pc->len == 0)
1341 break;
1342 }
1343 else if (*pc->p == '\n')
1344 pc->linenr++;
1345 pc->p++; pc->len--;
1346 }
1347 } else {
1348 /* Include leading colons */
1349 while (*pc->p == ':') {
1350 pc->p++;
1351 pc->len--;
1352 }
1353 while (!stop) {
1354 if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1355 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1356 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1357 stop = 1;
1358 else {
1359 pc->p++; pc->len--;
1360 }
1361 }
1362 /* Parse [dict get] syntax sugar. */
1363 if (*pc->p == '(') {
1364 while (*pc->p != ')' && pc->len) {
1365 pc->p++; pc->len--;
1366 if (*pc->p == '\\' && pc->len >= 2) {
1367 pc->p += 2; pc->len -= 2;
1368 }
1369 }
1370 if (*pc->p != '\0') {
1371 pc->p++; pc->len--;
1372 }
1373 ttype = JIM_TT_DICTSUGAR;
1374 }
1375 pc->tend = pc->p-1;
1376 }
1377 /* Check if we parsed just the '$' character.
1378 * That's not a variable so an error is returned
1379 * to tell the state machine to consider this '$' just
1380 * a string. */
1381 if (pc->tstart == pc->p) {
1382 pc->p--; pc->len++;
1383 return JIM_ERR;
1384 }
1385 pc->tt = ttype;
1386 return JIM_OK;
1387 }
1388
1389 int JimParseBrace(struct JimParserCtx *pc)
1390 {
1391 int level = 1;
1392
1393 pc->tstart = ++pc->p; pc->len--;
1394 pc->tline = pc->linenr;
1395 while (1) {
1396 if (*pc->p == '\\' && pc->len >= 2) {
1397 pc->p++; pc->len--;
1398 if (*pc->p == '\n')
1399 pc->linenr++;
1400 } else if (*pc->p == '{') {
1401 level++;
1402 } else if (pc->len == 0 || *pc->p == '}') {
1403 level--;
1404 if (pc->len == 0 || level == 0) {
1405 pc->tend = pc->p-1;
1406 if (pc->len != 0) {
1407 pc->p++; pc->len--;
1408 }
1409 pc->tt = JIM_TT_STR;
1410 return JIM_OK;
1411 }
1412 } else if (*pc->p == '\n') {
1413 pc->linenr++;
1414 }
1415 pc->p++; pc->len--;
1416 }
1417 return JIM_OK; /* unreached */
1418 }
1419
1420 int JimParseStr(struct JimParserCtx *pc)
1421 {
1422 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1423 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1424 if (newword && *pc->p == '{') {
1425 return JimParseBrace(pc);
1426 } else if (newword && *pc->p == '"') {
1427 pc->state = JIM_PS_QUOTE;
1428 pc->p++; pc->len--;
1429 }
1430 pc->tstart = pc->p;
1431 pc->tline = pc->linenr;
1432 while (1) {
1433 if (pc->len == 0) {
1434 pc->tend = pc->p-1;
1435 pc->tt = JIM_TT_ESC;
1436 return JIM_OK;
1437 }
1438 switch(*pc->p) {
1439 case '\\':
1440 if (pc->state == JIM_PS_DEF &&
1441 *(pc->p+1) == '\n') {
1442 pc->tend = pc->p-1;
1443 pc->tt = JIM_TT_ESC;
1444 return JIM_OK;
1445 }
1446 if (pc->len >= 2) {
1447 pc->p++; pc->len--;
1448 }
1449 break;
1450 case '$':
1451 case '[':
1452 pc->tend = pc->p-1;
1453 pc->tt = JIM_TT_ESC;
1454 return JIM_OK;
1455 case ' ':
1456 case '\t':
1457 case '\n':
1458 case '\r':
1459 case ';':
1460 if (pc->state == JIM_PS_DEF) {
1461 pc->tend = pc->p-1;
1462 pc->tt = JIM_TT_ESC;
1463 return JIM_OK;
1464 } else if (*pc->p == '\n') {
1465 pc->linenr++;
1466 }
1467 break;
1468 case '"':
1469 if (pc->state == JIM_PS_QUOTE) {
1470 pc->tend = pc->p-1;
1471 pc->tt = JIM_TT_ESC;
1472 pc->p++; pc->len--;
1473 pc->state = JIM_PS_DEF;
1474 return JIM_OK;
1475 }
1476 break;
1477 }
1478 pc->p++; pc->len--;
1479 }
1480 return JIM_OK; /* unreached */
1481 }
1482
1483 int JimParseComment(struct JimParserCtx *pc)
1484 {
1485 while (*pc->p) {
1486 if (*pc->p == '\n') {
1487 pc->linenr++;
1488 if (*(pc->p-1) != '\\') {
1489 pc->p++; pc->len--;
1490 return JIM_OK;
1491 }
1492 }
1493 pc->p++; pc->len--;
1494 }
1495 return JIM_OK;
1496 }
1497
1498 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1499 static int xdigitval(int c)
1500 {
1501 if (c >= '0' && c <= '9') return c-'0';
1502 if (c >= 'a' && c <= 'f') return c-'a'+10;
1503 if (c >= 'A' && c <= 'F') return c-'A'+10;
1504 return -1;
1505 }
1506
1507 static int odigitval(int c)
1508 {
1509 if (c >= '0' && c <= '7') return c-'0';
1510 return -1;
1511 }
1512
1513 /* Perform Tcl escape substitution of 's', storing the result
1514 * string into 'dest'. The escaped string is guaranteed to
1515 * be the same length or shorted than the source string.
1516 * Slen is the length of the string at 's', if it's -1 the string
1517 * length will be calculated by the function.
1518 *
1519 * The function returns the length of the resulting string. */
1520 static int JimEscape(char *dest, const char *s, int slen)
1521 {
1522 char *p = dest;
1523 int i, len;
1524
1525 if (slen == -1)
1526 slen = strlen(s);
1527
1528 for (i = 0; i < slen; i++) {
1529 switch(s[i]) {
1530 case '\\':
1531 switch(s[i+1]) {
1532 case 'a': *p++ = 0x7; i++; break;
1533 case 'b': *p++ = 0x8; i++; break;
1534 case 'f': *p++ = 0xc; i++; break;
1535 case 'n': *p++ = 0xa; i++; break;
1536 case 'r': *p++ = 0xd; i++; break;
1537 case 't': *p++ = 0x9; i++; break;
1538 case 'v': *p++ = 0xb; i++; break;
1539 case '\0': *p++ = '\\'; i++; break;
1540 case '\n': *p++ = ' '; i++; break;
1541 default:
1542 if (s[i+1] == 'x') {
1543 int val = 0;
1544 int c = xdigitval(s[i+2]);
1545 if (c == -1) {
1546 *p++ = 'x';
1547 i++;
1548 break;
1549 }
1550 val = c;
1551 c = xdigitval(s[i+3]);
1552 if (c == -1) {
1553 *p++ = val;
1554 i += 2;
1555 break;
1556 }
1557 val = (val*16)+c;
1558 *p++ = val;
1559 i += 3;
1560 break;
1561 } else if (s[i+1] >= '0' && s[i+1] <= '7')
1562 {
1563 int val = 0;
1564 int c = odigitval(s[i+1]);
1565 val = c;
1566 c = odigitval(s[i+2]);
1567 if (c == -1) {
1568 *p++ = val;
1569 i ++;
1570 break;
1571 }
1572 val = (val*8)+c;
1573 c = odigitval(s[i+3]);
1574 if (c == -1) {
1575 *p++ = val;
1576 i += 2;
1577 break;
1578 }
1579 val = (val*8)+c;
1580 *p++ = val;
1581 i += 3;
1582 } else {
1583 *p++ = s[i+1];
1584 i++;
1585 }
1586 break;
1587 }
1588 break;
1589 default:
1590 *p++ = s[i];
1591 break;
1592 }
1593 }
1594 len = p-dest;
1595 *p++ = '\0';
1596 return len;
1597 }
1598
1599 /* Returns a dynamically allocated copy of the current token in the
1600 * parser context. The function perform conversion of escapes if
1601 * the token is of type JIM_TT_ESC.
1602 *
1603 * Note that after the conversion, tokens that are grouped with
1604 * braces in the source code, are always recognizable from the
1605 * identical string obtained in a different way from the type.
1606 *
1607 * For exmple the string:
1608 *
1609 * {expand}$a
1610 *
1611 * will return as first token "expand", of type JIM_TT_STR
1612 *
1613 * While the string:
1614 *
1615 * expand$a
1616 *
1617 * will return as first token "expand", of type JIM_TT_ESC
1618 */
1619 char *JimParserGetToken(struct JimParserCtx *pc,
1620 int *lenPtr, int *typePtr, int *linePtr)
1621 {
1622 const char *start, *end;
1623 char *token;
1624 int len;
1625
1626 start = JimParserTstart(pc);
1627 end = JimParserTend(pc);
1628 if (start > end) {
1629 if (lenPtr) *lenPtr = 0;
1630 if (typePtr) *typePtr = JimParserTtype(pc);
1631 if (linePtr) *linePtr = JimParserTline(pc);
1632 token = Jim_Alloc(1);
1633 token[0] = '\0';
1634 return token;
1635 }
1636 len = (end-start)+1;
1637 token = Jim_Alloc(len+1);
1638 if (JimParserTtype(pc) != JIM_TT_ESC) {
1639 /* No escape conversion needed? Just copy it. */
1640 memcpy(token, start, len);
1641 token[len] = '\0';
1642 } else {
1643 /* Else convert the escape chars. */
1644 len = JimEscape(token, start, len);
1645 }
1646 if (lenPtr) *lenPtr = len;
1647 if (typePtr) *typePtr = JimParserTtype(pc);
1648 if (linePtr) *linePtr = JimParserTline(pc);
1649 return token;
1650 }
1651
1652 /* The following functin is not really part of the parsing engine of Jim,
1653 * but it somewhat related. Given an string and its length, it tries
1654 * to guess if the script is complete or there are instead " " or { }
1655 * open and not completed. This is useful for interactive shells
1656 * implementation and for [info complete].
1657 *
1658 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1659 * '{' on scripts incomplete missing one or more '}' to be balanced.
1660 * '"' on scripts incomplete missing a '"' char.
1661 *
1662 * If the script is complete, 1 is returned, otherwise 0. */
1663 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1664 {
1665 int level = 0;
1666 int state = ' ';
1667
1668 while(len) {
1669 switch (*s) {
1670 case '\\':
1671 if (len > 1)
1672 s++;
1673 break;
1674 case '"':
1675 if (state == ' ') {
1676 state = '"';
1677 } else if (state == '"') {
1678 state = ' ';
1679 }
1680 break;
1681 case '{':
1682 if (state == '{') {
1683 level++;
1684 } else if (state == ' ') {
1685 state = '{';
1686 level++;
1687 }
1688 break;
1689 case '}':
1690 if (state == '{') {
1691 level--;
1692 if (level == 0)
1693 state = ' ';
1694 }
1695 break;
1696 }
1697 s++;
1698 len--;
1699 }
1700 if (stateCharPtr)
1701 *stateCharPtr = state;
1702 return state == ' ';
1703 }
1704
1705 /* -----------------------------------------------------------------------------
1706 * Tcl Lists parsing
1707 * ---------------------------------------------------------------------------*/
1708 static int JimParseListSep(struct JimParserCtx *pc);
1709 static int JimParseListStr(struct JimParserCtx *pc);
1710
1711 int JimParseList(struct JimParserCtx *pc)
1712 {
1713 if (pc->len == 0) {
1714 pc->tstart = pc->tend = pc->p;
1715 pc->tline = pc->linenr;
1716 pc->tt = JIM_TT_EOL;
1717 pc->eof = 1;
1718 return JIM_OK;
1719 }
1720 switch(*pc->p) {
1721 case ' ':
1722 case '\n':
1723 case '\t':
1724 case '\r':
1725 if (pc->state == JIM_PS_DEF)
1726 return JimParseListSep(pc);
1727 else
1728 return JimParseListStr(pc);
1729 break;
1730 default:
1731 return JimParseListStr(pc);
1732 break;
1733 }
1734 return JIM_OK;
1735 }
1736
1737 int JimParseListSep(struct JimParserCtx *pc)
1738 {
1739 pc->tstart = pc->p;
1740 pc->tline = pc->linenr;
1741 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1742 {
1743 pc->p++; pc->len--;
1744 }
1745 pc->tend = pc->p-1;
1746 pc->tt = JIM_TT_SEP;
1747 return JIM_OK;
1748 }
1749
1750 int JimParseListStr(struct JimParserCtx *pc)
1751 {
1752 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1753 pc->tt == JIM_TT_NONE);
1754 if (newword && *pc->p == '{') {
1755 return JimParseBrace(pc);
1756 } else if (newword && *pc->p == '"') {
1757 pc->state = JIM_PS_QUOTE;
1758 pc->p++; pc->len--;
1759 }
1760 pc->tstart = pc->p;
1761 pc->tline = pc->linenr;
1762 while (1) {
1763 if (pc->len == 0) {
1764 pc->tend = pc->p-1;
1765 pc->tt = JIM_TT_ESC;
1766 return JIM_OK;
1767 }
1768 switch(*pc->p) {
1769 case '\\':
1770 pc->p++; pc->len--;
1771 break;
1772 case ' ':
1773 case '\t':
1774 case '\n':
1775 case '\r':
1776 if (pc->state == JIM_PS_DEF) {
1777 pc->tend = pc->p-1;
1778 pc->tt = JIM_TT_ESC;
1779 return JIM_OK;
1780 } else if (*pc->p == '\n') {
1781 pc->linenr++;
1782 }
1783 break;
1784 case '"':
1785 if (pc->state == JIM_PS_QUOTE) {
1786 pc->tend = pc->p-1;
1787 pc->tt = JIM_TT_ESC;
1788 pc->p++; pc->len--;
1789 pc->state = JIM_PS_DEF;
1790 return JIM_OK;
1791 }
1792 break;
1793 }
1794 pc->p++; pc->len--;
1795 }
1796 return JIM_OK; /* unreached */
1797 }
1798
1799 /* -----------------------------------------------------------------------------
1800 * Jim_Obj related functions
1801 * ---------------------------------------------------------------------------*/
1802
1803 /* Return a new initialized object. */
1804 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1805 {
1806 Jim_Obj *objPtr;
1807
1808 /* -- Check if there are objects in the free list -- */
1809 if (interp->freeList != NULL) {
1810 /* -- Unlink the object from the free list -- */
1811 objPtr = interp->freeList;
1812 interp->freeList = objPtr->nextObjPtr;
1813 } else {
1814 /* -- No ready to use objects: allocate a new one -- */
1815 objPtr = Jim_Alloc(sizeof(*objPtr));
1816 }
1817
1818 /* Object is returned with refCount of 0. Every
1819 * kind of GC implemented should take care to don't try
1820 * to scan objects with refCount == 0. */
1821 objPtr->refCount = 0;
1822 /* All the other fields are left not initialized to save time.
1823 * The caller will probably want set they to the right
1824 * value anyway. */
1825
1826 /* -- Put the object into the live list -- */
1827 objPtr->prevObjPtr = NULL;
1828 objPtr->nextObjPtr = interp->liveList;
1829 if (interp->liveList)
1830 interp->liveList->prevObjPtr = objPtr;
1831 interp->liveList = objPtr;
1832
1833 return objPtr;
1834 }
1835
1836 /* Free an object. Actually objects are never freed, but
1837 * just moved to the free objects list, where they will be
1838 * reused by Jim_NewObj(). */
1839 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1840 {
1841 /* Check if the object was already freed, panic. */
1842 if (objPtr->refCount != 0) {
1843 Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1844 objPtr->refCount);
1845 }
1846 /* Free the internal representation */
1847 Jim_FreeIntRep(interp, objPtr);
1848 /* Free the string representation */
1849 if (objPtr->bytes != NULL) {
1850 if (objPtr->bytes != JimEmptyStringRep)
1851 Jim_Free(objPtr->bytes);
1852 }
1853 /* Unlink the object from the live objects list */
1854 if (objPtr->prevObjPtr)
1855 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1856 if (objPtr->nextObjPtr)
1857 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1858 if (interp->liveList == objPtr)
1859 interp->liveList = objPtr->nextObjPtr;
1860 /* Link the object into the free objects list */
1861 objPtr->prevObjPtr = NULL;
1862 objPtr->nextObjPtr = interp->freeList;
1863 if (interp->freeList)
1864 interp->freeList->prevObjPtr = objPtr;
1865 interp->freeList = objPtr;
1866 objPtr->refCount = -1;
1867 }
1868
1869 /* Invalidate the string representation of an object. */
1870 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1871 {
1872 if (objPtr->bytes != NULL) {
1873 if (objPtr->bytes != JimEmptyStringRep)
1874 Jim_Free(objPtr->bytes);
1875 }
1876 objPtr->bytes = NULL;
1877 }
1878
1879 #define Jim_SetStringRep(o, b, l) \
1880 do { (o)->bytes = b; (o)->length = l; } while (0)
1881
1882 /* Set the initial string representation for an object.
1883 * Does not try to free an old one. */
1884 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1885 {
1886 if (length == 0) {
1887 objPtr->bytes = JimEmptyStringRep;
1888 objPtr->length = 0;
1889 } else {
1890 objPtr->bytes = Jim_Alloc(length+1);
1891 objPtr->length = length;
1892 memcpy(objPtr->bytes, bytes, length);
1893 objPtr->bytes[length] = '\0';
1894 }
1895 }
1896
1897 /* Duplicate an object. The returned object has refcount = 0. */
1898 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1899 {
1900 Jim_Obj *dupPtr;
1901
1902 dupPtr = Jim_NewObj(interp);
1903 if (objPtr->bytes == NULL) {
1904 /* Object does not have a valid string representation. */
1905 dupPtr->bytes = NULL;
1906 } else {
1907 Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1908 }
1909 if (objPtr->typePtr != NULL) {
1910 if (objPtr->typePtr->dupIntRepProc == NULL) {
1911 dupPtr->internalRep = objPtr->internalRep;
1912 } else {
1913 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1914 }
1915 dupPtr->typePtr = objPtr->typePtr;
1916 } else {
1917 dupPtr->typePtr = NULL;
1918 }
1919 return dupPtr;
1920 }
1921
1922 /* Return the string representation for objPtr. If the object
1923 * string representation is invalid, calls the method to create
1924 * a new one starting from the internal representation of the object. */
1925 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1926 {
1927 if (objPtr->bytes == NULL) {
1928 /* Invalid string repr. Generate it. */
1929 if (objPtr->typePtr->updateStringProc == NULL) {
1930 Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1931 objPtr->typePtr->name);
1932 }
1933 objPtr->typePtr->updateStringProc(objPtr);
1934 }
1935 if (lenPtr)
1936 *lenPtr = objPtr->length;
1937 return objPtr->bytes;
1938 }
1939
1940 /* Just returns the length of the object's string rep */
1941 int Jim_Length(Jim_Obj *objPtr)
1942 {
1943 int len;
1944
1945 Jim_GetString(objPtr, &len);
1946 return len;
1947 }
1948
1949 /* -----------------------------------------------------------------------------
1950 * String Object
1951 * ---------------------------------------------------------------------------*/
1952 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1953 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1954
1955 static Jim_ObjType stringObjType = {
1956 "string",
1957 NULL,
1958 DupStringInternalRep,
1959 NULL,
1960 JIM_TYPE_REFERENCES,
1961 };
1962
1963 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1964 {
1965 JIM_NOTUSED(interp);
1966
1967 /* This is a bit subtle: the only caller of this function
1968 * should be Jim_DuplicateObj(), that will copy the
1969 * string representaion. After the copy, the duplicated
1970 * object will not have more room in teh buffer than
1971 * srcPtr->length bytes. So we just set it to length. */
1972 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1973 }
1974
1975 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1976 {
1977 /* Get a fresh string representation. */
1978 (void) Jim_GetString(objPtr, NULL);
1979 /* Free any other internal representation. */
1980 Jim_FreeIntRep(interp, objPtr);
1981 /* Set it as string, i.e. just set the maxLength field. */
1982 objPtr->typePtr = &stringObjType;
1983 objPtr->internalRep.strValue.maxLength = objPtr->length;
1984 return JIM_OK;
1985 }
1986
1987 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1988 {
1989 Jim_Obj *objPtr = Jim_NewObj(interp);
1990
1991 if (len == -1)
1992 len = strlen(s);
1993 /* Alloc/Set the string rep. */
1994 if (len == 0) {
1995 objPtr->bytes = JimEmptyStringRep;
1996 objPtr->length = 0;
1997 } else {
1998 objPtr->bytes = Jim_Alloc(len+1);
1999 objPtr->length = len;
2000 memcpy(objPtr->bytes, s, len);
2001 objPtr->bytes[len] = '\0';
2002 }
2003
2004 /* No typePtr field for the vanilla string object. */
2005 objPtr->typePtr = NULL;
2006 return objPtr;
2007 }
2008
2009 /* This version does not try to duplicate the 's' pointer, but
2010 * use it directly. */
2011 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
2012 {
2013 Jim_Obj *objPtr = Jim_NewObj(interp);
2014
2015 if (len == -1)
2016 len = strlen(s);
2017 Jim_SetStringRep(objPtr, s, len);
2018 objPtr->typePtr = NULL;
2019 return objPtr;
2020 }
2021
2022 /* Low-level string append. Use it only against objects
2023 * of type "string". */
2024 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
2025 {
2026 int needlen;
2027
2028 if (len == -1)
2029 len = strlen(str);
2030 needlen = objPtr->length + len;
2031 if (objPtr->internalRep.strValue.maxLength < needlen ||
2032 objPtr->internalRep.strValue.maxLength == 0) {
2033 if (objPtr->bytes == JimEmptyStringRep) {
2034 objPtr->bytes = Jim_Alloc((needlen*2)+1);
2035 } else {
2036 objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
2037 }
2038 objPtr->internalRep.strValue.maxLength = needlen*2;
2039 }
2040 memcpy(objPtr->bytes + objPtr->length, str, len);
2041 objPtr->bytes[objPtr->length+len] = '\0';
2042 objPtr->length += len;
2043 }
2044
2045 /* Low-level wrapper to append an object. */
2046 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
2047 {
2048 int len;
2049 const char *str;
2050
2051 str = Jim_GetString(appendObjPtr, &len);
2052 StringAppendString(objPtr, str, len);
2053 }
2054
2055 /* Higher level API to append strings to objects. */
2056 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
2057 int len)
2058 {
2059 if (Jim_IsShared(objPtr))
2060 Jim_Panic(interp,"Jim_AppendString called with shared object");
2061 if (objPtr->typePtr != &stringObjType)
2062 SetStringFromAny(interp, objPtr);
2063 StringAppendString(objPtr, str, len);
2064 }
2065
2066 void Jim_AppendString_sprintf( Jim_Interp *interp, Jim_Obj *objPtr, const char *fmt, ... )
2067 {
2068 char *buf;
2069 va_list ap;
2070
2071 va_start( ap, fmt );
2072 buf = jim_vasprintf( fmt, ap );
2073 va_end(ap);
2074
2075 if( buf ){
2076 Jim_AppendString( interp, objPtr, buf, -1 );
2077 jim_vasprintf_done(buf);
2078 }
2079 }
2080
2081
2082 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
2083 Jim_Obj *appendObjPtr)
2084 {
2085 int len;
2086 const char *str;
2087
2088 str = Jim_GetString(appendObjPtr, &len);
2089 Jim_AppendString(interp, objPtr, str, len);
2090 }
2091
2092 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2093 {
2094 va_list ap;
2095
2096 if (objPtr->typePtr != &stringObjType)
2097 SetStringFromAny(interp, objPtr);
2098 va_start(ap, objPtr);
2099 while (1) {
2100 char *s = va_arg(ap, char*);
2101
2102 if (s == NULL) break;
2103 Jim_AppendString(interp, objPtr, s, -1);
2104 }
2105 va_end(ap);
2106 }
2107
2108 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2109 {
2110 const char *aStr, *bStr;
2111 int aLen, bLen, i;
2112
2113 if (aObjPtr == bObjPtr) return 1;
2114 aStr = Jim_GetString(aObjPtr, &aLen);
2115 bStr = Jim_GetString(bObjPtr, &bLen);
2116 if (aLen != bLen) return 0;
2117 if (nocase == 0)
2118 return memcmp(aStr, bStr, aLen) == 0;
2119 for (i = 0; i < aLen; i++) {
2120 if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2121 return 0;
2122 }
2123 return 1;
2124 }
2125
2126 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2127 int nocase)
2128 {
2129 const char *pattern, *string;
2130 int patternLen, stringLen;
2131
2132 pattern = Jim_GetString(patternObjPtr, &patternLen);
2133 string = Jim_GetString(objPtr, &stringLen);
2134 return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2135 }
2136
2137 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2138 Jim_Obj *secondObjPtr, int nocase)
2139 {
2140 const char *s1, *s2;
2141 int l1, l2;
2142
2143 s1 = Jim_GetString(firstObjPtr, &l1);
2144 s2 = Jim_GetString(secondObjPtr, &l2);
2145 return JimStringCompare(s1, l1, s2, l2, nocase);
2146 }
2147
2148 /* Convert a range, as returned by Jim_GetRange(), into
2149 * an absolute index into an object of the specified length.
2150 * This function may return negative values, or values
2151 * bigger or equal to the length of the list if the index
2152 * is out of range. */
2153 static int JimRelToAbsIndex(int len, int index)
2154 {
2155 if (index < 0)
2156 return len + index;
2157 return index;
2158 }
2159
2160 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2161 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2162 * for implementation of commands like [string range] and [lrange].
2163 *
2164 * The resulting range is guaranteed to address valid elements of
2165 * the structure. */
2166 static void JimRelToAbsRange(int len, int first, int last,
2167 int *firstPtr, int *lastPtr, int *rangeLenPtr)
2168 {
2169 int rangeLen;
2170
2171 if (first > last) {
2172 rangeLen = 0;
2173 } else {
2174 rangeLen = last-first+1;
2175 if (rangeLen) {
2176 if (first < 0) {
2177 rangeLen += first;
2178 first = 0;
2179 }
2180 if (last >= len) {
2181 rangeLen -= (last-(len-1));
2182 last = len-1;
2183 }
2184 }
2185 }
2186 if (rangeLen < 0) rangeLen = 0;
2187
2188 *firstPtr = first;
2189 *lastPtr = last;
2190 *rangeLenPtr = rangeLen;
2191 }
2192
2193 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2194 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2195 {
2196 int first, last;
2197 const char *str;
2198 int len, rangeLen;
2199
2200 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2201 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2202 return NULL;
2203 str = Jim_GetString(strObjPtr, &len);
2204 first = JimRelToAbsIndex(len, first);
2205 last = JimRelToAbsIndex(len, last);
2206 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2207 return Jim_NewStringObj(interp, str+first, rangeLen);
2208 }
2209
2210 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2211 {
2212 char *buf;
2213 int i;
2214 if (strObjPtr->typePtr != &stringObjType) {
2215 SetStringFromAny(interp, strObjPtr);
2216 }
2217
2218 buf = Jim_Alloc(strObjPtr->length+1);
2219
2220 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2221 for (i = 0; i < strObjPtr->length; i++)
2222 buf[i] = tolower(buf[i]);
2223 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2224 }
2225
2226 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2227 {
2228 char *buf;
2229 int i;
2230 if (strObjPtr->typePtr != &stringObjType) {
2231 SetStringFromAny(interp, strObjPtr);
2232 }
2233
2234 buf = Jim_Alloc(strObjPtr->length+1);
2235
2236 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2237 for (i = 0; i < strObjPtr->length; i++)
2238 buf[i] = toupper(buf[i]);
2239 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2240 }
2241
2242 /* This is the core of the [format] command.
2243 * TODO: Lots of things work - via a hack
2244 * However, no format item can be >= JIM_MAX_FMT
2245 */
2246 #define JIM_MAX_FMT 2048
2247 static Jim_Obj *Jim_FormatString_Inner(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2248 int objc, Jim_Obj *const *objv, char *sprintf_buf)
2249 {
2250 const char *fmt, *_fmt;
2251 int fmtLen;
2252 Jim_Obj *resObjPtr;
2253
2254
2255 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2256 _fmt = fmt;
2257 resObjPtr = Jim_NewStringObj(interp, "", 0);
2258 while (fmtLen) {
2259 const char *p = fmt;
2260 char spec[2], c;
2261 jim_wide wideValue;
2262 double doubleValue;
2263 /* we cheat and use Sprintf()! */
2264 char fmt_str[100];
2265 char *cp;
2266 int width;
2267 int ljust;
2268 int zpad;
2269 int spad;
2270 int altfm;
2271 int forceplus;
2272 int prec;
2273 int inprec;
2274 int haveprec;
2275 int accum;
2276
2277 while (*fmt != '%' && fmtLen) {
2278 fmt++; fmtLen--;
2279 }
2280 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2281 if (fmtLen == 0)
2282 break;
2283 fmt++; fmtLen--; /* skip '%' */
2284 zpad = 0;
2285 spad = 0;
2286 width = -1;
2287 ljust = 0;
2288 altfm = 0;
2289 forceplus = 0;
2290 inprec = 0;
2291 haveprec = 0;
2292 prec = -1; /* not found yet */
2293 next_fmt:
2294 if( fmtLen <= 0 ){
2295 break;
2296 }
2297 switch( *fmt ){
2298 /* terminals */
2299 case 'b': /* binary - not all printfs() do this */
2300 case 's': /* string */
2301 case 'i': /* integer */
2302 case 'd': /* decimal */
2303 case 'x': /* hex */
2304 case 'X': /* CAP hex */
2305 case 'c': /* char */
2306 case 'o': /* octal */
2307 case 'u': /* unsigned */
2308 case 'f': /* float */
2309 break;
2310
2311 /* non-terminals */
2312 case '0': /* zero pad */
2313 zpad = 1;
2314 fmt++; fmtLen--;
2315 goto next_fmt;
2316 break;
2317 case '+':
2318 forceplus = 1;
2319 fmt++; fmtLen--;
2320 goto next_fmt;
2321 break;
2322 case ' ': /* sign space */
2323 spad = 1;
2324 fmt++; fmtLen--;
2325 goto next_fmt;
2326 break;
2327 case '-':
2328 ljust = 1;
2329 fmt++; fmtLen--;
2330 goto next_fmt;
2331 break;
2332 case '#':
2333 altfm = 1;
2334 fmt++; fmtLen--;
2335 goto next_fmt;
2336
2337 case '.':
2338 inprec = 1;
2339 fmt++; fmtLen--;
2340 goto next_fmt;
2341 break;
2342 case '1':
2343 case '2':
2344 case '3':
2345 case '4':
2346 case '5':
2347 case '6':
2348 case '7':
2349 case '8':
2350 case '9':
2351 accum = 0;
2352 while( isdigit(*fmt) && (fmtLen > 0) ){
2353 accum = (accum * 10) + (*fmt - '0');
2354 fmt++; fmtLen--;
2355 }
2356 if( inprec ){
2357 haveprec = 1;
2358 prec = accum;
2359 } else {
2360 width = accum;
2361 }
2362 goto next_fmt;
2363 case '*':
2364 /* suck up the next item as an integer */
2365 fmt++; fmtLen--;
2366 objc--;
2367 if( objc <= 0 ){
2368 goto not_enough_args;
2369 }
2370 if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2371 Jim_FreeNewObj(interp, resObjPtr );
2372 return NULL;
2373 }
2374 if( inprec ){
2375 haveprec = 1;
2376 prec = wideValue;
2377 if( prec < 0 ){
2378 /* man 3 printf says */
2379 /* if prec is negative, it is zero */
2380 prec = 0;
2381 }
2382 } else {
2383 width = wideValue;
2384 if( width < 0 ){
2385 ljust = 1;
2386 width = -width;
2387 }
2388 }
2389 objv++;
2390 goto next_fmt;
2391 break;
2392 }
2393
2394
2395 if (*fmt != '%') {
2396 if (objc == 0) {
2397 not_enough_args:
2398 Jim_FreeNewObj(interp, resObjPtr);
2399 Jim_SetResultString(interp,
2400 "not enough arguments for all format specifiers", -1);
2401 return NULL;
2402 } else {
2403 objc--;
2404 }
2405 }
2406
2407 /*
2408 * Create the formatter
2409 * cause we cheat and use sprintf()
2410 */
2411 cp = fmt_str;
2412 *cp++ = '%';
2413 if( altfm ){
2414 *cp++ = '#';
2415 }
2416 if( forceplus ){
2417 *cp++ = '+';
2418 } else if( spad ){
2419 /* PLUS overrides */
2420 *cp++ = ' ';
2421 }
2422 if( ljust ){
2423 *cp++ = '-';
2424 }
2425 if( zpad ){
2426 *cp++ = '0';
2427 }
2428 if( width > 0 ){
2429 sprintf( cp, "%d", width );
2430 /* skip ahead */
2431 cp = strchr(cp,0);
2432 }
2433 /* did we find a period? */
2434 if( inprec ){
2435 /* then add it */
2436 *cp++ = '.';
2437 /* did something occur after the period? */
2438 if( haveprec ){
2439 sprintf( cp, "%d", prec );
2440 }
2441 cp = strchr(cp,0);
2442 }
2443 *cp = 0;
2444
2445 /* here we do the work */
2446 /* actually - we make sprintf() do it for us */
2447 switch(*fmt) {
2448 case 's':
2449 *cp++ = 's';
2450 *cp = 0;
2451 /* BUG: we do not handled embeded NULLs */
2452 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2453 break;
2454 case 'c':
2455 *cp++ = 'c';
2456 *cp = 0;
2457 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2458 Jim_FreeNewObj(interp, resObjPtr);
2459 return NULL;
2460 }
2461 c = (char) wideValue;
2462 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2463 break;
2464 case 'f':
2465 case 'F':
2466 case 'g':
2467 case 'G':
2468 case 'e':
2469 case 'E':
2470 *cp++ = *fmt;
2471 *cp = 0;
2472 if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2473 Jim_FreeNewObj( interp, resObjPtr );
2474 return NULL;
2475 }
2476 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2477 break;
2478 case 'b':
2479 case 'd':
2480 case 'o':
2481 case 'i':
2482 case 'u':
2483 case 'x':
2484 case 'X':
2485 /* jim widevaluse are 64bit */
2486 if( sizeof(jim_wide) == sizeof(long long) ){
2487 *cp++ = 'l';
2488 *cp++ = 'l';
2489 } else {
2490 *cp++ = 'l';
2491 }
2492 *cp++ = *fmt;
2493 *cp = 0;
2494 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2495 Jim_FreeNewObj(interp, resObjPtr);
2496 return NULL;
2497 }
2498 snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2499 break;
2500 case '%':
2501 sprintf_buf[0] = '%';
2502 sprintf_buf[1] = 0;
2503 objv--; /* undo the objv++ below */
2504 break;
2505 default:
2506 spec[0] = *fmt; spec[1] = '\0';
2507 Jim_FreeNewObj(interp, resObjPtr);
2508 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2509 Jim_AppendStrings(interp, Jim_GetResult(interp),
2510 "bad field specifier \"", spec, "\"", NULL);
2511 return NULL;
2512 }
2513 /* force terminate */
2514 #if 0
2515 printf("FMT was: %s\n", fmt_str );
2516 printf("RES was: |%s|\n", sprintf_buf );
2517 #endif
2518
2519 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2520 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2521 /* next obj */
2522 objv++;
2523 fmt++;
2524 fmtLen--;
2525 }
2526 return resObjPtr;
2527 }
2528
2529 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2530 int objc, Jim_Obj *const *objv)
2531 {
2532 char *sprintf_buf=malloc(JIM_MAX_FMT);
2533 Jim_Obj *t=Jim_FormatString_Inner(interp, fmtObjPtr, objc, objv, sprintf_buf);
2534 free(sprintf_buf);
2535 return t;
2536 }
2537
2538 /* -----------------------------------------------------------------------------
2539 * Compared String Object
2540 * ---------------------------------------------------------------------------*/
2541
2542 /* This is strange object that allows to compare a C literal string
2543 * with a Jim object in very short time if the same comparison is done
2544 * multiple times. For example every time the [if] command is executed,
2545 * Jim has to check if a given argument is "else". This comparions if
2546 * the code has no errors are true most of the times, so we can cache
2547 * inside the object the pointer of the string of the last matching
2548 * comparison. Because most C compilers perform literal sharing,
2549 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2550 * this works pretty well even if comparisons are at different places
2551 * inside the C code. */
2552
2553 static Jim_ObjType comparedStringObjType = {
2554 "compared-string",
2555 NULL,
2556 NULL,
2557 NULL,
2558 JIM_TYPE_REFERENCES,
2559 };
2560
2561 /* The only way this object is exposed to the API is via the following
2562 * function. Returns true if the string and the object string repr.
2563 * are the same, otherwise zero is returned.
2564 *
2565 * Note: this isn't binary safe, but it hardly needs to be.*/
2566 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2567 const char *str)
2568 {
2569 if (objPtr->typePtr == &comparedStringObjType &&
2570 objPtr->internalRep.ptr == str)
2571 return 1;
2572 else {
2573 const char *objStr = Jim_GetString(objPtr, NULL);
2574 if (strcmp(str, objStr) != 0) return 0;
2575 if (objPtr->typePtr != &comparedStringObjType) {
2576 Jim_FreeIntRep(interp, objPtr);
2577 objPtr->typePtr = &comparedStringObjType;
2578 }
2579 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2580 return 1;
2581 }
2582 }
2583
2584 int qsortCompareStringPointers(const void *a, const void *b)
2585 {
2586 char * const *sa = (char * const *)a;
2587 char * const *sb = (char * const *)b;
2588 return strcmp(*sa, *sb);
2589 }
2590
2591 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2592 const char * const *tablePtr, int *indexPtr, const char *name, int flags)
2593 {
2594 const char * const *entryPtr = NULL;
2595 char **tablePtrSorted;
2596 int i, count = 0;
2597
2598 *indexPtr = -1;
2599 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2600 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2601 *indexPtr = i;
2602 return JIM_OK;
2603 }
2604 count++; /* If nothing matches, this will reach the len of tablePtr */
2605 }
2606 if (flags & JIM_ERRMSG) {
2607 if (name == NULL)
2608 name = "option";
2609 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2610 Jim_AppendStrings(interp, Jim_GetResult(interp),
2611 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2612 NULL);
2613 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2614 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2615 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2616 for (i = 0; i < count; i++) {
2617 if (i+1 == count && count > 1)
2618 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2619 Jim_AppendString(interp, Jim_GetResult(interp),
2620 tablePtrSorted[i], -1);
2621 if (i+1 != count)
2622 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2623 }
2624 Jim_Free(tablePtrSorted);
2625 }
2626 return JIM_ERR;
2627 }
2628
2629 int Jim_GetNvp(Jim_Interp *interp,
2630 Jim_Obj *objPtr,
2631 const Jim_Nvp *nvp_table,
2632 const Jim_Nvp ** result)
2633 {
2634 Jim_Nvp *n;
2635 int e;
2636
2637 e = Jim_Nvp_name2value_obj( interp, nvp_table, objPtr, &n );
2638 if( e == JIM_ERR ){
2639 return e;
2640 }
2641
2642 /* Success? found? */
2643 if( n->name ){
2644 /* remove const */
2645 *result = (Jim_Nvp *)n;
2646 return JIM_OK;
2647 } else {
2648 return JIM_ERR;
2649 }
2650 }
2651
2652 /* -----------------------------------------------------------------------------
2653 * Source Object
2654 *
2655 * This object is just a string from the language point of view, but
2656 * in the internal representation it contains the filename and line number
2657 * where this given token was read. This information is used by
2658 * Jim_EvalObj() if the object passed happens to be of type "source".
2659 *
2660 * This allows to propagate the information about line numbers and file
2661 * names and give error messages with absolute line numbers.
2662 *
2663 * Note that this object uses shared strings for filenames, and the
2664 * pointer to the filename together with the line number is taken into
2665 * the space for the "inline" internal represenation of the Jim_Object,
2666 * so there is almost memory zero-overhead.
2667 *
2668 * Also the object will be converted to something else if the given
2669 * token it represents in the source file is not something to be
2670 * evaluated (not a script), and will be specialized in some other way,
2671 * so the time overhead is alzo null.
2672 * ---------------------------------------------------------------------------*/
2673
2674 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2675 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2676
2677 static Jim_ObjType sourceObjType = {
2678 "source",
2679 FreeSourceInternalRep,
2680 DupSourceInternalRep,
2681 NULL,
2682 JIM_TYPE_REFERENCES,
2683 };
2684
2685 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2686 {
2687 Jim_ReleaseSharedString(interp,
2688 objPtr->internalRep.sourceValue.fileName);
2689 }
2690
2691 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2692 {
2693 dupPtr->internalRep.sourceValue.fileName =
2694 Jim_GetSharedString(interp,
2695 srcPtr->internalRep.sourceValue.fileName);
2696 dupPtr->internalRep.sourceValue.lineNumber =
2697 dupPtr->internalRep.sourceValue.lineNumber;
2698 dupPtr->typePtr = &sourceObjType;
2699 }
2700
2701 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2702 const char *fileName, int lineNumber)
2703 {
2704 if (Jim_IsShared(objPtr))
2705 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2706 if (objPtr->typePtr != NULL)
2707 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2708 objPtr->internalRep.sourceValue.fileName =
2709 Jim_GetSharedString(interp, fileName);
2710 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2711 objPtr->typePtr = &sourceObjType;
2712 }
2713
2714 /* -----------------------------------------------------------------------------
2715 * Script Object
2716 * ---------------------------------------------------------------------------*/
2717
2718 #define JIM_CMDSTRUCT_EXPAND -1
2719
2720 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2721 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2722 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2723
2724 static Jim_ObjType scriptObjType = {
2725 "script",
2726 FreeScriptInternalRep,
2727 DupScriptInternalRep,
2728 NULL,
2729 JIM_TYPE_REFERENCES,
2730 };
2731
2732 /* The ScriptToken structure represents every token into a scriptObj.
2733 * Every token contains an associated Jim_Obj that can be specialized
2734 * by commands operating on it. */
2735 typedef struct ScriptToken {
2736 int type;
2737 Jim_Obj *objPtr;
2738 int linenr;
2739 } ScriptToken;
2740
2741 /* This is the script object internal representation. An array of
2742 * ScriptToken structures, with an associated command structure array.
2743 * The command structure is a pre-computed representation of the
2744 * command length and arguments structure as a simple liner array
2745 * of integers.
2746 *
2747 * For example the script:
2748 *
2749 * puts hello
2750 * set $i $x$y [foo]BAR
2751 *
2752 * will produce a ScriptObj with the following Tokens:
2753 *
2754 * ESC puts
2755 * SEP
2756 * ESC hello
2757 * EOL
2758 * ESC set
2759 * EOL
2760 * VAR i
2761 * SEP
2762 * VAR x
2763 * VAR y
2764 * SEP
2765 * CMD foo
2766 * ESC BAR
2767 * EOL
2768 *
2769 * This is a description of the tokens, separators, and of lines.
2770 * The command structure instead represents the number of arguments
2771 * of every command, followed by the tokens of which every argument
2772 * is composed. So for the example script, the cmdstruct array will
2773 * contain:
2774 *
2775 * 2 1 1 4 1 1 2 2
2776 *
2777 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2778 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2779 * composed of single tokens (1 1) and the last two of double tokens
2780 * (2 2).
2781 *
2782 * The precomputation of the command structure makes Jim_Eval() faster,
2783 * and simpler because there aren't dynamic lengths / allocations.
2784 *
2785 * -- {expand} handling --
2786 *
2787 * Expand is handled in a special way. When a command
2788 * contains at least an argument with the {expand} prefix,
2789 * the command structure presents a -1 before the integer
2790 * describing the number of arguments. This is used in order
2791 * to send the command exection to a different path in case
2792 * of {expand} and guarantee a fast path for the more common
2793 * case. Also, the integers describing the number of tokens
2794 * are expressed with negative sign, to allow for fast check
2795 * of what's an {expand}-prefixed argument and what not.
2796 *
2797 * For example the command:
2798 *
2799 * list {expand}{1 2}
2800 *
2801 * Will produce the following cmdstruct array:
2802 *
2803 * -1 2 1 -2
2804 *
2805 * -- the substFlags field of the structure --
2806 *
2807 * The scriptObj structure is used to represent both "script" objects
2808 * and "subst" objects. In the second case, the cmdStruct related
2809 * fields are not used at all, but there is an additional field used
2810 * that is 'substFlags': this represents the flags used to turn
2811 * the string into the intenral representation used to perform the
2812 * substitution. If this flags are not what the application requires
2813 * the scriptObj is created again. For example the script:
2814 *
2815 * subst -nocommands $string
2816 * subst -novariables $string
2817 *
2818 * Will recreate the internal representation of the $string object
2819 * two times.
2820 */
2821 typedef struct ScriptObj {
2822 int len; /* Length as number of tokens. */
2823 int commands; /* number of top-level commands in script. */
2824 ScriptToken *token; /* Tokens array. */
2825 int *cmdStruct; /* commands structure */
2826 int csLen; /* length of the cmdStruct array. */
2827 int substFlags; /* flags used for the compilation of "subst" objects */
2828 int inUse; /* Used to share a ScriptObj. Currently
2829 only used by Jim_EvalObj() as protection against
2830 shimmering of the currently evaluated object. */
2831 char *fileName;
2832 } ScriptObj;
2833
2834 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2835 {
2836 int i;
2837 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2838
2839 script->inUse--;
2840 if (script->inUse != 0) return;
2841 for (i = 0; i < script->len; i++) {
2842 if (script->token[i].objPtr != NULL)
2843 Jim_DecrRefCount(interp, script->token[i].objPtr);
2844 }
2845 Jim_Free(script->token);
2846 Jim_Free(script->cmdStruct);
2847 Jim_Free(script->fileName);
2848 Jim_Free(script);
2849 }
2850
2851 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2852 {
2853 JIM_NOTUSED(interp);
2854 JIM_NOTUSED(srcPtr);
2855
2856 /* Just returns an simple string. */
2857 dupPtr->typePtr = NULL;
2858 }
2859
2860 /* Add a new token to the internal repr of a script object */
2861 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2862 char *strtoken, int len, int type, char *filename, int linenr)
2863 {
2864 int prevtype;
2865 struct ScriptToken *token;
2866
2867 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2868 script->token[script->len-1].type;
2869 /* Skip tokens without meaning, like words separators
2870 * following a word separator or an end of command and
2871 * so on. */
2872 if (prevtype == JIM_TT_EOL) {
2873 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2874 Jim_Free(strtoken);
2875 return;
2876 }
2877 } else if (prevtype == JIM_TT_SEP) {
2878 if (type == JIM_TT_SEP) {
2879 Jim_Free(strtoken);
2880 return;
2881 } else if (type == JIM_TT_EOL) {
2882 /* If an EOL is following by a SEP, drop the previous
2883 * separator. */
2884 script->len--;
2885 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2886 }
2887 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2888 type == JIM_TT_ESC && len == 0)
2889 {
2890 /* Don't add empty tokens used in interpolation */
2891 Jim_Free(strtoken);
2892 return;
2893 }
2894 /* Make space for a new istruction */
2895 script->len++;
2896 script->token = Jim_Realloc(script->token,
2897 sizeof(ScriptToken)*script->len);
2898 /* Initialize the new token */
2899 token = script->token+(script->len-1);
2900 token->type = type;
2901 /* Every object is intially as a string, but the
2902 * internal type may be specialized during execution of the
2903 * script. */
2904 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2905 /* To add source info to SEP and EOL tokens is useless because
2906 * they will never by called as arguments of Jim_EvalObj(). */
2907 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2908 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2909 Jim_IncrRefCount(token->objPtr);
2910 token->linenr = linenr;
2911 }
2912
2913 /* Add an integer into the command structure field of the script object. */
2914 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2915 {
2916 script->csLen++;
2917 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2918 sizeof(int)*script->csLen);
2919 script->cmdStruct[script->csLen-1] = val;
2920 }
2921
2922 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2923 * of objPtr. Search nested script objects recursively. */
2924 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2925 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2926 {
2927 int i;
2928
2929 for (i = 0; i < script->len; i++) {
2930 if (script->token[i].objPtr != objPtr &&
2931 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2932 return script->token[i].objPtr;
2933 }
2934 /* Enter recursively on scripts only if the object
2935 * is not the same as the one we are searching for
2936 * shared occurrences. */
2937 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2938 script->token[i].objPtr != objPtr) {
2939 Jim_Obj *foundObjPtr;
2940
2941 ScriptObj *subScript =
2942 script->token[i].objPtr->internalRep.ptr;
2943 /* Don't recursively enter the script we are trying
2944 * to make shared to avoid circular references. */
2945 if (subScript == scriptBarrier) continue;
2946 if (subScript != script) {
2947 foundObjPtr =
2948 ScriptSearchLiteral(interp, subScript,
2949 scriptBarrier, objPtr);
2950 if (foundObjPtr != NULL)
2951 return foundObjPtr;
2952 }
2953 }
2954 }
2955 return NULL;
2956 }
2957
2958 /* Share literals of a script recursively sharing sub-scripts literals. */
2959 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2960 ScriptObj *topLevelScript)
2961 {
2962 int i, j;
2963
2964 return;
2965 /* Try to share with toplevel object. */
2966 if (topLevelScript != NULL) {
2967 for (i = 0; i < script->len; i++) {
2968 Jim_Obj *foundObjPtr;
2969 char *str = script->token[i].objPtr->bytes;
2970
2971 if (script->token[i].objPtr->refCount != 1) continue;
2972 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2973 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2974 foundObjPtr = ScriptSearchLiteral(interp,
2975 topLevelScript,
2976 script, /* barrier */
2977 script->token[i].objPtr);
2978 if (foundObjPtr != NULL) {
2979 Jim_IncrRefCount(foundObjPtr);
2980 Jim_DecrRefCount(interp,
2981 script->token[i].objPtr);
2982 script->token[i].objPtr = foundObjPtr;
2983 }
2984 }
2985 }
2986 /* Try to share locally */
2987 for (i = 0; i < script->len; i++) {
2988 char *str = script->token[i].objPtr->bytes;
2989
2990 if (script->token[i].objPtr->refCount != 1) continue;
2991 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2992 for (j = 0; j < script->len; j++) {
2993 if (script->token[i].objPtr !=
2994 script->token[j].objPtr &&
2995 Jim_StringEqObj(script->token[i].objPtr,
2996 script->token[j].objPtr, 0))
2997 {
2998 Jim_IncrRefCount(script->token[j].objPtr);
2999 Jim_DecrRefCount(interp,
3000 script->token[i].objPtr);
3001 script->token[i].objPtr =
3002 script->token[j].objPtr;
3003 }
3004 }
3005 }
3006 }
3007
3008 /* This method takes the string representation of an object
3009 * as a Tcl script, and generates the pre-parsed internal representation
3010 * of the script. */
3011 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3012 {
3013 int scriptTextLen;
3014 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
3015 struct JimParserCtx parser;
3016 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
3017 ScriptToken *token;
3018 int args, tokens, start, end, i;
3019 int initialLineNumber;
3020 int propagateSourceInfo = 0;
3021
3022 script->len = 0;
3023 script->csLen = 0;
3024 script->commands = 0;
3025 script->token = NULL;
3026 script->cmdStruct = NULL;
3027 script->inUse = 1;
3028 /* Try to get information about filename / line number */
3029 if (objPtr->typePtr == &sourceObjType) {
3030 script->fileName =
3031 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
3032 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
3033 propagateSourceInfo = 1;
3034 } else {
3035 script->fileName = Jim_StrDup("");
3036 initialLineNumber = 1;
3037 }
3038
3039 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
3040 while(!JimParserEof(&parser)) {
3041 char *token;
3042 int len, type, linenr;
3043
3044 JimParseScript(&parser);
3045 token = JimParserGetToken(&parser, &len, &type, &linenr);
3046 ScriptObjAddToken(interp, script, token, len, type,
3047 propagateSourceInfo ? script->fileName : NULL,
3048 linenr);
3049 }
3050 token = script->token;
3051
3052 /* Compute the command structure array
3053 * (see the ScriptObj struct definition for more info) */
3054 start = 0; /* Current command start token index */
3055 end = -1; /* Current command end token index */
3056 while (1) {
3057 int expand = 0; /* expand flag. set to 1 on {expand} form. */
3058 int interpolation = 0; /* set to 1 if there is at least one
3059 argument of the command obtained via
3060 interpolation of more tokens. */
3061 /* Search for the end of command, while
3062 * count the number of args. */
3063 start = ++end;
3064 if (start >= script->len) break;
3065 args = 1; /* Number of args in current command */
3066 while (token[end].type != JIM_TT_EOL) {
3067 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
3068 token[end-1].type == JIM_TT_EOL)
3069 {
3070 if (token[end].type == JIM_TT_STR &&
3071 token[end+1].type != JIM_TT_SEP &&
3072 token[end+1].type != JIM_TT_EOL &&
3073 (!strcmp(token[end].objPtr->bytes, "expand") ||
3074 !strcmp(token[end].objPtr->bytes, "*")))
3075 expand++;
3076 }
3077 if (token[end].type == JIM_TT_SEP)
3078 args++;
3079 end++;
3080 }
3081 interpolation = !((end-start+1) == args*2);
3082 /* Add the 'number of arguments' info into cmdstruct.
3083 * Negative value if there is list expansion involved. */
3084 if (expand)
3085 ScriptObjAddInt(script, -1);
3086 ScriptObjAddInt(script, args);
3087 /* Now add info about the number of tokens. */
3088 tokens = 0; /* Number of tokens in current argument. */
3089 expand = 0;
3090 for (i = start; i <= end; i++) {
3091 if (token[i].type == JIM_TT_SEP ||
3092 token[i].type == JIM_TT_EOL)
3093 {
3094 if (tokens == 1 && expand)
3095 expand = 0;
3096 ScriptObjAddInt(script,
3097 expand ? -tokens : tokens);
3098
3099 expand = 0;
3100 tokens = 0;
3101 continue;
3102 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
3103 (!strcmp(token[i].objPtr->bytes, "expand") ||
3104 !strcmp(token[i].objPtr->bytes, "*")))
3105 {
3106 expand++;
3107 }
3108 tokens++;
3109 }
3110 }
3111 /* Perform literal sharing, but only for objects that appear
3112 * to be scripts written as literals inside the source code,
3113 * and not computed at runtime. Literal sharing is a costly
3114 * operation that should be done only against objects that
3115 * are likely to require compilation only the first time, and
3116 * then are executed multiple times. */
3117 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
3118 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
3119 if (bodyObjPtr->typePtr == &scriptObjType) {
3120 ScriptObj *bodyScript =
3121 bodyObjPtr->internalRep.ptr;
3122 ScriptShareLiterals(interp, script, bodyScript);
3123 }
3124 } else if (propagateSourceInfo) {
3125 ScriptShareLiterals(interp, script, NULL);
3126 }
3127 /* Free the old internal rep and set the new one. */
3128 Jim_FreeIntRep(interp, objPtr);
3129 Jim_SetIntRepPtr(objPtr, script);
3130 objPtr->typePtr = &scriptObjType;
3131 return JIM_OK;
3132 }
3133
3134 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
3135 {
3136 if (objPtr->typePtr != &scriptObjType) {
3137 SetScriptFromAny(interp, objPtr);
3138 }
3139 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
3140 }
3141
3142 /* -----------------------------------------------------------------------------
3143 * Commands
3144 * ---------------------------------------------------------------------------*/
3145
3146 /* Commands HashTable Type.
3147 *
3148 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
3149 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
3150 {
3151 Jim_Cmd *cmdPtr = (void*) val;
3152
3153 if (cmdPtr->cmdProc == NULL) {
3154 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3155 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3156 if (cmdPtr->staticVars) {
3157 Jim_FreeHashTable(cmdPtr->staticVars);
3158 Jim_Free(cmdPtr->staticVars);
3159 }
3160 } else if (cmdPtr->delProc != NULL) {
3161 /* If it was a C coded command, call the delProc if any */
3162 cmdPtr->delProc(interp, cmdPtr->privData);
3163 }
3164 Jim_Free(val);
3165 }
3166
3167 static Jim_HashTableType JimCommandsHashTableType = {
3168 JimStringCopyHTHashFunction, /* hash function */
3169 JimStringCopyHTKeyDup, /* key dup */
3170 NULL, /* val dup */
3171 JimStringCopyHTKeyCompare, /* key compare */
3172 JimStringCopyHTKeyDestructor, /* key destructor */
3173 Jim_CommandsHT_ValDestructor /* val destructor */
3174 };
3175
3176 /* ------------------------- Commands related functions --------------------- */
3177
3178 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3179 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3180 {
3181 Jim_HashEntry *he;
3182 Jim_Cmd *cmdPtr;
3183
3184 he = Jim_FindHashEntry(&interp->commands, cmdName);
3185 if (he == NULL) { /* New command to create */
3186 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3187 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3188 } else {
3189 Jim_InterpIncrProcEpoch(interp);
3190 /* Free the arglist/body objects if it was a Tcl procedure */
3191 cmdPtr = he->val;
3192 if (cmdPtr->cmdProc == NULL) {
3193 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3194 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3195 if (cmdPtr->staticVars) {
3196 Jim_FreeHashTable(cmdPtr->staticVars);
3197 Jim_Free(cmdPtr->staticVars);
3198 }
3199 cmdPtr->staticVars = NULL;
3200 } else if (cmdPtr->delProc != NULL) {
3201 /* If it was a C coded command, call the delProc if any */
3202 cmdPtr->delProc(interp, cmdPtr->privData);
3203 }
3204 }
3205
3206 /* Store the new details for this proc */
3207 cmdPtr->delProc = delProc;
3208 cmdPtr->cmdProc = cmdProc;
3209 cmdPtr->privData = privData;
3210
3211 /* There is no need to increment the 'proc epoch' because
3212 * creation of a new procedure can never affect existing
3213 * cached commands. We don't do negative caching. */
3214 return JIM_OK;
3215 }
3216
3217 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3218 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3219 int arityMin, int arityMax)
3220 {
3221 Jim_Cmd *cmdPtr;
3222
3223 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3224 cmdPtr->cmdProc = NULL; /* Not a C coded command */
3225 cmdPtr->argListObjPtr = argListObjPtr;
3226 cmdPtr->bodyObjPtr = bodyObjPtr;
3227 Jim_IncrRefCount(argListObjPtr);
3228 Jim_IncrRefCount(bodyObjPtr);
3229 cmdPtr->arityMin = arityMin;
3230 cmdPtr->arityMax = arityMax;
3231 cmdPtr->staticVars = NULL;
3232
3233 /* Create the statics hash table. */
3234 if (staticsListObjPtr) {
3235 int len, i;
3236
3237 Jim_ListLength(interp, staticsListObjPtr, &len);
3238 if (len != 0) {
3239 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3240 Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
3241 interp);
3242 for (i = 0; i < len; i++) {
3243 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3244 Jim_Var *varPtr;
3245 int subLen;
3246
3247 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3248 /* Check if it's composed of two elements. */
3249 Jim_ListLength(interp, objPtr, &subLen);
3250 if (subLen == 1 || subLen == 2) {
3251 /* Try to get the variable value from the current
3252 * environment. */
3253 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3254 if (subLen == 1) {
3255 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3256 JIM_NONE);
3257 if (initObjPtr == NULL) {
3258 Jim_SetResult(interp,
3259 Jim_NewEmptyStringObj(interp));
3260 Jim_AppendStrings(interp, Jim_GetResult(interp),
3261 "variable for initialization of static \"",
3262 Jim_GetString(nameObjPtr, NULL),
3263 "\" not found in the local context",
3264 NULL);
3265 goto err;
3266 }
3267 } else {
3268 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3269 }
3270 varPtr = Jim_Alloc(sizeof(*varPtr));
3271 varPtr->objPtr = initObjPtr;
3272 Jim_IncrRefCount(initObjPtr);
3273 varPtr->linkFramePtr = NULL;
3274 if (Jim_AddHashEntry(cmdPtr->staticVars,
3275 Jim_GetString(nameObjPtr, NULL),
3276 varPtr) != JIM_OK)
3277 {
3278 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3279 Jim_AppendStrings(interp, Jim_GetResult(interp),
3280 "static variable name \"",
3281 Jim_GetString(objPtr, NULL), "\"",
3282 " duplicated in statics list", NULL);
3283 Jim_DecrRefCount(interp, initObjPtr);
3284 Jim_Free(varPtr);
3285 goto err;
3286 }
3287 } else {
3288 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3289 Jim_AppendStrings(interp, Jim_GetResult(interp),
3290 "too many fields in static specifier \"",
3291 objPtr, "\"", NULL);
3292 goto err;
3293 }
3294 }
3295 }
3296 }
3297
3298 /* Add the new command */
3299
3300 /* it may already exist, so we try to delete the old one */
3301 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3302 /* There was an old procedure with the same name, this requires
3303 * a 'proc epoch' update. */
3304 Jim_InterpIncrProcEpoch(interp);
3305 }
3306 /* If a procedure with the same name didn't existed there is no need
3307 * to increment the 'proc epoch' because creation of a new procedure
3308 * can never affect existing cached commands. We don't do
3309 * negative caching. */
3310 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3311 return JIM_OK;
3312
3313 err:
3314 Jim_FreeHashTable(cmdPtr->staticVars);
3315 Jim_Free(cmdPtr->staticVars);
3316 Jim_DecrRefCount(interp, argListObjPtr);
3317 Jim_DecrRefCount(interp, bodyObjPtr);
3318 Jim_Free(cmdPtr);
3319 return JIM_ERR;
3320 }
3321
3322 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3323 {
3324 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3325 return JIM_ERR;
3326 Jim_InterpIncrProcEpoch(interp);
3327 return JIM_OK;
3328 }
3329
3330 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
3331 const char *newName)
3332 {
3333 Jim_Cmd *cmdPtr;
3334 Jim_HashEntry *he;
3335 Jim_Cmd *copyCmdPtr;
3336
3337 if (newName[0] == '\0') /* Delete! */
3338 return Jim_DeleteCommand(interp, oldName);
3339 /* Rename */
3340 he = Jim_FindHashEntry(&interp->commands, oldName);
3341 if (he == NULL)
3342 return JIM_ERR; /* Invalid command name */
3343 cmdPtr = he->val;
3344 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3345 *copyCmdPtr = *cmdPtr;
3346 /* In order to avoid that a procedure will get arglist/body/statics
3347 * freed by the hash table methods, fake a C-coded command
3348 * setting cmdPtr->cmdProc as not NULL */
3349 cmdPtr->cmdProc = (void*)1;
3350 /* Also make sure delProc is NULL. */
3351 cmdPtr->delProc = NULL;
3352 /* Destroy the old command, and make sure the new is freed
3353 * as well. */
3354 Jim_DeleteHashEntry(&interp->commands, oldName);
3355 Jim_DeleteHashEntry(&interp->commands, newName);
3356 /* Now the new command. We are sure it can't fail because
3357 * the target name was already freed. */
3358 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3359 /* Increment the epoch */
3360 Jim_InterpIncrProcEpoch(interp);
3361 return JIM_OK;
3362 }
3363
3364 /* -----------------------------------------------------------------------------
3365 * Command object
3366 * ---------------------------------------------------------------------------*/
3367
3368 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3369
3370 static Jim_ObjType commandObjType = {
3371 "command",
3372 NULL,
3373 NULL,
3374 NULL,
3375 JIM_TYPE_REFERENCES,
3376 };
3377
3378 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3379 {
3380 Jim_HashEntry *he;
3381 const char *cmdName;
3382
3383 /* Get the string representation */
3384 cmdName = Jim_GetString(objPtr, NULL);
3385 /* Lookup this name into the commands hash table */
3386 he = Jim_FindHashEntry(&interp->commands, cmdName);
3387 if (he == NULL)
3388 return JIM_ERR;
3389
3390 /* Free the old internal repr and set the new one. */
3391 Jim_FreeIntRep(interp, objPtr);
3392 objPtr->typePtr = &commandObjType;
3393 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3394 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3395 return JIM_OK;
3396 }
3397
3398 /* This function returns the command structure for the command name
3399 * stored in objPtr. It tries to specialize the objPtr to contain
3400 * a cached info instead to perform the lookup into the hash table
3401 * every time. The information cached may not be uptodate, in such
3402 * a case the lookup is performed and the cache updated. */
3403 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3404 {
3405 if ((objPtr->typePtr != &commandObjType ||
3406 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3407 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3408 if (flags & JIM_ERRMSG) {
3409 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3410 Jim_AppendStrings(interp, Jim_GetResult(interp),
3411 "invalid command name \"", objPtr->bytes, "\"",
3412 NULL);
3413 }
3414 return NULL;
3415 }
3416 return objPtr->internalRep.cmdValue.cmdPtr;
3417 }
3418
3419 /* -----------------------------------------------------------------------------
3420 * Variables
3421 * ---------------------------------------------------------------------------*/
3422
3423 /* Variables HashTable Type.
3424 *
3425 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3426 static void JimVariablesHTValDestructor(void *interp, void *val)
3427 {
3428 Jim_Var *varPtr = (void*) val;
3429
3430 Jim_DecrRefCount(interp, varPtr->objPtr);
3431 Jim_Free(val);
3432 }
3433
3434 static Jim_HashTableType JimVariablesHashTableType = {
3435 JimStringCopyHTHashFunction, /* hash function */
3436 JimStringCopyHTKeyDup, /* key dup */
3437 NULL, /* val dup */
3438 JimStringCopyHTKeyCompare, /* key compare */
3439 JimStringCopyHTKeyDestructor, /* key destructor */
3440 JimVariablesHTValDestructor /* val destructor */
3441 };
3442
3443 /* -----------------------------------------------------------------------------
3444 * Variable object
3445 * ---------------------------------------------------------------------------*/
3446
3447 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3448
3449 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3450
3451 static Jim_ObjType variableObjType = {
3452 "variable",
3453 NULL,
3454 NULL,
3455 NULL,
3456 JIM_TYPE_REFERENCES,
3457 };
3458
3459 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3460 * is in the form "varname(key)". */
3461 static int Jim_NameIsDictSugar(const char *str, int len)
3462 {
3463 if (len == -1)
3464 len = strlen(str);
3465 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3466 return 1;
3467 return 0;
3468 }
3469
3470 /* This method should be called only by the variable API.
3471 * It returns JIM_OK on success (variable already exists),
3472 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3473 * a variable name, but syntax glue for [dict] i.e. the last
3474 * character is ')' */
3475 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3476 {
3477 Jim_HashEntry *he;
3478 const char *varName;
3479 int len;
3480
3481 /* Check if the object is already an uptodate variable */
3482 if (objPtr->typePtr == &variableObjType &&
3483 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3484 return JIM_OK; /* nothing to do */
3485 /* Get the string representation */
3486 varName = Jim_GetString(objPtr, &len);
3487 /* Make sure it's not syntax glue to get/set dict. */
3488 if (Jim_NameIsDictSugar(varName, len))
3489 return JIM_DICT_SUGAR;
3490 if (varName[0] == ':' && varName[1] == ':') {
3491 he = Jim_FindHashEntry(&interp->topFramePtr->vars, varName + 2);
3492 if (he == NULL) {
3493 return JIM_ERR;
3494 }
3495 }
3496 else {
3497 /* Lookup this name into the variables hash table */
3498 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3499 if (he == NULL) {
3500 /* Try with static vars. */
3501 if (interp->framePtr->staticVars == NULL)
3502 return JIM_ERR;
3503 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3504 return JIM_ERR;
3505 }
3506 }
3507 /* Free the old internal repr and set the new one. */
3508 Jim_FreeIntRep(interp, objPtr);
3509 objPtr->typePtr = &variableObjType;
3510 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3511 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3512 return JIM_OK;
3513 }
3514
3515 /* -------------------- Variables related functions ------------------------- */
3516 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3517 Jim_Obj *valObjPtr);
3518 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3519
3520 /* For now that's dummy. Variables lookup should be optimized
3521 * in many ways, with caching of lookups, and possibly with
3522 * a table of pre-allocated vars in every CallFrame for local vars.
3523 * All the caching should also have an 'epoch' mechanism similar
3524 * to the one used by Tcl for procedures lookup caching. */
3525
3526 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3527 {
3528 const char *name;
3529 Jim_Var *var;
3530 int err;
3531
3532 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3533 /* Check for [dict] syntax sugar. */
3534 if (err == JIM_DICT_SUGAR)
3535 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3536 /* New variable to create */
3537 name = Jim_GetString(nameObjPtr, NULL);
3538
3539 var = Jim_Alloc(sizeof(*var));
3540 var->objPtr = valObjPtr;
3541 Jim_IncrRefCount(valObjPtr);
3542 var->linkFramePtr = NULL;
3543 /* Insert the new variable */
3544 if (name[0] == ':' && name[1] == ':') {
3545 /* Into to the top evel frame */
3546 Jim_AddHashEntry(&interp->topFramePtr->vars, name + 2, var);
3547 }
3548 else {
3549 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3550 }
3551 /* Make the object int rep a variable */
3552 Jim_FreeIntRep(interp, nameObjPtr);
3553 nameObjPtr->typePtr = &variableObjType;
3554 nameObjPtr->internalRep.varValue.callFrameId =
3555 interp->framePtr->id;
3556 nameObjPtr->internalRep.varValue.varPtr = var;
3557 } else {
3558 var = nameObjPtr->internalRep.varValue.varPtr;
3559 if (var->linkFramePtr == NULL) {
3560 Jim_IncrRefCount(valObjPtr);
3561 Jim_DecrRefCount(interp, var->objPtr);
3562 var->objPtr = valObjPtr;
3563 } else { /* Else handle the link */
3564 Jim_CallFrame *savedCallFrame;
3565
3566 savedCallFrame = interp->framePtr;
3567 interp->framePtr = var->linkFramePtr;
3568 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3569 interp->framePtr = savedCallFrame;
3570 if (err != JIM_OK)
3571 return err;
3572 }
3573 }
3574 return JIM_OK;
3575 }
3576
3577 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3578 {
3579 Jim_Obj *nameObjPtr;
3580 int result;
3581
3582 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3583 Jim_IncrRefCount(nameObjPtr);
3584 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3585 Jim_DecrRefCount(interp, nameObjPtr);
3586 return result;
3587 }
3588
3589 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3590 {
3591 Jim_CallFrame *savedFramePtr;
3592 int result;
3593
3594 savedFramePtr = interp->framePtr;
3595 interp->framePtr = interp->topFramePtr;
3596 result = Jim_SetVariableStr(interp, name, objPtr);
3597 interp->framePtr = savedFramePtr;
3598 return result;
3599 }
3600
3601 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3602 {
3603 Jim_Obj *nameObjPtr, *valObjPtr;
3604 int result;
3605
3606 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3607 valObjPtr = Jim_NewStringObj(interp, val, -1);
3608 Jim_IncrRefCount(nameObjPtr);
3609 Jim_IncrRefCount(valObjPtr);
3610 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3611 Jim_DecrRefCount(interp, nameObjPtr);
3612 Jim_DecrRefCount(interp, valObjPtr);
3613 return result;
3614 }
3615
3616 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3617 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3618 {
3619 const char *varName;
3620 int len;
3621
3622 /* Check for cycles. */
3623 if (interp->framePtr == targetCallFrame) {
3624 Jim_Obj *objPtr = targetNameObjPtr;
3625 Jim_Var *varPtr;
3626 /* Cycles are only possible with 'uplevel 0' */
3627 while(1) {
3628 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3629 Jim_SetResultString(interp,
3630 "can't upvar from variable to itself", -1);
3631 return JIM_ERR;
3632 }
3633 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3634 break;
3635 varPtr = objPtr->internalRep.varValue.varPtr;
3636 if (varPtr->linkFramePtr != targetCallFrame) break;
3637 objPtr = varPtr->objPtr;
3638 }
3639 }
3640 varName = Jim_GetString(nameObjPtr, &len);
3641 if (Jim_NameIsDictSugar(varName, len)) {
3642 Jim_SetResultString(interp,
3643 "Dict key syntax invalid as link source", -1);
3644 return JIM_ERR;
3645 }
3646 /* Perform the binding */
3647 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3648 /* We are now sure 'nameObjPtr' type is variableObjType */
3649 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3650 return JIM_OK;
3651 }
3652
3653 /* Return the Jim_Obj pointer associated with a variable name,
3654 * or NULL if the variable was not found in the current context.
3655 * The same optimization discussed in the comment to the
3656 * 'SetVariable' function should apply here. */
3657 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3658 {
3659 int err;
3660
3661 /* All the rest is handled here */
3662 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3663 /* Check for [dict] syntax sugar. */
3664 if (err == JIM_DICT_SUGAR)
3665 return JimDictSugarGet(interp, nameObjPtr);
3666 if (flags & JIM_ERRMSG) {
3667 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3668 Jim_AppendStrings(interp, Jim_GetResult(interp),
3669 "can't read \"", nameObjPtr->bytes,
3670 "\": no such variable", NULL);
3671 }
3672 return NULL;
3673 } else {
3674 Jim_Var *varPtr;
3675 Jim_Obj *objPtr;
3676 Jim_CallFrame *savedCallFrame;
3677
3678 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3679 if (varPtr->linkFramePtr == NULL)
3680 return varPtr->objPtr;
3681 /* The variable is a link? Resolve it. */
3682 savedCallFrame = interp->framePtr;
3683 interp->framePtr = varPtr->linkFramePtr;
3684 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3685 if (objPtr == NULL && flags & JIM_ERRMSG) {
3686 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3687 Jim_AppendStrings(interp, Jim_GetResult(interp),
3688 "can't read \"", nameObjPtr->bytes,
3689 "\": no such variable", NULL);
3690 }
3691 interp->framePtr = savedCallFrame;
3692 return objPtr;
3693 }
3694 }
3695
3696 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3697 int flags)
3698 {
3699 Jim_CallFrame *savedFramePtr;
3700 Jim_Obj *objPtr;
3701
3702 savedFramePtr = interp->framePtr;
3703 interp->framePtr = interp->topFramePtr;
3704 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3705 interp->framePtr = savedFramePtr;
3706
3707 return objPtr;
3708 }
3709
3710 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3711 {
3712 Jim_Obj *nameObjPtr, *varObjPtr;
3713
3714 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3715 Jim_IncrRefCount(nameObjPtr);
3716 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3717 Jim_DecrRefCount(interp, nameObjPtr);
3718 return varObjPtr;
3719 }
3720
3721 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3722 int flags)
3723 {
3724 Jim_CallFrame *savedFramePtr;
3725 Jim_Obj *objPtr;
3726
3727 savedFramePtr = interp->framePtr;
3728 interp->framePtr = interp->topFramePtr;
3729 objPtr = Jim_GetVariableStr(interp, name, flags);
3730 interp->framePtr = savedFramePtr;
3731
3732 return objPtr;
3733 }
3734
3735 /* Unset a variable.
3736 * Note: On success unset invalidates all the variable objects created
3737 * in the current call frame incrementing. */
3738 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3739 {
3740 const char *name;
3741 Jim_Var *varPtr;
3742 int err;
3743
3744 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3745 /* Check for [dict] syntax sugar. */
3746 if (err == JIM_DICT_SUGAR)
3747 return JimDictSugarSet(interp, nameObjPtr, NULL);
3748 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3749 Jim_AppendStrings(interp, Jim_GetResult(interp),
3750 "can't unset \"", nameObjPtr->bytes,
3751 "\": no such variable", NULL);
3752 return JIM_ERR; /* var not found */
3753 }
3754 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3755 /* If it's a link call UnsetVariable recursively */
3756 if (varPtr->linkFramePtr) {
3757 int retval;
3758
3759 Jim_CallFrame *savedCallFrame;
3760
3761 savedCallFrame = interp->framePtr;
3762 interp->framePtr = varPtr->linkFramePtr;
3763 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3764 interp->framePtr = savedCallFrame;
3765 if (retval != JIM_OK && flags & JIM_ERRMSG) {
3766 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3767 Jim_AppendStrings(interp, Jim_GetResult(interp),
3768 "can't unset \"", nameObjPtr->bytes,
3769 "\": no such variable", NULL);
3770 }
3771 return retval;
3772 } else {
3773 name = Jim_GetString(nameObjPtr, NULL);
3774 if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3775 != JIM_OK) return JIM_ERR;
3776 /* Change the callframe id, invalidating var lookup caching */
3777 JimChangeCallFrameId(interp, interp->framePtr);
3778 return JIM_OK;
3779 }
3780 }
3781
3782 /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
3783
3784 /* Given a variable name for [dict] operation syntax sugar,
3785 * this function returns two objects, the first with the name
3786 * of the variable to set, and the second with the rispective key.
3787 * For example "foo(bar)" will return objects with string repr. of
3788 * "foo" and "bar".
3789 *
3790 * The returned objects have refcount = 1. The function can't fail. */
3791 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3792 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3793 {
3794 const char *str, *p;
3795 char *t;
3796 int len, keyLen, nameLen;
3797 Jim_Obj *varObjPtr, *keyObjPtr;
3798
3799 str = Jim_GetString(objPtr, &len);
3800 p = strchr(str, '(');
3801 p++;
3802 keyLen = len-((p-str)+1);
3803 nameLen = (p-str)-1;
3804 /* Create the objects with the variable name and key. */
3805 t = Jim_Alloc(nameLen+1);
3806 memcpy(t, str, nameLen);
3807 t[nameLen] = '\0';
3808 varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3809
3810 t = Jim_Alloc(keyLen+1);
3811 memcpy(t, p, keyLen);
3812 t[keyLen] = '\0';
3813 keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3814
3815 Jim_IncrRefCount(varObjPtr);
3816 Jim_IncrRefCount(keyObjPtr);
3817 *varPtrPtr = varObjPtr;
3818 *keyPtrPtr = keyObjPtr;
3819 }
3820
3821 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3822 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3823 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3824 Jim_Obj *valObjPtr)
3825 {
3826 Jim_Obj *varObjPtr, *keyObjPtr;
3827 int err = JIM_OK;
3828
3829 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3830 err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3831 valObjPtr);
3832 Jim_DecrRefCount(interp, varObjPtr);
3833 Jim_DecrRefCount(interp, keyObjPtr);
3834 return err;
3835 }
3836
3837 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3838 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3839 {
3840 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3841
3842 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3843 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3844 if (!dictObjPtr) {
3845 resObjPtr = NULL;
3846 goto err;
3847 }
3848 if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3849 != JIM_OK) {
3850 resObjPtr = NULL;
3851 }
3852 err:
3853 Jim_DecrRefCount(interp, varObjPtr);
3854 Jim_DecrRefCount(interp, keyObjPtr);
3855 return resObjPtr;
3856 }
3857
3858 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3859
3860 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3861 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3862 Jim_Obj *dupPtr);
3863
3864 static Jim_ObjType dictSubstObjType = {
3865 "dict-substitution",
3866 FreeDictSubstInternalRep,
3867 DupDictSubstInternalRep,
3868 NULL,
3869 JIM_TYPE_NONE,
3870 };
3871
3872 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3873 {
3874 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3875 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3876 }
3877
3878 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3879 Jim_Obj *dupPtr)
3880 {
3881 JIM_NOTUSED(interp);
3882
3883 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3884 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3885 dupPtr->internalRep.dictSubstValue.indexObjPtr =
3886 srcPtr->internalRep.dictSubstValue.indexObjPtr;
3887 dupPtr->typePtr = &dictSubstObjType;
3888 }
3889
3890 /* This function is used to expand [dict get] sugar in the form
3891 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3892 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3893 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3894 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3895 * the [dict]ionary contained in variable VARNAME. */
3896 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3897 {
3898 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3899 Jim_Obj *substKeyObjPtr = NULL;
3900
3901 if (objPtr->typePtr != &dictSubstObjType) {
3902 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3903 Jim_FreeIntRep(interp, objPtr);
3904 objPtr->typePtr = &dictSubstObjType;
3905 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3906 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3907 }
3908 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3909 &substKeyObjPtr, JIM_NONE)
3910 != JIM_OK) {
3911 substKeyObjPtr = NULL;
3912 goto err;
3913 }
3914 Jim_IncrRefCount(substKeyObjPtr);
3915 dictObjPtr = Jim_GetVariable(interp,
3916 objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3917 if (!dictObjPtr) {
3918 resObjPtr = NULL;
3919 goto err;
3920 }
3921 if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3922 != JIM_OK) {
3923 resObjPtr = NULL;
3924 goto err;
3925 }
3926 err:
3927 if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3928 return resObjPtr;
3929 }
3930
3931 /* -----------------------------------------------------------------------------
3932 * CallFrame
3933 * ---------------------------------------------------------------------------*/
3934
3935 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3936 {
3937 Jim_CallFrame *cf;
3938 if (interp->freeFramesList) {
3939 cf = interp->freeFramesList;
3940 interp->freeFramesList = cf->nextFramePtr;
3941 } else {
3942 cf = Jim_Alloc(sizeof(*cf));
3943 cf->vars.table = NULL;
3944 }
3945
3946 cf->id = interp->callFrameEpoch++;
3947 cf->parentCallFrame = NULL;
3948 cf->argv = NULL;
3949 cf->argc = 0;
3950 cf->procArgsObjPtr = NULL;
3951 cf->procBodyObjPtr = NULL;
3952 cf->nextFramePtr = NULL;
3953 cf->staticVars = NULL;
3954 if (cf->vars.table == NULL)
3955 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3956 return cf;
3957 }
3958
3959 /* Used to invalidate every caching related to callframe stability. */
3960 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3961 {
3962 cf->id = interp->callFrameEpoch++;
3963 }
3964
3965 #define JIM_FCF_NONE 0 /* no flags */
3966 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3967 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3968 int flags)
3969 {
3970 if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3971 if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3972 if (!(flags & JIM_FCF_NOHT))
3973 Jim_FreeHashTable(&cf->vars);
3974 else {
3975 int i;
3976 Jim_HashEntry **table = cf->vars.table, *he;
3977
3978 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3979 he = table[i];
3980 while (he != NULL) {
3981 Jim_HashEntry *nextEntry = he->next;
3982 Jim_Var *varPtr = (void*) he->val;
3983
3984 Jim_DecrRefCount(interp, varPtr->objPtr);
3985 Jim_Free(he->val);
3986 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3987 Jim_Free(he);
3988 table[i] = NULL;
3989 he = nextEntry;
3990 }
3991 }
3992 cf->vars.used = 0;
3993 }
3994 cf->nextFramePtr = interp->freeFramesList;
3995 interp->freeFramesList = cf;
3996 }
3997
3998 /* -----------------------------------------------------------------------------
3999 * References
4000 * ---------------------------------------------------------------------------*/
4001
4002 /* References HashTable Type.
4003 *
4004 * Keys are jim_wide integers, dynamically allocated for now but in the
4005 * future it's worth to cache this 8 bytes objects. Values are poitners
4006 * to Jim_References. */
4007 static void JimReferencesHTValDestructor(void *interp, void *val)
4008 {
4009 Jim_Reference *refPtr = (void*) val;
4010
4011 Jim_DecrRefCount(interp, refPtr->objPtr);
4012 if (refPtr->finalizerCmdNamePtr != NULL) {
4013 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4014 }
4015 Jim_Free(val);
4016 }
4017
4018 unsigned int JimReferencesHTHashFunction(const void *key)
4019 {
4020 /* Only the least significant bits are used. */
4021 const jim_wide *widePtr = key;
4022 unsigned int intValue = (unsigned int) *widePtr;
4023 return Jim_IntHashFunction(intValue);
4024 }
4025
4026 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
4027 {
4028 /* Only the least significant bits are used. */
4029 const jim_wide *widePtr = key;
4030 unsigned int intValue = (unsigned int) *widePtr;
4031 return intValue; /* identity function. */
4032 }
4033
4034 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
4035 {
4036 void *copy = Jim_Alloc(sizeof(jim_wide));
4037 JIM_NOTUSED(privdata);
4038
4039 memcpy(copy, key, sizeof(jim_wide));
4040 return copy;
4041 }
4042
4043 int JimReferencesHTKeyCompare(void *privdata, const void *key1,
4044 const void *key2)
4045 {
4046 JIM_NOTUSED(privdata);
4047
4048 return memcmp(key1, key2, sizeof(jim_wide)) == 0;
4049 }
4050
4051 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
4052 {
4053 JIM_NOTUSED(privdata);
4054
4055 Jim_Free((void*)key);
4056 }
4057
4058 static Jim_HashTableType JimReferencesHashTableType = {
4059 JimReferencesHTHashFunction, /* hash function */
4060 JimReferencesHTKeyDup, /* key dup */
4061 NULL, /* val dup */
4062 JimReferencesHTKeyCompare, /* key compare */
4063 JimReferencesHTKeyDestructor, /* key destructor */
4064 JimReferencesHTValDestructor /* val destructor */
4065 };
4066
4067 /* -----------------------------------------------------------------------------
4068 * Reference object type and References API
4069 * ---------------------------------------------------------------------------*/
4070
4071 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
4072
4073 static Jim_ObjType referenceObjType = {
4074 "reference",
4075 NULL,
4076 NULL,
4077 UpdateStringOfReference,
4078 JIM_TYPE_REFERENCES,
4079 };
4080
4081 void UpdateStringOfReference(struct Jim_Obj *objPtr)
4082 {
4083 int len;
4084 char buf[JIM_REFERENCE_SPACE+1];
4085 Jim_Reference *refPtr;
4086
4087 refPtr = objPtr->internalRep.refValue.refPtr;
4088 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
4089 objPtr->bytes = Jim_Alloc(len+1);
4090 memcpy(objPtr->bytes, buf, len+1);
4091 objPtr->length = len;
4092 }
4093
4094 /* returns true if 'c' is a valid reference tag character.
4095 * i.e. inside the range [_a-zA-Z0-9] */
4096 static int isrefchar(int c)
4097 {
4098 if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
4099 (c >= '0' && c <= '9')) return 1;
4100 return 0;
4101 }
4102
4103 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4104 {
4105 jim_wide wideValue;
4106 int i, len;
4107 const char *str, *start, *end;
4108 char refId[21];
4109 Jim_Reference *refPtr;
4110 Jim_HashEntry *he;
4111
4112 /* Get the string representation */
4113 str = Jim_GetString(objPtr, &len);
4114 /* Check if it looks like a reference */
4115 if (len < JIM_REFERENCE_SPACE) goto badformat;
4116 /* Trim spaces */
4117 start = str;
4118 end = str+len-1;
4119 while (*start == ' ') start++;
4120 while (*end == ' ' && end > start) end--;
4121 if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
4122 /* <reference.<1234567>.%020> */
4123 if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
4124 if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
4125 /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
4126 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4127 if (!isrefchar(start[12+i])) goto badformat;
4128 }
4129 /* Extract info from the refernece. */
4130 memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
4131 refId[20] = '\0';
4132 /* Try to convert the ID into a jim_wide */
4133 if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
4134 /* Check if the reference really exists! */
4135 he = Jim_FindHashEntry(&interp->references, &wideValue);
4136 if (he == NULL) {
4137 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4138 Jim_AppendStrings(interp, Jim_GetResult(interp),
4139 "Invalid reference ID \"", str, "\"", NULL);
4140 return JIM_ERR;
4141 }
4142 refPtr = he->val;
4143 /* Free the old internal repr and set the new one. */
4144 Jim_FreeIntRep(interp, objPtr);
4145 objPtr->typePtr = &referenceObjType;
4146 objPtr->internalRep.refValue.id = wideValue;
4147 objPtr->internalRep.refValue.refPtr = refPtr;
4148 return JIM_OK;
4149
4150 badformat:
4151 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4152 Jim_AppendStrings(interp, Jim_GetResult(interp),
4153 "expected reference but got \"", str, "\"", NULL);
4154 return JIM_ERR;
4155 }
4156
4157 /* Returns a new reference pointing to objPtr, having cmdNamePtr
4158 * as finalizer command (or NULL if there is no finalizer).
4159 * The returned reference object has refcount = 0. */
4160 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
4161 Jim_Obj *cmdNamePtr)
4162 {
4163 struct Jim_Reference *refPtr;
4164 jim_wide wideValue = interp->referenceNextId;
4165 Jim_Obj *refObjPtr;
4166 const char *tag;
4167 int tagLen, i;
4168
4169 /* Perform the Garbage Collection if needed. */
4170 Jim_CollectIfNeeded(interp);
4171
4172 refPtr = Jim_Alloc(sizeof(*refPtr));
4173 refPtr->objPtr = objPtr;
4174 Jim_IncrRefCount(objPtr);
4175 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4176 if (cmdNamePtr)
4177 Jim_IncrRefCount(cmdNamePtr);
4178 Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4179 refObjPtr = Jim_NewObj(interp);
4180 refObjPtr->typePtr = &referenceObjType;
4181 refObjPtr->bytes = NULL;
4182 refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4183 refObjPtr->internalRep.refValue.refPtr = refPtr;
4184 interp->referenceNextId++;
4185 /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4186 * that does not pass the 'isrefchar' test is replaced with '_' */
4187 tag = Jim_GetString(tagPtr, &tagLen);
4188 if (tagLen > JIM_REFERENCE_TAGLEN)
4189 tagLen = JIM_REFERENCE_TAGLEN;
4190 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4191 if (i < tagLen)
4192 refPtr->tag[i] = tag[i];
4193 else
4194 refPtr->tag[i] = '_';
4195 }
4196 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4197 return refObjPtr;
4198 }
4199
4200 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4201 {
4202 if (objPtr->typePtr != &referenceObjType &&
4203 SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4204 return NULL;
4205 return objPtr->internalRep.refValue.refPtr;
4206 }
4207
4208 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4209 {
4210 Jim_Reference *refPtr;
4211
4212 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4213 return JIM_ERR;
4214 Jim_IncrRefCount(cmdNamePtr);
4215 if (refPtr->finalizerCmdNamePtr)
4216 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4217 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4218 return JIM_OK;
4219 }
4220
4221 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4222 {
4223 Jim_Reference *refPtr;
4224
4225 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4226 return JIM_ERR;
4227 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4228 return JIM_OK;
4229 }
4230
4231 /* -----------------------------------------------------------------------------
4232 * References Garbage Collection
4233 * ---------------------------------------------------------------------------*/
4234
4235 /* This the hash table type for the "MARK" phase of the GC */
4236 static Jim_HashTableType JimRefMarkHashTableType = {
4237 JimReferencesHTHashFunction, /* hash function */
4238 JimReferencesHTKeyDup, /* key dup */
4239 NULL, /* val dup */
4240 JimReferencesHTKeyCompare, /* key compare */
4241 JimReferencesHTKeyDestructor, /* key destructor */
4242 NULL /* val destructor */
4243 };
4244
4245 /* #define JIM_DEBUG_GC 1 */
4246
4247 /* Performs the garbage collection. */
4248 int Jim_Collect(Jim_Interp *interp)
4249 {
4250 Jim_HashTable marks;
4251 Jim_HashTableIterator *htiter;
4252 Jim_HashEntry *he;
4253 Jim_Obj *objPtr;
4254 int collected = 0;
4255
4256 /* Avoid recursive calls */
4257 if (interp->lastCollectId == -1) {
4258 /* Jim_Collect() already running. Return just now. */
4259 return 0;
4260 }
4261 interp->lastCollectId = -1;
4262
4263 /* Mark all the references found into the 'mark' hash table.
4264 * The references are searched in every live object that
4265 * is of a type that can contain references. */
4266 Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4267 objPtr = interp->liveList;
4268 while(objPtr) {
4269 if (objPtr->typePtr == NULL ||
4270 objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4271 const char *str, *p;
4272 int len;
4273
4274 /* If the object is of type reference, to get the
4275 * Id is simple... */
4276 if (objPtr->typePtr == &referenceObjType) {
4277 Jim_AddHashEntry(&marks,
4278 &objPtr->internalRep.refValue.id, NULL);
4279 #ifdef JIM_DEBUG_GC
4280 Jim_fprintf(interp,interp->cookie_stdout,
4281 "MARK (reference): %d refcount: %d" JIM_NL,
4282 (int) objPtr->internalRep.refValue.id,
4283 objPtr->refCount);
4284 #endif
4285 objPtr = objPtr->nextObjPtr;
4286 continue;
4287 }
4288 /* Get the string repr of the object we want
4289 * to scan for references. */
4290 p = str = Jim_GetString(objPtr, &len);
4291 /* Skip objects too little to contain references. */
4292 if (len < JIM_REFERENCE_SPACE) {
4293 objPtr = objPtr->nextObjPtr;
4294 continue;
4295 }
4296 /* Extract references from the object string repr. */
4297 while(1) {
4298 int i;
4299 jim_wide id;
4300 char buf[21];
4301
4302 if ((p = strstr(p, "<reference.<")) == NULL)
4303 break;
4304 /* Check if it's a valid reference. */
4305 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4306 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4307 for (i = 21; i <= 40; i++)
4308 if (!isdigit((int)p[i]))
4309 break;
4310 /* Get the ID */
4311 memcpy(buf, p+21, 20);
4312 buf[20] = '\0';
4313 Jim_StringToWide(buf, &id, 10);
4314
4315 /* Ok, a reference for the given ID
4316 * was found. Mark it. */
4317 Jim_AddHashEntry(&marks, &id, NULL);
4318 #ifdef JIM_DEBUG_GC
4319 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4320 #endif
4321 p += JIM_REFERENCE_SPACE;
4322 }
4323 }
4324 objPtr = objPtr->nextObjPtr;
4325 }
4326
4327 /* Run the references hash table to destroy every reference that
4328 * is not referenced outside (not present in the mark HT). */
4329 htiter = Jim_GetHashTableIterator(&interp->references);
4330 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4331 const jim_wide *refId;
4332 Jim_Reference *refPtr;
4333
4334 refId = he->key;
4335 /* Check if in the mark phase we encountered
4336 * this reference. */
4337 if (Jim_FindHashEntry(&marks, refId) == NULL) {
4338 #ifdef JIM_DEBUG_GC
4339 Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4340 #endif
4341 collected++;
4342 /* Drop the reference, but call the
4343 * finalizer first if registered. */
4344 refPtr = he->val;
4345 if (refPtr->finalizerCmdNamePtr) {
4346 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4347 Jim_Obj *objv[3], *oldResult;
4348
4349 JimFormatReference(refstr, refPtr, *refId);
4350
4351 objv[0] = refPtr->finalizerCmdNamePtr;
4352 objv[1] = Jim_NewStringObjNoAlloc(interp,
4353 refstr, 32);
4354 objv[2] = refPtr->objPtr;
4355 Jim_IncrRefCount(objv[0]);
4356 Jim_IncrRefCount(objv[1]);
4357 Jim_IncrRefCount(objv[2]);
4358
4359 /* Drop the reference itself */
4360 Jim_DeleteHashEntry(&interp->references, refId);
4361
4362 /* Call the finalizer. Errors ignored. */
4363 oldResult = interp->result;
4364 Jim_IncrRefCount(oldResult);
4365 Jim_EvalObjVector(interp, 3, objv);
4366 Jim_SetResult(interp, oldResult);
4367 Jim_DecrRefCount(interp, oldResult);
4368
4369 Jim_DecrRefCount(interp, objv[0]);
4370 Jim_DecrRefCount(interp, objv[1]);
4371 Jim_DecrRefCount(interp, objv[2]);
4372 } else {
4373 Jim_DeleteHashEntry(&interp->references, refId);
4374 }
4375 }
4376 }
4377 Jim_FreeHashTableIterator(htiter);
4378 Jim_FreeHashTable(&marks);
4379 interp->lastCollectId = interp->referenceNextId;
4380 interp->lastCollectTime = time(NULL);
4381 return collected;
4382 }
4383
4384 #define JIM_COLLECT_ID_PERIOD 5000
4385 #define JIM_COLLECT_TIME_PERIOD 300
4386
4387 void Jim_CollectIfNeeded(Jim_Interp *interp)
4388 {
4389 jim_wide elapsedId;
4390 int elapsedTime;
4391
4392 elapsedId = interp->referenceNextId - interp->lastCollectId;
4393 elapsedTime = time(NULL) - interp->lastCollectTime;
4394
4395
4396 if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4397 elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4398 Jim_Collect(interp);
4399 }
4400 }
4401
4402 /* -----------------------------------------------------------------------------
4403 * Interpreter related functions
4404 * ---------------------------------------------------------------------------*/
4405
4406 Jim_Interp *Jim_CreateInterp(void)
4407 {
4408 Jim_Interp *i = Jim_Alloc(sizeof(*i));
4409 Jim_Obj *pathPtr;
4410
4411 i->errorLine = 0;
4412 i->errorFileName = Jim_StrDup("");
4413 i->numLevels = 0;
4414 i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4415 i->returnCode = JIM_OK;
4416 i->exitCode = 0;
4417 i->procEpoch = 0;
4418 i->callFrameEpoch = 0;
4419 i->liveList = i->freeList = NULL;
4420 i->scriptFileName = Jim_StrDup("");
4421 i->referenceNextId = 0;
4422 i->lastCollectId = 0;
4423 i->lastCollectTime = time(NULL);
4424 i->freeFramesList = NULL;
4425 i->prngState = NULL;
4426 i->evalRetcodeLevel = -1;
4427 i->cookie_stdin = stdin;
4428 i->cookie_stdout = stdout;
4429 i->cookie_stderr = stderr;
4430 i->cb_fwrite = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4431 i->cb_fread = ((size_t (*)( void *, size_t, size_t, void *))(fread));
4432 i->cb_vfprintf = ((int (*)( void *, const char *fmt, va_list ))(vfprintf));
4433 i->cb_fflush = ((int (*)( void *))(fflush));
4434 i->cb_fgets = ((char * (*)( char *, int, void *))(fgets));
4435
4436 /* Note that we can create objects only after the
4437 * interpreter liveList and freeList pointers are
4438 * initialized to NULL. */
4439 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4440 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4441 Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4442 NULL);
4443 Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4444 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4445 Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4446 i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4447 i->emptyObj = Jim_NewEmptyStringObj(i);
4448 i->result = i->emptyObj;
4449 i->stackTrace = Jim_NewListObj(i, NULL, 0);
4450 i->unknown = Jim_NewStringObj(i, "unknown", -1);
4451 i->unknown_called = 0;
4452 Jim_IncrRefCount(i->emptyObj);
4453 Jim_IncrRefCount(i->result);
4454 Jim_IncrRefCount(i->stackTrace);
4455 Jim_IncrRefCount(i->unknown);
4456
4457 /* Initialize key variables every interpreter should contain */
4458 pathPtr = Jim_NewStringObj(i, "./", -1);
4459 Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4460 Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4461
4462 /* Export the core API to extensions */
4463 JimRegisterCoreApi(i);
4464 return i;
4465 }
4466
4467 /* This is the only function Jim exports directly without
4468 * to use the STUB system. It is only used by embedders
4469 * in order to get an interpreter with the Jim API pointers
4470 * registered. */
4471 Jim_Interp *ExportedJimCreateInterp(void)
4472 {
4473 return Jim_CreateInterp();
4474 }
4475
4476 void Jim_FreeInterp(Jim_Interp *i)
4477 {
4478 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4479 Jim_Obj *objPtr, *nextObjPtr;
4480
4481 Jim_DecrRefCount(i, i->emptyObj);
4482 Jim_DecrRefCount(i, i->result);
4483 Jim_DecrRefCount(i, i->stackTrace);
4484 Jim_DecrRefCount(i, i->unknown);
4485 Jim_Free((void*)i->errorFileName);
4486 Jim_Free((void*)i->scriptFileName);
4487 Jim_FreeHashTable(&i->commands);
4488 Jim_FreeHashTable(&i->references);
4489 Jim_FreeHashTable(&i->stub);
4490 Jim_FreeHashTable(&i->assocData);
4491 Jim_FreeHashTable(&i->packages);
4492 Jim_Free(i->prngState);
4493 /* Free the call frames list */
4494 while(cf) {
4495 prevcf = cf->parentCallFrame;
4496 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4497 cf = prevcf;
4498 }
4499 /* Check that the live object list is empty, otherwise
4500 * there is a memory leak. */
4501 if (i->liveList != NULL) {
4502 Jim_Obj *objPtr = i->liveList;
4503
4504 Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4505 Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4506 while(objPtr) {
4507 const char *type = objPtr->typePtr ?
4508 objPtr->typePtr->name : "";
4509 Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4510 objPtr, type,
4511 objPtr->bytes ? objPtr->bytes
4512 : "(null)", objPtr->refCount);
4513 if (objPtr->typePtr == &sourceObjType) {
4514 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4515 objPtr->internalRep.sourceValue.fileName,
4516 objPtr->internalRep.sourceValue.lineNumber);
4517 }
4518 objPtr = objPtr->nextObjPtr;
4519 }
4520 Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4521 Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4522 }
4523 /* Free all the freed objects. */
4524 objPtr = i->freeList;
4525 while (objPtr) {
4526 nextObjPtr = objPtr->nextObjPtr;
4527 Jim_Free(objPtr);
4528 objPtr = nextObjPtr;
4529 }
4530 /* Free cached CallFrame structures */
4531 cf = i->freeFramesList;
4532 while(cf) {
4533 nextcf = cf->nextFramePtr;
4534 if (cf->vars.table != NULL)
4535 Jim_Free(cf->vars.table);
4536 Jim_Free(cf);
4537 cf = nextcf;
4538 }
4539 /* Free the sharedString hash table. Make sure to free it
4540 * after every other Jim_Object was freed. */
4541 Jim_FreeHashTable(&i->sharedStrings);
4542 /* Free the interpreter structure. */
4543 Jim_Free(i);
4544 }
4545
4546 /* Store the call frame relative to the level represented by
4547 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4548 * level is assumed to be '1'.
4549 *
4550 * If a newLevelptr int pointer is specified, the function stores
4551 * the absolute level integer value of the new target callframe into
4552 * *newLevelPtr. (this is used to adjust interp->numLevels
4553 * in the implementation of [uplevel], so that [info level] will
4554 * return a correct information).
4555 *
4556 * This function accepts the 'level' argument in the form
4557 * of the commands [uplevel] and [upvar].
4558 *
4559 * For a function accepting a relative integer as level suitable
4560 * for implementation of [info level ?level?] check the
4561 * GetCallFrameByInteger() function. */
4562 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4563 Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4564 {
4565 long level;
4566 const char *str;
4567 Jim_CallFrame *framePtr;
4568
4569 if (newLevelPtr) *newLevelPtr = interp->numLevels;
4570 if (levelObjPtr) {
4571 str = Jim_GetString(levelObjPtr, NULL);
4572 if (str[0] == '#') {
4573 char *endptr;
4574 /* speedup for the toplevel (level #0) */
4575 if (str[1] == '0' && str[2] == '\0') {
4576 if (newLevelPtr) *newLevelPtr = 0;
4577 *framePtrPtr = interp->topFramePtr;
4578 return JIM_OK;
4579 }
4580
4581 level = strtol(str+1, &endptr, 0);
4582 if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4583 goto badlevel;
4584 /* An 'absolute' level is converted into the
4585 * 'number of levels to go back' format. */
4586 level = interp->numLevels - level;
4587 if (level < 0) goto badlevel;
4588 } else {
4589 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4590 goto badlevel;
4591 }
4592 } else {
4593 str = "1"; /* Needed to format the error message. */
4594 level = 1;
4595 }
4596 /* Lookup */
4597 framePtr = interp->framePtr;
4598 if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4599 while (level--) {
4600 framePtr = framePtr->parentCallFrame;
4601 if (framePtr == NULL) goto badlevel;
4602 }
4603 *framePtrPtr = framePtr;
4604 return JIM_OK;
4605 badlevel:
4606 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4607 Jim_AppendStrings(interp, Jim_GetResult(interp),
4608 "bad level \"", str, "\"", NULL);
4609 return JIM_ERR;
4610 }
4611
4612 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4613 * as a relative integer like in the [info level ?level?] command. */
4614 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4615 Jim_CallFrame **framePtrPtr)
4616 {
4617 jim_wide level;
4618 jim_wide relLevel; /* level relative to the current one. */
4619 Jim_CallFrame *framePtr;
4620
4621 if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4622 goto badlevel;
4623 if (level > 0) {
4624 /* An 'absolute' level is converted into the
4625 * 'number of levels to go back' format. */
4626 relLevel = interp->numLevels - level;
4627 } else {
4628 relLevel = -level;
4629 }
4630 /* Lookup */
4631 framePtr = interp->framePtr;
4632 while (relLevel--) {
4633 framePtr = framePtr->parentCallFrame;
4634 if (framePtr == NULL) goto badlevel;
4635 }
4636 *framePtrPtr = framePtr;
4637 return JIM_OK;
4638 badlevel:
4639 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4640 Jim_AppendStrings(interp, Jim_GetResult(interp),
4641 "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4642 return JIM_ERR;
4643 }
4644
4645 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4646 {
4647 Jim_Free((void*)interp->errorFileName);
4648 interp->errorFileName = Jim_StrDup(filename);
4649 }
4650
4651 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4652 {
4653 interp->errorLine = linenr;
4654 }
4655
4656 static void JimResetStackTrace(Jim_Interp *interp)
4657 {
4658 Jim_DecrRefCount(interp, interp->stackTrace);
4659 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4660 Jim_IncrRefCount(interp->stackTrace);
4661 }
4662
4663 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4664 const char *filename, int linenr)
4665 {
4666 /* No need to add this dummy entry to the stack trace */
4667 if (strcmp(procname, "unknown") == 0) {
4668 return;
4669 }
4670
4671 if (Jim_IsShared(interp->stackTrace)) {
4672 interp->stackTrace =
4673 Jim_DuplicateObj(interp, interp->stackTrace);
4674 Jim_IncrRefCount(interp->stackTrace);
4675 }
4676 Jim_ListAppendElement(interp, interp->stackTrace,
4677 Jim_NewStringObj(interp, procname, -1));
4678 Jim_ListAppendElement(interp, interp->stackTrace,
4679 Jim_NewStringObj(interp, filename, -1));
4680 Jim_ListAppendElement(interp, interp->stackTrace,
4681 Jim_NewIntObj(interp, linenr));
4682 }
4683
4684 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4685 {
4686 AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4687 assocEntryPtr->delProc = delProc;
4688 assocEntryPtr->data = data;
4689 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4690 }
4691
4692 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4693 {
4694 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4695 if (entryPtr != NULL) {
4696 AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4697 return assocEntryPtr->data;
4698 }
4699 return NULL;
4700 }
4701
4702 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4703 {
4704 return Jim_DeleteHashEntry(&interp->assocData, key);
4705 }
4706
4707 int Jim_GetExitCode(Jim_Interp *interp) {
4708 return interp->exitCode;
4709 }
4710
4711 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4712 {
4713 if (fp != NULL) interp->cookie_stdin = fp;
4714 return interp->cookie_stdin;
4715 }
4716
4717 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4718 {
4719 if (fp != NULL) interp->cookie_stdout = fp;
4720 return interp->cookie_stdout;
4721 }
4722
4723 void *Jim_SetStderr(Jim_Interp *interp, void *fp)
4724 {
4725 if (fp != NULL) interp->cookie_stderr = fp;
4726 return interp->cookie_stderr;
4727 }
4728
4729 /* -----------------------------------------------------------------------------
4730 * Shared strings.
4731 * Every interpreter has an hash table where to put shared dynamically
4732 * allocate strings that are likely to be used a lot of times.
4733 * For example, in the 'source' object type, there is a pointer to
4734 * the filename associated with that object. Every script has a lot
4735 * of this objects with the identical file name, so it is wise to share
4736 * this info.
4737 *
4738 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4739 * returns the pointer to the shared string. Every time a reference
4740 * to the string is no longer used, the user should call
4741 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4742 * a given string, it is removed from the hash table.
4743 * ---------------------------------------------------------------------------*/
4744 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4745 {
4746 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4747
4748 if (he == NULL) {
4749 char *strCopy = Jim_StrDup(str);
4750
4751 Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4752 return strCopy;
4753 } else {
4754 long refCount = (long) he->val;
4755
4756 refCount++;
4757 he->val = (void*) refCount;
4758 return he->key;
4759 }
4760 }
4761
4762 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4763 {
4764 long refCount;
4765 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4766
4767 if (he == NULL)
4768 Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4769 "unknown shared string '%s'", str);
4770 refCount = (long) he->val;
4771 refCount--;
4772 if (refCount == 0) {
4773 Jim_DeleteHashEntry(&interp->sharedStrings, str);
4774 } else {
4775 he->val = (void*) refCount;
4776 }
4777 }
4778
4779 /* -----------------------------------------------------------------------------
4780 * Integer object
4781 * ---------------------------------------------------------------------------*/
4782 #define JIM_INTEGER_SPACE 24
4783
4784 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4785 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4786
4787 static Jim_ObjType intObjType = {
4788 "int",
4789 NULL,
4790 NULL,
4791 UpdateStringOfInt,
4792 JIM_TYPE_NONE,
4793 };
4794
4795 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4796 {
4797 int len;
4798 char buf[JIM_INTEGER_SPACE+1];
4799
4800 len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4801 objPtr->bytes = Jim_Alloc(len+1);
4802 memcpy(objPtr->bytes, buf, len+1);
4803 objPtr->length = len;
4804 }
4805
4806 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4807 {
4808 jim_wide wideValue;
4809 const char *str;
4810
4811 /* Get the string representation */
4812 str = Jim_GetString(objPtr, NULL);
4813 /* Try to convert into a jim_wide */
4814 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4815 if (flags & JIM_ERRMSG) {
4816 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4817 Jim_AppendStrings(interp, Jim_GetResult(interp),
4818 "expected integer but got \"", str, "\"", NULL);
4819 }
4820 return JIM_ERR;
4821 }
4822 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4823 errno == ERANGE) {
4824 Jim_SetResultString(interp,
4825 "Integer value too big to be represented", -1);
4826 return JIM_ERR;
4827 }
4828 /* Free the old internal repr and set the new one. */
4829 Jim_FreeIntRep(interp, objPtr);
4830 objPtr->typePtr = &intObjType;
4831 objPtr->internalRep.wideValue = wideValue;
4832 return JIM_OK;
4833 }
4834
4835 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4836 {
4837 if (objPtr->typePtr != &intObjType &&
4838 SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4839 return JIM_ERR;
4840 *widePtr = objPtr->internalRep.wideValue;
4841 return JIM_OK;
4842 }
4843
4844 /* Get a wide but does not set an error if the format is bad. */
4845 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4846 jim_wide *widePtr)
4847 {
4848 if (objPtr->typePtr != &intObjType &&
4849 SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4850 return JIM_ERR;
4851 *widePtr = objPtr->internalRep.wideValue;
4852 return JIM_OK;
4853 }
4854
4855 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4856 {
4857 jim_wide wideValue;
4858 int retval;
4859
4860 retval = Jim_GetWide(interp, objPtr, &wideValue);
4861 if (retval == JIM_OK) {
4862 *longPtr = (long) wideValue;
4863 return JIM_OK;
4864 }
4865 return JIM_ERR;
4866 }
4867
4868 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4869 {
4870 if (Jim_IsShared(objPtr))
4871 Jim_Panic(interp,"Jim_SetWide called with shared object");
4872 if (objPtr->typePtr != &intObjType) {
4873 Jim_FreeIntRep(interp, objPtr);
4874 objPtr->typePtr = &intObjType;
4875 }
4876 Jim_InvalidateStringRep(objPtr);
4877 objPtr->internalRep.wideValue = wideValue;
4878 }
4879
4880 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4881 {
4882 Jim_Obj *objPtr;
4883
4884 objPtr = Jim_NewObj(interp);
4885 objPtr->typePtr = &intObjType;
4886 objPtr->bytes = NULL;
4887 objPtr->internalRep.wideValue = wideValue;
4888 return objPtr;
4889 }
4890
4891 /* -----------------------------------------------------------------------------
4892 * Double object
4893 * ---------------------------------------------------------------------------*/
4894 #define JIM_DOUBLE_SPACE 30
4895
4896 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4897 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4898
4899 static Jim_ObjType doubleObjType = {
4900 "double",
4901 NULL,
4902 NULL,
4903 UpdateStringOfDouble,
4904 JIM_TYPE_NONE,
4905 };
4906
4907 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4908 {
4909 int len;
4910 char buf[JIM_DOUBLE_SPACE+1];
4911
4912 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4913 objPtr->bytes = Jim_Alloc(len+1);
4914 memcpy(objPtr->bytes, buf, len+1);
4915 objPtr->length = len;
4916 }
4917
4918 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4919 {
4920 double doubleValue;
4921 const char *str;
4922
4923 /* Get the string representation */
4924 str = Jim_GetString(objPtr, NULL);
4925 /* Try to convert into a double */
4926 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4927 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4928 Jim_AppendStrings(interp, Jim_GetResult(interp),
4929 "expected number but got '", str, "'", NULL);
4930 return JIM_ERR;
4931 }
4932 /* Free the old internal repr and set the new one. */
4933 Jim_FreeIntRep(interp, objPtr);
4934 objPtr->typePtr = &doubleObjType;
4935 objPtr->internalRep.doubleValue = doubleValue;
4936 return JIM_OK;
4937 }
4938
4939 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4940 {
4941 if (objPtr->typePtr != &doubleObjType &&
4942 SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4943 return JIM_ERR;
4944 *doublePtr = objPtr->internalRep.doubleValue;
4945 return JIM_OK;
4946 }
4947
4948 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4949 {
4950 if (Jim_IsShared(objPtr))
4951 Jim_Panic(interp,"Jim_SetDouble called with shared object");
4952 if (objPtr->typePtr != &doubleObjType) {
4953 Jim_FreeIntRep(interp, objPtr);
4954 objPtr->typePtr = &doubleObjType;
4955 }
4956 Jim_InvalidateStringRep(objPtr);
4957 objPtr->internalRep.doubleValue = doubleValue;
4958 }
4959
4960 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4961 {
4962 Jim_Obj *objPtr;
4963
4964 objPtr = Jim_NewObj(interp);
4965 objPtr->typePtr = &doubleObjType;
4966 objPtr->bytes = NULL;
4967 objPtr->internalRep.doubleValue = doubleValue;
4968 return objPtr;
4969 }
4970
4971 /* -----------------------------------------------------------------------------
4972 * List object
4973 * ---------------------------------------------------------------------------*/
4974 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4975 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4976 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4977 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4978 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4979
4980 /* Note that while the elements of the list may contain references,
4981 * the list object itself can't. This basically means that the
4982 * list object string representation as a whole can't contain references
4983 * that are not presents in the single elements. */
4984 static Jim_ObjType listObjType = {
4985 "list",
4986 FreeListInternalRep,
4987 DupListInternalRep,
4988 UpdateStringOfList,
4989 JIM_TYPE_NONE,
4990 };
4991
4992 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4993 {
4994 int i;
4995
4996 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4997 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4998 }
4999 Jim_Free(objPtr->internalRep.listValue.ele);
5000 }
5001
5002 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5003 {
5004 int i;
5005 JIM_NOTUSED(interp);
5006
5007 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
5008 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
5009 dupPtr->internalRep.listValue.ele =
5010 Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
5011 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
5012 sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
5013 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
5014 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
5015 }
5016 dupPtr->typePtr = &listObjType;
5017 }
5018
5019 /* The following function checks if a given string can be encoded
5020 * into a list element without any kind of quoting, surrounded by braces,
5021 * or using escapes to quote. */
5022 #define JIM_ELESTR_SIMPLE 0
5023 #define JIM_ELESTR_BRACE 1
5024 #define JIM_ELESTR_QUOTE 2
5025 static int ListElementQuotingType(const char *s, int len)
5026 {
5027 int i, level, trySimple = 1;
5028
5029 /* Try with the SIMPLE case */
5030 if (len == 0) return JIM_ELESTR_BRACE;
5031 if (s[0] == '"' || s[0] == '{') {
5032 trySimple = 0;
5033 goto testbrace;
5034 }
5035 for (i = 0; i < len; i++) {
5036 switch(s[i]) {
5037 case ' ':
5038 case '$':
5039 case '"':
5040 case '[':
5041 case ']':
5042 case ';':
5043 case '\\':
5044 case '\r':
5045 case '\n':
5046 case '\t':
5047 case '\f':
5048 case '\v':
5049 trySimple = 0;
5050 case '{':
5051 case '}':
5052 goto testbrace;
5053 }
5054 }
5055 return JIM_ELESTR_SIMPLE;
5056
5057 testbrace:
5058 /* Test if it's possible to do with braces */
5059 if (s[len-1] == '\\' ||
5060 s[len-1] == ']') return JIM_ELESTR_QUOTE;
5061 level = 0;
5062 for (i = 0; i < len; i++) {
5063 switch(s[i]) {
5064 case '{': level++; break;
5065 case '}': level--;
5066 if (level < 0) return JIM_ELESTR_QUOTE;
5067 break;
5068 case '\\':
5069 if (s[i+1] == '\n')
5070 return JIM_ELESTR_QUOTE;
5071 else
5072 if (s[i+1] != '\0') i++;
5073 break;
5074 }
5075 }
5076 if (level == 0) {
5077 if (!trySimple) return JIM_ELESTR_BRACE;
5078 for (i = 0; i < len; i++) {
5079 switch(s[i]) {
5080 case ' ':
5081 case '$':
5082 case '"':
5083 case '[':
5084 case ']':
5085 case ';':
5086 case '\\':
5087 case '\r':
5088 case '\n':
5089 case '\t':
5090 case '\f':
5091 case '\v':
5092 return JIM_ELESTR_BRACE;
5093 break;
5094 }
5095 }
5096 return JIM_ELESTR_SIMPLE;
5097 }
5098 return JIM_ELESTR_QUOTE;
5099 }
5100
5101 /* Returns the malloc-ed representation of a string
5102 * using backslash to quote special chars. */
5103 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
5104 {
5105 char *q = Jim_Alloc(len*2+1), *p;
5106
5107 p = q;
5108 while(*s) {
5109 switch (*s) {
5110 case ' ':
5111 case '$':
5112 case '"':
5113 case '[':
5114 case ']':
5115 case '{':
5116 case '}':
5117 case ';':
5118 case '\\':
5119 *p++ = '\\';
5120 *p++ = *s++;
5121 break;
5122 case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
5123 case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
5124 case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
5125 case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
5126 case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
5127 default:
5128 *p++ = *s++;
5129 break;
5130 }
5131 }
5132 *p = '\0';
5133 *qlenPtr = p-q;
5134 return q;
5135 }
5136
5137 void UpdateStringOfList(struct Jim_Obj *objPtr)
5138 {
5139 int i, bufLen, realLength;
5140 const char *strRep;
5141 char *p;
5142 int *quotingType;
5143 Jim_Obj **ele = objPtr->internalRep.listValue.ele;
5144
5145 /* (Over) Estimate the space needed. */
5146 quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
5147 bufLen = 0;
5148 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5149 int len;
5150
5151 strRep = Jim_GetString(ele[i], &len);
5152 quotingType[i] = ListElementQuotingType(strRep, len);
5153 switch (quotingType[i]) {
5154 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5155 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5156 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5157 }
5158 bufLen++; /* elements separator. */
5159 }
5160 bufLen++;
5161
5162 /* Generate the string rep. */
5163 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5164 realLength = 0;
5165 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5166 int len, qlen;
5167 const char *strRep = Jim_GetString(ele[i], &len);
5168 char *q;
5169
5170 switch(quotingType[i]) {
5171 case JIM_ELESTR_SIMPLE:
5172 memcpy(p, strRep, len);
5173 p += len;
5174 realLength += len;
5175 break;
5176 case JIM_ELESTR_BRACE:
5177 *p++ = '{';
5178 memcpy(p, strRep, len);
5179 p += len;
5180 *p++ = '}';
5181 realLength += len+2;
5182 break;
5183 case JIM_ELESTR_QUOTE:
5184 q = BackslashQuoteString(strRep, len, &qlen);
5185 memcpy(p, q, qlen);
5186 Jim_Free(q);
5187 p += qlen;
5188 realLength += qlen;
5189 break;
5190 }
5191 /* Add a separating space */
5192 if (i+1 != objPtr->internalRep.listValue.len) {
5193 *p++ = ' ';
5194 realLength ++;
5195 }
5196 }
5197 *p = '\0'; /* nul term. */
5198 objPtr->length = realLength;
5199 Jim_Free(quotingType);
5200 }
5201
5202 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5203 {
5204 struct JimParserCtx parser;
5205 const char *str;
5206 int strLen;
5207
5208 /* Get the string representation */
5209 str = Jim_GetString(objPtr, &strLen);
5210
5211 /* Free the old internal repr just now and initialize the
5212 * new one just now. The string->list conversion can't fail. */
5213 Jim_FreeIntRep(interp, objPtr);
5214 objPtr->typePtr = &listObjType;
5215 objPtr->internalRep.listValue.len = 0;
5216 objPtr->internalRep.listValue.maxLen = 0;
5217 objPtr->internalRep.listValue.ele = NULL;
5218
5219 /* Convert into a list */
5220 JimParserInit(&parser, str, strLen, 1);
5221 while(!JimParserEof(&parser)) {
5222 char *token;
5223 int tokenLen, type;
5224 Jim_Obj *elementPtr;
5225
5226 JimParseList(&parser);
5227 if (JimParserTtype(&parser) != JIM_TT_STR &&
5228 JimParserTtype(&parser) != JIM_TT_ESC)
5229 continue;
5230 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5231 elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5232 ListAppendElement(objPtr, elementPtr);
5233 }
5234 return JIM_OK;
5235 }
5236
5237 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
5238 int len)
5239 {
5240 Jim_Obj *objPtr;
5241 int i;
5242
5243 objPtr = Jim_NewObj(interp);
5244 objPtr->typePtr = &listObjType;
5245 objPtr->bytes = NULL;
5246 objPtr->internalRep.listValue.ele = NULL;
5247 objPtr->internalRep.listValue.len = 0;
5248 objPtr->internalRep.listValue.maxLen = 0;
5249 for (i = 0; i < len; i++) {
5250 ListAppendElement(objPtr, elements[i]);
5251 }
5252 return objPtr;
5253 }
5254
5255 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5256 * length of the vector. Note that the user of this function should make
5257 * sure that the list object can't shimmer while the vector returned
5258 * is in use, this vector is the one stored inside the internal representation
5259 * of the list object. This function is not exported, extensions should
5260 * always access to the List object elements using Jim_ListIndex(). */
5261 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5262 Jim_Obj ***listVec)
5263 {
5264 Jim_ListLength(interp, listObj, argc);
5265 assert(listObj->typePtr == &listObjType);
5266 *listVec = listObj->internalRep.listValue.ele;
5267 }
5268
5269 /* ListSortElements type values */
5270 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5271 JIM_LSORT_NOCASE_DECR};
5272
5273 /* Sort the internal rep of a list. */
5274 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5275 {
5276 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5277 }
5278
5279 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5280 {
5281 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5282 }
5283
5284 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5285 {
5286 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5287 }
5288
5289 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5290 {
5291 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5292 }
5293
5294 /* Sort a list *in place*. MUST be called with non-shared objects. */
5295 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5296 {
5297 typedef int (qsort_comparator)(const void *, const void *);
5298 int (*fn)(Jim_Obj**, Jim_Obj**);
5299 Jim_Obj **vector;
5300 int len;
5301
5302 if (Jim_IsShared(listObjPtr))
5303 Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5304 if (listObjPtr->typePtr != &listObjType)
5305 SetListFromAny(interp, listObjPtr);
5306
5307 vector = listObjPtr->internalRep.listValue.ele;
5308 len = listObjPtr->internalRep.listValue.len;
5309 switch (type) {
5310 case JIM_LSORT_ASCII: fn = ListSortString; break;
5311 case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
5312 case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
5313 case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
5314 default:
5315 fn = NULL; /* avoid warning */
5316 Jim_Panic(interp,"ListSort called with invalid sort type");
5317 }
5318 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5319 Jim_InvalidateStringRep(listObjPtr);
5320 }
5321
5322 /* This is the low-level function to append an element to a list.
5323 * The higher-level Jim_ListAppendElement() performs shared object
5324 * check and invalidate the string repr. This version is used
5325 * in the internals of the List Object and is not exported.
5326 *
5327 * NOTE: this function can be called only against objects
5328 * with internal type of List. */
5329 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5330 {
5331 int requiredLen = listPtr->internalRep.listValue.len + 1;
5332
5333 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5334 int maxLen = requiredLen * 2;
5335
5336 listPtr->internalRep.listValue.ele =
5337 Jim_Realloc(listPtr->internalRep.listValue.ele,
5338 sizeof(Jim_Obj*)*maxLen);
5339 listPtr->internalRep.listValue.maxLen = maxLen;
5340 }
5341 listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5342 objPtr;
5343 listPtr->internalRep.listValue.len ++;
5344 Jim_IncrRefCount(objPtr);
5345 }
5346
5347 /* This is the low-level function to insert elements into a list.
5348 * The higher-level Jim_ListInsertElements() performs shared object
5349 * check and invalidate the string repr. This version is used
5350 * in the internals of the List Object and is not exported.
5351 *
5352 * NOTE: this function can be called only against objects
5353 * with internal type of List. */
5354 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5355 Jim_Obj *const *elemVec)
5356 {
5357 int currentLen = listPtr->internalRep.listValue.len;
5358 int requiredLen = currentLen + elemc;
5359 int i;
5360 Jim_Obj **point;
5361
5362 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5363 int maxLen = requiredLen * 2;
5364
5365 listPtr->internalRep.listValue.ele =
5366 Jim_Realloc(listPtr->internalRep.listValue.ele,
5367 sizeof(Jim_Obj*)*maxLen);
5368 listPtr->internalRep.listValue.maxLen = maxLen;
5369 }
5370 point = listPtr->internalRep.listValue.ele + index;
5371 memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5372 for (i=0; i < elemc; ++i) {
5373 point[i] = elemVec[i];
5374 Jim_IncrRefCount(point[i]);
5375 }
5376 listPtr->internalRep.listValue.len += elemc;
5377 }
5378
5379 /* Appends every element of appendListPtr into listPtr.
5380 * Both have to be of the list type. */
5381 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5382 {
5383 int i, oldLen = listPtr->internalRep.listValue.len;
5384 int appendLen = appendListPtr->internalRep.listValue.len;
5385 int requiredLen = oldLen + appendLen;
5386
5387 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5388 int maxLen = requiredLen * 2;
5389
5390 listPtr->internalRep.listValue.ele =
5391 Jim_Realloc(listPtr->internalRep.listValue.ele,
5392 sizeof(Jim_Obj*)*maxLen);
5393 listPtr->internalRep.listValue.maxLen = maxLen;
5394 }
5395 for (i = 0; i < appendLen; i++) {
5396 Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5397 listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5398 Jim_IncrRefCount(objPtr);
5399 }
5400 listPtr->internalRep.listValue.len += appendLen;
5401 }
5402
5403 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5404 {
5405 if (Jim_IsShared(listPtr))
5406 Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5407 if (listPtr->typePtr != &listObjType)
5408 SetListFromAny(interp, listPtr);
5409 Jim_InvalidateStringRep(listPtr);
5410 ListAppendElement(listPtr, objPtr);
5411 }
5412
5413 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5414 {
5415 if (Jim_IsShared(listPtr))
5416 Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5417 if (listPtr->typePtr != &listObjType)
5418 SetListFromAny(interp, listPtr);
5419 Jim_InvalidateStringRep(listPtr);
5420 ListAppendList(listPtr, appendListPtr);
5421 }
5422
5423 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5424 {
5425 if (listPtr->typePtr != &listObjType)
5426 SetListFromAny(interp, listPtr);
5427 *intPtr = listPtr->internalRep.listValue.len;
5428 }
5429
5430 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5431 int objc, Jim_Obj *const *objVec)
5432 {
5433 if (Jim_IsShared(listPtr))
5434 Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5435 if (listPtr->typePtr != &listObjType)
5436 SetListFromAny(interp, listPtr);
5437 if (index >= 0 && index > listPtr->internalRep.listValue.len)
5438 index = listPtr->internalRep.listValue.len;
5439 else if (index < 0 )
5440 index = 0;
5441 Jim_InvalidateStringRep(listPtr);
5442 ListInsertElements(listPtr, index, objc, objVec);
5443 }
5444
5445 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5446 Jim_Obj **objPtrPtr, int flags)
5447 {
5448 if (listPtr->typePtr != &listObjType)
5449 SetListFromAny(interp, listPtr);
5450 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5451 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5452 if (flags & JIM_ERRMSG) {
5453 Jim_SetResultString(interp,
5454 "list index out of range", -1);
5455 }
5456 return JIM_ERR;
5457 }
5458 if (index < 0)
5459 index = listPtr->internalRep.listValue.len+index;
5460 *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5461 return JIM_OK;
5462 }
5463
5464 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5465 Jim_Obj *newObjPtr, int flags)
5466 {
5467 if (listPtr->typePtr != &listObjType)
5468 SetListFromAny(interp, listPtr);
5469 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5470 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5471 if (flags & JIM_ERRMSG) {
5472 Jim_SetResultString(interp,
5473 "list index out of range", -1);
5474 }
5475 return JIM_ERR;
5476 }
5477 if (index < 0)
5478 index = listPtr->internalRep.listValue.len+index;
5479 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5480 listPtr->internalRep.listValue.ele[index] = newObjPtr;
5481 Jim_IncrRefCount(newObjPtr);
5482 return JIM_OK;
5483 }
5484
5485 /* Modify the list stored into the variable named 'varNamePtr'
5486 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5487 * with the new element 'newObjptr'. */
5488 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5489 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5490 {
5491 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5492 int shared, i, index;
5493
5494 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5495 if (objPtr == NULL)
5496 return JIM_ERR;
5497 if ((shared = Jim_IsShared(objPtr)))
5498 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5499 for (i = 0; i < indexc-1; i++) {
5500 listObjPtr = objPtr;
5501 if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5502 goto err;
5503 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5504 JIM_ERRMSG) != JIM_OK) {
5505 goto err;
5506 }
5507 if (Jim_IsShared(objPtr)) {
5508 objPtr = Jim_DuplicateObj(interp, objPtr);
5509 ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5510 }
5511 Jim_InvalidateStringRep(listObjPtr);
5512 }
5513 if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5514 goto err;
5515 if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5516 goto err;
5517 Jim_InvalidateStringRep(objPtr);
5518 Jim_InvalidateStringRep(varObjPtr);
5519 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5520 goto err;
5521 Jim_SetResult(interp, varObjPtr);
5522 return JIM_OK;
5523 err:
5524 if (shared) {
5525 Jim_FreeNewObj(interp, varObjPtr);
5526 }
5527 return JIM_ERR;
5528 }
5529
5530 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5531 {
5532 int i;
5533
5534 /* If all the objects in objv are lists without string rep.
5535 * it's possible to return a list as result, that's the
5536 * concatenation of all the lists. */
5537 for (i = 0; i < objc; i++) {
5538 if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5539 break;
5540 }
5541 if (i == objc) {
5542 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5543 for (i = 0; i < objc; i++)
5544 Jim_ListAppendList(interp, objPtr, objv[i]);
5545 return objPtr;
5546 } else {
5547 /* Else... we have to glue strings together */
5548 int len = 0, objLen;
5549 char *bytes, *p;
5550
5551 /* Compute the length */
5552 for (i = 0; i < objc; i++) {
5553 Jim_GetString(objv[i], &objLen);
5554 len += objLen;
5555 }
5556 if (objc) len += objc-1;
5557 /* Create the string rep, and a stinrg object holding it. */
5558 p = bytes = Jim_Alloc(len+1);
5559 for (i = 0; i < objc; i++) {
5560 const char *s = Jim_GetString(objv[i], &objLen);
5561 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5562 {
5563 s++; objLen--; len--;
5564 }
5565 while (objLen && (s[objLen-1] == ' ' ||
5566 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5567 objLen--; len--;
5568 }
5569 memcpy(p, s, objLen);
5570 p += objLen;
5571 if (objLen && i+1 != objc) {
5572 *p++ = ' ';
5573 } else if (i+1 != objc) {
5574 /* Drop the space calcuated for this
5575 * element that is instead null. */
5576 len--;
5577 }
5578 }
5579 *p = '\0';
5580 return Jim_NewStringObjNoAlloc(interp, bytes, len);
5581 }
5582 }
5583
5584 /* Returns a list composed of the elements in the specified range.
5585 * first and start are directly accepted as Jim_Objects and
5586 * processed for the end?-index? case. */
5587 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5588 {
5589 int first, last;
5590 int len, rangeLen;
5591
5592 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5593 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5594 return NULL;
5595 Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5596 first = JimRelToAbsIndex(len, first);
5597 last = JimRelToAbsIndex(len, last);
5598 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5599 return Jim_NewListObj(interp,
5600 listObjPtr->internalRep.listValue.ele+first, rangeLen);
5601 }
5602
5603 /* -----------------------------------------------------------------------------
5604 * Dict object
5605 * ---------------------------------------------------------------------------*/
5606 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5607 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5608 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5609 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5610
5611 /* Dict HashTable Type.
5612 *
5613 * Keys and Values are Jim objects. */
5614
5615 unsigned int JimObjectHTHashFunction(const void *key)
5616 {
5617 const char *str;
5618 Jim_Obj *objPtr = (Jim_Obj*) key;
5619 int len, h;
5620
5621 str = Jim_GetString(objPtr, &len);
5622 h = Jim_GenHashFunction((unsigned char*)str, len);
5623 return h;
5624 }
5625
5626 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5627 {
5628 JIM_NOTUSED(privdata);
5629
5630 return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5631 }
5632
5633 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5634 {
5635 Jim_Obj *objPtr = val;
5636
5637 Jim_DecrRefCount(interp, objPtr);
5638 }
5639
5640 static Jim_HashTableType JimDictHashTableType = {
5641 JimObjectHTHashFunction, /* hash function */
5642 NULL, /* key dup */
5643 NULL, /* val dup */
5644 JimObjectHTKeyCompare, /* key compare */
5645 (void(*)(void*, const void*)) /* ATTENTION: const cast */
5646 JimObjectHTKeyValDestructor, /* key destructor */
5647 JimObjectHTKeyValDestructor /* val destructor */
5648 };
5649
5650 /* Note that while the elements of the dict may contain references,
5651 * the list object itself can't. This basically means that the
5652 * dict object string representation as a whole can't contain references
5653 * that are not presents in the single elements. */
5654 static Jim_ObjType dictObjType = {
5655 "dict",
5656 FreeDictInternalRep,
5657 DupDictInternalRep,
5658 UpdateStringOfDict,
5659 JIM_TYPE_NONE,
5660 };
5661
5662 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5663 {
5664 JIM_NOTUSED(interp);
5665
5666 Jim_FreeHashTable(objPtr->internalRep.ptr);
5667 Jim_Free(objPtr->internalRep.ptr);
5668 }
5669
5670 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5671 {
5672 Jim_HashTable *ht, *dupHt;
5673 Jim_HashTableIterator *htiter;
5674 Jim_HashEntry *he;
5675
5676 /* Create a new hash table */
5677 ht = srcPtr->internalRep.ptr;
5678 dupHt = Jim_Alloc(sizeof(*dupHt));
5679 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5680 if (ht->size != 0)
5681 Jim_ExpandHashTable(dupHt, ht->size);
5682 /* Copy every element from the source to the dup hash table */
5683 htiter = Jim_GetHashTableIterator(ht);
5684 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5685 const Jim_Obj *keyObjPtr = he->key;
5686 Jim_Obj *valObjPtr = he->val;
5687
5688 Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
5689 Jim_IncrRefCount(valObjPtr);
5690 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5691 }
5692 Jim_FreeHashTableIterator(htiter);
5693
5694 dupPtr->internalRep.ptr = dupHt;
5695 dupPtr->typePtr = &dictObjType;
5696 }
5697
5698 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5699 {
5700 int i, bufLen, realLength;
5701 const char *strRep;
5702 char *p;
5703 int *quotingType, objc;
5704 Jim_HashTable *ht;
5705 Jim_HashTableIterator *htiter;
5706 Jim_HashEntry *he;
5707 Jim_Obj **objv;
5708
5709 /* Trun the hash table into a flat vector of Jim_Objects. */
5710 ht = objPtr->internalRep.ptr;
5711 objc = ht->used*2;
5712 objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5713 htiter = Jim_GetHashTableIterator(ht);
5714 i = 0;
5715 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5716 objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
5717 objv[i++] = he->val;
5718 }
5719 Jim_FreeHashTableIterator(htiter);
5720 /* (Over) Estimate the space needed. */
5721 quotingType = Jim_Alloc(sizeof(int)*objc);
5722 bufLen = 0;
5723 for (i = 0; i < objc; i++) {
5724 int len;
5725
5726 strRep = Jim_GetString(objv[i], &len);
5727 quotingType[i] = ListElementQuotingType(strRep, len);
5728 switch (quotingType[i]) {
5729 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5730 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5731 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5732 }
5733 bufLen++; /* elements separator. */
5734 }
5735 bufLen++;
5736
5737 /* Generate the string rep. */
5738 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5739 realLength = 0;
5740 for (i = 0; i < objc; i++) {
5741 int len, qlen;
5742 const char *strRep = Jim_GetString(objv[i], &len);
5743 char *q;
5744
5745 switch(quotingType[i]) {
5746 case JIM_ELESTR_SIMPLE:
5747 memcpy(p, strRep, len);
5748 p += len;
5749 realLength += len;
5750 break;
5751 case JIM_ELESTR_BRACE:
5752 *p++ = '{';
5753 memcpy(p, strRep, len);
5754 p += len;
5755 *p++ = '}';
5756 realLength += len+2;
5757 break;
5758 case JIM_ELESTR_QUOTE:
5759 q = BackslashQuoteString(strRep, len, &qlen);
5760 memcpy(p, q, qlen);
5761 Jim_Free(q);
5762 p += qlen;
5763 realLength += qlen;
5764 break;
5765 }
5766 /* Add a separating space */
5767 if (i+1 != objc) {
5768 *p++ = ' ';
5769 realLength ++;
5770 }
5771 }
5772 *p = '\0'; /* nul term. */
5773 objPtr->length = realLength;
5774 Jim_Free(quotingType);
5775 Jim_Free(objv);
5776 }
5777
5778 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5779 {
5780 struct JimParserCtx parser;
5781 Jim_HashTable *ht;
5782 Jim_Obj *objv[2];
5783 const char *str;
5784 int i, strLen;
5785
5786 /* Get the string representation */
5787 str = Jim_GetString(objPtr, &strLen);
5788
5789 /* Free the old internal repr just now and initialize the
5790 * new one just now. The string->list conversion can't fail. */
5791 Jim_FreeIntRep(interp, objPtr);
5792 ht = Jim_Alloc(sizeof(*ht));
5793 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5794 objPtr->typePtr = &dictObjType;
5795 objPtr->internalRep.ptr = ht;
5796
5797 /* Convert into a dict */
5798 JimParserInit(&parser, str, strLen, 1);
5799 i = 0;
5800 while(!JimParserEof(&parser)) {
5801 char *token;
5802 int tokenLen, type;
5803
5804 JimParseList(&parser);
5805 if (JimParserTtype(&parser) != JIM_TT_STR &&
5806 JimParserTtype(&parser) != JIM_TT_ESC)
5807 continue;
5808 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5809 objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5810 if (i == 2) {
5811 i = 0;
5812 Jim_IncrRefCount(objv[0]);
5813 Jim_IncrRefCount(objv[1]);
5814 if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5815 Jim_HashEntry *he;
5816 he = Jim_FindHashEntry(ht, objv[0]);
5817 Jim_DecrRefCount(interp, objv[0]);
5818 /* ATTENTION: const cast */
5819 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5820 he->val = objv[1];
5821 }
5822 }
5823 }
5824 if (i) {
5825 Jim_FreeNewObj(interp, objv[0]);
5826 objPtr->typePtr = NULL;
5827 Jim_FreeHashTable(ht);
5828 Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5829 return JIM_ERR;
5830 }
5831 return JIM_OK;
5832 }
5833
5834 /* Dict object API */
5835
5836 /* Add an element to a dict. objPtr must be of the "dict" type.
5837 * The higer-level exported function is Jim_DictAddElement().
5838 * If an element with the specified key already exists, the value
5839 * associated is replaced with the new one.
5840 *
5841 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5842 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5843 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5844 {
5845 Jim_HashTable *ht = objPtr->internalRep.ptr;
5846
5847 if (valueObjPtr == NULL) { /* unset */
5848 Jim_DeleteHashEntry(ht, keyObjPtr);
5849 return;
5850 }
5851 Jim_IncrRefCount(keyObjPtr);
5852 Jim_IncrRefCount(valueObjPtr);
5853 if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5854 Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5855 Jim_DecrRefCount(interp, keyObjPtr);
5856 /* ATTENTION: const cast */
5857 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5858 he->val = valueObjPtr;
5859 }
5860 }
5861
5862 /* Add an element, higher-level interface for DictAddElement().
5863 * If valueObjPtr == NULL, the key is removed if it exists. */
5864 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5865 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5866 {
5867 if (Jim_IsShared(objPtr))
5868 Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5869 if (objPtr->typePtr != &dictObjType) {
5870 if (SetDictFromAny(interp, objPtr) != JIM_OK)
5871 return JIM_ERR;
5872 }
5873 DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5874 Jim_InvalidateStringRep(objPtr);
5875 return JIM_OK;
5876 }
5877
5878 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5879 {
5880 Jim_Obj *objPtr;
5881 int i;
5882
5883 if (len % 2)
5884 Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5885
5886 objPtr = Jim_NewObj(interp);
5887 objPtr->typePtr = &dictObjType;
5888 objPtr->bytes = NULL;
5889 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5890 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5891 for (i = 0; i < len; i += 2)
5892 DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5893 return objPtr;
5894 }
5895
5896 /* Return the value associated to the specified dict key */
5897 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5898 Jim_Obj **objPtrPtr, int flags)
5899 {
5900 Jim_HashEntry *he;
5901 Jim_HashTable *ht;
5902
5903 if (dictPtr->typePtr != &dictObjType) {
5904 if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5905 return JIM_ERR;
5906 }
5907 ht = dictPtr->internalRep.ptr;
5908 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5909 if (flags & JIM_ERRMSG) {
5910 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5911 Jim_AppendStrings(interp, Jim_GetResult(interp),
5912 "key \"", Jim_GetString(keyPtr, NULL),
5913 "\" not found in dictionary", NULL);
5914 }
5915 return JIM_ERR;
5916 }
5917 *objPtrPtr = he->val;
5918 return JIM_OK;
5919 }
5920
5921 /* Return the value associated to the specified dict keys */
5922 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5923 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5924 {
5925 Jim_Obj *objPtr;
5926 int i;
5927
5928 if (keyc == 0) {
5929 *objPtrPtr = dictPtr;
5930 return JIM_OK;
5931 }
5932
5933 for (i = 0; i < keyc; i++) {
5934 if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5935 != JIM_OK)
5936 return JIM_ERR;
5937 dictPtr = objPtr;
5938 }
5939 *objPtrPtr = objPtr;
5940 return JIM_OK;
5941 }
5942
5943 /* Modify the dict stored into the variable named 'varNamePtr'
5944 * setting the element specified by the 'keyc' keys objects in 'keyv',
5945 * with the new value of the element 'newObjPtr'.
5946 *
5947 * If newObjPtr == NULL the operation is to remove the given key
5948 * from the dictionary. */
5949 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5950 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5951 {
5952 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5953 int shared, i;
5954
5955 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5956 if (objPtr == NULL) {
5957 if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5958 return JIM_ERR;
5959 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5960 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5961 Jim_FreeNewObj(interp, varObjPtr);
5962 return JIM_ERR;
5963 }
5964 }
5965 if ((shared = Jim_IsShared(objPtr)))
5966 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5967 for (i = 0; i < keyc-1; i++) {
5968 dictObjPtr = objPtr;
5969
5970 /* Check if it's a valid dictionary */
5971 if (dictObjPtr->typePtr != &dictObjType) {
5972 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5973 goto err;
5974 }
5975 /* Check if the given key exists. */
5976 Jim_InvalidateStringRep(dictObjPtr);
5977 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5978 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5979 {
5980 /* This key exists at the current level.
5981 * Make sure it's not shared!. */
5982 if (Jim_IsShared(objPtr)) {
5983 objPtr = Jim_DuplicateObj(interp, objPtr);
5984 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5985 }
5986 } else {
5987 /* Key not found. If it's an [unset] operation
5988 * this is an error. Only the last key may not
5989 * exist. */
5990 if (newObjPtr == NULL)
5991 goto err;
5992 /* Otherwise set an empty dictionary
5993 * as key's value. */
5994 objPtr = Jim_NewDictObj(interp, NULL, 0);
5995 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5996 }
5997 }
5998 if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5999 != JIM_OK)
6000 goto err;
6001 Jim_InvalidateStringRep(objPtr);
6002 Jim_InvalidateStringRep(varObjPtr);
6003 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
6004 goto err;
6005 Jim_SetResult(interp, varObjPtr);
6006 return JIM_OK;
6007 err:
6008 if (shared) {
6009 Jim_FreeNewObj(interp, varObjPtr);
6010 }
6011 return JIM_ERR;
6012 }
6013
6014 /* -----------------------------------------------------------------------------
6015 * Index object
6016 * ---------------------------------------------------------------------------*/
6017 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
6018 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6019
6020 static Jim_ObjType indexObjType = {
6021 "index",
6022 NULL,
6023 NULL,
6024 UpdateStringOfIndex,
6025 JIM_TYPE_NONE,
6026 };
6027
6028 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
6029 {
6030 int len;
6031 char buf[JIM_INTEGER_SPACE+1];
6032
6033 if (objPtr->internalRep.indexValue >= 0)
6034 len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
6035 else if (objPtr->internalRep.indexValue == -1)
6036 len = sprintf(buf, "end");
6037 else {
6038 len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
6039 }
6040 objPtr->bytes = Jim_Alloc(len+1);
6041 memcpy(objPtr->bytes, buf, len+1);
6042 objPtr->length = len;
6043 }
6044
6045 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6046 {
6047 int index, end = 0;
6048 const char *str;
6049
6050 /* Get the string representation */
6051 str = Jim_GetString(objPtr, NULL);
6052 /* Try to convert into an index */
6053 if (!strcmp(str, "end")) {
6054 index = 0;
6055 end = 1;
6056 } else {
6057 if (!strncmp(str, "end-", 4)) {
6058 str += 4;
6059 end = 1;
6060 }
6061 if (Jim_StringToIndex(str, &index) != JIM_OK) {
6062 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6063 Jim_AppendStrings(interp, Jim_GetResult(interp),
6064 "bad index \"", Jim_GetString(objPtr, NULL), "\": "
6065 "must be integer or end?-integer?", NULL);
6066 return JIM_ERR;
6067 }
6068 }
6069 if (end) {
6070 if (index < 0)
6071 index = INT_MAX;
6072 else
6073 index = -(index+1);
6074 } else if (!end && index < 0)
6075 index = -INT_MAX;
6076 /* Free the old internal repr and set the new one. */
6077 Jim_FreeIntRep(interp, objPtr);
6078 objPtr->typePtr = &indexObjType;
6079 objPtr->internalRep.indexValue = index;
6080 return JIM_OK;
6081 }
6082
6083 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
6084 {
6085 /* Avoid shimmering if the object is an integer. */
6086 if (objPtr->typePtr == &intObjType) {
6087 jim_wide val = objPtr->internalRep.wideValue;
6088 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
6089 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
6090 return JIM_OK;
6091 }
6092 }
6093 if (objPtr->typePtr != &indexObjType &&
6094 SetIndexFromAny(interp, objPtr) == JIM_ERR)
6095 return JIM_ERR;
6096 *indexPtr = objPtr->internalRep.indexValue;
6097 return JIM_OK;
6098 }
6099
6100 /* -----------------------------------------------------------------------------
6101 * Return Code Object.
6102 * ---------------------------------------------------------------------------*/
6103
6104 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6105
6106 static Jim_ObjType returnCodeObjType = {
6107 "return-code",
6108 NULL,
6109 NULL,
6110 NULL,
6111 JIM_TYPE_NONE,
6112 };
6113
6114 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6115 {
6116 const char *str;
6117 int strLen, returnCode;
6118 jim_wide wideValue;
6119
6120 /* Get the string representation */
6121 str = Jim_GetString(objPtr, &strLen);
6122 /* Try to convert into an integer */
6123 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
6124 returnCode = (int) wideValue;
6125 else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
6126 returnCode = JIM_OK;
6127 else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
6128 returnCode = JIM_ERR;
6129 else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
6130 returnCode = JIM_RETURN;
6131 else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
6132 returnCode = JIM_BREAK;
6133 else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
6134 returnCode = JIM_CONTINUE;
6135 else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
6136 returnCode = JIM_EVAL;
6137 else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
6138 returnCode = JIM_EXIT;
6139 else {
6140 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6141 Jim_AppendStrings(interp, Jim_GetResult(interp),
6142 "expected return code but got '", str, "'",
6143 NULL);
6144 return JIM_ERR;
6145 }
6146 /* Free the old internal repr and set the new one. */
6147 Jim_FreeIntRep(interp, objPtr);
6148 objPtr->typePtr = &returnCodeObjType;
6149 objPtr->internalRep.returnCode = returnCode;
6150 return JIM_OK;
6151 }
6152
6153 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
6154 {
6155 if (objPtr->typePtr != &returnCodeObjType &&
6156 SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
6157 return JIM_ERR;
6158 *intPtr = objPtr->internalRep.returnCode;
6159 return JIM_OK;
6160 }
6161
6162 /* -----------------------------------------------------------------------------
6163 * Expression Parsing
6164 * ---------------------------------------------------------------------------*/
6165 static int JimParseExprOperator(struct JimParserCtx *pc);
6166 static int JimParseExprNumber(struct JimParserCtx *pc);
6167 static int JimParseExprIrrational(struct JimParserCtx *pc);
6168
6169 /* Exrp's Stack machine operators opcodes. */
6170
6171 /* Binary operators (numbers) */
6172 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
6173 #define JIM_EXPROP_MUL 0
6174 #define JIM_EXPROP_DIV 1
6175 #define JIM_EXPROP_MOD 2
6176 #define JIM_EXPROP_SUB 3
6177 #define JIM_EXPROP_ADD 4
6178 #define JIM_EXPROP_LSHIFT 5
6179 #define JIM_EXPROP_RSHIFT 6
6180 #define JIM_EXPROP_ROTL 7
6181 #define JIM_EXPROP_ROTR 8
6182 #define JIM_EXPROP_LT 9
6183 #define JIM_EXPROP_GT 10
6184 #define JIM_EXPROP_LTE 11
6185 #define JIM_EXPROP_GTE 12
6186 #define JIM_EXPROP_NUMEQ 13
6187 #define JIM_EXPROP_NUMNE 14
6188 #define JIM_EXPROP_BITAND 15
6189 #define JIM_EXPROP_BITXOR 16
6190 #define JIM_EXPROP_BITOR 17
6191 #define JIM_EXPROP_LOGICAND 18
6192 #define JIM_EXPROP_LOGICOR 19
6193 #define JIM_EXPROP_LOGICAND_LEFT 20
6194 #define JIM_EXPROP_LOGICOR_LEFT 21
6195 #define JIM_EXPROP_POW 22
6196 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6197
6198 /* Binary operators (strings) */
6199 #define JIM_EXPROP_STREQ 23
6200 #define JIM_EXPROP_STRNE 24
6201
6202 /* Unary operators (numbers) */
6203 #define JIM_EXPROP_NOT 25
6204 #define JIM_EXPROP_BITNOT 26
6205 #define JIM_EXPROP_UNARYMINUS 27
6206 #define JIM_EXPROP_UNARYPLUS 28
6207 #define JIM_EXPROP_LOGICAND_RIGHT 29
6208 #define JIM_EXPROP_LOGICOR_RIGHT 30
6209
6210 /* Ternary operators */
6211 #define JIM_EXPROP_TERNARY 31
6212
6213 /* Operands */
6214 #define JIM_EXPROP_NUMBER 32
6215 #define JIM_EXPROP_COMMAND 33
6216 #define JIM_EXPROP_VARIABLE 34
6217 #define JIM_EXPROP_DICTSUGAR 35
6218 #define JIM_EXPROP_SUBST 36
6219 #define JIM_EXPROP_STRING 37
6220
6221 /* Operators table */
6222 typedef struct Jim_ExprOperator {
6223 const char *name;
6224 int precedence;
6225 int arity;
6226 int opcode;
6227 } Jim_ExprOperator;
6228
6229 /* name - precedence - arity - opcode */
6230 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6231 {"!", 300, 1, JIM_EXPROP_NOT},
6232 {"~", 300, 1, JIM_EXPROP_BITNOT},
6233 {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6234 {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6235
6236 {"**", 250, 2, JIM_EXPROP_POW},
6237
6238 {"*", 200, 2, JIM_EXPROP_MUL},
6239 {"/", 200, 2, JIM_EXPROP_DIV},
6240 {"%", 200, 2, JIM_EXPROP_MOD},
6241
6242 {"-", 100, 2, JIM_EXPROP_SUB},
6243 {"+", 100, 2, JIM_EXPROP_ADD},
6244
6245 {"<<<", 90, 3, JIM_EXPROP_ROTL},
6246 {">>>", 90, 3, JIM_EXPROP_ROTR},
6247 {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6248 {">>", 90, 2, JIM_EXPROP_RSHIFT},
6249
6250 {"<", 80, 2, JIM_EXPROP_LT},
6251 {">", 80, 2, JIM_EXPROP_GT},
6252 {"<=", 80, 2, JIM_EXPROP_LTE},
6253 {">=", 80, 2, JIM_EXPROP_GTE},
6254
6255 {"==", 70, 2, JIM_EXPROP_NUMEQ},
6256 {"!=", 70, 2, JIM_EXPROP_NUMNE},
6257
6258 {"eq", 60, 2, JIM_EXPROP_STREQ},
6259 {"ne", 60, 2, JIM_EXPROP_STRNE},
6260
6261 {"&", 50, 2, JIM_EXPROP_BITAND},
6262 {"^", 49, 2, JIM_EXPROP_BITXOR},
6263 {"|", 48, 2, JIM_EXPROP_BITOR},
6264
6265 {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6266 {"||", 10, 2, JIM_EXPROP_LOGICOR},
6267
6268 {"?", 5, 3, JIM_EXPROP_TERNARY},
6269 /* private operators */
6270 {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6271 {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6272 {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6273 {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6274 };
6275
6276 #define JIM_EXPR_OPERATORS_NUM \
6277 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6278
6279 int JimParseExpression(struct JimParserCtx *pc)
6280 {
6281 /* Discard spaces and quoted newline */
6282 while(*(pc->p) == ' ' ||
6283 *(pc->p) == '\t' ||
6284 *(pc->p) == '\r' ||
6285 *(pc->p) == '\n' ||
6286 (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6287 pc->p++; pc->len--;
6288 }
6289
6290 if (pc->len == 0) {
6291 pc->tstart = pc->tend = pc->p;
6292 pc->tline = pc->linenr;
6293 pc->tt = JIM_TT_EOL;
6294 pc->eof = 1;
6295 return JIM_OK;
6296 }
6297 switch(*(pc->p)) {
6298 case '(':
6299 pc->tstart = pc->tend = pc->p;
6300 pc->tline = pc->linenr;
6301 pc->tt = JIM_TT_SUBEXPR_START;
6302 pc->p++; pc->len--;
6303 break;
6304 case ')':
6305 pc->tstart = pc->tend = pc->p;
6306 pc->tline = pc->linenr;
6307 pc->tt = JIM_TT_SUBEXPR_END;
6308 pc->p++; pc->len--;
6309 break;
6310 case '[':
6311 return JimParseCmd(pc);
6312 break;
6313 case '$':
6314 if (JimParseVar(pc) == JIM_ERR)
6315 return JimParseExprOperator(pc);
6316 else
6317 return JIM_OK;
6318 break;
6319 case '-':
6320 if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6321 isdigit((int)*(pc->p+1)))
6322 return JimParseExprNumber(pc);
6323 else
6324 return JimParseExprOperator(pc);
6325 break;
6326 case '0': case '1': case '2': case '3': case '4':
6327 case '5': case '6': case '7': case '8': case '9': case '.':
6328 return JimParseExprNumber(pc);
6329 break;
6330 case '"':
6331 case '{':
6332 /* Here it's possible to reuse the List String parsing. */
6333 pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6334 return JimParseListStr(pc);
6335 break;
6336 case 'N': case 'I':
6337 case 'n': case 'i':
6338 if (JimParseExprIrrational(pc) == JIM_ERR)
6339 return JimParseExprOperator(pc);
6340 break;
6341 default:
6342 return JimParseExprOperator(pc);
6343 break;
6344 }
6345 return JIM_OK;
6346 }
6347
6348 int JimParseExprNumber(struct JimParserCtx *pc)
6349 {
6350 int allowdot = 1;
6351 int allowhex = 0;
6352
6353 pc->tstart = pc->p;
6354 pc->tline = pc->linenr;
6355 if (*pc->p == '-') {
6356 pc->p++; pc->len--;
6357 }
6358 while ( isdigit((int)*pc->p)
6359 || (allowhex && isxdigit((int)*pc->p) )
6360 || (allowdot && *pc->p == '.')
6361 || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6362 (*pc->p == 'x' || *pc->p == 'X'))
6363 )
6364 {
6365 if ((*pc->p == 'x') || (*pc->p == 'X')) {
6366 allowhex = 1;
6367 allowdot = 0;
6368 }
6369 if (*pc->p == '.')
6370 allowdot = 0;
6371 pc->p++; pc->len--;
6372 if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6373 pc->p += 2; pc->len -= 2;
6374 }
6375 }
6376 pc->tend = pc->p-1;
6377 pc->tt = JIM_TT_EXPR_NUMBER;
6378 return JIM_OK;
6379 }
6380
6381 int JimParseExprIrrational(struct JimParserCtx *pc)
6382 {
6383 const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6384 const char **token;
6385 for (token = Tokens; *token != NULL; token++) {
6386 int len = strlen(*token);
6387 if (strncmp(*token, pc->p, len) == 0) {
6388 pc->tstart = pc->p;
6389 pc->tend = pc->p + len - 1;
6390 pc->p += len; pc->len -= len;
6391 pc->tline = pc->linenr;
6392 pc->tt = JIM_TT_EXPR_NUMBER;
6393 return JIM_OK;
6394 }
6395 }
6396 return JIM_ERR;
6397 }
6398
6399 int JimParseExprOperator(struct JimParserCtx *pc)
6400 {
6401 int i;
6402 int bestIdx = -1, bestLen = 0;
6403
6404 /* Try to get the longest match. */
6405 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6406 const char *opname;
6407 int oplen;
6408
6409 opname = Jim_ExprOperators[i].name;
6410 if (opname == NULL) continue;
6411 oplen = strlen(opname);
6412
6413 if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6414 bestIdx = i;
6415 bestLen = oplen;
6416 }
6417 }
6418 if (bestIdx == -1) return JIM_ERR;
6419 pc->tstart = pc->p;
6420 pc->tend = pc->p + bestLen - 1;
6421 pc->p += bestLen; pc->len -= bestLen;
6422 pc->tline = pc->linenr;
6423 pc->tt = JIM_TT_EXPR_OPERATOR;
6424 return JIM_OK;
6425 }
6426
6427 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6428 {
6429 int i;
6430 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6431 if (Jim_ExprOperators[i].name &&
6432 strcmp(opname, Jim_ExprOperators[i].name) == 0)
6433 return &Jim_ExprOperators[i];
6434 return NULL;
6435 }
6436
6437 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6438 {
6439 int i;
6440 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6441 if (Jim_ExprOperators[i].opcode == opcode)
6442 return &Jim_ExprOperators[i];
6443 return NULL;
6444 }
6445
6446 /* -----------------------------------------------------------------------------
6447 * Expression Object
6448 * ---------------------------------------------------------------------------*/
6449 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6450 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6451 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6452
6453 static Jim_ObjType exprObjType = {
6454 "expression",
6455 FreeExprInternalRep,
6456 DupExprInternalRep,
6457 NULL,
6458 JIM_TYPE_REFERENCES,
6459 };
6460
6461 /* Expr bytecode structure */
6462 typedef struct ExprByteCode {
6463 int *opcode; /* Integer array of opcodes. */
6464 Jim_Obj **obj; /* Array of associated Jim Objects. */
6465 int len; /* Bytecode length */
6466 int inUse; /* Used for sharing. */
6467 } ExprByteCode;
6468
6469 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6470 {
6471 int i;
6472 ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6473
6474 expr->inUse--;
6475 if (expr->inUse != 0) return;
6476 for (i = 0; i < expr->len; i++)
6477 Jim_DecrRefCount(interp, expr->obj[i]);
6478 Jim_Free(expr->opcode);
6479 Jim_Free(expr->obj);
6480 Jim_Free(expr);
6481 }
6482
6483 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6484 {
6485 JIM_NOTUSED(interp);
6486 JIM_NOTUSED(srcPtr);
6487
6488 /* Just returns an simple string. */
6489 dupPtr->typePtr = NULL;
6490 }
6491
6492 /* Add a new instruction to an expression bytecode structure. */
6493 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6494 int opcode, char *str, int len)
6495 {
6496 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6497 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6498 expr->opcode[expr->len] = opcode;
6499 expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6500 Jim_IncrRefCount(expr->obj[expr->len]);
6501 expr->len++;
6502 }
6503
6504 /* Check if an expr program looks correct. */
6505 static int ExprCheckCorrectness(ExprByteCode *expr)
6506 {
6507 int i;
6508 int stacklen = 0;
6509
6510 /* Try to check if there are stack underflows,
6511 * and make sure at the end of the program there is
6512 * a single result on the stack. */
6513 for (i = 0; i < expr->len; i++) {
6514 switch(expr->opcode[i]) {
6515 case JIM_EXPROP_NUMBER:
6516 case JIM_EXPROP_STRING:
6517 case JIM_EXPROP_SUBST:
6518 case JIM_EXPROP_VARIABLE:
6519 case JIM_EXPROP_DICTSUGAR:
6520 case JIM_EXPROP_COMMAND:
6521 stacklen++;
6522 break;
6523 case JIM_EXPROP_NOT:
6524 case JIM_EXPROP_BITNOT:
6525 case JIM_EXPROP_UNARYMINUS:
6526 case JIM_EXPROP_UNARYPLUS:
6527 /* Unary operations */
6528 if (stacklen < 1) return JIM_ERR;
6529 break;
6530 case JIM_EXPROP_ADD:
6531 case JIM_EXPROP_SUB:
6532 case JIM_EXPROP_MUL:
6533 case JIM_EXPROP_DIV:
6534 case JIM_EXPROP_MOD:
6535 case JIM_EXPROP_LT:
6536 case JIM_EXPROP_GT:
6537 case JIM_EXPROP_LTE:
6538 case JIM_EXPROP_GTE:
6539 case JIM_EXPROP_ROTL:
6540 case JIM_EXPROP_ROTR:
6541 case JIM_EXPROP_LSHIFT:
6542 case JIM_EXPROP_RSHIFT:
6543 case JIM_EXPROP_NUMEQ:
6544 case JIM_EXPROP_NUMNE:
6545 case JIM_EXPROP_STREQ:
6546 case JIM_EXPROP_STRNE:
6547 case JIM_EXPROP_BITAND:
6548 case JIM_EXPROP_BITXOR:
6549 case JIM_EXPROP_BITOR:
6550 case JIM_EXPROP_LOGICAND:
6551 case JIM_EXPROP_LOGICOR:
6552 case JIM_EXPROP_POW:
6553 /* binary operations */
6554 if (stacklen < 2) return JIM_ERR;
6555 stacklen--;
6556 break;
6557 default:
6558 Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6559 break;
6560 }
6561 }
6562 if (stacklen != 1) return JIM_ERR;
6563 return JIM_OK;
6564 }
6565
6566 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6567 ScriptObj *topLevelScript)
6568 {
6569 int i;
6570
6571 return;
6572 for (i = 0; i < expr->len; i++) {
6573 Jim_Obj *foundObjPtr;
6574
6575 if (expr->obj[i] == NULL) continue;
6576 foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6577 NULL, expr->obj[i]);
6578 if (foundObjPtr != NULL) {
6579 Jim_IncrRefCount(foundObjPtr);
6580 Jim_DecrRefCount(interp, expr->obj[i]);
6581 expr->obj[i] = foundObjPtr;
6582 }
6583 }
6584 }
6585
6586 /* This procedure converts every occurrence of || and && opereators
6587 * in lazy unary versions.
6588 *
6589 * a b || is converted into:
6590 *
6591 * a <offset> |L b |R
6592 *
6593 * a b && is converted into:
6594 *
6595 * a <offset> &L b &R
6596 *
6597 * "|L" checks if 'a' is true:
6598 * 1) if it is true pushes 1 and skips <offset> istructions to reach
6599 * the opcode just after |R.
6600 * 2) if it is false does nothing.
6601 * "|R" checks if 'b' is true:
6602 * 1) if it is true pushes 1, otherwise pushes 0.
6603 *
6604 * "&L" checks if 'a' is true:
6605 * 1) if it is true does nothing.
6606 * 2) If it is false pushes 0 and skips <offset> istructions to reach
6607 * the opcode just after &R
6608 * "&R" checks if 'a' is true:
6609 * if it is true pushes 1, otherwise pushes 0.
6610 */
6611 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6612 {
6613 while (1) {
6614 int index = -1, leftindex, arity, i, offset;
6615 Jim_ExprOperator *op;
6616
6617 /* Search for || or && */
6618 for (i = 0; i < expr->len; i++) {
6619 if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6620 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6621 index = i;
6622 break;
6623 }
6624 }
6625 if (index == -1) return;
6626 /* Search for the end of the first operator */
6627 leftindex = index-1;
6628 arity = 1;
6629 while(arity) {
6630 switch(expr->opcode[leftindex]) {
6631 case JIM_EXPROP_NUMBER:
6632 case JIM_EXPROP_COMMAND:
6633 case JIM_EXPROP_VARIABLE:
6634 case JIM_EXPROP_DICTSUGAR:
6635 case JIM_EXPROP_SUBST:
6636 case JIM_EXPROP_STRING:
6637 break;
6638 default:
6639 op = JimExprOperatorInfoByOpcode(expr->opcode[leftindex]);
6640 if (op == NULL) {
6641 Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6642 }
6643 arity += op->arity;
6644 break;
6645 }
6646 arity--;
6647 leftindex--;
6648 }
6649 leftindex++;
6650 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6651 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6652 memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6653 sizeof(int)*(expr->len-leftindex));
6654 memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6655 sizeof(Jim_Obj*)*(expr->len-leftindex));
6656 expr->len += 2;
6657 index += 2;
6658 offset = (index-leftindex)-1;
6659 Jim_DecrRefCount(interp, expr->obj[index]);
6660 if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6661 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6662 expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6663 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6664 expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6665 } else {
6666 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6667 expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6668 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6669 expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6670 }
6671 expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6672 expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6673 Jim_IncrRefCount(expr->obj[index]);
6674 Jim_IncrRefCount(expr->obj[leftindex]);
6675 Jim_IncrRefCount(expr->obj[leftindex+1]);
6676 }
6677 }
6678
6679 /* This method takes the string representation of an expression
6680 * and generates a program for the Expr's stack-based VM. */
6681 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6682 {
6683 int exprTextLen;
6684 const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6685 struct JimParserCtx parser;
6686 int i, shareLiterals;
6687 ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6688 Jim_Stack stack;
6689 Jim_ExprOperator *op;
6690
6691 /* Perform literal sharing with the current procedure
6692 * running only if this expression appears to be not generated
6693 * at runtime. */
6694 shareLiterals = objPtr->typePtr == &sourceObjType;
6695
6696 expr->opcode = NULL;
6697 expr->obj = NULL;
6698 expr->len = 0;
6699 expr->inUse = 1;
6700
6701 Jim_InitStack(&stack);
6702 JimParserInit(&parser, exprText, exprTextLen, 1);
6703 while(!JimParserEof(&parser)) {
6704 char *token;
6705 int len, type;
6706
6707 if (JimParseExpression(&parser) != JIM_OK) {
6708 Jim_SetResultString(interp, "Syntax error in expression", -1);
6709 goto err;
6710 }
6711 token = JimParserGetToken(&parser, &len, &type, NULL);
6712 if (type == JIM_TT_EOL) {
6713 Jim_Free(token);
6714 break;
6715 }
6716 switch(type) {
6717 case JIM_TT_STR:
6718 ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6719 break;
6720 case JIM_TT_ESC:
6721 ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6722 break;
6723 case JIM_TT_VAR:
6724 ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6725 break;
6726 case JIM_TT_DICTSUGAR:
6727 ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6728 break;
6729 case JIM_TT_CMD:
6730 ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6731 break;
6732 case JIM_TT_EXPR_NUMBER:
6733 ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6734 break;
6735 case JIM_TT_EXPR_OPERATOR:
6736 op = JimExprOperatorInfo(token);
6737 while(1) {
6738 Jim_ExprOperator *stackTopOp;
6739
6740 if (Jim_StackPeek(&stack) != NULL) {
6741 stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6742 } else {
6743 stackTopOp = NULL;
6744 }
6745 if (Jim_StackLen(&stack) && op->arity != 1 &&
6746 stackTopOp && stackTopOp->precedence >= op->precedence)
6747 {
6748 ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6749 Jim_StackPeek(&stack), -1);
6750 Jim_StackPop(&stack);
6751 } else {
6752 break;
6753 }
6754 }
6755 Jim_StackPush(&stack, token);
6756 break;
6757 case JIM_TT_SUBEXPR_START:
6758 Jim_StackPush(&stack, Jim_StrDup("("));
6759 Jim_Free(token);
6760 break;
6761 case JIM_TT_SUBEXPR_END:
6762 {
6763 int found = 0;
6764 while(Jim_StackLen(&stack)) {
6765 char *opstr = Jim_StackPop(&stack);
6766 if (!strcmp(opstr, "(")) {
6767 Jim_Free(opstr);
6768 found = 1;
6769 break;
6770 }
6771 op = JimExprOperatorInfo(opstr);
6772 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6773 }
6774 if (!found) {
6775 Jim_SetResultString(interp,
6776 "Unexpected close parenthesis", -1);
6777 goto err;
6778 }
6779 }
6780 Jim_Free(token);
6781 break;
6782 default:
6783 Jim_Panic(interp,"Default reached in SetExprFromAny()");
6784 break;
6785 }
6786 }
6787 while (Jim_StackLen(&stack)) {
6788 char *opstr = Jim_StackPop(&stack);
6789 op = JimExprOperatorInfo(opstr);
6790 if (op == NULL && !strcmp(opstr, "(")) {
6791 Jim_Free(opstr);
6792 Jim_SetResultString(interp, "Missing close parenthesis", -1);
6793 goto err;
6794 }
6795 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6796 }
6797 /* Check program correctness. */
6798 if (ExprCheckCorrectness(expr) != JIM_OK) {
6799 Jim_SetResultString(interp, "Invalid expression", -1);
6800 goto err;
6801 }
6802
6803 /* Free the stack used for the compilation. */
6804 Jim_FreeStackElements(&stack, Jim_Free);
6805 Jim_FreeStack(&stack);
6806
6807 /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6808 ExprMakeLazy(interp, expr);
6809
6810 /* Perform literal sharing */
6811 if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6812 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6813 if (bodyObjPtr->typePtr == &scriptObjType) {
6814 ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6815 ExprShareLiterals(interp, expr, bodyScript);
6816 }
6817 }
6818
6819 /* Free the old internal rep and set the new one. */
6820 Jim_FreeIntRep(interp, objPtr);
6821 Jim_SetIntRepPtr(objPtr, expr);
6822 objPtr->typePtr = &exprObjType;
6823 return JIM_OK;
6824
6825 err: /* we jump here on syntax/compile errors. */
6826 Jim_FreeStackElements(&stack, Jim_Free);
6827 Jim_FreeStack(&stack);
6828 Jim_Free(expr->opcode);
6829 for (i = 0; i < expr->len; i++) {
6830 Jim_DecrRefCount(interp,expr->obj[i]);
6831 }
6832 Jim_Free(expr->obj);
6833 Jim_Free(expr);
6834 return JIM_ERR;
6835 }
6836
6837 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6838 {
6839 if (objPtr->typePtr != &exprObjType) {
6840 if (SetExprFromAny(interp, objPtr) != JIM_OK)
6841 return NULL;
6842 }
6843 return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6844 }
6845
6846 /* -----------------------------------------------------------------------------
6847 * Expressions evaluation.
6848 * Jim uses a specialized stack-based virtual machine for expressions,
6849 * that takes advantage of the fact that expr's operators
6850 * can't be redefined.
6851 *
6852 * Jim_EvalExpression() uses the bytecode compiled by
6853 * SetExprFromAny() method of the "expression" object.
6854 *
6855 * On success a Tcl Object containing the result of the evaluation
6856 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6857 * returned.
6858 * On error the function returns a retcode != to JIM_OK and set a suitable
6859 * error on the interp.
6860 * ---------------------------------------------------------------------------*/
6861 #define JIM_EE_STATICSTACK_LEN 10
6862
6863 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6864 Jim_Obj **exprResultPtrPtr)
6865 {
6866 ExprByteCode *expr;
6867 Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6868 int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6869
6870 Jim_IncrRefCount(exprObjPtr);
6871 expr = Jim_GetExpression(interp, exprObjPtr);
6872 if (!expr) {
6873 Jim_DecrRefCount(interp, exprObjPtr);
6874 return JIM_ERR; /* error in expression. */
6875 }
6876 /* In order to avoid that the internal repr gets freed due to
6877 * shimmering of the exprObjPtr's object, we make the internal rep
6878 * shared. */
6879 expr->inUse++;
6880
6881 /* The stack-based expr VM itself */
6882
6883 /* Stack allocation. Expr programs have the feature that
6884 * a program of length N can't require a stack longer than
6885 * N. */
6886 if (expr->len > JIM_EE_STATICSTACK_LEN)
6887 stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6888 else
6889 stack = staticStack;
6890
6891 /* Execute every istruction */
6892 for (i = 0; i < expr->len; i++) {
6893 Jim_Obj *A, *B, *objPtr;
6894 jim_wide wA, wB, wC;
6895 double dA, dB, dC;
6896 const char *sA, *sB;
6897 int Alen, Blen, retcode;
6898 int opcode = expr->opcode[i];
6899
6900 if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6901 stack[stacklen++] = expr->obj[i];
6902 Jim_IncrRefCount(expr->obj[i]);
6903 } else if (opcode == JIM_EXPROP_VARIABLE) {
6904 objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6905 if (objPtr == NULL) {
6906 error = 1;
6907 goto err;
6908 }
6909 stack[stacklen++] = objPtr;
6910 Jim_IncrRefCount(objPtr);
6911 } else if (opcode == JIM_EXPROP_SUBST) {
6912 if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6913 &objPtr, JIM_NONE)) != JIM_OK)
6914 {
6915 error = 1;
6916 errRetCode = retcode;
6917 goto err;
6918 }
6919 stack[stacklen++] = objPtr;
6920 Jim_IncrRefCount(objPtr);
6921 } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6922 objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6923 if (objPtr == NULL) {
6924 error = 1;
6925 goto err;
6926 }
6927 stack[stacklen++] = objPtr;
6928 Jim_IncrRefCount(objPtr);
6929 } else if (opcode == JIM_EXPROP_COMMAND) {
6930 if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6931 error = 1;
6932 errRetCode = retcode;
6933 goto err;
6934 }
6935 stack[stacklen++] = interp->result;
6936 Jim_IncrRefCount(interp->result);
6937 } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6938 opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6939 {
6940 /* Note that there isn't to increment the
6941 * refcount of objects. the references are moved
6942 * from stack to A and B. */
6943 B = stack[--stacklen];
6944 A = stack[--stacklen];
6945
6946 /* --- Integer --- */
6947 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6948 (B->typePtr == &doubleObjType && !B->bytes) ||
6949 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6950 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6951 goto trydouble;
6952 }
6953 Jim_DecrRefCount(interp, A);
6954 Jim_DecrRefCount(interp, B);
6955 switch(expr->opcode[i]) {
6956 case JIM_EXPROP_ADD: wC = wA+wB; break;
6957 case JIM_EXPROP_SUB: wC = wA-wB; break;
6958 case JIM_EXPROP_MUL: wC = wA*wB; break;
6959 case JIM_EXPROP_LT: wC = wA<wB; break;
6960 case JIM_EXPROP_GT: wC = wA>wB; break;
6961 case JIM_EXPROP_LTE: wC = wA<=wB; break;
6962 case JIM_EXPROP_GTE: wC = wA>=wB; break;
6963 case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6964 case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6965 case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6966 case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6967 case JIM_EXPROP_BITAND: wC = wA&wB; break;
6968 case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6969 case JIM_EXPROP_BITOR: wC = wA|wB; break;
6970 case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6971 case JIM_EXPROP_LOGICAND_LEFT:
6972 if (wA == 0) {
6973 i += (int)wB;
6974 wC = 0;
6975 } else {
6976 continue;
6977 }
6978 break;
6979 case JIM_EXPROP_LOGICOR_LEFT:
6980 if (wA != 0) {
6981 i += (int)wB;
6982 wC = 1;
6983 } else {
6984 continue;
6985 }
6986 break;
6987 case JIM_EXPROP_DIV:
6988 if (wB == 0) goto divbyzero;
6989 wC = wA/wB;
6990 break;
6991 case JIM_EXPROP_MOD:
6992 if (wB == 0) goto divbyzero;
6993 wC = wA%wB;
6994 break;
6995 case JIM_EXPROP_ROTL: {
6996 /* uint32_t would be better. But not everyone has inttypes.h?*/
6997 unsigned long uA = (unsigned long)wA;
6998 #ifdef _MSC_VER
6999 wC = _rotl(uA,(unsigned long)wB);
7000 #else
7001 const unsigned int S = sizeof(unsigned long) * 8;
7002 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
7003 #endif
7004 break;
7005 }
7006 case JIM_EXPROP_ROTR: {
7007 unsigned long uA = (unsigned long)wA;
7008 #ifdef _MSC_VER
7009 wC = _rotr(uA,(unsigned long)wB);
7010 #else
7011 const unsigned int S = sizeof(unsigned long) * 8;
7012 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
7013 #endif
7014 break;
7015 }
7016
7017 default:
7018 wC = 0; /* avoid gcc warning */
7019 break;
7020 }
7021 stack[stacklen] = Jim_NewIntObj(interp, wC);
7022 Jim_IncrRefCount(stack[stacklen]);
7023 stacklen++;
7024 continue;
7025 trydouble:
7026 /* --- Double --- */
7027 if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
7028 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
7029
7030 /* Hmmm! For compatibility, maybe convert != and == into ne and eq */
7031 if (expr->opcode[i] == JIM_EXPROP_NUMNE) {
7032 opcode = JIM_EXPROP_STRNE;
7033 goto retry_as_string;
7034 }
7035 else if (expr->opcode[i] == JIM_EXPROP_NUMEQ) {
7036 opcode = JIM_EXPROP_STREQ;
7037 goto retry_as_string;
7038 }
7039 Jim_DecrRefCount(interp, A);
7040 Jim_DecrRefCount(interp, B);
7041 error = 1;
7042 goto err;
7043 }
7044 Jim_DecrRefCount(interp, A);
7045 Jim_DecrRefCount(interp, B);
7046 switch(expr->opcode[i]) {
7047 case JIM_EXPROP_ROTL:
7048 case JIM_EXPROP_ROTR:
7049 case JIM_EXPROP_LSHIFT:
7050 case JIM_EXPROP_RSHIFT:
7051 case JIM_EXPROP_BITAND:
7052 case JIM_EXPROP_BITXOR:
7053 case JIM_EXPROP_BITOR:
7054 case JIM_EXPROP_MOD:
7055 case JIM_EXPROP_POW:
7056 Jim_SetResultString(interp,
7057 "Got floating-point value where integer was expected", -1);
7058 error = 1;
7059 goto err;
7060 break;
7061 case JIM_EXPROP_ADD: dC = dA+dB; break;
7062 case JIM_EXPROP_SUB: dC = dA-dB; break;
7063 case JIM_EXPROP_MUL: dC = dA*dB; break;
7064 case JIM_EXPROP_LT: dC = dA<dB; break;
7065 case JIM_EXPROP_GT: dC = dA>dB; break;
7066 case JIM_EXPROP_LTE: dC = dA<=dB; break;
7067 case JIM_EXPROP_GTE: dC = dA>=dB; break;
7068 case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
7069 case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
7070 case JIM_EXPROP_LOGICAND_LEFT:
7071 if (dA == 0) {
7072 i += (int)dB;
7073 dC = 0;
7074 } else {
7075 continue;
7076 }
7077 break;
7078 case JIM_EXPROP_LOGICOR_LEFT:
7079 if (dA != 0) {
7080 i += (int)dB;
7081 dC = 1;
7082 } else {
7083 continue;
7084 }
7085 break;
7086 case JIM_EXPROP_DIV:
7087 if (dB == 0) goto divbyzero;
7088 dC = dA/dB;
7089 break;
7090 default:
7091 dC = 0; /* avoid gcc warning */
7092 break;
7093 }
7094 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7095 Jim_IncrRefCount(stack[stacklen]);
7096 stacklen++;
7097 } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
7098 B = stack[--stacklen];
7099 A = stack[--stacklen];
7100 retry_as_string:
7101 sA = Jim_GetString(A, &Alen);
7102 sB = Jim_GetString(B, &Blen);
7103 switch(opcode) {
7104 case JIM_EXPROP_STREQ:
7105 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
7106 wC = 1;
7107 else
7108 wC = 0;
7109 break;
7110 case JIM_EXPROP_STRNE:
7111 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
7112 wC = 1;
7113 else
7114 wC = 0;
7115 break;
7116 default:
7117 wC = 0; /* avoid gcc warning */
7118 break;
7119 }
7120 Jim_DecrRefCount(interp, A);
7121 Jim_DecrRefCount(interp, B);
7122 stack[stacklen] = Jim_NewIntObj(interp, wC);
7123 Jim_IncrRefCount(stack[stacklen]);
7124 stacklen++;
7125 } else if (opcode == JIM_EXPROP_NOT ||
7126 opcode == JIM_EXPROP_BITNOT ||
7127 opcode == JIM_EXPROP_LOGICAND_RIGHT ||
7128 opcode == JIM_EXPROP_LOGICOR_RIGHT) {
7129 /* Note that there isn't to increment the
7130 * refcount of objects. the references are moved
7131 * from stack to A and B. */
7132 A = stack[--stacklen];
7133
7134 /* --- Integer --- */
7135 if ((A->typePtr == &doubleObjType && !A->bytes) ||
7136 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
7137 goto trydouble_unary;
7138 }
7139 Jim_DecrRefCount(interp, A);
7140 switch(expr->opcode[i]) {
7141 case JIM_EXPROP_NOT: wC = !wA; break;
7142 case JIM_EXPROP_BITNOT: wC = ~wA; break;
7143 case JIM_EXPROP_LOGICAND_RIGHT:
7144 case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
7145 default:
7146 wC = 0; /* avoid gcc warning */
7147 break;
7148 }
7149 stack[stacklen] = Jim_NewIntObj(interp, wC);
7150 Jim_IncrRefCount(stack[stacklen]);
7151 stacklen++;
7152 continue;
7153 trydouble_unary:
7154 /* --- Double --- */
7155 if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
7156 Jim_DecrRefCount(interp, A);
7157 error = 1;
7158 goto err;
7159 }
7160 Jim_DecrRefCount(interp, A);
7161 switch(expr->opcode[i]) {
7162 case JIM_EXPROP_NOT: dC = !dA; break;
7163 case JIM_EXPROP_LOGICAND_RIGHT:
7164 case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
7165 case JIM_EXPROP_BITNOT:
7166 Jim_SetResultString(interp,
7167 "Got floating-point value where integer was expected", -1);
7168 error = 1;
7169 goto err;
7170 break;
7171 default:
7172 dC = 0; /* avoid gcc warning */
7173 break;
7174 }
7175 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7176 Jim_IncrRefCount(stack[stacklen]);
7177 stacklen++;
7178 } else {
7179 Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
7180 }
7181 }
7182 err:
7183 /* There is no need to decerement the inUse field because
7184 * this reference is transfered back into the exprObjPtr. */
7185 Jim_FreeIntRep(interp, exprObjPtr);
7186 exprObjPtr->typePtr = &exprObjType;
7187 Jim_SetIntRepPtr(exprObjPtr, expr);
7188 Jim_DecrRefCount(interp, exprObjPtr);
7189 if (!error) {
7190 *exprResultPtrPtr = stack[0];
7191 Jim_IncrRefCount(stack[0]);
7192 errRetCode = JIM_OK;
7193 }
7194 for (i = 0; i < stacklen; i++) {
7195 Jim_DecrRefCount(interp, stack[i]);
7196 }
7197 if (stack != staticStack)
7198 Jim_Free(stack);
7199 return errRetCode;
7200 divbyzero:
7201 error = 1;
7202 Jim_SetResultString(interp, "Division by zero", -1);
7203 goto err;
7204 }
7205
7206 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7207 {
7208 int retcode;
7209 jim_wide wideValue;
7210 double doubleValue;
7211 Jim_Obj *exprResultPtr;
7212
7213 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7214 if (retcode != JIM_OK)
7215 return retcode;
7216 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7217 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7218 {
7219 Jim_DecrRefCount(interp, exprResultPtr);
7220 return JIM_ERR;
7221 } else {
7222 Jim_DecrRefCount(interp, exprResultPtr);
7223 *boolPtr = doubleValue != 0;
7224 return JIM_OK;
7225 }
7226 }
7227 Jim_DecrRefCount(interp, exprResultPtr);
7228 *boolPtr = wideValue != 0;
7229 return JIM_OK;
7230 }
7231
7232 /* -----------------------------------------------------------------------------
7233 * ScanFormat String Object
7234 * ---------------------------------------------------------------------------*/
7235
7236 /* This Jim_Obj will held a parsed representation of a format string passed to
7237 * the Jim_ScanString command. For error diagnostics, the scanformat string has
7238 * to be parsed in its entirely first and then, if correct, can be used for
7239 * scanning. To avoid endless re-parsing, the parsed representation will be
7240 * stored in an internal representation and re-used for performance reason. */
7241
7242 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7243 * scanformat string. This part will later be used to extract information
7244 * out from the string to be parsed by Jim_ScanString */
7245
7246 typedef struct ScanFmtPartDescr {
7247 char type; /* Type of conversion (e.g. c, d, f) */
7248 char modifier; /* Modify type (e.g. l - long, h - short */
7249 size_t width; /* Maximal width of input to be converted */
7250 int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
7251 char *arg; /* Specification of a CHARSET conversion */
7252 char *prefix; /* Prefix to be scanned literally before conversion */
7253 } ScanFmtPartDescr;
7254
7255 /* The ScanFmtStringObj will held the internal representation of a scanformat
7256 * string parsed and separated in part descriptions. Furthermore it contains
7257 * the original string representation of the scanformat string to allow for
7258 * fast update of the Jim_Obj's string representation part.
7259 *
7260 * As add-on the internal object representation add some scratch pad area
7261 * for usage by Jim_ScanString to avoid endless allocating and freeing of
7262 * memory for purpose of string scanning.
7263 *
7264 * The error member points to a static allocated string in case of a mal-
7265 * formed scanformat string or it contains '0' (NULL) in case of a valid
7266 * parse representation.
7267 *
7268 * The whole memory of the internal representation is allocated as a single
7269 * area of memory that will be internally separated. So freeing and duplicating
7270 * of such an object is cheap */
7271
7272 typedef struct ScanFmtStringObj {
7273 jim_wide size; /* Size of internal repr in bytes */
7274 char *stringRep; /* Original string representation */
7275 size_t count; /* Number of ScanFmtPartDescr contained */
7276 size_t convCount; /* Number of conversions that will assign */
7277 size_t maxPos; /* Max position index if XPG3 is used */
7278 const char *error; /* Ptr to error text (NULL if no error */
7279 char *scratch; /* Some scratch pad used by Jim_ScanString */
7280 ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
7281 } ScanFmtStringObj;
7282
7283
7284 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7285 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7286 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7287
7288 static Jim_ObjType scanFmtStringObjType = {
7289 "scanformatstring",
7290 FreeScanFmtInternalRep,
7291 DupScanFmtInternalRep,
7292 UpdateStringOfScanFmt,
7293 JIM_TYPE_NONE,
7294 };
7295
7296 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7297 {
7298 JIM_NOTUSED(interp);
7299 Jim_Free((char*)objPtr->internalRep.ptr);
7300 objPtr->internalRep.ptr = 0;
7301 }
7302
7303 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7304 {
7305 size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7306 ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7307
7308 JIM_NOTUSED(interp);
7309 memcpy(newVec, srcPtr->internalRep.ptr, size);
7310 dupPtr->internalRep.ptr = newVec;
7311 dupPtr->typePtr = &scanFmtStringObjType;
7312 }
7313
7314 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7315 {
7316 char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7317
7318 objPtr->bytes = Jim_StrDup(bytes);
7319 objPtr->length = strlen(bytes);
7320 }
7321
7322 /* SetScanFmtFromAny will parse a given string and create the internal
7323 * representation of the format specification. In case of an error
7324 * the error data member of the internal representation will be set
7325 * to an descriptive error text and the function will be left with
7326 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7327 * specification */
7328
7329 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7330 {
7331 ScanFmtStringObj *fmtObj;
7332 char *buffer;
7333 int maxCount, i, approxSize, lastPos = -1;
7334 const char *fmt = objPtr->bytes;
7335 int maxFmtLen = objPtr->length;
7336 const char *fmtEnd = fmt + maxFmtLen;
7337 int curr;
7338
7339 Jim_FreeIntRep(interp, objPtr);
7340 /* Count how many conversions could take place maximally */
7341 for (i=0, maxCount=0; i < maxFmtLen; ++i)
7342 if (fmt[i] == '%')
7343 ++maxCount;
7344 /* Calculate an approximation of the memory necessary */
7345 approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
7346 + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7347 + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
7348 + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
7349 + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
7350 + (maxCount +1) * sizeof(char) /* '\0' for every partial */
7351 + 1; /* safety byte */
7352 fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7353 memset(fmtObj, 0, approxSize);
7354 fmtObj->size = approxSize;
7355 fmtObj->maxPos = 0;
7356 fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7357 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7358 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7359 buffer = fmtObj->stringRep + maxFmtLen + 1;
7360 objPtr->internalRep.ptr = fmtObj;
7361 objPtr->typePtr = &scanFmtStringObjType;
7362 for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7363 int width=0, skip;
7364 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7365 fmtObj->count++;
7366 descr->width = 0; /* Assume width unspecified */
7367 /* Overread and store any "literal" prefix */
7368 if (*fmt != '%' || fmt[1] == '%') {
7369 descr->type = 0;
7370 descr->prefix = &buffer[i];
7371 for (; fmt < fmtEnd; ++fmt) {
7372 if (*fmt == '%') {
7373 if (fmt[1] != '%') break;
7374 ++fmt;
7375 }
7376 buffer[i++] = *fmt;
7377 }
7378 buffer[i++] = 0;
7379 }
7380 /* Skip the conversion introducing '%' sign */
7381 ++fmt;
7382 /* End reached due to non-conversion literal only? */
7383 if (fmt >= fmtEnd)
7384 goto done;
7385 descr->pos = 0; /* Assume "natural" positioning */
7386 if (*fmt == '*') {
7387 descr->pos = -1; /* Okay, conversion will not be assigned */
7388 ++fmt;
7389 } else
7390 fmtObj->convCount++; /* Otherwise count as assign-conversion */
7391 /* Check if next token is a number (could be width or pos */
7392 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7393 fmt += skip;
7394 /* Was the number a XPG3 position specifier? */
7395 if (descr->pos != -1 && *fmt == '$') {
7396 int prev;
7397 ++fmt;
7398 descr->pos = width;
7399 width = 0;
7400 /* Look if "natural" postioning and XPG3 one was mixed */
7401 if ((lastPos == 0 && descr->pos > 0)
7402 || (lastPos > 0 && descr->pos == 0)) {
7403 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7404 return JIM_ERR;
7405 }
7406 /* Look if this position was already used */
7407 for (prev=0; prev < curr; ++prev) {
7408 if (fmtObj->descr[prev].pos == -1) continue;
7409 if (fmtObj->descr[prev].pos == descr->pos) {
7410 fmtObj->error = "same \"%n$\" conversion specifier "
7411 "used more than once";
7412 return JIM_ERR;
7413 }
7414 }
7415 /* Try to find a width after the XPG3 specifier */
7416 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7417 descr->width = width;
7418 fmt += skip;
7419 }
7420 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7421 fmtObj->maxPos = descr->pos;
7422 } else {
7423 /* Number was not a XPG3, so it has to be a width */
7424 descr->width = width;
7425 }
7426 }
7427 /* If positioning mode was undetermined yet, fix this */
7428 if (lastPos == -1)
7429 lastPos = descr->pos;
7430 /* Handle CHARSET conversion type ... */
7431 if (*fmt == '[') {
7432 int swapped = 1, beg = i, end, j;
7433 descr->type = '[';
7434 descr->arg = &buffer[i];
7435 ++fmt;
7436 if (*fmt == '^') buffer[i++] = *fmt++;
7437 if (*fmt == ']') buffer[i++] = *fmt++;
7438 while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7439 if (*fmt != ']') {
7440 fmtObj->error = "unmatched [ in format string";
7441 return JIM_ERR;
7442 }
7443 end = i;
7444 buffer[i++] = 0;
7445 /* In case a range fence was given "backwards", swap it */
7446 while (swapped) {
7447 swapped = 0;
7448 for (j=beg+1; j < end-1; ++j) {
7449 if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7450 char tmp = buffer[j-1];
7451 buffer[j-1] = buffer[j+1];
7452 buffer[j+1] = tmp;
7453 swapped = 1;
7454 }
7455 }
7456 }
7457 } else {
7458 /* Remember any valid modifier if given */
7459 if (strchr("hlL", *fmt) != 0)
7460 descr->modifier = tolower((int)*fmt++);
7461
7462 descr->type = *fmt;
7463 if (strchr("efgcsndoxui", *fmt) == 0) {
7464 fmtObj->error = "bad scan conversion character";
7465 return JIM_ERR;
7466 } else if (*fmt == 'c' && descr->width != 0) {
7467 fmtObj->error = "field width may not be specified in %c "
7468 "conversion";
7469 return JIM_ERR;
7470 } else if (*fmt == 'u' && descr->modifier == 'l') {
7471 fmtObj->error = "unsigned wide not supported";
7472 return JIM_ERR;
7473 }
7474 }
7475 curr++;
7476 }
7477 done:
7478 if (fmtObj->convCount == 0) {
7479 fmtObj->error = "no any conversion specifier given";
7480 return JIM_ERR;
7481 }
7482 return JIM_OK;
7483 }
7484
7485 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7486
7487 #define FormatGetCnvCount(_fo_) \
7488 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7489 #define FormatGetMaxPos(_fo_) \
7490 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7491 #define FormatGetError(_fo_) \
7492 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7493
7494 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7495 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7496 * bitvector implementation in Jim? */
7497
7498 static int JimTestBit(const char *bitvec, char ch)
7499 {
7500 div_t pos = div(ch-1, 8);
7501 return bitvec[pos.quot] & (1 << pos.rem);
7502 }
7503
7504 static void JimSetBit(char *bitvec, char ch)
7505 {
7506 div_t pos = div(ch-1, 8);
7507 bitvec[pos.quot] |= (1 << pos.rem);
7508 }
7509
7510 #if 0 /* currently not used */
7511 static void JimClearBit(char *bitvec, char ch)
7512 {
7513 div_t pos = div(ch-1, 8);
7514 bitvec[pos.quot] &= ~(1 << pos.rem);
7515 }
7516 #endif
7517
7518 /* JimScanAString is used to scan an unspecified string that ends with
7519 * next WS, or a string that is specified via a charset. The charset
7520 * is currently implemented in a way to only allow for usage with
7521 * ASCII. Whenever we will switch to UNICODE, another idea has to
7522 * be born :-/
7523 *
7524 * FIXME: Works only with ASCII */
7525
7526 static Jim_Obj *
7527 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7528 {
7529 size_t i;
7530 Jim_Obj *result;
7531 char charset[256/8+1]; /* A Charset may contain max 256 chars */
7532 char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7533
7534 /* First init charset to nothing or all, depending if a specified
7535 * or an unspecified string has to be parsed */
7536 memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7537 if (sdescr) {
7538 /* There was a set description given, that means we are parsing
7539 * a specified string. So we have to build a corresponding
7540 * charset reflecting the description */
7541 int notFlag = 0;
7542 /* Should the set be negated at the end? */
7543 if (*sdescr == '^') {
7544 notFlag = 1;
7545 ++sdescr;
7546 }
7547 /* Here '-' is meant literally and not to define a range */
7548 if (*sdescr == '-') {
7549 JimSetBit(charset, '-');
7550 ++sdescr;
7551 }
7552 while (*sdescr) {
7553 if (sdescr[1] == '-' && sdescr[2] != 0) {
7554 /* Handle range definitions */
7555 int i;
7556 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7557 JimSetBit(charset, (char)i);
7558 sdescr += 3;
7559 } else {
7560 /* Handle verbatim character definitions */
7561 JimSetBit(charset, *sdescr++);
7562 }
7563 }
7564 /* Negate the charset if there was a NOT given */
7565 for (i=0; notFlag && i < sizeof(charset); ++i)
7566 charset[i] = ~charset[i];
7567 }
7568 /* And after all the mess above, the real work begin ... */
7569 while (str && *str) {
7570 if (!sdescr && isspace((int)*str))
7571 break; /* EOS via WS if unspecified */
7572 if (JimTestBit(charset, *str)) *buffer++ = *str++;
7573 else break; /* EOS via mismatch if specified scanning */
7574 }
7575 *buffer = 0; /* Close the string properly ... */
7576 result = Jim_NewStringObj(interp, anchor, -1);
7577 Jim_Free(anchor); /* ... and free it afer usage */
7578 return result;
7579 }
7580
7581 /* ScanOneEntry will scan one entry out of the string passed as argument.
7582 * It use the sscanf() function for this task. After extracting and
7583 * converting of the value, the count of scanned characters will be
7584 * returned of -1 in case of no conversion tool place and string was
7585 * already scanned thru */
7586
7587 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7588 ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7589 {
7590 # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7591 ? sizeof(jim_wide) \
7592 : sizeof(double))
7593 char buffer[MAX_SIZE];
7594 char *value = buffer;
7595 const char *tok;
7596 const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7597 size_t sLen = strlen(&str[pos]), scanned = 0;
7598 size_t anchor = pos;
7599 int i;
7600
7601 /* First pessimiticly assume, we will not scan anything :-) */
7602 *valObjPtr = 0;
7603 if (descr->prefix) {
7604 /* There was a prefix given before the conversion, skip it and adjust
7605 * the string-to-be-parsed accordingly */
7606 for (i=0; str[pos] && descr->prefix[i]; ++i) {
7607 /* If prefix require, skip WS */
7608 if (isspace((int)descr->prefix[i]))
7609 while (str[pos] && isspace((int)str[pos])) ++pos;
7610 else if (descr->prefix[i] != str[pos])
7611 break; /* Prefix do not match here, leave the loop */
7612 else
7613 ++pos; /* Prefix matched so far, next round */
7614 }
7615 if (str[pos] == 0)
7616 return -1; /* All of str consumed: EOF condition */
7617 else if (descr->prefix[i] != 0)
7618 return 0; /* Not whole prefix consumed, no conversion possible */
7619 }
7620 /* For all but following conversion, skip leading WS */
7621 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7622 while (isspace((int)str[pos])) ++pos;
7623 /* Determine how much skipped/scanned so far */
7624 scanned = pos - anchor;
7625 if (descr->type == 'n') {
7626 /* Return pseudo conversion means: how much scanned so far? */
7627 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7628 } else if (str[pos] == 0) {
7629 /* Cannot scan anything, as str is totally consumed */
7630 return -1;
7631 } else {
7632 /* Processing of conversions follows ... */
7633 if (descr->width > 0) {
7634 /* Do not try to scan as fas as possible but only the given width.
7635 * To ensure this, we copy the part that should be scanned. */
7636 size_t tLen = descr->width > sLen ? sLen : descr->width;
7637 tok = Jim_StrDupLen(&str[pos], tLen);
7638 } else {
7639 /* As no width was given, simply refer to the original string */
7640 tok = &str[pos];
7641 }
7642 switch (descr->type) {
7643 case 'c':
7644 *valObjPtr = Jim_NewIntObj(interp, *tok);
7645 scanned += 1;
7646 break;
7647 case 'd': case 'o': case 'x': case 'u': case 'i': {
7648 char *endp; /* Position where the number finished */
7649 int base = descr->type == 'o' ? 8
7650 : descr->type == 'x' ? 16
7651 : descr->type == 'i' ? 0
7652 : 10;
7653
7654 do {
7655 /* Try to scan a number with the given base */
7656 if (descr->modifier == 'l')
7657 #ifdef HAVE_LONG_LONG
7658 *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7659 #else
7660 *(jim_wide*)value = strtol(tok, &endp, base);
7661 #endif
7662 else
7663 if (descr->type == 'u')
7664 *(long*)value = strtoul(tok, &endp, base);
7665 else
7666 *(long*)value = strtol(tok, &endp, base);
7667 /* If scanning failed, and base was undetermined, simply
7668 * put it to 10 and try once more. This should catch the
7669 * case where %i begin to parse a number prefix (e.g.
7670 * '0x' but no further digits follows. This will be
7671 * handled as a ZERO followed by a char 'x' by Tcl */
7672 if (endp == tok && base == 0) base = 10;
7673 else break;
7674 } while (1);
7675 if (endp != tok) {
7676 /* There was some number sucessfully scanned! */
7677 if (descr->modifier == 'l')
7678 *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7679 else
7680 *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7681 /* Adjust the number-of-chars scanned so far */
7682 scanned += endp - tok;
7683 } else {
7684 /* Nothing was scanned. We have to determine if this
7685 * happened due to e.g. prefix mismatch or input str
7686 * exhausted */
7687 scanned = *tok ? 0 : -1;
7688 }
7689 break;
7690 }
7691 case 's': case '[': {
7692 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7693 scanned += Jim_Length(*valObjPtr);
7694 break;
7695 }
7696 case 'e': case 'f': case 'g': {
7697 char *endp;
7698
7699 *(double*)value = strtod(tok, &endp);
7700 if (endp != tok) {
7701 /* There was some number sucessfully scanned! */
7702 *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7703 /* Adjust the number-of-chars scanned so far */
7704 scanned += endp - tok;
7705 } else {
7706 /* Nothing was scanned. We have to determine if this
7707 * happened due to e.g. prefix mismatch or input str
7708 * exhausted */
7709 scanned = *tok ? 0 : -1;
7710 }
7711 break;
7712 }
7713 }
7714 /* If a substring was allocated (due to pre-defined width) do not
7715 * forget to free it */
7716 if (tok != &str[pos])
7717 Jim_Free((char*)tok);
7718 }
7719 return scanned;
7720 }
7721
7722 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7723 * string and returns all converted (and not ignored) values in a list back
7724 * to the caller. If an error occured, a NULL pointer will be returned */
7725
7726 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7727 Jim_Obj *fmtObjPtr, int flags)
7728 {
7729 size_t i, pos;
7730 int scanned = 1;
7731 const char *str = Jim_GetString(strObjPtr, 0);
7732 Jim_Obj *resultList = 0;
7733 Jim_Obj **resultVec;
7734 int resultc;
7735 Jim_Obj *emptyStr = 0;
7736 ScanFmtStringObj *fmtObj;
7737
7738 /* If format specification is not an object, convert it! */
7739 if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7740 SetScanFmtFromAny(interp, fmtObjPtr);
7741 fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7742 /* Check if format specification was valid */
7743 if (fmtObj->error != 0) {
7744 if (flags & JIM_ERRMSG)
7745 Jim_SetResultString(interp, fmtObj->error, -1);
7746 return 0;
7747 }
7748 /* Allocate a new "shared" empty string for all unassigned conversions */
7749 emptyStr = Jim_NewEmptyStringObj(interp);
7750 Jim_IncrRefCount(emptyStr);
7751 /* Create a list and fill it with empty strings up to max specified XPG3 */
7752 resultList = Jim_NewListObj(interp, 0, 0);
7753 if (fmtObj->maxPos > 0) {
7754 for (i=0; i < fmtObj->maxPos; ++i)
7755 Jim_ListAppendElement(interp, resultList, emptyStr);
7756 JimListGetElements(interp, resultList, &resultc, &resultVec);
7757 }
7758 /* Now handle every partial format description */
7759 for (i=0, pos=0; i < fmtObj->count; ++i) {
7760 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7761 Jim_Obj *value = 0;
7762 /* Only last type may be "literal" w/o conversion - skip it! */
7763 if (descr->type == 0) continue;
7764 /* As long as any conversion could be done, we will proceed */
7765 if (scanned > 0)
7766 scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7767 /* In case our first try results in EOF, we will leave */
7768 if (scanned == -1 && i == 0)
7769 goto eof;
7770 /* Advance next pos-to-be-scanned for the amount scanned already */
7771 pos += scanned;
7772 /* value == 0 means no conversion took place so take empty string */
7773 if (value == 0)
7774 value = Jim_NewEmptyStringObj(interp);
7775 /* If value is a non-assignable one, skip it */
7776 if (descr->pos == -1) {
7777 Jim_FreeNewObj(interp, value);
7778 } else if (descr->pos == 0)
7779 /* Otherwise append it to the result list if no XPG3 was given */
7780 Jim_ListAppendElement(interp, resultList, value);
7781 else if (resultVec[descr->pos-1] == emptyStr) {
7782 /* But due to given XPG3, put the value into the corr. slot */
7783 Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7784 Jim_IncrRefCount(value);
7785 resultVec[descr->pos-1] = value;
7786 } else {
7787 /* Otherwise, the slot was already used - free obj and ERROR */
7788 Jim_FreeNewObj(interp, value);
7789 goto err;
7790 }
7791 }
7792 Jim_DecrRefCount(interp, emptyStr);
7793 return resultList;
7794 eof:
7795 Jim_DecrRefCount(interp, emptyStr);
7796 Jim_FreeNewObj(interp, resultList);
7797 return (Jim_Obj*)EOF;
7798 err:
7799 Jim_DecrRefCount(interp, emptyStr);
7800 Jim_FreeNewObj(interp, resultList);
7801 return 0;
7802 }
7803
7804 /* -----------------------------------------------------------------------------
7805 * Pseudo Random Number Generation
7806 * ---------------------------------------------------------------------------*/
7807 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7808 int seedLen);
7809
7810 /* Initialize the sbox with the numbers from 0 to 255 */
7811 static void JimPrngInit(Jim_Interp *interp)
7812 {
7813 int i;
7814 unsigned int seed[256];
7815
7816 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7817 for (i = 0; i < 256; i++)
7818 seed[i] = (rand() ^ time(NULL) ^ clock());
7819 JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7820 }
7821
7822 /* Generates N bytes of random data */
7823 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7824 {
7825 Jim_PrngState *prng;
7826 unsigned char *destByte = (unsigned char*) dest;
7827 unsigned int si, sj, x;
7828
7829 /* initialization, only needed the first time */
7830 if (interp->prngState == NULL)
7831 JimPrngInit(interp);
7832 prng = interp->prngState;
7833 /* generates 'len' bytes of pseudo-random numbers */
7834 for (x = 0; x < len; x++) {
7835 prng->i = (prng->i+1) & 0xff;
7836 si = prng->sbox[prng->i];
7837 prng->j = (prng->j + si) & 0xff;
7838 sj = prng->sbox[prng->j];
7839 prng->sbox[prng->i] = sj;
7840 prng->sbox[prng->j] = si;
7841 *destByte++ = prng->sbox[(si+sj)&0xff];
7842 }
7843 }
7844
7845 /* Re-seed the generator with user-provided bytes */
7846 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7847 int seedLen)
7848 {
7849 int i;
7850 unsigned char buf[256];
7851 Jim_PrngState *prng;
7852
7853 /* initialization, only needed the first time */
7854 if (interp->prngState == NULL)
7855 JimPrngInit(interp);
7856 prng = interp->prngState;
7857
7858 /* Set the sbox[i] with i */
7859 for (i = 0; i < 256; i++)
7860 prng->sbox[i] = i;
7861 /* Now use the seed to perform a random permutation of the sbox */
7862 for (i = 0; i < seedLen; i++) {
7863 unsigned char t;
7864
7865 t = prng->sbox[i&0xFF];
7866 prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7867 prng->sbox[seed[i]] = t;
7868 }
7869 prng->i = prng->j = 0;
7870 /* discard the first 256 bytes of stream. */
7871 JimRandomBytes(interp, buf, 256);
7872 }
7873
7874 /* -----------------------------------------------------------------------------
7875 * Dynamic libraries support (WIN32 not supported)
7876 * ---------------------------------------------------------------------------*/
7877
7878 #ifdef JIM_DYNLIB
7879 #ifdef WIN32
7880 #define RTLD_LAZY 0
7881 void * dlopen(const char *path, int mode)
7882 {
7883 JIM_NOTUSED(mode);
7884
7885 return (void *)LoadLibraryA(path);
7886 }
7887 int dlclose(void *handle)
7888 {
7889 FreeLibrary((HANDLE)handle);
7890 return 0;
7891 }
7892 void *dlsym(void *handle, const char *symbol)
7893 {
7894 return GetProcAddress((HMODULE)handle, symbol);
7895 }
7896 static char win32_dlerror_string[121];
7897 const char *dlerror(void)
7898 {
7899 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7900 LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7901 return win32_dlerror_string;
7902 }
7903 #endif /* WIN32 */
7904
7905 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7906 {
7907 Jim_Obj *libPathObjPtr;
7908 int prefixc, i;
7909 void *handle;
7910 int (*onload)(Jim_Interp *interp);
7911
7912 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7913 if (libPathObjPtr == NULL) {
7914 prefixc = 0;
7915 libPathObjPtr = NULL;
7916 } else {
7917 Jim_IncrRefCount(libPathObjPtr);
7918 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7919 }
7920
7921 for (i = -1; i < prefixc; i++) {
7922 if (i < 0) {
7923 handle = dlopen(pathName, RTLD_LAZY);
7924 } else {
7925 FILE *fp;
7926 char buf[JIM_PATH_LEN];
7927 const char *prefix;
7928 int prefixlen;
7929 Jim_Obj *prefixObjPtr;
7930
7931 buf[0] = '\0';
7932 if (Jim_ListIndex(interp, libPathObjPtr, i,
7933 &prefixObjPtr, JIM_NONE) != JIM_OK)
7934 continue;
7935 prefix = Jim_GetString(prefixObjPtr, &prefixlen);
7936 if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7937 continue;
7938 if (*pathName == '/') {
7939 strcpy(buf, pathName);
7940 }
7941 else if (prefixlen && prefix[prefixlen-1] == '/')
7942 sprintf(buf, "%s%s", prefix, pathName);
7943 else
7944 sprintf(buf, "%s/%s", prefix, pathName);
7945 fp = fopen(buf, "r");
7946 if (fp == NULL)
7947 continue;
7948 fclose(fp);
7949 handle = dlopen(buf, RTLD_LAZY);
7950 }
7951 if (handle == NULL) {
7952 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7953 Jim_AppendStrings(interp, Jim_GetResult(interp),
7954 "error loading extension \"", pathName,
7955 "\": ", dlerror(), NULL);
7956 if (i < 0)
7957 continue;
7958 goto err;
7959 }
7960 if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7961 Jim_SetResultString(interp,
7962 "No Jim_OnLoad symbol found on extension", -1);
7963 goto err;
7964 }
7965 if (onload(interp) == JIM_ERR) {
7966 dlclose(handle);
7967 goto err;
7968 }
7969 Jim_SetEmptyResult(interp);
7970 if (libPathObjPtr != NULL)
7971 Jim_DecrRefCount(interp, libPathObjPtr);
7972 return JIM_OK;
7973 }
7974 err:
7975 if (libPathObjPtr != NULL)
7976 Jim_DecrRefCount(interp, libPathObjPtr);
7977 return JIM_ERR;
7978 }
7979 #else /* JIM_DYNLIB */
7980 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7981 {
7982 JIM_NOTUSED(interp);
7983 JIM_NOTUSED(pathName);
7984
7985 Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7986 return JIM_ERR;
7987 }
7988 #endif/* JIM_DYNLIB */
7989
7990 /* -----------------------------------------------------------------------------
7991 * Packages handling
7992 * ---------------------------------------------------------------------------*/
7993
7994 #define JIM_PKG_ANY_VERSION -1
7995
7996 /* Convert a string of the type "1.2" into an integer.
7997 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
7998 * to the integer with value 102 */
7999 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
8000 int *intPtr, int flags)
8001 {
8002 char *copy;
8003 jim_wide major, minor;
8004 char *majorStr, *minorStr, *p;
8005
8006 if (v[0] == '\0') {
8007 *intPtr = JIM_PKG_ANY_VERSION;
8008 return JIM_OK;
8009 }
8010
8011 copy = Jim_StrDup(v);
8012 p = strchr(copy, '.');
8013 if (p == NULL) goto badfmt;
8014 *p = '\0';
8015 majorStr = copy;
8016 minorStr = p+1;
8017
8018 if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
8019 Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
8020 goto badfmt;
8021 *intPtr = (int)(major*100+minor);
8022 Jim_Free(copy);
8023 return JIM_OK;
8024
8025 badfmt:
8026 Jim_Free(copy);
8027 if (flags & JIM_ERRMSG) {
8028 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8029 Jim_AppendStrings(interp, Jim_GetResult(interp),
8030 "invalid package version '", v, "'", NULL);
8031 }
8032 return JIM_ERR;
8033 }
8034
8035 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
8036 static int JimPackageMatchVersion(int needed, int actual, int flags)
8037 {
8038 if (needed == JIM_PKG_ANY_VERSION) return 1;
8039 if (flags & JIM_MATCHVER_EXACT) {
8040 return needed == actual;
8041 } else {
8042 return needed/100 == actual/100 && (needed <= actual);
8043 }
8044 }
8045
8046 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
8047 int flags)
8048 {
8049 int intVersion;
8050 /* Check if the version format is ok */
8051 if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
8052 return JIM_ERR;
8053 /* If the package was already provided returns an error. */
8054 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
8055 if (flags & JIM_ERRMSG) {
8056 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8057 Jim_AppendStrings(interp, Jim_GetResult(interp),
8058 "package '", name, "' was already provided", NULL);
8059 }
8060 return JIM_ERR;
8061 }
8062 Jim_AddHashEntry(&interp->packages, name, (char*) ver);
8063 return JIM_OK;
8064 }
8065
8066 #ifndef JIM_ANSIC
8067
8068 #ifndef WIN32
8069 # include <sys/types.h>
8070 # include <dirent.h>
8071 #else
8072 # include <io.h>
8073 /* Posix dirent.h compatiblity layer for WIN32.
8074 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
8075 * Copyright Salvatore Sanfilippo ,2005.
8076 *
8077 * Permission to use, copy, modify, and distribute this software and its
8078 * documentation for any purpose is hereby granted without fee, provided
8079 * that this copyright and permissions notice appear in all copies and
8080 * derivatives.
8081 *
8082 * This software is supplied "as is" without express or implied warranty.
8083 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
8084 */
8085
8086 struct dirent {
8087 char *d_name;
8088 };
8089
8090 typedef struct DIR {
8091 long handle; /* -1 for failed rewind */
8092 struct _finddata_t info;
8093 struct dirent result; /* d_name null iff first time */
8094 char *name; /* null-terminated char string */
8095 } DIR;
8096
8097 DIR *opendir(const char *name)
8098 {
8099 DIR *dir = 0;
8100
8101 if(name && name[0]) {
8102 size_t base_length = strlen(name);
8103 const char *all = /* search pattern must end with suitable wildcard */
8104 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
8105
8106 if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
8107 (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
8108 {
8109 strcat(strcpy(dir->name, name), all);
8110
8111 if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
8112 dir->result.d_name = 0;
8113 else { /* rollback */
8114 Jim_Free(dir->name);
8115 Jim_Free(dir);
8116 dir = 0;
8117 }
8118 } else { /* rollback */
8119 Jim_Free(dir);
8120 dir = 0;
8121 errno = ENOMEM;
8122 }
8123 } else {
8124 errno = EINVAL;
8125 }
8126 return dir;
8127 }
8128
8129 int closedir(DIR *dir)
8130 {
8131 int result = -1;
8132
8133 if(dir) {
8134 if(dir->handle != -1)
8135 result = _findclose(dir->handle);
8136 Jim_Free(dir->name);
8137 Jim_Free(dir);
8138 }
8139 if(result == -1) /* map all errors to EBADF */
8140 errno = EBADF;
8141 return result;
8142 }
8143
8144 struct dirent *readdir(DIR *dir)
8145 {
8146 struct dirent *result = 0;
8147
8148 if(dir && dir->handle != -1) {
8149 if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
8150 result = &dir->result;
8151 result->d_name = dir->info.name;
8152 }
8153 } else {
8154 errno = EBADF;
8155 }
8156 return result;
8157 }
8158
8159 #endif /* WIN32 */
8160
8161 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8162 int prefixc, const char *pkgName, int pkgVer, int flags)
8163 {
8164 int bestVer = -1, i;
8165 int pkgNameLen = strlen(pkgName);
8166 char *bestPackage = NULL;
8167 struct dirent *de;
8168
8169 for (i = 0; i < prefixc; i++) {
8170 DIR *dir;
8171 char buf[JIM_PATH_LEN];
8172 int prefixLen;
8173
8174 if (prefixes[i] == NULL) continue;
8175 strncpy(buf, prefixes[i], JIM_PATH_LEN);
8176 buf[JIM_PATH_LEN-1] = '\0';
8177 prefixLen = strlen(buf);
8178 if (prefixLen && buf[prefixLen-1] == '/')
8179 buf[prefixLen-1] = '\0';
8180
8181 if ((dir = opendir(buf)) == NULL) continue;
8182 while ((de = readdir(dir)) != NULL) {
8183 char *fileName = de->d_name;
8184 int fileNameLen = strlen(fileName);
8185
8186 if (strncmp(fileName, "jim-", 4) == 0 &&
8187 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
8188 *(fileName+4+pkgNameLen) == '-' &&
8189 fileNameLen > 4 && /* note that this is not really useful */
8190 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
8191 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
8192 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
8193 {
8194 char ver[6]; /* xx.yy<nulterm> */
8195 char *p = strrchr(fileName, '.');
8196 int verLen, fileVer;
8197
8198 verLen = p - (fileName+4+pkgNameLen+1);
8199 if (verLen < 3 || verLen > 5) continue;
8200 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8201 ver[verLen] = '\0';
8202 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8203 != JIM_OK) continue;
8204 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8205 (bestVer == -1 || bestVer < fileVer))
8206 {
8207 bestVer = fileVer;
8208 Jim_Free(bestPackage);
8209 bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8210 sprintf(bestPackage, "%s/%s", buf, fileName);
8211 }
8212 }
8213 }
8214 closedir(dir);
8215 }
8216 return bestPackage;
8217 }
8218
8219 #else /* JIM_ANSIC */
8220
8221 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8222 int prefixc, const char *pkgName, int pkgVer, int flags)
8223 {
8224 JIM_NOTUSED(interp);
8225 JIM_NOTUSED(prefixes);
8226 JIM_NOTUSED(prefixc);
8227 JIM_NOTUSED(pkgName);
8228 JIM_NOTUSED(pkgVer);
8229 JIM_NOTUSED(flags);
8230 return NULL;
8231 }
8232
8233 #endif /* JIM_ANSIC */
8234
8235 /* Search for a suitable package under every dir specified by jim_libpath
8236 * and load it if possible. If a suitable package was loaded with success
8237 * JIM_OK is returned, otherwise JIM_ERR is returned. */
8238 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8239 int flags)
8240 {
8241 Jim_Obj *libPathObjPtr;
8242 char **prefixes, *best;
8243 int prefixc, i, retCode = JIM_OK;
8244
8245 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8246 if (libPathObjPtr == NULL) {
8247 prefixc = 0;
8248 libPathObjPtr = NULL;
8249 } else {
8250 Jim_IncrRefCount(libPathObjPtr);
8251 Jim_ListLength(interp, libPathObjPtr, &prefixc);
8252 }
8253
8254 prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8255 for (i = 0; i < prefixc; i++) {
8256 Jim_Obj *prefixObjPtr;
8257 if (Jim_ListIndex(interp, libPathObjPtr, i,
8258 &prefixObjPtr, JIM_NONE) != JIM_OK)
8259 {
8260 prefixes[i] = NULL;
8261 continue;
8262 }
8263 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8264 }
8265 /* Scan every directory to find the "best" package. */
8266 best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8267 if (best != NULL) {
8268 char *p = strrchr(best, '.');
8269 /* Try to load/source it */
8270 if (p && strcmp(p, ".tcl") == 0) {
8271 retCode = Jim_EvalFile(interp, best);
8272 } else {
8273 retCode = Jim_LoadLibrary(interp, best);
8274 }
8275 } else {
8276 retCode = JIM_ERR;
8277 }
8278 Jim_Free(best);
8279 for (i = 0; i < prefixc; i++)
8280 Jim_Free(prefixes[i]);
8281 Jim_Free(prefixes);
8282 if (libPathObjPtr)
8283 Jim_DecrRefCount(interp, libPathObjPtr);
8284 return retCode;
8285 }
8286
8287 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8288 const char *ver, int flags)
8289 {
8290 Jim_HashEntry *he;
8291 int requiredVer;
8292
8293 /* Start with an empty error string */
8294 Jim_SetResultString(interp, "", 0);
8295
8296 if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8297 return NULL;
8298 he = Jim_FindHashEntry(&interp->packages, name);
8299 if (he == NULL) {
8300 /* Try to load the package. */
8301 if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8302 he = Jim_FindHashEntry(&interp->packages, name);
8303 if (he == NULL) {
8304 return "?";
8305 }
8306 return he->val;
8307 }
8308 /* No way... return an error. */
8309 if (flags & JIM_ERRMSG) {
8310 int len;
8311 Jim_GetString(Jim_GetResult(interp), &len);
8312 Jim_AppendStrings(interp, Jim_GetResult(interp), len ? "\n" : "",
8313 "Can't find package '", name, "'", NULL);
8314 }
8315 return NULL;
8316 } else {
8317 int actualVer;
8318 if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8319 != JIM_OK)
8320 {
8321 return NULL;
8322 }
8323 /* Check if version matches. */
8324 if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8325 Jim_AppendStrings(interp, Jim_GetResult(interp),
8326 "Package '", name, "' already loaded, but with version ",
8327 he->val, NULL);
8328 return NULL;
8329 }
8330 return he->val;
8331 }
8332 }
8333
8334 /* -----------------------------------------------------------------------------
8335 * Eval
8336 * ---------------------------------------------------------------------------*/
8337 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8338 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8339
8340 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8341 Jim_Obj *const *argv);
8342
8343 /* Handle calls to the [unknown] command */
8344 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8345 {
8346 Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8347 int retCode;
8348
8349 /* If JimUnknown() is recursively called (e.g. error in the unknown proc,
8350 * done here
8351 */
8352 if (interp->unknown_called) {
8353 return JIM_ERR;
8354 }
8355
8356 /* If the [unknown] command does not exists returns
8357 * just now */
8358 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8359 return JIM_ERR;
8360
8361 /* The object interp->unknown just contains
8362 * the "unknown" string, it is used in order to
8363 * avoid to lookup the unknown command every time
8364 * but instread to cache the result. */
8365 if (argc+1 <= JIM_EVAL_SARGV_LEN)
8366 v = sv;
8367 else
8368 v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8369 /* Make a copy of the arguments vector, but shifted on
8370 * the right of one position. The command name of the
8371 * command will be instead the first argument of the
8372 * [unknonw] call. */
8373 memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8374 v[0] = interp->unknown;
8375 /* Call it */
8376 interp->unknown_called++;
8377 retCode = Jim_EvalObjVector(interp, argc+1, v);
8378 interp->unknown_called--;
8379
8380 /* Clean up */
8381 if (v != sv)
8382 Jim_Free(v);
8383 return retCode;
8384 }
8385
8386 /* Eval the object vector 'objv' composed of 'objc' elements.
8387 * Every element is used as single argument.
8388 * Jim_EvalObj() will call this function every time its object
8389 * argument is of "list" type, with no string representation.
8390 *
8391 * This is possible because the string representation of a
8392 * list object generated by the UpdateStringOfList is made
8393 * in a way that ensures that every list element is a different
8394 * command argument. */
8395 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8396 {
8397 int i, retcode;
8398 Jim_Cmd *cmdPtr;
8399
8400 /* Incr refcount of arguments. */
8401 for (i = 0; i < objc; i++)
8402 Jim_IncrRefCount(objv[i]);
8403 /* Command lookup */
8404 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8405 if (cmdPtr == NULL) {
8406 retcode = JimUnknown(interp, objc, objv);
8407 } else {
8408 /* Call it -- Make sure result is an empty object. */
8409 Jim_SetEmptyResult(interp);
8410 if (cmdPtr->cmdProc) {
8411 interp->cmdPrivData = cmdPtr->privData;
8412 retcode = cmdPtr->cmdProc(interp, objc, objv);
8413 if (retcode == JIM_ERR_ADDSTACK) {
8414 //JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8415 retcode = JIM_ERR;
8416 }
8417 } else {
8418 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8419 if (retcode == JIM_ERR) {
8420 JimAppendStackTrace(interp,
8421 Jim_GetString(objv[0], NULL), "", 1);
8422 }
8423 }
8424 }
8425 /* Decr refcount of arguments and return the retcode */
8426 for (i = 0; i < objc; i++)
8427 Jim_DecrRefCount(interp, objv[i]);
8428 return retcode;
8429 }
8430
8431 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8432 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8433 * The returned object has refcount = 0. */
8434 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8435 int tokens, Jim_Obj **objPtrPtr)
8436 {
8437 int totlen = 0, i, retcode;
8438 Jim_Obj **intv;
8439 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8440 Jim_Obj *objPtr;
8441 char *s;
8442
8443 if (tokens <= JIM_EVAL_SINTV_LEN)
8444 intv = sintv;
8445 else
8446 intv = Jim_Alloc(sizeof(Jim_Obj*)*
8447 tokens);
8448 /* Compute every token forming the argument
8449 * in the intv objects vector. */
8450 for (i = 0; i < tokens; i++) {
8451 switch(token[i].type) {
8452 case JIM_TT_ESC:
8453 case JIM_TT_STR:
8454 intv[i] = token[i].objPtr;
8455 break;
8456 case JIM_TT_VAR:
8457 intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8458 if (!intv[i]) {
8459 retcode = JIM_ERR;
8460 goto err;
8461 }
8462 break;
8463 case JIM_TT_DICTSUGAR:
8464 intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8465 if (!intv[i]) {
8466 retcode = JIM_ERR;
8467 goto err;
8468 }
8469 break;
8470 case JIM_TT_CMD:
8471 retcode = Jim_EvalObj(interp, token[i].objPtr);
8472 if (retcode != JIM_OK)
8473 goto err;
8474 intv[i] = Jim_GetResult(interp);
8475 break;
8476 default:
8477 Jim_Panic(interp,
8478 "default token type reached "
8479 "in Jim_InterpolateTokens().");
8480 break;
8481 }
8482 Jim_IncrRefCount(intv[i]);
8483 /* Make sure there is a valid
8484 * string rep, and add the string
8485 * length to the total legnth. */
8486 Jim_GetString(intv[i], NULL);
8487 totlen += intv[i]->length;
8488 }
8489 /* Concatenate every token in an unique
8490 * object. */
8491 objPtr = Jim_NewStringObjNoAlloc(interp,
8492 NULL, 0);
8493 s = objPtr->bytes = Jim_Alloc(totlen+1);
8494 objPtr->length = totlen;
8495 for (i = 0; i < tokens; i++) {
8496 memcpy(s, intv[i]->bytes, intv[i]->length);
8497 s += intv[i]->length;
8498 Jim_DecrRefCount(interp, intv[i]);
8499 }
8500 objPtr->bytes[totlen] = '\0';
8501 /* Free the intv vector if not static. */
8502 if (tokens > JIM_EVAL_SINTV_LEN)
8503 Jim_Free(intv);
8504 *objPtrPtr = objPtr;
8505 return JIM_OK;
8506 err:
8507 i--;
8508 for (; i >= 0; i--)
8509 Jim_DecrRefCount(interp, intv[i]);
8510 if (tokens > JIM_EVAL_SINTV_LEN)
8511 Jim_Free(intv);
8512 return retcode;
8513 }
8514
8515 /* Helper of Jim_EvalObj() to perform argument expansion.
8516 * Basically this function append an argument to 'argv'
8517 * (and increments argc by reference accordingly), performing
8518 * expansion of the list object if 'expand' is non-zero, or
8519 * just adding objPtr to argv if 'expand' is zero. */
8520 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8521 int *argcPtr, int expand, Jim_Obj *objPtr)
8522 {
8523 if (!expand) {
8524 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8525 /* refcount of objPtr not incremented because
8526 * we are actually transfering a reference from
8527 * the old 'argv' to the expanded one. */
8528 (*argv)[*argcPtr] = objPtr;
8529 (*argcPtr)++;
8530 } else {
8531 int len, i;
8532
8533 Jim_ListLength(interp, objPtr, &len);
8534 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8535 for (i = 0; i < len; i++) {
8536 (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8537 Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8538 (*argcPtr)++;
8539 }
8540 /* The original object reference is no longer needed,
8541 * after the expansion it is no longer present on
8542 * the argument vector, but the single elements are
8543 * in its place. */
8544 Jim_DecrRefCount(interp, objPtr);
8545 }
8546 }
8547
8548 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8549 {
8550 int i, j = 0, len;
8551 ScriptObj *script;
8552 ScriptToken *token;
8553 int *cs; /* command structure array */
8554 int retcode = JIM_OK;
8555 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8556
8557 interp->errorFlag = 0;
8558
8559 /* If the object is of type "list" and there is no
8560 * string representation for this object, we can call
8561 * a specialized version of Jim_EvalObj() */
8562 if (scriptObjPtr->typePtr == &listObjType &&
8563 scriptObjPtr->internalRep.listValue.len &&
8564 scriptObjPtr->bytes == NULL) {
8565 Jim_IncrRefCount(scriptObjPtr);
8566 retcode = Jim_EvalObjVector(interp,
8567 scriptObjPtr->internalRep.listValue.len,
8568 scriptObjPtr->internalRep.listValue.ele);
8569 Jim_DecrRefCount(interp, scriptObjPtr);
8570 return retcode;
8571 }
8572
8573 Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8574 script = Jim_GetScript(interp, scriptObjPtr);
8575 /* Now we have to make sure the internal repr will not be
8576 * freed on shimmering.
8577 *
8578 * Think for example to this:
8579 *
8580 * set x {llength $x; ... some more code ...}; eval $x
8581 *
8582 * In order to preserve the internal rep, we increment the
8583 * inUse field of the script internal rep structure. */
8584 script->inUse++;
8585
8586 token = script->token;
8587 len = script->len;
8588 cs = script->cmdStruct;
8589 i = 0; /* 'i' is the current token index. */
8590
8591 /* Reset the interpreter result. This is useful to
8592 * return the emtpy result in the case of empty program. */
8593 Jim_SetEmptyResult(interp);
8594
8595 /* Execute every command sequentially, returns on
8596 * error (i.e. if a command does not return JIM_OK) */
8597 while (i < len) {
8598 int expand = 0;
8599 int argc = *cs++; /* Get the number of arguments */
8600 Jim_Cmd *cmd;
8601
8602 /* Set the expand flag if needed. */
8603 if (argc == -1) {
8604 expand++;
8605 argc = *cs++;
8606 }
8607 /* Allocate the arguments vector */
8608 if (argc <= JIM_EVAL_SARGV_LEN)
8609 argv = sargv;
8610 else
8611 argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8612 /* Populate the arguments objects. */
8613 for (j = 0; j < argc; j++) {
8614 int tokens = *cs++;
8615
8616 /* tokens is negative if expansion is needed.
8617 * for this argument. */
8618 if (tokens < 0) {
8619 tokens = (-tokens)-1;
8620 i++;
8621 }
8622 if (tokens == 1) {
8623 /* Fast path if the token does not
8624 * need interpolation */
8625 switch(token[i].type) {
8626 case JIM_TT_ESC:
8627 case JIM_TT_STR:
8628 argv[j] = token[i].objPtr;
8629 break;
8630 case JIM_TT_VAR:
8631 tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8632 JIM_ERRMSG);
8633 if (!tmpObjPtr) {
8634 retcode = JIM_ERR;
8635 goto err;
8636 }
8637 argv[j] = tmpObjPtr;
8638 break;
8639 case JIM_TT_DICTSUGAR:
8640 tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8641 if (!tmpObjPtr) {
8642 retcode = JIM_ERR;
8643 goto err;
8644 }
8645 argv[j] = tmpObjPtr;
8646 break;
8647 case JIM_TT_CMD:
8648 retcode = Jim_EvalObj(interp, token[i].objPtr);
8649 if (retcode != JIM_OK)
8650 goto err;
8651 argv[j] = Jim_GetResult(interp);
8652 break;
8653 default:
8654 Jim_Panic(interp,
8655 "default token type reached "
8656 "in Jim_EvalObj().");
8657 break;
8658 }
8659 Jim_IncrRefCount(argv[j]);
8660 i += 2;
8661 } else {
8662 /* For interpolation we call an helper
8663 * function doing the work for us. */
8664 if ((retcode = Jim_InterpolateTokens(interp,
8665 token+i, tokens, &tmpObjPtr)) != JIM_OK)
8666 {
8667 goto err;
8668 }
8669 argv[j] = tmpObjPtr;
8670 Jim_IncrRefCount(argv[j]);
8671 i += tokens+1;
8672 }
8673 }
8674 /* Handle {expand} expansion */
8675 if (expand) {
8676 int *ecs = cs - argc;
8677 int eargc = 0;
8678 Jim_Obj **eargv = NULL;
8679
8680 for (j = 0; j < argc; j++) {
8681 Jim_ExpandArgument( interp, &eargv, &eargc,
8682 ecs[j] < 0, argv[j]);
8683 }
8684 if (argv != sargv)
8685 Jim_Free(argv);
8686 argc = eargc;
8687 argv = eargv;
8688 j = argc;
8689 if (argc == 0) {
8690 /* Nothing to do with zero args. */
8691 Jim_Free(eargv);
8692 continue;
8693 }
8694 }
8695 /* Lookup the command to call */
8696 cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8697 if (cmd != NULL) {
8698 /* Call it -- Make sure result is an empty object. */
8699 Jim_SetEmptyResult(interp);
8700 if (cmd->cmdProc) {
8701 interp->cmdPrivData = cmd->privData;
8702 retcode = cmd->cmdProc(interp, argc, argv);
8703 if ((retcode == JIM_ERR)||(retcode == JIM_ERR_ADDSTACK)) {
8704 JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8705 retcode = JIM_ERR;
8706 }
8707 } else {
8708 retcode = JimCallProcedure(interp, cmd, argc, argv);
8709 if (retcode == JIM_ERR) {
8710 JimAppendStackTrace(interp,
8711 Jim_GetString(argv[0], NULL), script->fileName,
8712 token[i-argc*2].linenr);
8713 }
8714 }
8715 } else {
8716 /* Call [unknown] */
8717 retcode = JimUnknown(interp, argc, argv);
8718 if (retcode == JIM_ERR) {
8719 JimAppendStackTrace(interp,
8720 "", script->fileName,
8721 token[i-argc*2].linenr);
8722 }
8723 }
8724 if (retcode != JIM_OK) {
8725 i -= argc*2; /* point to the command name. */
8726 goto err;
8727 }
8728 /* Decrement the arguments count */
8729 for (j = 0; j < argc; j++) {
8730 Jim_DecrRefCount(interp, argv[j]);
8731 }
8732
8733 if (argv != sargv) {
8734 Jim_Free(argv);
8735 argv = NULL;
8736 }
8737 }
8738 /* Note that we don't have to decrement inUse, because the
8739 * following code transfers our use of the reference again to
8740 * the script object. */
8741 j = 0; /* on normal termination, the argv array is already
8742 Jim_DecrRefCount-ed. */
8743 err:
8744 /* Handle errors. */
8745 if (retcode == JIM_ERR && !interp->errorFlag) {
8746 interp->errorFlag = 1;
8747 JimSetErrorFileName(interp, script->fileName);
8748 JimSetErrorLineNumber(interp, token[i].linenr);
8749 JimResetStackTrace(interp);
8750 }
8751 Jim_FreeIntRep(interp, scriptObjPtr);
8752 scriptObjPtr->typePtr = &scriptObjType;
8753 Jim_SetIntRepPtr(scriptObjPtr, script);
8754 Jim_DecrRefCount(interp, scriptObjPtr);
8755 for (i = 0; i < j; i++) {
8756 Jim_DecrRefCount(interp, argv[i]);
8757 }
8758 if (argv != sargv)
8759 Jim_Free(argv);
8760 return retcode;
8761 }
8762
8763 /* Call a procedure implemented in Tcl.
8764 * It's possible to speed-up a lot this function, currently
8765 * the callframes are not cached, but allocated and
8766 * destroied every time. What is expecially costly is
8767 * to create/destroy the local vars hash table every time.
8768 *
8769 * This can be fixed just implementing callframes caching
8770 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8771 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8772 Jim_Obj *const *argv)
8773 {
8774 int i, retcode;
8775 Jim_CallFrame *callFramePtr;
8776 int num_args;
8777
8778 /* Check arity */
8779 if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8780 argc > cmd->arityMax)) {
8781 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8782 Jim_AppendStrings(interp, objPtr,
8783 "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8784 (cmd->arityMin > 1) ? " " : "",
8785 Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8786 Jim_SetResult(interp, objPtr);
8787 return JIM_ERR;
8788 }
8789 /* Check if there are too nested calls */
8790 if (interp->numLevels == interp->maxNestingDepth) {
8791 Jim_SetResultString(interp,
8792 "Too many nested calls. Infinite recursion?", -1);
8793 return JIM_ERR;
8794 }
8795 /* Create a new callframe */
8796 callFramePtr = JimCreateCallFrame(interp);
8797 callFramePtr->parentCallFrame = interp->framePtr;
8798 callFramePtr->argv = argv;
8799 callFramePtr->argc = argc;
8800 callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8801 callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8802 callFramePtr->staticVars = cmd->staticVars;
8803 Jim_IncrRefCount(cmd->argListObjPtr);
8804 Jim_IncrRefCount(cmd->bodyObjPtr);
8805 interp->framePtr = callFramePtr;
8806 interp->numLevels ++;
8807
8808 /* Set arguments */
8809 Jim_ListLength(interp, cmd->argListObjPtr, &num_args);
8810
8811 /* If last argument is 'args', don't set it here */
8812 if (cmd->arityMax == -1) {
8813 num_args--;
8814 }
8815
8816 for (i = 0; i < num_args; i++) {
8817 Jim_Obj *argObjPtr;
8818 Jim_Obj *nameObjPtr;
8819 Jim_Obj *valueObjPtr;
8820
8821 Jim_ListIndex(interp, cmd->argListObjPtr, i, &argObjPtr, JIM_NONE);
8822 if (i + 1 >= cmd->arityMin) {
8823 /* The name is the first element of the list */
8824 Jim_ListIndex(interp, argObjPtr, 0, &nameObjPtr, JIM_NONE);
8825 }
8826 else {
8827 /* The element arg is the name */
8828 nameObjPtr = argObjPtr;
8829 }
8830
8831 if (i + 1 >= argc) {
8832 /* No more values, so use default */
8833 /* The value is the second element of the list */
8834 Jim_ListIndex(interp, argObjPtr, 1, &valueObjPtr, JIM_NONE);
8835 }
8836 else {
8837 valueObjPtr = argv[i+1];
8838 }
8839 Jim_SetVariable(interp, nameObjPtr, valueObjPtr);
8840 }
8841 /* Set optional arguments */
8842 if (cmd->arityMax == -1) {
8843 Jim_Obj *listObjPtr, *objPtr;
8844
8845 i++;
8846 listObjPtr = Jim_NewListObj(interp, argv+i, argc-i);
8847 Jim_ListIndex(interp, cmd->argListObjPtr, num_args, &objPtr, JIM_NONE);
8848 Jim_SetVariable(interp, objPtr, listObjPtr);
8849 }
8850 /* Eval the body */
8851 retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8852
8853 /* Destroy the callframe */
8854 interp->numLevels --;
8855 interp->framePtr = interp->framePtr->parentCallFrame;
8856 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8857 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8858 } else {
8859 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8860 }
8861 /* Handle the JIM_EVAL return code */
8862 if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8863 int savedLevel = interp->evalRetcodeLevel;
8864
8865 interp->evalRetcodeLevel = interp->numLevels;
8866 while (retcode == JIM_EVAL) {
8867 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8868 Jim_IncrRefCount(resultScriptObjPtr);
8869 retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8870 Jim_DecrRefCount(interp, resultScriptObjPtr);
8871 }
8872 interp->evalRetcodeLevel = savedLevel;
8873 }
8874 /* Handle the JIM_RETURN return code */
8875 if (retcode == JIM_RETURN) {
8876 retcode = interp->returnCode;
8877 interp->returnCode = JIM_OK;
8878 }
8879 return retcode;
8880 }
8881
8882 int Jim_Eval_Named(Jim_Interp *interp, const char *script, const char *filename, int lineno)
8883 {
8884 int retval;
8885 Jim_Obj *scriptObjPtr;
8886
8887 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8888 Jim_IncrRefCount(scriptObjPtr);
8889
8890
8891 if( filename ){
8892 JimSetSourceInfo( interp, scriptObjPtr, filename, lineno );
8893 }
8894
8895 retval = Jim_EvalObj(interp, scriptObjPtr);
8896 Jim_DecrRefCount(interp, scriptObjPtr);
8897 return retval;
8898 }
8899
8900 int Jim_Eval(Jim_Interp *interp, const char *script)
8901 {
8902 return Jim_Eval_Named( interp, script, NULL, 0 );
8903 }
8904
8905
8906
8907 /* Execute script in the scope of the global level */
8908 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8909 {
8910 Jim_CallFrame *savedFramePtr;
8911 int retval;
8912
8913 savedFramePtr = interp->framePtr;
8914 interp->framePtr = interp->topFramePtr;
8915 retval = Jim_Eval(interp, script);
8916 interp->framePtr = savedFramePtr;
8917 return retval;
8918 }
8919
8920 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8921 {
8922 Jim_CallFrame *savedFramePtr;
8923 int retval;
8924
8925 savedFramePtr = interp->framePtr;
8926 interp->framePtr = interp->topFramePtr;
8927 retval = Jim_EvalObj(interp, scriptObjPtr);
8928 interp->framePtr = savedFramePtr;
8929 /* Try to report the error (if any) via the bgerror proc */
8930 if (retval != JIM_OK) {
8931 Jim_Obj *objv[2];
8932
8933 objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8934 objv[1] = Jim_GetResult(interp);
8935 Jim_IncrRefCount(objv[0]);
8936 Jim_IncrRefCount(objv[1]);
8937 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8938 /* Report the error to stderr. */
8939 Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8940 Jim_PrintErrorMessage(interp);
8941 }
8942 Jim_DecrRefCount(interp, objv[0]);
8943 Jim_DecrRefCount(interp, objv[1]);
8944 }
8945 return retval;
8946 }
8947
8948 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8949 {
8950 char *prg = NULL;
8951 FILE *fp;
8952 int nread, totread, maxlen, buflen;
8953 int retval;
8954 Jim_Obj *scriptObjPtr;
8955
8956 if ((fp = fopen(filename, "r")) == NULL) {
8957 const int cwd_len=2048;
8958 char *cwd=malloc(cwd_len);
8959 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8960 if (!getcwd( cwd, cwd_len )) strcpy(cwd, "unknown");
8961 Jim_AppendStrings(interp, Jim_GetResult(interp),
8962 "Error loading script \"", filename, "\"",
8963 " cwd: ", cwd,
8964 " err: ", strerror(errno), NULL);
8965 free(cwd);
8966 return JIM_ERR;
8967 }
8968 buflen = 1024;
8969 maxlen = totread = 0;
8970 while (1) {
8971 if (maxlen < totread+buflen+1) {
8972 maxlen = totread+buflen+1;
8973 prg = Jim_Realloc(prg, maxlen);
8974 }
8975 /* do not use Jim_fread() - this is really a file */
8976 if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8977 totread += nread;
8978 }
8979 prg[totread] = '\0';
8980 /* do not use Jim_fclose() - this is really a file */
8981 fclose(fp);
8982
8983 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8984 JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8985 Jim_IncrRefCount(scriptObjPtr);
8986 retval = Jim_EvalObj(interp, scriptObjPtr);
8987 Jim_DecrRefCount(interp, scriptObjPtr);
8988 return retval;
8989 }
8990
8991 /* -----------------------------------------------------------------------------
8992 * Subst
8993 * ---------------------------------------------------------------------------*/
8994 static int JimParseSubstStr(struct JimParserCtx *pc)
8995 {
8996 pc->tstart = pc->p;
8997 pc->tline = pc->linenr;
8998 while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8999 pc->p++; pc->len--;
9000 }
9001 pc->tend = pc->p-1;
9002 pc->tt = JIM_TT_ESC;
9003 return JIM_OK;
9004 }
9005
9006 static int JimParseSubst(struct JimParserCtx *pc, int flags)
9007 {
9008 int retval;
9009
9010 if (pc->len == 0) {
9011 pc->tstart = pc->tend = pc->p;
9012 pc->tline = pc->linenr;
9013 pc->tt = JIM_TT_EOL;
9014 pc->eof = 1;
9015 return JIM_OK;
9016 }
9017 switch(*pc->p) {
9018 case '[':
9019 retval = JimParseCmd(pc);
9020 if (flags & JIM_SUBST_NOCMD) {
9021 pc->tstart--;
9022 pc->tend++;
9023 pc->tt = (flags & JIM_SUBST_NOESC) ?
9024 JIM_TT_STR : JIM_TT_ESC;
9025 }
9026 return retval;
9027 break;
9028 case '$':
9029 if (JimParseVar(pc) == JIM_ERR) {
9030 pc->tstart = pc->tend = pc->p++; pc->len--;
9031 pc->tline = pc->linenr;
9032 pc->tt = JIM_TT_STR;
9033 } else {
9034 if (flags & JIM_SUBST_NOVAR) {
9035 pc->tstart--;
9036 if (flags & JIM_SUBST_NOESC)
9037 pc->tt = JIM_TT_STR;
9038 else
9039 pc->tt = JIM_TT_ESC;
9040 if (*pc->tstart == '{') {
9041 pc->tstart--;
9042 if (*(pc->tend+1))
9043 pc->tend++;
9044 }
9045 }
9046 }
9047 break;
9048 default:
9049 retval = JimParseSubstStr(pc);
9050 if (flags & JIM_SUBST_NOESC)
9051 pc->tt = JIM_TT_STR;
9052 return retval;
9053 break;
9054 }
9055 return JIM_OK;
9056 }
9057
9058 /* The subst object type reuses most of the data structures and functions
9059 * of the script object. Script's data structures are a bit more complex
9060 * for what is needed for [subst]itution tasks, but the reuse helps to
9061 * deal with a single data structure at the cost of some more memory
9062 * usage for substitutions. */
9063 static Jim_ObjType substObjType = {
9064 "subst",
9065 FreeScriptInternalRep,
9066 DupScriptInternalRep,
9067 NULL,
9068 JIM_TYPE_REFERENCES,
9069 };
9070
9071 /* This method takes the string representation of an object
9072 * as a Tcl string where to perform [subst]itution, and generates
9073 * the pre-parsed internal representation. */
9074 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
9075 {
9076 int scriptTextLen;
9077 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9078 struct JimParserCtx parser;
9079 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
9080
9081 script->len = 0;
9082 script->csLen = 0;
9083 script->commands = 0;
9084 script->token = NULL;
9085 script->cmdStruct = NULL;
9086 script->inUse = 1;
9087 script->substFlags = flags;
9088 script->fileName = NULL;
9089
9090 JimParserInit(&parser, scriptText, scriptTextLen, 1);
9091 while(1) {
9092 char *token;
9093 int len, type, linenr;
9094
9095 JimParseSubst(&parser, flags);
9096 if (JimParserEof(&parser)) break;
9097 token = JimParserGetToken(&parser, &len, &type, &linenr);
9098 ScriptObjAddToken(interp, script, token, len, type,
9099 NULL, linenr);
9100 }
9101 /* Free the old internal rep and set the new one. */
9102 Jim_FreeIntRep(interp, objPtr);
9103 Jim_SetIntRepPtr(objPtr, script);
9104 objPtr->typePtr = &scriptObjType;
9105 return JIM_OK;
9106 }
9107
9108 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9109 {
9110 struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
9111
9112 if (objPtr->typePtr != &substObjType || script->substFlags != flags)
9113 SetSubstFromAny(interp, objPtr, flags);
9114 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
9115 }
9116
9117 /* Performs commands,variables,blackslashes substitution,
9118 * storing the result object (with refcount 0) into
9119 * resObjPtrPtr. */
9120 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
9121 Jim_Obj **resObjPtrPtr, int flags)
9122 {
9123 ScriptObj *script;
9124 ScriptToken *token;
9125 int i, len, retcode = JIM_OK;
9126 Jim_Obj *resObjPtr, *savedResultObjPtr;
9127
9128 script = Jim_GetSubst(interp, substObjPtr, flags);
9129 #ifdef JIM_OPTIMIZATION
9130 /* Fast path for a very common case with array-alike syntax,
9131 * that's: $foo($bar) */
9132 if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
9133 Jim_Obj *varObjPtr = script->token[0].objPtr;
9134
9135 Jim_IncrRefCount(varObjPtr);
9136 resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
9137 if (resObjPtr == NULL) {
9138 Jim_DecrRefCount(interp, varObjPtr);
9139 return JIM_ERR;
9140 }
9141 Jim_DecrRefCount(interp, varObjPtr);
9142 *resObjPtrPtr = resObjPtr;
9143 return JIM_OK;
9144 }
9145 #endif
9146
9147 Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
9148 /* In order to preserve the internal rep, we increment the
9149 * inUse field of the script internal rep structure. */
9150 script->inUse++;
9151
9152 token = script->token;
9153 len = script->len;
9154
9155 /* Save the interp old result, to set it again before
9156 * to return. */
9157 savedResultObjPtr = interp->result;
9158 Jim_IncrRefCount(savedResultObjPtr);
9159
9160 /* Perform the substitution. Starts with an empty object
9161 * and adds every token (performing the appropriate
9162 * var/command/escape substitution). */
9163 resObjPtr = Jim_NewStringObj(interp, "", 0);
9164 for (i = 0; i < len; i++) {
9165 Jim_Obj *objPtr;
9166
9167 switch(token[i].type) {
9168 case JIM_TT_STR:
9169 case JIM_TT_ESC:
9170 Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
9171 break;
9172 case JIM_TT_VAR:
9173 objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
9174 if (objPtr == NULL) goto err;
9175 Jim_IncrRefCount(objPtr);
9176 Jim_AppendObj(interp, resObjPtr, objPtr);
9177 Jim_DecrRefCount(interp, objPtr);
9178 break;
9179 case JIM_TT_DICTSUGAR:
9180 objPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
9181 if (!objPtr) {
9182 retcode = JIM_ERR;
9183 goto err;
9184 }
9185 break;
9186 case JIM_TT_CMD:
9187 if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
9188 goto err;
9189 Jim_AppendObj(interp, resObjPtr, interp->result);
9190 break;
9191 default:
9192 Jim_Panic(interp,
9193 "default token type (%d) reached "
9194 "in Jim_SubstObj().", token[i].type);
9195 break;
9196 }
9197 }
9198 ok:
9199 if (retcode == JIM_OK)
9200 Jim_SetResult(interp, savedResultObjPtr);
9201 Jim_DecrRefCount(interp, savedResultObjPtr);
9202 /* Note that we don't have to decrement inUse, because the
9203 * following code transfers our use of the reference again to
9204 * the script object. */
9205 Jim_FreeIntRep(interp, substObjPtr);
9206 substObjPtr->typePtr = &scriptObjType;
9207 Jim_SetIntRepPtr(substObjPtr, script);
9208 Jim_DecrRefCount(interp, substObjPtr);
9209 *resObjPtrPtr = resObjPtr;
9210 return retcode;
9211 err:
9212 Jim_FreeNewObj(interp, resObjPtr);
9213 retcode = JIM_ERR;
9214 goto ok;
9215 }
9216
9217 /* -----------------------------------------------------------------------------
9218 * API Input/Export functions
9219 * ---------------------------------------------------------------------------*/
9220
9221 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
9222 {
9223 Jim_HashEntry *he;
9224
9225 he = Jim_FindHashEntry(&interp->stub, funcname);
9226 if (!he)
9227 return JIM_ERR;
9228 memcpy(targetPtrPtr, &he->val, sizeof(void*));
9229 return JIM_OK;
9230 }
9231
9232 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
9233 {
9234 return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
9235 }
9236
9237 #define JIM_REGISTER_API(name) \
9238 Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
9239
9240 void JimRegisterCoreApi(Jim_Interp *interp)
9241 {
9242 interp->getApiFuncPtr = Jim_GetApi;
9243 JIM_REGISTER_API(Alloc);
9244 JIM_REGISTER_API(Free);
9245 JIM_REGISTER_API(Eval);
9246 JIM_REGISTER_API(Eval_Named);
9247 JIM_REGISTER_API(EvalGlobal);
9248 JIM_REGISTER_API(EvalFile);
9249 JIM_REGISTER_API(EvalObj);
9250 JIM_REGISTER_API(EvalObjBackground);
9251 JIM_REGISTER_API(EvalObjVector);
9252 JIM_REGISTER_API(InitHashTable);
9253 JIM_REGISTER_API(ExpandHashTable);
9254 JIM_REGISTER_API(AddHashEntry);
9255 JIM_REGISTER_API(ReplaceHashEntry);
9256 JIM_REGISTER_API(DeleteHashEntry);
9257 JIM_REGISTER_API(FreeHashTable);
9258 JIM_REGISTER_API(FindHashEntry);
9259 JIM_REGISTER_API(ResizeHashTable);
9260 JIM_REGISTER_API(GetHashTableIterator);
9261 JIM_REGISTER_API(NextHashEntry);
9262 JIM_REGISTER_API(NewObj);
9263 JIM_REGISTER_API(FreeObj);
9264 JIM_REGISTER_API(InvalidateStringRep);
9265 JIM_REGISTER_API(InitStringRep);
9266 JIM_REGISTER_API(DuplicateObj);
9267 JIM_REGISTER_API(GetString);
9268 JIM_REGISTER_API(Length);
9269 JIM_REGISTER_API(InvalidateStringRep);
9270 JIM_REGISTER_API(NewStringObj);
9271 JIM_REGISTER_API(NewStringObjNoAlloc);
9272 JIM_REGISTER_API(AppendString);
9273 JIM_REGISTER_API(AppendString_sprintf);
9274 JIM_REGISTER_API(AppendObj);
9275 JIM_REGISTER_API(AppendStrings);
9276 JIM_REGISTER_API(StringEqObj);
9277 JIM_REGISTER_API(StringMatchObj);
9278 JIM_REGISTER_API(StringRangeObj);
9279 JIM_REGISTER_API(FormatString);
9280 JIM_REGISTER_API(CompareStringImmediate);
9281 JIM_REGISTER_API(NewReference);
9282 JIM_REGISTER_API(GetReference);
9283 JIM_REGISTER_API(SetFinalizer);
9284 JIM_REGISTER_API(GetFinalizer);
9285 JIM_REGISTER_API(CreateInterp);
9286 JIM_REGISTER_API(FreeInterp);
9287 JIM_REGISTER_API(GetExitCode);
9288 JIM_REGISTER_API(SetStdin);
9289 JIM_REGISTER_API(SetStdout);
9290 JIM_REGISTER_API(SetStderr);
9291 JIM_REGISTER_API(CreateCommand);
9292 JIM_REGISTER_API(CreateProcedure);
9293 JIM_REGISTER_API(DeleteCommand);
9294 JIM_REGISTER_API(RenameCommand);
9295 JIM_REGISTER_API(GetCommand);
9296 JIM_REGISTER_API(SetVariable);
9297 JIM_REGISTER_API(SetVariableStr);
9298 JIM_REGISTER_API(SetGlobalVariableStr);
9299 JIM_REGISTER_API(SetVariableStrWithStr);
9300 JIM_REGISTER_API(SetVariableLink);
9301 JIM_REGISTER_API(GetVariable);
9302 JIM_REGISTER_API(GetCallFrameByLevel);
9303 JIM_REGISTER_API(Collect);
9304 JIM_REGISTER_API(CollectIfNeeded);
9305 JIM_REGISTER_API(GetIndex);
9306 JIM_REGISTER_API(NewListObj);
9307 JIM_REGISTER_API(ListAppendElement);
9308 JIM_REGISTER_API(ListAppendList);
9309 JIM_REGISTER_API(ListLength);
9310 JIM_REGISTER_API(ListIndex);
9311 JIM_REGISTER_API(SetListIndex);
9312 JIM_REGISTER_API(ConcatObj);
9313 JIM_REGISTER_API(NewDictObj);
9314 JIM_REGISTER_API(DictKey);
9315 JIM_REGISTER_API(DictKeysVector);
9316 JIM_REGISTER_API(GetIndex);
9317 JIM_REGISTER_API(GetReturnCode);
9318 JIM_REGISTER_API(EvalExpression);
9319 JIM_REGISTER_API(GetBoolFromExpr);
9320 JIM_REGISTER_API(GetWide);
9321 JIM_REGISTER_API(GetLong);
9322 JIM_REGISTER_API(SetWide);
9323 JIM_REGISTER_API(NewIntObj);
9324 JIM_REGISTER_API(GetDouble);
9325 JIM_REGISTER_API(SetDouble);
9326 JIM_REGISTER_API(NewDoubleObj);
9327 JIM_REGISTER_API(WrongNumArgs);
9328 JIM_REGISTER_API(SetDictKeysVector);
9329 JIM_REGISTER_API(SubstObj);
9330 JIM_REGISTER_API(RegisterApi);
9331 JIM_REGISTER_API(PrintErrorMessage);
9332 JIM_REGISTER_API(InteractivePrompt);
9333 JIM_REGISTER_API(RegisterCoreCommands);
9334 JIM_REGISTER_API(GetSharedString);
9335 JIM_REGISTER_API(ReleaseSharedString);
9336 JIM_REGISTER_API(Panic);
9337 JIM_REGISTER_API(StrDup);
9338 JIM_REGISTER_API(UnsetVariable);
9339 JIM_REGISTER_API(GetVariableStr);
9340 JIM_REGISTER_API(GetGlobalVariable);
9341 JIM_REGISTER_API(GetGlobalVariableStr);
9342 JIM_REGISTER_API(GetAssocData);
9343 JIM_REGISTER_API(SetAssocData);
9344 JIM_REGISTER_API(DeleteAssocData);
9345 JIM_REGISTER_API(GetEnum);
9346 JIM_REGISTER_API(ScriptIsComplete);
9347 JIM_REGISTER_API(PackageRequire);
9348 JIM_REGISTER_API(PackageProvide);
9349 JIM_REGISTER_API(InitStack);
9350 JIM_REGISTER_API(FreeStack);
9351 JIM_REGISTER_API(StackLen);
9352 JIM_REGISTER_API(StackPush);
9353 JIM_REGISTER_API(StackPop);
9354 JIM_REGISTER_API(StackPeek);
9355 JIM_REGISTER_API(FreeStackElements);
9356 JIM_REGISTER_API(fprintf );
9357 JIM_REGISTER_API(vfprintf );
9358 JIM_REGISTER_API(fwrite );
9359 JIM_REGISTER_API(fread );
9360 JIM_REGISTER_API(fflush );
9361 JIM_REGISTER_API(fgets );
9362 JIM_REGISTER_API(GetNvp);
9363 JIM_REGISTER_API(Nvp_name2value);
9364 JIM_REGISTER_API(Nvp_name2value_simple);
9365 JIM_REGISTER_API(Nvp_name2value_obj);
9366 JIM_REGISTER_API(Nvp_name2value_nocase);
9367 JIM_REGISTER_API(Nvp_name2value_obj_nocase);
9368
9369 JIM_REGISTER_API(Nvp_value2name);
9370 JIM_REGISTER_API(Nvp_value2name_simple);
9371 JIM_REGISTER_API(Nvp_value2name_obj);
9372
9373 JIM_REGISTER_API(GetOpt_Setup);
9374 JIM_REGISTER_API(GetOpt_Debug);
9375 JIM_REGISTER_API(GetOpt_Obj);
9376 JIM_REGISTER_API(GetOpt_String);
9377 JIM_REGISTER_API(GetOpt_Double);
9378 JIM_REGISTER_API(GetOpt_Wide);
9379 JIM_REGISTER_API(GetOpt_Nvp);
9380 JIM_REGISTER_API(GetOpt_NvpUnknown);
9381 JIM_REGISTER_API(GetOpt_Enum);
9382
9383 JIM_REGISTER_API(Debug_ArgvString);
9384 JIM_REGISTER_API(SetResult_sprintf);
9385 JIM_REGISTER_API(SetResult_NvpUnknown);
9386
9387 }
9388
9389 /* -----------------------------------------------------------------------------
9390 * Core commands utility functions
9391 * ---------------------------------------------------------------------------*/
9392 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
9393 const char *msg)
9394 {
9395 int i;
9396 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9397
9398 Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9399 for (i = 0; i < argc; i++) {
9400 Jim_AppendObj(interp, objPtr, argv[i]);
9401 if (!(i+1 == argc && msg[0] == '\0'))
9402 Jim_AppendString(interp, objPtr, " ", 1);
9403 }
9404 Jim_AppendString(interp, objPtr, msg, -1);
9405 Jim_AppendString(interp, objPtr, "\"", 1);
9406 Jim_SetResult(interp, objPtr);
9407 }
9408
9409 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9410 {
9411 Jim_HashTableIterator *htiter;
9412 Jim_HashEntry *he;
9413 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9414 const char *pattern;
9415 int patternLen;
9416
9417 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9418 htiter = Jim_GetHashTableIterator(&interp->commands);
9419 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9420 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9421 strlen((const char*)he->key), 0))
9422 continue;
9423 Jim_ListAppendElement(interp, listObjPtr,
9424 Jim_NewStringObj(interp, he->key, -1));
9425 }
9426 Jim_FreeHashTableIterator(htiter);
9427 return listObjPtr;
9428 }
9429
9430 #define JIM_VARLIST_GLOBALS 0
9431 #define JIM_VARLIST_LOCALS 1
9432 #define JIM_VARLIST_VARS 2
9433
9434 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9435 int mode)
9436 {
9437 Jim_HashTableIterator *htiter;
9438 Jim_HashEntry *he;
9439 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9440 const char *pattern;
9441 int patternLen;
9442
9443 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9444 if (mode == JIM_VARLIST_GLOBALS) {
9445 htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9446 } else {
9447 /* For [info locals], if we are at top level an emtpy list
9448 * is returned. I don't agree, but we aim at compatibility (SS) */
9449 if (mode == JIM_VARLIST_LOCALS &&
9450 interp->framePtr == interp->topFramePtr)
9451 return listObjPtr;
9452 htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9453 }
9454 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9455 Jim_Var *varPtr = (Jim_Var*) he->val;
9456 if (mode == JIM_VARLIST_LOCALS) {
9457 if (varPtr->linkFramePtr != NULL)
9458 continue;
9459 }
9460 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9461 strlen((const char*)he->key), 0))
9462 continue;
9463 Jim_ListAppendElement(interp, listObjPtr,
9464 Jim_NewStringObj(interp, he->key, -1));
9465 }
9466 Jim_FreeHashTableIterator(htiter);
9467 return listObjPtr;
9468 }
9469
9470 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9471 Jim_Obj **objPtrPtr)
9472 {
9473 Jim_CallFrame *targetCallFrame;
9474
9475 if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9476 != JIM_OK)
9477 return JIM_ERR;
9478 /* No proc call at toplevel callframe */
9479 if (targetCallFrame == interp->topFramePtr) {
9480 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9481 Jim_AppendStrings(interp, Jim_GetResult(interp),
9482 "bad level \"",
9483 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9484 return JIM_ERR;
9485 }
9486 *objPtrPtr = Jim_NewListObj(interp,
9487 targetCallFrame->argv,
9488 targetCallFrame->argc);
9489 return JIM_OK;
9490 }
9491
9492 /* -----------------------------------------------------------------------------
9493 * Core commands
9494 * ---------------------------------------------------------------------------*/
9495
9496 /* fake [puts] -- not the real puts, just for debugging. */
9497 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9498 Jim_Obj *const *argv)
9499 {
9500 const char *str;
9501 int len, nonewline = 0;
9502
9503 if (argc != 2 && argc != 3) {
9504 Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9505 return JIM_ERR;
9506 }
9507 if (argc == 3) {
9508 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9509 {
9510 Jim_SetResultString(interp, "The second argument must "
9511 "be -nonewline", -1);
9512 return JIM_OK;
9513 } else {
9514 nonewline = 1;
9515 argv++;
9516 }
9517 }
9518 str = Jim_GetString(argv[1], &len);
9519 Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9520 if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9521 return JIM_OK;
9522 }
9523
9524 /* Helper for [+] and [*] */
9525 static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
9526 Jim_Obj *const *argv, int op)
9527 {
9528 jim_wide wideValue, res;
9529 double doubleValue, doubleRes;
9530 int i;
9531
9532 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9533
9534 for (i = 1; i < argc; i++) {
9535 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9536 goto trydouble;
9537 if (op == JIM_EXPROP_ADD)
9538 res += wideValue;
9539 else
9540 res *= wideValue;
9541 }
9542 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9543 return JIM_OK;
9544 trydouble:
9545 doubleRes = (double) res;
9546 for (;i < argc; i++) {
9547 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9548 return JIM_ERR;
9549 if (op == JIM_EXPROP_ADD)
9550 doubleRes += doubleValue;
9551 else
9552 doubleRes *= doubleValue;
9553 }
9554 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9555 return JIM_OK;
9556 }
9557
9558 /* Helper for [-] and [/] */
9559 static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9560 Jim_Obj *const *argv, int op)
9561 {
9562 jim_wide wideValue, res = 0;
9563 double doubleValue, doubleRes = 0;
9564 int i = 2;
9565
9566 if (argc < 2) {
9567 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9568 return JIM_ERR;
9569 } else if (argc == 2) {
9570 /* The arity = 2 case is different. For [- x] returns -x,
9571 * while [/ x] returns 1/x. */
9572 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9573 if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9574 JIM_OK)
9575 {
9576 return JIM_ERR;
9577 } else {
9578 if (op == JIM_EXPROP_SUB)
9579 doubleRes = -doubleValue;
9580 else
9581 doubleRes = 1.0/doubleValue;
9582 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9583 doubleRes));
9584 return JIM_OK;
9585 }
9586 }
9587 if (op == JIM_EXPROP_SUB) {
9588 res = -wideValue;
9589 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9590 } else {
9591 doubleRes = 1.0/wideValue;
9592 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9593 doubleRes));
9594 }
9595 return JIM_OK;
9596 } else {
9597 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9598 if (Jim_GetDouble(interp, argv[1], &doubleRes)
9599 != JIM_OK) {
9600 return JIM_ERR;
9601 } else {
9602 goto trydouble;
9603 }
9604 }
9605 }
9606 for (i = 2; i < argc; i++) {
9607 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9608 doubleRes = (double) res;
9609 goto trydouble;
9610 }
9611 if (op == JIM_EXPROP_SUB)
9612 res -= wideValue;
9613 else
9614 res /= wideValue;
9615 }
9616 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9617 return JIM_OK;
9618 trydouble:
9619 for (;i < argc; i++) {
9620 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9621 return JIM_ERR;
9622 if (op == JIM_EXPROP_SUB)
9623 doubleRes -= doubleValue;
9624 else
9625 doubleRes /= doubleValue;
9626 }
9627 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9628 return JIM_OK;
9629 }
9630
9631
9632 /* [+] */
9633 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9634 Jim_Obj *const *argv)
9635 {
9636 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9637 }
9638
9639 /* [*] */
9640 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9641 Jim_Obj *const *argv)
9642 {
9643 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9644 }
9645
9646 /* [-] */
9647 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9648 Jim_Obj *const *argv)
9649 {
9650 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9651 }
9652
9653 /* [/] */
9654 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9655 Jim_Obj *const *argv)
9656 {
9657 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9658 }
9659
9660 /* [set] */
9661 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9662 Jim_Obj *const *argv)
9663 {
9664 if (argc != 2 && argc != 3) {
9665 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9666 return JIM_ERR;
9667 }
9668 if (argc == 2) {
9669 Jim_Obj *objPtr;
9670 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9671 if (!objPtr)
9672 return JIM_ERR;
9673 Jim_SetResult(interp, objPtr);
9674 return JIM_OK;
9675 }
9676 /* argc == 3 case. */
9677 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9678 return JIM_ERR;
9679 Jim_SetResult(interp, argv[2]);
9680 return JIM_OK;
9681 }
9682
9683 /* [unset] */
9684 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9685 Jim_Obj *const *argv)
9686 {
9687 int i;
9688
9689 if (argc < 2) {
9690 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9691 return JIM_ERR;
9692 }
9693 for (i = 1; i < argc; i++) {
9694 if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9695 return JIM_ERR;
9696 }
9697 return JIM_OK;
9698 }
9699
9700 /* [incr] */
9701 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9702 Jim_Obj *const *argv)
9703 {
9704 jim_wide wideValue, increment = 1;
9705 Jim_Obj *intObjPtr;
9706
9707 if (argc != 2 && argc != 3) {
9708 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9709 return JIM_ERR;
9710 }
9711 if (argc == 3) {
9712 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9713 return JIM_ERR;
9714 }
9715 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9716 if (!intObjPtr) return JIM_ERR;
9717 if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9718 return JIM_ERR;
9719 if (Jim_IsShared(intObjPtr)) {
9720 intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9721 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9722 Jim_FreeNewObj(interp, intObjPtr);
9723 return JIM_ERR;
9724 }
9725 } else {
9726 Jim_SetWide(interp, intObjPtr, wideValue+increment);
9727 /* The following step is required in order to invalidate the
9728 * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9729 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9730 return JIM_ERR;
9731 }
9732 }
9733 Jim_SetResult(interp, intObjPtr);
9734 return JIM_OK;
9735 }
9736
9737 /* [while] */
9738 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9739 Jim_Obj *const *argv)
9740 {
9741 if (argc != 3) {
9742 Jim_WrongNumArgs(interp, 1, argv, "condition body");
9743 return JIM_ERR;
9744 }
9745 /* Try to run a specialized version of while if the expression
9746 * is in one of the following forms:
9747 *
9748 * $a < CONST, $a < $b
9749 * $a <= CONST, $a <= $b
9750 * $a > CONST, $a > $b
9751 * $a >= CONST, $a >= $b
9752 * $a != CONST, $a != $b
9753 * $a == CONST, $a == $b
9754 * $a
9755 * !$a
9756 * CONST
9757 */
9758
9759 #ifdef JIM_OPTIMIZATION
9760 {
9761 ExprByteCode *expr;
9762 Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9763 int exprLen, retval;
9764
9765 /* STEP 1 -- Check if there are the conditions to run the specialized
9766 * version of while */
9767
9768 if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9769 if (expr->len <= 0 || expr->len > 3) goto noopt;
9770 switch(expr->len) {
9771 case 1:
9772 if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9773 expr->opcode[0] != JIM_EXPROP_NUMBER)
9774 goto noopt;
9775 break;
9776 case 2:
9777 if (expr->opcode[1] != JIM_EXPROP_NOT ||
9778 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9779 goto noopt;
9780 break;
9781 case 3:
9782 if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9783 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9784 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9785 goto noopt;
9786 switch(expr->opcode[2]) {
9787 case JIM_EXPROP_LT:
9788 case JIM_EXPROP_LTE:
9789 case JIM_EXPROP_GT:
9790 case JIM_EXPROP_GTE:
9791 case JIM_EXPROP_NUMEQ:
9792 case JIM_EXPROP_NUMNE:
9793 /* nothing to do */
9794 break;
9795 default:
9796 goto noopt;
9797 }
9798 break;
9799 default:
9800 Jim_Panic(interp,
9801 "Unexpected default reached in Jim_WhileCoreCommand()");
9802 break;
9803 }
9804
9805 /* STEP 2 -- conditions meet. Initialization. Take different
9806 * branches for different expression lengths. */
9807 exprLen = expr->len;
9808
9809 if (exprLen == 1) {
9810 jim_wide wideValue;
9811
9812 if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9813 varAObjPtr = expr->obj[0];
9814 Jim_IncrRefCount(varAObjPtr);
9815 } else {
9816 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9817 goto noopt;
9818 }
9819 while (1) {
9820 if (varAObjPtr) {
9821 if (!(objPtr =
9822 Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9823 Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9824 {
9825 Jim_DecrRefCount(interp, varAObjPtr);
9826 goto noopt;
9827 }
9828 }
9829 if (!wideValue) break;
9830 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9831 switch(retval) {
9832 case JIM_BREAK:
9833 if (varAObjPtr)
9834 Jim_DecrRefCount(interp, varAObjPtr);
9835 goto out;
9836 break;
9837 case JIM_CONTINUE:
9838 continue;
9839 break;
9840 default:
9841 if (varAObjPtr)
9842 Jim_DecrRefCount(interp, varAObjPtr);
9843 return retval;
9844 }
9845 }
9846 }
9847 if (varAObjPtr)
9848 Jim_DecrRefCount(interp, varAObjPtr);
9849 } else if (exprLen == 3) {
9850 jim_wide wideValueA, wideValueB, cmpRes = 0;
9851 int cmpType = expr->opcode[2];
9852
9853 varAObjPtr = expr->obj[0];
9854 Jim_IncrRefCount(varAObjPtr);
9855 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9856 varBObjPtr = expr->obj[1];
9857 Jim_IncrRefCount(varBObjPtr);
9858 } else {
9859 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9860 goto noopt;
9861 }
9862 while (1) {
9863 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9864 Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9865 {
9866 Jim_DecrRefCount(interp, varAObjPtr);
9867 if (varBObjPtr)
9868 Jim_DecrRefCount(interp, varBObjPtr);
9869 goto noopt;
9870 }
9871 if (varBObjPtr) {
9872 if (!(objPtr =
9873 Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9874 Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9875 {
9876 Jim_DecrRefCount(interp, varAObjPtr);
9877 if (varBObjPtr)
9878 Jim_DecrRefCount(interp, varBObjPtr);
9879 goto noopt;
9880 }
9881 }
9882 switch(cmpType) {
9883 case JIM_EXPROP_LT:
9884 cmpRes = wideValueA < wideValueB; break;
9885 case JIM_EXPROP_LTE:
9886 cmpRes = wideValueA <= wideValueB; break;
9887 case JIM_EXPROP_GT:
9888 cmpRes = wideValueA > wideValueB; break;
9889 case JIM_EXPROP_GTE:
9890 cmpRes = wideValueA >= wideValueB; break;
9891 case JIM_EXPROP_NUMEQ:
9892 cmpRes = wideValueA == wideValueB; break;
9893 case JIM_EXPROP_NUMNE:
9894 cmpRes = wideValueA != wideValueB; break;
9895 }
9896 if (!cmpRes) break;
9897 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9898 switch(retval) {
9899 case JIM_BREAK:
9900 Jim_DecrRefCount(interp, varAObjPtr);
9901 if (varBObjPtr)
9902 Jim_DecrRefCount(interp, varBObjPtr);
9903 goto out;
9904 break;
9905 case JIM_CONTINUE:
9906 continue;
9907 break;
9908 default:
9909 Jim_DecrRefCount(interp, varAObjPtr);
9910 if (varBObjPtr)
9911 Jim_DecrRefCount(interp, varBObjPtr);
9912 return retval;
9913 }
9914 }
9915 }
9916 Jim_DecrRefCount(interp, varAObjPtr);
9917 if (varBObjPtr)
9918 Jim_DecrRefCount(interp, varBObjPtr);
9919 } else {
9920 /* TODO: case for len == 2 */
9921 goto noopt;
9922 }
9923 Jim_SetEmptyResult(interp);
9924 return JIM_OK;
9925 }
9926 noopt:
9927 #endif
9928
9929 /* The general purpose implementation of while starts here */
9930 while (1) {
9931 int boolean, retval;
9932
9933 if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9934 &boolean)) != JIM_OK)
9935 return retval;
9936 if (!boolean) break;
9937 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9938 switch(retval) {
9939 case JIM_BREAK:
9940 goto out;
9941 break;
9942 case JIM_CONTINUE:
9943 continue;
9944 break;
9945 default:
9946 return retval;
9947 }
9948 }
9949 }
9950 out:
9951 Jim_SetEmptyResult(interp);
9952 return JIM_OK;
9953 }
9954
9955 /* [for] */
9956 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9957 Jim_Obj *const *argv)
9958 {
9959 int retval;
9960
9961 if (argc != 5) {
9962 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9963 return JIM_ERR;
9964 }
9965 /* Check if the for is on the form:
9966 * for {set i CONST} {$i < CONST} {incr i}
9967 * for {set i CONST} {$i < $j} {incr i}
9968 * for {set i CONST} {$i <= CONST} {incr i}
9969 * for {set i CONST} {$i <= $j} {incr i}
9970 * XXX: NOTE: if variable traces are implemented, this optimization
9971 * need to be modified to check for the proc epoch at every variable
9972 * update. */
9973 #ifdef JIM_OPTIMIZATION
9974 {
9975 ScriptObj *initScript, *incrScript;
9976 ExprByteCode *expr;
9977 jim_wide start, stop, currentVal;
9978 unsigned jim_wide procEpoch = interp->procEpoch;
9979 Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9980 int cmpType;
9981 struct Jim_Cmd *cmdPtr;
9982
9983 /* Do it only if there aren't shared arguments */
9984 if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9985 goto evalstart;
9986 initScript = Jim_GetScript(interp, argv[1]);
9987 expr = Jim_GetExpression(interp, argv[2]);
9988 incrScript = Jim_GetScript(interp, argv[3]);
9989
9990 /* Ensure proper lengths to start */
9991 if (initScript->len != 6) goto evalstart;
9992 if (incrScript->len != 4) goto evalstart;
9993 if (expr->len != 3) goto evalstart;
9994 /* Ensure proper token types. */
9995 if (initScript->token[2].type != JIM_TT_ESC ||
9996 initScript->token[4].type != JIM_TT_ESC ||
9997 incrScript->token[2].type != JIM_TT_ESC ||
9998 expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9999 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
10000 expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
10001 (expr->opcode[2] != JIM_EXPROP_LT &&
10002 expr->opcode[2] != JIM_EXPROP_LTE))
10003 goto evalstart;
10004 cmpType = expr->opcode[2];
10005 /* Initialization command must be [set] */
10006 cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
10007 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
10008 goto evalstart;
10009 /* Update command must be incr */
10010 cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
10011 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
10012 goto evalstart;
10013 /* set, incr, expression must be about the same variable */
10014 if (!Jim_StringEqObj(initScript->token[2].objPtr,
10015 incrScript->token[2].objPtr, 0))
10016 goto evalstart;
10017 if (!Jim_StringEqObj(initScript->token[2].objPtr,
10018 expr->obj[0], 0))
10019 goto evalstart;
10020 /* Check that the initialization and comparison are valid integers */
10021 if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
10022 goto evalstart;
10023 if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
10024 Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
10025 {
10026 goto evalstart;
10027 }
10028
10029 /* Initialization */
10030 varNamePtr = expr->obj[0];
10031 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
10032 stopVarNamePtr = expr->obj[1];
10033 Jim_IncrRefCount(stopVarNamePtr);
10034 }
10035 Jim_IncrRefCount(varNamePtr);
10036
10037 /* --- OPTIMIZED FOR --- */
10038 /* Start to loop */
10039 objPtr = Jim_NewIntObj(interp, start);
10040 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
10041 Jim_DecrRefCount(interp, varNamePtr);
10042 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10043 Jim_FreeNewObj(interp, objPtr);
10044 goto evalstart;
10045 }
10046 while (1) {
10047 /* === Check condition === */
10048 /* Common code: */
10049 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
10050 if (objPtr == NULL ||
10051 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
10052 {
10053 Jim_DecrRefCount(interp, varNamePtr);
10054 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10055 goto testcond;
10056 }
10057 /* Immediate or Variable? get the 'stop' value if the latter. */
10058 if (stopVarNamePtr) {
10059 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
10060 if (objPtr == NULL ||
10061 Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
10062 {
10063 Jim_DecrRefCount(interp, varNamePtr);
10064 Jim_DecrRefCount(interp, stopVarNamePtr);
10065 goto testcond;
10066 }
10067 }
10068 if (cmpType == JIM_EXPROP_LT) {
10069 if (currentVal >= stop) break;
10070 } else {
10071 if (currentVal > stop) break;
10072 }
10073 /* Eval body */
10074 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10075 switch(retval) {
10076 case JIM_BREAK:
10077 if (stopVarNamePtr)
10078 Jim_DecrRefCount(interp, stopVarNamePtr);
10079 Jim_DecrRefCount(interp, varNamePtr);
10080 goto out;
10081 case JIM_CONTINUE:
10082 /* nothing to do */
10083 break;
10084 default:
10085 if (stopVarNamePtr)
10086 Jim_DecrRefCount(interp, stopVarNamePtr);
10087 Jim_DecrRefCount(interp, varNamePtr);
10088 return retval;
10089 }
10090 }
10091 /* If there was a change in procedures/command continue
10092 * with the usual [for] command implementation */
10093 if (procEpoch != interp->procEpoch) {
10094 if (stopVarNamePtr)
10095 Jim_DecrRefCount(interp, stopVarNamePtr);
10096 Jim_DecrRefCount(interp, varNamePtr);
10097 goto evalnext;
10098 }
10099 /* Increment */
10100 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
10101 if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
10102 objPtr->internalRep.wideValue ++;
10103 Jim_InvalidateStringRep(objPtr);
10104 } else {
10105 Jim_Obj *auxObjPtr;
10106
10107 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
10108 if (stopVarNamePtr)
10109 Jim_DecrRefCount(interp, stopVarNamePtr);
10110 Jim_DecrRefCount(interp, varNamePtr);
10111 goto evalnext;
10112 }
10113 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
10114 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
10115 if (stopVarNamePtr)
10116 Jim_DecrRefCount(interp, stopVarNamePtr);
10117 Jim_DecrRefCount(interp, varNamePtr);
10118 Jim_FreeNewObj(interp, auxObjPtr);
10119 goto evalnext;
10120 }
10121 }
10122 }
10123 if (stopVarNamePtr)
10124 Jim_DecrRefCount(interp, stopVarNamePtr);
10125 Jim_DecrRefCount(interp, varNamePtr);
10126 Jim_SetEmptyResult(interp);
10127 return JIM_OK;
10128 }
10129 #endif
10130 evalstart:
10131 /* Eval start */
10132 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10133 return retval;
10134 while (1) {
10135 int boolean;
10136 testcond:
10137 /* Test the condition */
10138 if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
10139 != JIM_OK)
10140 return retval;
10141 if (!boolean) break;
10142 /* Eval body */
10143 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10144 switch(retval) {
10145 case JIM_BREAK:
10146 goto out;
10147 break;
10148 case JIM_CONTINUE:
10149 /* Nothing to do */
10150 break;
10151 default:
10152 return retval;
10153 }
10154 }
10155 evalnext:
10156 /* Eval next */
10157 if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
10158 switch(retval) {
10159 case JIM_BREAK:
10160 goto out;
10161 break;
10162 case JIM_CONTINUE:
10163 continue;
10164 break;
10165 default:
10166 return retval;
10167 }
10168 }
10169 }
10170 out:
10171 Jim_SetEmptyResult(interp);
10172 return JIM_OK;
10173 }
10174
10175 /* foreach + lmap implementation. */
10176 static int JimForeachMapHelper(Jim_Interp *interp, int argc,
10177 Jim_Obj *const *argv, int doMap)
10178 {
10179 int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
10180 int nbrOfLoops = 0;
10181 Jim_Obj *emptyStr, *script, *mapRes = NULL;
10182
10183 if (argc < 4 || argc % 2 != 0) {
10184 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
10185 return JIM_ERR;
10186 }
10187 if (doMap) {
10188 mapRes = Jim_NewListObj(interp, NULL, 0);
10189 Jim_IncrRefCount(mapRes);
10190 }
10191 emptyStr = Jim_NewEmptyStringObj(interp);
10192 Jim_IncrRefCount(emptyStr);
10193 script = argv[argc-1]; /* Last argument is a script */
10194 nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
10195 listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
10196 listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
10197 /* Initialize iterators and remember max nbr elements each list */
10198 memset(listsIdx, 0, nbrOfLists * sizeof(int));
10199 /* Remember lengths of all lists and calculate how much rounds to loop */
10200 for (i=0; i < nbrOfLists*2; i += 2) {
10201 div_t cnt;
10202 int count;
10203 Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
10204 Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
10205 if (listsEnd[i] == 0) {
10206 Jim_SetResultString(interp, "foreach varlist is empty", -1);
10207 goto err;
10208 }
10209 cnt = div(listsEnd[i+1], listsEnd[i]);
10210 count = cnt.quot + (cnt.rem ? 1 : 0);
10211 if (count > nbrOfLoops)
10212 nbrOfLoops = count;
10213 }
10214 for (; nbrOfLoops-- > 0; ) {
10215 for (i=0; i < nbrOfLists; ++i) {
10216 int varIdx = 0, var = i * 2;
10217 while (varIdx < listsEnd[var]) {
10218 Jim_Obj *varName, *ele;
10219 int lst = i * 2 + 1;
10220 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
10221 != JIM_OK)
10222 goto err;
10223 if (listsIdx[i] < listsEnd[lst]) {
10224 if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
10225 != JIM_OK)
10226 goto err;
10227 if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
10228 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10229 goto err;
10230 }
10231 ++listsIdx[i]; /* Remember next iterator of current list */
10232 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
10233 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10234 goto err;
10235 }
10236 ++varIdx; /* Next variable */
10237 }
10238 }
10239 switch (result = Jim_EvalObj(interp, script)) {
10240 case JIM_OK:
10241 if (doMap)
10242 Jim_ListAppendElement(interp, mapRes, interp->result);
10243 break;
10244 case JIM_CONTINUE:
10245 break;
10246 case JIM_BREAK:
10247 goto out;
10248 break;
10249 default:
10250 goto err;
10251 }
10252 }
10253 out:
10254 result = JIM_OK;
10255 if (doMap)
10256 Jim_SetResult(interp, mapRes);
10257 else
10258 Jim_SetEmptyResult(interp);
10259 err:
10260 if (doMap)
10261 Jim_DecrRefCount(interp, mapRes);
10262 Jim_DecrRefCount(interp, emptyStr);
10263 Jim_Free(listsIdx);
10264 Jim_Free(listsEnd);
10265 return result;
10266 }
10267
10268 /* [foreach] */
10269 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
10270 Jim_Obj *const *argv)
10271 {
10272 return JimForeachMapHelper(interp, argc, argv, 0);
10273 }
10274
10275 /* [lmap] */
10276 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
10277 Jim_Obj *const *argv)
10278 {
10279 return JimForeachMapHelper(interp, argc, argv, 1);
10280 }
10281
10282 /* [if] */
10283 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
10284 Jim_Obj *const *argv)
10285 {
10286 int boolean, retval, current = 1, falsebody = 0;
10287 if (argc >= 3) {
10288 while (1) {
10289 /* Far not enough arguments given! */
10290 if (current >= argc) goto err;
10291 if ((retval = Jim_GetBoolFromExpr(interp,
10292 argv[current++], &boolean))
10293 != JIM_OK)
10294 return retval;
10295 /* There lacks something, isn't it? */
10296 if (current >= argc) goto err;
10297 if (Jim_CompareStringImmediate(interp, argv[current],
10298 "then")) current++;
10299 /* Tsk tsk, no then-clause? */
10300 if (current >= argc) goto err;
10301 if (boolean)
10302 return Jim_EvalObj(interp, argv[current]);
10303 /* Ok: no else-clause follows */
10304 if (++current >= argc) {
10305 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10306 return JIM_OK;
10307 }
10308 falsebody = current++;
10309 if (Jim_CompareStringImmediate(interp, argv[falsebody],
10310 "else")) {
10311 /* IIICKS - else-clause isn't last cmd? */
10312 if (current != argc-1) goto err;
10313 return Jim_EvalObj(interp, argv[current]);
10314 } else if (Jim_CompareStringImmediate(interp,
10315 argv[falsebody], "elseif"))
10316 /* Ok: elseif follows meaning all the stuff
10317 * again (how boring...) */
10318 continue;
10319 /* OOPS - else-clause is not last cmd?*/
10320 else if (falsebody != argc-1)
10321 goto err;
10322 return Jim_EvalObj(interp, argv[falsebody]);
10323 }
10324 return JIM_OK;
10325 }
10326 err:
10327 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10328 return JIM_ERR;
10329 }
10330
10331 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10332
10333 /* [switch] */
10334 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
10335 Jim_Obj *const *argv)
10336 {
10337 int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10338 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10339 Jim_Obj *script = 0;
10340 if (argc < 3) goto wrongnumargs;
10341 for (opt=1; opt < argc; ++opt) {
10342 const char *option = Jim_GetString(argv[opt], 0);
10343 if (*option != '-') break;
10344 else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10345 else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10346 else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10347 else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10348 else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10349 if ((argc - opt) < 2) goto wrongnumargs;
10350 command = argv[++opt];
10351 } else {
10352 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10353 Jim_AppendStrings(interp, Jim_GetResult(interp),
10354 "bad option \"", option, "\": must be -exact, -glob, "
10355 "-regexp, -command procname or --", 0);
10356 goto err;
10357 }
10358 if ((argc - opt) < 2) goto wrongnumargs;
10359 }
10360 strObj = argv[opt++];
10361 patCount = argc - opt;
10362 if (patCount == 1) {
10363 Jim_Obj **vector;
10364 JimListGetElements(interp, argv[opt], &patCount, &vector);
10365 caseList = vector;
10366 } else
10367 caseList = &argv[opt];
10368 if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10369 for (i=0; script == 0 && i < patCount; i += 2) {
10370 Jim_Obj *patObj = caseList[i];
10371 if (!Jim_CompareStringImmediate(interp, patObj, "default")
10372 || i < (patCount-2)) {
10373 switch (matchOpt) {
10374 case SWITCH_EXACT:
10375 if (Jim_StringEqObj(strObj, patObj, 0))
10376 script = caseList[i+1];
10377 break;
10378 case SWITCH_GLOB:
10379 if (Jim_StringMatchObj(patObj, strObj, 0))
10380 script = caseList[i+1];
10381 break;
10382 case SWITCH_RE:
10383 command = Jim_NewStringObj(interp, "regexp", -1);
10384 /* Fall thru intentionally */
10385 case SWITCH_CMD: {
10386 Jim_Obj *parms[] = {command, patObj, strObj};
10387 int rc = Jim_EvalObjVector(interp, 3, parms);
10388 long matching;
10389 /* After the execution of a command we need to
10390 * make sure to reconvert the object into a list
10391 * again. Only for the single-list style [switch]. */
10392 if (argc-opt == 1) {
10393 Jim_Obj **vector;
10394 JimListGetElements(interp, argv[opt], &patCount,
10395 &vector);
10396 caseList = vector;
10397 }
10398 /* command is here already decref'd */
10399 if (rc != JIM_OK) {
10400 retcode = rc;
10401 goto err;
10402 }
10403 rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10404 if (rc != JIM_OK) {
10405 retcode = rc;
10406 goto err;
10407 }
10408 if (matching)
10409 script = caseList[i+1];
10410 break;
10411 }
10412 default:
10413 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10414 Jim_AppendStrings(interp, Jim_GetResult(interp),
10415 "internal error: no such option implemented", 0);
10416 goto err;
10417 }
10418 } else {
10419 script = caseList[i+1];
10420 }
10421 }
10422 for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10423 i += 2)
10424 script = caseList[i+1];
10425 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10426 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10427 Jim_AppendStrings(interp, Jim_GetResult(interp),
10428 "no body specified for pattern \"",
10429 Jim_GetString(caseList[i-2], 0), "\"", 0);
10430 goto err;
10431 }
10432 retcode = JIM_OK;
10433 Jim_SetEmptyResult(interp);
10434 if (script != 0)
10435 retcode = Jim_EvalObj(interp, script);
10436 return retcode;
10437 wrongnumargs:
10438 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10439 "pattern body ... ?default body? or "
10440 "{pattern body ?pattern body ...?}");
10441 err:
10442 return retcode;
10443 }
10444
10445 /* [list] */
10446 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
10447 Jim_Obj *const *argv)
10448 {
10449 Jim_Obj *listObjPtr;
10450
10451 listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10452 Jim_SetResult(interp, listObjPtr);
10453 return JIM_OK;
10454 }
10455
10456 /* [lindex] */
10457 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
10458 Jim_Obj *const *argv)
10459 {
10460 Jim_Obj *objPtr, *listObjPtr;
10461 int i;
10462 int index;
10463
10464 if (argc < 3) {
10465 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10466 return JIM_ERR;
10467 }
10468 objPtr = argv[1];
10469 Jim_IncrRefCount(objPtr);
10470 for (i = 2; i < argc; i++) {
10471 listObjPtr = objPtr;
10472 if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10473 Jim_DecrRefCount(interp, listObjPtr);
10474 return JIM_ERR;
10475 }
10476 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10477 JIM_NONE) != JIM_OK) {
10478 /* Returns an empty object if the index
10479 * is out of range. */
10480 Jim_DecrRefCount(interp, listObjPtr);
10481 Jim_SetEmptyResult(interp);
10482 return JIM_OK;
10483 }
10484 Jim_IncrRefCount(objPtr);
10485 Jim_DecrRefCount(interp, listObjPtr);
10486 }
10487 Jim_SetResult(interp, objPtr);
10488 Jim_DecrRefCount(interp, objPtr);
10489 return JIM_OK;
10490 }
10491
10492 /* [llength] */
10493 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
10494 Jim_Obj *const *argv)
10495 {
10496 int len;
10497
10498 if (argc != 2) {
10499 Jim_WrongNumArgs(interp, 1, argv, "list");
10500 return JIM_ERR;
10501 }
10502 Jim_ListLength(interp, argv[1], &len);
10503 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10504 return JIM_OK;
10505 }
10506
10507 /* [lappend] */
10508 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
10509 Jim_Obj *const *argv)
10510 {
10511 Jim_Obj *listObjPtr;
10512 int shared, i;
10513
10514 if (argc < 2) {
10515 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10516 return JIM_ERR;
10517 }
10518 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10519 if (!listObjPtr) {
10520 /* Create the list if it does not exists */
10521 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10522 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10523 Jim_FreeNewObj(interp, listObjPtr);
10524 return JIM_ERR;
10525 }
10526 }
10527 shared = Jim_IsShared(listObjPtr);
10528 if (shared)
10529 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10530 for (i = 2; i < argc; i++)
10531 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10532 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10533 if (shared)
10534 Jim_FreeNewObj(interp, listObjPtr);
10535 return JIM_ERR;
10536 }
10537 Jim_SetResult(interp, listObjPtr);
10538 return JIM_OK;
10539 }
10540
10541 /* [linsert] */
10542 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10543 Jim_Obj *const *argv)
10544 {
10545 int index, len;
10546 Jim_Obj *listPtr;
10547
10548 if (argc < 4) {
10549 Jim_WrongNumArgs(interp, 1, argv, "list index element "
10550 "?element ...?");
10551 return JIM_ERR;
10552 }
10553 listPtr = argv[1];
10554 if (Jim_IsShared(listPtr))
10555 listPtr = Jim_DuplicateObj(interp, listPtr);
10556 if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10557 goto err;
10558 Jim_ListLength(interp, listPtr, &len);
10559 if (index >= len)
10560 index = len;
10561 else if (index < 0)
10562 index = len + index + 1;
10563 Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10564 Jim_SetResult(interp, listPtr);
10565 return JIM_OK;
10566 err:
10567 if (listPtr != argv[1]) {
10568 Jim_FreeNewObj(interp, listPtr);
10569 }
10570 return JIM_ERR;
10571 }
10572
10573 /* [lset] */
10574 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10575 Jim_Obj *const *argv)
10576 {
10577 if (argc < 3) {
10578 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10579 return JIM_ERR;
10580 } else if (argc == 3) {
10581 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10582 return JIM_ERR;
10583 Jim_SetResult(interp, argv[2]);
10584 return JIM_OK;
10585 }
10586 if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10587 == JIM_ERR) return JIM_ERR;
10588 return JIM_OK;
10589 }
10590
10591 /* [lsort] */
10592 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10593 {
10594 const char *options[] = {
10595 "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10596 };
10597 enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10598 Jim_Obj *resObj;
10599 int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10600 int decreasing = 0;
10601
10602 if (argc < 2) {
10603 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10604 return JIM_ERR;
10605 }
10606 for (i = 1; i < (argc-1); i++) {
10607 int option;
10608
10609 if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10610 != JIM_OK)
10611 return JIM_ERR;
10612 switch(option) {
10613 case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10614 case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10615 case OPT_INCREASING: decreasing = 0; break;
10616 case OPT_DECREASING: decreasing = 1; break;
10617 }
10618 }
10619 if (decreasing) {
10620 switch(lsortType) {
10621 case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10622 case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10623 }
10624 }
10625 resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10626 ListSortElements(interp, resObj, lsortType);
10627 Jim_SetResult(interp, resObj);
10628 return JIM_OK;
10629 }
10630
10631 /* [append] */
10632 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10633 Jim_Obj *const *argv)
10634 {
10635 Jim_Obj *stringObjPtr;
10636 int shared, i;
10637
10638 if (argc < 2) {
10639 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10640 return JIM_ERR;
10641 }
10642 if (argc == 2) {
10643 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10644 if (!stringObjPtr) return JIM_ERR;
10645 } else {
10646 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10647 if (!stringObjPtr) {
10648 /* Create the string if it does not exists */
10649 stringObjPtr = Jim_NewEmptyStringObj(interp);
10650 if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10651 != JIM_OK) {
10652 Jim_FreeNewObj(interp, stringObjPtr);
10653 return JIM_ERR;
10654 }
10655 }
10656 }
10657 shared = Jim_IsShared(stringObjPtr);
10658 if (shared)
10659 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10660 for (i = 2; i < argc; i++)
10661 Jim_AppendObj(interp, stringObjPtr, argv[i]);
10662 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10663 if (shared)
10664 Jim_FreeNewObj(interp, stringObjPtr);
10665 return JIM_ERR;
10666 }
10667 Jim_SetResult(interp, stringObjPtr);
10668 return JIM_OK;
10669 }
10670
10671 /* [debug] */
10672 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10673 Jim_Obj *const *argv)
10674 {
10675 const char *options[] = {
10676 "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10677 "exprbc",
10678 NULL
10679 };
10680 enum {
10681 OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10682 OPT_EXPRLEN, OPT_EXPRBC
10683 };
10684 int option;
10685
10686 if (argc < 2) {
10687 Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10688 return JIM_ERR;
10689 }
10690 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10691 JIM_ERRMSG) != JIM_OK)
10692 return JIM_ERR;
10693 if (option == OPT_REFCOUNT) {
10694 if (argc != 3) {
10695 Jim_WrongNumArgs(interp, 2, argv, "object");
10696 return JIM_ERR;
10697 }
10698 Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10699 return JIM_OK;
10700 } else if (option == OPT_OBJCOUNT) {
10701 int freeobj = 0, liveobj = 0;
10702 char buf[256];
10703 Jim_Obj *objPtr;
10704
10705 if (argc != 2) {
10706 Jim_WrongNumArgs(interp, 2, argv, "");
10707 return JIM_ERR;
10708 }
10709 /* Count the number of free objects. */
10710 objPtr = interp->freeList;
10711 while (objPtr) {
10712 freeobj++;
10713 objPtr = objPtr->nextObjPtr;
10714 }
10715 /* Count the number of live objects. */
10716 objPtr = interp->liveList;
10717 while (objPtr) {
10718 liveobj++;
10719 objPtr = objPtr->nextObjPtr;
10720 }
10721 /* Set the result string and return. */
10722 sprintf(buf, "free %d used %d", freeobj, liveobj);
10723 Jim_SetResultString(interp, buf, -1);
10724 return JIM_OK;
10725 } else if (option == OPT_OBJECTS) {
10726 Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10727 /* Count the number of live objects. */
10728 objPtr = interp->liveList;
10729 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10730 while (objPtr) {
10731 char buf[128];
10732 const char *type = objPtr->typePtr ?
10733 objPtr->typePtr->name : "";
10734 subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10735 sprintf(buf, "%p", objPtr);
10736 Jim_ListAppendElement(interp, subListObjPtr,
10737 Jim_NewStringObj(interp, buf, -1));
10738 Jim_ListAppendElement(interp, subListObjPtr,
10739 Jim_NewStringObj(interp, type, -1));
10740 Jim_ListAppendElement(interp, subListObjPtr,
10741 Jim_NewIntObj(interp, objPtr->refCount));
10742 Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10743 Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10744 objPtr = objPtr->nextObjPtr;
10745 }
10746 Jim_SetResult(interp, listObjPtr);
10747 return JIM_OK;
10748 } else if (option == OPT_INVSTR) {
10749 Jim_Obj *objPtr;
10750
10751 if (argc != 3) {
10752 Jim_WrongNumArgs(interp, 2, argv, "object");
10753 return JIM_ERR;
10754 }
10755 objPtr = argv[2];
10756 if (objPtr->typePtr != NULL)
10757 Jim_InvalidateStringRep(objPtr);
10758 Jim_SetEmptyResult(interp);
10759 return JIM_OK;
10760 } else if (option == OPT_SCRIPTLEN) {
10761 ScriptObj *script;
10762 if (argc != 3) {
10763 Jim_WrongNumArgs(interp, 2, argv, "script");
10764 return JIM_ERR;
10765 }
10766 script = Jim_GetScript(interp, argv[2]);
10767 Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10768 return JIM_OK;
10769 } else if (option == OPT_EXPRLEN) {
10770 ExprByteCode *expr;
10771 if (argc != 3) {
10772 Jim_WrongNumArgs(interp, 2, argv, "expression");
10773 return JIM_ERR;
10774 }
10775 expr = Jim_GetExpression(interp, argv[2]);
10776 if (expr == NULL)
10777 return JIM_ERR;
10778 Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10779 return JIM_OK;
10780 } else if (option == OPT_EXPRBC) {
10781 Jim_Obj *objPtr;
10782 ExprByteCode *expr;
10783 int i;
10784
10785 if (argc != 3) {
10786 Jim_WrongNumArgs(interp, 2, argv, "expression");
10787 return JIM_ERR;
10788 }
10789 expr = Jim_GetExpression(interp, argv[2]);
10790 if (expr == NULL)
10791 return JIM_ERR;
10792 objPtr = Jim_NewListObj(interp, NULL, 0);
10793 for (i = 0; i < expr->len; i++) {
10794 const char *type;
10795 Jim_ExprOperator *op;
10796
10797 switch(expr->opcode[i]) {
10798 case JIM_EXPROP_NUMBER: type = "number"; break;
10799 case JIM_EXPROP_COMMAND: type = "command"; break;
10800 case JIM_EXPROP_VARIABLE: type = "variable"; break;
10801 case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10802 case JIM_EXPROP_SUBST: type = "subst"; break;
10803 case JIM_EXPROP_STRING: type = "string"; break;
10804 default:
10805 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10806 if (op == NULL) {
10807 type = "private";
10808 } else {
10809 type = "operator";
10810 }
10811 break;
10812 }
10813 Jim_ListAppendElement(interp, objPtr,
10814 Jim_NewStringObj(interp, type, -1));
10815 Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10816 }
10817 Jim_SetResult(interp, objPtr);
10818 return JIM_OK;
10819 } else {
10820 Jim_SetResultString(interp,
10821 "bad option. Valid options are refcount, "
10822 "objcount, objects, invstr", -1);
10823 return JIM_ERR;
10824 }
10825 return JIM_OK; /* unreached */
10826 }
10827
10828 /* [eval] */
10829 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10830 Jim_Obj *const *argv)
10831 {
10832 if (argc == 2) {
10833 return Jim_EvalObj(interp, argv[1]);
10834 } else if (argc > 2) {
10835 Jim_Obj *objPtr;
10836 int retcode;
10837
10838 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10839 Jim_IncrRefCount(objPtr);
10840 retcode = Jim_EvalObj(interp, objPtr);
10841 Jim_DecrRefCount(interp, objPtr);
10842 return retcode;
10843 } else {
10844 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10845 return JIM_ERR;
10846 }
10847 }
10848
10849 /* [uplevel] */
10850 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10851 Jim_Obj *const *argv)
10852 {
10853 if (argc >= 2) {
10854 int retcode, newLevel, oldLevel;
10855 Jim_CallFrame *savedCallFrame, *targetCallFrame;
10856 Jim_Obj *objPtr;
10857 const char *str;
10858
10859 /* Save the old callframe pointer */
10860 savedCallFrame = interp->framePtr;
10861
10862 /* Lookup the target frame pointer */
10863 str = Jim_GetString(argv[1], NULL);
10864 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10865 {
10866 if (Jim_GetCallFrameByLevel(interp, argv[1],
10867 &targetCallFrame,
10868 &newLevel) != JIM_OK)
10869 return JIM_ERR;
10870 argc--;
10871 argv++;
10872 } else {
10873 if (Jim_GetCallFrameByLevel(interp, NULL,
10874 &targetCallFrame,
10875 &newLevel) != JIM_OK)
10876 return JIM_ERR;
10877 }
10878 if (argc < 2) {
10879 argc++;
10880 argv--;
10881 Jim_WrongNumArgs(interp, 1, argv,
10882 "?level? command ?arg ...?");
10883 return JIM_ERR;
10884 }
10885 /* Eval the code in the target callframe. */
10886 interp->framePtr = targetCallFrame;
10887 oldLevel = interp->numLevels;
10888 interp->numLevels = newLevel;
10889 if (argc == 2) {
10890 retcode = Jim_EvalObj(interp, argv[1]);
10891 } else {
10892 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10893 Jim_IncrRefCount(objPtr);
10894 retcode = Jim_EvalObj(interp, objPtr);
10895 Jim_DecrRefCount(interp, objPtr);
10896 }
10897 interp->numLevels = oldLevel;
10898 interp->framePtr = savedCallFrame;
10899 return retcode;
10900 } else {
10901 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10902 return JIM_ERR;
10903 }
10904 }
10905
10906 /* [expr] */
10907 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10908 Jim_Obj *const *argv)
10909 {
10910 Jim_Obj *exprResultPtr;
10911 int retcode;
10912
10913 if (argc == 2) {
10914 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10915 } else if (argc > 2) {
10916 Jim_Obj *objPtr;
10917
10918 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10919 Jim_IncrRefCount(objPtr);
10920 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10921 Jim_DecrRefCount(interp, objPtr);
10922 } else {
10923 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10924 return JIM_ERR;
10925 }
10926 if (retcode != JIM_OK) return retcode;
10927 Jim_SetResult(interp, exprResultPtr);
10928 Jim_DecrRefCount(interp, exprResultPtr);
10929 return JIM_OK;
10930 }
10931
10932 /* [break] */
10933 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10934 Jim_Obj *const *argv)
10935 {
10936 if (argc != 1) {
10937 Jim_WrongNumArgs(interp, 1, argv, "");
10938 return JIM_ERR;
10939 }
10940 return JIM_BREAK;
10941 }
10942
10943 /* [continue] */
10944 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10945 Jim_Obj *const *argv)
10946 {
10947 if (argc != 1) {
10948 Jim_WrongNumArgs(interp, 1, argv, "");
10949 return JIM_ERR;
10950 }
10951 return JIM_CONTINUE;
10952 }
10953
10954 /* [return] */
10955 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10956 Jim_Obj *const *argv)
10957 {
10958 if (argc == 1) {
10959 return JIM_RETURN;
10960 } else if (argc == 2) {
10961 Jim_SetResult(interp, argv[1]);
10962 interp->returnCode = JIM_OK;
10963 return JIM_RETURN;
10964 } else if (argc == 3 || argc == 4) {
10965 int returnCode;
10966 if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10967 return JIM_ERR;
10968 interp->returnCode = returnCode;
10969 if (argc == 4)
10970 Jim_SetResult(interp, argv[3]);
10971 return JIM_RETURN;
10972 } else {
10973 Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10974 return JIM_ERR;
10975 }
10976 return JIM_RETURN; /* unreached */
10977 }
10978
10979 /* [tailcall] */
10980 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10981 Jim_Obj *const *argv)
10982 {
10983 Jim_Obj *objPtr;
10984
10985 objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10986 Jim_SetResult(interp, objPtr);
10987 return JIM_EVAL;
10988 }
10989
10990 /* [proc] */
10991 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
10992 Jim_Obj *const *argv)
10993 {
10994 int argListLen;
10995 int arityMin, arityMax;
10996
10997 if (argc != 4 && argc != 5) {
10998 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10999 return JIM_ERR;
11000 }
11001 Jim_ListLength(interp, argv[2], &argListLen);
11002 arityMin = arityMax = argListLen+1;
11003
11004 if (argListLen) {
11005 const char *str;
11006 int len;
11007 Jim_Obj *argPtr;
11008
11009 /* Check for 'args' and adjust arityMin and arityMax if necessary */
11010 Jim_ListIndex(interp, argv[2], argListLen-1, &argPtr, JIM_NONE);
11011 str = Jim_GetString(argPtr, &len);
11012 if (len == 4 && memcmp(str, "args", 4) == 0) {
11013 arityMin--;
11014 arityMax = -1;
11015 }
11016
11017 /* Check for default arguments and reduce arityMin if necessary */
11018 while (arityMin > 1) {
11019 int len;
11020 Jim_ListIndex(interp, argv[2], arityMin - 2, &argPtr, JIM_NONE);
11021 Jim_ListLength(interp, argPtr, &len);
11022 if (len != 2) {
11023 /* No default argument */
11024 break;
11025 }
11026 arityMin--;
11027 }
11028 }
11029 if (argc == 4) {
11030 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11031 argv[2], NULL, argv[3], arityMin, arityMax);
11032 } else {
11033 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11034 argv[2], argv[3], argv[4], arityMin, arityMax);
11035 }
11036 }
11037
11038 /* [concat] */
11039 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
11040 Jim_Obj *const *argv)
11041 {
11042 Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
11043 return JIM_OK;
11044 }
11045
11046 /* [upvar] */
11047 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
11048 Jim_Obj *const *argv)
11049 {
11050 const char *str;
11051 int i;
11052 Jim_CallFrame *targetCallFrame;
11053
11054 /* Lookup the target frame pointer */
11055 str = Jim_GetString(argv[1], NULL);
11056 if (argc > 3 &&
11057 ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
11058 {
11059 if (Jim_GetCallFrameByLevel(interp, argv[1],
11060 &targetCallFrame, NULL) != JIM_OK)
11061 return JIM_ERR;
11062 argc--;
11063 argv++;
11064 } else {
11065 if (Jim_GetCallFrameByLevel(interp, NULL,
11066 &targetCallFrame, NULL) != JIM_OK)
11067 return JIM_ERR;
11068 }
11069 /* Check for arity */
11070 if (argc < 3 || ((argc-1)%2) != 0) {
11071 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
11072 return JIM_ERR;
11073 }
11074 /* Now... for every other/local couple: */
11075 for (i = 1; i < argc; i += 2) {
11076 if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
11077 targetCallFrame) != JIM_OK) return JIM_ERR;
11078 }
11079 return JIM_OK;
11080 }
11081
11082 /* [global] */
11083 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
11084 Jim_Obj *const *argv)
11085 {
11086 int i;
11087
11088 if (argc < 2) {
11089 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
11090 return JIM_ERR;
11091 }
11092 /* Link every var to the toplevel having the same name */
11093 if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
11094 for (i = 1; i < argc; i++) {
11095 if (Jim_SetVariableLink(interp, argv[i], argv[i],
11096 interp->topFramePtr) != JIM_OK) return JIM_ERR;
11097 }
11098 return JIM_OK;
11099 }
11100
11101 /* does the [string map] operation. On error NULL is returned,
11102 * otherwise a new string object with the result, having refcount = 0,
11103 * is returned. */
11104 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
11105 Jim_Obj *objPtr, int nocase)
11106 {
11107 int numMaps;
11108 const char **key, *str, *noMatchStart = NULL;
11109 Jim_Obj **value;
11110 int *keyLen, strLen, i;
11111 Jim_Obj *resultObjPtr;
11112
11113 Jim_ListLength(interp, mapListObjPtr, &numMaps);
11114 if (numMaps % 2) {
11115 Jim_SetResultString(interp,
11116 "list must contain an even number of elements", -1);
11117 return NULL;
11118 }
11119 /* Initialization */
11120 numMaps /= 2;
11121 key = Jim_Alloc(sizeof(char*)*numMaps);
11122 keyLen = Jim_Alloc(sizeof(int)*numMaps);
11123 value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
11124 resultObjPtr = Jim_NewStringObj(interp, "", 0);
11125 for (i = 0; i < numMaps; i++) {
11126 Jim_Obj *eleObjPtr;
11127
11128 Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
11129 key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
11130 Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
11131 value[i] = eleObjPtr;
11132 }
11133 str = Jim_GetString(objPtr, &strLen);
11134 /* Map it */
11135 while(strLen) {
11136 for (i = 0; i < numMaps; i++) {
11137 if (strLen >= keyLen[i] && keyLen[i]) {
11138 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
11139 nocase))
11140 {
11141 if (noMatchStart) {
11142 Jim_AppendString(interp, resultObjPtr,
11143 noMatchStart, str-noMatchStart);
11144 noMatchStart = NULL;
11145 }
11146 Jim_AppendObj(interp, resultObjPtr, value[i]);
11147 str += keyLen[i];
11148 strLen -= keyLen[i];
11149 break;
11150 }
11151 }
11152 }
11153 if (i == numMaps) { /* no match */
11154 if (noMatchStart == NULL)
11155 noMatchStart = str;
11156 str ++;
11157 strLen --;
11158 }
11159 }
11160 if (noMatchStart) {
11161 Jim_AppendString(interp, resultObjPtr,
11162 noMatchStart, str-noMatchStart);
11163 }
11164 Jim_Free((void*)key);
11165 Jim_Free(keyLen);
11166 Jim_Free(value);
11167 return resultObjPtr;
11168 }
11169
11170 /* [string] */
11171 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
11172 Jim_Obj *const *argv)
11173 {
11174 int option;
11175 const char *options[] = {
11176 "length", "compare", "match", "equal", "range", "map", "repeat",
11177 "index", "first", "tolower", "toupper", NULL
11178 };
11179 enum {
11180 OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
11181 OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
11182 };
11183
11184 if (argc < 2) {
11185 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11186 return JIM_ERR;
11187 }
11188 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11189 JIM_ERRMSG) != JIM_OK)
11190 return JIM_ERR;
11191
11192 if (option == OPT_LENGTH) {
11193 int len;
11194
11195 if (argc != 3) {
11196 Jim_WrongNumArgs(interp, 2, argv, "string");
11197 return JIM_ERR;
11198 }
11199 Jim_GetString(argv[2], &len);
11200 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
11201 return JIM_OK;
11202 } else if (option == OPT_COMPARE) {
11203 int nocase = 0;
11204 if ((argc != 4 && argc != 5) ||
11205 (argc == 5 && Jim_CompareStringImmediate(interp,
11206 argv[2], "-nocase") == 0)) {
11207 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11208 return JIM_ERR;
11209 }
11210 if (argc == 5) {
11211 nocase = 1;
11212 argv++;
11213 }
11214 Jim_SetResult(interp, Jim_NewIntObj(interp,
11215 Jim_StringCompareObj(argv[2],
11216 argv[3], nocase)));
11217 return JIM_OK;
11218 } else if (option == OPT_MATCH) {
11219 int nocase = 0;
11220 if ((argc != 4 && argc != 5) ||
11221 (argc == 5 && Jim_CompareStringImmediate(interp,
11222 argv[2], "-nocase") == 0)) {
11223 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
11224 "string");
11225 return JIM_ERR;
11226 }
11227 if (argc == 5) {
11228 nocase = 1;
11229 argv++;
11230 }
11231 Jim_SetResult(interp,
11232 Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
11233 argv[3], nocase)));
11234 return JIM_OK;
11235 } else if (option == OPT_EQUAL) {
11236 if (argc != 4) {
11237 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11238 return JIM_ERR;
11239 }
11240 Jim_SetResult(interp,
11241 Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
11242 argv[3], 0)));
11243 return JIM_OK;
11244 } else if (option == OPT_RANGE) {
11245 Jim_Obj *objPtr;
11246
11247 if (argc != 5) {
11248 Jim_WrongNumArgs(interp, 2, argv, "string first last");
11249 return JIM_ERR;
11250 }
11251 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
11252 if (objPtr == NULL)
11253 return JIM_ERR;
11254 Jim_SetResult(interp, objPtr);
11255 return JIM_OK;
11256 } else if (option == OPT_MAP) {
11257 int nocase = 0;
11258 Jim_Obj *objPtr;
11259
11260 if ((argc != 4 && argc != 5) ||
11261 (argc == 5 && Jim_CompareStringImmediate(interp,
11262 argv[2], "-nocase") == 0)) {
11263 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
11264 "string");
11265 return JIM_ERR;
11266 }
11267 if (argc == 5) {
11268 nocase = 1;
11269 argv++;
11270 }
11271 objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
11272 if (objPtr == NULL)
11273 return JIM_ERR;
11274 Jim_SetResult(interp, objPtr);
11275 return JIM_OK;
11276 } else if (option == OPT_REPEAT) {
11277 Jim_Obj *objPtr;
11278 jim_wide count;
11279
11280 if (argc != 4) {
11281 Jim_WrongNumArgs(interp, 2, argv, "string count");
11282 return JIM_ERR;
11283 }
11284 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
11285 return JIM_ERR;
11286 objPtr = Jim_NewStringObj(interp, "", 0);
11287 while (count--) {
11288 Jim_AppendObj(interp, objPtr, argv[2]);
11289 }
11290 Jim_SetResult(interp, objPtr);
11291 return JIM_OK;
11292 } else if (option == OPT_INDEX) {
11293 int index, len;
11294 const char *str;
11295
11296 if (argc != 4) {
11297 Jim_WrongNumArgs(interp, 2, argv, "string index");
11298 return JIM_ERR;
11299 }
11300 if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
11301 return JIM_ERR;
11302 str = Jim_GetString(argv[2], &len);
11303 if (index != INT_MIN && index != INT_MAX)
11304 index = JimRelToAbsIndex(len, index);
11305 if (index < 0 || index >= len) {
11306 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11307 return JIM_OK;
11308 } else {
11309 Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
11310 return JIM_OK;
11311 }
11312 } else if (option == OPT_FIRST) {
11313 int index = 0, l1, l2;
11314 const char *s1, *s2;
11315
11316 if (argc != 4 && argc != 5) {
11317 Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
11318 return JIM_ERR;
11319 }
11320 s1 = Jim_GetString(argv[2], &l1);
11321 s2 = Jim_GetString(argv[3], &l2);
11322 if (argc == 5) {
11323 if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
11324 return JIM_ERR;
11325 index = JimRelToAbsIndex(l2, index);
11326 }
11327 Jim_SetResult(interp, Jim_NewIntObj(interp,
11328 JimStringFirst(s1, l1, s2, l2, index)));
11329 return JIM_OK;
11330 } else if (option == OPT_TOLOWER) {
11331 if (argc != 3) {
11332 Jim_WrongNumArgs(interp, 2, argv, "string");
11333 return JIM_ERR;
11334 }
11335 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11336 } else if (option == OPT_TOUPPER) {
11337 if (argc != 3) {
11338 Jim_WrongNumArgs(interp, 2, argv, "string");
11339 return JIM_ERR;
11340 }
11341 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11342 }
11343 return JIM_OK;
11344 }
11345
11346 /* [time] */
11347 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
11348 Jim_Obj *const *argv)
11349 {
11350 long i, count = 1;
11351 jim_wide start, elapsed;
11352 char buf [256];
11353 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11354
11355 if (argc < 2) {
11356 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11357 return JIM_ERR;
11358 }
11359 if (argc == 3) {
11360 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11361 return JIM_ERR;
11362 }
11363 if (count < 0)
11364 return JIM_OK;
11365 i = count;
11366 start = JimClock();
11367 while (i-- > 0) {
11368 int retval;
11369
11370 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11371 return retval;
11372 }
11373 elapsed = JimClock() - start;
11374 sprintf(buf, fmt, elapsed/count);
11375 Jim_SetResultString(interp, buf, -1);
11376 return JIM_OK;
11377 }
11378
11379 /* [exit] */
11380 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
11381 Jim_Obj *const *argv)
11382 {
11383 long exitCode = 0;
11384
11385 if (argc > 2) {
11386 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11387 return JIM_ERR;
11388 }
11389 if (argc == 2) {
11390 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11391 return JIM_ERR;
11392 }
11393 interp->exitCode = exitCode;
11394 return JIM_EXIT;
11395 }
11396
11397 /* [catch] */
11398 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
11399 Jim_Obj *const *argv)
11400 {
11401 int exitCode = 0;
11402
11403 if (argc != 2 && argc != 3) {
11404 Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11405 return JIM_ERR;
11406 }
11407 exitCode = Jim_EvalObj(interp, argv[1]);
11408 if (argc == 3) {
11409 if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11410 != JIM_OK)
11411 return JIM_ERR;
11412 }
11413 Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11414 return JIM_OK;
11415 }
11416
11417 /* [ref] */
11418 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
11419 Jim_Obj *const *argv)
11420 {
11421 if (argc != 3 && argc != 4) {
11422 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11423 return JIM_ERR;
11424 }
11425 if (argc == 3) {
11426 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11427 } else {
11428 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11429 argv[3]));
11430 }
11431 return JIM_OK;
11432 }
11433
11434 /* [getref] */
11435 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
11436 Jim_Obj *const *argv)
11437 {
11438 Jim_Reference *refPtr;
11439
11440 if (argc != 2) {
11441 Jim_WrongNumArgs(interp, 1, argv, "reference");
11442 return JIM_ERR;
11443 }
11444 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11445 return JIM_ERR;
11446 Jim_SetResult(interp, refPtr->objPtr);
11447 return JIM_OK;
11448 }
11449
11450 /* [setref] */
11451 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
11452 Jim_Obj *const *argv)
11453 {
11454 Jim_Reference *refPtr;
11455
11456 if (argc != 3) {
11457 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11458 return JIM_ERR;
11459 }
11460 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11461 return JIM_ERR;
11462 Jim_IncrRefCount(argv[2]);
11463 Jim_DecrRefCount(interp, refPtr->objPtr);
11464 refPtr->objPtr = argv[2];
11465 Jim_SetResult(interp, argv[2]);
11466 return JIM_OK;
11467 }
11468
11469 /* [collect] */
11470 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
11471 Jim_Obj *const *argv)
11472 {
11473 if (argc != 1) {
11474 Jim_WrongNumArgs(interp, 1, argv, "");
11475 return JIM_ERR;
11476 }
11477 Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11478 return JIM_OK;
11479 }
11480
11481 /* [finalize] reference ?newValue? */
11482 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
11483 Jim_Obj *const *argv)
11484 {
11485 if (argc != 2 && argc != 3) {
11486 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11487 return JIM_ERR;
11488 }
11489 if (argc == 2) {
11490 Jim_Obj *cmdNamePtr;
11491
11492 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11493 return JIM_ERR;
11494 if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11495 Jim_SetResult(interp, cmdNamePtr);
11496 } else {
11497 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11498 return JIM_ERR;
11499 Jim_SetResult(interp, argv[2]);
11500 }
11501 return JIM_OK;
11502 }
11503
11504 /* TODO */
11505 /* [info references] (list of all the references/finalizers) */
11506
11507 /* [rename] */
11508 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
11509 Jim_Obj *const *argv)
11510 {
11511 const char *oldName, *newName;
11512
11513 if (argc != 3) {
11514 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11515 return JIM_ERR;
11516 }
11517 oldName = Jim_GetString(argv[1], NULL);
11518 newName = Jim_GetString(argv[2], NULL);
11519 if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11520 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11521 Jim_AppendStrings(interp, Jim_GetResult(interp),
11522 "can't rename \"", oldName, "\": ",
11523 "command doesn't exist", NULL);
11524 return JIM_ERR;
11525 }
11526 return JIM_OK;
11527 }
11528
11529 /* [dict] */
11530 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
11531 Jim_Obj *const *argv)
11532 {
11533 int option;
11534 const char *options[] = {
11535 "create", "get", "set", "unset", "exists", NULL
11536 };
11537 enum {
11538 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11539 };
11540
11541 if (argc < 2) {
11542 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11543 return JIM_ERR;
11544 }
11545
11546 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11547 JIM_ERRMSG) != JIM_OK)
11548 return JIM_ERR;
11549
11550 if (option == OPT_CREATE) {
11551 Jim_Obj *objPtr;
11552
11553 if (argc % 2) {
11554 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11555 return JIM_ERR;
11556 }
11557 objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11558 Jim_SetResult(interp, objPtr);
11559 return JIM_OK;
11560 } else if (option == OPT_GET) {
11561 Jim_Obj *objPtr;
11562
11563 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11564 JIM_ERRMSG) != JIM_OK)
11565 return JIM_ERR;
11566 Jim_SetResult(interp, objPtr);
11567 return JIM_OK;
11568 } else if (option == OPT_SET) {
11569 if (argc < 5) {
11570 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11571 return JIM_ERR;
11572 }
11573 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11574 argv[argc-1]);
11575 } else if (option == OPT_UNSET) {
11576 if (argc < 4) {
11577 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11578 return JIM_ERR;
11579 }
11580 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11581 NULL);
11582 } else if (option == OPT_EXIST) {
11583 Jim_Obj *objPtr;
11584 int exists;
11585
11586 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11587 JIM_ERRMSG) == JIM_OK)
11588 exists = 1;
11589 else
11590 exists = 0;
11591 Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11592 return JIM_OK;
11593 } else {
11594 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11595 Jim_AppendStrings(interp, Jim_GetResult(interp),
11596 "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11597 " must be create, get, set", NULL);
11598 return JIM_ERR;
11599 }
11600 return JIM_OK;
11601 }
11602
11603 /* [load] */
11604 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11605 Jim_Obj *const *argv)
11606 {
11607 if (argc < 2) {
11608 Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11609 return JIM_ERR;
11610 }
11611 return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11612 }
11613
11614 /* [subst] */
11615 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11616 Jim_Obj *const *argv)
11617 {
11618 int i, flags = 0;
11619 Jim_Obj *objPtr;
11620
11621 if (argc < 2) {
11622 Jim_WrongNumArgs(interp, 1, argv,
11623 "?-nobackslashes? ?-nocommands? ?-novariables? string");
11624 return JIM_ERR;
11625 }
11626 i = argc-2;
11627 while(i--) {
11628 if (Jim_CompareStringImmediate(interp, argv[i+1],
11629 "-nobackslashes"))
11630 flags |= JIM_SUBST_NOESC;
11631 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11632 "-novariables"))
11633 flags |= JIM_SUBST_NOVAR;
11634 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11635 "-nocommands"))
11636 flags |= JIM_SUBST_NOCMD;
11637 else {
11638 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11639 Jim_AppendStrings(interp, Jim_GetResult(interp),
11640 "bad option \"", Jim_GetString(argv[i+1], NULL),
11641 "\": must be -nobackslashes, -nocommands, or "
11642 "-novariables", NULL);
11643 return JIM_ERR;
11644 }
11645 }
11646 if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11647 return JIM_ERR;
11648 Jim_SetResult(interp, objPtr);
11649 return JIM_OK;
11650 }
11651
11652 /* [info] */
11653 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11654 Jim_Obj *const *argv)
11655 {
11656 int cmd, result = JIM_OK;
11657 static const char *commands[] = {
11658 "body", "commands", "exists", "globals", "level", "locals",
11659 "vars", "version", "complete", "args", "hostname", NULL
11660 };
11661 enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11662 INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS, INFO_HOSTNAME};
11663
11664 if (argc < 2) {
11665 Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11666 return JIM_ERR;
11667 }
11668 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11669 != JIM_OK) {
11670 return JIM_ERR;
11671 }
11672
11673 if (cmd == INFO_COMMANDS) {
11674 if (argc != 2 && argc != 3) {
11675 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11676 return JIM_ERR;
11677 }
11678 if (argc == 3)
11679 Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11680 else
11681 Jim_SetResult(interp, JimCommandsList(interp, NULL));
11682 } else if (cmd == INFO_EXISTS) {
11683 Jim_Obj *exists;
11684 if (argc != 3) {
11685 Jim_WrongNumArgs(interp, 2, argv, "varName");
11686 return JIM_ERR;
11687 }
11688 exists = Jim_GetVariable(interp, argv[2], 0);
11689 Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11690 } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11691 int mode;
11692 switch (cmd) {
11693 case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11694 case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
11695 case INFO_VARS: mode = JIM_VARLIST_VARS; break;
11696 default: mode = 0; /* avoid warning */; break;
11697 }
11698 if (argc != 2 && argc != 3) {
11699 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11700 return JIM_ERR;
11701 }
11702 if (argc == 3)
11703 Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11704 else
11705 Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11706 } else if (cmd == INFO_LEVEL) {
11707 Jim_Obj *objPtr;
11708 switch (argc) {
11709 case 2:
11710 Jim_SetResult(interp,
11711 Jim_NewIntObj(interp, interp->numLevels));
11712 break;
11713 case 3:
11714 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11715 return JIM_ERR;
11716 Jim_SetResult(interp, objPtr);
11717 break;
11718 default:
11719 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11720 return JIM_ERR;
11721 }
11722 } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11723 Jim_Cmd *cmdPtr;
11724
11725 if (argc != 3) {
11726 Jim_WrongNumArgs(interp, 2, argv, "procname");
11727 return JIM_ERR;
11728 }
11729 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11730 return JIM_ERR;
11731 if (cmdPtr->cmdProc != NULL) {
11732 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11733 Jim_AppendStrings(interp, Jim_GetResult(interp),
11734 "command \"", Jim_GetString(argv[2], NULL),
11735 "\" is not a procedure", NULL);
11736 return JIM_ERR;
11737 }
11738 if (cmd == INFO_BODY)
11739 Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11740 else
11741 Jim_SetResult(interp, cmdPtr->argListObjPtr);
11742 } else if (cmd == INFO_VERSION) {
11743 char buf[(JIM_INTEGER_SPACE * 2) + 1];
11744 sprintf(buf, "%d.%d",
11745 JIM_VERSION / 100, JIM_VERSION % 100);
11746 Jim_SetResultString(interp, buf, -1);
11747 } else if (cmd == INFO_COMPLETE) {
11748 const char *s;
11749 int len;
11750
11751 if (argc != 3) {
11752 Jim_WrongNumArgs(interp, 2, argv, "script");
11753 return JIM_ERR;
11754 }
11755 s = Jim_GetString(argv[2], &len);
11756 Jim_SetResult(interp,
11757 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11758 } else if (cmd == INFO_HOSTNAME) {
11759 /* Redirect to os.hostname if it exists */
11760 Jim_Obj *command = Jim_NewStringObj(interp, "os.gethostname", -1);
11761 result = Jim_EvalObjVector(interp, 1, &command);
11762 }
11763 return result;
11764 }
11765
11766 /* [split] */
11767 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11768 Jim_Obj *const *argv)
11769 {
11770 const char *str, *splitChars, *noMatchStart;
11771 int splitLen, strLen, i;
11772 Jim_Obj *resObjPtr;
11773
11774 if (argc != 2 && argc != 3) {
11775 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11776 return JIM_ERR;
11777 }
11778 /* Init */
11779 if (argc == 2) {
11780 splitChars = " \n\t\r";
11781 splitLen = 4;
11782 } else {
11783 splitChars = Jim_GetString(argv[2], &splitLen);
11784 }
11785 str = Jim_GetString(argv[1], &strLen);
11786 if (!strLen) return JIM_OK;
11787 noMatchStart = str;
11788 resObjPtr = Jim_NewListObj(interp, NULL, 0);
11789 /* Split */
11790 if (splitLen) {
11791 while (strLen) {
11792 for (i = 0; i < splitLen; i++) {
11793 if (*str == splitChars[i]) {
11794 Jim_Obj *objPtr;
11795
11796 objPtr = Jim_NewStringObj(interp, noMatchStart,
11797 (str-noMatchStart));
11798 Jim_ListAppendElement(interp, resObjPtr, objPtr);
11799 noMatchStart = str+1;
11800 break;
11801 }
11802 }
11803 str ++;
11804 strLen --;
11805 }
11806 Jim_ListAppendElement(interp, resObjPtr,
11807 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11808 } else {
11809 /* This handles the special case of splitchars eq {}. This
11810 * is trivial but we want to perform object sharing as Tcl does. */
11811 Jim_Obj *objCache[256];
11812 const unsigned char *u = (unsigned char*) str;
11813 memset(objCache, 0, sizeof(objCache));
11814 for (i = 0; i < strLen; i++) {
11815 int c = u[i];
11816
11817 if (objCache[c] == NULL)
11818 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11819 Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11820 }
11821 }
11822 Jim_SetResult(interp, resObjPtr);
11823 return JIM_OK;
11824 }
11825
11826 /* [join] */
11827 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11828 Jim_Obj *const *argv)
11829 {
11830 const char *joinStr;
11831 int joinStrLen, i, listLen;
11832 Jim_Obj *resObjPtr;
11833
11834 if (argc != 2 && argc != 3) {
11835 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11836 return JIM_ERR;
11837 }
11838 /* Init */
11839 if (argc == 2) {
11840 joinStr = " ";
11841 joinStrLen = 1;
11842 } else {
11843 joinStr = Jim_GetString(argv[2], &joinStrLen);
11844 }
11845 Jim_ListLength(interp, argv[1], &listLen);
11846 resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11847 /* Split */
11848 for (i = 0; i < listLen; i++) {
11849 Jim_Obj *objPtr;
11850
11851 Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11852 Jim_AppendObj(interp, resObjPtr, objPtr);
11853 if (i+1 != listLen) {
11854 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11855 }
11856 }
11857 Jim_SetResult(interp, resObjPtr);
11858 return JIM_OK;
11859 }
11860
11861 /* [format] */
11862 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11863 Jim_Obj *const *argv)
11864 {
11865 Jim_Obj *objPtr;
11866
11867 if (argc < 2) {
11868 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11869 return JIM_ERR;
11870 }
11871 objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11872 if (objPtr == NULL)
11873 return JIM_ERR;
11874 Jim_SetResult(interp, objPtr);
11875 return JIM_OK;
11876 }
11877
11878 /* [scan] */
11879 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11880 Jim_Obj *const *argv)
11881 {
11882 Jim_Obj *listPtr, **outVec;
11883 int outc, i, count = 0;
11884
11885 if (argc < 3) {
11886 Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11887 return JIM_ERR;
11888 }
11889 if (argv[2]->typePtr != &scanFmtStringObjType)
11890 SetScanFmtFromAny(interp, argv[2]);
11891 if (FormatGetError(argv[2]) != 0) {
11892 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11893 return JIM_ERR;
11894 }
11895 if (argc > 3) {
11896 int maxPos = FormatGetMaxPos(argv[2]);
11897 int count = FormatGetCnvCount(argv[2]);
11898 if (maxPos > argc-3) {
11899 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11900 return JIM_ERR;
11901 } else if (count != 0 && count < argc-3) {
11902 Jim_SetResultString(interp, "variable is not assigned by any "
11903 "conversion specifiers", -1);
11904 return JIM_ERR;
11905 } else if (count > argc-3) {
11906 Jim_SetResultString(interp, "different numbers of variable names and "
11907 "field specifiers", -1);
11908 return JIM_ERR;
11909 }
11910 }
11911 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11912 if (listPtr == 0)
11913 return JIM_ERR;
11914 if (argc > 3) {
11915 int len = 0;
11916 if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11917 Jim_ListLength(interp, listPtr, &len);
11918 if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11919 Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11920 return JIM_OK;
11921 }
11922 JimListGetElements(interp, listPtr, &outc, &outVec);
11923 for (i = 0; i < outc; ++i) {
11924 if (Jim_Length(outVec[i]) > 0) {
11925 ++count;
11926 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11927 goto err;
11928 }
11929 }
11930 Jim_FreeNewObj(interp, listPtr);
11931 Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11932 } else {
11933 if (listPtr == (Jim_Obj*)EOF) {
11934 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11935 return JIM_OK;
11936 }
11937 Jim_SetResult(interp, listPtr);
11938 }
11939 return JIM_OK;
11940 err:
11941 Jim_FreeNewObj(interp, listPtr);
11942 return JIM_ERR;
11943 }
11944
11945 /* [error] */
11946 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11947 Jim_Obj *const *argv)
11948 {
11949 if (argc != 2) {
11950 Jim_WrongNumArgs(interp, 1, argv, "message");
11951 return JIM_ERR;
11952 }
11953 Jim_SetResult(interp, argv[1]);
11954 return JIM_ERR;
11955 }
11956
11957 /* [lrange] */
11958 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11959 Jim_Obj *const *argv)
11960 {
11961 Jim_Obj *objPtr;
11962
11963 if (argc != 4) {
11964 Jim_WrongNumArgs(interp, 1, argv, "list first last");
11965 return JIM_ERR;
11966 }
11967 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11968 return JIM_ERR;
11969 Jim_SetResult(interp, objPtr);
11970 return JIM_OK;
11971 }
11972
11973 /* [env] */
11974 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11975 Jim_Obj *const *argv)
11976 {
11977 const char *key;
11978 char *val;
11979
11980 if (argc == 1) {
11981
11982 #if !defined(_WIN32) && !defined(__USE_GNU)
11983 extern char **environ;
11984 #endif
11985
11986 int i;
11987 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
11988
11989 for (i = 0; environ[i]; i++) {
11990 const char *equals = strchr(environ[i], '=');
11991 if (equals) {
11992 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, environ[i], equals - environ[i]));
11993 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
11994 }
11995 }
11996
11997 Jim_SetResult(interp, listObjPtr);
11998 return JIM_OK;
11999 }
12000
12001 if (argc != 2) {
12002 Jim_WrongNumArgs(interp, 1, argv, "varName");
12003 return JIM_ERR;
12004 }
12005 key = Jim_GetString(argv[1], NULL);
12006 val = getenv(key);
12007 if (val == NULL) {
12008 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
12009 Jim_AppendStrings(interp, Jim_GetResult(interp),
12010 "environment variable \"",
12011 key, "\" does not exist", NULL);
12012 return JIM_ERR;
12013 }
12014 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
12015 return JIM_OK;
12016 }
12017
12018 /* [source] */
12019 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
12020 Jim_Obj *const *argv)
12021 {
12022 int retval;
12023
12024 if (argc != 2) {
12025 Jim_WrongNumArgs(interp, 1, argv, "fileName");
12026 return JIM_ERR;
12027 }
12028 retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
12029 if (retval == JIM_ERR) {
12030 return JIM_ERR_ADDSTACK;
12031 }
12032 if (retval == JIM_RETURN)
12033 return JIM_OK;
12034 return retval;
12035 }
12036
12037 /* [lreverse] */
12038 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
12039 Jim_Obj *const *argv)
12040 {
12041 Jim_Obj *revObjPtr, **ele;
12042 int len;
12043
12044 if (argc != 2) {
12045 Jim_WrongNumArgs(interp, 1, argv, "list");
12046 return JIM_ERR;
12047 }
12048 JimListGetElements(interp, argv[1], &len, &ele);
12049 len--;
12050 revObjPtr = Jim_NewListObj(interp, NULL, 0);
12051 while (len >= 0)
12052 ListAppendElement(revObjPtr, ele[len--]);
12053 Jim_SetResult(interp, revObjPtr);
12054 return JIM_OK;
12055 }
12056
12057 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
12058 {
12059 jim_wide len;
12060
12061 if (step == 0) return -1;
12062 if (start == end) return 0;
12063 else if (step > 0 && start > end) return -1;
12064 else if (step < 0 && end > start) return -1;
12065 len = end-start;
12066 if (len < 0) len = -len; /* abs(len) */
12067 if (step < 0) step = -step; /* abs(step) */
12068 len = 1 + ((len-1)/step);
12069 /* We can truncate safely to INT_MAX, the range command
12070 * will always return an error for a such long range
12071 * because Tcl lists can't be so long. */
12072 if (len > INT_MAX) len = INT_MAX;
12073 return (int)((len < 0) ? -1 : len);
12074 }
12075
12076 /* [range] */
12077 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
12078 Jim_Obj *const *argv)
12079 {
12080 jim_wide start = 0, end, step = 1;
12081 int len, i;
12082 Jim_Obj *objPtr;
12083
12084 if (argc < 2 || argc > 4) {
12085 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
12086 return JIM_ERR;
12087 }
12088 if (argc == 2) {
12089 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
12090 return JIM_ERR;
12091 } else {
12092 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
12093 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
12094 return JIM_ERR;
12095 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
12096 return JIM_ERR;
12097 }
12098 if ((len = JimRangeLen(start, end, step)) == -1) {
12099 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
12100 return JIM_ERR;
12101 }
12102 objPtr = Jim_NewListObj(interp, NULL, 0);
12103 for (i = 0; i < len; i++)
12104 ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
12105 Jim_SetResult(interp, objPtr);
12106 return JIM_OK;
12107 }
12108
12109 /* [rand] */
12110 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
12111 Jim_Obj *const *argv)
12112 {
12113 jim_wide min = 0, max, len, maxMul;
12114
12115 if (argc < 1 || argc > 3) {
12116 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
12117 return JIM_ERR;
12118 }
12119 if (argc == 1) {
12120 max = JIM_WIDE_MAX;
12121 } else if (argc == 2) {
12122 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
12123 return JIM_ERR;
12124 } else if (argc == 3) {
12125 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
12126 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
12127 return JIM_ERR;
12128 }
12129 len = max-min;
12130 if (len < 0) {
12131 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
12132 return JIM_ERR;
12133 }
12134 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
12135 while (1) {
12136 jim_wide r;
12137
12138 JimRandomBytes(interp, &r, sizeof(jim_wide));
12139 if (r < 0 || r >= maxMul) continue;
12140 r = (len == 0) ? 0 : r%len;
12141 Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
12142 return JIM_OK;
12143 }
12144 }
12145
12146 /* [package] */
12147 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
12148 Jim_Obj *const *argv)
12149 {
12150 int option;
12151 const char *options[] = {
12152 "require", "provide", NULL
12153 };
12154 enum {OPT_REQUIRE, OPT_PROVIDE};
12155
12156 if (argc < 2) {
12157 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
12158 return JIM_ERR;
12159 }
12160 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
12161 JIM_ERRMSG) != JIM_OK)
12162 return JIM_ERR;
12163
12164 if (option == OPT_REQUIRE) {
12165 int exact = 0;
12166 const char *ver;
12167
12168 if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
12169 exact = 1;
12170 argv++;
12171 argc--;
12172 }
12173 if (argc != 3 && argc != 4) {
12174 Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
12175 return JIM_ERR;
12176 }
12177 ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
12178 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
12179 JIM_ERRMSG);
12180 if (ver == NULL)
12181 return JIM_ERR_ADDSTACK;
12182 Jim_SetResultString(interp, ver, -1);
12183 } else if (option == OPT_PROVIDE) {
12184 if (argc != 4) {
12185 Jim_WrongNumArgs(interp, 2, argv, "package version");
12186 return JIM_ERR;
12187 }
12188 return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
12189 Jim_GetString(argv[3], NULL), JIM_ERRMSG);
12190 }
12191 return JIM_OK;
12192 }
12193
12194 static struct {
12195 const char *name;
12196 Jim_CmdProc cmdProc;
12197 } Jim_CoreCommandsTable[] = {
12198 {"set", Jim_SetCoreCommand},
12199 {"unset", Jim_UnsetCoreCommand},
12200 {"puts", Jim_PutsCoreCommand},
12201 {"+", Jim_AddCoreCommand},
12202 {"*", Jim_MulCoreCommand},
12203 {"-", Jim_SubCoreCommand},
12204 {"/", Jim_DivCoreCommand},
12205 {"incr", Jim_IncrCoreCommand},
12206 {"while", Jim_WhileCoreCommand},
12207 {"for", Jim_ForCoreCommand},
12208 {"foreach", Jim_ForeachCoreCommand},
12209 {"lmap", Jim_LmapCoreCommand},
12210 {"if", Jim_IfCoreCommand},
12211 {"switch", Jim_SwitchCoreCommand},
12212 {"list", Jim_ListCoreCommand},
12213 {"lindex", Jim_LindexCoreCommand},
12214 {"lset", Jim_LsetCoreCommand},
12215 {"llength", Jim_LlengthCoreCommand},
12216 {"lappend", Jim_LappendCoreCommand},
12217 {"linsert", Jim_LinsertCoreCommand},
12218 {"lsort", Jim_LsortCoreCommand},
12219 {"append", Jim_AppendCoreCommand},
12220 {"debug", Jim_DebugCoreCommand},
12221 {"eval", Jim_EvalCoreCommand},
12222 {"uplevel", Jim_UplevelCoreCommand},
12223 {"expr", Jim_ExprCoreCommand},
12224 {"break", Jim_BreakCoreCommand},
12225 {"continue", Jim_ContinueCoreCommand},
12226 {"proc", Jim_ProcCoreCommand},
12227 {"concat", Jim_ConcatCoreCommand},
12228 {"return", Jim_ReturnCoreCommand},
12229 {"upvar", Jim_UpvarCoreCommand},
12230 {"global", Jim_GlobalCoreCommand},
12231 {"string", Jim_StringCoreCommand},
12232 {"time", Jim_TimeCoreCommand},
12233 {"exit", Jim_ExitCoreCommand},
12234 {"catch", Jim_CatchCoreCommand},
12235 {"ref", Jim_RefCoreCommand},
12236 {"getref", Jim_GetrefCoreCommand},
12237 {"setref", Jim_SetrefCoreCommand},
12238 {"finalize", Jim_FinalizeCoreCommand},
12239 {"collect", Jim_CollectCoreCommand},
12240 {"rename", Jim_RenameCoreCommand},
12241 {"dict", Jim_DictCoreCommand},
12242 {"load", Jim_LoadCoreCommand},
12243 {"subst", Jim_SubstCoreCommand},
12244 {"info", Jim_InfoCoreCommand},
12245 {"split", Jim_SplitCoreCommand},
12246 {"join", Jim_JoinCoreCommand},
12247 {"format", Jim_FormatCoreCommand},
12248 {"scan", Jim_ScanCoreCommand},
12249 {"error", Jim_ErrorCoreCommand},
12250 {"lrange", Jim_LrangeCoreCommand},
12251 {"env", Jim_EnvCoreCommand},
12252 {"source", Jim_SourceCoreCommand},
12253 {"lreverse", Jim_LreverseCoreCommand},
12254 {"range", Jim_RangeCoreCommand},
12255 {"rand", Jim_RandCoreCommand},
12256 {"package", Jim_PackageCoreCommand},
12257 {"tailcall", Jim_TailcallCoreCommand},
12258 {NULL, NULL},
12259 };
12260
12261 /* Some Jim core command is actually a procedure written in Jim itself. */
12262 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
12263 {
12264 Jim_Eval(interp, (char*)
12265 "proc lambda {arglist args} {\n"
12266 " set name [ref {} function lambdaFinalizer]\n"
12267 " uplevel 1 [list proc $name $arglist {expand}$args]\n"
12268 " return $name\n"
12269 "}\n"
12270 "proc lambdaFinalizer {name val} {\n"
12271 " rename $name {}\n"
12272 "}\n"
12273 );
12274 }
12275
12276 void Jim_RegisterCoreCommands(Jim_Interp *interp)
12277 {
12278 int i = 0;
12279
12280 while(Jim_CoreCommandsTable[i].name != NULL) {
12281 Jim_CreateCommand(interp,
12282 Jim_CoreCommandsTable[i].name,
12283 Jim_CoreCommandsTable[i].cmdProc,
12284 NULL, NULL);
12285 i++;
12286 }
12287 Jim_RegisterCoreProcedures(interp);
12288 }
12289
12290 /* -----------------------------------------------------------------------------
12291 * Interactive prompt
12292 * ---------------------------------------------------------------------------*/
12293 void Jim_PrintErrorMessage(Jim_Interp *interp)
12294 {
12295 int len, i;
12296
12297 if (*interp->errorFileName) {
12298 Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL " ",
12299 interp->errorFileName, interp->errorLine);
12300 }
12301 Jim_fprintf(interp,interp->cookie_stderr, "%s" JIM_NL,
12302 Jim_GetString(interp->result, NULL));
12303 Jim_ListLength(interp, interp->stackTrace, &len);
12304 for (i = len-3; i >= 0; i-= 3) {
12305 Jim_Obj *objPtr;
12306 const char *proc, *file, *line;
12307
12308 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
12309 proc = Jim_GetString(objPtr, NULL);
12310 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
12311 JIM_NONE);
12312 file = Jim_GetString(objPtr, NULL);
12313 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
12314 JIM_NONE);
12315 line = Jim_GetString(objPtr, NULL);
12316 if (*proc) {
12317 Jim_fprintf( interp, interp->cookie_stderr,
12318 "in procedure '%s' ", proc);
12319 }
12320 if (*file) {
12321 Jim_fprintf( interp, interp->cookie_stderr,
12322 "called at file \"%s\", line %s",
12323 file, line);
12324 }
12325 if (*file || *proc) {
12326 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL);
12327 }
12328 }
12329 }
12330
12331 int Jim_InteractivePrompt(Jim_Interp *interp)
12332 {
12333 int retcode = JIM_OK;
12334 Jim_Obj *scriptObjPtr;
12335
12336 Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
12337 "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
12338 JIM_VERSION / 100, JIM_VERSION % 100);
12339 Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
12340 while (1) {
12341 char buf[1024];
12342 const char *result;
12343 const char *retcodestr[] = {
12344 "ok", "error", "return", "break", "continue", "eval", "exit"
12345 };
12346 int reslen;
12347
12348 if (retcode != 0) {
12349 if (retcode >= 2 && retcode <= 6)
12350 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
12351 else
12352 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
12353 } else
12354 Jim_fprintf( interp, interp->cookie_stdout, ". ");
12355 Jim_fflush( interp, interp->cookie_stdout);
12356 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
12357 Jim_IncrRefCount(scriptObjPtr);
12358 while(1) {
12359 const char *str;
12360 char state;
12361 int len;
12362
12363 if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
12364 Jim_DecrRefCount(interp, scriptObjPtr);
12365 goto out;
12366 }
12367 Jim_AppendString(interp, scriptObjPtr, buf, -1);
12368 str = Jim_GetString(scriptObjPtr, &len);
12369 if (Jim_ScriptIsComplete(str, len, &state))
12370 break;
12371 Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12372 Jim_fflush( interp, interp->cookie_stdout);
12373 }
12374 retcode = Jim_EvalObj(interp, scriptObjPtr);
12375 Jim_DecrRefCount(interp, scriptObjPtr);
12376 result = Jim_GetString(Jim_GetResult(interp), &reslen);
12377 if (retcode == JIM_ERR) {
12378 Jim_PrintErrorMessage(interp);
12379 } else if (retcode == JIM_EXIT) {
12380 exit(Jim_GetExitCode(interp));
12381 } else {
12382 if (reslen) {
12383 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12384 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12385 }
12386 }
12387 }
12388 out:
12389 return 0;
12390 }
12391
12392 /* -----------------------------------------------------------------------------
12393 * Jim's idea of STDIO..
12394 * ---------------------------------------------------------------------------*/
12395
12396 int Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12397 {
12398 int r;
12399
12400 va_list ap;
12401 va_start(ap,fmt);
12402 r = Jim_vfprintf( interp, cookie, fmt,ap );
12403 va_end(ap);
12404 return r;
12405 }
12406
12407 int Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12408 {
12409 if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12410 errno = ENOTSUP;
12411 return -1;
12412 }
12413 return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12414 }
12415
12416 size_t Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12417 {
12418 if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12419 errno = ENOTSUP;
12420 return 0;
12421 }
12422 return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12423 }
12424
12425 size_t Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12426 {
12427 if( (interp == NULL) || (interp->cb_fread == NULL) ){
12428 errno = ENOTSUP;
12429 return 0;
12430 }
12431 return (*(interp->cb_fread))( ptr, size, n, cookie);
12432 }
12433
12434 int Jim_fflush( Jim_Interp *interp, void *cookie )
12435 {
12436 if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12437 /* pretend all is well */
12438 return 0;
12439 }
12440 return (*(interp->cb_fflush))( cookie );
12441 }
12442
12443 char* Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12444 {
12445 if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12446 errno = ENOTSUP;
12447 return NULL;
12448 }
12449 return (*(interp->cb_fgets))( s, size, cookie );
12450 }
12451 Jim_Nvp *
12452 Jim_Nvp_name2value_simple( const Jim_Nvp *p, const char *name )
12453 {
12454 while( p->name ){
12455 if( 0 == strcmp( name, p->name ) ){
12456 break;
12457 }
12458 p++;
12459 }
12460 return ((Jim_Nvp *)(p));
12461 }
12462
12463 Jim_Nvp *
12464 Jim_Nvp_name2value_nocase_simple( const Jim_Nvp *p, const char *name )
12465 {
12466 while( p->name ){
12467 if( 0 == strcasecmp( name, p->name ) ){
12468 break;
12469 }
12470 p++;
12471 }
12472 return ((Jim_Nvp *)(p));
12473 }
12474
12475 int
12476 Jim_Nvp_name2value_obj( Jim_Interp *interp,
12477 const Jim_Nvp *p,
12478 Jim_Obj *o,
12479 Jim_Nvp **result )
12480 {
12481 return Jim_Nvp_name2value( interp, p, Jim_GetString( o, NULL ), result );
12482 }
12483
12484
12485 int
12486 Jim_Nvp_name2value( Jim_Interp *interp,
12487 const Jim_Nvp *_p,
12488 const char *name,
12489 Jim_Nvp **result)
12490 {
12491 const Jim_Nvp *p;
12492
12493 p = Jim_Nvp_name2value_simple( _p, name );
12494
12495 /* result */
12496 if( result ){
12497 *result = (Jim_Nvp *)(p);
12498 }
12499
12500 /* found? */
12501 if( p->name ){
12502 return JIM_OK;
12503 } else {
12504 return JIM_ERR;
12505 }
12506 }
12507
12508 int
12509 Jim_Nvp_name2value_obj_nocase( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **puthere )
12510 {
12511 return Jim_Nvp_name2value_nocase( interp, p, Jim_GetString( o, NULL ), puthere );
12512 }
12513
12514 int
12515 Jim_Nvp_name2value_nocase( Jim_Interp *interp, const Jim_Nvp *_p, const char *name, Jim_Nvp **puthere )
12516 {
12517 const Jim_Nvp *p;
12518
12519 p = Jim_Nvp_name2value_nocase_simple( _p, name );
12520
12521 if( puthere ){
12522 *puthere = (Jim_Nvp *)(p);
12523 }
12524 /* found */
12525 if( p->name ){
12526 return JIM_OK;
12527 } else {
12528 return JIM_ERR;
12529 }
12530 }
12531
12532
12533 int
12534 Jim_Nvp_value2name_obj( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **result )
12535 {
12536 int e;;
12537 jim_wide w;
12538
12539 e = Jim_GetWide( interp, o, &w );
12540 if( e != JIM_OK ){
12541 return e;
12542 }
12543
12544 return Jim_Nvp_value2name( interp, p, w, result );
12545 }
12546
12547 Jim_Nvp *
12548 Jim_Nvp_value2name_simple( const Jim_Nvp *p, int value )
12549 {
12550 while( p->name ){
12551 if( value == p->value ){
12552 break;
12553 }
12554 p++;
12555 }
12556 return ((Jim_Nvp *)(p));
12557 }
12558
12559
12560 int
12561 Jim_Nvp_value2name( Jim_Interp *interp, const Jim_Nvp *_p, int value, Jim_Nvp **result )
12562 {
12563 const Jim_Nvp *p;
12564
12565 p = Jim_Nvp_value2name_simple( _p, value );
12566
12567 if( result ){
12568 *result = (Jim_Nvp *)(p);
12569 }
12570
12571 if( p->name ){
12572 return JIM_OK;
12573 } else {
12574 return JIM_ERR;
12575 }
12576 }
12577
12578
12579 int
12580 Jim_GetOpt_Setup( Jim_GetOptInfo *p, Jim_Interp *interp, int argc, Jim_Obj * const * argv)
12581 {
12582 memset( p, 0, sizeof(*p) );
12583 p->interp = interp;
12584 p->argc = argc;
12585 p->argv = argv;
12586
12587 return JIM_OK;
12588 }
12589
12590 void
12591 Jim_GetOpt_Debug( Jim_GetOptInfo *p )
12592 {
12593 int x;
12594
12595 Jim_fprintf( p->interp, p->interp->cookie_stderr, "---args---\n");
12596 for( x = 0 ; x < p->argc ; x++ ){
12597 Jim_fprintf( p->interp, p->interp->cookie_stderr,
12598 "%2d) %s\n",
12599 x,
12600 Jim_GetString( p->argv[x], NULL ) );
12601 }
12602 Jim_fprintf( p->interp, p->interp->cookie_stderr, "-------\n");
12603 }
12604
12605
12606 int
12607 Jim_GetOpt_Obj( Jim_GetOptInfo *goi, Jim_Obj **puthere )
12608 {
12609 Jim_Obj *o;
12610
12611 o = NULL; // failure
12612 if( goi->argc ){
12613 // success
12614 o = goi->argv[0];
12615 goi->argc -= 1;
12616 goi->argv += 1;
12617 }
12618 if( puthere ){
12619 *puthere = o;
12620 }
12621 if( o != NULL ){
12622 return JIM_OK;
12623 } else {
12624 return JIM_ERR;
12625 }
12626 }
12627
12628 int
12629 Jim_GetOpt_String( Jim_GetOptInfo *goi, char **puthere, int *len )
12630 {
12631 int r;
12632 Jim_Obj *o;
12633 const char *cp;
12634
12635
12636 r = Jim_GetOpt_Obj( goi, &o );
12637 if( r == JIM_OK ){
12638 cp = Jim_GetString( o, len );
12639 if( puthere ){
12640 /* remove const */
12641 *puthere = (char *)(cp);
12642 }
12643 }
12644 return r;
12645 }
12646
12647 int
12648 Jim_GetOpt_Double( Jim_GetOptInfo *goi, double *puthere )
12649 {
12650 int r;
12651 Jim_Obj *o;
12652 double _safe;
12653
12654 if( puthere == NULL ){
12655 puthere = &_safe;
12656 }
12657
12658 r = Jim_GetOpt_Obj( goi, &o );
12659 if( r == JIM_OK ){
12660 r = Jim_GetDouble( goi->interp, o, puthere );
12661 if( r != JIM_OK ){
12662 Jim_SetResult_sprintf( goi->interp,
12663 "not a number: %s",
12664 Jim_GetString( o, NULL ) );
12665 }
12666 }
12667 return r;
12668 }
12669
12670 int
12671 Jim_GetOpt_Wide( Jim_GetOptInfo *goi, jim_wide *puthere )
12672 {
12673 int r;
12674 Jim_Obj *o;
12675 jim_wide _safe;
12676
12677 if( puthere == NULL ){
12678 puthere = &_safe;
12679 }
12680
12681 r = Jim_GetOpt_Obj( goi, &o );
12682 if( r == JIM_OK ){
12683 r = Jim_GetWide( goi->interp, o, puthere );
12684 }
12685 return r;
12686 }
12687
12688 int Jim_GetOpt_Nvp( Jim_GetOptInfo *goi,
12689 const Jim_Nvp *nvp,
12690 Jim_Nvp **puthere)
12691 {
12692 Jim_Nvp *_safe;
12693 Jim_Obj *o;
12694 int e;
12695
12696 if( puthere == NULL ){
12697 puthere = &_safe;
12698 }
12699
12700 e = Jim_GetOpt_Obj( goi, &o );
12701 if( e == JIM_OK ){
12702 e = Jim_Nvp_name2value_obj( goi->interp,
12703 nvp,
12704 o,
12705 puthere );
12706 }
12707
12708 return e;
12709 }
12710
12711 void
12712 Jim_GetOpt_NvpUnknown( Jim_GetOptInfo *goi,
12713 const Jim_Nvp *nvptable,
12714 int hadprefix )
12715 {
12716 if( hadprefix ){
12717 Jim_SetResult_NvpUnknown( goi->interp,
12718 goi->argv[-2],
12719 goi->argv[-1],
12720 nvptable );
12721 } else {
12722 Jim_SetResult_NvpUnknown( goi->interp,
12723 NULL,
12724 goi->argv[-1],
12725 nvptable );
12726 }
12727 }
12728
12729
12730 int
12731 Jim_GetOpt_Enum( Jim_GetOptInfo *goi,
12732 const char * const * lookup,
12733 int *puthere)
12734 {
12735 int _safe;
12736 Jim_Obj *o;
12737 int e;
12738
12739 if( puthere == NULL ){
12740 puthere = &_safe;
12741 }
12742 e = Jim_GetOpt_Obj( goi, &o );
12743 if( e == JIM_OK ){
12744 e = Jim_GetEnum( goi->interp,
12745 o,
12746 lookup,
12747 puthere,
12748 "option",
12749 JIM_ERRMSG );
12750 }
12751 return e;
12752 }
12753
12754
12755
12756 int
12757 Jim_SetResult_sprintf( Jim_Interp *interp, const char *fmt,... )
12758 {
12759 va_list ap;
12760 char *buf;
12761
12762 va_start(ap,fmt);
12763 buf = jim_vasprintf( fmt, ap );
12764 va_end(ap);
12765 if( buf ){
12766 Jim_SetResultString( interp, buf, -1 );
12767 jim_vasprintf_done(buf);
12768 }
12769 return JIM_OK;
12770 }
12771
12772
12773 void
12774 Jim_SetResult_NvpUnknown( Jim_Interp *interp,
12775 Jim_Obj *param_name,
12776 Jim_Obj *param_value,
12777 const Jim_Nvp *nvp )
12778 {
12779 if( param_name ){
12780 Jim_SetResult_sprintf( interp,
12781 "%s: Unknown: %s, try one of: ",
12782 Jim_GetString( param_name, NULL ),
12783 Jim_GetString( param_value, NULL ) );
12784 } else {
12785 Jim_SetResult_sprintf( interp,
12786 "Unknown param: %s, try one of: ",
12787 Jim_GetString( param_value, NULL ) );
12788 }
12789 while( nvp->name ){
12790 const char *a;
12791 const char *b;
12792
12793 if( (nvp+1)->name ){
12794 a = nvp->name;
12795 b = ", ";
12796 } else {
12797 a = "or ";
12798 b = nvp->name;
12799 }
12800 Jim_AppendStrings( interp,
12801 Jim_GetResult(interp),
12802 a, b, NULL );
12803 nvp++;
12804 }
12805 }
12806
12807
12808 static Jim_Obj *debug_string_obj;
12809
12810 const char *
12811 Jim_Debug_ArgvString( Jim_Interp *interp, int argc, Jim_Obj *const *argv )
12812 {
12813 int x;
12814
12815 if( debug_string_obj ){
12816 Jim_FreeObj( interp, debug_string_obj );
12817 }
12818
12819 debug_string_obj = Jim_NewEmptyStringObj( interp );
12820 for( x = 0 ; x < argc ; x++ ){
12821 Jim_AppendStrings( interp,
12822 debug_string_obj,
12823 Jim_GetString( argv[x], NULL ),
12824 " ",
12825 NULL );
12826 }
12827
12828 return Jim_GetString( debug_string_obj, NULL );
12829 }
12830
12831
12832
12833 /*
12834 * Local Variables: ***
12835 * c-basic-offset: 4 ***
12836 * tab-width: 4 ***
12837 * End: ***
12838 */

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)