Fix pointer cast alignment warnings in jim.c.
[openocd.git] / src / helper / jim.c
1 /* Jim - A small embeddable Tcl interpreter
2 *
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
6 * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
7 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
10 * Copyright 2008 Steve Bennett <steveb@workware.net.au>
11 *
12 * The FreeBSD license
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
26 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 *
38 * The views and conclusions contained in the software and documentation
39 * are those of the authors and should not be interpreted as representing
40 * official policies, either expressed or implied, of the Jim Tcl Project.
41 **/
42 #define __JIM_CORE__
43 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
44
45 #ifdef __ECOS
46 #include <pkgconf/jimtcl.h>
47 #endif
48 #ifndef JIM_ANSIC
49 #define JIM_DYNLIB /* Dynamic library support for UNIX and WIN32 */
50 #endif /* JIM_ANSIC */
51
52 #ifndef _GNU_SOURCE
53 #define _GNU_SOURCE /* for vasprintf() */
54 #endif
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <stdarg.h>
59 #include <ctype.h>
60 #include <limits.h>
61 #include <assert.h>
62 #include <errno.h>
63 #include <time.h>
64 #if defined(WIN32)
65 /* sys/time - need is different */
66 #else
67 #include <sys/time.h> // for gettimeofday()
68 #endif
69
70 #include "replacements.h"
71
72 /* Include the platform dependent libraries for
73 * dynamic loading of libraries. */
74 #ifdef JIM_DYNLIB
75 #if defined(_WIN32) || defined(WIN32)
76 #ifndef WIN32
77 #define WIN32 1
78 #endif
79 #ifndef STRICT
80 #define STRICT
81 #endif
82 #define WIN32_LEAN_AND_MEAN
83 #include <windows.h>
84 #if _MSC_VER >= 1000
85 #pragma warning(disable:4146)
86 #endif /* _MSC_VER */
87 #else
88 #include <dlfcn.h>
89 #endif /* WIN32 */
90 #endif /* JIM_DYNLIB */
91
92 #ifdef HAVE_UNISTD_H
93 #include <unistd.h>
94 #endif
95
96 #ifdef __ECOS
97 #include <cyg/jimtcl/jim.h>
98 #else
99 #include "jim.h"
100 #endif
101
102 #ifdef HAVE_BACKTRACE
103 #include <execinfo.h>
104 #endif
105
106 /* -----------------------------------------------------------------------------
107 * Global variables
108 * ---------------------------------------------------------------------------*/
109
110 /* A shared empty string for the objects string representation.
111 * Jim_InvalidateStringRep knows about it and don't try to free. */
112 static char *JimEmptyStringRep = (char*) "";
113
114 /* -----------------------------------------------------------------------------
115 * Required prototypes of not exported functions
116 * ---------------------------------------------------------------------------*/
117 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
118 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
119 static void JimRegisterCoreApi(Jim_Interp *interp);
120
121 static Jim_HashTableType *getJimVariablesHashTableType(void);
122
123 /* -----------------------------------------------------------------------------
124 * Utility functions
125 * ---------------------------------------------------------------------------*/
126
127 static char *
128 jim_vasprintf( const char *fmt, va_list ap )
129 {
130 #ifndef HAVE_VASPRINTF
131 /* yucky way */
132 static char buf[2048];
133 vsnprintf( buf, sizeof(buf), fmt, ap );
134 /* garentee termination */
135 buf[sizeof(buf)-1] = 0;
136 #else
137 char *buf;
138 int result;
139 result = vasprintf( &buf, fmt, ap );
140 if (result < 0) exit(-1);
141 #endif
142 return buf;
143 }
144
145 static void
146 jim_vasprintf_done( void *buf )
147 {
148 #ifndef HAVE_VASPRINTF
149 (void)(buf);
150 #else
151 free(buf);
152 #endif
153 }
154
155
156 /*
157 * Convert a string to a jim_wide INTEGER.
158 * This function originates from BSD.
159 *
160 * Ignores `locale' stuff. Assumes that the upper and lower case
161 * alphabets and digits are each contiguous.
162 */
163 #ifdef HAVE_LONG_LONG
164 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
165 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
166 {
167 register const char *s;
168 register unsigned jim_wide acc;
169 register unsigned char c;
170 register unsigned jim_wide qbase, cutoff;
171 register int neg, any, cutlim;
172
173 /*
174 * Skip white space and pick up leading +/- sign if any.
175 * If base is 0, allow 0x for hex and 0 for octal, else
176 * assume decimal; if base is already 16, allow 0x.
177 */
178 s = nptr;
179 do {
180 c = *s++;
181 } while (isspace(c));
182 if (c == '-') {
183 neg = 1;
184 c = *s++;
185 } else {
186 neg = 0;
187 if (c == '+')
188 c = *s++;
189 }
190 if ((base == 0 || base == 16) &&
191 c == '0' && (*s == 'x' || *s == 'X')) {
192 c = s[1];
193 s += 2;
194 base = 16;
195 }
196 if (base == 0)
197 base = c == '0' ? 8 : 10;
198
199 /*
200 * Compute the cutoff value between legal numbers and illegal
201 * numbers. That is the largest legal value, divided by the
202 * base. An input number that is greater than this value, if
203 * followed by a legal input character, is too big. One that
204 * is equal to this value may be valid or not; the limit
205 * between valid and invalid numbers is then based on the last
206 * digit. For instance, if the range for quads is
207 * [-9223372036854775808..9223372036854775807] and the input base
208 * is 10, cutoff will be set to 922337203685477580 and cutlim to
209 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
210 * accumulated a value > 922337203685477580, or equal but the
211 * next digit is > 7 (or 8), the number is too big, and we will
212 * return a range error.
213 *
214 * Set any if any `digits' consumed; make it negative to indicate
215 * overflow.
216 */
217 qbase = (unsigned)base;
218 cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
219 : LLONG_MAX;
220 cutlim = (int)(cutoff % qbase);
221 cutoff /= qbase;
222 for (acc = 0, any = 0;; c = *s++) {
223 if (!JimIsAscii(c))
224 break;
225 if (isdigit(c))
226 c -= '0';
227 else if (isalpha(c))
228 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
229 else
230 break;
231 if (c >= base)
232 break;
233 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
234 any = -1;
235 else {
236 any = 1;
237 acc *= qbase;
238 acc += c;
239 }
240 }
241 if (any < 0) {
242 acc = neg ? LLONG_MIN : LLONG_MAX;
243 errno = ERANGE;
244 } else if (neg)
245 acc = -acc;
246 if (endptr != 0)
247 *endptr = (char *)(any ? s - 1 : nptr);
248 return (acc);
249 }
250 #endif
251
252 /* Glob-style pattern matching. */
253 static int JimStringMatch(const char *pattern, int patternLen,
254 const char *string, int stringLen, int nocase)
255 {
256 while(patternLen) {
257 switch(pattern[0]) {
258 case '*':
259 while (pattern[1] == '*') {
260 pattern++;
261 patternLen--;
262 }
263 if (patternLen == 1)
264 return 1; /* match */
265 while(stringLen) {
266 if (JimStringMatch(pattern+1, patternLen-1,
267 string, stringLen, nocase))
268 return 1; /* match */
269 string++;
270 stringLen--;
271 }
272 return 0; /* no match */
273 break;
274 case '?':
275 if (stringLen == 0)
276 return 0; /* no match */
277 string++;
278 stringLen--;
279 break;
280 case '[':
281 {
282 int not, match;
283
284 pattern++;
285 patternLen--;
286 not = pattern[0] == '^';
287 if (not) {
288 pattern++;
289 patternLen--;
290 }
291 match = 0;
292 while(1) {
293 if (pattern[0] == '\\') {
294 pattern++;
295 patternLen--;
296 if (pattern[0] == string[0])
297 match = 1;
298 } else if (pattern[0] == ']') {
299 break;
300 } else if (patternLen == 0) {
301 pattern--;
302 patternLen++;
303 break;
304 } else if (pattern[1] == '-' && patternLen >= 3) {
305 int start = pattern[0];
306 int end = pattern[2];
307 int c = string[0];
308 if (start > end) {
309 int t = start;
310 start = end;
311 end = t;
312 }
313 if (nocase) {
314 start = tolower(start);
315 end = tolower(end);
316 c = tolower(c);
317 }
318 pattern += 2;
319 patternLen -= 2;
320 if (c >= start && c <= end)
321 match = 1;
322 } else {
323 if (!nocase) {
324 if (pattern[0] == string[0])
325 match = 1;
326 } else {
327 if (tolower((int)pattern[0]) == tolower((int)string[0]))
328 match = 1;
329 }
330 }
331 pattern++;
332 patternLen--;
333 }
334 if (not)
335 match = !match;
336 if (!match)
337 return 0; /* no match */
338 string++;
339 stringLen--;
340 break;
341 }
342 case '\\':
343 if (patternLen >= 2) {
344 pattern++;
345 patternLen--;
346 }
347 /* fall through */
348 default:
349 if (!nocase) {
350 if (pattern[0] != string[0])
351 return 0; /* no match */
352 } else {
353 if (tolower((int)pattern[0]) != tolower((int)string[0]))
354 return 0; /* no match */
355 }
356 string++;
357 stringLen--;
358 break;
359 }
360 pattern++;
361 patternLen--;
362 if (stringLen == 0) {
363 while(*pattern == '*') {
364 pattern++;
365 patternLen--;
366 }
367 break;
368 }
369 }
370 if (patternLen == 0 && stringLen == 0)
371 return 1;
372 return 0;
373 }
374
375 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
376 int nocase)
377 {
378 unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
379
380 if (nocase == 0) {
381 while(l1 && l2) {
382 if (*u1 != *u2)
383 return (int)*u1-*u2;
384 u1++; u2++; l1--; l2--;
385 }
386 if (!l1 && !l2) return 0;
387 return l1-l2;
388 } else {
389 while(l1 && l2) {
390 if (tolower((int)*u1) != tolower((int)*u2))
391 return tolower((int)*u1)-tolower((int)*u2);
392 u1++; u2++; l1--; l2--;
393 }
394 if (!l1 && !l2) return 0;
395 return l1-l2;
396 }
397 }
398
399 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
400 * The index of the first occurrence of s1 in s2 is returned.
401 * If s1 is not found inside s2, -1 is returned. */
402 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
403 {
404 int i;
405
406 if (!l1 || !l2 || l1 > l2) return -1;
407 if (index < 0) index = 0;
408 s2 += index;
409 for (i = index; i <= l2-l1; i++) {
410 if (memcmp(s2, s1, l1) == 0)
411 return i;
412 s2++;
413 }
414 return -1;
415 }
416
417 int Jim_WideToString(char *buf, jim_wide wideValue)
418 {
419 const char *fmt = "%" JIM_WIDE_MODIFIER;
420 return sprintf(buf, fmt, wideValue);
421 }
422
423 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
424 {
425 char *endptr;
426
427 #ifdef HAVE_LONG_LONG
428 *widePtr = JimStrtoll(str, &endptr, base);
429 #else
430 *widePtr = strtol(str, &endptr, base);
431 #endif
432 if ((str[0] == '\0') || (str == endptr) )
433 return JIM_ERR;
434 if (endptr[0] != '\0') {
435 while(*endptr) {
436 if (!isspace((int)*endptr))
437 return JIM_ERR;
438 endptr++;
439 }
440 }
441 return JIM_OK;
442 }
443
444 int Jim_StringToIndex(const char *str, int *intPtr)
445 {
446 char *endptr;
447
448 *intPtr = strtol(str, &endptr, 10);
449 if ( (str[0] == '\0') || (str == endptr) )
450 return JIM_ERR;
451 if (endptr[0] != '\0') {
452 while(*endptr) {
453 if (!isspace((int)*endptr))
454 return JIM_ERR;
455 endptr++;
456 }
457 }
458 return JIM_OK;
459 }
460
461 /* The string representation of references has two features in order
462 * to make the GC faster. The first is that every reference starts
463 * with a non common character '~', in order to make the string matching
464 * fater. The second is that the reference string rep his 32 characters
465 * in length, this allows to avoid to check every object with a string
466 * repr < 32, and usually there are many of this objects. */
467
468 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
469
470 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
471 {
472 const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
473 sprintf(buf, fmt, refPtr->tag, id);
474 return JIM_REFERENCE_SPACE;
475 }
476
477 int Jim_DoubleToString(char *buf, double doubleValue)
478 {
479 char *s;
480 int len;
481
482 len = sprintf(buf, "%.17g", doubleValue);
483 s = buf;
484 while(*s) {
485 if (*s == '.') return len;
486 s++;
487 }
488 /* Add a final ".0" if it's a number. But not
489 * for NaN or InF */
490 if (isdigit((int)buf[0])
491 || ((buf[0] == '-' || buf[0] == '+')
492 && isdigit((int)buf[1]))) {
493 s[0] = '.';
494 s[1] = '0';
495 s[2] = '\0';
496 return len+2;
497 }
498 return len;
499 }
500
501 int Jim_StringToDouble(const char *str, double *doublePtr)
502 {
503 char *endptr;
504
505 *doublePtr = strtod(str, &endptr);
506 if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
507 return JIM_ERR;
508 return JIM_OK;
509 }
510
511 static jim_wide JimPowWide(jim_wide b, jim_wide e)
512 {
513 jim_wide i, res = 1;
514 if ((b==0 && e!=0) || (e<0)) return 0;
515 for(i=0; i<e; i++) {res *= b;}
516 return res;
517 }
518
519 /* -----------------------------------------------------------------------------
520 * Special functions
521 * ---------------------------------------------------------------------------*/
522
523 /* Note that 'interp' may be NULL if not available in the
524 * context of the panic. It's only useful to get the error
525 * file descriptor, it will default to stderr otherwise. */
526 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
527 {
528 va_list ap;
529
530 va_start(ap, fmt);
531 /*
532 * Send it here first.. Assuming STDIO still works
533 */
534 fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
535 vfprintf(stderr, fmt, ap);
536 fprintf(stderr, JIM_NL JIM_NL);
537 va_end(ap);
538
539 #ifdef HAVE_BACKTRACE
540 {
541 void *array[40];
542 int size, i;
543 char **strings;
544
545 size = backtrace(array, 40);
546 strings = backtrace_symbols(array, size);
547 for (i = 0; i < size; i++)
548 fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
549 fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
550 fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
551 }
552 #endif
553
554 /* This may actually crash... we do it last */
555 if( interp && interp->cookie_stderr ){
556 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
557 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
558 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL JIM_NL );
559 }
560 abort();
561 }
562
563 /* -----------------------------------------------------------------------------
564 * Memory allocation
565 * ---------------------------------------------------------------------------*/
566
567 /* Macro used for memory debugging.
568 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
569 * and similary for Jim_Realloc and Jim_Free */
570 #if 0
571 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
572 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
573 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
574 #endif
575
576 void *Jim_Alloc(int size)
577 {
578 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
579 if (size==0)
580 size=1;
581 void *p = malloc(size);
582 if (p == NULL)
583 Jim_Panic(NULL,"malloc: Out of memory");
584 return p;
585 }
586
587 void Jim_Free(void *ptr) {
588 free(ptr);
589 }
590
591 void *Jim_Realloc(void *ptr, int size)
592 {
593 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
594 if (size==0)
595 size=1;
596 void *p = realloc(ptr, size);
597 if (p == NULL)
598 Jim_Panic(NULL,"realloc: Out of memory");
599 return p;
600 }
601
602 char *Jim_StrDup(const char *s)
603 {
604 int l = strlen(s);
605 char *copy = Jim_Alloc(l+1);
606
607 memcpy(copy, s, l+1);
608 return copy;
609 }
610
611 char *Jim_StrDupLen(const char *s, int l)
612 {
613 char *copy = Jim_Alloc(l+1);
614
615 memcpy(copy, s, l+1);
616 copy[l] = 0; /* Just to be sure, original could be substring */
617 return copy;
618 }
619
620 /* -----------------------------------------------------------------------------
621 * Time related functions
622 * ---------------------------------------------------------------------------*/
623 /* Returns microseconds of CPU used since start. */
624 static jim_wide JimClock(void)
625 {
626 #if (defined WIN32) && !(defined JIM_ANSIC)
627 LARGE_INTEGER t, f;
628 QueryPerformanceFrequency(&f);
629 QueryPerformanceCounter(&t);
630 return (long)((t.QuadPart * 1000000) / f.QuadPart);
631 #else /* !WIN32 */
632 clock_t clocks = clock();
633
634 return (long)(clocks*(1000000/CLOCKS_PER_SEC));
635 #endif /* WIN32 */
636 }
637
638 /* -----------------------------------------------------------------------------
639 * Hash Tables
640 * ---------------------------------------------------------------------------*/
641
642 /* -------------------------- private prototypes ---------------------------- */
643 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
644 static unsigned int JimHashTableNextPower(unsigned int size);
645 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
646
647 /* -------------------------- hash functions -------------------------------- */
648
649 /* Thomas Wang's 32 bit Mix Function */
650 unsigned int Jim_IntHashFunction(unsigned int key)
651 {
652 key += ~(key << 15);
653 key ^= (key >> 10);
654 key += (key << 3);
655 key ^= (key >> 6);
656 key += ~(key << 11);
657 key ^= (key >> 16);
658 return key;
659 }
660
661 /* Identity hash function for integer keys */
662 unsigned int Jim_IdentityHashFunction(unsigned int key)
663 {
664 return key;
665 }
666
667 /* Generic hash function (we are using to multiply by 9 and add the byte
668 * as Tcl) */
669 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
670 {
671 unsigned int h = 0;
672 while(len--)
673 h += (h<<3)+*buf++;
674 return h;
675 }
676
677 /* ----------------------------- API implementation ------------------------- */
678 /* reset an hashtable already initialized with ht_init().
679 * NOTE: This function should only called by ht_destroy(). */
680 static void JimResetHashTable(Jim_HashTable *ht)
681 {
682 ht->table = NULL;
683 ht->size = 0;
684 ht->sizemask = 0;
685 ht->used = 0;
686 ht->collisions = 0;
687 }
688
689 /* Initialize the hash table */
690 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
691 void *privDataPtr)
692 {
693 JimResetHashTable(ht);
694 ht->type = type;
695 ht->privdata = privDataPtr;
696 return JIM_OK;
697 }
698
699 /* Resize the table to the minimal size that contains all the elements,
700 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
701 int Jim_ResizeHashTable(Jim_HashTable *ht)
702 {
703 int minimal = ht->used;
704
705 if (minimal < JIM_HT_INITIAL_SIZE)
706 minimal = JIM_HT_INITIAL_SIZE;
707 return Jim_ExpandHashTable(ht, minimal);
708 }
709
710 /* Expand or create the hashtable */
711 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
712 {
713 Jim_HashTable n; /* the new hashtable */
714 unsigned int realsize = JimHashTableNextPower(size), i;
715
716 /* the size is invalid if it is smaller than the number of
717 * elements already inside the hashtable */
718 if (ht->used >= size)
719 return JIM_ERR;
720
721 Jim_InitHashTable(&n, ht->type, ht->privdata);
722 n.size = realsize;
723 n.sizemask = realsize-1;
724 n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
725
726 /* Initialize all the pointers to NULL */
727 memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
728
729 /* Copy all the elements from the old to the new table:
730 * note that if the old hash table is empty ht->size is zero,
731 * so Jim_ExpandHashTable just creates an hash table. */
732 n.used = ht->used;
733 for (i = 0; i < ht->size && ht->used > 0; i++) {
734 Jim_HashEntry *he, *nextHe;
735
736 if (ht->table[i] == NULL) continue;
737
738 /* For each hash entry on this slot... */
739 he = ht->table[i];
740 while(he) {
741 unsigned int h;
742
743 nextHe = he->next;
744 /* Get the new element index */
745 h = Jim_HashKey(ht, he->key) & n.sizemask;
746 he->next = n.table[h];
747 n.table[h] = he;
748 ht->used--;
749 /* Pass to the next element */
750 he = nextHe;
751 }
752 }
753 assert(ht->used == 0);
754 Jim_Free(ht->table);
755
756 /* Remap the new hashtable in the old */
757 *ht = n;
758 return JIM_OK;
759 }
760
761 /* Add an element to the target hash table */
762 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
763 {
764 int index;
765 Jim_HashEntry *entry;
766
767 /* Get the index of the new element, or -1 if
768 * the element already exists. */
769 if ((index = JimInsertHashEntry(ht, key)) == -1)
770 return JIM_ERR;
771
772 /* Allocates the memory and stores key */
773 entry = Jim_Alloc(sizeof(*entry));
774 entry->next = ht->table[index];
775 ht->table[index] = entry;
776
777 /* Set the hash entry fields. */
778 Jim_SetHashKey(ht, entry, key);
779 Jim_SetHashVal(ht, entry, val);
780 ht->used++;
781 return JIM_OK;
782 }
783
784 /* Add an element, discarding the old if the key already exists */
785 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
786 {
787 Jim_HashEntry *entry;
788
789 /* Try to add the element. If the key
790 * does not exists Jim_AddHashEntry will suceed. */
791 if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
792 return JIM_OK;
793 /* It already exists, get the entry */
794 entry = Jim_FindHashEntry(ht, key);
795 /* Free the old value and set the new one */
796 Jim_FreeEntryVal(ht, entry);
797 Jim_SetHashVal(ht, entry, val);
798 return JIM_OK;
799 }
800
801 /* Search and remove an element */
802 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
803 {
804 unsigned int h;
805 Jim_HashEntry *he, *prevHe;
806
807 if (ht->size == 0)
808 return JIM_ERR;
809 h = Jim_HashKey(ht, key) & ht->sizemask;
810 he = ht->table[h];
811
812 prevHe = NULL;
813 while(he) {
814 if (Jim_CompareHashKeys(ht, key, he->key)) {
815 /* Unlink the element from the list */
816 if (prevHe)
817 prevHe->next = he->next;
818 else
819 ht->table[h] = he->next;
820 Jim_FreeEntryKey(ht, he);
821 Jim_FreeEntryVal(ht, he);
822 Jim_Free(he);
823 ht->used--;
824 return JIM_OK;
825 }
826 prevHe = he;
827 he = he->next;
828 }
829 return JIM_ERR; /* not found */
830 }
831
832 /* Destroy an entire hash table */
833 int Jim_FreeHashTable(Jim_HashTable *ht)
834 {
835 unsigned int i;
836
837 /* Free all the elements */
838 for (i = 0; i < ht->size && ht->used > 0; i++) {
839 Jim_HashEntry *he, *nextHe;
840
841 if ((he = ht->table[i]) == NULL) continue;
842 while(he) {
843 nextHe = he->next;
844 Jim_FreeEntryKey(ht, he);
845 Jim_FreeEntryVal(ht, he);
846 Jim_Free(he);
847 ht->used--;
848 he = nextHe;
849 }
850 }
851 /* Free the table and the allocated cache structure */
852 Jim_Free(ht->table);
853 /* Re-initialize the table */
854 JimResetHashTable(ht);
855 return JIM_OK; /* never fails */
856 }
857
858 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
859 {
860 Jim_HashEntry *he;
861 unsigned int h;
862
863 if (ht->size == 0) return NULL;
864 h = Jim_HashKey(ht, key) & ht->sizemask;
865 he = ht->table[h];
866 while(he) {
867 if (Jim_CompareHashKeys(ht, key, he->key))
868 return he;
869 he = he->next;
870 }
871 return NULL;
872 }
873
874 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
875 {
876 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
877
878 iter->ht = ht;
879 iter->index = -1;
880 iter->entry = NULL;
881 iter->nextEntry = NULL;
882 return iter;
883 }
884
885 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
886 {
887 while (1) {
888 if (iter->entry == NULL) {
889 iter->index++;
890 if (iter->index >=
891 (signed)iter->ht->size) break;
892 iter->entry = iter->ht->table[iter->index];
893 } else {
894 iter->entry = iter->nextEntry;
895 }
896 if (iter->entry) {
897 /* We need to save the 'next' here, the iterator user
898 * may delete the entry we are returning. */
899 iter->nextEntry = iter->entry->next;
900 return iter->entry;
901 }
902 }
903 return NULL;
904 }
905
906 /* ------------------------- private functions ------------------------------ */
907
908 /* Expand the hash table if needed */
909 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
910 {
911 /* If the hash table is empty expand it to the intial size,
912 * if the table is "full" dobule its size. */
913 if (ht->size == 0)
914 return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
915 if (ht->size == ht->used)
916 return Jim_ExpandHashTable(ht, ht->size*2);
917 return JIM_OK;
918 }
919
920 /* Our hash table capability is a power of two */
921 static unsigned int JimHashTableNextPower(unsigned int size)
922 {
923 unsigned int i = JIM_HT_INITIAL_SIZE;
924
925 if (size >= 2147483648U)
926 return 2147483648U;
927 while(1) {
928 if (i >= size)
929 return i;
930 i *= 2;
931 }
932 }
933
934 /* Returns the index of a free slot that can be populated with
935 * an hash entry for the given 'key'.
936 * If the key already exists, -1 is returned. */
937 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
938 {
939 unsigned int h;
940 Jim_HashEntry *he;
941
942 /* Expand the hashtable if needed */
943 if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
944 return -1;
945 /* Compute the key hash value */
946 h = Jim_HashKey(ht, key) & ht->sizemask;
947 /* Search if this slot does not already contain the given key */
948 he = ht->table[h];
949 while(he) {
950 if (Jim_CompareHashKeys(ht, key, he->key))
951 return -1;
952 he = he->next;
953 }
954 return h;
955 }
956
957 /* ----------------------- StringCopy Hash Table Type ------------------------*/
958
959 static unsigned int JimStringCopyHTHashFunction(const void *key)
960 {
961 return Jim_GenHashFunction(key, strlen(key));
962 }
963
964 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
965 {
966 int len = strlen(key);
967 char *copy = Jim_Alloc(len+1);
968 JIM_NOTUSED(privdata);
969
970 memcpy(copy, key, len);
971 copy[len] = '\0';
972 return copy;
973 }
974
975 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
976 {
977 int len = strlen(val);
978 char *copy = Jim_Alloc(len+1);
979 JIM_NOTUSED(privdata);
980
981 memcpy(copy, val, len);
982 copy[len] = '\0';
983 return copy;
984 }
985
986 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
987 const void *key2)
988 {
989 JIM_NOTUSED(privdata);
990
991 return strcmp(key1, key2) == 0;
992 }
993
994 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
995 {
996 JIM_NOTUSED(privdata);
997
998 Jim_Free((void*)key); /* ATTENTION: const cast */
999 }
1000
1001 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
1002 {
1003 JIM_NOTUSED(privdata);
1004
1005 Jim_Free((void*)val); /* ATTENTION: const cast */
1006 }
1007
1008 static Jim_HashTableType JimStringCopyHashTableType = {
1009 JimStringCopyHTHashFunction, /* hash function */
1010 JimStringCopyHTKeyDup, /* key dup */
1011 NULL, /* val dup */
1012 JimStringCopyHTKeyCompare, /* key compare */
1013 JimStringCopyHTKeyDestructor, /* key destructor */
1014 NULL /* val destructor */
1015 };
1016
1017 /* This is like StringCopy but does not auto-duplicate the key.
1018 * It's used for intepreter's shared strings. */
1019 static Jim_HashTableType JimSharedStringsHashTableType = {
1020 JimStringCopyHTHashFunction, /* hash function */
1021 NULL, /* key dup */
1022 NULL, /* val dup */
1023 JimStringCopyHTKeyCompare, /* key compare */
1024 JimStringCopyHTKeyDestructor, /* key destructor */
1025 NULL /* val destructor */
1026 };
1027
1028 /* This is like StringCopy but also automatically handle dynamic
1029 * allocated C strings as values. */
1030 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
1031 JimStringCopyHTHashFunction, /* hash function */
1032 JimStringCopyHTKeyDup, /* key dup */
1033 JimStringKeyValCopyHTValDup, /* val dup */
1034 JimStringCopyHTKeyCompare, /* key compare */
1035 JimStringCopyHTKeyDestructor, /* key destructor */
1036 JimStringKeyValCopyHTValDestructor, /* val destructor */
1037 };
1038
1039 typedef struct AssocDataValue {
1040 Jim_InterpDeleteProc *delProc;
1041 void *data;
1042 } AssocDataValue;
1043
1044 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
1045 {
1046 AssocDataValue *assocPtr = (AssocDataValue *)data;
1047 if (assocPtr->delProc != NULL)
1048 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
1049 Jim_Free(data);
1050 }
1051
1052 static Jim_HashTableType JimAssocDataHashTableType = {
1053 JimStringCopyHTHashFunction, /* hash function */
1054 JimStringCopyHTKeyDup, /* key dup */
1055 NULL, /* val dup */
1056 JimStringCopyHTKeyCompare, /* key compare */
1057 JimStringCopyHTKeyDestructor, /* key destructor */
1058 JimAssocDataHashTableValueDestructor /* val destructor */
1059 };
1060
1061 /* -----------------------------------------------------------------------------
1062 * Stack - This is a simple generic stack implementation. It is used for
1063 * example in the 'expr' expression compiler.
1064 * ---------------------------------------------------------------------------*/
1065 void Jim_InitStack(Jim_Stack *stack)
1066 {
1067 stack->len = 0;
1068 stack->maxlen = 0;
1069 stack->vector = NULL;
1070 }
1071
1072 void Jim_FreeStack(Jim_Stack *stack)
1073 {
1074 Jim_Free(stack->vector);
1075 }
1076
1077 int Jim_StackLen(Jim_Stack *stack)
1078 {
1079 return stack->len;
1080 }
1081
1082 void Jim_StackPush(Jim_Stack *stack, void *element) {
1083 int neededLen = stack->len+1;
1084 if (neededLen > stack->maxlen) {
1085 stack->maxlen = neededLen*2;
1086 stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1087 }
1088 stack->vector[stack->len] = element;
1089 stack->len++;
1090 }
1091
1092 void *Jim_StackPop(Jim_Stack *stack)
1093 {
1094 if (stack->len == 0) return NULL;
1095 stack->len--;
1096 return stack->vector[stack->len];
1097 }
1098
1099 void *Jim_StackPeek(Jim_Stack *stack)
1100 {
1101 if (stack->len == 0) return NULL;
1102 return stack->vector[stack->len-1];
1103 }
1104
1105 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1106 {
1107 int i;
1108
1109 for (i = 0; i < stack->len; i++)
1110 freeFunc(stack->vector[i]);
1111 }
1112
1113 /* -----------------------------------------------------------------------------
1114 * Parser
1115 * ---------------------------------------------------------------------------*/
1116
1117 /* Token types */
1118 #define JIM_TT_NONE -1 /* No token returned */
1119 #define JIM_TT_STR 0 /* simple string */
1120 #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
1121 #define JIM_TT_VAR 2 /* var substitution */
1122 #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
1123 #define JIM_TT_CMD 4 /* command substitution */
1124 #define JIM_TT_SEP 5 /* word separator */
1125 #define JIM_TT_EOL 6 /* line separator */
1126
1127 /* Additional token types needed for expressions */
1128 #define JIM_TT_SUBEXPR_START 7
1129 #define JIM_TT_SUBEXPR_END 8
1130 #define JIM_TT_EXPR_NUMBER 9
1131 #define JIM_TT_EXPR_OPERATOR 10
1132
1133 /* Parser states */
1134 #define JIM_PS_DEF 0 /* Default state */
1135 #define JIM_PS_QUOTE 1 /* Inside "" */
1136
1137 /* Parser context structure. The same context is used both to parse
1138 * Tcl scripts and lists. */
1139 struct JimParserCtx {
1140 const char *prg; /* Program text */
1141 const char *p; /* Pointer to the point of the program we are parsing */
1142 int len; /* Left length of 'prg' */
1143 int linenr; /* Current line number */
1144 const char *tstart;
1145 const char *tend; /* Returned token is at tstart-tend in 'prg'. */
1146 int tline; /* Line number of the returned token */
1147 int tt; /* Token type */
1148 int eof; /* Non zero if EOF condition is true. */
1149 int state; /* Parser state */
1150 int comment; /* Non zero if the next chars may be a comment. */
1151 };
1152
1153 #define JimParserEof(c) ((c)->eof)
1154 #define JimParserTstart(c) ((c)->tstart)
1155 #define JimParserTend(c) ((c)->tend)
1156 #define JimParserTtype(c) ((c)->tt)
1157 #define JimParserTline(c) ((c)->tline)
1158
1159 static int JimParseScript(struct JimParserCtx *pc);
1160 static int JimParseSep(struct JimParserCtx *pc);
1161 static int JimParseEol(struct JimParserCtx *pc);
1162 static int JimParseCmd(struct JimParserCtx *pc);
1163 static int JimParseVar(struct JimParserCtx *pc);
1164 static int JimParseBrace(struct JimParserCtx *pc);
1165 static int JimParseStr(struct JimParserCtx *pc);
1166 static int JimParseComment(struct JimParserCtx *pc);
1167 static char *JimParserGetToken(struct JimParserCtx *pc,
1168 int *lenPtr, int *typePtr, int *linePtr);
1169
1170 /* Initialize a parser context.
1171 * 'prg' is a pointer to the program text, linenr is the line
1172 * number of the first line contained in the program. */
1173 void JimParserInit(struct JimParserCtx *pc, const char *prg,
1174 int len, int linenr)
1175 {
1176 pc->prg = prg;
1177 pc->p = prg;
1178 pc->len = len;
1179 pc->tstart = NULL;
1180 pc->tend = NULL;
1181 pc->tline = 0;
1182 pc->tt = JIM_TT_NONE;
1183 pc->eof = 0;
1184 pc->state = JIM_PS_DEF;
1185 pc->linenr = linenr;
1186 pc->comment = 1;
1187 }
1188
1189 int JimParseScript(struct JimParserCtx *pc)
1190 {
1191 while(1) { /* the while is used to reiterate with continue if needed */
1192 if (!pc->len) {
1193 pc->tstart = pc->p;
1194 pc->tend = pc->p-1;
1195 pc->tline = pc->linenr;
1196 pc->tt = JIM_TT_EOL;
1197 pc->eof = 1;
1198 return JIM_OK;
1199 }
1200 switch(*(pc->p)) {
1201 case '\\':
1202 if (*(pc->p+1) == '\n')
1203 return JimParseSep(pc);
1204 else {
1205 pc->comment = 0;
1206 return JimParseStr(pc);
1207 }
1208 break;
1209 case ' ':
1210 case '\t':
1211 case '\r':
1212 if (pc->state == JIM_PS_DEF)
1213 return JimParseSep(pc);
1214 else {
1215 pc->comment = 0;
1216 return JimParseStr(pc);
1217 }
1218 break;
1219 case '\n':
1220 case ';':
1221 pc->comment = 1;
1222 if (pc->state == JIM_PS_DEF)
1223 return JimParseEol(pc);
1224 else
1225 return JimParseStr(pc);
1226 break;
1227 case '[':
1228 pc->comment = 0;
1229 return JimParseCmd(pc);
1230 break;
1231 case '$':
1232 pc->comment = 0;
1233 if (JimParseVar(pc) == JIM_ERR) {
1234 pc->tstart = pc->tend = pc->p++; pc->len--;
1235 pc->tline = pc->linenr;
1236 pc->tt = JIM_TT_STR;
1237 return JIM_OK;
1238 } else
1239 return JIM_OK;
1240 break;
1241 case '#':
1242 if (pc->comment) {
1243 JimParseComment(pc);
1244 continue;
1245 } else {
1246 return JimParseStr(pc);
1247 }
1248 default:
1249 pc->comment = 0;
1250 return JimParseStr(pc);
1251 break;
1252 }
1253 return JIM_OK;
1254 }
1255 }
1256
1257 int JimParseSep(struct JimParserCtx *pc)
1258 {
1259 pc->tstart = pc->p;
1260 pc->tline = pc->linenr;
1261 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1262 (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1263 if (*pc->p == '\\') {
1264 pc->p++; pc->len--;
1265 pc->linenr++;
1266 }
1267 pc->p++; pc->len--;
1268 }
1269 pc->tend = pc->p-1;
1270 pc->tt = JIM_TT_SEP;
1271 return JIM_OK;
1272 }
1273
1274 int JimParseEol(struct JimParserCtx *pc)
1275 {
1276 pc->tstart = pc->p;
1277 pc->tline = pc->linenr;
1278 while (*pc->p == ' ' || *pc->p == '\n' ||
1279 *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1280 if (*pc->p == '\n')
1281 pc->linenr++;
1282 pc->p++; pc->len--;
1283 }
1284 pc->tend = pc->p-1;
1285 pc->tt = JIM_TT_EOL;
1286 return JIM_OK;
1287 }
1288
1289 /* Todo. Don't stop if ']' appears inside {} or quoted.
1290 * Also should handle the case of puts [string length "]"] */
1291 int JimParseCmd(struct JimParserCtx *pc)
1292 {
1293 int level = 1;
1294 int blevel = 0;
1295
1296 pc->tstart = ++pc->p; pc->len--;
1297 pc->tline = pc->linenr;
1298 while (1) {
1299 if (pc->len == 0) {
1300 break;
1301 } else if (*pc->p == '[' && blevel == 0) {
1302 level++;
1303 } else if (*pc->p == ']' && blevel == 0) {
1304 level--;
1305 if (!level) break;
1306 } else if (*pc->p == '\\') {
1307 pc->p++; pc->len--;
1308 } else if (*pc->p == '{') {
1309 blevel++;
1310 } else if (*pc->p == '}') {
1311 if (blevel != 0)
1312 blevel--;
1313 } else if (*pc->p == '\n')
1314 pc->linenr++;
1315 pc->p++; pc->len--;
1316 }
1317 pc->tend = pc->p-1;
1318 pc->tt = JIM_TT_CMD;
1319 if (*pc->p == ']') {
1320 pc->p++; pc->len--;
1321 }
1322 return JIM_OK;
1323 }
1324
1325 int JimParseVar(struct JimParserCtx *pc)
1326 {
1327 int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1328
1329 pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1330 pc->tline = pc->linenr;
1331 if (*pc->p == '{') {
1332 pc->tstart = ++pc->p; pc->len--;
1333 brace = 1;
1334 }
1335 if (brace) {
1336 while (!stop) {
1337 if (*pc->p == '}' || pc->len == 0) {
1338 pc->tend = pc->p-1;
1339 stop = 1;
1340 if (pc->len == 0)
1341 break;
1342 }
1343 else if (*pc->p == '\n')
1344 pc->linenr++;
1345 pc->p++; pc->len--;
1346 }
1347 } else {
1348 /* Include leading colons */
1349 while (*pc->p == ':') {
1350 pc->p++;
1351 pc->len--;
1352 }
1353 while (!stop) {
1354 if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1355 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1356 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1357 stop = 1;
1358 else {
1359 pc->p++; pc->len--;
1360 }
1361 }
1362 /* Parse [dict get] syntax sugar. */
1363 if (*pc->p == '(') {
1364 while (*pc->p != ')' && pc->len) {
1365 pc->p++; pc->len--;
1366 if (*pc->p == '\\' && pc->len >= 2) {
1367 pc->p += 2; pc->len -= 2;
1368 }
1369 }
1370 if (*pc->p != '\0') {
1371 pc->p++; pc->len--;
1372 }
1373 ttype = JIM_TT_DICTSUGAR;
1374 }
1375 pc->tend = pc->p-1;
1376 }
1377 /* Check if we parsed just the '$' character.
1378 * That's not a variable so an error is returned
1379 * to tell the state machine to consider this '$' just
1380 * a string. */
1381 if (pc->tstart == pc->p) {
1382 pc->p--; pc->len++;
1383 return JIM_ERR;
1384 }
1385 pc->tt = ttype;
1386 return JIM_OK;
1387 }
1388
1389 int JimParseBrace(struct JimParserCtx *pc)
1390 {
1391 int level = 1;
1392
1393 pc->tstart = ++pc->p; pc->len--;
1394 pc->tline = pc->linenr;
1395 while (1) {
1396 if (*pc->p == '\\' && pc->len >= 2) {
1397 pc->p++; pc->len--;
1398 if (*pc->p == '\n')
1399 pc->linenr++;
1400 } else if (*pc->p == '{') {
1401 level++;
1402 } else if (pc->len == 0 || *pc->p == '}') {
1403 level--;
1404 if (pc->len == 0 || level == 0) {
1405 pc->tend = pc->p-1;
1406 if (pc->len != 0) {
1407 pc->p++; pc->len--;
1408 }
1409 pc->tt = JIM_TT_STR;
1410 return JIM_OK;
1411 }
1412 } else if (*pc->p == '\n') {
1413 pc->linenr++;
1414 }
1415 pc->p++; pc->len--;
1416 }
1417 return JIM_OK; /* unreached */
1418 }
1419
1420 int JimParseStr(struct JimParserCtx *pc)
1421 {
1422 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1423 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1424 if (newword && *pc->p == '{') {
1425 return JimParseBrace(pc);
1426 } else if (newword && *pc->p == '"') {
1427 pc->state = JIM_PS_QUOTE;
1428 pc->p++; pc->len--;
1429 }
1430 pc->tstart = pc->p;
1431 pc->tline = pc->linenr;
1432 while (1) {
1433 if (pc->len == 0) {
1434 pc->tend = pc->p-1;
1435 pc->tt = JIM_TT_ESC;
1436 return JIM_OK;
1437 }
1438 switch(*pc->p) {
1439 case '\\':
1440 if (pc->state == JIM_PS_DEF &&
1441 *(pc->p+1) == '\n') {
1442 pc->tend = pc->p-1;
1443 pc->tt = JIM_TT_ESC;
1444 return JIM_OK;
1445 }
1446 if (pc->len >= 2) {
1447 pc->p++; pc->len--;
1448 }
1449 break;
1450 case '$':
1451 case '[':
1452 pc->tend = pc->p-1;
1453 pc->tt = JIM_TT_ESC;
1454 return JIM_OK;
1455 case ' ':
1456 case '\t':
1457 case '\n':
1458 case '\r':
1459 case ';':
1460 if (pc->state == JIM_PS_DEF) {
1461 pc->tend = pc->p-1;
1462 pc->tt = JIM_TT_ESC;
1463 return JIM_OK;
1464 } else if (*pc->p == '\n') {
1465 pc->linenr++;
1466 }
1467 break;
1468 case '"':
1469 if (pc->state == JIM_PS_QUOTE) {
1470 pc->tend = pc->p-1;
1471 pc->tt = JIM_TT_ESC;
1472 pc->p++; pc->len--;
1473 pc->state = JIM_PS_DEF;
1474 return JIM_OK;
1475 }
1476 break;
1477 }
1478 pc->p++; pc->len--;
1479 }
1480 return JIM_OK; /* unreached */
1481 }
1482
1483 int JimParseComment(struct JimParserCtx *pc)
1484 {
1485 while (*pc->p) {
1486 if (*pc->p == '\n') {
1487 pc->linenr++;
1488 if (*(pc->p-1) != '\\') {
1489 pc->p++; pc->len--;
1490 return JIM_OK;
1491 }
1492 }
1493 pc->p++; pc->len--;
1494 }
1495 return JIM_OK;
1496 }
1497
1498 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1499 static int xdigitval(int c)
1500 {
1501 if (c >= '0' && c <= '9') return c-'0';
1502 if (c >= 'a' && c <= 'f') return c-'a'+10;
1503 if (c >= 'A' && c <= 'F') return c-'A'+10;
1504 return -1;
1505 }
1506
1507 static int odigitval(int c)
1508 {
1509 if (c >= '0' && c <= '7') return c-'0';
1510 return -1;
1511 }
1512
1513 /* Perform Tcl escape substitution of 's', storing the result
1514 * string into 'dest'. The escaped string is guaranteed to
1515 * be the same length or shorted than the source string.
1516 * Slen is the length of the string at 's', if it's -1 the string
1517 * length will be calculated by the function.
1518 *
1519 * The function returns the length of the resulting string. */
1520 static int JimEscape(char *dest, const char *s, int slen)
1521 {
1522 char *p = dest;
1523 int i, len;
1524
1525 if (slen == -1)
1526 slen = strlen(s);
1527
1528 for (i = 0; i < slen; i++) {
1529 switch(s[i]) {
1530 case '\\':
1531 switch(s[i+1]) {
1532 case 'a': *p++ = 0x7; i++; break;
1533 case 'b': *p++ = 0x8; i++; break;
1534 case 'f': *p++ = 0xc; i++; break;
1535 case 'n': *p++ = 0xa; i++; break;
1536 case 'r': *p++ = 0xd; i++; break;
1537 case 't': *p++ = 0x9; i++; break;
1538 case 'v': *p++ = 0xb; i++; break;
1539 case '\0': *p++ = '\\'; i++; break;
1540 case '\n': *p++ = ' '; i++; break;
1541 default:
1542 if (s[i+1] == 'x') {
1543 int val = 0;
1544 int c = xdigitval(s[i+2]);
1545 if (c == -1) {
1546 *p++ = 'x';
1547 i++;
1548 break;
1549 }
1550 val = c;
1551 c = xdigitval(s[i+3]);
1552 if (c == -1) {
1553 *p++ = val;
1554 i += 2;
1555 break;
1556 }
1557 val = (val*16)+c;
1558 *p++ = val;
1559 i += 3;
1560 break;
1561 } else if (s[i+1] >= '0' && s[i+1] <= '7')
1562 {
1563 int val = 0;
1564 int c = odigitval(s[i+1]);
1565 val = c;
1566 c = odigitval(s[i+2]);
1567 if (c == -1) {
1568 *p++ = val;
1569 i ++;
1570 break;
1571 }
1572 val = (val*8)+c;
1573 c = odigitval(s[i+3]);
1574 if (c == -1) {
1575 *p++ = val;
1576 i += 2;
1577 break;
1578 }
1579 val = (val*8)+c;
1580 *p++ = val;
1581 i += 3;
1582 } else {
1583 *p++ = s[i+1];
1584 i++;
1585 }
1586 break;
1587 }
1588 break;
1589 default:
1590 *p++ = s[i];
1591 break;
1592 }
1593 }
1594 len = p-dest;
1595 *p++ = '\0';
1596 return len;
1597 }
1598
1599 /* Returns a dynamically allocated copy of the current token in the
1600 * parser context. The function perform conversion of escapes if
1601 * the token is of type JIM_TT_ESC.
1602 *
1603 * Note that after the conversion, tokens that are grouped with
1604 * braces in the source code, are always recognizable from the
1605 * identical string obtained in a different way from the type.
1606 *
1607 * For exmple the string:
1608 *
1609 * {expand}$a
1610 *
1611 * will return as first token "expand", of type JIM_TT_STR
1612 *
1613 * While the string:
1614 *
1615 * expand$a
1616 *
1617 * will return as first token "expand", of type JIM_TT_ESC
1618 */
1619 char *JimParserGetToken(struct JimParserCtx *pc,
1620 int *lenPtr, int *typePtr, int *linePtr)
1621 {
1622 const char *start, *end;
1623 char *token;
1624 int len;
1625
1626 start = JimParserTstart(pc);
1627 end = JimParserTend(pc);
1628 if (start > end) {
1629 if (lenPtr) *lenPtr = 0;
1630 if (typePtr) *typePtr = JimParserTtype(pc);
1631 if (linePtr) *linePtr = JimParserTline(pc);
1632 token = Jim_Alloc(1);
1633 token[0] = '\0';
1634 return token;
1635 }
1636 len = (end-start)+1;
1637 token = Jim_Alloc(len+1);
1638 if (JimParserTtype(pc) != JIM_TT_ESC) {
1639 /* No escape conversion needed? Just copy it. */
1640 memcpy(token, start, len);
1641 token[len] = '\0';
1642 } else {
1643 /* Else convert the escape chars. */
1644 len = JimEscape(token, start, len);
1645 }
1646 if (lenPtr) *lenPtr = len;
1647 if (typePtr) *typePtr = JimParserTtype(pc);
1648 if (linePtr) *linePtr = JimParserTline(pc);
1649 return token;
1650 }
1651
1652 /* The following functin is not really part of the parsing engine of Jim,
1653 * but it somewhat related. Given an string and its length, it tries
1654 * to guess if the script is complete or there are instead " " or { }
1655 * open and not completed. This is useful for interactive shells
1656 * implementation and for [info complete].
1657 *
1658 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1659 * '{' on scripts incomplete missing one or more '}' to be balanced.
1660 * '"' on scripts incomplete missing a '"' char.
1661 *
1662 * If the script is complete, 1 is returned, otherwise 0. */
1663 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1664 {
1665 int level = 0;
1666 int state = ' ';
1667
1668 while(len) {
1669 switch (*s) {
1670 case '\\':
1671 if (len > 1)
1672 s++;
1673 break;
1674 case '"':
1675 if (state == ' ') {
1676 state = '"';
1677 } else if (state == '"') {
1678 state = ' ';
1679 }
1680 break;
1681 case '{':
1682 if (state == '{') {
1683 level++;
1684 } else if (state == ' ') {
1685 state = '{';
1686 level++;
1687 }
1688 break;
1689 case '}':
1690 if (state == '{') {
1691 level--;
1692 if (level == 0)
1693 state = ' ';
1694 }
1695 break;
1696 }
1697 s++;
1698 len--;
1699 }
1700 if (stateCharPtr)
1701 *stateCharPtr = state;
1702 return state == ' ';
1703 }
1704
1705 /* -----------------------------------------------------------------------------
1706 * Tcl Lists parsing
1707 * ---------------------------------------------------------------------------*/
1708 static int JimParseListSep(struct JimParserCtx *pc);
1709 static int JimParseListStr(struct JimParserCtx *pc);
1710
1711 int JimParseList(struct JimParserCtx *pc)
1712 {
1713 if (pc->len == 0) {
1714 pc->tstart = pc->tend = pc->p;
1715 pc->tline = pc->linenr;
1716 pc->tt = JIM_TT_EOL;
1717 pc->eof = 1;
1718 return JIM_OK;
1719 }
1720 switch(*pc->p) {
1721 case ' ':
1722 case '\n':
1723 case '\t':
1724 case '\r':
1725 if (pc->state == JIM_PS_DEF)
1726 return JimParseListSep(pc);
1727 else
1728 return JimParseListStr(pc);
1729 break;
1730 default:
1731 return JimParseListStr(pc);
1732 break;
1733 }
1734 return JIM_OK;
1735 }
1736
1737 int JimParseListSep(struct JimParserCtx *pc)
1738 {
1739 pc->tstart = pc->p;
1740 pc->tline = pc->linenr;
1741 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1742 {
1743 pc->p++; pc->len--;
1744 }
1745 pc->tend = pc->p-1;
1746 pc->tt = JIM_TT_SEP;
1747 return JIM_OK;
1748 }
1749
1750 int JimParseListStr(struct JimParserCtx *pc)
1751 {
1752 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1753 pc->tt == JIM_TT_NONE);
1754 if (newword && *pc->p == '{') {
1755 return JimParseBrace(pc);
1756 } else if (newword && *pc->p == '"') {
1757 pc->state = JIM_PS_QUOTE;
1758 pc->p++; pc->len--;
1759 }
1760 pc->tstart = pc->p;
1761 pc->tline = pc->linenr;
1762 while (1) {
1763 if (pc->len == 0) {
1764 pc->tend = pc->p-1;
1765 pc->tt = JIM_TT_ESC;
1766 return JIM_OK;
1767 }
1768 switch(*pc->p) {
1769 case '\\':
1770 pc->p++; pc->len--;
1771 break;
1772 case ' ':
1773 case '\t':
1774 case '\n':
1775 case '\r':
1776 if (pc->state == JIM_PS_DEF) {
1777 pc->tend = pc->p-1;
1778 pc->tt = JIM_TT_ESC;
1779 return JIM_OK;
1780 } else if (*pc->p == '\n') {
1781 pc->linenr++;
1782 }
1783 break;
1784 case '"':
1785 if (pc->state == JIM_PS_QUOTE) {
1786 pc->tend = pc->p-1;
1787 pc->tt = JIM_TT_ESC;
1788 pc->p++; pc->len--;
1789 pc->state = JIM_PS_DEF;
1790 return JIM_OK;
1791 }
1792 break;
1793 }
1794 pc->p++; pc->len--;
1795 }
1796 return JIM_OK; /* unreached */
1797 }
1798
1799 /* -----------------------------------------------------------------------------
1800 * Jim_Obj related functions
1801 * ---------------------------------------------------------------------------*/
1802
1803 /* Return a new initialized object. */
1804 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1805 {
1806 Jim_Obj *objPtr;
1807
1808 /* -- Check if there are objects in the free list -- */
1809 if (interp->freeList != NULL) {
1810 /* -- Unlink the object from the free list -- */
1811 objPtr = interp->freeList;
1812 interp->freeList = objPtr->nextObjPtr;
1813 } else {
1814 /* -- No ready to use objects: allocate a new one -- */
1815 objPtr = Jim_Alloc(sizeof(*objPtr));
1816 }
1817
1818 /* Object is returned with refCount of 0. Every
1819 * kind of GC implemented should take care to don't try
1820 * to scan objects with refCount == 0. */
1821 objPtr->refCount = 0;
1822 /* All the other fields are left not initialized to save time.
1823 * The caller will probably want set they to the right
1824 * value anyway. */
1825
1826 /* -- Put the object into the live list -- */
1827 objPtr->prevObjPtr = NULL;
1828 objPtr->nextObjPtr = interp->liveList;
1829 if (interp->liveList)
1830 interp->liveList->prevObjPtr = objPtr;
1831 interp->liveList = objPtr;
1832
1833 return objPtr;
1834 }
1835
1836 /* Free an object. Actually objects are never freed, but
1837 * just moved to the free objects list, where they will be
1838 * reused by Jim_NewObj(). */
1839 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1840 {
1841 /* Check if the object was already freed, panic. */
1842 if (objPtr->refCount != 0) {
1843 Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1844 objPtr->refCount);
1845 }
1846 /* Free the internal representation */
1847 Jim_FreeIntRep(interp, objPtr);
1848 /* Free the string representation */
1849 if (objPtr->bytes != NULL) {
1850 if (objPtr->bytes != JimEmptyStringRep)
1851 Jim_Free(objPtr->bytes);
1852 }
1853 /* Unlink the object from the live objects list */
1854 if (objPtr->prevObjPtr)
1855 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1856 if (objPtr->nextObjPtr)
1857 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1858 if (interp->liveList == objPtr)
1859 interp->liveList = objPtr->nextObjPtr;
1860 /* Link the object into the free objects list */
1861 objPtr->prevObjPtr = NULL;
1862 objPtr->nextObjPtr = interp->freeList;
1863 if (interp->freeList)
1864 interp->freeList->prevObjPtr = objPtr;
1865 interp->freeList = objPtr;
1866 objPtr->refCount = -1;
1867 }
1868
1869 /* Invalidate the string representation of an object. */
1870 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1871 {
1872 if (objPtr->bytes != NULL) {
1873 if (objPtr->bytes != JimEmptyStringRep)
1874 Jim_Free(objPtr->bytes);
1875 }
1876 objPtr->bytes = NULL;
1877 }
1878
1879 #define Jim_SetStringRep(o, b, l) \
1880 do { (o)->bytes = b; (o)->length = l; } while (0)
1881
1882 /* Set the initial string representation for an object.
1883 * Does not try to free an old one. */
1884 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1885 {
1886 if (length == 0) {
1887 objPtr->bytes = JimEmptyStringRep;
1888 objPtr->length = 0;
1889 } else {
1890 objPtr->bytes = Jim_Alloc(length+1);
1891 objPtr->length = length;
1892 memcpy(objPtr->bytes, bytes, length);
1893 objPtr->bytes[length] = '\0';
1894 }
1895 }
1896
1897 /* Duplicate an object. The returned object has refcount = 0. */
1898 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1899 {
1900 Jim_Obj *dupPtr;
1901
1902 dupPtr = Jim_NewObj(interp);
1903 if (objPtr->bytes == NULL) {
1904 /* Object does not have a valid string representation. */
1905 dupPtr->bytes = NULL;
1906 } else {
1907 Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1908 }
1909 if (objPtr->typePtr != NULL) {
1910 if (objPtr->typePtr->dupIntRepProc == NULL) {
1911 dupPtr->internalRep = objPtr->internalRep;
1912 } else {
1913 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1914 }
1915 dupPtr->typePtr = objPtr->typePtr;
1916 } else {
1917 dupPtr->typePtr = NULL;
1918 }
1919 return dupPtr;
1920 }
1921
1922 /* Return the string representation for objPtr. If the object
1923 * string representation is invalid, calls the method to create
1924 * a new one starting from the internal representation of the object. */
1925 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1926 {
1927 if (objPtr->bytes == NULL) {
1928 /* Invalid string repr. Generate it. */
1929 if (objPtr->typePtr->updateStringProc == NULL) {
1930 Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1931 objPtr->typePtr->name);
1932 }
1933 objPtr->typePtr->updateStringProc(objPtr);
1934 }
1935 if (lenPtr)
1936 *lenPtr = objPtr->length;
1937 return objPtr->bytes;
1938 }
1939
1940 /* Just returns the length of the object's string rep */
1941 int Jim_Length(Jim_Obj *objPtr)
1942 {
1943 int len;
1944
1945 Jim_GetString(objPtr, &len);
1946 return len;
1947 }
1948
1949 /* -----------------------------------------------------------------------------
1950 * String Object
1951 * ---------------------------------------------------------------------------*/
1952 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1953 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1954
1955 static Jim_ObjType stringObjType = {
1956 "string",
1957 NULL,
1958 DupStringInternalRep,
1959 NULL,
1960 JIM_TYPE_REFERENCES,
1961 };
1962
1963 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1964 {
1965 JIM_NOTUSED(interp);
1966
1967 /* This is a bit subtle: the only caller of this function
1968 * should be Jim_DuplicateObj(), that will copy the
1969 * string representaion. After the copy, the duplicated
1970 * object will not have more room in teh buffer than
1971 * srcPtr->length bytes. So we just set it to length. */
1972 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1973 }
1974
1975 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1976 {
1977 /* Get a fresh string representation. */
1978 (void) Jim_GetString(objPtr, NULL);
1979 /* Free any other internal representation. */
1980 Jim_FreeIntRep(interp, objPtr);
1981 /* Set it as string, i.e. just set the maxLength field. */
1982 objPtr->typePtr = &stringObjType;
1983 objPtr->internalRep.strValue.maxLength = objPtr->length;
1984 return JIM_OK;
1985 }
1986
1987 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1988 {
1989 Jim_Obj *objPtr = Jim_NewObj(interp);
1990
1991 if (len == -1)
1992 len = strlen(s);
1993 /* Alloc/Set the string rep. */
1994 if (len == 0) {
1995 objPtr->bytes = JimEmptyStringRep;
1996 objPtr->length = 0;
1997 } else {
1998 objPtr->bytes = Jim_Alloc(len+1);
1999 objPtr->length = len;
2000 memcpy(objPtr->bytes, s, len);
2001 objPtr->bytes[len] = '\0';
2002 }
2003
2004 /* No typePtr field for the vanilla string object. */
2005 objPtr->typePtr = NULL;
2006 return objPtr;
2007 }
2008
2009 /* This version does not try to duplicate the 's' pointer, but
2010 * use it directly. */
2011 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
2012 {
2013 Jim_Obj *objPtr = Jim_NewObj(interp);
2014
2015 if (len == -1)
2016 len = strlen(s);
2017 Jim_SetStringRep(objPtr, s, len);
2018 objPtr->typePtr = NULL;
2019 return objPtr;
2020 }
2021
2022 /* Low-level string append. Use it only against objects
2023 * of type "string". */
2024 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
2025 {
2026 int needlen;
2027
2028 if (len == -1)
2029 len = strlen(str);
2030 needlen = objPtr->length + len;
2031 if (objPtr->internalRep.strValue.maxLength < needlen ||
2032 objPtr->internalRep.strValue.maxLength == 0) {
2033 if (objPtr->bytes == JimEmptyStringRep) {
2034 objPtr->bytes = Jim_Alloc((needlen*2)+1);
2035 } else {
2036 objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
2037 }
2038 objPtr->internalRep.strValue.maxLength = needlen*2;
2039 }
2040 memcpy(objPtr->bytes + objPtr->length, str, len);
2041 objPtr->bytes[objPtr->length+len] = '\0';
2042 objPtr->length += len;
2043 }
2044
2045 /* Low-level wrapper to append an object. */
2046 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
2047 {
2048 int len;
2049 const char *str;
2050
2051 str = Jim_GetString(appendObjPtr, &len);
2052 StringAppendString(objPtr, str, len);
2053 }
2054
2055 /* Higher level API to append strings to objects. */
2056 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
2057 int len)
2058 {
2059 if (Jim_IsShared(objPtr))
2060 Jim_Panic(interp,"Jim_AppendString called with shared object");
2061 if (objPtr->typePtr != &stringObjType)
2062 SetStringFromAny(interp, objPtr);
2063 StringAppendString(objPtr, str, len);
2064 }
2065
2066 void Jim_AppendString_sprintf( Jim_Interp *interp, Jim_Obj *objPtr, const char *fmt, ... )
2067 {
2068 char *buf;
2069 va_list ap;
2070
2071 va_start( ap, fmt );
2072 buf = jim_vasprintf( fmt, ap );
2073 va_end(ap);
2074
2075 if( buf ){
2076 Jim_AppendString( interp, objPtr, buf, -1 );
2077 jim_vasprintf_done(buf);
2078 }
2079 }
2080
2081
2082 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
2083 Jim_Obj *appendObjPtr)
2084 {
2085 int len;
2086 const char *str;
2087
2088 str = Jim_GetString(appendObjPtr, &len);
2089 Jim_AppendString(interp, objPtr, str, len);
2090 }
2091
2092 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2093 {
2094 va_list ap;
2095
2096 if (objPtr->typePtr != &stringObjType)
2097 SetStringFromAny(interp, objPtr);
2098 va_start(ap, objPtr);
2099 while (1) {
2100 char *s = va_arg(ap, char*);
2101
2102 if (s == NULL) break;
2103 Jim_AppendString(interp, objPtr, s, -1);
2104 }
2105 va_end(ap);
2106 }
2107
2108 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2109 {
2110 const char *aStr, *bStr;
2111 int aLen, bLen, i;
2112
2113 if (aObjPtr == bObjPtr) return 1;
2114 aStr = Jim_GetString(aObjPtr, &aLen);
2115 bStr = Jim_GetString(bObjPtr, &bLen);
2116 if (aLen != bLen) return 0;
2117 if (nocase == 0)
2118 return memcmp(aStr, bStr, aLen) == 0;
2119 for (i = 0; i < aLen; i++) {
2120 if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2121 return 0;
2122 }
2123 return 1;
2124 }
2125
2126 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2127 int nocase)
2128 {
2129 const char *pattern, *string;
2130 int patternLen, stringLen;
2131
2132 pattern = Jim_GetString(patternObjPtr, &patternLen);
2133 string = Jim_GetString(objPtr, &stringLen);
2134 return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2135 }
2136
2137 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2138 Jim_Obj *secondObjPtr, int nocase)
2139 {
2140 const char *s1, *s2;
2141 int l1, l2;
2142
2143 s1 = Jim_GetString(firstObjPtr, &l1);
2144 s2 = Jim_GetString(secondObjPtr, &l2);
2145 return JimStringCompare(s1, l1, s2, l2, nocase);
2146 }
2147
2148 /* Convert a range, as returned by Jim_GetRange(), into
2149 * an absolute index into an object of the specified length.
2150 * This function may return negative values, or values
2151 * bigger or equal to the length of the list if the index
2152 * is out of range. */
2153 static int JimRelToAbsIndex(int len, int index)
2154 {
2155 if (index < 0)
2156 return len + index;
2157 return index;
2158 }
2159
2160 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2161 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2162 * for implementation of commands like [string range] and [lrange].
2163 *
2164 * The resulting range is guaranteed to address valid elements of
2165 * the structure. */
2166 static void JimRelToAbsRange(int len, int first, int last,
2167 int *firstPtr, int *lastPtr, int *rangeLenPtr)
2168 {
2169 int rangeLen;
2170
2171 if (first > last) {
2172 rangeLen = 0;
2173 } else {
2174 rangeLen = last-first+1;
2175 if (rangeLen) {
2176 if (first < 0) {
2177 rangeLen += first;
2178 first = 0;
2179 }
2180 if (last >= len) {
2181 rangeLen -= (last-(len-1));
2182 last = len-1;
2183 }
2184 }
2185 }
2186 if (rangeLen < 0) rangeLen = 0;
2187
2188 *firstPtr = first;
2189 *lastPtr = last;
2190 *rangeLenPtr = rangeLen;
2191 }
2192
2193 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2194 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2195 {
2196 int first, last;
2197 const char *str;
2198 int len, rangeLen;
2199
2200 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2201 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2202 return NULL;
2203 str = Jim_GetString(strObjPtr, &len);
2204 first = JimRelToAbsIndex(len, first);
2205 last = JimRelToAbsIndex(len, last);
2206 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2207 return Jim_NewStringObj(interp, str+first, rangeLen);
2208 }
2209
2210 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2211 {
2212 char *buf;
2213 int i;
2214 if (strObjPtr->typePtr != &stringObjType) {
2215 SetStringFromAny(interp, strObjPtr);
2216 }
2217
2218 buf = Jim_Alloc(strObjPtr->length+1);
2219
2220 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2221 for (i = 0; i < strObjPtr->length; i++)
2222 buf[i] = tolower(buf[i]);
2223 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2224 }
2225
2226 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2227 {
2228 char *buf;
2229 int i;
2230 if (strObjPtr->typePtr != &stringObjType) {
2231 SetStringFromAny(interp, strObjPtr);
2232 }
2233
2234 buf = Jim_Alloc(strObjPtr->length+1);
2235
2236 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2237 for (i = 0; i < strObjPtr->length; i++)
2238 buf[i] = toupper(buf[i]);
2239 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2240 }
2241
2242 /* This is the core of the [format] command.
2243 * TODO: Lots of things work - via a hack
2244 * However, no format item can be >= JIM_MAX_FMT
2245 */
2246 #define JIM_MAX_FMT 2048
2247 static Jim_Obj *Jim_FormatString_Inner(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2248 int objc, Jim_Obj *const *objv, char *sprintf_buf)
2249 {
2250 const char *fmt, *_fmt;
2251 int fmtLen;
2252 Jim_Obj *resObjPtr;
2253
2254
2255 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2256 _fmt = fmt;
2257 resObjPtr = Jim_NewStringObj(interp, "", 0);
2258 while (fmtLen) {
2259 const char *p = fmt;
2260 char spec[2], c;
2261 jim_wide wideValue;
2262 double doubleValue;
2263 /* we cheat and use Sprintf()! */
2264 char fmt_str[100];
2265 char *cp;
2266 int width;
2267 int ljust;
2268 int zpad;
2269 int spad;
2270 int altfm;
2271 int forceplus;
2272 int prec;
2273 int inprec;
2274 int haveprec;
2275 int accum;
2276
2277 while (*fmt != '%' && fmtLen) {
2278 fmt++; fmtLen--;
2279 }
2280 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2281 if (fmtLen == 0)
2282 break;
2283 fmt++; fmtLen--; /* skip '%' */
2284 zpad = 0;
2285 spad = 0;
2286 width = -1;
2287 ljust = 0;
2288 altfm = 0;
2289 forceplus = 0;
2290 inprec = 0;
2291 haveprec = 0;
2292 prec = -1; /* not found yet */
2293 next_fmt:
2294 if( fmtLen <= 0 ){
2295 break;
2296 }
2297 switch( *fmt ){
2298 /* terminals */
2299 case 'b': /* binary - not all printfs() do this */
2300 case 's': /* string */
2301 case 'i': /* integer */
2302 case 'd': /* decimal */
2303 case 'x': /* hex */
2304 case 'X': /* CAP hex */
2305 case 'c': /* char */
2306 case 'o': /* octal */
2307 case 'u': /* unsigned */
2308 case 'f': /* float */
2309 break;
2310
2311 /* non-terminals */
2312 case '0': /* zero pad */
2313 zpad = 1;
2314 fmt++; fmtLen--;
2315 goto next_fmt;
2316 break;
2317 case '+':
2318 forceplus = 1;
2319 fmt++; fmtLen--;
2320 goto next_fmt;
2321 break;
2322 case ' ': /* sign space */
2323 spad = 1;
2324 fmt++; fmtLen--;
2325 goto next_fmt;
2326 break;
2327 case '-':
2328 ljust = 1;
2329 fmt++; fmtLen--;
2330 goto next_fmt;
2331 break;
2332 case '#':
2333 altfm = 1;
2334 fmt++; fmtLen--;
2335 goto next_fmt;
2336
2337 case '.':
2338 inprec = 1;
2339 fmt++; fmtLen--;
2340 goto next_fmt;
2341 break;
2342 case '1':
2343 case '2':
2344 case '3':
2345 case '4':
2346 case '5':
2347 case '6':
2348 case '7':
2349 case '8':
2350 case '9':
2351 accum = 0;
2352 while( isdigit(*fmt) && (fmtLen > 0) ){
2353 accum = (accum * 10) + (*fmt - '0');
2354 fmt++; fmtLen--;
2355 }
2356 if( inprec ){
2357 haveprec = 1;
2358 prec = accum;
2359 } else {
2360 width = accum;
2361 }
2362 goto next_fmt;
2363 case '*':
2364 /* suck up the next item as an integer */
2365 fmt++; fmtLen--;
2366 objc--;
2367 if( objc <= 0 ){
2368 goto not_enough_args;
2369 }
2370 if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2371 Jim_FreeNewObj(interp, resObjPtr );
2372 return NULL;
2373 }
2374 if( inprec ){
2375 haveprec = 1;
2376 prec = wideValue;
2377 if( prec < 0 ){
2378 /* man 3 printf says */
2379 /* if prec is negative, it is zero */
2380 prec = 0;
2381 }
2382 } else {
2383 width = wideValue;
2384 if( width < 0 ){
2385 ljust = 1;
2386 width = -width;
2387 }
2388 }
2389 objv++;
2390 goto next_fmt;
2391 break;
2392 }
2393
2394
2395 if (*fmt != '%') {
2396 if (objc == 0) {
2397 not_enough_args:
2398 Jim_FreeNewObj(interp, resObjPtr);
2399 Jim_SetResultString(interp,
2400 "not enough arguments for all format specifiers", -1);
2401 return NULL;
2402 } else {
2403 objc--;
2404 }
2405 }
2406
2407 /*
2408 * Create the formatter
2409 * cause we cheat and use sprintf()
2410 */
2411 cp = fmt_str;
2412 *cp++ = '%';
2413 if( altfm ){
2414 *cp++ = '#';
2415 }
2416 if( forceplus ){
2417 *cp++ = '+';
2418 } else if( spad ){
2419 /* PLUS overrides */
2420 *cp++ = ' ';
2421 }
2422 if( ljust ){
2423 *cp++ = '-';
2424 }
2425 if( zpad ){
2426 *cp++ = '0';
2427 }
2428 if( width > 0 ){
2429 sprintf( cp, "%d", width );
2430 /* skip ahead */
2431 cp = strchr(cp,0);
2432 }
2433 /* did we find a period? */
2434 if( inprec ){
2435 /* then add it */
2436 *cp++ = '.';
2437 /* did something occur after the period? */
2438 if( haveprec ){
2439 sprintf( cp, "%d", prec );
2440 }
2441 cp = strchr(cp,0);
2442 }
2443 *cp = 0;
2444
2445 /* here we do the work */
2446 /* actually - we make sprintf() do it for us */
2447 switch(*fmt) {
2448 case 's':
2449 *cp++ = 's';
2450 *cp = 0;
2451 /* BUG: we do not handled embeded NULLs */
2452 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2453 break;
2454 case 'c':
2455 *cp++ = 'c';
2456 *cp = 0;
2457 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2458 Jim_FreeNewObj(interp, resObjPtr);
2459 return NULL;
2460 }
2461 c = (char) wideValue;
2462 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2463 break;
2464 case 'f':
2465 case 'F':
2466 case 'g':
2467 case 'G':
2468 case 'e':
2469 case 'E':
2470 *cp++ = *fmt;
2471 *cp = 0;
2472 if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2473 Jim_FreeNewObj( interp, resObjPtr );
2474 return NULL;
2475 }
2476 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2477 break;
2478 case 'b':
2479 case 'd':
2480 case 'o':
2481 case 'i':
2482 case 'u':
2483 case 'x':
2484 case 'X':
2485 /* jim widevaluse are 64bit */
2486 if( sizeof(jim_wide) == sizeof(long long) ){
2487 *cp++ = 'l';
2488 *cp++ = 'l';
2489 } else {
2490 *cp++ = 'l';
2491 }
2492 *cp++ = *fmt;
2493 *cp = 0;
2494 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2495 Jim_FreeNewObj(interp, resObjPtr);
2496 return NULL;
2497 }
2498 snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2499 break;
2500 case '%':
2501 sprintf_buf[0] = '%';
2502 sprintf_buf[1] = 0;
2503 objv--; /* undo the objv++ below */
2504 break;
2505 default:
2506 spec[0] = *fmt; spec[1] = '\0';
2507 Jim_FreeNewObj(interp, resObjPtr);
2508 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2509 Jim_AppendStrings(interp, Jim_GetResult(interp),
2510 "bad field specifier \"", spec, "\"", NULL);
2511 return NULL;
2512 }
2513 /* force terminate */
2514 #if 0
2515 printf("FMT was: %s\n", fmt_str );
2516 printf("RES was: |%s|\n", sprintf_buf );
2517 #endif
2518
2519 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2520 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2521 /* next obj */
2522 objv++;
2523 fmt++;
2524 fmtLen--;
2525 }
2526 return resObjPtr;
2527 }
2528
2529 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2530 int objc, Jim_Obj *const *objv)
2531 {
2532 char *sprintf_buf=malloc(JIM_MAX_FMT);
2533 Jim_Obj *t=Jim_FormatString_Inner(interp, fmtObjPtr, objc, objv, sprintf_buf);
2534 free(sprintf_buf);
2535 return t;
2536 }
2537
2538 /* -----------------------------------------------------------------------------
2539 * Compared String Object
2540 * ---------------------------------------------------------------------------*/
2541
2542 /* This is strange object that allows to compare a C literal string
2543 * with a Jim object in very short time if the same comparison is done
2544 * multiple times. For example every time the [if] command is executed,
2545 * Jim has to check if a given argument is "else". This comparions if
2546 * the code has no errors are true most of the times, so we can cache
2547 * inside the object the pointer of the string of the last matching
2548 * comparison. Because most C compilers perform literal sharing,
2549 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2550 * this works pretty well even if comparisons are at different places
2551 * inside the C code. */
2552
2553 static Jim_ObjType comparedStringObjType = {
2554 "compared-string",
2555 NULL,
2556 NULL,
2557 NULL,
2558 JIM_TYPE_REFERENCES,
2559 };
2560
2561 /* The only way this object is exposed to the API is via the following
2562 * function. Returns true if the string and the object string repr.
2563 * are the same, otherwise zero is returned.
2564 *
2565 * Note: this isn't binary safe, but it hardly needs to be.*/
2566 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2567 const char *str)
2568 {
2569 if (objPtr->typePtr == &comparedStringObjType &&
2570 objPtr->internalRep.ptr == str)
2571 return 1;
2572 else {
2573 const char *objStr = Jim_GetString(objPtr, NULL);
2574 if (strcmp(str, objStr) != 0) return 0;
2575 if (objPtr->typePtr != &comparedStringObjType) {
2576 Jim_FreeIntRep(interp, objPtr);
2577 objPtr->typePtr = &comparedStringObjType;
2578 }
2579 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2580 return 1;
2581 }
2582 }
2583
2584 int qsortCompareStringPointers(const void *a, const void *b)
2585 {
2586 char * const *sa = (char * const *)a;
2587 char * const *sb = (char * const *)b;
2588 return strcmp(*sa, *sb);
2589 }
2590
2591 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2592 const char * const *tablePtr, int *indexPtr, const char *name, int flags)
2593 {
2594 const char * const *entryPtr = NULL;
2595 char **tablePtrSorted;
2596 int i, count = 0;
2597
2598 *indexPtr = -1;
2599 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2600 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2601 *indexPtr = i;
2602 return JIM_OK;
2603 }
2604 count++; /* If nothing matches, this will reach the len of tablePtr */
2605 }
2606 if (flags & JIM_ERRMSG) {
2607 if (name == NULL)
2608 name = "option";
2609 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2610 Jim_AppendStrings(interp, Jim_GetResult(interp),
2611 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2612 NULL);
2613 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2614 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2615 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2616 for (i = 0; i < count; i++) {
2617 if (i+1 == count && count > 1)
2618 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2619 Jim_AppendString(interp, Jim_GetResult(interp),
2620 tablePtrSorted[i], -1);
2621 if (i+1 != count)
2622 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2623 }
2624 Jim_Free(tablePtrSorted);
2625 }
2626 return JIM_ERR;
2627 }
2628
2629 int Jim_GetNvp(Jim_Interp *interp,
2630 Jim_Obj *objPtr,
2631 const Jim_Nvp *nvp_table,
2632 const Jim_Nvp ** result)
2633 {
2634 Jim_Nvp *n;
2635 int e;
2636
2637 e = Jim_Nvp_name2value_obj( interp, nvp_table, objPtr, &n );
2638 if( e == JIM_ERR ){
2639 return e;
2640 }
2641
2642 /* Success? found? */
2643 if( n->name ){
2644 /* remove const */
2645 *result = (Jim_Nvp *)n;
2646 return JIM_OK;
2647 } else {
2648 return JIM_ERR;
2649 }
2650 }
2651
2652 /* -----------------------------------------------------------------------------
2653 * Source Object
2654 *
2655 * This object is just a string from the language point of view, but
2656 * in the internal representation it contains the filename and line number
2657 * where this given token was read. This information is used by
2658 * Jim_EvalObj() if the object passed happens to be of type "source".
2659 *
2660 * This allows to propagate the information about line numbers and file
2661 * names and give error messages with absolute line numbers.
2662 *
2663 * Note that this object uses shared strings for filenames, and the
2664 * pointer to the filename together with the line number is taken into
2665 * the space for the "inline" internal represenation of the Jim_Object,
2666 * so there is almost memory zero-overhead.
2667 *
2668 * Also the object will be converted to something else if the given
2669 * token it represents in the source file is not something to be
2670 * evaluated (not a script), and will be specialized in some other way,
2671 * so the time overhead is alzo null.
2672 * ---------------------------------------------------------------------------*/
2673
2674 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2675 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2676
2677 static Jim_ObjType sourceObjType = {
2678 "source",
2679 FreeSourceInternalRep,
2680 DupSourceInternalRep,
2681 NULL,
2682 JIM_TYPE_REFERENCES,
2683 };
2684
2685 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2686 {
2687 Jim_ReleaseSharedString(interp,
2688 objPtr->internalRep.sourceValue.fileName);
2689 }
2690
2691 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2692 {
2693 dupPtr->internalRep.sourceValue.fileName =
2694 Jim_GetSharedString(interp,
2695 srcPtr->internalRep.sourceValue.fileName);
2696 dupPtr->internalRep.sourceValue.lineNumber =
2697 dupPtr->internalRep.sourceValue.lineNumber;
2698 dupPtr->typePtr = &sourceObjType;
2699 }
2700
2701 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2702 const char *fileName, int lineNumber)
2703 {
2704 if (Jim_IsShared(objPtr))
2705 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2706 if (objPtr->typePtr != NULL)
2707 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2708 objPtr->internalRep.sourceValue.fileName =
2709 Jim_GetSharedString(interp, fileName);
2710 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2711 objPtr->typePtr = &sourceObjType;
2712 }
2713
2714 /* -----------------------------------------------------------------------------
2715 * Script Object
2716 * ---------------------------------------------------------------------------*/
2717
2718 #define JIM_CMDSTRUCT_EXPAND -1
2719
2720 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2721 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2722 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2723
2724 static Jim_ObjType scriptObjType = {
2725 "script",
2726 FreeScriptInternalRep,
2727 DupScriptInternalRep,
2728 NULL,
2729 JIM_TYPE_REFERENCES,
2730 };
2731
2732 /* The ScriptToken structure represents every token into a scriptObj.
2733 * Every token contains an associated Jim_Obj that can be specialized
2734 * by commands operating on it. */
2735 typedef struct ScriptToken {
2736 int type;
2737 Jim_Obj *objPtr;
2738 int linenr;
2739 } ScriptToken;
2740
2741 /* This is the script object internal representation. An array of
2742 * ScriptToken structures, with an associated command structure array.
2743 * The command structure is a pre-computed representation of the
2744 * command length and arguments structure as a simple liner array
2745 * of integers.
2746 *
2747 * For example the script:
2748 *
2749 * puts hello
2750 * set $i $x$y [foo]BAR
2751 *
2752 * will produce a ScriptObj with the following Tokens:
2753 *
2754 * ESC puts
2755 * SEP
2756 * ESC hello
2757 * EOL
2758 * ESC set
2759 * EOL
2760 * VAR i
2761 * SEP
2762 * VAR x
2763 * VAR y
2764 * SEP
2765 * CMD foo
2766 * ESC BAR
2767 * EOL
2768 *
2769 * This is a description of the tokens, separators, and of lines.
2770 * The command structure instead represents the number of arguments
2771 * of every command, followed by the tokens of which every argument
2772 * is composed. So for the example script, the cmdstruct array will
2773 * contain:
2774 *
2775 * 2 1 1 4 1 1 2 2
2776 *
2777 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2778 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2779 * composed of single tokens (1 1) and the last two of double tokens
2780 * (2 2).
2781 *
2782 * The precomputation of the command structure makes Jim_Eval() faster,
2783 * and simpler because there aren't dynamic lengths / allocations.
2784 *
2785 * -- {expand} handling --
2786 *
2787 * Expand is handled in a special way. When a command
2788 * contains at least an argument with the {expand} prefix,
2789 * the command structure presents a -1 before the integer
2790 * describing the number of arguments. This is used in order
2791 * to send the command exection to a different path in case
2792 * of {expand} and guarantee a fast path for the more common
2793 * case. Also, the integers describing the number of tokens
2794 * are expressed with negative sign, to allow for fast check
2795 * of what's an {expand}-prefixed argument and what not.
2796 *
2797 * For example the command:
2798 *
2799 * list {expand}{1 2}
2800 *
2801 * Will produce the following cmdstruct array:
2802 *
2803 * -1 2 1 -2
2804 *
2805 * -- the substFlags field of the structure --
2806 *
2807 * The scriptObj structure is used to represent both "script" objects
2808 * and "subst" objects. In the second case, the cmdStruct related
2809 * fields are not used at all, but there is an additional field used
2810 * that is 'substFlags': this represents the flags used to turn
2811 * the string into the intenral representation used to perform the
2812 * substitution. If this flags are not what the application requires
2813 * the scriptObj is created again. For example the script:
2814 *
2815 * subst -nocommands $string
2816 * subst -novariables $string
2817 *
2818 * Will recreate the internal representation of the $string object
2819 * two times.
2820 */
2821 typedef struct ScriptObj {
2822 int len; /* Length as number of tokens. */
2823 int commands; /* number of top-level commands in script. */
2824 ScriptToken *token; /* Tokens array. */
2825 int *cmdStruct; /* commands structure */
2826 int csLen; /* length of the cmdStruct array. */
2827 int substFlags; /* flags used for the compilation of "subst" objects */
2828 int inUse; /* Used to share a ScriptObj. Currently
2829 only used by Jim_EvalObj() as protection against
2830 shimmering of the currently evaluated object. */
2831 char *fileName;
2832 } ScriptObj;
2833
2834 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2835 {
2836 int i;
2837 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2838
2839 script->inUse--;
2840 if (script->inUse != 0) return;
2841 for (i = 0; i < script->len; i++) {
2842 if (script->token[i].objPtr != NULL)
2843 Jim_DecrRefCount(interp, script->token[i].objPtr);
2844 }
2845 Jim_Free(script->token);
2846 Jim_Free(script->cmdStruct);
2847 Jim_Free(script->fileName);
2848 Jim_Free(script);
2849 }
2850
2851 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2852 {
2853 JIM_NOTUSED(interp);
2854 JIM_NOTUSED(srcPtr);
2855
2856 /* Just returns an simple string. */
2857 dupPtr->typePtr = NULL;
2858 }
2859
2860 /* Add a new token to the internal repr of a script object */
2861 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2862 char *strtoken, int len, int type, char *filename, int linenr)
2863 {
2864 int prevtype;
2865 struct ScriptToken *token;
2866
2867 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2868 script->token[script->len-1].type;
2869 /* Skip tokens without meaning, like words separators
2870 * following a word separator or an end of command and
2871 * so on. */
2872 if (prevtype == JIM_TT_EOL) {
2873 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2874 Jim_Free(strtoken);
2875 return;
2876 }
2877 } else if (prevtype == JIM_TT_SEP) {
2878 if (type == JIM_TT_SEP) {
2879 Jim_Free(strtoken);
2880 return;
2881 } else if (type == JIM_TT_EOL) {
2882 /* If an EOL is following by a SEP, drop the previous
2883 * separator. */
2884 script->len--;
2885 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2886 }
2887 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2888 type == JIM_TT_ESC && len == 0)
2889 {
2890 /* Don't add empty tokens used in interpolation */
2891 Jim_Free(strtoken);
2892 return;
2893 }
2894 /* Make space for a new istruction */
2895 script->len++;
2896 script->token = Jim_Realloc(script->token,
2897 sizeof(ScriptToken)*script->len);
2898 /* Initialize the new token */
2899 token = script->token+(script->len-1);
2900 token->type = type;
2901 /* Every object is intially as a string, but the
2902 * internal type may be specialized during execution of the
2903 * script. */
2904 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2905 /* To add source info to SEP and EOL tokens is useless because
2906 * they will never by called as arguments of Jim_EvalObj(). */
2907 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2908 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2909 Jim_IncrRefCount(token->objPtr);
2910 token->linenr = linenr;
2911 }
2912
2913 /* Add an integer into the command structure field of the script object. */
2914 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2915 {
2916 script->csLen++;
2917 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2918 sizeof(int)*script->csLen);
2919 script->cmdStruct[script->csLen-1] = val;
2920 }
2921
2922 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2923 * of objPtr. Search nested script objects recursively. */
2924 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2925 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2926 {
2927 int i;
2928
2929 for (i = 0; i < script->len; i++) {
2930 if (script->token[i].objPtr != objPtr &&
2931 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2932 return script->token[i].objPtr;
2933 }
2934 /* Enter recursively on scripts only if the object
2935 * is not the same as the one we are searching for
2936 * shared occurrences. */
2937 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2938 script->token[i].objPtr != objPtr) {
2939 Jim_Obj *foundObjPtr;
2940
2941 ScriptObj *subScript =
2942 script->token[i].objPtr->internalRep.ptr;
2943 /* Don't recursively enter the script we are trying
2944 * to make shared to avoid circular references. */
2945 if (subScript == scriptBarrier) continue;
2946 if (subScript != script) {
2947 foundObjPtr =
2948 ScriptSearchLiteral(interp, subScript,
2949 scriptBarrier, objPtr);
2950 if (foundObjPtr != NULL)
2951 return foundObjPtr;
2952 }
2953 }
2954 }
2955 return NULL;
2956 }
2957
2958 /* Share literals of a script recursively sharing sub-scripts literals. */
2959 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2960 ScriptObj *topLevelScript)
2961 {
2962 int i, j;
2963
2964 return;
2965 /* Try to share with toplevel object. */
2966 if (topLevelScript != NULL) {
2967 for (i = 0; i < script->len; i++) {
2968 Jim_Obj *foundObjPtr;
2969 char *str = script->token[i].objPtr->bytes;
2970
2971 if (script->token[i].objPtr->refCount != 1) continue;
2972 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2973 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2974 foundObjPtr = ScriptSearchLiteral(interp,
2975 topLevelScript,
2976 script, /* barrier */
2977 script->token[i].objPtr);
2978 if (foundObjPtr != NULL) {
2979 Jim_IncrRefCount(foundObjPtr);
2980 Jim_DecrRefCount(interp,
2981 script->token[i].objPtr);
2982 script->token[i].objPtr = foundObjPtr;
2983 }
2984 }
2985 }
2986 /* Try to share locally */
2987 for (i = 0; i < script->len; i++) {
2988 char *str = script->token[i].objPtr->bytes;
2989
2990 if (script->token[i].objPtr->refCount != 1) continue;
2991 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2992 for (j = 0; j < script->len; j++) {
2993 if (script->token[i].objPtr !=
2994 script->token[j].objPtr &&
2995 Jim_StringEqObj(script->token[i].objPtr,
2996 script->token[j].objPtr, 0))
2997 {
2998 Jim_IncrRefCount(script->token[j].objPtr);
2999 Jim_DecrRefCount(interp,
3000 script->token[i].objPtr);
3001 script->token[i].objPtr =
3002 script->token[j].objPtr;
3003 }
3004 }
3005 }
3006 }
3007
3008 /* This method takes the string representation of an object
3009 * as a Tcl script, and generates the pre-parsed internal representation
3010 * of the script. */
3011 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3012 {
3013 int scriptTextLen;
3014 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
3015 struct JimParserCtx parser;
3016 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
3017 ScriptToken *token;
3018 int args, tokens, start, end, i;
3019 int initialLineNumber;
3020 int propagateSourceInfo = 0;
3021
3022 script->len = 0;
3023 script->csLen = 0;
3024 script->commands = 0;
3025 script->token = NULL;
3026 script->cmdStruct = NULL;
3027 script->inUse = 1;
3028 /* Try to get information about filename / line number */
3029 if (objPtr->typePtr == &sourceObjType) {
3030 script->fileName =
3031 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
3032 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
3033 propagateSourceInfo = 1;
3034 } else {
3035 script->fileName = Jim_StrDup("");
3036 initialLineNumber = 1;
3037 }
3038
3039 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
3040 while(!JimParserEof(&parser)) {
3041 char *token;
3042 int len, type, linenr;
3043
3044 JimParseScript(&parser);
3045 token = JimParserGetToken(&parser, &len, &type, &linenr);
3046 ScriptObjAddToken(interp, script, token, len, type,
3047 propagateSourceInfo ? script->fileName : NULL,
3048 linenr);
3049 }
3050 token = script->token;
3051
3052 /* Compute the command structure array
3053 * (see the ScriptObj struct definition for more info) */
3054 start = 0; /* Current command start token index */
3055 end = -1; /* Current command end token index */
3056 while (1) {
3057 int expand = 0; /* expand flag. set to 1 on {expand} form. */
3058 int interpolation = 0; /* set to 1 if there is at least one
3059 argument of the command obtained via
3060 interpolation of more tokens. */
3061 /* Search for the end of command, while
3062 * count the number of args. */
3063 start = ++end;
3064 if (start >= script->len) break;
3065 args = 1; /* Number of args in current command */
3066 while (token[end].type != JIM_TT_EOL) {
3067 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
3068 token[end-1].type == JIM_TT_EOL)
3069 {
3070 if (token[end].type == JIM_TT_STR &&
3071 token[end+1].type != JIM_TT_SEP &&
3072 token[end+1].type != JIM_TT_EOL &&
3073 (!strcmp(token[end].objPtr->bytes, "expand") ||
3074 !strcmp(token[end].objPtr->bytes, "*")))
3075 expand++;
3076 }
3077 if (token[end].type == JIM_TT_SEP)
3078 args++;
3079 end++;
3080 }
3081 interpolation = !((end-start+1) == args*2);
3082 /* Add the 'number of arguments' info into cmdstruct.
3083 * Negative value if there is list expansion involved. */
3084 if (expand)
3085 ScriptObjAddInt(script, -1);
3086 ScriptObjAddInt(script, args);
3087 /* Now add info about the number of tokens. */
3088 tokens = 0; /* Number of tokens in current argument. */
3089 expand = 0;
3090 for (i = start; i <= end; i++) {
3091 if (token[i].type == JIM_TT_SEP ||
3092 token[i].type == JIM_TT_EOL)
3093 {
3094 if (tokens == 1 && expand)
3095 expand = 0;
3096 ScriptObjAddInt(script,
3097 expand ? -tokens : tokens);
3098
3099 expand = 0;
3100 tokens = 0;
3101 continue;
3102 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
3103 (!strcmp(token[i].objPtr->bytes, "expand") ||
3104 !strcmp(token[i].objPtr->bytes, "*")))
3105 {
3106 expand++;
3107 }
3108 tokens++;
3109 }
3110 }
3111 /* Perform literal sharing, but only for objects that appear
3112 * to be scripts written as literals inside the source code,
3113 * and not computed at runtime. Literal sharing is a costly
3114 * operation that should be done only against objects that
3115 * are likely to require compilation only the first time, and
3116 * then are executed multiple times. */
3117 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
3118 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
3119 if (bodyObjPtr->typePtr == &scriptObjType) {
3120 ScriptObj *bodyScript =
3121 bodyObjPtr->internalRep.ptr;
3122 ScriptShareLiterals(interp, script, bodyScript);
3123 }
3124 } else if (propagateSourceInfo) {
3125 ScriptShareLiterals(interp, script, NULL);
3126 }
3127 /* Free the old internal rep and set the new one. */
3128 Jim_FreeIntRep(interp, objPtr);
3129 Jim_SetIntRepPtr(objPtr, script);
3130 objPtr->typePtr = &scriptObjType;
3131 return JIM_OK;
3132 }
3133
3134 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
3135 {
3136 if (objPtr->typePtr != &scriptObjType) {
3137 SetScriptFromAny(interp, objPtr);
3138 }
3139 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
3140 }
3141
3142 /* -----------------------------------------------------------------------------
3143 * Commands
3144 * ---------------------------------------------------------------------------*/
3145
3146 /* Commands HashTable Type.
3147 *
3148 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
3149 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
3150 {
3151 Jim_Cmd *cmdPtr = (void*) val;
3152
3153 if (cmdPtr->cmdProc == NULL) {
3154 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3155 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3156 if (cmdPtr->staticVars) {
3157 Jim_FreeHashTable(cmdPtr->staticVars);
3158 Jim_Free(cmdPtr->staticVars);
3159 }
3160 } else if (cmdPtr->delProc != NULL) {
3161 /* If it was a C coded command, call the delProc if any */
3162 cmdPtr->delProc(interp, cmdPtr->privData);
3163 }
3164 Jim_Free(val);
3165 }
3166
3167 static Jim_HashTableType JimCommandsHashTableType = {
3168 JimStringCopyHTHashFunction, /* hash function */
3169 JimStringCopyHTKeyDup, /* key dup */
3170 NULL, /* val dup */
3171 JimStringCopyHTKeyCompare, /* key compare */
3172 JimStringCopyHTKeyDestructor, /* key destructor */
3173 Jim_CommandsHT_ValDestructor /* val destructor */
3174 };
3175
3176 /* ------------------------- Commands related functions --------------------- */
3177
3178 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3179 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3180 {
3181 Jim_HashEntry *he;
3182 Jim_Cmd *cmdPtr;
3183
3184 he = Jim_FindHashEntry(&interp->commands, cmdName);
3185 if (he == NULL) { /* New command to create */
3186 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3187 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3188 } else {
3189 Jim_InterpIncrProcEpoch(interp);
3190 /* Free the arglist/body objects if it was a Tcl procedure */
3191 cmdPtr = he->val;
3192 if (cmdPtr->cmdProc == NULL) {
3193 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3194 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3195 if (cmdPtr->staticVars) {
3196 Jim_FreeHashTable(cmdPtr->staticVars);
3197 Jim_Free(cmdPtr->staticVars);
3198 }
3199 cmdPtr->staticVars = NULL;
3200 } else if (cmdPtr->delProc != NULL) {
3201 /* If it was a C coded command, call the delProc if any */
3202 cmdPtr->delProc(interp, cmdPtr->privData);
3203 }
3204 }
3205
3206 /* Store the new details for this proc */
3207 cmdPtr->delProc = delProc;
3208 cmdPtr->cmdProc = cmdProc;
3209 cmdPtr->privData = privData;
3210
3211 /* There is no need to increment the 'proc epoch' because
3212 * creation of a new procedure can never affect existing
3213 * cached commands. We don't do negative caching. */
3214 return JIM_OK;
3215 }
3216
3217 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3218 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3219 int arityMin, int arityMax)
3220 {
3221 Jim_Cmd *cmdPtr;
3222
3223 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3224 cmdPtr->cmdProc = NULL; /* Not a C coded command */
3225 cmdPtr->argListObjPtr = argListObjPtr;
3226 cmdPtr->bodyObjPtr = bodyObjPtr;
3227 Jim_IncrRefCount(argListObjPtr);
3228 Jim_IncrRefCount(bodyObjPtr);
3229 cmdPtr->arityMin = arityMin;
3230 cmdPtr->arityMax = arityMax;
3231 cmdPtr->staticVars = NULL;
3232
3233 /* Create the statics hash table. */
3234 if (staticsListObjPtr) {
3235 int len, i;
3236
3237 Jim_ListLength(interp, staticsListObjPtr, &len);
3238 if (len != 0) {
3239 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3240 Jim_InitHashTable(cmdPtr->staticVars, getJimVariablesHashTableType(),
3241 interp);
3242 for (i = 0; i < len; i++) {
3243 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3244 Jim_Var *varPtr;
3245 int subLen;
3246
3247 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3248 /* Check if it's composed of two elements. */
3249 Jim_ListLength(interp, objPtr, &subLen);
3250 if (subLen == 1 || subLen == 2) {
3251 /* Try to get the variable value from the current
3252 * environment. */
3253 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3254 if (subLen == 1) {
3255 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3256 JIM_NONE);
3257 if (initObjPtr == NULL) {
3258 Jim_SetResult(interp,
3259 Jim_NewEmptyStringObj(interp));
3260 Jim_AppendStrings(interp, Jim_GetResult(interp),
3261 "variable for initialization of static \"",
3262 Jim_GetString(nameObjPtr, NULL),
3263 "\" not found in the local context",
3264 NULL);
3265 goto err;
3266 }
3267 } else {
3268 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3269 }
3270 varPtr = Jim_Alloc(sizeof(*varPtr));
3271 varPtr->objPtr = initObjPtr;
3272 Jim_IncrRefCount(initObjPtr);
3273 varPtr->linkFramePtr = NULL;
3274 if (Jim_AddHashEntry(cmdPtr->staticVars,
3275 Jim_GetString(nameObjPtr, NULL),
3276 varPtr) != JIM_OK)
3277 {
3278 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3279 Jim_AppendStrings(interp, Jim_GetResult(interp),
3280 "static variable name \"",
3281 Jim_GetString(objPtr, NULL), "\"",
3282 " duplicated in statics list", NULL);
3283 Jim_DecrRefCount(interp, initObjPtr);
3284 Jim_Free(varPtr);
3285 goto err;
3286 }
3287 } else {
3288 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3289 Jim_AppendStrings(interp, Jim_GetResult(interp),
3290 "too many fields in static specifier \"",
3291 objPtr, "\"", NULL);
3292 goto err;
3293 }
3294 }
3295 }
3296 }
3297
3298 /* Add the new command */
3299
3300 /* it may already exist, so we try to delete the old one */
3301 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3302 /* There was an old procedure with the same name, this requires
3303 * a 'proc epoch' update. */
3304 Jim_InterpIncrProcEpoch(interp);
3305 }
3306 /* If a procedure with the same name didn't existed there is no need
3307 * to increment the 'proc epoch' because creation of a new procedure
3308 * can never affect existing cached commands. We don't do
3309 * negative caching. */
3310 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3311 return JIM_OK;
3312
3313 err:
3314 Jim_FreeHashTable(cmdPtr->staticVars);
3315 Jim_Free(cmdPtr->staticVars);
3316 Jim_DecrRefCount(interp, argListObjPtr);
3317 Jim_DecrRefCount(interp, bodyObjPtr);
3318 Jim_Free(cmdPtr);
3319 return JIM_ERR;
3320 }
3321
3322 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3323 {
3324 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3325 return JIM_ERR;
3326 Jim_InterpIncrProcEpoch(interp);
3327 return JIM_OK;
3328 }
3329
3330 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
3331 const char *newName)
3332 {
3333 Jim_Cmd *cmdPtr;
3334 Jim_HashEntry *he;
3335 Jim_Cmd *copyCmdPtr;
3336
3337 if (newName[0] == '\0') /* Delete! */
3338 return Jim_DeleteCommand(interp, oldName);
3339 /* Rename */
3340 he = Jim_FindHashEntry(&interp->commands, oldName);
3341 if (he == NULL)
3342 return JIM_ERR; /* Invalid command name */
3343 cmdPtr = he->val;
3344 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3345 *copyCmdPtr = *cmdPtr;
3346 /* In order to avoid that a procedure will get arglist/body/statics
3347 * freed by the hash table methods, fake a C-coded command
3348 * setting cmdPtr->cmdProc as not NULL */
3349 cmdPtr->cmdProc = (void*)1;
3350 /* Also make sure delProc is NULL. */
3351 cmdPtr->delProc = NULL;
3352 /* Destroy the old command, and make sure the new is freed
3353 * as well. */
3354 Jim_DeleteHashEntry(&interp->commands, oldName);
3355 Jim_DeleteHashEntry(&interp->commands, newName);
3356 /* Now the new command. We are sure it can't fail because
3357 * the target name was already freed. */
3358 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3359 /* Increment the epoch */
3360 Jim_InterpIncrProcEpoch(interp);
3361 return JIM_OK;
3362 }
3363
3364 /* -----------------------------------------------------------------------------
3365 * Command object
3366 * ---------------------------------------------------------------------------*/
3367
3368 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3369
3370 static Jim_ObjType commandObjType = {
3371 "command",
3372 NULL,
3373 NULL,
3374 NULL,
3375 JIM_TYPE_REFERENCES,
3376 };
3377
3378 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3379 {
3380 Jim_HashEntry *he;
3381 const char *cmdName;
3382
3383 /* Get the string representation */
3384 cmdName = Jim_GetString(objPtr, NULL);
3385 /* Lookup this name into the commands hash table */
3386 he = Jim_FindHashEntry(&interp->commands, cmdName);
3387 if (he == NULL)
3388 return JIM_ERR;
3389
3390 /* Free the old internal repr and set the new one. */
3391 Jim_FreeIntRep(interp, objPtr);
3392 objPtr->typePtr = &commandObjType;
3393 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3394 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3395 return JIM_OK;
3396 }
3397
3398 /* This function returns the command structure for the command name
3399 * stored in objPtr. It tries to specialize the objPtr to contain
3400 * a cached info instead to perform the lookup into the hash table
3401 * every time. The information cached may not be uptodate, in such
3402 * a case the lookup is performed and the cache updated. */
3403 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3404 {
3405 if ((objPtr->typePtr != &commandObjType ||
3406 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3407 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3408 if (flags & JIM_ERRMSG) {
3409 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3410 Jim_AppendStrings(interp, Jim_GetResult(interp),
3411 "invalid command name \"", objPtr->bytes, "\"",
3412 NULL);
3413 }
3414 return NULL;
3415 }
3416 return objPtr->internalRep.cmdValue.cmdPtr;
3417 }
3418
3419 /* -----------------------------------------------------------------------------
3420 * Variables
3421 * ---------------------------------------------------------------------------*/
3422
3423 /* Variables HashTable Type.
3424 *
3425 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3426 static void JimVariablesHTValDestructor(void *interp, void *val)
3427 {
3428 Jim_Var *varPtr = (void*) val;
3429
3430 Jim_DecrRefCount(interp, varPtr->objPtr);
3431 Jim_Free(val);
3432 }
3433
3434 static Jim_HashTableType JimVariablesHashTableType = {
3435 JimStringCopyHTHashFunction, /* hash function */
3436 JimStringCopyHTKeyDup, /* key dup */
3437 NULL, /* val dup */
3438 JimStringCopyHTKeyCompare, /* key compare */
3439 JimStringCopyHTKeyDestructor, /* key destructor */
3440 JimVariablesHTValDestructor /* val destructor */
3441 };
3442
3443 static Jim_HashTableType *getJimVariablesHashTableType(void)
3444 {
3445 return &JimVariablesHashTableType;
3446 }
3447
3448 /* -----------------------------------------------------------------------------
3449 * Variable object
3450 * ---------------------------------------------------------------------------*/
3451
3452 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3453
3454 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3455
3456 static Jim_ObjType variableObjType = {
3457 "variable",
3458 NULL,
3459 NULL,
3460 NULL,
3461 JIM_TYPE_REFERENCES,
3462 };
3463
3464 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3465 * is in the form "varname(key)". */
3466 static int Jim_NameIsDictSugar(const char *str, int len)
3467 {
3468 if (len == -1)
3469 len = strlen(str);
3470 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3471 return 1;
3472 return 0;
3473 }
3474
3475 /* This method should be called only by the variable API.
3476 * It returns JIM_OK on success (variable already exists),
3477 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3478 * a variable name, but syntax glue for [dict] i.e. the last
3479 * character is ')' */
3480 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3481 {
3482 Jim_HashEntry *he;
3483 const char *varName;
3484 int len;
3485
3486 /* Check if the object is already an uptodate variable */
3487 if (objPtr->typePtr == &variableObjType &&
3488 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3489 return JIM_OK; /* nothing to do */
3490 /* Get the string representation */
3491 varName = Jim_GetString(objPtr, &len);
3492 /* Make sure it's not syntax glue to get/set dict. */
3493 if (Jim_NameIsDictSugar(varName, len))
3494 return JIM_DICT_SUGAR;
3495 if (varName[0] == ':' && varName[1] == ':') {
3496 he = Jim_FindHashEntry(&interp->topFramePtr->vars, varName + 2);
3497 if (he == NULL) {
3498 return JIM_ERR;
3499 }
3500 }
3501 else {
3502 /* Lookup this name into the variables hash table */
3503 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3504 if (he == NULL) {
3505 /* Try with static vars. */
3506 if (interp->framePtr->staticVars == NULL)
3507 return JIM_ERR;
3508 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3509 return JIM_ERR;
3510 }
3511 }
3512 /* Free the old internal repr and set the new one. */
3513 Jim_FreeIntRep(interp, objPtr);
3514 objPtr->typePtr = &variableObjType;
3515 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3516 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3517 return JIM_OK;
3518 }
3519
3520 /* -------------------- Variables related functions ------------------------- */
3521 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3522 Jim_Obj *valObjPtr);
3523 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3524
3525 /* For now that's dummy. Variables lookup should be optimized
3526 * in many ways, with caching of lookups, and possibly with
3527 * a table of pre-allocated vars in every CallFrame for local vars.
3528 * All the caching should also have an 'epoch' mechanism similar
3529 * to the one used by Tcl for procedures lookup caching. */
3530
3531 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3532 {
3533 const char *name;
3534 Jim_Var *var;
3535 int err;
3536
3537 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3538 /* Check for [dict] syntax sugar. */
3539 if (err == JIM_DICT_SUGAR)
3540 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3541 /* New variable to create */
3542 name = Jim_GetString(nameObjPtr, NULL);
3543
3544 var = Jim_Alloc(sizeof(*var));
3545 var->objPtr = valObjPtr;
3546 Jim_IncrRefCount(valObjPtr);
3547 var->linkFramePtr = NULL;
3548 /* Insert the new variable */
3549 if (name[0] == ':' && name[1] == ':') {
3550 /* Into to the top evel frame */
3551 Jim_AddHashEntry(&interp->topFramePtr->vars, name + 2, var);
3552 }
3553 else {
3554 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3555 }
3556 /* Make the object int rep a variable */
3557 Jim_FreeIntRep(interp, nameObjPtr);
3558 nameObjPtr->typePtr = &variableObjType;
3559 nameObjPtr->internalRep.varValue.callFrameId =
3560 interp->framePtr->id;
3561 nameObjPtr->internalRep.varValue.varPtr = var;
3562 } else {
3563 var = nameObjPtr->internalRep.varValue.varPtr;
3564 if (var->linkFramePtr == NULL) {
3565 Jim_IncrRefCount(valObjPtr);
3566 Jim_DecrRefCount(interp, var->objPtr);
3567 var->objPtr = valObjPtr;
3568 } else { /* Else handle the link */
3569 Jim_CallFrame *savedCallFrame;
3570
3571 savedCallFrame = interp->framePtr;
3572 interp->framePtr = var->linkFramePtr;
3573 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3574 interp->framePtr = savedCallFrame;
3575 if (err != JIM_OK)
3576 return err;
3577 }
3578 }
3579 return JIM_OK;
3580 }
3581
3582 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3583 {
3584 Jim_Obj *nameObjPtr;
3585 int result;
3586
3587 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3588 Jim_IncrRefCount(nameObjPtr);
3589 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3590 Jim_DecrRefCount(interp, nameObjPtr);
3591 return result;
3592 }
3593
3594 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3595 {
3596 Jim_CallFrame *savedFramePtr;
3597 int result;
3598
3599 savedFramePtr = interp->framePtr;
3600 interp->framePtr = interp->topFramePtr;
3601 result = Jim_SetVariableStr(interp, name, objPtr);
3602 interp->framePtr = savedFramePtr;
3603 return result;
3604 }
3605
3606 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3607 {
3608 Jim_Obj *nameObjPtr, *valObjPtr;
3609 int result;
3610
3611 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3612 valObjPtr = Jim_NewStringObj(interp, val, -1);
3613 Jim_IncrRefCount(nameObjPtr);
3614 Jim_IncrRefCount(valObjPtr);
3615 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3616 Jim_DecrRefCount(interp, nameObjPtr);
3617 Jim_DecrRefCount(interp, valObjPtr);
3618 return result;
3619 }
3620
3621 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3622 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3623 {
3624 const char *varName;
3625 int len;
3626
3627 /* Check for cycles. */
3628 if (interp->framePtr == targetCallFrame) {
3629 Jim_Obj *objPtr = targetNameObjPtr;
3630 Jim_Var *varPtr;
3631 /* Cycles are only possible with 'uplevel 0' */
3632 while(1) {
3633 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3634 Jim_SetResultString(interp,
3635 "can't upvar from variable to itself", -1);
3636 return JIM_ERR;
3637 }
3638 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3639 break;
3640 varPtr = objPtr->internalRep.varValue.varPtr;
3641 if (varPtr->linkFramePtr != targetCallFrame) break;
3642 objPtr = varPtr->objPtr;
3643 }
3644 }
3645 varName = Jim_GetString(nameObjPtr, &len);
3646 if (Jim_NameIsDictSugar(varName, len)) {
3647 Jim_SetResultString(interp,
3648 "Dict key syntax invalid as link source", -1);
3649 return JIM_ERR;
3650 }
3651 /* Perform the binding */
3652 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3653 /* We are now sure 'nameObjPtr' type is variableObjType */
3654 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3655 return JIM_OK;
3656 }
3657
3658 /* Return the Jim_Obj pointer associated with a variable name,
3659 * or NULL if the variable was not found in the current context.
3660 * The same optimization discussed in the comment to the
3661 * 'SetVariable' function should apply here. */
3662 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3663 {
3664 int err;
3665
3666 /* All the rest is handled here */
3667 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3668 /* Check for [dict] syntax sugar. */
3669 if (err == JIM_DICT_SUGAR)
3670 return JimDictSugarGet(interp, nameObjPtr);
3671 if (flags & JIM_ERRMSG) {
3672 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3673 Jim_AppendStrings(interp, Jim_GetResult(interp),
3674 "can't read \"", nameObjPtr->bytes,
3675 "\": no such variable", NULL);
3676 }
3677 return NULL;
3678 } else {
3679 Jim_Var *varPtr;
3680 Jim_Obj *objPtr;
3681 Jim_CallFrame *savedCallFrame;
3682
3683 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3684 if (varPtr->linkFramePtr == NULL)
3685 return varPtr->objPtr;
3686 /* The variable is a link? Resolve it. */
3687 savedCallFrame = interp->framePtr;
3688 interp->framePtr = varPtr->linkFramePtr;
3689 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3690 if (objPtr == NULL && flags & JIM_ERRMSG) {
3691 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3692 Jim_AppendStrings(interp, Jim_GetResult(interp),
3693 "can't read \"", nameObjPtr->bytes,
3694 "\": no such variable", NULL);
3695 }
3696 interp->framePtr = savedCallFrame;
3697 return objPtr;
3698 }
3699 }
3700
3701 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3702 int flags)
3703 {
3704 Jim_CallFrame *savedFramePtr;
3705 Jim_Obj *objPtr;
3706
3707 savedFramePtr = interp->framePtr;
3708 interp->framePtr = interp->topFramePtr;
3709 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3710 interp->framePtr = savedFramePtr;
3711
3712 return objPtr;
3713 }
3714
3715 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3716 {
3717 Jim_Obj *nameObjPtr, *varObjPtr;
3718
3719 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3720 Jim_IncrRefCount(nameObjPtr);
3721 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3722 Jim_DecrRefCount(interp, nameObjPtr);
3723 return varObjPtr;
3724 }
3725
3726 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3727 int flags)
3728 {
3729 Jim_CallFrame *savedFramePtr;
3730 Jim_Obj *objPtr;
3731
3732 savedFramePtr = interp->framePtr;
3733 interp->framePtr = interp->topFramePtr;
3734 objPtr = Jim_GetVariableStr(interp, name, flags);
3735 interp->framePtr = savedFramePtr;
3736
3737 return objPtr;
3738 }
3739
3740 /* Unset a variable.
3741 * Note: On success unset invalidates all the variable objects created
3742 * in the current call frame incrementing. */
3743 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3744 {
3745 const char *name;
3746 Jim_Var *varPtr;
3747 int err;
3748
3749 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3750 /* Check for [dict] syntax sugar. */
3751 if (err == JIM_DICT_SUGAR)
3752 return JimDictSugarSet(interp, nameObjPtr, NULL);
3753 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3754 Jim_AppendStrings(interp, Jim_GetResult(interp),
3755 "can't unset \"", nameObjPtr->bytes,
3756 "\": no such variable", NULL);
3757 return JIM_ERR; /* var not found */
3758 }
3759 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3760 /* If it's a link call UnsetVariable recursively */
3761 if (varPtr->linkFramePtr) {
3762 int retval;
3763
3764 Jim_CallFrame *savedCallFrame;
3765
3766 savedCallFrame = interp->framePtr;
3767 interp->framePtr = varPtr->linkFramePtr;
3768 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3769 interp->framePtr = savedCallFrame;
3770 if (retval != JIM_OK && flags & JIM_ERRMSG) {
3771 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3772 Jim_AppendStrings(interp, Jim_GetResult(interp),
3773 "can't unset \"", nameObjPtr->bytes,
3774 "\": no such variable", NULL);
3775 }
3776 return retval;
3777 } else {
3778 name = Jim_GetString(nameObjPtr, NULL);
3779 if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3780 != JIM_OK) return JIM_ERR;
3781 /* Change the callframe id, invalidating var lookup caching */
3782 JimChangeCallFrameId(interp, interp->framePtr);
3783 return JIM_OK;
3784 }
3785 }
3786
3787 /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
3788
3789 /* Given a variable name for [dict] operation syntax sugar,
3790 * this function returns two objects, the first with the name
3791 * of the variable to set, and the second with the rispective key.
3792 * For example "foo(bar)" will return objects with string repr. of
3793 * "foo" and "bar".
3794 *
3795 * The returned objects have refcount = 1. The function can't fail. */
3796 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3797 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3798 {
3799 const char *str, *p;
3800 char *t;
3801 int len, keyLen, nameLen;
3802 Jim_Obj *varObjPtr, *keyObjPtr;
3803
3804 str = Jim_GetString(objPtr, &len);
3805 p = strchr(str, '(');
3806 p++;
3807 keyLen = len-((p-str)+1);
3808 nameLen = (p-str)-1;
3809 /* Create the objects with the variable name and key. */
3810 t = Jim_Alloc(nameLen+1);
3811 memcpy(t, str, nameLen);
3812 t[nameLen] = '\0';
3813 varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3814
3815 t = Jim_Alloc(keyLen+1);
3816 memcpy(t, p, keyLen);
3817 t[keyLen] = '\0';
3818 keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3819
3820 Jim_IncrRefCount(varObjPtr);
3821 Jim_IncrRefCount(keyObjPtr);
3822 *varPtrPtr = varObjPtr;
3823 *keyPtrPtr = keyObjPtr;
3824 }
3825
3826 /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3827 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3828 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3829 Jim_Obj *valObjPtr)
3830 {
3831 Jim_Obj *varObjPtr, *keyObjPtr;
3832 int err = JIM_OK;
3833
3834 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3835 err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3836 valObjPtr);
3837 Jim_DecrRefCount(interp, varObjPtr);
3838 Jim_DecrRefCount(interp, keyObjPtr);
3839 return err;
3840 }
3841
3842 /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3843 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3844 {
3845 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3846
3847 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3848 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3849 if (!dictObjPtr) {
3850 resObjPtr = NULL;
3851 goto err;
3852 }
3853 if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3854 != JIM_OK) {
3855 resObjPtr = NULL;
3856 }
3857 err:
3858 Jim_DecrRefCount(interp, varObjPtr);
3859 Jim_DecrRefCount(interp, keyObjPtr);
3860 return resObjPtr;
3861 }
3862
3863 /* --------- $var(INDEX) substitution, using a specialized object ----------- */
3864
3865 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3866 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3867 Jim_Obj *dupPtr);
3868
3869 static Jim_ObjType dictSubstObjType = {
3870 "dict-substitution",
3871 FreeDictSubstInternalRep,
3872 DupDictSubstInternalRep,
3873 NULL,
3874 JIM_TYPE_NONE,
3875 };
3876
3877 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3878 {
3879 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3880 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3881 }
3882
3883 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3884 Jim_Obj *dupPtr)
3885 {
3886 JIM_NOTUSED(interp);
3887
3888 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3889 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3890 dupPtr->internalRep.dictSubstValue.indexObjPtr =
3891 srcPtr->internalRep.dictSubstValue.indexObjPtr;
3892 dupPtr->typePtr = &dictSubstObjType;
3893 }
3894
3895 /* This function is used to expand [dict get] sugar in the form
3896 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3897 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3898 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3899 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3900 * the [dict]ionary contained in variable VARNAME. */
3901 Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3902 {
3903 Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3904 Jim_Obj *substKeyObjPtr = NULL;
3905
3906 if (objPtr->typePtr != &dictSubstObjType) {
3907 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3908 Jim_FreeIntRep(interp, objPtr);
3909 objPtr->typePtr = &dictSubstObjType;
3910 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3911 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3912 }
3913 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3914 &substKeyObjPtr, JIM_NONE)
3915 != JIM_OK) {
3916 substKeyObjPtr = NULL;
3917 goto err;
3918 }
3919 Jim_IncrRefCount(substKeyObjPtr);
3920 dictObjPtr = Jim_GetVariable(interp,
3921 objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3922 if (!dictObjPtr) {
3923 resObjPtr = NULL;
3924 goto err;
3925 }
3926 if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3927 != JIM_OK) {
3928 resObjPtr = NULL;
3929 goto err;
3930 }
3931 err:
3932 if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3933 return resObjPtr;
3934 }
3935
3936 /* -----------------------------------------------------------------------------
3937 * CallFrame
3938 * ---------------------------------------------------------------------------*/
3939
3940 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3941 {
3942 Jim_CallFrame *cf;
3943 if (interp->freeFramesList) {
3944 cf = interp->freeFramesList;
3945 interp->freeFramesList = cf->nextFramePtr;
3946 } else {
3947 cf = Jim_Alloc(sizeof(*cf));
3948 cf->vars.table = NULL;
3949 }
3950
3951 cf->id = interp->callFrameEpoch++;
3952 cf->parentCallFrame = NULL;
3953 cf->argv = NULL;
3954 cf->argc = 0;
3955 cf->procArgsObjPtr = NULL;
3956 cf->procBodyObjPtr = NULL;
3957 cf->nextFramePtr = NULL;
3958 cf->staticVars = NULL;
3959 if (cf->vars.table == NULL)
3960 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3961 return cf;
3962 }
3963
3964 /* Used to invalidate every caching related to callframe stability. */
3965 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3966 {
3967 cf->id = interp->callFrameEpoch++;
3968 }
3969
3970 #define JIM_FCF_NONE 0 /* no flags */
3971 #define JIM_FCF_NOHT 1 /* don't free the hash table */
3972 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3973 int flags)
3974 {
3975 if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3976 if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3977 if (!(flags & JIM_FCF_NOHT))
3978 Jim_FreeHashTable(&cf->vars);
3979 else {
3980 int i;
3981 Jim_HashEntry **table = cf->vars.table, *he;
3982
3983 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3984 he = table[i];
3985 while (he != NULL) {
3986 Jim_HashEntry *nextEntry = he->next;
3987 Jim_Var *varPtr = (void*) he->val;
3988
3989 Jim_DecrRefCount(interp, varPtr->objPtr);
3990 Jim_Free(he->val);
3991 Jim_Free((void*)he->key); /* ATTENTION: const cast */
3992 Jim_Free(he);
3993 table[i] = NULL;
3994 he = nextEntry;
3995 }
3996 }
3997 cf->vars.used = 0;
3998 }
3999 cf->nextFramePtr = interp->freeFramesList;
4000 interp->freeFramesList = cf;
4001 }
4002
4003 /* -----------------------------------------------------------------------------
4004 * References
4005 * ---------------------------------------------------------------------------*/
4006
4007 /* References HashTable Type.
4008 *
4009 * Keys are jim_wide integers, dynamically allocated for now but in the
4010 * future it's worth to cache this 8 bytes objects. Values are poitners
4011 * to Jim_References. */
4012 static void JimReferencesHTValDestructor(void *interp, void *val)
4013 {
4014 Jim_Reference *refPtr = (void*) val;
4015
4016 Jim_DecrRefCount(interp, refPtr->objPtr);
4017 if (refPtr->finalizerCmdNamePtr != NULL) {
4018 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4019 }
4020 Jim_Free(val);
4021 }
4022
4023 unsigned int JimReferencesHTHashFunction(const void *key)
4024 {
4025 /* Only the least significant bits are used. */
4026 const jim_wide *widePtr = key;
4027 unsigned int intValue = (unsigned int) *widePtr;
4028 return Jim_IntHashFunction(intValue);
4029 }
4030
4031 unsigned int JimReferencesHTDoubleHashFunction(const void *key)
4032 {
4033 /* Only the least significant bits are used. */
4034 const jim_wide *widePtr = key;
4035 unsigned int intValue = (unsigned int) *widePtr;
4036 return intValue; /* identity function. */
4037 }
4038
4039 const void *JimReferencesHTKeyDup(void *privdata, const void *key)
4040 {
4041 void *copy = Jim_Alloc(sizeof(jim_wide));
4042 JIM_NOTUSED(privdata);
4043
4044 memcpy(copy, key, sizeof(jim_wide));
4045 return copy;
4046 }
4047
4048 int JimReferencesHTKeyCompare(void *privdata, const void *key1,
4049 const void *key2)
4050 {
4051 JIM_NOTUSED(privdata);
4052
4053 return memcmp(key1, key2, sizeof(jim_wide)) == 0;
4054 }
4055
4056 void JimReferencesHTKeyDestructor(void *privdata, const void *key)
4057 {
4058 JIM_NOTUSED(privdata);
4059
4060 Jim_Free((void*)key);
4061 }
4062
4063 static Jim_HashTableType JimReferencesHashTableType = {
4064 JimReferencesHTHashFunction, /* hash function */
4065 JimReferencesHTKeyDup, /* key dup */
4066 NULL, /* val dup */
4067 JimReferencesHTKeyCompare, /* key compare */
4068 JimReferencesHTKeyDestructor, /* key destructor */
4069 JimReferencesHTValDestructor /* val destructor */
4070 };
4071
4072 /* -----------------------------------------------------------------------------
4073 * Reference object type and References API
4074 * ---------------------------------------------------------------------------*/
4075
4076 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
4077
4078 static Jim_ObjType referenceObjType = {
4079 "reference",
4080 NULL,
4081 NULL,
4082 UpdateStringOfReference,
4083 JIM_TYPE_REFERENCES,
4084 };
4085
4086 void UpdateStringOfReference(struct Jim_Obj *objPtr)
4087 {
4088 int len;
4089 char buf[JIM_REFERENCE_SPACE+1];
4090 Jim_Reference *refPtr;
4091
4092 refPtr = objPtr->internalRep.refValue.refPtr;
4093 len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
4094 objPtr->bytes = Jim_Alloc(len+1);
4095 memcpy(objPtr->bytes, buf, len+1);
4096 objPtr->length = len;
4097 }
4098
4099 /* returns true if 'c' is a valid reference tag character.
4100 * i.e. inside the range [_a-zA-Z0-9] */
4101 static int isrefchar(int c)
4102 {
4103 if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
4104 (c >= '0' && c <= '9')) return 1;
4105 return 0;
4106 }
4107
4108 int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4109 {
4110 jim_wide wideValue;
4111 int i, len;
4112 const char *str, *start, *end;
4113 char refId[21];
4114 Jim_Reference *refPtr;
4115 Jim_HashEntry *he;
4116
4117 /* Get the string representation */
4118 str = Jim_GetString(objPtr, &len);
4119 /* Check if it looks like a reference */
4120 if (len < JIM_REFERENCE_SPACE) goto badformat;
4121 /* Trim spaces */
4122 start = str;
4123 end = str+len-1;
4124 while (*start == ' ') start++;
4125 while (*end == ' ' && end > start) end--;
4126 if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
4127 /* <reference.<1234567>.%020> */
4128 if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
4129 if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
4130 /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
4131 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4132 if (!isrefchar(start[12+i])) goto badformat;
4133 }
4134 /* Extract info from the refernece. */
4135 memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
4136 refId[20] = '\0';
4137 /* Try to convert the ID into a jim_wide */
4138 if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
4139 /* Check if the reference really exists! */
4140 he = Jim_FindHashEntry(&interp->references, &wideValue);
4141 if (he == NULL) {
4142 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4143 Jim_AppendStrings(interp, Jim_GetResult(interp),
4144 "Invalid reference ID \"", str, "\"", NULL);
4145 return JIM_ERR;
4146 }
4147 refPtr = he->val;
4148 /* Free the old internal repr and set the new one. */
4149 Jim_FreeIntRep(interp, objPtr);
4150 objPtr->typePtr = &referenceObjType;
4151 objPtr->internalRep.refValue.id = wideValue;
4152 objPtr->internalRep.refValue.refPtr = refPtr;
4153 return JIM_OK;
4154
4155 badformat:
4156 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4157 Jim_AppendStrings(interp, Jim_GetResult(interp),
4158 "expected reference but got \"", str, "\"", NULL);
4159 return JIM_ERR;
4160 }
4161
4162 /* Returns a new reference pointing to objPtr, having cmdNamePtr
4163 * as finalizer command (or NULL if there is no finalizer).
4164 * The returned reference object has refcount = 0. */
4165 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
4166 Jim_Obj *cmdNamePtr)
4167 {
4168 struct Jim_Reference *refPtr;
4169 jim_wide wideValue = interp->referenceNextId;
4170 Jim_Obj *refObjPtr;
4171 const char *tag;
4172 int tagLen, i;
4173
4174 /* Perform the Garbage Collection if needed. */
4175 Jim_CollectIfNeeded(interp);
4176
4177 refPtr = Jim_Alloc(sizeof(*refPtr));
4178 refPtr->objPtr = objPtr;
4179 Jim_IncrRefCount(objPtr);
4180 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4181 if (cmdNamePtr)
4182 Jim_IncrRefCount(cmdNamePtr);
4183 Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
4184 refObjPtr = Jim_NewObj(interp);
4185 refObjPtr->typePtr = &referenceObjType;
4186 refObjPtr->bytes = NULL;
4187 refObjPtr->internalRep.refValue.id = interp->referenceNextId;
4188 refObjPtr->internalRep.refValue.refPtr = refPtr;
4189 interp->referenceNextId++;
4190 /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
4191 * that does not pass the 'isrefchar' test is replaced with '_' */
4192 tag = Jim_GetString(tagPtr, &tagLen);
4193 if (tagLen > JIM_REFERENCE_TAGLEN)
4194 tagLen = JIM_REFERENCE_TAGLEN;
4195 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
4196 if (i < tagLen)
4197 refPtr->tag[i] = tag[i];
4198 else
4199 refPtr->tag[i] = '_';
4200 }
4201 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
4202 return refObjPtr;
4203 }
4204
4205 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
4206 {
4207 if (objPtr->typePtr != &referenceObjType &&
4208 SetReferenceFromAny(interp, objPtr) == JIM_ERR)
4209 return NULL;
4210 return objPtr->internalRep.refValue.refPtr;
4211 }
4212
4213 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
4214 {
4215 Jim_Reference *refPtr;
4216
4217 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4218 return JIM_ERR;
4219 Jim_IncrRefCount(cmdNamePtr);
4220 if (refPtr->finalizerCmdNamePtr)
4221 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
4222 refPtr->finalizerCmdNamePtr = cmdNamePtr;
4223 return JIM_OK;
4224 }
4225
4226 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
4227 {
4228 Jim_Reference *refPtr;
4229
4230 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
4231 return JIM_ERR;
4232 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
4233 return JIM_OK;
4234 }
4235
4236 /* -----------------------------------------------------------------------------
4237 * References Garbage Collection
4238 * ---------------------------------------------------------------------------*/
4239
4240 /* This the hash table type for the "MARK" phase of the GC */
4241 static Jim_HashTableType JimRefMarkHashTableType = {
4242 JimReferencesHTHashFunction, /* hash function */
4243 JimReferencesHTKeyDup, /* key dup */
4244 NULL, /* val dup */
4245 JimReferencesHTKeyCompare, /* key compare */
4246 JimReferencesHTKeyDestructor, /* key destructor */
4247 NULL /* val destructor */
4248 };
4249
4250 /* #define JIM_DEBUG_GC 1 */
4251
4252 /* Performs the garbage collection. */
4253 int Jim_Collect(Jim_Interp *interp)
4254 {
4255 Jim_HashTable marks;
4256 Jim_HashTableIterator *htiter;
4257 Jim_HashEntry *he;
4258 Jim_Obj *objPtr;
4259 int collected = 0;
4260
4261 /* Avoid recursive calls */
4262 if (interp->lastCollectId == -1) {
4263 /* Jim_Collect() already running. Return just now. */
4264 return 0;
4265 }
4266 interp->lastCollectId = -1;
4267
4268 /* Mark all the references found into the 'mark' hash table.
4269 * The references are searched in every live object that
4270 * is of a type that can contain references. */
4271 Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
4272 objPtr = interp->liveList;
4273 while(objPtr) {
4274 if (objPtr->typePtr == NULL ||
4275 objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
4276 const char *str, *p;
4277 int len;
4278
4279 /* If the object is of type reference, to get the
4280 * Id is simple... */
4281 if (objPtr->typePtr == &referenceObjType) {
4282 Jim_AddHashEntry(&marks,
4283 &objPtr->internalRep.refValue.id, NULL);
4284 #ifdef JIM_DEBUG_GC
4285 Jim_fprintf(interp,interp->cookie_stdout,
4286 "MARK (reference): %d refcount: %d" JIM_NL,
4287 (int) objPtr->internalRep.refValue.id,
4288 objPtr->refCount);
4289 #endif
4290 objPtr = objPtr->nextObjPtr;
4291 continue;
4292 }
4293 /* Get the string repr of the object we want
4294 * to scan for references. */
4295 p = str = Jim_GetString(objPtr, &len);
4296 /* Skip objects too little to contain references. */
4297 if (len < JIM_REFERENCE_SPACE) {
4298 objPtr = objPtr->nextObjPtr;
4299 continue;
4300 }
4301 /* Extract references from the object string repr. */
4302 while(1) {
4303 int i;
4304 jim_wide id;
4305 char buf[21];
4306
4307 if ((p = strstr(p, "<reference.<")) == NULL)
4308 break;
4309 /* Check if it's a valid reference. */
4310 if (len-(p-str) < JIM_REFERENCE_SPACE) break;
4311 if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
4312 for (i = 21; i <= 40; i++)
4313 if (!isdigit((int)p[i]))
4314 break;
4315 /* Get the ID */
4316 memcpy(buf, p+21, 20);
4317 buf[20] = '\0';
4318 Jim_StringToWide(buf, &id, 10);
4319
4320 /* Ok, a reference for the given ID
4321 * was found. Mark it. */
4322 Jim_AddHashEntry(&marks, &id, NULL);
4323 #ifdef JIM_DEBUG_GC
4324 Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
4325 #endif
4326 p += JIM_REFERENCE_SPACE;
4327 }
4328 }
4329 objPtr = objPtr->nextObjPtr;
4330 }
4331
4332 /* Run the references hash table to destroy every reference that
4333 * is not referenced outside (not present in the mark HT). */
4334 htiter = Jim_GetHashTableIterator(&interp->references);
4335 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
4336 const jim_wide *refId;
4337 Jim_Reference *refPtr;
4338
4339 refId = he->key;
4340 /* Check if in the mark phase we encountered
4341 * this reference. */
4342 if (Jim_FindHashEntry(&marks, refId) == NULL) {
4343 #ifdef JIM_DEBUG_GC
4344 Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
4345 #endif
4346 collected++;
4347 /* Drop the reference, but call the
4348 * finalizer first if registered. */
4349 refPtr = he->val;
4350 if (refPtr->finalizerCmdNamePtr) {
4351 char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
4352 Jim_Obj *objv[3], *oldResult;
4353
4354 JimFormatReference(refstr, refPtr, *refId);
4355
4356 objv[0] = refPtr->finalizerCmdNamePtr;
4357 objv[1] = Jim_NewStringObjNoAlloc(interp,
4358 refstr, 32);
4359 objv[2] = refPtr->objPtr;
4360 Jim_IncrRefCount(objv[0]);
4361 Jim_IncrRefCount(objv[1]);
4362 Jim_IncrRefCount(objv[2]);
4363
4364 /* Drop the reference itself */
4365 Jim_DeleteHashEntry(&interp->references, refId);
4366
4367 /* Call the finalizer. Errors ignored. */
4368 oldResult = interp->result;
4369 Jim_IncrRefCount(oldResult);
4370 Jim_EvalObjVector(interp, 3, objv);
4371 Jim_SetResult(interp, oldResult);
4372 Jim_DecrRefCount(interp, oldResult);
4373
4374 Jim_DecrRefCount(interp, objv[0]);
4375 Jim_DecrRefCount(interp, objv[1]);
4376 Jim_DecrRefCount(interp, objv[2]);
4377 } else {
4378 Jim_DeleteHashEntry(&interp->references, refId);
4379 }
4380 }
4381 }
4382 Jim_FreeHashTableIterator(htiter);
4383 Jim_FreeHashTable(&marks);
4384 interp->lastCollectId = interp->referenceNextId;
4385 interp->lastCollectTime = time(NULL);
4386 return collected;
4387 }
4388
4389 #define JIM_COLLECT_ID_PERIOD 5000
4390 #define JIM_COLLECT_TIME_PERIOD 300
4391
4392 void Jim_CollectIfNeeded(Jim_Interp *interp)
4393 {
4394 jim_wide elapsedId;
4395 int elapsedTime;
4396
4397 elapsedId = interp->referenceNextId - interp->lastCollectId;
4398 elapsedTime = time(NULL) - interp->lastCollectTime;
4399
4400
4401 if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4402 elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4403 Jim_Collect(interp);
4404 }
4405 }
4406
4407 /* -----------------------------------------------------------------------------
4408 * Interpreter related functions
4409 * ---------------------------------------------------------------------------*/
4410
4411 Jim_Interp *Jim_CreateInterp(void)
4412 {
4413 Jim_Interp *i = Jim_Alloc(sizeof(*i));
4414 Jim_Obj *pathPtr;
4415
4416 i->errorLine = 0;
4417 i->errorFileName = Jim_StrDup("");
4418 i->numLevels = 0;
4419 i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4420 i->returnCode = JIM_OK;
4421 i->exitCode = 0;
4422 i->procEpoch = 0;
4423 i->callFrameEpoch = 0;
4424 i->liveList = i->freeList = NULL;
4425 i->scriptFileName = Jim_StrDup("");
4426 i->referenceNextId = 0;
4427 i->lastCollectId = 0;
4428 i->lastCollectTime = time(NULL);
4429 i->freeFramesList = NULL;
4430 i->prngState = NULL;
4431 i->evalRetcodeLevel = -1;
4432 i->cookie_stdin = stdin;
4433 i->cookie_stdout = stdout;
4434 i->cookie_stderr = stderr;
4435 i->cb_fwrite = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite));
4436 i->cb_fread = ((size_t (*)( void *, size_t, size_t, void *))(fread));
4437 i->cb_vfprintf = ((int (*)( void *, const char *fmt, va_list ))(vfprintf));
4438 i->cb_fflush = ((int (*)( void *))(fflush));
4439 i->cb_fgets = ((char * (*)( char *, int, void *))(fgets));
4440
4441 /* Note that we can create objects only after the
4442 * interpreter liveList and freeList pointers are
4443 * initialized to NULL. */
4444 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4445 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4446 Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4447 NULL);
4448 Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4449 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4450 Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4451 i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4452 i->emptyObj = Jim_NewEmptyStringObj(i);
4453 i->result = i->emptyObj;
4454 i->stackTrace = Jim_NewListObj(i, NULL, 0);
4455 i->unknown = Jim_NewStringObj(i, "unknown", -1);
4456 i->unknown_called = 0;
4457 Jim_IncrRefCount(i->emptyObj);
4458 Jim_IncrRefCount(i->result);
4459 Jim_IncrRefCount(i->stackTrace);
4460 Jim_IncrRefCount(i->unknown);
4461
4462 /* Initialize key variables every interpreter should contain */
4463 pathPtr = Jim_NewStringObj(i, "./", -1);
4464 Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4465 Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4466
4467 /* Export the core API to extensions */
4468 JimRegisterCoreApi(i);
4469 return i;
4470 }
4471
4472 /* This is the only function Jim exports directly without
4473 * to use the STUB system. It is only used by embedders
4474 * in order to get an interpreter with the Jim API pointers
4475 * registered. */
4476 Jim_Interp *ExportedJimCreateInterp(void)
4477 {
4478 return Jim_CreateInterp();
4479 }
4480
4481 void Jim_FreeInterp(Jim_Interp *i)
4482 {
4483 Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4484 Jim_Obj *objPtr, *nextObjPtr;
4485
4486 Jim_DecrRefCount(i, i->emptyObj);
4487 Jim_DecrRefCount(i, i->result);
4488 Jim_DecrRefCount(i, i->stackTrace);
4489 Jim_DecrRefCount(i, i->unknown);
4490 Jim_Free((void*)i->errorFileName);
4491 Jim_Free((void*)i->scriptFileName);
4492 Jim_FreeHashTable(&i->commands);
4493 Jim_FreeHashTable(&i->references);
4494 Jim_FreeHashTable(&i->stub);
4495 Jim_FreeHashTable(&i->assocData);
4496 Jim_FreeHashTable(&i->packages);
4497 Jim_Free(i->prngState);
4498 /* Free the call frames list */
4499 while(cf) {
4500 prevcf = cf->parentCallFrame;
4501 JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4502 cf = prevcf;
4503 }
4504 /* Check that the live object list is empty, otherwise
4505 * there is a memory leak. */
4506 if (i->liveList != NULL) {
4507 Jim_Obj *objPtr = i->liveList;
4508
4509 Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
4510 Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
4511 while(objPtr) {
4512 const char *type = objPtr->typePtr ?
4513 objPtr->typePtr->name : "";
4514 Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4515 objPtr, type,
4516 objPtr->bytes ? objPtr->bytes
4517 : "(null)", objPtr->refCount);
4518 if (objPtr->typePtr == &sourceObjType) {
4519 Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
4520 objPtr->internalRep.sourceValue.fileName,
4521 objPtr->internalRep.sourceValue.lineNumber);
4522 }
4523 objPtr = objPtr->nextObjPtr;
4524 }
4525 Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
4526 Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4527 }
4528 /* Free all the freed objects. */
4529 objPtr = i->freeList;
4530 while (objPtr) {
4531 nextObjPtr = objPtr->nextObjPtr;
4532 Jim_Free(objPtr);
4533 objPtr = nextObjPtr;
4534 }
4535 /* Free cached CallFrame structures */
4536 cf = i->freeFramesList;
4537 while(cf) {
4538 nextcf = cf->nextFramePtr;
4539 if (cf->vars.table != NULL)
4540 Jim_Free(cf->vars.table);
4541 Jim_Free(cf);
4542 cf = nextcf;
4543 }
4544 /* Free the sharedString hash table. Make sure to free it
4545 * after every other Jim_Object was freed. */
4546 Jim_FreeHashTable(&i->sharedStrings);
4547 /* Free the interpreter structure. */
4548 Jim_Free(i);
4549 }
4550
4551 /* Store the call frame relative to the level represented by
4552 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4553 * level is assumed to be '1'.
4554 *
4555 * If a newLevelptr int pointer is specified, the function stores
4556 * the absolute level integer value of the new target callframe into
4557 * *newLevelPtr. (this is used to adjust interp->numLevels
4558 * in the implementation of [uplevel], so that [info level] will
4559 * return a correct information).
4560 *
4561 * This function accepts the 'level' argument in the form
4562 * of the commands [uplevel] and [upvar].
4563 *
4564 * For a function accepting a relative integer as level suitable
4565 * for implementation of [info level ?level?] check the
4566 * GetCallFrameByInteger() function. */
4567 int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4568 Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4569 {
4570 long level;
4571 const char *str;
4572 Jim_CallFrame *framePtr;
4573
4574 if (newLevelPtr) *newLevelPtr = interp->numLevels;
4575 if (levelObjPtr) {
4576 str = Jim_GetString(levelObjPtr, NULL);
4577 if (str[0] == '#') {
4578 char *endptr;
4579 /* speedup for the toplevel (level #0) */
4580 if (str[1] == '0' && str[2] == '\0') {
4581 if (newLevelPtr) *newLevelPtr = 0;
4582 *framePtrPtr = interp->topFramePtr;
4583 return JIM_OK;
4584 }
4585
4586 level = strtol(str+1, &endptr, 0);
4587 if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4588 goto badlevel;
4589 /* An 'absolute' level is converted into the
4590 * 'number of levels to go back' format. */
4591 level = interp->numLevels - level;
4592 if (level < 0) goto badlevel;
4593 } else {
4594 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4595 goto badlevel;
4596 }
4597 } else {
4598 str = "1"; /* Needed to format the error message. */
4599 level = 1;
4600 }
4601 /* Lookup */
4602 framePtr = interp->framePtr;
4603 if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4604 while (level--) {
4605 framePtr = framePtr->parentCallFrame;
4606 if (framePtr == NULL) goto badlevel;
4607 }
4608 *framePtrPtr = framePtr;
4609 return JIM_OK;
4610 badlevel:
4611 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4612 Jim_AppendStrings(interp, Jim_GetResult(interp),
4613 "bad level \"", str, "\"", NULL);
4614 return JIM_ERR;
4615 }
4616
4617 /* Similar to Jim_GetCallFrameByLevel() but the level is specified
4618 * as a relative integer like in the [info level ?level?] command. */
4619 static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4620 Jim_CallFrame **framePtrPtr)
4621 {
4622 jim_wide level;
4623 jim_wide relLevel; /* level relative to the current one. */
4624 Jim_CallFrame *framePtr;
4625
4626 if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4627 goto badlevel;
4628 if (level > 0) {
4629 /* An 'absolute' level is converted into the
4630 * 'number of levels to go back' format. */
4631 relLevel = interp->numLevels - level;
4632 } else {
4633 relLevel = -level;
4634 }
4635 /* Lookup */
4636 framePtr = interp->framePtr;
4637 while (relLevel--) {
4638 framePtr = framePtr->parentCallFrame;
4639 if (framePtr == NULL) goto badlevel;
4640 }
4641 *framePtrPtr = framePtr;
4642 return JIM_OK;
4643 badlevel:
4644 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4645 Jim_AppendStrings(interp, Jim_GetResult(interp),
4646 "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4647 return JIM_ERR;
4648 }
4649
4650 static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4651 {
4652 Jim_Free((void*)interp->errorFileName);
4653 interp->errorFileName = Jim_StrDup(filename);
4654 }
4655
4656 static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4657 {
4658 interp->errorLine = linenr;
4659 }
4660
4661 static void JimResetStackTrace(Jim_Interp *interp)
4662 {
4663 Jim_DecrRefCount(interp, interp->stackTrace);
4664 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4665 Jim_IncrRefCount(interp->stackTrace);
4666 }
4667
4668 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4669 const char *filename, int linenr)
4670 {
4671 /* No need to add this dummy entry to the stack trace */
4672 if (strcmp(procname, "unknown") == 0) {
4673 return;
4674 }
4675
4676 if (Jim_IsShared(interp->stackTrace)) {
4677 interp->stackTrace =
4678 Jim_DuplicateObj(interp, interp->stackTrace);
4679 Jim_IncrRefCount(interp->stackTrace);
4680 }
4681 Jim_ListAppendElement(interp, interp->stackTrace,
4682 Jim_NewStringObj(interp, procname, -1));
4683 Jim_ListAppendElement(interp, interp->stackTrace,
4684 Jim_NewStringObj(interp, filename, -1));
4685 Jim_ListAppendElement(interp, interp->stackTrace,
4686 Jim_NewIntObj(interp, linenr));
4687 }
4688
4689 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4690 {
4691 AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4692 assocEntryPtr->delProc = delProc;
4693 assocEntryPtr->data = data;
4694 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4695 }
4696
4697 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4698 {
4699 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4700 if (entryPtr != NULL) {
4701 AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4702 return assocEntryPtr->data;
4703 }
4704 return NULL;
4705 }
4706
4707 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4708 {
4709 return Jim_DeleteHashEntry(&interp->assocData, key);
4710 }
4711
4712 int Jim_GetExitCode(Jim_Interp *interp) {
4713 return interp->exitCode;
4714 }
4715
4716 void *Jim_SetStdin(Jim_Interp *interp, void *fp)
4717 {
4718 if (fp != NULL) interp->cookie_stdin = fp;
4719 return interp->cookie_stdin;
4720 }
4721
4722 void *Jim_SetStdout(Jim_Interp *interp, void *fp)
4723 {
4724 if (fp != NULL) interp->cookie_stdout = fp;
4725 return interp->cookie_stdout;
4726 }
4727
4728 void *Jim_SetStderr(Jim_Interp *interp, void *fp)
4729 {
4730 if (fp != NULL) interp->cookie_stderr = fp;
4731 return interp->cookie_stderr;
4732 }
4733
4734 /* -----------------------------------------------------------------------------
4735 * Shared strings.
4736 * Every interpreter has an hash table where to put shared dynamically
4737 * allocate strings that are likely to be used a lot of times.
4738 * For example, in the 'source' object type, there is a pointer to
4739 * the filename associated with that object. Every script has a lot
4740 * of this objects with the identical file name, so it is wise to share
4741 * this info.
4742 *
4743 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4744 * returns the pointer to the shared string. Every time a reference
4745 * to the string is no longer used, the user should call
4746 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4747 * a given string, it is removed from the hash table.
4748 * ---------------------------------------------------------------------------*/
4749 const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4750 {
4751 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4752
4753 if (he == NULL) {
4754 char *strCopy = Jim_StrDup(str);
4755
4756 Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4757 return strCopy;
4758 } else {
4759 long refCount = (long) he->val;
4760
4761 refCount++;
4762 he->val = (void*) refCount;
4763 return he->key;
4764 }
4765 }
4766
4767 void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4768 {
4769 long refCount;
4770 Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4771
4772 if (he == NULL)
4773 Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4774 "unknown shared string '%s'", str);
4775 refCount = (long) he->val;
4776 refCount--;
4777 if (refCount == 0) {
4778 Jim_DeleteHashEntry(&interp->sharedStrings, str);
4779 } else {
4780 he->val = (void*) refCount;
4781 }
4782 }
4783
4784 /* -----------------------------------------------------------------------------
4785 * Integer object
4786 * ---------------------------------------------------------------------------*/
4787 #define JIM_INTEGER_SPACE 24
4788
4789 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4790 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4791
4792 static Jim_ObjType intObjType = {
4793 "int",
4794 NULL,
4795 NULL,
4796 UpdateStringOfInt,
4797 JIM_TYPE_NONE,
4798 };
4799
4800 void UpdateStringOfInt(struct Jim_Obj *objPtr)
4801 {
4802 int len;
4803 char buf[JIM_INTEGER_SPACE+1];
4804
4805 len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4806 objPtr->bytes = Jim_Alloc(len+1);
4807 memcpy(objPtr->bytes, buf, len+1);
4808 objPtr->length = len;
4809 }
4810
4811 int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4812 {
4813 jim_wide wideValue;
4814 const char *str;
4815
4816 /* Get the string representation */
4817 str = Jim_GetString(objPtr, NULL);
4818 /* Try to convert into a jim_wide */
4819 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4820 if (flags & JIM_ERRMSG) {
4821 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4822 Jim_AppendStrings(interp, Jim_GetResult(interp),
4823 "expected integer but got \"", str, "\"", NULL);
4824 }
4825 return JIM_ERR;
4826 }
4827 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4828 errno == ERANGE) {
4829 Jim_SetResultString(interp,
4830 "Integer value too big to be represented", -1);
4831 return JIM_ERR;
4832 }
4833 /* Free the old internal repr and set the new one. */
4834 Jim_FreeIntRep(interp, objPtr);
4835 objPtr->typePtr = &intObjType;
4836 objPtr->internalRep.wideValue = wideValue;
4837 return JIM_OK;
4838 }
4839
4840 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4841 {
4842 if (objPtr->typePtr != &intObjType &&
4843 SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4844 return JIM_ERR;
4845 *widePtr = objPtr->internalRep.wideValue;
4846 return JIM_OK;
4847 }
4848
4849 /* Get a wide but does not set an error if the format is bad. */
4850 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4851 jim_wide *widePtr)
4852 {
4853 if (objPtr->typePtr != &intObjType &&
4854 SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4855 return JIM_ERR;
4856 *widePtr = objPtr->internalRep.wideValue;
4857 return JIM_OK;
4858 }
4859
4860 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4861 {
4862 jim_wide wideValue;
4863 int retval;
4864
4865 retval = Jim_GetWide(interp, objPtr, &wideValue);
4866 if (retval == JIM_OK) {
4867 *longPtr = (long) wideValue;
4868 return JIM_OK;
4869 }
4870 return JIM_ERR;
4871 }
4872
4873 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4874 {
4875 if (Jim_IsShared(objPtr))
4876 Jim_Panic(interp,"Jim_SetWide called with shared object");
4877 if (objPtr->typePtr != &intObjType) {
4878 Jim_FreeIntRep(interp, objPtr);
4879 objPtr->typePtr = &intObjType;
4880 }
4881 Jim_InvalidateStringRep(objPtr);
4882 objPtr->internalRep.wideValue = wideValue;
4883 }
4884
4885 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4886 {
4887 Jim_Obj *objPtr;
4888
4889 objPtr = Jim_NewObj(interp);
4890 objPtr->typePtr = &intObjType;
4891 objPtr->bytes = NULL;
4892 objPtr->internalRep.wideValue = wideValue;
4893 return objPtr;
4894 }
4895
4896 /* -----------------------------------------------------------------------------
4897 * Double object
4898 * ---------------------------------------------------------------------------*/
4899 #define JIM_DOUBLE_SPACE 30
4900
4901 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4902 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4903
4904 static Jim_ObjType doubleObjType = {
4905 "double",
4906 NULL,
4907 NULL,
4908 UpdateStringOfDouble,
4909 JIM_TYPE_NONE,
4910 };
4911
4912 void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4913 {
4914 int len;
4915 char buf[JIM_DOUBLE_SPACE+1];
4916
4917 len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4918 objPtr->bytes = Jim_Alloc(len+1);
4919 memcpy(objPtr->bytes, buf, len+1);
4920 objPtr->length = len;
4921 }
4922
4923 int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4924 {
4925 double doubleValue;
4926 const char *str;
4927
4928 /* Get the string representation */
4929 str = Jim_GetString(objPtr, NULL);
4930 /* Try to convert into a double */
4931 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4932 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4933 Jim_AppendStrings(interp, Jim_GetResult(interp),
4934 "expected number but got '", str, "'", NULL);
4935 return JIM_ERR;
4936 }
4937 /* Free the old internal repr and set the new one. */
4938 Jim_FreeIntRep(interp, objPtr);
4939 objPtr->typePtr = &doubleObjType;
4940 objPtr->internalRep.doubleValue = doubleValue;
4941 return JIM_OK;
4942 }
4943
4944 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4945 {
4946 if (objPtr->typePtr != &doubleObjType &&
4947 SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4948 return JIM_ERR;
4949 *doublePtr = objPtr->internalRep.doubleValue;
4950 return JIM_OK;
4951 }
4952
4953 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4954 {
4955 if (Jim_IsShared(objPtr))
4956 Jim_Panic(interp,"Jim_SetDouble called with shared object");
4957 if (objPtr->typePtr != &doubleObjType) {
4958 Jim_FreeIntRep(interp, objPtr);
4959 objPtr->typePtr = &doubleObjType;
4960 }
4961 Jim_InvalidateStringRep(objPtr);
4962 objPtr->internalRep.doubleValue = doubleValue;
4963 }
4964
4965 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4966 {
4967 Jim_Obj *objPtr;
4968
4969 objPtr = Jim_NewObj(interp);
4970 objPtr->typePtr = &doubleObjType;
4971 objPtr->bytes = NULL;
4972 objPtr->internalRep.doubleValue = doubleValue;
4973 return objPtr;
4974 }
4975
4976 /* -----------------------------------------------------------------------------
4977 * List object
4978 * ---------------------------------------------------------------------------*/
4979 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4980 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4981 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4982 static void UpdateStringOfList(struct Jim_Obj *objPtr);
4983 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4984
4985 /* Note that while the elements of the list may contain references,
4986 * the list object itself can't. This basically means that the
4987 * list object string representation as a whole can't contain references
4988 * that are not presents in the single elements. */
4989 static Jim_ObjType listObjType = {
4990 "list",
4991 FreeListInternalRep,
4992 DupListInternalRep,
4993 UpdateStringOfList,
4994 JIM_TYPE_NONE,
4995 };
4996
4997 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4998 {
4999 int i;
5000
5001 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5002 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
5003 }
5004 Jim_Free(objPtr->internalRep.listValue.ele);
5005 }
5006
5007 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5008 {
5009 int i;
5010 JIM_NOTUSED(interp);
5011
5012 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
5013 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
5014 dupPtr->internalRep.listValue.ele =
5015 Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
5016 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
5017 sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
5018 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
5019 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
5020 }
5021 dupPtr->typePtr = &listObjType;
5022 }
5023
5024 /* The following function checks if a given string can be encoded
5025 * into a list element without any kind of quoting, surrounded by braces,
5026 * or using escapes to quote. */
5027 #define JIM_ELESTR_SIMPLE 0
5028 #define JIM_ELESTR_BRACE 1
5029 #define JIM_ELESTR_QUOTE 2
5030 static int ListElementQuotingType(const char *s, int len)
5031 {
5032 int i, level, trySimple = 1;
5033
5034 /* Try with the SIMPLE case */
5035 if (len == 0) return JIM_ELESTR_BRACE;
5036 if (s[0] == '"' || s[0] == '{') {
5037 trySimple = 0;
5038 goto testbrace;
5039 }
5040 for (i = 0; i < len; i++) {
5041 switch(s[i]) {
5042 case ' ':
5043 case '$':
5044 case '"':
5045 case '[':
5046 case ']':
5047 case ';':
5048 case '\\':
5049 case '\r':
5050 case '\n':
5051 case '\t':
5052 case '\f':
5053 case '\v':
5054 trySimple = 0;
5055 case '{':
5056 case '}':
5057 goto testbrace;
5058 }
5059 }
5060 return JIM_ELESTR_SIMPLE;
5061
5062 testbrace:
5063 /* Test if it's possible to do with braces */
5064 if (s[len-1] == '\\' ||
5065 s[len-1] == ']') return JIM_ELESTR_QUOTE;
5066 level = 0;
5067 for (i = 0; i < len; i++) {
5068 switch(s[i]) {
5069 case '{': level++; break;
5070 case '}': level--;
5071 if (level < 0) return JIM_ELESTR_QUOTE;
5072 break;
5073 case '\\':
5074 if (s[i+1] == '\n')
5075 return JIM_ELESTR_QUOTE;
5076 else
5077 if (s[i+1] != '\0') i++;
5078 break;
5079 }
5080 }
5081 if (level == 0) {
5082 if (!trySimple) return JIM_ELESTR_BRACE;
5083 for (i = 0; i < len; i++) {
5084 switch(s[i]) {
5085 case ' ':
5086 case '$':
5087 case '"':
5088 case '[':
5089 case ']':
5090 case ';':
5091 case '\\':
5092 case '\r':
5093 case '\n':
5094 case '\t':
5095 case '\f':
5096 case '\v':
5097 return JIM_ELESTR_BRACE;
5098 break;
5099 }
5100 }
5101 return JIM_ELESTR_SIMPLE;
5102 }
5103 return JIM_ELESTR_QUOTE;
5104 }
5105
5106 /* Returns the malloc-ed representation of a string
5107 * using backslash to quote special chars. */
5108 char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
5109 {
5110 char *q = Jim_Alloc(len*2+1), *p;
5111
5112 p = q;
5113 while(*s) {
5114 switch (*s) {
5115 case ' ':
5116 case '$':
5117 case '"':
5118 case '[':
5119 case ']':
5120 case '{':
5121 case '}':
5122 case ';':
5123 case '\\':
5124 *p++ = '\\';
5125 *p++ = *s++;
5126 break;
5127 case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
5128 case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
5129 case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
5130 case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
5131 case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
5132 default:
5133 *p++ = *s++;
5134 break;
5135 }
5136 }
5137 *p = '\0';
5138 *qlenPtr = p-q;
5139 return q;
5140 }
5141
5142 void UpdateStringOfList(struct Jim_Obj *objPtr)
5143 {
5144 int i, bufLen, realLength;
5145 const char *strRep;
5146 char *p;
5147 int *quotingType;
5148 Jim_Obj **ele = objPtr->internalRep.listValue.ele;
5149
5150 /* (Over) Estimate the space needed. */
5151 quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
5152 bufLen = 0;
5153 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5154 int len;
5155
5156 strRep = Jim_GetString(ele[i], &len);
5157 quotingType[i] = ListElementQuotingType(strRep, len);
5158 switch (quotingType[i]) {
5159 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5160 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5161 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5162 }
5163 bufLen++; /* elements separator. */
5164 }
5165 bufLen++;
5166
5167 /* Generate the string rep. */
5168 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5169 realLength = 0;
5170 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
5171 int len, qlen;
5172 const char *strRep = Jim_GetString(ele[i], &len);
5173 char *q;
5174
5175 switch(quotingType[i]) {
5176 case JIM_ELESTR_SIMPLE:
5177 memcpy(p, strRep, len);
5178 p += len;
5179 realLength += len;
5180 break;
5181 case JIM_ELESTR_BRACE:
5182 *p++ = '{';
5183 memcpy(p, strRep, len);
5184 p += len;
5185 *p++ = '}';
5186 realLength += len+2;
5187 break;
5188 case JIM_ELESTR_QUOTE:
5189 q = BackslashQuoteString(strRep, len, &qlen);
5190 memcpy(p, q, qlen);
5191 Jim_Free(q);
5192 p += qlen;
5193 realLength += qlen;
5194 break;
5195 }
5196 /* Add a separating space */
5197 if (i+1 != objPtr->internalRep.listValue.len) {
5198 *p++ = ' ';
5199 realLength ++;
5200 }
5201 }
5202 *p = '\0'; /* nul term. */
5203 objPtr->length = realLength;
5204 Jim_Free(quotingType);
5205 }
5206
5207 int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5208 {
5209 struct JimParserCtx parser;
5210 const char *str;
5211 int strLen;
5212
5213 /* Get the string representation */
5214 str = Jim_GetString(objPtr, &strLen);
5215
5216 /* Free the old internal repr just now and initialize the
5217 * new one just now. The string->list conversion can't fail. */
5218 Jim_FreeIntRep(interp, objPtr);
5219 objPtr->typePtr = &listObjType;
5220 objPtr->internalRep.listValue.len = 0;
5221 objPtr->internalRep.listValue.maxLen = 0;
5222 objPtr->internalRep.listValue.ele = NULL;
5223
5224 /* Convert into a list */
5225 JimParserInit(&parser, str, strLen, 1);
5226 while(!JimParserEof(&parser)) {
5227 char *token;
5228 int tokenLen, type;
5229 Jim_Obj *elementPtr;
5230
5231 JimParseList(&parser);
5232 if (JimParserTtype(&parser) != JIM_TT_STR &&
5233 JimParserTtype(&parser) != JIM_TT_ESC)
5234 continue;
5235 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5236 elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5237 ListAppendElement(objPtr, elementPtr);
5238 }
5239 return JIM_OK;
5240 }
5241
5242 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
5243 int len)
5244 {
5245 Jim_Obj *objPtr;
5246 int i;
5247
5248 objPtr = Jim_NewObj(interp);
5249 objPtr->typePtr = &listObjType;
5250 objPtr->bytes = NULL;
5251 objPtr->internalRep.listValue.ele = NULL;
5252 objPtr->internalRep.listValue.len = 0;
5253 objPtr->internalRep.listValue.maxLen = 0;
5254 for (i = 0; i < len; i++) {
5255 ListAppendElement(objPtr, elements[i]);
5256 }
5257 return objPtr;
5258 }
5259
5260 /* Return a vector of Jim_Obj with the elements of a Jim list, and the
5261 * length of the vector. Note that the user of this function should make
5262 * sure that the list object can't shimmer while the vector returned
5263 * is in use, this vector is the one stored inside the internal representation
5264 * of the list object. This function is not exported, extensions should
5265 * always access to the List object elements using Jim_ListIndex(). */
5266 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
5267 Jim_Obj ***listVec)
5268 {
5269 Jim_ListLength(interp, listObj, argc);
5270 assert(listObj->typePtr == &listObjType);
5271 *listVec = listObj->internalRep.listValue.ele;
5272 }
5273
5274 /* ListSortElements type values */
5275 enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
5276 JIM_LSORT_NOCASE_DECR};
5277
5278 /* Sort the internal rep of a list. */
5279 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5280 {
5281 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
5282 }
5283
5284 static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5285 {
5286 return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
5287 }
5288
5289 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5290 {
5291 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
5292 }
5293
5294 static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
5295 {
5296 return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
5297 }
5298
5299 /* Sort a list *in place*. MUST be called with non-shared objects. */
5300 static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
5301 {
5302 typedef int (qsort_comparator)(const void *, const void *);
5303 int (*fn)(Jim_Obj**, Jim_Obj**);
5304 Jim_Obj **vector;
5305 int len;
5306
5307 if (Jim_IsShared(listObjPtr))
5308 Jim_Panic(interp,"Jim_ListSortElements called with shared object");
5309 if (listObjPtr->typePtr != &listObjType)
5310 SetListFromAny(interp, listObjPtr);
5311
5312 vector = listObjPtr->internalRep.listValue.ele;
5313 len = listObjPtr->internalRep.listValue.len;
5314 switch (type) {
5315 case JIM_LSORT_ASCII: fn = ListSortString; break;
5316 case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
5317 case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
5318 case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
5319 default:
5320 fn = NULL; /* avoid warning */
5321 Jim_Panic(interp,"ListSort called with invalid sort type");
5322 }
5323 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
5324 Jim_InvalidateStringRep(listObjPtr);
5325 }
5326
5327 /* This is the low-level function to append an element to a list.
5328 * The higher-level Jim_ListAppendElement() performs shared object
5329 * check and invalidate the string repr. This version is used
5330 * in the internals of the List Object and is not exported.
5331 *
5332 * NOTE: this function can be called only against objects
5333 * with internal type of List. */
5334 void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
5335 {
5336 int requiredLen = listPtr->internalRep.listValue.len + 1;
5337
5338 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5339 int maxLen = requiredLen * 2;
5340
5341 listPtr->internalRep.listValue.ele =
5342 Jim_Realloc(listPtr->internalRep.listValue.ele,
5343 sizeof(Jim_Obj*)*maxLen);
5344 listPtr->internalRep.listValue.maxLen = maxLen;
5345 }
5346 listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
5347 objPtr;
5348 listPtr->internalRep.listValue.len ++;
5349 Jim_IncrRefCount(objPtr);
5350 }
5351
5352 /* This is the low-level function to insert elements into a list.
5353 * The higher-level Jim_ListInsertElements() performs shared object
5354 * check and invalidate the string repr. This version is used
5355 * in the internals of the List Object and is not exported.
5356 *
5357 * NOTE: this function can be called only against objects
5358 * with internal type of List. */
5359 void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
5360 Jim_Obj *const *elemVec)
5361 {
5362 int currentLen = listPtr->internalRep.listValue.len;
5363 int requiredLen = currentLen + elemc;
5364 int i;
5365 Jim_Obj **point;
5366
5367 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5368 int maxLen = requiredLen * 2;
5369
5370 listPtr->internalRep.listValue.ele =
5371 Jim_Realloc(listPtr->internalRep.listValue.ele,
5372 sizeof(Jim_Obj*)*maxLen);
5373 listPtr->internalRep.listValue.maxLen = maxLen;
5374 }
5375 point = listPtr->internalRep.listValue.ele + index;
5376 memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
5377 for (i=0; i < elemc; ++i) {
5378 point[i] = elemVec[i];
5379 Jim_IncrRefCount(point[i]);
5380 }
5381 listPtr->internalRep.listValue.len += elemc;
5382 }
5383
5384 /* Appends every element of appendListPtr into listPtr.
5385 * Both have to be of the list type. */
5386 void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5387 {
5388 int i, oldLen = listPtr->internalRep.listValue.len;
5389 int appendLen = appendListPtr->internalRep.listValue.len;
5390 int requiredLen = oldLen + appendLen;
5391
5392 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
5393 int maxLen = requiredLen * 2;
5394
5395 listPtr->internalRep.listValue.ele =
5396 Jim_Realloc(listPtr->internalRep.listValue.ele,
5397 sizeof(Jim_Obj*)*maxLen);
5398 listPtr->internalRep.listValue.maxLen = maxLen;
5399 }
5400 for (i = 0; i < appendLen; i++) {
5401 Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5402 listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5403 Jim_IncrRefCount(objPtr);
5404 }
5405 listPtr->internalRep.listValue.len += appendLen;
5406 }
5407
5408 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5409 {
5410 if (Jim_IsShared(listPtr))
5411 Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5412 if (listPtr->typePtr != &listObjType)
5413 SetListFromAny(interp, listPtr);
5414 Jim_InvalidateStringRep(listPtr);
5415 ListAppendElement(listPtr, objPtr);
5416 }
5417
5418 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5419 {
5420 if (Jim_IsShared(listPtr))
5421 Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5422 if (listPtr->typePtr != &listObjType)
5423 SetListFromAny(interp, listPtr);
5424 Jim_InvalidateStringRep(listPtr);
5425 ListAppendList(listPtr, appendListPtr);
5426 }
5427
5428 void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5429 {
5430 if (listPtr->typePtr != &listObjType)
5431 SetListFromAny(interp, listPtr);
5432 *intPtr = listPtr->internalRep.listValue.len;
5433 }
5434
5435 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5436 int objc, Jim_Obj *const *objVec)
5437 {
5438 if (Jim_IsShared(listPtr))
5439 Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5440 if (listPtr->typePtr != &listObjType)
5441 SetListFromAny(interp, listPtr);
5442 if (index >= 0 && index > listPtr->internalRep.listValue.len)
5443 index = listPtr->internalRep.listValue.len;
5444 else if (index < 0 )
5445 index = 0;
5446 Jim_InvalidateStringRep(listPtr);
5447 ListInsertElements(listPtr, index, objc, objVec);
5448 }
5449
5450 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5451 Jim_Obj **objPtrPtr, int flags)
5452 {
5453 if (listPtr->typePtr != &listObjType)
5454 SetListFromAny(interp, listPtr);
5455 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5456 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5457 if (flags & JIM_ERRMSG) {
5458 Jim_SetResultString(interp,
5459 "list index out of range", -1);
5460 }
5461 return JIM_ERR;
5462 }
5463 if (index < 0)
5464 index = listPtr->internalRep.listValue.len+index;
5465 *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5466 return JIM_OK;
5467 }
5468
5469 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5470 Jim_Obj *newObjPtr, int flags)
5471 {
5472 if (listPtr->typePtr != &listObjType)
5473 SetListFromAny(interp, listPtr);
5474 if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5475 (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5476 if (flags & JIM_ERRMSG) {
5477 Jim_SetResultString(interp,
5478 "list index out of range", -1);
5479 }
5480 return JIM_ERR;
5481 }
5482 if (index < 0)
5483 index = listPtr->internalRep.listValue.len+index;
5484 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5485 listPtr->internalRep.listValue.ele[index] = newObjPtr;
5486 Jim_IncrRefCount(newObjPtr);
5487 return JIM_OK;
5488 }
5489
5490 /* Modify the list stored into the variable named 'varNamePtr'
5491 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5492 * with the new element 'newObjptr'. */
5493 int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5494 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5495 {
5496 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5497 int shared, i, index;
5498
5499 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5500 if (objPtr == NULL)
5501 return JIM_ERR;
5502 if ((shared = Jim_IsShared(objPtr)))
5503 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5504 for (i = 0; i < indexc-1; i++) {
5505 listObjPtr = objPtr;
5506 if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5507 goto err;
5508 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5509 JIM_ERRMSG) != JIM_OK) {
5510 goto err;
5511 }
5512 if (Jim_IsShared(objPtr)) {
5513 objPtr = Jim_DuplicateObj(interp, objPtr);
5514 ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5515 }
5516 Jim_InvalidateStringRep(listObjPtr);
5517 }
5518 if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5519 goto err;
5520 if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5521 goto err;
5522 Jim_InvalidateStringRep(objPtr);
5523 Jim_InvalidateStringRep(varObjPtr);
5524 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5525 goto err;
5526 Jim_SetResult(interp, varObjPtr);
5527 return JIM_OK;
5528 err:
5529 if (shared) {
5530 Jim_FreeNewObj(interp, varObjPtr);
5531 }
5532 return JIM_ERR;
5533 }
5534
5535 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5536 {
5537 int i;
5538
5539 /* If all the objects in objv are lists without string rep.
5540 * it's possible to return a list as result, that's the
5541 * concatenation of all the lists. */
5542 for (i = 0; i < objc; i++) {
5543 if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5544 break;
5545 }
5546 if (i == objc) {
5547 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5548 for (i = 0; i < objc; i++)
5549 Jim_ListAppendList(interp, objPtr, objv[i]);
5550 return objPtr;
5551 } else {
5552 /* Else... we have to glue strings together */
5553 int len = 0, objLen;
5554 char *bytes, *p;
5555
5556 /* Compute the length */
5557 for (i = 0; i < objc; i++) {
5558 Jim_GetString(objv[i], &objLen);
5559 len += objLen;
5560 }
5561 if (objc) len += objc-1;
5562 /* Create the string rep, and a stinrg object holding it. */
5563 p = bytes = Jim_Alloc(len+1);
5564 for (i = 0; i < objc; i++) {
5565 const char *s = Jim_GetString(objv[i], &objLen);
5566 while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5567 {
5568 s++; objLen--; len--;
5569 }
5570 while (objLen && (s[objLen-1] == ' ' ||
5571 s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5572 objLen--; len--;
5573 }
5574 memcpy(p, s, objLen);
5575 p += objLen;
5576 if (objLen && i+1 != objc) {
5577 *p++ = ' ';
5578 } else if (i+1 != objc) {
5579 /* Drop the space calcuated for this
5580 * element that is instead null. */
5581 len--;
5582 }
5583 }
5584 *p = '\0';
5585 return Jim_NewStringObjNoAlloc(interp, bytes, len);
5586 }
5587 }
5588
5589 /* Returns a list composed of the elements in the specified range.
5590 * first and start are directly accepted as Jim_Objects and
5591 * processed for the end?-index? case. */
5592 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5593 {
5594 int first, last;
5595 int len, rangeLen;
5596
5597 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5598 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5599 return NULL;
5600 Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5601 first = JimRelToAbsIndex(len, first);
5602 last = JimRelToAbsIndex(len, last);
5603 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5604 return Jim_NewListObj(interp,
5605 listObjPtr->internalRep.listValue.ele+first, rangeLen);
5606 }
5607
5608 /* -----------------------------------------------------------------------------
5609 * Dict object
5610 * ---------------------------------------------------------------------------*/
5611 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5612 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5613 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5614 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5615
5616 /* Dict HashTable Type.
5617 *
5618 * Keys and Values are Jim objects. */
5619
5620 unsigned int JimObjectHTHashFunction(const void *key)
5621 {
5622 const char *str;
5623 Jim_Obj *objPtr = (Jim_Obj*) key;
5624 int len, h;
5625
5626 str = Jim_GetString(objPtr, &len);
5627 h = Jim_GenHashFunction((unsigned char*)str, len);
5628 return h;
5629 }
5630
5631 int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5632 {
5633 JIM_NOTUSED(privdata);
5634
5635 return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5636 }
5637
5638 static void JimObjectHTKeyValDestructor(void *interp, void *val)
5639 {
5640 Jim_Obj *objPtr = val;
5641
5642 Jim_DecrRefCount(interp, objPtr);
5643 }
5644
5645 static Jim_HashTableType JimDictHashTableType = {
5646 JimObjectHTHashFunction, /* hash function */
5647 NULL, /* key dup */
5648 NULL, /* val dup */
5649 JimObjectHTKeyCompare, /* key compare */
5650 (void(*)(void*, const void*)) /* ATTENTION: const cast */
5651 JimObjectHTKeyValDestructor, /* key destructor */
5652 JimObjectHTKeyValDestructor /* val destructor */
5653 };
5654
5655 /* Note that while the elements of the dict may contain references,
5656 * the list object itself can't. This basically means that the
5657 * dict object string representation as a whole can't contain references
5658 * that are not presents in the single elements. */
5659 static Jim_ObjType dictObjType = {
5660 "dict",
5661 FreeDictInternalRep,
5662 DupDictInternalRep,
5663 UpdateStringOfDict,
5664 JIM_TYPE_NONE,
5665 };
5666
5667 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5668 {
5669 JIM_NOTUSED(interp);
5670
5671 Jim_FreeHashTable(objPtr->internalRep.ptr);
5672 Jim_Free(objPtr->internalRep.ptr);
5673 }
5674
5675 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5676 {
5677 Jim_HashTable *ht, *dupHt;
5678 Jim_HashTableIterator *htiter;
5679 Jim_HashEntry *he;
5680
5681 /* Create a new hash table */
5682 ht = srcPtr->internalRep.ptr;
5683 dupHt = Jim_Alloc(sizeof(*dupHt));
5684 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5685 if (ht->size != 0)
5686 Jim_ExpandHashTable(dupHt, ht->size);
5687 /* Copy every element from the source to the dup hash table */
5688 htiter = Jim_GetHashTableIterator(ht);
5689 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5690 const Jim_Obj *keyObjPtr = he->key;
5691 Jim_Obj *valObjPtr = he->val;
5692
5693 Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
5694 Jim_IncrRefCount(valObjPtr);
5695 Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5696 }
5697 Jim_FreeHashTableIterator(htiter);
5698
5699 dupPtr->internalRep.ptr = dupHt;
5700 dupPtr->typePtr = &dictObjType;
5701 }
5702
5703 void UpdateStringOfDict(struct Jim_Obj *objPtr)
5704 {
5705 int i, bufLen, realLength;
5706 const char *strRep;
5707 char *p;
5708 int *quotingType, objc;
5709 Jim_HashTable *ht;
5710 Jim_HashTableIterator *htiter;
5711 Jim_HashEntry *he;
5712 Jim_Obj **objv;
5713
5714 /* Trun the hash table into a flat vector of Jim_Objects. */
5715 ht = objPtr->internalRep.ptr;
5716 objc = ht->used*2;
5717 objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5718 htiter = Jim_GetHashTableIterator(ht);
5719 i = 0;
5720 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5721 objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
5722 objv[i++] = he->val;
5723 }
5724 Jim_FreeHashTableIterator(htiter);
5725 /* (Over) Estimate the space needed. */
5726 quotingType = Jim_Alloc(sizeof(int)*objc);
5727 bufLen = 0;
5728 for (i = 0; i < objc; i++) {
5729 int len;
5730
5731 strRep = Jim_GetString(objv[i], &len);
5732 quotingType[i] = ListElementQuotingType(strRep, len);
5733 switch (quotingType[i]) {
5734 case JIM_ELESTR_SIMPLE: bufLen += len; break;
5735 case JIM_ELESTR_BRACE: bufLen += len+2; break;
5736 case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5737 }
5738 bufLen++; /* elements separator. */
5739 }
5740 bufLen++;
5741
5742 /* Generate the string rep. */
5743 p = objPtr->bytes = Jim_Alloc(bufLen+1);
5744 realLength = 0;
5745 for (i = 0; i < objc; i++) {
5746 int len, qlen;
5747 const char *strRep = Jim_GetString(objv[i], &len);
5748 char *q;
5749
5750 switch(quotingType[i]) {
5751 case JIM_ELESTR_SIMPLE:
5752 memcpy(p, strRep, len);
5753 p += len;
5754 realLength += len;
5755 break;
5756 case JIM_ELESTR_BRACE:
5757 *p++ = '{';
5758 memcpy(p, strRep, len);
5759 p += len;
5760 *p++ = '}';
5761 realLength += len+2;
5762 break;
5763 case JIM_ELESTR_QUOTE:
5764 q = BackslashQuoteString(strRep, len, &qlen);
5765 memcpy(p, q, qlen);
5766 Jim_Free(q);
5767 p += qlen;
5768 realLength += qlen;
5769 break;
5770 }
5771 /* Add a separating space */
5772 if (i+1 != objc) {
5773 *p++ = ' ';
5774 realLength ++;
5775 }
5776 }
5777 *p = '\0'; /* nul term. */
5778 objPtr->length = realLength;
5779 Jim_Free(quotingType);
5780 Jim_Free(objv);
5781 }
5782
5783 int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5784 {
5785 struct JimParserCtx parser;
5786 Jim_HashTable *ht;
5787 Jim_Obj *objv[2];
5788 const char *str;
5789 int i, strLen;
5790
5791 /* Get the string representation */
5792 str = Jim_GetString(objPtr, &strLen);
5793
5794 /* Free the old internal repr just now and initialize the
5795 * new one just now. The string->list conversion can't fail. */
5796 Jim_FreeIntRep(interp, objPtr);
5797 ht = Jim_Alloc(sizeof(*ht));
5798 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5799 objPtr->typePtr = &dictObjType;
5800 objPtr->internalRep.ptr = ht;
5801
5802 /* Convert into a dict */
5803 JimParserInit(&parser, str, strLen, 1);
5804 i = 0;
5805 while(!JimParserEof(&parser)) {
5806 char *token;
5807 int tokenLen, type;
5808
5809 JimParseList(&parser);
5810 if (JimParserTtype(&parser) != JIM_TT_STR &&
5811 JimParserTtype(&parser) != JIM_TT_ESC)
5812 continue;
5813 token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5814 objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5815 if (i == 2) {
5816 i = 0;
5817 Jim_IncrRefCount(objv[0]);
5818 Jim_IncrRefCount(objv[1]);
5819 if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5820 Jim_HashEntry *he;
5821 he = Jim_FindHashEntry(ht, objv[0]);
5822 Jim_DecrRefCount(interp, objv[0]);
5823 /* ATTENTION: const cast */
5824 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5825 he->val = objv[1];
5826 }
5827 }
5828 }
5829 if (i) {
5830 Jim_FreeNewObj(interp, objv[0]);
5831 objPtr->typePtr = NULL;
5832 Jim_FreeHashTable(ht);
5833 Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5834 return JIM_ERR;
5835 }
5836 return JIM_OK;
5837 }
5838
5839 /* Dict object API */
5840
5841 /* Add an element to a dict. objPtr must be of the "dict" type.
5842 * The higer-level exported function is Jim_DictAddElement().
5843 * If an element with the specified key already exists, the value
5844 * associated is replaced with the new one.
5845 *
5846 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5847 static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5848 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5849 {
5850 Jim_HashTable *ht = objPtr->internalRep.ptr;
5851
5852 if (valueObjPtr == NULL) { /* unset */
5853 Jim_DeleteHashEntry(ht, keyObjPtr);
5854 return;
5855 }
5856 Jim_IncrRefCount(keyObjPtr);
5857 Jim_IncrRefCount(valueObjPtr);
5858 if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5859 Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5860 Jim_DecrRefCount(interp, keyObjPtr);
5861 /* ATTENTION: const cast */
5862 Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5863 he->val = valueObjPtr;
5864 }
5865 }
5866
5867 /* Add an element, higher-level interface for DictAddElement().
5868 * If valueObjPtr == NULL, the key is removed if it exists. */
5869 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5870 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5871 {
5872 if (Jim_IsShared(objPtr))
5873 Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5874 if (objPtr->typePtr != &dictObjType) {
5875 if (SetDictFromAny(interp, objPtr) != JIM_OK)
5876 return JIM_ERR;
5877 }
5878 DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5879 Jim_InvalidateStringRep(objPtr);
5880 return JIM_OK;
5881 }
5882
5883 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5884 {
5885 Jim_Obj *objPtr;
5886 int i;
5887
5888 if (len % 2)
5889 Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5890
5891 objPtr = Jim_NewObj(interp);
5892 objPtr->typePtr = &dictObjType;
5893 objPtr->bytes = NULL;
5894 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5895 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5896 for (i = 0; i < len; i += 2)
5897 DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5898 return objPtr;
5899 }
5900
5901 /* Return the value associated to the specified dict key */
5902 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5903 Jim_Obj **objPtrPtr, int flags)
5904 {
5905 Jim_HashEntry *he;
5906 Jim_HashTable *ht;
5907
5908 if (dictPtr->typePtr != &dictObjType) {
5909 if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5910 return JIM_ERR;
5911 }
5912 ht = dictPtr->internalRep.ptr;
5913 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5914 if (flags & JIM_ERRMSG) {
5915 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5916 Jim_AppendStrings(interp, Jim_GetResult(interp),
5917 "key \"", Jim_GetString(keyPtr, NULL),
5918 "\" not found in dictionary", NULL);
5919 }
5920 return JIM_ERR;
5921 }
5922 *objPtrPtr = he->val;
5923 return JIM_OK;
5924 }
5925
5926 /* Return the value associated to the specified dict keys */
5927 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5928 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5929 {
5930 Jim_Obj *objPtr;
5931 int i;
5932
5933 if (keyc == 0) {
5934 *objPtrPtr = dictPtr;
5935 return JIM_OK;
5936 }
5937
5938 for (i = 0; i < keyc; i++) {
5939 if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5940 != JIM_OK)
5941 return JIM_ERR;
5942 dictPtr = objPtr;
5943 }
5944 *objPtrPtr = objPtr;
5945 return JIM_OK;
5946 }
5947
5948 /* Modify the dict stored into the variable named 'varNamePtr'
5949 * setting the element specified by the 'keyc' keys objects in 'keyv',
5950 * with the new value of the element 'newObjPtr'.
5951 *
5952 * If newObjPtr == NULL the operation is to remove the given key
5953 * from the dictionary. */
5954 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5955 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5956 {
5957 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5958 int shared, i;
5959
5960 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5961 if (objPtr == NULL) {
5962 if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5963 return JIM_ERR;
5964 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5965 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5966 Jim_FreeNewObj(interp, varObjPtr);
5967 return JIM_ERR;
5968 }
5969 }
5970 if ((shared = Jim_IsShared(objPtr)))
5971 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5972 for (i = 0; i < keyc-1; i++) {
5973 dictObjPtr = objPtr;
5974
5975 /* Check if it's a valid dictionary */
5976 if (dictObjPtr->typePtr != &dictObjType) {
5977 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5978 goto err;
5979 }
5980 /* Check if the given key exists. */
5981 Jim_InvalidateStringRep(dictObjPtr);
5982 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5983 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5984 {
5985 /* This key exists at the current level.
5986 * Make sure it's not shared!. */
5987 if (Jim_IsShared(objPtr)) {
5988 objPtr = Jim_DuplicateObj(interp, objPtr);
5989 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5990 }
5991 } else {
5992 /* Key not found. If it's an [unset] operation
5993 * this is an error. Only the last key may not
5994 * exist. */
5995 if (newObjPtr == NULL)
5996 goto err;
5997 /* Otherwise set an empty dictionary
5998 * as key's value. */
5999 objPtr = Jim_NewDictObj(interp, NULL, 0);
6000 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
6001 }
6002 }
6003 if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
6004 != JIM_OK)
6005 goto err;
6006 Jim_InvalidateStringRep(objPtr);
6007 Jim_InvalidateStringRep(varObjPtr);
6008 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
6009 goto err;
6010 Jim_SetResult(interp, varObjPtr);
6011 return JIM_OK;
6012 err:
6013 if (shared) {
6014 Jim_FreeNewObj(interp, varObjPtr);
6015 }
6016 return JIM_ERR;
6017 }
6018
6019 /* -----------------------------------------------------------------------------
6020 * Index object
6021 * ---------------------------------------------------------------------------*/
6022 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
6023 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6024
6025 static Jim_ObjType indexObjType = {
6026 "index",
6027 NULL,
6028 NULL,
6029 UpdateStringOfIndex,
6030 JIM_TYPE_NONE,
6031 };
6032
6033 void UpdateStringOfIndex(struct Jim_Obj *objPtr)
6034 {
6035 int len;
6036 char buf[JIM_INTEGER_SPACE+1];
6037
6038 if (objPtr->internalRep.indexValue >= 0)
6039 len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
6040 else if (objPtr->internalRep.indexValue == -1)
6041 len = sprintf(buf, "end");
6042 else {
6043 len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
6044 }
6045 objPtr->bytes = Jim_Alloc(len+1);
6046 memcpy(objPtr->bytes, buf, len+1);
6047 objPtr->length = len;
6048 }
6049
6050 int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6051 {
6052 int index, end = 0;
6053 const char *str;
6054
6055 /* Get the string representation */
6056 str = Jim_GetString(objPtr, NULL);
6057 /* Try to convert into an index */
6058 if (!strcmp(str, "end")) {
6059 index = 0;
6060 end = 1;
6061 } else {
6062 if (!strncmp(str, "end-", 4)) {
6063 str += 4;
6064 end = 1;
6065 }
6066 if (Jim_StringToIndex(str, &index) != JIM_OK) {
6067 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6068 Jim_AppendStrings(interp, Jim_GetResult(interp),
6069 "bad index \"", Jim_GetString(objPtr, NULL), "\": "
6070 "must be integer or end?-integer?", NULL);
6071 return JIM_ERR;
6072 }
6073 }
6074 if (end) {
6075 if (index < 0)
6076 index = INT_MAX;
6077 else
6078 index = -(index+1);
6079 } else if (!end && index < 0)
6080 index = -INT_MAX;
6081 /* Free the old internal repr and set the new one. */
6082 Jim_FreeIntRep(interp, objPtr);
6083 objPtr->typePtr = &indexObjType;
6084 objPtr->internalRep.indexValue = index;
6085 return JIM_OK;
6086 }
6087
6088 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
6089 {
6090 /* Avoid shimmering if the object is an integer. */
6091 if (objPtr->typePtr == &intObjType) {
6092 jim_wide val = objPtr->internalRep.wideValue;
6093 if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
6094 *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
6095 return JIM_OK;
6096 }
6097 }
6098 if (objPtr->typePtr != &indexObjType &&
6099 SetIndexFromAny(interp, objPtr) == JIM_ERR)
6100 return JIM_ERR;
6101 *indexPtr = objPtr->internalRep.indexValue;
6102 return JIM_OK;
6103 }
6104
6105 /* -----------------------------------------------------------------------------
6106 * Return Code Object.
6107 * ---------------------------------------------------------------------------*/
6108
6109 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6110
6111 static Jim_ObjType returnCodeObjType = {
6112 "return-code",
6113 NULL,
6114 NULL,
6115 NULL,
6116 JIM_TYPE_NONE,
6117 };
6118
6119 int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6120 {
6121 const char *str;
6122 int strLen, returnCode;
6123 jim_wide wideValue;
6124
6125 /* Get the string representation */
6126 str = Jim_GetString(objPtr, &strLen);
6127 /* Try to convert into an integer */
6128 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
6129 returnCode = (int) wideValue;
6130 else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
6131 returnCode = JIM_OK;
6132 else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
6133 returnCode = JIM_ERR;
6134 else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
6135 returnCode = JIM_RETURN;
6136 else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
6137 returnCode = JIM_BREAK;
6138 else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
6139 returnCode = JIM_CONTINUE;
6140 else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
6141 returnCode = JIM_EVAL;
6142 else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
6143 returnCode = JIM_EXIT;
6144 else {
6145 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
6146 Jim_AppendStrings(interp, Jim_GetResult(interp),
6147 "expected return code but got '", str, "'",
6148 NULL);
6149 return JIM_ERR;
6150 }
6151 /* Free the old internal repr and set the new one. */
6152 Jim_FreeIntRep(interp, objPtr);
6153 objPtr->typePtr = &returnCodeObjType;
6154 objPtr->internalRep.returnCode = returnCode;
6155 return JIM_OK;
6156 }
6157
6158 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
6159 {
6160 if (objPtr->typePtr != &returnCodeObjType &&
6161 SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
6162 return JIM_ERR;
6163 *intPtr = objPtr->internalRep.returnCode;
6164 return JIM_OK;
6165 }
6166
6167 /* -----------------------------------------------------------------------------
6168 * Expression Parsing
6169 * ---------------------------------------------------------------------------*/
6170 static int JimParseExprOperator(struct JimParserCtx *pc);
6171 static int JimParseExprNumber(struct JimParserCtx *pc);
6172 static int JimParseExprIrrational(struct JimParserCtx *pc);
6173
6174 /* Exrp's Stack machine operators opcodes. */
6175
6176 /* Binary operators (numbers) */
6177 #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
6178 #define JIM_EXPROP_MUL 0
6179 #define JIM_EXPROP_DIV 1
6180 #define JIM_EXPROP_MOD 2
6181 #define JIM_EXPROP_SUB 3
6182 #define JIM_EXPROP_ADD 4
6183 #define JIM_EXPROP_LSHIFT 5
6184 #define JIM_EXPROP_RSHIFT 6
6185 #define JIM_EXPROP_ROTL 7
6186 #define JIM_EXPROP_ROTR 8
6187 #define JIM_EXPROP_LT 9
6188 #define JIM_EXPROP_GT 10
6189 #define JIM_EXPROP_LTE 11
6190 #define JIM_EXPROP_GTE 12
6191 #define JIM_EXPROP_NUMEQ 13
6192 #define JIM_EXPROP_NUMNE 14
6193 #define JIM_EXPROP_BITAND 15
6194 #define JIM_EXPROP_BITXOR 16
6195 #define JIM_EXPROP_BITOR 17
6196 #define JIM_EXPROP_LOGICAND 18
6197 #define JIM_EXPROP_LOGICOR 19
6198 #define JIM_EXPROP_LOGICAND_LEFT 20
6199 #define JIM_EXPROP_LOGICOR_LEFT 21
6200 #define JIM_EXPROP_POW 22
6201 #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
6202
6203 /* Binary operators (strings) */
6204 #define JIM_EXPROP_STREQ 23
6205 #define JIM_EXPROP_STRNE 24
6206
6207 /* Unary operators (numbers) */
6208 #define JIM_EXPROP_NOT 25
6209 #define JIM_EXPROP_BITNOT 26
6210 #define JIM_EXPROP_UNARYMINUS 27
6211 #define JIM_EXPROP_UNARYPLUS 28
6212 #define JIM_EXPROP_LOGICAND_RIGHT 29
6213 #define JIM_EXPROP_LOGICOR_RIGHT 30
6214
6215 /* Ternary operators */
6216 #define JIM_EXPROP_TERNARY 31
6217
6218 /* Operands */
6219 #define JIM_EXPROP_NUMBER 32
6220 #define JIM_EXPROP_COMMAND 33
6221 #define JIM_EXPROP_VARIABLE 34
6222 #define JIM_EXPROP_DICTSUGAR 35
6223 #define JIM_EXPROP_SUBST 36
6224 #define JIM_EXPROP_STRING 37
6225
6226 /* Operators table */
6227 typedef struct Jim_ExprOperator {
6228 const char *name;
6229 int precedence;
6230 int arity;
6231 int opcode;
6232 } Jim_ExprOperator;
6233
6234 /* name - precedence - arity - opcode */
6235 static struct Jim_ExprOperator Jim_ExprOperators[] = {
6236 {"!", 300, 1, JIM_EXPROP_NOT},
6237 {"~", 300, 1, JIM_EXPROP_BITNOT},
6238 {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
6239 {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
6240
6241 {"**", 250, 2, JIM_EXPROP_POW},
6242
6243 {"*", 200, 2, JIM_EXPROP_MUL},
6244 {"/", 200, 2, JIM_EXPROP_DIV},
6245 {"%", 200, 2, JIM_EXPROP_MOD},
6246
6247 {"-", 100, 2, JIM_EXPROP_SUB},
6248 {"+", 100, 2, JIM_EXPROP_ADD},
6249
6250 {"<<<", 90, 3, JIM_EXPROP_ROTL},
6251 {">>>", 90, 3, JIM_EXPROP_ROTR},
6252 {"<<", 90, 2, JIM_EXPROP_LSHIFT},
6253 {">>", 90, 2, JIM_EXPROP_RSHIFT},
6254
6255 {"<", 80, 2, JIM_EXPROP_LT},
6256 {">", 80, 2, JIM_EXPROP_GT},
6257 {"<=", 80, 2, JIM_EXPROP_LTE},
6258 {">=", 80, 2, JIM_EXPROP_GTE},
6259
6260 {"==", 70, 2, JIM_EXPROP_NUMEQ},
6261 {"!=", 70, 2, JIM_EXPROP_NUMNE},
6262
6263 {"eq", 60, 2, JIM_EXPROP_STREQ},
6264 {"ne", 60, 2, JIM_EXPROP_STRNE},
6265
6266 {"&", 50, 2, JIM_EXPROP_BITAND},
6267 {"^", 49, 2, JIM_EXPROP_BITXOR},
6268 {"|", 48, 2, JIM_EXPROP_BITOR},
6269
6270 {"&&", 10, 2, JIM_EXPROP_LOGICAND},
6271 {"||", 10, 2, JIM_EXPROP_LOGICOR},
6272
6273 {"?", 5, 3, JIM_EXPROP_TERNARY},
6274 /* private operators */
6275 {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
6276 {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
6277 {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
6278 {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
6279 };
6280
6281 #define JIM_EXPR_OPERATORS_NUM \
6282 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
6283
6284 int JimParseExpression(struct JimParserCtx *pc)
6285 {
6286 /* Discard spaces and quoted newline */
6287 while(*(pc->p) == ' ' ||
6288 *(pc->p) == '\t' ||
6289 *(pc->p) == '\r' ||
6290 *(pc->p) == '\n' ||
6291 (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
6292 pc->p++; pc->len--;
6293 }
6294
6295 if (pc->len == 0) {
6296 pc->tstart = pc->tend = pc->p;
6297 pc->tline = pc->linenr;
6298 pc->tt = JIM_TT_EOL;
6299 pc->eof = 1;
6300 return JIM_OK;
6301 }
6302 switch(*(pc->p)) {
6303 case '(':
6304 pc->tstart = pc->tend = pc->p;
6305 pc->tline = pc->linenr;
6306 pc->tt = JIM_TT_SUBEXPR_START;
6307 pc->p++; pc->len--;
6308 break;
6309 case ')':
6310 pc->tstart = pc->tend = pc->p;
6311 pc->tline = pc->linenr;
6312 pc->tt = JIM_TT_SUBEXPR_END;
6313 pc->p++; pc->len--;
6314 break;
6315 case '[':
6316 return JimParseCmd(pc);
6317 break;
6318 case '$':
6319 if (JimParseVar(pc) == JIM_ERR)
6320 return JimParseExprOperator(pc);
6321 else
6322 return JIM_OK;
6323 break;
6324 case '-':
6325 if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
6326 isdigit((int)*(pc->p+1)))
6327 return JimParseExprNumber(pc);
6328 else
6329 return JimParseExprOperator(pc);
6330 break;
6331 case '0': case '1': case '2': case '3': case '4':
6332 case '5': case '6': case '7': case '8': case '9': case '.':
6333 return JimParseExprNumber(pc);
6334 break;
6335 case '"':
6336 case '{':
6337 /* Here it's possible to reuse the List String parsing. */
6338 pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
6339 return JimParseListStr(pc);
6340 break;
6341 case 'N': case 'I':
6342 case 'n': case 'i':
6343 if (JimParseExprIrrational(pc) == JIM_ERR)
6344 return JimParseExprOperator(pc);
6345 break;
6346 default:
6347 return JimParseExprOperator(pc);
6348 break;
6349 }
6350 return JIM_OK;
6351 }
6352
6353 int JimParseExprNumber(struct JimParserCtx *pc)
6354 {
6355 int allowdot = 1;
6356 int allowhex = 0;
6357
6358 pc->tstart = pc->p;
6359 pc->tline = pc->linenr;
6360 if (*pc->p == '-') {
6361 pc->p++; pc->len--;
6362 }
6363 while ( isdigit((int)*pc->p)
6364 || (allowhex && isxdigit((int)*pc->p) )
6365 || (allowdot && *pc->p == '.')
6366 || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
6367 (*pc->p == 'x' || *pc->p == 'X'))
6368 )
6369 {
6370 if ((*pc->p == 'x') || (*pc->p == 'X')) {
6371 allowhex = 1;
6372 allowdot = 0;
6373 }
6374 if (*pc->p == '.')
6375 allowdot = 0;
6376 pc->p++; pc->len--;
6377 if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
6378 pc->p += 2; pc->len -= 2;
6379 }
6380 }
6381 pc->tend = pc->p-1;
6382 pc->tt = JIM_TT_EXPR_NUMBER;
6383 return JIM_OK;
6384 }
6385
6386 int JimParseExprIrrational(struct JimParserCtx *pc)
6387 {
6388 const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
6389 const char **token;
6390 for (token = Tokens; *token != NULL; token++) {
6391 int len = strlen(*token);
6392 if (strncmp(*token, pc->p, len) == 0) {
6393 pc->tstart = pc->p;
6394 pc->tend = pc->p + len - 1;
6395 pc->p += len; pc->len -= len;
6396 pc->tline = pc->linenr;
6397 pc->tt = JIM_TT_EXPR_NUMBER;
6398 return JIM_OK;
6399 }
6400 }
6401 return JIM_ERR;
6402 }
6403
6404 int JimParseExprOperator(struct JimParserCtx *pc)
6405 {
6406 int i;
6407 int bestIdx = -1, bestLen = 0;
6408
6409 /* Try to get the longest match. */
6410 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6411 const char *opname;
6412 int oplen;
6413
6414 opname = Jim_ExprOperators[i].name;
6415 if (opname == NULL) continue;
6416 oplen = strlen(opname);
6417
6418 if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6419 bestIdx = i;
6420 bestLen = oplen;
6421 }
6422 }
6423 if (bestIdx == -1) return JIM_ERR;
6424 pc->tstart = pc->p;
6425 pc->tend = pc->p + bestLen - 1;
6426 pc->p += bestLen; pc->len -= bestLen;
6427 pc->tline = pc->linenr;
6428 pc->tt = JIM_TT_EXPR_OPERATOR;
6429 return JIM_OK;
6430 }
6431
6432 struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6433 {
6434 int i;
6435 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6436 if (Jim_ExprOperators[i].name &&
6437 strcmp(opname, Jim_ExprOperators[i].name) == 0)
6438 return &Jim_ExprOperators[i];
6439 return NULL;
6440 }
6441
6442 struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6443 {
6444 int i;
6445 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6446 if (Jim_ExprOperators[i].opcode == opcode)
6447 return &Jim_ExprOperators[i];
6448 return NULL;
6449 }
6450
6451 /* -----------------------------------------------------------------------------
6452 * Expression Object
6453 * ---------------------------------------------------------------------------*/
6454 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6455 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6456 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6457
6458 static Jim_ObjType exprObjType = {
6459 "expression",
6460 FreeExprInternalRep,
6461 DupExprInternalRep,
6462 NULL,
6463 JIM_TYPE_REFERENCES,
6464 };
6465
6466 /* Expr bytecode structure */
6467 typedef struct ExprByteCode {
6468 int *opcode; /* Integer array of opcodes. */
6469 Jim_Obj **obj; /* Array of associated Jim Objects. */
6470 int len; /* Bytecode length */
6471 int inUse; /* Used for sharing. */
6472 } ExprByteCode;
6473
6474 void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6475 {
6476 int i;
6477 ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6478
6479 expr->inUse--;
6480 if (expr->inUse != 0) return;
6481 for (i = 0; i < expr->len; i++)
6482 Jim_DecrRefCount(interp, expr->obj[i]);
6483 Jim_Free(expr->opcode);
6484 Jim_Free(expr->obj);
6485 Jim_Free(expr);
6486 }
6487
6488 void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6489 {
6490 JIM_NOTUSED(interp);
6491 JIM_NOTUSED(srcPtr);
6492
6493 /* Just returns an simple string. */
6494 dupPtr->typePtr = NULL;
6495 }
6496
6497 /* Add a new instruction to an expression bytecode structure. */
6498 static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6499 int opcode, char *str, int len)
6500 {
6501 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6502 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6503 expr->opcode[expr->len] = opcode;
6504 expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6505 Jim_IncrRefCount(expr->obj[expr->len]);
6506 expr->len++;
6507 }
6508
6509 /* Check if an expr program looks correct. */
6510 static int ExprCheckCorrectness(ExprByteCode *expr)
6511 {
6512 int i;
6513 int stacklen = 0;
6514
6515 /* Try to check if there are stack underflows,
6516 * and make sure at the end of the program there is
6517 * a single result on the stack. */
6518 for (i = 0; i < expr->len; i++) {
6519 switch(expr->opcode[i]) {
6520 case JIM_EXPROP_NUMBER:
6521 case JIM_EXPROP_STRING:
6522 case JIM_EXPROP_SUBST:
6523 case JIM_EXPROP_VARIABLE:
6524 case JIM_EXPROP_DICTSUGAR:
6525 case JIM_EXPROP_COMMAND:
6526 stacklen++;
6527 break;
6528 case JIM_EXPROP_NOT:
6529 case JIM_EXPROP_BITNOT:
6530 case JIM_EXPROP_UNARYMINUS:
6531 case JIM_EXPROP_UNARYPLUS:
6532 /* Unary operations */
6533 if (stacklen < 1) return JIM_ERR;
6534 break;
6535 case JIM_EXPROP_ADD:
6536 case JIM_EXPROP_SUB:
6537 case JIM_EXPROP_MUL:
6538 case JIM_EXPROP_DIV:
6539 case JIM_EXPROP_MOD:
6540 case JIM_EXPROP_LT:
6541 case JIM_EXPROP_GT:
6542 case JIM_EXPROP_LTE:
6543 case JIM_EXPROP_GTE:
6544 case JIM_EXPROP_ROTL:
6545 case JIM_EXPROP_ROTR:
6546 case JIM_EXPROP_LSHIFT:
6547 case JIM_EXPROP_RSHIFT:
6548 case JIM_EXPROP_NUMEQ:
6549 case JIM_EXPROP_NUMNE:
6550 case JIM_EXPROP_STREQ:
6551 case JIM_EXPROP_STRNE:
6552 case JIM_EXPROP_BITAND:
6553 case JIM_EXPROP_BITXOR:
6554 case JIM_EXPROP_BITOR:
6555 case JIM_EXPROP_LOGICAND:
6556 case JIM_EXPROP_LOGICOR:
6557 case JIM_EXPROP_POW:
6558 /* binary operations */
6559 if (stacklen < 2) return JIM_ERR;
6560 stacklen--;
6561 break;
6562 default:
6563 Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6564 break;
6565 }
6566 }
6567 if (stacklen != 1) return JIM_ERR;
6568 return JIM_OK;
6569 }
6570
6571 static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6572 ScriptObj *topLevelScript)
6573 {
6574 int i;
6575
6576 return;
6577 for (i = 0; i < expr->len; i++) {
6578 Jim_Obj *foundObjPtr;
6579
6580 if (expr->obj[i] == NULL) continue;
6581 foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6582 NULL, expr->obj[i]);
6583 if (foundObjPtr != NULL) {
6584 Jim_IncrRefCount(foundObjPtr);
6585 Jim_DecrRefCount(interp, expr->obj[i]);
6586 expr->obj[i] = foundObjPtr;
6587 }
6588 }
6589 }
6590
6591 /* This procedure converts every occurrence of || and && opereators
6592 * in lazy unary versions.
6593 *
6594 * a b || is converted into:
6595 *
6596 * a <offset> |L b |R
6597 *
6598 * a b && is converted into:
6599 *
6600 * a <offset> &L b &R
6601 *
6602 * "|L" checks if 'a' is true:
6603 * 1) if it is true pushes 1 and skips <offset> istructions to reach
6604 * the opcode just after |R.
6605 * 2) if it is false does nothing.
6606 * "|R" checks if 'b' is true:
6607 * 1) if it is true pushes 1, otherwise pushes 0.
6608 *
6609 * "&L" checks if 'a' is true:
6610 * 1) if it is true does nothing.
6611 * 2) If it is false pushes 0 and skips <offset> istructions to reach
6612 * the opcode just after &R
6613 * "&R" checks if 'a' is true:
6614 * if it is true pushes 1, otherwise pushes 0.
6615 */
6616 static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6617 {
6618 while (1) {
6619 int index = -1, leftindex, arity, i, offset;
6620 Jim_ExprOperator *op;
6621
6622 /* Search for || or && */
6623 for (i = 0; i < expr->len; i++) {
6624 if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6625 expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6626 index = i;
6627 break;
6628 }
6629 }
6630 if (index == -1) return;
6631 /* Search for the end of the first operator */
6632 leftindex = index-1;
6633 arity = 1;
6634 while(arity) {
6635 switch(expr->opcode[leftindex]) {
6636 case JIM_EXPROP_NUMBER:
6637 case JIM_EXPROP_COMMAND:
6638 case JIM_EXPROP_VARIABLE:
6639 case JIM_EXPROP_DICTSUGAR:
6640 case JIM_EXPROP_SUBST:
6641 case JIM_EXPROP_STRING:
6642 break;
6643 default:
6644 op = JimExprOperatorInfoByOpcode(expr->opcode[leftindex]);
6645 if (op == NULL) {
6646 Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6647 }
6648 arity += op->arity;
6649 break;
6650 }
6651 arity--;
6652 leftindex--;
6653 }
6654 leftindex++;
6655 expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6656 expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6657 memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6658 sizeof(int)*(expr->len-leftindex));
6659 memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6660 sizeof(Jim_Obj*)*(expr->len-leftindex));
6661 expr->len += 2;
6662 index += 2;
6663 offset = (index-leftindex)-1;
6664 Jim_DecrRefCount(interp, expr->obj[index]);
6665 if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6666 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6667 expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6668 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6669 expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6670 } else {
6671 expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6672 expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6673 expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6674 expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6675 }
6676 expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6677 expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6678 Jim_IncrRefCount(expr->obj[index]);
6679 Jim_IncrRefCount(expr->obj[leftindex]);
6680 Jim_IncrRefCount(expr->obj[leftindex+1]);
6681 }
6682 }
6683
6684 /* This method takes the string representation of an expression
6685 * and generates a program for the Expr's stack-based VM. */
6686 int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6687 {
6688 int exprTextLen;
6689 const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6690 struct JimParserCtx parser;
6691 int i, shareLiterals;
6692 ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6693 Jim_Stack stack;
6694 Jim_ExprOperator *op;
6695
6696 /* Perform literal sharing with the current procedure
6697 * running only if this expression appears to be not generated
6698 * at runtime. */
6699 shareLiterals = objPtr->typePtr == &sourceObjType;
6700
6701 expr->opcode = NULL;
6702 expr->obj = NULL;
6703 expr->len = 0;
6704 expr->inUse = 1;
6705
6706 Jim_InitStack(&stack);
6707 JimParserInit(&parser, exprText, exprTextLen, 1);
6708 while(!JimParserEof(&parser)) {
6709 char *token;
6710 int len, type;
6711
6712 if (JimParseExpression(&parser) != JIM_OK) {
6713 Jim_SetResultString(interp, "Syntax error in expression", -1);
6714 goto err;
6715 }
6716 token = JimParserGetToken(&parser, &len, &type, NULL);
6717 if (type == JIM_TT_EOL) {
6718 Jim_Free(token);
6719 break;
6720 }
6721 switch(type) {
6722 case JIM_TT_STR:
6723 ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6724 break;
6725 case JIM_TT_ESC:
6726 ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6727 break;
6728 case JIM_TT_VAR:
6729 ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6730 break;
6731 case JIM_TT_DICTSUGAR:
6732 ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6733 break;
6734 case JIM_TT_CMD:
6735 ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6736 break;
6737 case JIM_TT_EXPR_NUMBER:
6738 ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6739 break;
6740 case JIM_TT_EXPR_OPERATOR:
6741 op = JimExprOperatorInfo(token);
6742 while(1) {
6743 Jim_ExprOperator *stackTopOp;
6744
6745 if (Jim_StackPeek(&stack) != NULL) {
6746 stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6747 } else {
6748 stackTopOp = NULL;
6749 }
6750 if (Jim_StackLen(&stack) && op->arity != 1 &&
6751 stackTopOp && stackTopOp->precedence >= op->precedence)
6752 {
6753 ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6754 Jim_StackPeek(&stack), -1);
6755 Jim_StackPop(&stack);
6756 } else {
6757 break;
6758 }
6759 }
6760 Jim_StackPush(&stack, token);
6761 break;
6762 case JIM_TT_SUBEXPR_START:
6763 Jim_StackPush(&stack, Jim_StrDup("("));
6764 Jim_Free(token);
6765 break;
6766 case JIM_TT_SUBEXPR_END:
6767 {
6768 int found = 0;
6769 while(Jim_StackLen(&stack)) {
6770 char *opstr = Jim_StackPop(&stack);
6771 if (!strcmp(opstr, "(")) {
6772 Jim_Free(opstr);
6773 found = 1;
6774 break;
6775 }
6776 op = JimExprOperatorInfo(opstr);
6777 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6778 }
6779 if (!found) {
6780 Jim_SetResultString(interp,
6781 "Unexpected close parenthesis", -1);
6782 goto err;
6783 }
6784 }
6785 Jim_Free(token);
6786 break;
6787 default:
6788 Jim_Panic(interp,"Default reached in SetExprFromAny()");
6789 break;
6790 }
6791 }
6792 while (Jim_StackLen(&stack)) {
6793 char *opstr = Jim_StackPop(&stack);
6794 op = JimExprOperatorInfo(opstr);
6795 if (op == NULL && !strcmp(opstr, "(")) {
6796 Jim_Free(opstr);
6797 Jim_SetResultString(interp, "Missing close parenthesis", -1);
6798 goto err;
6799 }
6800 ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6801 }
6802 /* Check program correctness. */
6803 if (ExprCheckCorrectness(expr) != JIM_OK) {
6804 Jim_SetResultString(interp, "Invalid expression", -1);
6805 goto err;
6806 }
6807
6808 /* Free the stack used for the compilation. */
6809 Jim_FreeStackElements(&stack, Jim_Free);
6810 Jim_FreeStack(&stack);
6811
6812 /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6813 ExprMakeLazy(interp, expr);
6814
6815 /* Perform literal sharing */
6816 if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6817 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6818 if (bodyObjPtr->typePtr == &scriptObjType) {
6819 ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6820 ExprShareLiterals(interp, expr, bodyScript);
6821 }
6822 }
6823
6824 /* Free the old internal rep and set the new one. */
6825 Jim_FreeIntRep(interp, objPtr);
6826 Jim_SetIntRepPtr(objPtr, expr);
6827 objPtr->typePtr = &exprObjType;
6828 return JIM_OK;
6829
6830 err: /* we jump here on syntax/compile errors. */
6831 Jim_FreeStackElements(&stack, Jim_Free);
6832 Jim_FreeStack(&stack);
6833 Jim_Free(expr->opcode);
6834 for (i = 0; i < expr->len; i++) {
6835 Jim_DecrRefCount(interp,expr->obj[i]);
6836 }
6837 Jim_Free(expr->obj);
6838 Jim_Free(expr);
6839 return JIM_ERR;
6840 }
6841
6842 ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6843 {
6844 if (objPtr->typePtr != &exprObjType) {
6845 if (SetExprFromAny(interp, objPtr) != JIM_OK)
6846 return NULL;
6847 }
6848 return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6849 }
6850
6851 /* -----------------------------------------------------------------------------
6852 * Expressions evaluation.
6853 * Jim uses a specialized stack-based virtual machine for expressions,
6854 * that takes advantage of the fact that expr's operators
6855 * can't be redefined.
6856 *
6857 * Jim_EvalExpression() uses the bytecode compiled by
6858 * SetExprFromAny() method of the "expression" object.
6859 *
6860 * On success a Tcl Object containing the result of the evaluation
6861 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6862 * returned.
6863 * On error the function returns a retcode != to JIM_OK and set a suitable
6864 * error on the interp.
6865 * ---------------------------------------------------------------------------*/
6866 #define JIM_EE_STATICSTACK_LEN 10
6867
6868 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6869 Jim_Obj **exprResultPtrPtr)
6870 {
6871 ExprByteCode *expr;
6872 Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6873 int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6874
6875 Jim_IncrRefCount(exprObjPtr);
6876 expr = Jim_GetExpression(interp, exprObjPtr);
6877 if (!expr) {
6878 Jim_DecrRefCount(interp, exprObjPtr);
6879 return JIM_ERR; /* error in expression. */
6880 }
6881 /* In order to avoid that the internal repr gets freed due to
6882 * shimmering of the exprObjPtr's object, we make the internal rep
6883 * shared. */
6884 expr->inUse++;
6885
6886 /* The stack-based expr VM itself */
6887
6888 /* Stack allocation. Expr programs have the feature that
6889 * a program of length N can't require a stack longer than
6890 * N. */
6891 if (expr->len > JIM_EE_STATICSTACK_LEN)
6892 stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6893 else
6894 stack = staticStack;
6895
6896 /* Execute every istruction */
6897 for (i = 0; i < expr->len; i++) {
6898 Jim_Obj *A, *B, *objPtr;
6899 jim_wide wA, wB, wC;
6900 double dA, dB, dC;
6901 const char *sA, *sB;
6902 int Alen, Blen, retcode;
6903 int opcode = expr->opcode[i];
6904
6905 if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6906 stack[stacklen++] = expr->obj[i];
6907 Jim_IncrRefCount(expr->obj[i]);
6908 } else if (opcode == JIM_EXPROP_VARIABLE) {
6909 objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6910 if (objPtr == NULL) {
6911 error = 1;
6912 goto err;
6913 }
6914 stack[stacklen++] = objPtr;
6915 Jim_IncrRefCount(objPtr);
6916 } else if (opcode == JIM_EXPROP_SUBST) {
6917 if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6918 &objPtr, JIM_NONE)) != JIM_OK)
6919 {
6920 error = 1;
6921 errRetCode = retcode;
6922 goto err;
6923 }
6924 stack[stacklen++] = objPtr;
6925 Jim_IncrRefCount(objPtr);
6926 } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6927 objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6928 if (objPtr == NULL) {
6929 error = 1;
6930 goto err;
6931 }
6932 stack[stacklen++] = objPtr;
6933 Jim_IncrRefCount(objPtr);
6934 } else if (opcode == JIM_EXPROP_COMMAND) {
6935 if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6936 error = 1;
6937 errRetCode = retcode;
6938 goto err;
6939 }
6940 stack[stacklen++] = interp->result;
6941 Jim_IncrRefCount(interp->result);
6942 } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6943 opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6944 {
6945 /* Note that there isn't to increment the
6946 * refcount of objects. the references are moved
6947 * from stack to A and B. */
6948 B = stack[--stacklen];
6949 A = stack[--stacklen];
6950
6951 /* --- Integer --- */
6952 if ((A->typePtr == &doubleObjType && !A->bytes) ||
6953 (B->typePtr == &doubleObjType && !B->bytes) ||
6954 JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6955 JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6956 goto trydouble;
6957 }
6958 Jim_DecrRefCount(interp, A);
6959 Jim_DecrRefCount(interp, B);
6960 switch(expr->opcode[i]) {
6961 case JIM_EXPROP_ADD: wC = wA+wB; break;
6962 case JIM_EXPROP_SUB: wC = wA-wB; break;
6963 case JIM_EXPROP_MUL: wC = wA*wB; break;
6964 case JIM_EXPROP_LT: wC = wA<wB; break;
6965 case JIM_EXPROP_GT: wC = wA>wB; break;
6966 case JIM_EXPROP_LTE: wC = wA<=wB; break;
6967 case JIM_EXPROP_GTE: wC = wA>=wB; break;
6968 case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6969 case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6970 case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6971 case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6972 case JIM_EXPROP_BITAND: wC = wA&wB; break;
6973 case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6974 case JIM_EXPROP_BITOR: wC = wA|wB; break;
6975 case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6976 case JIM_EXPROP_LOGICAND_LEFT:
6977 if (wA == 0) {
6978 i += (int)wB;
6979 wC = 0;
6980 } else {
6981 continue;
6982 }
6983 break;
6984 case JIM_EXPROP_LOGICOR_LEFT:
6985 if (wA != 0) {
6986 i += (int)wB;
6987 wC = 1;
6988 } else {
6989 continue;
6990 }
6991 break;
6992 case JIM_EXPROP_DIV:
6993 if (wB == 0) goto divbyzero;
6994 wC = wA/wB;
6995 break;
6996 case JIM_EXPROP_MOD:
6997 if (wB == 0) goto divbyzero;
6998 wC = wA%wB;
6999 break;
7000 case JIM_EXPROP_ROTL: {
7001 /* uint32_t would be better. But not everyone has inttypes.h?*/
7002 unsigned long uA = (unsigned long)wA;
7003 #ifdef _MSC_VER
7004 wC = _rotl(uA,(unsigned long)wB);
7005 #else
7006 const unsigned int S = sizeof(unsigned long) * 8;
7007 wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
7008 #endif
7009 break;
7010 }
7011 case JIM_EXPROP_ROTR: {
7012 unsigned long uA = (unsigned long)wA;
7013 #ifdef _MSC_VER
7014 wC = _rotr(uA,(unsigned long)wB);
7015 #else
7016 const unsigned int S = sizeof(unsigned long) * 8;
7017 wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
7018 #endif
7019 break;
7020 }
7021
7022 default:
7023 wC = 0; /* avoid gcc warning */
7024 break;
7025 }
7026 stack[stacklen] = Jim_NewIntObj(interp, wC);
7027 Jim_IncrRefCount(stack[stacklen]);
7028 stacklen++;
7029 continue;
7030 trydouble:
7031 /* --- Double --- */
7032 if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
7033 Jim_GetDouble(interp, B, &dB) != JIM_OK) {
7034
7035 /* Hmmm! For compatibility, maybe convert != and == into ne and eq */
7036 if (expr->opcode[i] == JIM_EXPROP_NUMNE) {
7037 opcode = JIM_EXPROP_STRNE;
7038 goto retry_as_string;
7039 }
7040 else if (expr->opcode[i] == JIM_EXPROP_NUMEQ) {
7041 opcode = JIM_EXPROP_STREQ;
7042 goto retry_as_string;
7043 }
7044 Jim_DecrRefCount(interp, A);
7045 Jim_DecrRefCount(interp, B);
7046 error = 1;
7047 goto err;
7048 }
7049 Jim_DecrRefCount(interp, A);
7050 Jim_DecrRefCount(interp, B);
7051 switch(expr->opcode[i]) {
7052 case JIM_EXPROP_ROTL:
7053 case JIM_EXPROP_ROTR:
7054 case JIM_EXPROP_LSHIFT:
7055 case JIM_EXPROP_RSHIFT:
7056 case JIM_EXPROP_BITAND:
7057 case JIM_EXPROP_BITXOR:
7058 case JIM_EXPROP_BITOR:
7059 case JIM_EXPROP_MOD:
7060 case JIM_EXPROP_POW:
7061 Jim_SetResultString(interp,
7062 "Got floating-point value where integer was expected", -1);
7063 error = 1;
7064 goto err;
7065 break;
7066 case JIM_EXPROP_ADD: dC = dA+dB; break;
7067 case JIM_EXPROP_SUB: dC = dA-dB; break;
7068 case JIM_EXPROP_MUL: dC = dA*dB; break;
7069 case JIM_EXPROP_LT: dC = dA<dB; break;
7070 case JIM_EXPROP_GT: dC = dA>dB; break;
7071 case JIM_EXPROP_LTE: dC = dA<=dB; break;
7072 case JIM_EXPROP_GTE: dC = dA>=dB; break;
7073 case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
7074 case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
7075 case JIM_EXPROP_LOGICAND_LEFT:
7076 if (dA == 0) {
7077 i += (int)dB;
7078 dC = 0;
7079 } else {
7080 continue;
7081 }
7082 break;
7083 case JIM_EXPROP_LOGICOR_LEFT:
7084 if (dA != 0) {
7085 i += (int)dB;
7086 dC = 1;
7087 } else {
7088 continue;
7089 }
7090 break;
7091 case JIM_EXPROP_DIV:
7092 if (dB == 0) goto divbyzero;
7093 dC = dA/dB;
7094 break;
7095 default:
7096 dC = 0; /* avoid gcc warning */
7097 break;
7098 }
7099 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7100 Jim_IncrRefCount(stack[stacklen]);
7101 stacklen++;
7102 } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
7103 B = stack[--stacklen];
7104 A = stack[--stacklen];
7105 retry_as_string:
7106 sA = Jim_GetString(A, &Alen);
7107 sB = Jim_GetString(B, &Blen);
7108 switch(opcode) {
7109 case JIM_EXPROP_STREQ:
7110 if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
7111 wC = 1;
7112 else
7113 wC = 0;
7114 break;
7115 case JIM_EXPROP_STRNE:
7116 if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
7117 wC = 1;
7118 else
7119 wC = 0;
7120 break;
7121 default:
7122 wC = 0; /* avoid gcc warning */
7123 break;
7124 }
7125 Jim_DecrRefCount(interp, A);
7126 Jim_DecrRefCount(interp, B);
7127 stack[stacklen] = Jim_NewIntObj(interp, wC);
7128 Jim_IncrRefCount(stack[stacklen]);
7129 stacklen++;
7130 } else if (opcode == JIM_EXPROP_NOT ||
7131 opcode == JIM_EXPROP_BITNOT ||
7132 opcode == JIM_EXPROP_LOGICAND_RIGHT ||
7133 opcode == JIM_EXPROP_LOGICOR_RIGHT) {
7134 /* Note that there isn't to increment the
7135 * refcount of objects. the references are moved
7136 * from stack to A and B. */
7137 A = stack[--stacklen];
7138
7139 /* --- Integer --- */
7140 if ((A->typePtr == &doubleObjType && !A->bytes) ||
7141 JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
7142 goto trydouble_unary;
7143 }
7144 Jim_DecrRefCount(interp, A);
7145 switch(expr->opcode[i]) {
7146 case JIM_EXPROP_NOT: wC = !wA; break;
7147 case JIM_EXPROP_BITNOT: wC = ~wA; break;
7148 case JIM_EXPROP_LOGICAND_RIGHT:
7149 case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
7150 default:
7151 wC = 0; /* avoid gcc warning */
7152 break;
7153 }
7154 stack[stacklen] = Jim_NewIntObj(interp, wC);
7155 Jim_IncrRefCount(stack[stacklen]);
7156 stacklen++;
7157 continue;
7158 trydouble_unary:
7159 /* --- Double --- */
7160 if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
7161 Jim_DecrRefCount(interp, A);
7162 error = 1;
7163 goto err;
7164 }
7165 Jim_DecrRefCount(interp, A);
7166 switch(expr->opcode[i]) {
7167 case JIM_EXPROP_NOT: dC = !dA; break;
7168 case JIM_EXPROP_LOGICAND_RIGHT:
7169 case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
7170 case JIM_EXPROP_BITNOT:
7171 Jim_SetResultString(interp,
7172 "Got floating-point value where integer was expected", -1);
7173 error = 1;
7174 goto err;
7175 break;
7176 default:
7177 dC = 0; /* avoid gcc warning */
7178 break;
7179 }
7180 stack[stacklen] = Jim_NewDoubleObj(interp, dC);
7181 Jim_IncrRefCount(stack[stacklen]);
7182 stacklen++;
7183 } else {
7184 Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
7185 }
7186 }
7187 err:
7188 /* There is no need to decerement the inUse field because
7189 * this reference is transfered back into the exprObjPtr. */
7190 Jim_FreeIntRep(interp, exprObjPtr);
7191 exprObjPtr->typePtr = &exprObjType;
7192 Jim_SetIntRepPtr(exprObjPtr, expr);
7193 Jim_DecrRefCount(interp, exprObjPtr);
7194 if (!error) {
7195 *exprResultPtrPtr = stack[0];
7196 Jim_IncrRefCount(stack[0]);
7197 errRetCode = JIM_OK;
7198 }
7199 for (i = 0; i < stacklen; i++) {
7200 Jim_DecrRefCount(interp, stack[i]);
7201 }
7202 if (stack != staticStack)
7203 Jim_Free(stack);
7204 return errRetCode;
7205 divbyzero:
7206 error = 1;
7207 Jim_SetResultString(interp, "Division by zero", -1);
7208 goto err;
7209 }
7210
7211 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
7212 {
7213 int retcode;
7214 jim_wide wideValue;
7215 double doubleValue;
7216 Jim_Obj *exprResultPtr;
7217
7218 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
7219 if (retcode != JIM_OK)
7220 return retcode;
7221 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
7222 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
7223 {
7224 Jim_DecrRefCount(interp, exprResultPtr);
7225 return JIM_ERR;
7226 } else {
7227 Jim_DecrRefCount(interp, exprResultPtr);
7228 *boolPtr = doubleValue != 0;
7229 return JIM_OK;
7230 }
7231 }
7232 Jim_DecrRefCount(interp, exprResultPtr);
7233 *boolPtr = wideValue != 0;
7234 return JIM_OK;
7235 }
7236
7237 /* -----------------------------------------------------------------------------
7238 * ScanFormat String Object
7239 * ---------------------------------------------------------------------------*/
7240
7241 /* This Jim_Obj will held a parsed representation of a format string passed to
7242 * the Jim_ScanString command. For error diagnostics, the scanformat string has
7243 * to be parsed in its entirely first and then, if correct, can be used for
7244 * scanning. To avoid endless re-parsing, the parsed representation will be
7245 * stored in an internal representation and re-used for performance reason. */
7246
7247 /* A ScanFmtPartDescr will held the information of /one/ part of the whole
7248 * scanformat string. This part will later be used to extract information
7249 * out from the string to be parsed by Jim_ScanString */
7250
7251 typedef struct ScanFmtPartDescr {
7252 char type; /* Type of conversion (e.g. c, d, f) */
7253 char modifier; /* Modify type (e.g. l - long, h - short */
7254 size_t width; /* Maximal width of input to be converted */
7255 int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
7256 char *arg; /* Specification of a CHARSET conversion */
7257 char *prefix; /* Prefix to be scanned literally before conversion */
7258 } ScanFmtPartDescr;
7259
7260 /* The ScanFmtStringObj will held the internal representation of a scanformat
7261 * string parsed and separated in part descriptions. Furthermore it contains
7262 * the original string representation of the scanformat string to allow for
7263 * fast update of the Jim_Obj's string representation part.
7264 *
7265 * As add-on the internal object representation add some scratch pad area
7266 * for usage by Jim_ScanString to avoid endless allocating and freeing of
7267 * memory for purpose of string scanning.
7268 *
7269 * The error member points to a static allocated string in case of a mal-
7270 * formed scanformat string or it contains '0' (NULL) in case of a valid
7271 * parse representation.
7272 *
7273 * The whole memory of the internal representation is allocated as a single
7274 * area of memory that will be internally separated. So freeing and duplicating
7275 * of such an object is cheap */
7276
7277 typedef struct ScanFmtStringObj {
7278 jim_wide size; /* Size of internal repr in bytes */
7279 char *stringRep; /* Original string representation */
7280 size_t count; /* Number of ScanFmtPartDescr contained */
7281 size_t convCount; /* Number of conversions that will assign */
7282 size_t maxPos; /* Max position index if XPG3 is used */
7283 const char *error; /* Ptr to error text (NULL if no error */
7284 char *scratch; /* Some scratch pad used by Jim_ScanString */
7285 ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
7286 } ScanFmtStringObj;
7287
7288
7289 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7290 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7291 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
7292
7293 static Jim_ObjType scanFmtStringObjType = {
7294 "scanformatstring",
7295 FreeScanFmtInternalRep,
7296 DupScanFmtInternalRep,
7297 UpdateStringOfScanFmt,
7298 JIM_TYPE_NONE,
7299 };
7300
7301 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7302 {
7303 JIM_NOTUSED(interp);
7304 Jim_Free((char*)objPtr->internalRep.ptr);
7305 objPtr->internalRep.ptr = 0;
7306 }
7307
7308 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7309 {
7310 size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
7311 ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
7312
7313 JIM_NOTUSED(interp);
7314 memcpy(newVec, srcPtr->internalRep.ptr, size);
7315 dupPtr->internalRep.ptr = newVec;
7316 dupPtr->typePtr = &scanFmtStringObjType;
7317 }
7318
7319 void UpdateStringOfScanFmt(Jim_Obj *objPtr)
7320 {
7321 char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
7322
7323 objPtr->bytes = Jim_StrDup(bytes);
7324 objPtr->length = strlen(bytes);
7325 }
7326
7327 /* SetScanFmtFromAny will parse a given string and create the internal
7328 * representation of the format specification. In case of an error
7329 * the error data member of the internal representation will be set
7330 * to an descriptive error text and the function will be left with
7331 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
7332 * specification */
7333
7334 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7335 {
7336 ScanFmtStringObj *fmtObj;
7337 char *buffer;
7338 int maxCount, i, approxSize, lastPos = -1;
7339 const char *fmt = objPtr->bytes;
7340 int maxFmtLen = objPtr->length;
7341 const char *fmtEnd = fmt + maxFmtLen;
7342 int curr;
7343
7344 Jim_FreeIntRep(interp, objPtr);
7345 /* Count how many conversions could take place maximally */
7346 for (i=0, maxCount=0; i < maxFmtLen; ++i)
7347 if (fmt[i] == '%')
7348 ++maxCount;
7349 /* Calculate an approximation of the memory necessary */
7350 approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
7351 + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
7352 + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
7353 + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
7354 + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
7355 + (maxCount +1) * sizeof(char) /* '\0' for every partial */
7356 + 1; /* safety byte */
7357 fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
7358 memset(fmtObj, 0, approxSize);
7359 fmtObj->size = approxSize;
7360 fmtObj->maxPos = 0;
7361 fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
7362 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
7363 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
7364 buffer = fmtObj->stringRep + maxFmtLen + 1;
7365 objPtr->internalRep.ptr = fmtObj;
7366 objPtr->typePtr = &scanFmtStringObjType;
7367 for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
7368 int width=0, skip;
7369 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
7370 fmtObj->count++;
7371 descr->width = 0; /* Assume width unspecified */
7372 /* Overread and store any "literal" prefix */
7373 if (*fmt != '%' || fmt[1] == '%') {
7374 descr->type = 0;
7375 descr->prefix = &buffer[i];
7376 for (; fmt < fmtEnd; ++fmt) {
7377 if (*fmt == '%') {
7378 if (fmt[1] != '%') break;
7379 ++fmt;
7380 }
7381 buffer[i++] = *fmt;
7382 }
7383 buffer[i++] = 0;
7384 }
7385 /* Skip the conversion introducing '%' sign */
7386 ++fmt;
7387 /* End reached due to non-conversion literal only? */
7388 if (fmt >= fmtEnd)
7389 goto done;
7390 descr->pos = 0; /* Assume "natural" positioning */
7391 if (*fmt == '*') {
7392 descr->pos = -1; /* Okay, conversion will not be assigned */
7393 ++fmt;
7394 } else
7395 fmtObj->convCount++; /* Otherwise count as assign-conversion */
7396 /* Check if next token is a number (could be width or pos */
7397 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7398 fmt += skip;
7399 /* Was the number a XPG3 position specifier? */
7400 if (descr->pos != -1 && *fmt == '$') {
7401 int prev;
7402 ++fmt;
7403 descr->pos = width;
7404 width = 0;
7405 /* Look if "natural" postioning and XPG3 one was mixed */
7406 if ((lastPos == 0 && descr->pos > 0)
7407 || (lastPos > 0 && descr->pos == 0)) {
7408 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
7409 return JIM_ERR;
7410 }
7411 /* Look if this position was already used */
7412 for (prev=0; prev < curr; ++prev) {
7413 if (fmtObj->descr[prev].pos == -1) continue;
7414 if (fmtObj->descr[prev].pos == descr->pos) {
7415 fmtObj->error = "same \"%n$\" conversion specifier "
7416 "used more than once";
7417 return JIM_ERR;
7418 }
7419 }
7420 /* Try to find a width after the XPG3 specifier */
7421 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7422 descr->width = width;
7423 fmt += skip;
7424 }
7425 if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7426 fmtObj->maxPos = descr->pos;
7427 } else {
7428 /* Number was not a XPG3, so it has to be a width */
7429 descr->width = width;
7430 }
7431 }
7432 /* If positioning mode was undetermined yet, fix this */
7433 if (lastPos == -1)
7434 lastPos = descr->pos;
7435 /* Handle CHARSET conversion type ... */
7436 if (*fmt == '[') {
7437 int swapped = 1, beg = i, end, j;
7438 descr->type = '[';
7439 descr->arg = &buffer[i];
7440 ++fmt;
7441 if (*fmt == '^') buffer[i++] = *fmt++;
7442 if (*fmt == ']') buffer[i++] = *fmt++;
7443 while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7444 if (*fmt != ']') {
7445 fmtObj->error = "unmatched [ in format string";
7446 return JIM_ERR;
7447 }
7448 end = i;
7449 buffer[i++] = 0;
7450 /* In case a range fence was given "backwards", swap it */
7451 while (swapped) {
7452 swapped = 0;
7453 for (j=beg+1; j < end-1; ++j) {
7454 if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7455 char tmp = buffer[j-1];
7456 buffer[j-1] = buffer[j+1];
7457 buffer[j+1] = tmp;
7458 swapped = 1;
7459 }
7460 }
7461 }
7462 } else {
7463 /* Remember any valid modifier if given */
7464 if (strchr("hlL", *fmt) != 0)
7465 descr->modifier = tolower((int)*fmt++);
7466
7467 descr->type = *fmt;
7468 if (strchr("efgcsndoxui", *fmt) == 0) {
7469 fmtObj->error = "bad scan conversion character";
7470 return JIM_ERR;
7471 } else if (*fmt == 'c' && descr->width != 0) {
7472 fmtObj->error = "field width may not be specified in %c "
7473 "conversion";
7474 return JIM_ERR;
7475 } else if (*fmt == 'u' && descr->modifier == 'l') {
7476 fmtObj->error = "unsigned wide not supported";
7477 return JIM_ERR;
7478 }
7479 }
7480 curr++;
7481 }
7482 done:
7483 if (fmtObj->convCount == 0) {
7484 fmtObj->error = "no any conversion specifier given";
7485 return JIM_ERR;
7486 }
7487 return JIM_OK;
7488 }
7489
7490 /* Some accessor macros to allow lowlevel access to fields of internal repr */
7491
7492 #define FormatGetCnvCount(_fo_) \
7493 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7494 #define FormatGetMaxPos(_fo_) \
7495 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7496 #define FormatGetError(_fo_) \
7497 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7498
7499 /* Some Bit testing/setting/cleaning routines. For now only used in handling
7500 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7501 * bitvector implementation in Jim? */
7502
7503 static int JimTestBit(const char *bitvec, char ch)
7504 {
7505 div_t pos = div(ch-1, 8);
7506 return bitvec[pos.quot] & (1 << pos.rem);
7507 }
7508
7509 static void JimSetBit(char *bitvec, char ch)
7510 {
7511 div_t pos = div(ch-1, 8);
7512 bitvec[pos.quot] |= (1 << pos.rem);
7513 }
7514
7515 #if 0 /* currently not used */
7516 static void JimClearBit(char *bitvec, char ch)
7517 {
7518 div_t pos = div(ch-1, 8);
7519 bitvec[pos.quot] &= ~(1 << pos.rem);
7520 }
7521 #endif
7522
7523 /* JimScanAString is used to scan an unspecified string that ends with
7524 * next WS, or a string that is specified via a charset. The charset
7525 * is currently implemented in a way to only allow for usage with
7526 * ASCII. Whenever we will switch to UNICODE, another idea has to
7527 * be born :-/
7528 *
7529 * FIXME: Works only with ASCII */
7530
7531 static Jim_Obj *
7532 JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7533 {
7534 size_t i;
7535 Jim_Obj *result;
7536 char charset[256/8+1]; /* A Charset may contain max 256 chars */
7537 char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7538
7539 /* First init charset to nothing or all, depending if a specified
7540 * or an unspecified string has to be parsed */
7541 memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7542 if (sdescr) {
7543 /* There was a set description given, that means we are parsing
7544 * a specified string. So we have to build a corresponding
7545 * charset reflecting the description */
7546 int notFlag = 0;
7547 /* Should the set be negated at the end? */
7548 if (*sdescr == '^') {
7549 notFlag = 1;
7550 ++sdescr;
7551 }
7552 /* Here '-' is meant literally and not to define a range */
7553 if (*sdescr == '-') {
7554 JimSetBit(charset, '-');
7555 ++sdescr;
7556 }
7557 while (*sdescr) {
7558 if (sdescr[1] == '-' && sdescr[2] != 0) {
7559 /* Handle range definitions */
7560 int i;
7561 for (i=sdescr[0]; i <= sdescr[2]; ++i)
7562 JimSetBit(charset, (char)i);
7563 sdescr += 3;
7564 } else {
7565 /* Handle verbatim character definitions */
7566 JimSetBit(charset, *sdescr++);
7567 }
7568 }
7569 /* Negate the charset if there was a NOT given */
7570 for (i=0; notFlag && i < sizeof(charset); ++i)
7571 charset[i] = ~charset[i];
7572 }
7573 /* And after all the mess above, the real work begin ... */
7574 while (str && *str) {
7575 if (!sdescr && isspace((int)*str))
7576 break; /* EOS via WS if unspecified */
7577 if (JimTestBit(charset, *str)) *buffer++ = *str++;
7578 else break; /* EOS via mismatch if specified scanning */
7579 }
7580 *buffer = 0; /* Close the string properly ... */
7581 result = Jim_NewStringObj(interp, anchor, -1);
7582 Jim_Free(anchor); /* ... and free it afer usage */
7583 return result;
7584 }
7585
7586 /* ScanOneEntry will scan one entry out of the string passed as argument.
7587 * It use the sscanf() function for this task. After extracting and
7588 * converting of the value, the count of scanned characters will be
7589 * returned of -1 in case of no conversion tool place and string was
7590 * already scanned thru */
7591
7592 static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7593 ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7594 {
7595 # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7596 ? sizeof(jim_wide) \
7597 : sizeof(double))
7598 char buffer[MAX_SIZE];
7599 char *value = buffer;
7600 const char *tok;
7601 const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7602 size_t sLen = strlen(&str[pos]), scanned = 0;
7603 size_t anchor = pos;
7604 int i;
7605
7606 /* First pessimiticly assume, we will not scan anything :-) */
7607 *valObjPtr = 0;
7608 if (descr->prefix) {
7609 /* There was a prefix given before the conversion, skip it and adjust
7610 * the string-to-be-parsed accordingly */
7611 for (i=0; str[pos] && descr->prefix[i]; ++i) {
7612 /* If prefix require, skip WS */
7613 if (isspace((int)descr->prefix[i]))
7614 while (str[pos] && isspace((int)str[pos])) ++pos;
7615 else if (descr->prefix[i] != str[pos])
7616 break; /* Prefix do not match here, leave the loop */
7617 else
7618 ++pos; /* Prefix matched so far, next round */
7619 }
7620 if (str[pos] == 0)
7621 return -1; /* All of str consumed: EOF condition */
7622 else if (descr->prefix[i] != 0)
7623 return 0; /* Not whole prefix consumed, no conversion possible */
7624 }
7625 /* For all but following conversion, skip leading WS */
7626 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7627 while (isspace((int)str[pos])) ++pos;
7628 /* Determine how much skipped/scanned so far */
7629 scanned = pos - anchor;
7630 if (descr->type == 'n') {
7631 /* Return pseudo conversion means: how much scanned so far? */
7632 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7633 } else if (str[pos] == 0) {
7634 /* Cannot scan anything, as str is totally consumed */
7635 return -1;
7636 } else {
7637 /* Processing of conversions follows ... */
7638 if (descr->width > 0) {
7639 /* Do not try to scan as fas as possible but only the given width.
7640 * To ensure this, we copy the part that should be scanned. */
7641 size_t tLen = descr->width > sLen ? sLen : descr->width;
7642 tok = Jim_StrDupLen(&str[pos], tLen);
7643 } else {
7644 /* As no width was given, simply refer to the original string */
7645 tok = &str[pos];
7646 }
7647 switch (descr->type) {
7648 case 'c':
7649 *valObjPtr = Jim_NewIntObj(interp, *tok);
7650 scanned += 1;
7651 break;
7652 case 'd': case 'o': case 'x': case 'u': case 'i': {
7653 jim_wide jwvalue;
7654 long lvalue;
7655 char *endp; /* Position where the number finished */
7656 int base = descr->type == 'o' ? 8
7657 : descr->type == 'x' ? 16
7658 : descr->type == 'i' ? 0
7659 : 10;
7660
7661 do {
7662 /* Try to scan a number with the given base */
7663 if (descr->modifier == 'l')
7664 {
7665 #ifdef HAVE_LONG_LONG
7666 jwvalue = JimStrtoll(tok, &endp, base),
7667 #else
7668 jwvalue = strtol(tok, &endp, base),
7669 #endif
7670 memcpy(value, &jwvalue, sizeof(jim_wide));
7671 }
7672 else
7673 {
7674 if (descr->type == 'u')
7675 lvalue = strtoul(tok, &endp, base);
7676 else
7677 lvalue = strtol(tok, &endp, base);
7678 memcpy(value, &lvalue, sizeof(lvalue));
7679 }
7680 /* If scanning failed, and base was undetermined, simply
7681 * put it to 10 and try once more. This should catch the
7682 * case where %i begin to parse a number prefix (e.g.
7683 * '0x' but no further digits follows. This will be
7684 * handled as a ZERO followed by a char 'x' by Tcl */
7685 if (endp == tok && base == 0) base = 10;
7686 else break;
7687 } while (1);
7688 if (endp != tok) {
7689 /* There was some number sucessfully scanned! */
7690 if (descr->modifier == 'l')
7691 *valObjPtr = Jim_NewIntObj(interp, jwvalue);
7692 else
7693 *valObjPtr = Jim_NewIntObj(interp, lvalue);
7694 /* Adjust the number-of-chars scanned so far */
7695 scanned += endp - tok;
7696 } else {
7697 /* Nothing was scanned. We have to determine if this
7698 * happened due to e.g. prefix mismatch or input str
7699 * exhausted */
7700 scanned = *tok ? 0 : -1;
7701 }
7702 break;
7703 }
7704 case 's': case '[': {
7705 *valObjPtr = JimScanAString(interp, descr->arg, tok);
7706 scanned += Jim_Length(*valObjPtr);
7707 break;
7708 }
7709 case 'e': case 'f': case 'g': {
7710 char *endp;
7711
7712 double dvalue = strtod(tok, &endp);
7713 memcpy(value, &dvalue, sizeof(double));
7714 if (endp != tok) {
7715 /* There was some number sucessfully scanned! */
7716 *valObjPtr = Jim_NewDoubleObj(interp, dvalue);
7717 /* Adjust the number-of-chars scanned so far */
7718 scanned += endp - tok;
7719 } else {
7720 /* Nothing was scanned. We have to determine if this
7721 * happened due to e.g. prefix mismatch or input str
7722 * exhausted */
7723 scanned = *tok ? 0 : -1;
7724 }
7725 break;
7726 }
7727 }
7728 /* If a substring was allocated (due to pre-defined width) do not
7729 * forget to free it */
7730 if (tok != &str[pos])
7731 Jim_Free((char*)tok);
7732 }
7733 return scanned;
7734 }
7735
7736 /* Jim_ScanString is the workhorse of string scanning. It will scan a given
7737 * string and returns all converted (and not ignored) values in a list back
7738 * to the caller. If an error occured, a NULL pointer will be returned */
7739
7740 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7741 Jim_Obj *fmtObjPtr, int flags)
7742 {
7743 size_t i, pos;
7744 int scanned = 1;
7745 const char *str = Jim_GetString(strObjPtr, 0);
7746 Jim_Obj *resultList = 0;
7747 Jim_Obj **resultVec;
7748 int resultc;
7749 Jim_Obj *emptyStr = 0;
7750 ScanFmtStringObj *fmtObj;
7751
7752 /* If format specification is not an object, convert it! */
7753 if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7754 SetScanFmtFromAny(interp, fmtObjPtr);
7755 fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7756 /* Check if format specification was valid */
7757 if (fmtObj->error != 0) {
7758 if (flags & JIM_ERRMSG)
7759 Jim_SetResultString(interp, fmtObj->error, -1);
7760 return 0;
7761 }
7762 /* Allocate a new "shared" empty string for all unassigned conversions */
7763 emptyStr = Jim_NewEmptyStringObj(interp);
7764 Jim_IncrRefCount(emptyStr);
7765 /* Create a list and fill it with empty strings up to max specified XPG3 */
7766 resultList = Jim_NewListObj(interp, 0, 0);
7767 if (fmtObj->maxPos > 0) {
7768 for (i=0; i < fmtObj->maxPos; ++i)
7769 Jim_ListAppendElement(interp, resultList, emptyStr);
7770 JimListGetElements(interp, resultList, &resultc, &resultVec);
7771 }
7772 /* Now handle every partial format description */
7773 for (i=0, pos=0; i < fmtObj->count; ++i) {
7774 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7775 Jim_Obj *value = 0;
7776 /* Only last type may be "literal" w/o conversion - skip it! */
7777 if (descr->type == 0) continue;
7778 /* As long as any conversion could be done, we will proceed */
7779 if (scanned > 0)
7780 scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7781 /* In case our first try results in EOF, we will leave */
7782 if (scanned == -1 && i == 0)
7783 goto eof;
7784 /* Advance next pos-to-be-scanned for the amount scanned already */
7785 pos += scanned;
7786 /* value == 0 means no conversion took place so take empty string */
7787 if (value == 0)
7788 value = Jim_NewEmptyStringObj(interp);
7789 /* If value is a non-assignable one, skip it */
7790 if (descr->pos == -1) {
7791 Jim_FreeNewObj(interp, value);
7792 } else if (descr->pos == 0)
7793 /* Otherwise append it to the result list if no XPG3 was given */
7794 Jim_ListAppendElement(interp, resultList, value);
7795 else if (resultVec[descr->pos-1] == emptyStr) {
7796 /* But due to given XPG3, put the value into the corr. slot */
7797 Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7798 Jim_IncrRefCount(value);
7799 resultVec[descr->pos-1] = value;
7800 } else {
7801 /* Otherwise, the slot was already used - free obj and ERROR */
7802 Jim_FreeNewObj(interp, value);
7803 goto err;
7804 }
7805 }
7806 Jim_DecrRefCount(interp, emptyStr);
7807 return resultList;
7808 eof:
7809 Jim_DecrRefCount(interp, emptyStr);
7810 Jim_FreeNewObj(interp, resultList);
7811 return (Jim_Obj*)EOF;
7812 err:
7813 Jim_DecrRefCount(interp, emptyStr);
7814 Jim_FreeNewObj(interp, resultList);
7815 return 0;
7816 }
7817
7818 /* -----------------------------------------------------------------------------
7819 * Pseudo Random Number Generation
7820 * ---------------------------------------------------------------------------*/
7821 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7822 int seedLen);
7823
7824 /* Initialize the sbox with the numbers from 0 to 255 */
7825 static void JimPrngInit(Jim_Interp *interp)
7826 {
7827 int i;
7828 unsigned int seed[256];
7829
7830 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7831 for (i = 0; i < 256; i++)
7832 seed[i] = (rand() ^ time(NULL) ^ clock());
7833 JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7834 }
7835
7836 /* Generates N bytes of random data */
7837 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7838 {
7839 Jim_PrngState *prng;
7840 unsigned char *destByte = (unsigned char*) dest;
7841 unsigned int si, sj, x;
7842
7843 /* initialization, only needed the first time */
7844 if (interp->prngState == NULL)
7845 JimPrngInit(interp);
7846 prng = interp->prngState;
7847 /* generates 'len' bytes of pseudo-random numbers */
7848 for (x = 0; x < len; x++) {
7849 prng->i = (prng->i+1) & 0xff;
7850 si = prng->sbox[prng->i];
7851 prng->j = (prng->j + si) & 0xff;
7852 sj = prng->sbox[prng->j];
7853 prng->sbox[prng->i] = sj;
7854 prng->sbox[prng->j] = si;
7855 *destByte++ = prng->sbox[(si+sj)&0xff];
7856 }
7857 }
7858
7859 /* Re-seed the generator with user-provided bytes */
7860 static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7861 int seedLen)
7862 {
7863 int i;
7864 unsigned char buf[256];
7865 Jim_PrngState *prng;
7866
7867 /* initialization, only needed the first time */
7868 if (interp->prngState == NULL)
7869 JimPrngInit(interp);
7870 prng = interp->prngState;
7871
7872 /* Set the sbox[i] with i */
7873 for (i = 0; i < 256; i++)
7874 prng->sbox[i] = i;
7875 /* Now use the seed to perform a random permutation of the sbox */
7876 for (i = 0; i < seedLen; i++) {
7877 unsigned char t;
7878
7879 t = prng->sbox[i&0xFF];
7880 prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7881 prng->sbox[seed[i]] = t;
7882 }
7883 prng->i = prng->j = 0;
7884 /* discard the first 256 bytes of stream. */
7885 JimRandomBytes(interp, buf, 256);
7886 }
7887
7888 /* -----------------------------------------------------------------------------
7889 * Dynamic libraries support (WIN32 not supported)
7890 * ---------------------------------------------------------------------------*/
7891
7892 #ifdef JIM_DYNLIB
7893 #ifdef WIN32
7894 #define RTLD_LAZY 0
7895 void * dlopen(const char *path, int mode)
7896 {
7897 JIM_NOTUSED(mode);
7898
7899 return (void *)LoadLibraryA(path);
7900 }
7901 int dlclose(void *handle)
7902 {
7903 FreeLibrary((HANDLE)handle);
7904 return 0;
7905 }
7906 void *dlsym(void *handle, const char *symbol)
7907 {
7908 return GetProcAddress((HMODULE)handle, symbol);
7909 }
7910 static char win32_dlerror_string[121];
7911 const char *dlerror(void)
7912 {
7913 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7914 LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7915 return win32_dlerror_string;
7916 }
7917 #endif /* WIN32 */
7918
7919 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7920 {
7921 Jim_Obj *libPathObjPtr;
7922 int prefixc, i;
7923 void *handle;
7924 int (*onload)(Jim_Interp *interp);
7925
7926 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7927 if (libPathObjPtr == NULL) {
7928 prefixc = 0;
7929 libPathObjPtr = NULL;
7930 } else {
7931 Jim_IncrRefCount(libPathObjPtr);
7932 Jim_ListLength(interp, libPathObjPtr, &prefixc);
7933 }
7934
7935 for (i = -1; i < prefixc; i++) {
7936 if (i < 0) {
7937 handle = dlopen(pathName, RTLD_LAZY);
7938 } else {
7939 FILE *fp;
7940 char buf[JIM_PATH_LEN];
7941 const char *prefix;
7942 int prefixlen;
7943 Jim_Obj *prefixObjPtr;
7944
7945 buf[0] = '\0';
7946 if (Jim_ListIndex(interp, libPathObjPtr, i,
7947 &prefixObjPtr, JIM_NONE) != JIM_OK)
7948 continue;
7949 prefix = Jim_GetString(prefixObjPtr, &prefixlen);
7950 if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7951 continue;
7952 if (*pathName == '/') {
7953 strcpy(buf, pathName);
7954 }
7955 else if (prefixlen && prefix[prefixlen-1] == '/')
7956 sprintf(buf, "%s%s", prefix, pathName);
7957 else
7958 sprintf(buf, "%s/%s", prefix, pathName);
7959 fp = fopen(buf, "r");
7960 if (fp == NULL)
7961 continue;
7962 fclose(fp);
7963 handle = dlopen(buf, RTLD_LAZY);
7964 }
7965 if (handle == NULL) {
7966 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7967 Jim_AppendStrings(interp, Jim_GetResult(interp),
7968 "error loading extension \"", pathName,
7969 "\": ", dlerror(), NULL);
7970 if (i < 0)
7971 continue;
7972 goto err;
7973 }
7974 if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7975 Jim_SetResultString(interp,
7976 "No Jim_OnLoad symbol found on extension", -1);
7977 goto err;
7978 }
7979 if (onload(interp) == JIM_ERR) {
7980 dlclose(handle);
7981 goto err;
7982 }
7983 Jim_SetEmptyResult(interp);
7984 if (libPathObjPtr != NULL)
7985 Jim_DecrRefCount(interp, libPathObjPtr);
7986 return JIM_OK;
7987 }
7988 err:
7989 if (libPathObjPtr != NULL)
7990 Jim_DecrRefCount(interp, libPathObjPtr);
7991 return JIM_ERR;
7992 }
7993 #else /* JIM_DYNLIB */
7994 int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7995 {
7996 JIM_NOTUSED(interp);
7997 JIM_NOTUSED(pathName);
7998
7999 Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
8000 return JIM_ERR;
8001 }
8002 #endif/* JIM_DYNLIB */
8003
8004 /* -----------------------------------------------------------------------------
8005 * Packages handling
8006 * ---------------------------------------------------------------------------*/
8007
8008 #define JIM_PKG_ANY_VERSION -1
8009
8010 /* Convert a string of the type "1.2" into an integer.
8011 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
8012 * to the integer with value 102 */
8013 static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
8014 int *intPtr, int flags)
8015 {
8016 char *copy;
8017 jim_wide major, minor;
8018 char *majorStr, *minorStr, *p;
8019
8020 if (v[0] == '\0') {
8021 *intPtr = JIM_PKG_ANY_VERSION;
8022 return JIM_OK;
8023 }
8024
8025 copy = Jim_StrDup(v);
8026 p = strchr(copy, '.');
8027 if (p == NULL) goto badfmt;
8028 *p = '\0';
8029 majorStr = copy;
8030 minorStr = p+1;
8031
8032 if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
8033 Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
8034 goto badfmt;
8035 *intPtr = (int)(major*100+minor);
8036 Jim_Free(copy);
8037 return JIM_OK;
8038
8039 badfmt:
8040 Jim_Free(copy);
8041 if (flags & JIM_ERRMSG) {
8042 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8043 Jim_AppendStrings(interp, Jim_GetResult(interp),
8044 "invalid package version '", v, "'", NULL);
8045 }
8046 return JIM_ERR;
8047 }
8048
8049 #define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
8050 static int JimPackageMatchVersion(int needed, int actual, int flags)
8051 {
8052 if (needed == JIM_PKG_ANY_VERSION) return 1;
8053 if (flags & JIM_MATCHVER_EXACT) {
8054 return needed == actual;
8055 } else {
8056 return needed/100 == actual/100 && (needed <= actual);
8057 }
8058 }
8059
8060 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
8061 int flags)
8062 {
8063 int intVersion;
8064 /* Check if the version format is ok */
8065 if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
8066 return JIM_ERR;
8067 /* If the package was already provided returns an error. */
8068 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
8069 if (flags & JIM_ERRMSG) {
8070 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8071 Jim_AppendStrings(interp, Jim_GetResult(interp),
8072 "package '", name, "' was already provided", NULL);
8073 }
8074 return JIM_ERR;
8075 }
8076 Jim_AddHashEntry(&interp->packages, name, (char*) ver);
8077 return JIM_OK;
8078 }
8079
8080 #ifndef JIM_ANSIC
8081
8082 #ifndef WIN32
8083 # include <sys/types.h>
8084 # include <dirent.h>
8085 #else
8086 # include <io.h>
8087 /* Posix dirent.h compatiblity layer for WIN32.
8088 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
8089 * Copyright Salvatore Sanfilippo ,2005.
8090 *
8091 * Permission to use, copy, modify, and distribute this software and its
8092 * documentation for any purpose is hereby granted without fee, provided
8093 * that this copyright and permissions notice appear in all copies and
8094 * derivatives.
8095 *
8096 * This software is supplied "as is" without express or implied warranty.
8097 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
8098 */
8099
8100 struct dirent {
8101 char *d_name;
8102 };
8103
8104 typedef struct DIR {
8105 long handle; /* -1 for failed rewind */
8106 struct _finddata_t info;
8107 struct dirent result; /* d_name null iff first time */
8108 char *name; /* null-terminated char string */
8109 } DIR;
8110
8111 DIR *opendir(const char *name)
8112 {
8113 DIR *dir = 0;
8114
8115 if(name && name[0]) {
8116 size_t base_length = strlen(name);
8117 const char *all = /* search pattern must end with suitable wildcard */
8118 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
8119
8120 if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
8121 (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
8122 {
8123 strcat(strcpy(dir->name, name), all);
8124
8125 if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
8126 dir->result.d_name = 0;
8127 else { /* rollback */
8128 Jim_Free(dir->name);
8129 Jim_Free(dir);
8130 dir = 0;
8131 }
8132 } else { /* rollback */
8133 Jim_Free(dir);
8134 dir = 0;
8135 errno = ENOMEM;
8136 }
8137 } else {
8138 errno = EINVAL;
8139 }
8140 return dir;
8141 }
8142
8143 int closedir(DIR *dir)
8144 {
8145 int result = -1;
8146
8147 if(dir) {
8148 if(dir->handle != -1)
8149 result = _findclose(dir->handle);
8150 Jim_Free(dir->name);
8151 Jim_Free(dir);
8152 }
8153 if(result == -1) /* map all errors to EBADF */
8154 errno = EBADF;
8155 return result;
8156 }
8157
8158 struct dirent *readdir(DIR *dir)
8159 {
8160 struct dirent *result = 0;
8161
8162 if(dir && dir->handle != -1) {
8163 if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
8164 result = &dir->result;
8165 result->d_name = dir->info.name;
8166 }
8167 } else {
8168 errno = EBADF;
8169 }
8170 return result;
8171 }
8172
8173 #endif /* WIN32 */
8174
8175 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8176 int prefixc, const char *pkgName, int pkgVer, int flags)
8177 {
8178 int bestVer = -1, i;
8179 int pkgNameLen = strlen(pkgName);
8180 char *bestPackage = NULL;
8181 struct dirent *de;
8182
8183 for (i = 0; i < prefixc; i++) {
8184 DIR *dir;
8185 char buf[JIM_PATH_LEN];
8186 int prefixLen;
8187
8188 if (prefixes[i] == NULL) continue;
8189 strncpy(buf, prefixes[i], JIM_PATH_LEN);
8190 buf[JIM_PATH_LEN-1] = '\0';
8191 prefixLen = strlen(buf);
8192 if (prefixLen && buf[prefixLen-1] == '/')
8193 buf[prefixLen-1] = '\0';
8194
8195 if ((dir = opendir(buf)) == NULL) continue;
8196 while ((de = readdir(dir)) != NULL) {
8197 char *fileName = de->d_name;
8198 int fileNameLen = strlen(fileName);
8199
8200 if (strncmp(fileName, "jim-", 4) == 0 &&
8201 strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
8202 *(fileName+4+pkgNameLen) == '-' &&
8203 fileNameLen > 4 && /* note that this is not really useful */
8204 (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
8205 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
8206 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
8207 {
8208 char ver[6]; /* xx.yy<nulterm> */
8209 char *p = strrchr(fileName, '.');
8210 int verLen, fileVer;
8211
8212 verLen = p - (fileName+4+pkgNameLen+1);
8213 if (verLen < 3 || verLen > 5) continue;
8214 memcpy(ver, fileName+4+pkgNameLen+1, verLen);
8215 ver[verLen] = '\0';
8216 if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
8217 != JIM_OK) continue;
8218 if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
8219 (bestVer == -1 || bestVer < fileVer))
8220 {
8221 bestVer = fileVer;
8222 Jim_Free(bestPackage);
8223 bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
8224 sprintf(bestPackage, "%s/%s", buf, fileName);
8225 }
8226 }
8227 }
8228 closedir(dir);
8229 }
8230 return bestPackage;
8231 }
8232
8233 #else /* JIM_ANSIC */
8234
8235 static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
8236 int prefixc, const char *pkgName, int pkgVer, int flags)
8237 {
8238 JIM_NOTUSED(interp);
8239 JIM_NOTUSED(prefixes);
8240 JIM_NOTUSED(prefixc);
8241 JIM_NOTUSED(pkgName);
8242 JIM_NOTUSED(pkgVer);
8243 JIM_NOTUSED(flags);
8244 return NULL;
8245 }
8246
8247 #endif /* JIM_ANSIC */
8248
8249 /* Search for a suitable package under every dir specified by jim_libpath
8250 * and load it if possible. If a suitable package was loaded with success
8251 * JIM_OK is returned, otherwise JIM_ERR is returned. */
8252 static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
8253 int flags)
8254 {
8255 Jim_Obj *libPathObjPtr;
8256 char **prefixes, *best;
8257 int prefixc, i, retCode = JIM_OK;
8258
8259 libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
8260 if (libPathObjPtr == NULL) {
8261 prefixc = 0;
8262 libPathObjPtr = NULL;
8263 } else {
8264 Jim_IncrRefCount(libPathObjPtr);
8265 Jim_ListLength(interp, libPathObjPtr, &prefixc);
8266 }
8267
8268 prefixes = Jim_Alloc(sizeof(char*)*prefixc);
8269 for (i = 0; i < prefixc; i++) {
8270 Jim_Obj *prefixObjPtr;
8271 if (Jim_ListIndex(interp, libPathObjPtr, i,
8272 &prefixObjPtr, JIM_NONE) != JIM_OK)
8273 {
8274 prefixes[i] = NULL;
8275 continue;
8276 }
8277 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
8278 }
8279 /* Scan every directory to find the "best" package. */
8280 best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
8281 if (best != NULL) {
8282 char *p = strrchr(best, '.');
8283 /* Try to load/source it */
8284 if (p && strcmp(p, ".tcl") == 0) {
8285 retCode = Jim_EvalFile(interp, best);
8286 } else {
8287 retCode = Jim_LoadLibrary(interp, best);
8288 }
8289 } else {
8290 retCode = JIM_ERR;
8291 }
8292 Jim_Free(best);
8293 for (i = 0; i < prefixc; i++)
8294 Jim_Free(prefixes[i]);
8295 Jim_Free(prefixes);
8296 if (libPathObjPtr)
8297 Jim_DecrRefCount(interp, libPathObjPtr);
8298 return retCode;
8299 }
8300
8301 const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
8302 const char *ver, int flags)
8303 {
8304 Jim_HashEntry *he;
8305 int requiredVer;
8306
8307 /* Start with an empty error string */
8308 Jim_SetResultString(interp, "", 0);
8309
8310 if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
8311 return NULL;
8312 he = Jim_FindHashEntry(&interp->packages, name);
8313 if (he == NULL) {
8314 /* Try to load the package. */
8315 if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
8316 he = Jim_FindHashEntry(&interp->packages, name);
8317 if (he == NULL) {
8318 return "?";
8319 }
8320 return he->val;
8321 }
8322 /* No way... return an error. */
8323 if (flags & JIM_ERRMSG) {
8324 int len;
8325 Jim_GetString(Jim_GetResult(interp), &len);
8326 Jim_AppendStrings(interp, Jim_GetResult(interp), len ? "\n" : "",
8327 "Can't find package '", name, "'", NULL);
8328 }
8329 return NULL;
8330 } else {
8331 int actualVer;
8332 if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
8333 != JIM_OK)
8334 {
8335 return NULL;
8336 }
8337 /* Check if version matches. */
8338 if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
8339 Jim_AppendStrings(interp, Jim_GetResult(interp),
8340 "Package '", name, "' already loaded, but with version ",
8341 he->val, NULL);
8342 return NULL;
8343 }
8344 return he->val;
8345 }
8346 }
8347
8348 /* -----------------------------------------------------------------------------
8349 * Eval
8350 * ---------------------------------------------------------------------------*/
8351 #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
8352 #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
8353
8354 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8355 Jim_Obj *const *argv);
8356
8357 /* Handle calls to the [unknown] command */
8358 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
8359 {
8360 Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
8361 int retCode;
8362
8363 /* If JimUnknown() is recursively called (e.g. error in the unknown proc,
8364 * done here
8365 */
8366 if (interp->unknown_called) {
8367 return JIM_ERR;
8368 }
8369
8370 /* If the [unknown] command does not exists returns
8371 * just now */
8372 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
8373 return JIM_ERR;
8374
8375 /* The object interp->unknown just contains
8376 * the "unknown" string, it is used in order to
8377 * avoid to lookup the unknown command every time
8378 * but instread to cache the result. */
8379 if (argc+1 <= JIM_EVAL_SARGV_LEN)
8380 v = sv;
8381 else
8382 v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
8383 /* Make a copy of the arguments vector, but shifted on
8384 * the right of one position. The command name of the
8385 * command will be instead the first argument of the
8386 * [unknonw] call. */
8387 memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
8388 v[0] = interp->unknown;
8389 /* Call it */
8390 interp->unknown_called++;
8391 retCode = Jim_EvalObjVector(interp, argc+1, v);
8392 interp->unknown_called--;
8393
8394 /* Clean up */
8395 if (v != sv)
8396 Jim_Free(v);
8397 return retCode;
8398 }
8399
8400 /* Eval the object vector 'objv' composed of 'objc' elements.
8401 * Every element is used as single argument.
8402 * Jim_EvalObj() will call this function every time its object
8403 * argument is of "list" type, with no string representation.
8404 *
8405 * This is possible because the string representation of a
8406 * list object generated by the UpdateStringOfList is made
8407 * in a way that ensures that every list element is a different
8408 * command argument. */
8409 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
8410 {
8411 int i, retcode;
8412 Jim_Cmd *cmdPtr;
8413
8414 /* Incr refcount of arguments. */
8415 for (i = 0; i < objc; i++)
8416 Jim_IncrRefCount(objv[i]);
8417 /* Command lookup */
8418 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
8419 if (cmdPtr == NULL) {
8420 retcode = JimUnknown(interp, objc, objv);
8421 } else {
8422 /* Call it -- Make sure result is an empty object. */
8423 Jim_SetEmptyResult(interp);
8424 if (cmdPtr->cmdProc) {
8425 interp->cmdPrivData = cmdPtr->privData;
8426 retcode = cmdPtr->cmdProc(interp, objc, objv);
8427 if (retcode == JIM_ERR_ADDSTACK) {
8428 //JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8429 retcode = JIM_ERR;
8430 }
8431 } else {
8432 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
8433 if (retcode == JIM_ERR) {
8434 JimAppendStackTrace(interp,
8435 Jim_GetString(objv[0], NULL), "", 1);
8436 }
8437 }
8438 }
8439 /* Decr refcount of arguments and return the retcode */
8440 for (i = 0; i < objc; i++)
8441 Jim_DecrRefCount(interp, objv[i]);
8442 return retcode;
8443 }
8444
8445 /* Interpolate the given tokens into a unique Jim_Obj returned by reference
8446 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8447 * The returned object has refcount = 0. */
8448 int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8449 int tokens, Jim_Obj **objPtrPtr)
8450 {
8451 int totlen = 0, i, retcode;
8452 Jim_Obj **intv;
8453 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8454 Jim_Obj *objPtr;
8455 char *s;
8456
8457 if (tokens <= JIM_EVAL_SINTV_LEN)
8458 intv = sintv;
8459 else
8460 intv = Jim_Alloc(sizeof(Jim_Obj*)*
8461 tokens);
8462 /* Compute every token forming the argument
8463 * in the intv objects vector. */
8464 for (i = 0; i < tokens; i++) {
8465 switch(token[i].type) {
8466 case JIM_TT_ESC:
8467 case JIM_TT_STR:
8468 intv[i] = token[i].objPtr;
8469 break;
8470 case JIM_TT_VAR:
8471 intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8472 if (!intv[i]) {
8473 retcode = JIM_ERR;
8474 goto err;
8475 }
8476 break;
8477 case JIM_TT_DICTSUGAR:
8478 intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8479 if (!intv[i]) {
8480 retcode = JIM_ERR;
8481 goto err;
8482 }
8483 break;
8484 case JIM_TT_CMD:
8485 retcode = Jim_EvalObj(interp, token[i].objPtr);
8486 if (retcode != JIM_OK)
8487 goto err;
8488 intv[i] = Jim_GetResult(interp);
8489 break;
8490 default:
8491 Jim_Panic(interp,
8492 "default token type reached "
8493 "in Jim_InterpolateTokens().");
8494 break;
8495 }
8496 Jim_IncrRefCount(intv[i]);
8497 /* Make sure there is a valid
8498 * string rep, and add the string
8499 * length to the total legnth. */
8500 Jim_GetString(intv[i], NULL);
8501 totlen += intv[i]->length;
8502 }
8503 /* Concatenate every token in an unique
8504 * object. */
8505 objPtr = Jim_NewStringObjNoAlloc(interp,
8506 NULL, 0);
8507 s = objPtr->bytes = Jim_Alloc(totlen+1);
8508 objPtr->length = totlen;
8509 for (i = 0; i < tokens; i++) {
8510 memcpy(s, intv[i]->bytes, intv[i]->length);
8511 s += intv[i]->length;
8512 Jim_DecrRefCount(interp, intv[i]);
8513 }
8514 objPtr->bytes[totlen] = '\0';
8515 /* Free the intv vector if not static. */
8516 if (tokens > JIM_EVAL_SINTV_LEN)
8517 Jim_Free(intv);
8518 *objPtrPtr = objPtr;
8519 return JIM_OK;
8520 err:
8521 i--;
8522 for (; i >= 0; i--)
8523 Jim_DecrRefCount(interp, intv[i]);
8524 if (tokens > JIM_EVAL_SINTV_LEN)
8525 Jim_Free(intv);
8526 return retcode;
8527 }
8528
8529 /* Helper of Jim_EvalObj() to perform argument expansion.
8530 * Basically this function append an argument to 'argv'
8531 * (and increments argc by reference accordingly), performing
8532 * expansion of the list object if 'expand' is non-zero, or
8533 * just adding objPtr to argv if 'expand' is zero. */
8534 void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8535 int *argcPtr, int expand, Jim_Obj *objPtr)
8536 {
8537 if (!expand) {
8538 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8539 /* refcount of objPtr not incremented because
8540 * we are actually transfering a reference from
8541 * the old 'argv' to the expanded one. */
8542 (*argv)[*argcPtr] = objPtr;
8543 (*argcPtr)++;
8544 } else {
8545 int len, i;
8546
8547 Jim_ListLength(interp, objPtr, &len);
8548 (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8549 for (i = 0; i < len; i++) {
8550 (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8551 Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8552 (*argcPtr)++;
8553 }
8554 /* The original object reference is no longer needed,
8555 * after the expansion it is no longer present on
8556 * the argument vector, but the single elements are
8557 * in its place. */
8558 Jim_DecrRefCount(interp, objPtr);
8559 }
8560 }
8561
8562 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8563 {
8564 int i, j = 0, len;
8565 ScriptObj *script;
8566 ScriptToken *token;
8567 int *cs; /* command structure array */
8568 int retcode = JIM_OK;
8569 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8570
8571 interp->errorFlag = 0;
8572
8573 /* If the object is of type "list" and there is no
8574 * string representation for this object, we can call
8575 * a specialized version of Jim_EvalObj() */
8576 if (scriptObjPtr->typePtr == &listObjType &&
8577 scriptObjPtr->internalRep.listValue.len &&
8578 scriptObjPtr->bytes == NULL) {
8579 Jim_IncrRefCount(scriptObjPtr);
8580 retcode = Jim_EvalObjVector(interp,
8581 scriptObjPtr->internalRep.listValue.len,
8582 scriptObjPtr->internalRep.listValue.ele);
8583 Jim_DecrRefCount(interp, scriptObjPtr);
8584 return retcode;
8585 }
8586
8587 Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8588 script = Jim_GetScript(interp, scriptObjPtr);
8589 /* Now we have to make sure the internal repr will not be
8590 * freed on shimmering.
8591 *
8592 * Think for example to this:
8593 *
8594 * set x {llength $x; ... some more code ...}; eval $x
8595 *
8596 * In order to preserve the internal rep, we increment the
8597 * inUse field of the script internal rep structure. */
8598 script->inUse++;
8599
8600 token = script->token;
8601 len = script->len;
8602 cs = script->cmdStruct;
8603 i = 0; /* 'i' is the current token index. */
8604
8605 /* Reset the interpreter result. This is useful to
8606 * return the emtpy result in the case of empty program. */
8607 Jim_SetEmptyResult(interp);
8608
8609 /* Execute every command sequentially, returns on
8610 * error (i.e. if a command does not return JIM_OK) */
8611 while (i < len) {
8612 int expand = 0;
8613 int argc = *cs++; /* Get the number of arguments */
8614 Jim_Cmd *cmd;
8615
8616 /* Set the expand flag if needed. */
8617 if (argc == -1) {
8618 expand++;
8619 argc = *cs++;
8620 }
8621 /* Allocate the arguments vector */
8622 if (argc <= JIM_EVAL_SARGV_LEN)
8623 argv = sargv;
8624 else
8625 argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8626 /* Populate the arguments objects. */
8627 for (j = 0; j < argc; j++) {
8628 int tokens = *cs++;
8629
8630 /* tokens is negative if expansion is needed.
8631 * for this argument. */
8632 if (tokens < 0) {
8633 tokens = (-tokens)-1;
8634 i++;
8635 }
8636 if (tokens == 1) {
8637 /* Fast path if the token does not
8638 * need interpolation */
8639 switch(token[i].type) {
8640 case JIM_TT_ESC:
8641 case JIM_TT_STR:
8642 argv[j] = token[i].objPtr;
8643 break;
8644 case JIM_TT_VAR:
8645 tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8646 JIM_ERRMSG);
8647 if (!tmpObjPtr) {
8648 retcode = JIM_ERR;
8649 goto err;
8650 }
8651 argv[j] = tmpObjPtr;
8652 break;
8653 case JIM_TT_DICTSUGAR:
8654 tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8655 if (!tmpObjPtr) {
8656 retcode = JIM_ERR;
8657 goto err;
8658 }
8659 argv[j] = tmpObjPtr;
8660 break;
8661 case JIM_TT_CMD:
8662 retcode = Jim_EvalObj(interp, token[i].objPtr);
8663 if (retcode != JIM_OK)
8664 goto err;
8665 argv[j] = Jim_GetResult(interp);
8666 break;
8667 default:
8668 Jim_Panic(interp,
8669 "default token type reached "
8670 "in Jim_EvalObj().");
8671 break;
8672 }
8673 Jim_IncrRefCount(argv[j]);
8674 i += 2;
8675 } else {
8676 /* For interpolation we call an helper
8677 * function doing the work for us. */
8678 if ((retcode = Jim_InterpolateTokens(interp,
8679 token+i, tokens, &tmpObjPtr)) != JIM_OK)
8680 {
8681 goto err;
8682 }
8683 argv[j] = tmpObjPtr;
8684 Jim_IncrRefCount(argv[j]);
8685 i += tokens+1;
8686 }
8687 }
8688 /* Handle {expand} expansion */
8689 if (expand) {
8690 int *ecs = cs - argc;
8691 int eargc = 0;
8692 Jim_Obj **eargv = NULL;
8693
8694 for (j = 0; j < argc; j++) {
8695 Jim_ExpandArgument( interp, &eargv, &eargc,
8696 ecs[j] < 0, argv[j]);
8697 }
8698 if (argv != sargv)
8699 Jim_Free(argv);
8700 argc = eargc;
8701 argv = eargv;
8702 j = argc;
8703 if (argc == 0) {
8704 /* Nothing to do with zero args. */
8705 Jim_Free(eargv);
8706 continue;
8707 }
8708 }
8709 /* Lookup the command to call */
8710 cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8711 if (cmd != NULL) {
8712 /* Call it -- Make sure result is an empty object. */
8713 Jim_SetEmptyResult(interp);
8714 if (cmd->cmdProc) {
8715 interp->cmdPrivData = cmd->privData;
8716 retcode = cmd->cmdProc(interp, argc, argv);
8717 if ((retcode == JIM_ERR)||(retcode == JIM_ERR_ADDSTACK)) {
8718 JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
8719 retcode = JIM_ERR;
8720 }
8721 } else {
8722 retcode = JimCallProcedure(interp, cmd, argc, argv);
8723 if (retcode == JIM_ERR) {
8724 JimAppendStackTrace(interp,
8725 Jim_GetString(argv[0], NULL), script->fileName,
8726 token[i-argc*2].linenr);
8727 }
8728 }
8729 } else {
8730 /* Call [unknown] */
8731 retcode = JimUnknown(interp, argc, argv);
8732 if (retcode == JIM_ERR) {
8733 JimAppendStackTrace(interp,
8734 "", script->fileName,
8735 token[i-argc*2].linenr);
8736 }
8737 }
8738 if (retcode != JIM_OK) {
8739 i -= argc*2; /* point to the command name. */
8740 goto err;
8741 }
8742 /* Decrement the arguments count */
8743 for (j = 0; j < argc; j++) {
8744 Jim_DecrRefCount(interp, argv[j]);
8745 }
8746
8747 if (argv != sargv) {
8748 Jim_Free(argv);
8749 argv = NULL;
8750 }
8751 }
8752 /* Note that we don't have to decrement inUse, because the
8753 * following code transfers our use of the reference again to
8754 * the script object. */
8755 j = 0; /* on normal termination, the argv array is already
8756 Jim_DecrRefCount-ed. */
8757 err:
8758 /* Handle errors. */
8759 if (retcode == JIM_ERR && !interp->errorFlag) {
8760 interp->errorFlag = 1;
8761 JimSetErrorFileName(interp, script->fileName);
8762 JimSetErrorLineNumber(interp, token[i].linenr);
8763 JimResetStackTrace(interp);
8764 }
8765 Jim_FreeIntRep(interp, scriptObjPtr);
8766 scriptObjPtr->typePtr = &scriptObjType;
8767 Jim_SetIntRepPtr(scriptObjPtr, script);
8768 Jim_DecrRefCount(interp, scriptObjPtr);
8769 for (i = 0; i < j; i++) {
8770 Jim_DecrRefCount(interp, argv[i]);
8771 }
8772 if (argv != sargv)
8773 Jim_Free(argv);
8774 return retcode;
8775 }
8776
8777 /* Call a procedure implemented in Tcl.
8778 * It's possible to speed-up a lot this function, currently
8779 * the callframes are not cached, but allocated and
8780 * destroied every time. What is expecially costly is
8781 * to create/destroy the local vars hash table every time.
8782 *
8783 * This can be fixed just implementing callframes caching
8784 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8785 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8786 Jim_Obj *const *argv)
8787 {
8788 int i, retcode;
8789 Jim_CallFrame *callFramePtr;
8790 int num_args;
8791
8792 /* Check arity */
8793 if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8794 argc > cmd->arityMax)) {
8795 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8796 Jim_AppendStrings(interp, objPtr,
8797 "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8798 (cmd->arityMin > 1) ? " " : "",
8799 Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8800 Jim_SetResult(interp, objPtr);
8801 return JIM_ERR;
8802 }
8803 /* Check if there are too nested calls */
8804 if (interp->numLevels == interp->maxNestingDepth) {
8805 Jim_SetResultString(interp,
8806 "Too many nested calls. Infinite recursion?", -1);
8807 return JIM_ERR;
8808 }
8809 /* Create a new callframe */
8810 callFramePtr = JimCreateCallFrame(interp);
8811 callFramePtr->parentCallFrame = interp->framePtr;
8812 callFramePtr->argv = argv;
8813 callFramePtr->argc = argc;
8814 callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8815 callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8816 callFramePtr->staticVars = cmd->staticVars;
8817 Jim_IncrRefCount(cmd->argListObjPtr);
8818 Jim_IncrRefCount(cmd->bodyObjPtr);
8819 interp->framePtr = callFramePtr;
8820 interp->numLevels ++;
8821
8822 /* Set arguments */
8823 Jim_ListLength(interp, cmd->argListObjPtr, &num_args);
8824
8825 /* If last argument is 'args', don't set it here */
8826 if (cmd->arityMax == -1) {
8827 num_args--;
8828 }
8829
8830 for (i = 0; i < num_args; i++) {
8831 Jim_Obj *argObjPtr;
8832 Jim_Obj *nameObjPtr;
8833 Jim_Obj *valueObjPtr;
8834
8835 Jim_ListIndex(interp, cmd->argListObjPtr, i, &argObjPtr, JIM_NONE);
8836 if (i + 1 >= cmd->arityMin) {
8837 /* The name is the first element of the list */
8838 Jim_ListIndex(interp, argObjPtr, 0, &nameObjPtr, JIM_NONE);
8839 }
8840 else {
8841 /* The element arg is the name */
8842 nameObjPtr = argObjPtr;
8843 }
8844
8845 if (i + 1 >= argc) {
8846 /* No more values, so use default */
8847 /* The value is the second element of the list */
8848 Jim_ListIndex(interp, argObjPtr, 1, &valueObjPtr, JIM_NONE);
8849 }
8850 else {
8851 valueObjPtr = argv[i+1];
8852 }
8853 Jim_SetVariable(interp, nameObjPtr, valueObjPtr);
8854 }
8855 /* Set optional arguments */
8856 if (cmd->arityMax == -1) {
8857 Jim_Obj *listObjPtr, *objPtr;
8858
8859 i++;
8860 listObjPtr = Jim_NewListObj(interp, argv+i, argc-i);
8861 Jim_ListIndex(interp, cmd->argListObjPtr, num_args, &objPtr, JIM_NONE);
8862 Jim_SetVariable(interp, objPtr, listObjPtr);
8863 }
8864 /* Eval the body */
8865 retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8866
8867 /* Destroy the callframe */
8868 interp->numLevels --;
8869 interp->framePtr = interp->framePtr->parentCallFrame;
8870 if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8871 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8872 } else {
8873 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8874 }
8875 /* Handle the JIM_EVAL return code */
8876 if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8877 int savedLevel = interp->evalRetcodeLevel;
8878
8879 interp->evalRetcodeLevel = interp->numLevels;
8880 while (retcode == JIM_EVAL) {
8881 Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8882 Jim_IncrRefCount(resultScriptObjPtr);
8883 retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8884 Jim_DecrRefCount(interp, resultScriptObjPtr);
8885 }
8886 interp->evalRetcodeLevel = savedLevel;
8887 }
8888 /* Handle the JIM_RETURN return code */
8889 if (retcode == JIM_RETURN) {
8890 retcode = interp->returnCode;
8891 interp->returnCode = JIM_OK;
8892 }
8893 return retcode;
8894 }
8895
8896 int Jim_Eval_Named(Jim_Interp *interp, const char *script, const char *filename, int lineno)
8897 {
8898 int retval;
8899 Jim_Obj *scriptObjPtr;
8900
8901 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8902 Jim_IncrRefCount(scriptObjPtr);
8903
8904
8905 if( filename ){
8906 JimSetSourceInfo( interp, scriptObjPtr, filename, lineno );
8907 }
8908
8909 retval = Jim_EvalObj(interp, scriptObjPtr);
8910 Jim_DecrRefCount(interp, scriptObjPtr);
8911 return retval;
8912 }
8913
8914 int Jim_Eval(Jim_Interp *interp, const char *script)
8915 {
8916 return Jim_Eval_Named( interp, script, NULL, 0 );
8917 }
8918
8919
8920
8921 /* Execute script in the scope of the global level */
8922 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8923 {
8924 Jim_CallFrame *savedFramePtr;
8925 int retval;
8926
8927 savedFramePtr = interp->framePtr;
8928 interp->framePtr = interp->topFramePtr;
8929 retval = Jim_Eval(interp, script);
8930 interp->framePtr = savedFramePtr;
8931 return retval;
8932 }
8933
8934 int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8935 {
8936 Jim_CallFrame *savedFramePtr;
8937 int retval;
8938
8939 savedFramePtr = interp->framePtr;
8940 interp->framePtr = interp->topFramePtr;
8941 retval = Jim_EvalObj(interp, scriptObjPtr);
8942 interp->framePtr = savedFramePtr;
8943 /* Try to report the error (if any) via the bgerror proc */
8944 if (retval != JIM_OK) {
8945 Jim_Obj *objv[2];
8946
8947 objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8948 objv[1] = Jim_GetResult(interp);
8949 Jim_IncrRefCount(objv[0]);
8950 Jim_IncrRefCount(objv[1]);
8951 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8952 /* Report the error to stderr. */
8953 Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL);
8954 Jim_PrintErrorMessage(interp);
8955 }
8956 Jim_DecrRefCount(interp, objv[0]);
8957 Jim_DecrRefCount(interp, objv[1]);
8958 }
8959 return retval;
8960 }
8961
8962 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8963 {
8964 char *prg = NULL;
8965 FILE *fp;
8966 int nread, totread, maxlen, buflen;
8967 int retval;
8968 Jim_Obj *scriptObjPtr;
8969
8970 if ((fp = fopen(filename, "r")) == NULL) {
8971 const int cwd_len=2048;
8972 char *cwd=malloc(cwd_len);
8973 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8974 if (!getcwd( cwd, cwd_len )) strcpy(cwd, "unknown");
8975 Jim_AppendStrings(interp, Jim_GetResult(interp),
8976 "Error loading script \"", filename, "\"",
8977 " cwd: ", cwd,
8978 " err: ", strerror(errno), NULL);
8979 free(cwd);
8980 return JIM_ERR;
8981 }
8982 buflen = 1024;
8983 maxlen = totread = 0;
8984 while (1) {
8985 if (maxlen < totread+buflen+1) {
8986 maxlen = totread+buflen+1;
8987 prg = Jim_Realloc(prg, maxlen);
8988 }
8989 /* do not use Jim_fread() - this is really a file */
8990 if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8991 totread += nread;
8992 }
8993 prg[totread] = '\0';
8994 /* do not use Jim_fclose() - this is really a file */
8995 fclose(fp);
8996
8997 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8998 JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8999 Jim_IncrRefCount(scriptObjPtr);
9000 retval = Jim_EvalObj(interp, scriptObjPtr);
9001 Jim_DecrRefCount(interp, scriptObjPtr);
9002 return retval;
9003 }
9004
9005 /* -----------------------------------------------------------------------------
9006 * Subst
9007 * ---------------------------------------------------------------------------*/
9008 static int JimParseSubstStr(struct JimParserCtx *pc)
9009 {
9010 pc->tstart = pc->p;
9011 pc->tline = pc->linenr;
9012 while (*pc->p && *pc->p != '$' && *pc->p != '[') {
9013 pc->p++; pc->len--;
9014 }
9015 pc->tend = pc->p-1;
9016 pc->tt = JIM_TT_ESC;
9017 return JIM_OK;
9018 }
9019
9020 static int JimParseSubst(struct JimParserCtx *pc, int flags)
9021 {
9022 int retval;
9023
9024 if (pc->len == 0) {
9025 pc->tstart = pc->tend = pc->p;
9026 pc->tline = pc->linenr;
9027 pc->tt = JIM_TT_EOL;
9028 pc->eof = 1;
9029 return JIM_OK;
9030 }
9031 switch(*pc->p) {
9032 case '[':
9033 retval = JimParseCmd(pc);
9034 if (flags & JIM_SUBST_NOCMD) {
9035 pc->tstart--;
9036 pc->tend++;
9037 pc->tt = (flags & JIM_SUBST_NOESC) ?
9038 JIM_TT_STR : JIM_TT_ESC;
9039 }
9040 return retval;
9041 break;
9042 case '$':
9043 if (JimParseVar(pc) == JIM_ERR) {
9044 pc->tstart = pc->tend = pc->p++; pc->len--;
9045 pc->tline = pc->linenr;
9046 pc->tt = JIM_TT_STR;
9047 } else {
9048 if (flags & JIM_SUBST_NOVAR) {
9049 pc->tstart--;
9050 if (flags & JIM_SUBST_NOESC)
9051 pc->tt = JIM_TT_STR;
9052 else
9053 pc->tt = JIM_TT_ESC;
9054 if (*pc->tstart == '{') {
9055 pc->tstart--;
9056 if (*(pc->tend+1))
9057 pc->tend++;
9058 }
9059 }
9060 }
9061 break;
9062 default:
9063 retval = JimParseSubstStr(pc);
9064 if (flags & JIM_SUBST_NOESC)
9065 pc->tt = JIM_TT_STR;
9066 return retval;
9067 break;
9068 }
9069 return JIM_OK;
9070 }
9071
9072 /* The subst object type reuses most of the data structures and functions
9073 * of the script object. Script's data structures are a bit more complex
9074 * for what is needed for [subst]itution tasks, but the reuse helps to
9075 * deal with a single data structure at the cost of some more memory
9076 * usage for substitutions. */
9077 static Jim_ObjType substObjType = {
9078 "subst",
9079 FreeScriptInternalRep,
9080 DupScriptInternalRep,
9081 NULL,
9082 JIM_TYPE_REFERENCES,
9083 };
9084
9085 /* This method takes the string representation of an object
9086 * as a Tcl string where to perform [subst]itution, and generates
9087 * the pre-parsed internal representation. */
9088 int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
9089 {
9090 int scriptTextLen;
9091 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9092 struct JimParserCtx parser;
9093 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
9094
9095 script->len = 0;
9096 script->csLen = 0;
9097 script->commands = 0;
9098 script->token = NULL;
9099 script->cmdStruct = NULL;
9100 script->inUse = 1;
9101 script->substFlags = flags;
9102 script->fileName = NULL;
9103
9104 JimParserInit(&parser, scriptText, scriptTextLen, 1);
9105 while(1) {
9106 char *token;
9107 int len, type, linenr;
9108
9109 JimParseSubst(&parser, flags);
9110 if (JimParserEof(&parser)) break;
9111 token = JimParserGetToken(&parser, &len, &type, &linenr);
9112 ScriptObjAddToken(interp, script, token, len, type,
9113 NULL, linenr);
9114 }
9115 /* Free the old internal rep and set the new one. */
9116 Jim_FreeIntRep(interp, objPtr);
9117 Jim_SetIntRepPtr(objPtr, script);
9118 objPtr->typePtr = &scriptObjType;
9119 return JIM_OK;
9120 }
9121
9122 ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9123 {
9124 struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
9125
9126 if (objPtr->typePtr != &substObjType || script->substFlags != flags)
9127 SetSubstFromAny(interp, objPtr, flags);
9128 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
9129 }
9130
9131 /* Performs commands,variables,blackslashes substitution,
9132 * storing the result object (with refcount 0) into
9133 * resObjPtrPtr. */
9134 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
9135 Jim_Obj **resObjPtrPtr, int flags)
9136 {
9137 ScriptObj *script;
9138 ScriptToken *token;
9139 int i, len, retcode = JIM_OK;
9140 Jim_Obj *resObjPtr, *savedResultObjPtr;
9141
9142 script = Jim_GetSubst(interp, substObjPtr, flags);
9143 #ifdef JIM_OPTIMIZATION
9144 /* Fast path for a very common case with array-alike syntax,
9145 * that's: $foo($bar) */
9146 if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
9147 Jim_Obj *varObjPtr = script->token[0].objPtr;
9148
9149 Jim_IncrRefCount(varObjPtr);
9150 resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
9151 if (resObjPtr == NULL) {
9152 Jim_DecrRefCount(interp, varObjPtr);
9153 return JIM_ERR;
9154 }
9155 Jim_DecrRefCount(interp, varObjPtr);
9156 *resObjPtrPtr = resObjPtr;
9157 return JIM_OK;
9158 }
9159 #endif
9160
9161 Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
9162 /* In order to preserve the internal rep, we increment the
9163 * inUse field of the script internal rep structure. */
9164 script->inUse++;
9165
9166 token = script->token;
9167 len = script->len;
9168
9169 /* Save the interp old result, to set it again before
9170 * to return. */
9171 savedResultObjPtr = interp->result;
9172 Jim_IncrRefCount(savedResultObjPtr);
9173
9174 /* Perform the substitution. Starts with an empty object
9175 * and adds every token (performing the appropriate
9176 * var/command/escape substitution). */
9177 resObjPtr = Jim_NewStringObj(interp, "", 0);
9178 for (i = 0; i < len; i++) {
9179 Jim_Obj *objPtr;
9180
9181 switch(token[i].type) {
9182 case JIM_TT_STR:
9183 case JIM_TT_ESC:
9184 Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
9185 break;
9186 case JIM_TT_VAR:
9187 objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
9188 if (objPtr == NULL) goto err;
9189 Jim_IncrRefCount(objPtr);
9190 Jim_AppendObj(interp, resObjPtr, objPtr);
9191 Jim_DecrRefCount(interp, objPtr);
9192 break;
9193 case JIM_TT_DICTSUGAR:
9194 objPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
9195 if (!objPtr) {
9196 retcode = JIM_ERR;
9197 goto err;
9198 }
9199 break;
9200 case JIM_TT_CMD:
9201 if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
9202 goto err;
9203 Jim_AppendObj(interp, resObjPtr, interp->result);
9204 break;
9205 default:
9206 Jim_Panic(interp,
9207 "default token type (%d) reached "
9208 "in Jim_SubstObj().", token[i].type);
9209 break;
9210 }
9211 }
9212 ok:
9213 if (retcode == JIM_OK)
9214 Jim_SetResult(interp, savedResultObjPtr);
9215 Jim_DecrRefCount(interp, savedResultObjPtr);
9216 /* Note that we don't have to decrement inUse, because the
9217 * following code transfers our use of the reference again to
9218 * the script object. */
9219 Jim_FreeIntRep(interp, substObjPtr);
9220 substObjPtr->typePtr = &scriptObjType;
9221 Jim_SetIntRepPtr(substObjPtr, script);
9222 Jim_DecrRefCount(interp, substObjPtr);
9223 *resObjPtrPtr = resObjPtr;
9224 return retcode;
9225 err:
9226 Jim_FreeNewObj(interp, resObjPtr);
9227 retcode = JIM_ERR;
9228 goto ok;
9229 }
9230
9231 /* -----------------------------------------------------------------------------
9232 * API Input/Export functions
9233 * ---------------------------------------------------------------------------*/
9234
9235 int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
9236 {
9237 Jim_HashEntry *he;
9238
9239 he = Jim_FindHashEntry(&interp->stub, funcname);
9240 if (!he)
9241 return JIM_ERR;
9242 memcpy(targetPtrPtr, &he->val, sizeof(void*));
9243 return JIM_OK;
9244 }
9245
9246 int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
9247 {
9248 return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
9249 }
9250
9251 #define JIM_REGISTER_API(name) \
9252 Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
9253
9254 void JimRegisterCoreApi(Jim_Interp *interp)
9255 {
9256 interp->getApiFuncPtr = Jim_GetApi;
9257 JIM_REGISTER_API(Alloc);
9258 JIM_REGISTER_API(Free);
9259 JIM_REGISTER_API(Eval);
9260 JIM_REGISTER_API(Eval_Named);
9261 JIM_REGISTER_API(EvalGlobal);
9262 JIM_REGISTER_API(EvalFile);
9263 JIM_REGISTER_API(EvalObj);
9264 JIM_REGISTER_API(EvalObjBackground);
9265 JIM_REGISTER_API(EvalObjVector);
9266 JIM_REGISTER_API(InitHashTable);
9267 JIM_REGISTER_API(ExpandHashTable);
9268 JIM_REGISTER_API(AddHashEntry);
9269 JIM_REGISTER_API(ReplaceHashEntry);
9270 JIM_REGISTER_API(DeleteHashEntry);
9271 JIM_REGISTER_API(FreeHashTable);
9272 JIM_REGISTER_API(FindHashEntry);
9273 JIM_REGISTER_API(ResizeHashTable);
9274 JIM_REGISTER_API(GetHashTableIterator);
9275 JIM_REGISTER_API(NextHashEntry);
9276 JIM_REGISTER_API(NewObj);
9277 JIM_REGISTER_API(FreeObj);
9278 JIM_REGISTER_API(InvalidateStringRep);
9279 JIM_REGISTER_API(InitStringRep);
9280 JIM_REGISTER_API(DuplicateObj);
9281 JIM_REGISTER_API(GetString);
9282 JIM_REGISTER_API(Length);
9283 JIM_REGISTER_API(InvalidateStringRep);
9284 JIM_REGISTER_API(NewStringObj);
9285 JIM_REGISTER_API(NewStringObjNoAlloc);
9286 JIM_REGISTER_API(AppendString);
9287 JIM_REGISTER_API(AppendString_sprintf);
9288 JIM_REGISTER_API(AppendObj);
9289 JIM_REGISTER_API(AppendStrings);
9290 JIM_REGISTER_API(StringEqObj);
9291 JIM_REGISTER_API(StringMatchObj);
9292 JIM_REGISTER_API(StringRangeObj);
9293 JIM_REGISTER_API(FormatString);
9294 JIM_REGISTER_API(CompareStringImmediate);
9295 JIM_REGISTER_API(NewReference);
9296 JIM_REGISTER_API(GetReference);
9297 JIM_REGISTER_API(SetFinalizer);
9298 JIM_REGISTER_API(GetFinalizer);
9299 JIM_REGISTER_API(CreateInterp);
9300 JIM_REGISTER_API(FreeInterp);
9301 JIM_REGISTER_API(GetExitCode);
9302 JIM_REGISTER_API(SetStdin);
9303 JIM_REGISTER_API(SetStdout);
9304 JIM_REGISTER_API(SetStderr);
9305 JIM_REGISTER_API(CreateCommand);
9306 JIM_REGISTER_API(CreateProcedure);
9307 JIM_REGISTER_API(DeleteCommand);
9308 JIM_REGISTER_API(RenameCommand);
9309 JIM_REGISTER_API(GetCommand);
9310 JIM_REGISTER_API(SetVariable);
9311 JIM_REGISTER_API(SetVariableStr);
9312 JIM_REGISTER_API(SetGlobalVariableStr);
9313 JIM_REGISTER_API(SetVariableStrWithStr);
9314 JIM_REGISTER_API(SetVariableLink);
9315 JIM_REGISTER_API(GetVariable);
9316 JIM_REGISTER_API(GetCallFrameByLevel);
9317 JIM_REGISTER_API(Collect);
9318 JIM_REGISTER_API(CollectIfNeeded);
9319 JIM_REGISTER_API(GetIndex);
9320 JIM_REGISTER_API(NewListObj);
9321 JIM_REGISTER_API(ListAppendElement);
9322 JIM_REGISTER_API(ListAppendList);
9323 JIM_REGISTER_API(ListLength);
9324 JIM_REGISTER_API(ListIndex);
9325 JIM_REGISTER_API(SetListIndex);
9326 JIM_REGISTER_API(ConcatObj);
9327 JIM_REGISTER_API(NewDictObj);
9328 JIM_REGISTER_API(DictKey);
9329 JIM_REGISTER_API(DictKeysVector);
9330 JIM_REGISTER_API(GetIndex);
9331 JIM_REGISTER_API(GetReturnCode);
9332 JIM_REGISTER_API(EvalExpression);
9333 JIM_REGISTER_API(GetBoolFromExpr);
9334 JIM_REGISTER_API(GetWide);
9335 JIM_REGISTER_API(GetLong);
9336 JIM_REGISTER_API(SetWide);
9337 JIM_REGISTER_API(NewIntObj);
9338 JIM_REGISTER_API(GetDouble);
9339 JIM_REGISTER_API(SetDouble);
9340 JIM_REGISTER_API(NewDoubleObj);
9341 JIM_REGISTER_API(WrongNumArgs);
9342 JIM_REGISTER_API(SetDictKeysVector);
9343 JIM_REGISTER_API(SubstObj);
9344 JIM_REGISTER_API(RegisterApi);
9345 JIM_REGISTER_API(PrintErrorMessage);
9346 JIM_REGISTER_API(InteractivePrompt);
9347 JIM_REGISTER_API(RegisterCoreCommands);
9348 JIM_REGISTER_API(GetSharedString);
9349 JIM_REGISTER_API(ReleaseSharedString);
9350 JIM_REGISTER_API(Panic);
9351 JIM_REGISTER_API(StrDup);
9352 JIM_REGISTER_API(UnsetVariable);
9353 JIM_REGISTER_API(GetVariableStr);
9354 JIM_REGISTER_API(GetGlobalVariable);
9355 JIM_REGISTER_API(GetGlobalVariableStr);
9356 JIM_REGISTER_API(GetAssocData);
9357 JIM_REGISTER_API(SetAssocData);
9358 JIM_REGISTER_API(DeleteAssocData);
9359 JIM_REGISTER_API(GetEnum);
9360 JIM_REGISTER_API(ScriptIsComplete);
9361 JIM_REGISTER_API(PackageRequire);
9362 JIM_REGISTER_API(PackageProvide);
9363 JIM_REGISTER_API(InitStack);
9364 JIM_REGISTER_API(FreeStack);
9365 JIM_REGISTER_API(StackLen);
9366 JIM_REGISTER_API(StackPush);
9367 JIM_REGISTER_API(StackPop);
9368 JIM_REGISTER_API(StackPeek);
9369 JIM_REGISTER_API(FreeStackElements);
9370 JIM_REGISTER_API(fprintf );
9371 JIM_REGISTER_API(vfprintf );
9372 JIM_REGISTER_API(fwrite );
9373 JIM_REGISTER_API(fread );
9374 JIM_REGISTER_API(fflush );
9375 JIM_REGISTER_API(fgets );
9376 JIM_REGISTER_API(GetNvp);
9377 JIM_REGISTER_API(Nvp_name2value);
9378 JIM_REGISTER_API(Nvp_name2value_simple);
9379 JIM_REGISTER_API(Nvp_name2value_obj);
9380 JIM_REGISTER_API(Nvp_name2value_nocase);
9381 JIM_REGISTER_API(Nvp_name2value_obj_nocase);
9382
9383 JIM_REGISTER_API(Nvp_value2name);
9384 JIM_REGISTER_API(Nvp_value2name_simple);
9385 JIM_REGISTER_API(Nvp_value2name_obj);
9386
9387 JIM_REGISTER_API(GetOpt_Setup);
9388 JIM_REGISTER_API(GetOpt_Debug);
9389 JIM_REGISTER_API(GetOpt_Obj);
9390 JIM_REGISTER_API(GetOpt_String);
9391 JIM_REGISTER_API(GetOpt_Double);
9392 JIM_REGISTER_API(GetOpt_Wide);
9393 JIM_REGISTER_API(GetOpt_Nvp);
9394 JIM_REGISTER_API(GetOpt_NvpUnknown);
9395 JIM_REGISTER_API(GetOpt_Enum);
9396
9397 JIM_REGISTER_API(Debug_ArgvString);
9398 JIM_REGISTER_API(SetResult_sprintf);
9399 JIM_REGISTER_API(SetResult_NvpUnknown);
9400
9401 }
9402
9403 /* -----------------------------------------------------------------------------
9404 * Core commands utility functions
9405 * ---------------------------------------------------------------------------*/
9406 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
9407 const char *msg)
9408 {
9409 int i;
9410 Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
9411
9412 Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
9413 for (i = 0; i < argc; i++) {
9414 Jim_AppendObj(interp, objPtr, argv[i]);
9415 if (!(i+1 == argc && msg[0] == '\0'))
9416 Jim_AppendString(interp, objPtr, " ", 1);
9417 }
9418 Jim_AppendString(interp, objPtr, msg, -1);
9419 Jim_AppendString(interp, objPtr, "\"", 1);
9420 Jim_SetResult(interp, objPtr);
9421 }
9422
9423 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
9424 {
9425 Jim_HashTableIterator *htiter;
9426 Jim_HashEntry *he;
9427 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9428 const char *pattern;
9429 int patternLen;
9430
9431 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9432 htiter = Jim_GetHashTableIterator(&interp->commands);
9433 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9434 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9435 strlen((const char*)he->key), 0))
9436 continue;
9437 Jim_ListAppendElement(interp, listObjPtr,
9438 Jim_NewStringObj(interp, he->key, -1));
9439 }
9440 Jim_FreeHashTableIterator(htiter);
9441 return listObjPtr;
9442 }
9443
9444 #define JIM_VARLIST_GLOBALS 0
9445 #define JIM_VARLIST_LOCALS 1
9446 #define JIM_VARLIST_VARS 2
9447
9448 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
9449 int mode)
9450 {
9451 Jim_HashTableIterator *htiter;
9452 Jim_HashEntry *he;
9453 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
9454 const char *pattern;
9455 int patternLen;
9456
9457 pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
9458 if (mode == JIM_VARLIST_GLOBALS) {
9459 htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
9460 } else {
9461 /* For [info locals], if we are at top level an emtpy list
9462 * is returned. I don't agree, but we aim at compatibility (SS) */
9463 if (mode == JIM_VARLIST_LOCALS &&
9464 interp->framePtr == interp->topFramePtr)
9465 return listObjPtr;
9466 htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
9467 }
9468 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
9469 Jim_Var *varPtr = (Jim_Var*) he->val;
9470 if (mode == JIM_VARLIST_LOCALS) {
9471 if (varPtr->linkFramePtr != NULL)
9472 continue;
9473 }
9474 if (pattern && !JimStringMatch(pattern, patternLen, he->key,
9475 strlen((const char*)he->key), 0))
9476 continue;
9477 Jim_ListAppendElement(interp, listObjPtr,
9478 Jim_NewStringObj(interp, he->key, -1));
9479 }
9480 Jim_FreeHashTableIterator(htiter);
9481 return listObjPtr;
9482 }
9483
9484 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
9485 Jim_Obj **objPtrPtr)
9486 {
9487 Jim_CallFrame *targetCallFrame;
9488
9489 if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
9490 != JIM_OK)
9491 return JIM_ERR;
9492 /* No proc call at toplevel callframe */
9493 if (targetCallFrame == interp->topFramePtr) {
9494 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9495 Jim_AppendStrings(interp, Jim_GetResult(interp),
9496 "bad level \"",
9497 Jim_GetString(levelObjPtr, NULL), "\"", NULL);
9498 return JIM_ERR;
9499 }
9500 *objPtrPtr = Jim_NewListObj(interp,
9501 targetCallFrame->argv,
9502 targetCallFrame->argc);
9503 return JIM_OK;
9504 }
9505
9506 /* -----------------------------------------------------------------------------
9507 * Core commands
9508 * ---------------------------------------------------------------------------*/
9509
9510 /* fake [puts] -- not the real puts, just for debugging. */
9511 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
9512 Jim_Obj *const *argv)
9513 {
9514 const char *str;
9515 int len, nonewline = 0;
9516
9517 if (argc != 2 && argc != 3) {
9518 Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
9519 return JIM_ERR;
9520 }
9521 if (argc == 3) {
9522 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
9523 {
9524 Jim_SetResultString(interp, "The second argument must "
9525 "be -nonewline", -1);
9526 return JIM_OK;
9527 } else {
9528 nonewline = 1;
9529 argv++;
9530 }
9531 }
9532 str = Jim_GetString(argv[1], &len);
9533 Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
9534 if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL);
9535 return JIM_OK;
9536 }
9537
9538 /* Helper for [+] and [*] */
9539 static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
9540 Jim_Obj *const *argv, int op)
9541 {
9542 jim_wide wideValue, res;
9543 double doubleValue, doubleRes;
9544 int i;
9545
9546 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9547
9548 for (i = 1; i < argc; i++) {
9549 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9550 goto trydouble;
9551 if (op == JIM_EXPROP_ADD)
9552 res += wideValue;
9553 else
9554 res *= wideValue;
9555 }
9556 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9557 return JIM_OK;
9558 trydouble:
9559 doubleRes = (double) res;
9560 for (;i < argc; i++) {
9561 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9562 return JIM_ERR;
9563 if (op == JIM_EXPROP_ADD)
9564 doubleRes += doubleValue;
9565 else
9566 doubleRes *= doubleValue;
9567 }
9568 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9569 return JIM_OK;
9570 }
9571
9572 /* Helper for [-] and [/] */
9573 static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9574 Jim_Obj *const *argv, int op)
9575 {
9576 jim_wide wideValue, res = 0;
9577 double doubleValue, doubleRes = 0;
9578 int i = 2;
9579
9580 if (argc < 2) {
9581 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9582 return JIM_ERR;
9583 } else if (argc == 2) {
9584 /* The arity = 2 case is different. For [- x] returns -x,
9585 * while [/ x] returns 1/x. */
9586 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9587 if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9588 JIM_OK)
9589 {
9590 return JIM_ERR;
9591 } else {
9592 if (op == JIM_EXPROP_SUB)
9593 doubleRes = -doubleValue;
9594 else
9595 doubleRes = 1.0/doubleValue;
9596 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9597 doubleRes));
9598 return JIM_OK;
9599 }
9600 }
9601 if (op == JIM_EXPROP_SUB) {
9602 res = -wideValue;
9603 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9604 } else {
9605 doubleRes = 1.0/wideValue;
9606 Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9607 doubleRes));
9608 }
9609 return JIM_OK;
9610 } else {
9611 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9612 if (Jim_GetDouble(interp, argv[1], &doubleRes)
9613 != JIM_OK) {
9614 return JIM_ERR;
9615 } else {
9616 goto trydouble;
9617 }
9618 }
9619 }
9620 for (i = 2; i < argc; i++) {
9621 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9622 doubleRes = (double) res;
9623 goto trydouble;
9624 }
9625 if (op == JIM_EXPROP_SUB)
9626 res -= wideValue;
9627 else
9628 res /= wideValue;
9629 }
9630 Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9631 return JIM_OK;
9632 trydouble:
9633 for (;i < argc; i++) {
9634 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9635 return JIM_ERR;
9636 if (op == JIM_EXPROP_SUB)
9637 doubleRes -= doubleValue;
9638 else
9639 doubleRes /= doubleValue;
9640 }
9641 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9642 return JIM_OK;
9643 }
9644
9645
9646 /* [+] */
9647 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9648 Jim_Obj *const *argv)
9649 {
9650 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9651 }
9652
9653 /* [*] */
9654 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9655 Jim_Obj *const *argv)
9656 {
9657 return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9658 }
9659
9660 /* [-] */
9661 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9662 Jim_Obj *const *argv)
9663 {
9664 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9665 }
9666
9667 /* [/] */
9668 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9669 Jim_Obj *const *argv)
9670 {
9671 return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9672 }
9673
9674 /* [set] */
9675 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9676 Jim_Obj *const *argv)
9677 {
9678 if (argc != 2 && argc != 3) {
9679 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9680 return JIM_ERR;
9681 }
9682 if (argc == 2) {
9683 Jim_Obj *objPtr;
9684 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9685 if (!objPtr)
9686 return JIM_ERR;
9687 Jim_SetResult(interp, objPtr);
9688 return JIM_OK;
9689 }
9690 /* argc == 3 case. */
9691 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9692 return JIM_ERR;
9693 Jim_SetResult(interp, argv[2]);
9694 return JIM_OK;
9695 }
9696
9697 /* [unset] */
9698 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9699 Jim_Obj *const *argv)
9700 {
9701 int i;
9702
9703 if (argc < 2) {
9704 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9705 return JIM_ERR;
9706 }
9707 for (i = 1; i < argc; i++) {
9708 if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9709 return JIM_ERR;
9710 }
9711 return JIM_OK;
9712 }
9713
9714 /* [incr] */
9715 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9716 Jim_Obj *const *argv)
9717 {
9718 jim_wide wideValue, increment = 1;
9719 Jim_Obj *intObjPtr;
9720
9721 if (argc != 2 && argc != 3) {
9722 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9723 return JIM_ERR;
9724 }
9725 if (argc == 3) {
9726 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9727 return JIM_ERR;
9728 }
9729 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9730 if (!intObjPtr) return JIM_ERR;
9731 if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9732 return JIM_ERR;
9733 if (Jim_IsShared(intObjPtr)) {
9734 intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9735 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9736 Jim_FreeNewObj(interp, intObjPtr);
9737 return JIM_ERR;
9738 }
9739 } else {
9740 Jim_SetWide(interp, intObjPtr, wideValue+increment);
9741 /* The following step is required in order to invalidate the
9742 * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9743 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9744 return JIM_ERR;
9745 }
9746 }
9747 Jim_SetResult(interp, intObjPtr);
9748 return JIM_OK;
9749 }
9750
9751 /* [while] */
9752 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9753 Jim_Obj *const *argv)
9754 {
9755 if (argc != 3) {
9756 Jim_WrongNumArgs(interp, 1, argv, "condition body");
9757 return JIM_ERR;
9758 }
9759 /* Try to run a specialized version of while if the expression
9760 * is in one of the following forms:
9761 *
9762 * $a < CONST, $a < $b
9763 * $a <= CONST, $a <= $b
9764 * $a > CONST, $a > $b
9765 * $a >= CONST, $a >= $b
9766 * $a != CONST, $a != $b
9767 * $a == CONST, $a == $b
9768 * $a
9769 * !$a
9770 * CONST
9771 */
9772
9773 #ifdef JIM_OPTIMIZATION
9774 {
9775 ExprByteCode *expr;
9776 Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9777 int exprLen, retval;
9778
9779 /* STEP 1 -- Check if there are the conditions to run the specialized
9780 * version of while */
9781
9782 if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9783 if (expr->len <= 0 || expr->len > 3) goto noopt;
9784 switch(expr->len) {
9785 case 1:
9786 if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9787 expr->opcode[0] != JIM_EXPROP_NUMBER)
9788 goto noopt;
9789 break;
9790 case 2:
9791 if (expr->opcode[1] != JIM_EXPROP_NOT ||
9792 expr->opcode[0] != JIM_EXPROP_VARIABLE)
9793 goto noopt;
9794 break;
9795 case 3:
9796 if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9797 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9798 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9799 goto noopt;
9800 switch(expr->opcode[2]) {
9801 case JIM_EXPROP_LT:
9802 case JIM_EXPROP_LTE:
9803 case JIM_EXPROP_GT:
9804 case JIM_EXPROP_GTE:
9805 case JIM_EXPROP_NUMEQ:
9806 case JIM_EXPROP_NUMNE:
9807 /* nothing to do */
9808 break;
9809 default:
9810 goto noopt;
9811 }
9812 break;
9813 default:
9814 Jim_Panic(interp,
9815 "Unexpected default reached in Jim_WhileCoreCommand()");
9816 break;
9817 }
9818
9819 /* STEP 2 -- conditions meet. Initialization. Take different
9820 * branches for different expression lengths. */
9821 exprLen = expr->len;
9822
9823 if (exprLen == 1) {
9824 jim_wide wideValue;
9825
9826 if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9827 varAObjPtr = expr->obj[0];
9828 Jim_IncrRefCount(varAObjPtr);
9829 } else {
9830 if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9831 goto noopt;
9832 }
9833 while (1) {
9834 if (varAObjPtr) {
9835 if (!(objPtr =
9836 Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9837 Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9838 {
9839 Jim_DecrRefCount(interp, varAObjPtr);
9840 goto noopt;
9841 }
9842 }
9843 if (!wideValue) break;
9844 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9845 switch(retval) {
9846 case JIM_BREAK:
9847 if (varAObjPtr)
9848 Jim_DecrRefCount(interp, varAObjPtr);
9849 goto out;
9850 break;
9851 case JIM_CONTINUE:
9852 continue;
9853 break;
9854 default:
9855 if (varAObjPtr)
9856 Jim_DecrRefCount(interp, varAObjPtr);
9857 return retval;
9858 }
9859 }
9860 }
9861 if (varAObjPtr)
9862 Jim_DecrRefCount(interp, varAObjPtr);
9863 } else if (exprLen == 3) {
9864 jim_wide wideValueA, wideValueB, cmpRes = 0;
9865 int cmpType = expr->opcode[2];
9866
9867 varAObjPtr = expr->obj[0];
9868 Jim_IncrRefCount(varAObjPtr);
9869 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9870 varBObjPtr = expr->obj[1];
9871 Jim_IncrRefCount(varBObjPtr);
9872 } else {
9873 if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9874 goto noopt;
9875 }
9876 while (1) {
9877 if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9878 Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9879 {
9880 Jim_DecrRefCount(interp, varAObjPtr);
9881 if (varBObjPtr)
9882 Jim_DecrRefCount(interp, varBObjPtr);
9883 goto noopt;
9884 }
9885 if (varBObjPtr) {
9886 if (!(objPtr =
9887 Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9888 Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9889 {
9890 Jim_DecrRefCount(interp, varAObjPtr);
9891 if (varBObjPtr)
9892 Jim_DecrRefCount(interp, varBObjPtr);
9893 goto noopt;
9894 }
9895 }
9896 switch(cmpType) {
9897 case JIM_EXPROP_LT:
9898 cmpRes = wideValueA < wideValueB; break;
9899 case JIM_EXPROP_LTE:
9900 cmpRes = wideValueA <= wideValueB; break;
9901 case JIM_EXPROP_GT:
9902 cmpRes = wideValueA > wideValueB; break;
9903 case JIM_EXPROP_GTE:
9904 cmpRes = wideValueA >= wideValueB; break;
9905 case JIM_EXPROP_NUMEQ:
9906 cmpRes = wideValueA == wideValueB; break;
9907 case JIM_EXPROP_NUMNE:
9908 cmpRes = wideValueA != wideValueB; break;
9909 }
9910 if (!cmpRes) break;
9911 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9912 switch(retval) {
9913 case JIM_BREAK:
9914 Jim_DecrRefCount(interp, varAObjPtr);
9915 if (varBObjPtr)
9916 Jim_DecrRefCount(interp, varBObjPtr);
9917 goto out;
9918 break;
9919 case JIM_CONTINUE:
9920 continue;
9921 break;
9922 default:
9923 Jim_DecrRefCount(interp, varAObjPtr);
9924 if (varBObjPtr)
9925 Jim_DecrRefCount(interp, varBObjPtr);
9926 return retval;
9927 }
9928 }
9929 }
9930 Jim_DecrRefCount(interp, varAObjPtr);
9931 if (varBObjPtr)
9932 Jim_DecrRefCount(interp, varBObjPtr);
9933 } else {
9934 /* TODO: case for len == 2 */
9935 goto noopt;
9936 }
9937 Jim_SetEmptyResult(interp);
9938 return JIM_OK;
9939 }
9940 noopt:
9941 #endif
9942
9943 /* The general purpose implementation of while starts here */
9944 while (1) {
9945 int boolean, retval;
9946
9947 if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9948 &boolean)) != JIM_OK)
9949 return retval;
9950 if (!boolean) break;
9951 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9952 switch(retval) {
9953 case JIM_BREAK:
9954 goto out;
9955 break;
9956 case JIM_CONTINUE:
9957 continue;
9958 break;
9959 default:
9960 return retval;
9961 }
9962 }
9963 }
9964 out:
9965 Jim_SetEmptyResult(interp);
9966 return JIM_OK;
9967 }
9968
9969 /* [for] */
9970 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9971 Jim_Obj *const *argv)
9972 {
9973 int retval;
9974
9975 if (argc != 5) {
9976 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9977 return JIM_ERR;
9978 }
9979 /* Check if the for is on the form:
9980 * for {set i CONST} {$i < CONST} {incr i}
9981 * for {set i CONST} {$i < $j} {incr i}
9982 * for {set i CONST} {$i <= CONST} {incr i}
9983 * for {set i CONST} {$i <= $j} {incr i}
9984 * XXX: NOTE: if variable traces are implemented, this optimization
9985 * need to be modified to check for the proc epoch at every variable
9986 * update. */
9987 #ifdef JIM_OPTIMIZATION
9988 {
9989 ScriptObj *initScript, *incrScript;
9990 ExprByteCode *expr;
9991 jim_wide start, stop, currentVal;
9992 unsigned jim_wide procEpoch = interp->procEpoch;
9993 Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9994 int cmpType;
9995 struct Jim_Cmd *cmdPtr;
9996
9997 /* Do it only if there aren't shared arguments */
9998 if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9999 goto evalstart;
10000 initScript = Jim_GetScript(interp, argv[1]);
10001 expr = Jim_GetExpression(interp, argv[2]);
10002 incrScript = Jim_GetScript(interp, argv[3]);
10003
10004 /* Ensure proper lengths to start */
10005 if (initScript->len != 6) goto evalstart;
10006 if (incrScript->len != 4) goto evalstart;
10007 if (expr->len != 3) goto evalstart;
10008 /* Ensure proper token types. */
10009 if (initScript->token[2].type != JIM_TT_ESC ||
10010 initScript->token[4].type != JIM_TT_ESC ||
10011 incrScript->token[2].type != JIM_TT_ESC ||
10012 expr->opcode[0] != JIM_EXPROP_VARIABLE ||
10013 (expr->opcode[1] != JIM_EXPROP_NUMBER &&
10014 expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
10015 (expr->opcode[2] != JIM_EXPROP_LT &&
10016 expr->opcode[2] != JIM_EXPROP_LTE))
10017 goto evalstart;
10018 cmpType = expr->opcode[2];
10019 /* Initialization command must be [set] */
10020 cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
10021 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
10022 goto evalstart;
10023 /* Update command must be incr */
10024 cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
10025 if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
10026 goto evalstart;
10027 /* set, incr, expression must be about the same variable */
10028 if (!Jim_StringEqObj(initScript->token[2].objPtr,
10029 incrScript->token[2].objPtr, 0))
10030 goto evalstart;
10031 if (!Jim_StringEqObj(initScript->token[2].objPtr,
10032 expr->obj[0], 0))
10033 goto evalstart;
10034 /* Check that the initialization and comparison are valid integers */
10035 if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
10036 goto evalstart;
10037 if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
10038 Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
10039 {
10040 goto evalstart;
10041 }
10042
10043 /* Initialization */
10044 varNamePtr = expr->obj[0];
10045 if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
10046 stopVarNamePtr = expr->obj[1];
10047 Jim_IncrRefCount(stopVarNamePtr);
10048 }
10049 Jim_IncrRefCount(varNamePtr);
10050
10051 /* --- OPTIMIZED FOR --- */
10052 /* Start to loop */
10053 objPtr = Jim_NewIntObj(interp, start);
10054 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
10055 Jim_DecrRefCount(interp, varNamePtr);
10056 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10057 Jim_FreeNewObj(interp, objPtr);
10058 goto evalstart;
10059 }
10060 while (1) {
10061 /* === Check condition === */
10062 /* Common code: */
10063 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
10064 if (objPtr == NULL ||
10065 Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
10066 {
10067 Jim_DecrRefCount(interp, varNamePtr);
10068 if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
10069 goto testcond;
10070 }
10071 /* Immediate or Variable? get the 'stop' value if the latter. */
10072 if (stopVarNamePtr) {
10073 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
10074 if (objPtr == NULL ||
10075 Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
10076 {
10077 Jim_DecrRefCount(interp, varNamePtr);
10078 Jim_DecrRefCount(interp, stopVarNamePtr);
10079 goto testcond;
10080 }
10081 }
10082 if (cmpType == JIM_EXPROP_LT) {
10083 if (currentVal >= stop) break;
10084 } else {
10085 if (currentVal > stop) break;
10086 }
10087 /* Eval body */
10088 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10089 switch(retval) {
10090 case JIM_BREAK:
10091 if (stopVarNamePtr)
10092 Jim_DecrRefCount(interp, stopVarNamePtr);
10093 Jim_DecrRefCount(interp, varNamePtr);
10094 goto out;
10095 case JIM_CONTINUE:
10096 /* nothing to do */
10097 break;
10098 default:
10099 if (stopVarNamePtr)
10100 Jim_DecrRefCount(interp, stopVarNamePtr);
10101 Jim_DecrRefCount(interp, varNamePtr);
10102 return retval;
10103 }
10104 }
10105 /* If there was a change in procedures/command continue
10106 * with the usual [for] command implementation */
10107 if (procEpoch != interp->procEpoch) {
10108 if (stopVarNamePtr)
10109 Jim_DecrRefCount(interp, stopVarNamePtr);
10110 Jim_DecrRefCount(interp, varNamePtr);
10111 goto evalnext;
10112 }
10113 /* Increment */
10114 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
10115 if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
10116 objPtr->internalRep.wideValue ++;
10117 Jim_InvalidateStringRep(objPtr);
10118 } else {
10119 Jim_Obj *auxObjPtr;
10120
10121 if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
10122 if (stopVarNamePtr)
10123 Jim_DecrRefCount(interp, stopVarNamePtr);
10124 Jim_DecrRefCount(interp, varNamePtr);
10125 goto evalnext;
10126 }
10127 auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
10128 if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
10129 if (stopVarNamePtr)
10130 Jim_DecrRefCount(interp, stopVarNamePtr);
10131 Jim_DecrRefCount(interp, varNamePtr);
10132 Jim_FreeNewObj(interp, auxObjPtr);
10133 goto evalnext;
10134 }
10135 }
10136 }
10137 if (stopVarNamePtr)
10138 Jim_DecrRefCount(interp, stopVarNamePtr);
10139 Jim_DecrRefCount(interp, varNamePtr);
10140 Jim_SetEmptyResult(interp);
10141 return JIM_OK;
10142 }
10143 #endif
10144 evalstart:
10145 /* Eval start */
10146 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10147 return retval;
10148 while (1) {
10149 int boolean;
10150 testcond:
10151 /* Test the condition */
10152 if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
10153 != JIM_OK)
10154 return retval;
10155 if (!boolean) break;
10156 /* Eval body */
10157 if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
10158 switch(retval) {
10159 case JIM_BREAK:
10160 goto out;
10161 break;
10162 case JIM_CONTINUE:
10163 /* Nothing to do */
10164 break;
10165 default:
10166 return retval;
10167 }
10168 }
10169 evalnext:
10170 /* Eval next */
10171 if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
10172 switch(retval) {
10173 case JIM_BREAK:
10174 goto out;
10175 break;
10176 case JIM_CONTINUE:
10177 continue;
10178 break;
10179 default:
10180 return retval;
10181 }
10182 }
10183 }
10184 out:
10185 Jim_SetEmptyResult(interp);
10186 return JIM_OK;
10187 }
10188
10189 /* foreach + lmap implementation. */
10190 static int JimForeachMapHelper(Jim_Interp *interp, int argc,
10191 Jim_Obj *const *argv, int doMap)
10192 {
10193 int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
10194 int nbrOfLoops = 0;
10195 Jim_Obj *emptyStr, *script, *mapRes = NULL;
10196
10197 if (argc < 4 || argc % 2 != 0) {
10198 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
10199 return JIM_ERR;
10200 }
10201 if (doMap) {
10202 mapRes = Jim_NewListObj(interp, NULL, 0);
10203 Jim_IncrRefCount(mapRes);
10204 }
10205 emptyStr = Jim_NewEmptyStringObj(interp);
10206 Jim_IncrRefCount(emptyStr);
10207 script = argv[argc-1]; /* Last argument is a script */
10208 nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
10209 listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
10210 listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
10211 /* Initialize iterators and remember max nbr elements each list */
10212 memset(listsIdx, 0, nbrOfLists * sizeof(int));
10213 /* Remember lengths of all lists and calculate how much rounds to loop */
10214 for (i=0; i < nbrOfLists*2; i += 2) {
10215 div_t cnt;
10216 int count;
10217 Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
10218 Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
10219 if (listsEnd[i] == 0) {
10220 Jim_SetResultString(interp, "foreach varlist is empty", -1);
10221 goto err;
10222 }
10223 cnt = div(listsEnd[i+1], listsEnd[i]);
10224 count = cnt.quot + (cnt.rem ? 1 : 0);
10225 if (count > nbrOfLoops)
10226 nbrOfLoops = count;
10227 }
10228 for (; nbrOfLoops-- > 0; ) {
10229 for (i=0; i < nbrOfLists; ++i) {
10230 int varIdx = 0, var = i * 2;
10231 while (varIdx < listsEnd[var]) {
10232 Jim_Obj *varName, *ele;
10233 int lst = i * 2 + 1;
10234 if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
10235 != JIM_OK)
10236 goto err;
10237 if (listsIdx[i] < listsEnd[lst]) {
10238 if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
10239 != JIM_OK)
10240 goto err;
10241 if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
10242 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10243 goto err;
10244 }
10245 ++listsIdx[i]; /* Remember next iterator of current list */
10246 } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
10247 Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
10248 goto err;
10249 }
10250 ++varIdx; /* Next variable */
10251 }
10252 }
10253 switch (result = Jim_EvalObj(interp, script)) {
10254 case JIM_OK:
10255 if (doMap)
10256 Jim_ListAppendElement(interp, mapRes, interp->result);
10257 break;
10258 case JIM_CONTINUE:
10259 break;
10260 case JIM_BREAK:
10261 goto out;
10262 break;
10263 default:
10264 goto err;
10265 }
10266 }
10267 out:
10268 result = JIM_OK;
10269 if (doMap)
10270 Jim_SetResult(interp, mapRes);
10271 else
10272 Jim_SetEmptyResult(interp);
10273 err:
10274 if (doMap)
10275 Jim_DecrRefCount(interp, mapRes);
10276 Jim_DecrRefCount(interp, emptyStr);
10277 Jim_Free(listsIdx);
10278 Jim_Free(listsEnd);
10279 return result;
10280 }
10281
10282 /* [foreach] */
10283 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
10284 Jim_Obj *const *argv)
10285 {
10286 return JimForeachMapHelper(interp, argc, argv, 0);
10287 }
10288
10289 /* [lmap] */
10290 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
10291 Jim_Obj *const *argv)
10292 {
10293 return JimForeachMapHelper(interp, argc, argv, 1);
10294 }
10295
10296 /* [if] */
10297 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
10298 Jim_Obj *const *argv)
10299 {
10300 int boolean, retval, current = 1, falsebody = 0;
10301 if (argc >= 3) {
10302 while (1) {
10303 /* Far not enough arguments given! */
10304 if (current >= argc) goto err;
10305 if ((retval = Jim_GetBoolFromExpr(interp,
10306 argv[current++], &boolean))
10307 != JIM_OK)
10308 return retval;
10309 /* There lacks something, isn't it? */
10310 if (current >= argc) goto err;
10311 if (Jim_CompareStringImmediate(interp, argv[current],
10312 "then")) current++;
10313 /* Tsk tsk, no then-clause? */
10314 if (current >= argc) goto err;
10315 if (boolean)
10316 return Jim_EvalObj(interp, argv[current]);
10317 /* Ok: no else-clause follows */
10318 if (++current >= argc) {
10319 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10320 return JIM_OK;
10321 }
10322 falsebody = current++;
10323 if (Jim_CompareStringImmediate(interp, argv[falsebody],
10324 "else")) {
10325 /* IIICKS - else-clause isn't last cmd? */
10326 if (current != argc-1) goto err;
10327 return Jim_EvalObj(interp, argv[current]);
10328 } else if (Jim_CompareStringImmediate(interp,
10329 argv[falsebody], "elseif"))
10330 /* Ok: elseif follows meaning all the stuff
10331 * again (how boring...) */
10332 continue;
10333 /* OOPS - else-clause is not last cmd?*/
10334 else if (falsebody != argc-1)
10335 goto err;
10336 return Jim_EvalObj(interp, argv[falsebody]);
10337 }
10338 return JIM_OK;
10339 }
10340 err:
10341 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
10342 return JIM_ERR;
10343 }
10344
10345 enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
10346
10347 /* [switch] */
10348 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
10349 Jim_Obj *const *argv)
10350 {
10351 int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
10352 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
10353 Jim_Obj *script = 0;
10354 if (argc < 3) goto wrongnumargs;
10355 for (opt=1; opt < argc; ++opt) {
10356 const char *option = Jim_GetString(argv[opt], 0);
10357 if (*option != '-') break;
10358 else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
10359 else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
10360 else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
10361 else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
10362 else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
10363 if ((argc - opt) < 2) goto wrongnumargs;
10364 command = argv[++opt];
10365 } else {
10366 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10367 Jim_AppendStrings(interp, Jim_GetResult(interp),
10368 "bad option \"", option, "\": must be -exact, -glob, "
10369 "-regexp, -command procname or --", 0);
10370 goto err;
10371 }
10372 if ((argc - opt) < 2) goto wrongnumargs;
10373 }
10374 strObj = argv[opt++];
10375 patCount = argc - opt;
10376 if (patCount == 1) {
10377 Jim_Obj **vector;
10378 JimListGetElements(interp, argv[opt], &patCount, &vector);
10379 caseList = vector;
10380 } else
10381 caseList = &argv[opt];
10382 if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
10383 for (i=0; script == 0 && i < patCount; i += 2) {
10384 Jim_Obj *patObj = caseList[i];
10385 if (!Jim_CompareStringImmediate(interp, patObj, "default")
10386 || i < (patCount-2)) {
10387 switch (matchOpt) {
10388 case SWITCH_EXACT:
10389 if (Jim_StringEqObj(strObj, patObj, 0))
10390 script = caseList[i+1];
10391 break;
10392 case SWITCH_GLOB:
10393 if (Jim_StringMatchObj(patObj, strObj, 0))
10394 script = caseList[i+1];
10395 break;
10396 case SWITCH_RE:
10397 command = Jim_NewStringObj(interp, "regexp", -1);
10398 /* Fall thru intentionally */
10399 case SWITCH_CMD: {
10400 Jim_Obj *parms[] = {command, patObj, strObj};
10401 int rc = Jim_EvalObjVector(interp, 3, parms);
10402 long matching;
10403 /* After the execution of a command we need to
10404 * make sure to reconvert the object into a list
10405 * again. Only for the single-list style [switch]. */
10406 if (argc-opt == 1) {
10407 Jim_Obj **vector;
10408 JimListGetElements(interp, argv[opt], &patCount,
10409 &vector);
10410 caseList = vector;
10411 }
10412 /* command is here already decref'd */
10413 if (rc != JIM_OK) {
10414 retcode = rc;
10415 goto err;
10416 }
10417 rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
10418 if (rc != JIM_OK) {
10419 retcode = rc;
10420 goto err;
10421 }
10422 if (matching)
10423 script = caseList[i+1];
10424 break;
10425 }
10426 default:
10427 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10428 Jim_AppendStrings(interp, Jim_GetResult(interp),
10429 "internal error: no such option implemented", 0);
10430 goto err;
10431 }
10432 } else {
10433 script = caseList[i+1];
10434 }
10435 }
10436 for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
10437 i += 2)
10438 script = caseList[i+1];
10439 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
10440 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10441 Jim_AppendStrings(interp, Jim_GetResult(interp),
10442 "no body specified for pattern \"",
10443 Jim_GetString(caseList[i-2], 0), "\"", 0);
10444 goto err;
10445 }
10446 retcode = JIM_OK;
10447 Jim_SetEmptyResult(interp);
10448 if (script != 0)
10449 retcode = Jim_EvalObj(interp, script);
10450 return retcode;
10451 wrongnumargs:
10452 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
10453 "pattern body ... ?default body? or "
10454 "{pattern body ?pattern body ...?}");
10455 err:
10456 return retcode;
10457 }
10458
10459 /* [list] */
10460 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
10461 Jim_Obj *const *argv)
10462 {
10463 Jim_Obj *listObjPtr;
10464
10465 listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
10466 Jim_SetResult(interp, listObjPtr);
10467 return JIM_OK;
10468 }
10469
10470 /* [lindex] */
10471 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
10472 Jim_Obj *const *argv)
10473 {
10474 Jim_Obj *objPtr, *listObjPtr;
10475 int i;
10476 int index;
10477
10478 if (argc < 3) {
10479 Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
10480 return JIM_ERR;
10481 }
10482 objPtr = argv[1];
10483 Jim_IncrRefCount(objPtr);
10484 for (i = 2; i < argc; i++) {
10485 listObjPtr = objPtr;
10486 if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
10487 Jim_DecrRefCount(interp, listObjPtr);
10488 return JIM_ERR;
10489 }
10490 if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
10491 JIM_NONE) != JIM_OK) {
10492 /* Returns an empty object if the index
10493 * is out of range. */
10494 Jim_DecrRefCount(interp, listObjPtr);
10495 Jim_SetEmptyResult(interp);
10496 return JIM_OK;
10497 }
10498 Jim_IncrRefCount(objPtr);
10499 Jim_DecrRefCount(interp, listObjPtr);
10500 }
10501 Jim_SetResult(interp, objPtr);
10502 Jim_DecrRefCount(interp, objPtr);
10503 return JIM_OK;
10504 }
10505
10506 /* [llength] */
10507 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
10508 Jim_Obj *const *argv)
10509 {
10510 int len;
10511
10512 if (argc != 2) {
10513 Jim_WrongNumArgs(interp, 1, argv, "list");
10514 return JIM_ERR;
10515 }
10516 Jim_ListLength(interp, argv[1], &len);
10517 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10518 return JIM_OK;
10519 }
10520
10521 /* [lappend] */
10522 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
10523 Jim_Obj *const *argv)
10524 {
10525 Jim_Obj *listObjPtr;
10526 int shared, i;
10527
10528 if (argc < 2) {
10529 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10530 return JIM_ERR;
10531 }
10532 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10533 if (!listObjPtr) {
10534 /* Create the list if it does not exists */
10535 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10536 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10537 Jim_FreeNewObj(interp, listObjPtr);
10538 return JIM_ERR;
10539 }
10540 }
10541 shared = Jim_IsShared(listObjPtr);
10542 if (shared)
10543 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
10544 for (i = 2; i < argc; i++)
10545 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
10546 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
10547 if (shared)
10548 Jim_FreeNewObj(interp, listObjPtr);
10549 return JIM_ERR;
10550 }
10551 Jim_SetResult(interp, listObjPtr);
10552 return JIM_OK;
10553 }
10554
10555 /* [linsert] */
10556 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10557 Jim_Obj *const *argv)
10558 {
10559 int index, len;
10560 Jim_Obj *listPtr;
10561
10562 if (argc < 4) {
10563 Jim_WrongNumArgs(interp, 1, argv, "list index element "
10564 "?element ...?");
10565 return JIM_ERR;
10566 }
10567 listPtr = argv[1];
10568 if (Jim_IsShared(listPtr))
10569 listPtr = Jim_DuplicateObj(interp, listPtr);
10570 if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10571 goto err;
10572 Jim_ListLength(interp, listPtr, &len);
10573 if (index >= len)
10574 index = len;
10575 else if (index < 0)
10576 index = len + index + 1;
10577 Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10578 Jim_SetResult(interp, listPtr);
10579 return JIM_OK;
10580 err:
10581 if (listPtr != argv[1]) {
10582 Jim_FreeNewObj(interp, listPtr);
10583 }
10584 return JIM_ERR;
10585 }
10586
10587 /* [lset] */
10588 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10589 Jim_Obj *const *argv)
10590 {
10591 if (argc < 3) {
10592 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10593 return JIM_ERR;
10594 } else if (argc == 3) {
10595 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10596 return JIM_ERR;
10597 Jim_SetResult(interp, argv[2]);
10598 return JIM_OK;
10599 }
10600 if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10601 == JIM_ERR) return JIM_ERR;
10602 return JIM_OK;
10603 }
10604
10605 /* [lsort] */
10606 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10607 {
10608 const char *options[] = {
10609 "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10610 };
10611 enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10612 Jim_Obj *resObj;
10613 int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10614 int decreasing = 0;
10615
10616 if (argc < 2) {
10617 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10618 return JIM_ERR;
10619 }
10620 for (i = 1; i < (argc-1); i++) {
10621 int option;
10622
10623 if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10624 != JIM_OK)
10625 return JIM_ERR;
10626 switch(option) {
10627 case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10628 case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10629 case OPT_INCREASING: decreasing = 0; break;
10630 case OPT_DECREASING: decreasing = 1; break;
10631 }
10632 }
10633 if (decreasing) {
10634 switch(lsortType) {
10635 case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10636 case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10637 }
10638 }
10639 resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10640 ListSortElements(interp, resObj, lsortType);
10641 Jim_SetResult(interp, resObj);
10642 return JIM_OK;
10643 }
10644
10645 /* [append] */
10646 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10647 Jim_Obj *const *argv)
10648 {
10649 Jim_Obj *stringObjPtr;
10650 int shared, i;
10651
10652 if (argc < 2) {
10653 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10654 return JIM_ERR;
10655 }
10656 if (argc == 2) {
10657 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10658 if (!stringObjPtr) return JIM_ERR;
10659 } else {
10660 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10661 if (!stringObjPtr) {
10662 /* Create the string if it does not exists */
10663 stringObjPtr = Jim_NewEmptyStringObj(interp);
10664 if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10665 != JIM_OK) {
10666 Jim_FreeNewObj(interp, stringObjPtr);
10667 return JIM_ERR;
10668 }
10669 }
10670 }
10671 shared = Jim_IsShared(stringObjPtr);
10672 if (shared)
10673 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10674 for (i = 2; i < argc; i++)
10675 Jim_AppendObj(interp, stringObjPtr, argv[i]);
10676 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10677 if (shared)
10678 Jim_FreeNewObj(interp, stringObjPtr);
10679 return JIM_ERR;
10680 }
10681 Jim_SetResult(interp, stringObjPtr);
10682 return JIM_OK;
10683 }
10684
10685 /* [debug] */
10686 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10687 Jim_Obj *const *argv)
10688 {
10689 const char *options[] = {
10690 "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10691 "exprbc",
10692 NULL
10693 };
10694 enum {
10695 OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10696 OPT_EXPRLEN, OPT_EXPRBC
10697 };
10698 int option;
10699
10700 if (argc < 2) {
10701 Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10702 return JIM_ERR;
10703 }
10704 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10705 JIM_ERRMSG) != JIM_OK)
10706 return JIM_ERR;
10707 if (option == OPT_REFCOUNT) {
10708 if (argc != 3) {
10709 Jim_WrongNumArgs(interp, 2, argv, "object");
10710 return JIM_ERR;
10711 }
10712 Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10713 return JIM_OK;
10714 } else if (option == OPT_OBJCOUNT) {
10715 int freeobj = 0, liveobj = 0;
10716 char buf[256];
10717 Jim_Obj *objPtr;
10718
10719 if (argc != 2) {
10720 Jim_WrongNumArgs(interp, 2, argv, "");
10721 return JIM_ERR;
10722 }
10723 /* Count the number of free objects. */
10724 objPtr = interp->freeList;
10725 while (objPtr) {
10726 freeobj++;
10727 objPtr = objPtr->nextObjPtr;
10728 }
10729 /* Count the number of live objects. */
10730 objPtr = interp->liveList;
10731 while (objPtr) {
10732 liveobj++;
10733 objPtr = objPtr->nextObjPtr;
10734 }
10735 /* Set the result string and return. */
10736 sprintf(buf, "free %d used %d", freeobj, liveobj);
10737 Jim_SetResultString(interp, buf, -1);
10738 return JIM_OK;
10739 } else if (option == OPT_OBJECTS) {
10740 Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10741 /* Count the number of live objects. */
10742 objPtr = interp->liveList;
10743 listObjPtr = Jim_NewListObj(interp, NULL, 0);
10744 while (objPtr) {
10745 char buf[128];
10746 const char *type = objPtr->typePtr ?
10747 objPtr->typePtr->name : "";
10748 subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10749 sprintf(buf, "%p", objPtr);
10750 Jim_ListAppendElement(interp, subListObjPtr,
10751 Jim_NewStringObj(interp, buf, -1));
10752 Jim_ListAppendElement(interp, subListObjPtr,
10753 Jim_NewStringObj(interp, type, -1));
10754 Jim_ListAppendElement(interp, subListObjPtr,
10755 Jim_NewIntObj(interp, objPtr->refCount));
10756 Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10757 Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10758 objPtr = objPtr->nextObjPtr;
10759 }
10760 Jim_SetResult(interp, listObjPtr);
10761 return JIM_OK;
10762 } else if (option == OPT_INVSTR) {
10763 Jim_Obj *objPtr;
10764
10765 if (argc != 3) {
10766 Jim_WrongNumArgs(interp, 2, argv, "object");
10767 return JIM_ERR;
10768 }
10769 objPtr = argv[2];
10770 if (objPtr->typePtr != NULL)
10771 Jim_InvalidateStringRep(objPtr);
10772 Jim_SetEmptyResult(interp);
10773 return JIM_OK;
10774 } else if (option == OPT_SCRIPTLEN) {
10775 ScriptObj *script;
10776 if (argc != 3) {
10777 Jim_WrongNumArgs(interp, 2, argv, "script");
10778 return JIM_ERR;
10779 }
10780 script = Jim_GetScript(interp, argv[2]);
10781 Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10782 return JIM_OK;
10783 } else if (option == OPT_EXPRLEN) {
10784 ExprByteCode *expr;
10785 if (argc != 3) {
10786 Jim_WrongNumArgs(interp, 2, argv, "expression");
10787 return JIM_ERR;
10788 }
10789 expr = Jim_GetExpression(interp, argv[2]);
10790 if (expr == NULL)
10791 return JIM_ERR;
10792 Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10793 return JIM_OK;
10794 } else if (option == OPT_EXPRBC) {
10795 Jim_Obj *objPtr;
10796 ExprByteCode *expr;
10797 int i;
10798
10799 if (argc != 3) {
10800 Jim_WrongNumArgs(interp, 2, argv, "expression");
10801 return JIM_ERR;
10802 }
10803 expr = Jim_GetExpression(interp, argv[2]);
10804 if (expr == NULL)
10805 return JIM_ERR;
10806 objPtr = Jim_NewListObj(interp, NULL, 0);
10807 for (i = 0; i < expr->len; i++) {
10808 const char *type;
10809 Jim_ExprOperator *op;
10810
10811 switch(expr->opcode[i]) {
10812 case JIM_EXPROP_NUMBER: type = "number"; break;
10813 case JIM_EXPROP_COMMAND: type = "command"; break;
10814 case JIM_EXPROP_VARIABLE: type = "variable"; break;
10815 case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10816 case JIM_EXPROP_SUBST: type = "subst"; break;
10817 case JIM_EXPROP_STRING: type = "string"; break;
10818 default:
10819 op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10820 if (op == NULL) {
10821 type = "private";
10822 } else {
10823 type = "operator";
10824 }
10825 break;
10826 }
10827 Jim_ListAppendElement(interp, objPtr,
10828 Jim_NewStringObj(interp, type, -1));
10829 Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10830 }
10831 Jim_SetResult(interp, objPtr);
10832 return JIM_OK;
10833 } else {
10834 Jim_SetResultString(interp,
10835 "bad option. Valid options are refcount, "
10836 "objcount, objects, invstr", -1);
10837 return JIM_ERR;
10838 }
10839 return JIM_OK; /* unreached */
10840 }
10841
10842 /* [eval] */
10843 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10844 Jim_Obj *const *argv)
10845 {
10846 if (argc == 2) {
10847 return Jim_EvalObj(interp, argv[1]);
10848 } else if (argc > 2) {
10849 Jim_Obj *objPtr;
10850 int retcode;
10851
10852 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10853 Jim_IncrRefCount(objPtr);
10854 retcode = Jim_EvalObj(interp, objPtr);
10855 Jim_DecrRefCount(interp, objPtr);
10856 return retcode;
10857 } else {
10858 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10859 return JIM_ERR;
10860 }
10861 }
10862
10863 /* [uplevel] */
10864 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10865 Jim_Obj *const *argv)
10866 {
10867 if (argc >= 2) {
10868 int retcode, newLevel, oldLevel;
10869 Jim_CallFrame *savedCallFrame, *targetCallFrame;
10870 Jim_Obj *objPtr;
10871 const char *str;
10872
10873 /* Save the old callframe pointer */
10874 savedCallFrame = interp->framePtr;
10875
10876 /* Lookup the target frame pointer */
10877 str = Jim_GetString(argv[1], NULL);
10878 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10879 {
10880 if (Jim_GetCallFrameByLevel(interp, argv[1],
10881 &targetCallFrame,
10882 &newLevel) != JIM_OK)
10883 return JIM_ERR;
10884 argc--;
10885 argv++;
10886 } else {
10887 if (Jim_GetCallFrameByLevel(interp, NULL,
10888 &targetCallFrame,
10889 &newLevel) != JIM_OK)
10890 return JIM_ERR;
10891 }
10892 if (argc < 2) {
10893 argc++;
10894 argv--;
10895 Jim_WrongNumArgs(interp, 1, argv,
10896 "?level? command ?arg ...?");
10897 return JIM_ERR;
10898 }
10899 /* Eval the code in the target callframe. */
10900 interp->framePtr = targetCallFrame;
10901 oldLevel = interp->numLevels;
10902 interp->numLevels = newLevel;
10903 if (argc == 2) {
10904 retcode = Jim_EvalObj(interp, argv[1]);
10905 } else {
10906 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10907 Jim_IncrRefCount(objPtr);
10908 retcode = Jim_EvalObj(interp, objPtr);
10909 Jim_DecrRefCount(interp, objPtr);
10910 }
10911 interp->numLevels = oldLevel;
10912 interp->framePtr = savedCallFrame;
10913 return retcode;
10914 } else {
10915 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10916 return JIM_ERR;
10917 }
10918 }
10919
10920 /* [expr] */
10921 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10922 Jim_Obj *const *argv)
10923 {
10924 Jim_Obj *exprResultPtr;
10925 int retcode;
10926
10927 if (argc == 2) {
10928 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10929 } else if (argc > 2) {
10930 Jim_Obj *objPtr;
10931
10932 objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10933 Jim_IncrRefCount(objPtr);
10934 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10935 Jim_DecrRefCount(interp, objPtr);
10936 } else {
10937 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10938 return JIM_ERR;
10939 }
10940 if (retcode != JIM_OK) return retcode;
10941 Jim_SetResult(interp, exprResultPtr);
10942 Jim_DecrRefCount(interp, exprResultPtr);
10943 return JIM_OK;
10944 }
10945
10946 /* [break] */
10947 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10948 Jim_Obj *const *argv)
10949 {
10950 if (argc != 1) {
10951 Jim_WrongNumArgs(interp, 1, argv, "");
10952 return JIM_ERR;
10953 }
10954 return JIM_BREAK;
10955 }
10956
10957 /* [continue] */
10958 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10959 Jim_Obj *const *argv)
10960 {
10961 if (argc != 1) {
10962 Jim_WrongNumArgs(interp, 1, argv, "");
10963 return JIM_ERR;
10964 }
10965 return JIM_CONTINUE;
10966 }
10967
10968 /* [return] */
10969 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10970 Jim_Obj *const *argv)
10971 {
10972 if (argc == 1) {
10973 return JIM_RETURN;
10974 } else if (argc == 2) {
10975 Jim_SetResult(interp, argv[1]);
10976 interp->returnCode = JIM_OK;
10977 return JIM_RETURN;
10978 } else if (argc == 3 || argc == 4) {
10979 int returnCode;
10980 if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10981 return JIM_ERR;
10982 interp->returnCode = returnCode;
10983 if (argc == 4)
10984 Jim_SetResult(interp, argv[3]);
10985 return JIM_RETURN;
10986 } else {
10987 Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10988 return JIM_ERR;
10989 }
10990 return JIM_RETURN; /* unreached */
10991 }
10992
10993 /* [tailcall] */
10994 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10995 Jim_Obj *const *argv)
10996 {
10997 Jim_Obj *objPtr;
10998
10999 objPtr = Jim_NewListObj(interp, argv+1, argc-1);
11000 Jim_SetResult(interp, objPtr);
11001 return JIM_EVAL;
11002 }
11003
11004 /* [proc] */
11005 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
11006 Jim_Obj *const *argv)
11007 {
11008 int argListLen;
11009 int arityMin, arityMax;
11010
11011 if (argc != 4 && argc != 5) {
11012 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
11013 return JIM_ERR;
11014 }
11015 Jim_ListLength(interp, argv[2], &argListLen);
11016 arityMin = arityMax = argListLen+1;
11017
11018 if (argListLen) {
11019 const char *str;
11020 int len;
11021 Jim_Obj *argPtr;
11022
11023 /* Check for 'args' and adjust arityMin and arityMax if necessary */
11024 Jim_ListIndex(interp, argv[2], argListLen-1, &argPtr, JIM_NONE);
11025 str = Jim_GetString(argPtr, &len);
11026 if (len == 4 && memcmp(str, "args", 4) == 0) {
11027 arityMin--;
11028 arityMax = -1;
11029 }
11030
11031 /* Check for default arguments and reduce arityMin if necessary */
11032 while (arityMin > 1) {
11033 int len;
11034 Jim_ListIndex(interp, argv[2], arityMin - 2, &argPtr, JIM_NONE);
11035 Jim_ListLength(interp, argPtr, &len);
11036 if (len != 2) {
11037 /* No default argument */
11038 break;
11039 }
11040 arityMin--;
11041 }
11042 }
11043 if (argc == 4) {
11044 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11045 argv[2], NULL, argv[3], arityMin, arityMax);
11046 } else {
11047 return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
11048 argv[2], argv[3], argv[4], arityMin, arityMax);
11049 }
11050 }
11051
11052 /* [concat] */
11053 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
11054 Jim_Obj *const *argv)
11055 {
11056 Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
11057 return JIM_OK;
11058 }
11059
11060 /* [upvar] */
11061 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
11062 Jim_Obj *const *argv)
11063 {
11064 const char *str;
11065 int i;
11066 Jim_CallFrame *targetCallFrame;
11067
11068 /* Lookup the target frame pointer */
11069 str = Jim_GetString(argv[1], NULL);
11070 if (argc > 3 &&
11071 ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
11072 {
11073 if (Jim_GetCallFrameByLevel(interp, argv[1],
11074 &targetCallFrame, NULL) != JIM_OK)
11075 return JIM_ERR;
11076 argc--;
11077 argv++;
11078 } else {
11079 if (Jim_GetCallFrameByLevel(interp, NULL,
11080 &targetCallFrame, NULL) != JIM_OK)
11081 return JIM_ERR;
11082 }
11083 /* Check for arity */
11084 if (argc < 3 || ((argc-1)%2) != 0) {
11085 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
11086 return JIM_ERR;
11087 }
11088 /* Now... for every other/local couple: */
11089 for (i = 1; i < argc; i += 2) {
11090 if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
11091 targetCallFrame) != JIM_OK) return JIM_ERR;
11092 }
11093 return JIM_OK;
11094 }
11095
11096 /* [global] */
11097 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
11098 Jim_Obj *const *argv)
11099 {
11100 int i;
11101
11102 if (argc < 2) {
11103 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
11104 return JIM_ERR;
11105 }
11106 /* Link every var to the toplevel having the same name */
11107 if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
11108 for (i = 1; i < argc; i++) {
11109 if (Jim_SetVariableLink(interp, argv[i], argv[i],
11110 interp->topFramePtr) != JIM_OK) return JIM_ERR;
11111 }
11112 return JIM_OK;
11113 }
11114
11115 /* does the [string map] operation. On error NULL is returned,
11116 * otherwise a new string object with the result, having refcount = 0,
11117 * is returned. */
11118 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
11119 Jim_Obj *objPtr, int nocase)
11120 {
11121 int numMaps;
11122 const char **key, *str, *noMatchStart = NULL;
11123 Jim_Obj **value;
11124 int *keyLen, strLen, i;
11125 Jim_Obj *resultObjPtr;
11126
11127 Jim_ListLength(interp, mapListObjPtr, &numMaps);
11128 if (numMaps % 2) {
11129 Jim_SetResultString(interp,
11130 "list must contain an even number of elements", -1);
11131 return NULL;
11132 }
11133 /* Initialization */
11134 numMaps /= 2;
11135 key = Jim_Alloc(sizeof(char*)*numMaps);
11136 keyLen = Jim_Alloc(sizeof(int)*numMaps);
11137 value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
11138 resultObjPtr = Jim_NewStringObj(interp, "", 0);
11139 for (i = 0; i < numMaps; i++) {
11140 Jim_Obj *eleObjPtr;
11141
11142 Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
11143 key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
11144 Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
11145 value[i] = eleObjPtr;
11146 }
11147 str = Jim_GetString(objPtr, &strLen);
11148 /* Map it */
11149 while(strLen) {
11150 for (i = 0; i < numMaps; i++) {
11151 if (strLen >= keyLen[i] && keyLen[i]) {
11152 if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
11153 nocase))
11154 {
11155 if (noMatchStart) {
11156 Jim_AppendString(interp, resultObjPtr,
11157 noMatchStart, str-noMatchStart);
11158 noMatchStart = NULL;
11159 }
11160 Jim_AppendObj(interp, resultObjPtr, value[i]);
11161 str += keyLen[i];
11162 strLen -= keyLen[i];
11163 break;
11164 }
11165 }
11166 }
11167 if (i == numMaps) { /* no match */
11168 if (noMatchStart == NULL)
11169 noMatchStart = str;
11170 str ++;
11171 strLen --;
11172 }
11173 }
11174 if (noMatchStart) {
11175 Jim_AppendString(interp, resultObjPtr,
11176 noMatchStart, str-noMatchStart);
11177 }
11178 Jim_Free((void*)key);
11179 Jim_Free(keyLen);
11180 Jim_Free(value);
11181 return resultObjPtr;
11182 }
11183
11184 /* [string] */
11185 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
11186 Jim_Obj *const *argv)
11187 {
11188 int option;
11189 const char *options[] = {
11190 "length", "compare", "match", "equal", "range", "map", "repeat",
11191 "index", "first", "tolower", "toupper", NULL
11192 };
11193 enum {
11194 OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
11195 OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
11196 };
11197
11198 if (argc < 2) {
11199 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11200 return JIM_ERR;
11201 }
11202 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11203 JIM_ERRMSG) != JIM_OK)
11204 return JIM_ERR;
11205
11206 if (option == OPT_LENGTH) {
11207 int len;
11208
11209 if (argc != 3) {
11210 Jim_WrongNumArgs(interp, 2, argv, "string");
11211 return JIM_ERR;
11212 }
11213 Jim_GetString(argv[2], &len);
11214 Jim_SetResult(interp, Jim_NewIntObj(interp, len));
11215 return JIM_OK;
11216 } else if (option == OPT_COMPARE) {
11217 int nocase = 0;
11218 if ((argc != 4 && argc != 5) ||
11219 (argc == 5 && Jim_CompareStringImmediate(interp,
11220 argv[2], "-nocase") == 0)) {
11221 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11222 return JIM_ERR;
11223 }
11224 if (argc == 5) {
11225 nocase = 1;
11226 argv++;
11227 }
11228 Jim_SetResult(interp, Jim_NewIntObj(interp,
11229 Jim_StringCompareObj(argv[2],
11230 argv[3], nocase)));
11231 return JIM_OK;
11232 } else if (option == OPT_MATCH) {
11233 int nocase = 0;
11234 if ((argc != 4 && argc != 5) ||
11235 (argc == 5 && Jim_CompareStringImmediate(interp,
11236 argv[2], "-nocase") == 0)) {
11237 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
11238 "string");
11239 return JIM_ERR;
11240 }
11241 if (argc == 5) {
11242 nocase = 1;
11243 argv++;
11244 }
11245 Jim_SetResult(interp,
11246 Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
11247 argv[3], nocase)));
11248 return JIM_OK;
11249 } else if (option == OPT_EQUAL) {
11250 if (argc != 4) {
11251 Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
11252 return JIM_ERR;
11253 }
11254 Jim_SetResult(interp,
11255 Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
11256 argv[3], 0)));
11257 return JIM_OK;
11258 } else if (option == OPT_RANGE) {
11259 Jim_Obj *objPtr;
11260
11261 if (argc != 5) {
11262 Jim_WrongNumArgs(interp, 2, argv, "string first last");
11263 return JIM_ERR;
11264 }
11265 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
11266 if (objPtr == NULL)
11267 return JIM_ERR;
11268 Jim_SetResult(interp, objPtr);
11269 return JIM_OK;
11270 } else if (option == OPT_MAP) {
11271 int nocase = 0;
11272 Jim_Obj *objPtr;
11273
11274 if ((argc != 4 && argc != 5) ||
11275 (argc == 5 && Jim_CompareStringImmediate(interp,
11276 argv[2], "-nocase") == 0)) {
11277 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
11278 "string");
11279 return JIM_ERR;
11280 }
11281 if (argc == 5) {
11282 nocase = 1;
11283 argv++;
11284 }
11285 objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
11286 if (objPtr == NULL)
11287 return JIM_ERR;
11288 Jim_SetResult(interp, objPtr);
11289 return JIM_OK;
11290 } else if (option == OPT_REPEAT) {
11291 Jim_Obj *objPtr;
11292 jim_wide count;
11293
11294 if (argc != 4) {
11295 Jim_WrongNumArgs(interp, 2, argv, "string count");
11296 return JIM_ERR;
11297 }
11298 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
11299 return JIM_ERR;
11300 objPtr = Jim_NewStringObj(interp, "", 0);
11301 while (count--) {
11302 Jim_AppendObj(interp, objPtr, argv[2]);
11303 }
11304 Jim_SetResult(interp, objPtr);
11305 return JIM_OK;
11306 } else if (option == OPT_INDEX) {
11307 int index, len;
11308 const char *str;
11309
11310 if (argc != 4) {
11311 Jim_WrongNumArgs(interp, 2, argv, "string index");
11312 return JIM_ERR;
11313 }
11314 if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
11315 return JIM_ERR;
11316 str = Jim_GetString(argv[2], &len);
11317 if (index != INT_MIN && index != INT_MAX)
11318 index = JimRelToAbsIndex(len, index);
11319 if (index < 0 || index >= len) {
11320 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11321 return JIM_OK;
11322 } else {
11323 Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
11324 return JIM_OK;
11325 }
11326 } else if (option == OPT_FIRST) {
11327 int index = 0, l1, l2;
11328 const char *s1, *s2;
11329
11330 if (argc != 4 && argc != 5) {
11331 Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
11332 return JIM_ERR;
11333 }
11334 s1 = Jim_GetString(argv[2], &l1);
11335 s2 = Jim_GetString(argv[3], &l2);
11336 if (argc == 5) {
11337 if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
11338 return JIM_ERR;
11339 index = JimRelToAbsIndex(l2, index);
11340 }
11341 Jim_SetResult(interp, Jim_NewIntObj(interp,
11342 JimStringFirst(s1, l1, s2, l2, index)));
11343 return JIM_OK;
11344 } else if (option == OPT_TOLOWER) {
11345 if (argc != 3) {
11346 Jim_WrongNumArgs(interp, 2, argv, "string");
11347 return JIM_ERR;
11348 }
11349 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
11350 } else if (option == OPT_TOUPPER) {
11351 if (argc != 3) {
11352 Jim_WrongNumArgs(interp, 2, argv, "string");
11353 return JIM_ERR;
11354 }
11355 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
11356 }
11357 return JIM_OK;
11358 }
11359
11360 /* [time] */
11361 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
11362 Jim_Obj *const *argv)
11363 {
11364 long i, count = 1;
11365 jim_wide start, elapsed;
11366 char buf [256];
11367 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
11368
11369 if (argc < 2) {
11370 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
11371 return JIM_ERR;
11372 }
11373 if (argc == 3) {
11374 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
11375 return JIM_ERR;
11376 }
11377 if (count < 0)
11378 return JIM_OK;
11379 i = count;
11380 start = JimClock();
11381 while (i-- > 0) {
11382 int retval;
11383
11384 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
11385 return retval;
11386 }
11387 elapsed = JimClock() - start;
11388 sprintf(buf, fmt, elapsed/count);
11389 Jim_SetResultString(interp, buf, -1);
11390 return JIM_OK;
11391 }
11392
11393 /* [exit] */
11394 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
11395 Jim_Obj *const *argv)
11396 {
11397 long exitCode = 0;
11398
11399 if (argc > 2) {
11400 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
11401 return JIM_ERR;
11402 }
11403 if (argc == 2) {
11404 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
11405 return JIM_ERR;
11406 }
11407 interp->exitCode = exitCode;
11408 return JIM_EXIT;
11409 }
11410
11411 /* [catch] */
11412 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
11413 Jim_Obj *const *argv)
11414 {
11415 int exitCode = 0;
11416
11417 if (argc != 2 && argc != 3) {
11418 Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
11419 return JIM_ERR;
11420 }
11421 exitCode = Jim_EvalObj(interp, argv[1]);
11422 if (argc == 3) {
11423 if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
11424 != JIM_OK)
11425 return JIM_ERR;
11426 }
11427 Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
11428 return JIM_OK;
11429 }
11430
11431 /* [ref] */
11432 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
11433 Jim_Obj *const *argv)
11434 {
11435 if (argc != 3 && argc != 4) {
11436 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
11437 return JIM_ERR;
11438 }
11439 if (argc == 3) {
11440 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
11441 } else {
11442 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
11443 argv[3]));
11444 }
11445 return JIM_OK;
11446 }
11447
11448 /* [getref] */
11449 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
11450 Jim_Obj *const *argv)
11451 {
11452 Jim_Reference *refPtr;
11453
11454 if (argc != 2) {
11455 Jim_WrongNumArgs(interp, 1, argv, "reference");
11456 return JIM_ERR;
11457 }
11458 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11459 return JIM_ERR;
11460 Jim_SetResult(interp, refPtr->objPtr);
11461 return JIM_OK;
11462 }
11463
11464 /* [setref] */
11465 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
11466 Jim_Obj *const *argv)
11467 {
11468 Jim_Reference *refPtr;
11469
11470 if (argc != 3) {
11471 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
11472 return JIM_ERR;
11473 }
11474 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
11475 return JIM_ERR;
11476 Jim_IncrRefCount(argv[2]);
11477 Jim_DecrRefCount(interp, refPtr->objPtr);
11478 refPtr->objPtr = argv[2];
11479 Jim_SetResult(interp, argv[2]);
11480 return JIM_OK;
11481 }
11482
11483 /* [collect] */
11484 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
11485 Jim_Obj *const *argv)
11486 {
11487 if (argc != 1) {
11488 Jim_WrongNumArgs(interp, 1, argv, "");
11489 return JIM_ERR;
11490 }
11491 Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
11492 return JIM_OK;
11493 }
11494
11495 /* [finalize] reference ?newValue? */
11496 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
11497 Jim_Obj *const *argv)
11498 {
11499 if (argc != 2 && argc != 3) {
11500 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
11501 return JIM_ERR;
11502 }
11503 if (argc == 2) {
11504 Jim_Obj *cmdNamePtr;
11505
11506 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
11507 return JIM_ERR;
11508 if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
11509 Jim_SetResult(interp, cmdNamePtr);
11510 } else {
11511 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
11512 return JIM_ERR;
11513 Jim_SetResult(interp, argv[2]);
11514 }
11515 return JIM_OK;
11516 }
11517
11518 /* TODO */
11519 /* [info references] (list of all the references/finalizers) */
11520
11521 /* [rename] */
11522 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
11523 Jim_Obj *const *argv)
11524 {
11525 const char *oldName, *newName;
11526
11527 if (argc != 3) {
11528 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
11529 return JIM_ERR;
11530 }
11531 oldName = Jim_GetString(argv[1], NULL);
11532 newName = Jim_GetString(argv[2], NULL);
11533 if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
11534 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11535 Jim_AppendStrings(interp, Jim_GetResult(interp),
11536 "can't rename \"", oldName, "\": ",
11537 "command doesn't exist", NULL);
11538 return JIM_ERR;
11539 }
11540 return JIM_OK;
11541 }
11542
11543 /* [dict] */
11544 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
11545 Jim_Obj *const *argv)
11546 {
11547 int option;
11548 const char *options[] = {
11549 "create", "get", "set", "unset", "exists", NULL
11550 };
11551 enum {
11552 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
11553 };
11554
11555 if (argc < 2) {
11556 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11557 return JIM_ERR;
11558 }
11559
11560 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11561 JIM_ERRMSG) != JIM_OK)
11562 return JIM_ERR;
11563
11564 if (option == OPT_CREATE) {
11565 Jim_Obj *objPtr;
11566
11567 if (argc % 2) {
11568 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11569 return JIM_ERR;
11570 }
11571 objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11572 Jim_SetResult(interp, objPtr);
11573 return JIM_OK;
11574 } else if (option == OPT_GET) {
11575 Jim_Obj *objPtr;
11576
11577 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11578 JIM_ERRMSG) != JIM_OK)
11579 return JIM_ERR;
11580 Jim_SetResult(interp, objPtr);
11581 return JIM_OK;
11582 } else if (option == OPT_SET) {
11583 if (argc < 5) {
11584 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11585 return JIM_ERR;
11586 }
11587 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11588 argv[argc-1]);
11589 } else if (option == OPT_UNSET) {
11590 if (argc < 4) {
11591 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11592 return JIM_ERR;
11593 }
11594 return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11595 NULL);
11596 } else if (option == OPT_EXIST) {
11597 Jim_Obj *objPtr;
11598 int exists;
11599
11600 if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11601 JIM_ERRMSG) == JIM_OK)
11602 exists = 1;
11603 else
11604 exists = 0;
11605 Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11606 return JIM_OK;
11607 } else {
11608 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11609 Jim_AppendStrings(interp, Jim_GetResult(interp),
11610 "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11611 " must be create, get, set", NULL);
11612 return JIM_ERR;
11613 }
11614 return JIM_OK;
11615 }
11616
11617 /* [load] */
11618 static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11619 Jim_Obj *const *argv)
11620 {
11621 if (argc < 2) {
11622 Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11623 return JIM_ERR;
11624 }
11625 return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11626 }
11627
11628 /* [subst] */
11629 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11630 Jim_Obj *const *argv)
11631 {
11632 int i, flags = 0;
11633 Jim_Obj *objPtr;
11634
11635 if (argc < 2) {
11636 Jim_WrongNumArgs(interp, 1, argv,
11637 "?-nobackslashes? ?-nocommands? ?-novariables? string");
11638 return JIM_ERR;
11639 }
11640 i = argc-2;
11641 while(i--) {
11642 if (Jim_CompareStringImmediate(interp, argv[i+1],
11643 "-nobackslashes"))
11644 flags |= JIM_SUBST_NOESC;
11645 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11646 "-novariables"))
11647 flags |= JIM_SUBST_NOVAR;
11648 else if (Jim_CompareStringImmediate(interp, argv[i+1],
11649 "-nocommands"))
11650 flags |= JIM_SUBST_NOCMD;
11651 else {
11652 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11653 Jim_AppendStrings(interp, Jim_GetResult(interp),
11654 "bad option \"", Jim_GetString(argv[i+1], NULL),
11655 "\": must be -nobackslashes, -nocommands, or "
11656 "-novariables", NULL);
11657 return JIM_ERR;
11658 }
11659 }
11660 if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11661 return JIM_ERR;
11662 Jim_SetResult(interp, objPtr);
11663 return JIM_OK;
11664 }
11665
11666 /* [info] */
11667 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11668 Jim_Obj *const *argv)
11669 {
11670 int cmd, result = JIM_OK;
11671 static const char *commands[] = {
11672 "body", "commands", "exists", "globals", "level", "locals",
11673 "vars", "version", "complete", "args", "hostname", NULL
11674 };
11675 enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11676 INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS, INFO_HOSTNAME};
11677
11678 if (argc < 2) {
11679 Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11680 return JIM_ERR;
11681 }
11682 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11683 != JIM_OK) {
11684 return JIM_ERR;
11685 }
11686
11687 if (cmd == INFO_COMMANDS) {
11688 if (argc != 2 && argc != 3) {
11689 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11690 return JIM_ERR;
11691 }
11692 if (argc == 3)
11693 Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11694 else
11695 Jim_SetResult(interp, JimCommandsList(interp, NULL));
11696 } else if (cmd == INFO_EXISTS) {
11697 Jim_Obj *exists;
11698 if (argc != 3) {
11699 Jim_WrongNumArgs(interp, 2, argv, "varName");
11700 return JIM_ERR;
11701 }
11702 exists = Jim_GetVariable(interp, argv[2], 0);
11703 Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11704 } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11705 int mode;
11706 switch (cmd) {
11707 case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11708 case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
11709 case INFO_VARS: mode = JIM_VARLIST_VARS; break;
11710 default: mode = 0; /* avoid warning */; break;
11711 }
11712 if (argc != 2 && argc != 3) {
11713 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11714 return JIM_ERR;
11715 }
11716 if (argc == 3)
11717 Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11718 else
11719 Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11720 } else if (cmd == INFO_LEVEL) {
11721 Jim_Obj *objPtr;
11722 switch (argc) {
11723 case 2:
11724 Jim_SetResult(interp,
11725 Jim_NewIntObj(interp, interp->numLevels));
11726 break;
11727 case 3:
11728 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11729 return JIM_ERR;
11730 Jim_SetResult(interp, objPtr);
11731 break;
11732 default:
11733 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11734 return JIM_ERR;
11735 }
11736 } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11737 Jim_Cmd *cmdPtr;
11738
11739 if (argc != 3) {
11740 Jim_WrongNumArgs(interp, 2, argv, "procname");
11741 return JIM_ERR;
11742 }
11743 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11744 return JIM_ERR;
11745 if (cmdPtr->cmdProc != NULL) {
11746 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11747 Jim_AppendStrings(interp, Jim_GetResult(interp),
11748 "command \"", Jim_GetString(argv[2], NULL),
11749 "\" is not a procedure", NULL);
11750 return JIM_ERR;
11751 }
11752 if (cmd == INFO_BODY)
11753 Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11754 else
11755 Jim_SetResult(interp, cmdPtr->argListObjPtr);
11756 } else if (cmd == INFO_VERSION) {
11757 char buf[(JIM_INTEGER_SPACE * 2) + 1];
11758 sprintf(buf, "%d.%d",
11759 JIM_VERSION / 100, JIM_VERSION % 100);
11760 Jim_SetResultString(interp, buf, -1);
11761 } else if (cmd == INFO_COMPLETE) {
11762 const char *s;
11763 int len;
11764
11765 if (argc != 3) {
11766 Jim_WrongNumArgs(interp, 2, argv, "script");
11767 return JIM_ERR;
11768 }
11769 s = Jim_GetString(argv[2], &len);
11770 Jim_SetResult(interp,
11771 Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11772 } else if (cmd == INFO_HOSTNAME) {
11773 /* Redirect to os.hostname if it exists */
11774 Jim_Obj *command = Jim_NewStringObj(interp, "os.gethostname", -1);
11775 result = Jim_EvalObjVector(interp, 1, &command);
11776 }
11777 return result;
11778 }
11779
11780 /* [split] */
11781 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11782 Jim_Obj *const *argv)
11783 {
11784 const char *str, *splitChars, *noMatchStart;
11785 int splitLen, strLen, i;
11786 Jim_Obj *resObjPtr;
11787
11788 if (argc != 2 && argc != 3) {
11789 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11790 return JIM_ERR;
11791 }
11792 /* Init */
11793 if (argc == 2) {
11794 splitChars = " \n\t\r";
11795 splitLen = 4;
11796 } else {
11797 splitChars = Jim_GetString(argv[2], &splitLen);
11798 }
11799 str = Jim_GetString(argv[1], &strLen);
11800 if (!strLen) return JIM_OK;
11801 noMatchStart = str;
11802 resObjPtr = Jim_NewListObj(interp, NULL, 0);
11803 /* Split */
11804 if (splitLen) {
11805 while (strLen) {
11806 for (i = 0; i < splitLen; i++) {
11807 if (*str == splitChars[i]) {
11808 Jim_Obj *objPtr;
11809
11810 objPtr = Jim_NewStringObj(interp, noMatchStart,
11811 (str-noMatchStart));
11812 Jim_ListAppendElement(interp, resObjPtr, objPtr);
11813 noMatchStart = str+1;
11814 break;
11815 }
11816 }
11817 str ++;
11818 strLen --;
11819 }
11820 Jim_ListAppendElement(interp, resObjPtr,
11821 Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11822 } else {
11823 /* This handles the special case of splitchars eq {}. This
11824 * is trivial but we want to perform object sharing as Tcl does. */
11825 Jim_Obj *objCache[256];
11826 const unsigned char *u = (unsigned char*) str;
11827 memset(objCache, 0, sizeof(objCache));
11828 for (i = 0; i < strLen; i++) {
11829 int c = u[i];
11830
11831 if (objCache[c] == NULL)
11832 objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11833 Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11834 }
11835 }
11836 Jim_SetResult(interp, resObjPtr);
11837 return JIM_OK;
11838 }
11839
11840 /* [join] */
11841 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11842 Jim_Obj *const *argv)
11843 {
11844 const char *joinStr;
11845 int joinStrLen, i, listLen;
11846 Jim_Obj *resObjPtr;
11847
11848 if (argc != 2 && argc != 3) {
11849 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11850 return JIM_ERR;
11851 }
11852 /* Init */
11853 if (argc == 2) {
11854 joinStr = " ";
11855 joinStrLen = 1;
11856 } else {
11857 joinStr = Jim_GetString(argv[2], &joinStrLen);
11858 }
11859 Jim_ListLength(interp, argv[1], &listLen);
11860 resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11861 /* Split */
11862 for (i = 0; i < listLen; i++) {
11863 Jim_Obj *objPtr;
11864
11865 Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11866 Jim_AppendObj(interp, resObjPtr, objPtr);
11867 if (i+1 != listLen) {
11868 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11869 }
11870 }
11871 Jim_SetResult(interp, resObjPtr);
11872 return JIM_OK;
11873 }
11874
11875 /* [format] */
11876 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11877 Jim_Obj *const *argv)
11878 {
11879 Jim_Obj *objPtr;
11880
11881 if (argc < 2) {
11882 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11883 return JIM_ERR;
11884 }
11885 objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11886 if (objPtr == NULL)
11887 return JIM_ERR;
11888 Jim_SetResult(interp, objPtr);
11889 return JIM_OK;
11890 }
11891
11892 /* [scan] */
11893 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11894 Jim_Obj *const *argv)
11895 {
11896 Jim_Obj *listPtr, **outVec;
11897 int outc, i, count = 0;
11898
11899 if (argc < 3) {
11900 Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11901 return JIM_ERR;
11902 }
11903 if (argv[2]->typePtr != &scanFmtStringObjType)
11904 SetScanFmtFromAny(interp, argv[2]);
11905 if (FormatGetError(argv[2]) != 0) {
11906 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11907 return JIM_ERR;
11908 }
11909 if (argc > 3) {
11910 int maxPos = FormatGetMaxPos(argv[2]);
11911 int count = FormatGetCnvCount(argv[2]);
11912 if (maxPos > argc-3) {
11913 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11914 return JIM_ERR;
11915 } else if (count != 0 && count < argc-3) {
11916 Jim_SetResultString(interp, "variable is not assigned by any "
11917 "conversion specifiers", -1);
11918 return JIM_ERR;
11919 } else if (count > argc-3) {
11920 Jim_SetResultString(interp, "different numbers of variable names and "
11921 "field specifiers", -1);
11922 return JIM_ERR;
11923 }
11924 }
11925 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11926 if (listPtr == 0)
11927 return JIM_ERR;
11928 if (argc > 3) {
11929 int len = 0;
11930 if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11931 Jim_ListLength(interp, listPtr, &len);
11932 if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11933 Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11934 return JIM_OK;
11935 }
11936 JimListGetElements(interp, listPtr, &outc, &outVec);
11937 for (i = 0; i < outc; ++i) {
11938 if (Jim_Length(outVec[i]) > 0) {
11939 ++count;
11940 if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11941 goto err;
11942 }
11943 }
11944 Jim_FreeNewObj(interp, listPtr);
11945 Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11946 } else {
11947 if (listPtr == (Jim_Obj*)EOF) {
11948 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11949 return JIM_OK;
11950 }
11951 Jim_SetResult(interp, listPtr);
11952 }
11953 return JIM_OK;
11954 err:
11955 Jim_FreeNewObj(interp, listPtr);
11956 return JIM_ERR;
11957 }
11958
11959 /* [error] */
11960 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11961 Jim_Obj *const *argv)
11962 {
11963 if (argc != 2) {
11964 Jim_WrongNumArgs(interp, 1, argv, "message");
11965 return JIM_ERR;
11966 }
11967 Jim_SetResult(interp, argv[1]);
11968 return JIM_ERR;
11969 }
11970
11971 /* [lrange] */
11972 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11973 Jim_Obj *const *argv)
11974 {
11975 Jim_Obj *objPtr;
11976
11977 if (argc != 4) {
11978 Jim_WrongNumArgs(interp, 1, argv, "list first last");
11979 return JIM_ERR;
11980 }
11981 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11982 return JIM_ERR;
11983 Jim_SetResult(interp, objPtr);
11984 return JIM_OK;
11985 }
11986
11987 /* [env] */
11988 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11989 Jim_Obj *const *argv)
11990 {
11991 const char *key;
11992 char *val;
11993
11994 if (argc == 1) {
11995
11996 #ifdef NEED_ENVIRON_EXTERN
11997 extern char **environ;
11998 #endif
11999
12000 int i;
12001 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
12002
12003 for (i = 0; environ[i]; i++) {
12004 const char *equals = strchr(environ[i], '=');
12005 if (equals) {
12006 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, environ[i], equals - environ[i]));
12007 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
12008 }
12009 }
12010
12011 Jim_SetResult(interp, listObjPtr);
12012 return JIM_OK;
12013 }
12014
12015 if (argc != 2) {
12016 Jim_WrongNumArgs(interp, 1, argv, "varName");
12017 return JIM_ERR;
12018 }
12019 key = Jim_GetString(argv[1], NULL);
12020 val = getenv(key);
12021 if (val == NULL) {
12022 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
12023 Jim_AppendStrings(interp, Jim_GetResult(interp),
12024 "environment variable \"",
12025 key, "\" does not exist", NULL);
12026 return JIM_ERR;
12027 }
12028 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
12029 return JIM_OK;
12030 }
12031
12032 /* [source] */
12033 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
12034 Jim_Obj *const *argv)
12035 {
12036 int retval;
12037
12038 if (argc != 2) {
12039 Jim_WrongNumArgs(interp, 1, argv, "fileName");
12040 return JIM_ERR;
12041 }
12042 retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
12043 if (retval == JIM_ERR) {
12044 return JIM_ERR_ADDSTACK;
12045 }
12046 if (retval == JIM_RETURN)
12047 return JIM_OK;
12048 return retval;
12049 }
12050
12051 /* [lreverse] */
12052 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
12053 Jim_Obj *const *argv)
12054 {
12055 Jim_Obj *revObjPtr, **ele;
12056 int len;
12057
12058 if (argc != 2) {
12059 Jim_WrongNumArgs(interp, 1, argv, "list");
12060 return JIM_ERR;
12061 }
12062 JimListGetElements(interp, argv[1], &len, &ele);
12063 len--;
12064 revObjPtr = Jim_NewListObj(interp, NULL, 0);
12065 while (len >= 0)
12066 ListAppendElement(revObjPtr, ele[len--]);
12067 Jim_SetResult(interp, revObjPtr);
12068 return JIM_OK;
12069 }
12070
12071 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
12072 {
12073 jim_wide len;
12074
12075 if (step == 0) return -1;
12076 if (start == end) return 0;
12077 else if (step > 0 && start > end) return -1;
12078 else if (step < 0 && end > start) return -1;
12079 len = end-start;
12080 if (len < 0) len = -len; /* abs(len) */
12081 if (step < 0) step = -step; /* abs(step) */
12082 len = 1 + ((len-1)/step);
12083 /* We can truncate safely to INT_MAX, the range command
12084 * will always return an error for a such long range
12085 * because Tcl lists can't be so long. */
12086 if (len > INT_MAX) len = INT_MAX;
12087 return (int)((len < 0) ? -1 : len);
12088 }
12089
12090 /* [range] */
12091 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
12092 Jim_Obj *const *argv)
12093 {
12094 jim_wide start = 0, end, step = 1;
12095 int len, i;
12096 Jim_Obj *objPtr;
12097
12098 if (argc < 2 || argc > 4) {
12099 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
12100 return JIM_ERR;
12101 }
12102 if (argc == 2) {
12103 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
12104 return JIM_ERR;
12105 } else {
12106 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
12107 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
12108 return JIM_ERR;
12109 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
12110 return JIM_ERR;
12111 }
12112 if ((len = JimRangeLen(start, end, step)) == -1) {
12113 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
12114 return JIM_ERR;
12115 }
12116 objPtr = Jim_NewListObj(interp, NULL, 0);
12117 for (i = 0; i < len; i++)
12118 ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
12119 Jim_SetResult(interp, objPtr);
12120 return JIM_OK;
12121 }
12122
12123 /* [rand] */
12124 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
12125 Jim_Obj *const *argv)
12126 {
12127 jim_wide min = 0, max, len, maxMul;
12128
12129 if (argc < 1 || argc > 3) {
12130 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
12131 return JIM_ERR;
12132 }
12133 if (argc == 1) {
12134 max = JIM_WIDE_MAX;
12135 } else if (argc == 2) {
12136 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
12137 return JIM_ERR;
12138 } else if (argc == 3) {
12139 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
12140 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
12141 return JIM_ERR;
12142 }
12143 len = max-min;
12144 if (len < 0) {
12145 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
12146 return JIM_ERR;
12147 }
12148 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
12149 while (1) {
12150 jim_wide r;
12151
12152 JimRandomBytes(interp, &r, sizeof(jim_wide));
12153 if (r < 0 || r >= maxMul) continue;
12154 r = (len == 0) ? 0 : r%len;
12155 Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
12156 return JIM_OK;
12157 }
12158 }
12159
12160 /* [package] */
12161 static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
12162 Jim_Obj *const *argv)
12163 {
12164 int option;
12165 const char *options[] = {
12166 "require", "provide", NULL
12167 };
12168 enum {OPT_REQUIRE, OPT_PROVIDE};
12169
12170 if (argc < 2) {
12171 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
12172 return JIM_ERR;
12173 }
12174 if (Jim_GetEnum(interp, argv[1], options, &option, "option",
12175 JIM_ERRMSG) != JIM_OK)
12176 return JIM_ERR;
12177
12178 if (option == OPT_REQUIRE) {
12179 int exact = 0;
12180 const char *ver;
12181
12182 if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
12183 exact = 1;
12184 argv++;
12185 argc--;
12186 }
12187 if (argc != 3 && argc != 4) {
12188 Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
12189 return JIM_ERR;
12190 }
12191 ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
12192 argc == 4 ? Jim_GetString(argv[3], NULL) : "",
12193 JIM_ERRMSG);
12194 if (ver == NULL)
12195 return JIM_ERR_ADDSTACK;
12196 Jim_SetResultString(interp, ver, -1);
12197 } else if (option == OPT_PROVIDE) {
12198 if (argc != 4) {
12199 Jim_WrongNumArgs(interp, 2, argv, "package version");
12200 return JIM_ERR;
12201 }
12202 return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
12203 Jim_GetString(argv[3], NULL), JIM_ERRMSG);
12204 }
12205 return JIM_OK;
12206 }
12207
12208 static struct {
12209 const char *name;
12210 Jim_CmdProc cmdProc;
12211 } Jim_CoreCommandsTable[] = {
12212 {"set", Jim_SetCoreCommand},
12213 {"unset", Jim_UnsetCoreCommand},
12214 {"puts", Jim_PutsCoreCommand},
12215 {"+", Jim_AddCoreCommand},
12216 {"*", Jim_MulCoreCommand},
12217 {"-", Jim_SubCoreCommand},
12218 {"/", Jim_DivCoreCommand},
12219 {"incr", Jim_IncrCoreCommand},
12220 {"while", Jim_WhileCoreCommand},
12221 {"for", Jim_ForCoreCommand},
12222 {"foreach", Jim_ForeachCoreCommand},
12223 {"lmap", Jim_LmapCoreCommand},
12224 {"if", Jim_IfCoreCommand},
12225 {"switch", Jim_SwitchCoreCommand},
12226 {"list", Jim_ListCoreCommand},
12227 {"lindex", Jim_LindexCoreCommand},
12228 {"lset", Jim_LsetCoreCommand},
12229 {"llength", Jim_LlengthCoreCommand},
12230 {"lappend", Jim_LappendCoreCommand},
12231 {"linsert", Jim_LinsertCoreCommand},
12232 {"lsort", Jim_LsortCoreCommand},
12233 {"append", Jim_AppendCoreCommand},
12234 {"debug", Jim_DebugCoreCommand},
12235 {"eval", Jim_EvalCoreCommand},
12236 {"uplevel", Jim_UplevelCoreCommand},
12237 {"expr", Jim_ExprCoreCommand},
12238 {"break", Jim_BreakCoreCommand},
12239 {"continue", Jim_ContinueCoreCommand},
12240 {"proc", Jim_ProcCoreCommand},
12241 {"concat", Jim_ConcatCoreCommand},
12242 {"return", Jim_ReturnCoreCommand},
12243 {"upvar", Jim_UpvarCoreCommand},
12244 {"global", Jim_GlobalCoreCommand},
12245 {"string", Jim_StringCoreCommand},
12246 {"time", Jim_TimeCoreCommand},
12247 {"exit", Jim_ExitCoreCommand},
12248 {"catch", Jim_CatchCoreCommand},
12249 {"ref", Jim_RefCoreCommand},
12250 {"getref", Jim_GetrefCoreCommand},
12251 {"setref", Jim_SetrefCoreCommand},
12252 {"finalize", Jim_FinalizeCoreCommand},
12253 {"collect", Jim_CollectCoreCommand},
12254 {"rename", Jim_RenameCoreCommand},
12255 {"dict", Jim_DictCoreCommand},
12256 {"load", Jim_LoadCoreCommand},
12257 {"subst", Jim_SubstCoreCommand},
12258 {"info", Jim_InfoCoreCommand},
12259 {"split", Jim_SplitCoreCommand},
12260 {"join", Jim_JoinCoreCommand},
12261 {"format", Jim_FormatCoreCommand},
12262 {"scan", Jim_ScanCoreCommand},
12263 {"error", Jim_ErrorCoreCommand},
12264 {"lrange", Jim_LrangeCoreCommand},
12265 {"env", Jim_EnvCoreCommand},
12266 {"source", Jim_SourceCoreCommand},
12267 {"lreverse", Jim_LreverseCoreCommand},
12268 {"range", Jim_RangeCoreCommand},
12269 {"rand", Jim_RandCoreCommand},
12270 {"package", Jim_PackageCoreCommand},
12271 {"tailcall", Jim_TailcallCoreCommand},
12272 {NULL, NULL},
12273 };
12274
12275 /* Some Jim core command is actually a procedure written in Jim itself. */
12276 static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
12277 {
12278 Jim_Eval(interp, (char*)
12279 "proc lambda {arglist args} {\n"
12280 " set name [ref {} function lambdaFinalizer]\n"
12281 " uplevel 1 [list proc $name $arglist {expand}$args]\n"
12282 " return $name\n"
12283 "}\n"
12284 "proc lambdaFinalizer {name val} {\n"
12285 " rename $name {}\n"
12286 "}\n"
12287 );
12288 }
12289
12290 void Jim_RegisterCoreCommands(Jim_Interp *interp)
12291 {
12292 int i = 0;
12293
12294 while(Jim_CoreCommandsTable[i].name != NULL) {
12295 Jim_CreateCommand(interp,
12296 Jim_CoreCommandsTable[i].name,
12297 Jim_CoreCommandsTable[i].cmdProc,
12298 NULL, NULL);
12299 i++;
12300 }
12301 Jim_RegisterCoreProcedures(interp);
12302 }
12303
12304 /* -----------------------------------------------------------------------------
12305 * Interactive prompt
12306 * ---------------------------------------------------------------------------*/
12307 void Jim_PrintErrorMessage(Jim_Interp *interp)
12308 {
12309 int len, i;
12310
12311 if (*interp->errorFileName) {
12312 Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL " ",
12313 interp->errorFileName, interp->errorLine);
12314 }
12315 Jim_fprintf(interp,interp->cookie_stderr, "%s" JIM_NL,
12316 Jim_GetString(interp->result, NULL));
12317 Jim_ListLength(interp, interp->stackTrace, &len);
12318 for (i = len-3; i >= 0; i-= 3) {
12319 Jim_Obj *objPtr;
12320 const char *proc, *file, *line;
12321
12322 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
12323 proc = Jim_GetString(objPtr, NULL);
12324 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
12325 JIM_NONE);
12326 file = Jim_GetString(objPtr, NULL);
12327 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
12328 JIM_NONE);
12329 line = Jim_GetString(objPtr, NULL);
12330 if (*proc) {
12331 Jim_fprintf( interp, interp->cookie_stderr,
12332 "in procedure '%s' ", proc);
12333 }
12334 if (*file) {
12335 Jim_fprintf( interp, interp->cookie_stderr,
12336 "called at file \"%s\", line %s",
12337 file, line);
12338 }
12339 if (*file || *proc) {
12340 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL);
12341 }
12342 }
12343 }
12344
12345 int Jim_InteractivePrompt(Jim_Interp *interp)
12346 {
12347 int retcode = JIM_OK;
12348 Jim_Obj *scriptObjPtr;
12349
12350 Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
12351 "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
12352 JIM_VERSION / 100, JIM_VERSION % 100);
12353 Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
12354 while (1) {
12355 char buf[1024];
12356 const char *result;
12357 const char *retcodestr[] = {
12358 "ok", "error", "return", "break", "continue", "eval", "exit"
12359 };
12360 int reslen;
12361
12362 if (retcode != 0) {
12363 if (retcode >= 2 && retcode <= 6)
12364 Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
12365 else
12366 Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
12367 } else
12368 Jim_fprintf( interp, interp->cookie_stdout, ". ");
12369 Jim_fflush( interp, interp->cookie_stdout);
12370 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
12371 Jim_IncrRefCount(scriptObjPtr);
12372 while(1) {
12373 const char *str;
12374 char state;
12375 int len;
12376
12377 if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
12378 Jim_DecrRefCount(interp, scriptObjPtr);
12379 goto out;
12380 }
12381 Jim_AppendString(interp, scriptObjPtr, buf, -1);
12382 str = Jim_GetString(scriptObjPtr, &len);
12383 if (Jim_ScriptIsComplete(str, len, &state))
12384 break;
12385 Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state);
12386 Jim_fflush( interp, interp->cookie_stdout);
12387 }
12388 retcode = Jim_EvalObj(interp, scriptObjPtr);
12389 Jim_DecrRefCount(interp, scriptObjPtr);
12390 result = Jim_GetString(Jim_GetResult(interp), &reslen);
12391 if (retcode == JIM_ERR) {
12392 Jim_PrintErrorMessage(interp);
12393 } else if (retcode == JIM_EXIT) {
12394 exit(Jim_GetExitCode(interp));
12395 } else {
12396 if (reslen) {
12397 Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout);
12398 Jim_fprintf( interp,interp->cookie_stdout, JIM_NL);
12399 }
12400 }
12401 }
12402 out:
12403 return 0;
12404 }
12405
12406 /* -----------------------------------------------------------------------------
12407 * Jim's idea of STDIO..
12408 * ---------------------------------------------------------------------------*/
12409
12410 int Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... )
12411 {
12412 int r;
12413
12414 va_list ap;
12415 va_start(ap,fmt);
12416 r = Jim_vfprintf( interp, cookie, fmt,ap );
12417 va_end(ap);
12418 return r;
12419 }
12420
12421 int Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap )
12422 {
12423 if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){
12424 errno = ENOTSUP;
12425 return -1;
12426 }
12427 return (*(interp->cb_vfprintf))( cookie, fmt, ap );
12428 }
12429
12430 size_t Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie )
12431 {
12432 if( (interp == NULL) || (interp->cb_fwrite == NULL) ){
12433 errno = ENOTSUP;
12434 return 0;
12435 }
12436 return (*(interp->cb_fwrite))( ptr, size, n, cookie);
12437 }
12438
12439 size_t Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie )
12440 {
12441 if( (interp == NULL) || (interp->cb_fread == NULL) ){
12442 errno = ENOTSUP;
12443 return 0;
12444 }
12445 return (*(interp->cb_fread))( ptr, size, n, cookie);
12446 }
12447
12448 int Jim_fflush( Jim_Interp *interp, void *cookie )
12449 {
12450 if( (interp == NULL) || (interp->cb_fflush == NULL) ){
12451 /* pretend all is well */
12452 return 0;
12453 }
12454 return (*(interp->cb_fflush))( cookie );
12455 }
12456
12457 char* Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie )
12458 {
12459 if( (interp == NULL) || (interp->cb_fgets == NULL) ){
12460 errno = ENOTSUP;
12461 return NULL;
12462 }
12463 return (*(interp->cb_fgets))( s, size, cookie );
12464 }
12465 Jim_Nvp *
12466 Jim_Nvp_name2value_simple( const Jim_Nvp *p, const char *name )
12467 {
12468 while( p->name ){
12469 if( 0 == strcmp( name, p->name ) ){
12470 break;
12471 }
12472 p++;
12473 }
12474 return ((Jim_Nvp *)(p));
12475 }
12476
12477 Jim_Nvp *
12478 Jim_Nvp_name2value_nocase_simple( const Jim_Nvp *p, const char *name )
12479 {
12480 while( p->name ){
12481 if( 0 == strcasecmp( name, p->name ) ){
12482 break;
12483 }
12484 p++;
12485 }
12486 return ((Jim_Nvp *)(p));
12487 }
12488
12489 int
12490 Jim_Nvp_name2value_obj( Jim_Interp *interp,
12491 const Jim_Nvp *p,
12492 Jim_Obj *o,
12493 Jim_Nvp **result )
12494 {
12495 return Jim_Nvp_name2value( interp, p, Jim_GetString( o, NULL ), result );
12496 }
12497
12498
12499 int
12500 Jim_Nvp_name2value( Jim_Interp *interp,
12501 const Jim_Nvp *_p,
12502 const char *name,
12503 Jim_Nvp **result)
12504 {
12505 const Jim_Nvp *p;
12506
12507 p = Jim_Nvp_name2value_simple( _p, name );
12508
12509 /* result */
12510 if( result ){
12511 *result = (Jim_Nvp *)(p);
12512 }
12513
12514 /* found? */
12515 if( p->name ){
12516 return JIM_OK;
12517 } else {
12518 return JIM_ERR;
12519 }
12520 }
12521
12522 int
12523 Jim_Nvp_name2value_obj_nocase( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **puthere )
12524 {
12525 return Jim_Nvp_name2value_nocase( interp, p, Jim_GetString( o, NULL ), puthere );
12526 }
12527
12528 int
12529 Jim_Nvp_name2value_nocase( Jim_Interp *interp, const Jim_Nvp *_p, const char *name, Jim_Nvp **puthere )
12530 {
12531 const Jim_Nvp *p;
12532
12533 p = Jim_Nvp_name2value_nocase_simple( _p, name );
12534
12535 if( puthere ){
12536 *puthere = (Jim_Nvp *)(p);
12537 }
12538 /* found */
12539 if( p->name ){
12540 return JIM_OK;
12541 } else {
12542 return JIM_ERR;
12543 }
12544 }
12545
12546
12547 int
12548 Jim_Nvp_value2name_obj( Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **result )
12549 {
12550 int e;;
12551 jim_wide w;
12552
12553 e = Jim_GetWide( interp, o, &w );
12554 if( e != JIM_OK ){
12555 return e;
12556 }
12557
12558 return Jim_Nvp_value2name( interp, p, w, result );
12559 }
12560
12561 Jim_Nvp *
12562 Jim_Nvp_value2name_simple( const Jim_Nvp *p, int value )
12563 {
12564 while( p->name ){
12565 if( value == p->value ){
12566 break;
12567 }
12568 p++;
12569 }
12570 return ((Jim_Nvp *)(p));
12571 }
12572
12573
12574 int
12575 Jim_Nvp_value2name( Jim_Interp *interp, const Jim_Nvp *_p, int value, Jim_Nvp **result )
12576 {
12577 const Jim_Nvp *p;
12578
12579 p = Jim_Nvp_value2name_simple( _p, value );
12580
12581 if( result ){
12582 *result = (Jim_Nvp *)(p);
12583 }
12584
12585 if( p->name ){
12586 return JIM_OK;
12587 } else {
12588 return JIM_ERR;
12589 }
12590 }
12591
12592
12593 int
12594 Jim_GetOpt_Setup( Jim_GetOptInfo *p, Jim_Interp *interp, int argc, Jim_Obj * const * argv)
12595 {
12596 memset( p, 0, sizeof(*p) );
12597 p->interp = interp;
12598 p->argc = argc;
12599 p->argv = argv;
12600
12601 return JIM_OK;
12602 }
12603
12604 void
12605 Jim_GetOpt_Debug( Jim_GetOptInfo *p )
12606 {
12607 int x;
12608
12609 Jim_fprintf( p->interp, p->interp->cookie_stderr, "---args---\n");
12610 for( x = 0 ; x < p->argc ; x++ ){
12611 Jim_fprintf( p->interp, p->interp->cookie_stderr,
12612 "%2d) %s\n",
12613 x,
12614 Jim_GetString( p->argv[x], NULL ) );
12615 }
12616 Jim_fprintf( p->interp, p->interp->cookie_stderr, "-------\n");
12617 }
12618
12619
12620 int
12621 Jim_GetOpt_Obj( Jim_GetOptInfo *goi, Jim_Obj **puthere )
12622 {
12623 Jim_Obj *o;
12624
12625 o = NULL; // failure
12626 if( goi->argc ){
12627 // success
12628 o = goi->argv[0];
12629 goi->argc -= 1;
12630 goi->argv += 1;
12631 }
12632 if( puthere ){
12633 *puthere = o;
12634 }
12635 if( o != NULL ){
12636 return JIM_OK;
12637 } else {
12638 return JIM_ERR;
12639 }
12640 }
12641
12642 int
12643 Jim_GetOpt_String( Jim_GetOptInfo *goi, char **puthere, int *len )
12644 {
12645 int r;
12646 Jim_Obj *o;
12647 const char *cp;
12648
12649
12650 r = Jim_GetOpt_Obj( goi, &o );
12651 if( r == JIM_OK ){
12652 cp = Jim_GetString( o, len );
12653 if( puthere ){
12654 /* remove const */
12655 *puthere = (char *)(cp);
12656 }
12657 }
12658 return r;
12659 }
12660
12661 int
12662 Jim_GetOpt_Double( Jim_GetOptInfo *goi, double *puthere )
12663 {
12664 int r;
12665 Jim_Obj *o;
12666 double _safe;
12667
12668 if( puthere == NULL ){
12669 puthere = &_safe;
12670 }
12671
12672 r = Jim_GetOpt_Obj( goi, &o );
12673 if( r == JIM_OK ){
12674 r = Jim_GetDouble( goi->interp, o, puthere );
12675 if( r != JIM_OK ){
12676 Jim_SetResult_sprintf( goi->interp,
12677 "not a number: %s",
12678 Jim_GetString( o, NULL ) );
12679 }
12680 }
12681 return r;
12682 }
12683
12684 int
12685 Jim_GetOpt_Wide( Jim_GetOptInfo *goi, jim_wide *puthere )
12686 {
12687 int r;
12688 Jim_Obj *o;
12689 jim_wide _safe;
12690
12691 if( puthere == NULL ){
12692 puthere = &_safe;
12693 }
12694
12695 r = Jim_GetOpt_Obj( goi, &o );
12696 if( r == JIM_OK ){
12697 r = Jim_GetWide( goi->interp, o, puthere );
12698 }
12699 return r;
12700 }
12701
12702 int Jim_GetOpt_Nvp( Jim_GetOptInfo *goi,
12703 const Jim_Nvp *nvp,
12704 Jim_Nvp **puthere)
12705 {
12706 Jim_Nvp *_safe;
12707 Jim_Obj *o;
12708 int e;
12709
12710 if( puthere == NULL ){
12711 puthere = &_safe;
12712 }
12713
12714 e = Jim_GetOpt_Obj( goi, &o );
12715 if( e == JIM_OK ){
12716 e = Jim_Nvp_name2value_obj( goi->interp,
12717 nvp,
12718 o,
12719 puthere );
12720 }
12721
12722 return e;
12723 }
12724
12725 void
12726 Jim_GetOpt_NvpUnknown( Jim_GetOptInfo *goi,
12727 const Jim_Nvp *nvptable,
12728 int hadprefix )
12729 {
12730 if( hadprefix ){
12731 Jim_SetResult_NvpUnknown( goi->interp,
12732 goi->argv[-2],
12733 goi->argv[-1],
12734 nvptable );
12735 } else {
12736 Jim_SetResult_NvpUnknown( goi->interp,
12737 NULL,
12738 goi->argv[-1],
12739 nvptable );
12740 }
12741 }
12742
12743
12744 int
12745 Jim_GetOpt_Enum( Jim_GetOptInfo *goi,
12746 const char * const * lookup,
12747 int *puthere)
12748 {
12749 int _safe;
12750 Jim_Obj *o;
12751 int e;
12752
12753 if( puthere == NULL ){
12754 puthere = &_safe;
12755 }
12756 e = Jim_GetOpt_Obj( goi, &o );
12757 if( e == JIM_OK ){
12758 e = Jim_GetEnum( goi->interp,
12759 o,
12760 lookup,
12761 puthere,
12762 "option",
12763 JIM_ERRMSG );
12764 }
12765 return e;
12766 }
12767
12768
12769
12770 int
12771 Jim_SetResult_sprintf( Jim_Interp *interp, const char *fmt,... )
12772 {
12773 va_list ap;
12774 char *buf;
12775
12776 va_start(ap,fmt);
12777 buf = jim_vasprintf( fmt, ap );
12778 va_end(ap);
12779 if( buf ){
12780 Jim_SetResultString( interp, buf, -1 );
12781 jim_vasprintf_done(buf);
12782 }
12783 return JIM_OK;
12784 }
12785
12786
12787 void
12788 Jim_SetResult_NvpUnknown( Jim_Interp *interp,
12789 Jim_Obj *param_name,
12790 Jim_Obj *param_value,
12791 const Jim_Nvp *nvp )
12792 {
12793 if( param_name ){
12794 Jim_SetResult_sprintf( interp,
12795 "%s: Unknown: %s, try one of: ",
12796 Jim_GetString( param_name, NULL ),
12797 Jim_GetString( param_value, NULL ) );
12798 } else {
12799 Jim_SetResult_sprintf( interp,
12800 "Unknown param: %s, try one of: ",
12801 Jim_GetString( param_value, NULL ) );
12802 }
12803 while( nvp->name ){
12804 const char *a;
12805 const char *b;
12806
12807 if( (nvp+1)->name ){
12808 a = nvp->name;
12809 b = ", ";
12810 } else {
12811 a = "or ";
12812 b = nvp->name;
12813 }
12814 Jim_AppendStrings( interp,
12815 Jim_GetResult(interp),
12816 a, b, NULL );
12817 nvp++;
12818 }
12819 }
12820
12821
12822 static Jim_Obj *debug_string_obj;
12823
12824 const char *
12825 Jim_Debug_ArgvString( Jim_Interp *interp, int argc, Jim_Obj *const *argv )
12826 {
12827 int x;
12828
12829 if( debug_string_obj ){
12830 Jim_FreeObj( interp, debug_string_obj );
12831 }
12832
12833 debug_string_obj = Jim_NewEmptyStringObj( interp );
12834 for( x = 0 ; x < argc ; x++ ){
12835 Jim_AppendStrings( interp,
12836 debug_string_obj,
12837 Jim_GetString( argv[x], NULL ),
12838 " ",
12839 NULL );
12840 }
12841
12842 return Jim_GetString( debug_string_obj, NULL );
12843 }
12844
12845
12846
12847 /*
12848 * Local Variables: ***
12849 * c-basic-offset: 4 ***
12850 * tab-width: 4 ***
12851 * End: ***
12852 */

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)