c1434dbb0bf7dcbd87c61b1113db9a69d9dac23d
[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 int prec;
2184 int inprec;
2185 int haveprec;
2186 int accum;
2187
2188 while (*fmt != '%' && fmtLen) {
2189 fmt++; fmtLen--;
2190 }
2191 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2192 if (fmtLen == 0)
2193 break;
2194 fmt++; fmtLen--; /* skip '%' */
2195 zpad = 0;
2196 spad = 0;
2197 width = -1;
2198 ljust = 0;
2199 altfm = 0;
2200 forceplus = 0;
2201 inprec = 0;
2202 haveprec = 0;
2203 prec = -1; /* not found yet */
2204 next_fmt:
2205 if( fmtLen <= 0 ){
2206 break;
2207 }
2208 switch( *fmt ){
2209 /* terminals */
2210 case 'b': /* binary - not all printfs() do this */
2211 case 's': /* string */
2212 case 'i': /* integer */
2213 case 'd': /* decimal */
2214 case 'x': /* hex */
2215 case 'X': /* CAP hex */
2216 case 'c': /* char */
2217 case 'o': /* octal */
2218 case 'u': /* unsigned */
2219 case 'f': /* float */
2220 break;
2221
2222 /* non-terminals */
2223 case '0': /* zero pad */
2224 zpad = 1;
2225 *fmt++; fmtLen--;
2226 goto next_fmt;
2227 break;
2228 case '+':
2229 forceplus = 1;
2230 *fmt++; fmtLen--;
2231 goto next_fmt;
2232 break;
2233 case ' ': /* sign space */
2234 spad = 1;
2235 *fmt++; fmtLen--;
2236 goto next_fmt;
2237 break;
2238 case '-':
2239 ljust = 1;
2240 *fmt++; fmtLen--;
2241 goto next_fmt;
2242 break;
2243 case '#':
2244 altfm = 1;
2245 *fmt++; fmtLen--;
2246 goto next_fmt;
2247
2248 case '.':
2249 inprec = 1;
2250 *fmt++; fmtLen--;
2251 goto next_fmt;
2252 break;
2253 case '1':
2254 case '2':
2255 case '3':
2256 case '4':
2257 case '5':
2258 case '6':
2259 case '7':
2260 case '8':
2261 case '9':
2262 accum = 0;
2263 while( isdigit(*fmt) && (fmtLen > 0) ){
2264 accum = (accum * 10) + (*fmt - '0');
2265 fmt++; fmtLen--;
2266 }
2267 if( inprec ){
2268 haveprec = 1;
2269 prec = accum;
2270 } else {
2271 width = accum;
2272 }
2273 goto next_fmt;
2274 case '*':
2275 /* suck up the next item as an integer */
2276 *fmt++; fmtLen--;
2277 objc--;
2278 if( objc <= 0 ){
2279 goto not_enough_args;
2280 }
2281 if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2282 Jim_FreeNewObj(interp, resObjPtr );
2283 return NULL;
2284 }
2285 if( inprec ){
2286 haveprec = 1;
2287 prec = wideValue;
2288 if( prec < 0 ){
2289 /* man 3 printf says */
2290 /* if prec is negative, it is zero */
2291 prec = 0;
2292 }
2293 } else {
2294 width = wideValue;
2295 if( width < 0 ){
2296 ljust = 1;
2297 width = -width;
2298 }
2299 }
2300 objv++;
2301 goto next_fmt;
2302 break;
2303 }
2304
2305
2306 if (*fmt != '%') {
2307 if (objc == 0) {
2308 not_enough_args:
2309 Jim_FreeNewObj(interp, resObjPtr);
2310 Jim_SetResultString(interp,
2311 "not enough arguments for all format specifiers", -1);
2312 return NULL;
2313 } else {
2314 objc--;
2315 }
2316 }
2317
2318 /*
2319 * Create the formatter
2320 * cause we cheat and use sprintf()
2321 */
2322 cp = fmt_str;
2323 *cp++ = '%';
2324 if( altfm ){
2325 *cp++ = '#';
2326 }
2327 if( forceplus ){
2328 *cp++ = '+';
2329 } else if( spad ){
2330 /* PLUS overrides */
2331 *cp++ = ' ';
2332 }
2333 if( ljust ){
2334 *cp++ = '-';
2335 }
2336 if( zpad ){
2337 *cp++ = '0';
2338 }
2339 if( width > 0 ){
2340 sprintf( cp, "%d", width );
2341 /* skip ahead */
2342 cp = strchr(cp,0);
2343 }
2344 /* did we find a period? */
2345 if( inprec ){
2346 /* then add it */
2347 *cp++ = '.';
2348 /* did something occur after the period? */
2349 if( haveprec ){
2350 sprintf( cp, "%d", prec );
2351 }
2352 cp = strchr(cp,0);
2353 }
2354 *cp = 0;
2355
2356 /* here we do the work */
2357 /* actually - we make sprintf() do it for us */
2358 switch(*fmt) {
2359 case 's':
2360 *cp++ = 's';
2361 *cp = 0;
2362 /* BUG: we do not handled embeded NULLs */
2363 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2364 break;
2365 case 'c':
2366 *cp++ = 'c';
2367 *cp = 0;
2368 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2369 Jim_FreeNewObj(interp, resObjPtr);
2370 return NULL;
2371 }
2372 c = (char) wideValue;
2373 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2374 break;
2375 case 'f':
2376 case 'F':
2377 case 'g':
2378 case 'G':
2379 case 'e':
2380 case 'E':
2381 *cp++ = *fmt;
2382 *cp = 0;
2383 if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2384 Jim_FreeNewObj( interp, resObjPtr );
2385 return NULL;
2386 }
2387 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2388 break;
2389 case 'b':
2390 case 'd':
2391 case 'i':
2392 case 'u':
2393 case 'x':
2394 case 'X':
2395 /* jim widevaluse are 64bit */
2396 if( sizeof(jim_wide) == sizeof(long long) ){
2397 *cp++ = 'l';
2398 *cp++ = 'l';
2399 } else {
2400 *cp++ = 'l';
2401 }
2402 *cp++ = *fmt;
2403 *cp = 0;
2404 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2405 Jim_FreeNewObj(interp, resObjPtr);
2406 return NULL;
2407 }
2408 snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2409 break;
2410 case '%':
2411 sprintf_buf[0] = '%';
2412 sprintf_buf[1] = 0;
2413 objv--; /* undo the objv++ below */
2414 break;
2415 default:
2416 spec[0] = *fmt; spec[1] = '\0';
2417 Jim_FreeNewObj(interp, resObjPtr);
2418 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2419 Jim_AppendStrings(interp, Jim_GetResult(interp),
2420 "bad field specifier \"", spec, "\"", NULL);
2421 return NULL;
2422 }
2423 /* force terminate */
2424 #if 0
2425 printf("FMT was: %s\n", fmt_str );
2426 printf("RES was: |%s|\n", sprintf_buf );
2427 #endif
2428
2429 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2430 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2431 /* next obj */
2432 objv++;
2433 fmt++;
2434 fmtLen--;
2435 }
2436 return resObjPtr;
2437 }
2438
2439 /* -----------------------------------------------------------------------------
2440 * Compared String Object
2441 * ---------------------------------------------------------------------------*/
2442
2443 /* This is strange object that allows to compare a C literal string
2444 * with a Jim object in very short time if the same comparison is done
2445 * multiple times. For example every time the [if] command is executed,
2446 * Jim has to check if a given argument is "else". This comparions if
2447 * the code has no errors are true most of the times, so we can cache
2448 * inside the object the pointer of the string of the last matching
2449 * comparison. Because most C compilers perform literal sharing,
2450 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2451 * this works pretty well even if comparisons are at different places
2452 * inside the C code. */
2453
2454 static Jim_ObjType comparedStringObjType = {
2455 "compared-string",
2456 NULL,
2457 NULL,
2458 NULL,
2459 JIM_TYPE_REFERENCES,
2460 };
2461
2462 /* The only way this object is exposed to the API is via the following
2463 * function. Returns true if the string and the object string repr.
2464 * are the same, otherwise zero is returned.
2465 *
2466 * Note: this isn't binary safe, but it hardly needs to be.*/
2467 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2468 const char *str)
2469 {
2470 if (objPtr->typePtr == &comparedStringObjType &&
2471 objPtr->internalRep.ptr == str)
2472 return 1;
2473 else {
2474 const char *objStr = Jim_GetString(objPtr, NULL);
2475 if (strcmp(str, objStr) != 0) return 0;
2476 if (objPtr->typePtr != &comparedStringObjType) {
2477 Jim_FreeIntRep(interp, objPtr);
2478 objPtr->typePtr = &comparedStringObjType;
2479 }
2480 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2481 return 1;
2482 }
2483 }
2484
2485 int qsortCompareStringPointers(const void *a, const void *b)
2486 {
2487 char * const *sa = (char * const *)a;
2488 char * const *sb = (char * const *)b;
2489 return strcmp(*sa, *sb);
2490 }
2491
2492 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2493 const char **tablePtr, int *indexPtr, const char *name, int flags)
2494 {
2495 const char **entryPtr = NULL;
2496 char **tablePtrSorted;
2497 int i, count = 0;
2498
2499 *indexPtr = -1;
2500 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2501 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2502 *indexPtr = i;
2503 return JIM_OK;
2504 }
2505 count++; /* If nothing matches, this will reach the len of tablePtr */
2506 }
2507 if (flags & JIM_ERRMSG) {
2508 if (name == NULL)
2509 name = "option";
2510 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2511 Jim_AppendStrings(interp, Jim_GetResult(interp),
2512 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2513 NULL);
2514 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2515 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2516 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2517 for (i = 0; i < count; i++) {
2518 if (i+1 == count && count > 1)
2519 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2520 Jim_AppendString(interp, Jim_GetResult(interp),
2521 tablePtrSorted[i], -1);
2522 if (i+1 != count)
2523 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2524 }
2525 Jim_Free(tablePtrSorted);
2526 }
2527 return JIM_ERR;
2528 }
2529
2530 /* -----------------------------------------------------------------------------
2531 * Source Object
2532 *
2533 * This object is just a string from the language point of view, but
2534 * in the internal representation it contains the filename and line number
2535 * where this given token was read. This information is used by
2536 * Jim_EvalObj() if the object passed happens to be of type "source".
2537 *
2538 * This allows to propagate the information about line numbers and file
2539 * names and give error messages with absolute line numbers.
2540 *
2541 * Note that this object uses shared strings for filenames, and the
2542 * pointer to the filename together with the line number is taken into
2543 * the space for the "inline" internal represenation of the Jim_Object,
2544 * so there is almost memory zero-overhead.
2545 *
2546 * Also the object will be converted to something else if the given
2547 * token it represents in the source file is not something to be
2548 * evaluated (not a script), and will be specialized in some other way,
2549 * so the time overhead is alzo null.
2550 * ---------------------------------------------------------------------------*/
2551
2552 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2553 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2554
2555 static Jim_ObjType sourceObjType = {
2556 "source",
2557 FreeSourceInternalRep,
2558 DupSourceInternalRep,
2559 NULL,
2560 JIM_TYPE_REFERENCES,
2561 };
2562
2563 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2564 {
2565 Jim_ReleaseSharedString(interp,
2566 objPtr->internalRep.sourceValue.fileName);
2567 }
2568
2569 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2570 {
2571 dupPtr->internalRep.sourceValue.fileName =
2572 Jim_GetSharedString(interp,
2573 srcPtr->internalRep.sourceValue.fileName);
2574 dupPtr->internalRep.sourceValue.lineNumber =
2575 dupPtr->internalRep.sourceValue.lineNumber;
2576 dupPtr->typePtr = &sourceObjType;
2577 }
2578
2579 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2580 const char *fileName, int lineNumber)
2581 {
2582 if (Jim_IsShared(objPtr))
2583 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2584 if (objPtr->typePtr != NULL)
2585 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2586 objPtr->internalRep.sourceValue.fileName =
2587 Jim_GetSharedString(interp, fileName);
2588 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2589 objPtr->typePtr = &sourceObjType;
2590 }
2591
2592 /* -----------------------------------------------------------------------------
2593 * Script Object
2594 * ---------------------------------------------------------------------------*/
2595
2596 #define JIM_CMDSTRUCT_EXPAND -1
2597
2598 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2599 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2600 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2601
2602 static Jim_ObjType scriptObjType = {
2603 "script",
2604 FreeScriptInternalRep,
2605 DupScriptInternalRep,
2606 NULL,
2607 JIM_TYPE_REFERENCES,
2608 };
2609
2610 /* The ScriptToken structure represents every token into a scriptObj.
2611 * Every token contains an associated Jim_Obj that can be specialized
2612 * by commands operating on it. */
2613 typedef struct ScriptToken {
2614 int type;
2615 Jim_Obj *objPtr;
2616 int linenr;
2617 } ScriptToken;
2618
2619 /* This is the script object internal representation. An array of
2620 * ScriptToken structures, with an associated command structure array.
2621 * The command structure is a pre-computed representation of the
2622 * command length and arguments structure as a simple liner array
2623 * of integers.
2624 *
2625 * For example the script:
2626 *
2627 * puts hello
2628 * set $i $x$y [foo]BAR
2629 *
2630 * will produce a ScriptObj with the following Tokens:
2631 *
2632 * ESC puts
2633 * SEP
2634 * ESC hello
2635 * EOL
2636 * ESC set
2637 * EOL
2638 * VAR i
2639 * SEP
2640 * VAR x
2641 * VAR y
2642 * SEP
2643 * CMD foo
2644 * ESC BAR
2645 * EOL
2646 *
2647 * This is a description of the tokens, separators, and of lines.
2648 * The command structure instead represents the number of arguments
2649 * of every command, followed by the tokens of which every argument
2650 * is composed. So for the example script, the cmdstruct array will
2651 * contain:
2652 *
2653 * 2 1 1 4 1 1 2 2
2654 *
2655 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2656 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2657 * composed of single tokens (1 1) and the last two of double tokens
2658 * (2 2).
2659 *
2660 * The precomputation of the command structure makes Jim_Eval() faster,
2661 * and simpler because there aren't dynamic lengths / allocations.
2662 *
2663 * -- {expand} handling --
2664 *
2665 * Expand is handled in a special way. When a command
2666 * contains at least an argument with the {expand} prefix,
2667 * the command structure presents a -1 before the integer
2668 * describing the number of arguments. This is used in order
2669 * to send the command exection to a different path in case
2670 * of {expand} and guarantee a fast path for the more common
2671 * case. Also, the integers describing the number of tokens
2672 * are expressed with negative sign, to allow for fast check
2673 * of what's an {expand}-prefixed argument and what not.
2674 *
2675 * For example the command:
2676 *
2677 * list {expand}{1 2}
2678 *
2679 * Will produce the following cmdstruct array:
2680 *
2681 * -1 2 1 -2
2682 *
2683 * -- the substFlags field of the structure --
2684 *
2685 * The scriptObj structure is used to represent both "script" objects
2686 * and "subst" objects. In the second case, the cmdStruct related
2687 * fields are not used at all, but there is an additional field used
2688 * that is 'substFlags': this represents the flags used to turn
2689 * the string into the intenral representation used to perform the
2690 * substitution. If this flags are not what the application requires
2691 * the scriptObj is created again. For example the script:
2692 *
2693 * subst -nocommands $string
2694 * subst -novariables $string
2695 *
2696 * Will recreate the internal representation of the $string object
2697 * two times.
2698 */
2699 typedef struct ScriptObj {
2700 int len; /* Length as number of tokens. */
2701 int commands; /* number of top-level commands in script. */
2702 ScriptToken *token; /* Tokens array. */
2703 int *cmdStruct; /* commands structure */
2704 int csLen; /* length of the cmdStruct array. */
2705 int substFlags; /* flags used for the compilation of "subst" objects */
2706 int inUse; /* Used to share a ScriptObj. Currently
2707 only used by Jim_EvalObj() as protection against
2708 shimmering of the currently evaluated object. */
2709 char *fileName;
2710 } ScriptObj;
2711
2712 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2713 {
2714 int i;
2715 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2716
2717 script->inUse--;
2718 if (script->inUse != 0) return;
2719 for (i = 0; i < script->len; i++) {
2720 if (script->token[i].objPtr != NULL)
2721 Jim_DecrRefCount(interp, script->token[i].objPtr);
2722 }
2723 Jim_Free(script->token);
2724 Jim_Free(script->cmdStruct);
2725 Jim_Free(script->fileName);
2726 Jim_Free(script);
2727 }
2728
2729 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2730 {
2731 JIM_NOTUSED(interp);
2732 JIM_NOTUSED(srcPtr);
2733
2734 /* Just returns an simple string. */
2735 dupPtr->typePtr = NULL;
2736 }
2737
2738 /* Add a new token to the internal repr of a script object */
2739 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2740 char *strtoken, int len, int type, char *filename, int linenr)
2741 {
2742 int prevtype;
2743 struct ScriptToken *token;
2744
2745 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2746 script->token[script->len-1].type;
2747 /* Skip tokens without meaning, like words separators
2748 * following a word separator or an end of command and
2749 * so on. */
2750 if (prevtype == JIM_TT_EOL) {
2751 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2752 Jim_Free(strtoken);
2753 return;
2754 }
2755 } else if (prevtype == JIM_TT_SEP) {
2756 if (type == JIM_TT_SEP) {
2757 Jim_Free(strtoken);
2758 return;
2759 } else if (type == JIM_TT_EOL) {
2760 /* If an EOL is following by a SEP, drop the previous
2761 * separator. */
2762 script->len--;
2763 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2764 }
2765 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2766 type == JIM_TT_ESC && len == 0)
2767 {
2768 /* Don't add empty tokens used in interpolation */
2769 Jim_Free(strtoken);
2770 return;
2771 }
2772 /* Make space for a new istruction */
2773 script->len++;
2774 script->token = Jim_Realloc(script->token,
2775 sizeof(ScriptToken)*script->len);
2776 /* Initialize the new token */
2777 token = script->token+(script->len-1);
2778 token->type = type;
2779 /* Every object is intially as a string, but the
2780 * internal type may be specialized during execution of the
2781 * script. */
2782 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2783 /* To add source info to SEP and EOL tokens is useless because
2784 * they will never by called as arguments of Jim_EvalObj(). */
2785 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2786 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2787 Jim_IncrRefCount(token->objPtr);
2788 token->linenr = linenr;
2789 }
2790
2791 /* Add an integer into the command structure field of the script object. */
2792 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2793 {
2794 script->csLen++;
2795 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2796 sizeof(int)*script->csLen);
2797 script->cmdStruct[script->csLen-1] = val;
2798 }
2799
2800 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2801 * of objPtr. Search nested script objects recursively. */
2802 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2803 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2804 {
2805 int i;
2806
2807 for (i = 0; i < script->len; i++) {
2808 if (script->token[i].objPtr != objPtr &&
2809 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2810 return script->token[i].objPtr;
2811 }
2812 /* Enter recursively on scripts only if the object
2813 * is not the same as the one we are searching for
2814 * shared occurrences. */
2815 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2816 script->token[i].objPtr != objPtr) {
2817 Jim_Obj *foundObjPtr;
2818
2819 ScriptObj *subScript =
2820 script->token[i].objPtr->internalRep.ptr;
2821 /* Don't recursively enter the script we are trying
2822 * to make shared to avoid circular references. */
2823 if (subScript == scriptBarrier) continue;
2824 if (subScript != script) {
2825 foundObjPtr =
2826 ScriptSearchLiteral(interp, subScript,
2827 scriptBarrier, objPtr);
2828 if (foundObjPtr != NULL)
2829 return foundObjPtr;
2830 }
2831 }
2832 }
2833 return NULL;
2834 }
2835
2836 /* Share literals of a script recursively sharing sub-scripts literals. */
2837 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2838 ScriptObj *topLevelScript)
2839 {
2840 int i, j;
2841
2842 return;
2843 /* Try to share with toplevel object. */
2844 if (topLevelScript != NULL) {
2845 for (i = 0; i < script->len; i++) {
2846 Jim_Obj *foundObjPtr;
2847 char *str = script->token[i].objPtr->bytes;
2848
2849 if (script->token[i].objPtr->refCount != 1) continue;
2850 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2851 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2852 foundObjPtr = ScriptSearchLiteral(interp,
2853 topLevelScript,
2854 script, /* barrier */
2855 script->token[i].objPtr);
2856 if (foundObjPtr != NULL) {
2857 Jim_IncrRefCount(foundObjPtr);
2858 Jim_DecrRefCount(interp,
2859 script->token[i].objPtr);
2860 script->token[i].objPtr = foundObjPtr;
2861 }
2862 }
2863 }
2864 /* Try to share locally */
2865 for (i = 0; i < script->len; i++) {
2866 char *str = script->token[i].objPtr->bytes;
2867
2868 if (script->token[i].objPtr->refCount != 1) continue;
2869 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2870 for (j = 0; j < script->len; j++) {
2871 if (script->token[i].objPtr !=
2872 script->token[j].objPtr &&
2873 Jim_StringEqObj(script->token[i].objPtr,
2874 script->token[j].objPtr, 0))
2875 {
2876 Jim_IncrRefCount(script->token[j].objPtr);
2877 Jim_DecrRefCount(interp,
2878 script->token[i].objPtr);
2879 script->token[i].objPtr =
2880 script->token[j].objPtr;
2881 }
2882 }
2883 }
2884 }
2885
2886 /* This method takes the string representation of an object
2887 * as a Tcl script, and generates the pre-parsed internal representation
2888 * of the script. */
2889 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2890 {
2891 int scriptTextLen;
2892 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2893 struct JimParserCtx parser;
2894 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2895 ScriptToken *token;
2896 int args, tokens, start, end, i;
2897 int initialLineNumber;
2898 int propagateSourceInfo = 0;
2899
2900 script->len = 0;
2901 script->csLen = 0;
2902 script->commands = 0;
2903 script->token = NULL;
2904 script->cmdStruct = NULL;
2905 script->inUse = 1;
2906 /* Try to get information about filename / line number */
2907 if (objPtr->typePtr == &sourceObjType) {
2908 script->fileName =
2909 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2910 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2911 propagateSourceInfo = 1;
2912 } else {
2913 script->fileName = Jim_StrDup("?");
2914 initialLineNumber = 1;
2915 }
2916
2917 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2918 while(!JimParserEof(&parser)) {
2919 char *token;
2920 int len, type, linenr;
2921
2922 JimParseScript(&parser);
2923 token = JimParserGetToken(&parser, &len, &type, &linenr);
2924 ScriptObjAddToken(interp, script, token, len, type,
2925 propagateSourceInfo ? script->fileName : NULL,
2926 linenr);
2927 }
2928 token = script->token;
2929
2930 /* Compute the command structure array
2931 * (see the ScriptObj struct definition for more info) */
2932 start = 0; /* Current command start token index */
2933 end = -1; /* Current command end token index */
2934 while (1) {
2935 int expand = 0; /* expand flag. set to 1 on {expand} form. */
2936 int interpolation = 0; /* set to 1 if there is at least one
2937 argument of the command obtained via
2938 interpolation of more tokens. */
2939 /* Search for the end of command, while
2940 * count the number of args. */
2941 start = ++end;
2942 if (start >= script->len) break;
2943 args = 1; /* Number of args in current command */
2944 while (token[end].type != JIM_TT_EOL) {
2945 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2946 token[end-1].type == JIM_TT_EOL)
2947 {
2948 if (token[end].type == JIM_TT_STR &&
2949 token[end+1].type != JIM_TT_SEP &&
2950 token[end+1].type != JIM_TT_EOL &&
2951 (!strcmp(token[end].objPtr->bytes, "expand") ||
2952 !strcmp(token[end].objPtr->bytes, "*")))
2953 expand++;
2954 }
2955 if (token[end].type == JIM_TT_SEP)
2956 args++;
2957 end++;
2958 }
2959 interpolation = !((end-start+1) == args*2);
2960 /* Add the 'number of arguments' info into cmdstruct.
2961 * Negative value if there is list expansion involved. */
2962 if (expand)
2963 ScriptObjAddInt(script, -1);
2964 ScriptObjAddInt(script, args);
2965 /* Now add info about the number of tokens. */
2966 tokens = 0; /* Number of tokens in current argument. */
2967 expand = 0;
2968 for (i = start; i <= end; i++) {
2969 if (token[i].type == JIM_TT_SEP ||
2970 token[i].type == JIM_TT_EOL)
2971 {
2972 if (tokens == 1 && expand)
2973 expand = 0;
2974 ScriptObjAddInt(script,
2975 expand ? -tokens : tokens);
2976
2977 expand = 0;
2978 tokens = 0;
2979 continue;
2980 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2981 (!strcmp(token[i].objPtr->bytes, "expand") ||
2982 !strcmp(token[i].objPtr->bytes, "*")))
2983 {
2984 expand++;
2985 }
2986 tokens++;
2987 }
2988 }
2989 /* Perform literal sharing, but only for objects that appear
2990 * to be scripts written as literals inside the source code,
2991 * and not computed at runtime. Literal sharing is a costly
2992 * operation that should be done only against objects that
2993 * are likely to require compilation only the first time, and
2994 * then are executed multiple times. */
2995 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2996 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2997 if (bodyObjPtr->typePtr == &scriptObjType) {
2998 ScriptObj *bodyScript =
2999 bodyObjPtr->internalRep.ptr;
3000 ScriptShareLiterals(interp, script, bodyScript);
3001 }
3002 } else if (propagateSourceInfo) {
3003 ScriptShareLiterals(interp, script, NULL);
3004 }
3005 /* Free the old internal rep and set the new one. */
3006 Jim_FreeIntRep(interp, objPtr);
3007 Jim_SetIntRepPtr(objPtr, script);
3008 objPtr->typePtr = &scriptObjType;
3009 return JIM_OK;
3010 }
3011
3012 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
3013 {
3014 if (objPtr->typePtr != &scriptObjType) {
3015 SetScriptFromAny(interp, objPtr);
3016 }
3017 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
3018 }
3019
3020 /* -----------------------------------------------------------------------------
3021 * Commands
3022 * ---------------------------------------------------------------------------*/
3023
3024 /* Commands HashTable Type.
3025 *
3026 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
3027 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
3028 {
3029 Jim_Cmd *cmdPtr = (void*) val;
3030
3031 if (cmdPtr->cmdProc == NULL) {
3032 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3033 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3034 if (cmdPtr->staticVars) {
3035 Jim_FreeHashTable(cmdPtr->staticVars);
3036 Jim_Free(cmdPtr->staticVars);
3037 }
3038 } else if (cmdPtr->delProc != NULL) {
3039 /* If it was a C coded command, call the delProc if any */
3040 cmdPtr->delProc(interp, cmdPtr->privData);
3041 }
3042 Jim_Free(val);
3043 }
3044
3045 static Jim_HashTableType JimCommandsHashTableType = {
3046 JimStringCopyHTHashFunction, /* hash function */
3047 JimStringCopyHTKeyDup, /* key dup */
3048 NULL, /* val dup */
3049 JimStringCopyHTKeyCompare, /* key compare */
3050 JimStringCopyHTKeyDestructor, /* key destructor */
3051 Jim_CommandsHT_ValDestructor /* val destructor */
3052 };
3053
3054 /* ------------------------- Commands related functions --------------------- */
3055
3056 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3057 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3058 {
3059 Jim_HashEntry *he;
3060 Jim_Cmd *cmdPtr;
3061
3062 he = Jim_FindHashEntry(&interp->commands, cmdName);
3063 if (he == NULL) { /* New command to create */
3064 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3065 cmdPtr->cmdProc = cmdProc;
3066 cmdPtr->privData = privData;
3067 cmdPtr->delProc = delProc;
3068 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3069 } else {
3070 Jim_InterpIncrProcEpoch(interp);
3071 /* Free the arglist/body objects if it was a Tcl procedure */
3072 cmdPtr = he->val;
3073 if (cmdPtr->cmdProc == NULL) {
3074 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3075 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3076 if (cmdPtr->staticVars) {
3077 Jim_FreeHashTable(cmdPtr->staticVars);
3078 Jim_Free(cmdPtr->staticVars);
3079 }
3080 cmdPtr->staticVars = NULL;
3081 } else if (cmdPtr->delProc != NULL) {
3082 /* If it was a C coded command, call the delProc if any */
3083 cmdPtr->delProc(interp, cmdPtr->privData);
3084 }
3085 cmdPtr->cmdProc = cmdProc;
3086 cmdPtr->privData = privData;
3087 }
3088 /* There is no need to increment the 'proc epoch' because
3089 * creation of a new procedure can never affect existing
3090 * cached commands. We don't do negative caching. */
3091 return JIM_OK;
3092 }
3093
3094 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3095 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3096 int arityMin, int arityMax)
3097 {
3098 Jim_Cmd *cmdPtr;
3099
3100 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3101 cmdPtr->cmdProc = NULL; /* Not a C coded command */
3102 cmdPtr->argListObjPtr = argListObjPtr;
3103 cmdPtr->bodyObjPtr = bodyObjPtr;
3104 Jim_IncrRefCount(argListObjPtr);
3105 Jim_IncrRefCount(bodyObjPtr);
3106 cmdPtr->arityMin = arityMin;
3107 cmdPtr->arityMax = arityMax;
3108 cmdPtr->staticVars = NULL;
3109
3110 /* Create the statics hash table. */
3111 if (staticsListObjPtr) {
3112 int len, i;
3113
3114 Jim_ListLength(interp, staticsListObjPtr, &len);
3115 if (len != 0) {
3116 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3117 Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
3118 interp);
3119 for (i = 0; i < len; i++) {
3120 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3121 Jim_Var *varPtr;
3122 int subLen;
3123
3124 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3125 /* Check if it's composed of two elements. */
3126 Jim_ListLength(interp, objPtr, &subLen);
3127 if (subLen == 1 || subLen == 2) {
3128 /* Try to get the variable value from the current
3129 * environment. */
3130 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3131 if (subLen == 1) {
3132 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3133 JIM_NONE);
3134 if (initObjPtr == NULL) {
3135 Jim_SetResult(interp,
3136 Jim_NewEmptyStringObj(interp));
3137 Jim_AppendStrings(interp, Jim_GetResult(interp),
3138 "variable for initialization of static \"",
3139 Jim_GetString(nameObjPtr, NULL),
3140 "\" not found in the local context",
3141 NULL);
3142 goto err;
3143 }
3144 } else {
3145 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3146 }
3147 varPtr = Jim_Alloc(sizeof(*varPtr));
3148 varPtr->objPtr = initObjPtr;
3149 Jim_IncrRefCount(initObjPtr);
3150 varPtr->linkFramePtr = NULL;
3151 if (Jim_AddHashEntry(cmdPtr->staticVars,
3152 Jim_GetString(nameObjPtr, NULL),
3153 varPtr) != JIM_OK)
3154 {
3155 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3156 Jim_AppendStrings(interp, Jim_GetResult(interp),
3157 "static variable name \"",
3158 Jim_GetString(objPtr, NULL), "\"",
3159 " duplicated in statics list", NULL);
3160 Jim_DecrRefCount(interp, initObjPtr);
3161 Jim_Free(varPtr);
3162 goto err;
3163 }
3164 } else {
3165 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3166 Jim_AppendStrings(interp, Jim_GetResult(interp),
3167 "too many fields in static specifier \"",
3168 objPtr, "\"", NULL);
3169 goto err;
3170 }
3171 }
3172 }
3173 }
3174
3175 /* Add the new command */
3176
3177 /* it may already exist, so we try to delete the old one */
3178 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3179 /* There was an old procedure with the same name, this requires
3180 * a 'proc epoch' update. */
3181 Jim_InterpIncrProcEpoch(interp);
3182 }
3183 /* If a procedure with the same name didn't existed there is no need
3184 * to increment the 'proc epoch' because creation of a new procedure
3185 * can never affect existing cached commands. We don't do
3186 * negative caching. */
3187 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3188 return JIM_OK;
3189
3190 err:
3191 Jim_FreeHashTable(cmdPtr->staticVars);
3192 Jim_Free(cmdPtr->staticVars);
3193 Jim_DecrRefCount(interp, argListObjPtr);
3194 Jim_DecrRefCount(interp, bodyObjPtr);
3195 Jim_Free(cmdPtr);
3196 return JIM_ERR;
3197 }
3198
3199 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3200 {
3201 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3202 return JIM_ERR;
3203 Jim_InterpIncrProcEpoch(interp);
3204 return JIM_OK;
3205 }
3206
3207 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
3208 const char *newName)
3209 {
3210 Jim_Cmd *cmdPtr;
3211 Jim_HashEntry *he;
3212 Jim_Cmd *copyCmdPtr;
3213
3214 if (newName[0] == '\0') /* Delete! */
3215 return Jim_DeleteCommand(interp, oldName);
3216 /* Rename */
3217 he = Jim_FindHashEntry(&interp->commands, oldName);
3218 if (he == NULL)
3219 return JIM_ERR; /* Invalid command name */
3220 cmdPtr = he->val;
3221 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3222 *copyCmdPtr = *cmdPtr;
3223 /* In order to avoid that a procedure will get arglist/body/statics
3224 * freed by the hash table methods, fake a C-coded command
3225 * setting cmdPtr->cmdProc as not NULL */
3226 cmdPtr->cmdProc = (void*)1;
3227 /* Also make sure delProc is NULL. */
3228 cmdPtr->delProc = NULL;
3229 /* Destroy the old command, and make sure the new is freed
3230 * as well. */
3231 Jim_DeleteHashEntry(&interp->commands, oldName);
3232 Jim_DeleteHashEntry(&interp->commands, newName);
3233 /* Now the new command. We are sure it can't fail because
3234 * the target name was already freed. */
3235 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3236 /* Increment the epoch */
3237 Jim_InterpIncrProcEpoch(interp);
3238 return JIM_OK;
3239 }
3240
3241 /* -----------------------------------------------------------------------------
3242 * Command object
3243 * ---------------------------------------------------------------------------*/
3244
3245 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3246
3247 static Jim_ObjType commandObjType = {
3248 "command",
3249 NULL,
3250 NULL,
3251 NULL,
3252 JIM_TYPE_REFERENCES,
3253 };
3254
3255 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3256 {
3257 Jim_HashEntry *he;
3258 const char *cmdName;
3259
3260 /* Get the string representation */
3261 cmdName = Jim_GetString(objPtr, NULL);
3262 /* Lookup this name into the commands hash table */
3263 he = Jim_FindHashEntry(&interp->commands, cmdName);
3264 if (he == NULL)
3265 return JIM_ERR;
3266
3267 /* Free the old internal repr and set the new one. */
3268 Jim_FreeIntRep(interp, objPtr);
3269 objPtr->typePtr = &commandObjType;
3270 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3271 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3272 return JIM_OK;
3273 }
3274
3275 /* This function returns the command structure for the command name
3276 * stored in objPtr. It tries to specialize the objPtr to contain
3277 * a cached info instead to perform the lookup into the hash table
3278 * every time. The information cached may not be uptodate, in such
3279 * a case the lookup is performed and the cache updated. */
3280 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3281 {
3282 if ((objPtr->typePtr != &commandObjType ||
3283 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3284 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3285 if (flags & JIM_ERRMSG) {
3286 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3287 Jim_AppendStrings(interp, Jim_GetResult(interp),
3288 "invalid command name \"", objPtr->bytes, "\"",
3289 NULL);
3290 }
3291 return NULL;
3292 }
3293 return objPtr->internalRep.cmdValue.cmdPtr;
3294 }
3295
3296 /* -----------------------------------------------------------------------------
3297 * Variables
3298 * ---------------------------------------------------------------------------*/
3299
3300 /* Variables HashTable Type.
3301 *
3302 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3303 static void JimVariablesHTValDestructor(void *interp, void *val)
3304 {
3305 Jim_Var *varPtr = (void*) val;
3306
3307 Jim_DecrRefCount(interp, varPtr->objPtr);
3308 Jim_Free(val);
3309 }
3310
3311 static Jim_HashTableType JimVariablesHashTableType = {
3312 JimStringCopyHTHashFunction, /* hash function */
3313 JimStringCopyHTKeyDup, /* key dup */
3314 NULL, /* val dup */
3315 JimStringCopyHTKeyCompare, /* key compare */
3316 JimStringCopyHTKeyDestructor, /* key destructor */
3317 JimVariablesHTValDestructor /* val destructor */
3318 };
3319
3320 /* -----------------------------------------------------------------------------
3321 * Variable object
3322 * ---------------------------------------------------------------------------*/
3323
3324 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3325
3326 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3327
3328 static Jim_ObjType variableObjType = {
3329 "variable",
3330 NULL,
3331 NULL,
3332 NULL,
3333 JIM_TYPE_REFERENCES,
3334 };
3335
3336 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3337 * is in the form "varname(key)". */
3338 static int Jim_NameIsDictSugar(const char *str, int len)
3339 {
3340 if (len == -1)
3341 len = strlen(str);
3342 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3343 return 1;
3344 return 0;
3345 }
3346
3347 /* This method should be called only by the variable API.
3348 * It returns JIM_OK on success (variable already exists),
3349 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3350 * a variable name, but syntax glue for [dict] i.e. the last
3351 * character is ')' */
3352 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3353 {
3354 Jim_HashEntry *he;
3355 const char *varName;
3356 int len;
3357
3358 /* Check if the object is already an uptodate variable */
3359 if (objPtr->typePtr == &variableObjType &&
3360 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3361 return JIM_OK; /* nothing to do */
3362 /* Get the string representation */
3363 varName = Jim_GetString(objPtr, &len);
3364 /* Make sure it's not syntax glue to get/set dict. */
3365 if (Jim_NameIsDictSugar(varName, len))
3366 return JIM_DICT_SUGAR;
3367 /* Lookup this name into the variables hash table */
3368 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3369 if (he == NULL) {
3370 /* Try with static vars. */
3371 if (interp->framePtr->staticVars == NULL)
3372 return JIM_ERR;
3373 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3374 return JIM_ERR;
3375 }
3376 /* Free the old internal repr and set the new one. */
3377 Jim_FreeIntRep(interp, objPtr);
3378 objPtr->typePtr = &variableObjType;
3379 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3380 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3381 return JIM_OK;
3382 }
3383
3384 /* -------------------- Variables related functions ------------------------- */
3385 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3386 Jim_Obj *valObjPtr);
3387 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3388
3389 /* For now that's dummy. Variables lookup should be optimized
3390 * in many ways, with caching of lookups, and possibly with
3391 * a table of pre-allocated vars in every CallFrame for local vars.
3392 * All the caching should also have an 'epoch' mechanism similar
3393 * to the one used by Tcl for procedures lookup caching. */
3394
3395 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3396 {
3397 const char *name;
3398 Jim_Var *var;
3399 int err;
3400
3401 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3402 /* Check for [dict] syntax sugar. */
3403 if (err == JIM_DICT_SUGAR)
3404 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3405 /* New variable to create */
3406 name = Jim_GetString(nameObjPtr, NULL);
3407
3408 var = Jim_Alloc(sizeof(*var));
3409 var->objPtr = valObjPtr;
3410 Jim_IncrRefCount(valObjPtr);
3411 var->linkFramePtr = NULL;
3412 /* Insert the new variable */
3413 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3414 /* Make the object int rep a variable */
3415 Jim_FreeIntRep(interp, nameObjPtr);
3416 nameObjPtr->typePtr = &variableObjType;
3417 nameObjPtr->internalRep.varValue.callFrameId =
3418 interp->framePtr->id;
3419 nameObjPtr->internalRep.varValue.varPtr = var;
3420 } else {
3421 var = nameObjPtr->internalRep.varValue.varPtr;
3422 if (var->linkFramePtr == NULL) {
3423 Jim_IncrRefCount(valObjPtr);
3424 Jim_DecrRefCount(interp, var->objPtr);
3425 var->objPtr = valObjPtr;
3426 } else { /* Else handle the link */
3427 Jim_CallFrame *savedCallFrame;
3428
3429 savedCallFrame = interp->framePtr;
3430 interp->framePtr = var->linkFramePtr;
3431 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3432 interp->framePtr = savedCallFrame;
3433 if (err != JIM_OK)
3434 return err;
3435 }
3436 }
3437 return JIM_OK;
3438 }
3439
3440 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3441 {
3442 Jim_Obj *nameObjPtr;
3443 int result;
3444
3445 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3446 Jim_IncrRefCount(nameObjPtr);
3447 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3448 Jim_DecrRefCount(interp, nameObjPtr);
3449 return result;
3450 }
3451
3452 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3453 {
3454 Jim_CallFrame *savedFramePtr;
3455 int result;
3456
3457 savedFramePtr = interp->framePtr;
3458 interp->framePtr = interp->topFramePtr;
3459 result = Jim_SetVariableStr(interp, name, objPtr);
3460 interp->framePtr = savedFramePtr;
3461 return result;
3462 }
3463
3464 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3465 {
3466 Jim_Obj *nameObjPtr, *valObjPtr;
3467 int result;
3468
3469 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3470 valObjPtr = Jim_NewStringObj(interp, val, -1);
3471 Jim_IncrRefCount(nameObjPtr);
3472 Jim_IncrRefCount(valObjPtr);
3473 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3474 Jim_DecrRefCount(interp, nameObjPtr);
3475 Jim_DecrRefCount(interp, valObjPtr);
3476 return result;
3477 }
3478
3479 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3480 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3481 {
3482 const char *varName;
3483 int len;
3484
3485 /* Check for cycles. */
3486 if (interp->framePtr == targetCallFrame) {
3487 Jim_Obj *objPtr = targetNameObjPtr;
3488 Jim_Var *varPtr;
3489 /* Cycles are only possible with 'uplevel 0' */
3490 while(1) {
3491 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3492 Jim_SetResultString(interp,
3493 "can't upvar from variable to itself", -1);
3494 return JIM_ERR;
3495 }
3496 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3497 break;
3498 varPtr = objPtr->internalRep.varValue.varPtr;
3499 if (varPtr->linkFramePtr != targetCallFrame) break;
3500 objPtr = varPtr->objPtr;
3501 }
3502 }
3503 varName = Jim_GetString(nameObjPtr, &len);
3504 if (Jim_NameIsDictSugar(varName, len)) {
3505 Jim_SetResultString(interp,
3506 "Dict key syntax invalid as link source", -1);
3507 return JIM_ERR;
3508 }
3509 /* Perform the binding */
3510 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3511 /* We are now sure 'nameObjPtr' type is variableObjType */
3512 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3513 return JIM_OK;
3514 }
3515
3516 /* Return the Jim_Obj pointer associated with a variable name,
3517 * or NULL if the variable was not found in the current context.
3518 * The same optimization discussed in the comment to the
3519 * 'SetVariable' function should apply here. */
3520 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3521 {
3522 int err;
3523
3524 /* All the rest is handled here */
3525 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3526 /* Check for [dict] syntax sugar. */
3527 if (err == JIM_DICT_SUGAR)
3528 return JimDictSugarGet(interp, nameObjPtr);
3529 if (flags & JIM_ERRMSG) {
3530 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3531 Jim_AppendStrings(interp, Jim_GetResult(interp),
3532 "can't read \"", nameObjPtr->bytes,
3533 "\": no such variable", NULL);
3534 }
3535 return NULL;
3536 } else {
3537 Jim_Var *varPtr;
3538 Jim_Obj *objPtr;
3539 Jim_CallFrame *savedCallFrame;
3540
3541 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3542 if (varPtr->linkFramePtr == NULL)
3543 return varPtr->objPtr;
3544 /* The variable is a link? Resolve it. */
3545 savedCallFrame = interp->framePtr;
3546 interp->framePtr = varPtr->linkFramePtr;
3547 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3548 if (objPtr == NULL && flags & JIM_ERRMSG) {
3549 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3550 Jim_AppendStrings(interp, Jim_GetResult(interp),
3551 "can't read \"", nameObjPtr->bytes,
3552 "\": no such variable", NULL);
3553 }
3554 interp->framePtr = savedCallFrame;
3555 return objPtr;
3556 }
3557 }
3558
3559 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3560 int flags)
3561 {
3562 Jim_CallFrame *savedFramePtr;
3563 Jim_Obj *objPtr;
3564
3565 savedFramePtr = interp->framePtr;
3566 interp->framePtr = interp->topFramePtr;
3567 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3568 interp->framePtr = savedFramePtr;
3569
3570 return objPtr;
3571 }
3572
3573 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3574 {
3575 Jim_Obj *nameObjPtr, *varObjPtr;
3576
3577 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3578 Jim_IncrRefCount(nameObjPtr);
3579 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3580 Jim_DecrRefCount(interp, nameObjPtr);
3581 return varObjPtr;
3582 }
3583
3584 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3585 int flags)
3586 {
3587 Jim_CallFrame *savedFramePtr;
3588 Jim_Obj *objPtr;
3589
3590 savedFramePtr = interp->framePtr;
3591 interp->framePtr = interp->topFramePtr;
3592 objPtr = Jim_GetVariableStr(interp, name, flags);
3593 interp->framePtr = savedFramePtr;
3594
3595 return objPtr;
3596 }
3597
3598 /* Unset a variable.
3599 * Note: On success unset invalidates all the variable objects created
3600 * in the current call frame incrementing. */
3601 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3602 {
3603 const char *name;
3604 Jim_Var *varPtr;
3605 int err;
3606
3607 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3608 /* Check for [dict] syntax sugar. */
3609 if (err == JIM_DICT_SUGAR)
3610 return JimDictSugarSet(interp, nameObjPtr, NULL);
3611 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3612 Jim_AppendStrings(interp, Jim_GetResult(interp),
3613 "can't unset \"", nameObjPtr->bytes,
3614 "\": no such variable", NULL);
3615 return JIM_ERR; /* var not found */
3616 }
3617 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3618 /* If it's a link call UnsetVariable recursively */
3619 if (varPtr->linkFramePtr) {
3620 int retval;
3621
3622 Jim_CallFrame *savedCallFrame;
3623
3624 savedCallFrame = interp->framePtr;
3625 interp->framePtr = varPtr->linkFramePtr;
3626 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3627 interp->framePtr = savedCallFrame;
3628 if (retval != JIM_OK && flags & JIM_ERRMSG) {
3629 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3630 Jim_AppendStrings(interp, Jim_GetResult(interp),
3631 "can't unset \"", nameObjPtr->bytes,
3632 "\": no such variable", NULL);
3633 }
3634 return retval;
3635 } else {
3636 name = Jim_GetString(nameObjPtr, NULL);
3637 if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3638 != JIM_OK) return JIM_ERR;
3639 /* Change the callframe id, invalidating var lookup caching */
3640 JimChangeCallFrameId(interp, interp->framePtr);
3641 return JIM_OK;
3642 }
3643 }
3644
3645 /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
3646
3647 /* Given a variable name for [dict] operation syntax sugar,
3648 * this function returns two objects, the first with the name
3649 * of the variable to set, and the second with the rispective key.
3650 * For example "foo(bar)" will return objects with string repr. of
3651 * "foo" and "bar".
3652 *
3653 * The returned objects have refcount = 1. The function can't fail. */
3654 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3655 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3656 {
3657 const char *str, *p;
3658 char *t;
3659 int len, keyLen, nameLen;
3660 Jim_Obj *varObjPtr, *keyObjPtr;
3661
3662 str = Jim_GetString(objPtr, &len);
3663 p = strchr(str, '(');
3664 p++;
3665 keyLen = len-((p-str)+1);
3666 nameLen = (p-str)-1;
3667 /* Create the objects with the variable name and key. */
3668 t = Jim_Alloc(nameLen+1);
3669 memcpy(t, str, nameLen);
3670 t[nameLen] = '\0';
3671 varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3672
3673 t = Jim_Alloc(keyLen+1);
3674 memcpy(t, p, keyLen);
3675 t[keyLen] = '\0';
3676 keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3677
3678 Jim_IncrRefCount(varObjPtr);
3679 Jim_IncrRefCount(keyObjPtr);
3680 *varPtrPtr = varObjPtr;
3681 *keyPtrPtr = keyObjPtr;
3682 }
3683
3684 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3685 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3686 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3687 Jim_Obj *valObjPtr)
3688 {
3689 Jim_Obj *varObjPtr, *keyObjPtr;
3690 int err = JIM_OK;
3691
3692 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3693 err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3694 valObjPtr);
3695 Jim_DecrRefCount(interp, varObjPtr);
3696 Jim_DecrRefCount(interp, keyObjPtr);
3697 return err;
3698 }
3699
3700 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3701 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3702 {
3703 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3704
3705 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3706 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3707 if (!dictObjPtr) {
3708 resObjPtr = NULL;
3709 goto err;
3710 }
3711 if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3712 != JIM_OK) {
3713 resObjPtr = NULL;
3714 }
3715 err:
3716 Jim_DecrRefCount(interp, varObjPtr);
3717 Jim_DecrRefCount(interp, keyObjPtr);
3718 return resObjPtr;
3719 }
3720
3721 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3722
3723 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3724 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3725 Jim_Obj *dupPtr);
3726
3727 static Jim_ObjType dictSubstObjType = {
3728 "dict-substitution",
3729 FreeDictSubstInternalRep,
3730 DupDictSubstInternalRep,
3731 NULL,
3732 JIM_TYPE_NONE,
3733 };
3734
3735 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3736 {
3737 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3738 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3739 }
3740
3741 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3742 Jim_Obj *dupPtr)
3743 {
3744 JIM_NOTUSED(interp);
3745
3746 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3747 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3748 dupPtr->internalRep.dictSubstValue.indexObjPtr =
3749 srcPtr->internalRep.dictSubstValue.indexObjPtr;
3750 dupPtr->typePtr = &dictSubstObjType;
3751 }
3752
3753 /* This function is used to expand [dict get] sugar in the form
3754 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3755 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3756 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3757 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3758 * the [dict]ionary contained in variable VARNAME. */
3759 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3760 {
3761 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3762 Jim_Obj *substKeyObjPtr = NULL;
3763
3764 if (objPtr->typePtr != &dictSubstObjType) {
3765 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3766 Jim_FreeIntRep(interp, objPtr);
3767 objPtr->typePtr = &dictSubstObjType;
3768 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3769 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3770 }
3771 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3772 &substKeyObjPtr, JIM_NONE)
3773 != JIM_OK) {
3774 substKeyObjPtr = NULL;
3775 goto err;
3776 }
3777 Jim_IncrRefCount(substKeyObjPtr);
3778 dictObjPtr = Jim_GetVariable(interp,
3779 objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3780 if (!dictObjPtr) {
3781 resObjPtr = NULL;
3782 goto err;
3783 }
3784 if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3785 != JIM_OK) {
3786 resObjPtr = NULL;
3787 goto err;
3788 }
3789 err:
3790 if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3791 return resObjPtr;
3792 }
3793
3794 /* -----------------------------------------------------------------------------
3795 * CallFrame
3796 * ---------------------------------------------------------------------------*/
3797
3798 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3799 {
3800 Jim_CallFrame *cf;
3801 if (interp->freeFramesList) {
3802 cf = interp->freeFramesList;
3803 interp->freeFramesList = cf->nextFramePtr;
3804 } else {
3805 cf = Jim_Alloc(sizeof(*cf));
3806 cf->vars.table = NULL;
3807 }
3808
3809 cf->id = interp->callFrameEpoch++;
3810 cf->parentCallFrame = NULL;
3811 cf->argv = NULL;
3812 cf->argc = 0;
3813 cf->procArgsObjPtr = NULL;
3814 cf->procBodyObjPtr = NULL;
3815 cf->nextFramePtr = NULL;
3816 cf->staticVars = NULL;
3817 if (cf->vars.table == NULL)
3818 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3819 return cf;
3820 }
3821
3822 /* Used to invalidate every caching related to callframe stability. */
3823 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3824 {
3825 cf->id = interp->callFrameEpoch++;
3826 }
3827
3828 #define JIM_FCF_NONE 0 /* no flags */
3829 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3830 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3831 int flags)
3832 {
3833 if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3834 if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3835 if (!(flags & JIM_FCF_NOHT))
3836 Jim_FreeHashTable(&cf->vars);
3837 else {
3838 int i;
3839 Jim_HashEntry **table = cf->vars.table, *he;
3840
3841 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3842 he = table[i];
3843 while (he != NULL) {
3844 Jim_HashEntry *nextEntry = he->next;
3845 Jim_Var *varPtr = (void*) he->val;
3846
3847 Jim_DecrRefCount(interp, varPtr->objPtr);
3848 Jim_Free(he->val);
3849 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3850 Jim_Free(he);
3851 table[i] = NULL;
3852 he = nextEntry;
3853 }
3854 }
3855 cf->vars.used = 0;
3856 }
3857 cf->nextFramePtr = interp->freeFramesList;
3858 interp->freeFramesList = cf;
3859 }
3860
3861 /* -----------------------------------------------------------------------------
3862 * References
3863 * ---------------------------------------------------------------------------*/
3864
3865 /* References HashTable Type.
3866 *
3867 * Keys are jim_wide integers, dynamically allocated for now but in the
3868 * future it's worth to cache this 8 bytes objects. Values are poitners
3869 * to Jim_References. */
3870 static void JimReferencesHTValDestructor(void *interp, void *val)
3871 {
3872 Jim_Reference *refPtr = (void*) val;
3873
3874 Jim_DecrRefCount(interp, refPtr->objPtr);
3875 if (refPtr->finalizerCmdNamePtr != NULL) {
3876 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3877 }
3878 Jim_Free(val);
3879 }
3880
3881 unsigned int JimReferencesHTHashFunction(const void *key)
3882 {
3883 /* Only the least significant bits are used. */
3884 const jim_wide *widePtr = key;
3885 unsigned int intValue = (unsigned int) *widePtr;
3886 return Jim_IntHashFunction(intValue);
3887 }
3888
3889 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3890 {
3891 /* Only the least significant bits are used. */
3892 const jim_wide *widePtr = key;
3893 unsigned int intValue = (unsigned int) *widePtr;
3894 return intValue; /* identity function. */
3895 }
3896
3897 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3898 {
3899 void *copy = Jim_Alloc(sizeof(jim_wide));
3900 JIM_NOTUSED(privdata);
3901
3902 memcpy(copy, key, sizeof(jim_wide));
3903 return copy;
3904 }
3905
3906 int JimReferencesHTKeyCompare(void *privdata, const void *key1,
3907 const void *key2)
3908 {
3909 JIM_NOTUSED(privdata);
3910
3911 return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3912 }
3913
3914 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3915 {
3916 JIM_NOTUSED(privdata);
3917
3918 Jim_Free((void*)key);
3919 }
3920
3921 static Jim_HashTableType JimReferencesHashTableType = {
3922 JimReferencesHTHashFunction, /* hash function */
3923 JimReferencesHTKeyDup, /* key dup */
3924 NULL, /* val dup */
3925 JimReferencesHTKeyCompare, /* key compare */
3926 JimReferencesHTKeyDestructor, /* key destructor */
3927 JimReferencesHTValDestructor /* val destructor */
3928 };
3929
3930 /* -----------------------------------------------------------------------------
3931 * Reference object type and References API
3932 * ---------------------------------------------------------------------------*/
3933
3934 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3935
3936 static Jim_ObjType referenceObjType = {
3937 "reference",
3938 NULL,
3939 NULL,
3940 UpdateStringOfReference,
3941 JIM_TYPE_REFERENCES,
3942 };
3943
3944 void UpdateStringOfReference(struct Jim_Obj *objPtr)
3945 {
3946 int len;
3947 char buf[JIM_REFERENCE_SPACE+1];
3948 Jim_Reference *refPtr;
3949
3950 refPtr = objPtr->internalRep.refValue.refPtr;
3951 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3952 objPtr->bytes = Jim_Alloc(len+1);
3953 memcpy(objPtr->bytes, buf, len+1);
3954 objPtr->length = len;
3955 }
3956
3957 /* returns true if 'c' is a valid reference tag character.
3958 * i.e. inside the range [_a-zA-Z0-9] */
3959 static int isrefchar(int c)
3960 {
3961 if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3962 (c >= '0' && c <= '9')) return 1;
3963 return 0;
3964 }
3965
3966 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3967 {
3968 jim_wide wideValue;
3969 int i, len;
3970 const char *str, *start, *end;
3971 char refId[21];
3972 Jim_Reference *refPtr;
3973 Jim_HashEntry *he;
3974
3975 /* Get the string representation */
3976 str = Jim_GetString(objPtr, &len);
3977 /* Check if it looks like a reference */
3978 if (len < JIM_REFERENCE_SPACE) goto badformat;
3979 /* Trim spaces */
3980 start = str;
3981 end = str+len-1;
3982 while (*start == ' ') start++;
3983 while (*end == ' ' && end > start) end--;
3984 if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3985 /* <reference.<1234567>.%020> */
3986 if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3987 if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3988 /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3989 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3990 if (!isrefchar(start[12+i])) goto badformat;
3991 }
3992 /* Extract info from the refernece. */
3993 memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3994 refId[20] = '\0';
3995 /* Try to convert the ID into a jim_wide */
3996 if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3997 /* Check if the reference really exists! */
3998 he = Jim_FindHashEntry(&interp->references, &wideValue);
3999 if (he == NULL) {
4000 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4001 Jim_AppendStrings(interp, Jim_GetResult(interp),
4002 "Invalid reference ID \"", str, "\"", NULL);
4003 return JIM_ERR;
4004 }
4005 refPtr = he->val;
4006 /* Free the old internal repr and set the new one. */
4007 Jim_FreeIntRep(interp, objPtr);
4008 objPtr->typePtr = &referenceObjType;
4009 objPtr->internalRep.refValue.id = wideValue;
4010 objPtr->internalRep.refValue.refPtr = refPtr;
4011 return JIM_OK;
4012
4013 badformat:
4014 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4015 Jim_AppendStrings(interp, Jim_GetResult(interp),
4016 "expected reference but got \"", str, "\"", NULL);
4017 return JIM_ERR;
4018 }
4019
4020 /* Returns a new reference pointing to objPtr, having cmdNamePtr
4021 * as finalizer command (or NULL if there is no finalizer).
4022 * The returned reference object has refcount = 0. */
4023 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
4024 Jim_Obj *cmdNamePtr)
4025 {
4026 struct Jim_Reference *refPtr;
4027 jim_wide wideValue = interp->referenceNextId;
4028 Jim_Obj *refObjPtr;
4029 const char *tag;
4030 int tagLen, i;
4031
4032 /* Perform the Garbage Collection if needed. */
4033 Jim_CollectIfNeeded(interp);
4034
4035 refPtr = Jim_Alloc(sizeof(*refPtr));
4036 refPtr->objPtr = objPtr;
4037 Jim_IncrRefCount(objPtr);
4038 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4039 if (cmdNamePtr)
4040 Jim_IncrRefCount(cmdNamePtr);
4041 Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4042 refObjPtr = Jim_NewObj(interp);
4043 refObjPtr->typePtr = &referenceObjType;
4044 refObjPtr->bytes = NULL;
4045 refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4046 refObjPtr->internalRep.refValue.refPtr = refPtr;
4047 interp->referenceNextId++;
4048 /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4049 * that does not pass the 'isrefchar' test is replaced with '_' */
4050 tag = Jim_GetString(tagPtr, &tagLen);
4051 if (tagLen > JIM_REFERENCE_TAGLEN)
4052 tagLen = JIM_REFERENCE_TAGLEN;
4053 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4054 if (i < tagLen)
4055 refPtr->tag[i] = tag[i];
4056 else
4057 refPtr->tag[i] = '_';
4058 }
4059 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4060 return refObjPtr;
4061 }
4062
4063 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4064 {
4065 if (objPtr->typePtr != &referenceObjType &&
4066 SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4067 return NULL;
4068 return objPtr->internalRep.refValue.refPtr;
4069 }
4070
4071 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4072 {
4073 Jim_Reference *refPtr;
4074
4075 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4076 return JIM_ERR;
4077 Jim_IncrRefCount(cmdNamePtr);
4078 if (refPtr->finalizerCmdNamePtr)
4079 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4080 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4081 return JIM_OK;
4082 }
4083
4084 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4085 {
4086 Jim_Reference *refPtr;
4087
4088 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4089 return JIM_ERR;
4090 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4091 return JIM_OK;
4092 }
4093
4094 /* -----------------------------------------------------------------------------
4095 * References Garbage Collection
4096 * ---------------------------------------------------------------------------*/
4097
4098 /* This the hash table type for the "MARK" phase of the GC */
4099 static Jim_HashTableType JimRefMarkHashTableType = {
4100 JimReferencesHTHashFunction, /* hash function */
4101 JimReferencesHTKeyDup, /* key dup */
4102 NULL, /* val dup */
4103 JimReferencesHTKeyCompare, /* key compare */
4104 JimReferencesHTKeyDestructor, /* key destructor */
4105 NULL /* val destructor */
4106 };
4107
4108 /* #define JIM_DEBUG_GC 1 */
4109
4110 /* Performs the garbage collection. */
4111 int Jim_Collect(Jim_Interp *interp)
4112 {
4113 Jim_HashTable marks;
4114 Jim_HashTableIterator *htiter;
4115 Jim_HashEntry *he;
4116 Jim_Obj *objPtr;
4117 int collected = 0;
4118
4119 /* Avoid recursive calls */
4120 if (interp->lastCollectId == -1) {
4121 /* Jim_Collect() already running. Return just now. */
4122 return 0;
4123 }
4124 interp->lastCollectId = -1;
4125
4126 /* Mark all the references found into the 'mark' hash table.
4127 * The references are searched in every live object that
4128 * is of a type that can contain references. */
4129 Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4130 objPtr = interp->liveList;
4131 while(objPtr) {
4132 if (objPtr->typePtr == NULL ||
4133 objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4134 const char *str, *p;
4135 int len;
4136
4137 /* If the object is of type reference, to get the
4138 * Id is simple... */
4139 if (objPtr->typePtr == &referenceObjType) {
4140 Jim_AddHashEntry(&marks,
4141 &objPtr->internalRep.refValue.id, NULL);
4142 #ifdef JIM_DEBUG_GC
4143 Jim_fprintf(interp,interp->cookie_stdout,
4144 "MARK (reference): %d refcount: %d" JIM_NL,
4145 (int) objPtr->internalRep.refValue.id,
4146 objPtr->refCount);
4147 #endif
4148 objPtr = objPtr->nextObjPtr;
4149 continue;
4150 }
4151 /* Get the string repr of the object we want
4152 * to scan for references. */
4153 p = str = Jim_GetString(objPtr, &len);
4154 /* Skip objects too little to contain references. */
4155 if (len < JIM_REFERENCE_SPACE) {
4156 objPtr = objPtr->nextObjPtr;
4157 continue;
4158 }
4159 /* Extract references from the object string repr. */
4160 while(1) {
4161 int i;
4162 jim_wide id;
4163 char buf[21];
4164
4165 if ((p = strstr(p, "<reference.<")) == NULL)
4166 break;
4167 /* Check if it's a valid reference. */
4168 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4169 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4170 for (i = 21; i <= 40; i++)
4171 if (!isdigit((int)p[i]))
4172 break;
4173 /* Get the ID */
4174 memcpy(buf, p+21, 20);
4175 buf[20] = '\0';
4176 Jim_StringToWide(buf, &id, 10);
4177
4178 /* Ok, a reference for the given ID
4179 * was found. Mark it. */
4180 Jim_AddHashEntry(&marks, &id, NULL);
4181 #ifdef JIM_DEBUG_GC
4182 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4183 #endif
4184 p += JIM_REFERENCE_SPACE;
4185 }
4186 }
4187 objPtr = objPtr->nextObjPtr;
4188 }
4189
4190 /* Run the references hash table to destroy every reference that
4191 * is not referenced outside (not present in the mark HT). */
4192 htiter = Jim_GetHashTableIterator(&interp->references);
4193 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4194 const jim_wide *refId;
4195 Jim_Reference *refPtr;
4196
4197 refId = he->key;
4198 /* Check if in the mark phase we encountered
4199 * this reference. */
4200 if (Jim_FindHashEntry(&marks, refId) == NULL) {
4201 #ifdef JIM_DEBUG_GC
4202 Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4203 #endif
4204 collected++;
4205 /* Drop the reference, but call the
4206 * finalizer first if registered. */
4207 refPtr = he->val;
4208 if (refPtr->finalizerCmdNamePtr) {
4209 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4210 Jim_Obj *objv[3], *oldResult;
4211
4212 JimFormatReference(refstr, refPtr, *refId);
4213
4214 objv[0] = refPtr->finalizerCmdNamePtr;
4215 objv[1] = Jim_NewStringObjNoAlloc(interp,
4216 refstr, 32);
4217 objv[2] = refPtr->objPtr;
4218 Jim_IncrRefCount(objv[0]);
4219 Jim_IncrRefCount(objv[1]);
4220 Jim_IncrRefCount(objv[2]);
4221
4222 /* Drop the reference itself */
4223 Jim_DeleteHashEntry(&interp->references, refId);
4224
4225 /* Call the finalizer. Errors ignored. */
4226 oldResult = interp->result;
4227 Jim_IncrRefCount(oldResult);
4228 Jim_EvalObjVector(interp, 3, objv);
4229 Jim_SetResult(interp, oldResult);
4230 Jim_DecrRefCount(interp, oldResult);
4231
4232 Jim_DecrRefCount(interp, objv[0]);
4233 Jim_DecrRefCount(interp, objv[1]);
4234 Jim_DecrRefCount(interp, objv[2]);
4235 } else {
4236 Jim_DeleteHashEntry(&interp->references, refId);
4237 }
4238 }
4239 }
4240 Jim_FreeHashTableIterator(htiter);
4241 Jim_FreeHashTable(&marks);
4242 interp->lastCollectId = interp->referenceNextId;
4243 interp->lastCollectTime = time(NULL);
4244 return collected;
4245 }
4246
4247 #define JIM_COLLECT_ID_PERIOD 5000
4248 #define JIM_COLLECT_TIME_PERIOD 300
4249
4250 void Jim_CollectIfNeeded(Jim_Interp *interp)
4251 {
4252 jim_wide elapsedId;
4253 int elapsedTime;
4254
4255 elapsedId = interp->referenceNextId - interp->lastCollectId;
4256 elapsedTime = time(NULL) - interp->lastCollectTime;
4257
4258
4259 if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4260 elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4261 Jim_Collect(interp);
4262 }
4263 }
4264
4265 /* -----------------------------------------------------------------------------
4266 * Interpreter related functions
4267 * ---------------------------------------------------------------------------*/
4268
4269 Jim_Interp *Jim_CreateInterp(void)
4270 {
4271 Jim_Interp *i = Jim_Alloc(sizeof(*i));
4272 Jim_Obj *pathPtr;
4273
4274 i->errorLine = 0;
4275 i->errorFileName = Jim_StrDup("");
4276 i->numLevels = 0;
4277 i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4278 i->returnCode = JIM_OK;
4279 i->exitCode = 0;
4280 i->procEpoch = 0;
4281 i->callFrameEpoch = 0;
4282 i->liveList = i->freeList = NULL;
4283 i->scriptFileName = Jim_StrDup("");
4284 i->referenceNextId = 0;
4285 i->lastCollectId = 0;
4286 i->lastCollectTime = time(NULL);
4287 i->freeFramesList = NULL;
4288 i->prngState = NULL;
4289 i->evalRetcodeLevel = -1;
4290 i->cookie_stdin = stdin;
4291 i->cookie_stdout = stdout;
4292 i->cookie_stderr = stderr;
4293 i->cb_fwrite = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4294 i->cb_fread = ((size_t (*)( void *, size_t, size_t, void *))(fread));
4295 i->cb_vfprintf = ((int (*)( void *, const char *fmt, va_list ))(vfprintf));
4296 i->cb_fflush = ((int (*)( void *))(fflush));
4297 i->cb_fgets = ((char * (*)( char *, int, void *))(fgets));
4298
4299 /* Note that we can create objects only after the
4300 * interpreter liveList and freeList pointers are
4301 * initialized to NULL. */
4302 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4303 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4304 Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4305 NULL);
4306 Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4307 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4308 Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4309 i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4310 i->emptyObj = Jim_NewEmptyStringObj(i);
4311 i->result = i->emptyObj;
4312 i->stackTrace = Jim_NewListObj(i, NULL, 0);
4313 i->unknown = Jim_NewStringObj(i, "unknown", -1);
4314 Jim_IncrRefCount(i->emptyObj);
4315 Jim_IncrRefCount(i->result);
4316 Jim_IncrRefCount(i->stackTrace);
4317 Jim_IncrRefCount(i->unknown);
4318
4319 /* Initialize key variables every interpreter should contain */
4320 pathPtr = Jim_NewStringObj(i, "./", -1);
4321 Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4322 Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4323
4324 /* Export the core API to extensions */
4325 JimRegisterCoreApi(i);
4326 return i;
4327 }
4328
4329 /* This is the only function Jim exports directly without
4330 * to use the STUB system. It is only used by embedders
4331 * in order to get an interpreter with the Jim API pointers
4332 * registered. */
4333 Jim_Interp *ExportedJimCreateInterp(void)
4334 {
4335 return Jim_CreateInterp();
4336 }
4337
4338 void Jim_FreeInterp(Jim_Interp *i)
4339 {
4340 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4341 Jim_Obj *objPtr, *nextObjPtr;
4342
4343 Jim_DecrRefCount(i, i->emptyObj);
4344 Jim_DecrRefCount(i, i->result);
4345 Jim_DecrRefCount(i, i->stackTrace);
4346 Jim_DecrRefCount(i, i->unknown);
4347 Jim_Free((void*)i->errorFileName);
4348 Jim_Free((void*)i->scriptFileName);
4349 Jim_FreeHashTable(&i->commands);
4350 Jim_FreeHashTable(&i->references);
4351 Jim_FreeHashTable(&i->stub);
4352 Jim_FreeHashTable(&i->assocData);
4353 Jim_FreeHashTable(&i->packages);
4354 Jim_Free(i->prngState);
4355 /* Free the call frames list */
4356 while(cf) {
4357 prevcf = cf->parentCallFrame;
4358 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4359 cf = prevcf;
4360 }
4361 /* Check that the live object list is empty, otherwise
4362 * there is a memory leak. */
4363 if (i->liveList != NULL) {
4364 Jim_Obj *objPtr = i->liveList;
4365
4366 Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4367 Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4368 while(objPtr) {
4369 const char *type = objPtr->typePtr ?
4370 objPtr->typePtr->name : "";
4371 Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4372 objPtr, type,
4373 objPtr->bytes ? objPtr->bytes
4374 : "(null)", objPtr->refCount);
4375 if (objPtr->typePtr == &sourceObjType) {
4376 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4377 objPtr->internalRep.sourceValue.fileName,
4378 objPtr->internalRep.sourceValue.lineNumber);
4379 }
4380 objPtr = objPtr->nextObjPtr;
4381 }
4382 Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4383 Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4384 }
4385 /* Free all the freed objects. */
4386 objPtr = i->freeList;
4387 while (objPtr) {
4388 nextObjPtr = objPtr->nextObjPtr;
4389 Jim_Free(objPtr);
4390 objPtr = nextObjPtr;
4391 }
4392 /* Free cached CallFrame structures */
4393 cf = i->freeFramesList;
4394 while(cf) {
4395 nextcf = cf->nextFramePtr;
4396 if (cf->vars.table != NULL)
4397 Jim_Free(cf->vars.table);
4398 Jim_Free(cf);
4399 cf = nextcf;
4400 }
4401 /* Free the sharedString hash table. Make sure to free it
4402 * after every other Jim_Object was freed. */
4403 Jim_FreeHashTable(&i->sharedStrings);
4404 /* Free the interpreter structure. */
4405 Jim_Free(i);
4406 }
4407
4408 /* Store the call frame relative to the level represented by
4409 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4410 * level is assumed to be '1'.
4411 *
4412 * If a newLevelptr int pointer is specified, the function stores
4413 * the absolute level integer value of the new target callframe into
4414 * *newLevelPtr. (this is used to adjust interp->numLevels
4415 * in the implementation of [uplevel], so that [info level] will
4416 * return a correct information).
4417 *
4418 * This function accepts the 'level' argument in the form
4419 * of the commands [uplevel] and [upvar].
4420 *
4421 * For a function accepting a relative integer as level suitable
4422 * for implementation of [info level ?level?] check the
4423 * GetCallFrameByInteger() function. */
4424 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4425 Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4426 {
4427 long level;
4428 const char *str;
4429 Jim_CallFrame *framePtr;
4430
4431 if (newLevelPtr) *newLevelPtr = interp->numLevels;
4432 if (levelObjPtr) {
4433 str = Jim_GetString(levelObjPtr, NULL);
4434 if (str[0] == '#') {
4435 char *endptr;
4436 /* speedup for the toplevel (level #0) */
4437 if (str[1] == '0' && str[2] == '\0') {
4438 if (newLevelPtr) *newLevelPtr = 0;
4439 *framePtrPtr = interp->topFramePtr;
4440 return JIM_OK;
4441 }
4442
4443 level = strtol(str+1, &endptr, 0);
4444 if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4445 goto badlevel;
4446 /* An 'absolute' level is converted into the
4447 * 'number of levels to go back' format. */
4448 level = interp->numLevels - level;
4449 if (level < 0) goto badlevel;
4450 } else {
4451 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4452 goto badlevel;
4453 }
4454 } else {
4455 str = "1"; /* Needed to format the error message. */
4456 level = 1;
4457 }
4458 /* Lookup */
4459 framePtr = interp->framePtr;
4460 if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4461 while (level--) {
4462 framePtr = framePtr->parentCallFrame;
4463 if (framePtr == NULL) goto badlevel;
4464 }
4465 *framePtrPtr = framePtr;
4466 return JIM_OK;
4467 badlevel:
4468 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4469 Jim_AppendStrings(interp, Jim_GetResult(interp),
4470 "bad level \"", str, "\"", NULL);
4471 return JIM_ERR;
4472 }
4473
4474 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4475 * as a relative integer like in the [info level ?level?] command. */
4476 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4477 Jim_CallFrame **framePtrPtr)
4478 {
4479 jim_wide level;
4480 jim_wide relLevel; /* level relative to the current one. */
4481 Jim_CallFrame *framePtr;
4482
4483 if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4484 goto badlevel;
4485 if (level > 0) {
4486 /* An 'absolute' level is converted into the
4487 * 'number of levels to go back' format. */
4488 relLevel = interp->numLevels - level;
4489 } else {
4490 relLevel = -level;
4491 }
4492 /* Lookup */
4493 framePtr = interp->framePtr;
4494 while (relLevel--) {
4495 framePtr = framePtr->parentCallFrame;
4496 if (framePtr == NULL) goto badlevel;
4497 }
4498 *framePtrPtr = framePtr;
4499 return JIM_OK;
4500 badlevel:
4501 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4502 Jim_AppendStrings(interp, Jim_GetResult(interp),
4503 "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4504 return JIM_ERR;
4505 }
4506
4507 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4508 {
4509 Jim_Free((void*)interp->errorFileName);
4510 interp->errorFileName = Jim_StrDup(filename);
4511 }
4512
4513 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4514 {
4515 interp->errorLine = linenr;
4516 }
4517
4518 static void JimResetStackTrace(Jim_Interp *interp)
4519 {
4520 Jim_DecrRefCount(interp, interp->stackTrace);
4521 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4522 Jim_IncrRefCount(interp->stackTrace);
4523 }
4524
4525 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4526 const char *filename, int linenr)
4527 {
4528 if (Jim_IsShared(interp->stackTrace)) {
4529 interp->stackTrace =
4530 Jim_DuplicateObj(interp, interp->stackTrace);
4531 Jim_IncrRefCount(interp->stackTrace);
4532 }
4533 Jim_ListAppendElement(interp, interp->stackTrace,
4534 Jim_NewStringObj(interp, procname, -1));
4535 Jim_ListAppendElement(interp, interp->stackTrace,
4536 Jim_NewStringObj(interp, filename, -1));
4537 Jim_ListAppendElement(interp, interp->stackTrace,
4538 Jim_NewIntObj(interp, linenr));
4539 }
4540
4541 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4542 {
4543 AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4544 assocEntryPtr->delProc = delProc;
4545 assocEntryPtr->data = data;
4546 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4547 }
4548
4549 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4550 {
4551 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4552 if (entryPtr != NULL) {
4553 AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4554 return assocEntryPtr->data;
4555 }
4556 return NULL;
4557 }
4558
4559 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4560 {
4561 return Jim_DeleteHashEntry(&interp->assocData, key);
4562 }
4563
4564 int Jim_GetExitCode(Jim_Interp *interp) {
4565 return interp->exitCode;
4566 }
4567
4568 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4569 {
4570 if (fp != NULL) interp->cookie_stdin = fp;
4571 return interp->cookie_stdin;
4572 }
4573
4574 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4575 {
4576 if (fp != NULL) interp->cookie_stdout = fp;
4577 return interp->cookie_stdout;
4578 }
4579
4580 void *Jim_SetStderr(Jim_Interp *interp, void *fp)
4581 {
4582 if (fp != NULL) interp->cookie_stderr = fp;
4583 return interp->cookie_stderr;
4584 }
4585
4586 /* -----------------------------------------------------------------------------
4587 * Shared strings.
4588 * Every interpreter has an hash table where to put shared dynamically
4589 * allocate strings that are likely to be used a lot of times.
4590 * For example, in the 'source' object type, there is a pointer to
4591 * the filename associated with that object. Every script has a lot
4592 * of this objects with the identical file name, so it is wise to share
4593 * this info.
4594 *
4595 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4596 * returns the pointer to the shared string. Every time a reference
4597 * to the string is no longer used, the user should call
4598 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4599 * a given string, it is removed from the hash table.
4600 * ---------------------------------------------------------------------------*/
4601 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4602 {
4603 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4604
4605 if (he == NULL) {
4606 char *strCopy = Jim_StrDup(str);
4607
4608 Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4609 return strCopy;
4610 } else {
4611 long refCount = (long) he->val;
4612
4613 refCount++;
4614 he->val = (void*) refCount;
4615 return he->key;
4616 }
4617 }
4618
4619 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4620 {
4621 long refCount;
4622 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4623
4624 if (he == NULL)
4625 Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4626 "unknown shared string '%s'", str);
4627 refCount = (long) he->val;
4628 refCount--;
4629 if (refCount == 0) {
4630 Jim_DeleteHashEntry(&interp->sharedStrings, str);
4631 } else {
4632 he->val = (void*) refCount;
4633 }
4634 }
4635
4636 /* -----------------------------------------------------------------------------
4637 * Integer object
4638 * ---------------------------------------------------------------------------*/
4639 #define JIM_INTEGER_SPACE 24
4640
4641 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4642 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4643
4644 static Jim_ObjType intObjType = {
4645 "int",
4646 NULL,
4647 NULL,
4648 UpdateStringOfInt,
4649 JIM_TYPE_NONE,
4650 };
4651
4652 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4653 {
4654 int len;
4655 char buf[JIM_INTEGER_SPACE+1];
4656
4657 len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4658 objPtr->bytes = Jim_Alloc(len+1);
4659 memcpy(objPtr->bytes, buf, len+1);
4660 objPtr->length = len;
4661 }
4662
4663 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4664 {
4665 jim_wide wideValue;
4666 const char *str;
4667
4668 /* Get the string representation */
4669 str = Jim_GetString(objPtr, NULL);
4670 /* Try to convert into a jim_wide */
4671 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4672 if (flags & JIM_ERRMSG) {
4673 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4674 Jim_AppendStrings(interp, Jim_GetResult(interp),
4675 "expected integer but got \"", str, "\"", NULL);
4676 }
4677 return JIM_ERR;
4678 }
4679 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4680 errno == ERANGE) {
4681 Jim_SetResultString(interp,
4682 "Integer value too big to be represented", -1);
4683 return JIM_ERR;
4684 }
4685 /* Free the old internal repr and set the new one. */
4686 Jim_FreeIntRep(interp, objPtr);
4687 objPtr->typePtr = &intObjType;
4688 objPtr->internalRep.wideValue = wideValue;
4689 return JIM_OK;
4690 }
4691
4692 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4693 {
4694 if (objPtr->typePtr != &intObjType &&
4695 SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4696 return JIM_ERR;
4697 *widePtr = objPtr->internalRep.wideValue;
4698 return JIM_OK;
4699 }
4700
4701 /* Get a wide but does not set an error if the format is bad. */
4702 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4703 jim_wide *widePtr)
4704 {
4705 if (objPtr->typePtr != &intObjType &&
4706 SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4707 return JIM_ERR;
4708 *widePtr = objPtr->internalRep.wideValue;
4709 return JIM_OK;
4710 }
4711
4712 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4713 {
4714 jim_wide wideValue;
4715 int retval;
4716
4717 retval = Jim_GetWide(interp, objPtr, &wideValue);
4718 if (retval == JIM_OK) {
4719 *longPtr = (long) wideValue;
4720 return JIM_OK;
4721 }
4722 return JIM_ERR;
4723 }
4724
4725 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4726 {
4727 if (Jim_IsShared(objPtr))
4728 Jim_Panic(interp,"Jim_SetWide called with shared object");
4729 if (objPtr->typePtr != &intObjType) {
4730 Jim_FreeIntRep(interp, objPtr);
4731 objPtr->typePtr = &intObjType;
4732 }
4733 Jim_InvalidateStringRep(objPtr);
4734 objPtr->internalRep.wideValue = wideValue;
4735 }
4736
4737 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4738 {
4739 Jim_Obj *objPtr;
4740
4741 objPtr = Jim_NewObj(interp);
4742 objPtr->typePtr = &intObjType;
4743 objPtr->bytes = NULL;
4744 objPtr->internalRep.wideValue = wideValue;
4745 return objPtr;
4746 }
4747
4748 /* -----------------------------------------------------------------------------
4749 * Double object
4750 * ---------------------------------------------------------------------------*/
4751 #define JIM_DOUBLE_SPACE 30
4752
4753 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4754 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4755
4756 static Jim_ObjType doubleObjType = {
4757 "double",
4758 NULL,
4759 NULL,
4760 UpdateStringOfDouble,
4761 JIM_TYPE_NONE,
4762 };
4763
4764 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4765 {
4766 int len;
4767 char buf[JIM_DOUBLE_SPACE+1];
4768
4769 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4770 objPtr->bytes = Jim_Alloc(len+1);
4771 memcpy(objPtr->bytes, buf, len+1);
4772 objPtr->length = len;
4773 }
4774
4775 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4776 {
4777 double doubleValue;
4778 const char *str;
4779
4780 /* Get the string representation */
4781 str = Jim_GetString(objPtr, NULL);
4782 /* Try to convert into a double */
4783 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4784 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4785 Jim_AppendStrings(interp, Jim_GetResult(interp),
4786 "expected number but got '", str, "'", NULL);
4787 return JIM_ERR;
4788 }
4789 /* Free the old internal repr and set the new one. */
4790 Jim_FreeIntRep(interp, objPtr);
4791 objPtr->typePtr = &doubleObjType;
4792 objPtr->internalRep.doubleValue = doubleValue;
4793 return JIM_OK;
4794 }
4795
4796 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4797 {
4798 if (objPtr->typePtr != &doubleObjType &&
4799 SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4800 return JIM_ERR;
4801 *doublePtr = objPtr->internalRep.doubleValue;
4802 return JIM_OK;
4803 }
4804
4805 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4806 {
4807 if (Jim_IsShared(objPtr))
4808 Jim_Panic(interp,"Jim_SetDouble called with shared object");
4809 if (objPtr->typePtr != &doubleObjType) {
4810 Jim_FreeIntRep(interp, objPtr);
4811 objPtr->typePtr = &doubleObjType;
4812 }
4813 Jim_InvalidateStringRep(objPtr);
4814 objPtr->internalRep.doubleValue = doubleValue;
4815 }
4816
4817 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4818 {
4819 Jim_Obj *objPtr;
4820
4821 objPtr = Jim_NewObj(interp);
4822 objPtr->typePtr = &doubleObjType;
4823 objPtr->bytes = NULL;
4824 objPtr->internalRep.doubleValue = doubleValue;
4825 return objPtr;
4826 }
4827
4828 /* -----------------------------------------------------------------------------
4829 * List object
4830 * ---------------------------------------------------------------------------*/
4831 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4832 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4833 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4834 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4835 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4836
4837 /* Note that while the elements of the list may contain references,
4838 * the list object itself can't. This basically means that the
4839 * list object string representation as a whole can't contain references
4840 * that are not presents in the single elements. */
4841 static Jim_ObjType listObjType = {
4842 "list",
4843 FreeListInternalRep,
4844 DupListInternalRep,
4845 UpdateStringOfList,
4846 JIM_TYPE_NONE,
4847 };
4848
4849 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4850 {
4851 int i;
4852
4853 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4854 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4855 }
4856 Jim_Free(objPtr->internalRep.listValue.ele);
4857 }
4858
4859 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4860 {
4861 int i;
4862 JIM_NOTUSED(interp);
4863
4864 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4865 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4866 dupPtr->internalRep.listValue.ele =
4867 Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4868 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4869 sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4870 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4871 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4872 }
4873 dupPtr->typePtr = &listObjType;
4874 }
4875
4876 /* The following function checks if a given string can be encoded
4877 * into a list element without any kind of quoting, surrounded by braces,
4878 * or using escapes to quote. */
4879 #define JIM_ELESTR_SIMPLE 0
4880 #define JIM_ELESTR_BRACE 1
4881 #define JIM_ELESTR_QUOTE 2
4882 static int ListElementQuotingType(const char *s, int len)
4883 {
4884 int i, level, trySimple = 1;
4885
4886 /* Try with the SIMPLE case */
4887 if (len == 0) return JIM_ELESTR_BRACE;
4888 if (s[0] == '"' || s[0] == '{') {
4889 trySimple = 0;
4890 goto testbrace;
4891 }
4892 for (i = 0; i < len; i++) {
4893 switch(s[i]) {
4894 case ' ':
4895 case '$':
4896 case '"':
4897 case '[':
4898 case ']':
4899 case ';':
4900 case '\\':
4901 case '\r':
4902 case '\n':
4903 case '\t':
4904 case '\f':
4905 case '\v':
4906 trySimple = 0;
4907 case '{':
4908 case '}':
4909 goto testbrace;
4910 }
4911 }
4912 return JIM_ELESTR_SIMPLE;
4913
4914 testbrace:
4915 /* Test if it's possible to do with braces */
4916 if (s[len-1] == '\\' ||
4917 s[len-1] == ']') return JIM_ELESTR_QUOTE;
4918 level = 0;
4919 for (i = 0; i < len; i++) {
4920 switch(s[i]) {
4921 case '{': level++; break;
4922 case '}': level--;
4923 if (level < 0) return JIM_ELESTR_QUOTE;
4924 break;
4925 case '\\':
4926 if (s[i+1] == '\n')
4927 return JIM_ELESTR_QUOTE;
4928 else
4929 if (s[i+1] != '\0') i++;
4930 break;
4931 }
4932 }
4933 if (level == 0) {
4934 if (!trySimple) return JIM_ELESTR_BRACE;
4935 for (i = 0; i < len; i++) {
4936 switch(s[i]) {
4937 case ' ':
4938 case '$':
4939 case '"':
4940 case '[':
4941 case ']':
4942 case ';':
4943 case '\\':
4944 case '\r':
4945 case '\n':
4946 case '\t':
4947 case '\f':
4948 case '\v':
4949 return JIM_ELESTR_BRACE;
4950 break;
4951 }
4952 }
4953 return JIM_ELESTR_SIMPLE;
4954 }
4955 return JIM_ELESTR_QUOTE;
4956 }
4957
4958 /* Returns the malloc-ed representation of a string
4959 * using backslash to quote special chars. */
4960 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4961 {
4962 char *q = Jim_Alloc(len*2+1), *p;
4963
4964 p = q;
4965 while(*s) {
4966 switch (*s) {
4967 case ' ':
4968 case '$':
4969 case '"':
4970 case '[':
4971 case ']':
4972 case '{':
4973 case '}':
4974 case ';':
4975 case '\\':
4976 *p++ = '\\';
4977 *p++ = *s++;
4978 break;
4979 case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4980 case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4981 case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4982 case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4983 case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4984 default:
4985 *p++ = *s++;
4986 break;
4987 }
4988 }
4989 *p = '\0';
4990 *qlenPtr = p-q;
4991 return q;
4992 }
4993
4994 void UpdateStringOfList(struct Jim_Obj *objPtr)
4995 {
4996 int i, bufLen, realLength;
4997 const char *strRep;
4998 char *p;
4999 int *quotingType;
5000 Jim_Obj **ele = objPtr->internalRep.listValue.ele;
5001
5002 /* (Over) Estimate the space needed. */
5003 quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
5004 bufLen = 0;
5005 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5006 int len;
5007
5008 strRep = Jim_GetString(ele[i], &len);
5009 quotingType[i] = ListElementQuotingType(strRep, len);
5010 switch (quotingType[i]) {
5011 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5012 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5013 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5014 }
5015 bufLen++; /* elements separator. */
5016 }
5017 bufLen++;
5018
5019 /* Generate the string rep. */
5020 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5021 realLength = 0;
5022 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5023 int len, qlen;
5024 const char *strRep = Jim_GetString(ele[i], &len);
5025 char *q;
5026
5027 switch(quotingType[i]) {
5028 case JIM_ELESTR_SIMPLE:
5029 memcpy(p, strRep, len);
5030 p += len;
5031 realLength += len;
5032 break;
5033 case JIM_ELESTR_BRACE:
5034 *p++ = '{';
5035 memcpy(p, strRep, len);
5036 p += len;
5037 *p++ = '}';
5038 realLength += len+2;
5039 break;
5040 case JIM_ELESTR_QUOTE:
5041 q = BackslashQuoteString(strRep, len, &qlen);
5042 memcpy(p, q, qlen);
5043 Jim_Free(q);
5044 p += qlen;
5045 realLength += qlen;
5046 break;
5047 }
5048 /* Add a separating space */
5049 if (i+1 != objPtr->internalRep.listValue.len) {
5050 *p++ = ' ';
5051 realLength ++;
5052 }
5053 }
5054 *p = '\0'; /* nul term. */
5055 objPtr->length = realLength;
5056 Jim_Free(quotingType);
5057 }
5058
5059 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5060 {
5061 struct JimParserCtx parser;
5062 const char *str;
5063 int strLen;
5064
5065 /* Get the string representation */
5066 str = Jim_GetString(objPtr, &strLen);
5067
5068 /* Free the old internal repr just now and initialize the
5069 * new one just now. The string->list conversion can't fail. */
5070 Jim_FreeIntRep(interp, objPtr);
5071 objPtr->typePtr = &listObjType;
5072 objPtr->internalRep.listValue.len = 0;
5073 objPtr->internalRep.listValue.maxLen = 0;
5074 objPtr->internalRep.listValue.ele = NULL;
5075
5076 /* Convert into a list */
5077 JimParserInit(&parser, str, strLen, 1);
5078 while(!JimParserEof(&parser)) {
5079 char *token;
5080 int tokenLen, type;
5081 Jim_Obj *elementPtr;
5082
5083 JimParseList(&parser);
5084 if (JimParserTtype(&parser) != JIM_TT_STR &&
5085 JimParserTtype(&parser) != JIM_TT_ESC)
5086 continue;
5087 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5088 elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5089 ListAppendElement(objPtr, elementPtr);
5090 }
5091 return JIM_OK;
5092 }
5093
5094 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
5095 int len)
5096 {
5097 Jim_Obj *objPtr;
5098 int i;
5099
5100 objPtr = Jim_NewObj(interp);
5101 objPtr->typePtr = &listObjType;
5102 objPtr->bytes = NULL;
5103 objPtr->internalRep.listValue.ele = NULL;
5104 objPtr->internalRep.listValue.len = 0;
5105 objPtr->internalRep.listValue.maxLen = 0;
5106 for (i = 0; i < len; i++) {
5107 ListAppendElement(objPtr, elements[i]);
5108 }
5109 return objPtr;
5110 }
5111
5112 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5113 * length of the vector. Note that the user of this function should make
5114 * sure that the list object can't shimmer while the vector returned
5115 * is in use, this vector is the one stored inside the internal representation
5116 * of the list object. This function is not exported, extensions should
5117 * always access to the List object elements using Jim_ListIndex(). */
5118 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5119 Jim_Obj ***listVec)
5120 {
5121 Jim_ListLength(interp, listObj, argc);
5122 assert(listObj->typePtr == &listObjType);
5123 *listVec = listObj->internalRep.listValue.ele;
5124 }
5125
5126 /* ListSortElements type values */
5127 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5128 JIM_LSORT_NOCASE_DECR};
5129
5130 /* Sort the internal rep of a list. */
5131 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5132 {
5133 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5134 }
5135
5136 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5137 {
5138 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5139 }
5140
5141 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5142 {
5143 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5144 }
5145
5146 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5147 {
5148 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5149 }
5150
5151 /* Sort a list *in place*. MUST be called with non-shared objects. */
5152 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5153 {
5154 typedef int (qsort_comparator)(const void *, const void *);
5155 int (*fn)(Jim_Obj**, Jim_Obj**);
5156 Jim_Obj **vector;
5157 int len;
5158
5159 if (Jim_IsShared(listObjPtr))
5160 Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5161 if (listObjPtr->typePtr != &listObjType)
5162 SetListFromAny(interp, listObjPtr);
5163
5164 vector = listObjPtr->internalRep.listValue.ele;
5165 len = listObjPtr->internalRep.listValue.len;
5166 switch (type) {
5167 case JIM_LSORT_ASCII: fn = ListSortString; break;
5168 case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
5169 case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
5170 case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
5171 default:
5172 fn = NULL; /* avoid warning */
5173 Jim_Panic(interp,"ListSort called with invalid sort type");
5174 }
5175 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5176 Jim_InvalidateStringRep(listObjPtr);
5177 }
5178
5179 /* This is the low-level function to append an element to a list.
5180 * The higher-level Jim_ListAppendElement() performs shared object
5181 * check and invalidate the string repr. This version is used
5182 * in the internals of the List Object and is not exported.
5183 *
5184 * NOTE: this function can be called only against objects
5185 * with internal type of List. */
5186 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5187 {
5188 int requiredLen = listPtr->internalRep.listValue.len + 1;
5189
5190 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5191 int maxLen = requiredLen * 2;
5192
5193 listPtr->internalRep.listValue.ele =
5194 Jim_Realloc(listPtr->internalRep.listValue.ele,
5195 sizeof(Jim_Obj*)*maxLen);
5196 listPtr->internalRep.listValue.maxLen = maxLen;
5197 }
5198 listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5199 objPtr;
5200 listPtr->internalRep.listValue.len ++;
5201 Jim_IncrRefCount(objPtr);
5202 }
5203
5204 /* This is the low-level function to insert elements into a list.
5205 * The higher-level Jim_ListInsertElements() performs shared object
5206 * check and invalidate the string repr. This version is used
5207 * in the internals of the List Object and is not exported.
5208 *
5209 * NOTE: this function can be called only against objects
5210 * with internal type of List. */
5211 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5212 Jim_Obj *const *elemVec)
5213 {
5214 int currentLen = listPtr->internalRep.listValue.len;
5215 int requiredLen = currentLen + elemc;
5216 int i;
5217 Jim_Obj **point;
5218
5219 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5220 int maxLen = requiredLen * 2;
5221
5222 listPtr->internalRep.listValue.ele =
5223 Jim_Realloc(listPtr->internalRep.listValue.ele,
5224 sizeof(Jim_Obj*)*maxLen);
5225 listPtr->internalRep.listValue.maxLen = maxLen;
5226 }
5227 point = listPtr->internalRep.listValue.ele + index;
5228 memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5229 for (i=0; i < elemc; ++i) {
5230 point[i] = elemVec[i];
5231 Jim_IncrRefCount(point[i]);
5232 }
5233 listPtr->internalRep.listValue.len += elemc;
5234 }
5235
5236 /* Appends every element of appendListPtr into listPtr.
5237 * Both have to be of the list type. */
5238 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5239 {
5240 int i, oldLen = listPtr->internalRep.listValue.len;
5241 int appendLen = appendListPtr->internalRep.listValue.len;
5242 int requiredLen = oldLen + appendLen;
5243
5244 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5245 int maxLen = requiredLen * 2;
5246
5247 listPtr->internalRep.listValue.ele =
5248 Jim_Realloc(listPtr->internalRep.listValue.ele,
5249 sizeof(Jim_Obj*)*maxLen);
5250 listPtr->internalRep.listValue.maxLen = maxLen;
5251 }
5252 for (i = 0; i < appendLen; i++) {
5253 Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5254 listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5255 Jim_IncrRefCount(objPtr);
5256 }
5257 listPtr->internalRep.listValue.len += appendLen;
5258 }
5259
5260 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5261 {
5262 if (Jim_IsShared(listPtr))
5263 Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5264 if (listPtr->typePtr != &listObjType)
5265 SetListFromAny(interp, listPtr);
5266 Jim_InvalidateStringRep(listPtr);
5267 ListAppendElement(listPtr, objPtr);
5268 }
5269
5270 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5271 {
5272 if (Jim_IsShared(listPtr))
5273 Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5274 if (listPtr->typePtr != &listObjType)
5275 SetListFromAny(interp, listPtr);
5276 Jim_InvalidateStringRep(listPtr);
5277 ListAppendList(listPtr, appendListPtr);
5278 }
5279
5280 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5281 {
5282 if (listPtr->typePtr != &listObjType)
5283 SetListFromAny(interp, listPtr);
5284 *intPtr = listPtr->internalRep.listValue.len;
5285 }
5286
5287 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5288 int objc, Jim_Obj *const *objVec)
5289 {
5290 if (Jim_IsShared(listPtr))
5291 Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5292 if (listPtr->typePtr != &listObjType)
5293 SetListFromAny(interp, listPtr);
5294 if (index >= 0 && index > listPtr->internalRep.listValue.len)
5295 index = listPtr->internalRep.listValue.len;
5296 else if (index < 0 )
5297 index = 0;
5298 Jim_InvalidateStringRep(listPtr);
5299 ListInsertElements(listPtr, index, objc, objVec);
5300 }
5301
5302 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5303 Jim_Obj **objPtrPtr, int flags)
5304 {
5305 if (listPtr->typePtr != &listObjType)
5306 SetListFromAny(interp, listPtr);
5307 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5308 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5309 if (flags & JIM_ERRMSG) {
5310 Jim_SetResultString(interp,
5311 "list index out of range", -1);
5312 }
5313 return JIM_ERR;
5314 }
5315 if (index < 0)
5316 index = listPtr->internalRep.listValue.len+index;
5317 *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5318 return JIM_OK;
5319 }
5320
5321 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5322 Jim_Obj *newObjPtr, int flags)
5323 {
5324 if (listPtr->typePtr != &listObjType)
5325 SetListFromAny(interp, listPtr);
5326 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5327 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5328 if (flags & JIM_ERRMSG) {
5329 Jim_SetResultString(interp,
5330 "list index out of range", -1);
5331 }
5332 return JIM_ERR;
5333 }
5334 if (index < 0)
5335 index = listPtr->internalRep.listValue.len+index;
5336 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5337 listPtr->internalRep.listValue.ele[index] = newObjPtr;
5338 Jim_IncrRefCount(newObjPtr);
5339 return JIM_OK;
5340 }
5341
5342 /* Modify the list stored into the variable named 'varNamePtr'
5343 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5344 * with the new element 'newObjptr'. */
5345 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5346 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5347 {
5348 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5349 int shared, i, index;
5350
5351 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5352 if (objPtr == NULL)
5353 return JIM_ERR;
5354 if ((shared = Jim_IsShared(objPtr)))
5355 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5356 for (i = 0; i < indexc-1; i++) {
5357 listObjPtr = objPtr;
5358 if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5359 goto err;
5360 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5361 JIM_ERRMSG) != JIM_OK) {
5362 goto err;
5363 }
5364 if (Jim_IsShared(objPtr)) {
5365 objPtr = Jim_DuplicateObj(interp, objPtr);
5366 ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5367 }
5368 Jim_InvalidateStringRep(listObjPtr);
5369 }
5370 if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5371 goto err;
5372 if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5373 goto err;
5374 Jim_InvalidateStringRep(objPtr);
5375 Jim_InvalidateStringRep(varObjPtr);
5376 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5377 goto err;
5378 Jim_SetResult(interp, varObjPtr);
5379 return JIM_OK;
5380 err:
5381 if (shared) {
5382 Jim_FreeNewObj(interp, varObjPtr);
5383 }
5384 return JIM_ERR;
5385 }
5386
5387 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5388 {
5389 int i;
5390
5391 /* If all the objects in objv are lists without string rep.
5392 * it's possible to return a list as result, that's the
5393 * concatenation of all the lists. */
5394 for (i = 0; i < objc; i++) {
5395 if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5396 break;
5397 }
5398 if (i == objc) {
5399 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5400 for (i = 0; i < objc; i++)
5401 Jim_ListAppendList(interp, objPtr, objv[i]);
5402 return objPtr;
5403 } else {
5404 /* Else... we have to glue strings together */
5405 int len = 0, objLen;
5406 char *bytes, *p;
5407
5408 /* Compute the length */
5409 for (i = 0; i < objc; i++) {
5410 Jim_GetString(objv[i], &objLen);
5411 len += objLen;
5412 }
5413 if (objc) len += objc-1;
5414 /* Create the string rep, and a stinrg object holding it. */
5415 p = bytes = Jim_Alloc(len+1);
5416 for (i = 0; i < objc; i++) {
5417 const char *s = Jim_GetString(objv[i], &objLen);
5418 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5419 {
5420 s++; objLen--; len--;
5421 }
5422 while (objLen && (s[objLen-1] == ' ' ||
5423 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5424 objLen--; len--;
5425 }
5426 memcpy(p, s, objLen);
5427 p += objLen;
5428 if (objLen && i+1 != objc) {
5429 *p++ = ' ';
5430 } else if (i+1 != objc) {
5431 /* Drop the space calcuated for this
5432 * element that is instead null. */
5433 len--;
5434 }
5435 }
5436 *p = '\0';
5437 return Jim_NewStringObjNoAlloc(interp, bytes, len);
5438 }
5439 }
5440
5441 /* Returns a list composed of the elements in the specified range.
5442 * first and start are directly accepted as Jim_Objects and
5443 * processed for the end?-index? case. */
5444 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5445 {
5446 int first, last;
5447 int len, rangeLen;
5448
5449 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5450 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5451 return NULL;
5452 Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5453 first = JimRelToAbsIndex(len, first);
5454 last = JimRelToAbsIndex(len, last);
5455 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5456 return Jim_NewListObj(interp,
5457 listObjPtr->internalRep.listValue.ele+first, rangeLen);
5458 }
5459
5460 /* -----------------------------------------------------------------------------
5461 * Dict object
5462 * ---------------------------------------------------------------------------*/
5463 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5464 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5465 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5466 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5467
5468 /* Dict HashTable Type.
5469 *
5470 * Keys and Values are Jim objects. */
5471
5472 unsigned int JimObjectHTHashFunction(const void *key)
5473 {
5474 const char *str;
5475 Jim_Obj *objPtr = (Jim_Obj*) key;
5476 int len, h;
5477
5478 str = Jim_GetString(objPtr, &len);
5479 h = Jim_GenHashFunction((unsigned char*)str, len);
5480 return h;
5481 }
5482
5483 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5484 {
5485 JIM_NOTUSED(privdata);
5486
5487 return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5488 }
5489
5490 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5491 {
5492 Jim_Obj *objPtr = val;
5493
5494 Jim_DecrRefCount(interp, objPtr);
5495 }
5496
5497 static Jim_HashTableType JimDictHashTableType = {
5498 JimObjectHTHashFunction, /* hash function */
5499 NULL, /* key dup */
5500 NULL, /* val dup */
5501 JimObjectHTKeyCompare, /* key compare */
5502 (void(*)(void*, const void*)) /* ATTENTION: const cast */
5503 JimObjectHTKeyValDestructor, /* key destructor */
5504 JimObjectHTKeyValDestructor /* val destructor */
5505 };
5506
5507 /* Note that while the elements of the dict may contain references,
5508 * the list object itself can't. This basically means that the
5509 * dict object string representation as a whole can't contain references
5510 * that are not presents in the single elements. */
5511 static Jim_ObjType dictObjType = {
5512 "dict",
5513 FreeDictInternalRep,
5514 DupDictInternalRep,
5515 UpdateStringOfDict,
5516 JIM_TYPE_NONE,
5517 };
5518
5519 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5520 {
5521 JIM_NOTUSED(interp);
5522
5523 Jim_FreeHashTable(objPtr->internalRep.ptr);
5524 Jim_Free(objPtr->internalRep.ptr);
5525 }
5526
5527 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5528 {
5529 Jim_HashTable *ht, *dupHt;
5530 Jim_HashTableIterator *htiter;
5531 Jim_HashEntry *he;
5532
5533 /* Create a new hash table */
5534 ht = srcPtr->internalRep.ptr;
5535 dupHt = Jim_Alloc(sizeof(*dupHt));
5536 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5537 if (ht->size != 0)
5538 Jim_ExpandHashTable(dupHt, ht->size);
5539 /* Copy every element from the source to the dup hash table */
5540 htiter = Jim_GetHashTableIterator(ht);
5541 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5542 const Jim_Obj *keyObjPtr = he->key;
5543 Jim_Obj *valObjPtr = he->val;
5544
5545 Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
5546 Jim_IncrRefCount(valObjPtr);
5547 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5548 }
5549 Jim_FreeHashTableIterator(htiter);
5550
5551 dupPtr->internalRep.ptr = dupHt;
5552 dupPtr->typePtr = &dictObjType;
5553 }
5554
5555 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5556 {
5557 int i, bufLen, realLength;
5558 const char *strRep;
5559 char *p;
5560 int *quotingType, objc;
5561 Jim_HashTable *ht;
5562 Jim_HashTableIterator *htiter;
5563 Jim_HashEntry *he;
5564 Jim_Obj **objv;
5565
5566 /* Trun the hash table into a flat vector of Jim_Objects. */
5567 ht = objPtr->internalRep.ptr;
5568 objc = ht->used*2;
5569 objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5570 htiter = Jim_GetHashTableIterator(ht);
5571 i = 0;
5572 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5573 objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
5574 objv[i++] = he->val;
5575 }
5576 Jim_FreeHashTableIterator(htiter);
5577 /* (Over) Estimate the space needed. */
5578 quotingType = Jim_Alloc(sizeof(int)*objc);
5579 bufLen = 0;
5580 for (i = 0; i < objc; i++) {
5581 int len;
5582
5583 strRep = Jim_GetString(objv[i], &len);
5584 quotingType[i] = ListElementQuotingType(strRep, len);
5585 switch (quotingType[i]) {
5586 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5587 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5588 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5589 }
5590 bufLen++; /* elements separator. */
5591 }
5592 bufLen++;
5593
5594 /* Generate the string rep. */
5595 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5596 realLength = 0;
5597 for (i = 0; i < objc; i++) {
5598 int len, qlen;
5599 const char *strRep = Jim_GetString(objv[i], &len);
5600 char *q;
5601
5602 switch(quotingType[i]) {
5603 case JIM_ELESTR_SIMPLE:
5604 memcpy(p, strRep, len);
5605 p += len;
5606 realLength += len;
5607 break;
5608 case JIM_ELESTR_BRACE:
5609 *p++ = '{';
5610 memcpy(p, strRep, len);
5611 p += len;
5612 *p++ = '}';
5613 realLength += len+2;
5614 break;
5615 case JIM_ELESTR_QUOTE:
5616 q = BackslashQuoteString(strRep, len, &qlen);
5617 memcpy(p, q, qlen);
5618 Jim_Free(q);
5619 p += qlen;
5620 realLength += qlen;
5621 break;
5622 }
5623 /* Add a separating space */
5624 if (i+1 != objc) {
5625 *p++ = ' ';
5626 realLength ++;
5627 }
5628 }
5629 *p = '\0'; /* nul term. */
5630 objPtr->length = realLength;
5631 Jim_Free(quotingType);
5632 Jim_Free(objv);
5633 }
5634
5635 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5636 {
5637 struct JimParserCtx parser;
5638 Jim_HashTable *ht;
5639 Jim_Obj *objv[2];
5640 const char *str;
5641 int i, strLen;
5642
5643 /* Get the string representation */
5644 str = Jim_GetString(objPtr, &strLen);
5645
5646 /* Free the old internal repr just now and initialize the
5647 * new one just now. The string->list conversion can't fail. */
5648 Jim_FreeIntRep(interp, objPtr);
5649 ht = Jim_Alloc(sizeof(*ht));
5650 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5651 objPtr->typePtr = &dictObjType;
5652 objPtr->internalRep.ptr = ht;
5653
5654 /* Convert into a dict */
5655 JimParserInit(&parser, str, strLen, 1);
5656 i = 0;
5657 while(!JimParserEof(&parser)) {
5658 char *token;
5659 int tokenLen, type;
5660
5661 JimParseList(&parser);
5662 if (JimParserTtype(&parser) != JIM_TT_STR &&
5663 JimParserTtype(&parser) != JIM_TT_ESC)
5664 continue;
5665 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5666 objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5667 if (i == 2) {
5668 i = 0;
5669 Jim_IncrRefCount(objv[0]);
5670 Jim_IncrRefCount(objv[1]);
5671 if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5672 Jim_HashEntry *he;
5673 he = Jim_FindHashEntry(ht, objv[0]);
5674 Jim_DecrRefCount(interp, objv[0]);
5675 /* ATTENTION: const cast */
5676 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5677 he->val = objv[1];
5678 }
5679 }
5680 }
5681 if (i) {
5682 Jim_FreeNewObj(interp, objv[0]);
5683 objPtr->typePtr = NULL;
5684 Jim_FreeHashTable(ht);
5685 Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5686 return JIM_ERR;
5687 }
5688 return JIM_OK;
5689 }
5690
5691 /* Dict object API */
5692
5693 /* Add an element to a dict. objPtr must be of the "dict" type.
5694 * The higer-level exported function is Jim_DictAddElement().
5695 * If an element with the specified key already exists, the value
5696 * associated is replaced with the new one.
5697 *
5698 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5699 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5700 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5701 {
5702 Jim_HashTable *ht = objPtr->internalRep.ptr;
5703
5704 if (valueObjPtr == NULL) { /* unset */
5705 Jim_DeleteHashEntry(ht, keyObjPtr);
5706 return;
5707 }
5708 Jim_IncrRefCount(keyObjPtr);
5709 Jim_IncrRefCount(valueObjPtr);
5710 if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5711 Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5712 Jim_DecrRefCount(interp, keyObjPtr);
5713 /* ATTENTION: const cast */
5714 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5715 he->val = valueObjPtr;
5716 }
5717 }
5718
5719 /* Add an element, higher-level interface for DictAddElement().
5720 * If valueObjPtr == NULL, the key is removed if it exists. */
5721 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5722 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5723 {
5724 if (Jim_IsShared(objPtr))
5725 Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5726 if (objPtr->typePtr != &dictObjType) {
5727 if (SetDictFromAny(interp, objPtr) != JIM_OK)
5728 return JIM_ERR;
5729 }
5730 DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5731 Jim_InvalidateStringRep(objPtr);
5732 return JIM_OK;
5733 }
5734
5735 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5736 {
5737 Jim_Obj *objPtr;
5738 int i;
5739
5740 if (len % 2)
5741 Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5742
5743 objPtr = Jim_NewObj(interp);
5744 objPtr->typePtr = &dictObjType;
5745 objPtr->bytes = NULL;
5746 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5747 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5748 for (i = 0; i < len; i += 2)
5749 DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5750 return objPtr;
5751 }
5752
5753 /* Return the value associated to the specified dict key */
5754 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5755 Jim_Obj **objPtrPtr, int flags)
5756 {
5757 Jim_HashEntry *he;
5758 Jim_HashTable *ht;
5759
5760 if (dictPtr->typePtr != &dictObjType) {
5761 if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5762 return JIM_ERR;
5763 }
5764 ht = dictPtr->internalRep.ptr;
5765 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5766 if (flags & JIM_ERRMSG) {
5767 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5768 Jim_AppendStrings(interp, Jim_GetResult(interp),
5769 "key \"", Jim_GetString(keyPtr, NULL),
5770 "\" not found in dictionary", NULL);
5771 }
5772 return JIM_ERR;
5773 }
5774 *objPtrPtr = he->val;
5775 return JIM_OK;
5776 }
5777
5778 /* Return the value associated to the specified dict keys */
5779 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5780 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5781 {
5782 Jim_Obj *objPtr;
5783 int i;
5784
5785 if (keyc == 0) {
5786 *objPtrPtr = dictPtr;
5787 return JIM_OK;
5788 }
5789
5790 for (i = 0; i < keyc; i++) {
5791 if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5792 != JIM_OK)
5793 return JIM_ERR;
5794 dictPtr = objPtr;
5795 }
5796 *objPtrPtr = objPtr;
5797 return JIM_OK;
5798 }
5799
5800 /* Modify the dict stored into the variable named 'varNamePtr'
5801 * setting the element specified by the 'keyc' keys objects in 'keyv',
5802 * with the new value of the element 'newObjPtr'.
5803 *
5804 * If newObjPtr == NULL the operation is to remove the given key
5805 * from the dictionary. */
5806 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5807 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5808 {
5809 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5810 int shared, i;
5811
5812 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5813 if (objPtr == NULL) {
5814 if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5815 return JIM_ERR;
5816 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5817 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5818 Jim_FreeNewObj(interp, varObjPtr);
5819 return JIM_ERR;
5820 }
5821 }
5822 if ((shared = Jim_IsShared(objPtr)))
5823 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5824 for (i = 0; i < keyc-1; i++) {
5825 dictObjPtr = objPtr;
5826
5827 /* Check if it's a valid dictionary */
5828 if (dictObjPtr->typePtr != &dictObjType) {
5829 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5830 goto err;
5831 }
5832 /* Check if the given key exists. */
5833 Jim_InvalidateStringRep(dictObjPtr);
5834 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5835 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5836 {
5837 /* This key exists at the current level.
5838 * Make sure it's not shared!. */
5839 if (Jim_IsShared(objPtr)) {
5840 objPtr = Jim_DuplicateObj(interp, objPtr);
5841 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5842 }
5843 } else {
5844 /* Key not found. If it's an [unset] operation
5845 * this is an error. Only the last key may not
5846 * exist. */
5847 if (newObjPtr == NULL)
5848 goto err;
5849 /* Otherwise set an empty dictionary
5850 * as key's value. */
5851 objPtr = Jim_NewDictObj(interp, NULL, 0);
5852 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5853 }
5854 }
5855 if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5856 != JIM_OK)
5857 goto err;
5858 Jim_InvalidateStringRep(objPtr);
5859 Jim_InvalidateStringRep(varObjPtr);
5860 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5861 goto err;
5862 Jim_SetResult(interp, varObjPtr);
5863 return JIM_OK;
5864 err:
5865 if (shared) {
5866 Jim_FreeNewObj(interp, varObjPtr);
5867 }
5868 return JIM_ERR;
5869 }
5870
5871 /* -----------------------------------------------------------------------------
5872 * Index object
5873 * ---------------------------------------------------------------------------*/
5874 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5875 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5876
5877 static Jim_ObjType indexObjType = {
5878 "index",
5879 NULL,
5880 NULL,
5881 UpdateStringOfIndex,
5882 JIM_TYPE_NONE,
5883 };
5884
5885 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5886 {
5887 int len;
5888 char buf[JIM_INTEGER_SPACE+1];
5889
5890 if (objPtr->internalRep.indexValue >= 0)
5891 len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5892 else if (objPtr->internalRep.indexValue == -1)
5893 len = sprintf(buf, "end");
5894 else {
5895 len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5896 }
5897 objPtr->bytes = Jim_Alloc(len+1);
5898 memcpy(objPtr->bytes, buf, len+1);
5899 objPtr->length = len;
5900 }
5901
5902 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5903 {
5904 int index, end = 0;
5905 const char *str;
5906
5907 /* Get the string representation */
5908 str = Jim_GetString(objPtr, NULL);
5909 /* Try to convert into an index */
5910 if (!strcmp(str, "end")) {
5911 index = 0;
5912 end = 1;
5913 } else {
5914 if (!strncmp(str, "end-", 4)) {
5915 str += 4;
5916 end = 1;
5917 }
5918 if (Jim_StringToIndex(str, &index) != JIM_OK) {
5919 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5920 Jim_AppendStrings(interp, Jim_GetResult(interp),
5921 "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5922 "must be integer or end?-integer?", NULL);
5923 return JIM_ERR;
5924 }
5925 }
5926 if (end) {
5927 if (index < 0)
5928 index = INT_MAX;
5929 else
5930 index = -(index+1);
5931 } else if (!end && index < 0)
5932 index = -INT_MAX;
5933 /* Free the old internal repr and set the new one. */
5934 Jim_FreeIntRep(interp, objPtr);
5935 objPtr->typePtr = &indexObjType;
5936 objPtr->internalRep.indexValue = index;
5937 return JIM_OK;
5938 }
5939
5940 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5941 {
5942 /* Avoid shimmering if the object is an integer. */
5943 if (objPtr->typePtr == &intObjType) {
5944 jim_wide val = objPtr->internalRep.wideValue;
5945 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5946 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5947 return JIM_OK;
5948 }
5949 }
5950 if (objPtr->typePtr != &indexObjType &&
5951 SetIndexFromAny(interp, objPtr) == JIM_ERR)
5952 return JIM_ERR;
5953 *indexPtr = objPtr->internalRep.indexValue;
5954 return JIM_OK;
5955 }
5956
5957 /* -----------------------------------------------------------------------------
5958 * Return Code Object.
5959 * ---------------------------------------------------------------------------*/
5960
5961 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5962
5963 static Jim_ObjType returnCodeObjType = {
5964 "return-code",
5965 NULL,
5966 NULL,
5967 NULL,
5968 JIM_TYPE_NONE,
5969 };
5970
5971 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5972 {
5973 const char *str;
5974 int strLen, returnCode;
5975 jim_wide wideValue;
5976
5977 /* Get the string representation */
5978 str = Jim_GetString(objPtr, &strLen);
5979 /* Try to convert into an integer */
5980 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5981 returnCode = (int) wideValue;
5982 else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5983 returnCode = JIM_OK;
5984 else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5985 returnCode = JIM_ERR;
5986 else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5987 returnCode = JIM_RETURN;
5988 else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5989 returnCode = JIM_BREAK;
5990 else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5991 returnCode = JIM_CONTINUE;
5992 else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5993 returnCode = JIM_EVAL;
5994 else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5995 returnCode = JIM_EXIT;
5996 else {
5997 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5998 Jim_AppendStrings(interp, Jim_GetResult(interp),
5999 "expected return code but got '", str, "'",
6000 NULL);
6001 return JIM_ERR;
6002 }
6003 /* Free the old internal repr and set the new one. */
6004 Jim_FreeIntRep(interp, objPtr);
6005 objPtr->typePtr = &returnCodeObjType;
6006 objPtr->internalRep.returnCode = returnCode;
6007 return JIM_OK;
6008 }
6009
6010 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
6011 {
6012 if (objPtr->typePtr != &returnCodeObjType &&
6013 SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
6014 return JIM_ERR;
6015 *intPtr = objPtr->internalRep.returnCode;
6016 return JIM_OK;
6017 }
6018
6019 /* -----------------------------------------------------------------------------
6020 * Expression Parsing
6021 * ---------------------------------------------------------------------------*/
6022 static int JimParseExprOperator(struct JimParserCtx *pc);
6023 static int JimParseExprNumber(struct JimParserCtx *pc);
6024 static int JimParseExprIrrational(struct JimParserCtx *pc);
6025
6026 /* Exrp's Stack machine operators opcodes. */
6027
6028 /* Binary operators (numbers) */
6029 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
6030 #define JIM_EXPROP_MUL 0
6031 #define JIM_EXPROP_DIV 1
6032 #define JIM_EXPROP_MOD 2
6033 #define JIM_EXPROP_SUB 3
6034 #define JIM_EXPROP_ADD 4
6035 #define JIM_EXPROP_LSHIFT 5
6036 #define JIM_EXPROP_RSHIFT 6
6037 #define JIM_EXPROP_ROTL 7
6038 #define JIM_EXPROP_ROTR 8
6039 #define JIM_EXPROP_LT 9
6040 #define JIM_EXPROP_GT 10
6041 #define JIM_EXPROP_LTE 11
6042 #define JIM_EXPROP_GTE 12
6043 #define JIM_EXPROP_NUMEQ 13
6044 #define JIM_EXPROP_NUMNE 14
6045 #define JIM_EXPROP_BITAND 15
6046 #define JIM_EXPROP_BITXOR 16
6047 #define JIM_EXPROP_BITOR 17
6048 #define JIM_EXPROP_LOGICAND 18
6049 #define JIM_EXPROP_LOGICOR 19
6050 #define JIM_EXPROP_LOGICAND_LEFT 20
6051 #define JIM_EXPROP_LOGICOR_LEFT 21
6052 #define JIM_EXPROP_POW 22
6053 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6054
6055 /* Binary operators (strings) */
6056 #define JIM_EXPROP_STREQ 23
6057 #define JIM_EXPROP_STRNE 24
6058
6059 /* Unary operators (numbers) */
6060 #define JIM_EXPROP_NOT 25
6061 #define JIM_EXPROP_BITNOT 26
6062 #define JIM_EXPROP_UNARYMINUS 27
6063 #define JIM_EXPROP_UNARYPLUS 28
6064 #define JIM_EXPROP_LOGICAND_RIGHT 29
6065 #define JIM_EXPROP_LOGICOR_RIGHT 30
6066
6067 /* Ternary operators */
6068 #define JIM_EXPROP_TERNARY 31
6069
6070 /* Operands */
6071 #define JIM_EXPROP_NUMBER 32
6072 #define JIM_EXPROP_COMMAND 33
6073 #define JIM_EXPROP_VARIABLE 34
6074 #define JIM_EXPROP_DICTSUGAR 35
6075 #define JIM_EXPROP_SUBST 36
6076 #define JIM_EXPROP_STRING 37
6077
6078 /* Operators table */
6079 typedef struct Jim_ExprOperator {
6080 const char *name;
6081 int precedence;
6082 int arity;
6083 int opcode;
6084 } Jim_ExprOperator;
6085
6086 /* name - precedence - arity - opcode */
6087 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6088 {"!", 300, 1, JIM_EXPROP_NOT},
6089 {"~", 300, 1, JIM_EXPROP_BITNOT},
6090 {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6091 {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6092
6093 {"**", 250, 2, JIM_EXPROP_POW},
6094
6095 {"*", 200, 2, JIM_EXPROP_MUL},
6096 {"/", 200, 2, JIM_EXPROP_DIV},
6097 {"%", 200, 2, JIM_EXPROP_MOD},
6098
6099 {"-", 100, 2, JIM_EXPROP_SUB},
6100 {"+", 100, 2, JIM_EXPROP_ADD},
6101
6102 {"<<<", 90, 3, JIM_EXPROP_ROTL},
6103 {">>>", 90, 3, JIM_EXPROP_ROTR},
6104 {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6105 {">>", 90, 2, JIM_EXPROP_RSHIFT},
6106
6107 {"<", 80, 2, JIM_EXPROP_LT},
6108 {">", 80, 2, JIM_EXPROP_GT},
6109 {"<=", 80, 2, JIM_EXPROP_LTE},
6110 {">=", 80, 2, JIM_EXPROP_GTE},
6111
6112 {"==", 70, 2, JIM_EXPROP_NUMEQ},
6113 {"!=", 70, 2, JIM_EXPROP_NUMNE},
6114
6115 {"eq", 60, 2, JIM_EXPROP_STREQ},
6116 {"ne", 60, 2, JIM_EXPROP_STRNE},
6117
6118 {"&", 50, 2, JIM_EXPROP_BITAND},
6119 {"^", 49, 2, JIM_EXPROP_BITXOR},
6120 {"|", 48, 2, JIM_EXPROP_BITOR},
6121
6122 {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6123 {"||", 10, 2, JIM_EXPROP_LOGICOR},
6124
6125 {"?", 5, 3, JIM_EXPROP_TERNARY},
6126 /* private operators */
6127 {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6128 {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6129 {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6130 {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6131 };
6132
6133 #define JIM_EXPR_OPERATORS_NUM \
6134 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6135
6136 int JimParseExpression(struct JimParserCtx *pc)
6137 {
6138 /* Discard spaces and quoted newline */
6139 while(*(pc->p) == ' ' ||
6140 *(pc->p) == '\t' ||
6141 *(pc->p) == '\r' ||
6142 *(pc->p) == '\n' ||
6143 (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6144 pc->p++; pc->len--;
6145 }
6146
6147 if (pc->len == 0) {
6148 pc->tstart = pc->tend = pc->p;
6149 pc->tline = pc->linenr;
6150 pc->tt = JIM_TT_EOL;
6151 pc->eof = 1;
6152 return JIM_OK;
6153 }
6154 switch(*(pc->p)) {
6155 case '(':
6156 pc->tstart = pc->tend = pc->p;
6157 pc->tline = pc->linenr;
6158 pc->tt = JIM_TT_SUBEXPR_START;
6159 pc->p++; pc->len--;
6160 break;
6161 case ')':
6162 pc->tstart = pc->tend = pc->p;
6163 pc->tline = pc->linenr;
6164 pc->tt = JIM_TT_SUBEXPR_END;
6165 pc->p++; pc->len--;
6166 break;
6167 case '[':
6168 return JimParseCmd(pc);
6169 break;
6170 case '$':
6171 if (JimParseVar(pc) == JIM_ERR)
6172 return JimParseExprOperator(pc);
6173 else
6174 return JIM_OK;
6175 break;
6176 case '-':
6177 if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6178 isdigit((int)*(pc->p+1)))
6179 return JimParseExprNumber(pc);
6180 else
6181 return JimParseExprOperator(pc);
6182 break;
6183 case '0': case '1': case '2': case '3': case '4':
6184 case '5': case '6': case '7': case '8': case '9': case '.':
6185 return JimParseExprNumber(pc);
6186 break;
6187 case '"':
6188 case '{':
6189 /* Here it's possible to reuse the List String parsing. */
6190 pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6191 return JimParseListStr(pc);
6192 break;
6193 case 'N': case 'I':
6194 case 'n': case 'i':
6195 if (JimParseExprIrrational(pc) == JIM_ERR)
6196 return JimParseExprOperator(pc);
6197 break;
6198 default:
6199 return JimParseExprOperator(pc);
6200 break;
6201 }
6202 return JIM_OK;
6203 }
6204
6205 int JimParseExprNumber(struct JimParserCtx *pc)
6206 {
6207 int allowdot = 1;
6208 int allowhex = 0;
6209
6210 pc->tstart = pc->p;
6211 pc->tline = pc->linenr;
6212 if (*pc->p == '-') {
6213 pc->p++; pc->len--;
6214 }
6215 while ( isdigit((int)*pc->p)
6216 || (allowhex && isxdigit((int)*pc->p) )
6217 || (allowdot && *pc->p == '.')
6218 || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6219 (*pc->p == 'x' || *pc->p == 'X'))
6220 )
6221 {
6222 if ((*pc->p == 'x') || (*pc->p == 'X')) {
6223 allowhex = 1;
6224 allowdot = 0;
6225 }
6226 if (*pc->p == '.')
6227 allowdot = 0;
6228 pc->p++; pc->len--;
6229 if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6230 pc->p += 2; pc->len -= 2;
6231 }
6232 }
6233 pc->tend = pc->p-1;
6234 pc->tt = JIM_TT_EXPR_NUMBER;
6235 return JIM_OK;
6236 }
6237
6238 int JimParseExprIrrational(struct JimParserCtx *pc)
6239 {
6240 const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6241 const char **token;
6242 for (token = Tokens; *token != NULL; token++) {
6243 int len = strlen(*token);
6244 if (strncmp(*token, pc->p, len) == 0) {
6245 pc->tstart = pc->p;
6246 pc->tend = pc->p + len - 1;
6247 pc->p += len; pc->len -= len;
6248 pc->tline = pc->linenr;
6249 pc->tt = JIM_TT_EXPR_NUMBER;
6250 return JIM_OK;
6251 }
6252 }
6253 return JIM_ERR;
6254 }
6255
6256 int JimParseExprOperator(struct JimParserCtx *pc)
6257 {
6258 int i;
6259 int bestIdx = -1, bestLen = 0;
6260
6261 /* Try to get the longest match. */
6262 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6263 const char *opname;
6264 int oplen;
6265
6266 opname = Jim_ExprOperators[i].name;
6267 if (opname == NULL) continue;
6268 oplen = strlen(opname);
6269
6270 if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6271 bestIdx = i;
6272 bestLen = oplen;
6273 }
6274 }
6275 if (bestIdx == -1) return JIM_ERR;
6276 pc->tstart = pc->p;
6277 pc->tend = pc->p + bestLen - 1;
6278 pc->p += bestLen; pc->len -= bestLen;
6279 pc->tline = pc->linenr;
6280 pc->tt = JIM_TT_EXPR_OPERATOR;
6281 return JIM_OK;
6282 }
6283
6284 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6285 {
6286 int i;
6287 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6288 if (Jim_ExprOperators[i].name &&
6289 strcmp(opname, Jim_ExprOperators[i].name) == 0)
6290 return &Jim_ExprOperators[i];
6291 return NULL;
6292 }
6293
6294 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6295 {
6296 int i;
6297 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6298 if (Jim_ExprOperators[i].opcode == opcode)
6299 return &Jim_ExprOperators[i];
6300 return NULL;
6301 }
6302
6303 /* -----------------------------------------------------------------------------
6304 * Expression Object
6305 * ---------------------------------------------------------------------------*/
6306 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6307 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6308 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6309
6310 static Jim_ObjType exprObjType = {
6311 "expression",
6312 FreeExprInternalRep,
6313 DupExprInternalRep,
6314 NULL,
6315 JIM_TYPE_REFERENCES,
6316 };
6317
6318 /* Expr bytecode structure */
6319 typedef struct ExprByteCode {
6320 int *opcode; /* Integer array of opcodes. */
6321 Jim_Obj **obj; /* Array of associated Jim Objects. */
6322 int len; /* Bytecode length */
6323 int inUse; /* Used for sharing. */
6324 } ExprByteCode;
6325
6326 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6327 {
6328 int i;
6329 ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6330
6331 expr->inUse--;
6332 if (expr->inUse != 0) return;
6333 for (i = 0; i < expr->len; i++)
6334 Jim_DecrRefCount(interp, expr->obj[i]);
6335 Jim_Free(expr->opcode);
6336 Jim_Free(expr->obj);
6337 Jim_Free(expr);
6338 }
6339
6340 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6341 {
6342 JIM_NOTUSED(interp);
6343 JIM_NOTUSED(srcPtr);
6344
6345 /* Just returns an simple string. */
6346 dupPtr->typePtr = NULL;
6347 }
6348
6349 /* Add a new instruction to an expression bytecode structure. */
6350 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6351 int opcode, char *str, int len)
6352 {
6353 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6354 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6355 expr->opcode[expr->len] = opcode;
6356 expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6357 Jim_IncrRefCount(expr->obj[expr->len]);
6358 expr->len++;
6359 }
6360
6361 /* Check if an expr program looks correct. */
6362 static int ExprCheckCorrectness(ExprByteCode *expr)
6363 {
6364 int i;
6365 int stacklen = 0;
6366
6367 /* Try to check if there are stack underflows,
6368 * and make sure at the end of the program there is
6369 * a single result on the stack. */
6370 for (i = 0; i < expr->len; i++) {
6371 switch(expr->opcode[i]) {
6372 case JIM_EXPROP_NUMBER:
6373 case JIM_EXPROP_STRING:
6374 case JIM_EXPROP_SUBST:
6375 case JIM_EXPROP_VARIABLE:
6376 case JIM_EXPROP_DICTSUGAR:
6377 case JIM_EXPROP_COMMAND:
6378 stacklen++;
6379 break;
6380 case JIM_EXPROP_NOT:
6381 case JIM_EXPROP_BITNOT:
6382 case JIM_EXPROP_UNARYMINUS:
6383 case JIM_EXPROP_UNARYPLUS:
6384 /* Unary operations */
6385 if (stacklen < 1) return JIM_ERR;
6386 break;
6387 case JIM_EXPROP_ADD:
6388 case JIM_EXPROP_SUB:
6389 case JIM_EXPROP_MUL:
6390 case JIM_EXPROP_DIV:
6391 case JIM_EXPROP_MOD:
6392 case JIM_EXPROP_LT:
6393 case JIM_EXPROP_GT:
6394 case JIM_EXPROP_LTE:
6395 case JIM_EXPROP_GTE:
6396 case JIM_EXPROP_ROTL:
6397 case JIM_EXPROP_ROTR:
6398 case JIM_EXPROP_LSHIFT:
6399 case JIM_EXPROP_RSHIFT:
6400 case JIM_EXPROP_NUMEQ:
6401 case JIM_EXPROP_NUMNE:
6402 case JIM_EXPROP_STREQ:
6403 case JIM_EXPROP_STRNE:
6404 case JIM_EXPROP_BITAND:
6405 case JIM_EXPROP_BITXOR:
6406 case JIM_EXPROP_BITOR:
6407 case JIM_EXPROP_LOGICAND:
6408 case JIM_EXPROP_LOGICOR:
6409 case JIM_EXPROP_POW:
6410 /* binary operations */
6411 if (stacklen < 2) return JIM_ERR;
6412 stacklen--;
6413 break;
6414 default:
6415 Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6416 break;
6417 }
6418 }
6419 if (stacklen != 1) return JIM_ERR;
6420 return JIM_OK;
6421 }
6422
6423 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6424 ScriptObj *topLevelScript)
6425 {
6426 int i;
6427
6428 return;
6429 for (i = 0; i < expr->len; i++) {
6430 Jim_Obj *foundObjPtr;
6431
6432 if (expr->obj[i] == NULL) continue;
6433 foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6434 NULL, expr->obj[i]);
6435 if (foundObjPtr != NULL) {
6436 Jim_IncrRefCount(foundObjPtr);
6437 Jim_DecrRefCount(interp, expr->obj[i]);
6438 expr->obj[i] = foundObjPtr;
6439 }
6440 }
6441 }
6442
6443 /* This procedure converts every occurrence of || and && opereators
6444 * in lazy unary versions.
6445 *
6446 * a b || is converted into:
6447 *
6448 * a <offset> |L b |R
6449 *
6450 * a b && is converted into:
6451 *
6452 * a <offset> &L b &R
6453 *
6454 * "|L" checks if 'a' is true:
6455 * 1) if it is true pushes 1 and skips <offset> istructions to reach
6456 * the opcode just after |R.
6457 * 2) if it is false does nothing.
6458 * "|R" checks if 'b' is true:
6459 * 1) if it is true pushes 1, otherwise pushes 0.
6460 *
6461 * "&L" checks if 'a' is true:
6462 * 1) if it is true does nothing.
6463 * 2) If it is false pushes 0 and skips <offset> istructions to reach
6464 * the opcode just after &R
6465 * "&R" checks if 'a' is true:
6466 * if it is true pushes 1, otherwise pushes 0.
6467 */
6468 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6469 {
6470 while (1) {
6471 int index = -1, leftindex, arity, i, offset;
6472 Jim_ExprOperator *op;
6473
6474 /* Search for || or && */
6475 for (i = 0; i < expr->len; i++) {
6476 if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6477 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6478 index = i;
6479 break;
6480 }
6481 }
6482 if (index == -1) return;
6483 /* Search for the end of the first operator */
6484 leftindex = index-1;
6485 arity = 1;
6486 while(arity) {
6487 switch(expr->opcode[leftindex]) {
6488 case JIM_EXPROP_NUMBER:
6489 case JIM_EXPROP_COMMAND:
6490 case JIM_EXPROP_VARIABLE:
6491 case JIM_EXPROP_DICTSUGAR:
6492 case JIM_EXPROP_SUBST:
6493 case JIM_EXPROP_STRING:
6494 break;
6495 default:
6496 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6497 if (op == NULL) {
6498 Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6499 }
6500 arity += op->arity;
6501 break;
6502 }
6503 arity--;
6504 leftindex--;
6505 }
6506 leftindex++;
6507 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6508 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6509 memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6510 sizeof(int)*(expr->len-leftindex));
6511 memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6512 sizeof(Jim_Obj*)*(expr->len-leftindex));
6513 expr->len += 2;
6514 index += 2;
6515 offset = (index-leftindex)-1;
6516 Jim_DecrRefCount(interp, expr->obj[index]);
6517 if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6518 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6519 expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6520 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6521 expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6522 } else {
6523 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6524 expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6525 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6526 expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6527 }
6528 expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6529 expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6530 Jim_IncrRefCount(expr->obj[index]);
6531 Jim_IncrRefCount(expr->obj[leftindex]);
6532 Jim_IncrRefCount(expr->obj[leftindex+1]);
6533 }
6534 }
6535
6536 /* This method takes the string representation of an expression
6537 * and generates a program for the Expr's stack-based VM. */
6538 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6539 {
6540 int exprTextLen;
6541 const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6542 struct JimParserCtx parser;
6543 int i, shareLiterals;
6544 ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6545 Jim_Stack stack;
6546 Jim_ExprOperator *op;
6547
6548 /* Perform literal sharing with the current procedure
6549 * running only if this expression appears to be not generated
6550 * at runtime. */
6551 shareLiterals = objPtr->typePtr == &sourceObjType;
6552
6553 expr->opcode = NULL;
6554 expr->obj = NULL;
6555 expr->len = 0;
6556 expr->inUse = 1;
6557
6558 Jim_InitStack(&stack);
6559 JimParserInit(&parser, exprText, exprTextLen, 1);
6560 while(!JimParserEof(&parser)) {
6561 char *token;
6562 int len, type;
6563
6564 if (JimParseExpression(&parser) != JIM_OK) {
6565 Jim_SetResultString(interp, "Syntax error in expression", -1);
6566 goto err;
6567 }
6568 token = JimParserGetToken(&parser, &len, &type, NULL);
6569 if (type == JIM_TT_EOL) {
6570 Jim_Free(token);
6571 break;
6572 }
6573 switch(type) {
6574 case JIM_TT_STR:
6575 ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6576 break;
6577 case JIM_TT_ESC:
6578 ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6579 break;
6580 case JIM_TT_VAR:
6581 ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6582 break;
6583 case JIM_TT_DICTSUGAR:
6584 ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6585 break;
6586 case JIM_TT_CMD:
6587 ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6588 break;
6589 case JIM_TT_EXPR_NUMBER:
6590 ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6591 break;
6592 case JIM_TT_EXPR_OPERATOR:
6593 op = JimExprOperatorInfo(token);
6594 while(1) {
6595 Jim_ExprOperator *stackTopOp;
6596
6597 if (Jim_StackPeek(&stack) != NULL) {
6598 stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6599 } else {
6600 stackTopOp = NULL;
6601 }
6602 if (Jim_StackLen(&stack) && op->arity != 1 &&
6603 stackTopOp && stackTopOp->precedence >= op->precedence)
6604 {
6605 ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6606 Jim_StackPeek(&stack), -1);
6607 Jim_StackPop(&stack);
6608 } else {
6609 break;
6610 }
6611 }
6612 Jim_StackPush(&stack, token);
6613 break;
6614 case JIM_TT_SUBEXPR_START:
6615 Jim_StackPush(&stack, Jim_StrDup("("));
6616 Jim_Free(token);
6617 break;
6618 case JIM_TT_SUBEXPR_END:
6619 {
6620 int found = 0;
6621 while(Jim_StackLen(&stack)) {
6622 char *opstr = Jim_StackPop(&stack);
6623 if (!strcmp(opstr, "(")) {
6624 Jim_Free(opstr);
6625 found = 1;
6626 break;
6627 }
6628 op = JimExprOperatorInfo(opstr);
6629 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6630 }
6631 if (!found) {
6632 Jim_SetResultString(interp,
6633 "Unexpected close parenthesis", -1);
6634 goto err;
6635 }
6636 }
6637 Jim_Free(token);
6638 break;
6639 default:
6640 Jim_Panic(interp,"Default reached in SetExprFromAny()");
6641 break;
6642 }
6643 }
6644 while (Jim_StackLen(&stack)) {
6645 char *opstr = Jim_StackPop(&stack);
6646 op = JimExprOperatorInfo(opstr);
6647 if (op == NULL && !strcmp(opstr, "(")) {
6648 Jim_Free(opstr);
6649 Jim_SetResultString(interp, "Missing close parenthesis", -1);
6650 goto err;
6651 }
6652 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6653 }
6654 /* Check program correctness. */
6655 if (ExprCheckCorrectness(expr) != JIM_OK) {
6656 Jim_SetResultString(interp, "Invalid expression", -1);
6657 goto err;
6658 }
6659
6660 /* Free the stack used for the compilation. */
6661 Jim_FreeStackElements(&stack, Jim_Free);
6662 Jim_FreeStack(&stack);
6663
6664 /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6665 ExprMakeLazy(interp, expr);
6666
6667 /* Perform literal sharing */
6668 if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6669 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6670 if (bodyObjPtr->typePtr == &scriptObjType) {
6671 ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6672 ExprShareLiterals(interp, expr, bodyScript);
6673 }
6674 }
6675
6676 /* Free the old internal rep and set the new one. */
6677 Jim_FreeIntRep(interp, objPtr);
6678 Jim_SetIntRepPtr(objPtr, expr);
6679 objPtr->typePtr = &exprObjType;
6680 return JIM_OK;
6681
6682 err: /* we jump here on syntax/compile errors. */
6683 Jim_FreeStackElements(&stack, Jim_Free);
6684 Jim_FreeStack(&stack);
6685 Jim_Free(expr->opcode);
6686 for (i = 0; i < expr->len; i++) {
6687 Jim_DecrRefCount(interp,expr->obj[i]);
6688 }
6689 Jim_Free(expr->obj);
6690 Jim_Free(expr);
6691 return JIM_ERR;
6692 }
6693
6694 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6695 {
6696 if (objPtr->typePtr != &exprObjType) {
6697 if (SetExprFromAny(interp, objPtr) != JIM_OK)
6698 return NULL;
6699 }
6700 return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6701 }
6702
6703 /* -----------------------------------------------------------------------------
6704 * Expressions evaluation.
6705 * Jim uses a specialized stack-based virtual machine for expressions,
6706 * that takes advantage of the fact that expr's operators
6707 * can't be redefined.
6708 *
6709 * Jim_EvalExpression() uses the bytecode compiled by
6710 * SetExprFromAny() method of the "expression" object.
6711 *
6712 * On success a Tcl Object containing the result of the evaluation
6713 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6714 * returned.
6715 * On error the function returns a retcode != to JIM_OK and set a suitable
6716 * error on the interp.
6717 * ---------------------------------------------------------------------------*/
6718 #define JIM_EE_STATICSTACK_LEN 10
6719
6720 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6721 Jim_Obj **exprResultPtrPtr)
6722 {
6723 ExprByteCode *expr;
6724 Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6725 int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6726
6727 Jim_IncrRefCount(exprObjPtr);
6728 expr = Jim_GetExpression(interp, exprObjPtr);
6729 if (!expr) {
6730 Jim_DecrRefCount(interp, exprObjPtr);
6731 return JIM_ERR; /* error in expression. */
6732 }
6733 /* In order to avoid that the internal repr gets freed due to
6734 * shimmering of the exprObjPtr's object, we make the internal rep
6735 * shared. */
6736 expr->inUse++;
6737
6738 /* The stack-based expr VM itself */
6739
6740 /* Stack allocation. Expr programs have the feature that
6741 * a program of length N can't require a stack longer than
6742 * N. */
6743 if (expr->len > JIM_EE_STATICSTACK_LEN)
6744 stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6745 else
6746 stack = staticStack;
6747
6748 /* Execute every istruction */
6749 for (i = 0; i < expr->len; i++) {
6750 Jim_Obj *A, *B, *objPtr;
6751 jim_wide wA, wB, wC;
6752 double dA, dB, dC;
6753 const char *sA, *sB;
6754 int Alen, Blen, retcode;
6755 int opcode = expr->opcode[i];
6756
6757 if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6758 stack[stacklen++] = expr->obj[i];
6759 Jim_IncrRefCount(expr->obj[i]);
6760 } else if (opcode == JIM_EXPROP_VARIABLE) {
6761 objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6762 if (objPtr == NULL) {
6763 error = 1;
6764 goto err;
6765 }
6766 stack[stacklen++] = objPtr;
6767 Jim_IncrRefCount(objPtr);
6768 } else if (opcode == JIM_EXPROP_SUBST) {
6769 if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6770 &objPtr, JIM_NONE)) != JIM_OK)
6771 {
6772 error = 1;
6773 errRetCode = retcode;
6774 goto err;
6775 }
6776 stack[stacklen++] = objPtr;
6777 Jim_IncrRefCount(objPtr);
6778 } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6779 objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6780 if (objPtr == NULL) {
6781 error = 1;
6782 goto err;
6783 }
6784 stack[stacklen++] = objPtr;
6785 Jim_IncrRefCount(objPtr);
6786 } else if (opcode == JIM_EXPROP_COMMAND) {
6787 if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6788 error = 1;
6789 errRetCode = retcode;
6790 goto err;
6791 }
6792 stack[stacklen++] = interp->result;
6793 Jim_IncrRefCount(interp->result);
6794 } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6795 opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6796 {
6797 /* Note that there isn't to increment the
6798 * refcount of objects. the references are moved
6799 * from stack to A and B. */
6800 B = stack[--stacklen];
6801 A = stack[--stacklen];
6802
6803 /* --- Integer --- */
6804 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6805 (B->typePtr == &doubleObjType && !B->bytes) ||
6806 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6807 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6808 goto trydouble;
6809 }
6810 Jim_DecrRefCount(interp, A);
6811 Jim_DecrRefCount(interp, B);
6812 switch(expr->opcode[i]) {
6813 case JIM_EXPROP_ADD: wC = wA+wB; break;
6814 case JIM_EXPROP_SUB: wC = wA-wB; break;
6815 case JIM_EXPROP_MUL: wC = wA*wB; break;
6816 case JIM_EXPROP_LT: wC = wA<wB; break;
6817 case JIM_EXPROP_GT: wC = wA>wB; break;
6818 case JIM_EXPROP_LTE: wC = wA<=wB; break;
6819 case JIM_EXPROP_GTE: wC = wA>=wB; break;
6820 case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6821 case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6822 case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6823 case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6824 case JIM_EXPROP_BITAND: wC = wA&wB; break;
6825 case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6826 case JIM_EXPROP_BITOR: wC = wA|wB; break;
6827 case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6828 case JIM_EXPROP_LOGICAND_LEFT:
6829 if (wA == 0) {
6830 i += (int)wB;
6831 wC = 0;
6832 } else {
6833 continue;
6834 }
6835 break;
6836 case JIM_EXPROP_LOGICOR_LEFT:
6837 if (wA != 0) {
6838 i += (int)wB;
6839 wC = 1;
6840 } else {
6841 continue;
6842 }
6843 break;
6844 case JIM_EXPROP_DIV:
6845 if (wB == 0) goto divbyzero;
6846 wC = wA/wB;
6847 break;
6848 case JIM_EXPROP_MOD:
6849 if (wB == 0) goto divbyzero;
6850 wC = wA%wB;
6851 break;
6852 case JIM_EXPROP_ROTL: {
6853 /* uint32_t would be better. But not everyone has inttypes.h?*/
6854 unsigned long uA = (unsigned long)wA;
6855 #ifdef _MSC_VER
6856 wC = _rotl(uA,(unsigned long)wB);
6857 #else
6858 const unsigned int S = sizeof(unsigned long) * 8;
6859 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6860 #endif
6861 break;
6862 }
6863 case JIM_EXPROP_ROTR: {
6864 unsigned long uA = (unsigned long)wA;
6865 #ifdef _MSC_VER
6866 wC = _rotr(uA,(unsigned long)wB);
6867 #else
6868 const unsigned int S = sizeof(unsigned long) * 8;
6869 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6870 #endif
6871 break;
6872 }
6873
6874 default:
6875 wC = 0; /* avoid gcc warning */
6876 break;
6877 }
6878 stack[stacklen] = Jim_NewIntObj(interp, wC);
6879 Jim_IncrRefCount(stack[stacklen]);
6880 stacklen++;
6881 continue;
6882 trydouble:
6883 /* --- Double --- */
6884 if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6885 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6886 Jim_DecrRefCount(interp, A);
6887 Jim_DecrRefCount(interp, B);
6888 error = 1;
6889 goto err;
6890 }
6891 Jim_DecrRefCount(interp, A);
6892 Jim_DecrRefCount(interp, B);
6893 switch(expr->opcode[i]) {
6894 case JIM_EXPROP_ROTL:
6895 case JIM_EXPROP_ROTR:
6896 case JIM_EXPROP_LSHIFT:
6897 case JIM_EXPROP_RSHIFT:
6898 case JIM_EXPROP_BITAND:
6899 case JIM_EXPROP_BITXOR:
6900 case JIM_EXPROP_BITOR:
6901 case JIM_EXPROP_MOD:
6902 case JIM_EXPROP_POW:
6903 Jim_SetResultString(interp,
6904 "Got floating-point value where integer was expected", -1);
6905 error = 1;
6906 goto err;
6907 break;
6908 case JIM_EXPROP_ADD: dC = dA+dB; break;
6909 case JIM_EXPROP_SUB: dC = dA-dB; break;
6910 case JIM_EXPROP_MUL: dC = dA*dB; break;
6911 case JIM_EXPROP_LT: dC = dA<dB; break;
6912 case JIM_EXPROP_GT: dC = dA>dB; break;
6913 case JIM_EXPROP_LTE: dC = dA<=dB; break;
6914 case JIM_EXPROP_GTE: dC = dA>=dB; break;
6915 case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6916 case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6917 case JIM_EXPROP_LOGICAND_LEFT:
6918 if (dA == 0) {
6919 i += (int)dB;
6920 dC = 0;
6921 } else {
6922 continue;
6923 }
6924 break;
6925 case JIM_EXPROP_LOGICOR_LEFT:
6926 if (dA != 0) {
6927 i += (int)dB;
6928 dC = 1;
6929 } else {
6930 continue;
6931 }
6932 break;
6933 case JIM_EXPROP_DIV:
6934 if (dB == 0) goto divbyzero;
6935 dC = dA/dB;
6936 break;
6937 default:
6938 dC = 0; /* avoid gcc warning */
6939 break;
6940 }
6941 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6942 Jim_IncrRefCount(stack[stacklen]);
6943 stacklen++;
6944 } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6945 B = stack[--stacklen];
6946 A = stack[--stacklen];
6947 sA = Jim_GetString(A, &Alen);
6948 sB = Jim_GetString(B, &Blen);
6949 switch(expr->opcode[i]) {
6950 case JIM_EXPROP_STREQ:
6951 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6952 wC = 1;
6953 else
6954 wC = 0;
6955 break;
6956 case JIM_EXPROP_STRNE:
6957 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6958 wC = 1;
6959 else
6960 wC = 0;
6961 break;
6962 default:
6963 wC = 0; /* avoid gcc warning */
6964 break;
6965 }
6966 Jim_DecrRefCount(interp, A);
6967 Jim_DecrRefCount(interp, B);
6968 stack[stacklen] = Jim_NewIntObj(interp, wC);
6969 Jim_IncrRefCount(stack[stacklen]);
6970 stacklen++;
6971 } else if (opcode == JIM_EXPROP_NOT ||
6972 opcode == JIM_EXPROP_BITNOT ||
6973 opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6974 opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6975 /* Note that there isn't to increment the
6976 * refcount of objects. the references are moved
6977 * from stack to A and B. */
6978 A = stack[--stacklen];
6979
6980 /* --- Integer --- */
6981 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6982 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6983 goto trydouble_unary;
6984 }
6985 Jim_DecrRefCount(interp, A);
6986 switch(expr->opcode[i]) {
6987 case JIM_EXPROP_NOT: wC = !wA; break;
6988 case JIM_EXPROP_BITNOT: wC = ~wA; break;
6989 case JIM_EXPROP_LOGICAND_RIGHT:
6990 case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6991 default:
6992 wC = 0; /* avoid gcc warning */
6993 break;
6994 }
6995 stack[stacklen] = Jim_NewIntObj(interp, wC);
6996 Jim_IncrRefCount(stack[stacklen]);
6997 stacklen++;
6998 continue;
6999 trydouble_unary:
7000 /* --- Double --- */
7001 if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
7002 Jim_DecrRefCount(interp, A);
7003 error = 1;
7004 goto err;
7005 }
7006 Jim_DecrRefCount(interp, A);
7007 switch(expr->opcode[i]) {
7008 case JIM_EXPROP_NOT: dC = !dA; break;
7009 case JIM_EXPROP_LOGICAND_RIGHT:
7010 case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
7011 case JIM_EXPROP_BITNOT:
7012 Jim_SetResultString(interp,
7013 "Got floating-point value where integer was expected", -1);
7014 error = 1;
7015 goto err;
7016 break;
7017 default:
7018 dC = 0; /* avoid gcc warning */
7019 break;
7020 }
7021 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7022 Jim_IncrRefCount(stack[stacklen]);
7023 stacklen++;
7024 } else {
7025 Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
7026 }
7027 }
7028 err:
7029 /* There is no need to decerement the inUse field because
7030 * this reference is transfered back into the exprObjPtr. */
7031 Jim_FreeIntRep(interp, exprObjPtr);
7032 exprObjPtr->typePtr = &exprObjType;
7033 Jim_SetIntRepPtr(exprObjPtr, expr);
7034 Jim_DecrRefCount(interp, exprObjPtr);
7035 if (!error) {
7036 *exprResultPtrPtr = stack[0];
7037 Jim_IncrRefCount(stack[0]);
7038 errRetCode = JIM_OK;
7039 }
7040 for (i = 0; i < stacklen; i++) {
7041 Jim_DecrRefCount(interp, stack[i]);
7042 }
7043 if (stack != staticStack)
7044 Jim_Free(stack);
7045 return errRetCode;
7046 divbyzero:
7047 error = 1;
7048 Jim_SetResultString(interp, "Division by zero", -1);
7049 goto err;
7050 }
7051
7052 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7053 {
7054 int retcode;
7055 jim_wide wideValue;
7056 double doubleValue;
7057 Jim_Obj *exprResultPtr;
7058
7059 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7060 if (retcode != JIM_OK)
7061 return retcode;
7062 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7063 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7064 {
7065 Jim_DecrRefCount(interp, exprResultPtr);
7066 return JIM_ERR;
7067 } else {
7068 Jim_DecrRefCount(interp, exprResultPtr);
7069 *boolPtr = doubleValue != 0;
7070 return JIM_OK;
7071 }
7072 }
7073 Jim_DecrRefCount(interp, exprResultPtr);
7074 *boolPtr = wideValue != 0;
7075 return JIM_OK;
7076 }
7077
7078 /* -----------------------------------------------------------------------------
7079 * ScanFormat String Object
7080 * ---------------------------------------------------------------------------*/
7081
7082 /* This Jim_Obj will held a parsed representation of a format string passed to
7083 * the Jim_ScanString command. For error diagnostics, the scanformat string has
7084 * to be parsed in its entirely first and then, if correct, can be used for
7085 * scanning. To avoid endless re-parsing, the parsed representation will be
7086 * stored in an internal representation and re-used for performance reason. */
7087
7088 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7089 * scanformat string. This part will later be used to extract information
7090 * out from the string to be parsed by Jim_ScanString */
7091
7092 typedef struct ScanFmtPartDescr {
7093 char type; /* Type of conversion (e.g. c, d, f) */
7094 char modifier; /* Modify type (e.g. l - long, h - short */
7095 size_t width; /* Maximal width of input to be converted */
7096 int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
7097 char *arg; /* Specification of a CHARSET conversion */
7098 char *prefix; /* Prefix to be scanned literally before conversion */
7099 } ScanFmtPartDescr;
7100
7101 /* The ScanFmtStringObj will held the internal representation of a scanformat
7102 * string parsed and separated in part descriptions. Furthermore it contains
7103 * the original string representation of the scanformat string to allow for
7104 * fast update of the Jim_Obj's string representation part.
7105 *
7106 * As add-on the internal object representation add some scratch pad area
7107 * for usage by Jim_ScanString to avoid endless allocating and freeing of
7108 * memory for purpose of string scanning.
7109 *
7110 * The error member points to a static allocated string in case of a mal-
7111 * formed scanformat string or it contains '0' (NULL) in case of a valid
7112 * parse representation.
7113 *
7114 * The whole memory of the internal representation is allocated as a single
7115 * area of memory that will be internally separated. So freeing and duplicating
7116 * of such an object is cheap */
7117
7118 typedef struct ScanFmtStringObj {
7119 jim_wide size; /* Size of internal repr in bytes */
7120 char *stringRep; /* Original string representation */
7121 size_t count; /* Number of ScanFmtPartDescr contained */
7122 size_t convCount; /* Number of conversions that will assign */
7123 size_t maxPos; /* Max position index if XPG3 is used */
7124 const char *error; /* Ptr to error text (NULL if no error */
7125 char *scratch; /* Some scratch pad used by Jim_ScanString */
7126 ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
7127 } ScanFmtStringObj;
7128
7129
7130 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7131 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7132 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7133
7134 static Jim_ObjType scanFmtStringObjType = {
7135 "scanformatstring",
7136 FreeScanFmtInternalRep,
7137 DupScanFmtInternalRep,
7138 UpdateStringOfScanFmt,
7139 JIM_TYPE_NONE,
7140 };
7141
7142 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7143 {
7144 JIM_NOTUSED(interp);
7145 Jim_Free((char*)objPtr->internalRep.ptr);
7146 objPtr->internalRep.ptr = 0;
7147 }
7148
7149 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7150 {
7151 size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7152 ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7153
7154 JIM_NOTUSED(interp);
7155 memcpy(newVec, srcPtr->internalRep.ptr, size);
7156 dupPtr->internalRep.ptr = newVec;
7157 dupPtr->typePtr = &scanFmtStringObjType;
7158 }
7159
7160 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7161 {
7162 char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7163
7164 objPtr->bytes = Jim_StrDup(bytes);
7165 objPtr->length = strlen(bytes);
7166 }
7167
7168 /* SetScanFmtFromAny will parse a given string and create the internal
7169 * representation of the format specification. In case of an error
7170 * the error data member of the internal representation will be set
7171 * to an descriptive error text and the function will be left with
7172 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7173 * specification */
7174
7175 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7176 {
7177 ScanFmtStringObj *fmtObj;
7178 char *buffer;
7179 int maxCount, i, approxSize, lastPos = -1;
7180 const char *fmt = objPtr->bytes;
7181 int maxFmtLen = objPtr->length;
7182 const char *fmtEnd = fmt + maxFmtLen;
7183 int curr;
7184
7185 Jim_FreeIntRep(interp, objPtr);
7186 /* Count how many conversions could take place maximally */
7187 for (i=0, maxCount=0; i < maxFmtLen; ++i)
7188 if (fmt[i] == '%')
7189 ++maxCount;
7190 /* Calculate an approximation of the memory necessary */
7191 approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
7192 + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7193 + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
7194 + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
7195 + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
7196 + (maxCount +1) * sizeof(char) /* '\0' for every partial */
7197 + 1; /* safety byte */
7198 fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7199 memset(fmtObj, 0, approxSize);
7200 fmtObj->size = approxSize;
7201 fmtObj->maxPos = 0;
7202 fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7203 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7204 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7205 buffer = fmtObj->stringRep + maxFmtLen + 1;
7206 objPtr->internalRep.ptr = fmtObj;
7207 objPtr->typePtr = &scanFmtStringObjType;
7208 for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7209 int width=0, skip;
7210 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7211 fmtObj->count++;
7212 descr->width = 0; /* Assume width unspecified */
7213 /* Overread and store any "literal" prefix */
7214 if (*fmt != '%' || fmt[1] == '%') {
7215 descr->type = 0;
7216 descr->prefix = &buffer[i];
7217 for (; fmt < fmtEnd; ++fmt) {
7218 if (*fmt == '%') {
7219 if (fmt[1] != '%') break;
7220 ++fmt;
7221 }
7222 buffer[i++] = *fmt;
7223 }
7224 buffer[i++] = 0;
7225 }
7226 /* Skip the conversion introducing '%' sign */
7227 ++fmt;
7228 /* End reached due to non-conversion literal only? */
7229 if (fmt >= fmtEnd)
7230 goto done;
7231 descr->pos = 0; /* Assume "natural" positioning */
7232 if (*fmt == '*') {
7233 descr->pos = -1; /* Okay, conversion will not be assigned */
7234 ++fmt;
7235 } else
7236 fmtObj->convCount++; /* Otherwise count as assign-conversion */
7237 /* Check if next token is a number (could be width or pos */
7238 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7239 fmt += skip;
7240 /* Was the number a XPG3 position specifier? */
7241 if (descr->pos != -1 && *fmt == '$') {
7242 int prev;
7243 ++fmt;
7244 descr->pos = width;
7245 width = 0;
7246 /* Look if "natural" postioning and XPG3 one was mixed */
7247 if ((lastPos == 0 && descr->pos > 0)
7248 || (lastPos > 0 && descr->pos == 0)) {
7249 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7250 return JIM_ERR;
7251 }
7252 /* Look if this position was already used */
7253 for (prev=0; prev < curr; ++prev) {
7254 if (fmtObj->descr[prev].pos == -1) continue;
7255 if (fmtObj->descr[prev].pos == descr->pos) {
7256 fmtObj->error = "same \"%n$\" conversion specifier "
7257 "used more than once";
7258 return JIM_ERR;
7259 }
7260 }
7261 /* Try to find a width after the XPG3 specifier */
7262 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7263 descr->width = width;
7264 fmt += skip;
7265 }
7266 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7267 fmtObj->maxPos = descr->pos;
7268 } else {
7269 /* Number was not a XPG3, so it has to be a width */
7270 descr->width = width;
7271 }
7272 }
7273 /* If positioning mode was undetermined yet, fix this */
7274 if (lastPos == -1)
7275 lastPos = descr->pos;
7276 /* Handle CHARSET conversion type ... */
7277 if (*fmt == '[') {
7278 int swapped = 1, beg = i, end, j;
7279 descr->type = '[';
7280 descr->arg = &buffer[i];
7281 ++fmt;
7282 if (*fmt == '^') buffer[i++] = *fmt++;
7283 if (*fmt == ']') buffer[i++] = *fmt++;
7284 while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7285 if (*fmt != ']') {
7286 fmtObj->error = "unmatched [ in format string";
7287 return JIM_ERR;
7288 }
7289 end = i;
7290 buffer[i++] = 0;
7291 /* In case a range fence was given "backwards", swap it */
7292 while (swapped) {
7293 swapped = 0;
7294 for (j=beg+1; j < end-1; ++j) {
7295 if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7296 char tmp = buffer[j-1];
7297 buffer[j-1] = buffer[j+1];
7298 buffer[j+1] = tmp;
7299 swapped = 1;
7300 }
7301 }
7302 }
7303 } else {
7304 /* Remember any valid modifier if given */
7305 if (strchr("hlL", *fmt) != 0)
7306 descr->modifier = tolower((int)*fmt++);
7307
7308 descr->type = *fmt;
7309 if (strchr("efgcsndoxui", *fmt) == 0) {
7310 fmtObj->error = "bad scan conversion character";
7311 return JIM_ERR;
7312 } else if (*fmt == 'c' && descr->width != 0) {
7313 fmtObj->error = "field width may not be specified in %c "
7314 "conversion";
7315 return JIM_ERR;
7316 } else if (*fmt == 'u' && descr->modifier == 'l') {
7317 fmtObj->error = "unsigned wide not supported";
7318 return JIM_ERR;
7319 }
7320 }
7321 curr++;
7322 }
7323 done:
7324 if (fmtObj->convCount == 0) {
7325 fmtObj->error = "no any conversion specifier given";
7326 return JIM_ERR;
7327 }
7328 return JIM_OK;
7329 }
7330
7331 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7332
7333 #define FormatGetCnvCount(_fo_) \
7334 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7335 #define FormatGetMaxPos(_fo_) \
7336 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7337 #define FormatGetError(_fo_) \
7338 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7339
7340 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7341 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7342 * bitvector implementation in Jim? */
7343
7344 static int JimTestBit(const char *bitvec, char ch)
7345 {
7346 div_t pos = div(ch-1, 8);
7347 return bitvec[pos.quot] & (1 << pos.rem);
7348 }
7349
7350 static void JimSetBit(char *bitvec, char ch)
7351 {
7352 div_t pos = div(ch-1, 8);
7353 bitvec[pos.quot] |= (1 << pos.rem);
7354 }
7355
7356 #if 0 /* currently not used */
7357 static void JimClearBit(char *bitvec, char ch)
7358 {
7359 div_t pos = div(ch-1, 8);
7360 bitvec[pos.quot] &= ~(1 << pos.rem);
7361 }
7362 #endif
7363
7364 /* JimScanAString is used to scan an unspecified string that ends with
7365 * next WS, or a string that is specified via a charset. The charset
7366 * is currently implemented in a way to only allow for usage with
7367 * ASCII. Whenever we will switch to UNICODE, another idea has to
7368 * be born :-/
7369 *
7370 * FIXME: Works only with ASCII */
7371
7372 static Jim_Obj *
7373 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7374 {
7375 size_t i;
7376 Jim_Obj *result;
7377 char charset[256/8+1]; /* A Charset may contain max 256 chars */
7378 char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7379
7380 /* First init charset to nothing or all, depending if a specified
7381 * or an unspecified string has to be parsed */
7382 memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7383 if (sdescr) {
7384 /* There was a set description given, that means we are parsing
7385 * a specified string. So we have to build a corresponding
7386 * charset reflecting the description */
7387 int notFlag = 0;
7388 /* Should the set be negated at the end? */
7389 if (*sdescr == '^') {
7390 notFlag = 1;
7391 ++sdescr;
7392 }
7393 /* Here '-' is meant literally and not to define a range */
7394 if (*sdescr == '-') {
7395 JimSetBit(charset, '-');
7396 ++sdescr;
7397 }
7398 while (*sdescr) {
7399 if (sdescr[1] == '-' && sdescr[2] != 0) {
7400 /* Handle range definitions */
7401 int i;
7402 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7403 JimSetBit(charset, (char)i);
7404 sdescr += 3;
7405 } else {
7406 /* Handle verbatim character definitions */
7407 JimSetBit(charset, *sdescr++);
7408 }
7409 }
7410 /* Negate the charset if there was a NOT given */
7411 for (i=0; notFlag && i < sizeof(charset); ++i)
7412 charset[i] = ~charset[i];
7413 }
7414 /* And after all the mess above, the real work begin ... */
7415 while (str && *str) {
7416 if (!sdescr && isspace((int)*str))
7417 break; /* EOS via WS if unspecified */
7418 if (JimTestBit(charset, *str)) *buffer++ = *str++;
7419 else break; /* EOS via mismatch if specified scanning */
7420 }
7421 *buffer = 0; /* Close the string properly ... */
7422 result = Jim_NewStringObj(interp, anchor, -1);
7423 Jim_Free(anchor); /* ... and free it afer usage */
7424 return result;
7425 }
7426
7427 /* ScanOneEntry will scan one entry out of the string passed as argument.
7428 * It use the sscanf() function for this task. After extracting and
7429 * converting of the value, the count of scanned characters will be
7430 * returned of -1 in case of no conversion tool place and string was
7431 * already scanned thru */
7432
7433 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7434 ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7435 {
7436 # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7437 ? sizeof(jim_wide) \
7438 : sizeof(double))
7439 char buffer[MAX_SIZE];
7440 char *value = buffer;
7441 const char *tok;
7442 const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7443 size_t sLen = strlen(&str[pos]), scanned = 0;
7444 size_t anchor = pos;
7445 int i;
7446
7447 /* First pessimiticly assume, we will not scan anything :-) */
7448 *valObjPtr = 0;
7449 if (descr->prefix) {
7450 /* There was a prefix given before the conversion, skip it and adjust
7451 * the string-to-be-parsed accordingly */
7452 for (i=0; str[pos] && descr->prefix[i]; ++i) {
7453 /* If prefix require, skip WS */
7454 if (isspace((int)descr->prefix[i]))
7455 while (str[pos] && isspace((int)str[pos])) ++pos;
7456 else if (descr->prefix[i] != str[pos])
7457 break; /* Prefix do not match here, leave the loop */
7458 else
7459 ++pos; /* Prefix matched so far, next round */
7460 }
7461 if (str[pos] == 0)
7462 return -1; /* All of str consumed: EOF condition */
7463 else if (descr->prefix[i] != 0)
7464 return 0; /* Not whole prefix consumed, no conversion possible */
7465 }
7466 /* For all but following conversion, skip leading WS */
7467 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7468 while (isspace((int)str[pos])) ++pos;
7469 /* Determine how much skipped/scanned so far */
7470 scanned = pos - anchor;
7471 if (descr->type == 'n') {
7472 /* Return pseudo conversion means: how much scanned so far? */
7473 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7474 } else if (str[pos] == 0) {
7475 /* Cannot scan anything, as str is totally consumed */
7476 return -1;
7477 } else {
7478 /* Processing of conversions follows ... */
7479 if (descr->width > 0) {
7480 /* Do not try to scan as fas as possible but only the given width.
7481 * To ensure this, we copy the part that should be scanned. */
7482 size_t tLen = descr->width > sLen ? sLen : descr->width;
7483 tok = Jim_StrDupLen(&str[pos], tLen);
7484 } else {
7485 /* As no width was given, simply refer to the original string */
7486 tok = &str[pos];
7487 }
7488 switch (descr->type) {
7489 case 'c':
7490 *valObjPtr = Jim_NewIntObj(interp, *tok);
7491 scanned += 1;
7492 break;
7493 case 'd': case 'o': case 'x': case 'u': case 'i': {
7494 char *endp; /* Position where the number finished */
7495 int base = descr->type == 'o' ? 8
7496 : descr->type == 'x' ? 16
7497 : descr->type == 'i' ? 0
7498 : 10;
7499
7500 do {
7501 /* Try to scan a number with the given base */
7502 if (descr->modifier == 'l')
7503 #ifdef HAVE_LONG_LONG
7504 *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7505 #else
7506 *(jim_wide*)value = strtol(tok, &endp, base);
7507 #endif
7508 else
7509 if (descr->type == 'u')
7510 *(long*)value = strtoul(tok, &endp, base);
7511 else
7512 *(long*)value = strtol(tok, &endp, base);
7513 /* If scanning failed, and base was undetermined, simply
7514 * put it to 10 and try once more. This should catch the
7515 * case where %i begin to parse a number prefix (e.g.
7516 * '0x' but no further digits follows. This will be
7517 * handled as a ZERO followed by a char 'x' by Tcl */
7518 if (endp == tok && base == 0) base = 10;
7519 else break;
7520 } while (1);
7521 if (endp != tok) {
7522 /* There was some number sucessfully scanned! */
7523 if (descr->modifier == 'l')
7524 *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7525 else
7526 *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7527 /* Adjust the number-of-chars scanned so far */
7528 scanned += endp - tok;
7529 } else {
7530 /* Nothing was scanned. We have to determine if this
7531 * happened due to e.g. prefix mismatch or input str
7532 * exhausted */
7533 scanned = *tok ? 0 : -1;
7534 }
7535 break;
7536 }
7537 case 's': case '[': {
7538 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7539 scanned += Jim_Length(*valObjPtr);
7540 break;
7541 }
7542 case 'e': case 'f': case 'g': {
7543 char *endp;
7544
7545 *(double*)value = strtod(tok, &endp);
7546 if (endp != tok) {
7547 /* There was some number sucessfully scanned! */
7548 *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7549 /* Adjust the number-of-chars scanned so far */
7550 scanned += endp - tok;
7551 } else {
7552 /* Nothing was scanned. We have to determine if this
7553 * happened due to e.g. prefix mismatch or input str
7554 * exhausted */
7555 scanned = *tok ? 0 : -1;
7556 }
7557 break;
7558 }
7559 }
7560 /* If a substring was allocated (due to pre-defined width) do not
7561 * forget to free it */
7562 if (tok != &str[pos])
7563 Jim_Free((char*)tok);
7564 }
7565 return scanned;
7566 }
7567
7568 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7569 * string and returns all converted (and not ignored) values in a list back
7570 * to the caller. If an error occured, a NULL pointer will be returned */
7571
7572 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7573 Jim_Obj *fmtObjPtr, int flags)
7574 {
7575 size_t i, pos;
7576 int scanned = 1;
7577 const char *str = Jim_GetString(strObjPtr, 0);
7578 Jim_Obj *resultList = 0;
7579 Jim_Obj **resultVec;
7580 int resultc;
7581 Jim_Obj *emptyStr = 0;
7582 ScanFmtStringObj *fmtObj;
7583
7584 /* If format specification is not an object, convert it! */
7585 if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7586 SetScanFmtFromAny(interp, fmtObjPtr);
7587 fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7588 /* Check if format specification was valid */
7589 if (fmtObj->error != 0) {
7590 if (flags & JIM_ERRMSG)
7591 Jim_SetResultString(interp, fmtObj->error, -1);
7592 return 0;
7593 }
7594 /* Allocate a new "shared" empty string for all unassigned conversions */
7595 emptyStr = Jim_NewEmptyStringObj(interp);
7596 Jim_IncrRefCount(emptyStr);
7597 /* Create a list and fill it with empty strings up to max specified XPG3 */
7598 resultList = Jim_NewListObj(interp, 0, 0);
7599 if (fmtObj->maxPos > 0) {
7600 for (i=0; i < fmtObj->maxPos; ++i)
7601 Jim_ListAppendElement(interp, resultList, emptyStr);
7602 JimListGetElements(interp, resultList, &resultc, &resultVec);
7603 }
7604 /* Now handle every partial format description */
7605 for (i=0, pos=0; i < fmtObj->count; ++i) {
7606 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7607 Jim_Obj *value = 0;
7608 /* Only last type may be "literal" w/o conversion - skip it! */
7609 if (descr->type == 0) continue;
7610 /* As long as any conversion could be done, we will proceed */
7611 if (scanned > 0)
7612 scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7613 /* In case our first try results in EOF, we will leave */
7614 if (scanned == -1 && i == 0)
7615 goto eof;
7616 /* Advance next pos-to-be-scanned for the amount scanned already */
7617 pos += scanned;
7618 /* value == 0 means no conversion took place so take empty string */
7619 if (value == 0)
7620 value = Jim_NewEmptyStringObj(interp);
7621 /* If value is a non-assignable one, skip it */
7622 if (descr->pos == -1) {
7623 Jim_FreeNewObj(interp, value);
7624 } else if (descr->pos == 0)
7625 /* Otherwise append it to the result list if no XPG3 was given */
7626 Jim_ListAppendElement(interp, resultList, value);
7627 else if (resultVec[descr->pos-1] == emptyStr) {
7628 /* But due to given XPG3, put the value into the corr. slot */
7629 Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7630 Jim_IncrRefCount(value);
7631 resultVec[descr->pos-1] = value;
7632 } else {
7633 /* Otherwise, the slot was already used - free obj and ERROR */
7634 Jim_FreeNewObj(interp, value);
7635 goto err;
7636 }
7637 }
7638 Jim_DecrRefCount(interp, emptyStr);
7639 return resultList;
7640 eof:
7641 Jim_DecrRefCount(interp, emptyStr);
7642 Jim_FreeNewObj(interp, resultList);
7643 return (Jim_Obj*)EOF;
7644 err:
7645 Jim_DecrRefCount(interp, emptyStr);
7646 Jim_FreeNewObj(interp, resultList);
7647 return 0;
7648 }
7649
7650 /* -----------------------------------------------------------------------------
7651 * Pseudo Random Number Generation
7652 * ---------------------------------------------------------------------------*/
7653 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7654 int seedLen);
7655
7656 /* Initialize the sbox with the numbers from 0 to 255 */
7657 static void JimPrngInit(Jim_Interp *interp)
7658 {
7659 int i;
7660 unsigned int seed[256];
7661
7662 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7663 for (i = 0; i < 256; i++)
7664 seed[i] = (rand() ^ time(NULL) ^ clock());
7665 JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7666 }
7667
7668 /* Generates N bytes of random data */
7669 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7670 {
7671 Jim_PrngState *prng;
7672 unsigned char *destByte = (unsigned char*) dest;
7673 unsigned int si, sj, x;
7674
7675 /* initialization, only needed the first time */
7676 if (interp->prngState == NULL)
7677 JimPrngInit(interp);
7678 prng = interp->prngState;
7679 /* generates 'len' bytes of pseudo-random numbers */
7680 for (x = 0; x < len; x++) {
7681 prng->i = (prng->i+1) & 0xff;
7682 si = prng->sbox[prng->i];
7683 prng->j = (prng->j + si) & 0xff;
7684 sj = prng->sbox[prng->j];
7685 prng->sbox[prng->i] = sj;
7686 prng->sbox[prng->j] = si;
7687 *destByte++ = prng->sbox[(si+sj)&0xff];
7688 }
7689 }
7690
7691 /* Re-seed the generator with user-provided bytes */
7692 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7693 int seedLen)
7694 {
7695 int i;
7696 unsigned char buf[256];
7697 Jim_PrngState *prng;
7698
7699 /* initialization, only needed the first time */
7700 if (interp->prngState == NULL)
7701 JimPrngInit(interp);
7702 prng = interp->prngState;
7703
7704 /* Set the sbox[i] with i */
7705 for (i = 0; i < 256; i++)
7706 prng->sbox[i] = i;
7707 /* Now use the seed to perform a random permutation of the sbox */
7708 for (i = 0; i < seedLen; i++) {
7709 unsigned char t;
7710
7711 t = prng->sbox[i&0xFF];
7712 prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7713 prng->sbox[seed[i]] = t;
7714 }
7715 prng->i = prng->j = 0;
7716 /* discard the first 256 bytes of stream. */
7717 JimRandomBytes(interp, buf, 256);
7718 }
7719
7720 /* -----------------------------------------------------------------------------
7721 * Dynamic libraries support (WIN32 not supported)
7722 * ---------------------------------------------------------------------------*/
7723
7724 #ifdef JIM_DYNLIB
7725 #ifdef WIN32
7726 #define RTLD_LAZY 0
7727 void * dlopen(const char *path, int mode)
7728 {
7729 JIM_NOTUSED(mode);
7730
7731 return (void *)LoadLibraryA(path);
7732 }
7733 int dlclose(void *handle)
7734 {
7735 FreeLibrary((HANDLE)handle);
7736 return 0;
7737 }
7738 void *dlsym(void *handle, const char *symbol)
7739 {
7740 return GetProcAddress((HMODULE)handle, symbol);
7741 }
7742 static char win32_dlerror_string[121];
7743 const char *dlerror()
7744 {
7745 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7746 LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7747 return win32_dlerror_string;
7748 }
7749 #endif /* WIN32 */
7750
7751 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7752 {
7753 Jim_Obj *libPathObjPtr;
7754 int prefixc, i;
7755 void *handle;
7756 int (*onload)(Jim_Interp *interp);
7757
7758 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7759 if (libPathObjPtr == NULL) {
7760 prefixc = 0;
7761 libPathObjPtr = NULL;
7762 } else {
7763 Jim_IncrRefCount(libPathObjPtr);
7764 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7765 }
7766
7767 for (i = -1; i < prefixc; i++) {
7768 if (i < 0) {
7769 handle = dlopen(pathName, RTLD_LAZY);
7770 } else {
7771 FILE *fp;
7772 char buf[JIM_PATH_LEN];
7773 const char *prefix;
7774 int prefixlen;
7775 Jim_Obj *prefixObjPtr;
7776
7777 buf[0] = '\0';
7778 if (Jim_ListIndex(interp, libPathObjPtr, i,
7779 &prefixObjPtr, JIM_NONE) != JIM_OK)
7780 continue;
7781 prefix = Jim_GetString(prefixObjPtr, NULL);
7782 prefixlen = strlen(prefix);
7783 if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7784 continue;
7785 if (prefixlen && prefix[prefixlen-1] == '/')
7786 sprintf(buf, "%s%s", prefix, pathName);
7787 else
7788 sprintf(buf, "%s/%s", prefix, pathName);
7789 printf("opening '%s'\n", buf);
7790 fp = fopen(buf, "r");
7791 if (fp == NULL)
7792 continue;
7793 fclose(fp);
7794 handle = dlopen(buf, RTLD_LAZY);
7795 printf("got handle %p\n", handle);
7796 }
7797 if (handle == NULL) {
7798 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7799 Jim_AppendStrings(interp, Jim_GetResult(interp),
7800 "error loading extension \"", pathName,
7801 "\": ", dlerror(), NULL);
7802 if (i < 0)
7803 continue;
7804 goto err;
7805 }
7806 if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7807 Jim_SetResultString(interp,
7808 "No Jim_OnLoad symbol found on extension", -1);
7809 goto err;
7810 }
7811 if (onload(interp) == JIM_ERR) {
7812 dlclose(handle);
7813 goto err;
7814 }
7815 Jim_SetEmptyResult(interp);
7816 if (libPathObjPtr != NULL)
7817 Jim_DecrRefCount(interp, libPathObjPtr);
7818 return JIM_OK;
7819 }
7820 err:
7821 if (libPathObjPtr != NULL)
7822 Jim_DecrRefCount(interp, libPathObjPtr);
7823 return JIM_ERR;
7824 }
7825 #else /* JIM_DYNLIB */
7826 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7827 {
7828 JIM_NOTUSED(interp);
7829 JIM_NOTUSED(pathName);
7830
7831 Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7832 return JIM_ERR;
7833 }
7834 #endif/* JIM_DYNLIB */
7835
7836 /* -----------------------------------------------------------------------------
7837 * Packages handling
7838 * ---------------------------------------------------------------------------*/
7839
7840 #define JIM_PKG_ANY_VERSION -1
7841
7842 /* Convert a string of the type "1.2" into an integer.
7843 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
7844 * to the integer with value 102 */
7845 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7846 int *intPtr, int flags)
7847 {
7848 char *copy;
7849 jim_wide major, minor;
7850 char *majorStr, *minorStr, *p;
7851
7852 if (v[0] == '\0') {
7853 *intPtr = JIM_PKG_ANY_VERSION;
7854 return JIM_OK;
7855 }
7856
7857 copy = Jim_StrDup(v);
7858 p = strchr(copy, '.');
7859 if (p == NULL) goto badfmt;
7860 *p = '\0';
7861 majorStr = copy;
7862 minorStr = p+1;
7863
7864 if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7865 Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7866 goto badfmt;
7867 *intPtr = (int)(major*100+minor);
7868 Jim_Free(copy);
7869 return JIM_OK;
7870
7871 badfmt:
7872 Jim_Free(copy);
7873 if (flags & JIM_ERRMSG) {
7874 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7875 Jim_AppendStrings(interp, Jim_GetResult(interp),
7876 "invalid package version '", v, "'", NULL);
7877 }
7878 return JIM_ERR;
7879 }
7880
7881 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7882 static int JimPackageMatchVersion(int needed, int actual, int flags)
7883 {
7884 if (needed == JIM_PKG_ANY_VERSION) return 1;
7885 if (flags & JIM_MATCHVER_EXACT) {
7886 return needed == actual;
7887 } else {
7888 return needed/100 == actual/100 && (needed <= actual);
7889 }
7890 }
7891
7892 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7893 int flags)
7894 {
7895 int intVersion;
7896 /* Check if the version format is ok */
7897 if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7898 return JIM_ERR;
7899 /* If the package was already provided returns an error. */
7900 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7901 if (flags & JIM_ERRMSG) {
7902 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7903 Jim_AppendStrings(interp, Jim_GetResult(interp),
7904 "package '", name, "' was already provided", NULL);
7905 }
7906 return JIM_ERR;
7907 }
7908 Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7909 return JIM_OK;
7910 }
7911
7912 #ifndef JIM_ANSIC
7913
7914 #ifndef WIN32
7915 # include <sys/types.h>
7916 # include <dirent.h>
7917 #else
7918 # include <io.h>
7919 /* Posix dirent.h compatiblity layer for WIN32.
7920 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7921 * Copyright Salvatore Sanfilippo ,2005.
7922 *
7923 * Permission to use, copy, modify, and distribute this software and its
7924 * documentation for any purpose is hereby granted without fee, provided
7925 * that this copyright and permissions notice appear in all copies and
7926 * derivatives.
7927 *
7928 * This software is supplied "as is" without express or implied warranty.
7929 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7930 */
7931
7932 struct dirent {
7933 char *d_name;
7934 };
7935
7936 typedef struct DIR {
7937 long handle; /* -1 for failed rewind */
7938 struct _finddata_t info;
7939 struct dirent result; /* d_name null iff first time */
7940 char *name; /* null-terminated char string */
7941 } DIR;
7942
7943 DIR *opendir(const char *name)
7944 {
7945 DIR *dir = 0;
7946
7947 if(name && name[0]) {
7948 size_t base_length = strlen(name);
7949 const char *all = /* search pattern must end with suitable wildcard */
7950 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7951
7952 if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7953 (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7954 {
7955 strcat(strcpy(dir->name, name), all);
7956
7957 if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7958 dir->result.d_name = 0;
7959 else { /* rollback */
7960 Jim_Free(dir->name);
7961 Jim_Free(dir);
7962 dir = 0;
7963 }
7964 } else { /* rollback */
7965 Jim_Free(dir);
7966 dir = 0;
7967 errno = ENOMEM;
7968 }
7969 } else {
7970 errno = EINVAL;
7971 }
7972 return dir;
7973 }
7974
7975 int closedir(DIR *dir)
7976 {
7977 int result = -1;
7978
7979 if(dir) {
7980 if(dir->handle != -1)
7981 result = _findclose(dir->handle);
7982 Jim_Free(dir->name);
7983 Jim_Free(dir);
7984 }
7985 if(result == -1) /* map all errors to EBADF */
7986 errno = EBADF;
7987 return result;
7988 }
7989
7990 struct dirent *readdir(DIR *dir)
7991 {
7992 struct dirent *result = 0;
7993
7994 if(dir && dir->handle != -1) {
7995 if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7996 result = &dir->result;
7997 result->d_name = dir->info.name;
7998 }
7999 } else {
8000 errno = EBADF;
8001 }
8002 return result;
8003 }
8004
8005 #endif /* WIN32 */
8006
8007 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8008 int prefixc, const char *pkgName, int pkgVer, int flags)
8009 {
8010 int bestVer = -1, i;
8011 int pkgNameLen = strlen(pkgName);
8012 char *bestPackage = NULL;
8013 struct dirent *de;
8014
8015 for (i = 0; i < prefixc; i++) {
8016 DIR *dir;
8017 char buf[JIM_PATH_LEN];
8018 int prefixLen;
8019
8020 if (prefixes[i] == NULL) continue;
8021 strncpy(buf, prefixes[i], JIM_PATH_LEN);
8022 buf[JIM_PATH_LEN-1] = '\0';
8023 prefixLen = strlen(buf);
8024 if (prefixLen && buf[prefixLen-1] == '/')
8025 buf[prefixLen-1] = '\0';
8026
8027 if ((dir = opendir(buf)) == NULL) continue;
8028 while ((de = readdir(dir)) != NULL) {
8029 char *fileName = de->d_name;
8030 int fileNameLen = strlen(fileName);
8031
8032 if (strncmp(fileName, "jim-", 4) == 0 &&
8033 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
8034 *(fileName+4+pkgNameLen) == '-' &&
8035 fileNameLen > 4 && /* note that this is not really useful */
8036 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
8037 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
8038 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
8039 {
8040 char ver[6]; /* xx.yy<nulterm> */
8041 char *p = strrchr(fileName, '.');
8042 int verLen, fileVer;
8043
8044 verLen = p - (fileName+4+pkgNameLen+1);
8045 if (verLen < 3 || verLen > 5) continue;
8046 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8047 ver[verLen] = '\0';
8048 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8049 != JIM_OK) continue;
8050 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8051 (bestVer == -1 || bestVer < fileVer))
8052 {
8053 bestVer = fileVer;
8054 Jim_Free(bestPackage);
8055 bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8056 sprintf(bestPackage, "%s/%s", buf, fileName);
8057 }
8058 }
8059 }
8060 closedir(dir);
8061 }
8062 return bestPackage;
8063 }
8064
8065 #else /* JIM_ANSIC */
8066
8067 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8068 int prefixc, const char *pkgName, int pkgVer, int flags)
8069 {
8070 JIM_NOTUSED(interp);
8071 JIM_NOTUSED(prefixes);
8072 JIM_NOTUSED(prefixc);
8073 JIM_NOTUSED(pkgName);
8074 JIM_NOTUSED(pkgVer);
8075 JIM_NOTUSED(flags);
8076 return NULL;
8077 }
8078
8079 #endif /* JIM_ANSIC */
8080
8081 /* Search for a suitable package under every dir specified by jim_libpath
8082 * and load it if possible. If a suitable package was loaded with success
8083 * JIM_OK is returned, otherwise JIM_ERR is returned. */
8084 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8085 int flags)
8086 {
8087 Jim_Obj *libPathObjPtr;
8088 char **prefixes, *best;
8089 int prefixc, i, retCode = JIM_OK;
8090
8091 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8092 if (libPathObjPtr == NULL) {
8093 prefixc = 0;
8094 libPathObjPtr = NULL;
8095 } else {
8096 Jim_IncrRefCount(libPathObjPtr);
8097 Jim_ListLength(interp, libPathObjPtr, &prefixc);
8098 }
8099
8100 prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8101 for (i = 0; i < prefixc; i++) {
8102 Jim_Obj *prefixObjPtr;
8103 if (Jim_ListIndex(interp, libPathObjPtr, i,
8104 &prefixObjPtr, JIM_NONE) != JIM_OK)
8105 {
8106 prefixes[i] = NULL;
8107 continue;
8108 }
8109 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8110 }
8111 /* Scan every directory to find the "best" package. */
8112 best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8113 if (best != NULL) {
8114 char *p = strrchr(best, '.');
8115 /* Try to load/source it */
8116 if (p && strcmp(p, ".tcl") == 0) {
8117 retCode = Jim_EvalFile(interp, best);
8118 } else {
8119 retCode = Jim_LoadLibrary(interp, best);
8120 }
8121 } else {
8122 retCode = JIM_ERR;
8123 }
8124 Jim_Free(best);
8125 for (i = 0; i < prefixc; i++)
8126 Jim_Free(prefixes[i]);
8127 Jim_Free(prefixes);
8128 if (libPathObjPtr)
8129 Jim_DecrRefCount(interp, libPathObjPtr);
8130 return retCode;
8131 }
8132
8133 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8134 const char *ver, int flags)
8135 {
8136 Jim_HashEntry *he;
8137 int requiredVer;
8138
8139 if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8140 return NULL;
8141 he = Jim_FindHashEntry(&interp->packages, name);
8142 if (he == NULL) {
8143 /* Try to load the package. */
8144 if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8145 he = Jim_FindHashEntry(&interp->packages, name);
8146 if (he == NULL) {
8147 return "?";
8148 }
8149 return he->val;
8150 }
8151 /* No way... return an error. */
8152 if (flags & JIM_ERRMSG) {
8153 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8154 Jim_AppendStrings(interp, Jim_GetResult(interp),
8155 "Can't find package '", name, "'", NULL);
8156 }
8157 return NULL;
8158 } else {
8159 int actualVer;
8160 if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8161 != JIM_OK)
8162 {
8163 return NULL;
8164 }
8165 /* Check if version matches. */
8166 if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8167 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8168 Jim_AppendStrings(interp, Jim_GetResult(interp),
8169 "Package '", name, "' already loaded, but with version ",
8170 he->val, NULL);
8171 return NULL;
8172 }
8173 return he->val;
8174 }
8175 }
8176
8177 /* -----------------------------------------------------------------------------
8178 * Eval
8179 * ---------------------------------------------------------------------------*/
8180 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8181 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8182
8183 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8184 Jim_Obj *const *argv);
8185
8186 /* Handle calls to the [unknown] command */
8187 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8188 {
8189 Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8190 int retCode;
8191
8192 /* If the [unknown] command does not exists returns
8193 * just now */
8194 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8195 return JIM_ERR;
8196
8197 /* The object interp->unknown just contains
8198 * the "unknown" string, it is used in order to
8199 * avoid to lookup the unknown command every time
8200 * but instread to cache the result. */
8201 if (argc+1 <= JIM_EVAL_SARGV_LEN)
8202 v = sv;
8203 else
8204 v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8205 /* Make a copy of the arguments vector, but shifted on
8206 * the right of one position. The command name of the
8207 * command will be instead the first argument of the
8208 * [unknonw] call. */
8209 memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8210 v[0] = interp->unknown;
8211 /* Call it */
8212 retCode = Jim_EvalObjVector(interp, argc+1, v);
8213 /* Clean up */
8214 if (v != sv)
8215 Jim_Free(v);
8216 return retCode;
8217 }
8218
8219 /* Eval the object vector 'objv' composed of 'objc' elements.
8220 * Every element is used as single argument.
8221 * Jim_EvalObj() will call this function every time its object
8222 * argument is of "list" type, with no string representation.
8223 *
8224 * This is possible because the string representation of a
8225 * list object generated by the UpdateStringOfList is made
8226 * in a way that ensures that every list element is a different
8227 * command argument. */
8228 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8229 {
8230 int i, retcode;
8231 Jim_Cmd *cmdPtr;
8232
8233 /* Incr refcount of arguments. */
8234 for (i = 0; i < objc; i++)
8235 Jim_IncrRefCount(objv[i]);
8236 /* Command lookup */
8237 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8238 if (cmdPtr == NULL) {
8239 retcode = JimUnknown(interp, objc, objv);
8240 } else {
8241 /* Call it -- Make sure result is an empty object. */
8242 Jim_SetEmptyResult(interp);
8243 if (cmdPtr->cmdProc) {
8244 interp->cmdPrivData = cmdPtr->privData;
8245 retcode = cmdPtr->cmdProc(interp, objc, objv);
8246 } else {
8247 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8248 if (retcode == JIM_ERR) {
8249 JimAppendStackTrace(interp,
8250 Jim_GetString(objv[0], NULL), "?", 1);
8251 }
8252 }
8253 }
8254 /* Decr refcount of arguments and return the retcode */
8255 for (i = 0; i < objc; i++)
8256 Jim_DecrRefCount(interp, objv[i]);
8257 return retcode;
8258 }
8259
8260 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8261 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8262 * The returned object has refcount = 0. */
8263 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8264 int tokens, Jim_Obj **objPtrPtr)
8265 {
8266 int totlen = 0, i, retcode;
8267 Jim_Obj **intv;
8268 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8269 Jim_Obj *objPtr;
8270 char *s;
8271
8272 if (tokens <= JIM_EVAL_SINTV_LEN)
8273 intv = sintv;
8274 else
8275 intv = Jim_Alloc(sizeof(Jim_Obj*)*
8276 tokens);
8277 /* Compute every token forming the argument
8278 * in the intv objects vector. */
8279 for (i = 0; i < tokens; i++) {
8280 switch(token[i].type) {
8281 case JIM_TT_ESC:
8282 case JIM_TT_STR:
8283 intv[i] = token[i].objPtr;
8284 break;
8285 case JIM_TT_VAR:
8286 intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8287 if (!intv[i]) {
8288 retcode = JIM_ERR;
8289 goto err;
8290 }
8291 break;
8292 case JIM_TT_DICTSUGAR:
8293 intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8294 if (!intv[i]) {
8295 retcode = JIM_ERR;
8296 goto err;
8297 }
8298 break;
8299 case JIM_TT_CMD:
8300 retcode = Jim_EvalObj(interp, token[i].objPtr);
8301 if (retcode != JIM_OK)
8302 goto err;
8303 intv[i] = Jim_GetResult(interp);
8304 break;
8305 default:
8306 Jim_Panic(interp,
8307 "default token type reached "
8308 "in Jim_InterpolateTokens().");
8309 break;
8310 }
8311 Jim_IncrRefCount(intv[i]);
8312 /* Make sure there is a valid
8313 * string rep, and add the string
8314 * length to the total legnth. */
8315 Jim_GetString(intv[i], NULL);
8316 totlen += intv[i]->length;
8317 }
8318 /* Concatenate every token in an unique
8319 * object. */
8320 objPtr = Jim_NewStringObjNoAlloc(interp,
8321 NULL, 0);
8322 s = objPtr->bytes = Jim_Alloc(totlen+1);
8323 objPtr->length = totlen;
8324 for (i = 0; i < tokens; i++) {
8325 memcpy(s, intv[i]->bytes, intv[i]->length);
8326 s += intv[i]->length;
8327 Jim_DecrRefCount(interp, intv[i]);
8328 }
8329 objPtr->bytes[totlen] = '\0';
8330 /* Free the intv vector if not static. */
8331 if (tokens > JIM_EVAL_SINTV_LEN)
8332 Jim_Free(intv);
8333 *objPtrPtr = objPtr;
8334 return JIM_OK;
8335 err:
8336 i--;
8337 for (; i >= 0; i--)
8338 Jim_DecrRefCount(interp, intv[i]);
8339 if (tokens > JIM_EVAL_SINTV_LEN)
8340 Jim_Free(intv);
8341 return retcode;
8342 }
8343
8344 /* Helper of Jim_EvalObj() to perform argument expansion.
8345 * Basically this function append an argument to 'argv'
8346 * (and increments argc by reference accordingly), performing
8347 * expansion of the list object if 'expand' is non-zero, or
8348 * just adding objPtr to argv if 'expand' is zero. */
8349 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8350 int *argcPtr, int expand, Jim_Obj *objPtr)
8351 {
8352 if (!expand) {
8353 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8354 /* refcount of objPtr not incremented because
8355 * we are actually transfering a reference from
8356 * the old 'argv' to the expanded one. */
8357 (*argv)[*argcPtr] = objPtr;
8358 (*argcPtr)++;
8359 } else {
8360 int len, i;
8361
8362 Jim_ListLength(interp, objPtr, &len);
8363 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8364 for (i = 0; i < len; i++) {
8365 (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8366 Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8367 (*argcPtr)++;
8368 }
8369 /* The original object reference is no longer needed,
8370 * after the expansion it is no longer present on
8371 * the argument vector, but the single elements are
8372 * in its place. */
8373 Jim_DecrRefCount(interp, objPtr);
8374 }
8375 }
8376
8377 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8378 {
8379 int i, j = 0, len;
8380 ScriptObj *script;
8381 ScriptToken *token;
8382 int *cs; /* command structure array */
8383 int retcode = JIM_OK;
8384 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8385
8386 interp->errorFlag = 0;
8387
8388 /* If the object is of type "list" and there is no
8389 * string representation for this object, we can call
8390 * a specialized version of Jim_EvalObj() */
8391 if (scriptObjPtr->typePtr == &listObjType &&
8392 scriptObjPtr->internalRep.listValue.len &&
8393 scriptObjPtr->bytes == NULL) {
8394 Jim_IncrRefCount(scriptObjPtr);
8395 retcode = Jim_EvalObjVector(interp,
8396 scriptObjPtr->internalRep.listValue.len,
8397 scriptObjPtr->internalRep.listValue.ele);
8398 Jim_DecrRefCount(interp, scriptObjPtr);
8399 return retcode;
8400 }
8401
8402 Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8403 script = Jim_GetScript(interp, scriptObjPtr);
8404 /* Now we have to make sure the internal repr will not be
8405 * freed on shimmering.
8406 *
8407 * Think for example to this:
8408 *
8409 * set x {llength $x; ... some more code ...}; eval $x
8410 *
8411 * In order to preserve the internal rep, we increment the
8412 * inUse field of the script internal rep structure. */
8413 script->inUse++;
8414
8415 token = script->token;
8416 len = script->len;
8417 cs = script->cmdStruct;
8418 i = 0; /* 'i' is the current token index. */
8419
8420 /* Reset the interpreter result. This is useful to
8421 * return the emtpy result in the case of empty program. */
8422 Jim_SetEmptyResult(interp);
8423
8424 /* Execute every command sequentially, returns on
8425 * error (i.e. if a command does not return JIM_OK) */
8426 while (i < len) {
8427 int expand = 0;
8428 int argc = *cs++; /* Get the number of arguments */
8429 Jim_Cmd *cmd;
8430
8431 /* Set the expand flag if needed. */
8432 if (argc == -1) {
8433 expand++;
8434 argc = *cs++;
8435 }
8436 /* Allocate the arguments vector */
8437 if (argc <= JIM_EVAL_SARGV_LEN)
8438 argv = sargv;
8439 else
8440 argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8441 /* Populate the arguments objects. */
8442 for (j = 0; j < argc; j++) {
8443 int tokens = *cs++;
8444
8445 /* tokens is negative if expansion is needed.
8446 * for this argument. */
8447 if (tokens < 0) {
8448 tokens = (-tokens)-1;
8449 i++;
8450 }
8451 if (tokens == 1) {
8452 /* Fast path if the token does not
8453 * need interpolation */
8454 switch(token[i].type) {
8455 case JIM_TT_ESC:
8456 case JIM_TT_STR:
8457 argv[j] = token[i].objPtr;
8458 break;
8459 case JIM_TT_VAR:
8460 tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8461 JIM_ERRMSG);
8462 if (!tmpObjPtr) {
8463 retcode = JIM_ERR;
8464 goto err;
8465 }
8466 argv[j] = tmpObjPtr;
8467 break;
8468 case JIM_TT_DICTSUGAR:
8469 tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8470 if (!tmpObjPtr) {
8471 retcode = JIM_ERR;
8472 goto err;
8473 }
8474 argv[j] = tmpObjPtr;
8475 break;
8476 case JIM_TT_CMD:
8477 retcode = Jim_EvalObj(interp, token[i].objPtr);
8478 if (retcode != JIM_OK)
8479 goto err;
8480 argv[j] = Jim_GetResult(interp);
8481 break;
8482 default:
8483 Jim_Panic(interp,
8484 "default token type reached "
8485 "in Jim_EvalObj().");
8486 break;
8487 }
8488 Jim_IncrRefCount(argv[j]);
8489 i += 2;
8490 } else {
8491 /* For interpolation we call an helper
8492 * function doing the work for us. */
8493 if ((retcode = Jim_InterpolateTokens(interp,
8494 token+i, tokens, &tmpObjPtr)) != JIM_OK)
8495 {
8496 goto err;
8497 }
8498 argv[j] = tmpObjPtr;
8499 Jim_IncrRefCount(argv[j]);
8500 i += tokens+1;
8501 }
8502 }
8503 /* Handle {expand} expansion */
8504 if (expand) {
8505 int *ecs = cs - argc;
8506 int eargc = 0;
8507 Jim_Obj **eargv = NULL;
8508
8509 for (j = 0; j < argc; j++) {
8510 Jim_ExpandArgument( interp, &eargv, &eargc,
8511 ecs[j] < 0, argv[j]);
8512 }
8513 if (argv != sargv)
8514 Jim_Free(argv);
8515 argc = eargc;
8516 argv = eargv;
8517 j = argc;
8518 if (argc == 0) {
8519 /* Nothing to do with zero args. */
8520 Jim_Free(eargv);
8521 continue;
8522 }
8523 }
8524 /* Lookup the command to call */
8525 cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8526 if (cmd != NULL) {
8527 /* Call it -- Make sure result is an empty object. */
8528 Jim_SetEmptyResult(interp);
8529 if (cmd->cmdProc) {
8530 interp->cmdPrivData = cmd->privData;
8531 retcode = cmd->cmdProc(interp, argc, argv);
8532 } else {
8533 retcode = JimCallProcedure(interp, cmd, argc, argv);
8534 if (retcode == JIM_ERR) {
8535 JimAppendStackTrace(interp,
8536 Jim_GetString(argv[0], NULL), script->fileName,
8537 token[i-argc*2].linenr);
8538 }
8539 }
8540 } else {
8541 /* Call [unknown] */
8542 retcode = JimUnknown(interp, argc, argv);
8543 if (retcode == JIM_ERR) {
8544 JimAppendStackTrace(interp,
8545 Jim_GetString(argv[0], NULL), script->fileName,
8546 token[i-argc*2].linenr);
8547 }
8548 }
8549 if (retcode != JIM_OK) {
8550 i -= argc*2; /* point to the command name. */
8551 goto err;
8552 }
8553 /* Decrement the arguments count */
8554 for (j = 0; j < argc; j++) {
8555 Jim_DecrRefCount(interp, argv[j]);
8556 }
8557
8558 if (argv != sargv) {
8559 Jim_Free(argv);
8560 argv = NULL;
8561 }
8562 }
8563 /* Note that we don't have to decrement inUse, because the
8564 * following code transfers our use of the reference again to
8565 * the script object. */
8566 j = 0; /* on normal termination, the argv array is already
8567 Jim_DecrRefCount-ed. */
8568 err:
8569 /* Handle errors. */
8570 if (retcode == JIM_ERR && !interp->errorFlag) {
8571 interp->errorFlag = 1;
8572 JimSetErrorFileName(interp, script->fileName);
8573 JimSetErrorLineNumber(interp, token[i].linenr);
8574 JimResetStackTrace(interp);
8575 }
8576 Jim_FreeIntRep(interp, scriptObjPtr);
8577 scriptObjPtr->typePtr = &scriptObjType;
8578 Jim_SetIntRepPtr(scriptObjPtr, script);
8579 Jim_DecrRefCount(interp, scriptObjPtr);
8580 for (i = 0; i < j; i++) {
8581 Jim_DecrRefCount(interp, argv[i]);
8582 }
8583 if (argv != sargv)
8584 Jim_Free(argv);
8585 return retcode;
8586 }
8587
8588 /* Call a procedure implemented in Tcl.
8589 * It's possible to speed-up a lot this function, currently
8590 * the callframes are not cached, but allocated and
8591 * destroied every time. What is expecially costly is
8592 * to create/destroy the local vars hash table every time.
8593 *
8594 * This can be fixed just implementing callframes caching
8595 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8596 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8597 Jim_Obj *const *argv)
8598 {
8599 int i, retcode;
8600 Jim_CallFrame *callFramePtr;
8601
8602 /* Check arity */
8603 if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8604 argc > cmd->arityMax)) {
8605 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8606 Jim_AppendStrings(interp, objPtr,
8607 "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8608 (cmd->arityMin > 1) ? " " : "",
8609 Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8610 Jim_SetResult(interp, objPtr);
8611 return JIM_ERR;
8612 }
8613 /* Check if there are too nested calls */
8614 if (interp->numLevels == interp->maxNestingDepth) {
8615 Jim_SetResultString(interp,
8616 "Too many nested calls. Infinite recursion?", -1);
8617 return JIM_ERR;
8618 }
8619 /* Create a new callframe */
8620 callFramePtr = JimCreateCallFrame(interp);
8621 callFramePtr->parentCallFrame = interp->framePtr;
8622 callFramePtr->argv = argv;
8623 callFramePtr->argc = argc;
8624 callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8625 callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8626 callFramePtr->staticVars = cmd->staticVars;
8627 Jim_IncrRefCount(cmd->argListObjPtr);
8628 Jim_IncrRefCount(cmd->bodyObjPtr);
8629 interp->framePtr = callFramePtr;
8630 interp->numLevels ++;
8631 /* Set arguments */
8632 for (i = 0; i < cmd->arityMin-1; i++) {
8633 Jim_Obj *objPtr;
8634
8635 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8636 Jim_SetVariable(interp, objPtr, argv[i+1]);
8637 }
8638 if (cmd->arityMax == -1) {
8639 Jim_Obj *listObjPtr, *objPtr;
8640
8641 listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8642 argc-cmd->arityMin);
8643 Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8644 Jim_SetVariable(interp, objPtr, listObjPtr);
8645 }
8646 /* Eval the body */
8647 retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8648
8649 /* Destroy the callframe */
8650 interp->numLevels --;
8651 interp->framePtr = interp->framePtr->parentCallFrame;
8652 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8653 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8654 } else {
8655 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8656 }
8657 /* Handle the JIM_EVAL return code */
8658 if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8659 int savedLevel = interp->evalRetcodeLevel;
8660
8661 interp->evalRetcodeLevel = interp->numLevels;
8662 while (retcode == JIM_EVAL) {
8663 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8664 Jim_IncrRefCount(resultScriptObjPtr);
8665 retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8666 Jim_DecrRefCount(interp, resultScriptObjPtr);
8667 }
8668 interp->evalRetcodeLevel = savedLevel;
8669 }
8670 /* Handle the JIM_RETURN return code */
8671 if (retcode == JIM_RETURN) {
8672 retcode = interp->returnCode;
8673 interp->returnCode = JIM_OK;
8674 }
8675 return retcode;
8676 }
8677
8678 int Jim_Eval(Jim_Interp *interp, const char *script)
8679 {
8680 Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8681 int retval;
8682
8683 Jim_IncrRefCount(scriptObjPtr);
8684 retval = Jim_EvalObj(interp, scriptObjPtr);
8685 Jim_DecrRefCount(interp, scriptObjPtr);
8686 return retval;
8687 }
8688
8689 /* Execute script in the scope of the global level */
8690 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8691 {
8692 Jim_CallFrame *savedFramePtr;
8693 int retval;
8694
8695 savedFramePtr = interp->framePtr;
8696 interp->framePtr = interp->topFramePtr;
8697 retval = Jim_Eval(interp, script);
8698 interp->framePtr = savedFramePtr;
8699 return retval;
8700 }
8701
8702 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8703 {
8704 Jim_CallFrame *savedFramePtr;
8705 int retval;
8706
8707 savedFramePtr = interp->framePtr;
8708 interp->framePtr = interp->topFramePtr;
8709 retval = Jim_EvalObj(interp, scriptObjPtr);
8710 interp->framePtr = savedFramePtr;
8711 /* Try to report the error (if any) via the bgerror proc */
8712 if (retval != JIM_OK) {
8713 Jim_Obj *objv[2];
8714
8715 objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8716 objv[1] = Jim_GetResult(interp);
8717 Jim_IncrRefCount(objv[0]);
8718 Jim_IncrRefCount(objv[1]);
8719 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8720 /* Report the error to stderr. */
8721 Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8722 Jim_PrintErrorMessage(interp);
8723 }
8724 Jim_DecrRefCount(interp, objv[0]);
8725 Jim_DecrRefCount(interp, objv[1]);
8726 }
8727 return retval;
8728 }
8729
8730 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8731 {
8732 char *prg = NULL;
8733 FILE *fp;
8734 int nread, totread, maxlen, buflen;
8735 int retval;
8736 Jim_Obj *scriptObjPtr;
8737
8738 if ((fp = fopen(filename, "r")) == NULL) {
8739 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8740 Jim_AppendStrings(interp, Jim_GetResult(interp),
8741 "Error loading script \"", filename, "\": ",
8742 strerror(errno), NULL);
8743 return JIM_ERR;
8744 }
8745 buflen = 1024;
8746 maxlen = totread = 0;
8747 while (1) {
8748 if (maxlen < totread+buflen+1) {
8749 maxlen = totread+buflen+1;
8750 prg = Jim_Realloc(prg, maxlen);
8751 }
8752 /* do not use Jim_fread() - this is really a file */
8753 if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8754 totread += nread;
8755 }
8756 prg[totread] = '\0';
8757 /* do not use Jim_fclose() - this is really a file */
8758 fclose(fp);
8759
8760 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8761 JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8762 Jim_IncrRefCount(scriptObjPtr);
8763 retval = Jim_EvalObj(interp, scriptObjPtr);
8764 Jim_DecrRefCount(interp, scriptObjPtr);
8765 return retval;
8766 }
8767
8768 /* -----------------------------------------------------------------------------
8769 * Subst
8770 * ---------------------------------------------------------------------------*/
8771 static int JimParseSubstStr(struct JimParserCtx *pc)
8772 {
8773 pc->tstart = pc->p;
8774 pc->tline = pc->linenr;
8775 while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8776 pc->p++; pc->len--;
8777 }
8778 pc->tend = pc->p-1;
8779 pc->tt = JIM_TT_ESC;
8780 return JIM_OK;
8781 }
8782
8783 static int JimParseSubst(struct JimParserCtx *pc, int flags)
8784 {
8785 int retval;
8786
8787 if (pc->len == 0) {
8788 pc->tstart = pc->tend = pc->p;
8789 pc->tline = pc->linenr;
8790 pc->tt = JIM_TT_EOL;
8791 pc->eof = 1;
8792 return JIM_OK;
8793 }
8794 switch(*pc->p) {
8795 case '[':
8796 retval = JimParseCmd(pc);
8797 if (flags & JIM_SUBST_NOCMD) {
8798 pc->tstart--;
8799 pc->tend++;
8800 pc->tt = (flags & JIM_SUBST_NOESC) ?
8801 JIM_TT_STR : JIM_TT_ESC;
8802 }
8803 return retval;
8804 break;
8805 case '$':
8806 if (JimParseVar(pc) == JIM_ERR) {
8807 pc->tstart = pc->tend = pc->p++; pc->len--;
8808 pc->tline = pc->linenr;
8809 pc->tt = JIM_TT_STR;
8810 } else {
8811 if (flags & JIM_SUBST_NOVAR) {
8812 pc->tstart--;
8813 if (flags & JIM_SUBST_NOESC)
8814 pc->tt = JIM_TT_STR;
8815 else
8816 pc->tt = JIM_TT_ESC;
8817 if (*pc->tstart == '{') {
8818 pc->tstart--;
8819 if (*(pc->tend+1))
8820 pc->tend++;
8821 }
8822 }
8823 }
8824 break;
8825 default:
8826 retval = JimParseSubstStr(pc);
8827 if (flags & JIM_SUBST_NOESC)
8828 pc->tt = JIM_TT_STR;
8829 return retval;
8830 break;
8831 }
8832 return JIM_OK;
8833 }
8834
8835 /* The subst object type reuses most of the data structures and functions
8836 * of the script object. Script's data structures are a bit more complex
8837 * for what is needed for [subst]itution tasks, but the reuse helps to
8838 * deal with a single data structure at the cost of some more memory
8839 * usage for substitutions. */
8840 static Jim_ObjType substObjType = {
8841 "subst",
8842 FreeScriptInternalRep,
8843 DupScriptInternalRep,
8844 NULL,
8845 JIM_TYPE_REFERENCES,
8846 };
8847
8848 /* This method takes the string representation of an object
8849 * as a Tcl string where to perform [subst]itution, and generates
8850 * the pre-parsed internal representation. */
8851 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8852 {
8853 int scriptTextLen;
8854 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8855 struct JimParserCtx parser;
8856 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8857
8858 script->len = 0;
8859 script->csLen = 0;
8860 script->commands = 0;
8861 script->token = NULL;
8862 script->cmdStruct = NULL;
8863 script->inUse = 1;
8864 script->substFlags = flags;
8865 script->fileName = NULL;
8866
8867 JimParserInit(&parser, scriptText, scriptTextLen, 1);
8868 while(1) {
8869 char *token;
8870 int len, type, linenr;
8871
8872 JimParseSubst(&parser, flags);
8873 if (JimParserEof(&parser)) break;
8874 token = JimParserGetToken(&parser, &len, &type, &linenr);
8875 ScriptObjAddToken(interp, script, token, len, type,
8876 NULL, linenr);
8877 }
8878 /* Free the old internal rep and set the new one. */
8879 Jim_FreeIntRep(interp, objPtr);
8880 Jim_SetIntRepPtr(objPtr, script);
8881 objPtr->typePtr = &scriptObjType;
8882 return JIM_OK;
8883 }
8884
8885 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8886 {
8887 struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8888
8889 if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8890 SetSubstFromAny(interp, objPtr, flags);
8891 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8892 }
8893
8894 /* Performs commands,variables,blackslashes substitution,
8895 * storing the result object (with refcount 0) into
8896 * resObjPtrPtr. */
8897 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8898 Jim_Obj **resObjPtrPtr, int flags)
8899 {
8900 ScriptObj *script;
8901 ScriptToken *token;
8902 int i, len, retcode = JIM_OK;
8903 Jim_Obj *resObjPtr, *savedResultObjPtr;
8904
8905 script = Jim_GetSubst(interp, substObjPtr, flags);
8906 #ifdef JIM_OPTIMIZATION
8907 /* Fast path for a very common case with array-alike syntax,
8908 * that's: $foo($bar) */
8909 if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8910 Jim_Obj *varObjPtr = script->token[0].objPtr;
8911
8912 Jim_IncrRefCount(varObjPtr);
8913 resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8914 if (resObjPtr == NULL) {
8915 Jim_DecrRefCount(interp, varObjPtr);
8916 return JIM_ERR;
8917 }
8918 Jim_DecrRefCount(interp, varObjPtr);
8919 *resObjPtrPtr = resObjPtr;
8920 return JIM_OK;
8921 }
8922 #endif
8923
8924 Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8925 /* In order to preserve the internal rep, we increment the
8926 * inUse field of the script internal rep structure. */
8927 script->inUse++;
8928
8929 token = script->token;
8930 len = script->len;
8931
8932 /* Save the interp old result, to set it again before
8933 * to return. */
8934 savedResultObjPtr = interp->result;
8935 Jim_IncrRefCount(savedResultObjPtr);
8936
8937 /* Perform the substitution. Starts with an empty object
8938 * and adds every token (performing the appropriate
8939 * var/command/escape substitution). */
8940 resObjPtr = Jim_NewStringObj(interp, "", 0);
8941 for (i = 0; i < len; i++) {
8942 Jim_Obj *objPtr;
8943
8944 switch(token[i].type) {
8945 case JIM_TT_STR:
8946 case JIM_TT_ESC:
8947 Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8948 break;
8949 case JIM_TT_VAR:
8950 objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8951 if (objPtr == NULL) goto err;
8952 Jim_IncrRefCount(objPtr);
8953 Jim_AppendObj(interp, resObjPtr, objPtr);
8954 Jim_DecrRefCount(interp, objPtr);
8955 break;
8956 case JIM_TT_CMD:
8957 if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8958 goto err;
8959 Jim_AppendObj(interp, resObjPtr, interp->result);
8960 break;
8961 default:
8962 Jim_Panic(interp,
8963 "default token type (%d) reached "
8964 "in Jim_SubstObj().", token[i].type);
8965 break;
8966 }
8967 }
8968 ok:
8969 if (retcode == JIM_OK)
8970 Jim_SetResult(interp, savedResultObjPtr);
8971 Jim_DecrRefCount(interp, savedResultObjPtr);
8972 /* Note that we don't have to decrement inUse, because the
8973 * following code transfers our use of the reference again to
8974 * the script object. */
8975 Jim_FreeIntRep(interp, substObjPtr);
8976 substObjPtr->typePtr = &scriptObjType;
8977 Jim_SetIntRepPtr(substObjPtr, script);
8978 Jim_DecrRefCount(interp, substObjPtr);
8979 *resObjPtrPtr = resObjPtr;
8980 return retcode;
8981 err:
8982 Jim_FreeNewObj(interp, resObjPtr);
8983 retcode = JIM_ERR;
8984 goto ok;
8985 }
8986
8987 /* -----------------------------------------------------------------------------
8988 * API Input/Export functions
8989 * ---------------------------------------------------------------------------*/
8990
8991 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8992 {
8993 Jim_HashEntry *he;
8994
8995 he = Jim_FindHashEntry(&interp->stub, funcname);
8996 if (!he)
8997 return JIM_ERR;
8998 memcpy(targetPtrPtr, &he->val, sizeof(void*));
8999 return JIM_OK;
9000 }
9001
9002 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
9003 {
9004 return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
9005 }
9006
9007 #define JIM_REGISTER_API(name) \
9008 Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
9009
9010 void JimRegisterCoreApi(Jim_Interp *interp)
9011 {
9012 interp->getApiFuncPtr = Jim_GetApi;
9013 JIM_REGISTER_API(Alloc);
9014 JIM_REGISTER_API(Free);
9015 JIM_REGISTER_API(Eval);
9016 JIM_REGISTER_API(EvalGlobal);
9017 JIM_REGISTER_API(EvalFile);
9018 JIM_REGISTER_API(EvalObj);
9019 JIM_REGISTER_API(EvalObjBackground);
9020 JIM_REGISTER_API(EvalObjVector);
9021 JIM_REGISTER_API(InitHashTable);
9022 JIM_REGISTER_API(ExpandHashTable);
9023 JIM_REGISTER_API(AddHashEntry);
9024 JIM_REGISTER_API(ReplaceHashEntry);
9025 JIM_REGISTER_API(DeleteHashEntry);
9026 JIM_REGISTER_API(FreeHashTable);
9027 JIM_REGISTER_API(FindHashEntry);
9028 JIM_REGISTER_API(ResizeHashTable);
9029 JIM_REGISTER_API(GetHashTableIterator);
9030 JIM_REGISTER_API(NextHashEntry);
9031 JIM_REGISTER_API(NewObj);
9032 JIM_REGISTER_API(FreeObj);
9033 JIM_REGISTER_API(InvalidateStringRep);
9034 JIM_REGISTER_API(InitStringRep);
9035 JIM_REGISTER_API(DuplicateObj);
9036 JIM_REGISTER_API(GetString);
9037 JIM_REGISTER_API(Length);
9038 JIM_REGISTER_API(InvalidateStringRep);
9039 JIM_REGISTER_API(NewStringObj);
9040 JIM_REGISTER_API(NewStringObjNoAlloc);
9041 JIM_REGISTER_API(AppendString);
9042 JIM_REGISTER_API(AppendObj);
9043 JIM_REGISTER_API(AppendStrings);
9044 JIM_REGISTER_API(StringEqObj);
9045 JIM_REGISTER_API(StringMatchObj);
9046 JIM_REGISTER_API(StringRangeObj);
9047 JIM_REGISTER_API(FormatString);
9048 JIM_REGISTER_API(CompareStringImmediate);
9049 JIM_REGISTER_API(NewReference);
9050 JIM_REGISTER_API(GetReference);
9051 JIM_REGISTER_API(SetFinalizer);
9052 JIM_REGISTER_API(GetFinalizer);
9053 JIM_REGISTER_API(CreateInterp);
9054 JIM_REGISTER_API(FreeInterp);
9055 JIM_REGISTER_API(GetExitCode);
9056 JIM_REGISTER_API(SetStdin);
9057 JIM_REGISTER_API(SetStdout);
9058 JIM_REGISTER_API(SetStderr);
9059 JIM_REGISTER_API(CreateCommand);
9060 JIM_REGISTER_API(CreateProcedure);
9061 JIM_REGISTER_API(DeleteCommand);
9062 JIM_REGISTER_API(RenameCommand);
9063 JIM_REGISTER_API(GetCommand);
9064 JIM_REGISTER_API(SetVariable);
9065 JIM_REGISTER_API(SetVariableStr);
9066 JIM_REGISTER_API(SetGlobalVariableStr);
9067 JIM_REGISTER_API(SetVariableStrWithStr);
9068 JIM_REGISTER_API(SetVariableLink);
9069 JIM_REGISTER_API(GetVariable);
9070 JIM_REGISTER_API(GetCallFrameByLevel);
9071 JIM_REGISTER_API(Collect);
9072 JIM_REGISTER_API(CollectIfNeeded);
9073 JIM_REGISTER_API(GetIndex);
9074 JIM_REGISTER_API(NewListObj);
9075 JIM_REGISTER_API(ListAppendElement);
9076 JIM_REGISTER_API(ListAppendList);
9077 JIM_REGISTER_API(ListLength);
9078 JIM_REGISTER_API(ListIndex);
9079 JIM_REGISTER_API(SetListIndex);
9080 JIM_REGISTER_API(ConcatObj);
9081 JIM_REGISTER_API(NewDictObj);
9082 JIM_REGISTER_API(DictKey);
9083 JIM_REGISTER_API(DictKeysVector);
9084 JIM_REGISTER_API(GetIndex);
9085 JIM_REGISTER_API(GetReturnCode);
9086 JIM_REGISTER_API(EvalExpression);
9087 JIM_REGISTER_API(GetBoolFromExpr);
9088 JIM_REGISTER_API(GetWide);
9089 JIM_REGISTER_API(GetLong);
9090 JIM_REGISTER_API(SetWide);
9091 JIM_REGISTER_API(NewIntObj);
9092 JIM_REGISTER_API(GetDouble);
9093 JIM_REGISTER_API(SetDouble);
9094 JIM_REGISTER_API(NewDoubleObj);
9095 JIM_REGISTER_API(WrongNumArgs);
9096 JIM_REGISTER_API(SetDictKeysVector);
9097 JIM_REGISTER_API(SubstObj);
9098 JIM_REGISTER_API(RegisterApi);
9099 JIM_REGISTER_API(PrintErrorMessage);
9100 JIM_REGISTER_API(InteractivePrompt);
9101 JIM_REGISTER_API(RegisterCoreCommands);
9102 JIM_REGISTER_API(GetSharedString);
9103 JIM_REGISTER_API(ReleaseSharedString);
9104 JIM_REGISTER_API(Panic);
9105 JIM_REGISTER_API(StrDup);
9106 JIM_REGISTER_API(UnsetVariable);
9107 JIM_REGISTER_API(GetVariableStr);
9108 JIM_REGISTER_API(GetGlobalVariable);
9109 JIM_REGISTER_API(GetGlobalVariableStr);
9110 JIM_REGISTER_API(GetAssocData);
9111 JIM_REGISTER_API(SetAssocData);
9112 JIM_REGISTER_API(DeleteAssocData);
9113 JIM_REGISTER_API(GetEnum);
9114 JIM_REGISTER_API(ScriptIsComplete);
9115 JIM_REGISTER_API(PackageRequire);
9116 JIM_REGISTER_API(PackageProvide);
9117 JIM_REGISTER_API(InitStack);
9118 JIM_REGISTER_API(FreeStack);
9119 JIM_REGISTER_API(StackLen);
9120 JIM_REGISTER_API(StackPush);
9121 JIM_REGISTER_API(StackPop);
9122 JIM_REGISTER_API(StackPeek);
9123 JIM_REGISTER_API(FreeStackElements);
9124 }
9125
9126 /* -----------------------------------------------------------------------------
9127 * Core commands utility functions
9128 * ---------------------------------------------------------------------------*/
9129 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
9130 const char *msg)
9131 {
9132 int i;
9133 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9134
9135 Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9136 for (i = 0; i < argc; i++) {
9137 Jim_AppendObj(interp, objPtr, argv[i]);
9138 if (!(i+1 == argc && msg[0] == '\0'))
9139 Jim_AppendString(interp, objPtr, " ", 1);
9140 }
9141 Jim_AppendString(interp, objPtr, msg, -1);
9142 Jim_AppendString(interp, objPtr, "\"", 1);
9143 Jim_SetResult(interp, objPtr);
9144 }
9145
9146 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9147 {
9148 Jim_HashTableIterator *htiter;
9149 Jim_HashEntry *he;
9150 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9151 const char *pattern;
9152 int patternLen;
9153
9154 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9155 htiter = Jim_GetHashTableIterator(&interp->commands);
9156 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9157 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9158 strlen((const char*)he->key), 0))
9159 continue;
9160 Jim_ListAppendElement(interp, listObjPtr,
9161 Jim_NewStringObj(interp, he->key, -1));
9162 }
9163 Jim_FreeHashTableIterator(htiter);
9164 return listObjPtr;
9165 }
9166
9167 #define JIM_VARLIST_GLOBALS 0
9168 #define JIM_VARLIST_LOCALS 1
9169 #define JIM_VARLIST_VARS 2
9170
9171 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9172 int mode)
9173 {
9174 Jim_HashTableIterator *htiter;
9175 Jim_HashEntry *he;
9176 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9177 const char *pattern;
9178 int patternLen;
9179
9180 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9181 if (mode == JIM_VARLIST_GLOBALS) {
9182 htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9183 } else {
9184 /* For [info locals], if we are at top level an emtpy list
9185 * is returned. I don't agree, but we aim at compatibility (SS) */
9186 if (mode == JIM_VARLIST_LOCALS &&
9187 interp->framePtr == interp->topFramePtr)
9188 return listObjPtr;
9189 htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9190 }
9191 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9192 Jim_Var *varPtr = (Jim_Var*) he->val;
9193 if (mode == JIM_VARLIST_LOCALS) {
9194 if (varPtr->linkFramePtr != NULL)
9195 continue;
9196 }
9197 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9198 strlen((const char*)he->key), 0))
9199 continue;
9200 Jim_ListAppendElement(interp, listObjPtr,
9201 Jim_NewStringObj(interp, he->key, -1));
9202 }
9203 Jim_FreeHashTableIterator(htiter);
9204 return listObjPtr;
9205 }
9206
9207 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9208 Jim_Obj **objPtrPtr)
9209 {
9210 Jim_CallFrame *targetCallFrame;
9211
9212 if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9213 != JIM_OK)
9214 return JIM_ERR;
9215 /* No proc call at toplevel callframe */
9216 if (targetCallFrame == interp->topFramePtr) {
9217 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9218 Jim_AppendStrings(interp, Jim_GetResult(interp),
9219 "bad level \"",
9220 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9221 return JIM_ERR;
9222 }
9223 *objPtrPtr = Jim_NewListObj(interp,
9224 targetCallFrame->argv,
9225 targetCallFrame->argc);
9226 return JIM_OK;
9227 }
9228
9229 /* -----------------------------------------------------------------------------
9230 * Core commands
9231 * ---------------------------------------------------------------------------*/
9232
9233 /* fake [puts] -- not the real puts, just for debugging. */
9234 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9235 Jim_Obj *const *argv)
9236 {
9237 const char *str;
9238 int len, nonewline = 0;
9239
9240 if (argc != 2 && argc != 3) {
9241 Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9242 return JIM_ERR;
9243 }
9244 if (argc == 3) {
9245 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9246 {
9247 Jim_SetResultString(interp, "The second argument must "
9248 "be -nonewline", -1);
9249 return JIM_OK;
9250 } else {
9251 nonewline = 1;
9252 argv++;
9253 }
9254 }
9255 str = Jim_GetString(argv[1], &len);
9256 Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9257 if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9258 return JIM_OK;
9259 }
9260
9261 /* Helper for [+] and [*] */
9262 static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
9263 Jim_Obj *const *argv, int op)
9264 {
9265 jim_wide wideValue, res;
9266 double doubleValue, doubleRes;
9267 int i;
9268
9269 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9270
9271 for (i = 1; i < argc; i++) {
9272 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9273 goto trydouble;
9274 if (op == JIM_EXPROP_ADD)
9275 res += wideValue;
9276 else
9277 res *= wideValue;
9278 }
9279 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9280 return JIM_OK;
9281 trydouble:
9282 doubleRes = (double) res;
9283 for (;i < argc; i++) {
9284 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9285 return JIM_ERR;
9286 if (op == JIM_EXPROP_ADD)
9287 doubleRes += doubleValue;
9288 else
9289 doubleRes *= doubleValue;
9290 }
9291 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9292 return JIM_OK;
9293 }
9294
9295 /* Helper for [-] and [/] */
9296 static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9297 Jim_Obj *const *argv, int op)
9298 {
9299 jim_wide wideValue, res = 0;
9300 double doubleValue, doubleRes = 0;
9301 int i = 2;
9302
9303 if (argc < 2) {
9304 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9305 return JIM_ERR;
9306 } else if (argc == 2) {
9307 /* The arity = 2 case is different. For [- x] returns -x,
9308 * while [/ x] returns 1/x. */
9309 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9310 if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9311 JIM_OK)
9312 {
9313 return JIM_ERR;
9314 } else {
9315 if (op == JIM_EXPROP_SUB)
9316 doubleRes = -doubleValue;
9317 else
9318 doubleRes = 1.0/doubleValue;
9319 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9320 doubleRes));
9321 return JIM_OK;
9322 }
9323 }
9324 if (op == JIM_EXPROP_SUB) {
9325 res = -wideValue;
9326 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9327 } else {
9328 doubleRes = 1.0/wideValue;
9329 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9330 doubleRes));
9331 }
9332 return JIM_OK;
9333 } else {
9334 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9335 if (Jim_GetDouble(interp, argv[1], &doubleRes)
9336 != JIM_OK) {
9337 return JIM_ERR;
9338 } else {
9339 goto trydouble;
9340 }
9341 }
9342 }
9343 for (i = 2; i < argc; i++) {
9344 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9345 doubleRes = (double) res;
9346 goto trydouble;
9347 }
9348 if (op == JIM_EXPROP_SUB)
9349 res -= wideValue;
9350 else
9351 res /= wideValue;
9352 }
9353 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9354 return JIM_OK;
9355 trydouble:
9356 for (;i < argc; i++) {
9357 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9358 return JIM_ERR;
9359 if (op == JIM_EXPROP_SUB)
9360 doubleRes -= doubleValue;
9361 else
9362 doubleRes /= doubleValue;
9363 }
9364 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9365 return JIM_OK;
9366 }
9367
9368
9369 /* [+] */
9370 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9371 Jim_Obj *const *argv)
9372 {
9373 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9374 }
9375
9376 /* [*] */
9377 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9378 Jim_Obj *const *argv)
9379 {
9380 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9381 }
9382
9383 /* [-] */
9384 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9385 Jim_Obj *const *argv)
9386 {
9387 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9388 }
9389
9390 /* [/] */
9391 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9392 Jim_Obj *const *argv)
9393 {
9394 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9395 }
9396
9397 /* [set] */
9398 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9399 Jim_Obj *const *argv)
9400 {
9401 if (argc != 2 && argc != 3) {
9402 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9403 return JIM_ERR;
9404 }
9405 if (argc == 2) {
9406 Jim_Obj *objPtr;
9407 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9408 if (!objPtr)
9409 return JIM_ERR;
9410 Jim_SetResult(interp, objPtr);
9411 return JIM_OK;
9412 }
9413 /* argc == 3 case. */
9414 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9415 return JIM_ERR;
9416 Jim_SetResult(interp, argv[2]);
9417 return JIM_OK;
9418 }
9419
9420 /* [unset] */
9421 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9422 Jim_Obj *const *argv)
9423 {
9424 int i;
9425
9426 if (argc < 2) {
9427 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9428 return JIM_ERR;
9429 }
9430 for (i = 1; i < argc; i++) {
9431 if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9432 return JIM_ERR;
9433 }
9434 return JIM_OK;
9435 }
9436
9437 /* [incr] */
9438 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9439 Jim_Obj *const *argv)
9440 {
9441 jim_wide wideValue, increment = 1;
9442 Jim_Obj *intObjPtr;
9443
9444 if (argc != 2 && argc != 3) {
9445 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9446 return JIM_ERR;
9447 }
9448 if (argc == 3) {
9449 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9450 return JIM_ERR;
9451 }
9452 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9453 if (!intObjPtr) return JIM_ERR;
9454 if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9455 return JIM_ERR;
9456 if (Jim_IsShared(intObjPtr)) {
9457 intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9458 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9459 Jim_FreeNewObj(interp, intObjPtr);
9460 return JIM_ERR;
9461 }
9462 } else {
9463 Jim_SetWide(interp, intObjPtr, wideValue+increment);
9464 /* The following step is required in order to invalidate the
9465 * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9466 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9467 return JIM_ERR;
9468 }
9469 }
9470 Jim_SetResult(interp, intObjPtr);
9471 return JIM_OK;
9472 }
9473
9474 /* [while] */
9475 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9476 Jim_Obj *const *argv)
9477 {
9478 if (argc != 3) {
9479 Jim_WrongNumArgs(interp, 1, argv, "condition body");
9480 return JIM_ERR;
9481 }
9482 /* Try to run a specialized version of while if the expression
9483 * is in one of the following forms:
9484 *
9485 * $a < CONST, $a < $b
9486 * $a <= CONST, $a <= $b
9487 * $a > CONST, $a > $b
9488 * $a >= CONST, $a >= $b
9489 * $a != CONST, $a != $b
9490 * $a == CONST, $a == $b
9491 * $a
9492 * !$a
9493 * CONST
9494 */
9495
9496 #ifdef JIM_OPTIMIZATION
9497 {
9498 ExprByteCode *expr;
9499 Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9500 int exprLen, retval;
9501
9502 /* STEP 1 -- Check if there are the conditions to run the specialized
9503 * version of while */
9504
9505 if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9506 if (expr->len <= 0 || expr->len > 3) goto noopt;
9507 switch(expr->len) {
9508 case 1:
9509 if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9510 expr->opcode[0] != JIM_EXPROP_NUMBER)
9511 goto noopt;
9512 break;
9513 case 2:
9514 if (expr->opcode[1] != JIM_EXPROP_NOT ||
9515 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9516 goto noopt;
9517 break;
9518 case 3:
9519 if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9520 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9521 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9522 goto noopt;
9523 switch(expr->opcode[2]) {
9524 case JIM_EXPROP_LT:
9525 case JIM_EXPROP_LTE:
9526 case JIM_EXPROP_GT:
9527 case JIM_EXPROP_GTE:
9528 case JIM_EXPROP_NUMEQ:
9529 case JIM_EXPROP_NUMNE:
9530 /* nothing to do */
9531 break;
9532 default:
9533 goto noopt;
9534 }
9535 break;
9536 default:
9537 Jim_Panic(interp,
9538 "Unexpected default reached in Jim_WhileCoreCommand()");
9539 break;
9540 }
9541
9542 /* STEP 2 -- conditions meet. Initialization. Take different
9543 * branches for different expression lengths. */
9544 exprLen = expr->len;
9545
9546 if (exprLen == 1) {
9547 jim_wide wideValue;
9548
9549 if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9550 varAObjPtr = expr->obj[0];
9551 Jim_IncrRefCount(varAObjPtr);
9552 } else {
9553 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9554 goto noopt;
9555 }
9556 while (1) {
9557 if (varAObjPtr) {
9558 if (!(objPtr =
9559 Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9560 Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9561 {
9562 Jim_DecrRefCount(interp, varAObjPtr);
9563 goto noopt;
9564 }
9565 }
9566 if (!wideValue) break;
9567 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9568 switch(retval) {
9569 case JIM_BREAK:
9570 if (varAObjPtr)
9571 Jim_DecrRefCount(interp, varAObjPtr);
9572 goto out;
9573 break;
9574 case JIM_CONTINUE:
9575 continue;
9576 break;
9577 default:
9578 if (varAObjPtr)
9579 Jim_DecrRefCount(interp, varAObjPtr);
9580 return retval;
9581 }
9582 }
9583 }
9584 if (varAObjPtr)
9585 Jim_DecrRefCount(interp, varAObjPtr);
9586 } else if (exprLen == 3) {
9587 jim_wide wideValueA, wideValueB, cmpRes = 0;
9588 int cmpType = expr->opcode[2];
9589
9590 varAObjPtr = expr->obj[0];
9591 Jim_IncrRefCount(varAObjPtr);
9592 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9593 varBObjPtr = expr->obj[1];
9594 Jim_IncrRefCount(varBObjPtr);
9595 } else {
9596 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9597 goto noopt;
9598 }
9599 while (1) {
9600 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9601 Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9602 {
9603 Jim_DecrRefCount(interp, varAObjPtr);
9604 if (varBObjPtr)
9605 Jim_DecrRefCount(interp, varBObjPtr);
9606 goto noopt;
9607 }
9608 if (varBObjPtr) {
9609 if (!(objPtr =
9610 Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9611 Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9612 {
9613 Jim_DecrRefCount(interp, varAObjPtr);
9614 if (varBObjPtr)
9615 Jim_DecrRefCount(interp, varBObjPtr);
9616 goto noopt;
9617 }
9618 }
9619 switch(cmpType) {
9620 case JIM_EXPROP_LT:
9621 cmpRes = wideValueA < wideValueB; break;
9622 case JIM_EXPROP_LTE:
9623 cmpRes = wideValueA <= wideValueB; break;
9624 case JIM_EXPROP_GT:
9625 cmpRes = wideValueA > wideValueB; break;
9626 case JIM_EXPROP_GTE:
9627 cmpRes = wideValueA >= wideValueB; break;
9628 case JIM_EXPROP_NUMEQ:
9629 cmpRes = wideValueA == wideValueB; break;
9630 case JIM_EXPROP_NUMNE:
9631 cmpRes = wideValueA != wideValueB; break;
9632 }
9633 if (!cmpRes) break;
9634 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9635 switch(retval) {
9636 case JIM_BREAK:
9637 Jim_DecrRefCount(interp, varAObjPtr);
9638 if (varBObjPtr)
9639 Jim_DecrRefCount(interp, varBObjPtr);
9640 goto out;
9641 break;
9642 case JIM_CONTINUE:
9643 continue;
9644 break;
9645 default:
9646 Jim_DecrRefCount(interp, varAObjPtr);
9647 if (varBObjPtr)
9648 Jim_DecrRefCount(interp, varBObjPtr);
9649 return retval;
9650 }
9651 }
9652 }
9653 Jim_DecrRefCount(interp, varAObjPtr);
9654 if (varBObjPtr)
9655 Jim_DecrRefCount(interp, varBObjPtr);
9656 } else {
9657 /* TODO: case for len == 2 */
9658 goto noopt;
9659 }
9660 Jim_SetEmptyResult(interp);
9661 return JIM_OK;
9662 }
9663 noopt:
9664 #endif
9665
9666 /* The general purpose implementation of while starts here */
9667 while (1) {
9668 int boolean, retval;
9669
9670 if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9671 &boolean)) != JIM_OK)
9672 return retval;
9673 if (!boolean) break;
9674 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9675 switch(retval) {
9676 case JIM_BREAK:
9677 goto out;
9678 break;
9679 case JIM_CONTINUE:
9680 continue;
9681 break;
9682 default:
9683 return retval;
9684 }
9685 }
9686 }
9687 out:
9688 Jim_SetEmptyResult(interp);
9689 return JIM_OK;
9690 }
9691
9692 /* [for] */
9693 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9694 Jim_Obj *const *argv)
9695 {
9696 int retval;
9697
9698 if (argc != 5) {
9699 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9700 return JIM_ERR;
9701 }
9702 /* Check if the for is on the form:
9703 * for {set i CONST} {$i < CONST} {incr i}
9704 * for {set i CONST} {$i < $j} {incr i}
9705 * for {set i CONST} {$i <= CONST} {incr i}
9706 * for {set i CONST} {$i <= $j} {incr i}
9707 * XXX: NOTE: if variable traces are implemented, this optimization
9708 * need to be modified to check for the proc epoch at every variable
9709 * update. */
9710 #ifdef JIM_OPTIMIZATION
9711 {
9712 ScriptObj *initScript, *incrScript;
9713 ExprByteCode *expr;
9714 jim_wide start, stop, currentVal;
9715 unsigned jim_wide procEpoch = interp->procEpoch;
9716 Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9717 int cmpType;
9718 struct Jim_Cmd *cmdPtr;
9719
9720 /* Do it only if there aren't shared arguments */
9721 if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9722 goto evalstart;
9723 initScript = Jim_GetScript(interp, argv[1]);
9724 expr = Jim_GetExpression(interp, argv[2]);
9725 incrScript = Jim_GetScript(interp, argv[3]);
9726
9727 /* Ensure proper lengths to start */
9728 if (initScript->len != 6) goto evalstart;
9729 if (incrScript->len != 4) goto evalstart;
9730 if (expr->len != 3) goto evalstart;
9731 /* Ensure proper token types. */
9732 if (initScript->token[2].type != JIM_TT_ESC ||
9733 initScript->token[4].type != JIM_TT_ESC ||
9734 incrScript->token[2].type != JIM_TT_ESC ||
9735 expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9736 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9737 expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9738 (expr->opcode[2] != JIM_EXPROP_LT &&
9739 expr->opcode[2] != JIM_EXPROP_LTE))
9740 goto evalstart;
9741 cmpType = expr->opcode[2];
9742 /* Initialization command must be [set] */
9743 cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9744 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9745 goto evalstart;
9746 /* Update command must be incr */
9747 cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9748 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9749 goto evalstart;
9750 /* set, incr, expression must be about the same variable */
9751 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9752 incrScript->token[2].objPtr, 0))
9753 goto evalstart;
9754 if (!Jim_StringEqObj(initScript->token[2].objPtr,
9755 expr->obj[0], 0))
9756 goto evalstart;
9757 /* Check that the initialization and comparison are valid integers */
9758 if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9759 goto evalstart;
9760 if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9761 Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9762 {
9763 goto evalstart;
9764 }
9765
9766 /* Initialization */
9767 varNamePtr = expr->obj[0];
9768 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9769 stopVarNamePtr = expr->obj[1];
9770 Jim_IncrRefCount(stopVarNamePtr);
9771 }
9772 Jim_IncrRefCount(varNamePtr);
9773
9774 /* --- OPTIMIZED FOR --- */
9775 /* Start to loop */
9776 objPtr = Jim_NewIntObj(interp, start);
9777 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9778 Jim_DecrRefCount(interp, varNamePtr);
9779 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9780 Jim_FreeNewObj(interp, objPtr);
9781 goto evalstart;
9782 }
9783 while (1) {
9784 /* === Check condition === */
9785 /* Common code: */
9786 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9787 if (objPtr == NULL ||
9788 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9789 {
9790 Jim_DecrRefCount(interp, varNamePtr);
9791 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9792 goto testcond;
9793 }
9794 /* Immediate or Variable? get the 'stop' value if the latter. */
9795 if (stopVarNamePtr) {
9796 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9797 if (objPtr == NULL ||
9798 Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9799 {
9800 Jim_DecrRefCount(interp, varNamePtr);
9801 Jim_DecrRefCount(interp, stopVarNamePtr);
9802 goto testcond;
9803 }
9804 }
9805 if (cmpType == JIM_EXPROP_LT) {
9806 if (currentVal >= stop) break;
9807 } else {
9808 if (currentVal > stop) break;
9809 }
9810 /* Eval body */
9811 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9812 switch(retval) {
9813 case JIM_BREAK:
9814 if (stopVarNamePtr)
9815 Jim_DecrRefCount(interp, stopVarNamePtr);
9816 Jim_DecrRefCount(interp, varNamePtr);
9817 goto out;
9818 case JIM_CONTINUE:
9819 /* nothing to do */
9820 break;
9821 default:
9822 if (stopVarNamePtr)
9823 Jim_DecrRefCount(interp, stopVarNamePtr);
9824 Jim_DecrRefCount(interp, varNamePtr);
9825 return retval;
9826 }
9827 }
9828 /* If there was a change in procedures/command continue
9829 * with the usual [for] command implementation */
9830 if (procEpoch != interp->procEpoch) {
9831 if (stopVarNamePtr)
9832 Jim_DecrRefCount(interp, stopVarNamePtr);
9833 Jim_DecrRefCount(interp, varNamePtr);
9834 goto evalnext;
9835 }
9836 /* Increment */
9837 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9838 if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9839 objPtr->internalRep.wideValue ++;
9840 Jim_InvalidateStringRep(objPtr);
9841 } else {
9842 Jim_Obj *auxObjPtr;
9843
9844 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9845 if (stopVarNamePtr)
9846 Jim_DecrRefCount(interp, stopVarNamePtr);
9847 Jim_DecrRefCount(interp, varNamePtr);
9848 goto evalnext;
9849 }
9850 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9851 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9852 if (stopVarNamePtr)
9853 Jim_DecrRefCount(interp, stopVarNamePtr);
9854 Jim_DecrRefCount(interp, varNamePtr);
9855 Jim_FreeNewObj(interp, auxObjPtr);
9856 goto evalnext;
9857 }
9858 }
9859 }
9860 if (stopVarNamePtr)
9861 Jim_DecrRefCount(interp, stopVarNamePtr);
9862 Jim_DecrRefCount(interp, varNamePtr);
9863 Jim_SetEmptyResult(interp);
9864 return JIM_OK;
9865 }
9866 #endif
9867 evalstart:
9868 /* Eval start */
9869 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9870 return retval;
9871 while (1) {
9872 int boolean;
9873 testcond:
9874 /* Test the condition */
9875 if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9876 != JIM_OK)
9877 return retval;
9878 if (!boolean) break;
9879 /* Eval body */
9880 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9881 switch(retval) {
9882 case JIM_BREAK:
9883 goto out;
9884 break;
9885 case JIM_CONTINUE:
9886 /* Nothing to do */
9887 break;
9888 default:
9889 return retval;
9890 }
9891 }
9892 evalnext:
9893 /* Eval next */
9894 if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9895 switch(retval) {
9896 case JIM_BREAK:
9897 goto out;
9898 break;
9899 case JIM_CONTINUE:
9900 continue;
9901 break;
9902 default:
9903 return retval;
9904 }
9905 }
9906 }
9907 out:
9908 Jim_SetEmptyResult(interp);
9909 return JIM_OK;
9910 }
9911
9912 /* foreach + lmap implementation. */
9913 static int JimForeachMapHelper(Jim_Interp *interp, int argc,
9914 Jim_Obj *const *argv, int doMap)
9915 {
9916 int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9917 int nbrOfLoops = 0;
9918 Jim_Obj *emptyStr, *script, *mapRes = NULL;
9919
9920 if (argc < 4 || argc % 2 != 0) {
9921 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9922 return JIM_ERR;
9923 }
9924 if (doMap) {
9925 mapRes = Jim_NewListObj(interp, NULL, 0);
9926 Jim_IncrRefCount(mapRes);
9927 }
9928 emptyStr = Jim_NewEmptyStringObj(interp);
9929 Jim_IncrRefCount(emptyStr);
9930 script = argv[argc-1]; /* Last argument is a script */
9931 nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
9932 listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9933 listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9934 /* Initialize iterators and remember max nbr elements each list */
9935 memset(listsIdx, 0, nbrOfLists * sizeof(int));
9936 /* Remember lengths of all lists and calculate how much rounds to loop */
9937 for (i=0; i < nbrOfLists*2; i += 2) {
9938 div_t cnt;
9939 int count;
9940 Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9941 Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9942 if (listsEnd[i] == 0) {
9943 Jim_SetResultString(interp, "foreach varlist is empty", -1);
9944 goto err;
9945 }
9946 cnt = div(listsEnd[i+1], listsEnd[i]);
9947 count = cnt.quot + (cnt.rem ? 1 : 0);
9948 if (count > nbrOfLoops)
9949 nbrOfLoops = count;
9950 }
9951 for (; nbrOfLoops-- > 0; ) {
9952 for (i=0; i < nbrOfLists; ++i) {
9953 int varIdx = 0, var = i * 2;
9954 while (varIdx < listsEnd[var]) {
9955 Jim_Obj *varName, *ele;
9956 int lst = i * 2 + 1;
9957 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9958 != JIM_OK)
9959 goto err;
9960 if (listsIdx[i] < listsEnd[lst]) {
9961 if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9962 != JIM_OK)
9963 goto err;
9964 if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9965 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9966 goto err;
9967 }
9968 ++listsIdx[i]; /* Remember next iterator of current list */
9969 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9970 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9971 goto err;
9972 }
9973 ++varIdx; /* Next variable */
9974 }
9975 }
9976 switch (result = Jim_EvalObj(interp, script)) {
9977 case JIM_OK:
9978 if (doMap)
9979 Jim_ListAppendElement(interp, mapRes, interp->result);
9980 break;
9981 case JIM_CONTINUE:
9982 break;
9983 case JIM_BREAK:
9984 goto out;
9985 break;
9986 default:
9987 goto err;
9988 }
9989 }
9990 out:
9991 result = JIM_OK;
9992 if (doMap)
9993 Jim_SetResult(interp, mapRes);
9994 else
9995 Jim_SetEmptyResult(interp);
9996 err:
9997 if (doMap)
9998 Jim_DecrRefCount(interp, mapRes);
9999 Jim_DecrRefCount(interp, emptyStr);
10000 Jim_Free(listsIdx);
10001 Jim_Free(listsEnd);
10002 return result;
10003 }
10004
10005 /* [foreach] */
10006 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
10007 Jim_Obj *const *argv)
10008 {
10009 return JimForeachMapHelper(interp, argc, argv, 0);
10010 }
10011
10012 /* [lmap] */
10013 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
10014 Jim_Obj *const *argv)
10015 {
10016 return JimForeachMapHelper(interp, argc, argv, 1);
10017 }
10018
10019 /* [if] */
10020 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
10021 Jim_Obj *const *argv)
10022 {
10023 int boolean, retval, current = 1, falsebody = 0;
10024 if (argc >= 3) {
10025 while (1) {
10026 /* Far not enough arguments given! */
10027 if (current >= argc) goto err;
10028 if ((retval = Jim_GetBoolFromExpr(interp,
10029 argv[current++], &boolean))
10030 != JIM_OK)
10031 return retval;
10032 /* There lacks something, isn't it? */
10033 if (current >= argc) goto err;
10034 if (Jim_CompareStringImmediate(interp, argv[current],
10035 "then")) current++;
10036 /* Tsk tsk, no then-clause? */
10037 if (current >= argc) goto err;
10038 if (boolean)
10039 return Jim_EvalObj(interp, argv[current]);
10040 /* Ok: no else-clause follows */
10041 if (++current >= argc) {
10042 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10043 return JIM_OK;
10044 }
10045 falsebody = current++;
10046 if (Jim_CompareStringImmediate(interp, argv[falsebody],
10047 "else")) {
10048 /* IIICKS - else-clause isn't last cmd? */
10049 if (current != argc-1) goto err;
10050 return Jim_EvalObj(interp, argv[current]);
10051 } else if (Jim_CompareStringImmediate(interp,
10052 argv[falsebody], "elseif"))
10053 /* Ok: elseif follows meaning all the stuff
10054 * again (how boring...) */
10055 continue;
10056 /* OOPS - else-clause is not last cmd?*/
10057 else if (falsebody != argc-1)
10058 goto err;
10059 return Jim_EvalObj(interp, argv[falsebody]);
10060 }
10061 return JIM_OK;
10062 }
10063 err:
10064 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10065 return JIM_ERR;
10066 }
10067
10068 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10069
10070 /* [switch] */
10071 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
10072 Jim_Obj *const *argv)
10073 {
10074 int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10075 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10076 Jim_Obj *script = 0;
10077 if (argc < 3) goto wrongnumargs;
10078 for (opt=1; opt < argc; ++opt) {
10079 const char *option = Jim_GetString(argv[opt], 0);
10080 if (*option != '-') break;
10081 else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10082 else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10083 else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10084 else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10085 else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10086 if ((argc - opt) < 2) goto wrongnumargs;
10087 command = argv[++opt];
10088 } else {
10089 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10090 Jim_AppendStrings(interp, Jim_GetResult(interp),
10091 "bad option \"", option, "\": must be -exact, -glob, "
10092 "-regexp, -command procname or --", 0);
10093 goto err;
10094 }
10095 if ((argc - opt) < 2) goto wrongnumargs;
10096 }
10097 strObj = argv[opt++];
10098 patCount = argc - opt;
10099 if (patCount == 1) {
10100 Jim_Obj **vector;
10101 JimListGetElements(interp, argv[opt], &patCount, &vector);
10102 caseList = vector;
10103 } else
10104 caseList = &argv[opt];
10105 if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10106 for (i=0; script == 0 && i < patCount; i += 2) {
10107 Jim_Obj *patObj = caseList[i];
10108 if (!Jim_CompareStringImmediate(interp, patObj, "default")
10109 || i < (patCount-2)) {
10110 switch (matchOpt) {
10111 case SWITCH_EXACT:
10112 if (Jim_StringEqObj(strObj, patObj, 0))
10113 script = caseList[i+1];
10114 break;
10115 case SWITCH_GLOB:
10116 if (Jim_StringMatchObj(patObj, strObj, 0))
10117 script = caseList[i+1];
10118 break;
10119 case SWITCH_RE:
10120 command = Jim_NewStringObj(interp, "regexp", -1);
10121 /* Fall thru intentionally */
10122 case SWITCH_CMD: {
10123 Jim_Obj *parms[] = {command, patObj, strObj};
10124 int rc = Jim_EvalObjVector(interp, 3, parms);
10125 long matching;
10126 /* After the execution of a command we need to
10127 * make sure to reconvert the object into a list
10128 * again. Only for the single-list style [switch]. */
10129 if (argc-opt == 1) {
10130 Jim_Obj **vector;
10131 JimListGetElements(interp, argv[opt], &patCount,
10132 &vector);
10133 caseList = vector;
10134 }
10135 /* command is here already decref'd */
10136 if (rc != JIM_OK) {
10137 retcode = rc;
10138 goto err;
10139 }
10140 rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10141 if (rc != JIM_OK) {
10142 retcode = rc;
10143 goto err;
10144 }
10145 if (matching)
10146 script = caseList[i+1];
10147 break;
10148 }
10149 default:
10150 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10151 Jim_AppendStrings(interp, Jim_GetResult(interp),
10152 "internal error: no such option implemented", 0);
10153 goto err;
10154 }
10155 } else {
10156 script = caseList[i+1];
10157 }
10158 }
10159 for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10160 i += 2)
10161 script = caseList[i+1];
10162 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10163 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10164 Jim_AppendStrings(interp, Jim_GetResult(interp),
10165 "no body specified for pattern \"",
10166 Jim_GetString(caseList[i-2], 0), "\"", 0);
10167 goto err;
10168 }
10169 retcode = JIM_OK;
10170 Jim_SetEmptyResult(interp);
10171 if (script != 0)
10172 retcode = Jim_EvalObj(interp, script);
10173 return retcode;
10174 wrongnumargs:
10175 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10176 "pattern body ... ?default body? or "
10177 "{pattern body ?pattern body ...?}");
10178 err:
10179 return retcode;
10180 }
10181
10182 /* [list] */
10183 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
10184 Jim_Obj *const *argv)
10185 {
10186 Jim_Obj *listObjPtr;
10187
10188 listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10189 Jim_SetResult(interp, listObjPtr);
10190 return JIM_OK;
10191 }
10192
10193 /* [lindex] */
10194 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
10195 Jim_Obj *const *argv)
10196 {
10197 Jim_Obj *objPtr, *listObjPtr;
10198 int i;
10199 int index;
10200
10201 if (argc < 3) {
10202 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10203 return JIM_ERR;
10204 }
10205 objPtr = argv[1];
10206 Jim_IncrRefCount(objPtr);
10207 for (i = 2; i < argc; i++) {
10208 listObjPtr = objPtr;
10209 if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10210 Jim_DecrRefCount(interp, listObjPtr);
10211 return JIM_ERR;
10212 }
10213 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10214 JIM_NONE) != JIM_OK) {
10215 /* Returns an empty object if the index
10216 * is out of range. */
10217 Jim_DecrRefCount(interp, listObjPtr);
10218 Jim_SetEmptyResult(interp);
10219 return JIM_OK;
10220 }
10221 Jim_IncrRefCount(objPtr);
10222 Jim_DecrRefCount(interp, listObjPtr);
10223 }
10224 Jim_SetResult(interp, objPtr);
10225 Jim_DecrRefCount(interp, objPtr);
10226 return JIM_OK;
10227 }
10228
10229 /* [llength] */
10230 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
10231 Jim_Obj *const *argv)
10232 {
10233 int len;
10234
10235 if (argc != 2) {
10236 Jim_WrongNumArgs(interp, 1, argv, "list");
10237 return JIM_ERR;
10238 }
10239 Jim_ListLength(interp, argv[1], &len);
10240 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10241 return JIM_OK;
10242 }
10243
10244 /* [lappend] */
10245 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
10246 Jim_Obj *const *argv)
10247 {
10248 Jim_Obj *listObjPtr;
10249 int shared, i;
10250
10251 if (argc < 2) {
10252 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10253 return JIM_ERR;
10254 }
10255 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10256 if (!listObjPtr) {
10257 /* Create the list if it does not exists */
10258 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10259 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10260 Jim_FreeNewObj(interp, listObjPtr);
10261 return JIM_ERR;
10262 }
10263 }
10264 shared = Jim_IsShared(listObjPtr);
10265 if (shared)
10266 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10267 for (i = 2; i < argc; i++)
10268 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10269 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10270 if (shared)
10271 Jim_FreeNewObj(interp, listObjPtr);
10272 return JIM_ERR;
10273 }
10274 Jim_SetResult(interp, listObjPtr);
10275 return JIM_OK;
10276 }
10277
10278 /* [linsert] */
10279 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10280 Jim_Obj *const *argv)
10281 {
10282 int index, len;
10283 Jim_Obj *listPtr;
10284
10285 if (argc < 4) {
10286 Jim_WrongNumArgs(interp, 1, argv, "list index element "
10287 "?element ...?");
10288 return JIM_ERR;
10289 }
10290 listPtr = argv[1];
10291 if (Jim_IsShared(listPtr))
10292 listPtr = Jim_DuplicateObj(interp, listPtr);
10293 if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10294 goto err;
10295 Jim_ListLength(interp, listPtr, &len);
10296 if (index >= len)
10297 index = len;
10298 else if (index < 0)
10299 index = len + index + 1;
10300 Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10301 Jim_SetResult(interp, listPtr);
10302 return JIM_OK;
10303 err:
10304 if (listPtr != argv[1]) {
10305 Jim_FreeNewObj(interp, listPtr);
10306 }
10307 return JIM_ERR;
10308 }
10309
10310 /* [lset] */
10311 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10312 Jim_Obj *const *argv)
10313 {
10314 if (argc < 3) {
10315 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10316 return JIM_ERR;
10317 } else if (argc == 3) {
10318 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10319 return JIM_ERR;
10320 Jim_SetResult(interp, argv[2]);
10321 return JIM_OK;
10322 }
10323 if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10324 == JIM_ERR) return JIM_ERR;
10325 return JIM_OK;
10326 }
10327
10328 /* [lsort] */
10329 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10330 {
10331 const char *options[] = {
10332 "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10333 };
10334 enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10335 Jim_Obj *resObj;
10336 int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10337 int decreasing = 0;
10338
10339 if (argc < 2) {
10340 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10341 return JIM_ERR;
10342 }
10343 for (i = 1; i < (argc-1); i++) {
10344 int option;
10345
10346 if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10347 != JIM_OK)
10348 return JIM_ERR;
10349 switch(option) {
10350 case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10351 case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10352 case OPT_INCREASING: decreasing = 0; break;
10353 case OPT_DECREASING: decreasing = 1; break;
10354 }
10355 }
10356 if (decreasing) {
10357 switch(lsortType) {
10358 case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10359 case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10360 }
10361 }
10362 resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10363 ListSortElements(interp, resObj, lsortType);
10364 Jim_SetResult(interp, resObj);
10365 return JIM_OK;
10366 }
10367
10368 /* [append] */
10369 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10370 Jim_Obj *const *argv)
10371 {
10372 Jim_Obj *stringObjPtr;
10373 int shared, i;
10374
10375 if (argc < 2) {
10376 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10377 return JIM_ERR;
10378 }
10379 if (argc == 2) {
10380 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10381 if (!stringObjPtr) return JIM_ERR;
10382 } else {
10383 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10384 if (!stringObjPtr) {
10385 /* Create the string if it does not exists */
10386 stringObjPtr = Jim_NewEmptyStringObj(interp);
10387 if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10388 != JIM_OK) {
10389 Jim_FreeNewObj(interp, stringObjPtr);
10390 return JIM_ERR;
10391 }
10392 }
10393 }
10394 shared = Jim_IsShared(stringObjPtr);
10395 if (shared)
10396 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10397 for (i = 2; i < argc; i++)
10398 Jim_AppendObj(interp, stringObjPtr, argv[i]);
10399 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10400 if (shared)
10401 Jim_FreeNewObj(interp, stringObjPtr);
10402 return JIM_ERR;
10403 }
10404 Jim_SetResult(interp, stringObjPtr);
10405 return JIM_OK;
10406 }
10407
10408 /* [debug] */
10409 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10410 Jim_Obj *const *argv)
10411 {
10412 const char *options[] = {
10413 "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10414 "exprbc",
10415 NULL
10416 };
10417 enum {
10418 OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10419 OPT_EXPRLEN, OPT_EXPRBC
10420 };
10421 int option;
10422
10423 if (argc < 2) {
10424 Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10425 return JIM_ERR;
10426 }
10427 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10428 JIM_ERRMSG) != JIM_OK)
10429 return JIM_ERR;
10430 if (option == OPT_REFCOUNT) {
10431 if (argc != 3) {
10432 Jim_WrongNumArgs(interp, 2, argv, "object");
10433 return JIM_ERR;
10434 }
10435 Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10436 return JIM_OK;
10437 } else if (option == OPT_OBJCOUNT) {
10438 int freeobj = 0, liveobj = 0;
10439 char buf[256];
10440 Jim_Obj *objPtr;
10441
10442 if (argc != 2) {
10443 Jim_WrongNumArgs(interp, 2, argv, "");
10444 return JIM_ERR;
10445 }
10446 /* Count the number of free objects. */
10447 objPtr = interp->freeList;
10448 while (objPtr) {
10449 freeobj++;
10450 objPtr = objPtr->nextObjPtr;
10451 }
10452 /* Count the number of live objects. */
10453 objPtr = interp->liveList;
10454 while (objPtr) {
10455 liveobj++;
10456 objPtr = objPtr->nextObjPtr;
10457 }
10458 /* Set the result string and return. */
10459 sprintf(buf, "free %d used %d", freeobj, liveobj);
10460 Jim_SetResultString(interp, buf, -1);
10461 return JIM_OK;
10462 } else if (option == OPT_OBJECTS) {
10463 Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10464 /* Count the number of live objects. */
10465 objPtr = interp->liveList;
10466 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10467 while (objPtr) {
10468 char buf[128];
10469 const char *type = objPtr->typePtr ?
10470 objPtr->typePtr->name : "";
10471 subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10472 sprintf(buf, "%p", objPtr);
10473 Jim_ListAppendElement(interp, subListObjPtr,
10474 Jim_NewStringObj(interp, buf, -1));
10475 Jim_ListAppendElement(interp, subListObjPtr,
10476 Jim_NewStringObj(interp, type, -1));
10477 Jim_ListAppendElement(interp, subListObjPtr,
10478 Jim_NewIntObj(interp, objPtr->refCount));
10479 Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10480 Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10481 objPtr = objPtr->nextObjPtr;
10482 }
10483 Jim_SetResult(interp, listObjPtr);
10484 return JIM_OK;
10485 } else if (option == OPT_INVSTR) {
10486 Jim_Obj *objPtr;
10487
10488 if (argc != 3) {
10489 Jim_WrongNumArgs(interp, 2, argv, "object");
10490 return JIM_ERR;
10491 }
10492 objPtr = argv[2];
10493 if (objPtr->typePtr != NULL)
10494 Jim_InvalidateStringRep(objPtr);
10495 Jim_SetEmptyResult(interp);
10496 return JIM_OK;
10497 } else if (option == OPT_SCRIPTLEN) {
10498 ScriptObj *script;
10499 if (argc != 3) {
10500 Jim_WrongNumArgs(interp, 2, argv, "script");
10501 return JIM_ERR;
10502 }
10503 script = Jim_GetScript(interp, argv[2]);
10504 Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10505 return JIM_OK;
10506 } else if (option == OPT_EXPRLEN) {
10507 ExprByteCode *expr;
10508 if (argc != 3) {
10509 Jim_WrongNumArgs(interp, 2, argv, "expression");
10510 return JIM_ERR;
10511 }
10512 expr = Jim_GetExpression(interp, argv[2]);
10513 if (expr == NULL)
10514 return JIM_ERR;
10515 Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10516 return JIM_OK;
10517 } else if (option == OPT_EXPRBC) {
10518 Jim_Obj *objPtr;
10519 ExprByteCode *expr;
10520 int i;
10521
10522 if (argc != 3) {
10523 Jim_WrongNumArgs(interp, 2, argv, "expression");
10524 return JIM_ERR;
10525 }
10526 expr = Jim_GetExpression(interp, argv[2]);
10527 if (expr == NULL)
10528 return JIM_ERR;
10529 objPtr = Jim_NewListObj(interp, NULL, 0);
10530 for (i = 0; i < expr->len; i++) {
10531 const char *type;
10532 Jim_ExprOperator *op;
10533
10534 switch(expr->opcode[i]) {
10535 case JIM_EXPROP_NUMBER: type = "number"; break;
10536 case JIM_EXPROP_COMMAND: type = "command"; break;
10537 case JIM_EXPROP_VARIABLE: type = "variable"; break;
10538 case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10539 case JIM_EXPROP_SUBST: type = "subst"; break;
10540 case JIM_EXPROP_STRING: type = "string"; break;
10541 default:
10542 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10543 if (op == NULL) {
10544 type = "private";
10545 } else {
10546 type = "operator";
10547 }
10548 break;
10549 }
10550 Jim_ListAppendElement(interp, objPtr,
10551 Jim_NewStringObj(interp, type, -1));
10552 Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10553 }
10554 Jim_SetResult(interp, objPtr);
10555 return JIM_OK;
10556 } else {
10557 Jim_SetResultString(interp,
10558 "bad option. Valid options are refcount, "
10559 "objcount, objects, invstr", -1);
10560 return JIM_ERR;
10561 }
10562 return JIM_OK; /* unreached */
10563 }
10564
10565 /* [eval] */
10566 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10567 Jim_Obj *const *argv)
10568 {
10569 if (argc == 2) {
10570 return Jim_EvalObj(interp, argv[1]);
10571 } else if (argc > 2) {
10572 Jim_Obj *objPtr;
10573 int retcode;
10574
10575 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10576 Jim_IncrRefCount(objPtr);
10577 retcode = Jim_EvalObj(interp, objPtr);
10578 Jim_DecrRefCount(interp, objPtr);
10579 return retcode;
10580 } else {
10581 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10582 return JIM_ERR;
10583 }
10584 }
10585
10586 /* [uplevel] */
10587 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10588 Jim_Obj *const *argv)
10589 {
10590 if (argc >= 2) {
10591 int retcode, newLevel, oldLevel;
10592 Jim_CallFrame *savedCallFrame, *targetCallFrame;
10593 Jim_Obj *objPtr;
10594 const char *str;
10595
10596 /* Save the old callframe pointer */
10597 savedCallFrame = interp->framePtr;
10598
10599 /* Lookup the target frame pointer */
10600 str = Jim_GetString(argv[1], NULL);
10601 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10602 {
10603 if (Jim_GetCallFrameByLevel(interp, argv[1],
10604 &targetCallFrame,
10605 &newLevel) != JIM_OK)
10606 return JIM_ERR;
10607 argc--;
10608 argv++;
10609 } else {
10610 if (Jim_GetCallFrameByLevel(interp, NULL,
10611 &targetCallFrame,
10612 &newLevel) != JIM_OK)
10613 return JIM_ERR;
10614 }
10615 if (argc < 2) {
10616 argc++;
10617 argv--;
10618 Jim_WrongNumArgs(interp, 1, argv,
10619 "?level? command ?arg ...?");
10620 return JIM_ERR;
10621 }
10622 /* Eval the code in the target callframe. */
10623 interp->framePtr = targetCallFrame;
10624 oldLevel = interp->numLevels;
10625 interp->numLevels = newLevel;
10626 if (argc == 2) {
10627 retcode = Jim_EvalObj(interp, argv[1]);
10628 } else {
10629 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10630 Jim_IncrRefCount(objPtr);
10631 retcode = Jim_EvalObj(interp, objPtr);
10632 Jim_DecrRefCount(interp, objPtr);
10633 }
10634 interp->numLevels = oldLevel;
10635 interp->framePtr = savedCallFrame;
10636 return retcode;
10637 } else {
10638 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10639 return JIM_ERR;
10640 }
10641 }
10642
10643 /* [expr] */
10644 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10645 Jim_Obj *const *argv)
10646 {
10647 Jim_Obj *exprResultPtr;
10648 int retcode;
10649
10650 if (argc == 2) {
10651 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10652 } else if (argc > 2) {
10653 Jim_Obj *objPtr;
10654
10655 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10656 Jim_IncrRefCount(objPtr);
10657 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10658 Jim_DecrRefCount(interp, objPtr);
10659 } else {
10660 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10661 return JIM_ERR;
10662 }
10663 if (retcode != JIM_OK) return retcode;
10664 Jim_SetResult(interp, exprResultPtr);
10665 Jim_DecrRefCount(interp, exprResultPtr);
10666 return JIM_OK;
10667 }
10668
10669 /* [break] */
10670 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10671 Jim_Obj *const *argv)
10672 {
10673 if (argc != 1) {
10674 Jim_WrongNumArgs(interp, 1, argv, "");
10675 return JIM_ERR;
10676 }
10677 return JIM_BREAK;
10678 }
10679
10680 /* [continue] */
10681 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10682 Jim_Obj *const *argv)
10683 {
10684 if (argc != 1) {
10685 Jim_WrongNumArgs(interp, 1, argv, "");
10686 return JIM_ERR;
10687 }
10688 return JIM_CONTINUE;
10689 }
10690
10691 /* [return] */
10692 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10693 Jim_Obj *const *argv)
10694 {
10695 if (argc == 1) {
10696 return JIM_RETURN;
10697 } else if (argc == 2) {
10698 Jim_SetResult(interp, argv[1]);
10699 interp->returnCode = JIM_OK;
10700 return JIM_RETURN;
10701 } else if (argc == 3 || argc == 4) {
10702 int returnCode;
10703 if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10704 return JIM_ERR;
10705 interp->returnCode = returnCode;
10706 if (argc == 4)
10707 Jim_SetResult(interp, argv[3]);
10708 return JIM_RETURN;
10709 } else {
10710 Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10711 return JIM_ERR;
10712 }
10713 return JIM_RETURN; /* unreached */
10714 }
10715
10716 /* [tailcall] */
10717 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10718 Jim_Obj *const *argv)
10719 {
10720 Jim_Obj *objPtr;
10721
10722 objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10723 Jim_SetResult(interp, objPtr);
10724 return JIM_EVAL;
10725 }
10726
10727 /* [proc] */
10728 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
10729 Jim_Obj *const *argv)
10730 {
10731 int argListLen;
10732 int arityMin, arityMax;
10733
10734 if (argc != 4 && argc != 5) {
10735 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10736 return JIM_ERR;
10737 }
10738 Jim_ListLength(interp, argv[2], &argListLen);
10739 arityMin = arityMax = argListLen+1;
10740 if (argListLen) {
10741 const char *str;
10742 int len;
10743 Jim_Obj *lastArgPtr;
10744
10745 Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10746 str = Jim_GetString(lastArgPtr, &len);
10747 if (len == 4 && memcmp(str, "args", 4) == 0) {
10748 arityMin--;
10749 arityMax = -1;
10750 }
10751 }
10752 if (argc == 4) {
10753 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10754 argv[2], NULL, argv[3], arityMin, arityMax);
10755 } else {
10756 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10757 argv[2], argv[3], argv[4], arityMin, arityMax);
10758 }
10759 }
10760
10761 /* [concat] */
10762 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
10763 Jim_Obj *const *argv)
10764 {
10765 Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10766 return JIM_OK;
10767 }
10768
10769 /* [upvar] */
10770 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
10771 Jim_Obj *const *argv)
10772 {
10773 const char *str;
10774 int i;
10775 Jim_CallFrame *targetCallFrame;
10776
10777 /* Lookup the target frame pointer */
10778 str = Jim_GetString(argv[1], NULL);
10779 if (argc > 3 &&
10780 ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10781 {
10782 if (Jim_GetCallFrameByLevel(interp, argv[1],
10783 &targetCallFrame, NULL) != JIM_OK)
10784 return JIM_ERR;
10785 argc--;
10786 argv++;
10787 } else {
10788 if (Jim_GetCallFrameByLevel(interp, NULL,
10789 &targetCallFrame, NULL) != JIM_OK)
10790 return JIM_ERR;
10791 }
10792 /* Check for arity */
10793 if (argc < 3 || ((argc-1)%2) != 0) {
10794 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10795 return JIM_ERR;
10796 }
10797 /* Now... for every other/local couple: */
10798 for (i = 1; i < argc; i += 2) {
10799 if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10800 targetCallFrame) != JIM_OK) return JIM_ERR;
10801 }
10802 return JIM_OK;
10803 }
10804
10805 /* [global] */
10806 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
10807 Jim_Obj *const *argv)
10808 {
10809 int i;
10810
10811 if (argc < 2) {
10812 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10813 return JIM_ERR;
10814 }
10815 /* Link every var to the toplevel having the same name */
10816 if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10817 for (i = 1; i < argc; i++) {
10818 if (Jim_SetVariableLink(interp, argv[i], argv[i],
10819 interp->topFramePtr) != JIM_OK) return JIM_ERR;
10820 }
10821 return JIM_OK;
10822 }
10823
10824 /* does the [string map] operation. On error NULL is returned,
10825 * otherwise a new string object with the result, having refcount = 0,
10826 * is returned. */
10827 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10828 Jim_Obj *objPtr, int nocase)
10829 {
10830 int numMaps;
10831 const char **key, *str, *noMatchStart = NULL;
10832 Jim_Obj **value;
10833 int *keyLen, strLen, i;
10834 Jim_Obj *resultObjPtr;
10835
10836 Jim_ListLength(interp, mapListObjPtr, &numMaps);
10837 if (numMaps % 2) {
10838 Jim_SetResultString(interp,
10839 "list must contain an even number of elements", -1);
10840 return NULL;
10841 }
10842 /* Initialization */
10843 numMaps /= 2;
10844 key = Jim_Alloc(sizeof(char*)*numMaps);
10845 keyLen = Jim_Alloc(sizeof(int)*numMaps);
10846 value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10847 resultObjPtr = Jim_NewStringObj(interp, "", 0);
10848 for (i = 0; i < numMaps; i++) {
10849 Jim_Obj *eleObjPtr;
10850
10851 Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10852 key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10853 Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10854 value[i] = eleObjPtr;
10855 }
10856 str = Jim_GetString(objPtr, &strLen);
10857 /* Map it */
10858 while(strLen) {
10859 for (i = 0; i < numMaps; i++) {
10860 if (strLen >= keyLen[i] && keyLen[i]) {
10861 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10862 nocase))
10863 {
10864 if (noMatchStart) {
10865 Jim_AppendString(interp, resultObjPtr,
10866 noMatchStart, str-noMatchStart);
10867 noMatchStart = NULL;
10868 }
10869 Jim_AppendObj(interp, resultObjPtr, value[i]);
10870 str += keyLen[i];
10871 strLen -= keyLen[i];
10872 break;
10873 }
10874 }
10875 }
10876 if (i == numMaps) { /* no match */
10877 if (noMatchStart == NULL)
10878 noMatchStart = str;
10879 str ++;
10880 strLen --;
10881 }
10882 }
10883 if (noMatchStart) {
10884 Jim_AppendString(interp, resultObjPtr,
10885 noMatchStart, str-noMatchStart);
10886 }
10887 Jim_Free((void*)key);
10888 Jim_Free(keyLen);
10889 Jim_Free(value);
10890 return resultObjPtr;
10891 }
10892
10893 /* [string] */
10894 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
10895 Jim_Obj *const *argv)
10896 {
10897 int option;
10898 const char *options[] = {
10899 "length", "compare", "match", "equal", "range", "map", "repeat",
10900 "index", "first", "tolower", "toupper", NULL
10901 };
10902 enum {
10903 OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10904 OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10905 };
10906
10907 if (argc < 2) {
10908 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10909 return JIM_ERR;
10910 }
10911 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10912 JIM_ERRMSG) != JIM_OK)
10913 return JIM_ERR;
10914
10915 if (option == OPT_LENGTH) {
10916 int len;
10917
10918 if (argc != 3) {
10919 Jim_WrongNumArgs(interp, 2, argv, "string");
10920 return JIM_ERR;
10921 }
10922 Jim_GetString(argv[2], &len);
10923 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10924 return JIM_OK;
10925 } else if (option == OPT_COMPARE) {
10926 int nocase = 0;
10927 if ((argc != 4 && argc != 5) ||
10928 (argc == 5 && Jim_CompareStringImmediate(interp,
10929 argv[2], "-nocase") == 0)) {
10930 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10931 return JIM_ERR;
10932 }
10933 if (argc == 5) {
10934 nocase = 1;
10935 argv++;
10936 }
10937 Jim_SetResult(interp, Jim_NewIntObj(interp,
10938 Jim_StringCompareObj(argv[2],
10939 argv[3], nocase)));
10940 return JIM_OK;
10941 } else if (option == OPT_MATCH) {
10942 int nocase = 0;
10943 if ((argc != 4 && argc != 5) ||
10944 (argc == 5 && Jim_CompareStringImmediate(interp,
10945 argv[2], "-nocase") == 0)) {
10946 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10947 "string");
10948 return JIM_ERR;
10949 }
10950 if (argc == 5) {
10951 nocase = 1;
10952 argv++;
10953 }
10954 Jim_SetResult(interp,
10955 Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10956 argv[3], nocase)));
10957 return JIM_OK;
10958 } else if (option == OPT_EQUAL) {
10959 if (argc != 4) {
10960 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10961 return JIM_ERR;
10962 }
10963 Jim_SetResult(interp,
10964 Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10965 argv[3], 0)));
10966 return JIM_OK;
10967 } else if (option == OPT_RANGE) {
10968 Jim_Obj *objPtr;
10969
10970 if (argc != 5) {
10971 Jim_WrongNumArgs(interp, 2, argv, "string first last");
10972 return JIM_ERR;
10973 }
10974 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10975 if (objPtr == NULL)
10976 return JIM_ERR;
10977 Jim_SetResult(interp, objPtr);
10978 return JIM_OK;
10979 } else if (option == OPT_MAP) {
10980 int nocase = 0;
10981 Jim_Obj *objPtr;
10982
10983 if ((argc != 4 && argc != 5) ||
10984 (argc == 5 && Jim_CompareStringImmediate(interp,
10985 argv[2], "-nocase") == 0)) {
10986 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10987 "string");
10988 return JIM_ERR;
10989 }
10990 if (argc == 5) {
10991 nocase = 1;
10992 argv++;
10993 }
10994 objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10995 if (objPtr == NULL)
10996 return JIM_ERR;
10997 Jim_SetResult(interp, objPtr);
10998 return JIM_OK;
10999 } else if (option == OPT_REPEAT) {
11000 Jim_Obj *objPtr;
11001 jim_wide count;
11002
11003 if (argc != 4) {
11004 Jim_WrongNumArgs(interp, 2, argv, "string count");
11005 return JIM_ERR;
11006 }
11007 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
11008 return JIM_ERR;
11009 objPtr = Jim_NewStringObj(interp, "", 0);
11010 while (count--) {
11011 Jim_AppendObj(interp, objPtr, argv[2]);
11012 }
11013 Jim_SetResult(interp, objPtr);
11014 return JIM_OK;
11015 } else if (option == OPT_INDEX) {
11016 int index, len;
11017 const char *str;
11018
11019 if (argc != 4) {
11020 Jim_WrongNumArgs(interp, 2, argv, "string index");
11021 return JIM_ERR;
11022 }
11023 if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
11024 return JIM_ERR;
11025 str = Jim_GetString(argv[2], &len);
11026 if (index != INT_MIN && index != INT_MAX)
11027 index = JimRelToAbsIndex(len, index);
11028 if (index < 0 || index >= len) {
11029 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11030 return JIM_OK;
11031 } else {
11032 Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
11033 return JIM_OK;
11034 }
11035 } else if (option == OPT_FIRST) {
11036 int index = 0, l1, l2;
11037 const char *s1, *s2;
11038
11039 if (argc != 4 && argc != 5) {
11040 Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
11041 return JIM_ERR;
11042 }
11043 s1 = Jim_GetString(argv[2], &l1);
11044 s2 = Jim_GetString(argv[3], &l2);
11045 if (argc == 5) {
11046 if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
11047 return JIM_ERR;
11048 index = JimRelToAbsIndex(l2, index);
11049 }
11050 Jim_SetResult(interp, Jim_NewIntObj(interp,
11051 JimStringFirst(s1, l1, s2, l2, index)));
11052 return JIM_OK;
11053 } else if (option == OPT_TOLOWER) {
11054 if (argc != 3) {
11055 Jim_WrongNumArgs(interp, 2, argv, "string");
11056 return JIM_ERR;
11057 }
11058 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11059 } else if (option == OPT_TOUPPER) {
11060 if (argc != 3) {
11061 Jim_WrongNumArgs(interp, 2, argv, "string");
11062 return JIM_ERR;
11063 }
11064 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11065 }
11066 return JIM_OK;
11067 }
11068
11069 /* [time] */
11070 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
11071 Jim_Obj *const *argv)
11072 {
11073 long i, count = 1;
11074 jim_wide start, elapsed;
11075 char buf [256];
11076 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11077
11078 if (argc < 2) {
11079 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11080 return JIM_ERR;
11081 }
11082 if (argc == 3) {
11083 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11084 return JIM_ERR;
11085 }
11086 if (count < 0)
11087 return JIM_OK;
11088 i = count;
11089 start = JimClock();
11090 while (i-- > 0) {
11091 int retval;
11092
11093 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11094 return retval;
11095 }
11096 elapsed = JimClock() - start;
11097 sprintf(buf, fmt, elapsed/count);
11098 Jim_SetResultString(interp, buf, -1);
11099 return JIM_OK;
11100 }
11101
11102 /* [exit] */
11103 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
11104 Jim_Obj *const *argv)
11105 {
11106 long exitCode = 0;
11107
11108 if (argc > 2) {
11109 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11110 return JIM_ERR;
11111 }
11112 if (argc == 2) {
11113 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11114 return JIM_ERR;
11115 }
11116 interp->exitCode = exitCode;
11117 return JIM_EXIT;
11118 }
11119
11120 /* [catch] */
11121 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
11122 Jim_Obj *const *argv)
11123 {
11124 int exitCode = 0;
11125
11126 if (argc != 2 && argc != 3) {
11127 Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11128 return JIM_ERR;
11129 }
11130 exitCode = Jim_EvalObj(interp, argv[1]);
11131 if (argc == 3) {
11132 if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11133 != JIM_OK)
11134 return JIM_ERR;
11135 }
11136 Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11137 return JIM_OK;
11138 }
11139
11140 /* [ref] */
11141 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
11142 Jim_Obj *const *argv)
11143 {
11144 if (argc != 3 && argc != 4) {
11145 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11146 return JIM_ERR;
11147 }
11148 if (argc == 3) {
11149 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11150 } else {
11151 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11152 argv[3]));
11153 }
11154 return JIM_OK;
11155 }
11156
11157 /* [getref] */
11158 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
11159 Jim_Obj *const *argv)
11160 {
11161 Jim_Reference *refPtr;
11162
11163 if (argc != 2) {
11164 Jim_WrongNumArgs(interp, 1, argv, "reference");
11165 return JIM_ERR;
11166 }
11167 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11168 return JIM_ERR;
11169 Jim_SetResult(interp, refPtr->objPtr);
11170 return JIM_OK;
11171 }
11172
11173 /* [setref] */
11174 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
11175 Jim_Obj *const *argv)
11176 {
11177 Jim_Reference *refPtr;
11178
11179 if (argc != 3) {
11180 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11181 return JIM_ERR;
11182 }
11183 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11184 return JIM_ERR;
11185 Jim_IncrRefCount(argv[2]);
11186 Jim_DecrRefCount(interp, refPtr->objPtr);
11187 refPtr->objPtr = argv[2];
11188 Jim_SetResult(interp, argv[2]);
11189 return JIM_OK;
11190 }
11191
11192 /* [collect] */
11193 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
11194 Jim_Obj *const *argv)
11195 {
11196 if (argc != 1) {
11197 Jim_WrongNumArgs(interp, 1, argv, "");
11198 return JIM_ERR;
11199 }
11200 Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11201 return JIM_OK;
11202 }
11203
11204 /* [finalize] reference ?newValue? */
11205 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
11206 Jim_Obj *const *argv)
11207 {
11208 if (argc != 2 && argc != 3) {
11209 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11210 return JIM_ERR;
11211 }
11212 if (argc == 2) {
11213 Jim_Obj *cmdNamePtr;
11214
11215 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11216 return JIM_ERR;
11217 if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11218 Jim_SetResult(interp, cmdNamePtr);
11219 } else {
11220 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11221 return JIM_ERR;
11222 Jim_SetResult(interp, argv[2]);
11223 }
11224 return JIM_OK;
11225 }
11226
11227 /* TODO */
11228 /* [info references] (list of all the references/finalizers) */
11229
11230 /* [rename] */
11231 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
11232 Jim_Obj *const *argv)
11233 {
11234 const char *oldName, *newName;
11235
11236 if (argc != 3) {
11237 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11238 return JIM_ERR;
11239 }
11240 oldName = Jim_GetString(argv[1], NULL);
11241 newName = Jim_GetString(argv[2], NULL);
11242 if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11243 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11244 Jim_AppendStrings(interp, Jim_GetResult(interp),
11245 "can't rename \"", oldName, "\": ",
11246 "command doesn't exist", NULL);
11247 return JIM_ERR;
11248 }
11249 return JIM_OK;
11250 }
11251
11252 /* [dict] */
11253 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
11254 Jim_Obj *const *argv)
11255 {
11256 int option;
11257 const char *options[] = {
11258 "create", "get", "set", "unset", "exists", NULL
11259 };
11260 enum {
11261 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11262 };
11263
11264 if (argc < 2) {
11265 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11266 return JIM_ERR;
11267 }
11268
11269 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11270 JIM_ERRMSG) != JIM_OK)
11271 return JIM_ERR;
11272
11273 if (option == OPT_CREATE) {
11274 Jim_Obj *objPtr;
11275
11276 if (argc % 2) {
11277 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11278 return JIM_ERR;
11279 }
11280 objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11281 Jim_SetResult(interp, objPtr);
11282 return JIM_OK;
11283 } else if (option == OPT_GET) {
11284 Jim_Obj *objPtr;
11285
11286 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11287 JIM_ERRMSG) != JIM_OK)
11288 return JIM_ERR;
11289 Jim_SetResult(interp, objPtr);
11290 return JIM_OK;
11291 } else if (option == OPT_SET) {
11292 if (argc < 5) {
11293 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11294 return JIM_ERR;
11295 }
11296 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11297 argv[argc-1]);
11298 } else if (option == OPT_UNSET) {
11299 if (argc < 4) {
11300 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11301 return JIM_ERR;
11302 }
11303 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11304 NULL);
11305 } else if (option == OPT_EXIST) {
11306 Jim_Obj *objPtr;
11307 int exists;
11308
11309 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11310 JIM_ERRMSG) == JIM_OK)
11311 exists = 1;
11312 else
11313 exists = 0;
11314 Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11315 return JIM_OK;
11316 } else {
11317 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11318 Jim_AppendStrings(interp, Jim_GetResult(interp),
11319 "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11320 " must be create, get, set", NULL);
11321 return JIM_ERR;
11322 }
11323 return JIM_OK;
11324 }
11325
11326 /* [load] */
11327 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11328 Jim_Obj *const *argv)
11329 {
11330 if (argc < 2) {
11331 Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11332 return JIM_ERR;
11333 }
11334 return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11335 }
11336
11337 /* [subst] */
11338 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11339 Jim_Obj *const *argv)
11340 {
11341 int i, flags = 0;
11342 Jim_Obj *objPtr;
11343
11344 if (argc < 2) {
11345 Jim_WrongNumArgs(interp, 1, argv,
11346 "?-nobackslashes? ?-nocommands? ?-novariables? string");
11347 return JIM_ERR;
11348 }
11349 i = argc-2;
11350 while(i--) {
11351 if (Jim_CompareStringImmediate(interp, argv[i+1],
11352 "-nobackslashes"))
11353 flags |= JIM_SUBST_NOESC;
11354 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11355 "-novariables"))
11356 flags |= JIM_SUBST_NOVAR;
11357 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11358 "-nocommands"))
11359 flags |= JIM_SUBST_NOCMD;
11360 else {
11361 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11362 Jim_AppendStrings(interp, Jim_GetResult(interp),
11363 "bad option \"", Jim_GetString(argv[i+1], NULL),
11364 "\": must be -nobackslashes, -nocommands, or "
11365 "-novariables", NULL);
11366 return JIM_ERR;
11367 }
11368 }
11369 if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11370 return JIM_ERR;
11371 Jim_SetResult(interp, objPtr);
11372 return JIM_OK;
11373 }
11374
11375 /* [info] */
11376 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11377 Jim_Obj *const *argv)
11378 {
11379 int cmd, result = JIM_OK;
11380 static const char *commands[] = {
11381 "body", "commands", "exists", "globals", "level", "locals",
11382 "vars", "version", "complete", "args", NULL
11383 };
11384 enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11385 INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11386
11387 if (argc < 2) {
11388 Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11389 return JIM_ERR;
11390 }
11391 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11392 != JIM_OK) {
11393 return JIM_ERR;
11394 }
11395
11396 if (cmd == INFO_COMMANDS) {
11397 if (argc != 2 && argc != 3) {
11398 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11399 return JIM_ERR;
11400 }
11401 if (argc == 3)
11402 Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11403 else
11404 Jim_SetResult(interp, JimCommandsList(interp, NULL));
11405 } else if (cmd == INFO_EXISTS) {
11406 Jim_Obj *exists;
11407 if (argc != 3) {
11408 Jim_WrongNumArgs(interp, 2, argv, "varName");
11409 return JIM_ERR;
11410 }
11411 exists = Jim_GetVariable(interp, argv[2], 0);
11412 Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11413 } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11414 int mode;
11415 switch (cmd) {
11416 case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11417 case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
11418 case INFO_VARS: mode = JIM_VARLIST_VARS; break;
11419 default: mode = 0; /* avoid warning */; break;
11420 }
11421 if (argc != 2 && argc != 3) {
11422 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11423 return JIM_ERR;
11424 }
11425 if (argc == 3)
11426 Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11427 else
11428 Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11429 } else if (cmd == INFO_LEVEL) {
11430 Jim_Obj *objPtr;
11431 switch (argc) {
11432 case 2:
11433 Jim_SetResult(interp,
11434 Jim_NewIntObj(interp, interp->numLevels));
11435 break;
11436 case 3:
11437 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11438 return JIM_ERR;
11439 Jim_SetResult(interp, objPtr);
11440 break;
11441 default:
11442 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11443 return JIM_ERR;
11444 }
11445 } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11446 Jim_Cmd *cmdPtr;
11447
11448 if (argc != 3) {
11449 Jim_WrongNumArgs(interp, 2, argv, "procname");
11450 return JIM_ERR;
11451 }
11452 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11453 return JIM_ERR;
11454 if (cmdPtr->cmdProc != NULL) {
11455 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11456 Jim_AppendStrings(interp, Jim_GetResult(interp),
11457 "command \"", Jim_GetString(argv[2], NULL),
11458 "\" is not a procedure", NULL);
11459 return JIM_ERR;
11460 }
11461 if (cmd == INFO_BODY)
11462 Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11463 else
11464 Jim_SetResult(interp, cmdPtr->argListObjPtr);
11465 } else if (cmd == INFO_VERSION) {
11466 char buf[(JIM_INTEGER_SPACE * 2) + 1];
11467 sprintf(buf, "%d.%d",
11468 JIM_VERSION / 100, JIM_VERSION % 100);
11469 Jim_SetResultString(interp, buf, -1);
11470 } else if (cmd == INFO_COMPLETE) {
11471 const char *s;
11472 int len;
11473
11474 if (argc != 3) {
11475 Jim_WrongNumArgs(interp, 2, argv, "script");
11476 return JIM_ERR;
11477 }
11478 s = Jim_GetString(argv[2], &len);
11479 Jim_SetResult(interp,
11480 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11481 }
11482 return result;
11483 }
11484
11485 /* [split] */
11486 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11487 Jim_Obj *const *argv)
11488 {
11489 const char *str, *splitChars, *noMatchStart;
11490 int splitLen, strLen, i;
11491 Jim_Obj *resObjPtr;
11492
11493 if (argc != 2 && argc != 3) {
11494 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11495 return JIM_ERR;
11496 }
11497 /* Init */
11498 if (argc == 2) {
11499 splitChars = " \n\t\r";
11500 splitLen = 4;
11501 } else {
11502 splitChars = Jim_GetString(argv[2], &splitLen);
11503 }
11504 str = Jim_GetString(argv[1], &strLen);
11505 if (!strLen) return JIM_OK;
11506 noMatchStart = str;
11507 resObjPtr = Jim_NewListObj(interp, NULL, 0);
11508 /* Split */
11509 if (splitLen) {
11510 while (strLen) {
11511 for (i = 0; i < splitLen; i++) {
11512 if (*str == splitChars[i]) {
11513 Jim_Obj *objPtr;
11514
11515 objPtr = Jim_NewStringObj(interp, noMatchStart,
11516 (str-noMatchStart));
11517 Jim_ListAppendElement(interp, resObjPtr, objPtr);
11518 noMatchStart = str+1;
11519 break;
11520 }
11521 }
11522 str ++;
11523 strLen --;
11524 }
11525 Jim_ListAppendElement(interp, resObjPtr,
11526 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11527 } else {
11528 /* This handles the special case of splitchars eq {}. This
11529 * is trivial but we want to perform object sharing as Tcl does. */
11530 Jim_Obj *objCache[256];
11531 const unsigned char *u = (unsigned char*) str;
11532 memset(objCache, 0, sizeof(objCache));
11533 for (i = 0; i < strLen; i++) {
11534 int c = u[i];
11535
11536 if (objCache[c] == NULL)
11537 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11538 Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11539 }
11540 }
11541 Jim_SetResult(interp, resObjPtr);
11542 return JIM_OK;
11543 }
11544
11545 /* [join] */
11546 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11547 Jim_Obj *const *argv)
11548 {
11549 const char *joinStr;
11550 int joinStrLen, i, listLen;
11551 Jim_Obj *resObjPtr;
11552
11553 if (argc != 2 && argc != 3) {
11554 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11555 return JIM_ERR;
11556 }
11557 /* Init */
11558 if (argc == 2) {
11559 joinStr = " ";
11560 joinStrLen = 1;
11561 } else {
11562 joinStr = Jim_GetString(argv[2], &joinStrLen);
11563 }
11564 Jim_ListLength(interp, argv[1], &listLen);
11565 resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11566 /* Split */
11567 for (i = 0; i < listLen; i++) {
11568 Jim_Obj *objPtr;
11569
11570 Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11571 Jim_AppendObj(interp, resObjPtr, objPtr);
11572 if (i+1 != listLen) {
11573 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11574 }
11575 }
11576 Jim_SetResult(interp, resObjPtr);
11577 return JIM_OK;
11578 }
11579
11580 /* [format] */
11581 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11582 Jim_Obj *const *argv)
11583 {
11584 Jim_Obj *objPtr;
11585
11586 if (argc < 2) {
11587 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11588 return JIM_ERR;
11589 }
11590 objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11591 if (objPtr == NULL)
11592 return JIM_ERR;
11593 Jim_SetResult(interp, objPtr);
11594 return JIM_OK;
11595 }
11596
11597 /* [scan] */
11598 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11599 Jim_Obj *const *argv)
11600 {
11601 Jim_Obj *listPtr, **outVec;
11602 int outc, i, count = 0;
11603
11604 if (argc < 3) {
11605 Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11606 return JIM_ERR;
11607 }
11608 if (argv[2]->typePtr != &scanFmtStringObjType)
11609 SetScanFmtFromAny(interp, argv[2]);
11610 if (FormatGetError(argv[2]) != 0) {
11611 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11612 return JIM_ERR;
11613 }
11614 if (argc > 3) {
11615 int maxPos = FormatGetMaxPos(argv[2]);
11616 int count = FormatGetCnvCount(argv[2]);
11617 if (maxPos > argc-3) {
11618 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11619 return JIM_ERR;
11620 } else if (count != 0 && count < argc-3) {
11621 Jim_SetResultString(interp, "variable is not assigned by any "
11622 "conversion specifiers", -1);
11623 return JIM_ERR;
11624 } else if (count > argc-3) {
11625 Jim_SetResultString(interp, "different numbers of variable names and "
11626 "field specifiers", -1);
11627 return JIM_ERR;
11628 }
11629 }
11630 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11631 if (listPtr == 0)
11632 return JIM_ERR;
11633 if (argc > 3) {
11634 int len = 0;
11635 if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11636 Jim_ListLength(interp, listPtr, &len);
11637 if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11638 Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11639 return JIM_OK;
11640 }
11641 JimListGetElements(interp, listPtr, &outc, &outVec);
11642 for (i = 0; i < outc; ++i) {
11643 if (Jim_Length(outVec[i]) > 0) {
11644 ++count;
11645 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11646 goto err;
11647 }
11648 }
11649 Jim_FreeNewObj(interp, listPtr);
11650 Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11651 } else {
11652 if (listPtr == (Jim_Obj*)EOF) {
11653 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11654 return JIM_OK;
11655 }
11656 Jim_SetResult(interp, listPtr);
11657 }
11658 return JIM_OK;
11659 err:
11660 Jim_FreeNewObj(interp, listPtr);
11661 return JIM_ERR;
11662 }
11663
11664 /* [error] */
11665 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11666 Jim_Obj *const *argv)
11667 {
11668 if (argc != 2) {
11669 Jim_WrongNumArgs(interp, 1, argv, "message");
11670 return JIM_ERR;
11671 }
11672 Jim_SetResult(interp, argv[1]);
11673 return JIM_ERR;
11674 }
11675
11676 /* [lrange] */
11677 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11678 Jim_Obj *const *argv)
11679 {
11680 Jim_Obj *objPtr;
11681
11682 if (argc != 4) {
11683 Jim_WrongNumArgs(interp, 1, argv, "list first last");
11684 return JIM_ERR;
11685 }
11686 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11687 return JIM_ERR;
11688 Jim_SetResult(interp, objPtr);
11689 return JIM_OK;
11690 }
11691
11692 /* [env] */
11693 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11694 Jim_Obj *const *argv)
11695 {
11696 const char *key;
11697 char *val;
11698
11699 if (argc != 2) {
11700 Jim_WrongNumArgs(interp, 1, argv, "varName");
11701 return JIM_ERR;
11702 }
11703 key = Jim_GetString(argv[1], NULL);
11704 val = getenv(key);
11705 if (val == NULL) {
11706 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11707 Jim_AppendStrings(interp, Jim_GetResult(interp),
11708 "environment variable \"",
11709 key, "\" does not exist", NULL);
11710 return JIM_ERR;
11711 }
11712 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11713 return JIM_OK;
11714 }
11715
11716 /* [source] */
11717 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11718 Jim_Obj *const *argv)
11719 {
11720 int retval;
11721
11722 if (argc != 2) {
11723 Jim_WrongNumArgs(interp, 1, argv, "fileName");
11724 return JIM_ERR;
11725 }
11726 retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11727 if (retval == JIM_RETURN)
11728 return JIM_OK;
11729 return retval;
11730 }
11731
11732 /* [lreverse] */
11733 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11734 Jim_Obj *const *argv)
11735 {
11736 Jim_Obj *revObjPtr, **ele;
11737 int len;
11738
11739 if (argc != 2) {
11740 Jim_WrongNumArgs(interp, 1, argv, "list");
11741 return JIM_ERR;
11742 }
11743 JimListGetElements(interp, argv[1], &len, &ele);
11744 len--;
11745 revObjPtr = Jim_NewListObj(interp, NULL, 0);
11746 while (len >= 0)
11747 ListAppendElement(revObjPtr, ele[len--]);
11748 Jim_SetResult(interp, revObjPtr);
11749 return JIM_OK;
11750 }
11751
11752 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11753 {
11754 jim_wide len;
11755
11756 if (step == 0) return -1;
11757 if (start == end) return 0;
11758 else if (step > 0 && start > end) return -1;
11759 else if (step < 0 && end > start) return -1;
11760 len = end-start;
11761 if (len < 0) len = -len; /* abs(len) */
11762 if (step < 0) step = -step; /* abs(step) */
11763 len = 1 + ((len-1)/step);
11764 /* We can truncate safely to INT_MAX, the range command
11765 * will always return an error for a such long range
11766 * because Tcl lists can't be so long. */
11767 if (len > INT_MAX) len = INT_MAX;
11768 return (int)((len < 0) ? -1 : len);
11769 }
11770
11771 /* [range] */
11772 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11773 Jim_Obj *const *argv)
11774 {
11775 jim_wide start = 0, end, step = 1;
11776 int len, i;
11777 Jim_Obj *objPtr;
11778
11779 if (argc < 2 || argc > 4) {
11780 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11781 return JIM_ERR;
11782 }
11783 if (argc == 2) {
11784 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11785 return JIM_ERR;
11786 } else {
11787 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11788 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11789 return JIM_ERR;
11790 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11791 return JIM_ERR;
11792 }
11793 if ((len = JimRangeLen(start, end, step)) == -1) {
11794 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11795 return JIM_ERR;
11796 }
11797 objPtr = Jim_NewListObj(interp, NULL, 0);
11798 for (i = 0; i < len; i++)
11799 ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11800 Jim_SetResult(interp, objPtr);
11801 return JIM_OK;
11802 }
11803
11804 /* [rand] */
11805 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11806 Jim_Obj *const *argv)
11807 {
11808 jim_wide min = 0, max, len, maxMul;
11809
11810 if (argc < 1 || argc > 3) {
11811 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11812 return JIM_ERR;
11813 }
11814 if (argc == 1) {
11815 max = JIM_WIDE_MAX;
11816 } else if (argc == 2) {
11817 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11818 return JIM_ERR;
11819 } else if (argc == 3) {
11820 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11821 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11822 return JIM_ERR;
11823 }
11824 len = max-min;
11825 if (len < 0) {
11826 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11827 return JIM_ERR;
11828 }
11829 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11830 while (1) {
11831 jim_wide r;
11832
11833 JimRandomBytes(interp, &r, sizeof(jim_wide));
11834 if (r < 0 || r >= maxMul) continue;
11835 r = (len == 0) ? 0 : r%len;
11836 Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11837 return JIM_OK;
11838 }
11839 }
11840
11841 /* [package] */
11842 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
11843 Jim_Obj *const *argv)
11844 {
11845 int option;
11846 const char *options[] = {
11847 "require", "provide", NULL
11848 };
11849 enum {OPT_REQUIRE, OPT_PROVIDE};
11850
11851 if (argc < 2) {
11852 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11853 return JIM_ERR;
11854 }
11855 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11856 JIM_ERRMSG) != JIM_OK)
11857 return JIM_ERR;
11858
11859 if (option == OPT_REQUIRE) {
11860 int exact = 0;
11861 const char *ver;
11862
11863 if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11864 exact = 1;
11865 argv++;
11866 argc--;
11867 }
11868 if (argc != 3 && argc != 4) {
11869 Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11870 return JIM_ERR;
11871 }
11872 ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11873 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11874 JIM_ERRMSG);
11875 if (ver == NULL)
11876 return JIM_ERR;
11877 Jim_SetResultString(interp, ver, -1);
11878 } else if (option == OPT_PROVIDE) {
11879 if (argc != 4) {
11880 Jim_WrongNumArgs(interp, 2, argv, "package version");
11881 return JIM_ERR;
11882 }
11883 return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11884 Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11885 }
11886 return JIM_OK;
11887 }
11888
11889 static struct {
11890 const char *name;
11891 Jim_CmdProc cmdProc;
11892 } Jim_CoreCommandsTable[] = {
11893 {"set", Jim_SetCoreCommand},
11894 {"unset", Jim_UnsetCoreCommand},
11895 {"puts", Jim_PutsCoreCommand},
11896 {"+", Jim_AddCoreCommand},
11897 {"*", Jim_MulCoreCommand},
11898 {"-", Jim_SubCoreCommand},
11899 {"/", Jim_DivCoreCommand},
11900 {"incr", Jim_IncrCoreCommand},
11901 {"while", Jim_WhileCoreCommand},
11902 {"for", Jim_ForCoreCommand},
11903 {"foreach", Jim_ForeachCoreCommand},
11904 {"lmap", Jim_LmapCoreCommand},
11905 {"if", Jim_IfCoreCommand},
11906 {"switch", Jim_SwitchCoreCommand},
11907 {"list", Jim_ListCoreCommand},
11908 {"lindex", Jim_LindexCoreCommand},
11909 {"lset", Jim_LsetCoreCommand},
11910 {"llength", Jim_LlengthCoreCommand},
11911 {"lappend", Jim_LappendCoreCommand},
11912 {"linsert", Jim_LinsertCoreCommand},
11913 {"lsort", Jim_LsortCoreCommand},
11914 {"append", Jim_AppendCoreCommand},
11915 {"debug", Jim_DebugCoreCommand},
11916 {"eval", Jim_EvalCoreCommand},
11917 {"uplevel", Jim_UplevelCoreCommand},
11918 {"expr", Jim_ExprCoreCommand},
11919 {"break", Jim_BreakCoreCommand},
11920 {"continue", Jim_ContinueCoreCommand},
11921 {"proc", Jim_ProcCoreCommand},
11922 {"concat", Jim_ConcatCoreCommand},
11923 {"return", Jim_ReturnCoreCommand},
11924 {"upvar", Jim_UpvarCoreCommand},
11925 {"global", Jim_GlobalCoreCommand},
11926 {"string", Jim_StringCoreCommand},
11927 {"time", Jim_TimeCoreCommand},
11928 {"exit", Jim_ExitCoreCommand},
11929 {"catch", Jim_CatchCoreCommand},
11930 {"ref", Jim_RefCoreCommand},
11931 {"getref", Jim_GetrefCoreCommand},
11932 {"setref", Jim_SetrefCoreCommand},
11933 {"finalize", Jim_FinalizeCoreCommand},
11934 {"collect", Jim_CollectCoreCommand},
11935 {"rename", Jim_RenameCoreCommand},
11936 {"dict", Jim_DictCoreCommand},
11937 {"load", Jim_LoadCoreCommand},
11938 {"subst", Jim_SubstCoreCommand},
11939 {"info", Jim_InfoCoreCommand},
11940 {"split", Jim_SplitCoreCommand},
11941 {"join", Jim_JoinCoreCommand},
11942 {"format", Jim_FormatCoreCommand},
11943 {"scan", Jim_ScanCoreCommand},
11944 {"error", Jim_ErrorCoreCommand},
11945 {"lrange", Jim_LrangeCoreCommand},
11946 {"env", Jim_EnvCoreCommand},
11947 {"source", Jim_SourceCoreCommand},
11948 {"lreverse", Jim_LreverseCoreCommand},
11949 {"range", Jim_RangeCoreCommand},
11950 {"rand", Jim_RandCoreCommand},
11951 {"package", Jim_PackageCoreCommand},
11952 {"tailcall", Jim_TailcallCoreCommand},
11953 {NULL, NULL},
11954 };
11955
11956 /* Some Jim core command is actually a procedure written in Jim itself. */
11957 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11958 {
11959 Jim_Eval(interp, (char*)
11960 "proc lambda {arglist args} {\n"
11961 " set name [ref {} function lambdaFinalizer]\n"
11962 " uplevel 1 [list proc $name $arglist {expand}$args]\n"
11963 " return $name\n"
11964 "}\n"
11965 "proc lambdaFinalizer {name val} {\n"
11966 " rename $name {}\n"
11967 "}\n"
11968 );
11969 }
11970
11971 void Jim_RegisterCoreCommands(Jim_Interp *interp)
11972 {
11973 int i = 0;
11974
11975 while(Jim_CoreCommandsTable[i].name != NULL) {
11976 Jim_CreateCommand(interp,
11977 Jim_CoreCommandsTable[i].name,
11978 Jim_CoreCommandsTable[i].cmdProc,
11979 NULL, NULL);
11980 i++;
11981 }
11982 Jim_RegisterCoreProcedures(interp);
11983 }
11984
11985 /* -----------------------------------------------------------------------------
11986 * Interactive prompt
11987 * ---------------------------------------------------------------------------*/
11988 void Jim_PrintErrorMessage(Jim_Interp *interp)
11989 {
11990 int len, i;
11991
11992 Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL,
11993 interp->errorFileName, interp->errorLine);
11994 Jim_fprintf(interp,interp->cookie_stderr, " %s" JIM_NL,
11995 Jim_GetString(interp->result, NULL));
11996 Jim_ListLength(interp, interp->stackTrace, &len);
11997 for (i = len-3; i >= 0; i-= 3) {
11998 Jim_Obj *objPtr;
11999 const char *proc, *file, *line;
12000
12001 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
12002 proc = Jim_GetString(objPtr, NULL);
12003 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
12004 JIM_NONE);
12005 file = Jim_GetString(objPtr, NULL);
12006 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
12007 JIM_NONE);
12008 line = Jim_GetString(objPtr, NULL);
12009 Jim_fprintf( interp, interp->cookie_stderr,
12010 "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
12011 proc, file, line);
12012 }
12013 }
12014
12015 int Jim_InteractivePrompt(Jim_Interp *interp)
12016 {
12017 int retcode = JIM_OK;
12018 Jim_Obj *scriptObjPtr;
12019
12020 Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
12021 "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
12022 JIM_VERSION / 100, JIM_VERSION % 100);
12023 Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
12024 while (1) {
12025 char buf[1024];
12026 const char *result;
12027 const char *retcodestr[] = {
12028 "ok", "error", "return", "break", "continue", "eval", "exit"
12029 };
12030 int reslen;
12031
12032 if (retcode != 0) {
12033 if (retcode >= 2 && retcode <= 6)
12034 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
12035 else
12036 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
12037 } else
12038 Jim_fprintf( interp, interp->cookie_stdout, ". ");
12039 Jim_fflush( interp, interp->cookie_stdout);
12040 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
12041 Jim_IncrRefCount(scriptObjPtr);
12042 while(1) {
12043 const char *str;
12044 char state;
12045 int len;
12046
12047 if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
12048 Jim_DecrRefCount(interp, scriptObjPtr);
12049 goto out;
12050 }
12051 Jim_AppendString(interp, scriptObjPtr, buf, -1);
12052 str = Jim_GetString(scriptObjPtr, &len);
12053 if (Jim_ScriptIsComplete(str, len, &state))
12054 break;
12055 Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12056 Jim_fflush( interp, interp->cookie_stdout);
12057 }
12058 retcode = Jim_EvalObj(interp, scriptObjPtr);
12059 Jim_DecrRefCount(interp, scriptObjPtr);
12060 result = Jim_GetString(Jim_GetResult(interp), &reslen);
12061 if (retcode == JIM_ERR) {
12062 Jim_PrintErrorMessage(interp);
12063 } else if (retcode == JIM_EXIT) {
12064 exit(Jim_GetExitCode(interp));
12065 } else {
12066 if (reslen) {
12067 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12068 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12069 }
12070 }
12071 }
12072 out:
12073 return 0;
12074 }
12075
12076 /* -----------------------------------------------------------------------------
12077 * Jim's idea of STDIO..
12078 * ---------------------------------------------------------------------------*/
12079
12080 int Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12081 {
12082 int r;
12083
12084 va_list ap;
12085 va_start(ap,fmt);
12086 r = Jim_vfprintf( interp, cookie, fmt,ap );
12087 va_end(ap);
12088 return r;
12089 }
12090
12091 int Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12092 {
12093 if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12094 errno = ENOTSUP;
12095 return -1;
12096 }
12097 return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12098 }
12099
12100 size_t Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12101 {
12102 if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12103 errno = ENOTSUP;
12104 return 0;
12105 }
12106 return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12107 }
12108
12109 size_t Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12110 {
12111 if( (interp == NULL) || (interp->cb_fread == NULL) ){
12112 errno = ENOTSUP;
12113 return 0;
12114 }
12115 return (*(interp->cb_fread))( ptr, size, n, cookie);
12116 }
12117
12118 int Jim_fflush( Jim_Interp *interp, void *cookie )
12119 {
12120 if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12121 /* pretend all is well */
12122 return 0;
12123 }
12124 return (*(interp->cb_fflush))( cookie );
12125 }
12126
12127 char* Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12128 {
12129 if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12130 errno = ENOTSUP;
12131 return NULL;
12132 }
12133 return (*(interp->cb_fgets))( s, size, cookie );
12134 }

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)