- fixed build issues with win32
[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 "replacements.h"
42
43 /* Include the platform dependent libraries for
44 * dynamic loading of libraries. */
45 #ifdef JIM_DYNLIB
46 #if defined(_WIN32) || defined(WIN32)
47 #ifndef WIN32
48 #define WIN32 1
49 #endif
50 #ifndef STRICT
51 #define STRICT
52 #endif
53 #define WIN32_LEAN_AND_MEAN
54 #include <windows.h>
55 #if _MSC_VER >= 1000
56 #pragma warning(disable:4146)
57 #endif /* _MSC_VER */
58 #else
59 #include <dlfcn.h>
60 #endif /* WIN32 */
61 #endif /* JIM_DYNLIB */
62
63 #ifdef __ECOS
64 #include <cyg/jimtcl/jim.h>
65 #else
66 #include "jim.h"
67 #endif
68
69 #ifdef HAVE_BACKTRACE
70 #include <execinfo.h>
71 #endif
72
73 /* -----------------------------------------------------------------------------
74 * Global variables
75 * ---------------------------------------------------------------------------*/
76
77 /* A shared empty string for the objects string representation.
78 * Jim_InvalidateStringRep knows about it and don't try to free. */
79 static char *JimEmptyStringRep = (char*) "";
80
81 /* -----------------------------------------------------------------------------
82 * Required prototypes of not exported functions
83 * ---------------------------------------------------------------------------*/
84 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
85 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
86 static void JimRegisterCoreApi(Jim_Interp *interp);
87
88 static Jim_HashTableType JimVariablesHashTableType;
89
90 /* -----------------------------------------------------------------------------
91 * Utility functions
92 * ---------------------------------------------------------------------------*/
93
94 /*
95 * Convert a string to a jim_wide INTEGER.
96 * This function originates from BSD.
97 *
98 * Ignores `locale' stuff. Assumes that the upper and lower case
99 * alphabets and digits are each contiguous.
100 */
101 #ifdef HAVE_LONG_LONG
102 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
103 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
104 {
105 register const char *s;
106 register unsigned jim_wide acc;
107 register unsigned char c;
108 register unsigned jim_wide qbase, cutoff;
109 register int neg, any, cutlim;
110
111 /*
112 * Skip white space and pick up leading +/- sign if any.
113 * If base is 0, allow 0x for hex and 0 for octal, else
114 * assume decimal; if base is already 16, allow 0x.
115 */
116 s = nptr;
117 do {
118 c = *s++;
119 } while (isspace(c));
120 if (c == '-') {
121 neg = 1;
122 c = *s++;
123 } else {
124 neg = 0;
125 if (c == '+')
126 c = *s++;
127 }
128 if ((base == 0 || base == 16) &&
129 c == '0' && (*s == 'x' || *s == 'X')) {
130 c = s[1];
131 s += 2;
132 base = 16;
133 }
134 if (base == 0)
135 base = c == '0' ? 8 : 10;
136
137 /*
138 * Compute the cutoff value between legal numbers and illegal
139 * numbers. That is the largest legal value, divided by the
140 * base. An input number that is greater than this value, if
141 * followed by a legal input character, is too big. One that
142 * is equal to this value may be valid or not; the limit
143 * between valid and invalid numbers is then based on the last
144 * digit. For instance, if the range for quads is
145 * [-9223372036854775808..9223372036854775807] and the input base
146 * is 10, cutoff will be set to 922337203685477580 and cutlim to
147 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
148 * accumulated a value > 922337203685477580, or equal but the
149 * next digit is > 7 (or 8), the number is too big, and we will
150 * return a range error.
151 *
152 * Set any if any `digits' consumed; make it negative to indicate
153 * overflow.
154 */
155 qbase = (unsigned)base;
156 cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
157 : LLONG_MAX;
158 cutlim = (int)(cutoff % qbase);
159 cutoff /= qbase;
160 for (acc = 0, any = 0;; c = *s++) {
161 if (!JimIsAscii(c))
162 break;
163 if (isdigit(c))
164 c -= '0';
165 else if (isalpha(c))
166 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
167 else
168 break;
169 if (c >= base)
170 break;
171 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
172 any = -1;
173 else {
174 any = 1;
175 acc *= qbase;
176 acc += c;
177 }
178 }
179 if (any < 0) {
180 acc = neg ? LLONG_MIN : LLONG_MAX;
181 errno = ERANGE;
182 } else if (neg)
183 acc = -acc;
184 if (endptr != 0)
185 *endptr = (char *)(any ? s - 1 : nptr);
186 return (acc);
187 }
188 #endif
189
190 /* Glob-style pattern matching. */
191 static int JimStringMatch(const char *pattern, int patternLen,
192 const char *string, int stringLen, int nocase)
193 {
194 while(patternLen) {
195 switch(pattern[0]) {
196 case '*':
197 while (pattern[1] == '*') {
198 pattern++;
199 patternLen--;
200 }
201 if (patternLen == 1)
202 return 1; /* match */
203 while(stringLen) {
204 if (JimStringMatch(pattern+1, patternLen-1,
205 string, stringLen, nocase))
206 return 1; /* match */
207 string++;
208 stringLen--;
209 }
210 return 0; /* no match */
211 break;
212 case '?':
213 if (stringLen == 0)
214 return 0; /* no match */
215 string++;
216 stringLen--;
217 break;
218 case '[':
219 {
220 int not, match;
221
222 pattern++;
223 patternLen--;
224 not = pattern[0] == '^';
225 if (not) {
226 pattern++;
227 patternLen--;
228 }
229 match = 0;
230 while(1) {
231 if (pattern[0] == '\\') {
232 pattern++;
233 patternLen--;
234 if (pattern[0] == string[0])
235 match = 1;
236 } else if (pattern[0] == ']') {
237 break;
238 } else if (patternLen == 0) {
239 pattern--;
240 patternLen++;
241 break;
242 } else if (pattern[1] == '-' && patternLen >= 3) {
243 int start = pattern[0];
244 int end = pattern[2];
245 int c = string[0];
246 if (start > end) {
247 int t = start;
248 start = end;
249 end = t;
250 }
251 if (nocase) {
252 start = tolower(start);
253 end = tolower(end);
254 c = tolower(c);
255 }
256 pattern += 2;
257 patternLen -= 2;
258 if (c >= start && c <= end)
259 match = 1;
260 } else {
261 if (!nocase) {
262 if (pattern[0] == string[0])
263 match = 1;
264 } else {
265 if (tolower((int)pattern[0]) == tolower((int)string[0]))
266 match = 1;
267 }
268 }
269 pattern++;
270 patternLen--;
271 }
272 if (not)
273 match = !match;
274 if (!match)
275 return 0; /* no match */
276 string++;
277 stringLen--;
278 break;
279 }
280 case '\\':
281 if (patternLen >= 2) {
282 pattern++;
283 patternLen--;
284 }
285 /* fall through */
286 default:
287 if (!nocase) {
288 if (pattern[0] != string[0])
289 return 0; /* no match */
290 } else {
291 if (tolower((int)pattern[0]) != tolower((int)string[0]))
292 return 0; /* no match */
293 }
294 string++;
295 stringLen--;
296 break;
297 }
298 pattern++;
299 patternLen--;
300 if (stringLen == 0) {
301 while(*pattern == '*') {
302 pattern++;
303 patternLen--;
304 }
305 break;
306 }
307 }
308 if (patternLen == 0 && stringLen == 0)
309 return 1;
310 return 0;
311 }
312
313 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
314 int nocase)
315 {
316 unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
317
318 if (nocase == 0) {
319 while(l1 && l2) {
320 if (*u1 != *u2)
321 return (int)*u1-*u2;
322 u1++; u2++; l1--; l2--;
323 }
324 if (!l1 && !l2) return 0;
325 return l1-l2;
326 } else {
327 while(l1 && l2) {
328 if (tolower((int)*u1) != tolower((int)*u2))
329 return tolower((int)*u1)-tolower((int)*u2);
330 u1++; u2++; l1--; l2--;
331 }
332 if (!l1 && !l2) return 0;
333 return l1-l2;
334 }
335 }
336
337 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
338 * The index of the first occurrence of s1 in s2 is returned.
339 * If s1 is not found inside s2, -1 is returned. */
340 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
341 {
342 int i;
343
344 if (!l1 || !l2 || l1 > l2) return -1;
345 if (index < 0) index = 0;
346 s2 += index;
347 for (i = index; i <= l2-l1; i++) {
348 if (memcmp(s2, s1, l1) == 0)
349 return i;
350 s2++;
351 }
352 return -1;
353 }
354
355 int Jim_WideToString(char *buf, jim_wide wideValue)
356 {
357 const char *fmt = "%" JIM_WIDE_MODIFIER;
358 return sprintf(buf, fmt, wideValue);
359 }
360
361 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
362 {
363 char *endptr;
364
365 #ifdef HAVE_LONG_LONG
366 *widePtr = JimStrtoll(str, &endptr, base);
367 #else
368 *widePtr = strtol(str, &endptr, base);
369 #endif
370 if ((str[0] == '\0') || (str == endptr) )
371 return JIM_ERR;
372 if (endptr[0] != '\0') {
373 while(*endptr) {
374 if (!isspace((int)*endptr))
375 return JIM_ERR;
376 endptr++;
377 }
378 }
379 return JIM_OK;
380 }
381
382 int Jim_StringToIndex(const char *str, int *intPtr)
383 {
384 char *endptr;
385
386 *intPtr = strtol(str, &endptr, 10);
387 if ( (str[0] == '\0') || (str == endptr) )
388 return JIM_ERR;
389 if (endptr[0] != '\0') {
390 while(*endptr) {
391 if (!isspace((int)*endptr))
392 return JIM_ERR;
393 endptr++;
394 }
395 }
396 return JIM_OK;
397 }
398
399 /* The string representation of references has two features in order
400 * to make the GC faster. The first is that every reference starts
401 * with a non common character '~', in order to make the string matching
402 * fater. The second is that the reference string rep his 32 characters
403 * in length, this allows to avoid to check every object with a string
404 * repr < 32, and usually there are many of this objects. */
405
406 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
407
408 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
409 {
410 const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
411 sprintf(buf, fmt, refPtr->tag, id);
412 return JIM_REFERENCE_SPACE;
413 }
414
415 int Jim_DoubleToString(char *buf, double doubleValue)
416 {
417 char *s;
418 int len;
419
420 len = sprintf(buf, "%.17g", doubleValue);
421 s = buf;
422 while(*s) {
423 if (*s == '.') return len;
424 s++;
425 }
426 /* Add a final ".0" if it's a number. But not
427 * for NaN or InF */
428 if (isdigit((int)buf[0])
429 || ((buf[0] == '-' || buf[0] == '+')
430 && isdigit((int)buf[1]))) {
431 s[0] = '.';
432 s[1] = '0';
433 s[2] = '\0';
434 return len+2;
435 }
436 return len;
437 }
438
439 int Jim_StringToDouble(const char *str, double *doublePtr)
440 {
441 char *endptr;
442
443 *doublePtr = strtod(str, &endptr);
444 if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
445 return JIM_ERR;
446 return JIM_OK;
447 }
448
449 static jim_wide JimPowWide(jim_wide b, jim_wide e)
450 {
451 jim_wide i, res = 1;
452 if ((b==0 && e!=0) || (e<0)) return 0;
453 for(i=0; i<e; i++) {res *= b;}
454 return res;
455 }
456
457 /* -----------------------------------------------------------------------------
458 * Special functions
459 * ---------------------------------------------------------------------------*/
460
461 /* Note that 'interp' may be NULL if not available in the
462 * context of the panic. It's only useful to get the error
463 * file descriptor, it will default to stderr otherwise. */
464 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
465 {
466 va_list ap;
467
468 va_start(ap, fmt);
469 /*
470 * Send it here first.. Assuming STDIO still works
471 */
472 fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
473 vfprintf(stderr, fmt, ap);
474 fprintf(stderr, JIM_NL JIM_NL);
475 va_end(ap);
476
477 #ifdef HAVE_BACKTRACE
478 {
479 void *array[40];
480 int size, i;
481 char **strings;
482
483 size = backtrace(array, 40);
484 strings = backtrace_symbols(array, size);
485 for (i = 0; i < size; i++)
486 fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
487 fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
488 fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
489 }
490 #endif
491
492 /* This may actually crash... we do it last */
493 if( interp && interp->cookie_stderr ){
494 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
495 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
496 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL JIM_NL );
497 }
498 abort();
499 }
500
501 /* -----------------------------------------------------------------------------
502 * Memory allocation
503 * ---------------------------------------------------------------------------*/
504
505 /* Macro used for memory debugging.
506 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
507 * and similary for Jim_Realloc and Jim_Free */
508 #if 0
509 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
510 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
511 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
512 #endif
513
514 void *Jim_Alloc(int size)
515 {
516 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
517 if (size==0)
518 size=1;
519 void *p = malloc(size);
520 if (p == NULL)
521 Jim_Panic(NULL,"malloc: Out of memory");
522 return p;
523 }
524
525 void Jim_Free(void *ptr) {
526 free(ptr);
527 }
528
529 void *Jim_Realloc(void *ptr, int size)
530 {
531 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
532 if (size==0)
533 size=1;
534 void *p = realloc(ptr, size);
535 if (p == NULL)
536 Jim_Panic(NULL,"realloc: Out of memory");
537 return p;
538 }
539
540 char *Jim_StrDup(const char *s)
541 {
542 int l = strlen(s);
543 char *copy = Jim_Alloc(l+1);
544
545 memcpy(copy, s, l+1);
546 return copy;
547 }
548
549 char *Jim_StrDupLen(const char *s, int l)
550 {
551 char *copy = Jim_Alloc(l+1);
552
553 memcpy(copy, s, l+1);
554 copy[l] = 0; /* Just to be sure, original could be substring */
555 return copy;
556 }
557
558 /* -----------------------------------------------------------------------------
559 * Time related functions
560 * ---------------------------------------------------------------------------*/
561 /* Returns microseconds of CPU used since start. */
562 static jim_wide JimClock(void)
563 {
564 #if (defined WIN32) && !(defined JIM_ANSIC)
565 LARGE_INTEGER t, f;
566 QueryPerformanceFrequency(&f);
567 QueryPerformanceCounter(&t);
568 return (long)((t.QuadPart * 1000000) / f.QuadPart);
569 #else /* !WIN32 */
570 clock_t clocks = clock();
571
572 return (long)(clocks*(1000000/CLOCKS_PER_SEC));
573 #endif /* WIN32 */
574 }
575
576 /* -----------------------------------------------------------------------------
577 * Hash Tables
578 * ---------------------------------------------------------------------------*/
579
580 /* -------------------------- private prototypes ---------------------------- */
581 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
582 static unsigned int JimHashTableNextPower(unsigned int size);
583 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
584
585 /* -------------------------- hash functions -------------------------------- */
586
587 /* Thomas Wang's 32 bit Mix Function */
588 unsigned int Jim_IntHashFunction(unsigned int key)
589 {
590 key += ~(key << 15);
591 key ^= (key >> 10);
592 key += (key << 3);
593 key ^= (key >> 6);
594 key += ~(key << 11);
595 key ^= (key >> 16);
596 return key;
597 }
598
599 /* Identity hash function for integer keys */
600 unsigned int Jim_IdentityHashFunction(unsigned int key)
601 {
602 return key;
603 }
604
605 /* Generic hash function (we are using to multiply by 9 and add the byte
606 * as Tcl) */
607 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
608 {
609 unsigned int h = 0;
610 while(len--)
611 h += (h<<3)+*buf++;
612 return h;
613 }
614
615 /* ----------------------------- API implementation ------------------------- */
616 /* reset an hashtable already initialized with ht_init().
617 * NOTE: This function should only called by ht_destroy(). */
618 static void JimResetHashTable(Jim_HashTable *ht)
619 {
620 ht->table = NULL;
621 ht->size = 0;
622 ht->sizemask = 0;
623 ht->used = 0;
624 ht->collisions = 0;
625 }
626
627 /* Initialize the hash table */
628 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
629 void *privDataPtr)
630 {
631 JimResetHashTable(ht);
632 ht->type = type;
633 ht->privdata = privDataPtr;
634 return JIM_OK;
635 }
636
637 /* Resize the table to the minimal size that contains all the elements,
638 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
639 int Jim_ResizeHashTable(Jim_HashTable *ht)
640 {
641 int minimal = ht->used;
642
643 if (minimal < JIM_HT_INITIAL_SIZE)
644 minimal = JIM_HT_INITIAL_SIZE;
645 return Jim_ExpandHashTable(ht, minimal);
646 }
647
648 /* Expand or create the hashtable */
649 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
650 {
651 Jim_HashTable n; /* the new hashtable */
652 unsigned int realsize = JimHashTableNextPower(size), i;
653
654 /* the size is invalid if it is smaller than the number of
655 * elements already inside the hashtable */
656 if (ht->used >= size)
657 return JIM_ERR;
658
659 Jim_InitHashTable(&n, ht->type, ht->privdata);
660 n.size = realsize;
661 n.sizemask = realsize-1;
662 n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
663
664 /* Initialize all the pointers to NULL */
665 memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
666
667 /* Copy all the elements from the old to the new table:
668 * note that if the old hash table is empty ht->size is zero,
669 * so Jim_ExpandHashTable just creates an hash table. */
670 n.used = ht->used;
671 for (i = 0; i < ht->size && ht->used > 0; i++) {
672 Jim_HashEntry *he, *nextHe;
673
674 if (ht->table[i] == NULL) continue;
675
676 /* For each hash entry on this slot... */
677 he = ht->table[i];
678 while(he) {
679 unsigned int h;
680
681 nextHe = he->next;
682 /* Get the new element index */
683 h = Jim_HashKey(ht, he->key) & n.sizemask;
684 he->next = n.table[h];
685 n.table[h] = he;
686 ht->used--;
687 /* Pass to the next element */
688 he = nextHe;
689 }
690 }
691 assert(ht->used == 0);
692 Jim_Free(ht->table);
693
694 /* Remap the new hashtable in the old */
695 *ht = n;
696 return JIM_OK;
697 }
698
699 /* Add an element to the target hash table */
700 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
701 {
702 int index;
703 Jim_HashEntry *entry;
704
705 /* Get the index of the new element, or -1 if
706 * the element already exists. */
707 if ((index = JimInsertHashEntry(ht, key)) == -1)
708 return JIM_ERR;
709
710 /* Allocates the memory and stores key */
711 entry = Jim_Alloc(sizeof(*entry));
712 entry->next = ht->table[index];
713 ht->table[index] = entry;
714
715 /* Set the hash entry fields. */
716 Jim_SetHashKey(ht, entry, key);
717 Jim_SetHashVal(ht, entry, val);
718 ht->used++;
719 return JIM_OK;
720 }
721
722 /* Add an element, discarding the old if the key already exists */
723 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
724 {
725 Jim_HashEntry *entry;
726
727 /* Try to add the element. If the key
728 * does not exists Jim_AddHashEntry will suceed. */
729 if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
730 return JIM_OK;
731 /* It already exists, get the entry */
732 entry = Jim_FindHashEntry(ht, key);
733 /* Free the old value and set the new one */
734 Jim_FreeEntryVal(ht, entry);
735 Jim_SetHashVal(ht, entry, val);
736 return JIM_OK;
737 }
738
739 /* Search and remove an element */
740 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
741 {
742 unsigned int h;
743 Jim_HashEntry *he, *prevHe;
744
745 if (ht->size == 0)
746 return JIM_ERR;
747 h = Jim_HashKey(ht, key) & ht->sizemask;
748 he = ht->table[h];
749
750 prevHe = NULL;
751 while(he) {
752 if (Jim_CompareHashKeys(ht, key, he->key)) {
753 /* Unlink the element from the list */
754 if (prevHe)
755 prevHe->next = he->next;
756 else
757 ht->table[h] = he->next;
758 Jim_FreeEntryKey(ht, he);
759 Jim_FreeEntryVal(ht, he);
760 Jim_Free(he);
761 ht->used--;
762 return JIM_OK;
763 }
764 prevHe = he;
765 he = he->next;
766 }
767 return JIM_ERR; /* not found */
768 }
769
770 /* Destroy an entire hash table */
771 int Jim_FreeHashTable(Jim_HashTable *ht)
772 {
773 unsigned int i;
774
775 /* Free all the elements */
776 for (i = 0; i < ht->size && ht->used > 0; i++) {
777 Jim_HashEntry *he, *nextHe;
778
779 if ((he = ht->table[i]) == NULL) continue;
780 while(he) {
781 nextHe = he->next;
782 Jim_FreeEntryKey(ht, he);
783 Jim_FreeEntryVal(ht, he);
784 Jim_Free(he);
785 ht->used--;
786 he = nextHe;
787 }
788 }
789 /* Free the table and the allocated cache structure */
790 Jim_Free(ht->table);
791 /* Re-initialize the table */
792 JimResetHashTable(ht);
793 return JIM_OK; /* never fails */
794 }
795
796 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
797 {
798 Jim_HashEntry *he;
799 unsigned int h;
800
801 if (ht->size == 0) return NULL;
802 h = Jim_HashKey(ht, key) & ht->sizemask;
803 he = ht->table[h];
804 while(he) {
805 if (Jim_CompareHashKeys(ht, key, he->key))
806 return he;
807 he = he->next;
808 }
809 return NULL;
810 }
811
812 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
813 {
814 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
815
816 iter->ht = ht;
817 iter->index = -1;
818 iter->entry = NULL;
819 iter->nextEntry = NULL;
820 return iter;
821 }
822
823 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
824 {
825 while (1) {
826 if (iter->entry == NULL) {
827 iter->index++;
828 if (iter->index >=
829 (signed)iter->ht->size) break;
830 iter->entry = iter->ht->table[iter->index];
831 } else {
832 iter->entry = iter->nextEntry;
833 }
834 if (iter->entry) {
835 /* We need to save the 'next' here, the iterator user
836 * may delete the entry we are returning. */
837 iter->nextEntry = iter->entry->next;
838 return iter->entry;
839 }
840 }
841 return NULL;
842 }
843
844 /* ------------------------- private functions ------------------------------ */
845
846 /* Expand the hash table if needed */
847 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
848 {
849 /* If the hash table is empty expand it to the intial size,
850 * if the table is "full" dobule its size. */
851 if (ht->size == 0)
852 return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
853 if (ht->size == ht->used)
854 return Jim_ExpandHashTable(ht, ht->size*2);
855 return JIM_OK;
856 }
857
858 /* Our hash table capability is a power of two */
859 static unsigned int JimHashTableNextPower(unsigned int size)
860 {
861 unsigned int i = JIM_HT_INITIAL_SIZE;
862
863 if (size >= 2147483648U)
864 return 2147483648U;
865 while(1) {
866 if (i >= size)
867 return i;
868 i *= 2;
869 }
870 }
871
872 /* Returns the index of a free slot that can be populated with
873 * an hash entry for the given 'key'.
874 * If the key already exists, -1 is returned. */
875 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
876 {
877 unsigned int h;
878 Jim_HashEntry *he;
879
880 /* Expand the hashtable if needed */
881 if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
882 return -1;
883 /* Compute the key hash value */
884 h = Jim_HashKey(ht, key) & ht->sizemask;
885 /* Search if this slot does not already contain the given key */
886 he = ht->table[h];
887 while(he) {
888 if (Jim_CompareHashKeys(ht, key, he->key))
889 return -1;
890 he = he->next;
891 }
892 return h;
893 }
894
895 /* ----------------------- StringCopy Hash Table Type ------------------------*/
896
897 static unsigned int JimStringCopyHTHashFunction(const void *key)
898 {
899 return Jim_GenHashFunction(key, strlen(key));
900 }
901
902 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
903 {
904 int len = strlen(key);
905 char *copy = Jim_Alloc(len+1);
906 JIM_NOTUSED(privdata);
907
908 memcpy(copy, key, len);
909 copy[len] = '\0';
910 return copy;
911 }
912
913 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
914 {
915 int len = strlen(val);
916 char *copy = Jim_Alloc(len+1);
917 JIM_NOTUSED(privdata);
918
919 memcpy(copy, val, len);
920 copy[len] = '\0';
921 return copy;
922 }
923
924 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
925 const void *key2)
926 {
927 JIM_NOTUSED(privdata);
928
929 return strcmp(key1, key2) == 0;
930 }
931
932 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
933 {
934 JIM_NOTUSED(privdata);
935
936 Jim_Free((void*)key); /* ATTENTION: const cast */
937 }
938
939 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
940 {
941 JIM_NOTUSED(privdata);
942
943 Jim_Free((void*)val); /* ATTENTION: const cast */
944 }
945
946 static Jim_HashTableType JimStringCopyHashTableType = {
947 JimStringCopyHTHashFunction, /* hash function */
948 JimStringCopyHTKeyDup, /* key dup */
949 NULL, /* val dup */
950 JimStringCopyHTKeyCompare, /* key compare */
951 JimStringCopyHTKeyDestructor, /* key destructor */
952 NULL /* val destructor */
953 };
954
955 /* This is like StringCopy but does not auto-duplicate the key.
956 * It's used for intepreter's shared strings. */
957 static Jim_HashTableType JimSharedStringsHashTableType = {
958 JimStringCopyHTHashFunction, /* hash function */
959 NULL, /* key dup */
960 NULL, /* val dup */
961 JimStringCopyHTKeyCompare, /* key compare */
962 JimStringCopyHTKeyDestructor, /* key destructor */
963 NULL /* val destructor */
964 };
965
966 /* This is like StringCopy but also automatically handle dynamic
967 * allocated C strings as values. */
968 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
969 JimStringCopyHTHashFunction, /* hash function */
970 JimStringCopyHTKeyDup, /* key dup */
971 JimStringKeyValCopyHTValDup, /* val dup */
972 JimStringCopyHTKeyCompare, /* key compare */
973 JimStringCopyHTKeyDestructor, /* key destructor */
974 JimStringKeyValCopyHTValDestructor, /* val destructor */
975 };
976
977 typedef struct AssocDataValue {
978 Jim_InterpDeleteProc *delProc;
979 void *data;
980 } AssocDataValue;
981
982 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
983 {
984 AssocDataValue *assocPtr = (AssocDataValue *)data;
985 if (assocPtr->delProc != NULL)
986 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
987 Jim_Free(data);
988 }
989
990 static Jim_HashTableType JimAssocDataHashTableType = {
991 JimStringCopyHTHashFunction, /* hash function */
992 JimStringCopyHTKeyDup, /* key dup */
993 NULL, /* val dup */
994 JimStringCopyHTKeyCompare, /* key compare */
995 JimStringCopyHTKeyDestructor, /* key destructor */
996 JimAssocDataHashTableValueDestructor /* val destructor */
997 };
998
999 /* -----------------------------------------------------------------------------
1000 * Stack - This is a simple generic stack implementation. It is used for
1001 * example in the 'expr' expression compiler.
1002 * ---------------------------------------------------------------------------*/
1003 void Jim_InitStack(Jim_Stack *stack)
1004 {
1005 stack->len = 0;
1006 stack->maxlen = 0;
1007 stack->vector = NULL;
1008 }
1009
1010 void Jim_FreeStack(Jim_Stack *stack)
1011 {
1012 Jim_Free(stack->vector);
1013 }
1014
1015 int Jim_StackLen(Jim_Stack *stack)
1016 {
1017 return stack->len;
1018 }
1019
1020 void Jim_StackPush(Jim_Stack *stack, void *element) {
1021 int neededLen = stack->len+1;
1022 if (neededLen > stack->maxlen) {
1023 stack->maxlen = neededLen*2;
1024 stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1025 }
1026 stack->vector[stack->len] = element;
1027 stack->len++;
1028 }
1029
1030 void *Jim_StackPop(Jim_Stack *stack)
1031 {
1032 if (stack->len == 0) return NULL;
1033 stack->len--;
1034 return stack->vector[stack->len];
1035 }
1036
1037 void *Jim_StackPeek(Jim_Stack *stack)
1038 {
1039 if (stack->len == 0) return NULL;
1040 return stack->vector[stack->len-1];
1041 }
1042
1043 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1044 {
1045 int i;
1046
1047 for (i = 0; i < stack->len; i++)
1048 freeFunc(stack->vector[i]);
1049 }
1050
1051 /* -----------------------------------------------------------------------------
1052 * Parser
1053 * ---------------------------------------------------------------------------*/
1054
1055 /* Token types */
1056 #define JIM_TT_NONE -1 /* No token returned */
1057 #define JIM_TT_STR 0 /* simple string */
1058 #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
1059 #define JIM_TT_VAR 2 /* var substitution */
1060 #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
1061 #define JIM_TT_CMD 4 /* command substitution */
1062 #define JIM_TT_SEP 5 /* word separator */
1063 #define JIM_TT_EOL 6 /* line separator */
1064
1065 /* Additional token types needed for expressions */
1066 #define JIM_TT_SUBEXPR_START 7
1067 #define JIM_TT_SUBEXPR_END 8
1068 #define JIM_TT_EXPR_NUMBER 9
1069 #define JIM_TT_EXPR_OPERATOR 10
1070
1071 /* Parser states */
1072 #define JIM_PS_DEF 0 /* Default state */
1073 #define JIM_PS_QUOTE 1 /* Inside "" */
1074
1075 /* Parser context structure. The same context is used both to parse
1076 * Tcl scripts and lists. */
1077 struct JimParserCtx {
1078 const char *prg; /* Program text */
1079 const char *p; /* Pointer to the point of the program we are parsing */
1080 int len; /* Left length of 'prg' */
1081 int linenr; /* Current line number */
1082 const char *tstart;
1083 const char *tend; /* Returned token is at tstart-tend in 'prg'. */
1084 int tline; /* Line number of the returned token */
1085 int tt; /* Token type */
1086 int eof; /* Non zero if EOF condition is true. */
1087 int state; /* Parser state */
1088 int comment; /* Non zero if the next chars may be a comment. */
1089 };
1090
1091 #define JimParserEof(c) ((c)->eof)
1092 #define JimParserTstart(c) ((c)->tstart)
1093 #define JimParserTend(c) ((c)->tend)
1094 #define JimParserTtype(c) ((c)->tt)
1095 #define JimParserTline(c) ((c)->tline)
1096
1097 static int JimParseScript(struct JimParserCtx *pc);
1098 static int JimParseSep(struct JimParserCtx *pc);
1099 static int JimParseEol(struct JimParserCtx *pc);
1100 static int JimParseCmd(struct JimParserCtx *pc);
1101 static int JimParseVar(struct JimParserCtx *pc);
1102 static int JimParseBrace(struct JimParserCtx *pc);
1103 static int JimParseStr(struct JimParserCtx *pc);
1104 static int JimParseComment(struct JimParserCtx *pc);
1105 static char *JimParserGetToken(struct JimParserCtx *pc,
1106 int *lenPtr, int *typePtr, int *linePtr);
1107
1108 /* Initialize a parser context.
1109 * 'prg' is a pointer to the program text, linenr is the line
1110 * number of the first line contained in the program. */
1111 void JimParserInit(struct JimParserCtx *pc, const char *prg,
1112 int len, int linenr)
1113 {
1114 pc->prg = prg;
1115 pc->p = prg;
1116 pc->len = len;
1117 pc->tstart = NULL;
1118 pc->tend = NULL;
1119 pc->tline = 0;
1120 pc->tt = JIM_TT_NONE;
1121 pc->eof = 0;
1122 pc->state = JIM_PS_DEF;
1123 pc->linenr = linenr;
1124 pc->comment = 1;
1125 }
1126
1127 int JimParseScript(struct JimParserCtx *pc)
1128 {
1129 while(1) { /* the while is used to reiterate with continue if needed */
1130 if (!pc->len) {
1131 pc->tstart = pc->p;
1132 pc->tend = pc->p-1;
1133 pc->tline = pc->linenr;
1134 pc->tt = JIM_TT_EOL;
1135 pc->eof = 1;
1136 return JIM_OK;
1137 }
1138 switch(*(pc->p)) {
1139 case '\\':
1140 if (*(pc->p+1) == '\n')
1141 return JimParseSep(pc);
1142 else {
1143 pc->comment = 0;
1144 return JimParseStr(pc);
1145 }
1146 break;
1147 case ' ':
1148 case '\t':
1149 case '\r':
1150 if (pc->state == JIM_PS_DEF)
1151 return JimParseSep(pc);
1152 else {
1153 pc->comment = 0;
1154 return JimParseStr(pc);
1155 }
1156 break;
1157 case '\n':
1158 case ';':
1159 pc->comment = 1;
1160 if (pc->state == JIM_PS_DEF)
1161 return JimParseEol(pc);
1162 else
1163 return JimParseStr(pc);
1164 break;
1165 case '[':
1166 pc->comment = 0;
1167 return JimParseCmd(pc);
1168 break;
1169 case '$':
1170 pc->comment = 0;
1171 if (JimParseVar(pc) == JIM_ERR) {
1172 pc->tstart = pc->tend = pc->p++; pc->len--;
1173 pc->tline = pc->linenr;
1174 pc->tt = JIM_TT_STR;
1175 return JIM_OK;
1176 } else
1177 return JIM_OK;
1178 break;
1179 case '#':
1180 if (pc->comment) {
1181 JimParseComment(pc);
1182 continue;
1183 } else {
1184 return JimParseStr(pc);
1185 }
1186 default:
1187 pc->comment = 0;
1188 return JimParseStr(pc);
1189 break;
1190 }
1191 return JIM_OK;
1192 }
1193 }
1194
1195 int JimParseSep(struct JimParserCtx *pc)
1196 {
1197 pc->tstart = pc->p;
1198 pc->tline = pc->linenr;
1199 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1200 (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1201 if (*pc->p == '\\') {
1202 pc->p++; pc->len--;
1203 pc->linenr++;
1204 }
1205 pc->p++; pc->len--;
1206 }
1207 pc->tend = pc->p-1;
1208 pc->tt = JIM_TT_SEP;
1209 return JIM_OK;
1210 }
1211
1212 int JimParseEol(struct JimParserCtx *pc)
1213 {
1214 pc->tstart = pc->p;
1215 pc->tline = pc->linenr;
1216 while (*pc->p == ' ' || *pc->p == '\n' ||
1217 *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1218 if (*pc->p == '\n')
1219 pc->linenr++;
1220 pc->p++; pc->len--;
1221 }
1222 pc->tend = pc->p-1;
1223 pc->tt = JIM_TT_EOL;
1224 return JIM_OK;
1225 }
1226
1227 /* Todo. Don't stop if ']' appears inside {} or quoted.
1228 * Also should handle the case of puts [string length "]"] */
1229 int JimParseCmd(struct JimParserCtx *pc)
1230 {
1231 int level = 1;
1232 int blevel = 0;
1233
1234 pc->tstart = ++pc->p; pc->len--;
1235 pc->tline = pc->linenr;
1236 while (1) {
1237 if (pc->len == 0) {
1238 break;
1239 } else if (*pc->p == '[' && blevel == 0) {
1240 level++;
1241 } else if (*pc->p == ']' && blevel == 0) {
1242 level--;
1243 if (!level) break;
1244 } else if (*pc->p == '\\') {
1245 pc->p++; pc->len--;
1246 } else if (*pc->p == '{') {
1247 blevel++;
1248 } else if (*pc->p == '}') {
1249 if (blevel != 0)
1250 blevel--;
1251 } else if (*pc->p == '\n')
1252 pc->linenr++;
1253 pc->p++; pc->len--;
1254 }
1255 pc->tend = pc->p-1;
1256 pc->tt = JIM_TT_CMD;
1257 if (*pc->p == ']') {
1258 pc->p++; pc->len--;
1259 }
1260 return JIM_OK;
1261 }
1262
1263 int JimParseVar(struct JimParserCtx *pc)
1264 {
1265 int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1266
1267 pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1268 pc->tline = pc->linenr;
1269 if (*pc->p == '{') {
1270 pc->tstart = ++pc->p; pc->len--;
1271 brace = 1;
1272 }
1273 if (brace) {
1274 while (!stop) {
1275 if (*pc->p == '}' || pc->len == 0) {
1276 stop = 1;
1277 if (pc->len == 0)
1278 continue;
1279 }
1280 else if (*pc->p == '\n')
1281 pc->linenr++;
1282 pc->p++; pc->len--;
1283 }
1284 if (pc->len == 0)
1285 pc->tend = pc->p-1;
1286 else
1287 pc->tend = pc->p-2;
1288 } else {
1289 while (!stop) {
1290 if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1291 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1292 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1293 stop = 1;
1294 else {
1295 pc->p++; pc->len--;
1296 }
1297 }
1298 /* Parse [dict get] syntax sugar. */
1299 if (*pc->p == '(') {
1300 while (*pc->p != ')' && pc->len) {
1301 pc->p++; pc->len--;
1302 if (*pc->p == '\\' && pc->len >= 2) {
1303 pc->p += 2; pc->len -= 2;
1304 }
1305 }
1306 if (*pc->p != '\0') {
1307 pc->p++; pc->len--;
1308 }
1309 ttype = JIM_TT_DICTSUGAR;
1310 }
1311 pc->tend = pc->p-1;
1312 }
1313 /* Check if we parsed just the '$' character.
1314 * That's not a variable so an error is returned
1315 * to tell the state machine to consider this '$' just
1316 * a string. */
1317 if (pc->tstart == pc->p) {
1318 pc->p--; pc->len++;
1319 return JIM_ERR;
1320 }
1321 pc->tt = ttype;
1322 return JIM_OK;
1323 }
1324
1325 int JimParseBrace(struct JimParserCtx *pc)
1326 {
1327 int level = 1;
1328
1329 pc->tstart = ++pc->p; pc->len--;
1330 pc->tline = pc->linenr;
1331 while (1) {
1332 if (*pc->p == '\\' && pc->len >= 2) {
1333 pc->p++; pc->len--;
1334 if (*pc->p == '\n')
1335 pc->linenr++;
1336 } else if (*pc->p == '{') {
1337 level++;
1338 } else if (pc->len == 0 || *pc->p == '}') {
1339 level--;
1340 if (pc->len == 0 || level == 0) {
1341 pc->tend = pc->p-1;
1342 if (pc->len != 0) {
1343 pc->p++; pc->len--;
1344 }
1345 pc->tt = JIM_TT_STR;
1346 return JIM_OK;
1347 }
1348 } else if (*pc->p == '\n') {
1349 pc->linenr++;
1350 }
1351 pc->p++; pc->len--;
1352 }
1353 return JIM_OK; /* unreached */
1354 }
1355
1356 int JimParseStr(struct JimParserCtx *pc)
1357 {
1358 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1359 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1360 if (newword && *pc->p == '{') {
1361 return JimParseBrace(pc);
1362 } else if (newword && *pc->p == '"') {
1363 pc->state = JIM_PS_QUOTE;
1364 pc->p++; pc->len--;
1365 }
1366 pc->tstart = pc->p;
1367 pc->tline = pc->linenr;
1368 while (1) {
1369 if (pc->len == 0) {
1370 pc->tend = pc->p-1;
1371 pc->tt = JIM_TT_ESC;
1372 return JIM_OK;
1373 }
1374 switch(*pc->p) {
1375 case '\\':
1376 if (pc->state == JIM_PS_DEF &&
1377 *(pc->p+1) == '\n') {
1378 pc->tend = pc->p-1;
1379 pc->tt = JIM_TT_ESC;
1380 return JIM_OK;
1381 }
1382 if (pc->len >= 2) {
1383 pc->p++; pc->len--;
1384 }
1385 break;
1386 case '$':
1387 case '[':
1388 pc->tend = pc->p-1;
1389 pc->tt = JIM_TT_ESC;
1390 return JIM_OK;
1391 case ' ':
1392 case '\t':
1393 case '\n':
1394 case '\r':
1395 case ';':
1396 if (pc->state == JIM_PS_DEF) {
1397 pc->tend = pc->p-1;
1398 pc->tt = JIM_TT_ESC;
1399 return JIM_OK;
1400 } else if (*pc->p == '\n') {
1401 pc->linenr++;
1402 }
1403 break;
1404 case '"':
1405 if (pc->state == JIM_PS_QUOTE) {
1406 pc->tend = pc->p-1;
1407 pc->tt = JIM_TT_ESC;
1408 pc->p++; pc->len--;
1409 pc->state = JIM_PS_DEF;
1410 return JIM_OK;
1411 }
1412 break;
1413 }
1414 pc->p++; pc->len--;
1415 }
1416 return JIM_OK; /* unreached */
1417 }
1418
1419 int JimParseComment(struct JimParserCtx *pc)
1420 {
1421 while (*pc->p) {
1422 if (*pc->p == '\n') {
1423 pc->linenr++;
1424 if (*(pc->p-1) != '\\') {
1425 pc->p++; pc->len--;
1426 return JIM_OK;
1427 }
1428 }
1429 pc->p++; pc->len--;
1430 }
1431 return JIM_OK;
1432 }
1433
1434 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1435 static int xdigitval(int c)
1436 {
1437 if (c >= '0' && c <= '9') return c-'0';
1438 if (c >= 'a' && c <= 'f') return c-'a'+10;
1439 if (c >= 'A' && c <= 'F') return c-'A'+10;
1440 return -1;
1441 }
1442
1443 static int odigitval(int c)
1444 {
1445 if (c >= '0' && c <= '7') return c-'0';
1446 return -1;
1447 }
1448
1449 /* Perform Tcl escape substitution of 's', storing the result
1450 * string into 'dest'. The escaped string is guaranteed to
1451 * be the same length or shorted than the source string.
1452 * Slen is the length of the string at 's', if it's -1 the string
1453 * length will be calculated by the function.
1454 *
1455 * The function returns the length of the resulting string. */
1456 static int JimEscape(char *dest, const char *s, int slen)
1457 {
1458 char *p = dest;
1459 int i, len;
1460
1461 if (slen == -1)
1462 slen = strlen(s);
1463
1464 for (i = 0; i < slen; i++) {
1465 switch(s[i]) {
1466 case '\\':
1467 switch(s[i+1]) {
1468 case 'a': *p++ = 0x7; i++; break;
1469 case 'b': *p++ = 0x8; i++; break;
1470 case 'f': *p++ = 0xc; i++; break;
1471 case 'n': *p++ = 0xa; i++; break;
1472 case 'r': *p++ = 0xd; i++; break;
1473 case 't': *p++ = 0x9; i++; break;
1474 case 'v': *p++ = 0xb; i++; break;
1475 case '\0': *p++ = '\\'; i++; break;
1476 case '\n': *p++ = ' '; i++; break;
1477 default:
1478 if (s[i+1] == 'x') {
1479 int val = 0;
1480 int c = xdigitval(s[i+2]);
1481 if (c == -1) {
1482 *p++ = 'x';
1483 i++;
1484 break;
1485 }
1486 val = c;
1487 c = xdigitval(s[i+3]);
1488 if (c == -1) {
1489 *p++ = val;
1490 i += 2;
1491 break;
1492 }
1493 val = (val*16)+c;
1494 *p++ = val;
1495 i += 3;
1496 break;
1497 } else if (s[i+1] >= '0' && s[i+1] <= '7')
1498 {
1499 int val = 0;
1500 int c = odigitval(s[i+1]);
1501 val = c;
1502 c = odigitval(s[i+2]);
1503 if (c == -1) {
1504 *p++ = val;
1505 i ++;
1506 break;
1507 }
1508 val = (val*8)+c;
1509 c = odigitval(s[i+3]);
1510 if (c == -1) {
1511 *p++ = val;
1512 i += 2;
1513 break;
1514 }
1515 val = (val*8)+c;
1516 *p++ = val;
1517 i += 3;
1518 } else {
1519 *p++ = s[i+1];
1520 i++;
1521 }
1522 break;
1523 }
1524 break;
1525 default:
1526 *p++ = s[i];
1527 break;
1528 }
1529 }
1530 len = p-dest;
1531 *p++ = '\0';
1532 return len;
1533 }
1534
1535 /* Returns a dynamically allocated copy of the current token in the
1536 * parser context. The function perform conversion of escapes if
1537 * the token is of type JIM_TT_ESC.
1538 *
1539 * Note that after the conversion, tokens that are grouped with
1540 * braces in the source code, are always recognizable from the
1541 * identical string obtained in a different way from the type.
1542 *
1543 * For exmple the string:
1544 *
1545 * {expand}$a
1546 *
1547 * will return as first token "expand", of type JIM_TT_STR
1548 *
1549 * While the string:
1550 *
1551 * expand$a
1552 *
1553 * will return as first token "expand", of type JIM_TT_ESC
1554 */
1555 char *JimParserGetToken(struct JimParserCtx *pc,
1556 int *lenPtr, int *typePtr, int *linePtr)
1557 {
1558 const char *start, *end;
1559 char *token;
1560 int len;
1561
1562 start = JimParserTstart(pc);
1563 end = JimParserTend(pc);
1564 if (start > end) {
1565 if (lenPtr) *lenPtr = 0;
1566 if (typePtr) *typePtr = JimParserTtype(pc);
1567 if (linePtr) *linePtr = JimParserTline(pc);
1568 token = Jim_Alloc(1);
1569 token[0] = '\0';
1570 return token;
1571 }
1572 len = (end-start)+1;
1573 token = Jim_Alloc(len+1);
1574 if (JimParserTtype(pc) != JIM_TT_ESC) {
1575 /* No escape conversion needed? Just copy it. */
1576 memcpy(token, start, len);
1577 token[len] = '\0';
1578 } else {
1579 /* Else convert the escape chars. */
1580 len = JimEscape(token, start, len);
1581 }
1582 if (lenPtr) *lenPtr = len;
1583 if (typePtr) *typePtr = JimParserTtype(pc);
1584 if (linePtr) *linePtr = JimParserTline(pc);
1585 return token;
1586 }
1587
1588 /* The following functin is not really part of the parsing engine of Jim,
1589 * but it somewhat related. Given an string and its length, it tries
1590 * to guess if the script is complete or there are instead " " or { }
1591 * open and not completed. This is useful for interactive shells
1592 * implementation and for [info complete].
1593 *
1594 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1595 * '{' on scripts incomplete missing one or more '}' to be balanced.
1596 * '"' on scripts incomplete missing a '"' char.
1597 *
1598 * If the script is complete, 1 is returned, otherwise 0. */
1599 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1600 {
1601 int level = 0;
1602 int state = ' ';
1603
1604 while(len) {
1605 switch (*s) {
1606 case '\\':
1607 if (len > 1)
1608 s++;
1609 break;
1610 case '"':
1611 if (state == ' ') {
1612 state = '"';
1613 } else if (state == '"') {
1614 state = ' ';
1615 }
1616 break;
1617 case '{':
1618 if (state == '{') {
1619 level++;
1620 } else if (state == ' ') {
1621 state = '{';
1622 level++;
1623 }
1624 break;
1625 case '}':
1626 if (state == '{') {
1627 level--;
1628 if (level == 0)
1629 state = ' ';
1630 }
1631 break;
1632 }
1633 s++;
1634 len--;
1635 }
1636 if (stateCharPtr)
1637 *stateCharPtr = state;
1638 return state == ' ';
1639 }
1640
1641 /* -----------------------------------------------------------------------------
1642 * Tcl Lists parsing
1643 * ---------------------------------------------------------------------------*/
1644 static int JimParseListSep(struct JimParserCtx *pc);
1645 static int JimParseListStr(struct JimParserCtx *pc);
1646
1647 int JimParseList(struct JimParserCtx *pc)
1648 {
1649 if (pc->len == 0) {
1650 pc->tstart = pc->tend = pc->p;
1651 pc->tline = pc->linenr;
1652 pc->tt = JIM_TT_EOL;
1653 pc->eof = 1;
1654 return JIM_OK;
1655 }
1656 switch(*pc->p) {
1657 case ' ':
1658 case '\n':
1659 case '\t':
1660 case '\r':
1661 if (pc->state == JIM_PS_DEF)
1662 return JimParseListSep(pc);
1663 else
1664 return JimParseListStr(pc);
1665 break;
1666 default:
1667 return JimParseListStr(pc);
1668 break;
1669 }
1670 return JIM_OK;
1671 }
1672
1673 int JimParseListSep(struct JimParserCtx *pc)
1674 {
1675 pc->tstart = pc->p;
1676 pc->tline = pc->linenr;
1677 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1678 {
1679 pc->p++; pc->len--;
1680 }
1681 pc->tend = pc->p-1;
1682 pc->tt = JIM_TT_SEP;
1683 return JIM_OK;
1684 }
1685
1686 int JimParseListStr(struct JimParserCtx *pc)
1687 {
1688 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1689 pc->tt == JIM_TT_NONE);
1690 if (newword && *pc->p == '{') {
1691 return JimParseBrace(pc);
1692 } else if (newword && *pc->p == '"') {
1693 pc->state = JIM_PS_QUOTE;
1694 pc->p++; pc->len--;
1695 }
1696 pc->tstart = pc->p;
1697 pc->tline = pc->linenr;
1698 while (1) {
1699 if (pc->len == 0) {
1700 pc->tend = pc->p-1;
1701 pc->tt = JIM_TT_ESC;
1702 return JIM_OK;
1703 }
1704 switch(*pc->p) {
1705 case '\\':
1706 pc->p++; pc->len--;
1707 break;
1708 case ' ':
1709 case '\t':
1710 case '\n':
1711 case '\r':
1712 if (pc->state == JIM_PS_DEF) {
1713 pc->tend = pc->p-1;
1714 pc->tt = JIM_TT_ESC;
1715 return JIM_OK;
1716 } else if (*pc->p == '\n') {
1717 pc->linenr++;
1718 }
1719 break;
1720 case '"':
1721 if (pc->state == JIM_PS_QUOTE) {
1722 pc->tend = pc->p-1;
1723 pc->tt = JIM_TT_ESC;
1724 pc->p++; pc->len--;
1725 pc->state = JIM_PS_DEF;
1726 return JIM_OK;
1727 }
1728 break;
1729 }
1730 pc->p++; pc->len--;
1731 }
1732 return JIM_OK; /* unreached */
1733 }
1734
1735 /* -----------------------------------------------------------------------------
1736 * Jim_Obj related functions
1737 * ---------------------------------------------------------------------------*/
1738
1739 /* Return a new initialized object. */
1740 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1741 {
1742 Jim_Obj *objPtr;
1743
1744 /* -- Check if there are objects in the free list -- */
1745 if (interp->freeList != NULL) {
1746 /* -- Unlink the object from the free list -- */
1747 objPtr = interp->freeList;
1748 interp->freeList = objPtr->nextObjPtr;
1749 } else {
1750 /* -- No ready to use objects: allocate a new one -- */
1751 objPtr = Jim_Alloc(sizeof(*objPtr));
1752 }
1753
1754 /* Object is returned with refCount of 0. Every
1755 * kind of GC implemented should take care to don't try
1756 * to scan objects with refCount == 0. */
1757 objPtr->refCount = 0;
1758 /* All the other fields are left not initialized to save time.
1759 * The caller will probably want set they to the right
1760 * value anyway. */
1761
1762 /* -- Put the object into the live list -- */
1763 objPtr->prevObjPtr = NULL;
1764 objPtr->nextObjPtr = interp->liveList;
1765 if (interp->liveList)
1766 interp->liveList->prevObjPtr = objPtr;
1767 interp->liveList = objPtr;
1768
1769 return objPtr;
1770 }
1771
1772 /* Free an object. Actually objects are never freed, but
1773 * just moved to the free objects list, where they will be
1774 * reused by Jim_NewObj(). */
1775 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1776 {
1777 /* Check if the object was already freed, panic. */
1778 if (objPtr->refCount != 0) {
1779 Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1780 objPtr->refCount);
1781 }
1782 /* Free the internal representation */
1783 Jim_FreeIntRep(interp, objPtr);
1784 /* Free the string representation */
1785 if (objPtr->bytes != NULL) {
1786 if (objPtr->bytes != JimEmptyStringRep)
1787 Jim_Free(objPtr->bytes);
1788 }
1789 /* Unlink the object from the live objects list */
1790 if (objPtr->prevObjPtr)
1791 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1792 if (objPtr->nextObjPtr)
1793 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1794 if (interp->liveList == objPtr)
1795 interp->liveList = objPtr->nextObjPtr;
1796 /* Link the object into the free objects list */
1797 objPtr->prevObjPtr = NULL;
1798 objPtr->nextObjPtr = interp->freeList;
1799 if (interp->freeList)
1800 interp->freeList->prevObjPtr = objPtr;
1801 interp->freeList = objPtr;
1802 objPtr->refCount = -1;
1803 }
1804
1805 /* Invalidate the string representation of an object. */
1806 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1807 {
1808 if (objPtr->bytes != NULL) {
1809 if (objPtr->bytes != JimEmptyStringRep)
1810 Jim_Free(objPtr->bytes);
1811 }
1812 objPtr->bytes = NULL;
1813 }
1814
1815 #define Jim_SetStringRep(o, b, l) \
1816 do { (o)->bytes = b; (o)->length = l; } while (0)
1817
1818 /* Set the initial string representation for an object.
1819 * Does not try to free an old one. */
1820 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1821 {
1822 if (length == 0) {
1823 objPtr->bytes = JimEmptyStringRep;
1824 objPtr->length = 0;
1825 } else {
1826 objPtr->bytes = Jim_Alloc(length+1);
1827 objPtr->length = length;
1828 memcpy(objPtr->bytes, bytes, length);
1829 objPtr->bytes[length] = '\0';
1830 }
1831 }
1832
1833 /* Duplicate an object. The returned object has refcount = 0. */
1834 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1835 {
1836 Jim_Obj *dupPtr;
1837
1838 dupPtr = Jim_NewObj(interp);
1839 if (objPtr->bytes == NULL) {
1840 /* Object does not have a valid string representation. */
1841 dupPtr->bytes = NULL;
1842 } else {
1843 Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1844 }
1845 if (objPtr->typePtr != NULL) {
1846 if (objPtr->typePtr->dupIntRepProc == NULL) {
1847 dupPtr->internalRep = objPtr->internalRep;
1848 } else {
1849 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1850 }
1851 dupPtr->typePtr = objPtr->typePtr;
1852 } else {
1853 dupPtr->typePtr = NULL;
1854 }
1855 return dupPtr;
1856 }
1857
1858 /* Return the string representation for objPtr. If the object
1859 * string representation is invalid, calls the method to create
1860 * a new one starting from the internal representation of the object. */
1861 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1862 {
1863 if (objPtr->bytes == NULL) {
1864 /* Invalid string repr. Generate it. */
1865 if (objPtr->typePtr->updateStringProc == NULL) {
1866 Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1867 objPtr->typePtr->name);
1868 }
1869 objPtr->typePtr->updateStringProc(objPtr);
1870 }
1871 if (lenPtr)
1872 *lenPtr = objPtr->length;
1873 return objPtr->bytes;
1874 }
1875
1876 /* Just returns the length of the object's string rep */
1877 int Jim_Length(Jim_Obj *objPtr)
1878 {
1879 int len;
1880
1881 Jim_GetString(objPtr, &len);
1882 return len;
1883 }
1884
1885 /* -----------------------------------------------------------------------------
1886 * String Object
1887 * ---------------------------------------------------------------------------*/
1888 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1889 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1890
1891 static Jim_ObjType stringObjType = {
1892 "string",
1893 NULL,
1894 DupStringInternalRep,
1895 NULL,
1896 JIM_TYPE_REFERENCES,
1897 };
1898
1899 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1900 {
1901 JIM_NOTUSED(interp);
1902
1903 /* This is a bit subtle: the only caller of this function
1904 * should be Jim_DuplicateObj(), that will copy the
1905 * string representaion. After the copy, the duplicated
1906 * object will not have more room in teh buffer than
1907 * srcPtr->length bytes. So we just set it to length. */
1908 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1909 }
1910
1911 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1912 {
1913 /* Get a fresh string representation. */
1914 (void) Jim_GetString(objPtr, NULL);
1915 /* Free any other internal representation. */
1916 Jim_FreeIntRep(interp, objPtr);
1917 /* Set it as string, i.e. just set the maxLength field. */
1918 objPtr->typePtr = &stringObjType;
1919 objPtr->internalRep.strValue.maxLength = objPtr->length;
1920 return JIM_OK;
1921 }
1922
1923 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1924 {
1925 Jim_Obj *objPtr = Jim_NewObj(interp);
1926
1927 if (len == -1)
1928 len = strlen(s);
1929 /* Alloc/Set the string rep. */
1930 if (len == 0) {
1931 objPtr->bytes = JimEmptyStringRep;
1932 objPtr->length = 0;
1933 } else {
1934 objPtr->bytes = Jim_Alloc(len+1);
1935 objPtr->length = len;
1936 memcpy(objPtr->bytes, s, len);
1937 objPtr->bytes[len] = '\0';
1938 }
1939
1940 /* No typePtr field for the vanilla string object. */
1941 objPtr->typePtr = NULL;
1942 return objPtr;
1943 }
1944
1945 /* This version does not try to duplicate the 's' pointer, but
1946 * use it directly. */
1947 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
1948 {
1949 Jim_Obj *objPtr = Jim_NewObj(interp);
1950
1951 if (len == -1)
1952 len = strlen(s);
1953 Jim_SetStringRep(objPtr, s, len);
1954 objPtr->typePtr = NULL;
1955 return objPtr;
1956 }
1957
1958 /* Low-level string append. Use it only against objects
1959 * of type "string". */
1960 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
1961 {
1962 int needlen;
1963
1964 if (len == -1)
1965 len = strlen(str);
1966 needlen = objPtr->length + len;
1967 if (objPtr->internalRep.strValue.maxLength < needlen ||
1968 objPtr->internalRep.strValue.maxLength == 0) {
1969 if (objPtr->bytes == JimEmptyStringRep) {
1970 objPtr->bytes = Jim_Alloc((needlen*2)+1);
1971 } else {
1972 objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
1973 }
1974 objPtr->internalRep.strValue.maxLength = needlen*2;
1975 }
1976 memcpy(objPtr->bytes + objPtr->length, str, len);
1977 objPtr->bytes[objPtr->length+len] = '\0';
1978 objPtr->length += len;
1979 }
1980
1981 /* Low-level wrapper to append an object. */
1982 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
1983 {
1984 int len;
1985 const char *str;
1986
1987 str = Jim_GetString(appendObjPtr, &len);
1988 StringAppendString(objPtr, str, len);
1989 }
1990
1991 /* Higher level API to append strings to objects. */
1992 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
1993 int len)
1994 {
1995 if (Jim_IsShared(objPtr))
1996 Jim_Panic(interp,"Jim_AppendString called with shared object");
1997 if (objPtr->typePtr != &stringObjType)
1998 SetStringFromAny(interp, objPtr);
1999 StringAppendString(objPtr, str, len);
2000 }
2001
2002 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
2003 Jim_Obj *appendObjPtr)
2004 {
2005 int len;
2006 const char *str;
2007
2008 str = Jim_GetString(appendObjPtr, &len);
2009 Jim_AppendString(interp, objPtr, str, len);
2010 }
2011
2012 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2013 {
2014 va_list ap;
2015
2016 if (objPtr->typePtr != &stringObjType)
2017 SetStringFromAny(interp, objPtr);
2018 va_start(ap, objPtr);
2019 while (1) {
2020 char *s = va_arg(ap, char*);
2021
2022 if (s == NULL) break;
2023 Jim_AppendString(interp, objPtr, s, -1);
2024 }
2025 va_end(ap);
2026 }
2027
2028 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2029 {
2030 const char *aStr, *bStr;
2031 int aLen, bLen, i;
2032
2033 if (aObjPtr == bObjPtr) return 1;
2034 aStr = Jim_GetString(aObjPtr, &aLen);
2035 bStr = Jim_GetString(bObjPtr, &bLen);
2036 if (aLen != bLen) return 0;
2037 if (nocase == 0)
2038 return memcmp(aStr, bStr, aLen) == 0;
2039 for (i = 0; i < aLen; i++) {
2040 if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2041 return 0;
2042 }
2043 return 1;
2044 }
2045
2046 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2047 int nocase)
2048 {
2049 const char *pattern, *string;
2050 int patternLen, stringLen;
2051
2052 pattern = Jim_GetString(patternObjPtr, &patternLen);
2053 string = Jim_GetString(objPtr, &stringLen);
2054 return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2055 }
2056
2057 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2058 Jim_Obj *secondObjPtr, int nocase)
2059 {
2060 const char *s1, *s2;
2061 int l1, l2;
2062
2063 s1 = Jim_GetString(firstObjPtr, &l1);
2064 s2 = Jim_GetString(secondObjPtr, &l2);
2065 return JimStringCompare(s1, l1, s2, l2, nocase);
2066 }
2067
2068 /* Convert a range, as returned by Jim_GetRange(), into
2069 * an absolute index into an object of the specified length.
2070 * This function may return negative values, or values
2071 * bigger or equal to the length of the list if the index
2072 * is out of range. */
2073 static int JimRelToAbsIndex(int len, int index)
2074 {
2075 if (index < 0)
2076 return len + index;
2077 return index;
2078 }
2079
2080 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2081 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2082 * for implementation of commands like [string range] and [lrange].
2083 *
2084 * The resulting range is guaranteed to address valid elements of
2085 * the structure. */
2086 static void JimRelToAbsRange(int len, int first, int last,
2087 int *firstPtr, int *lastPtr, int *rangeLenPtr)
2088 {
2089 int rangeLen;
2090
2091 if (first > last) {
2092 rangeLen = 0;
2093 } else {
2094 rangeLen = last-first+1;
2095 if (rangeLen) {
2096 if (first < 0) {
2097 rangeLen += first;
2098 first = 0;
2099 }
2100 if (last >= len) {
2101 rangeLen -= (last-(len-1));
2102 last = len-1;
2103 }
2104 }
2105 }
2106 if (rangeLen < 0) rangeLen = 0;
2107
2108 *firstPtr = first;
2109 *lastPtr = last;
2110 *rangeLenPtr = rangeLen;
2111 }
2112
2113 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2114 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2115 {
2116 int first, last;
2117 const char *str;
2118 int len, rangeLen;
2119
2120 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2121 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2122 return NULL;
2123 str = Jim_GetString(strObjPtr, &len);
2124 first = JimRelToAbsIndex(len, first);
2125 last = JimRelToAbsIndex(len, last);
2126 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2127 return Jim_NewStringObj(interp, str+first, rangeLen);
2128 }
2129
2130 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2131 {
2132 char *buf = Jim_Alloc(strObjPtr->length+1);
2133 int i;
2134
2135 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2136 for (i = 0; i < strObjPtr->length; i++)
2137 buf[i] = tolower(buf[i]);
2138 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2139 }
2140
2141 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2142 {
2143 char *buf = Jim_Alloc(strObjPtr->length+1);
2144 int i;
2145
2146 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2147 for (i = 0; i < strObjPtr->length; i++)
2148 buf[i] = toupper(buf[i]);
2149 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2150 }
2151
2152 /* This is the core of the [format] command.
2153 * TODO: Lots of things work - via a hack
2154 * However, no format item can be >= JIM_MAX_FMT
2155 */
2156 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2157 int objc, Jim_Obj *const *objv)
2158 {
2159 const char *fmt, *_fmt;
2160 int fmtLen;
2161 Jim_Obj *resObjPtr;
2162
2163
2164 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2165 _fmt = fmt;
2166 resObjPtr = Jim_NewStringObj(interp, "", 0);
2167 while (fmtLen) {
2168 const char *p = fmt;
2169 char spec[2], c;
2170 jim_wide wideValue;
2171 double doubleValue;
2172 /* we cheat and use Sprintf()! */
2173 #define JIM_MAX_FMT 2048
2174 char sprintf_buf[JIM_MAX_FMT];
2175 char fmt_str[100];
2176 char *cp;
2177 int width;
2178 int ljust;
2179 int zpad;
2180 int spad;
2181 int altfm;
2182 int forceplus;
2183
2184 while (*fmt != '%' && fmtLen) {
2185 fmt++; fmtLen--;
2186 }
2187 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2188 if (fmtLen == 0)
2189 break;
2190 fmt++; fmtLen--; /* skip '%' */
2191 zpad = 0;
2192 spad = 0;
2193 width = -1;
2194 ljust = 0;
2195 altfm = 0;
2196 forceplus = 0;
2197 next_fmt:
2198 if( fmtLen <= 0 ){
2199 break;
2200 }
2201 switch( *fmt ){
2202 /* terminals */
2203 case 'b': /* binary - not all printfs() do this */
2204 case 's': /* string */
2205 case 'i': /* integer */
2206 case 'd': /* decimal */
2207 case 'x': /* hex */
2208 case 'X': /* CAP hex */
2209 case 'c': /* char */
2210 case 'o': /* octal */
2211 case 'u': /* unsigned */
2212 case 'f': /* float */
2213 break;
2214
2215 /* non-terminals */
2216 case '0': /* zero pad */
2217 zpad = 1;
2218 *fmt++; fmtLen--;
2219 goto next_fmt;
2220 break;
2221 case '+':
2222 forceplus = 1;
2223 *fmt++; fmtLen--;
2224 goto next_fmt;
2225 break;
2226 case ' ': /* sign space */
2227 spad = 1;
2228 *fmt++; fmtLen--;
2229 goto next_fmt;
2230 break;
2231 case '-':
2232 ljust = 1;
2233 *fmt++; fmtLen--;
2234 goto next_fmt;
2235 break;
2236 case '#':
2237 altfm = 1;
2238 *fmt++; fmtLen--;
2239 goto next_fmt;
2240
2241 case '1':
2242 case '2':
2243 case '3':
2244 case '4':
2245 case '5':
2246 case '6':
2247 case '7':
2248 case '8':
2249 case '9':
2250 width = 0;
2251 while( isdigit(*fmt) && (fmtLen > 0) ){
2252 width = (width * 10) + (*fmt - '0');
2253 fmt++; fmtLen--;
2254 }
2255 goto next_fmt;
2256 case '*':
2257 /* suck up the next item as an integer */
2258 *fmt++; fmtLen--;
2259 objc--;
2260 if( objc <= 0 ){
2261 goto not_enough_args;
2262 }
2263 if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2264 Jim_FreeNewObj(interp, resObjPtr );
2265 return NULL;
2266 }
2267 width = wideValue;
2268 if( width < 0 ){
2269 ljust = 1;
2270 width = -width;
2271 }
2272 objv++;
2273 goto next_fmt;
2274 break;
2275 }
2276
2277
2278 if (*fmt != '%') {
2279 if (objc == 0) {
2280 not_enough_args:
2281 Jim_FreeNewObj(interp, resObjPtr);
2282 Jim_SetResultString(interp,
2283 "not enough arguments for all format specifiers", -1);
2284 return NULL;
2285 } else {
2286 objc--;
2287 }
2288 }
2289
2290 /*
2291 * Create the formatter
2292 * cause we cheat and use sprintf()
2293 */
2294 cp = fmt_str;
2295 *cp++ = '%';
2296 if( altfm ){
2297 *cp++ = '#';
2298 }
2299 if( forceplus ){
2300 *cp++ = '+';
2301 } else if( spad ){
2302 /* PLUS overrides */
2303 *cp++ = ' ';
2304 }
2305 if( ljust ){
2306 *cp++ = '-';
2307 }
2308 if( zpad ){
2309 *cp++ = '0';
2310 }
2311 if( width > 0 ){
2312 sprintf( cp, "%d", width );
2313 /* skip ahead */
2314 cp = strchr(cp,0);
2315 }
2316 *cp = 0;
2317
2318 /* here we do the work */
2319 /* actually - we make sprintf() do it for us */
2320 switch(*fmt) {
2321 case 's':
2322 *cp++ = 's';
2323 *cp = 0;
2324 /* BUG: we do not handled embeded NULLs */
2325 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2326 break;
2327 case 'c':
2328 *cp++ = 'c';
2329 *cp = 0;
2330 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2331 Jim_FreeNewObj(interp, resObjPtr);
2332 return NULL;
2333 }
2334 c = (char) wideValue;
2335 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2336 break;
2337 case 'f':
2338 case 'F':
2339 case 'g':
2340 case 'G':
2341 case 'e':
2342 case 'E':
2343 *cp++ = *fmt;
2344 *cp = 0;
2345 if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2346 Jim_FreeNewObj( interp, resObjPtr );
2347 return NULL;
2348 }
2349 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2350 break;
2351 case 'b':
2352 case 'd':
2353 case 'i':
2354 case 'u':
2355 case 'x':
2356 case 'X':
2357 /* jim widevaluse are 64bit */
2358 if( sizeof(jim_wide) == sizeof(long long) ){
2359 *cp++ = 'l';
2360 *cp++ = 'l';
2361 } else {
2362 *cp++ = 'l';
2363 }
2364 *cp++ = *fmt;
2365 *cp = 0;
2366 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2367 Jim_FreeNewObj(interp, resObjPtr);
2368 return NULL;
2369 }
2370 snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2371 break;
2372 case '%':
2373 sprintf_buf[0] = '%';
2374 sprintf_buf[1] = 0;
2375 objv--; /* undo the objv++ below */
2376 break;
2377 default:
2378 spec[0] = *fmt; spec[1] = '\0';
2379 Jim_FreeNewObj(interp, resObjPtr);
2380 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2381 Jim_AppendStrings(interp, Jim_GetResult(interp),
2382 "bad field specifier \"", spec, "\"", NULL);
2383 return NULL;
2384 }
2385 /* force terminate */
2386 #if 0
2387 printf("FMT was: %s\n", fmt_str );
2388 printf("RES was: |%s|\n", sprintf_buf );
2389 #endif
2390
2391 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2392 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2393 /* next obj */
2394 objv++;
2395 fmt++;
2396 fmtLen--;
2397 }
2398 return resObjPtr;
2399 }
2400
2401 /* -----------------------------------------------------------------------------
2402 * Compared String Object
2403 * ---------------------------------------------------------------------------*/
2404
2405 /* This is strange object that allows to compare a C literal string
2406 * with a Jim object in very short time if the same comparison is done
2407 * multiple times. For example every time the [if] command is executed,
2408 * Jim has to check if a given argument is "else". This comparions if
2409 * the code has no errors are true most of the times, so we can cache
2410 * inside the object the pointer of the string of the last matching
2411 * comparison. Because most C compilers perform literal sharing,
2412 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2413 * this works pretty well even if comparisons are at different places
2414 * inside the C code. */
2415
2416 static Jim_ObjType comparedStringObjType = {
2417 "compared-string",
2418 NULL,
2419 NULL,
2420 NULL,
2421 JIM_TYPE_REFERENCES,
2422 };
2423
2424 /* The only way this object is exposed to the API is via the following
2425 * function. Returns true if the string and the object string repr.
2426 * are the same, otherwise zero is returned.
2427 *
2428 * Note: this isn't binary safe, but it hardly needs to be.*/
2429 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2430 const char *str)
2431 {
2432 if (objPtr->typePtr == &comparedStringObjType &&
2433 objPtr->internalRep.ptr == str)
2434 return 1;
2435 else {
2436 const char *objStr = Jim_GetString(objPtr, NULL);
2437 if (strcmp(str, objStr) != 0) return 0;
2438 if (objPtr->typePtr != &comparedStringObjType) {
2439 Jim_FreeIntRep(interp, objPtr);
2440 objPtr->typePtr = &comparedStringObjType;
2441 }
2442 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2443 return 1;
2444 }
2445 }
2446
2447 int qsortCompareStringPointers(const void *a, const void *b)
2448 {
2449 char * const *sa = (char * const *)a;
2450 char * const *sb = (char * const *)b;
2451 return strcmp(*sa, *sb);
2452 }
2453
2454 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2455 const char **tablePtr, int *indexPtr, const char *name, int flags)
2456 {
2457 const char **entryPtr = NULL;
2458 char **tablePtrSorted;
2459 int i, count = 0;
2460
2461 *indexPtr = -1;
2462 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2463 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2464 *indexPtr = i;
2465 return JIM_OK;
2466 }
2467 count++; /* If nothing matches, this will reach the len of tablePtr */
2468 }
2469 if (flags & JIM_ERRMSG) {
2470 if (name == NULL)
2471 name = "option";
2472 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2473 Jim_AppendStrings(interp, Jim_GetResult(interp),
2474 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2475 NULL);
2476 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2477 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2478 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2479 for (i = 0; i < count; i++) {
2480 if (i+1 == count && count > 1)
2481 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2482 Jim_AppendString(interp, Jim_GetResult(interp),
2483 tablePtrSorted[i], -1);
2484 if (i+1 != count)
2485 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2486 }
2487 Jim_Free(tablePtrSorted);
2488 }
2489 return JIM_ERR;
2490 }
2491
2492 /* -----------------------------------------------------------------------------
2493 * Source Object
2494 *
2495 * This object is just a string from the language point of view, but
2496 * in the internal representation it contains the filename and line number
2497 * where this given token was read. This information is used by
2498 * Jim_EvalObj() if the object passed happens to be of type "source".
2499 *
2500 * This allows to propagate the information about line numbers and file
2501 * names and give error messages with absolute line numbers.
2502 *
2503 * Note that this object uses shared strings for filenames, and the
2504 * pointer to the filename together with the line number is taken into
2505 * the space for the "inline" internal represenation of the Jim_Object,
2506 * so there is almost memory zero-overhead.
2507 *
2508 * Also the object will be converted to something else if the given
2509 * token it represents in the source file is not something to be
2510 * evaluated (not a script), and will be specialized in some other way,
2511 * so the time overhead is alzo null.
2512 * ---------------------------------------------------------------------------*/
2513
2514 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2515 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2516
2517 static Jim_ObjType sourceObjType = {
2518 "source",
2519 FreeSourceInternalRep,
2520 DupSourceInternalRep,
2521 NULL,
2522 JIM_TYPE_REFERENCES,
2523 };
2524
2525 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2526 {
2527 Jim_ReleaseSharedString(interp,
2528 objPtr->internalRep.sourceValue.fileName);
2529 }
2530
2531 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2532 {
2533 dupPtr->internalRep.sourceValue.fileName =
2534 Jim_GetSharedString(interp,
2535 srcPtr->internalRep.sourceValue.fileName);
2536 dupPtr->internalRep.sourceValue.lineNumber =
2537 dupPtr->internalRep.sourceValue.lineNumber;
2538 dupPtr->typePtr = &sourceObjType;
2539 }
2540
2541 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2542 const char *fileName, int lineNumber)
2543 {
2544 if (Jim_IsShared(objPtr))
2545 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2546 if (objPtr->typePtr != NULL)
2547 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2548 objPtr->internalRep.sourceValue.fileName =
2549 Jim_GetSharedString(interp, fileName);
2550 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2551 objPtr->typePtr = &sourceObjType;
2552 }
2553
2554 /* -----------------------------------------------------------------------------
2555 * Script Object
2556 * ---------------------------------------------------------------------------*/
2557
2558 #define JIM_CMDSTRUCT_EXPAND -1
2559
2560 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2561 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2562 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2563
2564 static Jim_ObjType scriptObjType = {
2565 "script",
2566 FreeScriptInternalRep,
2567 DupScriptInternalRep,
2568 NULL,
2569 JIM_TYPE_REFERENCES,
2570 };
2571
2572 /* The ScriptToken structure represents every token into a scriptObj.
2573 * Every token contains an associated Jim_Obj that can be specialized
2574 * by commands operating on it. */
2575 typedef struct ScriptToken {
2576 int type;
2577 Jim_Obj *objPtr;
2578 int linenr;
2579 } ScriptToken;
2580
2581 /* This is the script object internal representation. An array of
2582 * ScriptToken structures, with an associated command structure array.
2583 * The command structure is a pre-computed representation of the
2584 * command length and arguments structure as a simple liner array
2585 * of integers.
2586 *
2587 * For example the script:
2588 *
2589 * puts hello
2590 * set $i $x$y [foo]BAR
2591 *
2592 * will produce a ScriptObj with the following Tokens:
2593 *
2594 * ESC puts
2595 * SEP
2596 * ESC hello
2597 * EOL
2598 * ESC set
2599 * EOL
2600 * VAR i
2601 * SEP
2602 * VAR x
2603 * VAR y
2604 * SEP
2605 * CMD foo
2606 * ESC BAR
2607 * EOL
2608 *
2609 * This is a description of the tokens, separators, and of lines.
2610 * The command structure instead represents the number of arguments
2611 * of every command, followed by the tokens of which every argument
2612 * is composed. So for the example script, the cmdstruct array will
2613 * contain:
2614 *
2615 * 2 1 1 4 1 1 2 2
2616 *
2617 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2618 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2619 * composed of single tokens (1 1) and the last two of double tokens
2620 * (2 2).
2621 *
2622 * The precomputation of the command structure makes Jim_Eval() faster,
2623 * and simpler because there aren't dynamic lengths / allocations.
2624 *
2625 * -- {expand} handling --
2626 *
2627 * Expand is handled in a special way. When a command
2628 * contains at least an argument with the {expand} prefix,
2629 * the command structure presents a -1 before the integer
2630 * describing the number of arguments. This is used in order
2631 * to send the command exection to a different path in case
2632 * of {expand} and guarantee a fast path for the more common
2633 * case. Also, the integers describing the number of tokens
2634 * are expressed with negative sign, to allow for fast check
2635 * of what's an {expand}-prefixed argument and what not.
2636 *
2637 * For example the command:
2638 *
2639 * list {expand}{1 2}
2640 *
2641 * Will produce the following cmdstruct array:
2642 *
2643 * -1 2 1 -2
2644 *
2645 * -- the substFlags field of the structure --
2646 *
2647 * The scriptObj structure is used to represent both "script" objects
2648 * and "subst" objects. In the second case, the cmdStruct related
2649 * fields are not used at all, but there is an additional field used
2650 * that is 'substFlags': this represents the flags used to turn
2651 * the string into the intenral representation used to perform the
2652 * substitution. If this flags are not what the application requires
2653 * the scriptObj is created again. For example the script:
2654 *
2655 * subst -nocommands $string
2656 * subst -novariables $string
2657 *
2658 * Will recreate the internal representation of the $string object
2659 * two times.
2660 */
2661 typedef struct ScriptObj {
2662 int len; /* Length as number of tokens. */
2663 int commands; /* number of top-level commands in script. */
2664 ScriptToken *token; /* Tokens array. */
2665 int *cmdStruct; /* commands structure */
2666 int csLen; /* length of the cmdStruct array. */
2667 int substFlags; /* flags used for the compilation of "subst" objects */
2668 int inUse; /* Used to share a ScriptObj. Currently
2669 only used by Jim_EvalObj() as protection against
2670 shimmering of the currently evaluated object. */
2671 char *fileName;
2672 } ScriptObj;
2673
2674 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2675 {
2676 int i;
2677 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2678
2679 script->inUse--;
2680 if (script->inUse != 0) return;
2681 for (i = 0; i < script->len; i++) {
2682 if (script->token[i].objPtr != NULL)
2683 Jim_DecrRefCount(interp, script->token[i].objPtr);
2684 }
2685 Jim_Free(script->token);
2686 Jim_Free(script->cmdStruct);
2687 Jim_Free(script->fileName);
2688 Jim_Free(script);
2689 }
2690
2691 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2692 {
2693 JIM_NOTUSED(interp);
2694 JIM_NOTUSED(srcPtr);
2695
2696 /* Just returns an simple string. */
2697 dupPtr->typePtr = NULL;
2698 }
2699
2700 /* Add a new token to the internal repr of a script object */
2701 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2702 char *strtoken, int len, int type, char *filename, int linenr)
2703 {
2704 int prevtype;
2705 struct ScriptToken *token;
2706
2707 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2708 script->token[script->len-1].type;
2709 /* Skip tokens without meaning, like words separators
2710 * following a word separator or an end of command and
2711 * so on. */
2712 if (prevtype == JIM_TT_EOL) {
2713 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2714 Jim_Free(strtoken);
2715 return;
2716 }
2717 } else if (prevtype == JIM_TT_SEP) {
2718 if (type == JIM_TT_SEP) {
2719 Jim_Free(strtoken);
2720 return;
2721 } else if (type == JIM_TT_EOL) {
2722 /* If an EOL is following by a SEP, drop the previous
2723 * separator. */
2724 script->len--;
2725 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2726 }
2727 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2728 type == JIM_TT_ESC && len == 0)
2729 {
2730 /* Don't add empty tokens used in interpolation */
2731 Jim_Free(strtoken);
2732 return;
2733 }
2734 /* Make space for a new istruction */
2735 script->len++;
2736 script->token = Jim_Realloc(script->token,
2737 sizeof(ScriptToken)*script->len);
2738 /* Initialize the new token */
2739 token = script->token+(script->len-1);
2740 token->type = type;
2741 /* Every object is intially as a string, but the
2742 * internal type may be specialized during execution of the
2743 * script. */
2744 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2745 /* To add source info to SEP and EOL tokens is useless because
2746 * they will never by called as arguments of Jim_EvalObj(). */
2747 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2748 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2749 Jim_IncrRefCount(token->objPtr);
2750 token->linenr = linenr;
2751 }
2752
2753 /* Add an integer into the command structure field of the script object. */
2754 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2755 {
2756 script->csLen++;
2757 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2758 sizeof(int)*script->csLen);
2759 script->cmdStruct[script->csLen-1] = val;
2760 }
2761
2762 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2763 * of objPtr. Search nested script objects recursively. */
2764 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2765 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2766 {
2767 int i;
2768
2769 for (i = 0; i < script->len; i++) {
2770 if (script->token[i].objPtr != objPtr &&
2771 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2772 return script->token[i].objPtr;
2773 }
2774 /* Enter recursively on scripts only if the object
2775 * is not the same as the one we are searching for
2776 * shared occurrences. */
2777 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2778 script->token[i].objPtr != objPtr) {
2779 Jim_Obj *foundObjPtr;
2780
2781 ScriptObj *subScript =
2782 script->token[i].objPtr->internalRep.ptr;
2783 /* Don't recursively enter the script we are trying
2784 * to make shared to avoid circular references. */
2785 if (subScript == scriptBarrier) continue;
2786 if (subScript != script) {
2787 foundObjPtr =
2788 ScriptSearchLiteral(interp, subScript,
2789 scriptBarrier, objPtr);
2790 if (foundObjPtr != NULL)
2791 return foundObjPtr;
2792 }
2793 }
2794 }
2795 return NULL;
2796 }
2797
2798 /* Share literals of a script recursively sharing sub-scripts literals. */
2799 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2800 ScriptObj *topLevelScript)
2801 {
2802 int i, j;
2803
2804 return;
2805 /* Try to share with toplevel object. */
2806 if (topLevelScript != NULL) {
2807 for (i = 0; i < script->len; i++) {
2808 Jim_Obj *foundObjPtr;
2809 char *str = script->token[i].objPtr->bytes;
2810
2811 if (script->token[i].objPtr->refCount != 1) continue;
2812 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2813 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2814 foundObjPtr = ScriptSearchLiteral(interp,
2815 topLevelScript,
2816 script, /* barrier */
2817 script->token[i].objPtr);
2818 if (foundObjPtr != NULL) {
2819 Jim_IncrRefCount(foundObjPtr);
2820 Jim_DecrRefCount(interp,
2821 script->token[i].objPtr);
2822 script->token[i].objPtr = foundObjPtr;
2823 }
2824 }
2825 }
2826 /* Try to share locally */
2827 for (i = 0; i < script->len; i++) {
2828 char *str = script->token[i].objPtr->bytes;
2829
2830 if (script->token[i].objPtr->refCount != 1) continue;
2831 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2832 for (j = 0; j < script->len; j++) {
2833 if (script->token[i].objPtr !=
2834 script->token[j].objPtr &&
2835 Jim_StringEqObj(script->token[i].objPtr,
2836 script->token[j].objPtr, 0))
2837 {
2838 Jim_IncrRefCount(script->token[j].objPtr);
2839 Jim_DecrRefCount(interp,
2840 script->token[i].objPtr);
2841 script->token[i].objPtr =
2842 script->token[j].objPtr;
2843 }
2844 }
2845 }
2846 }
2847
2848 /* This method takes the string representation of an object
2849 * as a Tcl script, and generates the pre-parsed internal representation
2850 * of the script. */
2851 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2852 {
2853 int scriptTextLen;
2854 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2855 struct JimParserCtx parser;
2856 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2857 ScriptToken *token;
2858 int args, tokens, start, end, i;
2859 int initialLineNumber;
2860 int propagateSourceInfo = 0;
2861
2862 script->len = 0;
2863 script->csLen = 0;
2864 script->commands = 0;
2865 script->token = NULL;
2866 script->cmdStruct = NULL;
2867 script->inUse = 1;
2868 /* Try to get information about filename / line number */
2869 if (objPtr->typePtr == &sourceObjType) {
2870 script->fileName =
2871 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2872 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2873 propagateSourceInfo = 1;
2874 } else {
2875 script->fileName = Jim_StrDup("?");
2876 initialLineNumber = 1;
2877 }
2878
2879 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2880 while(!JimParserEof(&parser)) {
2881 char *token;
2882 int len, type, linenr;
2883
2884 JimParseScript(&parser);
2885 token = JimParserGetToken(&parser, &len, &type, &linenr);
2886 ScriptObjAddToken(interp, script, token, len, type,
2887 propagateSourceInfo ? script->fileName : NULL,
2888 linenr);
2889 }
2890 token = script->token;
2891
2892 /* Compute the command structure array
2893 * (see the ScriptObj struct definition for more info) */
2894 start = 0; /* Current command start token index */
2895 end = -1; /* Current command end token index */
2896 while (1) {
2897 int expand = 0; /* expand flag. set to 1 on {expand} form. */
2898 int interpolation = 0; /* set to 1 if there is at least one
2899 argument of the command obtained via
2900 interpolation of more tokens. */
2901 /* Search for the end of command, while
2902 * count the number of args. */
2903 start = ++end;
2904 if (start >= script->len) break;
2905 args = 1; /* Number of args in current command */
2906 while (token[end].type != JIM_TT_EOL) {
2907 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2908 token[end-1].type == JIM_TT_EOL)
2909 {
2910 if (token[end].type == JIM_TT_STR &&
2911 token[end+1].type != JIM_TT_SEP &&
2912 token[end+1].type != JIM_TT_EOL &&
2913 (!strcmp(token[end].objPtr->bytes, "expand") ||
2914 !strcmp(token[end].objPtr->bytes, "*")))
2915 expand++;
2916 }
2917 if (token[end].type == JIM_TT_SEP)
2918 args++;
2919 end++;
2920 }
2921 interpolation = !((end-start+1) == args*2);
2922 /* Add the 'number of arguments' info into cmdstruct.
2923 * Negative value if there is list expansion involved. */
2924 if (expand)
2925 ScriptObjAddInt(script, -1);
2926 ScriptObjAddInt(script, args);
2927 /* Now add info about the number of tokens. */
2928 tokens = 0; /* Number of tokens in current argument. */
2929 expand = 0;
2930 for (i = start; i <= end; i++) {
2931 if (token[i].type == JIM_TT_SEP ||
2932 token[i].type == JIM_TT_EOL)
2933 {
2934 if (tokens == 1 && expand)
2935 expand = 0;
2936 ScriptObjAddInt(script,
2937 expand ? -tokens : tokens);
2938
2939 expand = 0;
2940 tokens = 0;
2941 continue;
2942 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2943 (!strcmp(token[i].objPtr->bytes, "expand") ||
2944 !strcmp(token[i].objPtr->bytes, "*")))
2945 {
2946 expand++;
2947 }
2948 tokens++;
2949 }
2950 }
2951 /* Perform literal sharing, but only for objects that appear
2952 * to be scripts written as literals inside the source code,
2953 * and not computed at runtime. Literal sharing is a costly
2954 * operation that should be done only against objects that
2955 * are likely to require compilation only the first time, and
2956 * then are executed multiple times. */
2957 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2958 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2959 if (bodyObjPtr->typePtr == &scriptObjType) {
2960 ScriptObj *bodyScript =
2961 bodyObjPtr->internalRep.ptr;
2962 ScriptShareLiterals(interp, script, bodyScript);
2963 }
2964 } else if (propagateSourceInfo) {
2965 ScriptShareLiterals(interp, script, NULL);
2966 }
2967 /* Free the old internal rep and set the new one. */
2968 Jim_FreeIntRep(interp, objPtr);
2969 Jim_SetIntRepPtr(objPtr, script);
2970 objPtr->typePtr = &scriptObjType;
2971 return JIM_OK;
2972 }
2973
2974 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
2975 {
2976 if (objPtr->typePtr != &scriptObjType) {
2977 SetScriptFromAny(interp, objPtr);
2978 }
2979 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
2980 }
2981
2982 /* -----------------------------------------------------------------------------
2983 * Commands
2984 * ---------------------------------------------------------------------------*/
2985
2986 /* Commands HashTable Type.
2987 *
2988 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
2989 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
2990 {
2991 Jim_Cmd *cmdPtr = (void*) val;
2992
2993 if (cmdPtr->cmdProc == NULL) {
2994 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2995 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2996 if (cmdPtr->staticVars) {
2997 Jim_FreeHashTable(cmdPtr->staticVars);
2998 Jim_Free(cmdPtr->staticVars);
2999 }
3000 } else if (cmdPtr->delProc != NULL) {
3001 /* If it was a C coded command, call the delProc if any */
3002 cmdPtr->delProc(interp, cmdPtr->privData);
3003 }
3004 Jim_Free(val);
3005 }
3006
3007 static Jim_HashTableType JimCommandsHashTableType = {
3008 JimStringCopyHTHashFunction, /* hash function */
3009 JimStringCopyHTKeyDup, /* key dup */
3010 NULL, /* val dup */
3011 JimStringCopyHTKeyCompare, /* key compare */
3012 JimStringCopyHTKeyDestructor, /* key destructor */
3013 Jim_CommandsHT_ValDestructor /* val destructor */
3014 };
3015
3016 /* ------------------------- Commands related functions --------------------- */
3017
3018 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3019 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3020 {
3021 Jim_HashEntry *he;
3022 Jim_Cmd *cmdPtr;
3023
3024 he = Jim_FindHashEntry(&interp->commands, cmdName);
3025 if (he == NULL) { /* New command to create */
3026 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3027 cmdPtr->cmdProc = cmdProc;
3028 cmdPtr->privData = privData;
3029 cmdPtr->delProc = delProc;
3030 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3031 } else {
3032 Jim_InterpIncrProcEpoch(interp);
3033 /* Free the arglist/body objects if it was a Tcl procedure */
3034 cmdPtr = he->val;
3035 if (cmdPtr->cmdProc == NULL) {
3036 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3037 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3038 if (cmdPtr->staticVars) {
3039 Jim_FreeHashTable(cmdPtr->staticVars);
3040 Jim_Free(cmdPtr->staticVars);
3041 }
3042 cmdPtr->staticVars = NULL;
3043 } else if (cmdPtr->delProc != NULL) {
3044 /* If it was a C coded command, call the delProc if any */
3045 cmdPtr->delProc(interp, cmdPtr->privData);
3046 }
3047 cmdPtr->cmdProc = cmdProc;
3048 cmdPtr->privData = privData;
3049 }
3050 /* There is no need to increment the 'proc epoch' because
3051 * creation of a new procedure can never affect existing
3052 * cached commands. We don't do negative caching. */
3053 return JIM_OK;
3054 }
3055
3056 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3057 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3058 int arityMin, int arityMax)
3059 {
3060 Jim_Cmd *cmdPtr;
3061
3062 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3063 cmdPtr->cmdProc = NULL; /* Not a C coded command */
3064 cmdPtr->argListObjPtr = argListObjPtr;
3065 cmdPtr->bodyObjPtr = bodyObjPtr;
3066 Jim_IncrRefCount(argListObjPtr);
3067 Jim_IncrRefCount(bodyObjPtr);
3068 cmdPtr->arityMin = arityMin;
3069 cmdPtr->arityMax = arityMax;
3070 cmdPtr->staticVars = NULL;
3071
3072 /* Create the statics hash table. */
3073 if (staticsListObjPtr) {
3074 int len, i;
3075
3076 Jim_ListLength(interp, staticsListObjPtr, &len);
3077 if (len != 0) {
3078 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3079 Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
3080 interp);
3081 for (i = 0; i < len; i++) {
3082 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3083 Jim_Var *varPtr;
3084 int subLen;
3085
3086 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3087 /* Check if it's composed of two elements. */
3088 Jim_ListLength(interp, objPtr, &subLen);
3089 if (subLen == 1 || subLen == 2) {
3090 /* Try to get the variable value from the current
3091 * environment. */
3092 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3093 if (subLen == 1) {
3094 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3095 JIM_NONE);
3096 if (initObjPtr == NULL) {
3097 Jim_SetResult(interp,
3098 Jim_NewEmptyStringObj(interp));
3099 Jim_AppendStrings(interp, Jim_GetResult(interp),
3100 "variable for initialization of static \"",
3101 Jim_GetString(nameObjPtr, NULL),
3102 "\" not found in the local context",
3103 NULL);
3104 goto err;
3105 }
3106 } else {
3107 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3108 }
3109 varPtr = Jim_Alloc(sizeof(*varPtr));
3110 varPtr->objPtr = initObjPtr;
3111 Jim_IncrRefCount(initObjPtr);
3112 varPtr->linkFramePtr = NULL;
3113 if (Jim_AddHashEntry(cmdPtr->staticVars,
3114 Jim_GetString(nameObjPtr, NULL),
3115 varPtr) != JIM_OK)
3116 {
3117 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3118 Jim_AppendStrings(interp, Jim_GetResult(interp),
3119 "static variable name \"",
3120 Jim_GetString(objPtr, NULL), "\"",
3121 " duplicated in statics list", NULL);
3122 Jim_DecrRefCount(interp, initObjPtr);
3123 Jim_Free(varPtr);
3124 goto err;
3125 }
3126 } else {
3127 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3128 Jim_AppendStrings(interp, Jim_GetResult(interp),
3129 "too many fields in static specifier \"",
3130 objPtr, "\"", NULL);
3131 goto err;
3132 }
3133 }
3134 }
3135 }
3136
3137 /* Add the new command */
3138
3139 /* it may already exist, so we try to delete the old one */
3140 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3141 /* There was an old procedure with the same name, this requires
3142 * a 'proc epoch' update. */
3143 Jim_InterpIncrProcEpoch(interp);
3144 }
3145 /* If a procedure with the same name didn't existed there is no need
3146 * to increment the 'proc epoch' because creation of a new procedure
3147 * can never affect existing cached commands. We don't do
3148 * negative caching. */
3149 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3150 return JIM_OK;
3151
3152 err:
3153 Jim_FreeHashTable(cmdPtr->staticVars);
3154 Jim_Free(cmdPtr->staticVars);
3155 Jim_DecrRefCount(interp, argListObjPtr);
3156 Jim_DecrRefCount(interp, bodyObjPtr);
3157 Jim_Free(cmdPtr);
3158 return JIM_ERR;
3159 }
3160
3161 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3162 {
3163 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3164 return JIM_ERR;
3165 Jim_InterpIncrProcEpoch(interp);
3166 return JIM_OK;
3167 }
3168
3169 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
3170 const char *newName)
3171 {
3172 Jim_Cmd *cmdPtr;
3173 Jim_HashEntry *he;
3174 Jim_Cmd *copyCmdPtr;
3175
3176 if (newName[0] == '\0') /* Delete! */
3177 return Jim_DeleteCommand(interp, oldName);
3178 /* Rename */
3179 he = Jim_FindHashEntry(&interp->commands, oldName);
3180 if (he == NULL)
3181 return JIM_ERR; /* Invalid command name */
3182 cmdPtr = he->val;
3183 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3184 *copyCmdPtr = *cmdPtr;
3185 /* In order to avoid that a procedure will get arglist/body/statics
3186 * freed by the hash table methods, fake a C-coded command
3187 * setting cmdPtr->cmdProc as not NULL */
3188 cmdPtr->cmdProc = (void*)1;
3189 /* Also make sure delProc is NULL. */
3190 cmdPtr->delProc = NULL;
3191 /* Destroy the old command, and make sure the new is freed
3192 * as well. */
3193 Jim_DeleteHashEntry(&interp->commands, oldName);
3194 Jim_DeleteHashEntry(&interp->commands, newName);
3195 /* Now the new command. We are sure it can't fail because
3196 * the target name was already freed. */
3197 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3198 /* Increment the epoch */
3199 Jim_InterpIncrProcEpoch(interp);
3200 return JIM_OK;
3201 }
3202
3203 /* -----------------------------------------------------------------------------
3204 * Command object
3205 * ---------------------------------------------------------------------------*/
3206
3207 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3208
3209 static Jim_ObjType commandObjType = {
3210 "command",
3211 NULL,
3212 NULL,
3213 NULL,
3214 JIM_TYPE_REFERENCES,
3215 };
3216
3217 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3218 {
3219 Jim_HashEntry *he;
3220 const char *cmdName;
3221
3222 /* Get the string representation */
3223 cmdName = Jim_GetString(objPtr, NULL);
3224 /* Lookup this name into the commands hash table */
3225 he = Jim_FindHashEntry(&interp->commands, cmdName);
3226 if (he == NULL)
3227 return JIM_ERR;
3228
3229 /* Free the old internal repr and set the new one. */
3230 Jim_FreeIntRep(interp, objPtr);
3231 objPtr->typePtr = &commandObjType;
3232 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3233 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3234 return JIM_OK;
3235 }
3236
3237 /* This function returns the command structure for the command name
3238 * stored in objPtr. It tries to specialize the objPtr to contain
3239 * a cached info instead to perform the lookup into the hash table
3240 * every time. The information cached may not be uptodate, in such
3241 * a case the lookup is performed and the cache updated. */
3242 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3243 {
3244 if ((objPtr->typePtr != &commandObjType ||
3245 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3246 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3247 if (flags & JIM_ERRMSG) {
3248 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3249 Jim_AppendStrings(interp, Jim_GetResult(interp),
3250 "invalid command name \"", objPtr->bytes, "\"",
3251 NULL);
3252 }
3253 return NULL;
3254 }
3255 return objPtr->internalRep.cmdValue.cmdPtr;
3256 }
3257
3258 /* -----------------------------------------------------------------------------
3259 * Variables
3260 * ---------------------------------------------------------------------------*/
3261
3262 /* Variables HashTable Type.
3263 *
3264 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3265 static void JimVariablesHTValDestructor(void *interp, void *val)
3266 {
3267 Jim_Var *varPtr = (void*) val;
3268
3269 Jim_DecrRefCount(interp, varPtr->objPtr);
3270 Jim_Free(val);
3271 }
3272
3273 static Jim_HashTableType JimVariablesHashTableType = {
3274 JimStringCopyHTHashFunction, /* hash function */
3275 JimStringCopyHTKeyDup, /* key dup */
3276 NULL, /* val dup */
3277 JimStringCopyHTKeyCompare, /* key compare */
3278 JimStringCopyHTKeyDestructor, /* key destructor */
3279 JimVariablesHTValDestructor /* val destructor */
3280 };
3281
3282 /* -----------------------------------------------------------------------------
3283 * Variable object
3284 * ---------------------------------------------------------------------------*/
3285
3286 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3287
3288 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3289
3290 static Jim_ObjType variableObjType = {
3291 "variable",
3292 NULL,
3293 NULL,
3294 NULL,
3295 JIM_TYPE_REFERENCES,
3296 };
3297
3298 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3299 * is in the form "varname(key)". */
3300 static int Jim_NameIsDictSugar(const char *str, int len)
3301 {
3302 if (len == -1)
3303 len = strlen(str);
3304 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3305 return 1;
3306 return 0;
3307 }
3308
3309 /* This method should be called only by the variable API.
3310 * It returns JIM_OK on success (variable already exists),
3311 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3312 * a variable name, but syntax glue for [dict] i.e. the last
3313 * character is ')' */
3314 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3315 {
3316 Jim_HashEntry *he;
3317 const char *varName;
3318 int len;
3319
3320 /* Check if the object is already an uptodate variable */
3321 if (objPtr->typePtr == &variableObjType &&
3322 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3323 return JIM_OK; /* nothing to do */
3324 /* Get the string representation */
3325 varName = Jim_GetString(objPtr, &len);
3326 /* Make sure it's not syntax glue to get/set dict. */
3327 if (Jim_NameIsDictSugar(varName, len))
3328 return JIM_DICT_SUGAR;
3329 /* Lookup this name into the variables hash table */
3330 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3331 if (he == NULL) {
3332 /* Try with static vars. */
3333 if (interp->framePtr->staticVars == NULL)
3334 return JIM_ERR;
3335 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3336 return JIM_ERR;
3337 }
3338 /* Free the old internal repr and set the new one. */
3339 Jim_FreeIntRep(interp, objPtr);
3340 objPtr->typePtr = &variableObjType;
3341 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3342 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3343 return JIM_OK;
3344 }
3345
3346 /* -------------------- Variables related functions ------------------------- */
3347 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3348 Jim_Obj *valObjPtr);
3349 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3350
3351 /* For now that's dummy. Variables lookup should be optimized
3352 * in many ways, with caching of lookups, and possibly with
3353 * a table of pre-allocated vars in every CallFrame for local vars.
3354 * All the caching should also have an 'epoch' mechanism similar
3355 * to the one used by Tcl for procedures lookup caching. */
3356
3357 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3358 {
3359 const char *name;
3360 Jim_Var *var;
3361 int err;
3362
3363 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3364 /* Check for [dict] syntax sugar. */
3365 if (err == JIM_DICT_SUGAR)
3366 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3367 /* New variable to create */
3368 name = Jim_GetString(nameObjPtr, NULL);
3369
3370 var = Jim_Alloc(sizeof(*var));
3371 var->objPtr = valObjPtr;
3372 Jim_IncrRefCount(valObjPtr);
3373 var->linkFramePtr = NULL;
3374 /* Insert the new variable */
3375 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3376 /* Make the object int rep a variable */
3377 Jim_FreeIntRep(interp, nameObjPtr);
3378 nameObjPtr->typePtr = &variableObjType;
3379 nameObjPtr->internalRep.varValue.callFrameId =
3380 interp->framePtr->id;
3381 nameObjPtr->internalRep.varValue.varPtr = var;
3382 } else {
3383 var = nameObjPtr->internalRep.varValue.varPtr;
3384 if (var->linkFramePtr == NULL) {
3385 Jim_IncrRefCount(valObjPtr);
3386 Jim_DecrRefCount(interp, var->objPtr);
3387 var->objPtr = valObjPtr;
3388 } else { /* Else handle the link */
3389 Jim_CallFrame *savedCallFrame;
3390
3391 savedCallFrame = interp->framePtr;
3392 interp->framePtr = var->linkFramePtr;
3393 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3394 interp->framePtr = savedCallFrame;
3395 if (err != JIM_OK)
3396 return err;
3397 }
3398 }
3399 return JIM_OK;
3400 }
3401
3402 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3403 {
3404 Jim_Obj *nameObjPtr;
3405 int result;
3406
3407 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3408 Jim_IncrRefCount(nameObjPtr);
3409 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3410 Jim_DecrRefCount(interp, nameObjPtr);
3411 return result;
3412 }
3413
3414 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3415 {
3416 Jim_CallFrame *savedFramePtr;
3417 int result;
3418
3419 savedFramePtr = interp->framePtr;
3420 interp->framePtr = interp->topFramePtr;
3421 result = Jim_SetVariableStr(interp, name, objPtr);
3422 interp->framePtr = savedFramePtr;
3423 return result;
3424 }
3425
3426 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3427 {
3428 Jim_Obj *nameObjPtr, *valObjPtr;
3429 int result;
3430
3431 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3432 valObjPtr = Jim_NewStringObj(interp, val, -1);
3433 Jim_IncrRefCount(nameObjPtr);
3434 Jim_IncrRefCount(valObjPtr);
3435 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3436 Jim_DecrRefCount(interp, nameObjPtr);
3437 Jim_DecrRefCount(interp, valObjPtr);
3438 return result;
3439 }
3440
3441 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3442 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3443 {
3444 const char *varName;
3445 int len;
3446
3447 /* Check for cycles. */
3448 if (interp->framePtr == targetCallFrame) {
3449 Jim_Obj *objPtr = targetNameObjPtr;
3450 Jim_Var *varPtr;
3451 /* Cycles are only possible with 'uplevel 0' */
3452 while(1) {
3453 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3454 Jim_SetResultString(interp,
3455 "can't upvar from variable to itself", -1);
3456 return JIM_ERR;
3457 }
3458 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3459 break;
3460 varPtr = objPtr->internalRep.varValue.varPtr;
3461 if (varPtr->linkFramePtr != targetCallFrame) break;
3462 objPtr = varPtr->objPtr;
3463 }
3464 }
3465 varName = Jim_GetString(nameObjPtr, &len);
3466 if (Jim_NameIsDictSugar(varName, len)) {
3467 Jim_SetResultString(interp,
3468 "Dict key syntax invalid as link source", -1);
3469 return JIM_ERR;
3470 }
3471 /* Perform the binding */
3472 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3473 /* We are now sure 'nameObjPtr' type is variableObjType */
3474 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3475 return JIM_OK;
3476 }
3477
3478 /* Return the Jim_Obj pointer associated with a variable name,
3479 * or NULL if the variable was not found in the current context.
3480 * The same optimization discussed in the comment to the
3481 * 'SetVariable' function should apply here. */
3482 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3483 {
3484 int err;
3485
3486 /* All the rest is handled here */
3487 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3488 /* Check for [dict] syntax sugar. */
3489 if (err == JIM_DICT_SUGAR)
3490 return JimDictSugarGet(interp, nameObjPtr);
3491 if (flags & JIM_ERRMSG) {
3492 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3493 Jim_AppendStrings(interp, Jim_GetResult(interp),
3494 "can't read \"", nameObjPtr->bytes,
3495 "\": no such variable", NULL);
3496 }
3497 return NULL;
3498 } else {
3499 Jim_Var *varPtr;
3500 Jim_Obj *objPtr;
3501 Jim_CallFrame *savedCallFrame;
3502
3503 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3504 if (varPtr->linkFramePtr == NULL)
3505 return varPtr->objPtr;
3506 /* The variable is a link? Resolve it. */
3507 savedCallFrame = interp->framePtr;
3508 interp->framePtr = varPtr->linkFramePtr;
3509 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3510 if (objPtr == NULL && flags & JIM_ERRMSG) {
3511 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3512 Jim_AppendStrings(interp, Jim_GetResult(interp),
3513 "can't read \"", nameObjPtr->bytes,
3514 "\": no such variable", NULL);
3515 }
3516 interp->framePtr = savedCallFrame;
3517 return objPtr;
3518 }
3519 }
3520
3521 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3522 int flags)
3523 {
3524 Jim_CallFrame *savedFramePtr;
3525 Jim_Obj *objPtr;
3526
3527 savedFramePtr = interp->framePtr;
3528 interp->framePtr = interp->topFramePtr;
3529 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3530 interp->framePtr = savedFramePtr;
3531
3532 return objPtr;
3533 }
3534
3535 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3536 {
3537 Jim_Obj *nameObjPtr, *varObjPtr;
3538
3539 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3540 Jim_IncrRefCount(nameObjPtr);
3541 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3542 Jim_DecrRefCount(interp, nameObjPtr);
3543 return varObjPtr;
3544 }
3545
3546 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3547 int flags)
3548 {
3549 Jim_CallFrame *savedFramePtr;
3550 Jim_Obj *objPtr;
3551
3552 savedFramePtr = interp->framePtr;
3553 interp->framePtr = interp->topFramePtr;
3554 objPtr = Jim_GetVariableStr(interp, name, flags);
3555 interp->framePtr = savedFramePtr;
3556
3557 return objPtr;
3558 }
3559
3560 /* Unset a variable.
3561 * Note: On success unset invalidates all the variable objects created
3562 * in the current call frame incrementing. */
3563 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3564 {
3565 const char *name;
3566 Jim_Var *varPtr;
3567 int err;
3568
3569 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3570 /* Check for [dict] syntax sugar. */
3571 if (err == JIM_DICT_SUGAR)
3572 return JimDictSugarSet(interp, nameObjPtr, NULL);
3573 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3574 Jim_AppendStrings(interp, Jim_GetResult(interp),
3575 "can't unset \"", nameObjPtr->bytes,
3576 "\": no such variable", NULL);
3577 return JIM_ERR; /* var not found */
3578 }
3579 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3580 /* If it's a link call UnsetVariable recursively */
3581 if (varPtr->linkFramePtr) {
3582 int retval;
3583
3584 Jim_CallFrame *savedCallFrame;
3585
3586 savedCallFrame = interp->framePtr;
3587 interp->framePtr = varPtr->linkFramePtr;
3588 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3589 interp->framePtr = savedCallFrame;
3590 if (retval != JIM_OK && flags & JIM_ERRMSG) {
3591 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3592 Jim_AppendStrings(interp, Jim_GetResult(interp),
3593 "can't unset \"", nameObjPtr->bytes,
3594 "\": no such variable", NULL);
3595 }
3596 return retval;
3597 } else {
3598 name = Jim_GetString(nameObjPtr, NULL);
3599 if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3600 != JIM_OK) return JIM_ERR;
3601 /* Change the callframe id, invalidating var lookup caching */
3602 JimChangeCallFrameId(interp, interp->framePtr);
3603 return JIM_OK;
3604 }
3605 }
3606
3607 /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
3608
3609 /* Given a variable name for [dict] operation syntax sugar,
3610 * this function returns two objects, the first with the name
3611 * of the variable to set, and the second with the rispective key.
3612 * For example "foo(bar)" will return objects with string repr. of
3613 * "foo" and "bar".
3614 *
3615 * The returned objects have refcount = 1. The function can't fail. */
3616 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3617 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3618 {
3619 const char *str, *p;
3620 char *t;
3621 int len, keyLen, nameLen;
3622 Jim_Obj *varObjPtr, *keyObjPtr;
3623
3624 str = Jim_GetString(objPtr, &len);
3625 p = strchr(str, '(');
3626 p++;
3627 keyLen = len-((p-str)+1);
3628 nameLen = (p-str)-1;
3629 /* Create the objects with the variable name and key. */
3630 t = Jim_Alloc(nameLen+1);
3631 memcpy(t, str, nameLen);
3632 t[nameLen] = '\0';
3633 varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3634
3635 t = Jim_Alloc(keyLen+1);
3636 memcpy(t, p, keyLen);
3637 t[keyLen] = '\0';
3638 keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3639
3640 Jim_IncrRefCount(varObjPtr);
3641 Jim_IncrRefCount(keyObjPtr);
3642 *varPtrPtr = varObjPtr;
3643 *keyPtrPtr = keyObjPtr;
3644 }
3645
3646 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3647 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3648 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3649 Jim_Obj *valObjPtr)
3650 {
3651 Jim_Obj *varObjPtr, *keyObjPtr;
3652 int err = JIM_OK;
3653
3654 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3655 err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3656 valObjPtr);
3657 Jim_DecrRefCount(interp, varObjPtr);
3658 Jim_DecrRefCount(interp, keyObjPtr);
3659 return err;
3660 }
3661
3662 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3663 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3664 {
3665 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3666
3667 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3668 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3669 if (!dictObjPtr) {
3670 resObjPtr = NULL;
3671 goto err;
3672 }
3673 if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3674 != JIM_OK) {
3675 resObjPtr = NULL;
3676 }
3677 err:
3678 Jim_DecrRefCount(interp, varObjPtr);
3679 Jim_DecrRefCount(interp, keyObjPtr);
3680 return resObjPtr;
3681 }
3682
3683 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3684
3685 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3686 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3687 Jim_Obj *dupPtr);
3688
3689 static Jim_ObjType dictSubstObjType = {
3690 "dict-substitution",
3691 FreeDictSubstInternalRep,
3692 DupDictSubstInternalRep,
3693 NULL,
3694 JIM_TYPE_NONE,
3695 };
3696
3697 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3698 {
3699 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3700 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3701 }
3702
3703 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3704 Jim_Obj *dupPtr)
3705 {
3706 JIM_NOTUSED(interp);
3707
3708 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3709 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3710 dupPtr->internalRep.dictSubstValue.indexObjPtr =
3711 srcPtr->internalRep.dictSubstValue.indexObjPtr;
3712 dupPtr->typePtr = &dictSubstObjType;
3713 }
3714
3715 /* This function is used to expand [dict get] sugar in the form
3716 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3717 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3718 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3719 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3720 * the [dict]ionary contained in variable VARNAME. */
3721 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3722 {
3723 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3724 Jim_Obj *substKeyObjPtr = NULL;
3725
3726 if (objPtr->typePtr != &dictSubstObjType) {
3727 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3728 Jim_FreeIntRep(interp, objPtr);
3729 objPtr->typePtr = &dictSubstObjType;
3730 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3731 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3732 }
3733 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3734 &substKeyObjPtr, JIM_NONE)
3735 != JIM_OK) {
3736 substKeyObjPtr = NULL;
3737 goto err;
3738 }
3739 Jim_IncrRefCount(substKeyObjPtr);
3740 dictObjPtr = Jim_GetVariable(interp,
3741 objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3742 if (!dictObjPtr) {
3743 resObjPtr = NULL;
3744 goto err;
3745 }
3746 if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3747 != JIM_OK) {
3748 resObjPtr = NULL;
3749 goto err;
3750 }
3751 err:
3752 if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3753 return resObjPtr;
3754 }
3755
3756 /* -----------------------------------------------------------------------------
3757 * CallFrame
3758 * ---------------------------------------------------------------------------*/
3759
3760 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3761 {
3762 Jim_CallFrame *cf;
3763 if (interp->freeFramesList) {
3764 cf = interp->freeFramesList;
3765 interp->freeFramesList = cf->nextFramePtr;
3766 } else {
3767 cf = Jim_Alloc(sizeof(*cf));
3768 cf->vars.table = NULL;
3769 }
3770
3771 cf->id = interp->callFrameEpoch++;
3772 cf->parentCallFrame = NULL;
3773 cf->argv = NULL;
3774 cf->argc = 0;
3775 cf->procArgsObjPtr = NULL;
3776 cf->procBodyObjPtr = NULL;
3777 cf->nextFramePtr = NULL;
3778 cf->staticVars = NULL;
3779 if (cf->vars.table == NULL)
3780 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3781 return cf;
3782 }
3783
3784 /* Used to invalidate every caching related to callframe stability. */
3785 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3786 {
3787 cf->id = interp->callFrameEpoch++;
3788 }
3789
3790 #define JIM_FCF_NONE 0 /* no flags */
3791 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3792 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3793 int flags)
3794 {
3795 if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3796 if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3797 if (!(flags & JIM_FCF_NOHT))
3798 Jim_FreeHashTable(&cf->vars);
3799 else {
3800 int i;
3801 Jim_HashEntry **table = cf->vars.table, *he;
3802
3803 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3804 he = table[i];
3805 while (he != NULL) {
3806 Jim_HashEntry *nextEntry = he->next;
3807 Jim_Var *varPtr = (void*) he->val;
3808
3809 Jim_DecrRefCount(interp, varPtr->objPtr);
3810 Jim_Free(he->val);
3811 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3812 Jim_Free(he);
3813 table[i] = NULL;
3814 he = nextEntry;
3815 }
3816 }
3817 cf->vars.used = 0;
3818 }
3819 cf->nextFramePtr = interp->freeFramesList;
3820 interp->freeFramesList = cf;
3821 }
3822
3823 /* -----------------------------------------------------------------------------
3824 * References
3825 * ---------------------------------------------------------------------------*/
3826
3827 /* References HashTable Type.
3828 *
3829 * Keys are jim_wide integers, dynamically allocated for now but in the
3830 * future it's worth to cache this 8 bytes objects. Values are poitners
3831 * to Jim_References. */
3832 static void JimReferencesHTValDestructor(void *interp, void *val)
3833 {
3834 Jim_Reference *refPtr = (void*) val;
3835
3836 Jim_DecrRefCount(interp, refPtr->objPtr);
3837 if (refPtr->finalizerCmdNamePtr != NULL) {
3838 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3839 }
3840 Jim_Free(val);
3841 }
3842
3843 unsigned int JimReferencesHTHashFunction(const void *key)
3844 {
3845 /* Only the least significant bits are used. */
3846 const jim_wide *widePtr = key;
3847 unsigned int intValue = (unsigned int) *widePtr;
3848 return Jim_IntHashFunction(intValue);
3849 }
3850
3851 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3852 {
3853 /* Only the least significant bits are used. */
3854 const jim_wide *widePtr = key;
3855 unsigned int intValue = (unsigned int) *widePtr;
3856 return intValue; /* identity function. */
3857 }
3858
3859 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3860 {
3861 void *copy = Jim_Alloc(sizeof(jim_wide));
3862 JIM_NOTUSED(privdata);
3863
3864 memcpy(copy, key, sizeof(jim_wide));
3865 return copy;
3866 }
3867
3868 int JimReferencesHTKeyCompare(void *privdata, const void *key1,
3869 const void *key2)
3870 {
3871 JIM_NOTUSED(privdata);
3872
3873 return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3874 }
3875
3876 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3877 {
3878 JIM_NOTUSED(privdata);
3879
3880 Jim_Free((void*)key);
3881 }
3882
3883 static Jim_HashTableType JimReferencesHashTableType = {
3884 JimReferencesHTHashFunction, /* hash function */
3885 JimReferencesHTKeyDup, /* key dup */
3886 NULL, /* val dup */
3887 JimReferencesHTKeyCompare, /* key compare */
3888 JimReferencesHTKeyDestructor, /* key destructor */
3889 JimReferencesHTValDestructor /* val destructor */
3890 };
3891
3892 /* -----------------------------------------------------------------------------
3893 * Reference object type and References API
3894 * ---------------------------------------------------------------------------*/
3895
3896 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3897
3898 static Jim_ObjType referenceObjType = {
3899 "reference",
3900 NULL,
3901 NULL,
3902 UpdateStringOfReference,
3903 JIM_TYPE_REFERENCES,
3904 };
3905
3906 void UpdateStringOfReference(struct Jim_Obj *objPtr)
3907 {
3908 int len;
3909 char buf[JIM_REFERENCE_SPACE+1];
3910 Jim_Reference *refPtr;
3911
3912 refPtr = objPtr->internalRep.refValue.refPtr;
3913 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3914 objPtr->bytes = Jim_Alloc(len+1);
3915 memcpy(objPtr->bytes, buf, len+1);
3916 objPtr->length = len;
3917 }
3918
3919 /* returns true if 'c' is a valid reference tag character.
3920 * i.e. inside the range [_a-zA-Z0-9] */
3921 static int isrefchar(int c)
3922 {
3923 if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3924 (c >= '0' && c <= '9')) return 1;
3925 return 0;
3926 }
3927
3928 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3929 {
3930 jim_wide wideValue;
3931 int i, len;
3932 const char *str, *start, *end;
3933 char refId[21];
3934 Jim_Reference *refPtr;
3935 Jim_HashEntry *he;
3936
3937 /* Get the string representation */
3938 str = Jim_GetString(objPtr, &len);
3939 /* Check if it looks like a reference */
3940 if (len < JIM_REFERENCE_SPACE) goto badformat;
3941 /* Trim spaces */
3942 start = str;
3943 end = str+len-1;
3944 while (*start == ' ') start++;
3945 while (*end == ' ' && end > start) end--;
3946 if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3947 /* <reference.<1234567>.%020> */
3948 if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3949 if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3950 /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3951 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3952 if (!isrefchar(start[12+i])) goto badformat;
3953 }
3954 /* Extract info from the refernece. */
3955 memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3956 refId[20] = '\0';
3957 /* Try to convert the ID into a jim_wide */
3958 if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3959 /* Check if the reference really exists! */
3960 he = Jim_FindHashEntry(&interp->references, &wideValue);
3961 if (he == NULL) {
3962 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3963 Jim_AppendStrings(interp, Jim_GetResult(interp),
3964 "Invalid reference ID \"", str, "\"", NULL);
3965 return JIM_ERR;
3966 }
3967 refPtr = he->val;
3968 /* Free the old internal repr and set the new one. */
3969 Jim_FreeIntRep(interp, objPtr);
3970 objPtr->typePtr = &referenceObjType;
3971 objPtr->internalRep.refValue.id = wideValue;
3972 objPtr->internalRep.refValue.refPtr = refPtr;
3973 return JIM_OK;
3974
3975 badformat:
3976 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3977 Jim_AppendStrings(interp, Jim_GetResult(interp),
3978 "expected reference but got \"", str, "\"", NULL);
3979 return JIM_ERR;
3980 }
3981
3982 /* Returns a new reference pointing to objPtr, having cmdNamePtr
3983 * as finalizer command (or NULL if there is no finalizer).
3984 * The returned reference object has refcount = 0. */
3985 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
3986 Jim_Obj *cmdNamePtr)
3987 {
3988 struct Jim_Reference *refPtr;
3989 jim_wide wideValue = interp->referenceNextId;
3990 Jim_Obj *refObjPtr;
3991 const char *tag;
3992 int tagLen, i;
3993
3994 /* Perform the Garbage Collection if needed. */
3995 Jim_CollectIfNeeded(interp);
3996
3997 refPtr = Jim_Alloc(sizeof(*refPtr));
3998 refPtr->objPtr = objPtr;
3999 Jim_IncrRefCount(objPtr);
4000 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4001 if (cmdNamePtr)
4002 Jim_IncrRefCount(cmdNamePtr);
4003 Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4004 refObjPtr = Jim_NewObj(interp);
4005 refObjPtr->typePtr = &referenceObjType;
4006 refObjPtr->bytes = NULL;
4007 refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4008 refObjPtr->internalRep.refValue.refPtr = refPtr;
4009 interp->referenceNextId++;
4010 /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4011 * that does not pass the 'isrefchar' test is replaced with '_' */
4012 tag = Jim_GetString(tagPtr, &tagLen);
4013 if (tagLen > JIM_REFERENCE_TAGLEN)
4014 tagLen = JIM_REFERENCE_TAGLEN;
4015 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4016 if (i < tagLen)
4017 refPtr->tag[i] = tag[i];
4018 else
4019 refPtr->tag[i] = '_';
4020 }
4021 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4022 return refObjPtr;
4023 }
4024
4025 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4026 {
4027 if (objPtr->typePtr != &referenceObjType &&
4028 SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4029 return NULL;
4030 return objPtr->internalRep.refValue.refPtr;
4031 }
4032
4033 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4034 {
4035 Jim_Reference *refPtr;
4036
4037 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4038 return JIM_ERR;
4039 Jim_IncrRefCount(cmdNamePtr);
4040 if (refPtr->finalizerCmdNamePtr)
4041 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4042 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4043 return JIM_OK;
4044 }
4045
4046 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4047 {
4048 Jim_Reference *refPtr;
4049
4050 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4051 return JIM_ERR;
4052 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4053 return JIM_OK;
4054 }
4055
4056 /* -----------------------------------------------------------------------------
4057 * References Garbage Collection
4058 * ---------------------------------------------------------------------------*/
4059
4060 /* This the hash table type for the "MARK" phase of the GC */
4061 static Jim_HashTableType JimRefMarkHashTableType = {
4062 JimReferencesHTHashFunction, /* hash function */
4063 JimReferencesHTKeyDup, /* key dup */
4064 NULL, /* val dup */
4065 JimReferencesHTKeyCompare, /* key compare */
4066 JimReferencesHTKeyDestructor, /* key destructor */
4067 NULL /* val destructor */
4068 };
4069
4070 /* #define JIM_DEBUG_GC 1 */
4071
4072 /* Performs the garbage collection. */
4073 int Jim_Collect(Jim_Interp *interp)
4074 {
4075 Jim_HashTable marks;
4076 Jim_HashTableIterator *htiter;
4077 Jim_HashEntry *he;
4078 Jim_Obj *objPtr;
4079 int collected = 0;
4080
4081 /* Avoid recursive calls */
4082 if (interp->lastCollectId == -1) {
4083 /* Jim_Collect() already running. Return just now. */
4084 return 0;
4085 }
4086 interp->lastCollectId = -1;
4087
4088 /* Mark all the references found into the 'mark' hash table.
4089 * The references are searched in every live object that
4090 * is of a type that can contain references. */
4091 Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4092 objPtr = interp->liveList;
4093 while(objPtr) {
4094 if (objPtr->typePtr == NULL ||
4095 objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4096 const char *str, *p;
4097 int len;
4098
4099 /* If the object is of type reference, to get the
4100 * Id is simple... */
4101 if (objPtr->typePtr == &referenceObjType) {
4102 Jim_AddHashEntry(&marks,
4103 &objPtr->internalRep.refValue.id, NULL);
4104 #ifdef JIM_DEBUG_GC
4105 Jim_fprintf(interp,interp->cookie_stdout,
4106 "MARK (reference): %d refcount: %d" JIM_NL,
4107 (int) objPtr->internalRep.refValue.id,
4108 objPtr->refCount);
4109 #endif
4110 objPtr = objPtr->nextObjPtr;
4111 continue;
4112 }
4113 /* Get the string repr of the object we want
4114 * to scan for references. */
4115 p = str = Jim_GetString(objPtr, &len);
4116 /* Skip objects too little to contain references. */
4117 if (len < JIM_REFERENCE_SPACE) {
4118 objPtr = objPtr->nextObjPtr;
4119 continue;
4120 }
4121 /* Extract references from the object string repr. */
4122 while(1) {
4123 int i;
4124 jim_wide id;
4125 char buf[21];
4126
4127 if ((p = strstr(p, "<reference.<")) == NULL)
4128 break;
4129 /* Check if it's a valid reference. */
4130 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4131 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4132 for (i = 21; i <= 40; i++)
4133 if (!isdigit((int)p[i]))
4134 break;
4135 /* Get the ID */
4136 memcpy(buf, p+21, 20);
4137 buf[20] = '\0';
4138 Jim_StringToWide(buf, &id, 10);
4139
4140 /* Ok, a reference for the given ID
4141 * was found. Mark it. */
4142 Jim_AddHashEntry(&marks, &id, NULL);
4143 #ifdef JIM_DEBUG_GC
4144 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4145 #endif
4146 p += JIM_REFERENCE_SPACE;
4147 }
4148 }
4149 objPtr = objPtr->nextObjPtr;
4150 }
4151
4152 /* Run the references hash table to destroy every reference that
4153 * is not referenced outside (not present in the mark HT). */
4154 htiter = Jim_GetHashTableIterator(&interp->references);
4155 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4156 const jim_wide *refId;
4157 Jim_Reference *refPtr;
4158
4159 refId = he->key;
4160 /* Check if in the mark phase we encountered
4161 * this reference. */
4162 if (Jim_FindHashEntry(&marks, refId) == NULL) {
4163 #ifdef JIM_DEBUG_GC
4164 Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4165 #endif
4166 collected++;
4167 /* Drop the reference, but call the
4168 * finalizer first if registered. */
4169 refPtr = he->val;
4170 if (refPtr->finalizerCmdNamePtr) {
4171 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4172 Jim_Obj *objv[3], *oldResult;
4173
4174 JimFormatReference(refstr, refPtr, *refId);
4175
4176 objv[0] = refPtr->finalizerCmdNamePtr;
4177 objv[1] = Jim_NewStringObjNoAlloc(interp,
4178 refstr, 32);
4179 objv[2] = refPtr->objPtr;
4180 Jim_IncrRefCount(objv[0]);
4181 Jim_IncrRefCount(objv[1]);
4182 Jim_IncrRefCount(objv[2]);
4183
4184 /* Drop the reference itself */
4185 Jim_DeleteHashEntry(&interp->references, refId);
4186
4187 /* Call the finalizer. Errors ignored. */
4188 oldResult = interp->result;
4189 Jim_IncrRefCount(oldResult);
4190 Jim_EvalObjVector(interp, 3, objv);
4191 Jim_SetResult(interp, oldResult);
4192 Jim_DecrRefCount(interp, oldResult);
4193
4194 Jim_DecrRefCount(interp, objv[0]);
4195 Jim_DecrRefCount(interp, objv[1]);
4196 Jim_DecrRefCount(interp, objv[2]);
4197 } else {
4198 Jim_DeleteHashEntry(&interp->references, refId);
4199 }
4200 }
4201 }
4202 Jim_FreeHashTableIterator(htiter);
4203 Jim_FreeHashTable(&marks);
4204 interp->lastCollectId = interp->referenceNextId;
4205 interp->lastCollectTime = time(NULL);
4206 return collected;
4207 }
4208
4209 #define JIM_COLLECT_ID_PERIOD 5000
4210 #define JIM_COLLECT_TIME_PERIOD 300
4211
4212 void Jim_CollectIfNeeded(Jim_Interp *interp)
4213 {
4214 jim_wide elapsedId;
4215 int elapsedTime;
4216
4217 elapsedId = interp->referenceNextId - interp->lastCollectId;
4218 elapsedTime = time(NULL) - interp->lastCollectTime;
4219
4220
4221 if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4222 elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4223 Jim_Collect(interp);
4224 }
4225 }
4226
4227 /* -----------------------------------------------------------------------------
4228 * Interpreter related functions
4229 * ---------------------------------------------------------------------------*/
4230
4231 Jim_Interp *Jim_CreateInterp(void)
4232 {
4233 Jim_Interp *i = Jim_Alloc(sizeof(*i));
4234 Jim_Obj *pathPtr;
4235
4236 i->errorLine = 0;
4237 i->errorFileName = Jim_StrDup("");
4238 i->numLevels = 0;
4239 i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4240 i->returnCode = JIM_OK;
4241 i->exitCode = 0;
4242 i->procEpoch = 0;
4243 i->callFrameEpoch = 0;
4244 i->liveList = i->freeList = NULL;
4245 i->scriptFileName = Jim_StrDup("");
4246 i->referenceNextId = 0;
4247 i->lastCollectId = 0;
4248 i->lastCollectTime = time(NULL);
4249 i->freeFramesList = NULL;
4250 i->prngState = NULL;
4251 i->evalRetcodeLevel = -1;
4252 i->cookie_stdin = stdin;
4253 i->cookie_stdout = stdout;
4254 i->cookie_stderr = stderr;
4255 i->cb_fwrite = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4256 i->cb_fread = ((size_t (*)( void *, size_t, size_t, void *))(fread));
4257 i->cb_vfprintf = ((int (*)( void *, const char *fmt, va_list ))(vfprintf));
4258 i->cb_fflush = ((int (*)( void *))(fflush));
4259 i->cb_fgets = ((char * (*)( char *, int, void *))(fgets));
4260
4261 /* Note that we can create objects only after the
4262 * interpreter liveList and freeList pointers are
4263 * initialized to NULL. */
4264 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4265 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4266 Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4267 NULL);
4268 Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4269 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4270 Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4271 i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4272 i->emptyObj = Jim_NewEmptyStringObj(i);
4273 i->result = i->emptyObj;
4274 i->stackTrace = Jim_NewListObj(i, NULL, 0);
4275 i->unknown = Jim_NewStringObj(i, "unknown", -1);
4276 Jim_IncrRefCount(i->emptyObj);
4277 Jim_IncrRefCount(i->result);
4278 Jim_IncrRefCount(i->stackTrace);
4279 Jim_IncrRefCount(i->unknown);
4280
4281 /* Initialize key variables every interpreter should contain */
4282 pathPtr = Jim_NewStringObj(i, "./", -1);
4283 Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4284 Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4285
4286 /* Export the core API to extensions */
4287 JimRegisterCoreApi(i);
4288 return i;
4289 }
4290
4291 /* This is the only function Jim exports directly without
4292 * to use the STUB system. It is only used by embedders
4293 * in order to get an interpreter with the Jim API pointers
4294 * registered. */
4295 Jim_Interp *ExportedJimCreateInterp(void)
4296 {
4297 return Jim_CreateInterp();
4298 }
4299
4300 void Jim_FreeInterp(Jim_Interp *i)
4301 {
4302 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4303 Jim_Obj *objPtr, *nextObjPtr;
4304
4305 Jim_DecrRefCount(i, i->emptyObj);
4306 Jim_DecrRefCount(i, i->result);
4307 Jim_DecrRefCount(i, i->stackTrace);
4308 Jim_DecrRefCount(i, i->unknown);
4309 Jim_Free((void*)i->errorFileName);
4310 Jim_Free((void*)i->scriptFileName);
4311 Jim_FreeHashTable(&i->commands);
4312 Jim_FreeHashTable(&i->references);
4313 Jim_FreeHashTable(&i->stub);
4314 Jim_FreeHashTable(&i->assocData);
4315 Jim_FreeHashTable(&i->packages);
4316 Jim_Free(i->prngState);
4317 /* Free the call frames list */
4318 while(cf) {
4319 prevcf = cf->parentCallFrame;
4320 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4321 cf = prevcf;
4322 }
4323 /* Check that the live object list is empty, otherwise
4324 * there is a memory leak. */
4325 if (i->liveList != NULL) {
4326 Jim_Obj *objPtr = i->liveList;
4327
4328 Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4329 Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4330 while(objPtr) {
4331 const char *type = objPtr->typePtr ?
4332 objPtr->typePtr->name : "";
4333 Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4334 objPtr, type,
4335 objPtr->bytes ? objPtr->bytes
4336 : "(null)", objPtr->refCount);
4337 if (objPtr->typePtr == &sourceObjType) {
4338 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4339 objPtr->internalRep.sourceValue.fileName,
4340 objPtr->internalRep.sourceValue.lineNumber);
4341 }
4342 objPtr = objPtr->nextObjPtr;
4343 }
4344 Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4345 Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4346 }
4347 /* Free all the freed objects. */
4348 objPtr = i->freeList;
4349 while (objPtr) {
4350 nextObjPtr = objPtr->nextObjPtr;
4351 Jim_Free(objPtr);
4352 objPtr = nextObjPtr;
4353 }
4354 /* Free cached CallFrame structures */
4355 cf = i->freeFramesList;
4356 while(cf) {
4357 nextcf = cf->nextFramePtr;
4358 if (cf->vars.table != NULL)
4359 Jim_Free(cf->vars.table);
4360 Jim_Free(cf);
4361 cf = nextcf;
4362 }
4363 /* Free the sharedString hash table. Make sure to free it
4364 * after every other Jim_Object was freed. */
4365 Jim_FreeHashTable(&i->sharedStrings);
4366 /* Free the interpreter structure. */
4367 Jim_Free(i);
4368 }
4369
4370 /* Store the call frame relative to the level represented by
4371 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4372 * level is assumed to be '1'.
4373 *
4374 * If a newLevelptr int pointer is specified, the function stores
4375 * the absolute level integer value of the new target callframe into
4376 * *newLevelPtr. (this is used to adjust interp->numLevels
4377 * in the implementation of [uplevel], so that [info level] will
4378 * return a correct information).
4379 *
4380 * This function accepts the 'level' argument in the form
4381 * of the commands [uplevel] and [upvar].
4382 *
4383 * For a function accepting a relative integer as level suitable
4384 * for implementation of [info level ?level?] check the
4385 * GetCallFrameByInteger() function. */
4386 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4387 Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4388 {
4389 long level;
4390 const char *str;
4391 Jim_CallFrame *framePtr;
4392
4393 if (newLevelPtr) *newLevelPtr = interp->numLevels;
4394 if (levelObjPtr) {
4395 str = Jim_GetString(levelObjPtr, NULL);
4396 if (str[0] == '#') {
4397 char *endptr;
4398 /* speedup for the toplevel (level #0) */
4399 if (str[1] == '0' && str[2] == '\0') {
4400 if (newLevelPtr) *newLevelPtr = 0;
4401 *framePtrPtr = interp->topFramePtr;
4402 return JIM_OK;
4403 }
4404
4405 level = strtol(str+1, &endptr, 0);
4406 if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4407 goto badlevel;
4408 /* An 'absolute' level is converted into the
4409 * 'number of levels to go back' format. */
4410 level = interp->numLevels - level;
4411 if (level < 0) goto badlevel;
4412 } else {
4413 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4414 goto badlevel;
4415 }
4416 } else {
4417 str = "1"; /* Needed to format the error message. */
4418 level = 1;
4419 }
4420 /* Lookup */
4421 framePtr = interp->framePtr;
4422 if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4423 while (level--) {
4424 framePtr = framePtr->parentCallFrame;
4425 if (framePtr == NULL) goto badlevel;
4426 }
4427 *framePtrPtr = framePtr;
4428 return JIM_OK;
4429 badlevel:
4430 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4431 Jim_AppendStrings(interp, Jim_GetResult(interp),
4432 "bad level \"", str, "\"", NULL);
4433 return JIM_ERR;
4434 }
4435
4436 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4437 * as a relative integer like in the [info level ?level?] command. */
4438 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4439 Jim_CallFrame **framePtrPtr)
4440 {
4441 jim_wide level;
4442 jim_wide relLevel; /* level relative to the current one. */
4443 Jim_CallFrame *framePtr;
4444
4445 if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4446 goto badlevel;
4447 if (level > 0) {
4448 /* An 'absolute' level is converted into the
4449 * 'number of levels to go back' format. */
4450 relLevel = interp->numLevels - level;
4451 } else {
4452 relLevel = -level;
4453 }
4454 /* Lookup */
4455 framePtr = interp->framePtr;
4456 while (relLevel--) {
4457 framePtr = framePtr->parentCallFrame;
4458 if (framePtr == NULL) goto badlevel;
4459 }
4460 *framePtrPtr = framePtr;
4461 return JIM_OK;
4462 badlevel:
4463 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4464 Jim_AppendStrings(interp, Jim_GetResult(interp),
4465 "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4466 return JIM_ERR;
4467 }
4468
4469 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4470 {
4471 Jim_Free((void*)interp->errorFileName);
4472 interp->errorFileName = Jim_StrDup(filename);
4473 }
4474
4475 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4476 {
4477 interp->errorLine = linenr;
4478 }
4479
4480 static void JimResetStackTrace(Jim_Interp *interp)
4481 {
4482 Jim_DecrRefCount(interp, interp->stackTrace);
4483 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4484 Jim_IncrRefCount(interp->stackTrace);
4485 }
4486
4487 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4488 const char *filename, int linenr)
4489 {
4490 if (Jim_IsShared(interp->stackTrace)) {
4491 interp->stackTrace =
4492 Jim_DuplicateObj(interp, interp->stackTrace);
4493 Jim_IncrRefCount(interp->stackTrace);
4494 }
4495 Jim_ListAppendElement(interp, interp->stackTrace,
4496 Jim_NewStringObj(interp, procname, -1));
4497 Jim_ListAppendElement(interp, interp->stackTrace,
4498 Jim_NewStringObj(interp, filename, -1));
4499 Jim_ListAppendElement(interp, interp->stackTrace,
4500 Jim_NewIntObj(interp, linenr));
4501 }
4502
4503 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4504 {
4505 AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4506 assocEntryPtr->delProc = delProc;
4507 assocEntryPtr->data = data;
4508 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4509 }
4510
4511 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4512 {
4513 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4514 if (entryPtr != NULL) {
4515 AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4516 return assocEntryPtr->data;
4517 }
4518 return NULL;
4519 }
4520
4521 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4522 {
4523 return Jim_DeleteHashEntry(&interp->assocData, key);
4524 }
4525
4526 int Jim_GetExitCode(Jim_Interp *interp) {
4527 return interp->exitCode;
4528 }
4529
4530 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4531 {
4532 if (fp != NULL) interp->cookie_stdin = fp;
4533 return interp->cookie_stdin;
4534 }
4535
4536 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4537 {
4538 if (fp != NULL) interp->cookie_stdout = fp;
4539 return interp->cookie_stdout;
4540 }
4541
4542 void *Jim_SetStderr(Jim_Interp *interp, void *fp)
4543 {
4544 if (fp != NULL) interp->cookie_stderr = fp;
4545 return interp->cookie_stderr;
4546 }
4547
4548 /* -----------------------------------------------------------------------------
4549 * Shared strings.
4550 * Every interpreter has an hash table where to put shared dynamically
4551 * allocate strings that are likely to be used a lot of times.
4552 * For example, in the 'source' object type, there is a pointer to
4553 * the filename associated with that object. Every script has a lot
4554 * of this objects with the identical file name, so it is wise to share
4555 * this info.
4556 *
4557 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4558 * returns the pointer to the shared string. Every time a reference
4559 * to the string is no longer used, the user should call
4560 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4561 * a given string, it is removed from the hash table.
4562 * ---------------------------------------------------------------------------*/
4563 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4564 {
4565 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4566
4567 if (he == NULL) {
4568 char *strCopy = Jim_StrDup(str);
4569
4570 Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4571 return strCopy;
4572 } else {
4573 long refCount = (long) he->val;
4574
4575 refCount++;
4576 he->val = (void*) refCount;
4577 return he->key;
4578 }
4579 }
4580
4581 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4582 {
4583 long refCount;
4584 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4585
4586 if (he == NULL)
4587 Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4588 "unknown shared string '%s'", str);
4589 refCount = (long) he->val;
4590 refCount--;
4591 if (refCount == 0) {
4592 Jim_DeleteHashEntry(&interp->sharedStrings, str);
4593 } else {
4594 he->val = (void*) refCount;
4595 }
4596 }
4597
4598 /* -----------------------------------------------------------------------------
4599 * Integer object
4600 * ---------------------------------------------------------------------------*/
4601 #define JIM_INTEGER_SPACE 24
4602
4603 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4604 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4605
4606 static Jim_ObjType intObjType = {
4607 "int",
4608 NULL,
4609 NULL,
4610 UpdateStringOfInt,
4611 JIM_TYPE_NONE,
4612 };
4613
4614 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4615 {
4616 int len;
4617 char buf[JIM_INTEGER_SPACE+1];
4618
4619 len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4620 objPtr->bytes = Jim_Alloc(len+1);
4621 memcpy(objPtr->bytes, buf, len+1);
4622 objPtr->length = len;
4623 }
4624
4625 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4626 {
4627 jim_wide wideValue;
4628 const char *str;
4629
4630 /* Get the string representation */
4631 str = Jim_GetString(objPtr, NULL);
4632 /* Try to convert into a jim_wide */
4633 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4634 if (flags & JIM_ERRMSG) {
4635 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4636 Jim_AppendStrings(interp, Jim_GetResult(interp),
4637 "expected integer but got \"", str, "\"", NULL);
4638 }
4639 return JIM_ERR;
4640 }
4641 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4642 errno == ERANGE) {
4643 Jim_SetResultString(interp,
4644 "Integer value too big to be represented", -1);
4645 return JIM_ERR;
4646 }
4647 /* Free the old internal repr and set the new one. */
4648 Jim_FreeIntRep(interp, objPtr);
4649 objPtr->typePtr = &intObjType;
4650 objPtr->internalRep.wideValue = wideValue;
4651 return JIM_OK;
4652 }
4653
4654 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4655 {
4656 if (objPtr->typePtr != &intObjType &&
4657 SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4658 return JIM_ERR;
4659 *widePtr = objPtr->internalRep.wideValue;
4660 return JIM_OK;
4661 }
4662
4663 /* Get a wide but does not set an error if the format is bad. */
4664 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4665 jim_wide *widePtr)
4666 {
4667 if (objPtr->typePtr != &intObjType &&
4668 SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4669 return JIM_ERR;
4670 *widePtr = objPtr->internalRep.wideValue;
4671 return JIM_OK;
4672 }
4673
4674 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4675 {
4676 jim_wide wideValue;
4677 int retval;
4678
4679 retval = Jim_GetWide(interp, objPtr, &wideValue);
4680 if (retval == JIM_OK) {
4681 *longPtr = (long) wideValue;
4682 return JIM_OK;
4683 }
4684 return JIM_ERR;
4685 }
4686
4687 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4688 {
4689 if (Jim_IsShared(objPtr))
4690 Jim_Panic(interp,"Jim_SetWide called with shared object");
4691 if (objPtr->typePtr != &intObjType) {
4692 Jim_FreeIntRep(interp, objPtr);
4693 objPtr->typePtr = &intObjType;
4694 }
4695 Jim_InvalidateStringRep(objPtr);
4696 objPtr->internalRep.wideValue = wideValue;
4697 }
4698
4699 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4700 {
4701 Jim_Obj *objPtr;
4702
4703 objPtr = Jim_NewObj(interp);
4704 objPtr->typePtr = &intObjType;
4705 objPtr->bytes = NULL;
4706 objPtr->internalRep.wideValue = wideValue;
4707 return objPtr;
4708 }
4709
4710 /* -----------------------------------------------------------------------------
4711 * Double object
4712 * ---------------------------------------------------------------------------*/
4713 #define JIM_DOUBLE_SPACE 30
4714
4715 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4716 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4717
4718 static Jim_ObjType doubleObjType = {
4719 "double",
4720 NULL,
4721 NULL,
4722 UpdateStringOfDouble,
4723 JIM_TYPE_NONE,
4724 };
4725
4726 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4727 {
4728 int len;
4729 char buf[JIM_DOUBLE_SPACE+1];
4730
4731 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4732 objPtr->bytes = Jim_Alloc(len+1);
4733 memcpy(objPtr->bytes, buf, len+1);
4734 objPtr->length = len;
4735 }
4736
4737 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4738 {
4739 double doubleValue;
4740 const char *str;
4741
4742 /* Get the string representation */
4743 str = Jim_GetString(objPtr, NULL);
4744 /* Try to convert into a double */
4745 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4746 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4747 Jim_AppendStrings(interp, Jim_GetResult(interp),
4748 "expected number but got '", str, "'", NULL);
4749 return JIM_ERR;
4750 }
4751 /* Free the old internal repr and set the new one. */
4752 Jim_FreeIntRep(interp, objPtr);
4753 objPtr->typePtr = &doubleObjType;
4754 objPtr->internalRep.doubleValue = doubleValue;
4755 return JIM_OK;
4756 }
4757
4758 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4759 {
4760 if (objPtr->typePtr != &doubleObjType &&
4761 SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4762 return JIM_ERR;
4763 *doublePtr = objPtr->internalRep.doubleValue;
4764 return JIM_OK;
4765 }
4766
4767 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4768 {
4769 if (Jim_IsShared(objPtr))
4770 Jim_Panic(interp,"Jim_SetDouble called with shared object");
4771 if (objPtr->typePtr != &doubleObjType) {
4772 Jim_FreeIntRep(interp, objPtr);
4773 objPtr->typePtr = &doubleObjType;
4774 }
4775 Jim_InvalidateStringRep(objPtr);
4776 objPtr->internalRep.doubleValue = doubleValue;
4777 }
4778
4779 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4780 {
4781 Jim_Obj *objPtr;
4782
4783 objPtr = Jim_NewObj(interp);
4784 objPtr->typePtr = &doubleObjType;
4785 objPtr->bytes = NULL;
4786 objPtr->internalRep.doubleValue = doubleValue;
4787 return objPtr;
4788 }
4789
4790 /* -----------------------------------------------------------------------------
4791 * List object
4792 * ---------------------------------------------------------------------------*/
4793 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4794 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4795 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4796 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4797 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4798
4799 /* Note that while the elements of the list may contain references,
4800 * the list object itself can't. This basically means that the
4801 * list object string representation as a whole can't contain references
4802 * that are not presents in the single elements. */
4803 static Jim_ObjType listObjType = {
4804 "list",
4805 FreeListInternalRep,
4806 DupListInternalRep,
4807 UpdateStringOfList,
4808 JIM_TYPE_NONE,
4809 };
4810
4811 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4812 {
4813 int i;
4814
4815 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4816 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4817 }
4818 Jim_Free(objPtr->internalRep.listValue.ele);
4819 }
4820
4821 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4822 {
4823 int i;
4824 JIM_NOTUSED(interp);
4825
4826 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4827 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4828 dupPtr->internalRep.listValue.ele =
4829 Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4830 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4831 sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4832 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4833 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4834 }
4835 dupPtr->typePtr = &listObjType;
4836 }
4837
4838 /* The following function checks if a given string can be encoded
4839 * into a list element without any kind of quoting, surrounded by braces,
4840 * or using escapes to quote. */
4841 #define JIM_ELESTR_SIMPLE 0
4842 #define JIM_ELESTR_BRACE 1
4843 #define JIM_ELESTR_QUOTE 2
4844 static int ListElementQuotingType(const char *s, int len)
4845 {
4846 int i, level, trySimple = 1;
4847
4848 /* Try with the SIMPLE case */
4849 if (len == 0) return JIM_ELESTR_BRACE;
4850 if (s[0] == '"' || s[0] == '{') {
4851 trySimple = 0;
4852 goto testbrace;
4853 }
4854 for (i = 0; i < len; i++) {
4855 switch(s[i]) {
4856 case ' ':
4857 case '$':
4858 case '"':
4859 case '[':
4860 case ']':
4861 case ';':
4862 case '\\':
4863 case '\r':
4864 case '\n':
4865 case '\t':
4866 case '\f':
4867 case '\v':
4868 trySimple = 0;
4869 case '{':
4870 case '}':
4871 goto testbrace;
4872 }
4873 }
4874 return JIM_ELESTR_SIMPLE;
4875
4876 testbrace:
4877 /* Test if it's possible to do with braces */
4878 if (s[len-1] == '\\' ||
4879 s[len-1] == ']') return JIM_ELESTR_QUOTE;
4880 level = 0;
4881 for (i = 0; i < len; i++) {
4882 switch(s[i]) {
4883 case '{': level++; break;
4884 case '}': level--;
4885 if (level < 0) return JIM_ELESTR_QUOTE;
4886 break;
4887 case '\\':
4888 if (s[i+1] == '\n')
4889 return JIM_ELESTR_QUOTE;
4890 else
4891 if (s[i+1] != '\0') i++;
4892 break;
4893 }
4894 }
4895 if (level == 0) {
4896 if (!trySimple) return JIM_ELESTR_BRACE;
4897 for (i = 0; i < len; i++) {
4898 switch(s[i]) {
4899 case ' ':
4900 case '$':
4901 case '"':
4902 case '[':
4903 case ']':
4904 case ';':
4905 case '\\':
4906 case '\r':
4907 case '\n':
4908 case '\t':
4909 case '\f':
4910 case '\v':
4911 return JIM_ELESTR_BRACE;
4912 break;
4913 }
4914 }
4915 return JIM_ELESTR_SIMPLE;
4916 }
4917 return JIM_ELESTR_QUOTE;
4918 }
4919
4920 /* Returns the malloc-ed representation of a string
4921 * using backslash to quote special chars. */
4922 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4923 {
4924 char *q = Jim_Alloc(len*2+1), *p;
4925
4926 p = q;
4927 while(*s) {
4928 switch (*s) {
4929 case ' ':
4930 case '$':
4931 case '"':
4932 case '[':
4933 case ']':
4934 case '{':
4935 case '}':
4936 case ';':
4937 case '\\':
4938 *p++ = '\\';
4939 *p++ = *s++;
4940 break;
4941 case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4942 case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4943 case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4944 case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4945 case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4946 default:
4947 *p++ = *s++;
4948 break;
4949 }
4950 }
4951 *p = '\0';
4952 *qlenPtr = p-q;
4953 return q;
4954 }
4955
4956 void UpdateStringOfList(struct Jim_Obj *objPtr)
4957 {
4958 int i, bufLen, realLength;
4959 const char *strRep;
4960 char *p;
4961 int *quotingType;
4962 Jim_Obj **ele = objPtr->internalRep.listValue.ele;
4963
4964 /* (Over) Estimate the space needed. */
4965 quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
4966 bufLen = 0;
4967 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4968 int len;
4969
4970 strRep = Jim_GetString(ele[i], &len);
4971 quotingType[i] = ListElementQuotingType(strRep, len);
4972 switch (quotingType[i]) {
4973 case JIM_ELESTR_SIMPLE: bufLen += len; break;
4974 case JIM_ELESTR_BRACE: bufLen += len+2; break;
4975 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
4976 }
4977 bufLen++; /* elements separator. */
4978 }
4979 bufLen++;
4980
4981 /* Generate the string rep. */
4982 p = objPtr->bytes = Jim_Alloc(bufLen+1);
4983 realLength = 0;
4984 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4985 int len, qlen;
4986 const char *strRep = Jim_GetString(ele[i], &len);
4987 char *q;
4988
4989 switch(quotingType[i]) {
4990 case JIM_ELESTR_SIMPLE:
4991 memcpy(p, strRep, len);
4992 p += len;
4993 realLength += len;
4994 break;
4995 case JIM_ELESTR_BRACE:
4996 *p++ = '{';
4997 memcpy(p, strRep, len);
4998 p += len;
4999 *p++ = '}';
5000 realLength += len+2;
5001 break;
5002 case JIM_ELESTR_QUOTE:
5003 q = BackslashQuoteString(strRep, len, &qlen);
5004 memcpy(p, q, qlen);
5005 Jim_Free(q);
5006 p += qlen;
5007 realLength += qlen;
5008 break;
5009 }
5010 /* Add a separating space */
5011 if (i+1 != objPtr->internalRep.listValue.len) {
5012 *p++ = ' ';
5013 realLength ++;
5014 }
5015 }
5016 *p = '\0'; /* nul term. */
5017 objPtr->length = realLength;
5018 Jim_Free(quotingType);
5019 }
5020
5021 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5022 {
5023 struct JimParserCtx parser;
5024 const char *str;
5025 int strLen;
5026
5027 /* Get the string representation */
5028 str = Jim_GetString(objPtr, &strLen);
5029
5030 /* Free the old internal repr just now and initialize the
5031 * new one just now. The string->list conversion can't fail. */
5032 Jim_FreeIntRep(interp, objPtr);
5033 objPtr->typePtr = &listObjType;
5034 objPtr->internalRep.listValue.len = 0;
5035 objPtr->internalRep.listValue.maxLen = 0;
5036 objPtr->internalRep.listValue.ele = NULL;
5037
5038 /* Convert into a list */
5039 JimParserInit(&parser, str, strLen, 1);
5040 while(!JimParserEof(&parser)) {
5041 char *token;
5042 int tokenLen, type;
5043 Jim_Obj *elementPtr;
5044
5045 JimParseList(&parser);
5046 if (JimParserTtype(&parser) != JIM_TT_STR &&
5047 JimParserTtype(&parser) != JIM_TT_ESC)
5048 continue;
5049 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5050 elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5051 ListAppendElement(objPtr, elementPtr);
5052 }
5053 return JIM_OK;
5054 }
5055
5056 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
5057 int len)
5058 {
5059 Jim_Obj *objPtr;
5060 int i;
5061
5062 objPtr = Jim_NewObj(interp);
5063 objPtr->typePtr = &listObjType;
5064 objPtr->bytes = NULL;
5065 objPtr->internalRep.listValue.ele = NULL;
5066 objPtr->internalRep.listValue.len = 0;
5067 objPtr->internalRep.listValue.maxLen = 0;
5068 for (i = 0; i < len; i++) {
5069 ListAppendElement(objPtr, elements[i]);
5070 }
5071 return objPtr;
5072 }
5073
5074 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5075 * length of the vector. Note that the user of this function should make
5076 * sure that the list object can't shimmer while the vector returned
5077 * is in use, this vector is the one stored inside the internal representation
5078 * of the list object. This function is not exported, extensions should
5079 * always access to the List object elements using Jim_ListIndex(). */
5080 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5081 Jim_Obj ***listVec)
5082 {
5083 Jim_ListLength(interp, listObj, argc);
5084 assert(listObj->typePtr == &listObjType);
5085 *listVec = listObj->internalRep.listValue.ele;
5086 }
5087
5088 /* ListSortElements type values */
5089 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5090 JIM_LSORT_NOCASE_DECR};
5091
5092 /* Sort the internal rep of a list. */
5093 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5094 {
5095 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5096 }
5097
5098 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5099 {
5100 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5101 }
5102
5103 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5104 {
5105 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5106 }
5107
5108 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5109 {
5110 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5111 }
5112
5113 /* Sort a list *in place*. MUST be called with non-shared objects. */
5114 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5115 {
5116 typedef int (qsort_comparator)(const void *, const void *);
5117 int (*fn)(Jim_Obj**, Jim_Obj**);
5118 Jim_Obj **vector;
5119 int len;
5120
5121 if (Jim_IsShared(listObjPtr))
5122 Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5123 if (listObjPtr->typePtr != &listObjType)
5124 SetListFromAny(interp, listObjPtr);
5125
5126 vector = listObjPtr->internalRep.listValue.ele;
5127 len = listObjPtr->internalRep.listValue.len;
5128 switch (type) {
5129 case JIM_LSORT_ASCII: fn = ListSortString; break;
5130 case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
5131 case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
5132 case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
5133 default:
5134 fn = NULL; /* avoid warning */
5135 Jim_Panic(interp,"ListSort called with invalid sort type");
5136 }
5137 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5138 Jim_InvalidateStringRep(listObjPtr);
5139 }
5140
5141 /* This is the low-level function to append an element to a list.
5142 * The higher-level Jim_ListAppendElement() performs shared object
5143 * check and invalidate the string repr. This version is used
5144 * in the internals of the List Object and is not exported.
5145 *
5146 * NOTE: this function can be called only against objects
5147 * with internal type of List. */
5148 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5149 {
5150 int requiredLen = listPtr->internalRep.listValue.len + 1;
5151
5152 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5153 int maxLen = requiredLen * 2;
5154
5155 listPtr->internalRep.listValue.ele =
5156 Jim_Realloc(listPtr->internalRep.listValue.ele,
5157 sizeof(Jim_Obj*)*maxLen);
5158 listPtr->internalRep.listValue.maxLen = maxLen;
5159 }
5160 listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5161 objPtr;
5162 listPtr->internalRep.listValue.len ++;
5163 Jim_IncrRefCount(objPtr);
5164 }
5165
5166 /* This is the low-level function to insert elements into a list.
5167 * The higher-level Jim_ListInsertElements() performs shared object
5168 * check and invalidate the string repr. This version is used
5169 * in the internals of the List Object and is not exported.
5170 *
5171 * NOTE: this function can be called only against objects
5172 * with internal type of List. */
5173 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5174 Jim_Obj *const *elemVec)
5175 {
5176 int currentLen = listPtr->internalRep.listValue.len;
5177 int requiredLen = currentLen + elemc;
5178 int i;
5179 Jim_Obj **point;
5180
5181 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5182 int maxLen = requiredLen * 2;
5183
5184 listPtr->internalRep.listValue.ele =
5185 Jim_Realloc(listPtr->internalRep.listValue.ele,
5186 sizeof(Jim_Obj*)*maxLen);
5187 listPtr->internalRep.listValue.maxLen = maxLen;
5188 }
5189 point = listPtr->internalRep.listValue.ele + index;
5190 memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5191 for (i=0; i < elemc; ++i) {
5192 point[i] = elemVec[i];
5193 Jim_IncrRefCount(point[i]);
5194 }
5195 listPtr->internalRep.listValue.len += elemc;
5196 }
5197
5198 /* Appends every element of appendListPtr into listPtr.
5199 * Both have to be of the list type. */
5200 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5201 {
5202 int i, oldLen = listPtr->internalRep.listValue.len;
5203 int appendLen = appendListPtr->internalRep.listValue.len;
5204 int requiredLen = oldLen + appendLen;
5205
5206 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5207 int maxLen = requiredLen * 2;
5208
5209 listPtr->internalRep.listValue.ele =
5210 Jim_Realloc(listPtr->internalRep.listValue.ele,
5211 sizeof(Jim_Obj*)*maxLen);
5212 listPtr->internalRep.listValue.maxLen = maxLen;
5213 }
5214 for (i = 0; i < appendLen; i++) {
5215 Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5216 listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5217 Jim_IncrRefCount(objPtr);
5218 }
5219 listPtr->internalRep.listValue.len += appendLen;
5220 }
5221
5222 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5223 {
5224 if (Jim_IsShared(listPtr))
5225 Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5226 if (listPtr->typePtr != &listObjType)
5227 SetListFromAny(interp, listPtr);
5228 Jim_InvalidateStringRep(listPtr);
5229 ListAppendElement(listPtr, objPtr);
5230 }
5231
5232 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5233 {
5234 if (Jim_IsShared(listPtr))
5235 Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5236 if (listPtr->typePtr != &listObjType)
5237 SetListFromAny(interp, listPtr);
5238 Jim_InvalidateStringRep(listPtr);
5239 ListAppendList(listPtr, appendListPtr);
5240 }
5241
5242 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5243 {
5244 if (listPtr->typePtr != &listObjType)
5245 SetListFromAny(interp, listPtr);
5246 *intPtr = listPtr->internalRep.listValue.len;
5247 }
5248
5249 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5250 int objc, Jim_Obj *const *objVec)
5251 {
5252 if (Jim_IsShared(listPtr))
5253 Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5254 if (listPtr->typePtr != &listObjType)
5255 SetListFromAny(interp, listPtr);
5256 if (index >= 0 && index > listPtr->internalRep.listValue.len)
5257 index = listPtr->internalRep.listValue.len;
5258 else if (index < 0 )
5259 index = 0;
5260 Jim_InvalidateStringRep(listPtr);
5261 ListInsertElements(listPtr, index, objc, objVec);
5262 }
5263
5264 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5265 Jim_Obj **objPtrPtr, int flags)
5266 {
5267 if (listPtr->typePtr != &listObjType)
5268 SetListFromAny(interp, listPtr);
5269 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5270 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5271 if (flags & JIM_ERRMSG) {
5272 Jim_SetResultString(interp,
5273 "list index out of range", -1);
5274 }
5275 return JIM_ERR;
5276 }
5277 if (index < 0)
5278 index = listPtr->internalRep.listValue.len+index;
5279 *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5280 return JIM_OK;
5281 }
5282
5283 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5284 Jim_Obj *newObjPtr, int flags)
5285 {
5286 if (listPtr->typePtr != &listObjType)
5287 SetListFromAny(interp, listPtr);
5288 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5289 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5290 if (flags & JIM_ERRMSG) {
5291 Jim_SetResultString(interp,
5292 "list index out of range", -1);
5293 }
5294 return JIM_ERR;
5295 }
5296 if (index < 0)
5297 index = listPtr->internalRep.listValue.len+index;
5298 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5299 listPtr->internalRep.listValue.ele[index] = newObjPtr;
5300 Jim_IncrRefCount(newObjPtr);
5301 return JIM_OK;
5302 }
5303
5304 /* Modify the list stored into the variable named 'varNamePtr'
5305 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5306 * with the new element 'newObjptr'. */
5307 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5308 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5309 {
5310 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5311 int shared, i, index;
5312
5313 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5314 if (objPtr == NULL)
5315 return JIM_ERR;
5316 if ((shared = Jim_IsShared(objPtr)))
5317 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5318 for (i = 0; i < indexc-1; i++) {
5319 listObjPtr = objPtr;
5320 if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5321 goto err;
5322 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5323 JIM_ERRMSG) != JIM_OK) {
5324 goto err;
5325 }
5326 if (Jim_IsShared(objPtr)) {
5327 objPtr = Jim_DuplicateObj(interp, objPtr);
5328 ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5329 }
5330 Jim_InvalidateStringRep(listObjPtr);
5331 }
5332 if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5333 goto err;
5334 if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5335 goto err;
5336 Jim_InvalidateStringRep(objPtr);
5337 Jim_InvalidateStringRep(varObjPtr);
5338 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5339 goto err;
5340 Jim_SetResult(interp, varObjPtr);
5341 return JIM_OK;
5342 err:
5343 if (shared) {
5344 Jim_FreeNewObj(interp, varObjPtr);
5345 }
5346 return JIM_ERR;
5347 }
5348
5349 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5350 {
5351 int i;
5352
5353 /* If all the objects in objv are lists without string rep.
5354 * it's possible to return a list as result, that's the
5355 * concatenation of all the lists. */
5356 for (i = 0; i < objc; i++) {
5357 if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5358 break;
5359 }
5360 if (i == objc) {
5361 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5362 for (i = 0; i < objc; i++)
5363 Jim_ListAppendList(interp, objPtr, objv[i]);
5364 return objPtr;
5365 } else {
5366 /* Else... we have to glue strings together */
5367 int len = 0, objLen;
5368 char *bytes, *p;
5369
5370 /* Compute the length */
5371 for (i = 0; i < objc; i++) {
5372 Jim_GetString(objv[i], &objLen);
5373 len += objLen;
5374 }
5375 if (objc) len += objc-1;
5376 /* Create the string rep, and a stinrg object holding it. */
5377 p = bytes = Jim_Alloc(len+1);
5378 for (i = 0; i < objc; i++) {
5379 const char *s = Jim_GetString(objv[i], &objLen);
5380 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5381 {
5382 s++; objLen--; len--;
5383 }
5384 while (objLen && (s[objLen-1] == ' ' ||
5385 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5386 objLen--; len--;
5387 }
5388 memcpy(p, s, objLen);
5389 p += objLen;
5390 if (objLen && i+1 != objc) {
5391 *p++ = ' ';
5392 } else if (i+1 != objc) {
5393 /* Drop the space calcuated for this
5394 * element that is instead null. */
5395 len--;
5396 }
5397 }
5398 *p = '\0';
5399 return Jim_NewStringObjNoAlloc(interp, bytes, len);
5400 }
5401 }
5402
5403 /* Returns a list composed of the elements in the specified range.
5404 * first and start are directly accepted as Jim_Objects and
5405 * processed for the end?-index? case. */
5406 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5407 {
5408 int first, last;
5409 int len, rangeLen;
5410
5411 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5412 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5413 return NULL;
5414 Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5415 first = JimRelToAbsIndex(len, first);
5416 last = JimRelToAbsIndex(len, last);
5417 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5418 return Jim_NewListObj(interp,
5419 listObjPtr->internalRep.listValue.ele+first, rangeLen);
5420 }
5421
5422 /* -----------------------------------------------------------------------------
5423 * Dict object
5424 * ---------------------------------------------------------------------------*/
5425 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5426 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5427 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5428 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5429
5430 /* Dict HashTable Type.
5431 *
5432 * Keys and Values are Jim objects. */
5433
5434 unsigned int JimObjectHTHashFunction(const void *key)
5435 {
5436 const char *str;
5437 Jim_Obj *objPtr = (Jim_Obj*) key;
5438 int len, h;
5439
5440 str = Jim_GetString(objPtr, &len);
5441 h = Jim_GenHashFunction((unsigned char*)str, len);
5442 return h;
5443 }
5444
5445 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5446 {
5447 JIM_NOTUSED(privdata);
5448
5449 return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5450 }
5451
5452 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5453 {
5454 Jim_Obj *objPtr = val;
5455
5456 Jim_DecrRefCount(interp, objPtr);
5457 }
5458
5459 static Jim_HashTableType JimDictHashTableType = {
5460 JimObjectHTHashFunction, /* hash function */
5461 NULL, /* key dup */
5462 NULL, /* val dup */
5463 JimObjectHTKeyCompare, /* key compare */
5464 (void(*)(void*, const void*)) /* ATTENTION: const cast */
5465 JimObjectHTKeyValDestructor, /* key destructor */
5466 JimObjectHTKeyValDestructor /* val destructor */
5467 };
5468
5469 /* Note that while the elements of the dict may contain references,
5470 * the list object itself can't. This basically means that the
5471 * dict object string representation as a whole can't contain references
5472 * that are not presents in the single elements. */
5473 static Jim_ObjType dictObjType = {
5474 "dict",
5475 FreeDictInternalRep,
5476 DupDictInternalRep,
5477 UpdateStringOfDict,
5478 JIM_TYPE_NONE,
5479 };
5480
5481 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5482 {
5483 JIM_NOTUSED(interp);
5484
5485 Jim_FreeHashTable(objPtr->internalRep.ptr);
5486 Jim_Free(objPtr->internalRep.ptr);
5487 }
5488
5489 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5490 {
5491 Jim_HashTable *ht, *dupHt;
5492 Jim_HashTableIterator *htiter;
5493 Jim_HashEntry *he;
5494
5495 /* Create a new hash table */
5496 ht = srcPtr->internalRep.ptr;
5497 dupHt = Jim_Alloc(sizeof(*dupHt));
5498 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5499 if (ht->size != 0)
5500 Jim_ExpandHashTable(dupHt, ht->size);
5501 /* Copy every element from the source to the dup hash table */
5502 htiter = Jim_GetHashTableIterator(ht);
5503 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5504 const Jim_Obj *keyObjPtr = he->key;
5505 Jim_Obj *valObjPtr = he->val;
5506
5507 Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
5508 Jim_IncrRefCount(valObjPtr);
5509 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5510 }
5511 Jim_FreeHashTableIterator(htiter);
5512
5513 dupPtr->internalRep.ptr = dupHt;
5514 dupPtr->typePtr = &dictObjType;
5515 }
5516
5517 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5518 {
5519 int i, bufLen, realLength;
5520 const char *strRep;
5521 char *p;
5522 int *quotingType, objc;
5523 Jim_HashTable *ht;
5524 Jim_HashTableIterator *htiter;
5525 Jim_HashEntry *he;
5526 Jim_Obj **objv;
5527
5528 /* Trun the hash table into a flat vector of Jim_Objects. */
5529 ht = objPtr->internalRep.ptr;
5530 objc = ht->used*2;
5531 objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5532 htiter = Jim_GetHashTableIterator(ht);
5533 i = 0;
5534 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5535 objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
5536 objv[i++] = he->val;
5537 }
5538 Jim_FreeHashTableIterator(htiter);
5539 /* (Over) Estimate the space needed. */
5540 quotingType = Jim_Alloc(sizeof(int)*objc);
5541 bufLen = 0;
5542 for (i = 0; i < objc; i++) {
5543 int len;
5544
5545 strRep = Jim_GetString(objv[i], &len);
5546 quotingType[i] = ListElementQuotingType(strRep, len);
5547 switch (quotingType[i]) {
5548 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5549 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5550 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5551 }
5552 bufLen++; /* elements separator. */
5553 }
5554 bufLen++;
5555
5556 /* Generate the string rep. */
5557 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5558 realLength = 0;
5559 for (i = 0; i < objc; i++) {
5560 int len, qlen;
5561 const char *strRep = Jim_GetString(objv[i], &len);
5562 char *q;
5563
5564 switch(quotingType[i]) {
5565 case JIM_ELESTR_SIMPLE:
5566 memcpy(p, strRep, len);
5567 p += len;
5568 realLength += len;
5569 break;
5570 case JIM_ELESTR_BRACE:
5571 *p++ = '{';
5572 memcpy(p, strRep, len);
5573 p += len;
5574 *p++ = '}';
5575 realLength += len+2;
5576 break;
5577 case JIM_ELESTR_QUOTE:
5578 q = BackslashQuoteString(strRep, len, &qlen);
5579 memcpy(p, q, qlen);
5580 Jim_Free(q);
5581 p += qlen;
5582 realLength += qlen;
5583 break;
5584 }
5585 /* Add a separating space */
5586 if (i+1 != objc) {
5587 *p++ = ' ';
5588 realLength ++;
5589 }
5590 }
5591 *p = '\0'; /* nul term. */
5592 objPtr->length = realLength;
5593 Jim_Free(quotingType);
5594 Jim_Free(objv);
5595 }
5596
5597 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5598 {
5599 struct JimParserCtx parser;
5600 Jim_HashTable *ht;
5601 Jim_Obj *objv[2];
5602 const char *str;
5603 int i, strLen;
5604
5605 /* Get the string representation */
5606 str = Jim_GetString(objPtr, &strLen);
5607
5608 /* Free the old internal repr just now and initialize the
5609 * new one just now. The string->list conversion can't fail. */
5610 Jim_FreeIntRep(interp, objPtr);
5611 ht = Jim_Alloc(sizeof(*ht));
5612 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5613 objPtr->typePtr = &dictObjType;
5614 objPtr->internalRep.ptr = ht;
5615
5616 /* Convert into a dict */
5617 JimParserInit(&parser, str, strLen, 1);
5618 i = 0;
5619 while(!JimParserEof(&parser)) {
5620 char *token;
5621 int tokenLen, type;
5622
5623 JimParseList(&parser);
5624 if (JimParserTtype(&parser) != JIM_TT_STR &&
5625 JimParserTtype(&parser) != JIM_TT_ESC)
5626 continue;
5627 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5628 objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5629 if (i == 2) {
5630 i = 0;
5631 Jim_IncrRefCount(objv[0]);
5632 Jim_IncrRefCount(objv[1]);
5633 if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5634 Jim_HashEntry *he;
5635 he = Jim_FindHashEntry(ht, objv[0]);
5636 Jim_DecrRefCount(interp, objv[0]);
5637 /* ATTENTION: const cast */
5638 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5639 he->val = objv[1];
5640 }
5641 }
5642 }
5643 if (i) {
5644 Jim_FreeNewObj(interp, objv[0]);
5645 objPtr->typePtr = NULL;
5646 Jim_FreeHashTable(ht);
5647 Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5648 return JIM_ERR;
5649 }
5650 return JIM_OK;
5651 }
5652
5653 /* Dict object API */
5654
5655 /* Add an element to a dict. objPtr must be of the "dict" type.
5656 * The higer-level exported function is Jim_DictAddElement().
5657 * If an element with the specified key already exists, the value
5658 * associated is replaced with the new one.
5659 *
5660 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5661 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5662 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5663 {
5664 Jim_HashTable *ht = objPtr->internalRep.ptr;
5665
5666 if (valueObjPtr == NULL) { /* unset */
5667 Jim_DeleteHashEntry(ht, keyObjPtr);
5668 return;
5669 }
5670 Jim_IncrRefCount(keyObjPtr);
5671 Jim_IncrRefCount(valueObjPtr);
5672 if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5673 Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5674 Jim_DecrRefCount(interp, keyObjPtr);
5675 /* ATTENTION: const cast */
5676 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5677 he->val = valueObjPtr;
5678 }
5679 }
5680
5681 /* Add an element, higher-level interface for DictAddElement().
5682 * If valueObjPtr == NULL, the key is removed if it exists. */
5683 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5684 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5685 {
5686 if (Jim_IsShared(objPtr))
5687 Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5688 if (objPtr->typePtr != &dictObjType) {
5689 if (SetDictFromAny(interp, objPtr) != JIM_OK)
5690 return JIM_ERR;
5691 }
5692 DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5693 Jim_InvalidateStringRep(objPtr);
5694 return JIM_OK;
5695 }
5696
5697 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5698 {
5699 Jim_Obj *objPtr;
5700 int i;
5701
5702 if (len % 2)
5703 Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5704
5705 objPtr = Jim_NewObj(interp);
5706 objPtr->typePtr = &dictObjType;
5707 objPtr->bytes = NULL;
5708 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5709 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5710 for (i = 0; i < len; i += 2)
5711 DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5712 return objPtr;
5713 }
5714
5715 /* Return the value associated to the specified dict key */
5716 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5717 Jim_Obj **objPtrPtr, int flags)
5718 {
5719 Jim_HashEntry *he;
5720 Jim_HashTable *ht;
5721
5722 if (dictPtr->typePtr != &dictObjType) {
5723 if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5724 return JIM_ERR;
5725 }
5726 ht = dictPtr->internalRep.ptr;
5727 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5728 if (flags & JIM_ERRMSG) {
5729 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5730 Jim_AppendStrings(interp, Jim_GetResult(interp),
5731 "key \"", Jim_GetString(keyPtr, NULL),
5732 "\" not found in dictionary", NULL);
5733 }
5734 return JIM_ERR;
5735 }
5736 *objPtrPtr = he->val;
5737 return JIM_OK;
5738 }
5739
5740 /* Return the value associated to the specified dict keys */
5741 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5742 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5743 {
5744 Jim_Obj *objPtr;
5745 int i;
5746
5747 if (keyc == 0) {
5748 *objPtrPtr = dictPtr;
5749 return JIM_OK;
5750 }
5751
5752 for (i = 0; i < keyc; i++) {
5753 if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5754 != JIM_OK)
5755 return JIM_ERR;
5756 dictPtr = objPtr;
5757 }
5758 *objPtrPtr = objPtr;
5759 return JIM_OK;
5760 }
5761
5762 /* Modify the dict stored into the variable named 'varNamePtr'
5763 * setting the element specified by the 'keyc' keys objects in 'keyv',
5764 * with the new value of the element 'newObjPtr'.
5765 *
5766 * If newObjPtr == NULL the operation is to remove the given key
5767 * from the dictionary. */
5768 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5769 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5770 {
5771 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5772 int shared, i;
5773
5774 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5775 if (objPtr == NULL) {
5776 if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5777 return JIM_ERR;
5778 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5779 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5780 Jim_FreeNewObj(interp, varObjPtr);
5781 return JIM_ERR;
5782 }
5783 }
5784 if ((shared = Jim_IsShared(objPtr)))
5785 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5786 for (i = 0; i < keyc-1; i++) {
5787 dictObjPtr = objPtr;
5788
5789 /* Check if it's a valid dictionary */
5790 if (dictObjPtr->typePtr != &dictObjType) {
5791 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5792 goto err;
5793 }
5794 /* Check if the given key exists. */
5795 Jim_InvalidateStringRep(dictObjPtr);
5796 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5797 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5798 {
5799 /* This key exists at the current level.
5800 * Make sure it's not shared!. */
5801 if (Jim_IsShared(objPtr)) {
5802 objPtr = Jim_DuplicateObj(interp, objPtr);
5803 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5804 }
5805 } else {
5806 /* Key not found. If it's an [unset] operation
5807 * this is an error. Only the last key may not
5808 * exist. */
5809 if (newObjPtr == NULL)
5810 goto err;
5811 /* Otherwise set an empty dictionary
5812 * as key's value. */
5813 objPtr = Jim_NewDictObj(interp, NULL, 0);
5814 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5815 }
5816 }
5817 if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5818 != JIM_OK)
5819 goto err;
5820 Jim_InvalidateStringRep(objPtr);
5821 Jim_InvalidateStringRep(varObjPtr);
5822 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5823 goto err;
5824 Jim_SetResult(interp, varObjPtr);
5825 return JIM_OK;
5826 err:
5827 if (shared) {
5828 Jim_FreeNewObj(interp, varObjPtr);
5829 }
5830 return JIM_ERR;
5831 }
5832
5833 /* -----------------------------------------------------------------------------
5834 * Index object
5835 * ---------------------------------------------------------------------------*/
5836 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5837 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5838
5839 static Jim_ObjType indexObjType = {
5840 "index",
5841 NULL,
5842 NULL,
5843 UpdateStringOfIndex,
5844 JIM_TYPE_NONE,
5845 };
5846
5847 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5848 {
5849 int len;
5850 char buf[JIM_INTEGER_SPACE+1];
5851
5852 if (objPtr->internalRep.indexValue >= 0)
5853 len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5854 else if (objPtr->internalRep.indexValue == -1)
5855 len = sprintf(buf, "end");
5856 else {
5857 len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5858 }
5859 objPtr->bytes = Jim_Alloc(len+1);
5860 memcpy(objPtr->bytes, buf, len+1);
5861 objPtr->length = len;
5862 }
5863
5864 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5865 {
5866 int index, end = 0;
5867 const char *str;
5868
5869 /* Get the string representation */
5870 str = Jim_GetString(objPtr, NULL);
5871 /* Try to convert into an index */
5872 if (!strcmp(str, "end")) {
5873 index = 0;
5874 end = 1;
5875 } else {
5876 if (!strncmp(str, "end-", 4)) {
5877 str += 4;
5878 end = 1;
5879 }
5880 if (Jim_StringToIndex(str, &index) != JIM_OK) {
5881 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5882 Jim_AppendStrings(interp, Jim_GetResult(interp),
5883 "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5884 "must be integer or end?-integer?", NULL);
5885 return JIM_ERR;
5886 }
5887 }
5888 if (end) {
5889 if (index < 0)
5890 index = INT_MAX;
5891 else
5892 index = -(index+1);
5893 } else if (!end && index < 0)
5894 index = -INT_MAX;
5895 /* Free the old internal repr and set the new one. */
5896 Jim_FreeIntRep(interp, objPtr);
5897 objPtr->typePtr = &indexObjType;
5898 objPtr->internalRep.indexValue = index;
5899 return JIM_OK;
5900 }
5901
5902 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5903 {
5904 /* Avoid shimmering if the object is an integer. */
5905 if (objPtr->typePtr == &intObjType) {
5906 jim_wide val = objPtr->internalRep.wideValue;
5907 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5908 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5909 return JIM_OK;
5910 }
5911 }
5912 if (objPtr->typePtr != &indexObjType &&
5913 SetIndexFromAny(interp, objPtr) == JIM_ERR)
5914 return JIM_ERR;
5915 *indexPtr = objPtr->internalRep.indexValue;
5916 return JIM_OK;
5917 }
5918
5919 /* -----------------------------------------------------------------------------
5920 * Return Code Object.
5921 * ---------------------------------------------------------------------------*/
5922
5923 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5924
5925 static Jim_ObjType returnCodeObjType = {
5926 "return-code",
5927 NULL,
5928 NULL,
5929 NULL,
5930 JIM_TYPE_NONE,
5931 };
5932
5933 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5934 {
5935 const char *str;
5936 int strLen, returnCode;
5937 jim_wide wideValue;
5938
5939 /* Get the string representation */
5940 str = Jim_GetString(objPtr, &strLen);
5941 /* Try to convert into an integer */
5942 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5943 returnCode = (int) wideValue;
5944 else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5945 returnCode = JIM_OK;
5946 else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5947 returnCode = JIM_ERR;
5948 else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5949 returnCode = JIM_RETURN;
5950 else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5951 returnCode = JIM_BREAK;
5952 else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5953 returnCode = JIM_CONTINUE;
5954 else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5955 returnCode = JIM_EVAL;
5956 else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5957 returnCode = JIM_EXIT;
5958 else {
5959 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5960 Jim_AppendStrings(interp, Jim_GetResult(interp),
5961 "expected return code but got '", str, "'",
5962 NULL);
5963 return JIM_ERR;
5964 }
5965 /* Free the old internal repr and set the new one. */
5966 Jim_FreeIntRep(interp, objPtr);
5967 objPtr->typePtr = &returnCodeObjType;
5968 objPtr->internalRep.returnCode = returnCode;
5969 return JIM_OK;
5970 }
5971
5972 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
5973 {
5974 if (objPtr->typePtr != &returnCodeObjType &&
5975 SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
5976 return JIM_ERR;
5977 *intPtr = objPtr->internalRep.returnCode;
5978 return JIM_OK;
5979 }
5980
5981 /* -----------------------------------------------------------------------------
5982 * Expression Parsing
5983 * ---------------------------------------------------------------------------*/
5984 static int JimParseExprOperator(struct JimParserCtx *pc);
5985 static int JimParseExprNumber(struct JimParserCtx *pc);
5986 static int JimParseExprIrrational(struct JimParserCtx *pc);
5987
5988 /* Exrp's Stack machine operators opcodes. */
5989
5990 /* Binary operators (numbers) */
5991 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
5992 #define JIM_EXPROP_MUL 0
5993 #define JIM_EXPROP_DIV 1
5994 #define JIM_EXPROP_MOD 2
5995 #define JIM_EXPROP_SUB 3
5996 #define JIM_EXPROP_ADD 4
5997 #define JIM_EXPROP_LSHIFT 5
5998 #define JIM_EXPROP_RSHIFT 6
5999 #define JIM_EXPROP_ROTL 7
6000 #define JIM_EXPROP_ROTR 8
6001 #define JIM_EXPROP_LT 9
6002 #define JIM_EXPROP_GT 10
6003 #define JIM_EXPROP_LTE 11
6004 #define JIM_EXPROP_GTE 12
6005 #define JIM_EXPROP_NUMEQ 13
6006 #define JIM_EXPROP_NUMNE 14
6007 #define JIM_EXPROP_BITAND 15
6008 #define JIM_EXPROP_BITXOR 16
6009 #define JIM_EXPROP_BITOR 17
6010 #define JIM_EXPROP_LOGICAND 18
6011 #define JIM_EXPROP_LOGICOR 19
6012 #define JIM_EXPROP_LOGICAND_LEFT 20
6013 #define JIM_EXPROP_LOGICOR_LEFT 21
6014 #define JIM_EXPROP_POW 22
6015 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6016
6017 /* Binary operators (strings) */
6018 #define JIM_EXPROP_STREQ 23
6019 #define JIM_EXPROP_STRNE 24
6020
6021 /* Unary operators (numbers) */
6022 #define JIM_EXPROP_NOT 25
6023 #define JIM_EXPROP_BITNOT 26
6024 #define JIM_EXPROP_UNARYMINUS 27
6025 #define JIM_EXPROP_UNARYPLUS 28
6026 #define JIM_EXPROP_LOGICAND_RIGHT 29
6027 #define JIM_EXPROP_LOGICOR_RIGHT 30
6028
6029 /* Ternary operators */
6030 #define JIM_EXPROP_TERNARY 31
6031
6032 /* Operands */
6033 #define JIM_EXPROP_NUMBER 32
6034 #define JIM_EXPROP_COMMAND 33
6035 #define JIM_EXPROP_VARIABLE 34
6036 #define JIM_EXPROP_DICTSUGAR 35
6037 #define JIM_EXPROP_SUBST 36
6038 #define JIM_EXPROP_STRING 37
6039
6040 /* Operators table */
6041 typedef struct Jim_ExprOperator {
6042 const char *name;
6043 int precedence;
6044 int arity;
6045 int opcode;
6046 } Jim_ExprOperator;
6047
6048 /* name - precedence - arity - opcode */
6049 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6050 {"!", 300, 1, JIM_EXPROP_NOT},
6051 {"~", 300, 1, JIM_EXPROP_BITNOT},
6052 {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6053 {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6054
6055 {"**", 250, 2, JIM_EXPROP_POW},
6056
6057 {"*", 200, 2, JIM_EXPROP_MUL},
6058 {"/", 200, 2, JIM_EXPROP_DIV},
6059 {"%", 200, 2, JIM_EXPROP_MOD},
6060
6061 {"-", 100, 2, JIM_EXPROP_SUB},
6062 {"+", 100, 2, JIM_EXPROP_ADD},
6063
6064 {"<<<", 90, 3, JIM_EXPROP_ROTL},
6065 {">>>", 90, 3, JIM_EXPROP_ROTR},
6066 {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6067 {">>", 90, 2, JIM_EXPROP_RSHIFT},
6068
6069 {"<", 80, 2, JIM_EXPROP_LT},
6070 {">", 80, 2, JIM_EXPROP_GT},
6071 {"<=", 80, 2, JIM_EXPROP_LTE},
6072 {">=", 80, 2, JIM_EXPROP_GTE},
6073
6074 {"==", 70, 2, JIM_EXPROP_NUMEQ},
6075 {"!=", 70, 2, JIM_EXPROP_NUMNE},
6076
6077 {"eq", 60, 2, JIM_EXPROP_STREQ},
6078 {"ne", 60, 2, JIM_EXPROP_STRNE},
6079
6080 {"&", 50, 2, JIM_EXPROP_BITAND},
6081 {"^", 49, 2, JIM_EXPROP_BITXOR},
6082 {"|", 48, 2, JIM_EXPROP_BITOR},
6083
6084 {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6085 {"||", 10, 2, JIM_EXPROP_LOGICOR},
6086
6087 {"?", 5, 3, JIM_EXPROP_TERNARY},
6088 /* private operators */
6089 {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6090 {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6091 {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6092 {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6093 };
6094
6095 #define JIM_EXPR_OPERATORS_NUM \
6096 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6097
6098 int JimParseExpression(struct JimParserCtx *pc)
6099 {
6100 /* Discard spaces and quoted newline */
6101 while(*(pc->p) == ' ' ||
6102 *(pc->p) == '\t' ||
6103 *(pc->p) == '\r' ||
6104 *(pc->p) == '\n' ||
6105 (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6106 pc->p++; pc->len--;
6107 }
6108
6109 if (pc->len == 0) {
6110 pc->tstart = pc->tend = pc->p;
6111 pc->tline = pc->linenr;
6112 pc->tt = JIM_TT_EOL;
6113 pc->eof = 1;
6114 return JIM_OK;
6115 }
6116 switch(*(pc->p)) {
6117 case '(':
6118 pc->tstart = pc->tend = pc->p;
6119 pc->tline = pc->linenr;
6120 pc->tt = JIM_TT_SUBEXPR_START;
6121 pc->p++; pc->len--;
6122 break;
6123 case ')':
6124 pc->tstart = pc->tend = pc->p;
6125 pc->tline = pc->linenr;
6126 pc->tt = JIM_TT_SUBEXPR_END;
6127 pc->p++; pc->len--;
6128 break;
6129 case '[':
6130 return JimParseCmd(pc);
6131 break;
6132 case '$':
6133 if (JimParseVar(pc) == JIM_ERR)
6134 return JimParseExprOperator(pc);
6135 else
6136 return JIM_OK;
6137 break;
6138 case '-':
6139 if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6140 isdigit((int)*(pc->p+1)))
6141 return JimParseExprNumber(pc);
6142 else
6143 return JimParseExprOperator(pc);
6144 break;
6145 case '0': case '1': case '2': case '3': case '4':
6146 case '5': case '6': case '7': case '8': case '9': case '.':
6147 return JimParseExprNumber(pc);
6148 break;
6149 case '"':
6150 case '{':
6151 /* Here it's possible to reuse the List String parsing. */
6152 pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6153 return JimParseListStr(pc);
6154 break;
6155 case 'N': case 'I':
6156 case 'n': case 'i':
6157 if (JimParseExprIrrational(pc) == JIM_ERR)
6158 return JimParseExprOperator(pc);
6159 break;
6160 default:
6161 return JimParseExprOperator(pc);
6162 break;
6163 }
6164 return JIM_OK;
6165 }
6166
6167 int JimParseExprNumber(struct JimParserCtx *pc)
6168 {
6169 int allowdot = 1;
6170 int allowhex = 0;
6171
6172 pc->tstart = pc->p;
6173 pc->tline = pc->linenr;
6174 if (*pc->p == '-') {
6175 pc->p++; pc->len--;
6176 }
6177 while ( isdigit((int)*pc->p)
6178 || (allowhex && isxdigit((int)*pc->p) )
6179 || (allowdot && *pc->p == '.')
6180 || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6181 (*pc->p == 'x' || *pc->p == 'X'))
6182 )
6183 {
6184 if ((*pc->p == 'x') || (*pc->p == 'X')) {
6185 allowhex = 1;
6186 allowdot = 0;
6187 }
6188 if (*pc->p == '.')
6189 allowdot = 0;
6190 pc->p++; pc->len--;
6191 if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6192 pc->p += 2; pc->len -= 2;
6193 }
6194 }
6195 pc->tend = pc->p-1;
6196 pc->tt = JIM_TT_EXPR_NUMBER;
6197 return JIM_OK;
6198 }
6199
6200 int JimParseExprIrrational(struct JimParserCtx *pc)
6201 {
6202 const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6203 const char **token;
6204 for (token = Tokens; *token != NULL; token++) {
6205 int len = strlen(*token);
6206 if (strncmp(*token, pc->p, len) == 0) {
6207 pc->tstart = pc->p;
6208 pc->tend = pc->p + len - 1;
6209 pc->p += len; pc->len -= len;
6210 pc->tline = pc->linenr;
6211 pc->tt = JIM_TT_EXPR_NUMBER;
6212 return JIM_OK;
6213 }
6214 }
6215 return JIM_ERR;
6216 }
6217
6218 int JimParseExprOperator(struct JimParserCtx *pc)
6219 {
6220 int i;
6221 int bestIdx = -1, bestLen = 0;
6222
6223 /* Try to get the longest match. */
6224 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6225 const char *opname;
6226 int oplen;
6227
6228 opname = Jim_ExprOperators[i].name;
6229 if (opname == NULL) continue;
6230 oplen = strlen(opname);
6231
6232 if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6233 bestIdx = i;
6234 bestLen = oplen;
6235 }
6236 }
6237 if (bestIdx == -1) return JIM_ERR;
6238 pc->tstart = pc->p;
6239 pc->tend = pc->p + bestLen - 1;
6240 pc->p += bestLen; pc->len -= bestLen;
6241 pc->tline = pc->linenr;
6242 pc->tt = JIM_TT_EXPR_OPERATOR;
6243 return JIM_OK;
6244 }
6245
6246 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6247 {
6248 int i;
6249 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6250 if (Jim_ExprOperators[i].name &&
6251 strcmp(opname, Jim_ExprOperators[i].name) == 0)
6252 return &Jim_ExprOperators[i];
6253 return NULL;
6254 }
6255
6256 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6257 {
6258 int i;
6259 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6260 if (Jim_ExprOperators[i].opcode == opcode)
6261 return &Jim_ExprOperators[i];
6262 return NULL;
6263 }
6264
6265 /* -----------------------------------------------------------------------------
6266 * Expression Object
6267 * ---------------------------------------------------------------------------*/
6268 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6269 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6270 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6271
6272 static Jim_ObjType exprObjType = {
6273 "expression",
6274 FreeExprInternalRep,
6275 DupExprInternalRep,
6276 NULL,
6277 JIM_TYPE_REFERENCES,
6278 };
6279
6280 /* Expr bytecode structure */
6281 typedef struct ExprByteCode {
6282 int *opcode; /* Integer array of opcodes. */
6283 Jim_Obj **obj; /* Array of associated Jim Objects. */
6284 int len; /* Bytecode length */
6285 int inUse; /* Used for sharing. */
6286 } ExprByteCode;
6287
6288 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6289 {
6290 int i;
6291 ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6292
6293 expr->inUse--;
6294 if (expr->inUse != 0) return;
6295 for (i = 0; i < expr->len; i++)
6296 Jim_DecrRefCount(interp, expr->obj[i]);
6297 Jim_Free(expr->opcode);
6298 Jim_Free(expr->obj);
6299 Jim_Free(expr);
6300 }
6301
6302 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6303 {
6304 JIM_NOTUSED(interp);
6305 JIM_NOTUSED(srcPtr);
6306
6307 /* Just returns an simple string. */
6308 dupPtr->typePtr = NULL;
6309 }
6310
6311 /* Add a new instruction to an expression bytecode structure. */
6312 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6313 int opcode, char *str, int len)
6314 {
6315 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6316 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6317 expr->opcode[expr->len] = opcode;
6318 expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6319 Jim_IncrRefCount(expr->obj[expr->len]);
6320 expr->len++;
6321 }
6322
6323 /* Check if an expr program looks correct. */
6324 static int ExprCheckCorrectness(ExprByteCode *expr)
6325 {
6326 int i;
6327 int stacklen = 0;
6328
6329 /* Try to check if there are stack underflows,
6330 * and make sure at the end of the program there is
6331 * a single result on the stack. */
6332 for (i = 0; i < expr->len; i++) {
6333 switch(expr->opcode[i]) {
6334 case JIM_EXPROP_NUMBER:
6335 case JIM_EXPROP_STRING:
6336 case JIM_EXPROP_SUBST:
6337 case JIM_EXPROP_VARIABLE:
6338 case JIM_EXPROP_DICTSUGAR:
6339 case JIM_EXPROP_COMMAND:
6340 stacklen++;
6341 break;
6342 case JIM_EXPROP_NOT:
6343 case JIM_EXPROP_BITNOT:
6344 case JIM_EXPROP_UNARYMINUS:
6345 case JIM_EXPROP_UNARYPLUS:
6346 /* Unary operations */
6347 if (stacklen < 1) return JIM_ERR;
6348 break;
6349 case JIM_EXPROP_ADD:
6350 case JIM_EXPROP_SUB:
6351 case JIM_EXPROP_MUL:
6352 case JIM_EXPROP_DIV:
6353 case JIM_EXPROP_MOD:
6354 case JIM_EXPROP_LT:
6355 case JIM_EXPROP_GT:
6356 case JIM_EXPROP_LTE:
6357 case JIM_EXPROP_GTE:
6358 case JIM_EXPROP_ROTL:
6359 case JIM_EXPROP_ROTR:
6360 case JIM_EXPROP_LSHIFT:
6361 case JIM_EXPROP_RSHIFT:
6362 case JIM_EXPROP_NUMEQ:
6363 case JIM_EXPROP_NUMNE:
6364 case JIM_EXPROP_STREQ:
6365 case JIM_EXPROP_STRNE:
6366 case JIM_EXPROP_BITAND:
6367 case JIM_EXPROP_BITXOR:
6368 case JIM_EXPROP_BITOR:
6369 case JIM_EXPROP_LOGICAND:
6370 case JIM_EXPROP_LOGICOR:
6371 case JIM_EXPROP_POW:
6372 /* binary operations */
6373 if (stacklen < 2) return JIM_ERR;
6374 stacklen--;
6375 break;
6376 default:
6377 Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6378 break;
6379 }
6380 }
6381 if (stacklen != 1) return JIM_ERR;
6382 return JIM_OK;
6383 }
6384
6385 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6386 ScriptObj *topLevelScript)
6387 {
6388 int i;
6389
6390 return;
6391 for (i = 0; i < expr->len; i++) {
6392 Jim_Obj *foundObjPtr;
6393
6394 if (expr->obj[i] == NULL) continue;
6395 foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6396 NULL, expr->obj[i]);
6397 if (foundObjPtr != NULL) {
6398 Jim_IncrRefCount(foundObjPtr);
6399 Jim_DecrRefCount(interp, expr->obj[i]);
6400 expr->obj[i] = foundObjPtr;
6401 }
6402 }
6403 }
6404
6405 /* This procedure converts every occurrence of || and && opereators
6406 * in lazy unary versions.
6407 *
6408 * a b || is converted into:
6409 *
6410 * a <offset> |L b |R
6411 *
6412 * a b && is converted into:
6413 *
6414 * a <offset> &L b &R
6415 *
6416 * "|L" checks if 'a' is true:
6417 * 1) if it is true pushes 1 and skips <offset> istructions to reach
6418 * the opcode just after |R.
6419 * 2) if it is false does nothing.
6420 * "|R" checks if 'b' is true:
6421 * 1) if it is true pushes 1, otherwise pushes 0.
6422 *
6423 * "&L" checks if 'a' is true:
6424 * 1) if it is true does nothing.
6425 * 2) If it is false pushes 0 and skips <offset> istructions to reach
6426 * the opcode just after &R
6427 * "&R" checks if 'a' is true:
6428 * if it is true pushes 1, otherwise pushes 0.
6429 */
6430 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6431 {
6432 while (1) {
6433 int index = -1, leftindex, arity, i, offset;
6434 Jim_ExprOperator *op;
6435
6436 /* Search for || or && */
6437 for (i = 0; i < expr->len; i++) {
6438 if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6439 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6440 index = i;
6441 break;
6442 }
6443 }
6444 if (index == -1) return;
6445 /* Search for the end of the first operator */
6446 leftindex = index-1;
6447 arity = 1;
6448 while(arity) {
6449 switch(expr->opcode[leftindex]) {
6450 case JIM_EXPROP_NUMBER:
6451 case JIM_EXPROP_COMMAND:
6452 case JIM_EXPROP_VARIABLE:
6453 case JIM_EXPROP_DICTSUGAR:
6454 case JIM_EXPROP_SUBST:
6455 case JIM_EXPROP_STRING:
6456 break;
6457 default:
6458 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6459 if (op == NULL) {
6460 Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6461 }
6462 arity += op->arity;
6463 break;
6464 }
6465 arity--;
6466 leftindex--;
6467 }
6468 leftindex++;
6469 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6470 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6471 memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6472 sizeof(int)*(expr->len-leftindex));
6473 memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6474 sizeof(Jim_Obj*)*(expr->len-leftindex));
6475 expr->len += 2;
6476 index += 2;
6477 offset = (index-leftindex)-1;
6478 Jim_DecrRefCount(interp, expr->obj[index]);
6479 if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6480 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6481 expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6482 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6483 expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6484 } else {
6485 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6486 expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6487 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6488 expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6489 }
6490 expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6491 expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6492 Jim_IncrRefCount(expr->obj[index]);
6493 Jim_IncrRefCount(expr->obj[leftindex]);
6494 Jim_IncrRefCount(expr->obj[leftindex+1]);
6495 }
6496 }
6497
6498 /* This method takes the string representation of an expression
6499 * and generates a program for the Expr's stack-based VM. */
6500 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6501 {
6502 int exprTextLen;
6503 const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6504 struct JimParserCtx parser;
6505 int i, shareLiterals;
6506 ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6507 Jim_Stack stack;
6508 Jim_ExprOperator *op;
6509
6510 /* Perform literal sharing with the current procedure
6511 * running only if this expression appears to be not generated
6512 * at runtime. */
6513 shareLiterals = objPtr->typePtr == &sourceObjType;
6514
6515 expr->opcode = NULL;
6516 expr->obj = NULL;
6517 expr->len = 0;
6518 expr->inUse = 1;
6519
6520 Jim_InitStack(&stack);
6521 JimParserInit(&parser, exprText, exprTextLen, 1);
6522 while(!JimParserEof(&parser)) {
6523 char *token;
6524 int len, type;
6525
6526 if (JimParseExpression(&parser) != JIM_OK) {
6527 Jim_SetResultString(interp, "Syntax error in expression", -1);
6528 goto err;
6529 }
6530 token = JimParserGetToken(&parser, &len, &type, NULL);
6531 if (type == JIM_TT_EOL) {
6532 Jim_Free(token);
6533 break;
6534 }
6535 switch(type) {
6536 case JIM_TT_STR:
6537 ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6538 break;
6539 case JIM_TT_ESC:
6540 ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6541 break;
6542 case JIM_TT_VAR:
6543 ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6544 break;
6545 case JIM_TT_DICTSUGAR:
6546 ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6547 break;
6548 case JIM_TT_CMD:
6549 ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6550 break;
6551 case JIM_TT_EXPR_NUMBER:
6552 ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6553 break;
6554 case JIM_TT_EXPR_OPERATOR:
6555 op = JimExprOperatorInfo(token);
6556 while(1) {
6557 Jim_ExprOperator *stackTopOp;
6558
6559 if (Jim_StackPeek(&stack) != NULL) {
6560 stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6561 } else {
6562 stackTopOp = NULL;
6563 }
6564 if (Jim_StackLen(&stack) && op->arity != 1 &&
6565 stackTopOp && stackTopOp->precedence >= op->precedence)
6566 {
6567 ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6568 Jim_StackPeek(&stack), -1);
6569 Jim_StackPop(&stack);
6570 } else {
6571 break;
6572 }
6573 }
6574 Jim_StackPush(&stack, token);
6575 break;
6576 case JIM_TT_SUBEXPR_START:
6577 Jim_StackPush(&stack, Jim_StrDup("("));
6578 Jim_Free(token);
6579 break;
6580 case JIM_TT_SUBEXPR_END:
6581 {
6582 int found = 0;
6583 while(Jim_StackLen(&stack)) {
6584 char *opstr = Jim_StackPop(&stack);
6585 if (!strcmp(opstr, "(")) {
6586 Jim_Free(opstr);
6587 found = 1;
6588 break;
6589 }
6590 op = JimExprOperatorInfo(opstr);
6591 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6592 }
6593 if (!found) {
6594 Jim_SetResultString(interp,
6595 "Unexpected close parenthesis", -1);
6596 goto err;
6597 }
6598 }
6599 Jim_Free(token);
6600 break;
6601 default:
6602 Jim_Panic(interp,"Default reached in SetExprFromAny()");
6603 break;
6604 }
6605 }
6606 while (Jim_StackLen(&stack)) {
6607 char *opstr = Jim_StackPop(&stack);
6608 op = JimExprOperatorInfo(opstr);
6609 if (op == NULL && !strcmp(opstr, "(")) {
6610 Jim_Free(opstr);
6611 Jim_SetResultString(interp, "Missing close parenthesis", -1);
6612 goto err;
6613 }
6614 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6615 }
6616 /* Check program correctness. */
6617 if (ExprCheckCorrectness(expr) != JIM_OK) {
6618 Jim_SetResultString(interp, "Invalid expression", -1);
6619 goto err;
6620 }
6621
6622 /* Free the stack used for the compilation. */
6623 Jim_FreeStackElements(&stack, Jim_Free);
6624 Jim_FreeStack(&stack);
6625
6626 /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6627 ExprMakeLazy(interp, expr);
6628
6629 /* Perform literal sharing */
6630 if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6631 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6632 if (bodyObjPtr->typePtr == &scriptObjType) {
6633 ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6634 ExprShareLiterals(interp, expr, bodyScript);
6635 }
6636 }
6637
6638 /* Free the old internal rep and set the new one. */
6639 Jim_FreeIntRep(interp, objPtr);
6640 Jim_SetIntRepPtr(objPtr, expr);
6641 objPtr->typePtr = &exprObjType;
6642 return JIM_OK;
6643
6644 err: /* we jump here on syntax/compile errors. */
6645 Jim_FreeStackElements(&stack, Jim_Free);
6646 Jim_FreeStack(&stack);
6647 Jim_Free(expr->opcode);
6648 for (i = 0; i < expr->len; i++) {
6649 Jim_DecrRefCount(interp,expr->obj[i]);
6650 }
6651 Jim_Free(expr->obj);
6652 Jim_Free(expr);
6653 return JIM_ERR;
6654 }
6655
6656 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6657 {
6658 if (objPtr->typePtr != &exprObjType) {
6659 if (SetExprFromAny(interp, objPtr) != JIM_OK)
6660 return NULL;
6661 }
6662 return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6663 }
6664
6665 /* -----------------------------------------------------------------------------
6666 * Expressions evaluation.
6667 * Jim uses a specialized stack-based virtual machine for expressions,
6668 * that takes advantage of the fact that expr's operators
6669 * can't be redefined.
6670 *
6671 * Jim_EvalExpression() uses the bytecode compiled by
6672 * SetExprFromAny() method of the "expression" object.
6673 *
6674 * On success a Tcl Object containing the result of the evaluation
6675 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6676 * returned.
6677 * On error the function returns a retcode != to JIM_OK and set a suitable
6678 * error on the interp.
6679 * ---------------------------------------------------------------------------*/
6680 #define JIM_EE_STATICSTACK_LEN 10
6681
6682 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6683 Jim_Obj **exprResultPtrPtr)
6684 {
6685 ExprByteCode *expr;
6686 Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6687 int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6688
6689 Jim_IncrRefCount(exprObjPtr);
6690 expr = Jim_GetExpression(interp, exprObjPtr);
6691 if (!expr) {
6692 Jim_DecrRefCount(interp, exprObjPtr);
6693 return JIM_ERR; /* error in expression. */
6694 }
6695 /* In order to avoid that the internal repr gets freed due to
6696 * shimmering of the exprObjPtr's object, we make the internal rep
6697 * shared. */
6698 expr->inUse++;
6699
6700 /* The stack-based expr VM itself */
6701
6702 /* Stack allocation. Expr programs have the feature that
6703 * a program of length N can't require a stack longer than
6704 * N. */
6705 if (expr->len > JIM_EE_STATICSTACK_LEN)
6706 stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6707 else
6708 stack = staticStack;
6709
6710 /* Execute every istruction */
6711 for (i = 0; i < expr->len; i++) {
6712 Jim_Obj *A, *B, *objPtr;
6713 jim_wide wA, wB, wC;
6714 double dA, dB, dC;
6715 const char *sA, *sB;
6716 int Alen, Blen, retcode;
6717 int opcode = expr->opcode[i];
6718
6719 if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6720 stack[stacklen++] = expr->obj[i];
6721 Jim_IncrRefCount(expr->obj[i]);
6722 } else if (opcode == JIM_EXPROP_VARIABLE) {
6723 objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6724 if (objPtr == NULL) {
6725 error = 1;
6726 goto err;
6727 }
6728 stack[stacklen++] = objPtr;
6729 Jim_IncrRefCount(objPtr);
6730 } else if (opcode == JIM_EXPROP_SUBST) {
6731 if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6732 &objPtr, JIM_NONE)) != JIM_OK)
6733 {
6734 error = 1;
6735 errRetCode = retcode;
6736 goto err;
6737 }
6738 stack[stacklen++] = objPtr;
6739 Jim_IncrRefCount(objPtr);
6740 } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6741 objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6742 if (objPtr == NULL) {
6743 error = 1;
6744 goto err;
6745 }
6746 stack[stacklen++] = objPtr;
6747 Jim_IncrRefCount(objPtr);
6748 } else if (opcode == JIM_EXPROP_COMMAND) {
6749 if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6750 error = 1;
6751 errRetCode = retcode;
6752 goto err;
6753 }
6754 stack[stacklen++] = interp->result;
6755 Jim_IncrRefCount(interp->result);
6756 } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6757 opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6758 {
6759 /* Note that there isn't to increment the
6760 * refcount of objects. the references are moved
6761 * from stack to A and B. */
6762 B = stack[--stacklen];
6763 A = stack[--stacklen];
6764
6765 /* --- Integer --- */
6766 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6767 (B->typePtr == &doubleObjType && !B->bytes) ||
6768 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6769 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6770 goto trydouble;
6771 }
6772 Jim_DecrRefCount(interp, A);
6773 Jim_DecrRefCount(interp, B);
6774 switch(expr->opcode[i]) {
6775 case JIM_EXPROP_ADD: wC = wA+wB; break;
6776 case JIM_EXPROP_SUB: wC = wA-wB; break;
6777 case JIM_EXPROP_MUL: wC = wA*wB; break;
6778 case JIM_EXPROP_LT: wC = wA<wB; break;
6779 case JIM_EXPROP_GT: wC = wA>wB; break;
6780 case JIM_EXPROP_LTE: wC = wA<=wB; break;
6781 case JIM_EXPROP_GTE: wC = wA>=wB; break;
6782 case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6783 case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6784 case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6785 case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6786 case JIM_EXPROP_BITAND: wC = wA&wB; break;
6787 case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6788 case JIM_EXPROP_BITOR: wC = wA|wB; break;
6789 case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6790 case JIM_EXPROP_LOGICAND_LEFT:
6791 if (wA == 0) {
6792 i += (int)wB;
6793 wC = 0;
6794 } else {
6795 continue;
6796 }
6797 break;
6798 case JIM_EXPROP_LOGICOR_LEFT:
6799 if (wA != 0) {
6800 i += (int)wB;
6801 wC = 1;
6802 } else {
6803 continue;
6804 }
6805 break;
6806 case JIM_EXPROP_DIV:
6807 if (wB == 0) goto divbyzero;
6808 wC = wA/wB;
6809 break;
6810 case JIM_EXPROP_MOD:
6811 if (wB == 0) goto divbyzero;
6812 wC = wA%wB;
6813 break;
6814 case JIM_EXPROP_ROTL: {
6815 /* uint32_t would be better. But not everyone has inttypes.h?*/
6816 unsigned long uA = (unsigned long)wA;
6817 #ifdef _MSC_VER
6818 wC = _rotl(uA,(unsigned long)wB);
6819 #else
6820 const unsigned int S = sizeof(unsigned long) * 8;
6821 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6822 #endif
6823 break;
6824 }
6825 case JIM_EXPROP_ROTR: {
6826 unsigned long uA = (unsigned long)wA;
6827 #ifdef _MSC_VER
6828 wC = _rotr(uA,(unsigned long)wB);
6829 #else
6830 const unsigned int S = sizeof(unsigned long) * 8;
6831 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6832 #endif
6833 break;
6834 }
6835
6836 default:
6837 wC = 0; /* avoid gcc warning */
6838 break;
6839 }
6840 stack[stacklen] = Jim_NewIntObj(interp, wC);
6841 Jim_IncrRefCount(stack[stacklen]);
6842 stacklen++;
6843 continue;
6844 trydouble:
6845 /* --- Double --- */
6846 if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6847 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6848 Jim_DecrRefCount(interp, A);
6849 Jim_DecrRefCount(interp, B);
6850 error = 1;
6851 goto err;
6852 }
6853 Jim_DecrRefCount(interp, A);
6854 Jim_DecrRefCount(interp, B);
6855 switch(expr->opcode[i]) {
6856 case JIM_EXPROP_ROTL:
6857 case JIM_EXPROP_ROTR:
6858 case JIM_EXPROP_LSHIFT:
6859 case JIM_EXPROP_RSHIFT:
6860 case JIM_EXPROP_BITAND:
6861 case JIM_EXPROP_BITXOR:
6862 case JIM_EXPROP_BITOR:
6863 case JIM_EXPROP_MOD:
6864 case JIM_EXPROP_POW:
6865 Jim_SetResultString(interp,
6866 "Got floating-point value where integer was expected", -1);
6867 error = 1;
6868 goto err;
6869 break;
6870 case JIM_EXPROP_ADD: dC = dA+dB; break;
6871 case JIM_EXPROP_SUB: dC = dA-dB; break;
6872 case JIM_EXPROP_MUL: dC = dA*dB; break;
6873 case JIM_EXPROP_LT: dC = dA<dB; break;
6874 case JIM_EXPROP_GT: dC = dA>dB; break;
6875 case JIM_EXPROP_LTE: dC = dA<=dB; break;
6876 case JIM_EXPROP_GTE: dC = dA>=dB; break;
6877 case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6878 case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6879 case JIM_EXPROP_LOGICAND_LEFT:
6880 if (dA == 0) {
6881 i += (int)dB;
6882 dC = 0;
6883 } else {
6884 continue;
6885 }
6886 break;
6887 case JIM_EXPROP_LOGICOR_LEFT:
6888 if (dA != 0) {
6889 i += (int)dB;
6890 dC = 1;
6891 } else {
6892 continue;
6893 }
6894 break;
6895 case JIM_EXPROP_DIV:
6896 if (dB == 0) goto divbyzero;
6897 dC = dA/dB;
6898 break;
6899 default:
6900 dC = 0; /* avoid gcc warning */
6901 break;
6902 }
6903 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6904 Jim_IncrRefCount(stack[stacklen]);
6905 stacklen++;
6906 } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6907 B = stack[--stacklen];
6908 A = stack[--stacklen];
6909 sA = Jim_GetString(A, &Alen);
6910 sB = Jim_GetString(B, &Blen);
6911 switch(expr->opcode[i]) {
6912 case JIM_EXPROP_STREQ:
6913 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6914 wC = 1;
6915 else
6916 wC = 0;
6917 break;
6918 case JIM_EXPROP_STRNE:
6919 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6920 wC = 1;
6921 else
6922 wC = 0;
6923 break;
6924 default:
6925 wC = 0; /* avoid gcc warning */
6926 break;
6927 }
6928 Jim_DecrRefCount(interp, A);
6929 Jim_DecrRefCount(interp, B);
6930 stack[stacklen] = Jim_NewIntObj(interp, wC);
6931 Jim_IncrRefCount(stack[stacklen]);
6932 stacklen++;
6933 } else if (opcode == JIM_EXPROP_NOT ||
6934 opcode == JIM_EXPROP_BITNOT ||
6935 opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6936 opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6937 /* Note that there isn't to increment the
6938 * refcount of objects. the references are moved
6939 * from stack to A and B. */
6940 A = stack[--stacklen];
6941
6942 /* --- Integer --- */
6943 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6944 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6945 goto trydouble_unary;
6946 }
6947 Jim_DecrRefCount(interp, A);
6948 switch(expr->opcode[i]) {
6949 case JIM_EXPROP_NOT: wC = !wA; break;
6950 case JIM_EXPROP_BITNOT: wC = ~wA; break;
6951 case JIM_EXPROP_LOGICAND_RIGHT:
6952 case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6953 default:
6954 wC = 0; /* avoid gcc warning */
6955 break;
6956 }
6957 stack[stacklen] = Jim_NewIntObj(interp, wC);
6958 Jim_IncrRefCount(stack[stacklen]);
6959 stacklen++;
6960 continue;
6961 trydouble_unary:
6962 /* --- Double --- */
6963 if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
6964 Jim_DecrRefCount(interp, A);
6965 error = 1;
6966 goto err;
6967 }
6968 Jim_DecrRefCount(interp, A);
6969 switch(expr->opcode[i]) {
6970 case JIM_EXPROP_NOT: dC = !dA; break;
6971 case JIM_EXPROP_LOGICAND_RIGHT:
6972 case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
6973 case JIM_EXPROP_BITNOT:
6974 Jim_SetResultString(interp,
6975 "Got floating-point value where integer was expected", -1);
6976 error = 1;
6977 goto err;
6978 break;
6979 default:
6980 dC = 0; /* avoid gcc warning */
6981 break;
6982 }
6983 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6984 Jim_IncrRefCount(stack[stacklen]);
6985 stacklen++;
6986 } else {
6987 Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
6988 }
6989 }
6990 err:
6991 /* There is no need to decerement the inUse field because
6992 * this reference is transfered back into the exprObjPtr. */
6993 Jim_FreeIntRep(interp, exprObjPtr);
6994 exprObjPtr->typePtr = &exprObjType;
6995 Jim_SetIntRepPtr(exprObjPtr, expr);
6996 Jim_DecrRefCount(interp, exprObjPtr);
6997 if (!error) {
6998 *exprResultPtrPtr = stack[0];
6999 Jim_IncrRefCount(stack[0]);
7000 errRetCode = JIM_OK;
7001 }
7002 for (i = 0; i < stacklen; i++) {
7003 Jim_DecrRefCount(interp, stack[i]);
7004 }
7005 if (stack != staticStack)
7006 Jim_Free(stack);
7007 return errRetCode;
7008 divbyzero:
7009 error = 1;
7010 Jim_SetResultString(interp, "Division by zero", -1);
7011 goto err;
7012 }
7013
7014 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7015 {
7016 int retcode;
7017 jim_wide wideValue;
7018 double doubleValue;
7019 Jim_Obj *exprResultPtr;
7020
7021 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7022 if (retcode != JIM_OK)
7023 return retcode;
7024 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7025 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7026 {
7027 Jim_DecrRefCount(interp, exprResultPtr);
7028 return JIM_ERR;
7029 } else {
7030 Jim_DecrRefCount(interp, exprResultPtr);
7031 *boolPtr = doubleValue != 0;
7032 return JIM_OK;
7033 }
7034 }
7035 Jim_DecrRefCount(interp, exprResultPtr);
7036 *boolPtr = wideValue != 0;
7037 return JIM_OK;
7038 }
7039
7040 /* -----------------------------------------------------------------------------
7041 * ScanFormat String Object
7042 * ---------------------------------------------------------------------------*/
7043
7044 /* This Jim_Obj will held a parsed representation of a format string passed to
7045 * the Jim_ScanString command. For error diagnostics, the scanformat string has
7046 * to be parsed in its entirely first and then, if correct, can be used for
7047 * scanning. To avoid endless re-parsing, the parsed representation will be
7048 * stored in an internal representation and re-used for performance reason. */
7049
7050 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7051 * scanformat string. This part will later be used to extract information
7052 * out from the string to be parsed by Jim_ScanString */
7053
7054 typedef struct ScanFmtPartDescr {
7055 char type; /* Type of conversion (e.g. c, d, f) */
7056 char modifier; /* Modify type (e.g. l - long, h - short */
7057 size_t width; /* Maximal width of input to be converted */
7058 int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
7059 char *arg; /* Specification of a CHARSET conversion */
7060 char *prefix; /* Prefix to be scanned literally before conversion */
7061 } ScanFmtPartDescr;
7062
7063 /* The ScanFmtStringObj will held the internal representation of a scanformat
7064 * string parsed and separated in part descriptions. Furthermore it contains
7065 * the original string representation of the scanformat string to allow for
7066 * fast update of the Jim_Obj's string representation part.
7067 *
7068 * As add-on the internal object representation add some scratch pad area
7069 * for usage by Jim_ScanString to avoid endless allocating and freeing of
7070 * memory for purpose of string scanning.
7071 *
7072 * The error member points to a static allocated string in case of a mal-
7073 * formed scanformat string or it contains '0' (NULL) in case of a valid
7074 * parse representation.
7075 *
7076 * The whole memory of the internal representation is allocated as a single
7077 * area of memory that will be internally separated. So freeing and duplicating
7078 * of such an object is cheap */
7079
7080 typedef struct ScanFmtStringObj {
7081 jim_wide size; /* Size of internal repr in bytes */
7082 char *stringRep; /* Original string representation */
7083 size_t count; /* Number of ScanFmtPartDescr contained */
7084 size_t convCount; /* Number of conversions that will assign */
7085 size_t maxPos; /* Max position index if XPG3 is used */
7086 const char *error; /* Ptr to error text (NULL if no error */
7087 char *scratch; /* Some scratch pad used by Jim_ScanString */
7088 ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
7089 } ScanFmtStringObj;
7090
7091
7092 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7093 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7094 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7095
7096 static Jim_ObjType scanFmtStringObjType = {
7097 "scanformatstring",
7098 FreeScanFmtInternalRep,
7099 DupScanFmtInternalRep,
7100 UpdateStringOfScanFmt,
7101 JIM_TYPE_NONE,
7102 };
7103
7104 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7105 {
7106 JIM_NOTUSED(interp);
7107 Jim_Free((char*)objPtr->internalRep.ptr);
7108 objPtr->internalRep.ptr = 0;
7109 }
7110
7111 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7112 {
7113 size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7114 ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7115
7116 JIM_NOTUSED(interp);
7117 memcpy(newVec, srcPtr->internalRep.ptr, size);
7118 dupPtr->internalRep.ptr = newVec;
7119 dupPtr->typePtr = &scanFmtStringObjType;
7120 }
7121
7122 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7123 {
7124 char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7125
7126 objPtr->bytes = Jim_StrDup(bytes);
7127 objPtr->length = strlen(bytes);
7128 }
7129
7130 /* SetScanFmtFromAny will parse a given string and create the internal
7131 * representation of the format specification. In case of an error
7132 * the error data member of the internal representation will be set
7133 * to an descriptive error text and the function will be left with
7134 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7135 * specification */
7136
7137 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7138 {
7139 ScanFmtStringObj *fmtObj;
7140 char *buffer;
7141 int maxCount, i, approxSize, lastPos = -1;
7142 const char *fmt = objPtr->bytes;
7143 int maxFmtLen = objPtr->length;
7144 const char *fmtEnd = fmt + maxFmtLen;
7145 int curr;
7146
7147 Jim_FreeIntRep(interp, objPtr);
7148 /* Count how many conversions could take place maximally */
7149 for (i=0, maxCount=0; i < maxFmtLen; ++i)
7150 if (fmt[i] == '%')
7151 ++maxCount;
7152 /* Calculate an approximation of the memory necessary */
7153 approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
7154 + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7155 + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
7156 + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
7157 + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
7158 + (maxCount +1) * sizeof(char) /* '\0' for every partial */
7159 + 1; /* safety byte */
7160 fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7161 memset(fmtObj, 0, approxSize);
7162 fmtObj->size = approxSize;
7163 fmtObj->maxPos = 0;
7164 fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7165 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7166 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7167 buffer = fmtObj->stringRep + maxFmtLen + 1;
7168 objPtr->internalRep.ptr = fmtObj;
7169 objPtr->typePtr = &scanFmtStringObjType;
7170 for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7171 int width=0, skip;
7172 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7173 fmtObj->count++;
7174 descr->width = 0; /* Assume width unspecified */
7175 /* Overread and store any "literal" prefix */
7176 if (*fmt != '%' || fmt[1] == '%') {
7177 descr->type = 0;
7178 descr->prefix = &buffer[i];
7179 for (; fmt < fmtEnd; ++fmt) {
7180 if (*fmt == '%') {
7181 if (fmt[1] != '%') break;
7182 ++fmt;
7183 }
7184 buffer[i++] = *fmt;
7185 }
7186 buffer[i++] = 0;
7187 }
7188 /* Skip the conversion introducing '%' sign */
7189 ++fmt;
7190 /* End reached due to non-conversion literal only? */
7191 if (fmt >= fmtEnd)
7192 goto done;
7193 descr->pos = 0; /* Assume "natural" positioning */
7194 if (*fmt == '*') {
7195 descr->pos = -1; /* Okay, conversion will not be assigned */
7196 ++fmt;
7197 } else
7198 fmtObj->convCount++; /* Otherwise count as assign-conversion */
7199 /* Check if next token is a number (could be width or pos */
7200 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7201 fmt += skip;
7202 /* Was the number a XPG3 position specifier? */
7203 if (descr->pos != -1 && *fmt == '$') {
7204 int prev;
7205 ++fmt;
7206 descr->pos = width;
7207 width = 0;
7208 /* Look if "natural" postioning and XPG3 one was mixed */
7209 if ((lastPos == 0 && descr->pos > 0)
7210 || (lastPos > 0 && descr->pos == 0)) {
7211 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7212 return JIM_ERR;
7213 }
7214 /* Look if this position was already used */
7215 for (prev=0; prev < curr; ++prev) {
7216 if (fmtObj->descr[prev].pos == -1) continue;
7217 if (fmtObj->descr[prev].pos == descr->pos) {
7218 fmtObj->error = "same \"%n$\" conversion specifier "
7219 "used more than once";
7220 return JIM_ERR;
7221 }
7222 }
7223 /* Try to find a width after the XPG3 specifier */
7224 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7225 descr->width = width;
7226 fmt += skip;
7227 }
7228 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7229 fmtObj->maxPos = descr->pos;
7230 } else {
7231 /* Number was not a XPG3, so it has to be a width */
7232 descr->width = width;
7233 }
7234 }
7235 /* If positioning mode was undetermined yet, fix this */
7236 if (lastPos == -1)
7237 lastPos = descr->pos;
7238 /* Handle CHARSET conversion type ... */
7239 if (*fmt == '[') {
7240 int swapped = 1, beg = i, end, j;
7241 descr->type = '[';
7242 descr->arg = &buffer[i];
7243 ++fmt;
7244 if (*fmt == '^') buffer[i++] = *fmt++;
7245 if (*fmt == ']') buffer[i++] = *fmt++;
7246 while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7247 if (*fmt != ']') {
7248 fmtObj->error = "unmatched [ in format string";
7249 return JIM_ERR;
7250 }
7251 end = i;
7252 buffer[i++] = 0;
7253 /* In case a range fence was given "backwards", swap it */
7254 while (swapped) {
7255 swapped = 0;
7256 for (j=beg+1; j < end-1; ++j) {
7257 if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7258 char tmp = buffer[j-1];
7259 buffer[j-1] = buffer[j+1];
7260 buffer[j+1] = tmp;
7261 swapped = 1;
7262 }
7263 }
7264 }
7265 } else {
7266 /* Remember any valid modifier if given */
7267 if (strchr("hlL", *fmt) != 0)
7268 descr->modifier = tolower((int)*fmt++);
7269
7270 descr->type = *fmt;
7271 if (strchr("efgcsndoxui", *fmt) == 0) {
7272 fmtObj->error = "bad scan conversion character";
7273 return JIM_ERR;
7274 } else if (*fmt == 'c' && descr->width != 0) {
7275 fmtObj->error = "field width may not be specified in %c "
7276 "conversion";
7277 return JIM_ERR;
7278 } else if (*fmt == 'u' && descr->modifier == 'l') {
7279 fmtObj->error = "unsigned wide not supported";
7280 return JIM_ERR;
7281 }
7282 }
7283 curr++;
7284 }
7285 done:
7286 if (fmtObj->convCount == 0) {
7287 fmtObj->error = "no any conversion specifier given";
7288 return JIM_ERR;
7289 }
7290 return JIM_OK;
7291 }
7292
7293 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7294
7295 #define FormatGetCnvCount(_fo_) \
7296 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7297 #define FormatGetMaxPos(_fo_) \
7298 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7299 #define FormatGetError(_fo_) \
7300 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7301
7302 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7303 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7304 * bitvector implementation in Jim? */
7305
7306 static int JimTestBit(const char *bitvec, char ch)
7307 {
7308 div_t pos = div(ch-1, 8);
7309 return bitvec[pos.quot] & (1 << pos.rem);
7310 }
7311
7312 static void JimSetBit(char *bitvec, char ch)
7313 {
7314 div_t pos = div(ch-1, 8);
7315 bitvec[pos.quot] |= (1 << pos.rem);
7316 }
7317
7318 #if 0 /* currently not used */
7319 static void JimClearBit(char *bitvec, char ch)
7320 {
7321 div_t pos = div(ch-1, 8);
7322 bitvec[pos.quot] &= ~(1 << pos.rem);
7323 }
7324 #endif
7325
7326 /* JimScanAString is used to scan an unspecified string that ends with
7327 * next WS, or a string that is specified via a charset. The charset
7328 * is currently implemented in a way to only allow for usage with
7329 * ASCII. Whenever we will switch to UNICODE, another idea has to
7330 * be born :-/
7331 *
7332 * FIXME: Works only with ASCII */
7333
7334 static Jim_Obj *
7335 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7336 {
7337 size_t i;
7338 Jim_Obj *result;
7339 char charset[256/8+1]; /* A Charset may contain max 256 chars */
7340 char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7341
7342 /* First init charset to nothing or all, depending if a specified
7343 * or an unspecified string has to be parsed */
7344 memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7345 if (sdescr) {
7346 /* There was a set description given, that means we are parsing
7347 * a specified string. So we have to build a corresponding
7348 * charset reflecting the description */
7349 int notFlag = 0;
7350 /* Should the set be negated at the end? */
7351 if (*sdescr == '^') {
7352 notFlag = 1;
7353 ++sdescr;
7354 }
7355 /* Here '-' is meant literally and not to define a range */
7356 if (*sdescr == '-') {
7357 JimSetBit(charset, '-');
7358 ++sdescr;
7359 }
7360 while (*sdescr) {
7361 if (sdescr[1] == '-' && sdescr[2] != 0) {
7362 /* Handle range definitions */
7363 int i;
7364 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7365 JimSetBit(charset, (char)i);
7366 sdescr += 3;
7367 } else {
7368 /* Handle verbatim character definitions */
7369 JimSetBit(charset, *sdescr++);
7370 }
7371 }
7372 /* Negate the charset if there was a NOT given */
7373 for (i=0; notFlag && i < sizeof(charset); ++i)
7374 charset[i] = ~charset[i];
7375 }
7376 /* And after all the mess above, the real work begin ... */
7377 while (str && *str) {
7378 if (!sdescr && isspace((int)*str))
7379 break; /* EOS via WS if unspecified */
7380 if (JimTestBit(charset, *str)) *buffer++ = *str++;
7381 else break; /* EOS via mismatch if specified scanning */
7382 }
7383 *buffer = 0; /* Close the string properly ... */
7384 result = Jim_NewStringObj(interp, anchor, -1);
7385 Jim_Free(anchor); /* ... and free it afer usage */
7386 return result;
7387 }
7388
7389 /* ScanOneEntry will scan one entry out of the string passed as argument.
7390 * It use the sscanf() function for this task. After extracting and
7391 * converting of the value, the count of scanned characters will be
7392 * returned of -1 in case of no conversion tool place and string was
7393 * already scanned thru */
7394
7395 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7396 ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7397 {
7398 # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7399 ? sizeof(jim_wide) \
7400 : sizeof(double))
7401 char buffer[MAX_SIZE];
7402 char *value = buffer;
7403 const char *tok;
7404 const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7405 size_t sLen = strlen(&str[pos]), scanned = 0;
7406 size_t anchor = pos;
7407 int i;
7408
7409 /* First pessimiticly assume, we will not scan anything :-) */
7410 *valObjPtr = 0;
7411 if (descr->prefix) {
7412 /* There was a prefix given before the conversion, skip it and adjust
7413 * the string-to-be-parsed accordingly */
7414 for (i=0; str[pos] && descr->prefix[i]; ++i) {
7415 /* If prefix require, skip WS */
7416 if (isspace((int)descr->prefix[i]))
7417 while (str[pos] && isspace((int)str[pos])) ++pos;
7418 else if (descr->prefix[i] != str[pos])
7419 break; /* Prefix do not match here, leave the loop */
7420 else
7421 ++pos; /* Prefix matched so far, next round */
7422 }
7423 if (str[pos] == 0)
7424 return -1; /* All of str consumed: EOF condition */
7425 else if (descr->prefix[i] != 0)
7426 return 0; /* Not whole prefix consumed, no conversion possible */
7427 }
7428 /* For all but following conversion, skip leading WS */
7429 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7430 while (isspace((int)str[pos])) ++pos;
7431 /* Determine how much skipped/scanned so far */
7432 scanned = pos - anchor;
7433 if (descr->type == 'n') {
7434 /* Return pseudo conversion means: how much scanned so far? */
7435 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7436 } else if (str[pos] == 0) {
7437 /* Cannot scan anything, as str is totally consumed */
7438 return -1;
7439 } else {
7440 /* Processing of conversions follows ... */
7441 if (descr->width > 0) {
7442 /* Do not try to scan as fas as possible but only the given width.
7443 * To ensure this, we copy the part that should be scanned. */
7444 size_t tLen = descr->width > sLen ? sLen : descr->width;
7445 tok = Jim_StrDupLen(&str[pos], tLen);
7446 } else {
7447 /* As no width was given, simply refer to the original string */
7448 tok = &str[pos];
7449 }
7450 switch (descr->type) {
7451 case 'c':
7452 *valObjPtr = Jim_NewIntObj(interp, *tok);
7453 scanned += 1;
7454 break;
7455 case 'd': case 'o': case 'x': case 'u': case 'i': {
7456 char *endp; /* Position where the number finished */
7457 int base = descr->type == 'o' ? 8
7458 : descr->type == 'x' ? 16
7459 : descr->type == 'i' ? 0
7460 : 10;
7461
7462 do {
7463 /* Try to scan a number with the given base */
7464 if (descr->modifier == 'l')
7465 #ifdef HAVE_LONG_LONG
7466 *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7467 #else
7468 *(jim_wide*)value = strtol(tok, &endp, base);
7469 #endif
7470 else
7471 if (descr->type == 'u')
7472 *(long*)value = strtoul(tok, &endp, base);
7473 else
7474 *(long*)value = strtol(tok, &endp, base);
7475 /* If scanning failed, and base was undetermined, simply
7476 * put it to 10 and try once more. This should catch the
7477 * case where %i begin to parse a number prefix (e.g.
7478 * '0x' but no further digits follows. This will be
7479 * handled as a ZERO followed by a char 'x' by Tcl */
7480 if (endp == tok && base == 0) base = 10;
7481 else break;
7482 } while (1);
7483 if (endp != tok) {
7484 /* There was some number sucessfully scanned! */
7485 if (descr->modifier == 'l')
7486 *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7487 else
7488 *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7489 /* Adjust the number-of-chars scanned so far */
7490 scanned += endp - tok;
7491 } else {
7492 /* Nothing was scanned. We have to determine if this
7493 * happened due to e.g. prefix mismatch or input str
7494 * exhausted */
7495 scanned = *tok ? 0 : -1;
7496 }
7497 break;
7498 }
7499 case 's': case '[': {
7500 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7501 scanned += Jim_Length(*valObjPtr);
7502 break;
7503 }
7504 case 'e': case 'f': case 'g': {
7505 char *endp;
7506
7507 *(double*)value = strtod(tok, &endp);
7508 if (endp != tok) {
7509 /* There was some number sucessfully scanned! */
7510 *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7511 /* Adjust the number-of-chars scanned so far */
7512 scanned += endp - tok;
7513 } else {
7514 /* Nothing was scanned. We have to determine if this
7515 * happened due to e.g. prefix mismatch or input str
7516 * exhausted */
7517 scanned = *tok ? 0 : -1;
7518 }
7519 break;
7520 }
7521 }
7522 /* If a substring was allocated (due to pre-defined width) do not
7523 * forget to free it */
7524 if (tok != &str[pos])
7525 Jim_Free((char*)tok);
7526 }
7527 return scanned;
7528 }
7529
7530 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7531 * string and returns all converted (and not ignored) values in a list back
7532 * to the caller. If an error occured, a NULL pointer will be returned */
7533
7534 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7535 Jim_Obj *fmtObjPtr, int flags)
7536 {
7537 size_t i, pos;
7538 int scanned = 1;
7539 const char *str = Jim_GetString(strObjPtr, 0);
7540 Jim_Obj *resultList = 0;
7541 Jim_Obj **resultVec;
7542 int resultc;
7543 Jim_Obj *emptyStr = 0;
7544 ScanFmtStringObj *fmtObj;
7545
7546 /* If format specification is not an object, convert it! */
7547 if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7548 SetScanFmtFromAny(interp, fmtObjPtr);
7549 fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7550 /* Check if format specification was valid */
7551 if (fmtObj->error != 0) {
7552 if (flags & JIM_ERRMSG)
7553 Jim_SetResultString(interp, fmtObj->error, -1);
7554 return 0;
7555 }
7556 /* Allocate a new "shared" empty string for all unassigned conversions */
7557 emptyStr = Jim_NewEmptyStringObj(interp);
7558 Jim_IncrRefCount(emptyStr);
7559 /* Create a list and fill it with empty strings up to max specified XPG3 */
7560 resultList = Jim_NewListObj(interp, 0, 0);
7561 if (fmtObj->maxPos > 0) {
7562 for (i=0; i < fmtObj->maxPos; ++i)
7563 Jim_ListAppendElement(interp, resultList, emptyStr);
7564 JimListGetElements(interp, resultList, &resultc, &resultVec);
7565 }
7566 /* Now handle every partial format description */
7567 for (i=0, pos=0; i < fmtObj->count; ++i) {
7568 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7569 Jim_Obj *value = 0;
7570 /* Only last type may be "literal" w/o conversion - skip it! */
7571 if (descr->type == 0) continue;
7572 /* As long as any conversion could be done, we will proceed */
7573 if (scanned > 0)
7574 scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7575 /* In case our first try results in EOF, we will leave */
7576 if (scanned == -1 && i == 0)
7577 goto eof;
7578 /* Advance next pos-to-be-scanned for the amount scanned already */
7579 pos += scanned;
7580 /* value == 0 means no conversion took place so take empty string */
7581 if (value == 0)
7582 value = Jim_NewEmptyStringObj(interp);
7583 /* If value is a non-assignable one, skip it */
7584 if (descr->pos == -1) {
7585 Jim_FreeNewObj(interp, value);
7586 } else if (descr->pos == 0)
7587 /* Otherwise append it to the result list if no XPG3 was given */
7588 Jim_ListAppendElement(interp, resultList, value);
7589 else if (resultVec[descr->pos-1] == emptyStr) {
7590 /* But due to given XPG3, put the value into the corr. slot */
7591 Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7592 Jim_IncrRefCount(value);
7593 resultVec[descr->pos-1] = value;
7594 } else {
7595 /* Otherwise, the slot was already used - free obj and ERROR */
7596 Jim_FreeNewObj(interp, value);
7597 goto err;
7598 }
7599 }
7600 Jim_DecrRefCount(interp, emptyStr);
7601 return resultList;
7602 eof:
7603 Jim_DecrRefCount(interp, emptyStr);
7604 Jim_FreeNewObj(interp, resultList);
7605 return (Jim_Obj*)EOF;
7606 err:
7607 Jim_DecrRefCount(interp, emptyStr);
7608 Jim_FreeNewObj(interp, resultList);
7609 return 0;
7610 }
7611
7612 /* -----------------------------------------------------------------------------
7613 * Pseudo Random Number Generation
7614 * ---------------------------------------------------------------------------*/
7615 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7616 int seedLen);
7617
7618 /* Initialize the sbox with the numbers from 0 to 255 */
7619 static void JimPrngInit(Jim_Interp *interp)
7620 {
7621 int i;
7622 unsigned int seed[256];
7623
7624 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7625 for (i = 0; i < 256; i++)
7626 seed[i] = (rand() ^ time(NULL) ^ clock());
7627 JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7628 }
7629
7630 /* Generates N bytes of random data */
7631 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7632 {
7633 Jim_PrngState *prng;
7634 unsigned char *destByte = (unsigned char*) dest;
7635 unsigned int si, sj, x;
7636
7637 /* initialization, only needed the first time */
7638 if (interp->prngState == NULL)
7639 JimPrngInit(interp);
7640 prng = interp->prngState;
7641 /* generates 'len' bytes of pseudo-random numbers */
7642 for (x = 0; x < len; x++) {
7643 prng->i = (prng->i+1) & 0xff;
7644 si = prng->sbox[prng->i];
7645 prng->j = (prng->j + si) & 0xff;
7646 sj = prng->sbox[prng->j];
7647 prng->sbox[prng->i] = sj;
7648 prng->sbox[prng->j] = si;
7649 *destByte++ = prng->sbox[(si+sj)&0xff];
7650 }
7651 }
7652
7653 /* Re-seed the generator with user-provided bytes */
7654 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7655 int seedLen)
7656 {
7657 int i;
7658 unsigned char buf[256];
7659 Jim_PrngState *prng;
7660
7661 /* initialization, only needed the first time */
7662 if (interp->prngState == NULL)
7663 JimPrngInit(interp);
7664 prng = interp->prngState;
7665
7666 /* Set the sbox[i] with i */
7667 for (i = 0; i < 256; i++)
7668 prng->sbox[i] = i;
7669 /* Now use the seed to perform a random permutation of the sbox */
7670 for (i = 0; i < seedLen; i++) {
7671 unsigned char t;
7672
7673 t = prng->sbox[i&0xFF];
7674 prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7675 prng->sbox[seed[i]] = t;
7676 }
7677 prng->i = prng->j = 0;
7678 /* discard the first 256 bytes of stream. */
7679 JimRandomBytes(interp, buf, 256);
7680 }
7681
7682 /* -----------------------------------------------------------------------------
7683 * Dynamic libraries support (WIN32 not supported)
7684 * ---------------------------------------------------------------------------*/
7685
7686 #ifdef JIM_DYNLIB
7687 #ifdef WIN32
7688 #define RTLD_LAZY 0
7689 void * dlopen(const char *path, int mode)
7690 {
7691 JIM_NOTUSED(mode);
7692
7693 return (void *)LoadLibraryA(path);
7694 }
7695 int dlclose(void *handle)
7696 {
7697 FreeLibrary((HANDLE)handle);
7698 return 0;
7699 }
7700 void *dlsym(void *handle, const char *symbol)
7701 {
7702 return GetProcAddress((HMODULE)handle, symbol);
7703 }
7704 static char win32_dlerror_string[121];
7705 const char *dlerror()
7706 {
7707 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7708 LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7709 return win32_dlerror_string;
7710 }
7711 #endif /* WIN32 */
7712
7713 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7714 {
7715 Jim_Obj *libPathObjPtr;
7716 int prefixc, i;
7717 void *handle;
7718 int (*onload)(Jim_Interp *interp);
7719
7720 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7721 if (libPathObjPtr == NULL) {
7722 prefixc = 0;
7723 libPathObjPtr = NULL;
7724 } else {
7725 Jim_IncrRefCount(libPathObjPtr);
7726 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7727 }
7728
7729 for (i = -1; i < prefixc; i++) {
7730 if (i < 0) {
7731 handle = dlopen(pathName, RTLD_LAZY);
7732 } else {
7733 FILE *fp;
7734 char buf[JIM_PATH_LEN];
7735 const char *prefix;
7736 int prefixlen;
7737 Jim_Obj *prefixObjPtr;
7738
7739 buf[0] = '\0';
7740 if (Jim_ListIndex(interp, libPathObjPtr, i,
7741 &prefixObjPtr, JIM_NONE) != JIM_OK)
7742 continue;
7743 prefix = Jim_GetString(prefixObjPtr, NULL);
7744 prefixlen = strlen(prefix);
7745 if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7746 continue;
7747 if (prefixlen && prefix[prefixlen-1] == '/')
7748 sprintf(buf, "%s%s", prefix, pathName);
7749 else
7750 sprintf(buf, "%s/%s", prefix, pathName);
7751 printf("opening '%s'\n", buf);
7752 fp = fopen(buf, "r");
7753 if (fp == NULL)
7754 continue;
7755 fclose(fp);
7756 handle = dlopen(buf, RTLD_LAZY);
7757 printf("got handle %p\n", handle);
7758 }
7759 if (handle == NULL) {
7760 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7761 Jim_AppendStrings(interp, Jim_GetResult(interp),
7762 "error loading extension \"", pathName,
7763 "\": ", dlerror(), NULL);
7764 if (i < 0)
7765 continue;
7766 goto err;
7767 }
7768 if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7769 Jim_SetResultString(interp,
7770 "No Jim_OnLoad symbol found on extension", -1);
7771 goto err;
7772 }
7773 if (onload(interp) == JIM_ERR) {
7774 dlclose(handle);
7775 goto err;
7776 }
7777 Jim_SetEmptyResult(interp);
7778 if (libPathObjPtr != NULL)
7779 Jim_DecrRefCount(interp, libPathObjPtr);
7780 return JIM_OK;
7781 }
7782 err:
7783 if (libPathObjPtr != NULL)
7784 Jim_DecrRefCount(interp, libPathObjPtr);
7785 return JIM_ERR;
7786 }
7787 #else /* JIM_DYNLIB */
7788 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7789 {
7790 JIM_NOTUSED(interp);
7791 JIM_NOTUSED(pathName);
7792
7793 Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7794 return JIM_ERR;
7795 }
7796 #endif/* JIM_DYNLIB */
7797
7798 /* -----------------------------------------------------------------------------
7799 * Packages handling
7800 * ---------------------------------------------------------------------------*/
7801
7802 #define JIM_PKG_ANY_VERSION -1
7803
7804 /* Convert a string of the type "1.2" into an integer.
7805 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
7806 * to the integer with value 102 */
7807 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7808 int *intPtr, int flags)
7809 {
7810 char *copy;
7811 jim_wide major, minor;
7812 char *majorStr, *minorStr, *p;
7813
7814 if (v[0] == '\0') {
7815 *intPtr = JIM_PKG_ANY_VERSION;
7816 return JIM_OK;
7817 }
7818
7819 copy = Jim_StrDup(v);
7820 p = strchr(copy, '.');
7821 if (p == NULL) goto badfmt;
7822 *p = '\0';
7823 majorStr = copy;
7824 minorStr = p+1;
7825
7826 if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7827 Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7828 goto badfmt;
7829 *intPtr = (int)(major*100+minor);
7830 Jim_Free(copy);
7831 return JIM_OK;
7832
7833 badfmt:
7834 Jim_Free(copy);
7835 if (flags & JIM_ERRMSG) {
7836 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7837 Jim_AppendStrings(interp, Jim_GetResult(interp),
7838 "invalid package version '", v, "'", NULL);
7839 }
7840 return JIM_ERR;
7841 }
7842
7843 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7844 static int JimPackageMatchVersion(int needed, int actual, int flags)
7845 {
7846 if (needed == JIM_PKG_ANY_VERSION) return 1;
7847 if (flags & JIM_MATCHVER_EXACT) {
7848 return needed == actual;
7849 } else {
7850 return needed/100 == actual/100 && (needed <= actual);
7851 }
7852 }
7853
7854 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7855 int flags)
7856 {
7857 int intVersion;
7858 /* Check if the version format is ok */
7859 if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7860 return JIM_ERR;
7861 /* If the package was already provided returns an error. */
7862 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7863 if (flags & JIM_ERRMSG) {
7864 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7865 Jim_AppendStrings(interp, Jim_GetResult(interp),
7866 "package '", name, "' was already provided", NULL);
7867 }
7868 return JIM_ERR;
7869 }
7870 Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7871 return JIM_OK;
7872 }
7873
7874 #ifndef JIM_ANSIC
7875
7876 #ifndef WIN32
7877 # include <sys/types.h>
7878 # include <dirent.h>
7879 #else
7880 # include <io.h>
7881 /* Posix dirent.h compatiblity layer for WIN32.
7882 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7883 * Copyright Salvatore Sanfilippo ,2005.
7884 *
7885 * Permission to use, copy, modify, and distribute this software and its
7886 * documentation for any purpose is hereby granted without fee, provided
7887 * that this copyright and permissions notice appear in all copies and
7888 * derivatives.
7889 *
7890 * This software is supplied "as is" without express or implied warranty.
7891 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7892 */
7893
7894 struct dirent {
7895 char *d_name;
7896 };
7897
7898 typedef struct DIR {
7899 long handle; /* -1 for failed rewind */
7900 struct _finddata_t info;
7901 struct dirent result; /* d_name null iff first time */
7902 char *name; /* null-terminated char string */
7903 } DIR;
7904
7905 DIR *opendir(const char *name)
7906 {
7907 DIR *dir = 0;
7908
7909 if(name && name[0]) {
7910 size_t base_length = strlen(name);
7911 const char *all = /* search pattern must end with suitable wildcard */
7912 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7913
7914 if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7915 (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7916 {
7917 strcat(strcpy(dir->name, name), all);
7918
7919 if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7920 dir->result.d_name = 0;
7921 else { /* rollback */
7922 Jim_Free(dir->name);
7923 Jim_Free(dir);
7924 dir = 0;
7925 }
7926 } else { /* rollback */
7927 Jim_Free(dir);
7928 dir = 0;
7929 errno = ENOMEM;
7930 }
7931 } else {
7932 errno = EINVAL;
7933 }
7934 return dir;
7935 }
7936
7937 int closedir(DIR *dir)
7938 {
7939 int result = -1;
7940
7941 if(dir) {
7942 if(dir->handle != -1)
7943 result = _findclose(dir->handle);
7944 Jim_Free(dir->name);
7945 Jim_Free(dir);
7946 }
7947 if(result == -1) /* map all errors to EBADF */
7948 errno = EBADF;
7949 return result;
7950 }
7951
7952 struct dirent *readdir(DIR *dir)
7953 {
7954 struct dirent *result = 0;
7955
7956 if(dir && dir->handle != -1) {
7957 if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7958 result = &dir->result;
7959 result->d_name = dir->info.name;
7960 }
7961 } else {
7962 errno = EBADF;
7963 }
7964 return result;
7965 }
7966
7967 #endif /* WIN32 */
7968
7969 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7970 int prefixc, const char *pkgName, int pkgVer, int flags)
7971 {
7972 int bestVer = -1, i;
7973 int pkgNameLen = strlen(pkgName);
7974 char *bestPackage = NULL;
7975 struct dirent *de;
7976
7977 for (i = 0; i < prefixc; i++) {
7978 DIR *dir;
7979 char buf[JIM_PATH_LEN];
7980 int prefixLen;
7981
7982 if (prefixes[i] == NULL) continue;
7983 strncpy(buf, prefixes[i], JIM_PATH_LEN);
7984 buf[JIM_PATH_LEN-1] = '\0';
7985 prefixLen = strlen(buf);
7986 if (prefixLen && buf[prefixLen-1] == '/')
7987 buf[prefixLen-1] = '\0';
7988
7989 if ((dir = opendir(buf)) == NULL) continue;
7990 while ((de = readdir(dir)) != NULL) {
7991 char *fileName = de->d_name;
7992 int fileNameLen = strlen(fileName);
7993
7994 if (strncmp(fileName, "jim-", 4) == 0 &&
7995 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
7996 *(fileName+4+pkgNameLen) == '-' &&
7997 fileNameLen > 4 && /* note that this is not really useful */
7998 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
7999 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
8000 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
8001 {
8002 char ver[6]; /* xx.yy<nulterm> */
8003 char *p = strrchr(fileName, '.');
8004 int verLen, fileVer;
8005
8006 verLen = p - (fileName+4+pkgNameLen+1);
8007 if (verLen < 3 || verLen > 5) continue;
8008 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8009 ver[verLen] = '\0';
8010 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8011 != JIM_OK) continue;
8012 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8013 (bestVer == -1 || bestVer < fileVer))
8014 {
8015 bestVer = fileVer;
8016 Jim_Free(bestPackage);
8017 bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8018 sprintf(bestPackage, "%s/%s", buf, fileName);
8019 }
8020 }
8021 }
8022 closedir(dir);
8023 }
8024 return bestPackage;
8025 }
8026
8027 #else /* JIM_ANSIC */
8028
8029 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8030 int prefixc, const char *pkgName, int pkgVer, int flags)
8031 {
8032 JIM_NOTUSED(interp);
8033 JIM_NOTUSED(prefixes);
8034 JIM_NOTUSED(prefixc);
8035 JIM_NOTUSED(pkgName);
8036 JIM_NOTUSED(pkgVer);
8037 JIM_NOTUSED(flags);
8038 return NULL;
8039 }
8040
8041 #endif /* JIM_ANSIC */
8042
8043 /* Search for a suitable package under every dir specified by jim_libpath
8044 * and load it if possible. If a suitable package was loaded with success
8045 * JIM_OK is returned, otherwise JIM_ERR is returned. */
8046 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8047 int flags)
8048 {
8049 Jim_Obj *libPathObjPtr;
8050 char **prefixes, *best;
8051 int prefixc, i, retCode = JIM_OK;
8052
8053 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8054 if (libPathObjPtr == NULL) {
8055 prefixc = 0;
8056 libPathObjPtr = NULL;
8057 } else {
8058 Jim_IncrRefCount(libPathObjPtr);
8059 Jim_ListLength(interp, libPathObjPtr, &prefixc);
8060 }
8061
8062 prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8063 for (i = 0; i < prefixc; i++) {
8064 Jim_Obj *prefixObjPtr;
8065 if (Jim_ListIndex(interp, libPathObjPtr, i,
8066 &prefixObjPtr, JIM_NONE) != JIM_OK)
8067 {
8068 prefixes[i] = NULL;
8069 continue;
8070 }
8071 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8072 }
8073 /* Scan every directory to find the "best" package. */
8074 best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8075 if (best != NULL) {
8076 char *p = strrchr(best, '.');
8077 /* Try to load/source it */
8078 if (p && strcmp(p, ".tcl") == 0) {
8079 retCode = Jim_EvalFile(interp, best);
8080 } else {
8081 retCode = Jim_LoadLibrary(interp, best);
8082 }
8083 } else {
8084 retCode = JIM_ERR;
8085 }
8086 Jim_Free(best);
8087 for (i = 0; i < prefixc; i++)
8088 Jim_Free(prefixes[i]);
8089 Jim_Free(prefixes);
8090 if (libPathObjPtr)
8091 Jim_DecrRefCount(interp, libPathObjPtr);
8092 return retCode;
8093 }
8094
8095 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8096 const char *ver, int flags)
8097 {
8098 Jim_HashEntry *he;
8099 int requiredVer;
8100
8101 if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8102 return NULL;
8103 he = Jim_FindHashEntry(&interp->packages, name);
8104 if (he == NULL) {
8105 /* Try to load the package. */
8106 if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8107 he = Jim_FindHashEntry(&interp->packages, name);
8108 if (he == NULL) {
8109 return "?";
8110 }
8111 return he->val;
8112 }
8113 /* No way... return an error. */
8114 if (flags & JIM_ERRMSG) {
8115 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8116 Jim_AppendStrings(interp, Jim_GetResult(interp),
8117 "Can't find package '", name, "'", NULL);
8118 }
8119 return NULL;
8120 } else {
8121 int actualVer;
8122 if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8123 != JIM_OK)
8124 {
8125 return NULL;
8126 }
8127 /* Check if version matches. */
8128 if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8129 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8130 Jim_AppendStrings(interp, Jim_GetResult(interp),
8131 "Package '", name, "' already loaded, but with version ",
8132 he->val, NULL);
8133 return NULL;
8134 }
8135 return he->val;
8136 }
8137 }
8138
8139 /* -----------------------------------------------------------------------------
8140 * Eval
8141 * ---------------------------------------------------------------------------*/
8142 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8143 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8144
8145 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8146 Jim_Obj *const *argv);
8147
8148 /* Handle calls to the [unknown] command */
8149 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8150 {
8151 Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8152 int retCode;
8153
8154 /* If the [unknown] command does not exists returns
8155 * just now */
8156 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8157 return JIM_ERR;
8158
8159 /* The object interp->unknown just contains
8160 * the "unknown" string, it is used in order to
8161 * avoid to lookup the unknown command every time
8162 * but instread to cache the result. */
8163 if (argc+1 <= JIM_EVAL_SARGV_LEN)
8164 v = sv;
8165 else
8166 v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8167 /* Make a copy of the arguments vector, but shifted on
8168 * the right of one position. The command name of the
8169 * command will be instead the first argument of the
8170 * [unknonw] call. */
8171 memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8172 v[0] = interp->unknown;
8173 /* Call it */
8174 retCode = Jim_EvalObjVector(interp, argc+1, v);
8175 /* Clean up */
8176 if (v != sv)
8177 Jim_Free(v);
8178 return retCode;
8179 }
8180
8181 /* Eval the object vector 'objv' composed of 'objc' elements.
8182 * Every element is used as single argument.
8183 * Jim_EvalObj() will call this function every time its object
8184 * argument is of "list" type, with no string representation.
8185 *
8186 * This is possible because the string representation of a
8187 * list object generated by the UpdateStringOfList is made
8188 * in a way that ensures that every list element is a different
8189 * command argument. */
8190 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8191 {
8192 int i, retcode;
8193 Jim_Cmd *cmdPtr;
8194
8195 /* Incr refcount of arguments. */
8196 for (i = 0; i < objc; i++)
8197 Jim_IncrRefCount(objv[i]);
8198 /* Command lookup */
8199 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8200 if (cmdPtr == NULL) {
8201 retcode = JimUnknown(interp, objc, objv);
8202 } else {
8203 /* Call it -- Make sure result is an empty object. */
8204 Jim_SetEmptyResult(interp);
8205 if (cmdPtr->cmdProc) {
8206 interp->cmdPrivData = cmdPtr->privData;
8207 retcode = cmdPtr->cmdProc(interp, objc, objv);
8208 } else {
8209 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8210 if (retcode == JIM_ERR) {
8211 JimAppendStackTrace(interp,
8212 Jim_GetString(objv[0], NULL), "?", 1);
8213 }
8214 }
8215 }
8216 /* Decr refcount of arguments and return the retcode */
8217 for (i = 0; i < objc; i++)
8218 Jim_DecrRefCount(interp, objv[i]);
8219 return retcode;
8220 }
8221
8222 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8223 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8224 * The returned object has refcount = 0. */
8225 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8226 int tokens, Jim_Obj **objPtrPtr)
8227 {
8228 int totlen = 0, i, retcode;
8229 Jim_Obj **intv;
8230 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8231 Jim_Obj *objPtr;
8232 char *s;
8233
8234 if (tokens <= JIM_EVAL_SINTV_LEN)
8235 intv = sintv;
8236 else
8237 intv = Jim_Alloc(sizeof(Jim_Obj*)*
8238 tokens);
8239 /* Compute every token forming the argument
8240 * in the intv objects vector. */
8241 for (i = 0; i < tokens; i++) {
8242 switch(token[i].type) {
8243 case JIM_TT_ESC:
8244 case JIM_TT_STR:
8245 intv[i] = token[i].objPtr;
8246 break;
8247 case JIM_TT_VAR:
8248 intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8249 if (!intv[i]) {
8250 retcode = JIM_ERR;
8251 goto err;
8252 }
8253 break;
8254 case JIM_TT_DICTSUGAR:
8255 intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8256 if (!intv[i]) {
8257 retcode = JIM_ERR;
8258 goto err;
8259 }
8260 break;
8261 case JIM_TT_CMD:
8262 retcode = Jim_EvalObj(interp, token[i].objPtr);
8263 if (retcode != JIM_OK)
8264 goto err;
8265 intv[i] = Jim_GetResult(interp);
8266 break;
8267 default:
8268 Jim_Panic(interp,
8269 "default token type reached "
8270 "in Jim_InterpolateTokens().");
8271 break;
8272 }
8273 Jim_IncrRefCount(intv[i]);
8274 /* Make sure there is a valid
8275 * string rep, and add the string
8276 * length to the total legnth. */
8277 Jim_GetString(intv[i], NULL);
8278 totlen += intv[i]->length;
8279 }
8280 /* Concatenate every token in an unique
8281 * object. */
8282 objPtr = Jim_NewStringObjNoAlloc(interp,
8283 NULL, 0);
8284 s = objPtr->bytes = Jim_Alloc(totlen+1);
8285 objPtr->length = totlen;
8286 for (i = 0; i < tokens; i++) {
8287 memcpy(s, intv[i]->bytes, intv[i]->length);
8288 s += intv[i]->length;
8289 Jim_DecrRefCount(interp, intv[i]);
8290 }
8291 objPtr->bytes[totlen] = '\0';
8292 /* Free the intv vector if not static. */
8293 if (tokens > JIM_EVAL_SINTV_LEN)
8294 Jim_Free(intv);
8295 *objPtrPtr = objPtr;
8296 return JIM_OK;
8297 err:
8298 i--;
8299 for (; i >= 0; i--)
8300 Jim_DecrRefCount(interp, intv[i]);
8301 if (tokens > JIM_EVAL_SINTV_LEN)
8302 Jim_Free(intv);
8303 return retcode;
8304 }
8305
8306 /* Helper of Jim_EvalObj() to perform argument expansion.
8307 * Basically this function append an argument to 'argv'
8308 * (and increments argc by reference accordingly), performing
8309 * expansion of the list object if 'expand' is non-zero, or
8310 * just adding objPtr to argv if 'expand' is zero. */
8311 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8312 int *argcPtr, int expand, Jim_Obj *objPtr)
8313 {
8314 if (!expand) {
8315 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8316 /* refcount of objPtr not incremented because
8317 * we are actually transfering a reference from
8318 * the old 'argv' to the expanded one. */
8319 (*argv)[*argcPtr] = objPtr;
8320 (*argcPtr)++;
8321 } else {
8322 int len, i;
8323
8324 Jim_ListLength(interp, objPtr, &len);
8325 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8326 for (i = 0; i < len; i++) {
8327 (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8328 Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8329 (*argcPtr)++;
8330 }
8331 /* The original object reference is no longer needed,
8332 * after the expansion it is no longer present on
8333 * the argument vector, but the single elements are
8334 * in its place. */
8335 Jim_DecrRefCount(interp, objPtr);
8336 }
8337 }
8338
8339 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8340 {
8341 int i, j = 0, len;
8342 ScriptObj *script;
8343 ScriptToken *token;
8344 int *cs; /* command structure array */
8345 int retcode = JIM_OK;
8346 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8347
8348 interp->errorFlag = 0;
8349
8350 /* If the object is of type "list" and there is no
8351 * string representation for this object, we can call
8352 * a specialized version of Jim_EvalObj() */
8353 if (scriptObjPtr->typePtr == &listObjType &&
8354 scriptObjPtr->internalRep.listValue.len &&
8355 scriptObjPtr->bytes == NULL) {
8356 Jim_IncrRefCount(scriptObjPtr);
8357 retcode = Jim_EvalObjVector(interp,
8358 scriptObjPtr->internalRep.listValue.len,
8359 scriptObjPtr->internalRep.listValue.ele);
8360 Jim_DecrRefCount(interp, scriptObjPtr);
8361 return retcode;
8362 }
8363
8364 Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8365 script = Jim_GetScript(interp, scriptObjPtr);
8366 /* Now we have to make sure the internal repr will not be
8367 * freed on shimmering.
8368 *
8369 * Think for example to this:
8370 *
8371 * set x {llength $x; ... some more code ...}; eval $x
8372 *
8373 * In order to preserve the internal rep, we increment the
8374 * inUse field of the script internal rep structure. */
8375 script->inUse++;
8376
8377 token = script->token;
8378 len = script->len;
8379 cs = script->cmdStruct;
8380 i = 0; /* 'i' is the current token index. */
8381
8382 /* Reset the interpreter result. This is useful to
8383 * return the emtpy result in the case of empty program. */
8384 Jim_SetEmptyResult(interp);
8385
8386 /* Execute every command sequentially, returns on
8387 * error (i.e. if a command does not return JIM_OK) */
8388 while (i < len) {
8389 int expand = 0;
8390 int argc = *cs++; /* Get the number of arguments */
8391 Jim_Cmd *cmd;
8392
8393 /* Set the expand flag if needed. */
8394 if (argc == -1) {
8395 expand++;
8396 argc = *cs++;
8397 }
8398 /* Allocate the arguments vector */
8399 if (argc <= JIM_EVAL_SARGV_LEN)
8400 argv = sargv;
8401 else
8402 argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8403 /* Populate the arguments objects. */
8404 for (j = 0; j < argc; j++) {
8405 int tokens = *cs++;
8406
8407 /* tokens is negative if expansion is needed.
8408 * for this argument. */
8409 if (tokens < 0) {
8410 tokens = (-tokens)-1;
8411 i++;
8412 }
8413 if (tokens == 1) {
8414 /* Fast path if the token does not
8415 * need interpolation */
8416 switch(token[i].type) {
8417 case JIM_TT_ESC:
8418 case JIM_TT_STR:
8419 argv[j] = token[i].objPtr;
8420 break;
8421 case JIM_TT_VAR:
8422 tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8423 JIM_ERRMSG);
8424 if (!tmpObjPtr) {
8425 retcode = JIM_ERR;
8426 goto err;
8427 }
8428 argv[j] = tmpObjPtr;
8429 break;
8430 case JIM_TT_DICTSUGAR:
8431 tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8432 if (!tmpObjPtr) {
8433 retcode = JIM_ERR;
8434 goto err;
8435 }
8436 argv[j] = tmpObjPtr;
8437 break;
8438 case JIM_TT_CMD:
8439 retcode = Jim_EvalObj(interp, token[i].objPtr);
8440 if (retcode != JIM_OK)
8441 goto err;
8442 argv[j] = Jim_GetResult(interp);
8443 break;
8444 default:
8445 Jim_Panic(interp,
8446 "default token type reached "
8447 "in Jim_EvalObj().");
8448 break;
8449 }
8450 Jim_IncrRefCount(argv[j]);
8451 i += 2;
8452 } else {
8453 /* For interpolation we call an helper
8454 * function doing the work for us. */
8455 if ((retcode = Jim_InterpolateTokens(interp,
8456 token+i, tokens, &tmpObjPtr)) != JIM_OK)
8457 {
8458 goto err;
8459 }
8460 argv[j] = tmpObjPtr;
8461 Jim_IncrRefCount(argv[j]);
8462 i += tokens+1;
8463 }
8464 }
8465 /* Handle {expand} expansion */
8466 if (expand) {
8467 int *ecs = cs - argc;
8468 int eargc = 0;
8469 Jim_Obj **eargv = NULL;
8470
8471 for (j = 0; j < argc; j++) {
8472 Jim_ExpandArgument( interp, &eargv, &eargc,
8473 ecs[j] < 0, argv[j]);
8474 }
8475 if (argv != sargv)
8476 Jim_Free(argv);
8477 argc = eargc;
8478 argv = eargv;
8479 j = argc;
8480 if (argc == 0) {
8481 /* Nothing to do with zero args. */
8482 Jim_Free(eargv);
8483 continue;
8484 }
8485 }
8486 /* Lookup the command to call */
8487 cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8488 if (cmd != NULL) {
8489 /* Call it -- Make sure result is an empty object. */
8490 Jim_SetEmptyResult(interp);
8491 if (cmd->cmdProc) {
8492 interp->cmdPrivData = cmd->privData;
8493 retcode = cmd->cmdProc(interp, argc, argv);
8494 } else {
8495 retcode = JimCallProcedure(interp, cmd, argc, argv);
8496 if (retcode == JIM_ERR) {
8497 JimAppendStackTrace(interp,
8498 Jim_GetString(argv[0], NULL), script->fileName,
8499 token[i-argc*2].linenr);
8500 }
8501 }
8502 } else {
8503 /* Call [unknown] */
8504 retcode = JimUnknown(interp, argc, argv);
8505 }
8506 if (retcode != JIM_OK) {
8507 i -= argc*2; /* point to the command name. */
8508 goto err;
8509 }
8510 /* Decrement the arguments count */
8511 for (j = 0; j < argc; j++) {
8512 Jim_DecrRefCount(interp, argv[j]);
8513 }
8514
8515 if (argv != sargv) {
8516 Jim_Free(argv);
8517 argv = NULL;
8518 }
8519 }
8520 /* Note that we don't have to decrement inUse, because the
8521 * following code transfers our use of the reference again to
8522 * the script object. */
8523 j = 0; /* on normal termination, the argv array is already
8524 Jim_DecrRefCount-ed. */
8525 err:
8526 /* Handle errors. */
8527 if (retcode == JIM_ERR && !interp->errorFlag) {
8528 interp->errorFlag = 1;
8529 JimSetErrorFileName(interp, script->fileName);
8530 JimSetErrorLineNumber(interp, token[i].linenr);
8531 JimResetStackTrace(interp);
8532 }
8533 Jim_FreeIntRep(interp, scriptObjPtr);
8534 scriptObjPtr->typePtr = &scriptObjType;
8535 Jim_SetIntRepPtr(scriptObjPtr, script);
8536 Jim_DecrRefCount(interp, scriptObjPtr);
8537 for (i = 0; i < j; i++) {
8538 Jim_DecrRefCount(interp, argv[i]);
8539 }
8540 if (argv != sargv)
8541 Jim_Free(argv);
8542 return retcode;
8543 }
8544
8545 /* Call a procedure implemented in Tcl.
8546 * It's possible to speed-up a lot this function, currently
8547 * the callframes are not cached, but allocated and
8548 * destroied every time. What is expecially costly is
8549 * to create/destroy the local vars hash table every time.
8550 *
8551 * This can be fixed just implementing callframes caching
8552 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8553 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8554 Jim_Obj *const *argv)
8555 {
8556 int i, retcode;
8557 Jim_CallFrame *callFramePtr;
8558
8559 /* Check arity */
8560 if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8561 argc > cmd->arityMax)) {
8562 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8563 Jim_AppendStrings(interp, objPtr,
8564 "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8565 (cmd->arityMin > 1) ? " " : "",
8566 Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8567 Jim_SetResult(interp, objPtr);
8568 return JIM_ERR;
8569 }
8570 /* Check if there are too nested calls */
8571 if (interp->numLevels == interp->maxNestingDepth) {
8572 Jim_SetResultString(interp,
8573 "Too many nested calls. Infinite recursion?", -1);
8574 return JIM_ERR;
8575 }
8576 /* Create a new callframe */
8577 callFramePtr = JimCreateCallFrame(interp);
8578 callFramePtr->parentCallFrame = interp->framePtr;
8579 callFramePtr->argv = argv;
8580 callFramePtr->argc = argc;
8581 callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8582 callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8583 callFramePtr->staticVars = cmd->staticVars;
8584 Jim_IncrRefCount(cmd->argListObjPtr);
8585 Jim_IncrRefCount(cmd->bodyObjPtr);
8586 interp->framePtr = callFramePtr;
8587 interp->numLevels ++;
8588 /* Set arguments */
8589 for (i = 0; i < cmd->arityMin-1; i++) {
8590 Jim_Obj *objPtr;
8591
8592 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8593 Jim_SetVariable(interp, objPtr, argv[i+1]);
8594 }
8595 if (cmd->arityMax == -1) {
8596 Jim_Obj *listObjPtr, *objPtr;
8597
8598 listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8599 argc-cmd->arityMin);
8600 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8601 Jim_SetVariable(interp, objPtr, listObjPtr);
8602 }
8603 /* Eval the body */
8604 retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8605
8606 /* Destroy the callframe */
8607 interp->numLevels --;
8608 interp->framePtr = interp->framePtr->parentCallFrame;
8609 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8610 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8611 } else {
8612 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8613 }
8614 /* Handle the JIM_EVAL return code */
8615 if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8616 int savedLevel = interp->evalRetcodeLevel;
8617
8618 interp->evalRetcodeLevel = interp->numLevels;
8619 while (retcode == JIM_EVAL) {
8620 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8621 Jim_IncrRefCount(resultScriptObjPtr);
8622 retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8623 Jim_DecrRefCount(interp, resultScriptObjPtr);
8624 }
8625 interp->evalRetcodeLevel = savedLevel;
8626 }
8627 /* Handle the JIM_RETURN return code */
8628 if (retcode == JIM_RETURN) {
8629 retcode = interp->returnCode;
8630 interp->returnCode = JIM_OK;
8631 }
8632 return retcode;
8633 }
8634
8635 int Jim_Eval(Jim_Interp *interp, const char *script)
8636 {
8637 Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8638 int retval;
8639
8640 Jim_IncrRefCount(scriptObjPtr);
8641 retval = Jim_EvalObj(interp, scriptObjPtr);
8642 Jim_DecrRefCount(interp, scriptObjPtr);
8643 return retval;
8644 }
8645
8646 /* Execute script in the scope of the global level */
8647 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8648 {
8649 Jim_CallFrame *savedFramePtr;
8650 int retval;
8651
8652 savedFramePtr = interp->framePtr;
8653 interp->framePtr = interp->topFramePtr;
8654 retval = Jim_Eval(interp, script);
8655 interp->framePtr = savedFramePtr;
8656 return retval;
8657 }
8658
8659 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8660 {
8661 Jim_CallFrame *savedFramePtr;
8662 int retval;
8663
8664 savedFramePtr = interp->framePtr;
8665 interp->framePtr = interp->topFramePtr;
8666 retval = Jim_EvalObj(interp, scriptObjPtr);
8667 interp->framePtr = savedFramePtr;
8668 /* Try to report the error (if any) via the bgerror proc */
8669 if (retval != JIM_OK) {
8670 Jim_Obj *objv[2];
8671
8672 objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8673 objv[1] = Jim_GetResult(interp);
8674 Jim_IncrRefCount(objv[0]);
8675 Jim_IncrRefCount(objv[1]);
8676 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8677 /* Report the error to stderr. */
8678 Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8679 Jim_PrintErrorMessage(interp);
8680 }
8681 Jim_DecrRefCount(interp, objv[0]);
8682 Jim_DecrRefCount(interp, objv[1]);
8683 }
8684 return retval;
8685 }
8686
8687 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8688 {
8689 char *prg = NULL;
8690 FILE *fp;
8691 int nread, totread, maxlen, buflen;
8692 int retval;
8693 Jim_Obj *scriptObjPtr;
8694
8695 if ((fp = fopen(filename, "r")) == NULL) {
8696 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8697 Jim_AppendStrings(interp, Jim_GetResult(interp),
8698 "Error loading script \"", filename, "\": ",
8699 strerror(errno), NULL);
8700 return JIM_ERR;
8701 }
8702 buflen = 1024;
8703 maxlen = totread = 0;
8704 while (1) {
8705 if (maxlen < totread+buflen+1) {
8706 maxlen = totread+buflen+1;
8707 prg = Jim_Realloc(prg, maxlen);
8708 }
8709 /* do not use Jim_fread() - this is really a file */
8710 if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8711 totread += nread;
8712 }
8713 prg[totread] = '\0';
8714 /* do not use Jim_fclose() - this is really a file */
8715 fclose(fp);
8716
8717 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8718 JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8719 Jim_IncrRefCount(scriptObjPtr);
8720 retval = Jim_EvalObj(interp, scriptObjPtr);
8721 Jim_DecrRefCount(interp, scriptObjPtr);
8722 return retval;
8723 }
8724
8725 /* -----------------------------------------------------------------------------
8726 * Subst
8727 * ---------------------------------------------------------------------------*/
8728 static int JimParseSubstStr(struct JimParserCtx *pc)
8729 {
8730 pc->tstart = pc->p;
8731 pc->tline = pc->linenr;
8732 while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8733 pc->p++; pc->len--;
8734 }
8735 pc->tend = pc->p-1;
8736 pc->tt = JIM_TT_ESC;
8737 return JIM_OK;
8738 }
8739
8740 static int JimParseSubst(struct JimParserCtx *pc, int flags)
8741 {
8742 int retval;
8743
8744 if (pc->len == 0) {
8745 pc->tstart = pc->tend = pc->p;
8746 pc->tline = pc->linenr;
8747 pc->tt = JIM_TT_EOL;
8748 pc->eof = 1;
8749 return JIM_OK;
8750 }
8751 switch(*pc->p) {
8752 case '[':
8753 retval = JimParseCmd(pc);
8754 if (flags & JIM_SUBST_NOCMD) {
8755 pc->tstart--;
8756 pc->tend++;
8757 pc->tt = (flags & JIM_SUBST_NOESC) ?
8758 JIM_TT_STR : JIM_TT_ESC;
8759 }
8760 return retval;
8761 break;
8762 case '$':
8763 if (JimParseVar(pc) == JIM_ERR) {
8764 pc->tstart = pc->tend = pc->p++; pc->len--;
8765 pc->tline = pc->linenr;
8766 pc->tt = JIM_TT_STR;
8767 } else {
8768 if (flags & JIM_SUBST_NOVAR) {
8769 pc->tstart--;
8770 if (flags & JIM_SUBST_NOESC)
8771 pc->tt = JIM_TT_STR;
8772 else
8773 pc->tt = JIM_TT_ESC;
8774 if (*pc->tstart == '{') {
8775 pc->tstart--;
8776 if (*(pc->tend+1))
8777 pc->tend++;
8778 }
8779 }
8780 }
8781 break;
8782 default:
8783 retval = JimParseSubstStr(pc);
8784 if (flags & JIM_SUBST_NOESC)
8785 pc->tt = JIM_TT_STR;
8786 return retval;
8787 break;
8788 }
8789 return JIM_OK;
8790 }
8791
8792 /* The subst object type reuses most of the data structures and functions
8793 * of the script object. Script's data structures are a bit more complex
8794 * for what is needed for [subst]itution tasks, but the reuse helps to
8795 * deal with a single data structure at the cost of some more memory
8796 * usage for substitutions. */
8797 static Jim_ObjType substObjType = {
8798 "subst",
8799 FreeScriptInternalRep,
8800 DupScriptInternalRep,
8801 NULL,
8802 JIM_TYPE_REFERENCES,
8803 };
8804
8805 /* This method takes the string representation of an object
8806 * as a Tcl string where to perform [subst]itution, and generates
8807 * the pre-parsed internal representation. */
8808 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8809 {
8810 int scriptTextLen;
8811 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8812 struct JimParserCtx parser;
8813 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8814
8815 script->len = 0;
8816 script->csLen = 0;
8817 script->commands = 0;
8818 script->token = NULL;
8819 script->cmdStruct = NULL;
8820 script->inUse = 1;
8821 script->substFlags = flags;
8822 script->fileName = NULL;
8823
8824 JimParserInit(&parser, scriptText, scriptTextLen, 1);
8825 while(1) {
8826 char *token;
8827 int len, type, linenr;
8828
8829 JimParseSubst(&parser, flags);
8830 if (JimParserEof(&parser)) break;
8831 token = JimParserGetToken(&parser, &len, &type, &linenr);
8832 ScriptObjAddToken(interp, script, token, len, type,
8833 NULL, linenr);
8834 }
8835 /* Free the old internal rep and set the new one. */
8836 Jim_FreeIntRep(interp, objPtr);
8837 Jim_SetIntRepPtr(objPtr, script);
8838 objPtr->typePtr = &scriptObjType;
8839 return JIM_OK;
8840 }
8841
8842 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8843 {
8844 struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8845
8846 if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8847 SetSubstFromAny(interp, objPtr, flags);
8848 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8849 }
8850
8851 /* Performs commands,variables,blackslashes substitution,
8852 * storing the result object (with refcount 0) into
8853 * resObjPtrPtr. */
8854 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8855 Jim_Obj **resObjPtrPtr, int flags)
8856 {
8857 ScriptObj *script;
8858 ScriptToken *token;
8859 int i, len, retcode = JIM_OK;
8860 Jim_Obj *resObjPtr, *savedResultObjPtr;
8861
8862 script = Jim_GetSubst(interp, substObjPtr, flags);
8863 #ifdef JIM_OPTIMIZATION
8864 /* Fast path for a very common case with array-alike syntax,
8865 * that's: $foo($bar) */
8866 if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8867 Jim_Obj *varObjPtr = script->token[0].objPtr;
8868
8869 Jim_IncrRefCount(varObjPtr);
8870 resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8871 if (resObjPtr == NULL) {
8872 Jim_DecrRefCount(interp, varObjPtr);
8873 return JIM_ERR;
8874 }
8875 Jim_DecrRefCount(interp, varObjPtr);
8876 *resObjPtrPtr = resObjPtr;
8877 return JIM_OK;
8878 }
8879 #endif
8880
8881 Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8882 /* In order to preserve the internal rep, we increment the
8883 * inUse field of the script internal rep structure. */
8884 script->inUse++;
8885
8886 token = script->token;
8887 len = script->len;
8888
8889 /* Save the interp old result, to set it again before
8890 * to return. */
8891 savedResultObjPtr = interp->result;
8892 Jim_IncrRefCount(savedResultObjPtr);
8893
8894 /* Perform the substitution. Starts with an empty object
8895 * and adds every token (performing the appropriate
8896 * var/command/escape substitution). */
8897 resObjPtr = Jim_NewStringObj(interp, "", 0);
8898 for (i = 0; i < len; i++) {
8899 Jim_Obj *objPtr;
8900
8901 switch(token[i].type) {
8902 case JIM_TT_STR:
8903 case JIM_TT_ESC:
8904 Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8905 break;
8906 case JIM_TT_VAR:
8907 objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8908 if (objPtr == NULL) goto err;
8909 Jim_IncrRefCount(objPtr);
8910 Jim_AppendObj(interp, resObjPtr, objPtr);
8911 Jim_DecrRefCount(interp, objPtr);
8912 break;
8913 case JIM_TT_CMD:
8914 if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8915 goto err;
8916 Jim_AppendObj(interp, resObjPtr, interp->result);
8917 break;
8918 default:
8919 Jim_Panic(interp,
8920 "default token type (%d) reached "
8921 "in Jim_SubstObj().", token[i].type);
8922 break;
8923 }
8924 }
8925 ok:
8926 if (retcode == JIM_OK)
8927 Jim_SetResult(interp, savedResultObjPtr);
8928 Jim_DecrRefCount(interp, savedResultObjPtr);
8929 /* Note that we don't have to decrement inUse, because the
8930 * following code transfers our use of the reference again to
8931 * the script object. */
8932 Jim_FreeIntRep(interp, substObjPtr);
8933 substObjPtr->typePtr = &scriptObjType;
8934 Jim_SetIntRepPtr(substObjPtr, script);
8935 Jim_DecrRefCount(interp, substObjPtr);
8936 *resObjPtrPtr = resObjPtr;
8937 return retcode;
8938 err:
8939 Jim_FreeNewObj(interp, resObjPtr);
8940 retcode = JIM_ERR;
8941 goto ok;
8942 }
8943
8944 /* -----------------------------------------------------------------------------
8945 * API Input/Export functions
8946 * ---------------------------------------------------------------------------*/
8947
8948 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8949 {
8950 Jim_HashEntry *he;
8951
8952 he = Jim_FindHashEntry(&interp->stub, funcname);
8953 if (!he)
8954 return JIM_ERR;
8955 memcpy(targetPtrPtr, &he->val, sizeof(void*));
8956 return JIM_OK;
8957 }
8958
8959 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
8960 {
8961 return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
8962 }
8963
8964 #define JIM_REGISTER_API(name) \
8965 Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
8966
8967 void JimRegisterCoreApi(Jim_Interp *interp)
8968 {
8969 interp->getApiFuncPtr = Jim_GetApi;
8970 JIM_REGISTER_API(Alloc);
8971 JIM_REGISTER_API(Free);
8972 JIM_REGISTER_API(Eval);
8973 JIM_REGISTER_API(EvalGlobal);
8974 JIM_REGISTER_API(EvalFile);
8975 JIM_REGISTER_API(EvalObj);
8976 JIM_REGISTER_API(EvalObjBackground);
8977 JIM_REGISTER_API(EvalObjVector);
8978 JIM_REGISTER_API(InitHashTable);
8979 JIM_REGISTER_API(ExpandHashTable);
8980 JIM_REGISTER_API(AddHashEntry);
8981 JIM_REGISTER_API(ReplaceHashEntry);
8982 JIM_REGISTER_API(DeleteHashEntry);
8983 JIM_REGISTER_API(FreeHashTable);
8984 JIM_REGISTER_API(FindHashEntry);
8985 JIM_REGISTER_API(ResizeHashTable);
8986 JIM_REGISTER_API(GetHashTableIterator);
8987 JIM_REGISTER_API(NextHashEntry);
8988 JIM_REGISTER_API(NewObj);
8989 JIM_REGISTER_API(FreeObj);
8990 JIM_REGISTER_API(InvalidateStringRep);
8991 JIM_REGISTER_API(InitStringRep);
8992 JIM_REGISTER_API(DuplicateObj);
8993 JIM_REGISTER_API(GetString);
8994 JIM_REGISTER_API(Length);
8995 JIM_REGISTER_API(InvalidateStringRep);
8996 JIM_REGISTER_API(NewStringObj);
8997 JIM_REGISTER_API(NewStringObjNoAlloc);
8998 JIM_REGISTER_API(AppendString);
8999 JIM_REGISTER_API(AppendObj);
9000 JIM_REGISTER_API(AppendStrings);
9001 JIM_REGISTER_API(StringEqObj);
9002 JIM_REGISTER_API(StringMatchObj);
9003 JIM_REGISTER_API(StringRangeObj);
9004 JIM_REGISTER_API(FormatString);
9005 JIM_REGISTER_API(CompareStringImmediate);
9006 JIM_REGISTER_API(NewReference);
9007 JIM_REGISTER_API(GetReference);
9008 JIM_REGISTER_API(SetFinalizer);
9009 JIM_REGISTER_API(GetFinalizer);
9010 JIM_REGISTER_API(CreateInterp);
9011 JIM_REGISTER_API(FreeInterp);
9012 JIM_REGISTER_API(GetExitCode);
9013 JIM_REGISTER_API(SetStdin);
9014 JIM_REGISTER_API(SetStdout);
9015 JIM_REGISTER_API(SetStderr);
9016 JIM_REGISTER_API(CreateCommand);
9017 JIM_REGISTER_API(CreateProcedure);
9018 JIM_REGISTER_API(DeleteCommand);
9019 JIM_REGISTER_API(RenameCommand);
9020 JIM_REGISTER_API(GetCommand);
9021 JIM_REGISTER_API(SetVariable);
9022 JIM_REGISTER_API(SetVariableStr);
9023 JIM_REGISTER_API(SetGlobalVariableStr);
9024 JIM_REGISTER_API(SetVariableStrWithStr);
9025 JIM_REGISTER_API(SetVariableLink);
9026 JIM_REGISTER_API(GetVariable);
9027 JIM_REGISTER_API(GetCallFrameByLevel);
9028 JIM_REGISTER_API(Collect);
9029 JIM_REGISTER_API(CollectIfNeeded);
9030 JIM_REGISTER_API(GetIndex);
9031 JIM_REGISTER_API(NewListObj);
9032 JIM_REGISTER_API(ListAppendElement);
9033 JIM_REGISTER_API(ListAppendList);
9034 JIM_REGISTER_API(ListLength);
9035 JIM_REGISTER_API(ListIndex);
9036 JIM_REGISTER_API(SetListIndex);
9037 JIM_REGISTER_API(ConcatObj);
9038 JIM_REGISTER_API(NewDictObj);
9039 JIM_REGISTER_API(DictKey);
9040 JIM_REGISTER_API(DictKeysVector);
9041 JIM_REGISTER_API(GetIndex);
9042 JIM_REGISTER_API(GetReturnCode);
9043 JIM_REGISTER_API(EvalExpression);
9044 JIM_REGISTER_API(GetBoolFromExpr);
9045 JIM_REGISTER_API(GetWide);
9046 JIM_REGISTER_API(GetLong);
9047 JIM_REGISTER_API(SetWide);
9048 JIM_REGISTER_API(NewIntObj);
9049 JIM_REGISTER_API(GetDouble);
9050 JIM_REGISTER_API(SetDouble);
9051 JIM_REGISTER_API(NewDoubleObj);
9052 JIM_REGISTER_API(WrongNumArgs);
9053 JIM_REGISTER_API(SetDictKeysVector);
9054 JIM_REGISTER_API(SubstObj);
9055 JIM_REGISTER_API(RegisterApi);
9056 JIM_REGISTER_API(PrintErrorMessage);
9057 JIM_REGISTER_API(InteractivePrompt);
9058 JIM_REGISTER_API(RegisterCoreCommands);
9059 JIM_REGISTER_API(GetSharedString);
9060 JIM_REGISTER_API(ReleaseSharedString);
9061 JIM_REGISTER_API(Panic);
9062 JIM_REGISTER_API(StrDup);
9063 JIM_REGISTER_API(UnsetVariable);
9064 JIM_REGISTER_API(GetVariableStr);
9065 JIM_REGISTER_API(GetGlobalVariable);
9066 JIM_REGISTER_API(GetGlobalVariableStr);
9067 JIM_REGISTER_API(GetAssocData);
9068 JIM_REGISTER_API(SetAssocData);
9069 JIM_REGISTER_API(DeleteAssocData);
9070 JIM_REGISTER_API(GetEnum);
9071 JIM_REGISTER_API(ScriptIsComplete);
9072 JIM_REGISTER_API(PackageRequire);
9073 JIM_REGISTER_API(PackageProvide);
9074 JIM_REGISTER_API(InitStack);
9075 JIM_REGISTER_API(FreeStack);
9076 JIM_REGISTER_API(StackLen);
9077 JIM_REGISTER_API(StackPush);
9078 JIM_REGISTER_API(StackPop);
9079 JIM_REGISTER_API(StackPeek);
9080 JIM_REGISTER_API(FreeStackElements);
9081 }
9082
9083 /* -----------------------------------------------------------------------------
9084 * Core commands utility functions
9085 * ---------------------------------------------------------------------------*/
9086 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
9087 const char *msg)
9088 {
9089 int i;
9090 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9091
9092 Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9093 for (i = 0; i < argc; i++) {
9094 Jim_AppendObj(interp, objPtr, argv[i]);
9095 if (!(i+1 == argc && msg[0] == '\0'))
9096 Jim_AppendString(interp, objPtr, " ", 1);
9097 }
9098 Jim_AppendString(interp, objPtr, msg, -1);
9099 Jim_AppendString(interp, objPtr, "\"", 1);
9100 Jim_SetResult(interp, objPtr);
9101 }
9102
9103 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9104 {
9105 Jim_HashTableIterator *htiter;
9106 Jim_HashEntry *he;
9107 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9108 const char *pattern;
9109 int patternLen;
9110
9111 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9112 htiter = Jim_GetHashTableIterator(&interp->commands);
9113 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9114 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9115 strlen((const char*)he->key), 0))
9116 continue;
9117 Jim_ListAppendElement(interp, listObjPtr,
9118 Jim_NewStringObj(interp, he->key, -1));
9119 }
9120 Jim_FreeHashTableIterator(htiter);
9121 return listObjPtr;
9122 }
9123
9124 #define JIM_VARLIST_GLOBALS 0
9125 #define JIM_VARLIST_LOCALS 1
9126 #define JIM_VARLIST_VARS 2
9127
9128 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9129 int mode)
9130 {
9131 Jim_HashTableIterator *htiter;
9132 Jim_HashEntry *he;
9133 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9134 const char *pattern;
9135 int patternLen;
9136
9137 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9138 if (mode == JIM_VARLIST_GLOBALS) {
9139 htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9140 } else {
9141 /* For [info locals], if we are at top level an emtpy list
9142 * is returned. I don't agree, but we aim at compatibility (SS) */
9143 if (mode == JIM_VARLIST_LOCALS &&
9144 interp->framePtr == interp->topFramePtr)
9145 return listObjPtr;
9146 htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9147 }
9148 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9149 Jim_Var *varPtr = (Jim_Var*) he->val;
9150 if (mode == JIM_VARLIST_LOCALS) {
9151 if (varPtr->linkFramePtr != NULL)
9152 continue;
9153 }
9154 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9155 strlen((const char*)he->key), 0))
9156 continue;
9157 Jim_ListAppendElement(interp, listObjPtr,
9158 Jim_NewStringObj(interp, he->key, -1));
9159 }
9160 Jim_FreeHashTableIterator(htiter);
9161 return listObjPtr;
9162 }
9163
9164 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9165 Jim_Obj **objPtrPtr)
9166 {
9167 Jim_CallFrame *targetCallFrame;
9168
9169 if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9170 != JIM_OK)
9171 return JIM_ERR;
9172 /* No proc call at toplevel callframe */
9173 if (targetCallFrame == interp->topFramePtr) {
9174 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9175 Jim_AppendStrings(interp, Jim_GetResult(interp),
9176 "bad level \"",
9177 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9178 return JIM_ERR;
9179 }
9180 *objPtrPtr = Jim_NewListObj(interp,
9181 targetCallFrame->argv,
9182 targetCallFrame->argc);
9183 return JIM_OK;
9184 }
9185
9186 /* -----------------------------------------------------------------------------
9187 * Core commands
9188 * ---------------------------------------------------------------------------*/
9189
9190 /* fake [puts] -- not the real puts, just for debugging. */
9191 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9192 Jim_Obj *const *argv)
9193 {
9194 const char *str;
9195 int len, nonewline = 0;
9196
9197 if (argc != 2 && argc != 3) {
9198 Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9199 return JIM_ERR;
9200 }
9201 if (argc == 3) {
9202 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9203 {
9204 Jim_SetResultString(interp, "The second argument must "
9205 "be -nonewline", -1);
9206 return JIM_OK;
9207 } else {
9208 nonewline = 1;
9209 argv++;
9210 }
9211 }
9212 str = Jim_GetString(argv[1], &len);
9213 Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9214 if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9215 return JIM_OK;
9216 }
9217
9218 /* Helper for [+] and [*] */
9219 static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
9220 Jim_Obj *const *argv, int op)
9221 {
9222 jim_wide wideValue, res;
9223 double doubleValue, doubleRes;
9224 int i;
9225
9226 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9227
9228 for (i = 1; i < argc; i++) {
9229 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9230 goto trydouble;
9231 if (op == JIM_EXPROP_ADD)
9232 res += wideValue;
9233 else
9234 res *= wideValue;
9235 }
9236 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9237 return JIM_OK;
9238 trydouble:
9239 doubleRes = (double) res;
9240 for (;i < argc; i++) {
9241 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9242 return JIM_ERR;
9243 if (op == JIM_EXPROP_ADD)
9244 doubleRes += doubleValue;
9245 else
9246 doubleRes *= doubleValue;
9247 }
9248 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9249 return JIM_OK;
9250 }
9251
9252 /* Helper for [-] and [/] */
9253 static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9254 Jim_Obj *const *argv, int op)
9255 {
9256 jim_wide wideValue, res = 0;
9257 double doubleValue, doubleRes = 0;
9258 int i = 2;
9259
9260 if (argc < 2) {
9261 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9262 return JIM_ERR;
9263 } else if (argc == 2) {
9264 /* The arity = 2 case is different. For [- x] returns -x,
9265 * while [/ x] returns 1/x. */
9266 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9267 if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9268 JIM_OK)
9269 {
9270 return JIM_ERR;
9271 } else {
9272 if (op == JIM_EXPROP_SUB)
9273 doubleRes = -doubleValue;
9274 else
9275 doubleRes = 1.0/doubleValue;
9276 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9277 doubleRes));
9278 return JIM_OK;
9279 }
9280 }
9281 if (op == JIM_EXPROP_SUB) {
9282 res = -wideValue;
9283 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9284 } else {
9285 doubleRes = 1.0/wideValue;
9286 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9287 doubleRes));
9288 }
9289 return JIM_OK;
9290 } else {
9291 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9292 if (Jim_GetDouble(interp, argv[1], &doubleRes)
9293 != JIM_OK) {
9294 return JIM_ERR;
9295 } else {
9296 goto trydouble;
9297 }
9298 }
9299 }
9300 for (i = 2; i < argc; i++) {
9301 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9302 doubleRes = (double) res;
9303 goto trydouble;
9304 }
9305 if (op == JIM_EXPROP_SUB)
9306 res -= wideValue;
9307 else
9308 res /= wideValue;
9309 }
9310 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9311 return JIM_OK;
9312 trydouble:
9313 for (;i < argc; i++) {
9314 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9315 return JIM_ERR;
9316 if (op == JIM_EXPROP_SUB)
9317 doubleRes -= doubleValue;
9318 else
9319 doubleRes /= doubleValue;
9320 }
9321 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9322 return JIM_OK;
9323 }
9324
9325
9326 /* [+] */
9327 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9328 Jim_Obj *const *argv)
9329 {
9330 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9331 }
9332
9333 /* [*] */
9334 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9335 Jim_Obj *const *argv)
9336 {
9337 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9338 }
9339
9340 /* [-] */
9341 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9342 Jim_Obj *const *argv)
9343 {
9344 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9345 }
9346
9347 /* [/] */
9348 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9349 Jim_Obj *const *argv)
9350 {
9351 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9352 }
9353
9354 /* [set] */
9355 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9356 Jim_Obj *const *argv)
9357 {
9358 if (argc != 2 && argc != 3) {
9359 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9360 return JIM_ERR;
9361 }
9362 if (argc == 2) {
9363 Jim_Obj *objPtr;
9364 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9365 if (!objPtr)
9366 return JIM_ERR;
9367 Jim_SetResult(interp, objPtr);
9368 return JIM_OK;
9369 }
9370 /* argc == 3 case. */
9371 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9372 return JIM_ERR;
9373 Jim_SetResult(interp, argv[2]);
9374 return JIM_OK;
9375 }
9376
9377 /* [unset] */
9378 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9379 Jim_Obj *const *argv)
9380 {
9381 int i;
9382
9383 if (argc < 2) {
9384 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9385 return JIM_ERR;
9386 }
9387 for (i = 1; i < argc; i++) {
9388 if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9389 return JIM_ERR;
9390 }
9391 return JIM_OK;
9392 }
9393
9394 /* [incr] */
9395 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9396 Jim_Obj *const *argv)
9397 {
9398 jim_wide wideValue, increment = 1;
9399 Jim_Obj *intObjPtr;
9400
9401 if (argc != 2 && argc != 3) {
9402 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9403 return JIM_ERR;
9404 }
9405 if (argc == 3) {
9406 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9407 return JIM_ERR;
9408 }
9409 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9410 if (!intObjPtr) return JIM_ERR;
9411 if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9412 return JIM_ERR;
9413 if (Jim_IsShared(intObjPtr)) {
9414 intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9415 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9416 Jim_FreeNewObj(interp, intObjPtr);
9417 return JIM_ERR;
9418 }
9419 } else {
9420 Jim_SetWide(interp, intObjPtr, wideValue+increment);
9421 /* The following step is required in order to invalidate the
9422 * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9423 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9424 return JIM_ERR;
9425 }
9426 }
9427 Jim_SetResult(interp, intObjPtr);
9428 return JIM_OK;
9429 }
9430
9431 /* [while] */
9432 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9433 Jim_Obj *const *argv)
9434 {
9435 if (argc != 3) {
9436 Jim_WrongNumArgs(interp, 1, argv, "condition body");
9437 return JIM_ERR;
9438 }
9439 /* Try to run a specialized version of while if the expression
9440 * is in one of the following forms:
9441 *
9442 * $a < CONST, $a < $b
9443 * $a <= CONST, $a <= $b
9444 * $a > CONST, $a > $b
9445 * $a >= CONST, $a >= $b
9446 * $a != CONST, $a != $b
9447 * $a == CONST, $a == $b
9448 * $a
9449 * !$a
9450 * CONST
9451 */
9452
9453 #ifdef JIM_OPTIMIZATION
9454 {
9455 ExprByteCode *expr;
9456 Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9457 int exprLen, retval;
9458
9459 /* STEP 1 -- Check if there are the conditions to run the specialized
9460 * version of while */
9461
9462 if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9463 if (expr->len <= 0 || expr->len > 3) goto noopt;
9464 switch(expr->len) {
9465 case 1:
9466 if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9467 expr->opcode[0] != JIM_EXPROP_NUMBER)
9468 goto noopt;
9469 break;
9470 case 2:
9471 if (expr->opcode[1] != JIM_EXPROP_NOT ||
9472 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9473 goto noopt;
9474 break;
9475 case 3:
9476 if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9477 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9478 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9479 goto noopt;
9480 switch(expr->opcode[2]) {
9481 case JIM_EXPROP_LT:
9482 case JIM_EXPROP_LTE:
9483 case JIM_EXPROP_GT:
9484 case JIM_EXPROP_GTE:
9485 case JIM_EXPROP_NUMEQ:
9486 case JIM_EXPROP_NUMNE:
9487 /* nothing to do */
9488 break;
9489 default:
9490 goto noopt;
9491 }
9492 break;
9493 default:
9494 Jim_Panic(interp,
9495 "Unexpected default reached in Jim_WhileCoreCommand()");
9496 break;
9497 }
9498
9499 /* STEP 2 -- conditions meet. Initialization. Take different
9500 * branches for different expression lengths. */
9501 exprLen = expr->len;
9502
9503 if (exprLen == 1) {
9504 jim_wide wideValue;
9505
9506 if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9507 varAObjPtr = expr->obj[0];
9508 Jim_IncrRefCount(varAObjPtr);
9509 } else {
9510 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9511 goto noopt;
9512 }
9513 while (1) {
9514 if (varAObjPtr) {
9515 if (!(objPtr =
9516 Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9517 Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9518 {
9519 Jim_DecrRefCount(interp, varAObjPtr);
9520 goto noopt;
9521 }
9522 }
9523 if (!wideValue) break;
9524 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9525 switch(retval) {
9526 case JIM_BREAK:
9527 if (varAObjPtr)
9528 Jim_DecrRefCount(interp, varAObjPtr);
9529 goto out;
9530 break;
9531 case JIM_CONTINUE:
9532 continue;
9533 break;
9534 default:
9535 if (varAObjPtr)
9536 Jim_DecrRefCount(interp, varAObjPtr);
9537 return retval;
9538 }
9539 }
9540 }
9541 if (varAObjPtr)
9542 Jim_DecrRefCount(interp, varAObjPtr);
9543 } else if (exprLen == 3) {
9544 jim_wide wideValueA, wideValueB, cmpRes = 0;
9545 int cmpType = expr->opcode[2];
9546
9547 varAObjPtr = expr->obj[0];
9548 Jim_IncrRefCount(varAObjPtr);
9549 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9550 varBObjPtr = expr->obj[1];
9551 Jim_IncrRefCount(varBObjPtr);
9552 } else {
9553 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9554 goto noopt;
9555 }
9556 while (1) {
9557 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9558 Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9559 {
9560 Jim_DecrRefCount(interp, varAObjPtr);
9561 if (varBObjPtr)
9562 Jim_DecrRefCount(interp, varBObjPtr);
9563 goto noopt;
9564 }
9565 if (varBObjPtr) {
9566 if (!(objPtr =
9567 Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9568 Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9569 {
9570 Jim_DecrRefCount(interp, varAObjPtr);
9571 if (varBObjPtr)
9572 Jim_DecrRefCount(interp, varBObjPtr);
9573 goto noopt;
9574 }
9575 }
9576 switch(cmpType) {
9577 case JIM_EXPROP_LT:
9578 cmpRes = wideValueA < wideValueB; break;
9579 case JIM_EXPROP_LTE:
9580 cmpRes = wideValueA <= wideValueB; break;
9581 case JIM_EXPROP_GT:
9582 cmpRes = wideValueA > wideValueB; break;
9583 case JIM_EXPROP_GTE:
9584 cmpRes = wideValueA >= wideValueB; break;
9585 case JIM_EXPROP_NUMEQ:
9586 cmpRes = wideValueA == wideValueB; break;
9587 case JIM_EXPROP_NUMNE:
9588 cmpRes = wideValueA != wideValueB; break;
9589 }
9590 if (!cmpRes) break;
9591 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9592 switch(retval) {
9593 case JIM_BREAK:
9594 Jim_DecrRefCount(interp, varAObjPtr);
9595 if (varBObjPtr)
9596 Jim_DecrRefCount(interp, varBObjPtr);
9597 goto out;
9598 break;
9599 case JIM_CONTINUE:
9600 continue;
9601 break;
9602 default:
9603 Jim_DecrRefCount(interp, varAObjPtr);
9604 if (varBObjPtr)
9605 Jim_DecrRefCount(interp, varBObjPtr);
9606 return retval;
9607 }
9608 }
9609 }
9610 Jim_DecrRefCount(interp, varAObjPtr);
9611 if (varBObjPtr)
9612 Jim_DecrRefCount(interp, varBObjPtr);
9613 } else {
9614 /* TODO: case for len == 2 */
9615 goto noopt;
9616 }
9617 Jim_SetEmptyResult(interp);
9618 return JIM_OK;
9619 }
9620 noopt:
9621 #endif
9622
9623 /* The general purpose implementation of while starts here */
9624 while (1) {
9625 int boolean, retval;
9626
9627 if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9628 &boolean)) != JIM_OK)
9629 return retval;
9630 if (!boolean) break;
9631 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9632 switch(retval) {
9633 case JIM_BREAK:
9634 goto out;
9635 break;
9636 case JIM_CONTINUE:
9637 continue;
9638 break;
9639 default:
9640 return retval;
9641 }
9642 }
9643 }
9644 out:
9645 Jim_SetEmptyResult(interp);
9646 return JIM_OK;
9647 }
9648
9649 /* [for] */
9650 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9651 Jim_Obj *const *argv)
9652 {
9653 int retval;
9654
9655 if (argc != 5) {
9656 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9657 return JIM_ERR;
9658 }
9659 /* Check if the for is on the form:
9660 * for {set i CONST} {$i < CONST} {incr i}
9661 * for {set i CONST} {$i < $j} {incr i}
9662 * for {set i CONST} {$i <= CONST} {incr i}
9663 * for {set i CONST} {$i <= $j} {incr i}
9664 * XXX: NOTE: if variable traces are implemented, this optimization
9665 * need to be modified to check for the proc epoch at every variable
9666 * update. */
9667 #ifdef JIM_OPTIMIZATION
9668 {
9669 ScriptObj *initScript, *incrScript;
9670 ExprByteCode *expr;
9671 jim_wide start, stop, currentVal;
9672 unsigned jim_wide procEpoch = interp->procEpoch;
9673 Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9674 int cmpType;
9675 struct Jim_Cmd *cmdPtr;
9676
9677 /* Do it only if there aren't shared arguments */
9678 if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9679 goto evalstart;
9680 initScript = Jim_GetScript(interp, argv[1]);
9681 expr = Jim_GetExpression(interp, argv[2]);
9682 incrScript = Jim_GetScript(interp, argv[3]);
9683
9684 /* Ensure proper lengths to start */
9685 if (initScript->len != 6) goto evalstart;
9686 if (incrScript->len != 4) goto evalstart;
9687 if (expr->len != 3) goto evalstart;
9688 /* Ensure proper token types. */
9689 if (initScript->token[2].type != JIM_TT_ESC ||
9690 initScript->token[4].type != JIM_TT_ESC ||
9691 incrScript->token[2].type != JIM_TT_ESC ||
9692 expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9693 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9694 expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9695 (expr->opcode[2] != JIM_EXPROP_LT &&
9696 expr->opcode[2] != JIM_EXPROP_LTE))
9697 goto evalstart;
9698 cmpType = expr->opcode[2];
9699 /* Initialization command must be [set] */
9700 cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9701 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9702 goto evalstart;
9703 /* Update command must be incr */
9704 cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9705 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9706 goto evalstart;
9707 /* set, incr, expression must be about the same variable */
9708 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9709 incrScript->token[2].objPtr, 0))
9710 goto evalstart;
9711 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9712 expr->obj[0], 0))
9713 goto evalstart;
9714 /* Check that the initialization and comparison are valid integers */
9715 if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9716 goto evalstart;
9717 if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9718 Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9719 {
9720 goto evalstart;
9721 }
9722
9723 /* Initialization */
9724 varNamePtr = expr->obj[0];
9725 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9726 stopVarNamePtr = expr->obj[1];
9727 Jim_IncrRefCount(stopVarNamePtr);
9728 }
9729 Jim_IncrRefCount(varNamePtr);
9730
9731 /* --- OPTIMIZED FOR --- */
9732 /* Start to loop */
9733 objPtr = Jim_NewIntObj(interp, start);
9734 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9735 Jim_DecrRefCount(interp, varNamePtr);
9736 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9737 Jim_FreeNewObj(interp, objPtr);
9738 goto evalstart;
9739 }
9740 while (1) {
9741 /* === Check condition === */
9742 /* Common code: */
9743 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9744 if (objPtr == NULL ||
9745 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9746 {
9747 Jim_DecrRefCount(interp, varNamePtr);
9748 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9749 goto testcond;
9750 }
9751 /* Immediate or Variable? get the 'stop' value if the latter. */
9752 if (stopVarNamePtr) {
9753 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9754 if (objPtr == NULL ||
9755 Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9756 {
9757 Jim_DecrRefCount(interp, varNamePtr);
9758 Jim_DecrRefCount(interp, stopVarNamePtr);
9759 goto testcond;
9760 }
9761 }
9762 if (cmpType == JIM_EXPROP_LT) {
9763 if (currentVal >= stop) break;
9764 } else {
9765 if (currentVal > stop) break;
9766 }
9767 /* Eval body */
9768 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9769 switch(retval) {
9770 case JIM_BREAK:
9771 if (stopVarNamePtr)
9772 Jim_DecrRefCount(interp, stopVarNamePtr);
9773 Jim_DecrRefCount(interp, varNamePtr);
9774 goto out;
9775 case JIM_CONTINUE:
9776 /* nothing to do */
9777 break;
9778 default:
9779 if (stopVarNamePtr)
9780 Jim_DecrRefCount(interp, stopVarNamePtr);
9781 Jim_DecrRefCount(interp, varNamePtr);
9782 return retval;
9783 }
9784 }
9785 /* If there was a change in procedures/command continue
9786 * with the usual [for] command implementation */
9787 if (procEpoch != interp->procEpoch) {
9788 if (stopVarNamePtr)
9789 Jim_DecrRefCount(interp, stopVarNamePtr);
9790 Jim_DecrRefCount(interp, varNamePtr);
9791 goto evalnext;
9792 }
9793 /* Increment */
9794 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9795 if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9796 objPtr->internalRep.wideValue ++;
9797 Jim_InvalidateStringRep(objPtr);
9798 } else {
9799 Jim_Obj *auxObjPtr;
9800
9801 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9802 if (stopVarNamePtr)
9803 Jim_DecrRefCount(interp, stopVarNamePtr);
9804 Jim_DecrRefCount(interp, varNamePtr);
9805 goto evalnext;
9806 }
9807 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9808 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9809 if (stopVarNamePtr)
9810 Jim_DecrRefCount(interp, stopVarNamePtr);
9811 Jim_DecrRefCount(interp, varNamePtr);
9812 Jim_FreeNewObj(interp, auxObjPtr);
9813 goto evalnext;
9814 }
9815 }
9816 }
9817 if (stopVarNamePtr)
9818 Jim_DecrRefCount(interp, stopVarNamePtr);
9819 Jim_DecrRefCount(interp, varNamePtr);
9820 Jim_SetEmptyResult(interp);
9821 return JIM_OK;
9822 }
9823 #endif
9824 evalstart:
9825 /* Eval start */
9826 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9827 return retval;
9828 while (1) {
9829 int boolean;
9830 testcond:
9831 /* Test the condition */
9832 if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9833 != JIM_OK)
9834 return retval;
9835 if (!boolean) break;
9836 /* Eval body */
9837 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9838 switch(retval) {
9839 case JIM_BREAK:
9840 goto out;
9841 break;
9842 case JIM_CONTINUE:
9843 /* Nothing to do */
9844 break;
9845 default:
9846 return retval;
9847 }
9848 }
9849 evalnext:
9850 /* Eval next */
9851 if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9852 switch(retval) {
9853 case JIM_BREAK:
9854 goto out;
9855 break;
9856 case JIM_CONTINUE:
9857 continue;
9858 break;
9859 default:
9860 return retval;
9861 }
9862 }
9863 }
9864 out:
9865 Jim_SetEmptyResult(interp);
9866 return JIM_OK;
9867 }
9868
9869 /* foreach + lmap implementation. */
9870 static int JimForeachMapHelper(Jim_Interp *interp, int argc,
9871 Jim_Obj *const *argv, int doMap)
9872 {
9873 int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9874 int nbrOfLoops = 0;
9875 Jim_Obj *emptyStr, *script, *mapRes = NULL;
9876
9877 if (argc < 4 || argc % 2 != 0) {
9878 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9879 return JIM_ERR;
9880 }
9881 if (doMap) {
9882 mapRes = Jim_NewListObj(interp, NULL, 0);
9883 Jim_IncrRefCount(mapRes);
9884 }
9885 emptyStr = Jim_NewEmptyStringObj(interp);
9886 Jim_IncrRefCount(emptyStr);
9887 script = argv[argc-1]; /* Last argument is a script */
9888 nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
9889 listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9890 listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9891 /* Initialize iterators and remember max nbr elements each list */
9892 memset(listsIdx, 0, nbrOfLists * sizeof(int));
9893 /* Remember lengths of all lists and calculate how much rounds to loop */
9894 for (i=0; i < nbrOfLists*2; i += 2) {
9895 div_t cnt;
9896 int count;
9897 Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9898 Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9899 if (listsEnd[i] == 0) {
9900 Jim_SetResultString(interp, "foreach varlist is empty", -1);
9901 goto err;
9902 }
9903 cnt = div(listsEnd[i+1], listsEnd[i]);
9904 count = cnt.quot + (cnt.rem ? 1 : 0);
9905 if (count > nbrOfLoops)
9906 nbrOfLoops = count;
9907 }
9908 for (; nbrOfLoops-- > 0; ) {
9909 for (i=0; i < nbrOfLists; ++i) {
9910 int varIdx = 0, var = i * 2;
9911 while (varIdx < listsEnd[var]) {
9912 Jim_Obj *varName, *ele;
9913 int lst = i * 2 + 1;
9914 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9915 != JIM_OK)
9916 goto err;
9917 if (listsIdx[i] < listsEnd[lst]) {
9918 if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9919 != JIM_OK)
9920 goto err;
9921 if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9922 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9923 goto err;
9924 }
9925 ++listsIdx[i]; /* Remember next iterator of current list */
9926 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9927 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9928 goto err;
9929 }
9930 ++varIdx; /* Next variable */
9931 }
9932 }
9933 switch (result = Jim_EvalObj(interp, script)) {
9934 case JIM_OK:
9935 if (doMap)
9936 Jim_ListAppendElement(interp, mapRes, interp->result);
9937 break;
9938 case JIM_CONTINUE:
9939 break;
9940 case JIM_BREAK:
9941 goto out;
9942 break;
9943 default:
9944 goto err;
9945 }
9946 }
9947 out:
9948 result = JIM_OK;
9949 if (doMap)
9950 Jim_SetResult(interp, mapRes);
9951 else
9952 Jim_SetEmptyResult(interp);
9953 err:
9954 if (doMap)
9955 Jim_DecrRefCount(interp, mapRes);
9956 Jim_DecrRefCount(interp, emptyStr);
9957 Jim_Free(listsIdx);
9958 Jim_Free(listsEnd);
9959 return result;
9960 }
9961
9962 /* [foreach] */
9963 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
9964 Jim_Obj *const *argv)
9965 {
9966 return JimForeachMapHelper(interp, argc, argv, 0);
9967 }
9968
9969 /* [lmap] */
9970 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
9971 Jim_Obj *const *argv)
9972 {
9973 return JimForeachMapHelper(interp, argc, argv, 1);
9974 }
9975
9976 /* [if] */
9977 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
9978 Jim_Obj *const *argv)
9979 {
9980 int boolean, retval, current = 1, falsebody = 0;
9981 if (argc >= 3) {
9982 while (1) {
9983 /* Far not enough arguments given! */
9984 if (current >= argc) goto err;
9985 if ((retval = Jim_GetBoolFromExpr(interp,
9986 argv[current++], &boolean))
9987 != JIM_OK)
9988 return retval;
9989 /* There lacks something, isn't it? */
9990 if (current >= argc) goto err;
9991 if (Jim_CompareStringImmediate(interp, argv[current],
9992 "then")) current++;
9993 /* Tsk tsk, no then-clause? */
9994 if (current >= argc) goto err;
9995 if (boolean)
9996 return Jim_EvalObj(interp, argv[current]);
9997 /* Ok: no else-clause follows */
9998 if (++current >= argc) return JIM_OK;
9999 falsebody = current++;
10000 if (Jim_CompareStringImmediate(interp, argv[falsebody],
10001 "else")) {
10002 /* IIICKS - else-clause isn't last cmd? */
10003 if (current != argc-1) goto err;
10004 return Jim_EvalObj(interp, argv[current]);
10005 } else if (Jim_CompareStringImmediate(interp,
10006 argv[falsebody], "elseif"))
10007 /* Ok: elseif follows meaning all the stuff
10008 * again (how boring...) */
10009 continue;
10010 /* OOPS - else-clause is not last cmd?*/
10011 else if (falsebody != argc-1)
10012 goto err;
10013 return Jim_EvalObj(interp, argv[falsebody]);
10014 }
10015 return JIM_OK;
10016 }
10017 err:
10018 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10019 return JIM_ERR;
10020 }
10021
10022 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10023
10024 /* [switch] */
10025 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
10026 Jim_Obj *const *argv)
10027 {
10028 int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10029 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10030 Jim_Obj *script = 0;
10031 if (argc < 3) goto wrongnumargs;
10032 for (opt=1; opt < argc; ++opt) {
10033 const char *option = Jim_GetString(argv[opt], 0);
10034 if (*option != '-') break;
10035 else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10036 else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10037 else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10038 else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10039 else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10040 if ((argc - opt) < 2) goto wrongnumargs;
10041 command = argv[++opt];
10042 } else {
10043 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10044 Jim_AppendStrings(interp, Jim_GetResult(interp),
10045 "bad option \"", option, "\": must be -exact, -glob, "
10046 "-regexp, -command procname or --", 0);
10047 goto err;
10048 }
10049 if ((argc - opt) < 2) goto wrongnumargs;
10050 }
10051 strObj = argv[opt++];
10052 patCount = argc - opt;
10053 if (patCount == 1) {
10054 Jim_Obj **vector;
10055 JimListGetElements(interp, argv[opt], &patCount, &vector);
10056 caseList = vector;
10057 } else
10058 caseList = &argv[opt];
10059 if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10060 for (i=0; script == 0 && i < patCount; i += 2) {
10061 Jim_Obj *patObj = caseList[i];
10062 if (!Jim_CompareStringImmediate(interp, patObj, "default")
10063 || i < (patCount-2)) {
10064 switch (matchOpt) {
10065 case SWITCH_EXACT:
10066 if (Jim_StringEqObj(strObj, patObj, 0))
10067 script = caseList[i+1];
10068 break;
10069 case SWITCH_GLOB:
10070 if (Jim_StringMatchObj(patObj, strObj, 0))
10071 script = caseList[i+1];
10072 break;
10073 case SWITCH_RE:
10074 command = Jim_NewStringObj(interp, "regexp", -1);
10075 /* Fall thru intentionally */
10076 case SWITCH_CMD: {
10077 Jim_Obj *parms[] = {command, patObj, strObj};
10078 int rc = Jim_EvalObjVector(interp, 3, parms);
10079 long matching;
10080 /* After the execution of a command we need to
10081 * make sure to reconvert the object into a list
10082 * again. Only for the single-list style [switch]. */
10083 if (argc-opt == 1) {
10084 Jim_Obj **vector;
10085 JimListGetElements(interp, argv[opt], &patCount,
10086 &vector);
10087 caseList = vector;
10088 }
10089 /* command is here already decref'd */
10090 if (rc != JIM_OK) {
10091 retcode = rc;
10092 goto err;
10093 }
10094 rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10095 if (rc != JIM_OK) {
10096 retcode = rc;
10097 goto err;
10098 }
10099 if (matching)
10100 script = caseList[i+1];
10101 break;
10102 }
10103 default:
10104 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10105 Jim_AppendStrings(interp, Jim_GetResult(interp),
10106 "internal error: no such option implemented", 0);
10107 goto err;
10108 }
10109 } else {
10110 script = caseList[i+1];
10111 }
10112 }
10113 for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10114 i += 2)
10115 script = caseList[i+1];
10116 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10117 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10118 Jim_AppendStrings(interp, Jim_GetResult(interp),
10119 "no body specified for pattern \"",
10120 Jim_GetString(caseList[i-2], 0), "\"", 0);
10121 goto err;
10122 }
10123 retcode = JIM_OK;
10124 Jim_SetEmptyResult(interp);
10125 if (script != 0)
10126 retcode = Jim_EvalObj(interp, script);
10127 return retcode;
10128 wrongnumargs:
10129 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10130 "pattern body ... ?default body? or "
10131 "{pattern body ?pattern body ...?}");
10132 err:
10133 return retcode;
10134 }
10135
10136 /* [list] */
10137 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
10138 Jim_Obj *const *argv)
10139 {
10140 Jim_Obj *listObjPtr;
10141
10142 listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10143 Jim_SetResult(interp, listObjPtr);
10144 return JIM_OK;
10145 }
10146
10147 /* [lindex] */
10148 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
10149 Jim_Obj *const *argv)
10150 {
10151 Jim_Obj *objPtr, *listObjPtr;
10152 int i;
10153 int index;
10154
10155 if (argc < 3) {
10156 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10157 return JIM_ERR;
10158 }
10159 objPtr = argv[1];
10160 Jim_IncrRefCount(objPtr);
10161 for (i = 2; i < argc; i++) {
10162 listObjPtr = objPtr;
10163 if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10164 Jim_DecrRefCount(interp, listObjPtr);
10165 return JIM_ERR;
10166 }
10167 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10168 JIM_NONE) != JIM_OK) {
10169 /* Returns an empty object if the index
10170 * is out of range. */
10171 Jim_DecrRefCount(interp, listObjPtr);
10172 Jim_SetEmptyResult(interp);
10173 return JIM_OK;
10174 }
10175 Jim_IncrRefCount(objPtr);
10176 Jim_DecrRefCount(interp, listObjPtr);
10177 }
10178 Jim_SetResult(interp, objPtr);
10179 Jim_DecrRefCount(interp, objPtr);
10180 return JIM_OK;
10181 }
10182
10183 /* [llength] */
10184 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
10185 Jim_Obj *const *argv)
10186 {
10187 int len;
10188
10189 if (argc != 2) {
10190 Jim_WrongNumArgs(interp, 1, argv, "list");
10191 return JIM_ERR;
10192 }
10193 Jim_ListLength(interp, argv[1], &len);
10194 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10195 return JIM_OK;
10196 }
10197
10198 /* [lappend] */
10199 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
10200 Jim_Obj *const *argv)
10201 {
10202 Jim_Obj *listObjPtr;
10203 int shared, i;
10204
10205 if (argc < 2) {
10206 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10207 return JIM_ERR;
10208 }
10209 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10210 if (!listObjPtr) {
10211 /* Create the list if it does not exists */
10212 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10213 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10214 Jim_FreeNewObj(interp, listObjPtr);
10215 return JIM_ERR;
10216 }
10217 }
10218 shared = Jim_IsShared(listObjPtr);
10219 if (shared)
10220 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10221 for (i = 2; i < argc; i++)
10222 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10223 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10224 if (shared)
10225 Jim_FreeNewObj(interp, listObjPtr);
10226 return JIM_ERR;
10227 }
10228 Jim_SetResult(interp, listObjPtr);
10229 return JIM_OK;
10230 }
10231
10232 /* [linsert] */
10233 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10234 Jim_Obj *const *argv)
10235 {
10236 int index, len;
10237 Jim_Obj *listPtr;
10238
10239 if (argc < 4) {
10240 Jim_WrongNumArgs(interp, 1, argv, "list index element "
10241 "?element ...?");
10242 return JIM_ERR;
10243 }
10244 listPtr = argv[1];
10245 if (Jim_IsShared(listPtr))
10246 listPtr = Jim_DuplicateObj(interp, listPtr);
10247 if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10248 goto err;
10249 Jim_ListLength(interp, listPtr, &len);
10250 if (index >= len)
10251 index = len;
10252 else if (index < 0)
10253 index = len + index + 1;
10254 Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10255 Jim_SetResult(interp, listPtr);
10256 return JIM_OK;
10257 err:
10258 if (listPtr != argv[1]) {
10259 Jim_FreeNewObj(interp, listPtr);
10260 }
10261 return JIM_ERR;
10262 }
10263
10264 /* [lset] */
10265 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10266 Jim_Obj *const *argv)
10267 {
10268 if (argc < 3) {
10269 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10270 return JIM_ERR;
10271 } else if (argc == 3) {
10272 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10273 return JIM_ERR;
10274 Jim_SetResult(interp, argv[2]);
10275 return JIM_OK;
10276 }
10277 if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10278 == JIM_ERR) return JIM_ERR;
10279 return JIM_OK;
10280 }
10281
10282 /* [lsort] */
10283 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10284 {
10285 const char *options[] = {
10286 "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10287 };
10288 enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10289 Jim_Obj *resObj;
10290 int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10291 int decreasing = 0;
10292
10293 if (argc < 2) {
10294 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10295 return JIM_ERR;
10296 }
10297 for (i = 1; i < (argc-1); i++) {
10298 int option;
10299
10300 if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10301 != JIM_OK)
10302 return JIM_ERR;
10303 switch(option) {
10304 case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10305 case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10306 case OPT_INCREASING: decreasing = 0; break;
10307 case OPT_DECREASING: decreasing = 1; break;
10308 }
10309 }
10310 if (decreasing) {
10311 switch(lsortType) {
10312 case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10313 case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10314 }
10315 }
10316 resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10317 ListSortElements(interp, resObj, lsortType);
10318 Jim_SetResult(interp, resObj);
10319 return JIM_OK;
10320 }
10321
10322 /* [append] */
10323 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10324 Jim_Obj *const *argv)
10325 {
10326 Jim_Obj *stringObjPtr;
10327 int shared, i;
10328
10329 if (argc < 2) {
10330 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10331 return JIM_ERR;
10332 }
10333 if (argc == 2) {
10334 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10335 if (!stringObjPtr) return JIM_ERR;
10336 } else {
10337 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10338 if (!stringObjPtr) {
10339 /* Create the string if it does not exists */
10340 stringObjPtr = Jim_NewEmptyStringObj(interp);
10341 if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10342 != JIM_OK) {
10343 Jim_FreeNewObj(interp, stringObjPtr);
10344 return JIM_ERR;
10345 }
10346 }
10347 }
10348 shared = Jim_IsShared(stringObjPtr);
10349 if (shared)
10350 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10351 for (i = 2; i < argc; i++)
10352 Jim_AppendObj(interp, stringObjPtr, argv[i]);
10353 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10354 if (shared)
10355 Jim_FreeNewObj(interp, stringObjPtr);
10356 return JIM_ERR;
10357 }
10358 Jim_SetResult(interp, stringObjPtr);
10359 return JIM_OK;
10360 }
10361
10362 /* [debug] */
10363 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10364 Jim_Obj *const *argv)
10365 {
10366 const char *options[] = {
10367 "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10368 "exprbc",
10369 NULL
10370 };
10371 enum {
10372 OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10373 OPT_EXPRLEN, OPT_EXPRBC
10374 };
10375 int option;
10376
10377 if (argc < 2) {
10378 Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10379 return JIM_ERR;
10380 }
10381 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10382 JIM_ERRMSG) != JIM_OK)
10383 return JIM_ERR;
10384 if (option == OPT_REFCOUNT) {
10385 if (argc != 3) {
10386 Jim_WrongNumArgs(interp, 2, argv, "object");
10387 return JIM_ERR;
10388 }
10389 Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10390 return JIM_OK;
10391 } else if (option == OPT_OBJCOUNT) {
10392 int freeobj = 0, liveobj = 0;
10393 char buf[256];
10394 Jim_Obj *objPtr;
10395
10396 if (argc != 2) {
10397 Jim_WrongNumArgs(interp, 2, argv, "");
10398 return JIM_ERR;
10399 }
10400 /* Count the number of free objects. */
10401 objPtr = interp->freeList;
10402 while (objPtr) {
10403 freeobj++;
10404 objPtr = objPtr->nextObjPtr;
10405 }
10406 /* Count the number of live objects. */
10407 objPtr = interp->liveList;
10408 while (objPtr) {
10409 liveobj++;
10410 objPtr = objPtr->nextObjPtr;
10411 }
10412 /* Set the result string and return. */
10413 sprintf(buf, "free %d used %d", freeobj, liveobj);
10414 Jim_SetResultString(interp, buf, -1);
10415 return JIM_OK;
10416 } else if (option == OPT_OBJECTS) {
10417 Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10418 /* Count the number of live objects. */
10419 objPtr = interp->liveList;
10420 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10421 while (objPtr) {
10422 char buf[128];
10423 const char *type = objPtr->typePtr ?
10424 objPtr->typePtr->name : "";
10425 subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10426 sprintf(buf, "%p", objPtr);
10427 Jim_ListAppendElement(interp, subListObjPtr,
10428 Jim_NewStringObj(interp, buf, -1));
10429 Jim_ListAppendElement(interp, subListObjPtr,
10430 Jim_NewStringObj(interp, type, -1));
10431 Jim_ListAppendElement(interp, subListObjPtr,
10432 Jim_NewIntObj(interp, objPtr->refCount));
10433 Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10434 Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10435 objPtr = objPtr->nextObjPtr;
10436 }
10437 Jim_SetResult(interp, listObjPtr);
10438 return JIM_OK;
10439 } else if (option == OPT_INVSTR) {
10440 Jim_Obj *objPtr;
10441
10442 if (argc != 3) {
10443 Jim_WrongNumArgs(interp, 2, argv, "object");
10444 return JIM_ERR;
10445 }
10446 objPtr = argv[2];
10447 if (objPtr->typePtr != NULL)
10448 Jim_InvalidateStringRep(objPtr);
10449 Jim_SetEmptyResult(interp);
10450 return JIM_OK;
10451 } else if (option == OPT_SCRIPTLEN) {
10452 ScriptObj *script;
10453 if (argc != 3) {
10454 Jim_WrongNumArgs(interp, 2, argv, "script");
10455 return JIM_ERR;
10456 }
10457 script = Jim_GetScript(interp, argv[2]);
10458 Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10459 return JIM_OK;
10460 } else if (option == OPT_EXPRLEN) {
10461 ExprByteCode *expr;
10462 if (argc != 3) {
10463 Jim_WrongNumArgs(interp, 2, argv, "expression");
10464 return JIM_ERR;
10465 }
10466 expr = Jim_GetExpression(interp, argv[2]);
10467 if (expr == NULL)
10468 return JIM_ERR;
10469 Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10470 return JIM_OK;
10471 } else if (option == OPT_EXPRBC) {
10472 Jim_Obj *objPtr;
10473 ExprByteCode *expr;
10474 int i;
10475
10476 if (argc != 3) {
10477 Jim_WrongNumArgs(interp, 2, argv, "expression");
10478 return JIM_ERR;
10479 }
10480 expr = Jim_GetExpression(interp, argv[2]);
10481 if (expr == NULL)
10482 return JIM_ERR;
10483 objPtr = Jim_NewListObj(interp, NULL, 0);
10484 for (i = 0; i < expr->len; i++) {
10485 const char *type;
10486 Jim_ExprOperator *op;
10487
10488 switch(expr->opcode[i]) {
10489 case JIM_EXPROP_NUMBER: type = "number"; break;
10490 case JIM_EXPROP_COMMAND: type = "command"; break;
10491 case JIM_EXPROP_VARIABLE: type = "variable"; break;
10492 case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10493 case JIM_EXPROP_SUBST: type = "subst"; break;
10494 case JIM_EXPROP_STRING: type = "string"; break;
10495 default:
10496 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10497 if (op == NULL) {
10498 type = "private";
10499 } else {
10500 type = "operator";
10501 }
10502 break;
10503 }
10504 Jim_ListAppendElement(interp, objPtr,
10505 Jim_NewStringObj(interp, type, -1));
10506 Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10507 }
10508 Jim_SetResult(interp, objPtr);
10509 return JIM_OK;
10510 } else {
10511 Jim_SetResultString(interp,
10512 "bad option. Valid options are refcount, "
10513 "objcount, objects, invstr", -1);
10514 return JIM_ERR;
10515 }
10516 return JIM_OK; /* unreached */
10517 }
10518
10519 /* [eval] */
10520 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10521 Jim_Obj *const *argv)
10522 {
10523 if (argc == 2) {
10524 return Jim_EvalObj(interp, argv[1]);
10525 } else if (argc > 2) {
10526 Jim_Obj *objPtr;
10527 int retcode;
10528
10529 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10530 Jim_IncrRefCount(objPtr);
10531 retcode = Jim_EvalObj(interp, objPtr);
10532 Jim_DecrRefCount(interp, objPtr);
10533 return retcode;
10534 } else {
10535 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10536 return JIM_ERR;
10537 }
10538 }
10539
10540 /* [uplevel] */
10541 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10542 Jim_Obj *const *argv)
10543 {
10544 if (argc >= 2) {
10545 int retcode, newLevel, oldLevel;
10546 Jim_CallFrame *savedCallFrame, *targetCallFrame;
10547 Jim_Obj *objPtr;
10548 const char *str;
10549
10550 /* Save the old callframe pointer */
10551 savedCallFrame = interp->framePtr;
10552
10553 /* Lookup the target frame pointer */
10554 str = Jim_GetString(argv[1], NULL);
10555 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10556 {
10557 if (Jim_GetCallFrameByLevel(interp, argv[1],
10558 &targetCallFrame,
10559 &newLevel) != JIM_OK)
10560 return JIM_ERR;
10561 argc--;
10562 argv++;
10563 } else {
10564 if (Jim_GetCallFrameByLevel(interp, NULL,
10565 &targetCallFrame,
10566 &newLevel) != JIM_OK)
10567 return JIM_ERR;
10568 }
10569 if (argc < 2) {
10570 argc++;
10571 argv--;
10572 Jim_WrongNumArgs(interp, 1, argv,
10573 "?level? command ?arg ...?");
10574 return JIM_ERR;
10575 }
10576 /* Eval the code in the target callframe. */
10577 interp->framePtr = targetCallFrame;
10578 oldLevel = interp->numLevels;
10579 interp->numLevels = newLevel;
10580 if (argc == 2) {
10581 retcode = Jim_EvalObj(interp, argv[1]);
10582 } else {
10583 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10584 Jim_IncrRefCount(objPtr);
10585 retcode = Jim_EvalObj(interp, objPtr);
10586 Jim_DecrRefCount(interp, objPtr);
10587 }
10588 interp->numLevels = oldLevel;
10589 interp->framePtr = savedCallFrame;
10590 return retcode;
10591 } else {
10592 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10593 return JIM_ERR;
10594 }
10595 }
10596
10597 /* [expr] */
10598 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10599 Jim_Obj *const *argv)
10600 {
10601 Jim_Obj *exprResultPtr;
10602 int retcode;
10603
10604 if (argc == 2) {
10605 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10606 } else if (argc > 2) {
10607 Jim_Obj *objPtr;
10608
10609 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10610 Jim_IncrRefCount(objPtr);
10611 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10612 Jim_DecrRefCount(interp, objPtr);
10613 } else {
10614 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10615 return JIM_ERR;
10616 }
10617 if (retcode != JIM_OK) return retcode;
10618 Jim_SetResult(interp, exprResultPtr);
10619 Jim_DecrRefCount(interp, exprResultPtr);
10620 return JIM_OK;
10621 }
10622
10623 /* [break] */
10624 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10625 Jim_Obj *const *argv)
10626 {
10627 if (argc != 1) {
10628 Jim_WrongNumArgs(interp, 1, argv, "");
10629 return JIM_ERR;
10630 }
10631 return JIM_BREAK;
10632 }
10633
10634 /* [continue] */
10635 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10636 Jim_Obj *const *argv)
10637 {
10638 if (argc != 1) {
10639 Jim_WrongNumArgs(interp, 1, argv, "");
10640 return JIM_ERR;
10641 }
10642 return JIM_CONTINUE;
10643 }
10644
10645 /* [return] */
10646 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10647 Jim_Obj *const *argv)
10648 {
10649 if (argc == 1) {
10650 return JIM_RETURN;
10651 } else if (argc == 2) {
10652 Jim_SetResult(interp, argv[1]);
10653 interp->returnCode = JIM_OK;
10654 return JIM_RETURN;
10655 } else if (argc == 3 || argc == 4) {
10656 int returnCode;
10657 if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10658 return JIM_ERR;
10659 interp->returnCode = returnCode;
10660 if (argc == 4)
10661 Jim_SetResult(interp, argv[3]);
10662 return JIM_RETURN;
10663 } else {
10664 Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10665 return JIM_ERR;
10666 }
10667 return JIM_RETURN; /* unreached */
10668 }
10669
10670 /* [tailcall] */
10671 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10672 Jim_Obj *const *argv)
10673 {
10674 Jim_Obj *objPtr;
10675
10676 objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10677 Jim_SetResult(interp, objPtr);
10678 return JIM_EVAL;
10679 }
10680
10681 /* [proc] */
10682 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
10683 Jim_Obj *const *argv)
10684 {
10685 int argListLen;
10686 int arityMin, arityMax;
10687
10688 if (argc != 4 && argc != 5) {
10689 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10690 return JIM_ERR;
10691 }
10692 Jim_ListLength(interp, argv[2], &argListLen);
10693 arityMin = arityMax = argListLen+1;
10694 if (argListLen) {
10695 const char *str;
10696 int len;
10697 Jim_Obj *lastArgPtr;
10698
10699 Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10700 str = Jim_GetString(lastArgPtr, &len);
10701 if (len == 4 && memcmp(str, "args", 4) == 0) {
10702 arityMin--;
10703 arityMax = -1;
10704 }
10705 }
10706 if (argc == 4) {
10707 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10708 argv[2], NULL, argv[3], arityMin, arityMax);
10709 } else {
10710 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10711 argv[2], argv[3], argv[4], arityMin, arityMax);
10712 }
10713 }
10714
10715 /* [concat] */
10716 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
10717 Jim_Obj *const *argv)
10718 {
10719 Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10720 return JIM_OK;
10721 }
10722
10723 /* [upvar] */
10724 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
10725 Jim_Obj *const *argv)
10726 {
10727 const char *str;
10728 int i;
10729 Jim_CallFrame *targetCallFrame;
10730
10731 /* Lookup the target frame pointer */
10732 str = Jim_GetString(argv[1], NULL);
10733 if (argc > 3 &&
10734 ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10735 {
10736 if (Jim_GetCallFrameByLevel(interp, argv[1],
10737 &targetCallFrame, NULL) != JIM_OK)
10738 return JIM_ERR;
10739 argc--;
10740 argv++;
10741 } else {
10742 if (Jim_GetCallFrameByLevel(interp, NULL,
10743 &targetCallFrame, NULL) != JIM_OK)
10744 return JIM_ERR;
10745 }
10746 /* Check for arity */
10747 if (argc < 3 || ((argc-1)%2) != 0) {
10748 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10749 return JIM_ERR;
10750 }
10751 /* Now... for every other/local couple: */
10752 for (i = 1; i < argc; i += 2) {
10753 if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10754 targetCallFrame) != JIM_OK) return JIM_ERR;
10755 }
10756 return JIM_OK;
10757 }
10758
10759 /* [global] */
10760 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
10761 Jim_Obj *const *argv)
10762 {
10763 int i;
10764
10765 if (argc < 2) {
10766 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10767 return JIM_ERR;
10768 }
10769 /* Link every var to the toplevel having the same name */
10770 if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10771 for (i = 1; i < argc; i++) {
10772 if (Jim_SetVariableLink(interp, argv[i], argv[i],
10773 interp->topFramePtr) != JIM_OK) return JIM_ERR;
10774 }
10775 return JIM_OK;
10776 }
10777
10778 /* does the [string map] operation. On error NULL is returned,
10779 * otherwise a new string object with the result, having refcount = 0,
10780 * is returned. */
10781 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10782 Jim_Obj *objPtr, int nocase)
10783 {
10784 int numMaps;
10785 const char **key, *str, *noMatchStart = NULL;
10786 Jim_Obj **value;
10787 int *keyLen, strLen, i;
10788 Jim_Obj *resultObjPtr;
10789
10790 Jim_ListLength(interp, mapListObjPtr, &numMaps);
10791 if (numMaps % 2) {
10792 Jim_SetResultString(interp,
10793 "list must contain an even number of elements", -1);
10794 return NULL;
10795 }
10796 /* Initialization */
10797 numMaps /= 2;
10798 key = Jim_Alloc(sizeof(char*)*numMaps);
10799 keyLen = Jim_Alloc(sizeof(int)*numMaps);
10800 value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10801 resultObjPtr = Jim_NewStringObj(interp, "", 0);
10802 for (i = 0; i < numMaps; i++) {
10803 Jim_Obj *eleObjPtr;
10804
10805 Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10806 key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10807 Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10808 value[i] = eleObjPtr;
10809 }
10810 str = Jim_GetString(objPtr, &strLen);
10811 /* Map it */
10812 while(strLen) {
10813 for (i = 0; i < numMaps; i++) {
10814 if (strLen >= keyLen[i] && keyLen[i]) {
10815 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10816 nocase))
10817 {
10818 if (noMatchStart) {
10819 Jim_AppendString(interp, resultObjPtr,
10820 noMatchStart, str-noMatchStart);
10821 noMatchStart = NULL;
10822 }
10823 Jim_AppendObj(interp, resultObjPtr, value[i]);
10824 str += keyLen[i];
10825 strLen -= keyLen[i];
10826 break;
10827 }
10828 }
10829 }
10830 if (i == numMaps) { /* no match */
10831 if (noMatchStart == NULL)
10832 noMatchStart = str;
10833 str ++;
10834 strLen --;
10835 }
10836 }
10837 if (noMatchStart) {
10838 Jim_AppendString(interp, resultObjPtr,
10839 noMatchStart, str-noMatchStart);
10840 }
10841 Jim_Free((void*)key);
10842 Jim_Free(keyLen);
10843 Jim_Free(value);
10844 return resultObjPtr;
10845 }
10846
10847 /* [string] */
10848 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
10849 Jim_Obj *const *argv)
10850 {
10851 int option;
10852 const char *options[] = {
10853 "length", "compare", "match", "equal", "range", "map", "repeat",
10854 "index", "first", "tolower", "toupper", NULL
10855 };
10856 enum {
10857 OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10858 OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10859 };
10860
10861 if (argc < 2) {
10862 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10863 return JIM_ERR;
10864 }
10865 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10866 JIM_ERRMSG) != JIM_OK)
10867 return JIM_ERR;
10868
10869 if (option == OPT_LENGTH) {
10870 int len;
10871
10872 if (argc != 3) {
10873 Jim_WrongNumArgs(interp, 2, argv, "string");
10874 return JIM_ERR;
10875 }
10876 Jim_GetString(argv[2], &len);
10877 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10878 return JIM_OK;
10879 } else if (option == OPT_COMPARE) {
10880 int nocase = 0;
10881 if ((argc != 4 && argc != 5) ||
10882 (argc == 5 && Jim_CompareStringImmediate(interp,
10883 argv[2], "-nocase") == 0)) {
10884 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10885 return JIM_ERR;
10886 }
10887 if (argc == 5) {
10888 nocase = 1;
10889 argv++;
10890 }
10891 Jim_SetResult(interp, Jim_NewIntObj(interp,
10892 Jim_StringCompareObj(argv[2],
10893 argv[3], nocase)));
10894 return JIM_OK;
10895 } else if (option == OPT_MATCH) {
10896 int nocase = 0;
10897 if ((argc != 4 && argc != 5) ||
10898 (argc == 5 && Jim_CompareStringImmediate(interp,
10899 argv[2], "-nocase") == 0)) {
10900 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10901 "string");
10902 return JIM_ERR;
10903 }
10904 if (argc == 5) {
10905 nocase = 1;
10906 argv++;
10907 }
10908 Jim_SetResult(interp,
10909 Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10910 argv[3], nocase)));
10911 return JIM_OK;
10912 } else if (option == OPT_EQUAL) {
10913 if (argc != 4) {
10914 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10915 return JIM_ERR;
10916 }
10917 Jim_SetResult(interp,
10918 Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10919 argv[3], 0)));
10920 return JIM_OK;
10921 } else if (option == OPT_RANGE) {
10922 Jim_Obj *objPtr;
10923
10924 if (argc != 5) {
10925 Jim_WrongNumArgs(interp, 2, argv, "string first last");
10926 return JIM_ERR;
10927 }
10928 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10929 if (objPtr == NULL)
10930 return JIM_ERR;
10931 Jim_SetResult(interp, objPtr);
10932 return JIM_OK;
10933 } else if (option == OPT_MAP) {
10934 int nocase = 0;
10935 Jim_Obj *objPtr;
10936
10937 if ((argc != 4 && argc != 5) ||
10938 (argc == 5 && Jim_CompareStringImmediate(interp,
10939 argv[2], "-nocase") == 0)) {
10940 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10941 "string");
10942 return JIM_ERR;
10943 }
10944 if (argc == 5) {
10945 nocase = 1;
10946 argv++;
10947 }
10948 objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10949 if (objPtr == NULL)
10950 return JIM_ERR;
10951 Jim_SetResult(interp, objPtr);
10952 return JIM_OK;
10953 } else if (option == OPT_REPEAT) {
10954 Jim_Obj *objPtr;
10955 jim_wide count;
10956
10957 if (argc != 4) {
10958 Jim_WrongNumArgs(interp, 2, argv, "string count");
10959 return JIM_ERR;
10960 }
10961 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
10962 return JIM_ERR;
10963 objPtr = Jim_NewStringObj(interp, "", 0);
10964 while (count--) {
10965 Jim_AppendObj(interp, objPtr, argv[2]);
10966 }
10967 Jim_SetResult(interp, objPtr);
10968 return JIM_OK;
10969 } else if (option == OPT_INDEX) {
10970 int index, len;
10971 const char *str;
10972
10973 if (argc != 4) {
10974 Jim_WrongNumArgs(interp, 2, argv, "string index");
10975 return JIM_ERR;
10976 }
10977 if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
10978 return JIM_ERR;
10979 str = Jim_GetString(argv[2], &len);
10980 if (index != INT_MIN && index != INT_MAX)
10981 index = JimRelToAbsIndex(len, index);
10982 if (index < 0 || index >= len) {
10983 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10984 return JIM_OK;
10985 } else {
10986 Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
10987 return JIM_OK;
10988 }
10989 } else if (option == OPT_FIRST) {
10990 int index = 0, l1, l2;
10991 const char *s1, *s2;
10992
10993 if (argc != 4 && argc != 5) {
10994 Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
10995 return JIM_ERR;
10996 }
10997 s1 = Jim_GetString(argv[2], &l1);
10998 s2 = Jim_GetString(argv[3], &l2);
10999 if (argc == 5) {
11000 if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
11001 return JIM_ERR;
11002 index = JimRelToAbsIndex(l2, index);
11003 }
11004 Jim_SetResult(interp, Jim_NewIntObj(interp,
11005 JimStringFirst(s1, l1, s2, l2, index)));
11006 return JIM_OK;
11007 } else if (option == OPT_TOLOWER) {
11008 if (argc != 3) {
11009 Jim_WrongNumArgs(interp, 2, argv, "string");
11010 return JIM_ERR;
11011 }
11012 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11013 } else if (option == OPT_TOUPPER) {
11014 if (argc != 3) {
11015 Jim_WrongNumArgs(interp, 2, argv, "string");
11016 return JIM_ERR;
11017 }
11018 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11019 }
11020 return JIM_OK;
11021 }
11022
11023 /* [time] */
11024 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
11025 Jim_Obj *const *argv)
11026 {
11027 long i, count = 1;
11028 jim_wide start, elapsed;
11029 char buf [256];
11030 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11031
11032 if (argc < 2) {
11033 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11034 return JIM_ERR;
11035 }
11036 if (argc == 3) {
11037 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11038 return JIM_ERR;
11039 }
11040 if (count < 0)
11041 return JIM_OK;
11042 i = count;
11043 start = JimClock();
11044 while (i-- > 0) {
11045 int retval;
11046
11047 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11048 return retval;
11049 }
11050 elapsed = JimClock() - start;
11051 sprintf(buf, fmt, elapsed/count);
11052 Jim_SetResultString(interp, buf, -1);
11053 return JIM_OK;
11054 }
11055
11056 /* [exit] */
11057 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
11058 Jim_Obj *const *argv)
11059 {
11060 long exitCode = 0;
11061
11062 if (argc > 2) {
11063 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11064 return JIM_ERR;
11065 }
11066 if (argc == 2) {
11067 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11068 return JIM_ERR;
11069 }
11070 interp->exitCode = exitCode;
11071 return JIM_EXIT;
11072 }
11073
11074 /* [catch] */
11075 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
11076 Jim_Obj *const *argv)
11077 {
11078 int exitCode = 0;
11079
11080 if (argc != 2 && argc != 3) {
11081 Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11082 return JIM_ERR;
11083 }
11084 exitCode = Jim_EvalObj(interp, argv[1]);
11085 if (argc == 3) {
11086 if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11087 != JIM_OK)
11088 return JIM_ERR;
11089 }
11090 Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11091 return JIM_OK;
11092 }
11093
11094 /* [ref] */
11095 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
11096 Jim_Obj *const *argv)
11097 {
11098 if (argc != 3 && argc != 4) {
11099 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11100 return JIM_ERR;
11101 }
11102 if (argc == 3) {
11103 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11104 } else {
11105 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11106 argv[3]));
11107 }
11108 return JIM_OK;
11109 }
11110
11111 /* [getref] */
11112 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
11113 Jim_Obj *const *argv)
11114 {
11115 Jim_Reference *refPtr;
11116
11117 if (argc != 2) {
11118 Jim_WrongNumArgs(interp, 1, argv, "reference");
11119 return JIM_ERR;
11120 }
11121 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11122 return JIM_ERR;
11123 Jim_SetResult(interp, refPtr->objPtr);
11124 return JIM_OK;
11125 }
11126
11127 /* [setref] */
11128 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
11129 Jim_Obj *const *argv)
11130 {
11131 Jim_Reference *refPtr;
11132
11133 if (argc != 3) {
11134 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11135 return JIM_ERR;
11136 }
11137 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11138 return JIM_ERR;
11139 Jim_IncrRefCount(argv[2]);
11140 Jim_DecrRefCount(interp, refPtr->objPtr);
11141 refPtr->objPtr = argv[2];
11142 Jim_SetResult(interp, argv[2]);
11143 return JIM_OK;
11144 }
11145
11146 /* [collect] */
11147 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
11148 Jim_Obj *const *argv)
11149 {
11150 if (argc != 1) {
11151 Jim_WrongNumArgs(interp, 1, argv, "");
11152 return JIM_ERR;
11153 }
11154 Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11155 return JIM_OK;
11156 }
11157
11158 /* [finalize] reference ?newValue? */
11159 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
11160 Jim_Obj *const *argv)
11161 {
11162 if (argc != 2 && argc != 3) {
11163 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11164 return JIM_ERR;
11165 }
11166 if (argc == 2) {
11167 Jim_Obj *cmdNamePtr;
11168
11169 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11170 return JIM_ERR;
11171 if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11172 Jim_SetResult(interp, cmdNamePtr);
11173 } else {
11174 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11175 return JIM_ERR;
11176 Jim_SetResult(interp, argv[2]);
11177 }
11178 return JIM_OK;
11179 }
11180
11181 /* TODO */
11182 /* [info references] (list of all the references/finalizers) */
11183
11184 /* [rename] */
11185 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
11186 Jim_Obj *const *argv)
11187 {
11188 const char *oldName, *newName;
11189
11190 if (argc != 3) {
11191 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11192 return JIM_ERR;
11193 }
11194 oldName = Jim_GetString(argv[1], NULL);
11195 newName = Jim_GetString(argv[2], NULL);
11196 if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11197 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11198 Jim_AppendStrings(interp, Jim_GetResult(interp),
11199 "can't rename \"", oldName, "\": ",
11200 "command doesn't exist", NULL);
11201 return JIM_ERR;
11202 }
11203 return JIM_OK;
11204 }
11205
11206 /* [dict] */
11207 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
11208 Jim_Obj *const *argv)
11209 {
11210 int option;
11211 const char *options[] = {
11212 "create", "get", "set", "unset", "exists", NULL
11213 };
11214 enum {
11215 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11216 };
11217
11218 if (argc < 2) {
11219 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11220 return JIM_ERR;
11221 }
11222
11223 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11224 JIM_ERRMSG) != JIM_OK)
11225 return JIM_ERR;
11226
11227 if (option == OPT_CREATE) {
11228 Jim_Obj *objPtr;
11229
11230 if (argc % 2) {
11231 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11232 return JIM_ERR;
11233 }
11234 objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11235 Jim_SetResult(interp, objPtr);
11236 return JIM_OK;
11237 } else if (option == OPT_GET) {
11238 Jim_Obj *objPtr;
11239
11240 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11241 JIM_ERRMSG) != JIM_OK)
11242 return JIM_ERR;
11243 Jim_SetResult(interp, objPtr);
11244 return JIM_OK;
11245 } else if (option == OPT_SET) {
11246 if (argc < 5) {
11247 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11248 return JIM_ERR;
11249 }
11250 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11251 argv[argc-1]);
11252 } else if (option == OPT_UNSET) {
11253 if (argc < 4) {
11254 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11255 return JIM_ERR;
11256 }
11257 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11258 NULL);
11259 } else if (option == OPT_EXIST) {
11260 Jim_Obj *objPtr;
11261 int exists;
11262
11263 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11264 JIM_ERRMSG) == JIM_OK)
11265 exists = 1;
11266 else
11267 exists = 0;
11268 Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11269 return JIM_OK;
11270 } else {
11271 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11272 Jim_AppendStrings(interp, Jim_GetResult(interp),
11273 "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11274 " must be create, get, set", NULL);
11275 return JIM_ERR;
11276 }
11277 return JIM_OK;
11278 }
11279
11280 /* [load] */
11281 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11282 Jim_Obj *const *argv)
11283 {
11284 if (argc < 2) {
11285 Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11286 return JIM_ERR;
11287 }
11288 return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11289 }
11290
11291 /* [subst] */
11292 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11293 Jim_Obj *const *argv)
11294 {
11295 int i, flags = 0;
11296 Jim_Obj *objPtr;
11297
11298 if (argc < 2) {
11299 Jim_WrongNumArgs(interp, 1, argv,
11300 "?-nobackslashes? ?-nocommands? ?-novariables? string");
11301 return JIM_ERR;
11302 }
11303 i = argc-2;
11304 while(i--) {
11305 if (Jim_CompareStringImmediate(interp, argv[i+1],
11306 "-nobackslashes"))
11307 flags |= JIM_SUBST_NOESC;
11308 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11309 "-novariables"))
11310 flags |= JIM_SUBST_NOVAR;
11311 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11312 "-nocommands"))
11313 flags |= JIM_SUBST_NOCMD;
11314 else {
11315 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11316 Jim_AppendStrings(interp, Jim_GetResult(interp),
11317 "bad option \"", Jim_GetString(argv[i+1], NULL),
11318 "\": must be -nobackslashes, -nocommands, or "
11319 "-novariables", NULL);
11320 return JIM_ERR;
11321 }
11322 }
11323 if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11324 return JIM_ERR;
11325 Jim_SetResult(interp, objPtr);
11326 return JIM_OK;
11327 }
11328
11329 /* [info] */
11330 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11331 Jim_Obj *const *argv)
11332 {
11333 int cmd, result = JIM_OK;
11334 static const char *commands[] = {
11335 "body", "commands", "exists", "globals", "level", "locals",
11336 "vars", "version", "complete", "args", NULL
11337 };
11338 enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11339 INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11340
11341 if (argc < 2) {
11342 Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11343 return JIM_ERR;
11344 }
11345 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11346 != JIM_OK) {
11347 return JIM_ERR;
11348 }
11349
11350 if (cmd == INFO_COMMANDS) {
11351 if (argc != 2 && argc != 3) {
11352 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11353 return JIM_ERR;
11354 }
11355 if (argc == 3)
11356 Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11357 else
11358 Jim_SetResult(interp, JimCommandsList(interp, NULL));
11359 } else if (cmd == INFO_EXISTS) {
11360 Jim_Obj *exists;
11361 if (argc != 3) {
11362 Jim_WrongNumArgs(interp, 2, argv, "varName");
11363 return JIM_ERR;
11364 }
11365 exists = Jim_GetVariable(interp, argv[2], 0);
11366 Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11367 } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11368 int mode;
11369 switch (cmd) {
11370 case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11371 case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
11372 case INFO_VARS: mode = JIM_VARLIST_VARS; break;
11373 default: mode = 0; /* avoid warning */; break;
11374 }
11375 if (argc != 2 && argc != 3) {
11376 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11377 return JIM_ERR;
11378 }
11379 if (argc == 3)
11380 Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11381 else
11382 Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11383 } else if (cmd == INFO_LEVEL) {
11384 Jim_Obj *objPtr;
11385 switch (argc) {
11386 case 2:
11387 Jim_SetResult(interp,
11388 Jim_NewIntObj(interp, interp->numLevels));
11389 break;
11390 case 3:
11391 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11392 return JIM_ERR;
11393 Jim_SetResult(interp, objPtr);
11394 break;
11395 default:
11396 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11397 return JIM_ERR;
11398 }
11399 } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11400 Jim_Cmd *cmdPtr;
11401
11402 if (argc != 3) {
11403 Jim_WrongNumArgs(interp, 2, argv, "procname");
11404 return JIM_ERR;
11405 }
11406 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11407 return JIM_ERR;
11408 if (cmdPtr->cmdProc != NULL) {
11409 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11410 Jim_AppendStrings(interp, Jim_GetResult(interp),
11411 "command \"", Jim_GetString(argv[2], NULL),
11412 "\" is not a procedure", NULL);
11413 return JIM_ERR;
11414 }
11415 if (cmd == INFO_BODY)
11416 Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11417 else
11418 Jim_SetResult(interp, cmdPtr->argListObjPtr);
11419 } else if (cmd == INFO_VERSION) {
11420 char buf[(JIM_INTEGER_SPACE * 2) + 1];
11421 sprintf(buf, "%d.%d",
11422 JIM_VERSION / 100, JIM_VERSION % 100);
11423 Jim_SetResultString(interp, buf, -1);
11424 } else if (cmd == INFO_COMPLETE) {
11425 const char *s;
11426 int len;
11427
11428 if (argc != 3) {
11429 Jim_WrongNumArgs(interp, 2, argv, "script");
11430 return JIM_ERR;
11431 }
11432 s = Jim_GetString(argv[2], &len);
11433 Jim_SetResult(interp,
11434 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11435 }
11436 return result;
11437 }
11438
11439 /* [split] */
11440 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11441 Jim_Obj *const *argv)
11442 {
11443 const char *str, *splitChars, *noMatchStart;
11444 int splitLen, strLen, i;
11445 Jim_Obj *resObjPtr;
11446
11447 if (argc != 2 && argc != 3) {
11448 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11449 return JIM_ERR;
11450 }
11451 /* Init */
11452 if (argc == 2) {
11453 splitChars = " \n\t\r";
11454 splitLen = 4;
11455 } else {
11456 splitChars = Jim_GetString(argv[2], &splitLen);
11457 }
11458 str = Jim_GetString(argv[1], &strLen);
11459 if (!strLen) return JIM_OK;
11460 noMatchStart = str;
11461 resObjPtr = Jim_NewListObj(interp, NULL, 0);
11462 /* Split */
11463 if (splitLen) {
11464 while (strLen) {
11465 for (i = 0; i < splitLen; i++) {
11466 if (*str == splitChars[i]) {
11467 Jim_Obj *objPtr;
11468
11469 objPtr = Jim_NewStringObj(interp, noMatchStart,
11470 (str-noMatchStart));
11471 Jim_ListAppendElement(interp, resObjPtr, objPtr);
11472 noMatchStart = str+1;
11473 break;
11474 }
11475 }
11476 str ++;
11477 strLen --;
11478 }
11479 Jim_ListAppendElement(interp, resObjPtr,
11480 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11481 } else {
11482 /* This handles the special case of splitchars eq {}. This
11483 * is trivial but we want to perform object sharing as Tcl does. */
11484 Jim_Obj *objCache[256];
11485 const unsigned char *u = (unsigned char*) str;
11486 memset(objCache, 0, sizeof(objCache));
11487 for (i = 0; i < strLen; i++) {
11488 int c = u[i];
11489
11490 if (objCache[c] == NULL)
11491 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11492 Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11493 }
11494 }
11495 Jim_SetResult(interp, resObjPtr);
11496 return JIM_OK;
11497 }
11498
11499 /* [join] */
11500 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11501 Jim_Obj *const *argv)
11502 {
11503 const char *joinStr;
11504 int joinStrLen, i, listLen;
11505 Jim_Obj *resObjPtr;
11506
11507 if (argc != 2 && argc != 3) {
11508 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11509 return JIM_ERR;
11510 }
11511 /* Init */
11512 if (argc == 2) {
11513 joinStr = " ";
11514 joinStrLen = 1;
11515 } else {
11516 joinStr = Jim_GetString(argv[2], &joinStrLen);
11517 }
11518 Jim_ListLength(interp, argv[1], &listLen);
11519 resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11520 /* Split */
11521 for (i = 0; i < listLen; i++) {
11522 Jim_Obj *objPtr;
11523
11524 Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11525 Jim_AppendObj(interp, resObjPtr, objPtr);
11526 if (i+1 != listLen) {
11527 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11528 }
11529 }
11530 Jim_SetResult(interp, resObjPtr);
11531 return JIM_OK;
11532 }
11533
11534 /* [format] */
11535 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11536 Jim_Obj *const *argv)
11537 {
11538 Jim_Obj *objPtr;
11539
11540 if (argc < 2) {
11541 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11542 return JIM_ERR;
11543 }
11544 objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11545 if (objPtr == NULL)
11546 return JIM_ERR;
11547 Jim_SetResult(interp, objPtr);
11548 return JIM_OK;
11549 }
11550
11551 /* [scan] */
11552 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11553 Jim_Obj *const *argv)
11554 {
11555 Jim_Obj *listPtr, **outVec;
11556 int outc, i, count = 0;
11557
11558 if (argc < 3) {
11559 Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11560 return JIM_ERR;
11561 }
11562 if (argv[2]->typePtr != &scanFmtStringObjType)
11563 SetScanFmtFromAny(interp, argv[2]);
11564 if (FormatGetError(argv[2]) != 0) {
11565 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11566 return JIM_ERR;
11567 }
11568 if (argc > 3) {
11569 int maxPos = FormatGetMaxPos(argv[2]);
11570 int count = FormatGetCnvCount(argv[2]);
11571 if (maxPos > argc-3) {
11572 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11573 return JIM_ERR;
11574 } else if (count != 0 && count < argc-3) {
11575 Jim_SetResultString(interp, "variable is not assigned by any "
11576 "conversion specifiers", -1);
11577 return JIM_ERR;
11578 } else if (count > argc-3) {
11579 Jim_SetResultString(interp, "different numbers of variable names and "
11580 "field specifiers", -1);
11581 return JIM_ERR;
11582 }
11583 }
11584 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11585 if (listPtr == 0)
11586 return JIM_ERR;
11587 if (argc > 3) {
11588 int len = 0;
11589 if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11590 Jim_ListLength(interp, listPtr, &len);
11591 if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11592 Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11593 return JIM_OK;
11594 }
11595 JimListGetElements(interp, listPtr, &outc, &outVec);
11596 for (i = 0; i < outc; ++i) {
11597 if (Jim_Length(outVec[i]) > 0) {
11598 ++count;
11599 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11600 goto err;
11601 }
11602 }
11603 Jim_FreeNewObj(interp, listPtr);
11604 Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11605 } else {
11606 if (listPtr == (Jim_Obj*)EOF) {
11607 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11608 return JIM_OK;
11609 }
11610 Jim_SetResult(interp, listPtr);
11611 }
11612 return JIM_OK;
11613 err:
11614 Jim_FreeNewObj(interp, listPtr);
11615 return JIM_ERR;
11616 }
11617
11618 /* [error] */
11619 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11620 Jim_Obj *const *argv)
11621 {
11622 if (argc != 2) {
11623 Jim_WrongNumArgs(interp, 1, argv, "message");
11624 return JIM_ERR;
11625 }
11626 Jim_SetResult(interp, argv[1]);
11627 return JIM_ERR;
11628 }
11629
11630 /* [lrange] */
11631 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11632 Jim_Obj *const *argv)
11633 {
11634 Jim_Obj *objPtr;
11635
11636 if (argc != 4) {
11637 Jim_WrongNumArgs(interp, 1, argv, "list first last");
11638 return JIM_ERR;
11639 }
11640 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11641 return JIM_ERR;
11642 Jim_SetResult(interp, objPtr);
11643 return JIM_OK;
11644 }
11645
11646 /* [env] */
11647 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11648 Jim_Obj *const *argv)
11649 {
11650 const char *key;
11651 char *val;
11652
11653 if (argc != 2) {
11654 Jim_WrongNumArgs(interp, 1, argv, "varName");
11655 return JIM_ERR;
11656 }
11657 key = Jim_GetString(argv[1], NULL);
11658 val = getenv(key);
11659 if (val == NULL) {
11660 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11661 Jim_AppendStrings(interp, Jim_GetResult(interp),
11662 "environment variable \"",
11663 key, "\" does not exist", NULL);
11664 return JIM_ERR;
11665 }
11666 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11667 return JIM_OK;
11668 }
11669
11670 /* [source] */
11671 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11672 Jim_Obj *const *argv)
11673 {
11674 int retval;
11675
11676 if (argc != 2) {
11677 Jim_WrongNumArgs(interp, 1, argv, "fileName");
11678 return JIM_ERR;
11679 }
11680 retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11681 if (retval == JIM_RETURN)
11682 return JIM_OK;
11683 return retval;
11684 }
11685
11686 /* [lreverse] */
11687 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11688 Jim_Obj *const *argv)
11689 {
11690 Jim_Obj *revObjPtr, **ele;
11691 int len;
11692
11693 if (argc != 2) {
11694 Jim_WrongNumArgs(interp, 1, argv, "list");
11695 return JIM_ERR;
11696 }
11697 JimListGetElements(interp, argv[1], &len, &ele);
11698 len--;
11699 revObjPtr = Jim_NewListObj(interp, NULL, 0);
11700 while (len >= 0)
11701 ListAppendElement(revObjPtr, ele[len--]);
11702 Jim_SetResult(interp, revObjPtr);
11703 return JIM_OK;
11704 }
11705
11706 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11707 {
11708 jim_wide len;
11709
11710 if (step == 0) return -1;
11711 if (start == end) return 0;
11712 else if (step > 0 && start > end) return -1;
11713 else if (step < 0 && end > start) return -1;
11714 len = end-start;
11715 if (len < 0) len = -len; /* abs(len) */
11716 if (step < 0) step = -step; /* abs(step) */
11717 len = 1 + ((len-1)/step);
11718 /* We can truncate safely to INT_MAX, the range command
11719 * will always return an error for a such long range
11720 * because Tcl lists can't be so long. */
11721 if (len > INT_MAX) len = INT_MAX;
11722 return (int)((len < 0) ? -1 : len);
11723 }
11724
11725 /* [range] */
11726 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11727 Jim_Obj *const *argv)
11728 {
11729 jim_wide start = 0, end, step = 1;
11730 int len, i;
11731 Jim_Obj *objPtr;
11732
11733 if (argc < 2 || argc > 4) {
11734 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11735 return JIM_ERR;
11736 }
11737 if (argc == 2) {
11738 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11739 return JIM_ERR;
11740 } else {
11741 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11742 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11743 return JIM_ERR;
11744 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11745 return JIM_ERR;
11746 }
11747 if ((len = JimRangeLen(start, end, step)) == -1) {
11748 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11749 return JIM_ERR;
11750 }
11751 objPtr = Jim_NewListObj(interp, NULL, 0);
11752 for (i = 0; i < len; i++)
11753 ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11754 Jim_SetResult(interp, objPtr);
11755 return JIM_OK;
11756 }
11757
11758 /* [rand] */
11759 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11760 Jim_Obj *const *argv)
11761 {
11762 jim_wide min = 0, max, len, maxMul;
11763
11764 if (argc < 1 || argc > 3) {
11765 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11766 return JIM_ERR;
11767 }
11768 if (argc == 1) {
11769 max = JIM_WIDE_MAX;
11770 } else if (argc == 2) {
11771 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11772 return JIM_ERR;
11773 } else if (argc == 3) {
11774 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11775 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11776 return JIM_ERR;
11777 }
11778 len = max-min;
11779 if (len < 0) {
11780 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11781 return JIM_ERR;
11782 }
11783 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11784 while (1) {
11785 jim_wide r;
11786
11787 JimRandomBytes(interp, &r, sizeof(jim_wide));
11788 if (r < 0 || r >= maxMul) continue;
11789 r = (len == 0) ? 0 : r%len;
11790 Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11791 return JIM_OK;
11792 }
11793 }
11794
11795 /* [package] */
11796 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
11797 Jim_Obj *const *argv)
11798 {
11799 int option;
11800 const char *options[] = {
11801 "require", "provide", NULL
11802 };
11803 enum {OPT_REQUIRE, OPT_PROVIDE};
11804
11805 if (argc < 2) {
11806 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11807 return JIM_ERR;
11808 }
11809 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11810 JIM_ERRMSG) != JIM_OK)
11811 return JIM_ERR;
11812
11813 if (option == OPT_REQUIRE) {
11814 int exact = 0;
11815 const char *ver;
11816
11817 if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11818 exact = 1;
11819 argv++;
11820 argc--;
11821 }
11822 if (argc != 3 && argc != 4) {
11823 Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11824 return JIM_ERR;
11825 }
11826 ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11827 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11828 JIM_ERRMSG);
11829 if (ver == NULL)
11830 return JIM_ERR;
11831 Jim_SetResultString(interp, ver, -1);
11832 } else if (option == OPT_PROVIDE) {
11833 if (argc != 4) {
11834 Jim_WrongNumArgs(interp, 2, argv, "package version");
11835 return JIM_ERR;
11836 }
11837 return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11838 Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11839 }
11840 return JIM_OK;
11841 }
11842
11843 static struct {
11844 const char *name;
11845 Jim_CmdProc cmdProc;
11846 } Jim_CoreCommandsTable[] = {
11847 {"set", Jim_SetCoreCommand},
11848 {"unset", Jim_UnsetCoreCommand},
11849 {"puts", Jim_PutsCoreCommand},
11850 {"+", Jim_AddCoreCommand},
11851 {"*", Jim_MulCoreCommand},
11852 {"-", Jim_SubCoreCommand},
11853 {"/", Jim_DivCoreCommand},
11854 {"incr", Jim_IncrCoreCommand},
11855 {"while", Jim_WhileCoreCommand},
11856 {"for", Jim_ForCoreCommand},
11857 {"foreach", Jim_ForeachCoreCommand},
11858 {"lmap", Jim_LmapCoreCommand},
11859 {"if", Jim_IfCoreCommand},
11860 {"switch", Jim_SwitchCoreCommand},
11861 {"list", Jim_ListCoreCommand},
11862 {"lindex", Jim_LindexCoreCommand},
11863 {"lset", Jim_LsetCoreCommand},
11864 {"llength", Jim_LlengthCoreCommand},
11865 {"lappend", Jim_LappendCoreCommand},
11866 {"linsert", Jim_LinsertCoreCommand},
11867 {"lsort", Jim_LsortCoreCommand},
11868 {"append", Jim_AppendCoreCommand},
11869 {"debug", Jim_DebugCoreCommand},
11870 {"eval", Jim_EvalCoreCommand},
11871 {"uplevel", Jim_UplevelCoreCommand},
11872 {"expr", Jim_ExprCoreCommand},
11873 {"break", Jim_BreakCoreCommand},
11874 {"continue", Jim_ContinueCoreCommand},
11875 {"proc", Jim_ProcCoreCommand},
11876 {"concat", Jim_ConcatCoreCommand},
11877 {"return", Jim_ReturnCoreCommand},
11878 {"upvar", Jim_UpvarCoreCommand},
11879 {"global", Jim_GlobalCoreCommand},
11880 {"string", Jim_StringCoreCommand},
11881 {"time", Jim_TimeCoreCommand},
11882 {"exit", Jim_ExitCoreCommand},
11883 {"catch", Jim_CatchCoreCommand},
11884 {"ref", Jim_RefCoreCommand},
11885 {"getref", Jim_GetrefCoreCommand},
11886 {"setref", Jim_SetrefCoreCommand},
11887 {"finalize", Jim_FinalizeCoreCommand},
11888 {"collect", Jim_CollectCoreCommand},
11889 {"rename", Jim_RenameCoreCommand},
11890 {"dict", Jim_DictCoreCommand},
11891 {"load", Jim_LoadCoreCommand},
11892 {"subst", Jim_SubstCoreCommand},
11893 {"info", Jim_InfoCoreCommand},
11894 {"split", Jim_SplitCoreCommand},
11895 {"join", Jim_JoinCoreCommand},
11896 {"format", Jim_FormatCoreCommand},
11897 {"scan", Jim_ScanCoreCommand},
11898 {"error", Jim_ErrorCoreCommand},
11899 {"lrange", Jim_LrangeCoreCommand},
11900 {"env", Jim_EnvCoreCommand},
11901 {"source", Jim_SourceCoreCommand},
11902 {"lreverse", Jim_LreverseCoreCommand},
11903 {"range", Jim_RangeCoreCommand},
11904 {"rand", Jim_RandCoreCommand},
11905 {"package", Jim_PackageCoreCommand},
11906 {"tailcall", Jim_TailcallCoreCommand},
11907 {NULL, NULL},
11908 };
11909
11910 /* Some Jim core command is actually a procedure written in Jim itself. */
11911 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11912 {
11913 Jim_Eval(interp, (char*)
11914 "proc lambda {arglist args} {\n"
11915 " set name [ref {} function lambdaFinalizer]\n"
11916 " uplevel 1 [list proc $name $arglist {expand}$args]\n"
11917 " return $name\n"
11918 "}\n"
11919 "proc lambdaFinalizer {name val} {\n"
11920 " rename $name {}\n"
11921 "}\n"
11922 );
11923 }
11924
11925 void Jim_RegisterCoreCommands(Jim_Interp *interp)
11926 {
11927 int i = 0;
11928
11929 while(Jim_CoreCommandsTable[i].name != NULL) {
11930 Jim_CreateCommand(interp,
11931 Jim_CoreCommandsTable[i].name,
11932 Jim_CoreCommandsTable[i].cmdProc,
11933 NULL, NULL);
11934 i++;
11935 }
11936 Jim_RegisterCoreProcedures(interp);
11937 }
11938
11939 /* -----------------------------------------------------------------------------
11940 * Interactive prompt
11941 * ---------------------------------------------------------------------------*/
11942 void Jim_PrintErrorMessage(Jim_Interp *interp)
11943 {
11944 int len, i;
11945
11946 Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL,
11947 interp->errorFileName, interp->errorLine);
11948 Jim_fprintf(interp,interp->cookie_stderr, " %s" JIM_NL,
11949 Jim_GetString(interp->result, NULL));
11950 Jim_ListLength(interp, interp->stackTrace, &len);
11951 for (i = 0; i < len; i+= 3) {
11952 Jim_Obj *objPtr;
11953 const char *proc, *file, *line;
11954
11955 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
11956 proc = Jim_GetString(objPtr, NULL);
11957 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
11958 JIM_NONE);
11959 file = Jim_GetString(objPtr, NULL);
11960 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
11961 JIM_NONE);
11962 line = Jim_GetString(objPtr, NULL);
11963 Jim_fprintf( interp, interp->cookie_stderr,
11964 "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
11965 proc, file, line);
11966 }
11967 }
11968
11969 int Jim_InteractivePrompt(Jim_Interp *interp)
11970 {
11971 int retcode = JIM_OK;
11972 Jim_Obj *scriptObjPtr;
11973
11974 Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
11975 "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
11976 JIM_VERSION / 100, JIM_VERSION % 100);
11977 Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
11978 while (1) {
11979 char buf[1024];
11980 const char *result;
11981 const char *retcodestr[] = {
11982 "ok", "error", "return", "break", "continue", "eval", "exit"
11983 };
11984 int reslen;
11985
11986 if (retcode != 0) {
11987 if (retcode >= 2 && retcode <= 6)
11988 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
11989 else
11990 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
11991 } else
11992 Jim_fprintf( interp, interp->cookie_stdout, ". ");
11993 Jim_fflush( interp, interp->cookie_stdout);
11994 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
11995 Jim_IncrRefCount(scriptObjPtr);
11996 while(1) {
11997 const char *str;
11998 char state;
11999 int len;
12000
12001 if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
12002 Jim_DecrRefCount(interp, scriptObjPtr);
12003 goto out;
12004 }
12005 Jim_AppendString(interp, scriptObjPtr, buf, -1);
12006 str = Jim_GetString(scriptObjPtr, &len);
12007 if (Jim_ScriptIsComplete(str, len, &state))
12008 break;
12009 Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12010 Jim_fflush( interp, interp->cookie_stdout);
12011 }
12012 retcode = Jim_EvalObj(interp, scriptObjPtr);
12013 Jim_DecrRefCount(interp, scriptObjPtr);
12014 result = Jim_GetString(Jim_GetResult(interp), &reslen);
12015 if (retcode == JIM_ERR) {
12016 Jim_PrintErrorMessage(interp);
12017 } else if (retcode == JIM_EXIT) {
12018 exit(Jim_GetExitCode(interp));
12019 } else {
12020 if (reslen) {
12021 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12022 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12023 }
12024 }
12025 }
12026 out:
12027 return 0;
12028 }
12029
12030 /* -----------------------------------------------------------------------------
12031 * Jim's idea of STDIO..
12032 * ---------------------------------------------------------------------------*/
12033
12034 int Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12035 {
12036 int r;
12037
12038 va_list ap;
12039 va_start(ap,fmt);
12040 r = Jim_vfprintf( interp, cookie, fmt,ap );
12041 va_end(ap);
12042 return r;
12043 }
12044
12045 int Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12046 {
12047 if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12048 errno = ENOTSUP;
12049 return -1;
12050 }
12051 return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12052 }
12053
12054 size_t Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12055 {
12056 if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12057 errno = ENOTSUP;
12058 return 0;
12059 }
12060 return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12061 }
12062
12063 size_t Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12064 {
12065 if( (interp == NULL) || (interp->cb_fread == NULL) ){
12066 errno = ENOTSUP;
12067 return 0;
12068 }
12069 return (*(interp->cb_fread))( ptr, size, n, cookie);
12070 }
12071
12072 int Jim_fflush( Jim_Interp *interp, void *cookie )
12073 {
12074 if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12075 /* pretend all is well */
12076 return 0;
12077 }
12078 return (*(interp->cb_fflush))( cookie );
12079 }
12080
12081 char* Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12082 {
12083 if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12084 errno = ENOTSUP;
12085 return NULL;
12086 }
12087 return (*(interp->cb_fgets))( s, size, cookie );
12088 }

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)