a466a08bbdebeb8d3d34ad74d7553f3d07091440
[openocd.git] / src / jim.c
1 /* Jim - A small embeddable Tcl interpreter
2 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
3 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * A copy of the license is also included in the source distribution
12 * of Jim, as a TXT file name called LICENSE.
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #define __JIM_CORE__
22 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
23
24 #ifdef __ECOS
25 #include <pkgconf/jimtcl.h>
26 #endif
27 #ifndef JIM_ANSIC
28 #define JIM_DYNLIB /* Dynamic library support for UNIX and WIN32 */
29 #endif /* JIM_ANSIC */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <ctype.h>
36 #include <limits.h>
37 #include <assert.h>
38 #include <errno.h>
39 #include <time.h>
40
41 /* Include the platform dependent libraries for
42 * dynamic loading of libraries. */
43 #ifdef JIM_DYNLIB
44 #if defined(_WIN32) || defined(WIN32)
45 #ifndef WIN32
46 #define WIN32 1
47 #endif
48 #define STRICT
49 #define WIN32_LEAN_AND_MEAN
50 #include <windows.h>
51 #if _MSC_VER >= 1000
52 #pragma warning(disable:4146)
53 #endif /* _MSC_VER */
54 #else
55 #include <dlfcn.h>
56 #endif /* WIN32 */
57 #endif /* JIM_DYNLIB */
58
59 #ifdef __ECOS
60 #include <cyg/jimtcl/jim.h>
61 #else
62 #include "jim.h"
63 #endif
64
65 #ifdef HAVE_BACKTRACE
66 #include <execinfo.h>
67 #endif
68
69 /* -----------------------------------------------------------------------------
70 * Global variables
71 * ---------------------------------------------------------------------------*/
72
73 /* A shared empty string for the objects string representation.
74 * Jim_InvalidateStringRep knows about it and don't try to free. */
75 static char *JimEmptyStringRep = (char*) "";
76
77 /* -----------------------------------------------------------------------------
78 * Required prototypes of not exported functions
79 * ---------------------------------------------------------------------------*/
80 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
81 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
82 static void JimRegisterCoreApi(Jim_Interp *interp);
83
84 static Jim_HashTableType JimVariablesHashTableType;
85
86 /* -----------------------------------------------------------------------------
87 * Utility functions
88 * ---------------------------------------------------------------------------*/
89
90 /*
91 * Convert a string to a jim_wide INTEGER.
92 * This function originates from BSD.
93 *
94 * Ignores `locale' stuff. Assumes that the upper and lower case
95 * alphabets and digits are each contiguous.
96 */
97 #ifdef HAVE_LONG_LONG
98 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
99 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
100 {
101 register const char *s;
102 register unsigned jim_wide acc;
103 register unsigned char c;
104 register unsigned jim_wide qbase, cutoff;
105 register int neg, any, cutlim;
106
107 /*
108 * Skip white space and pick up leading +/- sign if any.
109 * If base is 0, allow 0x for hex and 0 for octal, else
110 * assume decimal; if base is already 16, allow 0x.
111 */
112 s = nptr;
113 do {
114 c = *s++;
115 } while (isspace(c));
116 if (c == '-') {
117 neg = 1;
118 c = *s++;
119 } else {
120 neg = 0;
121 if (c == '+')
122 c = *s++;
123 }
124 if ((base == 0 || base == 16) &&
125 c == '0' && (*s == 'x' || *s == 'X')) {
126 c = s[1];
127 s += 2;
128 base = 16;
129 }
130 if (base == 0)
131 base = c == '0' ? 8 : 10;
132
133 /*
134 * Compute the cutoff value between legal numbers and illegal
135 * numbers. That is the largest legal value, divided by the
136 * base. An input number that is greater than this value, if
137 * followed by a legal input character, is too big. One that
138 * is equal to this value may be valid or not; the limit
139 * between valid and invalid numbers is then based on the last
140 * digit. For instance, if the range for quads is
141 * [-9223372036854775808..9223372036854775807] and the input base
142 * is 10, cutoff will be set to 922337203685477580 and cutlim to
143 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
144 * accumulated a value > 922337203685477580, or equal but the
145 * next digit is > 7 (or 8), the number is too big, and we will
146 * return a range error.
147 *
148 * Set any if any `digits' consumed; make it negative to indicate
149 * overflow.
150 */
151 qbase = (unsigned)base;
152 cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
153 : LLONG_MAX;
154 cutlim = (int)(cutoff % qbase);
155 cutoff /= qbase;
156 for (acc = 0, any = 0;; c = *s++) {
157 if (!JimIsAscii(c))
158 break;
159 if (isdigit(c))
160 c -= '0';
161 else if (isalpha(c))
162 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
163 else
164 break;
165 if (c >= base)
166 break;
167 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
168 any = -1;
169 else {
170 any = 1;
171 acc *= qbase;
172 acc += c;
173 }
174 }
175 if (any < 0) {
176 acc = neg ? LLONG_MIN : LLONG_MAX;
177 errno = ERANGE;
178 } else if (neg)
179 acc = -acc;
180 if (endptr != 0)
181 *endptr = (char *)(any ? s - 1 : nptr);
182 return (acc);
183 }
184 #endif
185
186 /* Glob-style pattern matching. */
187 static int JimStringMatch(const char *pattern, int patternLen,
188 const char *string, int stringLen, int nocase)
189 {
190 while(patternLen) {
191 switch(pattern[0]) {
192 case '*':
193 while (pattern[1] == '*') {
194 pattern++;
195 patternLen--;
196 }
197 if (patternLen == 1)
198 return 1; /* match */
199 while(stringLen) {
200 if (JimStringMatch(pattern+1, patternLen-1,
201 string, stringLen, nocase))
202 return 1; /* match */
203 string++;
204 stringLen--;
205 }
206 return 0; /* no match */
207 break;
208 case '?':
209 if (stringLen == 0)
210 return 0; /* no match */
211 string++;
212 stringLen--;
213 break;
214 case '[':
215 {
216 int not, match;
217
218 pattern++;
219 patternLen--;
220 not = pattern[0] == '^';
221 if (not) {
222 pattern++;
223 patternLen--;
224 }
225 match = 0;
226 while(1) {
227 if (pattern[0] == '\\') {
228 pattern++;
229 patternLen--;
230 if (pattern[0] == string[0])
231 match = 1;
232 } else if (pattern[0] == ']') {
233 break;
234 } else if (patternLen == 0) {
235 pattern--;
236 patternLen++;
237 break;
238 } else if (pattern[1] == '-' && patternLen >= 3) {
239 int start = pattern[0];
240 int end = pattern[2];
241 int c = string[0];
242 if (start > end) {
243 int t = start;
244 start = end;
245 end = t;
246 }
247 if (nocase) {
248 start = tolower(start);
249 end = tolower(end);
250 c = tolower(c);
251 }
252 pattern += 2;
253 patternLen -= 2;
254 if (c >= start && c <= end)
255 match = 1;
256 } else {
257 if (!nocase) {
258 if (pattern[0] == string[0])
259 match = 1;
260 } else {
261 if (tolower((int)pattern[0]) == tolower((int)string[0]))
262 match = 1;
263 }
264 }
265 pattern++;
266 patternLen--;
267 }
268 if (not)
269 match = !match;
270 if (!match)
271 return 0; /* no match */
272 string++;
273 stringLen--;
274 break;
275 }
276 case '\\':
277 if (patternLen >= 2) {
278 pattern++;
279 patternLen--;
280 }
281 /* fall through */
282 default:
283 if (!nocase) {
284 if (pattern[0] != string[0])
285 return 0; /* no match */
286 } else {
287 if (tolower((int)pattern[0]) != tolower((int)string[0]))
288 return 0; /* no match */
289 }
290 string++;
291 stringLen--;
292 break;
293 }
294 pattern++;
295 patternLen--;
296 if (stringLen == 0) {
297 while(*pattern == '*') {
298 pattern++;
299 patternLen--;
300 }
301 break;
302 }
303 }
304 if (patternLen == 0 && stringLen == 0)
305 return 1;
306 return 0;
307 }
308
309 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
310 int nocase)
311 {
312 unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
313
314 if (nocase == 0) {
315 while(l1 && l2) {
316 if (*u1 != *u2)
317 return (int)*u1-*u2;
318 u1++; u2++; l1--; l2--;
319 }
320 if (!l1 && !l2) return 0;
321 return l1-l2;
322 } else {
323 while(l1 && l2) {
324 if (tolower((int)*u1) != tolower((int)*u2))
325 return tolower((int)*u1)-tolower((int)*u2);
326 u1++; u2++; l1--; l2--;
327 }
328 if (!l1 && !l2) return 0;
329 return l1-l2;
330 }
331 }
332
333 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
334 * The index of the first occurrence of s1 in s2 is returned.
335 * If s1 is not found inside s2, -1 is returned. */
336 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
337 {
338 int i;
339
340 if (!l1 || !l2 || l1 > l2) return -1;
341 if (index < 0) index = 0;
342 s2 += index;
343 for (i = index; i <= l2-l1; i++) {
344 if (memcmp(s2, s1, l1) == 0)
345 return i;
346 s2++;
347 }
348 return -1;
349 }
350
351 int Jim_WideToString(char *buf, jim_wide wideValue)
352 {
353 const char *fmt = "%" JIM_WIDE_MODIFIER;
354 return sprintf(buf, fmt, wideValue);
355 }
356
357 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
358 {
359 char *endptr;
360
361 #ifdef HAVE_LONG_LONG
362 *widePtr = JimStrtoll(str, &endptr, base);
363 #else
364 *widePtr = strtol(str, &endptr, base);
365 #endif
366 if (str[0] == '\0')
367 return JIM_ERR;
368 if (endptr[0] != '\0') {
369 while(*endptr) {
370 if (!isspace((int)*endptr))
371 return JIM_ERR;
372 endptr++;
373 }
374 }
375 return JIM_OK;
376 }
377
378 int Jim_StringToIndex(const char *str, int *intPtr)
379 {
380 char *endptr;
381
382 *intPtr = strtol(str, &endptr, 10);
383 if (str[0] == '\0')
384 return JIM_ERR;
385 if (endptr[0] != '\0') {
386 while(*endptr) {
387 if (!isspace((int)*endptr))
388 return JIM_ERR;
389 endptr++;
390 }
391 }
392 return JIM_OK;
393 }
394
395 /* The string representation of references has two features in order
396 * to make the GC faster. The first is that every reference starts
397 * with a non common character '~', in order to make the string matching
398 * fater. The second is that the reference string rep his 32 characters
399 * in length, this allows to avoid to check every object with a string
400 * repr < 32, and usually there are many of this objects. */
401
402 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
403
404 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
405 {
406 const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
407 sprintf(buf, fmt, refPtr->tag, id);
408 return JIM_REFERENCE_SPACE;
409 }
410
411 int Jim_DoubleToString(char *buf, double doubleValue)
412 {
413 char *s;
414 int len;
415
416 len = sprintf(buf, "%.17g", doubleValue);
417 s = buf;
418 while(*s) {
419 if (*s == '.') return len;
420 s++;
421 }
422 /* Add a final ".0" if it's a number. But not
423 * for NaN or InF */
424 if (isdigit((int)buf[0])
425 || ((buf[0] == '-' || buf[0] == '+')
426 && isdigit((int)buf[1]))) {
427 s[0] = '.';
428 s[1] = '0';
429 s[2] = '\0';
430 return len+2;
431 }
432 return len;
433 }
434
435 int Jim_StringToDouble(const char *str, double *doublePtr)
436 {
437 char *endptr;
438
439 *doublePtr = strtod(str, &endptr);
440 if (str[0] == '\0' || endptr[0] != '\0')
441 return JIM_ERR;
442 return JIM_OK;
443 }
444
445 static jim_wide JimPowWide(jim_wide b, jim_wide e)
446 {
447 jim_wide i, res = 1;
448 if ((b==0 && e!=0) || (e<0)) return 0;
449 for(i=0; i<e; i++) {res *= b;}
450 return res;
451 }
452
453 /* -----------------------------------------------------------------------------
454 * Special functions
455 * ---------------------------------------------------------------------------*/
456
457 /* Note that 'interp' may be NULL if not available in the
458 * context of the panic. It's only useful to get the error
459 * file descriptor, it will default to stderr otherwise. */
460 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
461 {
462 va_list ap;
463 FILE *fp = interp ? interp->stderr_ : stderr;
464
465 va_start(ap, fmt);
466 fprintf(fp, JIM_NL "JIM INTERPRETER PANIC: ");
467 vfprintf(fp, fmt, ap);
468 fprintf(fp, JIM_NL JIM_NL);
469 va_end(ap);
470 #ifdef HAVE_BACKTRACE
471 {
472 void *array[40];
473 int size, i;
474 char **strings;
475
476 size = backtrace(array, 40);
477 strings = backtrace_symbols(array, size);
478 for (i = 0; i < size; i++)
479 fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
480 fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
481 fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
482 }
483 #endif
484 abort();
485 }
486
487 /* -----------------------------------------------------------------------------
488 * Memory allocation
489 * ---------------------------------------------------------------------------*/
490
491 /* Macro used for memory debugging.
492 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
493 * and similary for Jim_Realloc and Jim_Free */
494 #if 0
495 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
496 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
497 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
498 #endif
499
500 void *Jim_Alloc(int size)
501 {
502 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
503 if (size==0)
504 size=1;
505 void *p = malloc(size);
506 if (p == NULL)
507 Jim_Panic(NULL,"malloc: Out of memory");
508 return p;
509 }
510
511 void Jim_Free(void *ptr) {
512 free(ptr);
513 }
514
515 void *Jim_Realloc(void *ptr, int size)
516 {
517 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
518 if (size==0)
519 size=1;
520 void *p = realloc(ptr, size);
521 if (p == NULL)
522 Jim_Panic(NULL,"realloc: Out of memory");
523 return p;
524 }
525
526 char *Jim_StrDup(const char *s)
527 {
528 int l = strlen(s);
529 char *copy = Jim_Alloc(l+1);
530
531 memcpy(copy, s, l+1);
532 return copy;
533 }
534
535 char *Jim_StrDupLen(const char *s, int l)
536 {
537 char *copy = Jim_Alloc(l+1);
538
539 memcpy(copy, s, l+1);
540 copy[l] = 0; /* Just to be sure, original could be substring */
541 return copy;
542 }
543
544 /* -----------------------------------------------------------------------------
545 * Time related functions
546 * ---------------------------------------------------------------------------*/
547 /* Returns microseconds of CPU used since start. */
548 static jim_wide JimClock(void)
549 {
550 #if (defined WIN32) && !(defined JIM_ANSIC)
551 LARGE_INTEGER t, f;
552 QueryPerformanceFrequency(&f);
553 QueryPerformanceCounter(&t);
554 return (long)((t.QuadPart * 1000000) / f.QuadPart);
555 #else /* !WIN32 */
556 clock_t clocks = clock();
557
558 return (long)(clocks*(1000000/CLOCKS_PER_SEC));
559 #endif /* WIN32 */
560 }
561
562 /* -----------------------------------------------------------------------------
563 * Hash Tables
564 * ---------------------------------------------------------------------------*/
565
566 /* -------------------------- private prototypes ---------------------------- */
567 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
568 static unsigned int JimHashTableNextPower(unsigned int size);
569 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
570
571 /* -------------------------- hash functions -------------------------------- */
572
573 /* Thomas Wang's 32 bit Mix Function */
574 unsigned int Jim_IntHashFunction(unsigned int key)
575 {
576 key += ~(key << 15);
577 key ^= (key >> 10);
578 key += (key << 3);
579 key ^= (key >> 6);
580 key += ~(key << 11);
581 key ^= (key >> 16);
582 return key;
583 }
584
585 /* Identity hash function for integer keys */
586 unsigned int Jim_IdentityHashFunction(unsigned int key)
587 {
588 return key;
589 }
590
591 /* Generic hash function (we are using to multiply by 9 and add the byte
592 * as Tcl) */
593 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
594 {
595 unsigned int h = 0;
596 while(len--)
597 h += (h<<3)+*buf++;
598 return h;
599 }
600
601 /* ----------------------------- API implementation ------------------------- */
602 /* reset an hashtable already initialized with ht_init().
603 * NOTE: This function should only called by ht_destroy(). */
604 static void JimResetHashTable(Jim_HashTable *ht)
605 {
606 ht->table = NULL;
607 ht->size = 0;
608 ht->sizemask = 0;
609 ht->used = 0;
610 ht->collisions = 0;
611 }
612
613 /* Initialize the hash table */
614 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
615 void *privDataPtr)
616 {
617 JimResetHashTable(ht);
618 ht->type = type;
619 ht->privdata = privDataPtr;
620 return JIM_OK;
621 }
622
623 /* Resize the table to the minimal size that contains all the elements,
624 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
625 int Jim_ResizeHashTable(Jim_HashTable *ht)
626 {
627 int minimal = ht->used;
628
629 if (minimal < JIM_HT_INITIAL_SIZE)
630 minimal = JIM_HT_INITIAL_SIZE;
631 return Jim_ExpandHashTable(ht, minimal);
632 }
633
634 /* Expand or create the hashtable */
635 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
636 {
637 Jim_HashTable n; /* the new hashtable */
638 unsigned int realsize = JimHashTableNextPower(size), i;
639
640 /* the size is invalid if it is smaller than the number of
641 * elements already inside the hashtable */
642 if (ht->used >= size)
643 return JIM_ERR;
644
645 Jim_InitHashTable(&n, ht->type, ht->privdata);
646 n.size = realsize;
647 n.sizemask = realsize-1;
648 n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
649
650 /* Initialize all the pointers to NULL */
651 memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
652
653 /* Copy all the elements from the old to the new table:
654 * note that if the old hash table is empty ht->size is zero,
655 * so Jim_ExpandHashTable just creates an hash table. */
656 n.used = ht->used;
657 for (i = 0; i < ht->size && ht->used > 0; i++) {
658 Jim_HashEntry *he, *nextHe;
659
660 if (ht->table[i] == NULL) continue;
661
662 /* For each hash entry on this slot... */
663 he = ht->table[i];
664 while(he) {
665 unsigned int h;
666
667 nextHe = he->next;
668 /* Get the new element index */
669 h = Jim_HashKey(ht, he->key) & n.sizemask;
670 he->next = n.table[h];
671 n.table[h] = he;
672 ht->used--;
673 /* Pass to the next element */
674 he = nextHe;
675 }
676 }
677 assert(ht->used == 0);
678 Jim_Free(ht->table);
679
680 /* Remap the new hashtable in the old */
681 *ht = n;
682 return JIM_OK;
683 }
684
685 /* Add an element to the target hash table */
686 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
687 {
688 int index;
689 Jim_HashEntry *entry;
690
691 /* Get the index of the new element, or -1 if
692 * the element already exists. */
693 if ((index = JimInsertHashEntry(ht, key)) == -1)
694 return JIM_ERR;
695
696 /* Allocates the memory and stores key */
697 entry = Jim_Alloc(sizeof(*entry));
698 entry->next = ht->table[index];
699 ht->table[index] = entry;
700
701 /* Set the hash entry fields. */
702 Jim_SetHashKey(ht, entry, key);
703 Jim_SetHashVal(ht, entry, val);
704 ht->used++;
705 return JIM_OK;
706 }
707
708 /* Add an element, discarding the old if the key already exists */
709 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
710 {
711 Jim_HashEntry *entry;
712
713 /* Try to add the element. If the key
714 * does not exists Jim_AddHashEntry will suceed. */
715 if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
716 return JIM_OK;
717 /* It already exists, get the entry */
718 entry = Jim_FindHashEntry(ht, key);
719 /* Free the old value and set the new one */
720 Jim_FreeEntryVal(ht, entry);
721 Jim_SetHashVal(ht, entry, val);
722 return JIM_OK;
723 }
724
725 /* Search and remove an element */
726 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
727 {
728 unsigned int h;
729 Jim_HashEntry *he, *prevHe;
730
731 if (ht->size == 0)
732 return JIM_ERR;
733 h = Jim_HashKey(ht, key) & ht->sizemask;
734 he = ht->table[h];
735
736 prevHe = NULL;
737 while(he) {
738 if (Jim_CompareHashKeys(ht, key, he->key)) {
739 /* Unlink the element from the list */
740 if (prevHe)
741 prevHe->next = he->next;
742 else
743 ht->table[h] = he->next;
744 Jim_FreeEntryKey(ht, he);
745 Jim_FreeEntryVal(ht, he);
746 Jim_Free(he);
747 ht->used--;
748 return JIM_OK;
749 }
750 prevHe = he;
751 he = he->next;
752 }
753 return JIM_ERR; /* not found */
754 }
755
756 /* Destroy an entire hash table */
757 int Jim_FreeHashTable(Jim_HashTable *ht)
758 {
759 unsigned int i;
760
761 /* Free all the elements */
762 for (i = 0; i < ht->size && ht->used > 0; i++) {
763 Jim_HashEntry *he, *nextHe;
764
765 if ((he = ht->table[i]) == NULL) continue;
766 while(he) {
767 nextHe = he->next;
768 Jim_FreeEntryKey(ht, he);
769 Jim_FreeEntryVal(ht, he);
770 Jim_Free(he);
771 ht->used--;
772 he = nextHe;
773 }
774 }
775 /* Free the table and the allocated cache structure */
776 Jim_Free(ht->table);
777 /* Re-initialize the table */
778 JimResetHashTable(ht);
779 return JIM_OK; /* never fails */
780 }
781
782 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
783 {
784 Jim_HashEntry *he;
785 unsigned int h;
786
787 if (ht->size == 0) return NULL;
788 h = Jim_HashKey(ht, key) & ht->sizemask;
789 he = ht->table[h];
790 while(he) {
791 if (Jim_CompareHashKeys(ht, key, he->key))
792 return he;
793 he = he->next;
794 }
795 return NULL;
796 }
797
798 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
799 {
800 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
801
802 iter->ht = ht;
803 iter->index = -1;
804 iter->entry = NULL;
805 iter->nextEntry = NULL;
806 return iter;
807 }
808
809 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
810 {
811 while (1) {
812 if (iter->entry == NULL) {
813 iter->index++;
814 if (iter->index >=
815 (signed)iter->ht->size) break;
816 iter->entry = iter->ht->table[iter->index];
817 } else {
818 iter->entry = iter->nextEntry;
819 }
820 if (iter->entry) {
821 /* We need to save the 'next' here, the iterator user
822 * may delete the entry we are returning. */
823 iter->nextEntry = iter->entry->next;
824 return iter->entry;
825 }
826 }
827 return NULL;
828 }
829
830 /* ------------------------- private functions ------------------------------ */
831
832 /* Expand the hash table if needed */
833 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
834 {
835 /* If the hash table is empty expand it to the intial size,
836 * if the table is "full" dobule its size. */
837 if (ht->size == 0)
838 return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
839 if (ht->size == ht->used)
840 return Jim_ExpandHashTable(ht, ht->size*2);
841 return JIM_OK;
842 }
843
844 /* Our hash table capability is a power of two */
845 static unsigned int JimHashTableNextPower(unsigned int size)
846 {
847 unsigned int i = JIM_HT_INITIAL_SIZE;
848
849 if (size >= 2147483648U)
850 return 2147483648U;
851 while(1) {
852 if (i >= size)
853 return i;
854 i *= 2;
855 }
856 }
857
858 /* Returns the index of a free slot that can be populated with
859 * an hash entry for the given 'key'.
860 * If the key already exists, -1 is returned. */
861 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
862 {
863 unsigned int h;
864 Jim_HashEntry *he;
865
866 /* Expand the hashtable if needed */
867 if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
868 return -1;
869 /* Compute the key hash value */
870 h = Jim_HashKey(ht, key) & ht->sizemask;
871 /* Search if this slot does not already contain the given key */
872 he = ht->table[h];
873 while(he) {
874 if (Jim_CompareHashKeys(ht, key, he->key))
875 return -1;
876 he = he->next;
877 }
878 return h;
879 }
880
881 /* ----------------------- StringCopy Hash Table Type ------------------------*/
882
883 static unsigned int JimStringCopyHTHashFunction(const void *key)
884 {
885 return Jim_GenHashFunction(key, strlen(key));
886 }
887
888 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
889 {
890 int len = strlen(key);
891 char *copy = Jim_Alloc(len+1);
892 JIM_NOTUSED(privdata);
893
894 memcpy(copy, key, len);
895 copy[len] = '\0';
896 return copy;
897 }
898
899 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
900 {
901 int len = strlen(val);
902 char *copy = Jim_Alloc(len+1);
903 JIM_NOTUSED(privdata);
904
905 memcpy(copy, val, len);
906 copy[len] = '\0';
907 return copy;
908 }
909
910 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
911 const void *key2)
912 {
913 JIM_NOTUSED(privdata);
914
915 return strcmp(key1, key2) == 0;
916 }
917
918 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
919 {
920 JIM_NOTUSED(privdata);
921
922 Jim_Free((void*)key); /* ATTENTION: const cast */
923 }
924
925 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
926 {
927 JIM_NOTUSED(privdata);
928
929 Jim_Free((void*)val); /* ATTENTION: const cast */
930 }
931
932 static Jim_HashTableType JimStringCopyHashTableType = {
933 JimStringCopyHTHashFunction, /* hash function */
934 JimStringCopyHTKeyDup, /* key dup */
935 NULL, /* val dup */
936 JimStringCopyHTKeyCompare, /* key compare */
937 JimStringCopyHTKeyDestructor, /* key destructor */
938 NULL /* val destructor */
939 };
940
941 /* This is like StringCopy but does not auto-duplicate the key.
942 * It's used for intepreter's shared strings. */
943 static Jim_HashTableType JimSharedStringsHashTableType = {
944 JimStringCopyHTHashFunction, /* hash function */
945 NULL, /* key dup */
946 NULL, /* val dup */
947 JimStringCopyHTKeyCompare, /* key compare */
948 JimStringCopyHTKeyDestructor, /* key destructor */
949 NULL /* val destructor */
950 };
951
952 /* This is like StringCopy but also automatically handle dynamic
953 * allocated C strings as values. */
954 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
955 JimStringCopyHTHashFunction, /* hash function */
956 JimStringCopyHTKeyDup, /* key dup */
957 JimStringKeyValCopyHTValDup, /* val dup */
958 JimStringCopyHTKeyCompare, /* key compare */
959 JimStringCopyHTKeyDestructor, /* key destructor */
960 JimStringKeyValCopyHTValDestructor, /* val destructor */
961 };
962
963 typedef struct AssocDataValue {
964 Jim_InterpDeleteProc *delProc;
965 void *data;
966 } AssocDataValue;
967
968 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
969 {
970 AssocDataValue *assocPtr = (AssocDataValue *)data;
971 if (assocPtr->delProc != NULL)
972 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
973 Jim_Free(data);
974 }
975
976 static Jim_HashTableType JimAssocDataHashTableType = {
977 JimStringCopyHTHashFunction, /* hash function */
978 JimStringCopyHTKeyDup, /* key dup */
979 NULL, /* val dup */
980 JimStringCopyHTKeyCompare, /* key compare */
981 JimStringCopyHTKeyDestructor, /* key destructor */
982 JimAssocDataHashTableValueDestructor /* val destructor */
983 };
984
985 /* -----------------------------------------------------------------------------
986 * Stack - This is a simple generic stack implementation. It is used for
987 * example in the 'expr' expression compiler.
988 * ---------------------------------------------------------------------------*/
989 void Jim_InitStack(Jim_Stack *stack)
990 {
991 stack->len = 0;
992 stack->maxlen = 0;
993 stack->vector = NULL;
994 }
995
996 void Jim_FreeStack(Jim_Stack *stack)
997 {
998 Jim_Free(stack->vector);
999 }
1000
1001 int Jim_StackLen(Jim_Stack *stack)
1002 {
1003 return stack->len;
1004 }
1005
1006 void Jim_StackPush(Jim_Stack *stack, void *element) {
1007 int neededLen = stack->len+1;
1008 if (neededLen > stack->maxlen) {
1009 stack->maxlen = neededLen*2;
1010 stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1011 }
1012 stack->vector[stack->len] = element;
1013 stack->len++;
1014 }
1015
1016 void *Jim_StackPop(Jim_Stack *stack)
1017 {
1018 if (stack->len == 0) return NULL;
1019 stack->len--;
1020 return stack->vector[stack->len];
1021 }
1022
1023 void *Jim_StackPeek(Jim_Stack *stack)
1024 {
1025 if (stack->len == 0) return NULL;
1026 return stack->vector[stack->len-1];
1027 }
1028
1029 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1030 {
1031 int i;
1032
1033 for (i = 0; i < stack->len; i++)
1034 freeFunc(stack->vector[i]);
1035 }
1036
1037 /* -----------------------------------------------------------------------------
1038 * Parser
1039 * ---------------------------------------------------------------------------*/
1040
1041 /* Token types */
1042 #define JIM_TT_NONE -1 /* No token returned */
1043 #define JIM_TT_STR 0 /* simple string */
1044 #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
1045 #define JIM_TT_VAR 2 /* var substitution */
1046 #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
1047 #define JIM_TT_CMD 4 /* command substitution */
1048 #define JIM_TT_SEP 5 /* word separator */
1049 #define JIM_TT_EOL 6 /* line separator */
1050
1051 /* Additional token types needed for expressions */
1052 #define JIM_TT_SUBEXPR_START 7
1053 #define JIM_TT_SUBEXPR_END 8
1054 #define JIM_TT_EXPR_NUMBER 9
1055 #define JIM_TT_EXPR_OPERATOR 10
1056
1057 /* Parser states */
1058 #define JIM_PS_DEF 0 /* Default state */
1059 #define JIM_PS_QUOTE 1 /* Inside "" */
1060
1061 /* Parser context structure. The same context is used both to parse
1062 * Tcl scripts and lists. */
1063 struct JimParserCtx {
1064 const char *prg; /* Program text */
1065 const char *p; /* Pointer to the point of the program we are parsing */
1066 int len; /* Left length of 'prg' */
1067 int linenr; /* Current line number */
1068 const char *tstart;
1069 const char *tend; /* Returned token is at tstart-tend in 'prg'. */
1070 int tline; /* Line number of the returned token */
1071 int tt; /* Token type */
1072 int eof; /* Non zero if EOF condition is true. */
1073 int state; /* Parser state */
1074 int comment; /* Non zero if the next chars may be a comment. */
1075 };
1076
1077 #define JimParserEof(c) ((c)->eof)
1078 #define JimParserTstart(c) ((c)->tstart)
1079 #define JimParserTend(c) ((c)->tend)
1080 #define JimParserTtype(c) ((c)->tt)
1081 #define JimParserTline(c) ((c)->tline)
1082
1083 static int JimParseScript(struct JimParserCtx *pc);
1084 static int JimParseSep(struct JimParserCtx *pc);
1085 static int JimParseEol(struct JimParserCtx *pc);
1086 static int JimParseCmd(struct JimParserCtx *pc);
1087 static int JimParseVar(struct JimParserCtx *pc);
1088 static int JimParseBrace(struct JimParserCtx *pc);
1089 static int JimParseStr(struct JimParserCtx *pc);
1090 static int JimParseComment(struct JimParserCtx *pc);
1091 static char *JimParserGetToken(struct JimParserCtx *pc,
1092 int *lenPtr, int *typePtr, int *linePtr);
1093
1094 /* Initialize a parser context.
1095 * 'prg' is a pointer to the program text, linenr is the line
1096 * number of the first line contained in the program. */
1097 void JimParserInit(struct JimParserCtx *pc, const char *prg,
1098 int len, int linenr)
1099 {
1100 pc->prg = prg;
1101 pc->p = prg;
1102 pc->len = len;
1103 pc->tstart = NULL;
1104 pc->tend = NULL;
1105 pc->tline = 0;
1106 pc->tt = JIM_TT_NONE;
1107 pc->eof = 0;
1108 pc->state = JIM_PS_DEF;
1109 pc->linenr = linenr;
1110 pc->comment = 1;
1111 }
1112
1113 int JimParseScript(struct JimParserCtx *pc)
1114 {
1115 while(1) { /* the while is used to reiterate with continue if needed */
1116 if (!pc->len) {
1117 pc->tstart = pc->p;
1118 pc->tend = pc->p-1;
1119 pc->tline = pc->linenr;
1120 pc->tt = JIM_TT_EOL;
1121 pc->eof = 1;
1122 return JIM_OK;
1123 }
1124 switch(*(pc->p)) {
1125 case '\\':
1126 if (*(pc->p+1) == '\n')
1127 return JimParseSep(pc);
1128 else {
1129 pc->comment = 0;
1130 return JimParseStr(pc);
1131 }
1132 break;
1133 case ' ':
1134 case '\t':
1135 case '\r':
1136 if (pc->state == JIM_PS_DEF)
1137 return JimParseSep(pc);
1138 else {
1139 pc->comment = 0;
1140 return JimParseStr(pc);
1141 }
1142 break;
1143 case '\n':
1144 case ';':
1145 pc->comment = 1;
1146 if (pc->state == JIM_PS_DEF)
1147 return JimParseEol(pc);
1148 else
1149 return JimParseStr(pc);
1150 break;
1151 case '[':
1152 pc->comment = 0;
1153 return JimParseCmd(pc);
1154 break;
1155 case '$':
1156 pc->comment = 0;
1157 if (JimParseVar(pc) == JIM_ERR) {
1158 pc->tstart = pc->tend = pc->p++; pc->len--;
1159 pc->tline = pc->linenr;
1160 pc->tt = JIM_TT_STR;
1161 return JIM_OK;
1162 } else
1163 return JIM_OK;
1164 break;
1165 case '#':
1166 if (pc->comment) {
1167 JimParseComment(pc);
1168 continue;
1169 } else {
1170 return JimParseStr(pc);
1171 }
1172 default:
1173 pc->comment = 0;
1174 return JimParseStr(pc);
1175 break;
1176 }
1177 return JIM_OK;
1178 }
1179 }
1180
1181 int JimParseSep(struct JimParserCtx *pc)
1182 {
1183 pc->tstart = pc->p;
1184 pc->tline = pc->linenr;
1185 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1186 (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1187 if (*pc->p == '\\') {
1188 pc->p++; pc->len--;
1189 pc->linenr++;
1190 }
1191 pc->p++; pc->len--;
1192 }
1193 pc->tend = pc->p-1;
1194 pc->tt = JIM_TT_SEP;
1195 return JIM_OK;
1196 }
1197
1198 int JimParseEol(struct JimParserCtx *pc)
1199 {
1200 pc->tstart = pc->p;
1201 pc->tline = pc->linenr;
1202 while (*pc->p == ' ' || *pc->p == '\n' ||
1203 *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1204 if (*pc->p == '\n')
1205 pc->linenr++;
1206 pc->p++; pc->len--;
1207 }
1208 pc->tend = pc->p-1;
1209 pc->tt = JIM_TT_EOL;
1210 return JIM_OK;
1211 }
1212
1213 /* Todo. Don't stop if ']' appears inside {} or quoted.
1214 * Also should handle the case of puts [string length "]"] */
1215 int JimParseCmd(struct JimParserCtx *pc)
1216 {
1217 int level = 1;
1218 int blevel = 0;
1219
1220 pc->tstart = ++pc->p; pc->len--;
1221 pc->tline = pc->linenr;
1222 while (1) {
1223 if (pc->len == 0) {
1224 break;
1225 } else if (*pc->p == '[' && blevel == 0) {
1226 level++;
1227 } else if (*pc->p == ']' && blevel == 0) {
1228 level--;
1229 if (!level) break;
1230 } else if (*pc->p == '\\') {
1231 pc->p++; pc->len--;
1232 } else if (*pc->p == '{') {
1233 blevel++;
1234 } else if (*pc->p == '}') {
1235 if (blevel != 0)
1236 blevel--;
1237 } else if (*pc->p == '\n')
1238 pc->linenr++;
1239 pc->p++; pc->len--;
1240 }
1241 pc->tend = pc->p-1;
1242 pc->tt = JIM_TT_CMD;
1243 if (*pc->p == ']') {
1244 pc->p++; pc->len--;
1245 }
1246 return JIM_OK;
1247 }
1248
1249 int JimParseVar(struct JimParserCtx *pc)
1250 {
1251 int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1252
1253 pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1254 pc->tline = pc->linenr;
1255 if (*pc->p == '{') {
1256 pc->tstart = ++pc->p; pc->len--;
1257 brace = 1;
1258 }
1259 if (brace) {
1260 while (!stop) {
1261 if (*pc->p == '}' || pc->len == 0) {
1262 stop = 1;
1263 if (pc->len == 0)
1264 continue;
1265 }
1266 else if (*pc->p == '\n')
1267 pc->linenr++;
1268 pc->p++; pc->len--;
1269 }
1270 if (pc->len == 0)
1271 pc->tend = pc->p-1;
1272 else
1273 pc->tend = pc->p-2;
1274 } else {
1275 while (!stop) {
1276 if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1277 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1278 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1279 stop = 1;
1280 else {
1281 pc->p++; pc->len--;
1282 }
1283 }
1284 /* Parse [dict get] syntax sugar. */
1285 if (*pc->p == '(') {
1286 while (*pc->p != ')' && pc->len) {
1287 pc->p++; pc->len--;
1288 if (*pc->p == '\\' && pc->len >= 2) {
1289 pc->p += 2; pc->len -= 2;
1290 }
1291 }
1292 if (*pc->p != '\0') {
1293 pc->p++; pc->len--;
1294 }
1295 ttype = JIM_TT_DICTSUGAR;
1296 }
1297 pc->tend = pc->p-1;
1298 }
1299 /* Check if we parsed just the '$' character.
1300 * That's not a variable so an error is returned
1301 * to tell the state machine to consider this '$' just
1302 * a string. */
1303 if (pc->tstart == pc->p) {
1304 pc->p--; pc->len++;
1305 return JIM_ERR;
1306 }
1307 pc->tt = ttype;
1308 return JIM_OK;
1309 }
1310
1311 int JimParseBrace(struct JimParserCtx *pc)
1312 {
1313 int level = 1;
1314
1315 pc->tstart = ++pc->p; pc->len--;
1316 pc->tline = pc->linenr;
1317 while (1) {
1318 if (*pc->p == '\\' && pc->len >= 2) {
1319 pc->p++; pc->len--;
1320 if (*pc->p == '\n')
1321 pc->linenr++;
1322 } else if (*pc->p == '{') {
1323 level++;
1324 } else if (pc->len == 0 || *pc->p == '}') {
1325 level--;
1326 if (pc->len == 0 || level == 0) {
1327 pc->tend = pc->p-1;
1328 if (pc->len != 0) {
1329 pc->p++; pc->len--;
1330 }
1331 pc->tt = JIM_TT_STR;
1332 return JIM_OK;
1333 }
1334 } else if (*pc->p == '\n') {
1335 pc->linenr++;
1336 }
1337 pc->p++; pc->len--;
1338 }
1339 return JIM_OK; /* unreached */
1340 }
1341
1342 int JimParseStr(struct JimParserCtx *pc)
1343 {
1344 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1345 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1346 if (newword && *pc->p == '{') {
1347 return JimParseBrace(pc);
1348 } else if (newword && *pc->p == '"') {
1349 pc->state = JIM_PS_QUOTE;
1350 pc->p++; pc->len--;
1351 }
1352 pc->tstart = pc->p;
1353 pc->tline = pc->linenr;
1354 while (1) {
1355 if (pc->len == 0) {
1356 pc->tend = pc->p-1;
1357 pc->tt = JIM_TT_ESC;
1358 return JIM_OK;
1359 }
1360 switch(*pc->p) {
1361 case '\\':
1362 if (pc->state == JIM_PS_DEF &&
1363 *(pc->p+1) == '\n') {
1364 pc->tend = pc->p-1;
1365 pc->tt = JIM_TT_ESC;
1366 return JIM_OK;
1367 }
1368 if (pc->len >= 2) {
1369 pc->p++; pc->len--;
1370 }
1371 break;
1372 case '$':
1373 case '[':
1374 pc->tend = pc->p-1;
1375 pc->tt = JIM_TT_ESC;
1376 return JIM_OK;
1377 case ' ':
1378 case '\t':
1379 case '\n':
1380 case '\r':
1381 case ';':
1382 if (pc->state == JIM_PS_DEF) {
1383 pc->tend = pc->p-1;
1384 pc->tt = JIM_TT_ESC;
1385 return JIM_OK;
1386 } else if (*pc->p == '\n') {
1387 pc->linenr++;
1388 }
1389 break;
1390 case '"':
1391 if (pc->state == JIM_PS_QUOTE) {
1392 pc->tend = pc->p-1;
1393 pc->tt = JIM_TT_ESC;
1394 pc->p++; pc->len--;
1395 pc->state = JIM_PS_DEF;
1396 return JIM_OK;
1397 }
1398 break;
1399 }
1400 pc->p++; pc->len--;
1401 }
1402 return JIM_OK; /* unreached */
1403 }
1404
1405 int JimParseComment(struct JimParserCtx *pc)
1406 {
1407 while (*pc->p) {
1408 if (*pc->p == '\n') {
1409 pc->linenr++;
1410 if (*(pc->p-1) != '\\') {
1411 pc->p++; pc->len--;
1412 return JIM_OK;
1413 }
1414 }
1415 pc->p++; pc->len--;
1416 }
1417 return JIM_OK;
1418 }
1419
1420 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1421 static int xdigitval(int c)
1422 {
1423 if (c >= '0' && c <= '9') return c-'0';
1424 if (c >= 'a' && c <= 'f') return c-'a'+10;
1425 if (c >= 'A' && c <= 'F') return c-'A'+10;
1426 return -1;
1427 }
1428
1429 static int odigitval(int c)
1430 {
1431 if (c >= '0' && c <= '7') return c-'0';
1432 return -1;
1433 }
1434
1435 /* Perform Tcl escape substitution of 's', storing the result
1436 * string into 'dest'. The escaped string is guaranteed to
1437 * be the same length or shorted than the source string.
1438 * Slen is the length of the string at 's', if it's -1 the string
1439 * length will be calculated by the function.
1440 *
1441 * The function returns the length of the resulting string. */
1442 static int JimEscape(char *dest, const char *s, int slen)
1443 {
1444 char *p = dest;
1445 int i, len;
1446
1447 if (slen == -1)
1448 slen = strlen(s);
1449
1450 for (i = 0; i < slen; i++) {
1451 switch(s[i]) {
1452 case '\\':
1453 switch(s[i+1]) {
1454 case 'a': *p++ = 0x7; i++; break;
1455 case 'b': *p++ = 0x8; i++; break;
1456 case 'f': *p++ = 0xc; i++; break;
1457 case 'n': *p++ = 0xa; i++; break;
1458 case 'r': *p++ = 0xd; i++; break;
1459 case 't': *p++ = 0x9; i++; break;
1460 case 'v': *p++ = 0xb; i++; break;
1461 case '\0': *p++ = '\\'; i++; break;
1462 case '\n': *p++ = ' '; i++; break;
1463 default:
1464 if (s[i+1] == 'x') {
1465 int val = 0;
1466 int c = xdigitval(s[i+2]);
1467 if (c == -1) {
1468 *p++ = 'x';
1469 i++;
1470 break;
1471 }
1472 val = c;
1473 c = xdigitval(s[i+3]);
1474 if (c == -1) {
1475 *p++ = val;
1476 i += 2;
1477 break;
1478 }
1479 val = (val*16)+c;
1480 *p++ = val;
1481 i += 3;
1482 break;
1483 } else if (s[i+1] >= '0' && s[i+1] <= '7')
1484 {
1485 int val = 0;
1486 int c = odigitval(s[i+1]);
1487 val = c;
1488 c = odigitval(s[i+2]);
1489 if (c == -1) {
1490 *p++ = val;
1491 i ++;
1492 break;
1493 }
1494 val = (val*8)+c;
1495 c = odigitval(s[i+3]);
1496 if (c == -1) {
1497 *p++ = val;
1498 i += 2;
1499 break;
1500 }
1501 val = (val*8)+c;
1502 *p++ = val;
1503 i += 3;
1504 } else {
1505 *p++ = s[i+1];
1506 i++;
1507 }
1508 break;
1509 }
1510 break;
1511 default:
1512 *p++ = s[i];
1513 break;
1514 }
1515 }
1516 len = p-dest;
1517 *p++ = '\0';
1518 return len;
1519 }
1520
1521 /* Returns a dynamically allocated copy of the current token in the
1522 * parser context. The function perform conversion of escapes if
1523 * the token is of type JIM_TT_ESC.
1524 *
1525 * Note that after the conversion, tokens that are grouped with
1526 * braces in the source code, are always recognizable from the
1527 * identical string obtained in a different way from the type.
1528 *
1529 * For exmple the string:
1530 *
1531 * {expand}$a
1532 *
1533 * will return as first token "expand", of type JIM_TT_STR
1534 *
1535 * While the string:
1536 *
1537 * expand$a
1538 *
1539 * will return as first token "expand", of type JIM_TT_ESC
1540 */
1541 char *JimParserGetToken(struct JimParserCtx *pc,
1542 int *lenPtr, int *typePtr, int *linePtr)
1543 {
1544 const char *start, *end;
1545 char *token;
1546 int len;
1547
1548 start = JimParserTstart(pc);
1549 end = JimParserTend(pc);
1550 if (start > end) {
1551 if (lenPtr) *lenPtr = 0;
1552 if (typePtr) *typePtr = JimParserTtype(pc);
1553 if (linePtr) *linePtr = JimParserTline(pc);
1554 token = Jim_Alloc(1);
1555 token[0] = '\0';
1556 return token;
1557 }
1558 len = (end-start)+1;
1559 token = Jim_Alloc(len+1);
1560 if (JimParserTtype(pc) != JIM_TT_ESC) {
1561 /* No escape conversion needed? Just copy it. */
1562 memcpy(token, start, len);
1563 token[len] = '\0';
1564 } else {
1565 /* Else convert the escape chars. */
1566 len = JimEscape(token, start, len);
1567 }
1568 if (lenPtr) *lenPtr = len;
1569 if (typePtr) *typePtr = JimParserTtype(pc);
1570 if (linePtr) *linePtr = JimParserTline(pc);
1571 return token;
1572 }
1573
1574 /* The following functin is not really part of the parsing engine of Jim,
1575 * but it somewhat related. Given an string and its length, it tries
1576 * to guess if the script is complete or there are instead " " or { }
1577 * open and not completed. This is useful for interactive shells
1578 * implementation and for [info complete].
1579 *
1580 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1581 * '{' on scripts incomplete missing one or more '}' to be balanced.
1582 * '"' on scripts incomplete missing a '"' char.
1583 *
1584 * If the script is complete, 1 is returned, otherwise 0. */
1585 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1586 {
1587 int level = 0;
1588 int state = ' ';
1589
1590 while(len) {
1591 switch (*s) {
1592 case '\\':
1593 if (len > 1)
1594 s++;
1595 break;
1596 case '"':
1597 if (state == ' ') {
1598 state = '"';
1599 } else if (state == '"') {
1600 state = ' ';
1601 }
1602 break;
1603 case '{':
1604 if (state == '{') {
1605 level++;
1606 } else if (state == ' ') {
1607 state = '{';
1608 level++;
1609 }
1610 break;
1611 case '}':
1612 if (state == '{') {
1613 level--;
1614 if (level == 0)
1615 state = ' ';
1616 }
1617 break;
1618 }
1619 s++;
1620 len--;
1621 }
1622 if (stateCharPtr)
1623 *stateCharPtr = state;
1624 return state == ' ';
1625 }
1626
1627 /* -----------------------------------------------------------------------------
1628 * Tcl Lists parsing
1629 * ---------------------------------------------------------------------------*/
1630 static int JimParseListSep(struct JimParserCtx *pc);
1631 static int JimParseListStr(struct JimParserCtx *pc);
1632
1633 int JimParseList(struct JimParserCtx *pc)
1634 {
1635 if (pc->len == 0) {
1636 pc->tstart = pc->tend = pc->p;
1637 pc->tline = pc->linenr;
1638 pc->tt = JIM_TT_EOL;
1639 pc->eof = 1;
1640 return JIM_OK;
1641 }
1642 switch(*pc->p) {
1643 case ' ':
1644 case '\n':
1645 case '\t':
1646 case '\r':
1647 if (pc->state == JIM_PS_DEF)
1648 return JimParseListSep(pc);
1649 else
1650 return JimParseListStr(pc);
1651 break;
1652 default:
1653 return JimParseListStr(pc);
1654 break;
1655 }
1656 return JIM_OK;
1657 }
1658
1659 int JimParseListSep(struct JimParserCtx *pc)
1660 {
1661 pc->tstart = pc->p;
1662 pc->tline = pc->linenr;
1663 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1664 {
1665 pc->p++; pc->len--;
1666 }
1667 pc->tend = pc->p-1;
1668 pc->tt = JIM_TT_SEP;
1669 return JIM_OK;
1670 }
1671
1672 int JimParseListStr(struct JimParserCtx *pc)
1673 {
1674 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1675 pc->tt == JIM_TT_NONE);
1676 if (newword && *pc->p == '{') {
1677 return JimParseBrace(pc);
1678 } else if (newword && *pc->p == '"') {
1679 pc->state = JIM_PS_QUOTE;
1680 pc->p++; pc->len--;
1681 }
1682 pc->tstart = pc->p;
1683 pc->tline = pc->linenr;
1684 while (1) {
1685 if (pc->len == 0) {
1686 pc->tend = pc->p-1;
1687 pc->tt = JIM_TT_ESC;
1688 return JIM_OK;
1689 }
1690 switch(*pc->p) {
1691 case '\\':
1692 pc->p++; pc->len--;
1693 break;
1694 case ' ':
1695 case '\t':
1696 case '\n':
1697 case '\r':
1698 if (pc->state == JIM_PS_DEF) {
1699 pc->tend = pc->p-1;
1700 pc->tt = JIM_TT_ESC;
1701 return JIM_OK;
1702 } else if (*pc->p == '\n') {
1703 pc->linenr++;
1704 }
1705 break;
1706 case '"':
1707 if (pc->state == JIM_PS_QUOTE) {
1708 pc->tend = pc->p-1;
1709 pc->tt = JIM_TT_ESC;
1710 pc->p++; pc->len--;
1711 pc->state = JIM_PS_DEF;
1712 return JIM_OK;
1713 }
1714 break;
1715 }
1716 pc->p++; pc->len--;
1717 }
1718 return JIM_OK; /* unreached */
1719 }
1720
1721 /* -----------------------------------------------------------------------------
1722 * Jim_Obj related functions
1723 * ---------------------------------------------------------------------------*/
1724
1725 /* Return a new initialized object. */
1726 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1727 {
1728 Jim_Obj *objPtr;
1729
1730 /* -- Check if there are objects in the free list -- */
1731 if (interp->freeList != NULL) {
1732 /* -- Unlink the object from the free list -- */
1733 objPtr = interp->freeList;
1734 interp->freeList = objPtr->nextObjPtr;
1735 } else {
1736 /* -- No ready to use objects: allocate a new one -- */
1737 objPtr = Jim_Alloc(sizeof(*objPtr));
1738 }
1739
1740 /* Object is returned with refCount of 0. Every
1741 * kind of GC implemented should take care to don't try
1742 * to scan objects with refCount == 0. */
1743 objPtr->refCount = 0;
1744 /* All the other fields are left not initialized to save time.
1745 * The caller will probably want set they to the right
1746 * value anyway. */
1747
1748 /* -- Put the object into the live list -- */
1749 objPtr->prevObjPtr = NULL;
1750 objPtr->nextObjPtr = interp->liveList;
1751 if (interp->liveList)
1752 interp->liveList->prevObjPtr = objPtr;
1753 interp->liveList = objPtr;
1754
1755 return objPtr;
1756 }
1757
1758 /* Free an object. Actually objects are never freed, but
1759 * just moved to the free objects list, where they will be
1760 * reused by Jim_NewObj(). */
1761 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1762 {
1763 /* Check if the object was already freed, panic. */
1764 if (objPtr->refCount != 0) {
1765 Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1766 objPtr->refCount);
1767 }
1768 /* Free the internal representation */
1769 Jim_FreeIntRep(interp, objPtr);
1770 /* Free the string representation */
1771 if (objPtr->bytes != NULL) {
1772 if (objPtr->bytes != JimEmptyStringRep)
1773 Jim_Free(objPtr->bytes);
1774 }
1775 /* Unlink the object from the live objects list */
1776 if (objPtr->prevObjPtr)
1777 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1778 if (objPtr->nextObjPtr)
1779 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1780 if (interp->liveList == objPtr)
1781 interp->liveList = objPtr->nextObjPtr;
1782 /* Link the object into the free objects list */
1783 objPtr->prevObjPtr = NULL;
1784 objPtr->nextObjPtr = interp->freeList;
1785 if (interp->freeList)
1786 interp->freeList->prevObjPtr = objPtr;
1787 interp->freeList = objPtr;
1788 objPtr->refCount = -1;
1789 }
1790
1791 /* Invalidate the string representation of an object. */
1792 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1793 {
1794 if (objPtr->bytes != NULL) {
1795 if (objPtr->bytes != JimEmptyStringRep)
1796 Jim_Free(objPtr->bytes);
1797 }
1798 objPtr->bytes = NULL;
1799 }
1800
1801 #define Jim_SetStringRep(o, b, l) \
1802 do { (o)->bytes = b; (o)->length = l; } while (0)
1803
1804 /* Set the initial string representation for an object.
1805 * Does not try to free an old one. */
1806 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1807 {
1808 if (length == 0) {
1809 objPtr->bytes = JimEmptyStringRep;
1810 objPtr->length = 0;
1811 } else {
1812 objPtr->bytes = Jim_Alloc(length+1);
1813 objPtr->length = length;
1814 memcpy(objPtr->bytes, bytes, length);
1815 objPtr->bytes[length] = '\0';
1816 }
1817 }
1818
1819 /* Duplicate an object. The returned object has refcount = 0. */
1820 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1821 {
1822 Jim_Obj *dupPtr;
1823
1824 dupPtr = Jim_NewObj(interp);
1825 if (objPtr->bytes == NULL) {
1826 /* Object does not have a valid string representation. */
1827 dupPtr->bytes = NULL;
1828 } else {
1829 Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1830 }
1831 if (objPtr->typePtr != NULL) {
1832 if (objPtr->typePtr->dupIntRepProc == NULL) {
1833 dupPtr->internalRep = objPtr->internalRep;
1834 } else {
1835 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1836 }
1837 dupPtr->typePtr = objPtr->typePtr;
1838 } else {
1839 dupPtr->typePtr = NULL;
1840 }
1841 return dupPtr;
1842 }
1843
1844 /* Return the string representation for objPtr. If the object
1845 * string representation is invalid, calls the method to create
1846 * a new one starting from the internal representation of the object. */
1847 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1848 {
1849 if (objPtr->bytes == NULL) {
1850 /* Invalid string repr. Generate it. */
1851 if (objPtr->typePtr->updateStringProc == NULL) {
1852 Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1853 objPtr->typePtr->name);
1854 }
1855 objPtr->typePtr->updateStringProc(objPtr);
1856 }
1857 if (lenPtr)
1858 *lenPtr = objPtr->length;
1859 return objPtr->bytes;
1860 }
1861
1862 /* Just returns the length of the object's string rep */
1863 int Jim_Length(Jim_Obj *objPtr)
1864 {
1865 int len;
1866
1867 Jim_GetString(objPtr, &len);
1868 return len;
1869 }
1870
1871 /* -----------------------------------------------------------------------------
1872 * String Object
1873 * ---------------------------------------------------------------------------*/
1874 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1875 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1876
1877 static Jim_ObjType stringObjType = {
1878 "string",
1879 NULL,
1880 DupStringInternalRep,
1881 NULL,
1882 JIM_TYPE_REFERENCES,
1883 };
1884
1885 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1886 {
1887 JIM_NOTUSED(interp);
1888
1889 /* This is a bit subtle: the only caller of this function
1890 * should be Jim_DuplicateObj(), that will copy the
1891 * string representaion. After the copy, the duplicated
1892 * object will not have more room in teh buffer than
1893 * srcPtr->length bytes. So we just set it to length. */
1894 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1895 }
1896
1897 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1898 {
1899 /* Get a fresh string representation. */
1900 (void) Jim_GetString(objPtr, NULL);
1901 /* Free any other internal representation. */
1902 Jim_FreeIntRep(interp, objPtr);
1903 /* Set it as string, i.e. just set the maxLength field. */
1904 objPtr->typePtr = &stringObjType;
1905 objPtr->internalRep.strValue.maxLength = objPtr->length;
1906 return JIM_OK;
1907 }
1908
1909 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1910 {
1911 Jim_Obj *objPtr = Jim_NewObj(interp);
1912
1913 if (len == -1)
1914 len = strlen(s);
1915 /* Alloc/Set the string rep. */
1916 if (len == 0) {
1917 objPtr->bytes = JimEmptyStringRep;
1918 objPtr->length = 0;
1919 } else {
1920 objPtr->bytes = Jim_Alloc(len+1);
1921 objPtr->length = len;
1922 memcpy(objPtr->bytes, s, len);
1923 objPtr->bytes[len] = '\0';
1924 }
1925
1926 /* No typePtr field for the vanilla string object. */
1927 objPtr->typePtr = NULL;
1928 return objPtr;
1929 }
1930
1931 /* This version does not try to duplicate the 's' pointer, but
1932 * use it directly. */
1933 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
1934 {
1935 Jim_Obj *objPtr = Jim_NewObj(interp);
1936
1937 if (len == -1)
1938 len = strlen(s);
1939 Jim_SetStringRep(objPtr, s, len);
1940 objPtr->typePtr = NULL;
1941 return objPtr;
1942 }
1943
1944 /* Low-level string append. Use it only against objects
1945 * of type "string". */
1946 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
1947 {
1948 int needlen;
1949
1950 if (len == -1)
1951 len = strlen(str);
1952 needlen = objPtr->length + len;
1953 if (objPtr->internalRep.strValue.maxLength < needlen ||
1954 objPtr->internalRep.strValue.maxLength == 0) {
1955 if (objPtr->bytes == JimEmptyStringRep) {
1956 objPtr->bytes = Jim_Alloc((needlen*2)+1);
1957 } else {
1958 objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
1959 }
1960 objPtr->internalRep.strValue.maxLength = needlen*2;
1961 }
1962 memcpy(objPtr->bytes + objPtr->length, str, len);
1963 objPtr->bytes[objPtr->length+len] = '\0';
1964 objPtr->length += len;
1965 }
1966
1967 /* Low-level wrapper to append an object. */
1968 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
1969 {
1970 int len;
1971 const char *str;
1972
1973 str = Jim_GetString(appendObjPtr, &len);
1974 StringAppendString(objPtr, str, len);
1975 }
1976
1977 /* Higher level API to append strings to objects. */
1978 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
1979 int len)
1980 {
1981 if (Jim_IsShared(objPtr))
1982 Jim_Panic(interp,"Jim_AppendString called with shared object");
1983 if (objPtr->typePtr != &stringObjType)
1984 SetStringFromAny(interp, objPtr);
1985 StringAppendString(objPtr, str, len);
1986 }
1987
1988 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
1989 Jim_Obj *appendObjPtr)
1990 {
1991 int len;
1992 const char *str;
1993
1994 str = Jim_GetString(appendObjPtr, &len);
1995 Jim_AppendString(interp, objPtr, str, len);
1996 }
1997
1998 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
1999 {
2000 va_list ap;
2001
2002 if (objPtr->typePtr != &stringObjType)
2003 SetStringFromAny(interp, objPtr);
2004 va_start(ap, objPtr);
2005 while (1) {
2006 char *s = va_arg(ap, char*);
2007
2008 if (s == NULL) break;
2009 Jim_AppendString(interp, objPtr, s, -1);
2010 }
2011 va_end(ap);
2012 }
2013
2014 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2015 {
2016 const char *aStr, *bStr;
2017 int aLen, bLen, i;
2018
2019 if (aObjPtr == bObjPtr) return 1;
2020 aStr = Jim_GetString(aObjPtr, &aLen);
2021 bStr = Jim_GetString(bObjPtr, &bLen);
2022 if (aLen != bLen) return 0;
2023 if (nocase == 0)
2024 return memcmp(aStr, bStr, aLen) == 0;
2025 for (i = 0; i < aLen; i++) {
2026 if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2027 return 0;
2028 }
2029 return 1;
2030 }
2031
2032 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2033 int nocase)
2034 {
2035 const char *pattern, *string;
2036 int patternLen, stringLen;
2037
2038 pattern = Jim_GetString(patternObjPtr, &patternLen);
2039 string = Jim_GetString(objPtr, &stringLen);
2040 return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2041 }
2042
2043 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2044 Jim_Obj *secondObjPtr, int nocase)
2045 {
2046 const char *s1, *s2;
2047 int l1, l2;
2048
2049 s1 = Jim_GetString(firstObjPtr, &l1);
2050 s2 = Jim_GetString(secondObjPtr, &l2);
2051 return JimStringCompare(s1, l1, s2, l2, nocase);
2052 }
2053
2054 /* Convert a range, as returned by Jim_GetRange(), into
2055 * an absolute index into an object of the specified length.
2056 * This function may return negative values, or values
2057 * bigger or equal to the length of the list if the index
2058 * is out of range. */
2059 static int JimRelToAbsIndex(int len, int index)
2060 {
2061 if (index < 0)
2062 return len + index;
2063 return index;
2064 }
2065
2066 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2067 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2068 * for implementation of commands like [string range] and [lrange].
2069 *
2070 * The resulting range is guaranteed to address valid elements of
2071 * the structure. */
2072 static void JimRelToAbsRange(int len, int first, int last,
2073 int *firstPtr, int *lastPtr, int *rangeLenPtr)
2074 {
2075 int rangeLen;
2076
2077 if (first > last) {
2078 rangeLen = 0;
2079 } else {
2080 rangeLen = last-first+1;
2081 if (rangeLen) {
2082 if (first < 0) {
2083 rangeLen += first;
2084 first = 0;
2085 }
2086 if (last >= len) {
2087 rangeLen -= (last-(len-1));
2088 last = len-1;
2089 }
2090 }
2091 }
2092 if (rangeLen < 0) rangeLen = 0;
2093
2094 *firstPtr = first;
2095 *lastPtr = last;
2096 *rangeLenPtr = rangeLen;
2097 }
2098
2099 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2100 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2101 {
2102 int first, last;
2103 const char *str;
2104 int len, rangeLen;
2105
2106 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2107 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2108 return NULL;
2109 str = Jim_GetString(strObjPtr, &len);
2110 first = JimRelToAbsIndex(len, first);
2111 last = JimRelToAbsIndex(len, last);
2112 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2113 return Jim_NewStringObj(interp, str+first, rangeLen);
2114 }
2115
2116 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2117 {
2118 char *buf = Jim_Alloc(strObjPtr->length+1);
2119 int i;
2120
2121 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2122 for (i = 0; i < strObjPtr->length; i++)
2123 buf[i] = tolower(buf[i]);
2124 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2125 }
2126
2127 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2128 {
2129 char *buf = Jim_Alloc(strObjPtr->length+1);
2130 int i;
2131
2132 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2133 for (i = 0; i < strObjPtr->length; i++)
2134 buf[i] = toupper(buf[i]);
2135 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2136 }
2137
2138 /* This is the core of the [format] command.
2139 * TODO: Export it, make it real... for now only %s and %%
2140 * specifiers supported. */
2141 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2142 int objc, Jim_Obj *const *objv)
2143 {
2144 const char *fmt;
2145 int fmtLen;
2146 Jim_Obj *resObjPtr;
2147
2148 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2149 resObjPtr = Jim_NewStringObj(interp, "", 0);
2150 while (fmtLen) {
2151 const char *p = fmt;
2152 char spec[2], c;
2153 jim_wide wideValue;
2154
2155 while (*fmt != '%' && fmtLen) {
2156 fmt++; fmtLen--;
2157 }
2158 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2159 if (fmtLen == 0)
2160 break;
2161 fmt++; fmtLen--; /* skip '%' */
2162 if (*fmt != '%') {
2163 if (objc == 0) {
2164 Jim_FreeNewObj(interp, resObjPtr);
2165 Jim_SetResultString(interp,
2166 "not enough arguments for all format specifiers", -1);
2167 return NULL;
2168 } else {
2169 objc--;
2170 }
2171 }
2172 switch(*fmt) {
2173 case 's':
2174 Jim_AppendObj(interp, resObjPtr, objv[0]);
2175 objv++;
2176 break;
2177 case 'c':
2178 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2179 Jim_FreeNewObj(interp, resObjPtr);
2180 return NULL;
2181 }
2182 c = (char) wideValue;
2183 Jim_AppendString(interp, resObjPtr, &c, 1);
2184 break;
2185 case 'd':
2186 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2187 Jim_FreeNewObj(interp, resObjPtr);
2188 return NULL;
2189 }
2190 Jim_AppendObj(interp, resObjPtr, objv[0]);
2191 break;
2192 case '%':
2193 Jim_AppendString(interp, resObjPtr, "%" , 1);
2194 break;
2195 default:
2196 spec[0] = *fmt; spec[1] = '\0';
2197 Jim_FreeNewObj(interp, resObjPtr);
2198 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2199 Jim_AppendStrings(interp, Jim_GetResult(interp),
2200 "bad field specifier \"", spec, "\"", NULL);
2201 return NULL;
2202 }
2203 fmt++;
2204 fmtLen--;
2205 }
2206 return resObjPtr;
2207 }
2208
2209 /* -----------------------------------------------------------------------------
2210 * Compared String Object
2211 * ---------------------------------------------------------------------------*/
2212
2213 /* This is strange object that allows to compare a C literal string
2214 * with a Jim object in very short time if the same comparison is done
2215 * multiple times. For example every time the [if] command is executed,
2216 * Jim has to check if a given argument is "else". This comparions if
2217 * the code has no errors are true most of the times, so we can cache
2218 * inside the object the pointer of the string of the last matching
2219 * comparison. Because most C compilers perform literal sharing,
2220 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2221 * this works pretty well even if comparisons are at different places
2222 * inside the C code. */
2223
2224 static Jim_ObjType comparedStringObjType = {
2225 "compared-string",
2226 NULL,
2227 NULL,
2228 NULL,
2229 JIM_TYPE_REFERENCES,
2230 };
2231
2232 /* The only way this object is exposed to the API is via the following
2233 * function. Returns true if the string and the object string repr.
2234 * are the same, otherwise zero is returned.
2235 *
2236 * Note: this isn't binary safe, but it hardly needs to be.*/
2237 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2238 const char *str)
2239 {
2240 if (objPtr->typePtr == &comparedStringObjType &&
2241 objPtr->internalRep.ptr == str)
2242 return 1;
2243 else {
2244 const char *objStr = Jim_GetString(objPtr, NULL);
2245 if (strcmp(str, objStr) != 0) return 0;
2246 if (objPtr->typePtr != &comparedStringObjType) {
2247 Jim_FreeIntRep(interp, objPtr);
2248 objPtr->typePtr = &comparedStringObjType;
2249 }
2250 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2251 return 1;
2252 }
2253 }
2254
2255 int qsortCompareStringPointers(const void *a, const void *b)
2256 {
2257 char * const *sa = (char * const *)a;
2258 char * const *sb = (char * const *)b;
2259 return strcmp(*sa, *sb);
2260 }
2261
2262 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2263 const char **tablePtr, int *indexPtr, const char *name, int flags)
2264 {
2265 const char **entryPtr = NULL;
2266 char **tablePtrSorted;
2267 int i, count = 0;
2268
2269 *indexPtr = -1;
2270 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2271 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2272 *indexPtr = i;
2273 return JIM_OK;
2274 }
2275 count++; /* If nothing matches, this will reach the len of tablePtr */
2276 }
2277 if (flags & JIM_ERRMSG) {
2278 if (name == NULL)
2279 name = "option";
2280 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2281 Jim_AppendStrings(interp, Jim_GetResult(interp),
2282 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2283 NULL);
2284 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2285 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2286 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2287 for (i = 0; i < count; i++) {
2288 if (i+1 == count && count > 1)
2289 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2290 Jim_AppendString(interp, Jim_GetResult(interp),
2291 tablePtrSorted[i], -1);
2292 if (i+1 != count)
2293 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2294 }
2295 Jim_Free(tablePtrSorted);
2296 }
2297 return JIM_ERR;
2298 }
2299
2300 /* -----------------------------------------------------------------------------
2301 * Source Object
2302 *
2303 * This object is just a string from the language point of view, but
2304 * in the internal representation it contains the filename and line number
2305 * where this given token was read. This information is used by
2306 * Jim_EvalObj() if the object passed happens to be of type "source".
2307 *
2308 * This allows to propagate the information about line numbers and file
2309 * names and give error messages with absolute line numbers.
2310 *
2311 * Note that this object uses shared strings for filenames, and the
2312 * pointer to the filename together with the line number is taken into
2313 * the space for the "inline" internal represenation of the Jim_Object,
2314 * so there is almost memory zero-overhead.
2315 *
2316 * Also the object will be converted to something else if the given
2317 * token it represents in the source file is not something to be
2318 * evaluated (not a script), and will be specialized in some other way,
2319 * so the time overhead is alzo null.
2320 * ---------------------------------------------------------------------------*/
2321
2322 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2323 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2324
2325 static Jim_ObjType sourceObjType = {
2326 "source",
2327 FreeSourceInternalRep,
2328 DupSourceInternalRep,
2329 NULL,
2330 JIM_TYPE_REFERENCES,
2331 };
2332
2333 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2334 {
2335 Jim_ReleaseSharedString(interp,
2336 objPtr->internalRep.sourceValue.fileName);
2337 }
2338
2339 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2340 {
2341 dupPtr->internalRep.sourceValue.fileName =
2342 Jim_GetSharedString(interp,
2343 srcPtr->internalRep.sourceValue.fileName);
2344 dupPtr->internalRep.sourceValue.lineNumber =
2345 dupPtr->internalRep.sourceValue.lineNumber;
2346 dupPtr->typePtr = &sourceObjType;
2347 }
2348
2349 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2350 const char *fileName, int lineNumber)
2351 {
2352 if (Jim_IsShared(objPtr))
2353 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2354 if (objPtr->typePtr != NULL)
2355 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2356 objPtr->internalRep.sourceValue.fileName =
2357 Jim_GetSharedString(interp, fileName);
2358 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2359 objPtr->typePtr = &sourceObjType;
2360 }
2361
2362 /* -----------------------------------------------------------------------------
2363 * Script Object
2364 * ---------------------------------------------------------------------------*/
2365
2366 #define JIM_CMDSTRUCT_EXPAND -1
2367
2368 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2369 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2370 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2371
2372 static Jim_ObjType scriptObjType = {
2373 "script",
2374 FreeScriptInternalRep,
2375 DupScriptInternalRep,
2376 NULL,
2377 JIM_TYPE_REFERENCES,
2378 };
2379
2380 /* The ScriptToken structure represents every token into a scriptObj.
2381 * Every token contains an associated Jim_Obj that can be specialized
2382 * by commands operating on it. */
2383 typedef struct ScriptToken {
2384 int type;
2385 Jim_Obj *objPtr;
2386 int linenr;
2387 } ScriptToken;
2388
2389 /* This is the script object internal representation. An array of
2390 * ScriptToken structures, with an associated command structure array.
2391 * The command structure is a pre-computed representation of the
2392 * command length and arguments structure as a simple liner array
2393 * of integers.
2394 *
2395 * For example the script:
2396 *
2397 * puts hello
2398 * set $i $x$y [foo]BAR
2399 *
2400 * will produce a ScriptObj with the following Tokens:
2401 *
2402 * ESC puts
2403 * SEP
2404 * ESC hello
2405 * EOL
2406 * ESC set
2407 * EOL
2408 * VAR i
2409 * SEP
2410 * VAR x
2411 * VAR y
2412 * SEP
2413 * CMD foo
2414 * ESC BAR
2415 * EOL
2416 *
2417 * This is a description of the tokens, separators, and of lines.
2418 * The command structure instead represents the number of arguments
2419 * of every command, followed by the tokens of which every argument
2420 * is composed. So for the example script, the cmdstruct array will
2421 * contain:
2422 *
2423 * 2 1 1 4 1 1 2 2
2424 *
2425 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2426 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2427 * composed of single tokens (1 1) and the last two of double tokens
2428 * (2 2).
2429 *
2430 * The precomputation of the command structure makes Jim_Eval() faster,
2431 * and simpler because there aren't dynamic lengths / allocations.
2432 *
2433 * -- {expand} handling --
2434 *
2435 * Expand is handled in a special way. When a command
2436 * contains at least an argument with the {expand} prefix,
2437 * the command structure presents a -1 before the integer
2438 * describing the number of arguments. This is used in order
2439 * to send the command exection to a different path in case
2440 * of {expand} and guarantee a fast path for the more common
2441 * case. Also, the integers describing the number of tokens
2442 * are expressed with negative sign, to allow for fast check
2443 * of what's an {expand}-prefixed argument and what not.
2444 *
2445 * For example the command:
2446 *
2447 * list {expand}{1 2}
2448 *
2449 * Will produce the following cmdstruct array:
2450 *
2451 * -1 2 1 -2
2452 *
2453 * -- the substFlags field of the structure --
2454 *
2455 * The scriptObj structure is used to represent both "script" objects
2456 * and "subst" objects. In the second case, the cmdStruct related
2457 * fields are not used at all, but there is an additional field used
2458 * that is 'substFlags': this represents the flags used to turn
2459 * the string into the intenral representation used to perform the
2460 * substitution. If this flags are not what the application requires
2461 * the scriptObj is created again. For example the script:
2462 *
2463 * subst -nocommands $string
2464 * subst -novariables $string
2465 *
2466 * Will recreate the internal representation of the $string object
2467 * two times.
2468 */
2469 typedef struct ScriptObj {
2470 int len; /* Length as number of tokens. */
2471 int commands; /* number of top-level commands in script. */
2472 ScriptToken *token; /* Tokens array. */
2473 int *cmdStruct; /* commands structure */
2474 int csLen; /* length of the cmdStruct array. */
2475 int substFlags; /* flags used for the compilation of "subst" objects */
2476 int inUse; /* Used to share a ScriptObj. Currently
2477 only used by Jim_EvalObj() as protection against
2478 shimmering of the currently evaluated object. */
2479 char *fileName;
2480 } ScriptObj;
2481
2482 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2483 {
2484 int i;
2485 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2486
2487 script->inUse--;
2488 if (script->inUse != 0) return;
2489 for (i = 0; i < script->len; i++) {
2490 if (script->token[i].objPtr != NULL)
2491 Jim_DecrRefCount(interp, script->token[i].objPtr);
2492 }
2493 Jim_Free(script->token);
2494 Jim_Free(script->cmdStruct);
2495 Jim_Free(script->fileName);
2496 Jim_Free(script);
2497 }
2498
2499 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2500 {
2501 JIM_NOTUSED(interp);
2502 JIM_NOTUSED(srcPtr);
2503
2504 /* Just returns an simple string. */
2505 dupPtr->typePtr = NULL;
2506 }
2507
2508 /* Add a new token to the internal repr of a script object */
2509 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2510 char *strtoken, int len, int type, char *filename, int linenr)
2511 {
2512 int prevtype;
2513 struct ScriptToken *token;
2514
2515 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2516 script->token[script->len-1].type;
2517 /* Skip tokens without meaning, like words separators
2518 * following a word separator or an end of command and
2519 * so on. */
2520 if (prevtype == JIM_TT_EOL) {
2521 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2522 Jim_Free(strtoken);
2523 return;
2524 }
2525 } else if (prevtype == JIM_TT_SEP) {
2526 if (type == JIM_TT_SEP) {
2527 Jim_Free(strtoken);
2528 return;
2529 } else if (type == JIM_TT_EOL) {
2530 /* If an EOL is following by a SEP, drop the previous
2531 * separator. */
2532 script->len--;
2533 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2534 }
2535 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2536 type == JIM_TT_ESC && len == 0)
2537 {
2538 /* Don't add empty tokens used in interpolation */
2539 Jim_Free(strtoken);
2540 return;
2541 }
2542 /* Make space for a new istruction */
2543 script->len++;
2544 script->token = Jim_Realloc(script->token,
2545 sizeof(ScriptToken)*script->len);
2546 /* Initialize the new token */
2547 token = script->token+(script->len-1);
2548 token->type = type;
2549 /* Every object is intially as a string, but the
2550 * internal type may be specialized during execution of the
2551 * script. */
2552 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2553 /* To add source info to SEP and EOL tokens is useless because
2554 * they will never by called as arguments of Jim_EvalObj(). */
2555 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2556 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2557 Jim_IncrRefCount(token->objPtr);
2558 token->linenr = linenr;
2559 }
2560
2561 /* Add an integer into the command structure field of the script object. */
2562 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2563 {
2564 script->csLen++;
2565 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2566 sizeof(int)*script->csLen);
2567 script->cmdStruct[script->csLen-1] = val;
2568 }
2569
2570 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2571 * of objPtr. Search nested script objects recursively. */
2572 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2573 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2574 {
2575 int i;
2576
2577 for (i = 0; i < script->len; i++) {
2578 if (script->token[i].objPtr != objPtr &&
2579 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2580 return script->token[i].objPtr;
2581 }
2582 /* Enter recursively on scripts only if the object
2583 * is not the same as the one we are searching for
2584 * shared occurrences. */
2585 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2586 script->token[i].objPtr != objPtr) {
2587 Jim_Obj *foundObjPtr;
2588
2589 ScriptObj *subScript =
2590 script->token[i].objPtr->internalRep.ptr;
2591 /* Don't recursively enter the script we are trying
2592 * to make shared to avoid circular references. */
2593 if (subScript == scriptBarrier) continue;
2594 if (subScript != script) {
2595 foundObjPtr =
2596 ScriptSearchLiteral(interp, subScript,
2597 scriptBarrier, objPtr);
2598 if (foundObjPtr != NULL)
2599 return foundObjPtr;
2600 }
2601 }
2602 }
2603 return NULL;
2604 }
2605
2606 /* Share literals of a script recursively sharing sub-scripts literals. */
2607 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2608 ScriptObj *topLevelScript)
2609 {
2610 int i, j;
2611
2612 return;
2613 /* Try to share with toplevel object. */
2614 if (topLevelScript != NULL) {
2615 for (i = 0; i < script->len; i++) {
2616 Jim_Obj *foundObjPtr;
2617 char *str = script->token[i].objPtr->bytes;
2618
2619 if (script->token[i].objPtr->refCount != 1) continue;
2620 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2621 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2622 foundObjPtr = ScriptSearchLiteral(interp,
2623 topLevelScript,
2624 script, /* barrier */
2625 script->token[i].objPtr);
2626 if (foundObjPtr != NULL) {
2627 Jim_IncrRefCount(foundObjPtr);
2628 Jim_DecrRefCount(interp,
2629 script->token[i].objPtr);
2630 script->token[i].objPtr = foundObjPtr;
2631 }
2632 }
2633 }
2634 /* Try to share locally */
2635 for (i = 0; i < script->len; i++) {
2636 char *str = script->token[i].objPtr->bytes;
2637
2638 if (script->token[i].objPtr->refCount != 1) continue;
2639 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2640 for (j = 0; j < script->len; j++) {
2641 if (script->token[i].objPtr !=
2642 script->token[j].objPtr &&
2643 Jim_StringEqObj(script->token[i].objPtr,
2644 script->token[j].objPtr, 0))
2645 {
2646 Jim_IncrRefCount(script->token[j].objPtr);
2647 Jim_DecrRefCount(interp,
2648 script->token[i].objPtr);
2649 script->token[i].objPtr =
2650 script->token[j].objPtr;
2651 }
2652 }
2653 }
2654 }
2655
2656 /* This method takes the string representation of an object
2657 * as a Tcl script, and generates the pre-parsed internal representation
2658 * of the script. */
2659 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2660 {
2661 int scriptTextLen;
2662 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2663 struct JimParserCtx parser;
2664 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2665 ScriptToken *token;
2666 int args, tokens, start, end, i;
2667 int initialLineNumber;
2668 int propagateSourceInfo = 0;
2669
2670 script->len = 0;
2671 script->csLen = 0;
2672 script->commands = 0;
2673 script->token = NULL;
2674 script->cmdStruct = NULL;
2675 script->inUse = 1;
2676 /* Try to get information about filename / line number */
2677 if (objPtr->typePtr == &sourceObjType) {
2678 script->fileName =
2679 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2680 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2681 propagateSourceInfo = 1;
2682 } else {
2683 script->fileName = Jim_StrDup("?");
2684 initialLineNumber = 1;
2685 }
2686
2687 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2688 while(!JimParserEof(&parser)) {
2689 char *token;
2690 int len, type, linenr;
2691
2692 JimParseScript(&parser);
2693 token = JimParserGetToken(&parser, &len, &type, &linenr);
2694 ScriptObjAddToken(interp, script, token, len, type,
2695 propagateSourceInfo ? script->fileName : NULL,
2696 linenr);
2697 }
2698 token = script->token;
2699
2700 /* Compute the command structure array
2701 * (see the ScriptObj struct definition for more info) */
2702 start = 0; /* Current command start token index */
2703 end = -1; /* Current command end token index */
2704 while (1) {
2705 int expand = 0; /* expand flag. set to 1 on {expand} form. */
2706 int interpolation = 0; /* set to 1 if there is at least one
2707 argument of the command obtained via
2708 interpolation of more tokens. */
2709 /* Search for the end of command, while
2710 * count the number of args. */
2711 start = ++end;
2712 if (start >= script->len) break;
2713 args = 1; /* Number of args in current command */
2714 while (token[end].type != JIM_TT_EOL) {
2715 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2716 token[end-1].type == JIM_TT_EOL)
2717 {
2718 if (token[end].type == JIM_TT_STR &&
2719 token[end+1].type != JIM_TT_SEP &&
2720 token[end+1].type != JIM_TT_EOL &&
2721 (!strcmp(token[end].objPtr->bytes, "expand") ||
2722 !strcmp(token[end].objPtr->bytes, "*")))
2723 expand++;
2724 }
2725 if (token[end].type == JIM_TT_SEP)
2726 args++;
2727 end++;
2728 }
2729 interpolation = !((end-start+1) == args*2);
2730 /* Add the 'number of arguments' info into cmdstruct.
2731 * Negative value if there is list expansion involved. */
2732 if (expand)
2733 ScriptObjAddInt(script, -1);
2734 ScriptObjAddInt(script, args);
2735 /* Now add info about the number of tokens. */
2736 tokens = 0; /* Number of tokens in current argument. */
2737 expand = 0;
2738 for (i = start; i <= end; i++) {
2739 if (token[i].type == JIM_TT_SEP ||
2740 token[i].type == JIM_TT_EOL)
2741 {
2742 if (tokens == 1 && expand)
2743 expand = 0;
2744 ScriptObjAddInt(script,
2745 expand ? -tokens : tokens);
2746
2747 expand = 0;
2748 tokens = 0;
2749 continue;
2750 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2751 (!strcmp(token[i].objPtr->bytes, "expand") ||
2752 !strcmp(token[i].objPtr->bytes, "*")))
2753 {
2754 expand++;
2755 }
2756 tokens++;
2757 }
2758 }
2759 /* Perform literal sharing, but only for objects that appear
2760 * to be scripts written as literals inside the source code,
2761 * and not computed at runtime. Literal sharing is a costly
2762 * operation that should be done only against objects that
2763 * are likely to require compilation only the first time, and
2764 * then are executed multiple times. */
2765 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2766 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2767 if (bodyObjPtr->typePtr == &scriptObjType) {
2768 ScriptObj *bodyScript =
2769 bodyObjPtr->internalRep.ptr;
2770 ScriptShareLiterals(interp, script, bodyScript);
2771 }
2772 } else if (propagateSourceInfo) {
2773 ScriptShareLiterals(interp, script, NULL);
2774 }
2775 /* Free the old internal rep and set the new one. */
2776 Jim_FreeIntRep(interp, objPtr);
2777 Jim_SetIntRepPtr(objPtr, script);
2778 objPtr->typePtr = &scriptObjType;
2779 return JIM_OK;
2780 }
2781
2782 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
2783 {
2784 if (objPtr->typePtr != &scriptObjType) {
2785 SetScriptFromAny(interp, objPtr);
2786 }
2787 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
2788 }
2789
2790 /* -----------------------------------------------------------------------------
2791 * Commands
2792 * ---------------------------------------------------------------------------*/
2793
2794 /* Commands HashTable Type.
2795 *
2796 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
2797 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
2798 {
2799 Jim_Cmd *cmdPtr = (void*) val;
2800
2801 if (cmdPtr->cmdProc == NULL) {
2802 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2803 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2804 if (cmdPtr->staticVars) {
2805 Jim_FreeHashTable(cmdPtr->staticVars);
2806 Jim_Free(cmdPtr->staticVars);
2807 }
2808 } else if (cmdPtr->delProc != NULL) {
2809 /* If it was a C coded command, call the delProc if any */
2810 cmdPtr->delProc(interp, cmdPtr->privData);
2811 }
2812 Jim_Free(val);
2813 }
2814
2815 static Jim_HashTableType JimCommandsHashTableType = {
2816 JimStringCopyHTHashFunction, /* hash function */
2817 JimStringCopyHTKeyDup, /* key dup */
2818 NULL, /* val dup */
2819 JimStringCopyHTKeyCompare, /* key compare */
2820 JimStringCopyHTKeyDestructor, /* key destructor */
2821 Jim_CommandsHT_ValDestructor /* val destructor */
2822 };
2823
2824 /* ------------------------- Commands related functions --------------------- */
2825
2826 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
2827 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
2828 {
2829 Jim_HashEntry *he;
2830 Jim_Cmd *cmdPtr;
2831
2832 he = Jim_FindHashEntry(&interp->commands, cmdName);
2833 if (he == NULL) { /* New command to create */
2834 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2835 cmdPtr->cmdProc = cmdProc;
2836 cmdPtr->privData = privData;
2837 cmdPtr->delProc = delProc;
2838 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2839 } else {
2840 Jim_InterpIncrProcEpoch(interp);
2841 /* Free the arglist/body objects if it was a Tcl procedure */
2842 cmdPtr = he->val;
2843 if (cmdPtr->cmdProc == NULL) {
2844 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2845 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2846 if (cmdPtr->staticVars) {
2847 Jim_FreeHashTable(cmdPtr->staticVars);
2848 Jim_Free(cmdPtr->staticVars);
2849 }
2850 cmdPtr->staticVars = NULL;
2851 } else if (cmdPtr->delProc != NULL) {
2852 /* If it was a C coded command, call the delProc if any */
2853 cmdPtr->delProc(interp, cmdPtr->privData);
2854 }
2855 cmdPtr->cmdProc = cmdProc;
2856 cmdPtr->privData = privData;
2857 }
2858 /* There is no need to increment the 'proc epoch' because
2859 * creation of a new procedure can never affect existing
2860 * cached commands. We don't do negative caching. */
2861 return JIM_OK;
2862 }
2863
2864 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
2865 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
2866 int arityMin, int arityMax)
2867 {
2868 Jim_Cmd *cmdPtr;
2869
2870 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2871 cmdPtr->cmdProc = NULL; /* Not a C coded command */
2872 cmdPtr->argListObjPtr = argListObjPtr;
2873 cmdPtr->bodyObjPtr = bodyObjPtr;
2874 Jim_IncrRefCount(argListObjPtr);
2875 Jim_IncrRefCount(bodyObjPtr);
2876 cmdPtr->arityMin = arityMin;
2877 cmdPtr->arityMax = arityMax;
2878 cmdPtr->staticVars = NULL;
2879
2880 /* Create the statics hash table. */
2881 if (staticsListObjPtr) {
2882 int len, i;
2883
2884 Jim_ListLength(interp, staticsListObjPtr, &len);
2885 if (len != 0) {
2886 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
2887 Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
2888 interp);
2889 for (i = 0; i < len; i++) {
2890 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
2891 Jim_Var *varPtr;
2892 int subLen;
2893
2894 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
2895 /* Check if it's composed of two elements. */
2896 Jim_ListLength(interp, objPtr, &subLen);
2897 if (subLen == 1 || subLen == 2) {
2898 /* Try to get the variable value from the current
2899 * environment. */
2900 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
2901 if (subLen == 1) {
2902 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
2903 JIM_NONE);
2904 if (initObjPtr == NULL) {
2905 Jim_SetResult(interp,
2906 Jim_NewEmptyStringObj(interp));
2907 Jim_AppendStrings(interp, Jim_GetResult(interp),
2908 "variable for initialization of static \"",
2909 Jim_GetString(nameObjPtr, NULL),
2910 "\" not found in the local context",
2911 NULL);
2912 goto err;
2913 }
2914 } else {
2915 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
2916 }
2917 varPtr = Jim_Alloc(sizeof(*varPtr));
2918 varPtr->objPtr = initObjPtr;
2919 Jim_IncrRefCount(initObjPtr);
2920 varPtr->linkFramePtr = NULL;
2921 if (Jim_AddHashEntry(cmdPtr->staticVars,
2922 Jim_GetString(nameObjPtr, NULL),
2923 varPtr) != JIM_OK)
2924 {
2925 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2926 Jim_AppendStrings(interp, Jim_GetResult(interp),
2927 "static variable name \"",
2928 Jim_GetString(objPtr, NULL), "\"",
2929 " duplicated in statics list", NULL);
2930 Jim_DecrRefCount(interp, initObjPtr);
2931 Jim_Free(varPtr);
2932 goto err;
2933 }
2934 } else {
2935 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2936 Jim_AppendStrings(interp, Jim_GetResult(interp),
2937 "too many fields in static specifier \"",
2938 objPtr, "\"", NULL);
2939 goto err;
2940 }
2941 }
2942 }
2943 }
2944
2945 /* Add the new command */
2946
2947 /* it may already exist, so we try to delete the old one */
2948 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
2949 /* There was an old procedure with the same name, this requires
2950 * a 'proc epoch' update. */
2951 Jim_InterpIncrProcEpoch(interp);
2952 }
2953 /* If a procedure with the same name didn't existed there is no need
2954 * to increment the 'proc epoch' because creation of a new procedure
2955 * can never affect existing cached commands. We don't do
2956 * negative caching. */
2957 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2958 return JIM_OK;
2959
2960 err:
2961 Jim_FreeHashTable(cmdPtr->staticVars);
2962 Jim_Free(cmdPtr->staticVars);
2963 Jim_DecrRefCount(interp, argListObjPtr);
2964 Jim_DecrRefCount(interp, bodyObjPtr);
2965 Jim_Free(cmdPtr);
2966 return JIM_ERR;
2967 }
2968
2969 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
2970 {
2971 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
2972 return JIM_ERR;
2973 Jim_InterpIncrProcEpoch(interp);
2974 return JIM_OK;
2975 }
2976
2977 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
2978 const char *newName)
2979 {
2980 Jim_Cmd *cmdPtr;
2981 Jim_HashEntry *he;
2982 Jim_Cmd *copyCmdPtr;
2983
2984 if (newName[0] == '\0') /* Delete! */
2985 return Jim_DeleteCommand(interp, oldName);
2986 /* Rename */
2987 he = Jim_FindHashEntry(&interp->commands, oldName);
2988 if (he == NULL)
2989 return JIM_ERR; /* Invalid command name */
2990 cmdPtr = he->val;
2991 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
2992 *copyCmdPtr = *cmdPtr;
2993 /* In order to avoid that a procedure will get arglist/body/statics
2994 * freed by the hash table methods, fake a C-coded command
2995 * setting cmdPtr->cmdProc as not NULL */
2996 cmdPtr->cmdProc = (void*)1;
2997 /* Also make sure delProc is NULL. */
2998 cmdPtr->delProc = NULL;
2999 /* Destroy the old command, and make sure the new is freed
3000 * as well. */
3001 Jim_DeleteHashEntry(&interp->commands, oldName);
3002 Jim_DeleteHashEntry(&interp->commands, newName);
3003 /* Now the new command. We are sure it can't fail because
3004 * the target name was already freed. */
3005 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3006 /* Increment the epoch */
3007 Jim_InterpIncrProcEpoch(interp);
3008 return JIM_OK;
3009 }
3010
3011 /* -----------------------------------------------------------------------------
3012 * Command object
3013 * ---------------------------------------------------------------------------*/
3014
3015 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3016
3017 static Jim_ObjType commandObjType = {
3018 "command",
3019 NULL,
3020 NULL,
3021 NULL,
3022 JIM_TYPE_REFERENCES,
3023 };
3024
3025 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3026 {
3027 Jim_HashEntry *he;
3028 const char *cmdName;
3029
3030 /* Get the string representation */
3031 cmdName = Jim_GetString(objPtr, NULL);
3032 /* Lookup this name into the commands hash table */
3033 he = Jim_FindHashEntry(&interp->commands, cmdName);
3034 if (he == NULL)
3035 return JIM_ERR;
3036
3037 /* Free the old internal repr and set the new one. */
3038 Jim_FreeIntRep(interp, objPtr);
3039 objPtr->typePtr = &commandObjType;
3040 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3041 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3042 return JIM_OK;
3043 }
3044
3045 /* This function returns the command structure for the command name
3046 * stored in objPtr. It tries to specialize the objPtr to contain
3047 * a cached info instead to perform the lookup into the hash table
3048 * every time. The information cached may not be uptodate, in such
3049 * a case the lookup is performed and the cache updated. */
3050 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3051 {
3052 if ((objPtr->typePtr != &commandObjType ||
3053 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3054 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3055 if (flags & JIM_ERRMSG) {
3056 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3057 Jim_AppendStrings(interp, Jim_GetResult(interp),
3058 "invalid command name \"", objPtr->bytes, "\"",
3059 NULL);
3060 }
3061 return NULL;
3062 }
3063 return objPtr->internalRep.cmdValue.cmdPtr;
3064 }
3065
3066 /* -----------------------------------------------------------------------------
3067 * Variables
3068 * ---------------------------------------------------------------------------*/
3069
3070 /* Variables HashTable Type.
3071 *
3072 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3073 static void JimVariablesHTValDestructor(void *interp, void *val)
3074 {
3075 Jim_Var *varPtr = (void*) val;
3076
3077 Jim_DecrRefCount(interp, varPtr->objPtr);
3078 Jim_Free(val);
3079 }
3080
3081 static Jim_HashTableType JimVariablesHashTableType = {
3082 JimStringCopyHTHashFunction, /* hash function */
3083 JimStringCopyHTKeyDup, /* key dup */
3084 NULL, /* val dup */
3085 JimStringCopyHTKeyCompare, /* key compare */
3086 JimStringCopyHTKeyDestructor, /* key destructor */
3087 JimVariablesHTValDestructor /* val destructor */
3088 };
3089
3090 /* -----------------------------------------------------------------------------
3091 * Variable object
3092 * ---------------------------------------------------------------------------*/
3093
3094 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3095
3096 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3097
3098 static Jim_ObjType variableObjType = {
3099 "variable",
3100 NULL,
3101 NULL,
3102 NULL,
3103 JIM_TYPE_REFERENCES,
3104 };
3105
3106 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3107 * is in the form "varname(key)". */
3108 static int Jim_NameIsDictSugar(const char *str, int len)
3109 {
3110 if (len == -1)
3111 len = strlen(str);
3112 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3113 return 1;
3114 return 0;
3115 }
3116
3117 /* This method should be called only by the variable API.
3118 * It returns JIM_OK on success (variable already exists),
3119 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3120 * a variable name, but syntax glue for [dict] i.e. the last
3121 * character is ')' */
3122 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3123 {
3124 Jim_HashEntry *he;
3125 const char *varName;
3126 int len;
3127
3128 /* Check if the object is already an uptodate variable */
3129 if (objPtr->typePtr == &variableObjType &&
3130 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3131 return JIM_OK; /* nothing to do */
3132 /* Get the string representation */
3133 varName = Jim_GetString(objPtr, &len);
3134 /* Make sure it's not syntax glue to get/set dict. */
3135 if (Jim_NameIsDictSugar(varName, len))
3136 return JIM_DICT_SUGAR;
3137 /* Lookup this name into the variables hash table */
3138 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3139 if (he == NULL) {
3140 /* Try with static vars. */
3141 if (interp->framePtr->staticVars == NULL)
3142 return JIM_ERR;
3143 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3144 return JIM_ERR;
3145 }
3146 /* Free the old internal repr and set the new one. */
3147 Jim_FreeIntRep(interp, objPtr);
3148 objPtr->typePtr = &variableObjType;
3149 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3150 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3151 return JIM_OK;
3152 }
3153
3154 /* -------------------- Variables related functions ------------------------- */
3155 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3156 Jim_Obj *valObjPtr);
3157 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3158
3159 /* For now that's dummy. Variables lookup should be optimized
3160 * in many ways, with caching of lookups, and possibly with
3161 * a table of pre-allocated vars in every CallFrame for local vars.
3162 * All the caching should also have an 'epoch' mechanism similar
3163 * to the one used by Tcl for procedures lookup caching. */
3164
3165 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3166 {
3167 const char *name;
3168 Jim_Var *var;
3169 int err;
3170
3171 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3172 /* Check for [dict] syntax sugar. */
3173 if (err == JIM_DICT_SUGAR)
3174 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3175 /* New variable to create */
3176 name = Jim_GetString(nameObjPtr, NULL);
3177
3178 var = Jim_Alloc(sizeof(*var));
3179 var->objPtr = valObjPtr;
3180 Jim_IncrRefCount(valObjPtr);
3181 var->linkFramePtr = NULL;
3182 /* Insert the new variable */
3183 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3184 /* Make the object int rep a variable */
3185 Jim_FreeIntRep(interp, nameObjPtr);
3186 nameObjPtr->typePtr = &variableObjType;
3187 nameObjPtr->internalRep.varValue.callFrameId =
3188 interp->framePtr->id;
3189 nameObjPtr->internalRep.varValue.varPtr = var;
3190 } else {
3191 var = nameObjPtr->internalRep.varValue.varPtr;
3192 if (var->linkFramePtr == NULL) {
3193 Jim_IncrRefCount(valObjPtr);
3194 Jim_DecrRefCount(interp, var->objPtr);
3195 var->objPtr = valObjPtr;
3196 } else { /* Else handle the link */
3197 Jim_CallFrame *savedCallFrame;
3198
3199 savedCallFrame = interp->framePtr;
3200 interp->framePtr = var->linkFramePtr;
3201 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3202 interp->framePtr = savedCallFrame;
3203 if (err != JIM_OK)
3204 return err;
3205 }
3206 }
3207 return JIM_OK;
3208 }
3209
3210 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3211 {
3212 Jim_Obj *nameObjPtr;
3213 int result;
3214
3215 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3216 Jim_IncrRefCount(nameObjPtr);
3217 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3218 Jim_DecrRefCount(interp, nameObjPtr);
3219 return result;
3220 }
3221
3222 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3223 {
3224 Jim_CallFrame *savedFramePtr;
3225 int result;
3226
3227 savedFramePtr = interp->framePtr;
3228 interp->framePtr = interp->topFramePtr;
3229 result = Jim_SetVariableStr(interp, name, objPtr);
3230 interp->framePtr = savedFramePtr;
3231 return result;
3232 }
3233
3234 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3235 {
3236 Jim_Obj *nameObjPtr, *valObjPtr;
3237 int result;
3238
3239 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3240 valObjPtr = Jim_NewStringObj(interp, val, -1);
3241 Jim_IncrRefCount(nameObjPtr);
3242 Jim_IncrRefCount(valObjPtr);
3243 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3244 Jim_DecrRefCount(interp, nameObjPtr);
3245 Jim_DecrRefCount(interp, valObjPtr);
3246 return result;
3247 }
3248
3249 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3250 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3251 {
3252 const char *varName;
3253 int len;
3254
3255 /* Check for cycles. */
3256 if (interp->framePtr == targetCallFrame) {
3257 Jim_Obj *objPtr = targetNameObjPtr;
3258 Jim_Var *varPtr;
3259 /* Cycles are only possible with 'uplevel 0' */
3260 while(1) {
3261 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3262 Jim_SetResultString(interp,
3263 "can't upvar from variable to itself", -1);
3264 return JIM_ERR;
3265 }
3266 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3267 break;
3268 varPtr = objPtr->internalRep.varValue.varPtr;
3269 if (varPtr->linkFramePtr != targetCallFrame) break;
3270 objPtr = varPtr->objPtr;
3271 }
3272 }
3273 varName = Jim_GetString(nameObjPtr, &len);
3274 if (Jim_NameIsDictSugar(varName, len)) {
3275 Jim_SetResultString(interp,
3276 "Dict key syntax invalid as link source", -1);
3277 return JIM_ERR;
3278 }
3279 /* Perform the binding */
3280 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3281 /* We are now sure 'nameObjPtr' type is variableObjType */
3282 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3283 return JIM_OK;
3284 }
3285
3286 /* Return the Jim_Obj pointer associated with a variable name,
3287 * or NULL if the variable was not found in the current context.
3288 * The same optimization discussed in the comment to the
3289 * 'SetVariable' function should apply here. */
3290 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3291 {
3292 int err;
3293
3294 /* All the rest is handled here */
3295 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3296 /* Check for [dict] syntax sugar. */
3297 if (err == JIM_DICT_SUGAR)
3298 return JimDictSugarGet(interp, nameObjPtr);
3299 if (flags & JIM_ERRMSG) {
3300 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3301 Jim_AppendStrings(interp, Jim_GetResult(interp),
3302 "can't read \"", nameObjPtr->bytes,
3303 "\": no such variable", NULL);
3304 }
3305 return NULL;
3306 } else {
3307 Jim_Var *varPtr;
3308 Jim_Obj *objPtr;
3309 Jim_CallFrame *savedCallFrame;
3310
3311 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3312 if (varPtr->linkFramePtr == NULL)
3313 return varPtr->objPtr;
3314 /* The variable is a link? Resolve it. */
3315 savedCallFrame = interp->framePtr;
3316 interp->framePtr = varPtr->linkFramePtr;
3317 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3318 if (objPtr == NULL && flags & JIM_ERRMSG) {
3319 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3320 Jim_AppendStrings(interp, Jim_GetResult(interp),
3321 "can't read \"", nameObjPtr->bytes,
3322 "\": no such variable", NULL);
3323 }
3324 interp->framePtr = savedCallFrame;
3325 return objPtr;
3326 }
3327 }
3328
3329 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3330 int flags)
3331 {
3332 Jim_CallFrame *savedFramePtr;
3333 Jim_Obj *objPtr;
3334
3335 savedFramePtr = interp->framePtr;
3336 interp->framePtr = interp->topFramePtr;
3337 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3338 interp->framePtr = savedFramePtr;
3339
3340 return objPtr;
3341 }
3342
3343 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3344 {
3345 Jim_Obj *nameObjPtr, *varObjPtr;
3346
3347 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3348 Jim_IncrRefCount(nameObjPtr);
3349 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3350 Jim_DecrRefCount(interp, nameObjPtr);
3351 return varObjPtr;
3352 }
3353
3354 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3355 int flags)
3356 {
3357 Jim_CallFrame *savedFramePtr;
3358 Jim_Obj *objPtr;
3359
3360 savedFramePtr = interp->framePtr;
3361 interp->framePtr = interp->topFramePtr;
3362 objPtr = Jim_GetVariableStr(interp, name, flags);
3363 interp->framePtr = savedFramePtr;
3364
3365 return objPtr;
3366 }
3367
3368 /* Unset a variable.
3369 * Note: On success unset invalidates all the variable objects created
3370 * in the current call frame incrementing. */
3371 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3372 {
3373 const char *name;
3374 Jim_Var *varPtr;
3375 int err;
3376
3377 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3378 /* Check for [dict] syntax sugar. */
3379 if (err == JIM_DICT_SUGAR)
3380 return JimDictSugarSet(interp, nameObjPtr, NULL);
3381 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3382 Jim_AppendStrings(interp, Jim_GetResult(interp),
3383 "can't unset \"", nameObjPtr->bytes,
3384 "\": no such variable", NULL);
3385 return JIM_ERR; /* var not found */
3386 }
3387 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3388 /* If it's a link call UnsetVariable recursively */
3389 if (varPtr->linkFramePtr) {
3390 int retval;
3391
3392 Jim_CallFrame *savedCallFrame;
3393
3394 savedCallFrame = interp->framePtr;
3395 interp->framePtr = varPtr->linkFramePtr;
3396 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3397 interp->framePtr = savedCallFrame;
3398 if (retval != JIM_OK && flags & JIM_ERRMSG) {
3399 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3400 Jim_AppendStrings(interp, Jim_GetResult(interp),
3401 "can't unset \"", nameObjPtr->bytes,
3402 "\": no such variable", NULL);
3403 }
3404 return retval;
3405 } else {
3406 name = Jim_GetString(nameObjPtr, NULL);
3407 if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3408 != JIM_OK) return JIM_ERR;
3409 /* Change the callframe id, invalidating var lookup caching */
3410 JimChangeCallFrameId(interp, interp->framePtr);
3411 return JIM_OK;
3412 }
3413 }
3414
3415 /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
3416
3417 /* Given a variable name for [dict] operation syntax sugar,
3418 * this function returns two objects, the first with the name
3419 * of the variable to set, and the second with the rispective key.
3420 * For example "foo(bar)" will return objects with string repr. of
3421 * "foo" and "bar".
3422 *
3423 * The returned objects have refcount = 1. The function can't fail. */
3424 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3425 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3426 {
3427 const char *str, *p;
3428 char *t;
3429 int len, keyLen, nameLen;
3430 Jim_Obj *varObjPtr, *keyObjPtr;
3431
3432 str = Jim_GetString(objPtr, &len);
3433 p = strchr(str, '(');
3434 p++;
3435 keyLen = len-((p-str)+1);
3436 nameLen = (p-str)-1;
3437 /* Create the objects with the variable name and key. */
3438 t = Jim_Alloc(nameLen+1);
3439 memcpy(t, str, nameLen);
3440 t[nameLen] = '\0';
3441 varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3442
3443 t = Jim_Alloc(keyLen+1);
3444 memcpy(t, p, keyLen);
3445 t[keyLen] = '\0';
3446 keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3447
3448 Jim_IncrRefCount(varObjPtr);
3449 Jim_IncrRefCount(keyObjPtr);
3450 *varPtrPtr = varObjPtr;
3451 *keyPtrPtr = keyObjPtr;
3452 }
3453
3454 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3455 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3456 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3457 Jim_Obj *valObjPtr)
3458 {
3459 Jim_Obj *varObjPtr, *keyObjPtr;
3460 int err = JIM_OK;
3461
3462 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3463 err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3464 valObjPtr);
3465 Jim_DecrRefCount(interp, varObjPtr);
3466 Jim_DecrRefCount(interp, keyObjPtr);
3467 return err;
3468 }
3469
3470 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3471 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3472 {
3473 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3474
3475 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3476 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3477 if (!dictObjPtr) {
3478 resObjPtr = NULL;
3479 goto err;
3480 }
3481 if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3482 != JIM_OK) {
3483 resObjPtr = NULL;
3484 }
3485 err:
3486 Jim_DecrRefCount(interp, varObjPtr);
3487 Jim_DecrRefCount(interp, keyObjPtr);
3488 return resObjPtr;
3489 }
3490
3491 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3492
3493 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3494 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3495 Jim_Obj *dupPtr);
3496
3497 static Jim_ObjType dictSubstObjType = {
3498 "dict-substitution",
3499 FreeDictSubstInternalRep,
3500 DupDictSubstInternalRep,
3501 NULL,
3502 JIM_TYPE_NONE,
3503 };
3504
3505 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3506 {
3507 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3508 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3509 }
3510
3511 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3512 Jim_Obj *dupPtr)
3513 {
3514 JIM_NOTUSED(interp);
3515
3516 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3517 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3518 dupPtr->internalRep.dictSubstValue.indexObjPtr =
3519 srcPtr->internalRep.dictSubstValue.indexObjPtr;
3520 dupPtr->typePtr = &dictSubstObjType;
3521 }
3522
3523 /* This function is used to expand [dict get] sugar in the form
3524 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3525 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3526 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3527 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3528 * the [dict]ionary contained in variable VARNAME. */
3529 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3530 {
3531 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3532 Jim_Obj *substKeyObjPtr = NULL;
3533
3534 if (objPtr->typePtr != &dictSubstObjType) {
3535 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3536 Jim_FreeIntRep(interp, objPtr);
3537 objPtr->typePtr = &dictSubstObjType;
3538 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3539 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3540 }
3541 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3542 &substKeyObjPtr, JIM_NONE)
3543 != JIM_OK) {
3544 substKeyObjPtr = NULL;
3545 goto err;
3546 }
3547 Jim_IncrRefCount(substKeyObjPtr);
3548 dictObjPtr = Jim_GetVariable(interp,
3549 objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3550 if (!dictObjPtr) {
3551 resObjPtr = NULL;
3552 goto err;
3553 }
3554 if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3555 != JIM_OK) {
3556 resObjPtr = NULL;
3557 goto err;
3558 }
3559 err:
3560 if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3561 return resObjPtr;
3562 }
3563
3564 /* -----------------------------------------------------------------------------
3565 * CallFrame
3566 * ---------------------------------------------------------------------------*/
3567
3568 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3569 {
3570 Jim_CallFrame *cf;
3571 if (interp->freeFramesList) {
3572 cf = interp->freeFramesList;
3573 interp->freeFramesList = cf->nextFramePtr;
3574 } else {
3575 cf = Jim_Alloc(sizeof(*cf));
3576 cf->vars.table = NULL;
3577 }
3578
3579 cf->id = interp->callFrameEpoch++;
3580 cf->parentCallFrame = NULL;
3581 cf->argv = NULL;
3582 cf->argc = 0;
3583 cf->procArgsObjPtr = NULL;
3584 cf->procBodyObjPtr = NULL;
3585 cf->nextFramePtr = NULL;
3586 cf->staticVars = NULL;
3587 if (cf->vars.table == NULL)
3588 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3589 return cf;
3590 }
3591
3592 /* Used to invalidate every caching related to callframe stability. */
3593 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3594 {
3595 cf->id = interp->callFrameEpoch++;
3596 }
3597
3598 #define JIM_FCF_NONE 0 /* no flags */
3599 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3600 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3601 int flags)
3602 {
3603 if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3604 if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3605 if (!(flags & JIM_FCF_NOHT))
3606 Jim_FreeHashTable(&cf->vars);
3607 else {
3608 int i;
3609 Jim_HashEntry **table = cf->vars.table, *he;
3610
3611 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3612 he = table[i];
3613 while (he != NULL) {
3614 Jim_HashEntry *nextEntry = he->next;
3615 Jim_Var *varPtr = (void*) he->val;
3616
3617 Jim_DecrRefCount(interp, varPtr->objPtr);
3618 Jim_Free(he->val);
3619 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3620 Jim_Free(he);
3621 table[i] = NULL;
3622 he = nextEntry;
3623 }
3624 }
3625 cf->vars.used = 0;
3626 }
3627 cf->nextFramePtr = interp->freeFramesList;
3628 interp->freeFramesList = cf;
3629 }
3630
3631 /* -----------------------------------------------------------------------------
3632 * References
3633 * ---------------------------------------------------------------------------*/
3634
3635 /* References HashTable Type.
3636 *
3637 * Keys are jim_wide integers, dynamically allocated for now but in the
3638 * future it's worth to cache this 8 bytes objects. Values are poitners
3639 * to Jim_References. */
3640 static void JimReferencesHTValDestructor(void *interp, void *val)
3641 {
3642 Jim_Reference *refPtr = (void*) val;
3643
3644 Jim_DecrRefCount(interp, refPtr->objPtr);
3645 if (refPtr->finalizerCmdNamePtr != NULL) {
3646 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3647 }
3648 Jim_Free(val);
3649 }
3650
3651 unsigned int JimReferencesHTHashFunction(const void *key)
3652 {
3653 /* Only the least significant bits are used. */
3654 const jim_wide *widePtr = key;
3655 unsigned int intValue = (unsigned int) *widePtr;
3656 return Jim_IntHashFunction(intValue);
3657 }
3658
3659 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3660 {
3661 /* Only the least significant bits are used. */
3662 const jim_wide *widePtr = key;
3663 unsigned int intValue = (unsigned int) *widePtr;
3664 return intValue; /* identity function. */
3665 }
3666
3667 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3668 {
3669 void *copy = Jim_Alloc(sizeof(jim_wide));
3670 JIM_NOTUSED(privdata);
3671
3672 memcpy(copy, key, sizeof(jim_wide));
3673 return copy;
3674 }
3675
3676 int JimReferencesHTKeyCompare(void *privdata, const void *key1,
3677 const void *key2)
3678 {
3679 JIM_NOTUSED(privdata);
3680
3681 return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3682 }
3683
3684 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3685 {
3686 JIM_NOTUSED(privdata);
3687
3688 Jim_Free((void*)key);
3689 }
3690
3691 static Jim_HashTableType JimReferencesHashTableType = {
3692 JimReferencesHTHashFunction, /* hash function */
3693 JimReferencesHTKeyDup, /* key dup */
3694 NULL, /* val dup */
3695 JimReferencesHTKeyCompare, /* key compare */
3696 JimReferencesHTKeyDestructor, /* key destructor */
3697 JimReferencesHTValDestructor /* val destructor */
3698 };
3699
3700 /* -----------------------------------------------------------------------------
3701 * Reference object type and References API
3702 * ---------------------------------------------------------------------------*/
3703
3704 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3705
3706 static Jim_ObjType referenceObjType = {
3707 "reference",
3708 NULL,
3709 NULL,
3710 UpdateStringOfReference,
3711 JIM_TYPE_REFERENCES,
3712 };
3713
3714 void UpdateStringOfReference(struct Jim_Obj *objPtr)
3715 {
3716 int len;
3717 char buf[JIM_REFERENCE_SPACE+1];
3718 Jim_Reference *refPtr;
3719
3720 refPtr = objPtr->internalRep.refValue.refPtr;
3721 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3722 objPtr->bytes = Jim_Alloc(len+1);
3723 memcpy(objPtr->bytes, buf, len+1);
3724 objPtr->length = len;
3725 }
3726
3727 /* returns true if 'c' is a valid reference tag character.
3728 * i.e. inside the range [_a-zA-Z0-9] */
3729 static int isrefchar(int c)
3730 {
3731 if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3732 (c >= '0' && c <= '9')) return 1;
3733 return 0;
3734 }
3735
3736 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3737 {
3738 jim_wide wideValue;
3739 int i, len;
3740 const char *str, *start, *end;
3741 char refId[21];
3742 Jim_Reference *refPtr;
3743 Jim_HashEntry *he;
3744
3745 /* Get the string representation */
3746 str = Jim_GetString(objPtr, &len);
3747 /* Check if it looks like a reference */
3748 if (len < JIM_REFERENCE_SPACE) goto badformat;
3749 /* Trim spaces */
3750 start = str;
3751 end = str+len-1;
3752 while (*start == ' ') start++;
3753 while (*end == ' ' && end > start) end--;
3754 if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3755 /* <reference.<1234567>.%020> */
3756 if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3757 if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3758 /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3759 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3760 if (!isrefchar(start[12+i])) goto badformat;
3761 }
3762 /* Extract info from the refernece. */
3763 memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3764 refId[20] = '\0';
3765 /* Try to convert the ID into a jim_wide */
3766 if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3767 /* Check if the reference really exists! */
3768 he = Jim_FindHashEntry(&interp->references, &wideValue);
3769 if (he == NULL) {
3770 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3771 Jim_AppendStrings(interp, Jim_GetResult(interp),
3772 "Invalid reference ID \"", str, "\"", NULL);
3773 return JIM_ERR;
3774 }
3775 refPtr = he->val;
3776 /* Free the old internal repr and set the new one. */
3777 Jim_FreeIntRep(interp, objPtr);
3778 objPtr->typePtr = &referenceObjType;
3779 objPtr->internalRep.refValue.id = wideValue;
3780 objPtr->internalRep.refValue.refPtr = refPtr;
3781 return JIM_OK;
3782
3783 badformat:
3784 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3785 Jim_AppendStrings(interp, Jim_GetResult(interp),
3786 "expected reference but got \"", str, "\"", NULL);
3787 return JIM_ERR;
3788 }
3789
3790 /* Returns a new reference pointing to objPtr, having cmdNamePtr
3791 * as finalizer command (or NULL if there is no finalizer).
3792 * The returned reference object has refcount = 0. */
3793 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
3794 Jim_Obj *cmdNamePtr)
3795 {
3796 struct Jim_Reference *refPtr;
3797 jim_wide wideValue = interp->referenceNextId;
3798 Jim_Obj *refObjPtr;
3799 const char *tag;
3800 int tagLen, i;
3801
3802 /* Perform the Garbage Collection if needed. */
3803 Jim_CollectIfNeeded(interp);
3804
3805 refPtr = Jim_Alloc(sizeof(*refPtr));
3806 refPtr->objPtr = objPtr;
3807 Jim_IncrRefCount(objPtr);
3808 refPtr->finalizerCmdNamePtr = cmdNamePtr;
3809 if (cmdNamePtr)
3810 Jim_IncrRefCount(cmdNamePtr);
3811 Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
3812 refObjPtr = Jim_NewObj(interp);
3813 refObjPtr->typePtr = &referenceObjType;
3814 refObjPtr->bytes = NULL;
3815 refObjPtr->internalRep.refValue.id = interp->referenceNextId;
3816 refObjPtr->internalRep.refValue.refPtr = refPtr;
3817 interp->referenceNextId++;
3818 /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
3819 * that does not pass the 'isrefchar' test is replaced with '_' */
3820 tag = Jim_GetString(tagPtr, &tagLen);
3821 if (tagLen > JIM_REFERENCE_TAGLEN)
3822 tagLen = JIM_REFERENCE_TAGLEN;
3823 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3824 if (i < tagLen)
3825 refPtr->tag[i] = tag[i];
3826 else
3827 refPtr->tag[i] = '_';
3828 }
3829 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
3830 return refObjPtr;
3831 }
3832
3833 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
3834 {
3835 if (objPtr->typePtr != &referenceObjType &&
3836 SetReferenceFromAny(interp, objPtr) == JIM_ERR)
3837 return NULL;
3838 return objPtr->internalRep.refValue.refPtr;
3839 }
3840
3841 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
3842 {
3843 Jim_Reference *refPtr;
3844
3845 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3846 return JIM_ERR;
3847 Jim_IncrRefCount(cmdNamePtr);
3848 if (refPtr->finalizerCmdNamePtr)
3849 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3850 refPtr->finalizerCmdNamePtr = cmdNamePtr;
3851 return JIM_OK;
3852 }
3853
3854 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
3855 {
3856 Jim_Reference *refPtr;
3857
3858 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3859 return JIM_ERR;
3860 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
3861 return JIM_OK;
3862 }
3863
3864 /* -----------------------------------------------------------------------------
3865 * References Garbage Collection
3866 * ---------------------------------------------------------------------------*/
3867
3868 /* This the hash table type for the "MARK" phase of the GC */
3869 static Jim_HashTableType JimRefMarkHashTableType = {
3870 JimReferencesHTHashFunction, /* hash function */
3871 JimReferencesHTKeyDup, /* key dup */
3872 NULL, /* val dup */
3873 JimReferencesHTKeyCompare, /* key compare */
3874 JimReferencesHTKeyDestructor, /* key destructor */
3875 NULL /* val destructor */
3876 };
3877
3878 /* #define JIM_DEBUG_GC 1 */
3879
3880 /* Performs the garbage collection. */
3881 int Jim_Collect(Jim_Interp *interp)
3882 {
3883 Jim_HashTable marks;
3884 Jim_HashTableIterator *htiter;
3885 Jim_HashEntry *he;
3886 Jim_Obj *objPtr;
3887 int collected = 0;
3888
3889 /* Avoid recursive calls */
3890 if (interp->lastCollectId == -1) {
3891 /* Jim_Collect() already running. Return just now. */
3892 return 0;
3893 }
3894 interp->lastCollectId = -1;
3895
3896 /* Mark all the references found into the 'mark' hash table.
3897 * The references are searched in every live object that
3898 * is of a type that can contain references. */
3899 Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
3900 objPtr = interp->liveList;
3901 while(objPtr) {
3902 if (objPtr->typePtr == NULL ||
3903 objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
3904 const char *str, *p;
3905 int len;
3906
3907 /* If the object is of type reference, to get the
3908 * Id is simple... */
3909 if (objPtr->typePtr == &referenceObjType) {
3910 Jim_AddHashEntry(&marks,
3911 &objPtr->internalRep.refValue.id, NULL);
3912 #ifdef JIM_DEBUG_GC
3913 fprintf(interp->stdout_,
3914 "MARK (reference): %d refcount: %d" JIM_NL,
3915 (int) objPtr->internalRep.refValue.id,
3916 objPtr->refCount);
3917 #endif
3918 objPtr = objPtr->nextObjPtr;
3919 continue;
3920 }
3921 /* Get the string repr of the object we want
3922 * to scan for references. */
3923 p = str = Jim_GetString(objPtr, &len);
3924 /* Skip objects too little to contain references. */
3925 if (len < JIM_REFERENCE_SPACE) {
3926 objPtr = objPtr->nextObjPtr;
3927 continue;
3928 }
3929 /* Extract references from the object string repr. */
3930 while(1) {
3931 int i;
3932 jim_wide id;
3933 char buf[21];
3934
3935 if ((p = strstr(p, "<reference.<")) == NULL)
3936 break;
3937 /* Check if it's a valid reference. */
3938 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
3939 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
3940 for (i = 21; i <= 40; i++)
3941 if (!isdigit((int)p[i]))
3942 break;
3943 /* Get the ID */
3944 memcpy(buf, p+21, 20);
3945 buf[20] = '\0';
3946 Jim_StringToWide(buf, &id, 10);
3947
3948 /* Ok, a reference for the given ID
3949 * was found. Mark it. */
3950 Jim_AddHashEntry(&marks, &id, NULL);
3951 #ifdef JIM_DEBUG_GC
3952 fprintf(interp->stdout_,"MARK: %d" JIM_NL, (int)id);
3953 #endif
3954 p += JIM_REFERENCE_SPACE;
3955 }
3956 }
3957 objPtr = objPtr->nextObjPtr;
3958 }
3959
3960 /* Run the references hash table to destroy every reference that
3961 * is not referenced outside (not present in the mark HT). */
3962 htiter = Jim_GetHashTableIterator(&interp->references);
3963 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
3964 const jim_wide *refId;
3965 Jim_Reference *refPtr;
3966
3967 refId = he->key;
3968 /* Check if in the mark phase we encountered
3969 * this reference. */
3970 if (Jim_FindHashEntry(&marks, refId) == NULL) {
3971 #ifdef JIM_DEBUG_GC
3972 fprintf(interp->stdout_,"COLLECTING %d" JIM_NL, (int)*refId);
3973 #endif
3974 collected++;
3975 /* Drop the reference, but call the
3976 * finalizer first if registered. */
3977 refPtr = he->val;
3978 if (refPtr->finalizerCmdNamePtr) {
3979 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
3980 Jim_Obj *objv[3], *oldResult;
3981
3982 JimFormatReference(refstr, refPtr, *refId);
3983
3984 objv[0] = refPtr->finalizerCmdNamePtr;
3985 objv[1] = Jim_NewStringObjNoAlloc(interp,
3986 refstr, 32);
3987 objv[2] = refPtr->objPtr;
3988 Jim_IncrRefCount(objv[0]);
3989 Jim_IncrRefCount(objv[1]);
3990 Jim_IncrRefCount(objv[2]);
3991
3992 /* Drop the reference itself */
3993 Jim_DeleteHashEntry(&interp->references, refId);
3994
3995 /* Call the finalizer. Errors ignored. */
3996 oldResult = interp->result;
3997 Jim_IncrRefCount(oldResult);
3998 Jim_EvalObjVector(interp, 3, objv);
3999 Jim_SetResult(interp, oldResult);
4000 Jim_DecrRefCount(interp, oldResult);
4001
4002 Jim_DecrRefCount(interp, objv[0]);
4003 Jim_DecrRefCount(interp, objv[1]);
4004 Jim_DecrRefCount(interp, objv[2]);
4005 } else {
4006 Jim_DeleteHashEntry(&interp->references, refId);
4007 }
4008 }
4009 }
4010 Jim_FreeHashTableIterator(htiter);
4011 Jim_FreeHashTable(&marks);
4012 interp->lastCollectId = interp->referenceNextId;
4013 interp->lastCollectTime = time(NULL);
4014 return collected;
4015 }
4016
4017 #define JIM_COLLECT_ID_PERIOD 5000
4018 #define JIM_COLLECT_TIME_PERIOD 300
4019
4020 void Jim_CollectIfNeeded(Jim_Interp *interp)
4021 {
4022 jim_wide elapsedId;
4023 int elapsedTime;
4024
4025 elapsedId = interp->referenceNextId - interp->lastCollectId;
4026 elapsedTime = time(NULL) - interp->lastCollectTime;
4027
4028
4029 if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4030 elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4031 Jim_Collect(interp);
4032 }
4033 }
4034
4035 /* -----------------------------------------------------------------------------
4036 * Interpreter related functions
4037 * ---------------------------------------------------------------------------*/
4038
4039 Jim_Interp *Jim_CreateInterp(void)
4040 {
4041 Jim_Interp *i = Jim_Alloc(sizeof(*i));
4042 Jim_Obj *pathPtr;
4043
4044 i->errorLine = 0;
4045 i->errorFileName = Jim_StrDup("");
4046 i->numLevels = 0;
4047 i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4048 i->returnCode = JIM_OK;
4049 i->exitCode = 0;
4050 i->procEpoch = 0;
4051 i->callFrameEpoch = 0;
4052 i->liveList = i->freeList = NULL;
4053 i->scriptFileName = Jim_StrDup("");
4054 i->referenceNextId = 0;
4055 i->lastCollectId = 0;
4056 i->lastCollectTime = time(NULL);
4057 i->freeFramesList = NULL;
4058 i->prngState = NULL;
4059 i->evalRetcodeLevel = -1;
4060 i->stdin_ = stdin;
4061 i->stdout_ = stdout;
4062 i->stderr_ = stderr;
4063
4064 /* Note that we can create objects only after the
4065 * interpreter liveList and freeList pointers are
4066 * initialized to NULL. */
4067 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4068 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4069 Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4070 NULL);
4071 Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4072 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4073 Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4074 i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4075 i->emptyObj = Jim_NewEmptyStringObj(i);
4076 i->result = i->emptyObj;
4077 i->stackTrace = Jim_NewListObj(i, NULL, 0);
4078 i->unknown = Jim_NewStringObj(i, "unknown", -1);
4079 Jim_IncrRefCount(i->emptyObj);
4080 Jim_IncrRefCount(i->result);
4081 Jim_IncrRefCount(i->stackTrace);
4082 Jim_IncrRefCount(i->unknown);
4083
4084 /* Initialize key variables every interpreter should contain */
4085 pathPtr = Jim_NewStringObj(i, "./", -1);
4086 Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4087 Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4088
4089 /* Export the core API to extensions */
4090 JimRegisterCoreApi(i);
4091 return i;
4092 }
4093
4094 /* This is the only function Jim exports directly without
4095 * to use the STUB system. It is only used by embedders
4096 * in order to get an interpreter with the Jim API pointers
4097 * registered. */
4098 Jim_Interp *ExportedJimCreateInterp(void)
4099 {
4100 return Jim_CreateInterp();
4101 }
4102
4103 void Jim_FreeInterp(Jim_Interp *i)
4104 {
4105 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4106 Jim_Obj *objPtr, *nextObjPtr;
4107
4108 Jim_DecrRefCount(i, i->emptyObj);
4109 Jim_DecrRefCount(i, i->result);
4110 Jim_DecrRefCount(i, i->stackTrace);
4111 Jim_DecrRefCount(i, i->unknown);
4112 Jim_Free((void*)i->errorFileName);
4113 Jim_Free((void*)i->scriptFileName);
4114 Jim_FreeHashTable(&i->commands);
4115 Jim_FreeHashTable(&i->references);
4116 Jim_FreeHashTable(&i->stub);
4117 Jim_FreeHashTable(&i->assocData);
4118 Jim_FreeHashTable(&i->packages);
4119 Jim_Free(i->prngState);
4120 /* Free the call frames list */
4121 while(cf) {
4122 prevcf = cf->parentCallFrame;
4123 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4124 cf = prevcf;
4125 }
4126 /* Check that the live object list is empty, otherwise
4127 * there is a memory leak. */
4128 if (i->liveList != NULL) {
4129 Jim_Obj *objPtr = i->liveList;
4130
4131 fprintf(i->stdout_,JIM_NL "-------------------------------------" JIM_NL);
4132 fprintf(i->stdout_,"Objects still in the free list:" JIM_NL);
4133 while(objPtr) {
4134 const char *type = objPtr->typePtr ?
4135 objPtr->typePtr->name : "";
4136 fprintf(i->stdout_,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4137 objPtr, type,
4138 objPtr->bytes ? objPtr->bytes
4139 : "(null)", objPtr->refCount);
4140 if (objPtr->typePtr == &sourceObjType) {
4141 fprintf(i->stdout_, "FILE %s LINE %d" JIM_NL,
4142 objPtr->internalRep.sourceValue.fileName,
4143 objPtr->internalRep.sourceValue.lineNumber);
4144 }
4145 objPtr = objPtr->nextObjPtr;
4146 }
4147 fprintf(stdout, "-------------------------------------" JIM_NL JIM_NL);
4148 Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4149 }
4150 /* Free all the freed objects. */
4151 objPtr = i->freeList;
4152 while (objPtr) {
4153 nextObjPtr = objPtr->nextObjPtr;
4154 Jim_Free(objPtr);
4155 objPtr = nextObjPtr;
4156 }
4157 /* Free cached CallFrame structures */
4158 cf = i->freeFramesList;
4159 while(cf) {
4160 nextcf = cf->nextFramePtr;
4161 if (cf->vars.table != NULL)
4162 Jim_Free(cf->vars.table);
4163 Jim_Free(cf);
4164 cf = nextcf;
4165 }
4166 /* Free the sharedString hash table. Make sure to free it
4167 * after every other Jim_Object was freed. */
4168 Jim_FreeHashTable(&i->sharedStrings);
4169 /* Free the interpreter structure. */
4170 Jim_Free(i);
4171 }
4172
4173 /* Store the call frame relative to the level represented by
4174 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4175 * level is assumed to be '1'.
4176 *
4177 * If a newLevelptr int pointer is specified, the function stores
4178 * the absolute level integer value of the new target callframe into
4179 * *newLevelPtr. (this is used to adjust interp->numLevels
4180 * in the implementation of [uplevel], so that [info level] will
4181 * return a correct information).
4182 *
4183 * This function accepts the 'level' argument in the form
4184 * of the commands [uplevel] and [upvar].
4185 *
4186 * For a function accepting a relative integer as level suitable
4187 * for implementation of [info level ?level?] check the
4188 * GetCallFrameByInteger() function. */
4189 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4190 Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4191 {
4192 long level;
4193 const char *str;
4194 Jim_CallFrame *framePtr;
4195
4196 if (newLevelPtr) *newLevelPtr = interp->numLevels;
4197 if (levelObjPtr) {
4198 str = Jim_GetString(levelObjPtr, NULL);
4199 if (str[0] == '#') {
4200 char *endptr;
4201 /* speedup for the toplevel (level #0) */
4202 if (str[1] == '0' && str[2] == '\0') {
4203 if (newLevelPtr) *newLevelPtr = 0;
4204 *framePtrPtr = interp->topFramePtr;
4205 return JIM_OK;
4206 }
4207
4208 level = strtol(str+1, &endptr, 0);
4209 if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4210 goto badlevel;
4211 /* An 'absolute' level is converted into the
4212 * 'number of levels to go back' format. */
4213 level = interp->numLevels - level;
4214 if (level < 0) goto badlevel;
4215 } else {
4216 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4217 goto badlevel;
4218 }
4219 } else {
4220 str = "1"; /* Needed to format the error message. */
4221 level = 1;
4222 }
4223 /* Lookup */
4224 framePtr = interp->framePtr;
4225 if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4226 while (level--) {
4227 framePtr = framePtr->parentCallFrame;
4228 if (framePtr == NULL) goto badlevel;
4229 }
4230 *framePtrPtr = framePtr;
4231 return JIM_OK;
4232 badlevel:
4233 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4234 Jim_AppendStrings(interp, Jim_GetResult(interp),
4235 "bad level \"", str, "\"", NULL);
4236 return JIM_ERR;
4237 }
4238
4239 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4240 * as a relative integer like in the [info level ?level?] command. */
4241 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4242 Jim_CallFrame **framePtrPtr)
4243 {
4244 jim_wide level;
4245 jim_wide relLevel; /* level relative to the current one. */
4246 Jim_CallFrame *framePtr;
4247
4248 if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4249 goto badlevel;
4250 if (level > 0) {
4251 /* An 'absolute' level is converted into the
4252 * 'number of levels to go back' format. */
4253 relLevel = interp->numLevels - level;
4254 } else {
4255 relLevel = -level;
4256 }
4257 /* Lookup */
4258 framePtr = interp->framePtr;
4259 while (relLevel--) {
4260 framePtr = framePtr->parentCallFrame;
4261 if (framePtr == NULL) goto badlevel;
4262 }
4263 *framePtrPtr = framePtr;
4264 return JIM_OK;
4265 badlevel:
4266 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4267 Jim_AppendStrings(interp, Jim_GetResult(interp),
4268 "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4269 return JIM_ERR;
4270 }
4271
4272 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4273 {
4274 Jim_Free((void*)interp->errorFileName);
4275 interp->errorFileName = Jim_StrDup(filename);
4276 }
4277
4278 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4279 {
4280 interp->errorLine = linenr;
4281 }
4282
4283 static void JimResetStackTrace(Jim_Interp *interp)
4284 {
4285 Jim_DecrRefCount(interp, interp->stackTrace);
4286 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4287 Jim_IncrRefCount(interp->stackTrace);
4288 }
4289
4290 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4291 const char *filename, int linenr)
4292 {
4293 if (Jim_IsShared(interp->stackTrace)) {
4294 interp->stackTrace =
4295 Jim_DuplicateObj(interp, interp->stackTrace);
4296 Jim_IncrRefCount(interp->stackTrace);
4297 }
4298 Jim_ListAppendElement(interp, interp->stackTrace,
4299 Jim_NewStringObj(interp, procname, -1));
4300 Jim_ListAppendElement(interp, interp->stackTrace,
4301 Jim_NewStringObj(interp, filename, -1));
4302 Jim_ListAppendElement(interp, interp->stackTrace,
4303 Jim_NewIntObj(interp, linenr));
4304 }
4305
4306 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4307 {
4308 AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4309 assocEntryPtr->delProc = delProc;
4310 assocEntryPtr->data = data;
4311 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4312 }
4313
4314 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4315 {
4316 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4317 if (entryPtr != NULL) {
4318 AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4319 return assocEntryPtr->data;
4320 }
4321 return NULL;
4322 }
4323
4324 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4325 {
4326 return Jim_DeleteHashEntry(&interp->assocData, key);
4327 }
4328
4329 int Jim_GetExitCode(Jim_Interp *interp) {
4330 return interp->exitCode;
4331 }
4332
4333 FILE *Jim_SetStdin(Jim_Interp *interp, FILE *fp)
4334 {
4335 if (fp != NULL) interp->stdin_ = fp;
4336 return interp->stdin_;
4337 }
4338
4339 FILE *Jim_SetStdout(Jim_Interp *interp, FILE *fp)
4340 {
4341 if (fp != NULL) interp->stdout_ = fp;
4342 return interp->stdout_;
4343 }
4344
4345 FILE *Jim_SetStderr(Jim_Interp *interp, FILE *fp)
4346 {
4347 if (fp != NULL) interp->stderr_ = fp;
4348 return interp->stderr_;
4349 }
4350
4351 /* -----------------------------------------------------------------------------
4352 * Shared strings.
4353 * Every interpreter has an hash table where to put shared dynamically
4354 * allocate strings that are likely to be used a lot of times.
4355 * For example, in the 'source' object type, there is a pointer to
4356 * the filename associated with that object. Every script has a lot
4357 * of this objects with the identical file name, so it is wise to share
4358 * this info.
4359 *
4360 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4361 * returns the pointer to the shared string. Every time a reference
4362 * to the string is no longer used, the user should call
4363 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4364 * a given string, it is removed from the hash table.
4365 * ---------------------------------------------------------------------------*/
4366 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4367 {
4368 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4369
4370 if (he == NULL) {
4371 char *strCopy = Jim_StrDup(str);
4372
4373 Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4374 return strCopy;
4375 } else {
4376 long refCount = (long) he->val;
4377
4378 refCount++;
4379 he->val = (void*) refCount;
4380 return he->key;
4381 }
4382 }
4383
4384 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4385 {
4386 long refCount;
4387 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4388
4389 if (he == NULL)
4390 Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4391 "unknown shared string '%s'", str);
4392 refCount = (long) he->val;
4393 refCount--;
4394 if (refCount == 0) {
4395 Jim_DeleteHashEntry(&interp->sharedStrings, str);
4396 } else {
4397 he->val = (void*) refCount;
4398 }
4399 }
4400
4401 /* -----------------------------------------------------------------------------
4402 * Integer object
4403 * ---------------------------------------------------------------------------*/
4404 #define JIM_INTEGER_SPACE 24
4405
4406 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4407 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4408
4409 static Jim_ObjType intObjType = {
4410 "int",
4411 NULL,
4412 NULL,
4413 UpdateStringOfInt,
4414 JIM_TYPE_NONE,
4415 };
4416
4417 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4418 {
4419 int len;
4420 char buf[JIM_INTEGER_SPACE+1];
4421
4422 len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4423 objPtr->bytes = Jim_Alloc(len+1);
4424 memcpy(objPtr->bytes, buf, len+1);
4425 objPtr->length = len;
4426 }
4427
4428 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4429 {
4430 jim_wide wideValue;
4431 const char *str;
4432
4433 /* Get the string representation */
4434 str = Jim_GetString(objPtr, NULL);
4435 /* Try to convert into a jim_wide */
4436 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4437 if (flags & JIM_ERRMSG) {
4438 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4439 Jim_AppendStrings(interp, Jim_GetResult(interp),
4440 "expected integer but got \"", str, "\"", NULL);
4441 }
4442 return JIM_ERR;
4443 }
4444 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4445 errno == ERANGE) {
4446 Jim_SetResultString(interp,
4447 "Integer value too big to be represented", -1);
4448 return JIM_ERR;
4449 }
4450 /* Free the old internal repr and set the new one. */
4451 Jim_FreeIntRep(interp, objPtr);
4452 objPtr->typePtr = &intObjType;
4453 objPtr->internalRep.wideValue = wideValue;
4454 return JIM_OK;
4455 }
4456
4457 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4458 {
4459 if (objPtr->typePtr != &intObjType &&
4460 SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4461 return JIM_ERR;
4462 *widePtr = objPtr->internalRep.wideValue;
4463 return JIM_OK;
4464 }
4465
4466 /* Get a wide but does not set an error if the format is bad. */
4467 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4468 jim_wide *widePtr)
4469 {
4470 if (objPtr->typePtr != &intObjType &&
4471 SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4472 return JIM_ERR;
4473 *widePtr = objPtr->internalRep.wideValue;
4474 return JIM_OK;
4475 }
4476
4477 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4478 {
4479 jim_wide wideValue;
4480 int retval;
4481
4482 retval = Jim_GetWide(interp, objPtr, &wideValue);
4483 if (retval == JIM_OK) {
4484 *longPtr = (long) wideValue;
4485 return JIM_OK;
4486 }
4487 return JIM_ERR;
4488 }
4489
4490 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4491 {
4492 if (Jim_IsShared(objPtr))
4493 Jim_Panic(interp,"Jim_SetWide called with shared object");
4494 if (objPtr->typePtr != &intObjType) {
4495 Jim_FreeIntRep(interp, objPtr);
4496 objPtr->typePtr = &intObjType;
4497 }
4498 Jim_InvalidateStringRep(objPtr);
4499 objPtr->internalRep.wideValue = wideValue;
4500 }
4501
4502 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4503 {
4504 Jim_Obj *objPtr;
4505
4506 objPtr = Jim_NewObj(interp);
4507 objPtr->typePtr = &intObjType;
4508 objPtr->bytes = NULL;
4509 objPtr->internalRep.wideValue = wideValue;
4510 return objPtr;
4511 }
4512
4513 /* -----------------------------------------------------------------------------
4514 * Double object
4515 * ---------------------------------------------------------------------------*/
4516 #define JIM_DOUBLE_SPACE 30
4517
4518 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4519 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4520
4521 static Jim_ObjType doubleObjType = {
4522 "double",
4523 NULL,
4524 NULL,
4525 UpdateStringOfDouble,
4526 JIM_TYPE_NONE,
4527 };
4528
4529 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4530 {
4531 int len;
4532 char buf[JIM_DOUBLE_SPACE+1];
4533
4534 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4535 objPtr->bytes = Jim_Alloc(len+1);
4536 memcpy(objPtr->bytes, buf, len+1);
4537 objPtr->length = len;
4538 }
4539
4540 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4541 {
4542 double doubleValue;
4543 const char *str;
4544
4545 /* Get the string representation */
4546 str = Jim_GetString(objPtr, NULL);
4547 /* Try to convert into a double */
4548 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4549 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4550 Jim_AppendStrings(interp, Jim_GetResult(interp),
4551 "expected number but got '", str, "'", NULL);
4552 return JIM_ERR;
4553 }
4554 /* Free the old internal repr and set the new one. */
4555 Jim_FreeIntRep(interp, objPtr);
4556 objPtr->typePtr = &doubleObjType;
4557 objPtr->internalRep.doubleValue = doubleValue;
4558 return JIM_OK;
4559 }
4560
4561 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4562 {
4563 if (objPtr->typePtr != &doubleObjType &&
4564 SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4565 return JIM_ERR;
4566 *doublePtr = objPtr->internalRep.doubleValue;
4567 return JIM_OK;
4568 }
4569
4570 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4571 {
4572 if (Jim_IsShared(objPtr))
4573 Jim_Panic(interp,"Jim_SetDouble called with shared object");
4574 if (objPtr->typePtr != &doubleObjType) {
4575 Jim_FreeIntRep(interp, objPtr);
4576 objPtr->typePtr = &doubleObjType;
4577 }
4578 Jim_InvalidateStringRep(objPtr);
4579 objPtr->internalRep.doubleValue = doubleValue;
4580 }
4581
4582 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4583 {
4584 Jim_Obj *objPtr;
4585
4586 objPtr = Jim_NewObj(interp);
4587 objPtr->typePtr = &doubleObjType;
4588 objPtr->bytes = NULL;
4589 objPtr->internalRep.doubleValue = doubleValue;
4590 return objPtr;
4591 }
4592
4593 /* -----------------------------------------------------------------------------
4594 * List object
4595 * ---------------------------------------------------------------------------*/
4596 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4597 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4598 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4599 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4600 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4601
4602 /* Note that while the elements of the list may contain references,
4603 * the list object itself can't. This basically means that the
4604 * list object string representation as a whole can't contain references
4605 * that are not presents in the single elements. */
4606 static Jim_ObjType listObjType = {
4607 "list",
4608 FreeListInternalRep,
4609 DupListInternalRep,
4610 UpdateStringOfList,
4611 JIM_TYPE_NONE,
4612 };
4613
4614 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4615 {
4616 int i;
4617
4618 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4619 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4620 }
4621 Jim_Free(objPtr->internalRep.listValue.ele);
4622 }
4623
4624 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4625 {
4626 int i;
4627 JIM_NOTUSED(interp);
4628
4629 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4630 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4631 dupPtr->internalRep.listValue.ele =
4632 Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4633 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4634 sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4635 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4636 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4637 }
4638 dupPtr->typePtr = &listObjType;
4639 }
4640
4641 /* The following function checks if a given string can be encoded
4642 * into a list element without any kind of quoting, surrounded by braces,
4643 * or using escapes to quote. */
4644 #define JIM_ELESTR_SIMPLE 0
4645 #define JIM_ELESTR_BRACE 1
4646 #define JIM_ELESTR_QUOTE 2
4647 static int ListElementQuotingType(const char *s, int len)
4648 {
4649 int i, level, trySimple = 1;
4650
4651 /* Try with the SIMPLE case */
4652 if (len == 0) return JIM_ELESTR_BRACE;
4653 if (s[0] == '"' || s[0] == '{') {
4654 trySimple = 0;
4655 goto testbrace;
4656 }
4657 for (i = 0; i < len; i++) {
4658 switch(s[i]) {
4659 case ' ':
4660 case '$':
4661 case '"':
4662 case '[':
4663 case ']':
4664 case ';':
4665 case '\\':
4666 case '\r':
4667 case '\n':
4668 case '\t':
4669 case '\f':
4670 case '\v':
4671 trySimple = 0;
4672 case '{':
4673 case '}':
4674 goto testbrace;
4675 }
4676 }
4677 return JIM_ELESTR_SIMPLE;
4678
4679 testbrace:
4680 /* Test if it's possible to do with braces */
4681 if (s[len-1] == '\\' ||
4682 s[len-1] == ']') return JIM_ELESTR_QUOTE;
4683 level = 0;
4684 for (i = 0; i < len; i++) {
4685 switch(s[i]) {
4686 case '{': level++; break;
4687 case '}': level--;
4688 if (level < 0) return JIM_ELESTR_QUOTE;
4689 break;
4690 case '\\':
4691 if (s[i+1] == '\n')
4692 return JIM_ELESTR_QUOTE;
4693 else
4694 if (s[i+1] != '\0') i++;
4695 break;
4696 }
4697 }
4698 if (level == 0) {
4699 if (!trySimple) return JIM_ELESTR_BRACE;
4700 for (i = 0; i < len; i++) {
4701 switch(s[i]) {
4702 case ' ':
4703 case '$':
4704 case '"':
4705 case '[':
4706 case ']':
4707 case ';':
4708 case '\\':
4709 case '\r':
4710 case '\n':
4711 case '\t':
4712 case '\f':
4713 case '\v':
4714 return JIM_ELESTR_BRACE;
4715 break;
4716 }
4717 }
4718 return JIM_ELESTR_SIMPLE;
4719 }
4720 return JIM_ELESTR_QUOTE;
4721 }
4722
4723 /* Returns the malloc-ed representation of a string
4724 * using backslash to quote special chars. */
4725 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4726 {
4727 char *q = Jim_Alloc(len*2+1), *p;
4728
4729 p = q;
4730 while(*s) {
4731 switch (*s) {
4732 case ' ':
4733 case '$':
4734 case '"':
4735 case '[':
4736 case ']':
4737 case '{':
4738 case '}':
4739 case ';':
4740 case '\\':
4741 *p++ = '\\';
4742 *p++ = *s++;
4743 break;
4744 case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4745 case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4746 case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4747 case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4748 case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4749 default:
4750 *p++ = *s++;
4751 break;
4752 }
4753 }
4754 *p = '\0';
4755 *qlenPtr = p-q;
4756 return q;
4757 }
4758
4759 void UpdateStringOfList(struct Jim_Obj *objPtr)
4760 {
4761 int i, bufLen, realLength;
4762 const char *strRep;
4763 char *p;
4764 int *quotingType;
4765 Jim_Obj **ele = objPtr->internalRep.listValue.ele;
4766
4767 /* (Over) Estimate the space needed. */
4768 quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
4769 bufLen = 0;
4770 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4771 int len;
4772
4773 strRep = Jim_GetString(ele[i], &len);
4774 quotingType[i] = ListElementQuotingType(strRep, len);
4775 switch (quotingType[i]) {
4776 case JIM_ELESTR_SIMPLE: bufLen += len; break;
4777 case JIM_ELESTR_BRACE: bufLen += len+2; break;
4778 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
4779 }
4780 bufLen++; /* elements separator. */
4781 }
4782 bufLen++;
4783
4784 /* Generate the string rep. */
4785 p = objPtr->bytes = Jim_Alloc(bufLen+1);
4786 realLength = 0;
4787 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4788 int len, qlen;
4789 const char *strRep = Jim_GetString(ele[i], &len);
4790 char *q;
4791
4792 switch(quotingType[i]) {
4793 case JIM_ELESTR_SIMPLE:
4794 memcpy(p, strRep, len);
4795 p += len;
4796 realLength += len;
4797 break;
4798 case JIM_ELESTR_BRACE:
4799 *p++ = '{';
4800 memcpy(p, strRep, len);
4801 p += len;
4802 *p++ = '}';
4803 realLength += len+2;
4804 break;
4805 case JIM_ELESTR_QUOTE:
4806 q = BackslashQuoteString(strRep, len, &qlen);
4807 memcpy(p, q, qlen);
4808 Jim_Free(q);
4809 p += qlen;
4810 realLength += qlen;
4811 break;
4812 }
4813 /* Add a separating space */
4814 if (i+1 != objPtr->internalRep.listValue.len) {
4815 *p++ = ' ';
4816 realLength ++;
4817 }
4818 }
4819 *p = '\0'; /* nul term. */
4820 objPtr->length = realLength;
4821 Jim_Free(quotingType);
4822 }
4823
4824 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
4825 {
4826 struct JimParserCtx parser;
4827 const char *str;
4828 int strLen;
4829
4830 /* Get the string representation */
4831 str = Jim_GetString(objPtr, &strLen);
4832
4833 /* Free the old internal repr just now and initialize the
4834 * new one just now. The string->list conversion can't fail. */
4835 Jim_FreeIntRep(interp, objPtr);
4836 objPtr->typePtr = &listObjType;
4837 objPtr->internalRep.listValue.len = 0;
4838 objPtr->internalRep.listValue.maxLen = 0;
4839 objPtr->internalRep.listValue.ele = NULL;
4840
4841 /* Convert into a list */
4842 JimParserInit(&parser, str, strLen, 1);
4843 while(!JimParserEof(&parser)) {
4844 char *token;
4845 int tokenLen, type;
4846 Jim_Obj *elementPtr;
4847
4848 JimParseList(&parser);
4849 if (JimParserTtype(&parser) != JIM_TT_STR &&
4850 JimParserTtype(&parser) != JIM_TT_ESC)
4851 continue;
4852 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
4853 elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
4854 ListAppendElement(objPtr, elementPtr);
4855 }
4856 return JIM_OK;
4857 }
4858
4859 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
4860 int len)
4861 {
4862 Jim_Obj *objPtr;
4863 int i;
4864
4865 objPtr = Jim_NewObj(interp);
4866 objPtr->typePtr = &listObjType;
4867 objPtr->bytes = NULL;
4868 objPtr->internalRep.listValue.ele = NULL;
4869 objPtr->internalRep.listValue.len = 0;
4870 objPtr->internalRep.listValue.maxLen = 0;
4871 for (i = 0; i < len; i++) {
4872 ListAppendElement(objPtr, elements[i]);
4873 }
4874 return objPtr;
4875 }
4876
4877 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
4878 * length of the vector. Note that the user of this function should make
4879 * sure that the list object can't shimmer while the vector returned
4880 * is in use, this vector is the one stored inside the internal representation
4881 * of the list object. This function is not exported, extensions should
4882 * always access to the List object elements using Jim_ListIndex(). */
4883 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
4884 Jim_Obj ***listVec)
4885 {
4886 Jim_ListLength(interp, listObj, argc);
4887 assert(listObj->typePtr == &listObjType);
4888 *listVec = listObj->internalRep.listValue.ele;
4889 }
4890
4891 /* ListSortElements type values */
4892 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
4893 JIM_LSORT_NOCASE_DECR};
4894
4895 /* Sort the internal rep of a list. */
4896 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4897 {
4898 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
4899 }
4900
4901 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4902 {
4903 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
4904 }
4905
4906 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4907 {
4908 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
4909 }
4910
4911 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4912 {
4913 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
4914 }
4915
4916 /* Sort a list *in place*. MUST be called with non-shared objects. */
4917 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
4918 {
4919 typedef int (qsort_comparator)(const void *, const void *);
4920 int (*fn)(Jim_Obj**, Jim_Obj**);
4921 Jim_Obj **vector;
4922 int len;
4923
4924 if (Jim_IsShared(listObjPtr))
4925 Jim_Panic(interp,"Jim_ListSortElements called with shared object");
4926 if (listObjPtr->typePtr != &listObjType)
4927 SetListFromAny(interp, listObjPtr);
4928
4929 vector = listObjPtr->internalRep.listValue.ele;
4930 len = listObjPtr->internalRep.listValue.len;
4931 switch (type) {
4932 case JIM_LSORT_ASCII: fn = ListSortString; break;
4933 case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
4934 case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
4935 case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
4936 default:
4937 fn = NULL; /* avoid warning */
4938 Jim_Panic(interp,"ListSort called with invalid sort type");
4939 }
4940 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
4941 Jim_InvalidateStringRep(listObjPtr);
4942 }
4943
4944 /* This is the low-level function to append an element to a list.
4945 * The higher-level Jim_ListAppendElement() performs shared object
4946 * check and invalidate the string repr. This version is used
4947 * in the internals of the List Object and is not exported.
4948 *
4949 * NOTE: this function can be called only against objects
4950 * with internal type of List. */
4951 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
4952 {
4953 int requiredLen = listPtr->internalRep.listValue.len + 1;
4954
4955 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4956 int maxLen = requiredLen * 2;
4957
4958 listPtr->internalRep.listValue.ele =
4959 Jim_Realloc(listPtr->internalRep.listValue.ele,
4960 sizeof(Jim_Obj*)*maxLen);
4961 listPtr->internalRep.listValue.maxLen = maxLen;
4962 }
4963 listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
4964 objPtr;
4965 listPtr->internalRep.listValue.len ++;
4966 Jim_IncrRefCount(objPtr);
4967 }
4968
4969 /* This is the low-level function to insert elements into a list.
4970 * The higher-level Jim_ListInsertElements() performs shared object
4971 * check and invalidate the string repr. This version is used
4972 * in the internals of the List Object and is not exported.
4973 *
4974 * NOTE: this function can be called only against objects
4975 * with internal type of List. */
4976 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
4977 Jim_Obj *const *elemVec)
4978 {
4979 int currentLen = listPtr->internalRep.listValue.len;
4980 int requiredLen = currentLen + elemc;
4981 int i;
4982 Jim_Obj **point;
4983
4984 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4985 int maxLen = requiredLen * 2;
4986
4987 listPtr->internalRep.listValue.ele =
4988 Jim_Realloc(listPtr->internalRep.listValue.ele,
4989 sizeof(Jim_Obj*)*maxLen);
4990 listPtr->internalRep.listValue.maxLen = maxLen;
4991 }
4992 point = listPtr->internalRep.listValue.ele + index;
4993 memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
4994 for (i=0; i < elemc; ++i) {
4995 point[i] = elemVec[i];
4996 Jim_IncrRefCount(point[i]);
4997 }
4998 listPtr->internalRep.listValue.len += elemc;
4999 }
5000
5001 /* Appends every element of appendListPtr into listPtr.
5002 * Both have to be of the list type. */
5003 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5004 {
5005 int i, oldLen = listPtr->internalRep.listValue.len;
5006 int appendLen = appendListPtr->internalRep.listValue.len;
5007 int requiredLen = oldLen + appendLen;
5008
5009 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5010 int maxLen = requiredLen * 2;
5011
5012 listPtr->internalRep.listValue.ele =
5013 Jim_Realloc(listPtr->internalRep.listValue.ele,
5014 sizeof(Jim_Obj*)*maxLen);
5015 listPtr->internalRep.listValue.maxLen = maxLen;
5016 }
5017 for (i = 0; i < appendLen; i++) {
5018 Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5019 listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5020 Jim_IncrRefCount(objPtr);
5021 }
5022 listPtr->internalRep.listValue.len += appendLen;
5023 }
5024
5025 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5026 {
5027 if (Jim_IsShared(listPtr))
5028 Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5029 if (listPtr->typePtr != &listObjType)
5030 SetListFromAny(interp, listPtr);
5031 Jim_InvalidateStringRep(listPtr);
5032 ListAppendElement(listPtr, objPtr);
5033 }
5034
5035 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5036 {
5037 if (Jim_IsShared(listPtr))
5038 Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5039 if (listPtr->typePtr != &listObjType)
5040 SetListFromAny(interp, listPtr);
5041 Jim_InvalidateStringRep(listPtr);
5042 ListAppendList(listPtr, appendListPtr);
5043 }
5044
5045 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5046 {
5047 if (listPtr->typePtr != &listObjType)
5048 SetListFromAny(interp, listPtr);
5049 *intPtr = listPtr->internalRep.listValue.len;
5050 }
5051
5052 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5053 int objc, Jim_Obj *const *objVec)
5054 {
5055 if (Jim_IsShared(listPtr))
5056 Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5057 if (listPtr->typePtr != &listObjType)
5058 SetListFromAny(interp, listPtr);
5059 if (index >= 0 && index > listPtr->internalRep.listValue.len)
5060 index = listPtr->internalRep.listValue.len;
5061 else if (index < 0 )
5062 index = 0;
5063 Jim_InvalidateStringRep(listPtr);
5064 ListInsertElements(listPtr, index, objc, objVec);
5065 }
5066
5067 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5068 Jim_Obj **objPtrPtr, int flags)
5069 {
5070 if (listPtr->typePtr != &listObjType)
5071 SetListFromAny(interp, listPtr);
5072 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5073 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5074 if (flags & JIM_ERRMSG) {
5075 Jim_SetResultString(interp,
5076 "list index out of range", -1);
5077 }
5078 return JIM_ERR;
5079 }
5080 if (index < 0)
5081 index = listPtr->internalRep.listValue.len+index;
5082 *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5083 return JIM_OK;
5084 }
5085
5086 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5087 Jim_Obj *newObjPtr, int flags)
5088 {
5089 if (listPtr->typePtr != &listObjType)
5090 SetListFromAny(interp, listPtr);
5091 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5092 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5093 if (flags & JIM_ERRMSG) {
5094 Jim_SetResultString(interp,
5095 "list index out of range", -1);
5096 }
5097 return JIM_ERR;
5098 }
5099 if (index < 0)
5100 index = listPtr->internalRep.listValue.len+index;
5101 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5102 listPtr->internalRep.listValue.ele[index] = newObjPtr;
5103 Jim_IncrRefCount(newObjPtr);
5104 return JIM_OK;
5105 }
5106
5107 /* Modify the list stored into the variable named 'varNamePtr'
5108 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5109 * with the new element 'newObjptr'. */
5110 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5111 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5112 {
5113 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5114 int shared, i, index;
5115
5116 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5117 if (objPtr == NULL)
5118 return JIM_ERR;
5119 if ((shared = Jim_IsShared(objPtr)))
5120 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5121 for (i = 0; i < indexc-1; i++) {
5122 listObjPtr = objPtr;
5123 if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5124 goto err;
5125 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5126 JIM_ERRMSG) != JIM_OK) {
5127 goto err;
5128 }
5129 if (Jim_IsShared(objPtr)) {
5130 objPtr = Jim_DuplicateObj(interp, objPtr);
5131 ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5132 }
5133 Jim_InvalidateStringRep(listObjPtr);
5134 }
5135 if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5136 goto err;
5137 if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5138 goto err;
5139 Jim_InvalidateStringRep(objPtr);
5140 Jim_InvalidateStringRep(varObjPtr);
5141 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5142 goto err;
5143 Jim_SetResult(interp, varObjPtr);
5144 return JIM_OK;
5145 err:
5146 if (shared) {
5147 Jim_FreeNewObj(interp, varObjPtr);
5148 }
5149 return JIM_ERR;
5150 }
5151
5152 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5153 {
5154 int i;
5155
5156 /* If all the objects in objv are lists without string rep.
5157 * it's possible to return a list as result, that's the
5158 * concatenation of all the lists. */
5159 for (i = 0; i < objc; i++) {
5160 if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5161 break;
5162 }
5163 if (i == objc) {
5164 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5165 for (i = 0; i < objc; i++)
5166 Jim_ListAppendList(interp, objPtr, objv[i]);
5167 return objPtr;
5168 } else {
5169 /* Else... we have to glue strings together */
5170 int len = 0, objLen;
5171 char *bytes, *p;
5172
5173 /* Compute the length */
5174 for (i = 0; i < objc; i++) {
5175 Jim_GetString(objv[i], &objLen);
5176 len += objLen;
5177 }
5178 if (objc) len += objc-1;
5179 /* Create the string rep, and a stinrg object holding it. */
5180 p = bytes = Jim_Alloc(len+1);
5181 for (i = 0; i < objc; i++) {
5182 const char *s = Jim_GetString(objv[i], &objLen);
5183 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5184 {
5185 s++; objLen--; len--;
5186 }
5187 while (objLen && (s[objLen-1] == ' ' ||
5188 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5189 objLen--; len--;
5190 }
5191 memcpy(p, s, objLen);
5192 p += objLen;
5193 if (objLen && i+1 != objc) {
5194 *p++ = ' ';
5195 } else if (i+1 != objc) {
5196 /* Drop the space calcuated for this
5197 * element that is instead null. */
5198 len--;
5199 }
5200 }
5201 *p = '\0';
5202 return Jim_NewStringObjNoAlloc(interp, bytes, len);
5203 }
5204 }
5205
5206 /* Returns a list composed of the elements in the specified range.
5207 * first and start are directly accepted as Jim_Objects and
5208 * processed for the end?-index? case. */
5209 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5210 {
5211 int first, last;
5212 int len, rangeLen;
5213
5214 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5215 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5216 return NULL;
5217 Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5218 first = JimRelToAbsIndex(len, first);
5219 last = JimRelToAbsIndex(len, last);
5220 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5221 return Jim_NewListObj(interp,
5222 listObjPtr->internalRep.listValue.ele+first, rangeLen);
5223 }
5224
5225 /* -----------------------------------------------------------------------------
5226 * Dict object
5227 * ---------------------------------------------------------------------------*/
5228 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5229 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5230 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5231 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5232
5233 /* Dict HashTable Type.
5234 *
5235 * Keys and Values are Jim objects. */
5236
5237 unsigned int JimObjectHTHashFunction(const void *key)
5238 {
5239 const char *str;
5240 Jim_Obj *objPtr = (Jim_Obj*) key;
5241 int len, h;
5242
5243 str = Jim_GetString(objPtr, &len);
5244 h = Jim_GenHashFunction((unsigned char*)str, len);
5245 return h;
5246 }
5247
5248 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5249 {
5250 JIM_NOTUSED(privdata);
5251
5252 return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5253 }
5254
5255 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5256 {
5257 Jim_Obj *objPtr = val;
5258
5259 Jim_DecrRefCount(interp, objPtr);
5260 }
5261
5262 static Jim_HashTableType JimDictHashTableType = {
5263 JimObjectHTHashFunction, /* hash function */
5264 NULL, /* key dup */
5265 NULL, /* val dup */
5266 JimObjectHTKeyCompare, /* key compare */
5267 (void(*)(void*, const void*)) /* ATTENTION: const cast */
5268 JimObjectHTKeyValDestructor, /* key destructor */
5269 JimObjectHTKeyValDestructor /* val destructor */
5270 };
5271
5272 /* Note that while the elements of the dict may contain references,
5273 * the list object itself can't. This basically means that the
5274 * dict object string representation as a whole can't contain references
5275 * that are not presents in the single elements. */
5276 static Jim_ObjType dictObjType = {
5277 "dict",
5278 FreeDictInternalRep,
5279 DupDictInternalRep,
5280 UpdateStringOfDict,
5281 JIM_TYPE_NONE,
5282 };
5283
5284 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5285 {
5286 JIM_NOTUSED(interp);
5287
5288 Jim_FreeHashTable(objPtr->internalRep.ptr);
5289 Jim_Free(objPtr->internalRep.ptr);
5290 }
5291
5292 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5293 {
5294 Jim_HashTable *ht, *dupHt;
5295 Jim_HashTableIterator *htiter;
5296 Jim_HashEntry *he;
5297
5298 /* Create a new hash table */
5299 ht = srcPtr->internalRep.ptr;
5300 dupHt = Jim_Alloc(sizeof(*dupHt));
5301 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5302 if (ht->size != 0)
5303 Jim_ExpandHashTable(dupHt, ht->size);
5304 /* Copy every element from the source to the dup hash table */
5305 htiter = Jim_GetHashTableIterator(ht);
5306 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5307 const Jim_Obj *keyObjPtr = he->key;
5308 Jim_Obj *valObjPtr = he->val;
5309
5310 Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
5311 Jim_IncrRefCount(valObjPtr);
5312 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5313 }
5314 Jim_FreeHashTableIterator(htiter);
5315
5316 dupPtr->internalRep.ptr = dupHt;
5317 dupPtr->typePtr = &dictObjType;
5318 }
5319
5320 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5321 {
5322 int i, bufLen, realLength;
5323 const char *strRep;
5324 char *p;
5325 int *quotingType, objc;
5326 Jim_HashTable *ht;
5327 Jim_HashTableIterator *htiter;
5328 Jim_HashEntry *he;
5329 Jim_Obj **objv;
5330
5331 /* Trun the hash table into a flat vector of Jim_Objects. */
5332 ht = objPtr->internalRep.ptr;
5333 objc = ht->used*2;
5334 objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5335 htiter = Jim_GetHashTableIterator(ht);
5336 i = 0;
5337 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5338 objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
5339 objv[i++] = he->val;
5340 }
5341 Jim_FreeHashTableIterator(htiter);
5342 /* (Over) Estimate the space needed. */
5343 quotingType = Jim_Alloc(sizeof(int)*objc);
5344 bufLen = 0;
5345 for (i = 0; i < objc; i++) {
5346 int len;
5347
5348 strRep = Jim_GetString(objv[i], &len);
5349 quotingType[i] = ListElementQuotingType(strRep, len);
5350 switch (quotingType[i]) {
5351 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5352 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5353 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5354 }
5355 bufLen++; /* elements separator. */
5356 }
5357 bufLen++;
5358
5359 /* Generate the string rep. */
5360 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5361 realLength = 0;
5362 for (i = 0; i < objc; i++) {
5363 int len, qlen;
5364 const char *strRep = Jim_GetString(objv[i], &len);
5365 char *q;
5366
5367 switch(quotingType[i]) {
5368 case JIM_ELESTR_SIMPLE:
5369 memcpy(p, strRep, len);
5370 p += len;
5371 realLength += len;
5372 break;
5373 case JIM_ELESTR_BRACE:
5374 *p++ = '{';
5375 memcpy(p, strRep, len);
5376 p += len;
5377 *p++ = '}';
5378 realLength += len+2;
5379 break;
5380 case JIM_ELESTR_QUOTE:
5381 q = BackslashQuoteString(strRep, len, &qlen);
5382 memcpy(p, q, qlen);
5383 Jim_Free(q);
5384 p += qlen;
5385 realLength += qlen;
5386 break;
5387 }
5388 /* Add a separating space */
5389 if (i+1 != objc) {
5390 *p++ = ' ';
5391 realLength ++;
5392 }
5393 }
5394 *p = '\0'; /* nul term. */
5395 objPtr->length = realLength;
5396 Jim_Free(quotingType);
5397 Jim_Free(objv);
5398 }
5399
5400 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5401 {
5402 struct JimParserCtx parser;
5403 Jim_HashTable *ht;
5404 Jim_Obj *objv[2];
5405 const char *str;
5406 int i, strLen;
5407
5408 /* Get the string representation */
5409 str = Jim_GetString(objPtr, &strLen);
5410
5411 /* Free the old internal repr just now and initialize the
5412 * new one just now. The string->list conversion can't fail. */
5413 Jim_FreeIntRep(interp, objPtr);
5414 ht = Jim_Alloc(sizeof(*ht));
5415 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5416 objPtr->typePtr = &dictObjType;
5417 objPtr->internalRep.ptr = ht;
5418
5419 /* Convert into a dict */
5420 JimParserInit(&parser, str, strLen, 1);
5421 i = 0;
5422 while(!JimParserEof(&parser)) {
5423 char *token;
5424 int tokenLen, type;
5425
5426 JimParseList(&parser);
5427 if (JimParserTtype(&parser) != JIM_TT_STR &&
5428 JimParserTtype(&parser) != JIM_TT_ESC)
5429 continue;
5430 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5431 objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5432 if (i == 2) {
5433 i = 0;
5434 Jim_IncrRefCount(objv[0]);
5435 Jim_IncrRefCount(objv[1]);
5436 if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5437 Jim_HashEntry *he;
5438 he = Jim_FindHashEntry(ht, objv[0]);
5439 Jim_DecrRefCount(interp, objv[0]);
5440 /* ATTENTION: const cast */
5441 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5442 he->val = objv[1];
5443 }
5444 }
5445 }
5446 if (i) {
5447 Jim_FreeNewObj(interp, objv[0]);
5448 objPtr->typePtr = NULL;
5449 Jim_FreeHashTable(ht);
5450 Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5451 return JIM_ERR;
5452 }
5453 return JIM_OK;
5454 }
5455
5456 /* Dict object API */
5457
5458 /* Add an element to a dict. objPtr must be of the "dict" type.
5459 * The higer-level exported function is Jim_DictAddElement().
5460 * If an element with the specified key already exists, the value
5461 * associated is replaced with the new one.
5462 *
5463 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5464 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5465 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5466 {
5467 Jim_HashTable *ht = objPtr->internalRep.ptr;
5468
5469 if (valueObjPtr == NULL) { /* unset */
5470 Jim_DeleteHashEntry(ht, keyObjPtr);
5471 return;
5472 }
5473 Jim_IncrRefCount(keyObjPtr);
5474 Jim_IncrRefCount(valueObjPtr);
5475 if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5476 Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5477 Jim_DecrRefCount(interp, keyObjPtr);
5478 /* ATTENTION: const cast */
5479 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5480 he->val = valueObjPtr;
5481 }
5482 }
5483
5484 /* Add an element, higher-level interface for DictAddElement().
5485 * If valueObjPtr == NULL, the key is removed if it exists. */
5486 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5487 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5488 {
5489 if (Jim_IsShared(objPtr))
5490 Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5491 if (objPtr->typePtr != &dictObjType) {
5492 if (SetDictFromAny(interp, objPtr) != JIM_OK)
5493 return JIM_ERR;
5494 }
5495 DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5496 Jim_InvalidateStringRep(objPtr);
5497 return JIM_OK;
5498 }
5499
5500 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5501 {
5502 Jim_Obj *objPtr;
5503 int i;
5504
5505 if (len % 2)
5506 Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5507
5508 objPtr = Jim_NewObj(interp);
5509 objPtr->typePtr = &dictObjType;
5510 objPtr->bytes = NULL;
5511 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5512 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5513 for (i = 0; i < len; i += 2)
5514 DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5515 return objPtr;
5516 }
5517
5518 /* Return the value associated to the specified dict key */
5519 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5520 Jim_Obj **objPtrPtr, int flags)
5521 {
5522 Jim_HashEntry *he;
5523 Jim_HashTable *ht;
5524
5525 if (dictPtr->typePtr != &dictObjType) {
5526 if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5527 return JIM_ERR;
5528 }
5529 ht = dictPtr->internalRep.ptr;
5530 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5531 if (flags & JIM_ERRMSG) {
5532 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5533 Jim_AppendStrings(interp, Jim_GetResult(interp),
5534 "key \"", Jim_GetString(keyPtr, NULL),
5535 "\" not found in dictionary", NULL);
5536 }
5537 return JIM_ERR;
5538 }
5539 *objPtrPtr = he->val;
5540 return JIM_OK;
5541 }
5542
5543 /* Return the value associated to the specified dict keys */
5544 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5545 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5546 {
5547 Jim_Obj *objPtr;
5548 int i;
5549
5550 if (keyc == 0) {
5551 *objPtrPtr = dictPtr;
5552 return JIM_OK;
5553 }
5554
5555 for (i = 0; i < keyc; i++) {
5556 if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5557 != JIM_OK)
5558 return JIM_ERR;
5559 dictPtr = objPtr;
5560 }
5561 *objPtrPtr = objPtr;
5562 return JIM_OK;
5563 }
5564
5565 /* Modify the dict stored into the variable named 'varNamePtr'
5566 * setting the element specified by the 'keyc' keys objects in 'keyv',
5567 * with the new value of the element 'newObjPtr'.
5568 *
5569 * If newObjPtr == NULL the operation is to remove the given key
5570 * from the dictionary. */
5571 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5572 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5573 {
5574 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5575 int shared, i;
5576
5577 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5578 if (objPtr == NULL) {
5579 if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5580 return JIM_ERR;
5581 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5582 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5583 Jim_FreeNewObj(interp, varObjPtr);
5584 return JIM_ERR;
5585 }
5586 }
5587 if ((shared = Jim_IsShared(objPtr)))
5588 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5589 for (i = 0; i < keyc-1; i++) {
5590 dictObjPtr = objPtr;
5591
5592 /* Check if it's a valid dictionary */
5593 if (dictObjPtr->typePtr != &dictObjType) {
5594 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5595 goto err;
5596 }
5597 /* Check if the given key exists. */
5598 Jim_InvalidateStringRep(dictObjPtr);
5599 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5600 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5601 {
5602 /* This key exists at the current level.
5603 * Make sure it's not shared!. */
5604 if (Jim_IsShared(objPtr)) {
5605 objPtr = Jim_DuplicateObj(interp, objPtr);
5606 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5607 }
5608 } else {
5609 /* Key not found. If it's an [unset] operation
5610 * this is an error. Only the last key may not
5611 * exist. */
5612 if (newObjPtr == NULL)
5613 goto err;
5614 /* Otherwise set an empty dictionary
5615 * as key's value. */
5616 objPtr = Jim_NewDictObj(interp, NULL, 0);
5617 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5618 }
5619 }
5620 if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5621 != JIM_OK)
5622 goto err;
5623 Jim_InvalidateStringRep(objPtr);
5624 Jim_InvalidateStringRep(varObjPtr);
5625 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5626 goto err;
5627 Jim_SetResult(interp, varObjPtr);
5628 return JIM_OK;
5629 err:
5630 if (shared) {
5631 Jim_FreeNewObj(interp, varObjPtr);
5632 }
5633 return JIM_ERR;
5634 }
5635
5636 /* -----------------------------------------------------------------------------
5637 * Index object
5638 * ---------------------------------------------------------------------------*/
5639 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5640 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5641
5642 static Jim_ObjType indexObjType = {
5643 "index",
5644 NULL,
5645 NULL,
5646 UpdateStringOfIndex,
5647 JIM_TYPE_NONE,
5648 };
5649
5650 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5651 {
5652 int len;
5653 char buf[JIM_INTEGER_SPACE+1];
5654
5655 if (objPtr->internalRep.indexValue >= 0)
5656 len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5657 else if (objPtr->internalRep.indexValue == -1)
5658 len = sprintf(buf, "end");
5659 else {
5660 len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5661 }
5662 objPtr->bytes = Jim_Alloc(len+1);
5663 memcpy(objPtr->bytes, buf, len+1);
5664 objPtr->length = len;
5665 }
5666
5667 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5668 {
5669 int index, end = 0;
5670 const char *str;
5671
5672 /* Get the string representation */
5673 str = Jim_GetString(objPtr, NULL);
5674 /* Try to convert into an index */
5675 if (!strcmp(str, "end")) {
5676 index = 0;
5677 end = 1;
5678 } else {
5679 if (!strncmp(str, "end-", 4)) {
5680 str += 4;
5681 end = 1;
5682 }
5683 if (Jim_StringToIndex(str, &index) != JIM_OK) {
5684 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5685 Jim_AppendStrings(interp, Jim_GetResult(interp),
5686 "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5687 "must be integer or end?-integer?", NULL);
5688 return JIM_ERR;
5689 }
5690 }
5691 if (end) {
5692 if (index < 0)
5693 index = INT_MAX;
5694 else
5695 index = -(index+1);
5696 } else if (!end && index < 0)
5697 index = -INT_MAX;
5698 /* Free the old internal repr and set the new one. */
5699 Jim_FreeIntRep(interp, objPtr);
5700 objPtr->typePtr = &indexObjType;
5701 objPtr->internalRep.indexValue = index;
5702 return JIM_OK;
5703 }
5704
5705 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5706 {
5707 /* Avoid shimmering if the object is an integer. */
5708 if (objPtr->typePtr == &intObjType) {
5709 jim_wide val = objPtr->internalRep.wideValue;
5710 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5711 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5712 return JIM_OK;
5713 }
5714 }
5715 if (objPtr->typePtr != &indexObjType &&
5716 SetIndexFromAny(interp, objPtr) == JIM_ERR)
5717 return JIM_ERR;
5718 *indexPtr = objPtr->internalRep.indexValue;
5719 return JIM_OK;
5720 }
5721
5722 /* -----------------------------------------------------------------------------
5723 * Return Code Object.
5724 * ---------------------------------------------------------------------------*/
5725
5726 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5727
5728 static Jim_ObjType returnCodeObjType = {
5729 "return-code",
5730 NULL,
5731 NULL,
5732 NULL,
5733 JIM_TYPE_NONE,
5734 };
5735
5736 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5737 {
5738 const char *str;
5739 int strLen, returnCode;
5740 jim_wide wideValue;
5741
5742 /* Get the string representation */
5743 str = Jim_GetString(objPtr, &strLen);
5744 /* Try to convert into an integer */
5745 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5746 returnCode = (int) wideValue;
5747 else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5748 returnCode = JIM_OK;
5749 else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5750 returnCode = JIM_ERR;
5751 else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5752 returnCode = JIM_RETURN;
5753 else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5754 returnCode = JIM_BREAK;
5755 else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5756 returnCode = JIM_CONTINUE;
5757 else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5758 returnCode = JIM_EVAL;
5759 else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5760 returnCode = JIM_EXIT;
5761 else {
5762 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5763 Jim_AppendStrings(interp, Jim_GetResult(interp),
5764 "expected return code but got '", str, "'",
5765 NULL);
5766 return JIM_ERR;
5767 }
5768 /* Free the old internal repr and set the new one. */
5769 Jim_FreeIntRep(interp, objPtr);
5770 objPtr->typePtr = &returnCodeObjType;
5771 objPtr->internalRep.returnCode = returnCode;
5772 return JIM_OK;
5773 }
5774
5775 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
5776 {
5777 if (objPtr->typePtr != &returnCodeObjType &&
5778 SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
5779 return JIM_ERR;
5780 *intPtr = objPtr->internalRep.returnCode;
5781 return JIM_OK;
5782 }
5783
5784 /* -----------------------------------------------------------------------------
5785 * Expression Parsing
5786 * ---------------------------------------------------------------------------*/
5787 static int JimParseExprOperator(struct JimParserCtx *pc);
5788 static int JimParseExprNumber(struct JimParserCtx *pc);
5789 static int JimParseExprIrrational(struct JimParserCtx *pc);
5790
5791 /* Exrp's Stack machine operators opcodes. */
5792
5793 /* Binary operators (numbers) */
5794 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
5795 #define JIM_EXPROP_MUL 0
5796 #define JIM_EXPROP_DIV 1
5797 #define JIM_EXPROP_MOD 2
5798 #define JIM_EXPROP_SUB 3
5799 #define JIM_EXPROP_ADD 4
5800 #define JIM_EXPROP_LSHIFT 5
5801 #define JIM_EXPROP_RSHIFT 6
5802 #define JIM_EXPROP_ROTL 7
5803 #define JIM_EXPROP_ROTR 8
5804 #define JIM_EXPROP_LT 9
5805 #define JIM_EXPROP_GT 10
5806 #define JIM_EXPROP_LTE 11
5807 #define JIM_EXPROP_GTE 12
5808 #define JIM_EXPROP_NUMEQ 13
5809 #define JIM_EXPROP_NUMNE 14
5810 #define JIM_EXPROP_BITAND 15
5811 #define JIM_EXPROP_BITXOR 16
5812 #define JIM_EXPROP_BITOR 17
5813 #define JIM_EXPROP_LOGICAND 18
5814 #define JIM_EXPROP_LOGICOR 19
5815 #define JIM_EXPROP_LOGICAND_LEFT 20
5816 #define JIM_EXPROP_LOGICOR_LEFT 21
5817 #define JIM_EXPROP_POW 22
5818 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
5819
5820 /* Binary operators (strings) */
5821 #define JIM_EXPROP_STREQ 23
5822 #define JIM_EXPROP_STRNE 24
5823
5824 /* Unary operators (numbers) */
5825 #define JIM_EXPROP_NOT 25
5826 #define JIM_EXPROP_BITNOT 26
5827 #define JIM_EXPROP_UNARYMINUS 27
5828 #define JIM_EXPROP_UNARYPLUS 28
5829 #define JIM_EXPROP_LOGICAND_RIGHT 29
5830 #define JIM_EXPROP_LOGICOR_RIGHT 30
5831
5832 /* Ternary operators */
5833 #define JIM_EXPROP_TERNARY 31
5834
5835 /* Operands */
5836 #define JIM_EXPROP_NUMBER 32
5837 #define JIM_EXPROP_COMMAND 33
5838 #define JIM_EXPROP_VARIABLE 34
5839 #define JIM_EXPROP_DICTSUGAR 35
5840 #define JIM_EXPROP_SUBST 36
5841 #define JIM_EXPROP_STRING 37
5842
5843 /* Operators table */
5844 typedef struct Jim_ExprOperator {
5845 const char *name;
5846 int precedence;
5847 int arity;
5848 int opcode;
5849 } Jim_ExprOperator;
5850
5851 /* name - precedence - arity - opcode */
5852 static struct Jim_ExprOperator Jim_ExprOperators[] = {
5853 {"!", 300, 1, JIM_EXPROP_NOT},
5854 {"~", 300, 1, JIM_EXPROP_BITNOT},
5855 {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
5856 {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
5857
5858 {"**", 250, 2, JIM_EXPROP_POW},
5859
5860 {"*", 200, 2, JIM_EXPROP_MUL},
5861 {"/", 200, 2, JIM_EXPROP_DIV},
5862 {"%", 200, 2, JIM_EXPROP_MOD},
5863
5864 {"-", 100, 2, JIM_EXPROP_SUB},
5865 {"+", 100, 2, JIM_EXPROP_ADD},
5866
5867 {"<<<", 90, 3, JIM_EXPROP_ROTL},
5868 {">>>", 90, 3, JIM_EXPROP_ROTR},
5869 {"<<", 90, 2, JIM_EXPROP_LSHIFT},
5870 {">>", 90, 2, JIM_EXPROP_RSHIFT},
5871
5872 {"<", 80, 2, JIM_EXPROP_LT},
5873 {">", 80, 2, JIM_EXPROP_GT},
5874 {"<=", 80, 2, JIM_EXPROP_LTE},
5875 {">=", 80, 2, JIM_EXPROP_GTE},
5876
5877 {"==", 70, 2, JIM_EXPROP_NUMEQ},
5878 {"!=", 70, 2, JIM_EXPROP_NUMNE},
5879
5880 {"eq", 60, 2, JIM_EXPROP_STREQ},
5881 {"ne", 60, 2, JIM_EXPROP_STRNE},
5882
5883 {"&", 50, 2, JIM_EXPROP_BITAND},
5884 {"^", 49, 2, JIM_EXPROP_BITXOR},
5885 {"|", 48, 2, JIM_EXPROP_BITOR},
5886
5887 {"&&", 10, 2, JIM_EXPROP_LOGICAND},
5888 {"||", 10, 2, JIM_EXPROP_LOGICOR},
5889
5890 {"?", 5, 3, JIM_EXPROP_TERNARY},
5891 /* private operators */
5892 {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
5893 {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
5894 {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
5895 {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
5896 };
5897
5898 #define JIM_EXPR_OPERATORS_NUM \
5899 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
5900
5901 int JimParseExpression(struct JimParserCtx *pc)
5902 {
5903 /* Discard spaces and quoted newline */
5904 while(*(pc->p) == ' ' ||
5905 *(pc->p) == '\t' ||
5906 *(pc->p) == '\r' ||
5907 *(pc->p) == '\n' ||
5908 (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
5909 pc->p++; pc->len--;
5910 }
5911
5912 if (pc->len == 0) {
5913 pc->tstart = pc->tend = pc->p;
5914 pc->tline = pc->linenr;
5915 pc->tt = JIM_TT_EOL;
5916 pc->eof = 1;
5917 return JIM_OK;
5918 }
5919 switch(*(pc->p)) {
5920 case '(':
5921 pc->tstart = pc->tend = pc->p;
5922 pc->tline = pc->linenr;
5923 pc->tt = JIM_TT_SUBEXPR_START;
5924 pc->p++; pc->len--;
5925 break;
5926 case ')':
5927 pc->tstart = pc->tend = pc->p;
5928 pc->tline = pc->linenr;
5929 pc->tt = JIM_TT_SUBEXPR_END;
5930 pc->p++; pc->len--;
5931 break;
5932 case '[':
5933 return JimParseCmd(pc);
5934 break;
5935 case '$':
5936 if (JimParseVar(pc) == JIM_ERR)
5937 return JimParseExprOperator(pc);
5938 else
5939 return JIM_OK;
5940 break;
5941 case '-':
5942 if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
5943 isdigit((int)*(pc->p+1)))
5944 return JimParseExprNumber(pc);
5945 else
5946 return JimParseExprOperator(pc);
5947 break;
5948 case '0': case '1': case '2': case '3': case '4':
5949 case '5': case '6': case '7': case '8': case '9': case '.':
5950 return JimParseExprNumber(pc);
5951 break;
5952 case '"':
5953 case '{':
5954 /* Here it's possible to reuse the List String parsing. */
5955 pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
5956 return JimParseListStr(pc);
5957 break;
5958 case 'N': case 'I':
5959 case 'n': case 'i':
5960 if (JimParseExprIrrational(pc) == JIM_ERR)
5961 return JimParseExprOperator(pc);
5962 break;
5963 default:
5964 return JimParseExprOperator(pc);
5965 break;
5966 }
5967 return JIM_OK;
5968 }
5969
5970 int JimParseExprNumber(struct JimParserCtx *pc)
5971 {
5972 int allowdot = 1;
5973 int allowhex = 0;
5974
5975 pc->tstart = pc->p;
5976 pc->tline = pc->linenr;
5977 if (*pc->p == '-') {
5978 pc->p++; pc->len--;
5979 }
5980 while ( isdigit((int)*pc->p)
5981 || (allowhex && isxdigit((int)*pc->p) )
5982 || (allowdot && *pc->p == '.')
5983 || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
5984 (*pc->p == 'x' || *pc->p == 'X'))
5985 )
5986 {
5987 if ((*pc->p == 'x') || (*pc->p == 'X')) {
5988 allowhex = 1;
5989 allowdot = 0;
5990 }
5991 if (*pc->p == '.')
5992 allowdot = 0;
5993 pc->p++; pc->len--;
5994 if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
5995 pc->p += 2; pc->len -= 2;
5996 }
5997 }
5998 pc->tend = pc->p-1;
5999 pc->tt = JIM_TT_EXPR_NUMBER;
6000 return JIM_OK;
6001 }
6002
6003 int JimParseExprIrrational(struct JimParserCtx *pc)
6004 {
6005 const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6006 const char **token;
6007 for (token = Tokens; *token != NULL; token++) {
6008 int len = strlen(*token);
6009 if (strncmp(*token, pc->p, len) == 0) {
6010 pc->tstart = pc->p;
6011 pc->tend = pc->p + len - 1;
6012 pc->p += len; pc->len -= len;
6013 pc->tline = pc->linenr;
6014 pc->tt = JIM_TT_EXPR_NUMBER;
6015 return JIM_OK;
6016 }
6017 }
6018 return JIM_ERR;
6019 }
6020
6021 int JimParseExprOperator(struct JimParserCtx *pc)
6022 {
6023 int i;
6024 int bestIdx = -1, bestLen = 0;
6025
6026 /* Try to get the longest match. */
6027 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6028 const char *opname;
6029 int oplen;
6030
6031 opname = Jim_ExprOperators[i].name;
6032 if (opname == NULL) continue;
6033 oplen = strlen(opname);
6034
6035 if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6036 bestIdx = i;
6037 bestLen = oplen;
6038 }
6039 }
6040 if (bestIdx == -1) return JIM_ERR;
6041 pc->tstart = pc->p;
6042 pc->tend = pc->p + bestLen - 1;
6043 pc->p += bestLen; pc->len -= bestLen;
6044 pc->tline = pc->linenr;
6045 pc->tt = JIM_TT_EXPR_OPERATOR;
6046 return JIM_OK;
6047 }
6048
6049 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6050 {
6051 int i;
6052 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6053 if (Jim_ExprOperators[i].name &&
6054 strcmp(opname, Jim_ExprOperators[i].name) == 0)
6055 return &Jim_ExprOperators[i];
6056 return NULL;
6057 }
6058
6059 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6060 {
6061 int i;
6062 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6063 if (Jim_ExprOperators[i].opcode == opcode)
6064 return &Jim_ExprOperators[i];
6065 return NULL;
6066 }
6067
6068 /* -----------------------------------------------------------------------------
6069 * Expression Object
6070 * ---------------------------------------------------------------------------*/
6071 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6072 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6073 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6074
6075 static Jim_ObjType exprObjType = {
6076 "expression",
6077 FreeExprInternalRep,
6078 DupExprInternalRep,
6079 NULL,
6080 JIM_TYPE_REFERENCES,
6081 };
6082
6083 /* Expr bytecode structure */
6084 typedef struct ExprByteCode {
6085 int *opcode; /* Integer array of opcodes. */
6086 Jim_Obj **obj; /* Array of associated Jim Objects. */
6087 int len; /* Bytecode length */
6088 int inUse; /* Used for sharing. */
6089 } ExprByteCode;
6090
6091 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6092 {
6093 int i;
6094 ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6095
6096 expr->inUse--;
6097 if (expr->inUse != 0) return;
6098 for (i = 0; i < expr->len; i++)
6099 Jim_DecrRefCount(interp, expr->obj[i]);
6100 Jim_Free(expr->opcode);
6101 Jim_Free(expr->obj);
6102 Jim_Free(expr);
6103 }
6104
6105 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6106 {
6107 JIM_NOTUSED(interp);
6108 JIM_NOTUSED(srcPtr);
6109
6110 /* Just returns an simple string. */
6111 dupPtr->typePtr = NULL;
6112 }
6113
6114 /* Add a new instruction to an expression bytecode structure. */
6115 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6116 int opcode, char *str, int len)
6117 {
6118 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6119 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6120 expr->opcode[expr->len] = opcode;
6121 expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6122 Jim_IncrRefCount(expr->obj[expr->len]);
6123 expr->len++;
6124 }
6125
6126 /* Check if an expr program looks correct. */
6127 static int ExprCheckCorrectness(ExprByteCode *expr)
6128 {
6129 int i;
6130 int stacklen = 0;
6131
6132 /* Try to check if there are stack underflows,
6133 * and make sure at the end of the program there is
6134 * a single result on the stack. */
6135 for (i = 0; i < expr->len; i++) {
6136 switch(expr->opcode[i]) {
6137 case JIM_EXPROP_NUMBER:
6138 case JIM_EXPROP_STRING:
6139 case JIM_EXPROP_SUBST:
6140 case JIM_EXPROP_VARIABLE:
6141 case JIM_EXPROP_DICTSUGAR:
6142 case JIM_EXPROP_COMMAND:
6143 stacklen++;
6144 break;
6145 case JIM_EXPROP_NOT:
6146 case JIM_EXPROP_BITNOT:
6147 case JIM_EXPROP_UNARYMINUS:
6148 case JIM_EXPROP_UNARYPLUS:
6149 /* Unary operations */
6150 if (stacklen < 1) return JIM_ERR;
6151 break;
6152 case JIM_EXPROP_ADD:
6153 case JIM_EXPROP_SUB:
6154 case JIM_EXPROP_MUL:
6155 case JIM_EXPROP_DIV:
6156 case JIM_EXPROP_MOD:
6157 case JIM_EXPROP_LT:
6158 case JIM_EXPROP_GT:
6159 case JIM_EXPROP_LTE:
6160 case JIM_EXPROP_GTE:
6161 case JIM_EXPROP_ROTL:
6162 case JIM_EXPROP_ROTR:
6163 case JIM_EXPROP_LSHIFT:
6164 case JIM_EXPROP_RSHIFT:
6165 case JIM_EXPROP_NUMEQ:
6166 case JIM_EXPROP_NUMNE:
6167 case JIM_EXPROP_STREQ:
6168 case JIM_EXPROP_STRNE:
6169 case JIM_EXPROP_BITAND:
6170 case JIM_EXPROP_BITXOR:
6171 case JIM_EXPROP_BITOR:
6172 case JIM_EXPROP_LOGICAND:
6173 case JIM_EXPROP_LOGICOR:
6174 case JIM_EXPROP_POW:
6175 /* binary operations */
6176 if (stacklen < 2) return JIM_ERR;
6177 stacklen--;
6178 break;
6179 default:
6180 Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6181 break;
6182 }
6183 }
6184 if (stacklen != 1) return JIM_ERR;
6185 return JIM_OK;
6186 }
6187
6188 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6189 ScriptObj *topLevelScript)
6190 {
6191 int i;
6192
6193 return;
6194 for (i = 0; i < expr->len; i++) {
6195 Jim_Obj *foundObjPtr;
6196
6197 if (expr->obj[i] == NULL) continue;
6198 foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6199 NULL, expr->obj[i]);
6200 if (foundObjPtr != NULL) {
6201 Jim_IncrRefCount(foundObjPtr);
6202 Jim_DecrRefCount(interp, expr->obj[i]);
6203 expr->obj[i] = foundObjPtr;
6204 }
6205 }
6206 }
6207
6208 /* This procedure converts every occurrence of || and && opereators
6209 * in lazy unary versions.
6210 *
6211 * a b || is converted into:
6212 *
6213 * a <offset> |L b |R
6214 *
6215 * a b && is converted into:
6216 *
6217 * a <offset> &L b &R
6218 *
6219 * "|L" checks if 'a' is true:
6220 * 1) if it is true pushes 1 and skips <offset> istructions to reach
6221 * the opcode just after |R.
6222 * 2) if it is false does nothing.
6223 * "|R" checks if 'b' is true:
6224 * 1) if it is true pushes 1, otherwise pushes 0.
6225 *
6226 * "&L" checks if 'a' is true:
6227 * 1) if it is true does nothing.
6228 * 2) If it is false pushes 0 and skips <offset> istructions to reach
6229 * the opcode just after &R
6230 * "&R" checks if 'a' is true:
6231 * if it is true pushes 1, otherwise pushes 0.
6232 */
6233 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6234 {
6235 while (1) {
6236 int index = -1, leftindex, arity, i, offset;
6237 Jim_ExprOperator *op;
6238
6239 /* Search for || or && */
6240 for (i = 0; i < expr->len; i++) {
6241 if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6242 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6243 index = i;
6244 break;
6245 }
6246 }
6247 if (index == -1) return;
6248 /* Search for the end of the first operator */
6249 leftindex = index-1;
6250 arity = 1;
6251 while(arity) {
6252 switch(expr->opcode[leftindex]) {
6253 case JIM_EXPROP_NUMBER:
6254 case JIM_EXPROP_COMMAND:
6255 case JIM_EXPROP_VARIABLE:
6256 case JIM_EXPROP_DICTSUGAR:
6257 case JIM_EXPROP_SUBST:
6258 case JIM_EXPROP_STRING:
6259 break;
6260 default:
6261 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6262 if (op == NULL) {
6263 Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6264 }
6265 arity += op->arity;
6266 break;
6267 }
6268 arity--;
6269 leftindex--;
6270 }
6271 leftindex++;
6272 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6273 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6274 memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6275 sizeof(int)*(expr->len-leftindex));
6276 memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6277 sizeof(Jim_Obj*)*(expr->len-leftindex));
6278 expr->len += 2;
6279 index += 2;
6280 offset = (index-leftindex)-1;
6281 Jim_DecrRefCount(interp, expr->obj[index]);
6282 if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6283 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6284 expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6285 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6286 expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6287 } else {
6288 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6289 expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6290 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6291 expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6292 }
6293 expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6294 expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6295 Jim_IncrRefCount(expr->obj[index]);
6296 Jim_IncrRefCount(expr->obj[leftindex]);
6297 Jim_IncrRefCount(expr->obj[leftindex+1]);
6298 }
6299 }
6300
6301 /* This method takes the string representation of an expression
6302 * and generates a program for the Expr's stack-based VM. */
6303 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6304 {
6305 int exprTextLen;
6306 const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6307 struct JimParserCtx parser;
6308 int i, shareLiterals;
6309 ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6310 Jim_Stack stack;
6311 Jim_ExprOperator *op;
6312
6313 /* Perform literal sharing with the current procedure
6314 * running only if this expression appears to be not generated
6315 * at runtime. */
6316 shareLiterals = objPtr->typePtr == &sourceObjType;
6317
6318 expr->opcode = NULL;
6319 expr->obj = NULL;
6320 expr->len = 0;
6321 expr->inUse = 1;
6322
6323 Jim_InitStack(&stack);
6324 JimParserInit(&parser, exprText, exprTextLen, 1);
6325 while(!JimParserEof(&parser)) {
6326 char *token;
6327 int len, type;
6328
6329 if (JimParseExpression(&parser) != JIM_OK) {
6330 Jim_SetResultString(interp, "Syntax error in expression", -1);
6331 goto err;
6332 }
6333 token = JimParserGetToken(&parser, &len, &type, NULL);
6334 if (type == JIM_TT_EOL) {
6335 Jim_Free(token);
6336 break;
6337 }
6338 switch(type) {
6339 case JIM_TT_STR:
6340 ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6341 break;
6342 case JIM_TT_ESC:
6343 ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6344 break;
6345 case JIM_TT_VAR:
6346 ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6347 break;
6348 case JIM_TT_DICTSUGAR:
6349 ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6350 break;
6351 case JIM_TT_CMD:
6352 ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6353 break;
6354 case JIM_TT_EXPR_NUMBER:
6355 ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6356 break;
6357 case JIM_TT_EXPR_OPERATOR:
6358 op = JimExprOperatorInfo(token);
6359 while(1) {
6360 Jim_ExprOperator *stackTopOp;
6361
6362 if (Jim_StackPeek(&stack) != NULL) {
6363 stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6364 } else {
6365 stackTopOp = NULL;
6366 }
6367 if (Jim_StackLen(&stack) && op->arity != 1 &&
6368 stackTopOp && stackTopOp->precedence >= op->precedence)
6369 {
6370 ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6371 Jim_StackPeek(&stack), -1);
6372 Jim_StackPop(&stack);
6373 } else {
6374 break;
6375 }
6376 }
6377 Jim_StackPush(&stack, token);
6378 break;
6379 case JIM_TT_SUBEXPR_START:
6380 Jim_StackPush(&stack, Jim_StrDup("("));
6381 Jim_Free(token);
6382 break;
6383 case JIM_TT_SUBEXPR_END:
6384 {
6385 int found = 0;
6386 while(Jim_StackLen(&stack)) {
6387 char *opstr = Jim_StackPop(&stack);
6388 if (!strcmp(opstr, "(")) {
6389 Jim_Free(opstr);
6390 found = 1;
6391 break;
6392 }
6393 op = JimExprOperatorInfo(opstr);
6394 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6395 }
6396 if (!found) {
6397 Jim_SetResultString(interp,
6398 "Unexpected close parenthesis", -1);
6399 goto err;
6400 }
6401 }
6402 Jim_Free(token);
6403 break;
6404 default:
6405 Jim_Panic(interp,"Default reached in SetExprFromAny()");
6406 break;
6407 }
6408 }
6409 while (Jim_StackLen(&stack)) {
6410 char *opstr = Jim_StackPop(&stack);
6411 op = JimExprOperatorInfo(opstr);
6412 if (op == NULL && !strcmp(opstr, "(")) {
6413 Jim_Free(opstr);
6414 Jim_SetResultString(interp, "Missing close parenthesis", -1);
6415 goto err;
6416 }
6417 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6418 }
6419 /* Check program correctness. */
6420 if (ExprCheckCorrectness(expr) != JIM_OK) {
6421 Jim_SetResultString(interp, "Invalid expression", -1);
6422 goto err;
6423 }
6424
6425 /* Free the stack used for the compilation. */
6426 Jim_FreeStackElements(&stack, Jim_Free);
6427 Jim_FreeStack(&stack);
6428
6429 /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6430 ExprMakeLazy(interp, expr);
6431
6432 /* Perform literal sharing */
6433 if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6434 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6435 if (bodyObjPtr->typePtr == &scriptObjType) {
6436 ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6437 ExprShareLiterals(interp, expr, bodyScript);
6438 }
6439 }
6440
6441 /* Free the old internal rep and set the new one. */
6442 Jim_FreeIntRep(interp, objPtr);
6443 Jim_SetIntRepPtr(objPtr, expr);
6444 objPtr->typePtr = &exprObjType;
6445 return JIM_OK;
6446
6447 err: /* we jump here on syntax/compile errors. */
6448 Jim_FreeStackElements(&stack, Jim_Free);
6449 Jim_FreeStack(&stack);
6450 Jim_Free(expr->opcode);
6451 for (i = 0; i < expr->len; i++) {
6452 Jim_DecrRefCount(interp,expr->obj[i]);
6453 }
6454 Jim_Free(expr->obj);
6455 Jim_Free(expr);
6456 return JIM_ERR;
6457 }
6458
6459 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6460 {
6461 if (objPtr->typePtr != &exprObjType) {
6462 if (SetExprFromAny(interp, objPtr) != JIM_OK)
6463 return NULL;
6464 }
6465 return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6466 }
6467
6468 /* -----------------------------------------------------------------------------
6469 * Expressions evaluation.
6470 * Jim uses a specialized stack-based virtual machine for expressions,
6471 * that takes advantage of the fact that expr's operators
6472 * can't be redefined.
6473 *
6474 * Jim_EvalExpression() uses the bytecode compiled by
6475 * SetExprFromAny() method of the "expression" object.
6476 *
6477 * On success a Tcl Object containing the result of the evaluation
6478 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6479 * returned.
6480 * On error the function returns a retcode != to JIM_OK and set a suitable
6481 * error on the interp.
6482 * ---------------------------------------------------------------------------*/
6483 #define JIM_EE_STATICSTACK_LEN 10
6484
6485 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6486 Jim_Obj **exprResultPtrPtr)
6487 {
6488 ExprByteCode *expr;
6489 Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6490 int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6491
6492 Jim_IncrRefCount(exprObjPtr);
6493 expr = Jim_GetExpression(interp, exprObjPtr);
6494 if (!expr) {
6495 Jim_DecrRefCount(interp, exprObjPtr);
6496 return JIM_ERR; /* error in expression. */
6497 }
6498 /* In order to avoid that the internal repr gets freed due to
6499 * shimmering of the exprObjPtr's object, we make the internal rep
6500 * shared. */
6501 expr->inUse++;
6502
6503 /* The stack-based expr VM itself */
6504
6505 /* Stack allocation. Expr programs have the feature that
6506 * a program of length N can't require a stack longer than
6507 * N. */
6508 if (expr->len > JIM_EE_STATICSTACK_LEN)
6509 stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6510 else
6511 stack = staticStack;
6512
6513 /* Execute every istruction */
6514 for (i = 0; i < expr->len; i++) {
6515 Jim_Obj *A, *B, *objPtr;
6516 jim_wide wA, wB, wC;
6517 double dA, dB, dC;
6518 const char *sA, *sB;
6519 int Alen, Blen, retcode;
6520 int opcode = expr->opcode[i];
6521
6522 if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6523 stack[stacklen++] = expr->obj[i];
6524 Jim_IncrRefCount(expr->obj[i]);
6525 } else if (opcode == JIM_EXPROP_VARIABLE) {
6526 objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6527 if (objPtr == NULL) {
6528 error = 1;
6529 goto err;
6530 }
6531 stack[stacklen++] = objPtr;
6532 Jim_IncrRefCount(objPtr);
6533 } else if (opcode == JIM_EXPROP_SUBST) {
6534 if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6535 &objPtr, JIM_NONE)) != JIM_OK)
6536 {
6537 error = 1;
6538 errRetCode = retcode;
6539 goto err;
6540 }
6541 stack[stacklen++] = objPtr;
6542 Jim_IncrRefCount(objPtr);
6543 } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6544 objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6545 if (objPtr == NULL) {
6546 error = 1;
6547 goto err;
6548 }
6549 stack[stacklen++] = objPtr;
6550 Jim_IncrRefCount(objPtr);
6551 } else if (opcode == JIM_EXPROP_COMMAND) {
6552 if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6553 error = 1;
6554 errRetCode = retcode;
6555 goto err;
6556 }
6557 stack[stacklen++] = interp->result;
6558 Jim_IncrRefCount(interp->result);
6559 } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6560 opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6561 {
6562 /* Note that there isn't to increment the
6563 * refcount of objects. the references are moved
6564 * from stack to A and B. */
6565 B = stack[--stacklen];
6566 A = stack[--stacklen];
6567
6568 /* --- Integer --- */
6569 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6570 (B->typePtr == &doubleObjType && !B->bytes) ||
6571 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6572 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6573 goto trydouble;
6574 }
6575 Jim_DecrRefCount(interp, A);
6576 Jim_DecrRefCount(interp, B);
6577 switch(expr->opcode[i]) {
6578 case JIM_EXPROP_ADD: wC = wA+wB; break;
6579 case JIM_EXPROP_SUB: wC = wA-wB; break;
6580 case JIM_EXPROP_MUL: wC = wA*wB; break;
6581 case JIM_EXPROP_LT: wC = wA<wB; break;
6582 case JIM_EXPROP_GT: wC = wA>wB; break;
6583 case JIM_EXPROP_LTE: wC = wA<=wB; break;
6584 case JIM_EXPROP_GTE: wC = wA>=wB; break;
6585 case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6586 case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6587 case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6588 case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6589 case JIM_EXPROP_BITAND: wC = wA&wB; break;
6590 case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6591 case JIM_EXPROP_BITOR: wC = wA|wB; break;
6592 case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6593 case JIM_EXPROP_LOGICAND_LEFT:
6594 if (wA == 0) {
6595 i += (int)wB;
6596 wC = 0;
6597 } else {
6598 continue;
6599 }
6600 break;
6601 case JIM_EXPROP_LOGICOR_LEFT:
6602 if (wA != 0) {
6603 i += (int)wB;
6604 wC = 1;
6605 } else {
6606 continue;
6607 }
6608 break;
6609 case JIM_EXPROP_DIV:
6610 if (wB == 0) goto divbyzero;
6611 wC = wA/wB;
6612 break;
6613 case JIM_EXPROP_MOD:
6614 if (wB == 0) goto divbyzero;
6615 wC = wA%wB;
6616 break;
6617 case JIM_EXPROP_ROTL: {
6618 /* uint32_t would be better. But not everyone has inttypes.h?*/
6619 unsigned long uA = (unsigned long)wA;
6620 #ifdef _MSC_VER
6621 wC = _rotl(uA,(unsigned long)wB);
6622 #else
6623 const unsigned int S = sizeof(unsigned long) * 8;
6624 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6625 #endif
6626 break;
6627 }
6628 case JIM_EXPROP_ROTR: {
6629 unsigned long uA = (unsigned long)wA;
6630 #ifdef _MSC_VER
6631 wC = _rotr(uA,(unsigned long)wB);
6632 #else
6633 const unsigned int S = sizeof(unsigned long) * 8;
6634 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6635 #endif
6636 break;
6637 }
6638
6639 default:
6640 wC = 0; /* avoid gcc warning */
6641 break;
6642 }
6643 stack[stacklen] = Jim_NewIntObj(interp, wC);
6644 Jim_IncrRefCount(stack[stacklen]);
6645 stacklen++;
6646 continue;
6647 trydouble:
6648 /* --- Double --- */
6649 if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6650 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6651 Jim_DecrRefCount(interp, A);
6652 Jim_DecrRefCount(interp, B);
6653 error = 1;
6654 goto err;
6655 }
6656 Jim_DecrRefCount(interp, A);
6657 Jim_DecrRefCount(interp, B);
6658 switch(expr->opcode[i]) {
6659 case JIM_EXPROP_ROTL:
6660 case JIM_EXPROP_ROTR:
6661 case JIM_EXPROP_LSHIFT:
6662 case JIM_EXPROP_RSHIFT:
6663 case JIM_EXPROP_BITAND:
6664 case JIM_EXPROP_BITXOR:
6665 case JIM_EXPROP_BITOR:
6666 case JIM_EXPROP_MOD:
6667 case JIM_EXPROP_POW:
6668 Jim_SetResultString(interp,
6669 "Got floating-point value where integer was expected", -1);
6670 error = 1;
6671 goto err;
6672 break;
6673 case JIM_EXPROP_ADD: dC = dA+dB; break;
6674 case JIM_EXPROP_SUB: dC = dA-dB; break;
6675 case JIM_EXPROP_MUL: dC = dA*dB; break;
6676 case JIM_EXPROP_LT: dC = dA<dB; break;
6677 case JIM_EXPROP_GT: dC = dA>dB; break;
6678 case JIM_EXPROP_LTE: dC = dA<=dB; break;
6679 case JIM_EXPROP_GTE: dC = dA>=dB; break;
6680 case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6681 case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6682 case JIM_EXPROP_LOGICAND_LEFT:
6683 if (dA == 0) {
6684 i += (int)dB;
6685 dC = 0;
6686 } else {
6687 continue;
6688 }
6689 break;
6690 case JIM_EXPROP_LOGICOR_LEFT:
6691 if (dA != 0) {
6692 i += (int)dB;
6693 dC = 1;
6694 } else {
6695 continue;
6696 }
6697 break;
6698 case JIM_EXPROP_DIV:
6699 if (dB == 0) goto divbyzero;
6700 dC = dA/dB;
6701 break;
6702 default:
6703 dC = 0; /* avoid gcc warning */
6704 break;
6705 }
6706 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6707 Jim_IncrRefCount(stack[stacklen]);
6708 stacklen++;
6709 } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6710 B = stack[--stacklen];
6711 A = stack[--stacklen];
6712 sA = Jim_GetString(A, &Alen);
6713 sB = Jim_GetString(B, &Blen);
6714 switch(expr->opcode[i]) {
6715 case JIM_EXPROP_STREQ:
6716 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6717 wC = 1;
6718 else
6719 wC = 0;
6720 break;
6721 case JIM_EXPROP_STRNE:
6722 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6723 wC = 1;
6724 else
6725 wC = 0;
6726 break;
6727 default:
6728 wC = 0; /* avoid gcc warning */
6729 break;
6730 }
6731 Jim_DecrRefCount(interp, A);
6732 Jim_DecrRefCount(interp, B);
6733 stack[stacklen] = Jim_NewIntObj(interp, wC);
6734 Jim_IncrRefCount(stack[stacklen]);
6735 stacklen++;
6736 } else if (opcode == JIM_EXPROP_NOT ||
6737 opcode == JIM_EXPROP_BITNOT ||
6738 opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6739 opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6740 /* Note that there isn't to increment the
6741 * refcount of objects. the references are moved
6742 * from stack to A and B. */
6743 A = stack[--stacklen];
6744
6745 /* --- Integer --- */
6746 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6747 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6748 goto trydouble_unary;
6749 }
6750 Jim_DecrRefCount(interp, A);
6751 switch(expr->opcode[i]) {
6752 case JIM_EXPROP_NOT: wC = !wA; break;
6753 case JIM_EXPROP_BITNOT: wC = ~wA; break;
6754 case JIM_EXPROP_LOGICAND_RIGHT:
6755 case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6756 default:
6757 wC = 0; /* avoid gcc warning */
6758 break;
6759 }
6760 stack[stacklen] = Jim_NewIntObj(interp, wC);
6761 Jim_IncrRefCount(stack[stacklen]);
6762 stacklen++;
6763 continue;
6764 trydouble_unary:
6765 /* --- Double --- */
6766 if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
6767 Jim_DecrRefCount(interp, A);
6768 error = 1;
6769 goto err;
6770 }
6771 Jim_DecrRefCount(interp, A);
6772 switch(expr->opcode[i]) {
6773 case JIM_EXPROP_NOT: dC = !dA; break;
6774 case JIM_EXPROP_LOGICAND_RIGHT:
6775 case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
6776 case JIM_EXPROP_BITNOT:
6777 Jim_SetResultString(interp,
6778 "Got floating-point value where integer was expected", -1);
6779 error = 1;
6780 goto err;
6781 break;
6782 default:
6783 dC = 0; /* avoid gcc warning */
6784 break;
6785 }
6786 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6787 Jim_IncrRefCount(stack[stacklen]);
6788 stacklen++;
6789 } else {
6790 Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
6791 }
6792 }
6793 err:
6794 /* There is no need to decerement the inUse field because
6795 * this reference is transfered back into the exprObjPtr. */
6796 Jim_FreeIntRep(interp, exprObjPtr);
6797 exprObjPtr->typePtr = &exprObjType;
6798 Jim_SetIntRepPtr(exprObjPtr, expr);
6799 Jim_DecrRefCount(interp, exprObjPtr);
6800 if (!error) {
6801 *exprResultPtrPtr = stack[0];
6802 Jim_IncrRefCount(stack[0]);
6803 errRetCode = JIM_OK;
6804 }
6805 for (i = 0; i < stacklen; i++) {
6806 Jim_DecrRefCount(interp, stack[i]);
6807 }
6808 if (stack != staticStack)
6809 Jim_Free(stack);
6810 return errRetCode;
6811 divbyzero:
6812 error = 1;
6813 Jim_SetResultString(interp, "Division by zero", -1);
6814 goto err;
6815 }
6816
6817 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
6818 {
6819 int retcode;
6820 jim_wide wideValue;
6821 double doubleValue;
6822 Jim_Obj *exprResultPtr;
6823
6824 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
6825 if (retcode != JIM_OK)
6826 return retcode;
6827 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
6828 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
6829 {
6830 Jim_DecrRefCount(interp, exprResultPtr);
6831 return JIM_ERR;
6832 } else {
6833 Jim_DecrRefCount(interp, exprResultPtr);
6834 *boolPtr = doubleValue != 0;
6835 return JIM_OK;
6836 }
6837 }
6838 Jim_DecrRefCount(interp, exprResultPtr);
6839 *boolPtr = wideValue != 0;
6840 return JIM_OK;
6841 }
6842
6843 /* -----------------------------------------------------------------------------
6844 * ScanFormat String Object
6845 * ---------------------------------------------------------------------------*/
6846
6847 /* This Jim_Obj will held a parsed representation of a format string passed to
6848 * the Jim_ScanString command. For error diagnostics, the scanformat string has
6849 * to be parsed in its entirely first and then, if correct, can be used for
6850 * scanning. To avoid endless re-parsing, the parsed representation will be
6851 * stored in an internal representation and re-used for performance reason. */
6852
6853 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
6854 * scanformat string. This part will later be used to extract information
6855 * out from the string to be parsed by Jim_ScanString */
6856
6857 typedef struct ScanFmtPartDescr {
6858 char type; /* Type of conversion (e.g. c, d, f) */
6859 char modifier; /* Modify type (e.g. l - long, h - short */
6860 size_t width; /* Maximal width of input to be converted */
6861 int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
6862 char *arg; /* Specification of a CHARSET conversion */
6863 char *prefix; /* Prefix to be scanned literally before conversion */
6864 } ScanFmtPartDescr;
6865
6866 /* The ScanFmtStringObj will held the internal representation of a scanformat
6867 * string parsed and separated in part descriptions. Furthermore it contains
6868 * the original string representation of the scanformat string to allow for
6869 * fast update of the Jim_Obj's string representation part.
6870 *
6871 * As add-on the internal object representation add some scratch pad area
6872 * for usage by Jim_ScanString to avoid endless allocating and freeing of
6873 * memory for purpose of string scanning.
6874 *
6875 * The error member points to a static allocated string in case of a mal-
6876 * formed scanformat string or it contains '0' (NULL) in case of a valid
6877 * parse representation.
6878 *
6879 * The whole memory of the internal representation is allocated as a single
6880 * area of memory that will be internally separated. So freeing and duplicating
6881 * of such an object is cheap */
6882
6883 typedef struct ScanFmtStringObj {
6884 jim_wide size; /* Size of internal repr in bytes */
6885 char *stringRep; /* Original string representation */
6886 size_t count; /* Number of ScanFmtPartDescr contained */
6887 size_t convCount; /* Number of conversions that will assign */
6888 size_t maxPos; /* Max position index if XPG3 is used */
6889 const char *error; /* Ptr to error text (NULL if no error */
6890 char *scratch; /* Some scratch pad used by Jim_ScanString */
6891 ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
6892 } ScanFmtStringObj;
6893
6894
6895 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6896 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6897 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
6898
6899 static Jim_ObjType scanFmtStringObjType = {
6900 "scanformatstring",
6901 FreeScanFmtInternalRep,
6902 DupScanFmtInternalRep,
6903 UpdateStringOfScanFmt,
6904 JIM_TYPE_NONE,
6905 };
6906
6907 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6908 {
6909 JIM_NOTUSED(interp);
6910 Jim_Free((char*)objPtr->internalRep.ptr);
6911 objPtr->internalRep.ptr = 0;
6912 }
6913
6914 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6915 {
6916 size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
6917 ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
6918
6919 JIM_NOTUSED(interp);
6920 memcpy(newVec, srcPtr->internalRep.ptr, size);
6921 dupPtr->internalRep.ptr = newVec;
6922 dupPtr->typePtr = &scanFmtStringObjType;
6923 }
6924
6925 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
6926 {
6927 char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
6928
6929 objPtr->bytes = Jim_StrDup(bytes);
6930 objPtr->length = strlen(bytes);
6931 }
6932
6933 /* SetScanFmtFromAny will parse a given string and create the internal
6934 * representation of the format specification. In case of an error
6935 * the error data member of the internal representation will be set
6936 * to an descriptive error text and the function will be left with
6937 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
6938 * specification */
6939
6940 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6941 {
6942 ScanFmtStringObj *fmtObj;
6943 char *buffer;
6944 int maxCount, i, approxSize, lastPos = -1;
6945 const char *fmt = objPtr->bytes;
6946 int maxFmtLen = objPtr->length;
6947 const char *fmtEnd = fmt + maxFmtLen;
6948 int curr;
6949
6950 Jim_FreeIntRep(interp, objPtr);
6951 /* Count how many conversions could take place maximally */
6952 for (i=0, maxCount=0; i < maxFmtLen; ++i)
6953 if (fmt[i] == '%')
6954 ++maxCount;
6955 /* Calculate an approximation of the memory necessary */
6956 approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
6957 + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
6958 + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
6959 + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
6960 + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
6961 + (maxCount +1) * sizeof(char) /* '\0' for every partial */
6962 + 1; /* safety byte */
6963 fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
6964 memset(fmtObj, 0, approxSize);
6965 fmtObj->size = approxSize;
6966 fmtObj->maxPos = 0;
6967 fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
6968 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
6969 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
6970 buffer = fmtObj->stringRep + maxFmtLen + 1;
6971 objPtr->internalRep.ptr = fmtObj;
6972 objPtr->typePtr = &scanFmtStringObjType;
6973 for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
6974 int width=0, skip;
6975 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
6976 fmtObj->count++;
6977 descr->width = 0; /* Assume width unspecified */
6978 /* Overread and store any "literal" prefix */
6979 if (*fmt != '%' || fmt[1] == '%') {
6980 descr->type = 0;
6981 descr->prefix = &buffer[i];
6982 for (; fmt < fmtEnd; ++fmt) {
6983 if (*fmt == '%') {
6984 if (fmt[1] != '%') break;
6985 ++fmt;
6986 }
6987 buffer[i++] = *fmt;
6988 }
6989 buffer[i++] = 0;
6990 }
6991 /* Skip the conversion introducing '%' sign */
6992 ++fmt;
6993 /* End reached due to non-conversion literal only? */
6994 if (fmt >= fmtEnd)
6995 goto done;
6996 descr->pos = 0; /* Assume "natural" positioning */
6997 if (*fmt == '*') {
6998 descr->pos = -1; /* Okay, conversion will not be assigned */
6999 ++fmt;
7000 } else
7001 fmtObj->convCount++; /* Otherwise count as assign-conversion */
7002 /* Check if next token is a number (could be width or pos */
7003 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7004 fmt += skip;
7005 /* Was the number a XPG3 position specifier? */
7006 if (descr->pos != -1 && *fmt == '$') {
7007 int prev;
7008 ++fmt;
7009 descr->pos = width;
7010 width = 0;
7011 /* Look if "natural" postioning and XPG3 one was mixed */
7012 if ((lastPos == 0 && descr->pos > 0)
7013 || (lastPos > 0 && descr->pos == 0)) {
7014 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7015 return JIM_ERR;
7016 }
7017 /* Look if this position was already used */
7018 for (prev=0; prev < curr; ++prev) {
7019 if (fmtObj->descr[prev].pos == -1) continue;
7020 if (fmtObj->descr[prev].pos == descr->pos) {
7021 fmtObj->error = "same \"%n$\" conversion specifier "
7022 "used more than once";
7023 return JIM_ERR;
7024 }
7025 }
7026 /* Try to find a width after the XPG3 specifier */
7027 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7028 descr->width = width;
7029 fmt += skip;
7030 }
7031 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7032 fmtObj->maxPos = descr->pos;
7033 } else {
7034 /* Number was not a XPG3, so it has to be a width */
7035 descr->width = width;
7036 }
7037 }
7038 /* If positioning mode was undetermined yet, fix this */
7039 if (lastPos == -1)
7040 lastPos = descr->pos;
7041 /* Handle CHARSET conversion type ... */
7042 if (*fmt == '[') {
7043 int swapped = 1, beg = i, end, j;
7044 descr->type = '[';
7045 descr->arg = &buffer[i];
7046 ++fmt;
7047 if (*fmt == '^') buffer[i++] = *fmt++;
7048 if (*fmt == ']') buffer[i++] = *fmt++;
7049 while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7050 if (*fmt != ']') {
7051 fmtObj->error = "unmatched [ in format string";
7052 return JIM_ERR;
7053 }
7054 end = i;
7055 buffer[i++] = 0;
7056 /* In case a range fence was given "backwards", swap it */
7057 while (swapped) {
7058 swapped = 0;
7059 for (j=beg+1; j < end-1; ++j) {
7060 if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7061 char tmp = buffer[j-1];
7062 buffer[j-1] = buffer[j+1];
7063 buffer[j+1] = tmp;
7064 swapped = 1;
7065 }
7066 }
7067 }
7068 } else {
7069 /* Remember any valid modifier if given */
7070 if (strchr("hlL", *fmt) != 0)
7071 descr->modifier = tolower((int)*fmt++);
7072
7073 descr->type = *fmt;
7074 if (strchr("efgcsndoxui", *fmt) == 0) {
7075 fmtObj->error = "bad scan conversion character";
7076 return JIM_ERR;
7077 } else if (*fmt == 'c' && descr->width != 0) {
7078 fmtObj->error = "field width may not be specified in %c "
7079 "conversion";
7080 return JIM_ERR;
7081 } else if (*fmt == 'u' && descr->modifier == 'l') {
7082 fmtObj->error = "unsigned wide not supported";
7083 return JIM_ERR;
7084 }
7085 }
7086 curr++;
7087 }
7088 done:
7089 if (fmtObj->convCount == 0) {
7090 fmtObj->error = "no any conversion specifier given";
7091 return JIM_ERR;
7092 }
7093 return JIM_OK;
7094 }
7095
7096 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7097
7098 #define FormatGetCnvCount(_fo_) \
7099 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7100 #define FormatGetMaxPos(_fo_) \
7101 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7102 #define FormatGetError(_fo_) \
7103 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7104
7105 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7106 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7107 * bitvector implementation in Jim? */
7108
7109 static int JimTestBit(const char *bitvec, char ch)
7110 {
7111 div_t pos = div(ch-1, 8);
7112 return bitvec[pos.quot] & (1 << pos.rem);
7113 }
7114
7115 static void JimSetBit(char *bitvec, char ch)
7116 {
7117 div_t pos = div(ch-1, 8);
7118 bitvec[pos.quot] |= (1 << pos.rem);
7119 }
7120
7121 #if 0 /* currently not used */
7122 static void JimClearBit(char *bitvec, char ch)
7123 {
7124 div_t pos = div(ch-1, 8);
7125 bitvec[pos.quot] &= ~(1 << pos.rem);
7126 }
7127 #endif
7128
7129 /* JimScanAString is used to scan an unspecified string that ends with
7130 * next WS, or a string that is specified via a charset. The charset
7131 * is currently implemented in a way to only allow for usage with
7132 * ASCII. Whenever we will switch to UNICODE, another idea has to
7133 * be born :-/
7134 *
7135 * FIXME: Works only with ASCII */
7136
7137 static Jim_Obj *
7138 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7139 {
7140 size_t i;
7141 Jim_Obj *result;
7142 char charset[256/8+1]; /* A Charset may contain max 256 chars */
7143 char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7144
7145 /* First init charset to nothing or all, depending if a specified
7146 * or an unspecified string has to be parsed */
7147 memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7148 if (sdescr) {
7149 /* There was a set description given, that means we are parsing
7150 * a specified string. So we have to build a corresponding
7151 * charset reflecting the description */
7152 int notFlag = 0;
7153 /* Should the set be negated at the end? */
7154 if (*sdescr == '^') {
7155 notFlag = 1;
7156 ++sdescr;
7157 }
7158 /* Here '-' is meant literally and not to define a range */
7159 if (*sdescr == '-') {
7160 JimSetBit(charset, '-');
7161 ++sdescr;
7162 }
7163 while (*sdescr) {
7164 if (sdescr[1] == '-' && sdescr[2] != 0) {
7165 /* Handle range definitions */
7166 int i;
7167 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7168 JimSetBit(charset, (char)i);
7169 sdescr += 3;
7170 } else {
7171 /* Handle verbatim character definitions */
7172 JimSetBit(charset, *sdescr++);
7173 }
7174 }
7175 /* Negate the charset if there was a NOT given */
7176 for (i=0; notFlag && i < sizeof(charset); ++i)
7177 charset[i] = ~charset[i];
7178 }
7179 /* And after all the mess above, the real work begin ... */
7180 while (str && *str) {
7181 if (!sdescr && isspace((int)*str))
7182 break; /* EOS via WS if unspecified */
7183 if (JimTestBit(charset, *str)) *buffer++ = *str++;
7184 else break; /* EOS via mismatch if specified scanning */
7185 }
7186 *buffer = 0; /* Close the string properly ... */
7187 result = Jim_NewStringObj(interp, anchor, -1);
7188 Jim_Free(anchor); /* ... and free it afer usage */
7189 return result;
7190 }
7191
7192 /* ScanOneEntry will scan one entry out of the string passed as argument.
7193 * It use the sscanf() function for this task. After extracting and
7194 * converting of the value, the count of scanned characters will be
7195 * returned of -1 in case of no conversion tool place and string was
7196 * already scanned thru */
7197
7198 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7199 ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7200 {
7201 # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7202 ? sizeof(jim_wide) \
7203 : sizeof(double))
7204 char buffer[MAX_SIZE];
7205 char *value = buffer;
7206 const char *tok;
7207 const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7208 size_t sLen = strlen(&str[pos]), scanned = 0;
7209 size_t anchor = pos;
7210 int i;
7211
7212 /* First pessimiticly assume, we will not scan anything :-) */
7213 *valObjPtr = 0;
7214 if (descr->prefix) {
7215 /* There was a prefix given before the conversion, skip it and adjust
7216 * the string-to-be-parsed accordingly */
7217 for (i=0; str[pos] && descr->prefix[i]; ++i) {
7218 /* If prefix require, skip WS */
7219 if (isspace((int)descr->prefix[i]))
7220 while (str[pos] && isspace((int)str[pos])) ++pos;
7221 else if (descr->prefix[i] != str[pos])
7222 break; /* Prefix do not match here, leave the loop */
7223 else
7224 ++pos; /* Prefix matched so far, next round */
7225 }
7226 if (str[pos] == 0)
7227 return -1; /* All of str consumed: EOF condition */
7228 else if (descr->prefix[i] != 0)
7229 return 0; /* Not whole prefix consumed, no conversion possible */
7230 }
7231 /* For all but following conversion, skip leading WS */
7232 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7233 while (isspace((int)str[pos])) ++pos;
7234 /* Determine how much skipped/scanned so far */
7235 scanned = pos - anchor;
7236 if (descr->type == 'n') {
7237 /* Return pseudo conversion means: how much scanned so far? */
7238 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7239 } else if (str[pos] == 0) {
7240 /* Cannot scan anything, as str is totally consumed */
7241 return -1;
7242 } else {
7243 /* Processing of conversions follows ... */
7244 if (descr->width > 0) {
7245 /* Do not try to scan as fas as possible but only the given width.
7246 * To ensure this, we copy the part that should be scanned. */
7247 size_t tLen = descr->width > sLen ? sLen : descr->width;
7248 tok = Jim_StrDupLen(&str[pos], tLen);
7249 } else {
7250 /* As no width was given, simply refer to the original string */
7251 tok = &str[pos];
7252 }
7253 switch (descr->type) {
7254 case 'c':
7255 *valObjPtr = Jim_NewIntObj(interp, *tok);
7256 scanned += 1;
7257 break;
7258 case 'd': case 'o': case 'x': case 'u': case 'i': {
7259 char *endp; /* Position where the number finished */
7260 int base = descr->type == 'o' ? 8
7261 : descr->type == 'x' ? 16
7262 : descr->type == 'i' ? 0
7263 : 10;
7264
7265 do {
7266 /* Try to scan a number with the given base */
7267 if (descr->modifier == 'l')
7268 #ifdef HAVE_LONG_LONG
7269 *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7270 #else
7271 *(jim_wide*)value = strtol(tok, &endp, base);
7272 #endif
7273 else
7274 if (descr->type == 'u')
7275 *(long*)value = strtoul(tok, &endp, base);
7276 else
7277 *(long*)value = strtol(tok, &endp, base);
7278 /* If scanning failed, and base was undetermined, simply
7279 * put it to 10 and try once more. This should catch the
7280 * case where %i begin to parse a number prefix (e.g.
7281 * '0x' but no further digits follows. This will be
7282 * handled as a ZERO followed by a char 'x' by Tcl */
7283 if (endp == tok && base == 0) base = 10;
7284 else break;
7285 } while (1);
7286 if (endp != tok) {
7287 /* There was some number sucessfully scanned! */
7288 if (descr->modifier == 'l')
7289 *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7290 else
7291 *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7292 /* Adjust the number-of-chars scanned so far */
7293 scanned += endp - tok;
7294 } else {
7295 /* Nothing was scanned. We have to determine if this
7296 * happened due to e.g. prefix mismatch or input str
7297 * exhausted */
7298 scanned = *tok ? 0 : -1;
7299 }
7300 break;
7301 }
7302 case 's': case '[': {
7303 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7304 scanned += Jim_Length(*valObjPtr);
7305 break;
7306 }
7307 case 'e': case 'f': case 'g': {
7308 char *endp;
7309
7310 *(double*)value = strtod(tok, &endp);
7311 if (endp != tok) {
7312 /* There was some number sucessfully scanned! */
7313 *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7314 /* Adjust the number-of-chars scanned so far */
7315 scanned += endp - tok;
7316 } else {
7317 /* Nothing was scanned. We have to determine if this
7318 * happened due to e.g. prefix mismatch or input str
7319 * exhausted */
7320 scanned = *tok ? 0 : -1;
7321 }
7322 break;
7323 }
7324 }
7325 /* If a substring was allocated (due to pre-defined width) do not
7326 * forget to free it */
7327 if (tok != &str[pos])
7328 Jim_Free((char*)tok);
7329 }
7330 return scanned;
7331 }
7332
7333 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7334 * string and returns all converted (and not ignored) values in a list back
7335 * to the caller. If an error occured, a NULL pointer will be returned */
7336
7337 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7338 Jim_Obj *fmtObjPtr, int flags)
7339 {
7340 size_t i, pos;
7341 int scanned = 1;
7342 const char *str = Jim_GetString(strObjPtr, 0);
7343 Jim_Obj *resultList = 0;
7344 Jim_Obj **resultVec;
7345 int resultc;
7346 Jim_Obj *emptyStr = 0;
7347 ScanFmtStringObj *fmtObj;
7348
7349 /* If format specification is not an object, convert it! */
7350 if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7351 SetScanFmtFromAny(interp, fmtObjPtr);
7352 fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7353 /* Check if format specification was valid */
7354 if (fmtObj->error != 0) {
7355 if (flags & JIM_ERRMSG)
7356 Jim_SetResultString(interp, fmtObj->error, -1);
7357 return 0;
7358 }
7359 /* Allocate a new "shared" empty string for all unassigned conversions */
7360 emptyStr = Jim_NewEmptyStringObj(interp);
7361 Jim_IncrRefCount(emptyStr);
7362 /* Create a list and fill it with empty strings up to max specified XPG3 */
7363 resultList = Jim_NewListObj(interp, 0, 0);
7364 if (fmtObj->maxPos > 0) {
7365 for (i=0; i < fmtObj->maxPos; ++i)
7366 Jim_ListAppendElement(interp, resultList, emptyStr);
7367 JimListGetElements(interp, resultList, &resultc, &resultVec);
7368 }
7369 /* Now handle every partial format description */
7370 for (i=0, pos=0; i < fmtObj->count; ++i) {
7371 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7372 Jim_Obj *value = 0;
7373 /* Only last type may be "literal" w/o conversion - skip it! */
7374 if (descr->type == 0) continue;
7375 /* As long as any conversion could be done, we will proceed */
7376 if (scanned > 0)
7377 scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7378 /* In case our first try results in EOF, we will leave */
7379 if (scanned == -1 && i == 0)
7380 goto eof;
7381 /* Advance next pos-to-be-scanned for the amount scanned already */
7382 pos += scanned;
7383 /* value == 0 means no conversion took place so take empty string */
7384 if (value == 0)
7385 value = Jim_NewEmptyStringObj(interp);
7386 /* If value is a non-assignable one, skip it */
7387 if (descr->pos == -1) {
7388 Jim_FreeNewObj(interp, value);
7389 } else if (descr->pos == 0)
7390 /* Otherwise append it to the result list if no XPG3 was given */
7391 Jim_ListAppendElement(interp, resultList, value);
7392 else if (resultVec[descr->pos-1] == emptyStr) {
7393 /* But due to given XPG3, put the value into the corr. slot */
7394 Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7395 Jim_IncrRefCount(value);
7396 resultVec[descr->pos-1] = value;
7397 } else {
7398 /* Otherwise, the slot was already used - free obj and ERROR */
7399 Jim_FreeNewObj(interp, value);
7400 goto err;
7401 }
7402 }
7403 Jim_DecrRefCount(interp, emptyStr);
7404 return resultList;
7405 eof:
7406 Jim_DecrRefCount(interp, emptyStr);
7407 Jim_FreeNewObj(interp, resultList);
7408 return (Jim_Obj*)EOF;
7409 err:
7410 Jim_DecrRefCount(interp, emptyStr);
7411 Jim_FreeNewObj(interp, resultList);
7412 return 0;
7413 }
7414
7415 /* -----------------------------------------------------------------------------
7416 * Pseudo Random Number Generation
7417 * ---------------------------------------------------------------------------*/
7418 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7419 int seedLen);
7420
7421 /* Initialize the sbox with the numbers from 0 to 255 */
7422 static void JimPrngInit(Jim_Interp *interp)
7423 {
7424 int i;
7425 unsigned int seed[256];
7426
7427 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7428 for (i = 0; i < 256; i++)
7429 seed[i] = (rand() ^ time(NULL) ^ clock());
7430 JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7431 }
7432
7433 /* Generates N bytes of random data */
7434 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7435 {
7436 Jim_PrngState *prng;
7437 unsigned char *destByte = (unsigned char*) dest;
7438 unsigned int si, sj, x;
7439
7440 /* initialization, only needed the first time */
7441 if (interp->prngState == NULL)
7442 JimPrngInit(interp);
7443 prng = interp->prngState;
7444 /* generates 'len' bytes of pseudo-random numbers */
7445 for (x = 0; x < len; x++) {
7446 prng->i = (prng->i+1) & 0xff;
7447 si = prng->sbox[prng->i];
7448 prng->j = (prng->j + si) & 0xff;
7449 sj = prng->sbox[prng->j];
7450 prng->sbox[prng->i] = sj;
7451 prng->sbox[prng->j] = si;
7452 *destByte++ = prng->sbox[(si+sj)&0xff];
7453 }
7454 }
7455
7456 /* Re-seed the generator with user-provided bytes */
7457 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7458 int seedLen)
7459 {
7460 int i;
7461 unsigned char buf[256];
7462 Jim_PrngState *prng;
7463
7464 /* initialization, only needed the first time */
7465 if (interp->prngState == NULL)
7466 JimPrngInit(interp);
7467 prng = interp->prngState;
7468
7469 /* Set the sbox[i] with i */
7470 for (i = 0; i < 256; i++)
7471 prng->sbox[i] = i;
7472 /* Now use the seed to perform a random permutation of the sbox */
7473 for (i = 0; i < seedLen; i++) {
7474 unsigned char t;
7475
7476 t = prng->sbox[i&0xFF];
7477 prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7478 prng->sbox[seed[i]] = t;
7479 }
7480 prng->i = prng->j = 0;
7481 /* discard the first 256 bytes of stream. */
7482 JimRandomBytes(interp, buf, 256);
7483 }
7484
7485 /* -----------------------------------------------------------------------------
7486 * Dynamic libraries support (WIN32 not supported)
7487 * ---------------------------------------------------------------------------*/
7488
7489 #ifdef JIM_DYNLIB
7490 #ifdef WIN32
7491 #define RTLD_LAZY 0
7492 void * dlopen(const char *path, int mode)
7493 {
7494 JIM_NOTUSED(mode);
7495
7496 return (void *)LoadLibraryA(path);
7497 }
7498 int dlclose(void *handle)
7499 {
7500 FreeLibrary((HANDLE)handle);
7501 return 0;
7502 }
7503 void *dlsym(void *handle, const char *symbol)
7504 {
7505 return GetProcAddress((HMODULE)handle, symbol);
7506 }
7507 static char win32_dlerror_string[121];
7508 const char *dlerror()
7509 {
7510 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7511 LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7512 return win32_dlerror_string;
7513 }
7514 #endif /* WIN32 */
7515
7516 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7517 {
7518 Jim_Obj *libPathObjPtr;
7519 int prefixc, i;
7520 void *handle;
7521 int (*onload)(Jim_Interp *interp);
7522
7523 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7524 if (libPathObjPtr == NULL) {
7525 prefixc = 0;
7526 libPathObjPtr = NULL;
7527 } else {
7528 Jim_IncrRefCount(libPathObjPtr);
7529 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7530 }
7531
7532 for (i = -1; i < prefixc; i++) {
7533 if (i < 0) {
7534 handle = dlopen(pathName, RTLD_LAZY);
7535 } else {
7536 FILE *fp;
7537 char buf[JIM_PATH_LEN];
7538 const char *prefix;
7539 int prefixlen;
7540 Jim_Obj *prefixObjPtr;
7541
7542 buf[0] = '\0';
7543 if (Jim_ListIndex(interp, libPathObjPtr, i,
7544 &prefixObjPtr, JIM_NONE) != JIM_OK)
7545 continue;
7546 prefix = Jim_GetString(prefixObjPtr, NULL);
7547 prefixlen = strlen(prefix);
7548 if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7549 continue;
7550 if (prefixlen && prefix[prefixlen-1] == '/')
7551 sprintf(buf, "%s%s", prefix, pathName);
7552 else
7553 sprintf(buf, "%s/%s", prefix, pathName);
7554 printf("opening '%s'\n", buf);
7555 fp = fopen(buf, "r");
7556 if (fp == NULL)
7557 continue;
7558 fclose(fp);
7559 handle = dlopen(buf, RTLD_LAZY);
7560 printf("got handle %p\n", handle);
7561 }
7562 if (handle == NULL) {
7563 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7564 Jim_AppendStrings(interp, Jim_GetResult(interp),
7565 "error loading extension \"", pathName,
7566 "\": ", dlerror(), NULL);
7567 if (i < 0)
7568 continue;
7569 goto err;
7570 }
7571 if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7572 Jim_SetResultString(interp,
7573 "No Jim_OnLoad symbol found on extension", -1);
7574 goto err;
7575 }
7576 if (onload(interp) == JIM_ERR) {
7577 dlclose(handle);
7578 goto err;
7579 }
7580 Jim_SetEmptyResult(interp);
7581 if (libPathObjPtr != NULL)
7582 Jim_DecrRefCount(interp, libPathObjPtr);
7583 return JIM_OK;
7584 }
7585 err:
7586 if (libPathObjPtr != NULL)
7587 Jim_DecrRefCount(interp, libPathObjPtr);
7588 return JIM_ERR;
7589 }
7590 #else /* JIM_DYNLIB */
7591 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7592 {
7593 JIM_NOTUSED(interp);
7594 JIM_NOTUSED(pathName);
7595
7596 Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7597 return JIM_ERR;
7598 }
7599 #endif/* JIM_DYNLIB */
7600
7601 /* -----------------------------------------------------------------------------
7602 * Packages handling
7603 * ---------------------------------------------------------------------------*/
7604
7605 #define JIM_PKG_ANY_VERSION -1
7606
7607 /* Convert a string of the type "1.2" into an integer.
7608 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
7609 * to the integer with value 102 */
7610 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7611 int *intPtr, int flags)
7612 {
7613 char *copy;
7614 jim_wide major, minor;
7615 char *majorStr, *minorStr, *p;
7616
7617 if (v[0] == '\0') {
7618 *intPtr = JIM_PKG_ANY_VERSION;
7619 return JIM_OK;
7620 }
7621
7622 copy = Jim_StrDup(v);
7623 p = strchr(copy, '.');
7624 if (p == NULL) goto badfmt;
7625 *p = '\0';
7626 majorStr = copy;
7627 minorStr = p+1;
7628
7629 if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7630 Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7631 goto badfmt;
7632 *intPtr = (int)(major*100+minor);
7633 Jim_Free(copy);
7634 return JIM_OK;
7635
7636 badfmt:
7637 Jim_Free(copy);
7638 if (flags & JIM_ERRMSG) {
7639 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7640 Jim_AppendStrings(interp, Jim_GetResult(interp),
7641 "invalid package version '", v, "'", NULL);
7642 }
7643 return JIM_ERR;
7644 }
7645
7646 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7647 static int JimPackageMatchVersion(int needed, int actual, int flags)
7648 {
7649 if (needed == JIM_PKG_ANY_VERSION) return 1;
7650 if (flags & JIM_MATCHVER_EXACT) {
7651 return needed == actual;
7652 } else {
7653 return needed/100 == actual/100 && (needed <= actual);
7654 }
7655 }
7656
7657 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7658 int flags)
7659 {
7660 int intVersion;
7661 /* Check if the version format is ok */
7662 if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7663 return JIM_ERR;
7664 /* If the package was already provided returns an error. */
7665 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7666 if (flags & JIM_ERRMSG) {
7667 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7668 Jim_AppendStrings(interp, Jim_GetResult(interp),
7669 "package '", name, "' was already provided", NULL);
7670 }
7671 return JIM_ERR;
7672 }
7673 Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7674 return JIM_OK;
7675 }
7676
7677 #ifndef JIM_ANSIC
7678
7679 #ifndef WIN32
7680 # include <sys/types.h>
7681 # include <dirent.h>
7682 #else
7683 # include <io.h>
7684 /* Posix dirent.h compatiblity layer for WIN32.
7685 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7686 * Copyright Salvatore Sanfilippo ,2005.
7687 *
7688 * Permission to use, copy, modify, and distribute this software and its
7689 * documentation for any purpose is hereby granted without fee, provided
7690 * that this copyright and permissions notice appear in all copies and
7691 * derivatives.
7692 *
7693 * This software is supplied "as is" without express or implied warranty.
7694 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7695 */
7696
7697 struct dirent {
7698 char *d_name;
7699 };
7700
7701 typedef struct DIR {
7702 long handle; /* -1 for failed rewind */
7703 struct _finddata_t info;
7704 struct dirent result; /* d_name null iff first time */
7705 char *name; /* null-terminated char string */
7706 } DIR;
7707
7708 DIR *opendir(const char *name)
7709 {
7710 DIR *dir = 0;
7711
7712 if(name && name[0]) {
7713 size_t base_length = strlen(name);
7714 const char *all = /* search pattern must end with suitable wildcard */
7715 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7716
7717 if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7718 (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7719 {
7720 strcat(strcpy(dir->name, name), all);
7721
7722 if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7723 dir->result.d_name = 0;
7724 else { /* rollback */
7725 Jim_Free(dir->name);
7726 Jim_Free(dir);
7727 dir = 0;
7728 }
7729 } else { /* rollback */
7730 Jim_Free(dir);
7731 dir = 0;
7732 errno = ENOMEM;
7733 }
7734 } else {
7735 errno = EINVAL;
7736 }
7737 return dir;
7738 }
7739
7740 int closedir(DIR *dir)
7741 {
7742 int result = -1;
7743
7744 if(dir) {
7745 if(dir->handle != -1)
7746 result = _findclose(dir->handle);
7747 Jim_Free(dir->name);
7748 Jim_Free(dir);
7749 }
7750 if(result == -1) /* map all errors to EBADF */
7751 errno = EBADF;
7752 return result;
7753 }
7754
7755 struct dirent *readdir(DIR *dir)
7756 {
7757 struct dirent *result = 0;
7758
7759 if(dir && dir->handle != -1) {
7760 if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7761 result = &dir->result;
7762 result->d_name = dir->info.name;
7763 }
7764 } else {
7765 errno = EBADF;
7766 }
7767 return result;
7768 }
7769
7770 #endif /* WIN32 */
7771
7772 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7773 int prefixc, const char *pkgName, int pkgVer, int flags)
7774 {
7775 int bestVer = -1, i;
7776 int pkgNameLen = strlen(pkgName);
7777 char *bestPackage = NULL;
7778 struct dirent *de;
7779
7780 for (i = 0; i < prefixc; i++) {
7781 DIR *dir;
7782 char buf[JIM_PATH_LEN];
7783 int prefixLen;
7784
7785 if (prefixes[i] == NULL) continue;
7786 strncpy(buf, prefixes[i], JIM_PATH_LEN);
7787 buf[JIM_PATH_LEN-1] = '\0';
7788 prefixLen = strlen(buf);
7789 if (prefixLen && buf[prefixLen-1] == '/')
7790 buf[prefixLen-1] = '\0';
7791
7792 if ((dir = opendir(buf)) == NULL) continue;
7793 while ((de = readdir(dir)) != NULL) {
7794 char *fileName = de->d_name;
7795 int fileNameLen = strlen(fileName);
7796
7797 if (strncmp(fileName, "jim-", 4) == 0 &&
7798 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
7799 *(fileName+4+pkgNameLen) == '-' &&
7800 fileNameLen > 4 && /* note that this is not really useful */
7801 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
7802 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
7803 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
7804 {
7805 char ver[6]; /* xx.yy<nulterm> */
7806 char *p = strrchr(fileName, '.');
7807 int verLen, fileVer;
7808
7809 verLen = p - (fileName+4+pkgNameLen+1);
7810 if (verLen < 3 || verLen > 5) continue;
7811 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
7812 ver[verLen] = '\0';
7813 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
7814 != JIM_OK) continue;
7815 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
7816 (bestVer == -1 || bestVer < fileVer))
7817 {
7818 bestVer = fileVer;
7819 Jim_Free(bestPackage);
7820 bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
7821 sprintf(bestPackage, "%s/%s", buf, fileName);
7822 }
7823 }
7824 }
7825 closedir(dir);
7826 }
7827 return bestPackage;
7828 }
7829
7830 #else /* JIM_ANSIC */
7831
7832 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7833 int prefixc, const char *pkgName, int pkgVer, int flags)
7834 {
7835 JIM_NOTUSED(interp);
7836 JIM_NOTUSED(prefixes);
7837 JIM_NOTUSED(prefixc);
7838 JIM_NOTUSED(pkgName);
7839 JIM_NOTUSED(pkgVer);
7840 JIM_NOTUSED(flags);
7841 return NULL;
7842 }
7843
7844 #endif /* JIM_ANSIC */
7845
7846 /* Search for a suitable package under every dir specified by jim_libpath
7847 * and load it if possible. If a suitable package was loaded with success
7848 * JIM_OK is returned, otherwise JIM_ERR is returned. */
7849 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
7850 int flags)
7851 {
7852 Jim_Obj *libPathObjPtr;
7853 char **prefixes, *best;
7854 int prefixc, i, retCode = JIM_OK;
7855
7856 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7857 if (libPathObjPtr == NULL) {
7858 prefixc = 0;
7859 libPathObjPtr = NULL;
7860 } else {
7861 Jim_IncrRefCount(libPathObjPtr);
7862 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7863 }
7864
7865 prefixes = Jim_Alloc(sizeof(char*)*prefixc);
7866 for (i = 0; i < prefixc; i++) {
7867 Jim_Obj *prefixObjPtr;
7868 if (Jim_ListIndex(interp, libPathObjPtr, i,
7869 &prefixObjPtr, JIM_NONE) != JIM_OK)
7870 {
7871 prefixes[i] = NULL;
7872 continue;
7873 }
7874 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
7875 }
7876 /* Scan every directory to find the "best" package. */
7877 best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
7878 if (best != NULL) {
7879 char *p = strrchr(best, '.');
7880 /* Try to load/source it */
7881 if (p && strcmp(p, ".tcl") == 0) {
7882 retCode = Jim_EvalFile(interp, best);
7883 } else {
7884 retCode = Jim_LoadLibrary(interp, best);
7885 }
7886 } else {
7887 retCode = JIM_ERR;
7888 }
7889 Jim_Free(best);
7890 for (i = 0; i < prefixc; i++)
7891 Jim_Free(prefixes[i]);
7892 Jim_Free(prefixes);
7893 if (libPathObjPtr)
7894 Jim_DecrRefCount(interp, libPathObjPtr);
7895 return retCode;
7896 }
7897
7898 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
7899 const char *ver, int flags)
7900 {
7901 Jim_HashEntry *he;
7902 int requiredVer;
7903
7904 if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
7905 return NULL;
7906 he = Jim_FindHashEntry(&interp->packages, name);
7907 if (he == NULL) {
7908 /* Try to load the package. */
7909 if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
7910 he = Jim_FindHashEntry(&interp->packages, name);
7911 if (he == NULL) {
7912 return "?";
7913 }
7914 return he->val;
7915 }
7916 /* No way... return an error. */
7917 if (flags & JIM_ERRMSG) {
7918 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7919 Jim_AppendStrings(interp, Jim_GetResult(interp),
7920 "Can't find package '", name, "'", NULL);
7921 }
7922 return NULL;
7923 } else {
7924 int actualVer;
7925 if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
7926 != JIM_OK)
7927 {
7928 return NULL;
7929 }
7930 /* Check if version matches. */
7931 if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
7932 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7933 Jim_AppendStrings(interp, Jim_GetResult(interp),
7934 "Package '", name, "' already loaded, but with version ",
7935 he->val, NULL);
7936 return NULL;
7937 }
7938 return he->val;
7939 }
7940 }
7941
7942 /* -----------------------------------------------------------------------------
7943 * Eval
7944 * ---------------------------------------------------------------------------*/
7945 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
7946 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
7947
7948 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
7949 Jim_Obj *const *argv);
7950
7951 /* Handle calls to the [unknown] command */
7952 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
7953 {
7954 Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
7955 int retCode;
7956
7957 /* If the [unknown] command does not exists returns
7958 * just now */
7959 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
7960 return JIM_ERR;
7961
7962 /* The object interp->unknown just contains
7963 * the "unknown" string, it is used in order to
7964 * avoid to lookup the unknown command every time
7965 * but instread to cache the result. */
7966 if (argc+1 <= JIM_EVAL_SARGV_LEN)
7967 v = sv;
7968 else
7969 v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
7970 /* Make a copy of the arguments vector, but shifted on
7971 * the right of one position. The command name of the
7972 * command will be instead the first argument of the
7973 * [unknonw] call. */
7974 memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
7975 v[0] = interp->unknown;
7976 /* Call it */
7977 retCode = Jim_EvalObjVector(interp, argc+1, v);
7978 /* Clean up */
7979 if (v != sv)
7980 Jim_Free(v);
7981 return retCode;
7982 }
7983
7984 /* Eval the object vector 'objv' composed of 'objc' elements.
7985 * Every element is used as single argument.
7986 * Jim_EvalObj() will call this function every time its object
7987 * argument is of "list" type, with no string representation.
7988 *
7989 * This is possible because the string representation of a
7990 * list object generated by the UpdateStringOfList is made
7991 * in a way that ensures that every list element is a different
7992 * command argument. */
7993 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
7994 {
7995 int i, retcode;
7996 Jim_Cmd *cmdPtr;
7997
7998 /* Incr refcount of arguments. */
7999 for (i = 0; i < objc; i++)
8000 Jim_IncrRefCount(objv[i]);
8001 /* Command lookup */
8002 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8003 if (cmdPtr == NULL) {
8004 retcode = JimUnknown(interp, objc, objv);
8005 } else {
8006 /* Call it -- Make sure result is an empty object. */
8007 Jim_SetEmptyResult(interp);
8008 if (cmdPtr->cmdProc) {
8009 interp->cmdPrivData = cmdPtr->privData;
8010 retcode = cmdPtr->cmdProc(interp, objc, objv);
8011 } else {
8012 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8013 if (retcode == JIM_ERR) {
8014 JimAppendStackTrace(interp,
8015 Jim_GetString(objv[0], NULL), "?", 1);
8016 }
8017 }
8018 }
8019 /* Decr refcount of arguments and return the retcode */
8020 for (i = 0; i < objc; i++)
8021 Jim_DecrRefCount(interp, objv[i]);
8022 return retcode;
8023 }
8024
8025 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8026 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8027 * The returned object has refcount = 0. */
8028 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8029 int tokens, Jim_Obj **objPtrPtr)
8030 {
8031 int totlen = 0, i, retcode;
8032 Jim_Obj **intv;
8033 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8034 Jim_Obj *objPtr;
8035 char *s;
8036
8037 if (tokens <= JIM_EVAL_SINTV_LEN)
8038 intv = sintv;
8039 else
8040 intv = Jim_Alloc(sizeof(Jim_Obj*)*
8041 tokens);
8042 /* Compute every token forming the argument
8043 * in the intv objects vector. */
8044 for (i = 0; i < tokens; i++) {
8045 switch(token[i].type) {
8046 case JIM_TT_ESC:
8047 case JIM_TT_STR:
8048 intv[i] = token[i].objPtr;
8049 break;
8050 case JIM_TT_VAR:
8051 intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8052 if (!intv[i]) {
8053 retcode = JIM_ERR;
8054 goto err;
8055 }
8056 break;
8057 case JIM_TT_DICTSUGAR:
8058 intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8059 if (!intv[i]) {
8060 retcode = JIM_ERR;
8061 goto err;
8062 }
8063 break;
8064 case JIM_TT_CMD:
8065 retcode = Jim_EvalObj(interp, token[i].objPtr);
8066 if (retcode != JIM_OK)
8067 goto err;
8068 intv[i] = Jim_GetResult(interp);
8069 break;
8070 default:
8071 Jim_Panic(interp,
8072 "default token type reached "
8073 "in Jim_InterpolateTokens().");
8074 break;
8075 }
8076 Jim_IncrRefCount(intv[i]);
8077 /* Make sure there is a valid
8078 * string rep, and add the string
8079 * length to the total legnth. */
8080 Jim_GetString(intv[i], NULL);
8081 totlen += intv[i]->length;
8082 }
8083 /* Concatenate every token in an unique
8084 * object. */
8085 objPtr = Jim_NewStringObjNoAlloc(interp,
8086 NULL, 0);
8087 s = objPtr->bytes = Jim_Alloc(totlen+1);
8088 objPtr->length = totlen;
8089 for (i = 0; i < tokens; i++) {
8090 memcpy(s, intv[i]->bytes, intv[i]->length);
8091 s += intv[i]->length;
8092 Jim_DecrRefCount(interp, intv[i]);
8093 }
8094 objPtr->bytes[totlen] = '\0';
8095 /* Free the intv vector if not static. */
8096 if (tokens > JIM_EVAL_SINTV_LEN)
8097 Jim_Free(intv);
8098 *objPtrPtr = objPtr;
8099 return JIM_OK;
8100 err:
8101 i--;
8102 for (; i >= 0; i--)
8103 Jim_DecrRefCount(interp, intv[i]);
8104 if (tokens > JIM_EVAL_SINTV_LEN)
8105 Jim_Free(intv);
8106 return retcode;
8107 }
8108
8109 /* Helper of Jim_EvalObj() to perform argument expansion.
8110 * Basically this function append an argument to 'argv'
8111 * (and increments argc by reference accordingly), performing
8112 * expansion of the list object if 'expand' is non-zero, or
8113 * just adding objPtr to argv if 'expand' is zero. */
8114 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8115 int *argcPtr, int expand, Jim_Obj *objPtr)
8116 {
8117 if (!expand) {
8118 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8119 /* refcount of objPtr not incremented because
8120 * we are actually transfering a reference from
8121 * the old 'argv' to the expanded one. */
8122 (*argv)[*argcPtr] = objPtr;
8123 (*argcPtr)++;
8124 } else {
8125 int len, i;
8126
8127 Jim_ListLength(interp, objPtr, &len);
8128 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8129 for (i = 0; i < len; i++) {
8130 (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8131 Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8132 (*argcPtr)++;
8133 }
8134 /* The original object reference is no longer needed,
8135 * after the expansion it is no longer present on
8136 * the argument vector, but the single elements are
8137 * in its place. */
8138 Jim_DecrRefCount(interp, objPtr);
8139 }
8140 }
8141
8142 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8143 {
8144 int i, j = 0, len;
8145 ScriptObj *script;
8146 ScriptToken *token;
8147 int *cs; /* command structure array */
8148 int retcode = JIM_OK;
8149 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8150
8151 interp->errorFlag = 0;
8152
8153 /* If the object is of type "list" and there is no
8154 * string representation for this object, we can call
8155 * a specialized version of Jim_EvalObj() */
8156 if (scriptObjPtr->typePtr == &listObjType &&
8157 scriptObjPtr->internalRep.listValue.len &&
8158 scriptObjPtr->bytes == NULL) {
8159 Jim_IncrRefCount(scriptObjPtr);
8160 retcode = Jim_EvalObjVector(interp,
8161 scriptObjPtr->internalRep.listValue.len,
8162 scriptObjPtr->internalRep.listValue.ele);
8163 Jim_DecrRefCount(interp, scriptObjPtr);
8164 return retcode;
8165 }
8166
8167 Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8168 script = Jim_GetScript(interp, scriptObjPtr);
8169 /* Now we have to make sure the internal repr will not be
8170 * freed on shimmering.
8171 *
8172 * Think for example to this:
8173 *
8174 * set x {llength $x; ... some more code ...}; eval $x
8175 *
8176 * In order to preserve the internal rep, we increment the
8177 * inUse field of the script internal rep structure. */
8178 script->inUse++;
8179
8180 token = script->token;
8181 len = script->len;
8182 cs = script->cmdStruct;
8183 i = 0; /* 'i' is the current token index. */
8184
8185 /* Reset the interpreter result. This is useful to
8186 * return the emtpy result in the case of empty program. */
8187 Jim_SetEmptyResult(interp);
8188
8189 /* Execute every command sequentially, returns on
8190 * error (i.e. if a command does not return JIM_OK) */
8191 while (i < len) {
8192 int expand = 0;
8193 int argc = *cs++; /* Get the number of arguments */
8194 Jim_Cmd *cmd;
8195
8196 /* Set the expand flag if needed. */
8197 if (argc == -1) {
8198 expand++;
8199 argc = *cs++;
8200 }
8201 /* Allocate the arguments vector */
8202 if (argc <= JIM_EVAL_SARGV_LEN)
8203 argv = sargv;
8204 else
8205 argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8206 /* Populate the arguments objects. */
8207 for (j = 0; j < argc; j++) {
8208 int tokens = *cs++;
8209
8210 /* tokens is negative if expansion is needed.
8211 * for this argument. */
8212 if (tokens < 0) {
8213 tokens = (-tokens)-1;
8214 i++;
8215 }
8216 if (tokens == 1) {
8217 /* Fast path if the token does not
8218 * need interpolation */
8219 switch(token[i].type) {
8220 case JIM_TT_ESC:
8221 case JIM_TT_STR:
8222 argv[j] = token[i].objPtr;
8223 break;
8224 case JIM_TT_VAR:
8225 tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8226 JIM_ERRMSG);
8227 if (!tmpObjPtr) {
8228 retcode = JIM_ERR;
8229 goto err;
8230 }
8231 argv[j] = tmpObjPtr;
8232 break;
8233 case JIM_TT_DICTSUGAR:
8234 tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8235 if (!tmpObjPtr) {
8236 retcode = JIM_ERR;
8237 goto err;
8238 }
8239 argv[j] = tmpObjPtr;
8240 break;
8241 case JIM_TT_CMD:
8242 retcode = Jim_EvalObj(interp, token[i].objPtr);
8243 if (retcode != JIM_OK)
8244 goto err;
8245 argv[j] = Jim_GetResult(interp);
8246 break;
8247 default:
8248 Jim_Panic(interp,
8249 "default token type reached "
8250 "in Jim_EvalObj().");
8251 break;
8252 }
8253 Jim_IncrRefCount(argv[j]);
8254 i += 2;
8255 } else {
8256 /* For interpolation we call an helper
8257 * function doing the work for us. */
8258 if ((retcode = Jim_InterpolateTokens(interp,
8259 token+i, tokens, &tmpObjPtr)) != JIM_OK)
8260 {
8261 goto err;
8262 }
8263 argv[j] = tmpObjPtr;
8264 Jim_IncrRefCount(argv[j]);
8265 i += tokens+1;
8266 }
8267 }
8268 /* Handle {expand} expansion */
8269 if (expand) {
8270 int *ecs = cs - argc;
8271 int eargc = 0;
8272 Jim_Obj **eargv = NULL;
8273
8274 for (j = 0; j < argc; j++) {
8275 Jim_ExpandArgument( interp, &eargv, &eargc,
8276 ecs[j] < 0, argv[j]);
8277 }
8278 if (argv != sargv)
8279 Jim_Free(argv);
8280 argc = eargc;
8281 argv = eargv;
8282 j = argc;
8283 if (argc == 0) {
8284 /* Nothing to do with zero args. */
8285 Jim_Free(eargv);
8286 continue;
8287 }
8288 }
8289 /* Lookup the command to call */
8290 cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8291 if (cmd != NULL) {
8292 /* Call it -- Make sure result is an empty object. */
8293 Jim_SetEmptyResult(interp);
8294 if (cmd->cmdProc) {
8295 interp->cmdPrivData = cmd->privData;
8296 retcode = cmd->cmdProc(interp, argc, argv);
8297 } else {
8298 retcode = JimCallProcedure(interp, cmd, argc, argv);
8299 if (retcode == JIM_ERR) {
8300 JimAppendStackTrace(interp,
8301 Jim_GetString(argv[0], NULL), script->fileName,
8302 token[i-argc*2].linenr);
8303 }
8304 }
8305 } else {
8306 /* Call [unknown] */
8307 retcode = JimUnknown(interp, argc, argv);
8308 }
8309 if (retcode != JIM_OK) {
8310 i -= argc*2; /* point to the command name. */
8311 goto err;
8312 }
8313 /* Decrement the arguments count */
8314 for (j = 0; j < argc; j++) {
8315 Jim_DecrRefCount(interp, argv[j]);
8316 }
8317
8318 if (argv != sargv) {
8319 Jim_Free(argv);
8320 argv = NULL;
8321 }
8322 }
8323 /* Note that we don't have to decrement inUse, because the
8324 * following code transfers our use of the reference again to
8325 * the script object. */
8326 j = 0; /* on normal termination, the argv array is already
8327 Jim_DecrRefCount-ed. */
8328 err:
8329 /* Handle errors. */
8330 if (retcode == JIM_ERR && !interp->errorFlag) {
8331 interp->errorFlag = 1;
8332 JimSetErrorFileName(interp, script->fileName);
8333 JimSetErrorLineNumber(interp, token[i].linenr);
8334 JimResetStackTrace(interp);
8335 }
8336 Jim_FreeIntRep(interp, scriptObjPtr);
8337 scriptObjPtr->typePtr = &scriptObjType;
8338 Jim_SetIntRepPtr(scriptObjPtr, script);
8339 Jim_DecrRefCount(interp, scriptObjPtr);
8340 for (i = 0; i < j; i++) {
8341 Jim_DecrRefCount(interp, argv[i]);
8342 }
8343 if (argv != sargv)
8344 Jim_Free(argv);
8345 return retcode;
8346 }
8347
8348 /* Call a procedure implemented in Tcl.
8349 * It's possible to speed-up a lot this function, currently
8350 * the callframes are not cached, but allocated and
8351 * destroied every time. What is expecially costly is
8352 * to create/destroy the local vars hash table every time.
8353 *
8354 * This can be fixed just implementing callframes caching
8355 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8356 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8357 Jim_Obj *const *argv)
8358 {
8359 int i, retcode;
8360 Jim_CallFrame *callFramePtr;
8361
8362 /* Check arity */
8363 if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8364 argc > cmd->arityMax)) {
8365 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8366 Jim_AppendStrings(interp, objPtr,
8367 "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8368 (cmd->arityMin > 1) ? " " : "",
8369 Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8370 Jim_SetResult(interp, objPtr);
8371 return JIM_ERR;
8372 }
8373 /* Check if there are too nested calls */
8374 if (interp->numLevels == interp->maxNestingDepth) {
8375 Jim_SetResultString(interp,
8376 "Too many nested calls. Infinite recursion?", -1);
8377 return JIM_ERR;
8378 }
8379 /* Create a new callframe */
8380 callFramePtr = JimCreateCallFrame(interp);
8381 callFramePtr->parentCallFrame = interp->framePtr;
8382 callFramePtr->argv = argv;
8383 callFramePtr->argc = argc;
8384 callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8385 callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8386 callFramePtr->staticVars = cmd->staticVars;
8387 Jim_IncrRefCount(cmd->argListObjPtr);
8388 Jim_IncrRefCount(cmd->bodyObjPtr);
8389 interp->framePtr = callFramePtr;
8390 interp->numLevels ++;
8391 /* Set arguments */
8392 for (i = 0; i < cmd->arityMin-1; i++) {
8393 Jim_Obj *objPtr;
8394
8395 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8396 Jim_SetVariable(interp, objPtr, argv[i+1]);
8397 }
8398 if (cmd->arityMax == -1) {
8399 Jim_Obj *listObjPtr, *objPtr;
8400
8401 listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8402 argc-cmd->arityMin);
8403 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8404 Jim_SetVariable(interp, objPtr, listObjPtr);
8405 }
8406 /* Eval the body */
8407 retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8408
8409 /* Destroy the callframe */
8410 interp->numLevels --;
8411 interp->framePtr = interp->framePtr->parentCallFrame;
8412 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8413 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8414 } else {
8415 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8416 }
8417 /* Handle the JIM_EVAL return code */
8418 if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8419 int savedLevel = interp->evalRetcodeLevel;
8420
8421 interp->evalRetcodeLevel = interp->numLevels;
8422 while (retcode == JIM_EVAL) {
8423 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8424 Jim_IncrRefCount(resultScriptObjPtr);
8425 retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8426 Jim_DecrRefCount(interp, resultScriptObjPtr);
8427 }
8428 interp->evalRetcodeLevel = savedLevel;
8429 }
8430 /* Handle the JIM_RETURN return code */
8431 if (retcode == JIM_RETURN) {
8432 retcode = interp->returnCode;
8433 interp->returnCode = JIM_OK;
8434 }
8435 return retcode;
8436 }
8437
8438 int Jim_Eval(Jim_Interp *interp, const char *script)
8439 {
8440 Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8441 int retval;
8442
8443 Jim_IncrRefCount(scriptObjPtr);
8444 retval = Jim_EvalObj(interp, scriptObjPtr);
8445 Jim_DecrRefCount(interp, scriptObjPtr);
8446 return retval;
8447 }
8448
8449 /* Execute script in the scope of the global level */
8450 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8451 {
8452 Jim_CallFrame *savedFramePtr;
8453 int retval;
8454
8455 savedFramePtr = interp->framePtr;
8456 interp->framePtr = interp->topFramePtr;
8457 retval = Jim_Eval(interp, script);
8458 interp->framePtr = savedFramePtr;
8459 return retval;
8460 }
8461
8462 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8463 {
8464 Jim_CallFrame *savedFramePtr;
8465 int retval;
8466
8467 savedFramePtr = interp->framePtr;
8468 interp->framePtr = interp->topFramePtr;
8469 retval = Jim_EvalObj(interp, scriptObjPtr);
8470 interp->framePtr = savedFramePtr;
8471 /* Try to report the error (if any) via the bgerror proc */
8472 if (retval != JIM_OK) {
8473 Jim_Obj *objv[2];
8474
8475 objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8476 objv[1] = Jim_GetResult(interp);
8477 Jim_IncrRefCount(objv[0]);
8478 Jim_IncrRefCount(objv[1]);
8479 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8480 /* Report the error to stderr. */
8481 fprintf(interp->stderr_, "Background error:" JIM_NL);
8482 Jim_PrintErrorMessage(interp);
8483 }
8484 Jim_DecrRefCount(interp, objv[0]);
8485 Jim_DecrRefCount(interp, objv[1]);
8486 }
8487 return retval;
8488 }
8489
8490 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8491 {
8492 char *prg = NULL;
8493 FILE *fp;
8494 int nread, totread, maxlen, buflen;
8495 int retval;
8496 Jim_Obj *scriptObjPtr;
8497
8498 if ((fp = fopen(filename, "r")) == NULL) {
8499 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8500 Jim_AppendStrings(interp, Jim_GetResult(interp),
8501 "Error loading script \"", filename, "\": ",
8502 strerror(errno), NULL);
8503 return JIM_ERR;
8504 }
8505 buflen = 1024;
8506 maxlen = totread = 0;
8507 while (1) {
8508 if (maxlen < totread+buflen+1) {
8509 maxlen = totread+buflen+1;
8510 prg = Jim_Realloc(prg, maxlen);
8511 }
8512 if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8513 totread += nread;
8514 }
8515 prg[totread] = '\0';
8516 fclose(fp);
8517
8518 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8519 JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8520 Jim_IncrRefCount(scriptObjPtr);
8521 retval = Jim_EvalObj(interp, scriptObjPtr);
8522 Jim_DecrRefCount(interp, scriptObjPtr);
8523 return retval;
8524 }
8525
8526 /* -----------------------------------------------------------------------------
8527 * Subst
8528 * ---------------------------------------------------------------------------*/
8529 static int JimParseSubstStr(struct JimParserCtx *pc)
8530 {
8531 pc->tstart = pc->p;
8532 pc->tline = pc->linenr;
8533 while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8534 pc->p++; pc->len--;
8535 }
8536 pc->tend = pc->p-1;
8537 pc->tt = JIM_TT_ESC;
8538 return JIM_OK;
8539 }
8540
8541 static int JimParseSubst(struct JimParserCtx *pc, int flags)
8542 {
8543 int retval;
8544
8545 if (pc->len == 0) {
8546 pc->tstart = pc->tend = pc->p;
8547 pc->tline = pc->linenr;
8548 pc->tt = JIM_TT_EOL;
8549 pc->eof = 1;
8550 return JIM_OK;
8551 }
8552 switch(*pc->p) {
8553 case '[':
8554 retval = JimParseCmd(pc);
8555 if (flags & JIM_SUBST_NOCMD) {
8556 pc->tstart--;
8557 pc->tend++;
8558 pc->tt = (flags & JIM_SUBST_NOESC) ?
8559 JIM_TT_STR : JIM_TT_ESC;
8560 }
8561 return retval;
8562 break;
8563 case '$':
8564 if (JimParseVar(pc) == JIM_ERR) {
8565 pc->tstart = pc->tend = pc->p++; pc->len--;
8566 pc->tline = pc->linenr;
8567 pc->tt = JIM_TT_STR;
8568 } else {
8569 if (flags & JIM_SUBST_NOVAR) {
8570 pc->tstart--;
8571 if (flags & JIM_SUBST_NOESC)
8572 pc->tt = JIM_TT_STR;
8573 else
8574 pc->tt = JIM_TT_ESC;
8575 if (*pc->tstart == '{') {
8576 pc->tstart--;
8577 if (*(pc->tend+1))
8578 pc->tend++;
8579 }
8580 }
8581 }
8582 break;
8583 default:
8584 retval = JimParseSubstStr(pc);
8585 if (flags & JIM_SUBST_NOESC)
8586 pc->tt = JIM_TT_STR;
8587 return retval;
8588 break;
8589 }
8590 return JIM_OK;
8591 }
8592
8593 /* The subst object type reuses most of the data structures and functions
8594 * of the script object. Script's data structures are a bit more complex
8595 * for what is needed for [subst]itution tasks, but the reuse helps to
8596 * deal with a single data structure at the cost of some more memory
8597 * usage for substitutions. */
8598 static Jim_ObjType substObjType = {
8599 "subst",
8600 FreeScriptInternalRep,
8601 DupScriptInternalRep,
8602 NULL,
8603 JIM_TYPE_REFERENCES,
8604 };
8605
8606 /* This method takes the string representation of an object
8607 * as a Tcl string where to perform [subst]itution, and generates
8608 * the pre-parsed internal representation. */
8609 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8610 {
8611 int scriptTextLen;
8612 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8613 struct JimParserCtx parser;
8614 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8615
8616 script->len = 0;
8617 script->csLen = 0;
8618 script->commands = 0;
8619 script->token = NULL;
8620 script->cmdStruct = NULL;
8621 script->inUse = 1;
8622 script->substFlags = flags;
8623 script->fileName = NULL;
8624
8625 JimParserInit(&parser, scriptText, scriptTextLen, 1);
8626 while(1) {
8627 char *token;
8628 int len, type, linenr;
8629
8630 JimParseSubst(&parser, flags);
8631 if (JimParserEof(&parser)) break;
8632 token = JimParserGetToken(&parser, &len, &type, &linenr);
8633 ScriptObjAddToken(interp, script, token, len, type,
8634 NULL, linenr);
8635 }
8636 /* Free the old internal rep and set the new one. */
8637 Jim_FreeIntRep(interp, objPtr);
8638 Jim_SetIntRepPtr(objPtr, script);
8639 objPtr->typePtr = &scriptObjType;
8640 return JIM_OK;
8641 }
8642
8643 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8644 {
8645 struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8646
8647 if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8648 SetSubstFromAny(interp, objPtr, flags);
8649 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8650 }
8651
8652 /* Performs commands,variables,blackslashes substitution,
8653 * storing the result object (with refcount 0) into
8654 * resObjPtrPtr. */
8655 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8656 Jim_Obj **resObjPtrPtr, int flags)
8657 {
8658 ScriptObj *script;
8659 ScriptToken *token;
8660 int i, len, retcode = JIM_OK;
8661 Jim_Obj *resObjPtr, *savedResultObjPtr;
8662
8663 script = Jim_GetSubst(interp, substObjPtr, flags);
8664 #ifdef JIM_OPTIMIZATION
8665 /* Fast path for a very common case with array-alike syntax,
8666 * that's: $foo($bar) */
8667 if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8668 Jim_Obj *varObjPtr = script->token[0].objPtr;
8669
8670 Jim_IncrRefCount(varObjPtr);
8671 resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8672 if (resObjPtr == NULL) {
8673 Jim_DecrRefCount(interp, varObjPtr);
8674 return JIM_ERR;
8675 }
8676 Jim_DecrRefCount(interp, varObjPtr);
8677 *resObjPtrPtr = resObjPtr;
8678 return JIM_OK;
8679 }
8680 #endif
8681
8682 Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8683 /* In order to preserve the internal rep, we increment the
8684 * inUse field of the script internal rep structure. */
8685 script->inUse++;
8686
8687 token = script->token;
8688 len = script->len;
8689
8690 /* Save the interp old result, to set it again before
8691 * to return. */
8692 savedResultObjPtr = interp->result;
8693 Jim_IncrRefCount(savedResultObjPtr);
8694
8695 /* Perform the substitution. Starts with an empty object
8696 * and adds every token (performing the appropriate
8697 * var/command/escape substitution). */
8698 resObjPtr = Jim_NewStringObj(interp, "", 0);
8699 for (i = 0; i < len; i++) {
8700 Jim_Obj *objPtr;
8701
8702 switch(token[i].type) {
8703 case JIM_TT_STR:
8704 case JIM_TT_ESC:
8705 Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8706 break;
8707 case JIM_TT_VAR:
8708 objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8709 if (objPtr == NULL) goto err;
8710 Jim_IncrRefCount(objPtr);
8711 Jim_AppendObj(interp, resObjPtr, objPtr);
8712 Jim_DecrRefCount(interp, objPtr);
8713 break;
8714 case JIM_TT_CMD:
8715 if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8716 goto err;
8717 Jim_AppendObj(interp, resObjPtr, interp->result);
8718 break;
8719 default:
8720 Jim_Panic(interp,
8721 "default token type (%d) reached "
8722 "in Jim_SubstObj().", token[i].type);
8723 break;
8724 }
8725 }
8726 ok:
8727 if (retcode == JIM_OK)
8728 Jim_SetResult(interp, savedResultObjPtr);
8729 Jim_DecrRefCount(interp, savedResultObjPtr);
8730 /* Note that we don't have to decrement inUse, because the
8731 * following code transfers our use of the reference again to
8732 * the script object. */
8733 Jim_FreeIntRep(interp, substObjPtr);
8734 substObjPtr->typePtr = &scriptObjType;
8735 Jim_SetIntRepPtr(substObjPtr, script);
8736 Jim_DecrRefCount(interp, substObjPtr);
8737 *resObjPtrPtr = resObjPtr;
8738 return retcode;
8739 err:
8740 Jim_FreeNewObj(interp, resObjPtr);
8741 retcode = JIM_ERR;
8742 goto ok;
8743 }
8744
8745 /* -----------------------------------------------------------------------------
8746 * API Input/Export functions
8747 * ---------------------------------------------------------------------------*/
8748
8749 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8750 {
8751 Jim_HashEntry *he;
8752
8753 he = Jim_FindHashEntry(&interp->stub, funcname);
8754 if (!he)
8755 return JIM_ERR;
8756 memcpy(targetPtrPtr, &he->val, sizeof(void*));
8757 return JIM_OK;
8758 }
8759
8760 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
8761 {
8762 return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
8763 }
8764
8765 #define JIM_REGISTER_API(name) \
8766 Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
8767
8768 void JimRegisterCoreApi(Jim_Interp *interp)
8769 {
8770 interp->getApiFuncPtr = Jim_GetApi;
8771 JIM_REGISTER_API(Alloc);
8772 JIM_REGISTER_API(Free);
8773 JIM_REGISTER_API(Eval);
8774 JIM_REGISTER_API(EvalGlobal);
8775 JIM_REGISTER_API(EvalFile);
8776 JIM_REGISTER_API(EvalObj);
8777 JIM_REGISTER_API(EvalObjBackground);
8778 JIM_REGISTER_API(EvalObjVector);
8779 JIM_REGISTER_API(InitHashTable);
8780 JIM_REGISTER_API(ExpandHashTable);
8781 JIM_REGISTER_API(AddHashEntry);
8782 JIM_REGISTER_API(ReplaceHashEntry);
8783 JIM_REGISTER_API(DeleteHashEntry);
8784 JIM_REGISTER_API(FreeHashTable);
8785 JIM_REGISTER_API(FindHashEntry);
8786 JIM_REGISTER_API(ResizeHashTable);
8787 JIM_REGISTER_API(GetHashTableIterator);
8788 JIM_REGISTER_API(NextHashEntry);
8789 JIM_REGISTER_API(NewObj);
8790 JIM_REGISTER_API(FreeObj);
8791 JIM_REGISTER_API(InvalidateStringRep);
8792 JIM_REGISTER_API(InitStringRep);
8793 JIM_REGISTER_API(DuplicateObj);
8794 JIM_REGISTER_API(GetString);
8795 JIM_REGISTER_API(Length);
8796 JIM_REGISTER_API(InvalidateStringRep);
8797 JIM_REGISTER_API(NewStringObj);
8798 JIM_REGISTER_API(NewStringObjNoAlloc);
8799 JIM_REGISTER_API(AppendString);
8800 JIM_REGISTER_API(AppendObj);
8801 JIM_REGISTER_API(AppendStrings);
8802 JIM_REGISTER_API(StringEqObj);
8803 JIM_REGISTER_API(StringMatchObj);
8804 JIM_REGISTER_API(StringRangeObj);
8805 JIM_REGISTER_API(FormatString);
8806 JIM_REGISTER_API(CompareStringImmediate);
8807 JIM_REGISTER_API(NewReference);
8808 JIM_REGISTER_API(GetReference);
8809 JIM_REGISTER_API(SetFinalizer);
8810 JIM_REGISTER_API(GetFinalizer);
8811 JIM_REGISTER_API(CreateInterp);
8812 JIM_REGISTER_API(FreeInterp);
8813 JIM_REGISTER_API(GetExitCode);
8814 JIM_REGISTER_API(SetStdin);
8815 JIM_REGISTER_API(SetStdout);
8816 JIM_REGISTER_API(SetStderr);
8817 JIM_REGISTER_API(CreateCommand);
8818 JIM_REGISTER_API(CreateProcedure);
8819 JIM_REGISTER_API(DeleteCommand);
8820 JIM_REGISTER_API(RenameCommand);
8821 JIM_REGISTER_API(GetCommand);
8822 JIM_REGISTER_API(SetVariable);
8823 JIM_REGISTER_API(SetVariableStr);
8824 JIM_REGISTER_API(SetGlobalVariableStr);
8825 JIM_REGISTER_API(SetVariableStrWithStr);
8826 JIM_REGISTER_API(SetVariableLink);
8827 JIM_REGISTER_API(GetVariable);
8828 JIM_REGISTER_API(GetCallFrameByLevel);
8829 JIM_REGISTER_API(Collect);
8830 JIM_REGISTER_API(CollectIfNeeded);
8831 JIM_REGISTER_API(GetIndex);
8832 JIM_REGISTER_API(NewListObj);
8833 JIM_REGISTER_API(ListAppendElement);
8834 JIM_REGISTER_API(ListAppendList);
8835 JIM_REGISTER_API(ListLength);
8836 JIM_REGISTER_API(ListIndex);
8837 JIM_REGISTER_API(SetListIndex);
8838 JIM_REGISTER_API(ConcatObj);
8839 JIM_REGISTER_API(NewDictObj);
8840 JIM_REGISTER_API(DictKey);
8841 JIM_REGISTER_API(DictKeysVector);
8842 JIM_REGISTER_API(GetIndex);
8843 JIM_REGISTER_API(GetReturnCode);
8844 JIM_REGISTER_API(EvalExpression);
8845 JIM_REGISTER_API(GetBoolFromExpr);
8846 JIM_REGISTER_API(GetWide);
8847 JIM_REGISTER_API(GetLong);
8848 JIM_REGISTER_API(SetWide);
8849 JIM_REGISTER_API(NewIntObj);
8850 JIM_REGISTER_API(GetDouble);
8851 JIM_REGISTER_API(SetDouble);
8852 JIM_REGISTER_API(NewDoubleObj);
8853 JIM_REGISTER_API(WrongNumArgs);
8854 JIM_REGISTER_API(SetDictKeysVector);
8855 JIM_REGISTER_API(SubstObj);
8856 JIM_REGISTER_API(RegisterApi);
8857 JIM_REGISTER_API(PrintErrorMessage);
8858 JIM_REGISTER_API(InteractivePrompt);
8859 JIM_REGISTER_API(RegisterCoreCommands);
8860 JIM_REGISTER_API(GetSharedString);
8861 JIM_REGISTER_API(ReleaseSharedString);
8862 JIM_REGISTER_API(Panic);
8863 JIM_REGISTER_API(StrDup);
8864 JIM_REGISTER_API(UnsetVariable);
8865 JIM_REGISTER_API(GetVariableStr);
8866 JIM_REGISTER_API(GetGlobalVariable);
8867 JIM_REGISTER_API(GetGlobalVariableStr);
8868 JIM_REGISTER_API(GetAssocData);
8869 JIM_REGISTER_API(SetAssocData);
8870 JIM_REGISTER_API(DeleteAssocData);
8871 JIM_REGISTER_API(GetEnum);
8872 JIM_REGISTER_API(ScriptIsComplete);
8873 JIM_REGISTER_API(PackageRequire);
8874 JIM_REGISTER_API(PackageProvide);
8875 JIM_REGISTER_API(InitStack);
8876 JIM_REGISTER_API(FreeStack);
8877 JIM_REGISTER_API(StackLen);
8878 JIM_REGISTER_API(StackPush);
8879 JIM_REGISTER_API(StackPop);
8880 JIM_REGISTER_API(StackPeek);
8881 JIM_REGISTER_API(FreeStackElements);
8882 }
8883
8884 /* -----------------------------------------------------------------------------
8885 * Core commands utility functions
8886 * ---------------------------------------------------------------------------*/
8887 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
8888 const char *msg)
8889 {
8890 int i;
8891 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8892
8893 Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
8894 for (i = 0; i < argc; i++) {
8895 Jim_AppendObj(interp, objPtr, argv[i]);
8896 if (!(i+1 == argc && msg[0] == '\0'))
8897 Jim_AppendString(interp, objPtr, " ", 1);
8898 }
8899 Jim_AppendString(interp, objPtr, msg, -1);
8900 Jim_AppendString(interp, objPtr, "\"", 1);
8901 Jim_SetResult(interp, objPtr);
8902 }
8903
8904 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
8905 {
8906 Jim_HashTableIterator *htiter;
8907 Jim_HashEntry *he;
8908 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
8909 const char *pattern;
8910 int patternLen;
8911
8912 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
8913 htiter = Jim_GetHashTableIterator(&interp->commands);
8914 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8915 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
8916 strlen((const char*)he->key), 0))
8917 continue;
8918 Jim_ListAppendElement(interp, listObjPtr,
8919 Jim_NewStringObj(interp, he->key, -1));
8920 }
8921 Jim_FreeHashTableIterator(htiter);
8922 return listObjPtr;
8923 }
8924
8925 #define JIM_VARLIST_GLOBALS 0
8926 #define JIM_VARLIST_LOCALS 1
8927 #define JIM_VARLIST_VARS 2
8928
8929 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
8930 int mode)
8931 {
8932 Jim_HashTableIterator *htiter;
8933 Jim_HashEntry *he;
8934 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
8935 const char *pattern;
8936 int patternLen;
8937
8938 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
8939 if (mode == JIM_VARLIST_GLOBALS) {
8940 htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
8941 } else {
8942 /* For [info locals], if we are at top level an emtpy list
8943 * is returned. I don't agree, but we aim at compatibility (SS) */
8944 if (mode == JIM_VARLIST_LOCALS &&
8945 interp->framePtr == interp->topFramePtr)
8946 return listObjPtr;
8947 htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
8948 }
8949 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8950 Jim_Var *varPtr = (Jim_Var*) he->val;
8951 if (mode == JIM_VARLIST_LOCALS) {
8952 if (varPtr->linkFramePtr != NULL)
8953 continue;
8954 }
8955 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
8956 strlen((const char*)he->key), 0))
8957 continue;
8958 Jim_ListAppendElement(interp, listObjPtr,
8959 Jim_NewStringObj(interp, he->key, -1));
8960 }
8961 Jim_FreeHashTableIterator(htiter);
8962 return listObjPtr;
8963 }
8964
8965 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
8966 Jim_Obj **objPtrPtr)
8967 {
8968 Jim_CallFrame *targetCallFrame;
8969
8970 if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
8971 != JIM_OK)
8972 return JIM_ERR;
8973 /* No proc call at toplevel callframe */
8974 if (targetCallFrame == interp->topFramePtr) {
8975 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8976 Jim_AppendStrings(interp, Jim_GetResult(interp),
8977 "bad level \"",
8978 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
8979 return JIM_ERR;
8980 }
8981 *objPtrPtr = Jim_NewListObj(interp,
8982 targetCallFrame->argv,
8983 targetCallFrame->argc);
8984 return JIM_OK;
8985 }
8986
8987 /* -----------------------------------------------------------------------------
8988 * Core commands
8989 * ---------------------------------------------------------------------------*/
8990
8991 /* fake [puts] -- not the real puts, just for debugging. */
8992 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
8993 Jim_Obj *const *argv)
8994 {
8995 const char *str;
8996 int len, nonewline = 0;
8997
8998 if (argc != 2 && argc != 3) {
8999 Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9000 return JIM_ERR;
9001 }
9002 if (argc == 3) {
9003 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9004 {
9005 Jim_SetResultString(interp, "The second argument must "
9006 "be -nonewline", -1);
9007 return JIM_OK;
9008 } else {
9009 nonewline = 1;
9010 argv++;
9011 }
9012 }
9013 str = Jim_GetString(argv[1], &len);
9014 fwrite(str, 1, len, interp->stdout_);
9015 if (!nonewline) fprintf(interp->stdout_, JIM_NL);
9016 return JIM_OK;
9017 }
9018
9019 /* Helper for [+] and [*] */
9020 static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
9021 Jim_Obj *const *argv, int op)
9022 {
9023 jim_wide wideValue, res;
9024 double doubleValue, doubleRes;
9025 int i;
9026
9027 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9028
9029 for (i = 1; i < argc; i++) {
9030 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9031 goto trydouble;
9032 if (op == JIM_EXPROP_ADD)
9033 res += wideValue;
9034 else
9035 res *= wideValue;
9036 }
9037 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9038 return JIM_OK;
9039 trydouble:
9040 doubleRes = (double) res;
9041 for (;i < argc; i++) {
9042 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9043 return JIM_ERR;
9044 if (op == JIM_EXPROP_ADD)
9045 doubleRes += doubleValue;
9046 else
9047 doubleRes *= doubleValue;
9048 }
9049 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9050 return JIM_OK;
9051 }
9052
9053 /* Helper for [-] and [/] */
9054 static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9055 Jim_Obj *const *argv, int op)
9056 {
9057 jim_wide wideValue, res = 0;
9058 double doubleValue, doubleRes = 0;
9059 int i = 2;
9060
9061 if (argc < 2) {
9062 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9063 return JIM_ERR;
9064 } else if (argc == 2) {
9065 /* The arity = 2 case is different. For [- x] returns -x,
9066 * while [/ x] returns 1/x. */
9067 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9068 if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9069 JIM_OK)
9070 {
9071 return JIM_ERR;
9072 } else {
9073 if (op == JIM_EXPROP_SUB)
9074 doubleRes = -doubleValue;
9075 else
9076 doubleRes = 1.0/doubleValue;
9077 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9078 doubleRes));
9079 return JIM_OK;
9080 }
9081 }
9082 if (op == JIM_EXPROP_SUB) {
9083 res = -wideValue;
9084 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9085 } else {
9086 doubleRes = 1.0/wideValue;
9087 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9088 doubleRes));
9089 }
9090 return JIM_OK;
9091 } else {
9092 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9093 if (Jim_GetDouble(interp, argv[1], &doubleRes)
9094 != JIM_OK) {
9095 return JIM_ERR;
9096 } else {
9097 goto trydouble;
9098 }
9099 }
9100 }
9101 for (i = 2; i < argc; i++) {
9102 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9103 doubleRes = (double) res;
9104 goto trydouble;
9105 }
9106 if (op == JIM_EXPROP_SUB)
9107 res -= wideValue;
9108 else
9109 res /= wideValue;
9110 }
9111 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9112 return JIM_OK;
9113 trydouble:
9114 for (;i < argc; i++) {
9115 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9116 return JIM_ERR;
9117 if (op == JIM_EXPROP_SUB)
9118 doubleRes -= doubleValue;
9119 else
9120 doubleRes /= doubleValue;
9121 }
9122 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9123 return JIM_OK;
9124 }
9125
9126
9127 /* [+] */
9128 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9129 Jim_Obj *const *argv)
9130 {
9131 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9132 }
9133
9134 /* [*] */
9135 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9136 Jim_Obj *const *argv)
9137 {
9138 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9139 }
9140
9141 /* [-] */
9142 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9143 Jim_Obj *const *argv)
9144 {
9145 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9146 }
9147
9148 /* [/] */
9149 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9150 Jim_Obj *const *argv)
9151 {
9152 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9153 }
9154
9155 /* [set] */
9156 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9157 Jim_Obj *const *argv)
9158 {
9159 if (argc != 2 && argc != 3) {
9160 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9161 return JIM_ERR;
9162 }
9163 if (argc == 2) {
9164 Jim_Obj *objPtr;
9165 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9166 if (!objPtr)
9167 return JIM_ERR;
9168 Jim_SetResult(interp, objPtr);
9169 return JIM_OK;
9170 }
9171 /* argc == 3 case. */
9172 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9173 return JIM_ERR;
9174 Jim_SetResult(interp, argv[2]);
9175 return JIM_OK;
9176 }
9177
9178 /* [unset] */
9179 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9180 Jim_Obj *const *argv)
9181 {
9182 int i;
9183
9184 if (argc < 2) {
9185 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9186 return JIM_ERR;
9187 }
9188 for (i = 1; i < argc; i++) {
9189 if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9190 return JIM_ERR;
9191 }
9192 return JIM_OK;
9193 }
9194
9195 /* [incr] */
9196 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9197 Jim_Obj *const *argv)
9198 {
9199 jim_wide wideValue, increment = 1;
9200 Jim_Obj *intObjPtr;
9201
9202 if (argc != 2 && argc != 3) {
9203 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9204 return JIM_ERR;
9205 }
9206 if (argc == 3) {
9207 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9208 return JIM_ERR;
9209 }
9210 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9211 if (!intObjPtr) return JIM_ERR;
9212 if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9213 return JIM_ERR;
9214 if (Jim_IsShared(intObjPtr)) {
9215 intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9216 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9217 Jim_FreeNewObj(interp, intObjPtr);
9218 return JIM_ERR;
9219 }
9220 } else {
9221 Jim_SetWide(interp, intObjPtr, wideValue+increment);
9222 /* The following step is required in order to invalidate the
9223 * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9224 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9225 return JIM_ERR;
9226 }
9227 }
9228 Jim_SetResult(interp, intObjPtr);
9229 return JIM_OK;
9230 }
9231
9232 /* [while] */
9233 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9234 Jim_Obj *const *argv)
9235 {
9236 if (argc != 3) {
9237 Jim_WrongNumArgs(interp, 1, argv, "condition body");
9238 return JIM_ERR;
9239 }
9240 /* Try to run a specialized version of while if the expression
9241 * is in one of the following forms:
9242 *
9243 * $a < CONST, $a < $b
9244 * $a <= CONST, $a <= $b
9245 * $a > CONST, $a > $b
9246 * $a >= CONST, $a >= $b
9247 * $a != CONST, $a != $b
9248 * $a == CONST, $a == $b
9249 * $a
9250 * !$a
9251 * CONST
9252 */
9253
9254 #ifdef JIM_OPTIMIZATION
9255 {
9256 ExprByteCode *expr;
9257 Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9258 int exprLen, retval;
9259
9260 /* STEP 1 -- Check if there are the conditions to run the specialized
9261 * version of while */
9262
9263 if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9264 if (expr->len <= 0 || expr->len > 3) goto noopt;
9265 switch(expr->len) {
9266 case 1:
9267 if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9268 expr->opcode[0] != JIM_EXPROP_NUMBER)
9269 goto noopt;
9270 break;
9271 case 2:
9272 if (expr->opcode[1] != JIM_EXPROP_NOT ||
9273 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9274 goto noopt;
9275 break;
9276 case 3:
9277 if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9278 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9279 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9280 goto noopt;
9281 switch(expr->opcode[2]) {
9282 case JIM_EXPROP_LT:
9283 case JIM_EXPROP_LTE:
9284 case JIM_EXPROP_GT:
9285 case JIM_EXPROP_GTE:
9286 case JIM_EXPROP_NUMEQ:
9287 case JIM_EXPROP_NUMNE:
9288 /* nothing to do */
9289 break;
9290 default:
9291 goto noopt;
9292 }
9293 break;
9294 default:
9295 Jim_Panic(interp,
9296 "Unexpected default reached in Jim_WhileCoreCommand()");
9297 break;
9298 }
9299
9300 /* STEP 2 -- conditions meet. Initialization. Take different
9301 * branches for different expression lengths. */
9302 exprLen = expr->len;
9303
9304 if (exprLen == 1) {
9305 jim_wide wideValue;
9306
9307 if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9308 varAObjPtr = expr->obj[0];
9309 Jim_IncrRefCount(varAObjPtr);
9310 } else {
9311 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9312 goto noopt;
9313 }
9314 while (1) {
9315 if (varAObjPtr) {
9316 if (!(objPtr =
9317 Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9318 Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9319 {
9320 Jim_DecrRefCount(interp, varAObjPtr);
9321 goto noopt;
9322 }
9323 }
9324 if (!wideValue) break;
9325 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9326 switch(retval) {
9327 case JIM_BREAK:
9328 if (varAObjPtr)
9329 Jim_DecrRefCount(interp, varAObjPtr);
9330 goto out;
9331 break;
9332 case JIM_CONTINUE:
9333 continue;
9334 break;
9335 default:
9336 if (varAObjPtr)
9337 Jim_DecrRefCount(interp, varAObjPtr);
9338 return retval;
9339 }
9340 }
9341 }
9342 if (varAObjPtr)
9343 Jim_DecrRefCount(interp, varAObjPtr);
9344 } else if (exprLen == 3) {
9345 jim_wide wideValueA, wideValueB, cmpRes = 0;
9346 int cmpType = expr->opcode[2];
9347
9348 varAObjPtr = expr->obj[0];
9349 Jim_IncrRefCount(varAObjPtr);
9350 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9351 varBObjPtr = expr->obj[1];
9352 Jim_IncrRefCount(varBObjPtr);
9353 } else {
9354 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9355 goto noopt;
9356 }
9357 while (1) {
9358 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9359 Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9360 {
9361 Jim_DecrRefCount(interp, varAObjPtr);
9362 if (varBObjPtr)
9363 Jim_DecrRefCount(interp, varBObjPtr);
9364 goto noopt;
9365 }
9366 if (varBObjPtr) {
9367 if (!(objPtr =
9368 Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9369 Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9370 {
9371 Jim_DecrRefCount(interp, varAObjPtr);
9372 if (varBObjPtr)
9373 Jim_DecrRefCount(interp, varBObjPtr);
9374 goto noopt;
9375 }
9376 }
9377 switch(cmpType) {
9378 case JIM_EXPROP_LT:
9379 cmpRes = wideValueA < wideValueB; break;
9380 case JIM_EXPROP_LTE:
9381 cmpRes = wideValueA <= wideValueB; break;
9382 case JIM_EXPROP_GT:
9383 cmpRes = wideValueA > wideValueB; break;
9384 case JIM_EXPROP_GTE:
9385 cmpRes = wideValueA >= wideValueB; break;
9386 case JIM_EXPROP_NUMEQ:
9387 cmpRes = wideValueA == wideValueB; break;
9388 case JIM_EXPROP_NUMNE:
9389 cmpRes = wideValueA != wideValueB; break;
9390 }
9391 if (!cmpRes) break;
9392 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9393 switch(retval) {
9394 case JIM_BREAK:
9395 Jim_DecrRefCount(interp, varAObjPtr);
9396 if (varBObjPtr)
9397 Jim_DecrRefCount(interp, varBObjPtr);
9398 goto out;
9399 break;
9400 case JIM_CONTINUE:
9401 continue;
9402 break;
9403 default:
9404 Jim_DecrRefCount(interp, varAObjPtr);
9405 if (varBObjPtr)
9406 Jim_DecrRefCount(interp, varBObjPtr);
9407 return retval;
9408 }
9409 }
9410 }
9411 Jim_DecrRefCount(interp, varAObjPtr);
9412 if (varBObjPtr)
9413 Jim_DecrRefCount(interp, varBObjPtr);
9414 } else {
9415 /* TODO: case for len == 2 */
9416 goto noopt;
9417 }
9418 Jim_SetEmptyResult(interp);
9419 return JIM_OK;
9420 }
9421 noopt:
9422 #endif
9423
9424 /* The general purpose implementation of while starts here */
9425 while (1) {
9426 int boolean, retval;
9427
9428 if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9429 &boolean)) != JIM_OK)
9430 return retval;
9431 if (!boolean) break;
9432 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9433 switch(retval) {
9434 case JIM_BREAK:
9435 goto out;
9436 break;
9437 case JIM_CONTINUE:
9438 continue;
9439 break;
9440 default:
9441 return retval;
9442 }
9443 }
9444 }
9445 out:
9446 Jim_SetEmptyResult(interp);
9447 return JIM_OK;
9448 }
9449
9450 /* [for] */
9451 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9452 Jim_Obj *const *argv)
9453 {
9454 int retval;
9455
9456 if (argc != 5) {
9457 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9458 return JIM_ERR;
9459 }
9460 /* Check if the for is on the form:
9461 * for {set i CONST} {$i < CONST} {incr i}
9462 * for {set i CONST} {$i < $j} {incr i}
9463 * for {set i CONST} {$i <= CONST} {incr i}
9464 * for {set i CONST} {$i <= $j} {incr i}
9465 * XXX: NOTE: if variable traces are implemented, this optimization
9466 * need to be modified to check for the proc epoch at every variable
9467 * update. */
9468 #ifdef JIM_OPTIMIZATION
9469 {
9470 ScriptObj *initScript, *incrScript;
9471 ExprByteCode *expr;
9472 jim_wide start, stop, currentVal;
9473 unsigned jim_wide procEpoch = interp->procEpoch;
9474 Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9475 int cmpType;
9476 struct Jim_Cmd *cmdPtr;
9477
9478 /* Do it only if there aren't shared arguments */
9479 if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9480 goto evalstart;
9481 initScript = Jim_GetScript(interp, argv[1]);
9482 expr = Jim_GetExpression(interp, argv[2]);
9483 incrScript = Jim_GetScript(interp, argv[3]);
9484
9485 /* Ensure proper lengths to start */
9486 if (initScript->len != 6) goto evalstart;
9487 if (incrScript->len != 4) goto evalstart;
9488 if (expr->len != 3) goto evalstart;
9489 /* Ensure proper token types. */
9490 if (initScript->token[2].type != JIM_TT_ESC ||
9491 initScript->token[4].type != JIM_TT_ESC ||
9492 incrScript->token[2].type != JIM_TT_ESC ||
9493 expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9494 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9495 expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9496 (expr->opcode[2] != JIM_EXPROP_LT &&
9497 expr->opcode[2] != JIM_EXPROP_LTE))
9498 goto evalstart;
9499 cmpType = expr->opcode[2];
9500 /* Initialization command must be [set] */
9501 cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9502 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9503 goto evalstart;
9504 /* Update command must be incr */
9505 cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9506 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9507 goto evalstart;
9508 /* set, incr, expression must be about the same variable */
9509 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9510 incrScript->token[2].objPtr, 0))
9511 goto evalstart;
9512 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9513 expr->obj[0], 0))
9514 goto evalstart;
9515 /* Check that the initialization and comparison are valid integers */
9516 if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9517 goto evalstart;
9518 if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9519 Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9520 {
9521 goto evalstart;
9522 }
9523
9524 /* Initialization */
9525 varNamePtr = expr->obj[0];
9526 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9527 stopVarNamePtr = expr->obj[1];
9528 Jim_IncrRefCount(stopVarNamePtr);
9529 }
9530 Jim_IncrRefCount(varNamePtr);
9531
9532 /* --- OPTIMIZED FOR --- */
9533 /* Start to loop */
9534 objPtr = Jim_NewIntObj(interp, start);
9535 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9536 Jim_DecrRefCount(interp, varNamePtr);
9537 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9538 Jim_FreeNewObj(interp, objPtr);
9539 goto evalstart;
9540 }
9541 while (1) {
9542 /* === Check condition === */
9543 /* Common code: */
9544 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9545 if (objPtr == NULL ||
9546 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9547 {
9548 Jim_DecrRefCount(interp, varNamePtr);
9549 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9550 goto testcond;
9551 }
9552 /* Immediate or Variable? get the 'stop' value if the latter. */
9553 if (stopVarNamePtr) {
9554 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9555 if (objPtr == NULL ||
9556 Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9557 {
9558 Jim_DecrRefCount(interp, varNamePtr);
9559 Jim_DecrRefCount(interp, stopVarNamePtr);
9560 goto testcond;
9561 }
9562 }
9563 if (cmpType == JIM_EXPROP_LT) {
9564 if (currentVal >= stop) break;
9565 } else {
9566 if (currentVal > stop) break;
9567 }
9568 /* Eval body */
9569 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9570 switch(retval) {
9571 case JIM_BREAK:
9572 if (stopVarNamePtr)
9573 Jim_DecrRefCount(interp, stopVarNamePtr);
9574 Jim_DecrRefCount(interp, varNamePtr);
9575 goto out;
9576 case JIM_CONTINUE:
9577 /* nothing to do */
9578 break;
9579 default:
9580 if (stopVarNamePtr)
9581 Jim_DecrRefCount(interp, stopVarNamePtr);
9582 Jim_DecrRefCount(interp, varNamePtr);
9583 return retval;
9584 }
9585 }
9586 /* If there was a change in procedures/command continue
9587 * with the usual [for] command implementation */
9588 if (procEpoch != interp->procEpoch) {
9589 if (stopVarNamePtr)
9590 Jim_DecrRefCount(interp, stopVarNamePtr);
9591 Jim_DecrRefCount(interp, varNamePtr);
9592 goto evalnext;
9593 }
9594 /* Increment */
9595 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9596 if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9597 objPtr->internalRep.wideValue ++;
9598 Jim_InvalidateStringRep(objPtr);
9599 } else {
9600 Jim_Obj *auxObjPtr;
9601
9602 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9603 if (stopVarNamePtr)
9604 Jim_DecrRefCount(interp, stopVarNamePtr);
9605 Jim_DecrRefCount(interp, varNamePtr);
9606 goto evalnext;
9607 }
9608 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9609 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9610 if (stopVarNamePtr)
9611 Jim_DecrRefCount(interp, stopVarNamePtr);
9612 Jim_DecrRefCount(interp, varNamePtr);
9613 Jim_FreeNewObj(interp, auxObjPtr);
9614 goto evalnext;
9615 }
9616 }
9617 }
9618 if (stopVarNamePtr)
9619 Jim_DecrRefCount(interp, stopVarNamePtr);
9620 Jim_DecrRefCount(interp, varNamePtr);
9621 Jim_SetEmptyResult(interp);
9622 return JIM_OK;
9623 }
9624 #endif
9625 evalstart:
9626 /* Eval start */
9627 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9628 return retval;
9629 while (1) {
9630 int boolean;
9631 testcond:
9632 /* Test the condition */
9633 if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9634 != JIM_OK)
9635 return retval;
9636 if (!boolean) break;
9637 /* Eval body */
9638 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9639 switch(retval) {
9640 case JIM_BREAK:
9641 goto out;
9642 break;
9643 case JIM_CONTINUE:
9644 /* Nothing to do */
9645 break;
9646 default:
9647 return retval;
9648 }
9649 }
9650 evalnext:
9651 /* Eval next */
9652 if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9653 switch(retval) {
9654 case JIM_BREAK:
9655 goto out;
9656 break;
9657 case JIM_CONTINUE:
9658 continue;
9659 break;
9660 default:
9661 return retval;
9662 }
9663 }
9664 }
9665 out:
9666 Jim_SetEmptyResult(interp);
9667 return JIM_OK;
9668 }
9669
9670 /* foreach + lmap implementation. */
9671 static int JimForeachMapHelper(Jim_Interp *interp, int argc,
9672 Jim_Obj *const *argv, int doMap)
9673 {
9674 int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9675 int nbrOfLoops = 0;
9676 Jim_Obj *emptyStr, *script, *mapRes = NULL;
9677
9678 if (argc < 4 || argc % 2 != 0) {
9679 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9680 return JIM_ERR;
9681 }
9682 if (doMap) {
9683 mapRes = Jim_NewListObj(interp, NULL, 0);
9684 Jim_IncrRefCount(mapRes);
9685 }
9686 emptyStr = Jim_NewEmptyStringObj(interp);
9687 Jim_IncrRefCount(emptyStr);
9688 script = argv[argc-1]; /* Last argument is a script */
9689 nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
9690 listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9691 listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9692 /* Initialize iterators and remember max nbr elements each list */
9693 memset(listsIdx, 0, nbrOfLists * sizeof(int));
9694 /* Remember lengths of all lists and calculate how much rounds to loop */
9695 for (i=0; i < nbrOfLists*2; i += 2) {
9696 div_t cnt;
9697 int count;
9698 Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9699 Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9700 if (listsEnd[i] == 0) {
9701 Jim_SetResultString(interp, "foreach varlist is empty", -1);
9702 goto err;
9703 }
9704 cnt = div(listsEnd[i+1], listsEnd[i]);
9705 count = cnt.quot + (cnt.rem ? 1 : 0);
9706 if (count > nbrOfLoops)
9707 nbrOfLoops = count;
9708 }
9709 for (; nbrOfLoops-- > 0; ) {
9710 for (i=0; i < nbrOfLists; ++i) {
9711 int varIdx = 0, var = i * 2;
9712 while (varIdx < listsEnd[var]) {
9713 Jim_Obj *varName, *ele;
9714 int lst = i * 2 + 1;
9715 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9716 != JIM_OK)
9717 goto err;
9718 if (listsIdx[i] < listsEnd[lst]) {
9719 if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9720 != JIM_OK)
9721 goto err;
9722 if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9723 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9724 goto err;
9725 }
9726 ++listsIdx[i]; /* Remember next iterator of current list */
9727 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9728 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9729 goto err;
9730 }
9731 ++varIdx; /* Next variable */
9732 }
9733 }
9734 switch (result = Jim_EvalObj(interp, script)) {
9735 case JIM_OK:
9736 if (doMap)
9737 Jim_ListAppendElement(interp, mapRes, interp->result);
9738 break;
9739 case JIM_CONTINUE:
9740 break;
9741 case JIM_BREAK:
9742 goto out;
9743 break;
9744 default:
9745 goto err;
9746 }
9747 }
9748 out:
9749 result = JIM_OK;
9750 if (doMap)
9751 Jim_SetResult(interp, mapRes);
9752 else
9753 Jim_SetEmptyResult(interp);
9754 err:
9755 if (doMap)
9756 Jim_DecrRefCount(interp, mapRes);
9757 Jim_DecrRefCount(interp, emptyStr);
9758 Jim_Free(listsIdx);
9759 Jim_Free(listsEnd);
9760 return result;
9761 }
9762
9763 /* [foreach] */
9764 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
9765 Jim_Obj *const *argv)
9766 {
9767 return JimForeachMapHelper(interp, argc, argv, 0);
9768 }
9769
9770 /* [lmap] */
9771 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
9772 Jim_Obj *const *argv)
9773 {
9774 return JimForeachMapHelper(interp, argc, argv, 1);
9775 }
9776
9777 /* [if] */
9778 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
9779 Jim_Obj *const *argv)
9780 {
9781 int boolean, retval, current = 1, falsebody = 0;
9782 if (argc >= 3) {
9783 while (1) {
9784 /* Far not enough arguments given! */
9785 if (current >= argc) goto err;
9786 if ((retval = Jim_GetBoolFromExpr(interp,
9787 argv[current++], &boolean))
9788 != JIM_OK)
9789 return retval;
9790 /* There lacks something, isn't it? */
9791 if (current >= argc) goto err;
9792 if (Jim_CompareStringImmediate(interp, argv[current],
9793 "then")) current++;
9794 /* Tsk tsk, no then-clause? */
9795 if (current >= argc) goto err;
9796 if (boolean)
9797 return Jim_EvalObj(interp, argv[current]);
9798 /* Ok: no else-clause follows */
9799 if (++current >= argc) return JIM_OK;
9800 falsebody = current++;
9801 if (Jim_CompareStringImmediate(interp, argv[falsebody],
9802 "else")) {
9803 /* IIICKS - else-clause isn't last cmd? */
9804 if (current != argc-1) goto err;
9805 return Jim_EvalObj(interp, argv[current]);
9806 } else if (Jim_CompareStringImmediate(interp,
9807 argv[falsebody], "elseif"))
9808 /* Ok: elseif follows meaning all the stuff
9809 * again (how boring...) */
9810 continue;
9811 /* OOPS - else-clause is not last cmd?*/
9812 else if (falsebody != argc-1)
9813 goto err;
9814 return Jim_EvalObj(interp, argv[falsebody]);
9815 }
9816 return JIM_OK;
9817 }
9818 err:
9819 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
9820 return JIM_ERR;
9821 }
9822
9823 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
9824
9825 /* [switch] */
9826 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
9827 Jim_Obj *const *argv)
9828 {
9829 int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
9830 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
9831 Jim_Obj *script = 0;
9832 if (argc < 3) goto wrongnumargs;
9833 for (opt=1; opt < argc; ++opt) {
9834 const char *option = Jim_GetString(argv[opt], 0);
9835 if (*option != '-') break;
9836 else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
9837 else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
9838 else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
9839 else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
9840 else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
9841 if ((argc - opt) < 2) goto wrongnumargs;
9842 command = argv[++opt];
9843 } else {
9844 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9845 Jim_AppendStrings(interp, Jim_GetResult(interp),
9846 "bad option \"", option, "\": must be -exact, -glob, "
9847 "-regexp, -command procname or --", 0);
9848 goto err;
9849 }
9850 if ((argc - opt) < 2) goto wrongnumargs;
9851 }
9852 strObj = argv[opt++];
9853 patCount = argc - opt;
9854 if (patCount == 1) {
9855 Jim_Obj **vector;
9856 JimListGetElements(interp, argv[opt], &patCount, &vector);
9857 caseList = vector;
9858 } else
9859 caseList = &argv[opt];
9860 if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
9861 for (i=0; script == 0 && i < patCount; i += 2) {
9862 Jim_Obj *patObj = caseList[i];
9863 if (!Jim_CompareStringImmediate(interp, patObj, "default")
9864 || i < (patCount-2)) {
9865 switch (matchOpt) {
9866 case SWITCH_EXACT:
9867 if (Jim_StringEqObj(strObj, patObj, 0))
9868 script = caseList[i+1];
9869 break;
9870 case SWITCH_GLOB:
9871 if (Jim_StringMatchObj(patObj, strObj, 0))
9872 script = caseList[i+1];
9873 break;
9874 case SWITCH_RE:
9875 command = Jim_NewStringObj(interp, "regexp", -1);
9876 /* Fall thru intentionally */
9877 case SWITCH_CMD: {
9878 Jim_Obj *parms[] = {command, patObj, strObj};
9879 int rc = Jim_EvalObjVector(interp, 3, parms);
9880 long matching;
9881 /* After the execution of a command we need to
9882 * make sure to reconvert the object into a list
9883 * again. Only for the single-list style [switch]. */
9884 if (argc-opt == 1) {
9885 Jim_Obj **vector;
9886 JimListGetElements(interp, argv[opt], &patCount,
9887 &vector);
9888 caseList = vector;
9889 }
9890 /* command is here already decref'd */
9891 if (rc != JIM_OK) {
9892 retcode = rc;
9893 goto err;
9894 }
9895 rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
9896 if (rc != JIM_OK) {
9897 retcode = rc;
9898 goto err;
9899 }
9900 if (matching)
9901 script = caseList[i+1];
9902 break;
9903 }
9904 default:
9905 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9906 Jim_AppendStrings(interp, Jim_GetResult(interp),
9907 "internal error: no such option implemented", 0);
9908 goto err;
9909 }
9910 } else {
9911 script = caseList[i+1];
9912 }
9913 }
9914 for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
9915 i += 2)
9916 script = caseList[i+1];
9917 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
9918 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9919 Jim_AppendStrings(interp, Jim_GetResult(interp),
9920 "no body specified for pattern \"",
9921 Jim_GetString(caseList[i-2], 0), "\"", 0);
9922 goto err;
9923 }
9924 retcode = JIM_OK;
9925 Jim_SetEmptyResult(interp);
9926 if (script != 0)
9927 retcode = Jim_EvalObj(interp, script);
9928 return retcode;
9929 wrongnumargs:
9930 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
9931 "pattern body ... ?default body? or "
9932 "{pattern body ?pattern body ...?}");
9933 err:
9934 return retcode;
9935 }
9936
9937 /* [list] */
9938 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
9939 Jim_Obj *const *argv)
9940 {
9941 Jim_Obj *listObjPtr;
9942
9943 listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
9944 Jim_SetResult(interp, listObjPtr);
9945 return JIM_OK;
9946 }
9947
9948 /* [lindex] */
9949 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
9950 Jim_Obj *const *argv)
9951 {
9952 Jim_Obj *objPtr, *listObjPtr;
9953 int i;
9954 int index;
9955
9956 if (argc < 3) {
9957 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
9958 return JIM_ERR;
9959 }
9960 objPtr = argv[1];
9961 Jim_IncrRefCount(objPtr);
9962 for (i = 2; i < argc; i++) {
9963 listObjPtr = objPtr;
9964 if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
9965 Jim_DecrRefCount(interp, listObjPtr);
9966 return JIM_ERR;
9967 }
9968 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
9969 JIM_NONE) != JIM_OK) {
9970 /* Returns an empty object if the index
9971 * is out of range. */
9972 Jim_DecrRefCount(interp, listObjPtr);
9973 Jim_SetEmptyResult(interp);
9974 return JIM_OK;
9975 }
9976 Jim_IncrRefCount(objPtr);
9977 Jim_DecrRefCount(interp, listObjPtr);
9978 }
9979 Jim_SetResult(interp, objPtr);
9980 Jim_DecrRefCount(interp, objPtr);
9981 return JIM_OK;
9982 }
9983
9984 /* [llength] */
9985 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
9986 Jim_Obj *const *argv)
9987 {
9988 int len;
9989
9990 if (argc != 2) {
9991 Jim_WrongNumArgs(interp, 1, argv, "list");
9992 return JIM_ERR;
9993 }
9994 Jim_ListLength(interp, argv[1], &len);
9995 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
9996 return JIM_OK;
9997 }
9998
9999 /* [lappend] */
10000 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
10001 Jim_Obj *const *argv)
10002 {
10003 Jim_Obj *listObjPtr;
10004 int shared, i;
10005
10006 if (argc < 2) {
10007 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10008 return JIM_ERR;
10009 }
10010 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10011 if (!listObjPtr) {
10012 /* Create the list if it does not exists */
10013 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10014 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10015 Jim_FreeNewObj(interp, listObjPtr);
10016 return JIM_ERR;
10017 }
10018 }
10019 shared = Jim_IsShared(listObjPtr);
10020 if (shared)
10021 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10022 for (i = 2; i < argc; i++)
10023 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10024 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10025 if (shared)
10026 Jim_FreeNewObj(interp, listObjPtr);
10027 return JIM_ERR;
10028 }
10029 Jim_SetResult(interp, listObjPtr);
10030 return JIM_OK;
10031 }
10032
10033 /* [linsert] */
10034 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10035 Jim_Obj *const *argv)
10036 {
10037 int index, len;
10038 Jim_Obj *listPtr;
10039
10040 if (argc < 4) {
10041 Jim_WrongNumArgs(interp, 1, argv, "list index element "
10042 "?element ...?");
10043 return JIM_ERR;
10044 }
10045 listPtr = argv[1];
10046 if (Jim_IsShared(listPtr))
10047 listPtr = Jim_DuplicateObj(interp, listPtr);
10048 if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10049 goto err;
10050 Jim_ListLength(interp, listPtr, &len);
10051 if (index >= len)
10052 index = len;
10053 else if (index < 0)
10054 index = len + index + 1;
10055 Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10056 Jim_SetResult(interp, listPtr);
10057 return JIM_OK;
10058 err:
10059 if (listPtr != argv[1]) {
10060 Jim_FreeNewObj(interp, listPtr);
10061 }
10062 return JIM_ERR;
10063 }
10064
10065 /* [lset] */
10066 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10067 Jim_Obj *const *argv)
10068 {
10069 if (argc < 3) {
10070 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10071 return JIM_ERR;
10072 } else if (argc == 3) {
10073 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10074 return JIM_ERR;
10075 Jim_SetResult(interp, argv[2]);
10076 return JIM_OK;
10077 }
10078 if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10079 == JIM_ERR) return JIM_ERR;
10080 return JIM_OK;
10081 }
10082
10083 /* [lsort] */
10084 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10085 {
10086 const char *options[] = {
10087 "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10088 };
10089 enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10090 Jim_Obj *resObj;
10091 int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10092 int decreasing = 0;
10093
10094 if (argc < 2) {
10095 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10096 return JIM_ERR;
10097 }
10098 for (i = 1; i < (argc-1); i++) {
10099 int option;
10100
10101 if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10102 != JIM_OK)
10103 return JIM_ERR;
10104 switch(option) {
10105 case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10106 case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10107 case OPT_INCREASING: decreasing = 0; break;
10108 case OPT_DECREASING: decreasing = 1; break;
10109 }
10110 }
10111 if (decreasing) {
10112 switch(lsortType) {
10113 case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10114 case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10115 }
10116 }
10117 resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10118 ListSortElements(interp, resObj, lsortType);
10119 Jim_SetResult(interp, resObj);
10120 return JIM_OK;
10121 }
10122
10123 /* [append] */
10124 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10125 Jim_Obj *const *argv)
10126 {
10127 Jim_Obj *stringObjPtr;
10128 int shared, i;
10129
10130 if (argc < 2) {
10131 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10132 return JIM_ERR;
10133 }
10134 if (argc == 2) {
10135 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10136 if (!stringObjPtr) return JIM_ERR;
10137 } else {
10138 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10139 if (!stringObjPtr) {
10140 /* Create the string if it does not exists */
10141 stringObjPtr = Jim_NewEmptyStringObj(interp);
10142 if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10143 != JIM_OK) {
10144 Jim_FreeNewObj(interp, stringObjPtr);
10145 return JIM_ERR;
10146 }
10147 }
10148 }
10149 shared = Jim_IsShared(stringObjPtr);
10150 if (shared)
10151 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10152 for (i = 2; i < argc; i++)
10153 Jim_AppendObj(interp, stringObjPtr, argv[i]);
10154 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10155 if (shared)
10156 Jim_FreeNewObj(interp, stringObjPtr);
10157 return JIM_ERR;
10158 }
10159 Jim_SetResult(interp, stringObjPtr);
10160 return JIM_OK;
10161 }
10162
10163 /* [debug] */
10164 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10165 Jim_Obj *const *argv)
10166 {
10167 const char *options[] = {
10168 "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10169 "exprbc",
10170 NULL
10171 };
10172 enum {
10173 OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10174 OPT_EXPRLEN, OPT_EXPRBC
10175 };
10176 int option;
10177
10178 if (argc < 2) {
10179 Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10180 return JIM_ERR;
10181 }
10182 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10183 JIM_ERRMSG) != JIM_OK)
10184 return JIM_ERR;
10185 if (option == OPT_REFCOUNT) {
10186 if (argc != 3) {
10187 Jim_WrongNumArgs(interp, 2, argv, "object");
10188 return JIM_ERR;
10189 }
10190 Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10191 return JIM_OK;
10192 } else if (option == OPT_OBJCOUNT) {
10193 int freeobj = 0, liveobj = 0;
10194 char buf[256];
10195 Jim_Obj *objPtr;
10196
10197 if (argc != 2) {
10198 Jim_WrongNumArgs(interp, 2, argv, "");
10199 return JIM_ERR;
10200 }
10201 /* Count the number of free objects. */
10202 objPtr = interp->freeList;
10203 while (objPtr) {
10204 freeobj++;
10205 objPtr = objPtr->nextObjPtr;
10206 }
10207 /* Count the number of live objects. */
10208 objPtr = interp->liveList;
10209 while (objPtr) {
10210 liveobj++;
10211 objPtr = objPtr->nextObjPtr;
10212 }
10213 /* Set the result string and return. */
10214 sprintf(buf, "free %d used %d", freeobj, liveobj);
10215 Jim_SetResultString(interp, buf, -1);
10216 return JIM_OK;
10217 } else if (option == OPT_OBJECTS) {
10218 Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10219 /* Count the number of live objects. */
10220 objPtr = interp->liveList;
10221 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10222 while (objPtr) {
10223 char buf[128];
10224 const char *type = objPtr->typePtr ?
10225 objPtr->typePtr->name : "";
10226 subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10227 sprintf(buf, "%p", objPtr);
10228 Jim_ListAppendElement(interp, subListObjPtr,
10229 Jim_NewStringObj(interp, buf, -1));
10230 Jim_ListAppendElement(interp, subListObjPtr,
10231 Jim_NewStringObj(interp, type, -1));
10232 Jim_ListAppendElement(interp, subListObjPtr,
10233 Jim_NewIntObj(interp, objPtr->refCount));
10234 Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10235 Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10236 objPtr = objPtr->nextObjPtr;
10237 }
10238 Jim_SetResult(interp, listObjPtr);
10239 return JIM_OK;
10240 } else if (option == OPT_INVSTR) {
10241 Jim_Obj *objPtr;
10242
10243 if (argc != 3) {
10244 Jim_WrongNumArgs(interp, 2, argv, "object");
10245 return JIM_ERR;
10246 }
10247 objPtr = argv[2];
10248 if (objPtr->typePtr != NULL)
10249 Jim_InvalidateStringRep(objPtr);
10250 Jim_SetEmptyResult(interp);
10251 return JIM_OK;
10252 } else if (option == OPT_SCRIPTLEN) {
10253 ScriptObj *script;
10254 if (argc != 3) {
10255 Jim_WrongNumArgs(interp, 2, argv, "script");
10256 return JIM_ERR;
10257 }
10258 script = Jim_GetScript(interp, argv[2]);
10259 Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10260 return JIM_OK;
10261 } else if (option == OPT_EXPRLEN) {
10262 ExprByteCode *expr;
10263 if (argc != 3) {
10264 Jim_WrongNumArgs(interp, 2, argv, "expression");
10265 return JIM_ERR;
10266 }
10267 expr = Jim_GetExpression(interp, argv[2]);
10268 if (expr == NULL)
10269 return JIM_ERR;
10270 Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10271 return JIM_OK;
10272 } else if (option == OPT_EXPRBC) {
10273 Jim_Obj *objPtr;
10274 ExprByteCode *expr;
10275 int i;
10276
10277 if (argc != 3) {
10278 Jim_WrongNumArgs(interp, 2, argv, "expression");
10279 return JIM_ERR;
10280 }
10281 expr = Jim_GetExpression(interp, argv[2]);
10282 if (expr == NULL)
10283 return JIM_ERR;
10284 objPtr = Jim_NewListObj(interp, NULL, 0);
10285 for (i = 0; i < expr->len; i++) {
10286 const char *type;
10287 Jim_ExprOperator *op;
10288
10289 switch(expr->opcode[i]) {
10290 case JIM_EXPROP_NUMBER: type = "number"; break;
10291 case JIM_EXPROP_COMMAND: type = "command"; break;
10292 case JIM_EXPROP_VARIABLE: type = "variable"; break;
10293 case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10294 case JIM_EXPROP_SUBST: type = "subst"; break;
10295 case JIM_EXPROP_STRING: type = "string"; break;
10296 default:
10297 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10298 if (op == NULL) {
10299 type = "private";
10300 } else {
10301 type = "operator";
10302 }
10303 break;
10304 }
10305 Jim_ListAppendElement(interp, objPtr,
10306 Jim_NewStringObj(interp, type, -1));
10307 Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10308 }
10309 Jim_SetResult(interp, objPtr);
10310 return JIM_OK;
10311 } else {
10312 Jim_SetResultString(interp,
10313 "bad option. Valid options are refcount, "
10314 "objcount, objects, invstr", -1);
10315 return JIM_ERR;
10316 }
10317 return JIM_OK; /* unreached */
10318 }
10319
10320 /* [eval] */
10321 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10322 Jim_Obj *const *argv)
10323 {
10324 if (argc == 2) {
10325 return Jim_EvalObj(interp, argv[1]);
10326 } else if (argc > 2) {
10327 Jim_Obj *objPtr;
10328 int retcode;
10329
10330 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10331 Jim_IncrRefCount(objPtr);
10332 retcode = Jim_EvalObj(interp, objPtr);
10333 Jim_DecrRefCount(interp, objPtr);
10334 return retcode;
10335 } else {
10336 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10337 return JIM_ERR;
10338 }
10339 }
10340
10341 /* [uplevel] */
10342 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10343 Jim_Obj *const *argv)
10344 {
10345 if (argc >= 2) {
10346 int retcode, newLevel, oldLevel;
10347 Jim_CallFrame *savedCallFrame, *targetCallFrame;
10348 Jim_Obj *objPtr;
10349 const char *str;
10350
10351 /* Save the old callframe pointer */
10352 savedCallFrame = interp->framePtr;
10353
10354 /* Lookup the target frame pointer */
10355 str = Jim_GetString(argv[1], NULL);
10356 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10357 {
10358 if (Jim_GetCallFrameByLevel(interp, argv[1],
10359 &targetCallFrame,
10360 &newLevel) != JIM_OK)
10361 return JIM_ERR;
10362 argc--;
10363 argv++;
10364 } else {
10365 if (Jim_GetCallFrameByLevel(interp, NULL,
10366 &targetCallFrame,
10367 &newLevel) != JIM_OK)
10368 return JIM_ERR;
10369 }
10370 if (argc < 2) {
10371 argc++;
10372 argv--;
10373 Jim_WrongNumArgs(interp, 1, argv,
10374 "?level? command ?arg ...?");
10375 return JIM_ERR;
10376 }
10377 /* Eval the code in the target callframe. */
10378 interp->framePtr = targetCallFrame;
10379 oldLevel = interp->numLevels;
10380 interp->numLevels = newLevel;
10381 if (argc == 2) {
10382 retcode = Jim_EvalObj(interp, argv[1]);
10383 } else {
10384 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10385 Jim_IncrRefCount(objPtr);
10386 retcode = Jim_EvalObj(interp, objPtr);
10387 Jim_DecrRefCount(interp, objPtr);
10388 }
10389 interp->numLevels = oldLevel;
10390 interp->framePtr = savedCallFrame;
10391 return retcode;
10392 } else {
10393 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10394 return JIM_ERR;
10395 }
10396 }
10397
10398 /* [expr] */
10399 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10400 Jim_Obj *const *argv)
10401 {
10402 Jim_Obj *exprResultPtr;
10403 int retcode;
10404
10405 if (argc == 2) {
10406 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10407 } else if (argc > 2) {
10408 Jim_Obj *objPtr;
10409
10410 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10411 Jim_IncrRefCount(objPtr);
10412 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10413 Jim_DecrRefCount(interp, objPtr);
10414 } else {
10415 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10416 return JIM_ERR;
10417 }
10418 if (retcode != JIM_OK) return retcode;
10419 Jim_SetResult(interp, exprResultPtr);
10420 Jim_DecrRefCount(interp, exprResultPtr);
10421 return JIM_OK;
10422 }
10423
10424 /* [break] */
10425 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10426 Jim_Obj *const *argv)
10427 {
10428 if (argc != 1) {
10429 Jim_WrongNumArgs(interp, 1, argv, "");
10430 return JIM_ERR;
10431 }
10432 return JIM_BREAK;
10433 }
10434
10435 /* [continue] */
10436 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10437 Jim_Obj *const *argv)
10438 {
10439 if (argc != 1) {
10440 Jim_WrongNumArgs(interp, 1, argv, "");
10441 return JIM_ERR;
10442 }
10443 return JIM_CONTINUE;
10444 }
10445
10446 /* [return] */
10447 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10448 Jim_Obj *const *argv)
10449 {
10450 if (argc == 1) {
10451 return JIM_RETURN;
10452 } else if (argc == 2) {
10453 Jim_SetResult(interp, argv[1]);
10454 interp->returnCode = JIM_OK;
10455 return JIM_RETURN;
10456 } else if (argc == 3 || argc == 4) {
10457 int returnCode;
10458 if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10459 return JIM_ERR;
10460 interp->returnCode = returnCode;
10461 if (argc == 4)
10462 Jim_SetResult(interp, argv[3]);
10463 return JIM_RETURN;
10464 } else {
10465 Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10466 return JIM_ERR;
10467 }
10468 return JIM_RETURN; /* unreached */
10469 }
10470
10471 /* [tailcall] */
10472 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10473 Jim_Obj *const *argv)
10474 {
10475 Jim_Obj *objPtr;
10476
10477 objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10478 Jim_SetResult(interp, objPtr);
10479 return JIM_EVAL;
10480 }
10481
10482 /* [proc] */
10483 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
10484 Jim_Obj *const *argv)
10485 {
10486 int argListLen;
10487 int arityMin, arityMax;
10488
10489 if (argc != 4 && argc != 5) {
10490 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10491 return JIM_ERR;
10492 }
10493 Jim_ListLength(interp, argv[2], &argListLen);
10494 arityMin = arityMax = argListLen+1;
10495 if (argListLen) {
10496 const char *str;
10497 int len;
10498 Jim_Obj *lastArgPtr;
10499
10500 Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10501 str = Jim_GetString(lastArgPtr, &len);
10502 if (len == 4 && memcmp(str, "args", 4) == 0) {
10503 arityMin--;
10504 arityMax = -1;
10505 }
10506 }
10507 if (argc == 4) {
10508 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10509 argv[2], NULL, argv[3], arityMin, arityMax);
10510 } else {
10511 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10512 argv[2], argv[3], argv[4], arityMin, arityMax);
10513 }
10514 }
10515
10516 /* [concat] */
10517 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
10518 Jim_Obj *const *argv)
10519 {
10520 Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10521 return JIM_OK;
10522 }
10523
10524 /* [upvar] */
10525 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
10526 Jim_Obj *const *argv)
10527 {
10528 const char *str;
10529 int i;
10530 Jim_CallFrame *targetCallFrame;
10531
10532 /* Lookup the target frame pointer */
10533 str = Jim_GetString(argv[1], NULL);
10534 if (argc > 3 &&
10535 ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10536 {
10537 if (Jim_GetCallFrameByLevel(interp, argv[1],
10538 &targetCallFrame, NULL) != JIM_OK)
10539 return JIM_ERR;
10540 argc--;
10541 argv++;
10542 } else {
10543 if (Jim_GetCallFrameByLevel(interp, NULL,
10544 &targetCallFrame, NULL) != JIM_OK)
10545 return JIM_ERR;
10546 }
10547 /* Check for arity */
10548 if (argc < 3 || ((argc-1)%2) != 0) {
10549 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10550 return JIM_ERR;
10551 }
10552 /* Now... for every other/local couple: */
10553 for (i = 1; i < argc; i += 2) {
10554 if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10555 targetCallFrame) != JIM_OK) return JIM_ERR;
10556 }
10557 return JIM_OK;
10558 }
10559
10560 /* [global] */
10561 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
10562 Jim_Obj *const *argv)
10563 {
10564 int i;
10565
10566 if (argc < 2) {
10567 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10568 return JIM_ERR;
10569 }
10570 /* Link every var to the toplevel having the same name */
10571 if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10572 for (i = 1; i < argc; i++) {
10573 if (Jim_SetVariableLink(interp, argv[i], argv[i],
10574 interp->topFramePtr) != JIM_OK) return JIM_ERR;
10575 }
10576 return JIM_OK;
10577 }
10578
10579 /* does the [string map] operation. On error NULL is returned,
10580 * otherwise a new string object with the result, having refcount = 0,
10581 * is returned. */
10582 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10583 Jim_Obj *objPtr, int nocase)
10584 {
10585 int numMaps;
10586 const char **key, *str, *noMatchStart = NULL;
10587 Jim_Obj **value;
10588 int *keyLen, strLen, i;
10589 Jim_Obj *resultObjPtr;
10590
10591 Jim_ListLength(interp, mapListObjPtr, &numMaps);
10592 if (numMaps % 2) {
10593 Jim_SetResultString(interp,
10594 "list must contain an even number of elements", -1);
10595 return NULL;
10596 }
10597 /* Initialization */
10598 numMaps /= 2;
10599 key = Jim_Alloc(sizeof(char*)*numMaps);
10600 keyLen = Jim_Alloc(sizeof(int)*numMaps);
10601 value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10602 resultObjPtr = Jim_NewStringObj(interp, "", 0);
10603 for (i = 0; i < numMaps; i++) {
10604 Jim_Obj *eleObjPtr;
10605
10606 Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10607 key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10608 Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10609 value[i] = eleObjPtr;
10610 }
10611 str = Jim_GetString(objPtr, &strLen);
10612 /* Map it */
10613 while(strLen) {
10614 for (i = 0; i < numMaps; i++) {
10615 if (strLen >= keyLen[i] && keyLen[i]) {
10616 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10617 nocase))
10618 {
10619 if (noMatchStart) {
10620 Jim_AppendString(interp, resultObjPtr,
10621 noMatchStart, str-noMatchStart);
10622 noMatchStart = NULL;
10623 }
10624 Jim_AppendObj(interp, resultObjPtr, value[i]);
10625 str += keyLen[i];
10626 strLen -= keyLen[i];
10627 break;
10628 }
10629 }
10630 }
10631 if (i == numMaps) { /* no match */
10632 if (noMatchStart == NULL)
10633 noMatchStart = str;
10634 str ++;
10635 strLen --;
10636 }
10637 }
10638 if (noMatchStart) {
10639 Jim_AppendString(interp, resultObjPtr,
10640 noMatchStart, str-noMatchStart);
10641 }
10642 Jim_Free((void*)key);
10643 Jim_Free(keyLen);
10644 Jim_Free(value);
10645 return resultObjPtr;
10646 }
10647
10648 /* [string] */
10649 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
10650 Jim_Obj *const *argv)
10651 {
10652 int option;
10653 const char *options[] = {
10654 "length", "compare", "match", "equal", "range", "map", "repeat",
10655 "index", "first", "tolower", "toupper", NULL
10656 };
10657 enum {
10658 OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10659 OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10660 };
10661
10662 if (argc < 2) {
10663 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10664 return JIM_ERR;
10665 }
10666 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10667 JIM_ERRMSG) != JIM_OK)
10668 return JIM_ERR;
10669
10670 if (option == OPT_LENGTH) {
10671 int len;
10672
10673 if (argc != 3) {
10674 Jim_WrongNumArgs(interp, 2, argv, "string");
10675 return JIM_ERR;
10676 }
10677 Jim_GetString(argv[2], &len);
10678 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10679 return JIM_OK;
10680 } else if (option == OPT_COMPARE) {
10681 int nocase = 0;
10682 if ((argc != 4 && argc != 5) ||
10683 (argc == 5 && Jim_CompareStringImmediate(interp,
10684 argv[2], "-nocase") == 0)) {
10685 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10686 return JIM_ERR;
10687 }
10688 if (argc == 5) {
10689 nocase = 1;
10690 argv++;
10691 }
10692 Jim_SetResult(interp, Jim_NewIntObj(interp,
10693 Jim_StringCompareObj(argv[2],
10694 argv[3], nocase)));
10695 return JIM_OK;
10696 } else if (option == OPT_MATCH) {
10697 int nocase = 0;
10698 if ((argc != 4 && argc != 5) ||
10699 (argc == 5 && Jim_CompareStringImmediate(interp,
10700 argv[2], "-nocase") == 0)) {
10701 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10702 "string");
10703 return JIM_ERR;
10704 }
10705 if (argc == 5) {
10706 nocase = 1;
10707 argv++;
10708 }
10709 Jim_SetResult(interp,
10710 Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10711 argv[3], nocase)));
10712 return JIM_OK;
10713 } else if (option == OPT_EQUAL) {
10714 if (argc != 4) {
10715 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10716 return JIM_ERR;
10717 }
10718 Jim_SetResult(interp,
10719 Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10720 argv[3], 0)));
10721 return JIM_OK;
10722 } else if (option == OPT_RANGE) {
10723 Jim_Obj *objPtr;
10724
10725 if (argc != 5) {
10726 Jim_WrongNumArgs(interp, 2, argv, "string first last");
10727 return JIM_ERR;
10728 }
10729 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10730 if (objPtr == NULL)
10731 return JIM_ERR;
10732 Jim_SetResult(interp, objPtr);
10733 return JIM_OK;
10734 } else if (option == OPT_MAP) {
10735 int nocase = 0;
10736 Jim_Obj *objPtr;
10737
10738 if ((argc != 4 && argc != 5) ||
10739 (argc == 5 && Jim_CompareStringImmediate(interp,
10740 argv[2], "-nocase") == 0)) {
10741 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10742 "string");
10743 return JIM_ERR;
10744 }
10745 if (argc == 5) {
10746 nocase = 1;
10747 argv++;
10748 }
10749 objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10750 if (objPtr == NULL)
10751 return JIM_ERR;
10752 Jim_SetResult(interp, objPtr);
10753 return JIM_OK;
10754 } else if (option == OPT_REPEAT) {
10755 Jim_Obj *objPtr;
10756 jim_wide count;
10757
10758 if (argc != 4) {
10759 Jim_WrongNumArgs(interp, 2, argv, "string count");
10760 return JIM_ERR;
10761 }
10762 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
10763 return JIM_ERR;
10764 objPtr = Jim_NewStringObj(interp, "", 0);
10765 while (count--) {
10766 Jim_AppendObj(interp, objPtr, argv[2]);
10767 }
10768 Jim_SetResult(interp, objPtr);
10769 return JIM_OK;
10770 } else if (option == OPT_INDEX) {
10771 int index, len;
10772 const char *str;
10773
10774 if (argc != 4) {
10775 Jim_WrongNumArgs(interp, 2, argv, "string index");
10776 return JIM_ERR;
10777 }
10778 if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
10779 return JIM_ERR;
10780 str = Jim_GetString(argv[2], &len);
10781 if (index != INT_MIN && index != INT_MAX)
10782 index = JimRelToAbsIndex(len, index);
10783 if (index < 0 || index >= len) {
10784 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10785 return JIM_OK;
10786 } else {
10787 Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
10788 return JIM_OK;
10789 }
10790 } else if (option == OPT_FIRST) {
10791 int index = 0, l1, l2;
10792 const char *s1, *s2;
10793
10794 if (argc != 4 && argc != 5) {
10795 Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
10796 return JIM_ERR;
10797 }
10798 s1 = Jim_GetString(argv[2], &l1);
10799 s2 = Jim_GetString(argv[3], &l2);
10800 if (argc == 5) {
10801 if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
10802 return JIM_ERR;
10803 index = JimRelToAbsIndex(l2, index);
10804 }
10805 Jim_SetResult(interp, Jim_NewIntObj(interp,
10806 JimStringFirst(s1, l1, s2, l2, index)));
10807 return JIM_OK;
10808 } else if (option == OPT_TOLOWER) {
10809 if (argc != 3) {
10810 Jim_WrongNumArgs(interp, 2, argv, "string");
10811 return JIM_ERR;
10812 }
10813 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
10814 } else if (option == OPT_TOUPPER) {
10815 if (argc != 3) {
10816 Jim_WrongNumArgs(interp, 2, argv, "string");
10817 return JIM_ERR;
10818 }
10819 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
10820 }
10821 return JIM_OK;
10822 }
10823
10824 /* [time] */
10825 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
10826 Jim_Obj *const *argv)
10827 {
10828 long i, count = 1;
10829 jim_wide start, elapsed;
10830 char buf [256];
10831 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
10832
10833 if (argc < 2) {
10834 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
10835 return JIM_ERR;
10836 }
10837 if (argc == 3) {
10838 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
10839 return JIM_ERR;
10840 }
10841 if (count < 0)
10842 return JIM_OK;
10843 i = count;
10844 start = JimClock();
10845 while (i-- > 0) {
10846 int retval;
10847
10848 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10849 return retval;
10850 }
10851 elapsed = JimClock() - start;
10852 sprintf(buf, fmt, elapsed/count);
10853 Jim_SetResultString(interp, buf, -1);
10854 return JIM_OK;
10855 }
10856
10857 /* [exit] */
10858 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
10859 Jim_Obj *const *argv)
10860 {
10861 long exitCode = 0;
10862
10863 if (argc > 2) {
10864 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
10865 return JIM_ERR;
10866 }
10867 if (argc == 2) {
10868 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
10869 return JIM_ERR;
10870 }
10871 interp->exitCode = exitCode;
10872 return JIM_EXIT;
10873 }
10874
10875 /* [catch] */
10876 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
10877 Jim_Obj *const *argv)
10878 {
10879 int exitCode = 0;
10880
10881 if (argc != 2 && argc != 3) {
10882 Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
10883 return JIM_ERR;
10884 }
10885 exitCode = Jim_EvalObj(interp, argv[1]);
10886 if (argc == 3) {
10887 if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
10888 != JIM_OK)
10889 return JIM_ERR;
10890 }
10891 Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
10892 return JIM_OK;
10893 }
10894
10895 /* [ref] */
10896 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
10897 Jim_Obj *const *argv)
10898 {
10899 if (argc != 3 && argc != 4) {
10900 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
10901 return JIM_ERR;
10902 }
10903 if (argc == 3) {
10904 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
10905 } else {
10906 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
10907 argv[3]));
10908 }
10909 return JIM_OK;
10910 }
10911
10912 /* [getref] */
10913 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
10914 Jim_Obj *const *argv)
10915 {
10916 Jim_Reference *refPtr;
10917
10918 if (argc != 2) {
10919 Jim_WrongNumArgs(interp, 1, argv, "reference");
10920 return JIM_ERR;
10921 }
10922 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10923 return JIM_ERR;
10924 Jim_SetResult(interp, refPtr->objPtr);
10925 return JIM_OK;
10926 }
10927
10928 /* [setref] */
10929 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
10930 Jim_Obj *const *argv)
10931 {
10932 Jim_Reference *refPtr;
10933
10934 if (argc != 3) {
10935 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
10936 return JIM_ERR;
10937 }
10938 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10939 return JIM_ERR;
10940 Jim_IncrRefCount(argv[2]);
10941 Jim_DecrRefCount(interp, refPtr->objPtr);
10942 refPtr->objPtr = argv[2];
10943 Jim_SetResult(interp, argv[2]);
10944 return JIM_OK;
10945 }
10946
10947 /* [collect] */
10948 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
10949 Jim_Obj *const *argv)
10950 {
10951 if (argc != 1) {
10952 Jim_WrongNumArgs(interp, 1, argv, "");
10953 return JIM_ERR;
10954 }
10955 Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
10956 return JIM_OK;
10957 }
10958
10959 /* [finalize] reference ?newValue? */
10960 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
10961 Jim_Obj *const *argv)
10962 {
10963 if (argc != 2 && argc != 3) {
10964 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
10965 return JIM_ERR;
10966 }
10967 if (argc == 2) {
10968 Jim_Obj *cmdNamePtr;
10969
10970 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
10971 return JIM_ERR;
10972 if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
10973 Jim_SetResult(interp, cmdNamePtr);
10974 } else {
10975 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
10976 return JIM_ERR;
10977 Jim_SetResult(interp, argv[2]);
10978 }
10979 return JIM_OK;
10980 }
10981
10982 /* TODO */
10983 /* [info references] (list of all the references/finalizers) */
10984
10985 /* [rename] */
10986 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
10987 Jim_Obj *const *argv)
10988 {
10989 const char *oldName, *newName;
10990
10991 if (argc != 3) {
10992 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
10993 return JIM_ERR;
10994 }
10995 oldName = Jim_GetString(argv[1], NULL);
10996 newName = Jim_GetString(argv[2], NULL);
10997 if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
10998 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10999 Jim_AppendStrings(interp, Jim_GetResult(interp),
11000 "can't rename \"", oldName, "\": ",
11001 "command doesn't exist", NULL);
11002 return JIM_ERR;
11003 }
11004 return JIM_OK;
11005 }
11006
11007 /* [dict] */
11008 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
11009 Jim_Obj *const *argv)
11010 {
11011 int option;
11012 const char *options[] = {
11013 "create", "get", "set", "unset", "exists", NULL
11014 };
11015 enum {
11016 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11017 };
11018
11019 if (argc < 2) {
11020 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11021 return JIM_ERR;
11022 }
11023
11024 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11025 JIM_ERRMSG) != JIM_OK)
11026 return JIM_ERR;
11027
11028 if (option == OPT_CREATE) {
11029 Jim_Obj *objPtr;
11030
11031 if (argc % 2) {
11032 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11033 return JIM_ERR;
11034 }
11035 objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11036 Jim_SetResult(interp, objPtr);
11037 return JIM_OK;
11038 } else if (option == OPT_GET) {
11039 Jim_Obj *objPtr;
11040
11041 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11042 JIM_ERRMSG) != JIM_OK)
11043 return JIM_ERR;
11044 Jim_SetResult(interp, objPtr);
11045 return JIM_OK;
11046 } else if (option == OPT_SET) {
11047 if (argc < 5) {
11048 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11049 return JIM_ERR;
11050 }
11051 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11052 argv[argc-1]);
11053 } else if (option == OPT_UNSET) {
11054 if (argc < 4) {
11055 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11056 return JIM_ERR;
11057 }
11058 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11059 NULL);
11060 } else if (option == OPT_EXIST) {
11061 Jim_Obj *objPtr;
11062 int exists;
11063
11064 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11065 JIM_ERRMSG) == JIM_OK)
11066 exists = 1;
11067 else
11068 exists = 0;
11069 Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11070 return JIM_OK;
11071 } else {
11072 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11073 Jim_AppendStrings(interp, Jim_GetResult(interp),
11074 "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11075 " must be create, get, set", NULL);
11076 return JIM_ERR;
11077 }
11078 return JIM_OK;
11079 }
11080
11081 /* [load] */
11082 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11083 Jim_Obj *const *argv)
11084 {
11085 if (argc < 2) {
11086 Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11087 return JIM_ERR;
11088 }
11089 return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11090 }
11091
11092 /* [subst] */
11093 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11094 Jim_Obj *const *argv)
11095 {
11096 int i, flags = 0;
11097 Jim_Obj *objPtr;
11098
11099 if (argc < 2) {
11100 Jim_WrongNumArgs(interp, 1, argv,
11101 "?-nobackslashes? ?-nocommands? ?-novariables? string");
11102 return JIM_ERR;
11103 }
11104 i = argc-2;
11105 while(i--) {
11106 if (Jim_CompareStringImmediate(interp, argv[i+1],
11107 "-nobackslashes"))
11108 flags |= JIM_SUBST_NOESC;
11109 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11110 "-novariables"))
11111 flags |= JIM_SUBST_NOVAR;
11112 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11113 "-nocommands"))
11114 flags |= JIM_SUBST_NOCMD;
11115 else {
11116 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11117 Jim_AppendStrings(interp, Jim_GetResult(interp),
11118 "bad option \"", Jim_GetString(argv[i+1], NULL),
11119 "\": must be -nobackslashes, -nocommands, or "
11120 "-novariables", NULL);
11121 return JIM_ERR;
11122 }
11123 }
11124 if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11125 return JIM_ERR;
11126 Jim_SetResult(interp, objPtr);
11127 return JIM_OK;
11128 }
11129
11130 /* [info] */
11131 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11132 Jim_Obj *const *argv)
11133 {
11134 int cmd, result = JIM_OK;
11135 static const char *commands[] = {
11136 "body", "commands", "exists", "globals", "level", "locals",
11137 "vars", "version", "complete", "args", NULL
11138 };
11139 enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11140 INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11141
11142 if (argc < 2) {
11143 Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11144 return JIM_ERR;
11145 }
11146 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11147 != JIM_OK) {
11148 return JIM_ERR;
11149 }
11150
11151 if (cmd == INFO_COMMANDS) {
11152 if (argc != 2 && argc != 3) {
11153 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11154 return JIM_ERR;
11155 }
11156 if (argc == 3)
11157 Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11158 else
11159 Jim_SetResult(interp, JimCommandsList(interp, NULL));
11160 } else if (cmd == INFO_EXISTS) {
11161 Jim_Obj *exists;
11162 if (argc != 3) {
11163 Jim_WrongNumArgs(interp, 2, argv, "varName");
11164 return JIM_ERR;
11165 }
11166 exists = Jim_GetVariable(interp, argv[2], 0);
11167 Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11168 } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11169 int mode;
11170 switch (cmd) {
11171 case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11172 case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
11173 case INFO_VARS: mode = JIM_VARLIST_VARS; break;
11174 default: mode = 0; /* avoid warning */; break;
11175 }
11176 if (argc != 2 && argc != 3) {
11177 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11178 return JIM_ERR;
11179 }
11180 if (argc == 3)
11181 Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11182 else
11183 Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11184 } else if (cmd == INFO_LEVEL) {
11185 Jim_Obj *objPtr;
11186 switch (argc) {
11187 case 2:
11188 Jim_SetResult(interp,
11189 Jim_NewIntObj(interp, interp->numLevels));
11190 break;
11191 case 3:
11192 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11193 return JIM_ERR;
11194 Jim_SetResult(interp, objPtr);
11195 break;
11196 default:
11197 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11198 return JIM_ERR;
11199 }
11200 } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11201 Jim_Cmd *cmdPtr;
11202
11203 if (argc != 3) {
11204 Jim_WrongNumArgs(interp, 2, argv, "procname");
11205 return JIM_ERR;
11206 }
11207 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11208 return JIM_ERR;
11209 if (cmdPtr->cmdProc != NULL) {
11210 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11211 Jim_AppendStrings(interp, Jim_GetResult(interp),
11212 "command \"", Jim_GetString(argv[2], NULL),
11213 "\" is not a procedure", NULL);
11214 return JIM_ERR;
11215 }
11216 if (cmd == INFO_BODY)
11217 Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11218 else
11219 Jim_SetResult(interp, cmdPtr->argListObjPtr);
11220 } else if (cmd == INFO_VERSION) {
11221 char buf[(JIM_INTEGER_SPACE * 2) + 1];
11222 sprintf(buf, "%d.%d",
11223 JIM_VERSION / 100, JIM_VERSION % 100);
11224 Jim_SetResultString(interp, buf, -1);
11225 } else if (cmd == INFO_COMPLETE) {
11226 const char *s;
11227 int len;
11228
11229 if (argc != 3) {
11230 Jim_WrongNumArgs(interp, 2, argv, "script");
11231 return JIM_ERR;
11232 }
11233 s = Jim_GetString(argv[2], &len);
11234 Jim_SetResult(interp,
11235 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11236 }
11237 return result;
11238 }
11239
11240 /* [split] */
11241 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11242 Jim_Obj *const *argv)
11243 {
11244 const char *str, *splitChars, *noMatchStart;
11245 int splitLen, strLen, i;
11246 Jim_Obj *resObjPtr;
11247
11248 if (argc != 2 && argc != 3) {
11249 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11250 return JIM_ERR;
11251 }
11252 /* Init */
11253 if (argc == 2) {
11254 splitChars = " \n\t\r";
11255 splitLen = 4;
11256 } else {
11257 splitChars = Jim_GetString(argv[2], &splitLen);
11258 }
11259 str = Jim_GetString(argv[1], &strLen);
11260 if (!strLen) return JIM_OK;
11261 noMatchStart = str;
11262 resObjPtr = Jim_NewListObj(interp, NULL, 0);
11263 /* Split */
11264 if (splitLen) {
11265 while (strLen) {
11266 for (i = 0; i < splitLen; i++) {
11267 if (*str == splitChars[i]) {
11268 Jim_Obj *objPtr;
11269
11270 objPtr = Jim_NewStringObj(interp, noMatchStart,
11271 (str-noMatchStart));
11272 Jim_ListAppendElement(interp, resObjPtr, objPtr);
11273 noMatchStart = str+1;
11274 break;
11275 }
11276 }
11277 str ++;
11278 strLen --;
11279 }
11280 Jim_ListAppendElement(interp, resObjPtr,
11281 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11282 } else {
11283 /* This handles the special case of splitchars eq {}. This
11284 * is trivial but we want to perform object sharing as Tcl does. */
11285 Jim_Obj *objCache[256];
11286 const unsigned char *u = (unsigned char*) str;
11287 memset(objCache, 0, sizeof(objCache));
11288 for (i = 0; i < strLen; i++) {
11289 int c = u[i];
11290
11291 if (objCache[c] == NULL)
11292 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11293 Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11294 }
11295 }
11296 Jim_SetResult(interp, resObjPtr);
11297 return JIM_OK;
11298 }
11299
11300 /* [join] */
11301 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11302 Jim_Obj *const *argv)
11303 {
11304 const char *joinStr;
11305 int joinStrLen, i, listLen;
11306 Jim_Obj *resObjPtr;
11307
11308 if (argc != 2 && argc != 3) {
11309 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11310 return JIM_ERR;
11311 }
11312 /* Init */
11313 if (argc == 2) {
11314 joinStr = " ";
11315 joinStrLen = 1;
11316 } else {
11317 joinStr = Jim_GetString(argv[2], &joinStrLen);
11318 }
11319 Jim_ListLength(interp, argv[1], &listLen);
11320 resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11321 /* Split */
11322 for (i = 0; i < listLen; i++) {
11323 Jim_Obj *objPtr;
11324
11325 Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11326 Jim_AppendObj(interp, resObjPtr, objPtr);
11327 if (i+1 != listLen) {
11328 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11329 }
11330 }
11331 Jim_SetResult(interp, resObjPtr);
11332 return JIM_OK;
11333 }
11334
11335 /* [format] */
11336 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11337 Jim_Obj *const *argv)
11338 {
11339 Jim_Obj *objPtr;
11340
11341 if (argc < 2) {
11342 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11343 return JIM_ERR;
11344 }
11345 objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11346 if (objPtr == NULL)
11347 return JIM_ERR;
11348 Jim_SetResult(interp, objPtr);
11349 return JIM_OK;
11350 }
11351
11352 /* [scan] */
11353 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11354 Jim_Obj *const *argv)
11355 {
11356 Jim_Obj *listPtr, **outVec;
11357 int outc, i, count = 0;
11358
11359 if (argc < 3) {
11360 Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11361 return JIM_ERR;
11362 }
11363 if (argv[2]->typePtr != &scanFmtStringObjType)
11364 SetScanFmtFromAny(interp, argv[2]);
11365 if (FormatGetError(argv[2]) != 0) {
11366 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11367 return JIM_ERR;
11368 }
11369 if (argc > 3) {
11370 int maxPos = FormatGetMaxPos(argv[2]);
11371 int count = FormatGetCnvCount(argv[2]);
11372 if (maxPos > argc-3) {
11373 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11374 return JIM_ERR;
11375 } else if (count != 0 && count < argc-3) {
11376 Jim_SetResultString(interp, "variable is not assigned by any "
11377 "conversion specifiers", -1);
11378 return JIM_ERR;
11379 } else if (count > argc-3) {
11380 Jim_SetResultString(interp, "different numbers of variable names and "
11381 "field specifiers", -1);
11382 return JIM_ERR;
11383 }
11384 }
11385 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11386 if (listPtr == 0)
11387 return JIM_ERR;
11388 if (argc > 3) {
11389 int len = 0;
11390 if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11391 Jim_ListLength(interp, listPtr, &len);
11392 if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11393 Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11394 return JIM_OK;
11395 }
11396 JimListGetElements(interp, listPtr, &outc, &outVec);
11397 for (i = 0; i < outc; ++i) {
11398 if (Jim_Length(outVec[i]) > 0) {
11399 ++count;
11400 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11401 goto err;
11402 }
11403 }
11404 Jim_FreeNewObj(interp, listPtr);
11405 Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11406 } else {
11407 if (listPtr == (Jim_Obj*)EOF) {
11408 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11409 return JIM_OK;
11410 }
11411 Jim_SetResult(interp, listPtr);
11412 }
11413 return JIM_OK;
11414 err:
11415 Jim_FreeNewObj(interp, listPtr);
11416 return JIM_ERR;
11417 }
11418
11419 /* [error] */
11420 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11421 Jim_Obj *const *argv)
11422 {
11423 if (argc != 2) {
11424 Jim_WrongNumArgs(interp, 1, argv, "message");
11425 return JIM_ERR;
11426 }
11427 Jim_SetResult(interp, argv[1]);
11428 return JIM_ERR;
11429 }
11430
11431 /* [lrange] */
11432 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11433 Jim_Obj *const *argv)
11434 {
11435 Jim_Obj *objPtr;
11436
11437 if (argc != 4) {
11438 Jim_WrongNumArgs(interp, 1, argv, "list first last");
11439 return JIM_ERR;
11440 }
11441 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11442 return JIM_ERR;
11443 Jim_SetResult(interp, objPtr);
11444 return JIM_OK;
11445 }
11446
11447 /* [env] */
11448 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11449 Jim_Obj *const *argv)
11450 {
11451 const char *key;
11452 char *val;
11453
11454 if (argc != 2) {
11455 Jim_WrongNumArgs(interp, 1, argv, "varName");
11456 return JIM_ERR;
11457 }
11458 key = Jim_GetString(argv[1], NULL);
11459 val = getenv(key);
11460 if (val == NULL) {
11461 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11462 Jim_AppendStrings(interp, Jim_GetResult(interp),
11463 "environment variable \"",
11464 key, "\" does not exist", NULL);
11465 return JIM_ERR;
11466 }
11467 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11468 return JIM_OK;
11469 }
11470
11471 /* [source] */
11472 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11473 Jim_Obj *const *argv)
11474 {
11475 int retval;
11476
11477 if (argc != 2) {
11478 Jim_WrongNumArgs(interp, 1, argv, "fileName");
11479 return JIM_ERR;
11480 }
11481 retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11482 if (retval == JIM_RETURN)
11483 return JIM_OK;
11484 return retval;
11485 }
11486
11487 /* [lreverse] */
11488 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11489 Jim_Obj *const *argv)
11490 {
11491 Jim_Obj *revObjPtr, **ele;
11492 int len;
11493
11494 if (argc != 2) {
11495 Jim_WrongNumArgs(interp, 1, argv, "list");
11496 return JIM_ERR;
11497 }
11498 JimListGetElements(interp, argv[1], &len, &ele);
11499 len--;
11500 revObjPtr = Jim_NewListObj(interp, NULL, 0);
11501 while (len >= 0)
11502 ListAppendElement(revObjPtr, ele[len--]);
11503 Jim_SetResult(interp, revObjPtr);
11504 return JIM_OK;
11505 }
11506
11507 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11508 {
11509 jim_wide len;
11510
11511 if (step == 0) return -1;
11512 if (start == end) return 0;
11513 else if (step > 0 && start > end) return -1;
11514 else if (step < 0 && end > start) return -1;
11515 len = end-start;
11516 if (len < 0) len = -len; /* abs(len) */
11517 if (step < 0) step = -step; /* abs(step) */
11518 len = 1 + ((len-1)/step);
11519 /* We can truncate safely to INT_MAX, the range command
11520 * will always return an error for a such long range
11521 * because Tcl lists can't be so long. */
11522 if (len > INT_MAX) len = INT_MAX;
11523 return (int)((len < 0) ? -1 : len);
11524 }
11525
11526 /* [range] */
11527 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11528 Jim_Obj *const *argv)
11529 {
11530 jim_wide start = 0, end, step = 1;
11531 int len, i;
11532 Jim_Obj *objPtr;
11533
11534 if (argc < 2 || argc > 4) {
11535 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11536 return JIM_ERR;
11537 }
11538 if (argc == 2) {
11539 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11540 return JIM_ERR;
11541 } else {
11542 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11543 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11544 return JIM_ERR;
11545 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11546 return JIM_ERR;
11547 }
11548 if ((len = JimRangeLen(start, end, step)) == -1) {
11549 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11550 return JIM_ERR;
11551 }
11552 objPtr = Jim_NewListObj(interp, NULL, 0);
11553 for (i = 0; i < len; i++)
11554 ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11555 Jim_SetResult(interp, objPtr);
11556 return JIM_OK;
11557 }
11558
11559 /* [rand] */
11560 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11561 Jim_Obj *const *argv)
11562 {
11563 jim_wide min = 0, max, len, maxMul;
11564
11565 if (argc < 1 || argc > 3) {
11566 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11567 return JIM_ERR;
11568 }
11569 if (argc == 1) {
11570 max = JIM_WIDE_MAX;
11571 } else if (argc == 2) {
11572 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11573 return JIM_ERR;
11574 } else if (argc == 3) {
11575 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11576 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11577 return JIM_ERR;
11578 }
11579 len = max-min;
11580 if (len < 0) {
11581 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11582 return JIM_ERR;
11583 }
11584 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11585 while (1) {
11586 jim_wide r;
11587
11588 JimRandomBytes(interp, &r, sizeof(jim_wide));
11589 if (r < 0 || r >= maxMul) continue;
11590 r = (len == 0) ? 0 : r%len;
11591 Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11592 return JIM_OK;
11593 }
11594 }
11595
11596 /* [package] */
11597 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
11598 Jim_Obj *const *argv)
11599 {
11600 int option;
11601 const char *options[] = {
11602 "require", "provide", NULL
11603 };
11604 enum {OPT_REQUIRE, OPT_PROVIDE};
11605
11606 if (argc < 2) {
11607 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11608 return JIM_ERR;
11609 }
11610 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11611 JIM_ERRMSG) != JIM_OK)
11612 return JIM_ERR;
11613
11614 if (option == OPT_REQUIRE) {
11615 int exact = 0;
11616 const char *ver;
11617
11618 if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11619 exact = 1;
11620 argv++;
11621 argc--;
11622 }
11623 if (argc != 3 && argc != 4) {
11624 Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11625 return JIM_ERR;
11626 }
11627 ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11628 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11629 JIM_ERRMSG);
11630 if (ver == NULL)
11631 return JIM_ERR;
11632 Jim_SetResultString(interp, ver, -1);
11633 } else if (option == OPT_PROVIDE) {
11634 if (argc != 4) {
11635 Jim_WrongNumArgs(interp, 2, argv, "package version");
11636 return JIM_ERR;
11637 }
11638 return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11639 Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11640 }
11641 return JIM_OK;
11642 }
11643
11644 static struct {
11645 const char *name;
11646 Jim_CmdProc cmdProc;
11647 } Jim_CoreCommandsTable[] = {
11648 {"set", Jim_SetCoreCommand},
11649 {"unset", Jim_UnsetCoreCommand},
11650 {"puts", Jim_PutsCoreCommand},
11651 {"+", Jim_AddCoreCommand},
11652 {"*", Jim_MulCoreCommand},
11653 {"-", Jim_SubCoreCommand},
11654 {"/", Jim_DivCoreCommand},
11655 {"incr", Jim_IncrCoreCommand},
11656 {"while", Jim_WhileCoreCommand},
11657 {"for", Jim_ForCoreCommand},
11658 {"foreach", Jim_ForeachCoreCommand},
11659 {"lmap", Jim_LmapCoreCommand},
11660 {"if", Jim_IfCoreCommand},
11661 {"switch", Jim_SwitchCoreCommand},
11662 {"list", Jim_ListCoreCommand},
11663 {"lindex", Jim_LindexCoreCommand},
11664 {"lset", Jim_LsetCoreCommand},
11665 {"llength", Jim_LlengthCoreCommand},
11666 {"lappend", Jim_LappendCoreCommand},
11667 {"linsert", Jim_LinsertCoreCommand},
11668 {"lsort", Jim_LsortCoreCommand},
11669 {"append", Jim_AppendCoreCommand},
11670 {"debug", Jim_DebugCoreCommand},
11671 {"eval", Jim_EvalCoreCommand},
11672 {"uplevel", Jim_UplevelCoreCommand},
11673 {"expr", Jim_ExprCoreCommand},
11674 {"break", Jim_BreakCoreCommand},
11675 {"continue", Jim_ContinueCoreCommand},
11676 {"proc", Jim_ProcCoreCommand},
11677 {"concat", Jim_ConcatCoreCommand},
11678 {"return", Jim_ReturnCoreCommand},
11679 {"upvar", Jim_UpvarCoreCommand},
11680 {"global", Jim_GlobalCoreCommand},
11681 {"string", Jim_StringCoreCommand},
11682 {"time", Jim_TimeCoreCommand},
11683 {"exit", Jim_ExitCoreCommand},
11684 {"catch", Jim_CatchCoreCommand},
11685 {"ref", Jim_RefCoreCommand},
11686 {"getref", Jim_GetrefCoreCommand},
11687 {"setref", Jim_SetrefCoreCommand},
11688 {"finalize", Jim_FinalizeCoreCommand},
11689 {"collect", Jim_CollectCoreCommand},
11690 {"rename", Jim_RenameCoreCommand},
11691 {"dict", Jim_DictCoreCommand},
11692 {"load", Jim_LoadCoreCommand},
11693 {"subst", Jim_SubstCoreCommand},
11694 {"info", Jim_InfoCoreCommand},
11695 {"split", Jim_SplitCoreCommand},
11696 {"join", Jim_JoinCoreCommand},
11697 {"format", Jim_FormatCoreCommand},
11698 {"scan", Jim_ScanCoreCommand},
11699 {"error", Jim_ErrorCoreCommand},
11700 {"lrange", Jim_LrangeCoreCommand},
11701 {"env", Jim_EnvCoreCommand},
11702 {"source", Jim_SourceCoreCommand},
11703 {"lreverse", Jim_LreverseCoreCommand},
11704 {"range", Jim_RangeCoreCommand},
11705 {"rand", Jim_RandCoreCommand},
11706 {"package", Jim_PackageCoreCommand},
11707 {"tailcall", Jim_TailcallCoreCommand},
11708 {NULL, NULL},
11709 };
11710
11711 /* Some Jim core command is actually a procedure written in Jim itself. */
11712 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11713 {
11714 Jim_Eval(interp, (char*)
11715 "proc lambda {arglist args} {\n"
11716 " set name [ref {} function lambdaFinalizer]\n"
11717 " uplevel 1 [list proc $name $arglist {expand}$args]\n"
11718 " return $name\n"
11719 "}\n"
11720 "proc lambdaFinalizer {name val} {\n"
11721 " rename $name {}\n"
11722 "}\n"
11723 );
11724 }
11725
11726 void Jim_RegisterCoreCommands(Jim_Interp *interp)
11727 {
11728 int i = 0;
11729
11730 while(Jim_CoreCommandsTable[i].name != NULL) {
11731 Jim_CreateCommand(interp,
11732 Jim_CoreCommandsTable[i].name,
11733 Jim_CoreCommandsTable[i].cmdProc,
11734 NULL, NULL);
11735 i++;
11736 }
11737 Jim_RegisterCoreProcedures(interp);
11738 }
11739
11740 /* -----------------------------------------------------------------------------
11741 * Interactive prompt
11742 * ---------------------------------------------------------------------------*/
11743 void Jim_PrintErrorMessage(Jim_Interp *interp)
11744 {
11745 int len, i;
11746
11747 fprintf(interp->stderr_, "Runtime error, file \"%s\", line %d:" JIM_NL,
11748 interp->errorFileName, interp->errorLine);
11749 fprintf(interp->stderr_, " %s" JIM_NL,
11750 Jim_GetString(interp->result, NULL));
11751 Jim_ListLength(interp, interp->stackTrace, &len);
11752 for (i = 0; i < len; i+= 3) {
11753 Jim_Obj *objPtr;
11754 const char *proc, *file, *line;
11755
11756 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
11757 proc = Jim_GetString(objPtr, NULL);
11758 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
11759 JIM_NONE);
11760 file = Jim_GetString(objPtr, NULL);
11761 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
11762 JIM_NONE);
11763 line = Jim_GetString(objPtr, NULL);
11764 fprintf(interp->stderr_,
11765 "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
11766 proc, file, line);
11767 }
11768 }
11769
11770 int Jim_InteractivePrompt(Jim_Interp *interp)
11771 {
11772 int retcode = JIM_OK;
11773 Jim_Obj *scriptObjPtr;
11774
11775 fprintf(interp->stdout_, "Welcome to Jim version %d.%d, "
11776 "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
11777 JIM_VERSION / 100, JIM_VERSION % 100);
11778 Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
11779 while (1) {
11780 char buf[1024];
11781 const char *result;
11782 const char *retcodestr[] = {
11783 "ok", "error", "return", "break", "continue", "eval", "exit"
11784 };
11785 int reslen;
11786
11787 if (retcode != 0) {
11788 if (retcode >= 2 && retcode <= 6)
11789 fprintf(interp->stdout_, "[%s] . ", retcodestr[retcode]);
11790 else
11791 fprintf(interp->stdout_, "[%d] . ", retcode);
11792 } else
11793 fprintf(interp->stdout_, ". ");
11794 fflush(interp->stdout_);
11795 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
11796 Jim_IncrRefCount(scriptObjPtr);
11797 while(1) {
11798 const char *str;
11799 char state;
11800 int len;
11801
11802 if (fgets(buf, 1024, interp->stdin_) == NULL) {
11803 Jim_DecrRefCount(interp, scriptObjPtr);
11804 goto out;
11805 }
11806 Jim_AppendString(interp, scriptObjPtr, buf, -1);
11807 str = Jim_GetString(scriptObjPtr, &len);
11808 if (Jim_ScriptIsComplete(str, len, &state))
11809 break;
11810 fprintf(interp->stdout_, "%c> ", state);
11811 fflush(stdout);
11812 }
11813 retcode = Jim_EvalObj(interp, scriptObjPtr);
11814 Jim_DecrRefCount(interp, scriptObjPtr);
11815 result = Jim_GetString(Jim_GetResult(interp), &reslen);
11816 if (retcode == JIM_ERR) {
11817 Jim_PrintErrorMessage(interp);
11818 } else if (retcode == JIM_EXIT) {
11819 exit(Jim_GetExitCode(interp));
11820 } else {
11821 if (reslen) {
11822 fwrite(result, 1, reslen, interp->stdout_);
11823 fprintf(interp->stdout_, JIM_NL);
11824 }
11825 }
11826 }
11827 out:
11828 return 0;
11829 }

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)