75e7e6cb656f0468c7fe967252fcdb781fbe76f6
[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 *
11 * The FreeBSD license
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials
22 * provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * The views and conclusions contained in the software and documentation
38 * are those of the authors and should not be interpreted as representing
39 * official policies, either expressed or implied, of the Jim Tcl Project.
40 **/
41 #define __JIM_CORE__
42 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
43
44 #ifdef __ECOS
45 #include <pkgconf/jimtcl.h>
46 #endif
47 #ifndef JIM_ANSIC
48 #define JIM_DYNLIB /* Dynamic library support for UNIX and WIN32 */
49 #endif /* JIM_ANSIC */
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <stdarg.h>
55 #include <ctype.h>
56 #include <limits.h>
57 #include <assert.h>
58 #include <errno.h>
59 #include <time.h>
60 #if defined(WIN32)
61 /* sys/time - need is different */
62 #else
63 #include <sys/time.h> // for gettimeofday()
64 #endif
65
66 #include "replacements.h"
67
68 /* Include the platform dependent libraries for
69 * dynamic loading of libraries. */
70 #ifdef JIM_DYNLIB
71 #if defined(_WIN32) || defined(WIN32)
72 #ifndef WIN32
73 #define WIN32 1
74 #endif
75 #ifndef STRICT
76 #define STRICT
77 #endif
78 #define WIN32_LEAN_AND_MEAN
79 #include <windows.h>
80 #if _MSC_VER >= 1000
81 #pragma warning(disable:4146)
82 #endif /* _MSC_VER */
83 #else
84 #include <dlfcn.h>
85 #endif /* WIN32 */
86 #endif /* JIM_DYNLIB */
87
88 #ifdef __ECOS
89 #include <cyg/jimtcl/jim.h>
90 #else
91 #include "jim.h"
92 #endif
93
94 #ifdef HAVE_BACKTRACE
95 #include <execinfo.h>
96 #endif
97
98 /* -----------------------------------------------------------------------------
99 * Global variables
100 * ---------------------------------------------------------------------------*/
101
102 /* A shared empty string for the objects string representation.
103 * Jim_InvalidateStringRep knows about it and don't try to free. */
104 static char *JimEmptyStringRep = (char*) "";
105
106 /* -----------------------------------------------------------------------------
107 * Required prototypes of not exported functions
108 * ---------------------------------------------------------------------------*/
109 static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
110 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
111 static void JimRegisterCoreApi(Jim_Interp *interp);
112
113 static Jim_HashTableType JimVariablesHashTableType;
114
115 /* -----------------------------------------------------------------------------
116 * Utility functions
117 * ---------------------------------------------------------------------------*/
118
119 static char *
120 jim_vasprintf( const char *fmt, va_list ap )
121 {
122 #ifndef HAVE_VASPRINTF
123 /* yucky way */
124 static char buf[2048];
125 vsnprintf( buf, sizeof(buf), fmt, ap );
126 /* garentee termination */
127 buf[sizeof(buf)-1] = 0;
128 #else
129 char *buf;
130 vasprintf( &buf, fmt, ap );
131 #endif
132 return buf;
133 }
134
135 static void
136 jim_vasprintf_done( void *buf )
137 {
138 #ifndef HAVE_VASPRINTF
139 (void)(buf);
140 #else
141 free(buf);
142 #endif
143 }
144
145
146 /*
147 * Convert a string to a jim_wide INTEGER.
148 * This function originates from BSD.
149 *
150 * Ignores `locale' stuff. Assumes that the upper and lower case
151 * alphabets and digits are each contiguous.
152 */
153 #ifdef HAVE_LONG_LONG
154 #define JimIsAscii(c) (((c) & ~0x7f) == 0)
155 static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
156 {
157 register const char *s;
158 register unsigned jim_wide acc;
159 register unsigned char c;
160 register unsigned jim_wide qbase, cutoff;
161 register int neg, any, cutlim;
162
163 /*
164 * Skip white space and pick up leading +/- sign if any.
165 * If base is 0, allow 0x for hex and 0 for octal, else
166 * assume decimal; if base is already 16, allow 0x.
167 */
168 s = nptr;
169 do {
170 c = *s++;
171 } while (isspace(c));
172 if (c == '-') {
173 neg = 1;
174 c = *s++;
175 } else {
176 neg = 0;
177 if (c == '+')
178 c = *s++;
179 }
180 if ((base == 0 || base == 16) &&
181 c == '0' && (*s == 'x' || *s == 'X')) {
182 c = s[1];
183 s += 2;
184 base = 16;
185 }
186 if (base == 0)
187 base = c == '0' ? 8 : 10;
188
189 /*
190 * Compute the cutoff value between legal numbers and illegal
191 * numbers. That is the largest legal value, divided by the
192 * base. An input number that is greater than this value, if
193 * followed by a legal input character, is too big. One that
194 * is equal to this value may be valid or not; the limit
195 * between valid and invalid numbers is then based on the last
196 * digit. For instance, if the range for quads is
197 * [-9223372036854775808..9223372036854775807] and the input base
198 * is 10, cutoff will be set to 922337203685477580 and cutlim to
199 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
200 * accumulated a value > 922337203685477580, or equal but the
201 * next digit is > 7 (or 8), the number is too big, and we will
202 * return a range error.
203 *
204 * Set any if any `digits' consumed; make it negative to indicate
205 * overflow.
206 */
207 qbase = (unsigned)base;
208 cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
209 : LLONG_MAX;
210 cutlim = (int)(cutoff % qbase);
211 cutoff /= qbase;
212 for (acc = 0, any = 0;; c = *s++) {
213 if (!JimIsAscii(c))
214 break;
215 if (isdigit(c))
216 c -= '0';
217 else if (isalpha(c))
218 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
219 else
220 break;
221 if (c >= base)
222 break;
223 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
224 any = -1;
225 else {
226 any = 1;
227 acc *= qbase;
228 acc += c;
229 }
230 }
231 if (any < 0) {
232 acc = neg ? LLONG_MIN : LLONG_MAX;
233 errno = ERANGE;
234 } else if (neg)
235 acc = -acc;
236 if (endptr != 0)
237 *endptr = (char *)(any ? s - 1 : nptr);
238 return (acc);
239 }
240 #endif
241
242 /* Glob-style pattern matching. */
243 static int JimStringMatch(const char *pattern, int patternLen,
244 const char *string, int stringLen, int nocase)
245 {
246 while(patternLen) {
247 switch(pattern[0]) {
248 case '*':
249 while (pattern[1] == '*') {
250 pattern++;
251 patternLen--;
252 }
253 if (patternLen == 1)
254 return 1; /* match */
255 while(stringLen) {
256 if (JimStringMatch(pattern+1, patternLen-1,
257 string, stringLen, nocase))
258 return 1; /* match */
259 string++;
260 stringLen--;
261 }
262 return 0; /* no match */
263 break;
264 case '?':
265 if (stringLen == 0)
266 return 0; /* no match */
267 string++;
268 stringLen--;
269 break;
270 case '[':
271 {
272 int not, match;
273
274 pattern++;
275 patternLen--;
276 not = pattern[0] == '^';
277 if (not) {
278 pattern++;
279 patternLen--;
280 }
281 match = 0;
282 while(1) {
283 if (pattern[0] == '\\') {
284 pattern++;
285 patternLen--;
286 if (pattern[0] == string[0])
287 match = 1;
288 } else if (pattern[0] == ']') {
289 break;
290 } else if (patternLen == 0) {
291 pattern--;
292 patternLen++;
293 break;
294 } else if (pattern[1] == '-' && patternLen >= 3) {
295 int start = pattern[0];
296 int end = pattern[2];
297 int c = string[0];
298 if (start > end) {
299 int t = start;
300 start = end;
301 end = t;
302 }
303 if (nocase) {
304 start = tolower(start);
305 end = tolower(end);
306 c = tolower(c);
307 }
308 pattern += 2;
309 patternLen -= 2;
310 if (c >= start && c <= end)
311 match = 1;
312 } else {
313 if (!nocase) {
314 if (pattern[0] == string[0])
315 match = 1;
316 } else {
317 if (tolower((int)pattern[0]) == tolower((int)string[0]))
318 match = 1;
319 }
320 }
321 pattern++;
322 patternLen--;
323 }
324 if (not)
325 match = !match;
326 if (!match)
327 return 0; /* no match */
328 string++;
329 stringLen--;
330 break;
331 }
332 case '\\':
333 if (patternLen >= 2) {
334 pattern++;
335 patternLen--;
336 }
337 /* fall through */
338 default:
339 if (!nocase) {
340 if (pattern[0] != string[0])
341 return 0; /* no match */
342 } else {
343 if (tolower((int)pattern[0]) != tolower((int)string[0]))
344 return 0; /* no match */
345 }
346 string++;
347 stringLen--;
348 break;
349 }
350 pattern++;
351 patternLen--;
352 if (stringLen == 0) {
353 while(*pattern == '*') {
354 pattern++;
355 patternLen--;
356 }
357 break;
358 }
359 }
360 if (patternLen == 0 && stringLen == 0)
361 return 1;
362 return 0;
363 }
364
365 int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
366 int nocase)
367 {
368 unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
369
370 if (nocase == 0) {
371 while(l1 && l2) {
372 if (*u1 != *u2)
373 return (int)*u1-*u2;
374 u1++; u2++; l1--; l2--;
375 }
376 if (!l1 && !l2) return 0;
377 return l1-l2;
378 } else {
379 while(l1 && l2) {
380 if (tolower((int)*u1) != tolower((int)*u2))
381 return tolower((int)*u1)-tolower((int)*u2);
382 u1++; u2++; l1--; l2--;
383 }
384 if (!l1 && !l2) return 0;
385 return l1-l2;
386 }
387 }
388
389 /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
390 * The index of the first occurrence of s1 in s2 is returned.
391 * If s1 is not found inside s2, -1 is returned. */
392 int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
393 {
394 int i;
395
396 if (!l1 || !l2 || l1 > l2) return -1;
397 if (index < 0) index = 0;
398 s2 += index;
399 for (i = index; i <= l2-l1; i++) {
400 if (memcmp(s2, s1, l1) == 0)
401 return i;
402 s2++;
403 }
404 return -1;
405 }
406
407 int Jim_WideToString(char *buf, jim_wide wideValue)
408 {
409 const char *fmt = "%" JIM_WIDE_MODIFIER;
410 return sprintf(buf, fmt, wideValue);
411 }
412
413 int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
414 {
415 char *endptr;
416
417 #ifdef HAVE_LONG_LONG
418 *widePtr = JimStrtoll(str, &endptr, base);
419 #else
420 *widePtr = strtol(str, &endptr, base);
421 #endif
422 if ((str[0] == '\0') || (str == endptr) )
423 return JIM_ERR;
424 if (endptr[0] != '\0') {
425 while(*endptr) {
426 if (!isspace((int)*endptr))
427 return JIM_ERR;
428 endptr++;
429 }
430 }
431 return JIM_OK;
432 }
433
434 int Jim_StringToIndex(const char *str, int *intPtr)
435 {
436 char *endptr;
437
438 *intPtr = strtol(str, &endptr, 10);
439 if ( (str[0] == '\0') || (str == endptr) )
440 return JIM_ERR;
441 if (endptr[0] != '\0') {
442 while(*endptr) {
443 if (!isspace((int)*endptr))
444 return JIM_ERR;
445 endptr++;
446 }
447 }
448 return JIM_OK;
449 }
450
451 /* The string representation of references has two features in order
452 * to make the GC faster. The first is that every reference starts
453 * with a non common character '~', in order to make the string matching
454 * fater. The second is that the reference string rep his 32 characters
455 * in length, this allows to avoid to check every object with a string
456 * repr < 32, and usually there are many of this objects. */
457
458 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
459
460 static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
461 {
462 const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
463 sprintf(buf, fmt, refPtr->tag, id);
464 return JIM_REFERENCE_SPACE;
465 }
466
467 int Jim_DoubleToString(char *buf, double doubleValue)
468 {
469 char *s;
470 int len;
471
472 len = sprintf(buf, "%.17g", doubleValue);
473 s = buf;
474 while(*s) {
475 if (*s == '.') return len;
476 s++;
477 }
478 /* Add a final ".0" if it's a number. But not
479 * for NaN or InF */
480 if (isdigit((int)buf[0])
481 || ((buf[0] == '-' || buf[0] == '+')
482 && isdigit((int)buf[1]))) {
483 s[0] = '.';
484 s[1] = '0';
485 s[2] = '\0';
486 return len+2;
487 }
488 return len;
489 }
490
491 int Jim_StringToDouble(const char *str, double *doublePtr)
492 {
493 char *endptr;
494
495 *doublePtr = strtod(str, &endptr);
496 if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) )
497 return JIM_ERR;
498 return JIM_OK;
499 }
500
501 static jim_wide JimPowWide(jim_wide b, jim_wide e)
502 {
503 jim_wide i, res = 1;
504 if ((b==0 && e!=0) || (e<0)) return 0;
505 for(i=0; i<e; i++) {res *= b;}
506 return res;
507 }
508
509 /* -----------------------------------------------------------------------------
510 * Special functions
511 * ---------------------------------------------------------------------------*/
512
513 /* Note that 'interp' may be NULL if not available in the
514 * context of the panic. It's only useful to get the error
515 * file descriptor, it will default to stderr otherwise. */
516 void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
517 {
518 va_list ap;
519
520 va_start(ap, fmt);
521 /*
522 * Send it here first.. Assuming STDIO still works
523 */
524 fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
525 vfprintf(stderr, fmt, ap);
526 fprintf(stderr, JIM_NL JIM_NL);
527 va_end(ap);
528
529 #ifdef HAVE_BACKTRACE
530 {
531 void *array[40];
532 int size, i;
533 char **strings;
534
535 size = backtrace(array, 40);
536 strings = backtrace_symbols(array, size);
537 for (i = 0; i < size; i++)
538 fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
539 fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
540 fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
541 }
542 #endif
543
544 /* This may actually crash... we do it last */
545 if( interp && interp->cookie_stderr ){
546 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
547 Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap );
548 Jim_fprintf( interp, interp->cookie_stderr, JIM_NL JIM_NL );
549 }
550 abort();
551 }
552
553 /* -----------------------------------------------------------------------------
554 * Memory allocation
555 * ---------------------------------------------------------------------------*/
556
557 /* Macro used for memory debugging.
558 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
559 * and similary for Jim_Realloc and Jim_Free */
560 #if 0
561 #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
562 #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
563 #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
564 #endif
565
566 void *Jim_Alloc(int size)
567 {
568 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
569 if (size==0)
570 size=1;
571 void *p = malloc(size);
572 if (p == NULL)
573 Jim_Panic(NULL,"malloc: Out of memory");
574 return p;
575 }
576
577 void Jim_Free(void *ptr) {
578 free(ptr);
579 }
580
581 void *Jim_Realloc(void *ptr, int size)
582 {
583 /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
584 if (size==0)
585 size=1;
586 void *p = realloc(ptr, size);
587 if (p == NULL)
588 Jim_Panic(NULL,"realloc: Out of memory");
589 return p;
590 }
591
592 char *Jim_StrDup(const char *s)
593 {
594 int l = strlen(s);
595 char *copy = Jim_Alloc(l+1);
596
597 memcpy(copy, s, l+1);
598 return copy;
599 }
600
601 char *Jim_StrDupLen(const char *s, int l)
602 {
603 char *copy = Jim_Alloc(l+1);
604
605 memcpy(copy, s, l+1);
606 copy[l] = 0; /* Just to be sure, original could be substring */
607 return copy;
608 }
609
610 /* -----------------------------------------------------------------------------
611 * Time related functions
612 * ---------------------------------------------------------------------------*/
613 /* Returns microseconds of CPU used since start. */
614 static jim_wide JimClock(void)
615 {
616 #if (defined WIN32) && !(defined JIM_ANSIC)
617 LARGE_INTEGER t, f;
618 QueryPerformanceFrequency(&f);
619 QueryPerformanceCounter(&t);
620 return (long)((t.QuadPart * 1000000) / f.QuadPart);
621 #else /* !WIN32 */
622 clock_t clocks = clock();
623
624 return (long)(clocks*(1000000/CLOCKS_PER_SEC));
625 #endif /* WIN32 */
626 }
627
628 /* -----------------------------------------------------------------------------
629 * Hash Tables
630 * ---------------------------------------------------------------------------*/
631
632 /* -------------------------- private prototypes ---------------------------- */
633 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
634 static unsigned int JimHashTableNextPower(unsigned int size);
635 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
636
637 /* -------------------------- hash functions -------------------------------- */
638
639 /* Thomas Wang's 32 bit Mix Function */
640 unsigned int Jim_IntHashFunction(unsigned int key)
641 {
642 key += ~(key << 15);
643 key ^= (key >> 10);
644 key += (key << 3);
645 key ^= (key >> 6);
646 key += ~(key << 11);
647 key ^= (key >> 16);
648 return key;
649 }
650
651 /* Identity hash function for integer keys */
652 unsigned int Jim_IdentityHashFunction(unsigned int key)
653 {
654 return key;
655 }
656
657 /* Generic hash function (we are using to multiply by 9 and add the byte
658 * as Tcl) */
659 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
660 {
661 unsigned int h = 0;
662 while(len--)
663 h += (h<<3)+*buf++;
664 return h;
665 }
666
667 /* ----------------------------- API implementation ------------------------- */
668 /* reset an hashtable already initialized with ht_init().
669 * NOTE: This function should only called by ht_destroy(). */
670 static void JimResetHashTable(Jim_HashTable *ht)
671 {
672 ht->table = NULL;
673 ht->size = 0;
674 ht->sizemask = 0;
675 ht->used = 0;
676 ht->collisions = 0;
677 }
678
679 /* Initialize the hash table */
680 int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
681 void *privDataPtr)
682 {
683 JimResetHashTable(ht);
684 ht->type = type;
685 ht->privdata = privDataPtr;
686 return JIM_OK;
687 }
688
689 /* Resize the table to the minimal size that contains all the elements,
690 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
691 int Jim_ResizeHashTable(Jim_HashTable *ht)
692 {
693 int minimal = ht->used;
694
695 if (minimal < JIM_HT_INITIAL_SIZE)
696 minimal = JIM_HT_INITIAL_SIZE;
697 return Jim_ExpandHashTable(ht, minimal);
698 }
699
700 /* Expand or create the hashtable */
701 int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
702 {
703 Jim_HashTable n; /* the new hashtable */
704 unsigned int realsize = JimHashTableNextPower(size), i;
705
706 /* the size is invalid if it is smaller than the number of
707 * elements already inside the hashtable */
708 if (ht->used >= size)
709 return JIM_ERR;
710
711 Jim_InitHashTable(&n, ht->type, ht->privdata);
712 n.size = realsize;
713 n.sizemask = realsize-1;
714 n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
715
716 /* Initialize all the pointers to NULL */
717 memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
718
719 /* Copy all the elements from the old to the new table:
720 * note that if the old hash table is empty ht->size is zero,
721 * so Jim_ExpandHashTable just creates an hash table. */
722 n.used = ht->used;
723 for (i = 0; i < ht->size && ht->used > 0; i++) {
724 Jim_HashEntry *he, *nextHe;
725
726 if (ht->table[i] == NULL) continue;
727
728 /* For each hash entry on this slot... */
729 he = ht->table[i];
730 while(he) {
731 unsigned int h;
732
733 nextHe = he->next;
734 /* Get the new element index */
735 h = Jim_HashKey(ht, he->key) & n.sizemask;
736 he->next = n.table[h];
737 n.table[h] = he;
738 ht->used--;
739 /* Pass to the next element */
740 he = nextHe;
741 }
742 }
743 assert(ht->used == 0);
744 Jim_Free(ht->table);
745
746 /* Remap the new hashtable in the old */
747 *ht = n;
748 return JIM_OK;
749 }
750
751 /* Add an element to the target hash table */
752 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
753 {
754 int index;
755 Jim_HashEntry *entry;
756
757 /* Get the index of the new element, or -1 if
758 * the element already exists. */
759 if ((index = JimInsertHashEntry(ht, key)) == -1)
760 return JIM_ERR;
761
762 /* Allocates the memory and stores key */
763 entry = Jim_Alloc(sizeof(*entry));
764 entry->next = ht->table[index];
765 ht->table[index] = entry;
766
767 /* Set the hash entry fields. */
768 Jim_SetHashKey(ht, entry, key);
769 Jim_SetHashVal(ht, entry, val);
770 ht->used++;
771 return JIM_OK;
772 }
773
774 /* Add an element, discarding the old if the key already exists */
775 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
776 {
777 Jim_HashEntry *entry;
778
779 /* Try to add the element. If the key
780 * does not exists Jim_AddHashEntry will suceed. */
781 if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
782 return JIM_OK;
783 /* It already exists, get the entry */
784 entry = Jim_FindHashEntry(ht, key);
785 /* Free the old value and set the new one */
786 Jim_FreeEntryVal(ht, entry);
787 Jim_SetHashVal(ht, entry, val);
788 return JIM_OK;
789 }
790
791 /* Search and remove an element */
792 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
793 {
794 unsigned int h;
795 Jim_HashEntry *he, *prevHe;
796
797 if (ht->size == 0)
798 return JIM_ERR;
799 h = Jim_HashKey(ht, key) & ht->sizemask;
800 he = ht->table[h];
801
802 prevHe = NULL;
803 while(he) {
804 if (Jim_CompareHashKeys(ht, key, he->key)) {
805 /* Unlink the element from the list */
806 if (prevHe)
807 prevHe->next = he->next;
808 else
809 ht->table[h] = he->next;
810 Jim_FreeEntryKey(ht, he);
811 Jim_FreeEntryVal(ht, he);
812 Jim_Free(he);
813 ht->used--;
814 return JIM_OK;
815 }
816 prevHe = he;
817 he = he->next;
818 }
819 return JIM_ERR; /* not found */
820 }
821
822 /* Destroy an entire hash table */
823 int Jim_FreeHashTable(Jim_HashTable *ht)
824 {
825 unsigned int i;
826
827 /* Free all the elements */
828 for (i = 0; i < ht->size && ht->used > 0; i++) {
829 Jim_HashEntry *he, *nextHe;
830
831 if ((he = ht->table[i]) == NULL) continue;
832 while(he) {
833 nextHe = he->next;
834 Jim_FreeEntryKey(ht, he);
835 Jim_FreeEntryVal(ht, he);
836 Jim_Free(he);
837 ht->used--;
838 he = nextHe;
839 }
840 }
841 /* Free the table and the allocated cache structure */
842 Jim_Free(ht->table);
843 /* Re-initialize the table */
844 JimResetHashTable(ht);
845 return JIM_OK; /* never fails */
846 }
847
848 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
849 {
850 Jim_HashEntry *he;
851 unsigned int h;
852
853 if (ht->size == 0) return NULL;
854 h = Jim_HashKey(ht, key) & ht->sizemask;
855 he = ht->table[h];
856 while(he) {
857 if (Jim_CompareHashKeys(ht, key, he->key))
858 return he;
859 he = he->next;
860 }
861 return NULL;
862 }
863
864 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
865 {
866 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
867
868 iter->ht = ht;
869 iter->index = -1;
870 iter->entry = NULL;
871 iter->nextEntry = NULL;
872 return iter;
873 }
874
875 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
876 {
877 while (1) {
878 if (iter->entry == NULL) {
879 iter->index++;
880 if (iter->index >=
881 (signed)iter->ht->size) break;
882 iter->entry = iter->ht->table[iter->index];
883 } else {
884 iter->entry = iter->nextEntry;
885 }
886 if (iter->entry) {
887 /* We need to save the 'next' here, the iterator user
888 * may delete the entry we are returning. */
889 iter->nextEntry = iter->entry->next;
890 return iter->entry;
891 }
892 }
893 return NULL;
894 }
895
896 /* ------------------------- private functions ------------------------------ */
897
898 /* Expand the hash table if needed */
899 static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
900 {
901 /* If the hash table is empty expand it to the intial size,
902 * if the table is "full" dobule its size. */
903 if (ht->size == 0)
904 return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
905 if (ht->size == ht->used)
906 return Jim_ExpandHashTable(ht, ht->size*2);
907 return JIM_OK;
908 }
909
910 /* Our hash table capability is a power of two */
911 static unsigned int JimHashTableNextPower(unsigned int size)
912 {
913 unsigned int i = JIM_HT_INITIAL_SIZE;
914
915 if (size >= 2147483648U)
916 return 2147483648U;
917 while(1) {
918 if (i >= size)
919 return i;
920 i *= 2;
921 }
922 }
923
924 /* Returns the index of a free slot that can be populated with
925 * an hash entry for the given 'key'.
926 * If the key already exists, -1 is returned. */
927 static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
928 {
929 unsigned int h;
930 Jim_HashEntry *he;
931
932 /* Expand the hashtable if needed */
933 if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
934 return -1;
935 /* Compute the key hash value */
936 h = Jim_HashKey(ht, key) & ht->sizemask;
937 /* Search if this slot does not already contain the given key */
938 he = ht->table[h];
939 while(he) {
940 if (Jim_CompareHashKeys(ht, key, he->key))
941 return -1;
942 he = he->next;
943 }
944 return h;
945 }
946
947 /* ----------------------- StringCopy Hash Table Type ------------------------*/
948
949 static unsigned int JimStringCopyHTHashFunction(const void *key)
950 {
951 return Jim_GenHashFunction(key, strlen(key));
952 }
953
954 static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
955 {
956 int len = strlen(key);
957 char *copy = Jim_Alloc(len+1);
958 JIM_NOTUSED(privdata);
959
960 memcpy(copy, key, len);
961 copy[len] = '\0';
962 return copy;
963 }
964
965 static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
966 {
967 int len = strlen(val);
968 char *copy = Jim_Alloc(len+1);
969 JIM_NOTUSED(privdata);
970
971 memcpy(copy, val, len);
972 copy[len] = '\0';
973 return copy;
974 }
975
976 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
977 const void *key2)
978 {
979 JIM_NOTUSED(privdata);
980
981 return strcmp(key1, key2) == 0;
982 }
983
984 static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
985 {
986 JIM_NOTUSED(privdata);
987
988 Jim_Free((void*)key); /* ATTENTION: const cast */
989 }
990
991 static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
992 {
993 JIM_NOTUSED(privdata);
994
995 Jim_Free((void*)val); /* ATTENTION: const cast */
996 }
997
998 static Jim_HashTableType JimStringCopyHashTableType = {
999 JimStringCopyHTHashFunction, /* hash function */
1000 JimStringCopyHTKeyDup, /* key dup */
1001 NULL, /* val dup */
1002 JimStringCopyHTKeyCompare, /* key compare */
1003 JimStringCopyHTKeyDestructor, /* key destructor */
1004 NULL /* val destructor */
1005 };
1006
1007 /* This is like StringCopy but does not auto-duplicate the key.
1008 * It's used for intepreter's shared strings. */
1009 static Jim_HashTableType JimSharedStringsHashTableType = {
1010 JimStringCopyHTHashFunction, /* hash function */
1011 NULL, /* key dup */
1012 NULL, /* val dup */
1013 JimStringCopyHTKeyCompare, /* key compare */
1014 JimStringCopyHTKeyDestructor, /* key destructor */
1015 NULL /* val destructor */
1016 };
1017
1018 /* This is like StringCopy but also automatically handle dynamic
1019 * allocated C strings as values. */
1020 static Jim_HashTableType JimStringKeyValCopyHashTableType = {
1021 JimStringCopyHTHashFunction, /* hash function */
1022 JimStringCopyHTKeyDup, /* key dup */
1023 JimStringKeyValCopyHTValDup, /* val dup */
1024 JimStringCopyHTKeyCompare, /* key compare */
1025 JimStringCopyHTKeyDestructor, /* key destructor */
1026 JimStringKeyValCopyHTValDestructor, /* val destructor */
1027 };
1028
1029 typedef struct AssocDataValue {
1030 Jim_InterpDeleteProc *delProc;
1031 void *data;
1032 } AssocDataValue;
1033
1034 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
1035 {
1036 AssocDataValue *assocPtr = (AssocDataValue *)data;
1037 if (assocPtr->delProc != NULL)
1038 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
1039 Jim_Free(data);
1040 }
1041
1042 static Jim_HashTableType JimAssocDataHashTableType = {
1043 JimStringCopyHTHashFunction, /* hash function */
1044 JimStringCopyHTKeyDup, /* key dup */
1045 NULL, /* val dup */
1046 JimStringCopyHTKeyCompare, /* key compare */
1047 JimStringCopyHTKeyDestructor, /* key destructor */
1048 JimAssocDataHashTableValueDestructor /* val destructor */
1049 };
1050
1051 /* -----------------------------------------------------------------------------
1052 * Stack - This is a simple generic stack implementation. It is used for
1053 * example in the 'expr' expression compiler.
1054 * ---------------------------------------------------------------------------*/
1055 void Jim_InitStack(Jim_Stack *stack)
1056 {
1057 stack->len = 0;
1058 stack->maxlen = 0;
1059 stack->vector = NULL;
1060 }
1061
1062 void Jim_FreeStack(Jim_Stack *stack)
1063 {
1064 Jim_Free(stack->vector);
1065 }
1066
1067 int Jim_StackLen(Jim_Stack *stack)
1068 {
1069 return stack->len;
1070 }
1071
1072 void Jim_StackPush(Jim_Stack *stack, void *element) {
1073 int neededLen = stack->len+1;
1074 if (neededLen > stack->maxlen) {
1075 stack->maxlen = neededLen*2;
1076 stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1077 }
1078 stack->vector[stack->len] = element;
1079 stack->len++;
1080 }
1081
1082 void *Jim_StackPop(Jim_Stack *stack)
1083 {
1084 if (stack->len == 0) return NULL;
1085 stack->len--;
1086 return stack->vector[stack->len];
1087 }
1088
1089 void *Jim_StackPeek(Jim_Stack *stack)
1090 {
1091 if (stack->len == 0) return NULL;
1092 return stack->vector[stack->len-1];
1093 }
1094
1095 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1096 {
1097 int i;
1098
1099 for (i = 0; i < stack->len; i++)
1100 freeFunc(stack->vector[i]);
1101 }
1102
1103 /* -----------------------------------------------------------------------------
1104 * Parser
1105 * ---------------------------------------------------------------------------*/
1106
1107 /* Token types */
1108 #define JIM_TT_NONE -1 /* No token returned */
1109 #define JIM_TT_STR 0 /* simple string */
1110 #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
1111 #define JIM_TT_VAR 2 /* var substitution */
1112 #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
1113 #define JIM_TT_CMD 4 /* command substitution */
1114 #define JIM_TT_SEP 5 /* word separator */
1115 #define JIM_TT_EOL 6 /* line separator */
1116
1117 /* Additional token types needed for expressions */
1118 #define JIM_TT_SUBEXPR_START 7
1119 #define JIM_TT_SUBEXPR_END 8
1120 #define JIM_TT_EXPR_NUMBER 9
1121 #define JIM_TT_EXPR_OPERATOR 10
1122
1123 /* Parser states */
1124 #define JIM_PS_DEF 0 /* Default state */
1125 #define JIM_PS_QUOTE 1 /* Inside "" */
1126
1127 /* Parser context structure. The same context is used both to parse
1128 * Tcl scripts and lists. */
1129 struct JimParserCtx {
1130 const char *prg; /* Program text */
1131 const char *p; /* Pointer to the point of the program we are parsing */
1132 int len; /* Left length of 'prg' */
1133 int linenr; /* Current line number */
1134 const char *tstart;
1135 const char *tend; /* Returned token is at tstart-tend in 'prg'. */
1136 int tline; /* Line number of the returned token */
1137 int tt; /* Token type */
1138 int eof; /* Non zero if EOF condition is true. */
1139 int state; /* Parser state */
1140 int comment; /* Non zero if the next chars may be a comment. */
1141 };
1142
1143 #define JimParserEof(c) ((c)->eof)
1144 #define JimParserTstart(c) ((c)->tstart)
1145 #define JimParserTend(c) ((c)->tend)
1146 #define JimParserTtype(c) ((c)->tt)
1147 #define JimParserTline(c) ((c)->tline)
1148
1149 static int JimParseScript(struct JimParserCtx *pc);
1150 static int JimParseSep(struct JimParserCtx *pc);
1151 static int JimParseEol(struct JimParserCtx *pc);
1152 static int JimParseCmd(struct JimParserCtx *pc);
1153 static int JimParseVar(struct JimParserCtx *pc);
1154 static int JimParseBrace(struct JimParserCtx *pc);
1155 static int JimParseStr(struct JimParserCtx *pc);
1156 static int JimParseComment(struct JimParserCtx *pc);
1157 static char *JimParserGetToken(struct JimParserCtx *pc,
1158 int *lenPtr, int *typePtr, int *linePtr);
1159
1160 /* Initialize a parser context.
1161 * 'prg' is a pointer to the program text, linenr is the line
1162 * number of the first line contained in the program. */
1163 void JimParserInit(struct JimParserCtx *pc, const char *prg,
1164 int len, int linenr)
1165 {
1166 pc->prg = prg;
1167 pc->p = prg;
1168 pc->len = len;
1169 pc->tstart = NULL;
1170 pc->tend = NULL;
1171 pc->tline = 0;
1172 pc->tt = JIM_TT_NONE;
1173 pc->eof = 0;
1174 pc->state = JIM_PS_DEF;
1175 pc->linenr = linenr;
1176 pc->comment = 1;
1177 }
1178
1179 int JimParseScript(struct JimParserCtx *pc)
1180 {
1181 while(1) { /* the while is used to reiterate with continue if needed */
1182 if (!pc->len) {
1183 pc->tstart = pc->p;
1184 pc->tend = pc->p-1;
1185 pc->tline = pc->linenr;
1186 pc->tt = JIM_TT_EOL;
1187 pc->eof = 1;
1188 return JIM_OK;
1189 }
1190 switch(*(pc->p)) {
1191 case '\\':
1192 if (*(pc->p+1) == '\n')
1193 return JimParseSep(pc);
1194 else {
1195 pc->comment = 0;
1196 return JimParseStr(pc);
1197 }
1198 break;
1199 case ' ':
1200 case '\t':
1201 case '\r':
1202 if (pc->state == JIM_PS_DEF)
1203 return JimParseSep(pc);
1204 else {
1205 pc->comment = 0;
1206 return JimParseStr(pc);
1207 }
1208 break;
1209 case '\n':
1210 case ';':
1211 pc->comment = 1;
1212 if (pc->state == JIM_PS_DEF)
1213 return JimParseEol(pc);
1214 else
1215 return JimParseStr(pc);
1216 break;
1217 case '[':
1218 pc->comment = 0;
1219 return JimParseCmd(pc);
1220 break;
1221 case '$':
1222 pc->comment = 0;
1223 if (JimParseVar(pc) == JIM_ERR) {
1224 pc->tstart = pc->tend = pc->p++; pc->len--;
1225 pc->tline = pc->linenr;
1226 pc->tt = JIM_TT_STR;
1227 return JIM_OK;
1228 } else
1229 return JIM_OK;
1230 break;
1231 case '#':
1232 if (pc->comment) {
1233 JimParseComment(pc);
1234 continue;
1235 } else {
1236 return JimParseStr(pc);
1237 }
1238 default:
1239 pc->comment = 0;
1240 return JimParseStr(pc);
1241 break;
1242 }
1243 return JIM_OK;
1244 }
1245 }
1246
1247 int JimParseSep(struct JimParserCtx *pc)
1248 {
1249 pc->tstart = pc->p;
1250 pc->tline = pc->linenr;
1251 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1252 (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1253 if (*pc->p == '\\') {
1254 pc->p++; pc->len--;
1255 pc->linenr++;
1256 }
1257 pc->p++; pc->len--;
1258 }
1259 pc->tend = pc->p-1;
1260 pc->tt = JIM_TT_SEP;
1261 return JIM_OK;
1262 }
1263
1264 int JimParseEol(struct JimParserCtx *pc)
1265 {
1266 pc->tstart = pc->p;
1267 pc->tline = pc->linenr;
1268 while (*pc->p == ' ' || *pc->p == '\n' ||
1269 *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1270 if (*pc->p == '\n')
1271 pc->linenr++;
1272 pc->p++; pc->len--;
1273 }
1274 pc->tend = pc->p-1;
1275 pc->tt = JIM_TT_EOL;
1276 return JIM_OK;
1277 }
1278
1279 /* Todo. Don't stop if ']' appears inside {} or quoted.
1280 * Also should handle the case of puts [string length "]"] */
1281 int JimParseCmd(struct JimParserCtx *pc)
1282 {
1283 int level = 1;
1284 int blevel = 0;
1285
1286 pc->tstart = ++pc->p; pc->len--;
1287 pc->tline = pc->linenr;
1288 while (1) {
1289 if (pc->len == 0) {
1290 break;
1291 } else if (*pc->p == '[' && blevel == 0) {
1292 level++;
1293 } else if (*pc->p == ']' && blevel == 0) {
1294 level--;
1295 if (!level) break;
1296 } else if (*pc->p == '\\') {
1297 pc->p++; pc->len--;
1298 } else if (*pc->p == '{') {
1299 blevel++;
1300 } else if (*pc->p == '}') {
1301 if (blevel != 0)
1302 blevel--;
1303 } else if (*pc->p == '\n')
1304 pc->linenr++;
1305 pc->p++; pc->len--;
1306 }
1307 pc->tend = pc->p-1;
1308 pc->tt = JIM_TT_CMD;
1309 if (*pc->p == ']') {
1310 pc->p++; pc->len--;
1311 }
1312 return JIM_OK;
1313 }
1314
1315 int JimParseVar(struct JimParserCtx *pc)
1316 {
1317 int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1318
1319 pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1320 pc->tline = pc->linenr;
1321 if (*pc->p == '{') {
1322 pc->tstart = ++pc->p; pc->len--;
1323 brace = 1;
1324 }
1325 if (brace) {
1326 while (!stop) {
1327 if (*pc->p == '}' || pc->len == 0) {
1328 stop = 1;
1329 if (pc->len == 0)
1330 continue;
1331 }
1332 else if (*pc->p == '\n')
1333 pc->linenr++;
1334 pc->p++; pc->len--;
1335 }
1336 if (pc->len == 0)
1337 pc->tend = pc->p-1;
1338 else
1339 pc->tend = pc->p-2;
1340 } else {
1341 while (!stop) {
1342 if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1343 (*pc->p >= 'A' && *pc->p <= 'Z') ||
1344 (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1345 stop = 1;
1346 else {
1347 pc->p++; pc->len--;
1348 }
1349 }
1350 /* Parse [dict get] syntax sugar. */
1351 if (*pc->p == '(') {
1352 while (*pc->p != ')' && pc->len) {
1353 pc->p++; pc->len--;
1354 if (*pc->p == '\\' && pc->len >= 2) {
1355 pc->p += 2; pc->len -= 2;
1356 }
1357 }
1358 if (*pc->p != '\0') {
1359 pc->p++; pc->len--;
1360 }
1361 ttype = JIM_TT_DICTSUGAR;
1362 }
1363 pc->tend = pc->p-1;
1364 }
1365 /* Check if we parsed just the '$' character.
1366 * That's not a variable so an error is returned
1367 * to tell the state machine to consider this '$' just
1368 * a string. */
1369 if (pc->tstart == pc->p) {
1370 pc->p--; pc->len++;
1371 return JIM_ERR;
1372 }
1373 pc->tt = ttype;
1374 return JIM_OK;
1375 }
1376
1377 int JimParseBrace(struct JimParserCtx *pc)
1378 {
1379 int level = 1;
1380
1381 pc->tstart = ++pc->p; pc->len--;
1382 pc->tline = pc->linenr;
1383 while (1) {
1384 if (*pc->p == '\\' && pc->len >= 2) {
1385 pc->p++; pc->len--;
1386 if (*pc->p == '\n')
1387 pc->linenr++;
1388 } else if (*pc->p == '{') {
1389 level++;
1390 } else if (pc->len == 0 || *pc->p == '}') {
1391 level--;
1392 if (pc->len == 0 || level == 0) {
1393 pc->tend = pc->p-1;
1394 if (pc->len != 0) {
1395 pc->p++; pc->len--;
1396 }
1397 pc->tt = JIM_TT_STR;
1398 return JIM_OK;
1399 }
1400 } else if (*pc->p == '\n') {
1401 pc->linenr++;
1402 }
1403 pc->p++; pc->len--;
1404 }
1405 return JIM_OK; /* unreached */
1406 }
1407
1408 int JimParseStr(struct JimParserCtx *pc)
1409 {
1410 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1411 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1412 if (newword && *pc->p == '{') {
1413 return JimParseBrace(pc);
1414 } else if (newword && *pc->p == '"') {
1415 pc->state = JIM_PS_QUOTE;
1416 pc->p++; pc->len--;
1417 }
1418 pc->tstart = pc->p;
1419 pc->tline = pc->linenr;
1420 while (1) {
1421 if (pc->len == 0) {
1422 pc->tend = pc->p-1;
1423 pc->tt = JIM_TT_ESC;
1424 return JIM_OK;
1425 }
1426 switch(*pc->p) {
1427 case '\\':
1428 if (pc->state == JIM_PS_DEF &&
1429 *(pc->p+1) == '\n') {
1430 pc->tend = pc->p-1;
1431 pc->tt = JIM_TT_ESC;
1432 return JIM_OK;
1433 }
1434 if (pc->len >= 2) {
1435 pc->p++; pc->len--;
1436 }
1437 break;
1438 case '$':
1439 case '[':
1440 pc->tend = pc->p-1;
1441 pc->tt = JIM_TT_ESC;
1442 return JIM_OK;
1443 case ' ':
1444 case '\t':
1445 case '\n':
1446 case '\r':
1447 case ';':
1448 if (pc->state == JIM_PS_DEF) {
1449 pc->tend = pc->p-1;
1450 pc->tt = JIM_TT_ESC;
1451 return JIM_OK;
1452 } else if (*pc->p == '\n') {
1453 pc->linenr++;
1454 }
1455 break;
1456 case '"':
1457 if (pc->state == JIM_PS_QUOTE) {
1458 pc->tend = pc->p-1;
1459 pc->tt = JIM_TT_ESC;
1460 pc->p++; pc->len--;
1461 pc->state = JIM_PS_DEF;
1462 return JIM_OK;
1463 }
1464 break;
1465 }
1466 pc->p++; pc->len--;
1467 }
1468 return JIM_OK; /* unreached */
1469 }
1470
1471 int JimParseComment(struct JimParserCtx *pc)
1472 {
1473 while (*pc->p) {
1474 if (*pc->p == '\n') {
1475 pc->linenr++;
1476 if (*(pc->p-1) != '\\') {
1477 pc->p++; pc->len--;
1478 return JIM_OK;
1479 }
1480 }
1481 pc->p++; pc->len--;
1482 }
1483 return JIM_OK;
1484 }
1485
1486 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
1487 static int xdigitval(int c)
1488 {
1489 if (c >= '0' && c <= '9') return c-'0';
1490 if (c >= 'a' && c <= 'f') return c-'a'+10;
1491 if (c >= 'A' && c <= 'F') return c-'A'+10;
1492 return -1;
1493 }
1494
1495 static int odigitval(int c)
1496 {
1497 if (c >= '0' && c <= '7') return c-'0';
1498 return -1;
1499 }
1500
1501 /* Perform Tcl escape substitution of 's', storing the result
1502 * string into 'dest'. The escaped string is guaranteed to
1503 * be the same length or shorted than the source string.
1504 * Slen is the length of the string at 's', if it's -1 the string
1505 * length will be calculated by the function.
1506 *
1507 * The function returns the length of the resulting string. */
1508 static int JimEscape(char *dest, const char *s, int slen)
1509 {
1510 char *p = dest;
1511 int i, len;
1512
1513 if (slen == -1)
1514 slen = strlen(s);
1515
1516 for (i = 0; i < slen; i++) {
1517 switch(s[i]) {
1518 case '\\':
1519 switch(s[i+1]) {
1520 case 'a': *p++ = 0x7; i++; break;
1521 case 'b': *p++ = 0x8; i++; break;
1522 case 'f': *p++ = 0xc; i++; break;
1523 case 'n': *p++ = 0xa; i++; break;
1524 case 'r': *p++ = 0xd; i++; break;
1525 case 't': *p++ = 0x9; i++; break;
1526 case 'v': *p++ = 0xb; i++; break;
1527 case '\0': *p++ = '\\'; i++; break;
1528 case '\n': *p++ = ' '; i++; break;
1529 default:
1530 if (s[i+1] == 'x') {
1531 int val = 0;
1532 int c = xdigitval(s[i+2]);
1533 if (c == -1) {
1534 *p++ = 'x';
1535 i++;
1536 break;
1537 }
1538 val = c;
1539 c = xdigitval(s[i+3]);
1540 if (c == -1) {
1541 *p++ = val;
1542 i += 2;
1543 break;
1544 }
1545 val = (val*16)+c;
1546 *p++ = val;
1547 i += 3;
1548 break;
1549 } else if (s[i+1] >= '0' && s[i+1] <= '7')
1550 {
1551 int val = 0;
1552 int c = odigitval(s[i+1]);
1553 val = c;
1554 c = odigitval(s[i+2]);
1555 if (c == -1) {
1556 *p++ = val;
1557 i ++;
1558 break;
1559 }
1560 val = (val*8)+c;
1561 c = odigitval(s[i+3]);
1562 if (c == -1) {
1563 *p++ = val;
1564 i += 2;
1565 break;
1566 }
1567 val = (val*8)+c;
1568 *p++ = val;
1569 i += 3;
1570 } else {
1571 *p++ = s[i+1];
1572 i++;
1573 }
1574 break;
1575 }
1576 break;
1577 default:
1578 *p++ = s[i];
1579 break;
1580 }
1581 }
1582 len = p-dest;
1583 *p++ = '\0';
1584 return len;
1585 }
1586
1587 /* Returns a dynamically allocated copy of the current token in the
1588 * parser context. The function perform conversion of escapes if
1589 * the token is of type JIM_TT_ESC.
1590 *
1591 * Note that after the conversion, tokens that are grouped with
1592 * braces in the source code, are always recognizable from the
1593 * identical string obtained in a different way from the type.
1594 *
1595 * For exmple the string:
1596 *
1597 * {expand}$a
1598 *
1599 * will return as first token "expand", of type JIM_TT_STR
1600 *
1601 * While the string:
1602 *
1603 * expand$a
1604 *
1605 * will return as first token "expand", of type JIM_TT_ESC
1606 */
1607 char *JimParserGetToken(struct JimParserCtx *pc,
1608 int *lenPtr, int *typePtr, int *linePtr)
1609 {
1610 const char *start, *end;
1611 char *token;
1612 int len;
1613
1614 start = JimParserTstart(pc);
1615 end = JimParserTend(pc);
1616 if (start > end) {
1617 if (lenPtr) *lenPtr = 0;
1618 if (typePtr) *typePtr = JimParserTtype(pc);
1619 if (linePtr) *linePtr = JimParserTline(pc);
1620 token = Jim_Alloc(1);
1621 token[0] = '\0';
1622 return token;
1623 }
1624 len = (end-start)+1;
1625 token = Jim_Alloc(len+1);
1626 if (JimParserTtype(pc) != JIM_TT_ESC) {
1627 /* No escape conversion needed? Just copy it. */
1628 memcpy(token, start, len);
1629 token[len] = '\0';
1630 } else {
1631 /* Else convert the escape chars. */
1632 len = JimEscape(token, start, len);
1633 }
1634 if (lenPtr) *lenPtr = len;
1635 if (typePtr) *typePtr = JimParserTtype(pc);
1636 if (linePtr) *linePtr = JimParserTline(pc);
1637 return token;
1638 }
1639
1640 /* The following functin is not really part of the parsing engine of Jim,
1641 * but it somewhat related. Given an string and its length, it tries
1642 * to guess if the script is complete or there are instead " " or { }
1643 * open and not completed. This is useful for interactive shells
1644 * implementation and for [info complete].
1645 *
1646 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1647 * '{' on scripts incomplete missing one or more '}' to be balanced.
1648 * '"' on scripts incomplete missing a '"' char.
1649 *
1650 * If the script is complete, 1 is returned, otherwise 0. */
1651 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1652 {
1653 int level = 0;
1654 int state = ' ';
1655
1656 while(len) {
1657 switch (*s) {
1658 case '\\':
1659 if (len > 1)
1660 s++;
1661 break;
1662 case '"':
1663 if (state == ' ') {
1664 state = '"';
1665 } else if (state == '"') {
1666 state = ' ';
1667 }
1668 break;
1669 case '{':
1670 if (state == '{') {
1671 level++;
1672 } else if (state == ' ') {
1673 state = '{';
1674 level++;
1675 }
1676 break;
1677 case '}':
1678 if (state == '{') {
1679 level--;
1680 if (level == 0)
1681 state = ' ';
1682 }
1683 break;
1684 }
1685 s++;
1686 len--;
1687 }
1688 if (stateCharPtr)
1689 *stateCharPtr = state;
1690 return state == ' ';
1691 }
1692
1693 /* -----------------------------------------------------------------------------
1694 * Tcl Lists parsing
1695 * ---------------------------------------------------------------------------*/
1696 static int JimParseListSep(struct JimParserCtx *pc);
1697 static int JimParseListStr(struct JimParserCtx *pc);
1698
1699 int JimParseList(struct JimParserCtx *pc)
1700 {
1701 if (pc->len == 0) {
1702 pc->tstart = pc->tend = pc->p;
1703 pc->tline = pc->linenr;
1704 pc->tt = JIM_TT_EOL;
1705 pc->eof = 1;
1706 return JIM_OK;
1707 }
1708 switch(*pc->p) {
1709 case ' ':
1710 case '\n':
1711 case '\t':
1712 case '\r':
1713 if (pc->state == JIM_PS_DEF)
1714 return JimParseListSep(pc);
1715 else
1716 return JimParseListStr(pc);
1717 break;
1718 default:
1719 return JimParseListStr(pc);
1720 break;
1721 }
1722 return JIM_OK;
1723 }
1724
1725 int JimParseListSep(struct JimParserCtx *pc)
1726 {
1727 pc->tstart = pc->p;
1728 pc->tline = pc->linenr;
1729 while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1730 {
1731 pc->p++; pc->len--;
1732 }
1733 pc->tend = pc->p-1;
1734 pc->tt = JIM_TT_SEP;
1735 return JIM_OK;
1736 }
1737
1738 int JimParseListStr(struct JimParserCtx *pc)
1739 {
1740 int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1741 pc->tt == JIM_TT_NONE);
1742 if (newword && *pc->p == '{') {
1743 return JimParseBrace(pc);
1744 } else if (newword && *pc->p == '"') {
1745 pc->state = JIM_PS_QUOTE;
1746 pc->p++; pc->len--;
1747 }
1748 pc->tstart = pc->p;
1749 pc->tline = pc->linenr;
1750 while (1) {
1751 if (pc->len == 0) {
1752 pc->tend = pc->p-1;
1753 pc->tt = JIM_TT_ESC;
1754 return JIM_OK;
1755 }
1756 switch(*pc->p) {
1757 case '\\':
1758 pc->p++; pc->len--;
1759 break;
1760 case ' ':
1761 case '\t':
1762 case '\n':
1763 case '\r':
1764 if (pc->state == JIM_PS_DEF) {
1765 pc->tend = pc->p-1;
1766 pc->tt = JIM_TT_ESC;
1767 return JIM_OK;
1768 } else if (*pc->p == '\n') {
1769 pc->linenr++;
1770 }
1771 break;
1772 case '"':
1773 if (pc->state == JIM_PS_QUOTE) {
1774 pc->tend = pc->p-1;
1775 pc->tt = JIM_TT_ESC;
1776 pc->p++; pc->len--;
1777 pc->state = JIM_PS_DEF;
1778 return JIM_OK;
1779 }
1780 break;
1781 }
1782 pc->p++; pc->len--;
1783 }
1784 return JIM_OK; /* unreached */
1785 }
1786
1787 /* -----------------------------------------------------------------------------
1788 * Jim_Obj related functions
1789 * ---------------------------------------------------------------------------*/
1790
1791 /* Return a new initialized object. */
1792 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1793 {
1794 Jim_Obj *objPtr;
1795
1796 /* -- Check if there are objects in the free list -- */
1797 if (interp->freeList != NULL) {
1798 /* -- Unlink the object from the free list -- */
1799 objPtr = interp->freeList;
1800 interp->freeList = objPtr->nextObjPtr;
1801 } else {
1802 /* -- No ready to use objects: allocate a new one -- */
1803 objPtr = Jim_Alloc(sizeof(*objPtr));
1804 }
1805
1806 /* Object is returned with refCount of 0. Every
1807 * kind of GC implemented should take care to don't try
1808 * to scan objects with refCount == 0. */
1809 objPtr->refCount = 0;
1810 /* All the other fields are left not initialized to save time.
1811 * The caller will probably want set they to the right
1812 * value anyway. */
1813
1814 /* -- Put the object into the live list -- */
1815 objPtr->prevObjPtr = NULL;
1816 objPtr->nextObjPtr = interp->liveList;
1817 if (interp->liveList)
1818 interp->liveList->prevObjPtr = objPtr;
1819 interp->liveList = objPtr;
1820
1821 return objPtr;
1822 }
1823
1824 /* Free an object. Actually objects are never freed, but
1825 * just moved to the free objects list, where they will be
1826 * reused by Jim_NewObj(). */
1827 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1828 {
1829 /* Check if the object was already freed, panic. */
1830 if (objPtr->refCount != 0) {
1831 Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1832 objPtr->refCount);
1833 }
1834 /* Free the internal representation */
1835 Jim_FreeIntRep(interp, objPtr);
1836 /* Free the string representation */
1837 if (objPtr->bytes != NULL) {
1838 if (objPtr->bytes != JimEmptyStringRep)
1839 Jim_Free(objPtr->bytes);
1840 }
1841 /* Unlink the object from the live objects list */
1842 if (objPtr->prevObjPtr)
1843 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1844 if (objPtr->nextObjPtr)
1845 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1846 if (interp->liveList == objPtr)
1847 interp->liveList = objPtr->nextObjPtr;
1848 /* Link the object into the free objects list */
1849 objPtr->prevObjPtr = NULL;
1850 objPtr->nextObjPtr = interp->freeList;
1851 if (interp->freeList)
1852 interp->freeList->prevObjPtr = objPtr;
1853 interp->freeList = objPtr;
1854 objPtr->refCount = -1;
1855 }
1856
1857 /* Invalidate the string representation of an object. */
1858 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1859 {
1860 if (objPtr->bytes != NULL) {
1861 if (objPtr->bytes != JimEmptyStringRep)
1862 Jim_Free(objPtr->bytes);
1863 }
1864 objPtr->bytes = NULL;
1865 }
1866
1867 #define Jim_SetStringRep(o, b, l) \
1868 do { (o)->bytes = b; (o)->length = l; } while (0)
1869
1870 /* Set the initial string representation for an object.
1871 * Does not try to free an old one. */
1872 void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1873 {
1874 if (length == 0) {
1875 objPtr->bytes = JimEmptyStringRep;
1876 objPtr->length = 0;
1877 } else {
1878 objPtr->bytes = Jim_Alloc(length+1);
1879 objPtr->length = length;
1880 memcpy(objPtr->bytes, bytes, length);
1881 objPtr->bytes[length] = '\0';
1882 }
1883 }
1884
1885 /* Duplicate an object. The returned object has refcount = 0. */
1886 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1887 {
1888 Jim_Obj *dupPtr;
1889
1890 dupPtr = Jim_NewObj(interp);
1891 if (objPtr->bytes == NULL) {
1892 /* Object does not have a valid string representation. */
1893 dupPtr->bytes = NULL;
1894 } else {
1895 Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1896 }
1897 if (objPtr->typePtr != NULL) {
1898 if (objPtr->typePtr->dupIntRepProc == NULL) {
1899 dupPtr->internalRep = objPtr->internalRep;
1900 } else {
1901 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1902 }
1903 dupPtr->typePtr = objPtr->typePtr;
1904 } else {
1905 dupPtr->typePtr = NULL;
1906 }
1907 return dupPtr;
1908 }
1909
1910 /* Return the string representation for objPtr. If the object
1911 * string representation is invalid, calls the method to create
1912 * a new one starting from the internal representation of the object. */
1913 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1914 {
1915 if (objPtr->bytes == NULL) {
1916 /* Invalid string repr. Generate it. */
1917 if (objPtr->typePtr->updateStringProc == NULL) {
1918 Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1919 objPtr->typePtr->name);
1920 }
1921 objPtr->typePtr->updateStringProc(objPtr);
1922 }
1923 if (lenPtr)
1924 *lenPtr = objPtr->length;
1925 return objPtr->bytes;
1926 }
1927
1928 /* Just returns the length of the object's string rep */
1929 int Jim_Length(Jim_Obj *objPtr)
1930 {
1931 int len;
1932
1933 Jim_GetString(objPtr, &len);
1934 return len;
1935 }
1936
1937 /* -----------------------------------------------------------------------------
1938 * String Object
1939 * ---------------------------------------------------------------------------*/
1940 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1941 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1942
1943 static Jim_ObjType stringObjType = {
1944 "string",
1945 NULL,
1946 DupStringInternalRep,
1947 NULL,
1948 JIM_TYPE_REFERENCES,
1949 };
1950
1951 void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1952 {
1953 JIM_NOTUSED(interp);
1954
1955 /* This is a bit subtle: the only caller of this function
1956 * should be Jim_DuplicateObj(), that will copy the
1957 * string representaion. After the copy, the duplicated
1958 * object will not have more room in teh buffer than
1959 * srcPtr->length bytes. So we just set it to length. */
1960 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1961 }
1962
1963 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1964 {
1965 /* Get a fresh string representation. */
1966 (void) Jim_GetString(objPtr, NULL);
1967 /* Free any other internal representation. */
1968 Jim_FreeIntRep(interp, objPtr);
1969 /* Set it as string, i.e. just set the maxLength field. */
1970 objPtr->typePtr = &stringObjType;
1971 objPtr->internalRep.strValue.maxLength = objPtr->length;
1972 return JIM_OK;
1973 }
1974
1975 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1976 {
1977 Jim_Obj *objPtr = Jim_NewObj(interp);
1978
1979 if (len == -1)
1980 len = strlen(s);
1981 /* Alloc/Set the string rep. */
1982 if (len == 0) {
1983 objPtr->bytes = JimEmptyStringRep;
1984 objPtr->length = 0;
1985 } else {
1986 objPtr->bytes = Jim_Alloc(len+1);
1987 objPtr->length = len;
1988 memcpy(objPtr->bytes, s, len);
1989 objPtr->bytes[len] = '\0';
1990 }
1991
1992 /* No typePtr field for the vanilla string object. */
1993 objPtr->typePtr = NULL;
1994 return objPtr;
1995 }
1996
1997 /* This version does not try to duplicate the 's' pointer, but
1998 * use it directly. */
1999 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
2000 {
2001 Jim_Obj *objPtr = Jim_NewObj(interp);
2002
2003 if (len == -1)
2004 len = strlen(s);
2005 Jim_SetStringRep(objPtr, s, len);
2006 objPtr->typePtr = NULL;
2007 return objPtr;
2008 }
2009
2010 /* Low-level string append. Use it only against objects
2011 * of type "string". */
2012 void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
2013 {
2014 int needlen;
2015
2016 if (len == -1)
2017 len = strlen(str);
2018 needlen = objPtr->length + len;
2019 if (objPtr->internalRep.strValue.maxLength < needlen ||
2020 objPtr->internalRep.strValue.maxLength == 0) {
2021 if (objPtr->bytes == JimEmptyStringRep) {
2022 objPtr->bytes = Jim_Alloc((needlen*2)+1);
2023 } else {
2024 objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
2025 }
2026 objPtr->internalRep.strValue.maxLength = needlen*2;
2027 }
2028 memcpy(objPtr->bytes + objPtr->length, str, len);
2029 objPtr->bytes[objPtr->length+len] = '\0';
2030 objPtr->length += len;
2031 }
2032
2033 /* Low-level wrapper to append an object. */
2034 void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
2035 {
2036 int len;
2037 const char *str;
2038
2039 str = Jim_GetString(appendObjPtr, &len);
2040 StringAppendString(objPtr, str, len);
2041 }
2042
2043 /* Higher level API to append strings to objects. */
2044 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
2045 int len)
2046 {
2047 if (Jim_IsShared(objPtr))
2048 Jim_Panic(interp,"Jim_AppendString called with shared object");
2049 if (objPtr->typePtr != &stringObjType)
2050 SetStringFromAny(interp, objPtr);
2051 StringAppendString(objPtr, str, len);
2052 }
2053
2054 void Jim_AppendString_sprintf( Jim_Interp *interp, Jim_Obj *objPtr, const char *fmt, ... )
2055 {
2056 char *buf;
2057 va_list ap;
2058
2059 va_start( ap, fmt );
2060 buf = jim_vasprintf( fmt, ap );
2061 va_end(ap);
2062
2063 if( buf ){
2064 Jim_AppendString( interp, objPtr, buf, -1 );
2065 jim_vasprintf_done(buf);
2066 }
2067 }
2068
2069
2070 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
2071 Jim_Obj *appendObjPtr)
2072 {
2073 int len;
2074 const char *str;
2075
2076 str = Jim_GetString(appendObjPtr, &len);
2077 Jim_AppendString(interp, objPtr, str, len);
2078 }
2079
2080 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
2081 {
2082 va_list ap;
2083
2084 if (objPtr->typePtr != &stringObjType)
2085 SetStringFromAny(interp, objPtr);
2086 va_start(ap, objPtr);
2087 while (1) {
2088 char *s = va_arg(ap, char*);
2089
2090 if (s == NULL) break;
2091 Jim_AppendString(interp, objPtr, s, -1);
2092 }
2093 va_end(ap);
2094 }
2095
2096 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2097 {
2098 const char *aStr, *bStr;
2099 int aLen, bLen, i;
2100
2101 if (aObjPtr == bObjPtr) return 1;
2102 aStr = Jim_GetString(aObjPtr, &aLen);
2103 bStr = Jim_GetString(bObjPtr, &bLen);
2104 if (aLen != bLen) return 0;
2105 if (nocase == 0)
2106 return memcmp(aStr, bStr, aLen) == 0;
2107 for (i = 0; i < aLen; i++) {
2108 if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2109 return 0;
2110 }
2111 return 1;
2112 }
2113
2114 int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2115 int nocase)
2116 {
2117 const char *pattern, *string;
2118 int patternLen, stringLen;
2119
2120 pattern = Jim_GetString(patternObjPtr, &patternLen);
2121 string = Jim_GetString(objPtr, &stringLen);
2122 return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2123 }
2124
2125 int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2126 Jim_Obj *secondObjPtr, int nocase)
2127 {
2128 const char *s1, *s2;
2129 int l1, l2;
2130
2131 s1 = Jim_GetString(firstObjPtr, &l1);
2132 s2 = Jim_GetString(secondObjPtr, &l2);
2133 return JimStringCompare(s1, l1, s2, l2, nocase);
2134 }
2135
2136 /* Convert a range, as returned by Jim_GetRange(), into
2137 * an absolute index into an object of the specified length.
2138 * This function may return negative values, or values
2139 * bigger or equal to the length of the list if the index
2140 * is out of range. */
2141 static int JimRelToAbsIndex(int len, int index)
2142 {
2143 if (index < 0)
2144 return len + index;
2145 return index;
2146 }
2147
2148 /* Convert a pair of index as normalize by JimRelToAbsIndex(),
2149 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2150 * for implementation of commands like [string range] and [lrange].
2151 *
2152 * The resulting range is guaranteed to address valid elements of
2153 * the structure. */
2154 static void JimRelToAbsRange(int len, int first, int last,
2155 int *firstPtr, int *lastPtr, int *rangeLenPtr)
2156 {
2157 int rangeLen;
2158
2159 if (first > last) {
2160 rangeLen = 0;
2161 } else {
2162 rangeLen = last-first+1;
2163 if (rangeLen) {
2164 if (first < 0) {
2165 rangeLen += first;
2166 first = 0;
2167 }
2168 if (last >= len) {
2169 rangeLen -= (last-(len-1));
2170 last = len-1;
2171 }
2172 }
2173 }
2174 if (rangeLen < 0) rangeLen = 0;
2175
2176 *firstPtr = first;
2177 *lastPtr = last;
2178 *rangeLenPtr = rangeLen;
2179 }
2180
2181 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2182 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2183 {
2184 int first, last;
2185 const char *str;
2186 int len, rangeLen;
2187
2188 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2189 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2190 return NULL;
2191 str = Jim_GetString(strObjPtr, &len);
2192 first = JimRelToAbsIndex(len, first);
2193 last = JimRelToAbsIndex(len, last);
2194 JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2195 return Jim_NewStringObj(interp, str+first, rangeLen);
2196 }
2197
2198 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2199 {
2200 char *buf = Jim_Alloc(strObjPtr->length+1);
2201 int i;
2202
2203 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2204 for (i = 0; i < strObjPtr->length; i++)
2205 buf[i] = tolower(buf[i]);
2206 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2207 }
2208
2209 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2210 {
2211 char *buf = Jim_Alloc(strObjPtr->length+1);
2212 int i;
2213
2214 memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2215 for (i = 0; i < strObjPtr->length; i++)
2216 buf[i] = toupper(buf[i]);
2217 return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2218 }
2219
2220 /* This is the core of the [format] command.
2221 * TODO: Lots of things work - via a hack
2222 * However, no format item can be >= JIM_MAX_FMT
2223 */
2224 #define JIM_MAX_FMT 2048
2225 static Jim_Obj *Jim_FormatString_Inner(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2226 int objc, Jim_Obj *const *objv, char *sprintf_buf)
2227 {
2228 const char *fmt, *_fmt;
2229 int fmtLen;
2230 Jim_Obj *resObjPtr;
2231
2232
2233 fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2234 _fmt = fmt;
2235 resObjPtr = Jim_NewStringObj(interp, "", 0);
2236 while (fmtLen) {
2237 const char *p = fmt;
2238 char spec[2], c;
2239 jim_wide wideValue;
2240 double doubleValue;
2241 /* we cheat and use Sprintf()! */
2242 char fmt_str[100];
2243 char *cp;
2244 int width;
2245 int ljust;
2246 int zpad;
2247 int spad;
2248 int altfm;
2249 int forceplus;
2250 int prec;
2251 int inprec;
2252 int haveprec;
2253 int accum;
2254
2255 while (*fmt != '%' && fmtLen) {
2256 fmt++; fmtLen--;
2257 }
2258 Jim_AppendString(interp, resObjPtr, p, fmt-p);
2259 if (fmtLen == 0)
2260 break;
2261 fmt++; fmtLen--; /* skip '%' */
2262 zpad = 0;
2263 spad = 0;
2264 width = -1;
2265 ljust = 0;
2266 altfm = 0;
2267 forceplus = 0;
2268 inprec = 0;
2269 haveprec = 0;
2270 prec = -1; /* not found yet */
2271 next_fmt:
2272 if( fmtLen <= 0 ){
2273 break;
2274 }
2275 switch( *fmt ){
2276 /* terminals */
2277 case 'b': /* binary - not all printfs() do this */
2278 case 's': /* string */
2279 case 'i': /* integer */
2280 case 'd': /* decimal */
2281 case 'x': /* hex */
2282 case 'X': /* CAP hex */
2283 case 'c': /* char */
2284 case 'o': /* octal */
2285 case 'u': /* unsigned */
2286 case 'f': /* float */
2287 break;
2288
2289 /* non-terminals */
2290 case '0': /* zero pad */
2291 zpad = 1;
2292 *fmt++; fmtLen--;
2293 goto next_fmt;
2294 break;
2295 case '+':
2296 forceplus = 1;
2297 *fmt++; fmtLen--;
2298 goto next_fmt;
2299 break;
2300 case ' ': /* sign space */
2301 spad = 1;
2302 *fmt++; fmtLen--;
2303 goto next_fmt;
2304 break;
2305 case '-':
2306 ljust = 1;
2307 *fmt++; fmtLen--;
2308 goto next_fmt;
2309 break;
2310 case '#':
2311 altfm = 1;
2312 *fmt++; fmtLen--;
2313 goto next_fmt;
2314
2315 case '.':
2316 inprec = 1;
2317 *fmt++; fmtLen--;
2318 goto next_fmt;
2319 break;
2320 case '1':
2321 case '2':
2322 case '3':
2323 case '4':
2324 case '5':
2325 case '6':
2326 case '7':
2327 case '8':
2328 case '9':
2329 accum = 0;
2330 while( isdigit(*fmt) && (fmtLen > 0) ){
2331 accum = (accum * 10) + (*fmt - '0');
2332 fmt++; fmtLen--;
2333 }
2334 if( inprec ){
2335 haveprec = 1;
2336 prec = accum;
2337 } else {
2338 width = accum;
2339 }
2340 goto next_fmt;
2341 case '*':
2342 /* suck up the next item as an integer */
2343 *fmt++; fmtLen--;
2344 objc--;
2345 if( objc <= 0 ){
2346 goto not_enough_args;
2347 }
2348 if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){
2349 Jim_FreeNewObj(interp, resObjPtr );
2350 return NULL;
2351 }
2352 if( inprec ){
2353 haveprec = 1;
2354 prec = wideValue;
2355 if( prec < 0 ){
2356 /* man 3 printf says */
2357 /* if prec is negative, it is zero */
2358 prec = 0;
2359 }
2360 } else {
2361 width = wideValue;
2362 if( width < 0 ){
2363 ljust = 1;
2364 width = -width;
2365 }
2366 }
2367 objv++;
2368 goto next_fmt;
2369 break;
2370 }
2371
2372
2373 if (*fmt != '%') {
2374 if (objc == 0) {
2375 not_enough_args:
2376 Jim_FreeNewObj(interp, resObjPtr);
2377 Jim_SetResultString(interp,
2378 "not enough arguments for all format specifiers", -1);
2379 return NULL;
2380 } else {
2381 objc--;
2382 }
2383 }
2384
2385 /*
2386 * Create the formatter
2387 * cause we cheat and use sprintf()
2388 */
2389 cp = fmt_str;
2390 *cp++ = '%';
2391 if( altfm ){
2392 *cp++ = '#';
2393 }
2394 if( forceplus ){
2395 *cp++ = '+';
2396 } else if( spad ){
2397 /* PLUS overrides */
2398 *cp++ = ' ';
2399 }
2400 if( ljust ){
2401 *cp++ = '-';
2402 }
2403 if( zpad ){
2404 *cp++ = '0';
2405 }
2406 if( width > 0 ){
2407 sprintf( cp, "%d", width );
2408 /* skip ahead */
2409 cp = strchr(cp,0);
2410 }
2411 /* did we find a period? */
2412 if( inprec ){
2413 /* then add it */
2414 *cp++ = '.';
2415 /* did something occur after the period? */
2416 if( haveprec ){
2417 sprintf( cp, "%d", prec );
2418 }
2419 cp = strchr(cp,0);
2420 }
2421 *cp = 0;
2422
2423 /* here we do the work */
2424 /* actually - we make sprintf() do it for us */
2425 switch(*fmt) {
2426 case 's':
2427 *cp++ = 's';
2428 *cp = 0;
2429 /* BUG: we do not handled embeded NULLs */
2430 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL ));
2431 break;
2432 case 'c':
2433 *cp++ = 'c';
2434 *cp = 0;
2435 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2436 Jim_FreeNewObj(interp, resObjPtr);
2437 return NULL;
2438 }
2439 c = (char) wideValue;
2440 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c );
2441 break;
2442 case 'f':
2443 case 'F':
2444 case 'g':
2445 case 'G':
2446 case 'e':
2447 case 'E':
2448 *cp++ = *fmt;
2449 *cp = 0;
2450 if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){
2451 Jim_FreeNewObj( interp, resObjPtr );
2452 return NULL;
2453 }
2454 snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue );
2455 break;
2456 case 'b':
2457 case 'd':
2458 case 'i':
2459 case 'u':
2460 case 'x':
2461 case 'X':
2462 /* jim widevaluse are 64bit */
2463 if( sizeof(jim_wide) == sizeof(long long) ){
2464 *cp++ = 'l';
2465 *cp++ = 'l';
2466 } else {
2467 *cp++ = 'l';
2468 }
2469 *cp++ = *fmt;
2470 *cp = 0;
2471 if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2472 Jim_FreeNewObj(interp, resObjPtr);
2473 return NULL;
2474 }
2475 snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue );
2476 break;
2477 case '%':
2478 sprintf_buf[0] = '%';
2479 sprintf_buf[1] = 0;
2480 objv--; /* undo the objv++ below */
2481 break;
2482 default:
2483 spec[0] = *fmt; spec[1] = '\0';
2484 Jim_FreeNewObj(interp, resObjPtr);
2485 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2486 Jim_AppendStrings(interp, Jim_GetResult(interp),
2487 "bad field specifier \"", spec, "\"", NULL);
2488 return NULL;
2489 }
2490 /* force terminate */
2491 #if 0
2492 printf("FMT was: %s\n", fmt_str );
2493 printf("RES was: |%s|\n", sprintf_buf );
2494 #endif
2495
2496 sprintf_buf[ JIM_MAX_FMT - 1] = 0;
2497 Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) );
2498 /* next obj */
2499 objv++;
2500 fmt++;
2501 fmtLen--;
2502 }
2503 return resObjPtr;
2504 }
2505
2506 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2507 int objc, Jim_Obj *const *objv)
2508 {
2509 char *sprintf_buf=malloc(JIM_MAX_FMT);
2510 Jim_Obj *t=Jim_FormatString_Inner(interp, fmtObjPtr, objc, objv, sprintf_buf);
2511 free(sprintf_buf);
2512 return t;
2513 }
2514
2515 /* -----------------------------------------------------------------------------
2516 * Compared String Object
2517 * ---------------------------------------------------------------------------*/
2518
2519 /* This is strange object that allows to compare a C literal string
2520 * with a Jim object in very short time if the same comparison is done
2521 * multiple times. For example every time the [if] command is executed,
2522 * Jim has to check if a given argument is "else". This comparions if
2523 * the code has no errors are true most of the times, so we can cache
2524 * inside the object the pointer of the string of the last matching
2525 * comparison. Because most C compilers perform literal sharing,
2526 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2527 * this works pretty well even if comparisons are at different places
2528 * inside the C code. */
2529
2530 static Jim_ObjType comparedStringObjType = {
2531 "compared-string",
2532 NULL,
2533 NULL,
2534 NULL,
2535 JIM_TYPE_REFERENCES,
2536 };
2537
2538 /* The only way this object is exposed to the API is via the following
2539 * function. Returns true if the string and the object string repr.
2540 * are the same, otherwise zero is returned.
2541 *
2542 * Note: this isn't binary safe, but it hardly needs to be.*/
2543 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2544 const char *str)
2545 {
2546 if (objPtr->typePtr == &comparedStringObjType &&
2547 objPtr->internalRep.ptr == str)
2548 return 1;
2549 else {
2550 const char *objStr = Jim_GetString(objPtr, NULL);
2551 if (strcmp(str, objStr) != 0) return 0;
2552 if (objPtr->typePtr != &comparedStringObjType) {
2553 Jim_FreeIntRep(interp, objPtr);
2554 objPtr->typePtr = &comparedStringObjType;
2555 }
2556 objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2557 return 1;
2558 }
2559 }
2560
2561 int qsortCompareStringPointers(const void *a, const void *b)
2562 {
2563 char * const *sa = (char * const *)a;
2564 char * const *sb = (char * const *)b;
2565 return strcmp(*sa, *sb);
2566 }
2567
2568 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2569 const char * const *tablePtr, int *indexPtr, const char *name, int flags)
2570 {
2571 const char * const *entryPtr = NULL;
2572 char **tablePtrSorted;
2573 int i, count = 0;
2574
2575 *indexPtr = -1;
2576 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2577 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2578 *indexPtr = i;
2579 return JIM_OK;
2580 }
2581 count++; /* If nothing matches, this will reach the len of tablePtr */
2582 }
2583 if (flags & JIM_ERRMSG) {
2584 if (name == NULL)
2585 name = "option";
2586 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2587 Jim_AppendStrings(interp, Jim_GetResult(interp),
2588 "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2589 NULL);
2590 tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2591 memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2592 qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2593 for (i = 0; i < count; i++) {
2594 if (i+1 == count && count > 1)
2595 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2596 Jim_AppendString(interp, Jim_GetResult(interp),
2597 tablePtrSorted[i], -1);
2598 if (i+1 != count)
2599 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2600 }
2601 Jim_Free(tablePtrSorted);
2602 }
2603 return JIM_ERR;
2604 }
2605
2606 int Jim_GetNvp(Jim_Interp *interp,
2607 Jim_Obj *objPtr,
2608 const Jim_Nvp *nvp_table,
2609 const Jim_Nvp ** result)
2610 {
2611 Jim_Nvp *n;
2612 int e;
2613
2614 e = Jim_Nvp_name2value_obj( interp, nvp_table, objPtr, &n );
2615 if( e == JIM_ERR ){
2616 return e;
2617 }
2618
2619 /* Success? found? */
2620 if( n->name ){
2621 /* remove const */
2622 *result = (Jim_Nvp *)n;
2623 return JIM_OK;
2624 } else {
2625 return JIM_ERR;
2626 }
2627 }
2628
2629 /* -----------------------------------------------------------------------------
2630 * Source Object
2631 *
2632 * This object is just a string from the language point of view, but
2633 * in the internal representation it contains the filename and line number
2634 * where this given token was read. This information is used by
2635 * Jim_EvalObj() if the object passed happens to be of type "source".
2636 *
2637 * This allows to propagate the information about line numbers and file
2638 * names and give error messages with absolute line numbers.
2639 *
2640 * Note that this object uses shared strings for filenames, and the
2641 * pointer to the filename together with the line number is taken into
2642 * the space for the "inline" internal represenation of the Jim_Object,
2643 * so there is almost memory zero-overhead.
2644 *
2645 * Also the object will be converted to something else if the given
2646 * token it represents in the source file is not something to be
2647 * evaluated (not a script), and will be specialized in some other way,
2648 * so the time overhead is alzo null.
2649 * ---------------------------------------------------------------------------*/
2650
2651 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2652 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2653
2654 static Jim_ObjType sourceObjType = {
2655 "source",
2656 FreeSourceInternalRep,
2657 DupSourceInternalRep,
2658 NULL,
2659 JIM_TYPE_REFERENCES,
2660 };
2661
2662 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2663 {
2664 Jim_ReleaseSharedString(interp,
2665 objPtr->internalRep.sourceValue.fileName);
2666 }
2667
2668 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2669 {
2670 dupPtr->internalRep.sourceValue.fileName =
2671 Jim_GetSharedString(interp,
2672 srcPtr->internalRep.sourceValue.fileName);
2673 dupPtr->internalRep.sourceValue.lineNumber =
2674 dupPtr->internalRep.sourceValue.lineNumber;
2675 dupPtr->typePtr = &sourceObjType;
2676 }
2677
2678 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2679 const char *fileName, int lineNumber)
2680 {
2681 if (Jim_IsShared(objPtr))
2682 Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2683 if (objPtr->typePtr != NULL)
2684 Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2685 objPtr->internalRep.sourceValue.fileName =
2686 Jim_GetSharedString(interp, fileName);
2687 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2688 objPtr->typePtr = &sourceObjType;
2689 }
2690
2691 /* -----------------------------------------------------------------------------
2692 * Script Object
2693 * ---------------------------------------------------------------------------*/
2694
2695 #define JIM_CMDSTRUCT_EXPAND -1
2696
2697 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2698 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2699 static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2700
2701 static Jim_ObjType scriptObjType = {
2702 "script",
2703 FreeScriptInternalRep,
2704 DupScriptInternalRep,
2705 NULL,
2706 JIM_TYPE_REFERENCES,
2707 };
2708
2709 /* The ScriptToken structure represents every token into a scriptObj.
2710 * Every token contains an associated Jim_Obj that can be specialized
2711 * by commands operating on it. */
2712 typedef struct ScriptToken {
2713 int type;
2714 Jim_Obj *objPtr;
2715 int linenr;
2716 } ScriptToken;
2717
2718 /* This is the script object internal representation. An array of
2719 * ScriptToken structures, with an associated command structure array.
2720 * The command structure is a pre-computed representation of the
2721 * command length and arguments structure as a simple liner array
2722 * of integers.
2723 *
2724 * For example the script:
2725 *
2726 * puts hello
2727 * set $i $x$y [foo]BAR
2728 *
2729 * will produce a ScriptObj with the following Tokens:
2730 *
2731 * ESC puts
2732 * SEP
2733 * ESC hello
2734 * EOL
2735 * ESC set
2736 * EOL
2737 * VAR i
2738 * SEP
2739 * VAR x
2740 * VAR y
2741 * SEP
2742 * CMD foo
2743 * ESC BAR
2744 * EOL
2745 *
2746 * This is a description of the tokens, separators, and of lines.
2747 * The command structure instead represents the number of arguments
2748 * of every command, followed by the tokens of which every argument
2749 * is composed. So for the example script, the cmdstruct array will
2750 * contain:
2751 *
2752 * 2 1 1 4 1 1 2 2
2753 *
2754 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2755 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2756 * composed of single tokens (1 1) and the last two of double tokens
2757 * (2 2).
2758 *
2759 * The precomputation of the command structure makes Jim_Eval() faster,
2760 * and simpler because there aren't dynamic lengths / allocations.
2761 *
2762 * -- {expand} handling --
2763 *
2764 * Expand is handled in a special way. When a command
2765 * contains at least an argument with the {expand} prefix,
2766 * the command structure presents a -1 before the integer
2767 * describing the number of arguments. This is used in order
2768 * to send the command exection to a different path in case
2769 * of {expand} and guarantee a fast path for the more common
2770 * case. Also, the integers describing the number of tokens
2771 * are expressed with negative sign, to allow for fast check
2772 * of what's an {expand}-prefixed argument and what not.
2773 *
2774 * For example the command:
2775 *
2776 * list {expand}{1 2}
2777 *
2778 * Will produce the following cmdstruct array:
2779 *
2780 * -1 2 1 -2
2781 *
2782 * -- the substFlags field of the structure --
2783 *
2784 * The scriptObj structure is used to represent both "script" objects
2785 * and "subst" objects. In the second case, the cmdStruct related
2786 * fields are not used at all, but there is an additional field used
2787 * that is 'substFlags': this represents the flags used to turn
2788 * the string into the intenral representation used to perform the
2789 * substitution. If this flags are not what the application requires
2790 * the scriptObj is created again. For example the script:
2791 *
2792 * subst -nocommands $string
2793 * subst -novariables $string
2794 *
2795 * Will recreate the internal representation of the $string object
2796 * two times.
2797 */
2798 typedef struct ScriptObj {
2799 int len; /* Length as number of tokens. */
2800 int commands; /* number of top-level commands in script. */
2801 ScriptToken *token; /* Tokens array. */
2802 int *cmdStruct; /* commands structure */
2803 int csLen; /* length of the cmdStruct array. */
2804 int substFlags; /* flags used for the compilation of "subst" objects */
2805 int inUse; /* Used to share a ScriptObj. Currently
2806 only used by Jim_EvalObj() as protection against
2807 shimmering of the currently evaluated object. */
2808 char *fileName;
2809 } ScriptObj;
2810
2811 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2812 {
2813 int i;
2814 struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2815
2816 script->inUse--;
2817 if (script->inUse != 0) return;
2818 for (i = 0; i < script->len; i++) {
2819 if (script->token[i].objPtr != NULL)
2820 Jim_DecrRefCount(interp, script->token[i].objPtr);
2821 }
2822 Jim_Free(script->token);
2823 Jim_Free(script->cmdStruct);
2824 Jim_Free(script->fileName);
2825 Jim_Free(script);
2826 }
2827
2828 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2829 {
2830 JIM_NOTUSED(interp);
2831 JIM_NOTUSED(srcPtr);
2832
2833 /* Just returns an simple string. */
2834 dupPtr->typePtr = NULL;
2835 }
2836
2837 /* Add a new token to the internal repr of a script object */
2838 static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2839 char *strtoken, int len, int type, char *filename, int linenr)
2840 {
2841 int prevtype;
2842 struct ScriptToken *token;
2843
2844 prevtype = (script->len == 0) ? JIM_TT_EOL : \
2845 script->token[script->len-1].type;
2846 /* Skip tokens without meaning, like words separators
2847 * following a word separator or an end of command and
2848 * so on. */
2849 if (prevtype == JIM_TT_EOL) {
2850 if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2851 Jim_Free(strtoken);
2852 return;
2853 }
2854 } else if (prevtype == JIM_TT_SEP) {
2855 if (type == JIM_TT_SEP) {
2856 Jim_Free(strtoken);
2857 return;
2858 } else if (type == JIM_TT_EOL) {
2859 /* If an EOL is following by a SEP, drop the previous
2860 * separator. */
2861 script->len--;
2862 Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2863 }
2864 } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2865 type == JIM_TT_ESC && len == 0)
2866 {
2867 /* Don't add empty tokens used in interpolation */
2868 Jim_Free(strtoken);
2869 return;
2870 }
2871 /* Make space for a new istruction */
2872 script->len++;
2873 script->token = Jim_Realloc(script->token,
2874 sizeof(ScriptToken)*script->len);
2875 /* Initialize the new token */
2876 token = script->token+(script->len-1);
2877 token->type = type;
2878 /* Every object is intially as a string, but the
2879 * internal type may be specialized during execution of the
2880 * script. */
2881 token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2882 /* To add source info to SEP and EOL tokens is useless because
2883 * they will never by called as arguments of Jim_EvalObj(). */
2884 if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2885 JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2886 Jim_IncrRefCount(token->objPtr);
2887 token->linenr = linenr;
2888 }
2889
2890 /* Add an integer into the command structure field of the script object. */
2891 static void ScriptObjAddInt(struct ScriptObj *script, int val)
2892 {
2893 script->csLen++;
2894 script->cmdStruct = Jim_Realloc(script->cmdStruct,
2895 sizeof(int)*script->csLen);
2896 script->cmdStruct[script->csLen-1] = val;
2897 }
2898
2899 /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2900 * of objPtr. Search nested script objects recursively. */
2901 static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2902 ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2903 {
2904 int i;
2905
2906 for (i = 0; i < script->len; i++) {
2907 if (script->token[i].objPtr != objPtr &&
2908 Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2909 return script->token[i].objPtr;
2910 }
2911 /* Enter recursively on scripts only if the object
2912 * is not the same as the one we are searching for
2913 * shared occurrences. */
2914 if (script->token[i].objPtr->typePtr == &scriptObjType &&
2915 script->token[i].objPtr != objPtr) {
2916 Jim_Obj *foundObjPtr;
2917
2918 ScriptObj *subScript =
2919 script->token[i].objPtr->internalRep.ptr;
2920 /* Don't recursively enter the script we are trying
2921 * to make shared to avoid circular references. */
2922 if (subScript == scriptBarrier) continue;
2923 if (subScript != script) {
2924 foundObjPtr =
2925 ScriptSearchLiteral(interp, subScript,
2926 scriptBarrier, objPtr);
2927 if (foundObjPtr != NULL)
2928 return foundObjPtr;
2929 }
2930 }
2931 }
2932 return NULL;
2933 }
2934
2935 /* Share literals of a script recursively sharing sub-scripts literals. */
2936 static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2937 ScriptObj *topLevelScript)
2938 {
2939 int i, j;
2940
2941 return;
2942 /* Try to share with toplevel object. */
2943 if (topLevelScript != NULL) {
2944 for (i = 0; i < script->len; i++) {
2945 Jim_Obj *foundObjPtr;
2946 char *str = script->token[i].objPtr->bytes;
2947
2948 if (script->token[i].objPtr->refCount != 1) continue;
2949 if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2950 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2951 foundObjPtr = ScriptSearchLiteral(interp,
2952 topLevelScript,
2953 script, /* barrier */
2954 script->token[i].objPtr);
2955 if (foundObjPtr != NULL) {
2956 Jim_IncrRefCount(foundObjPtr);
2957 Jim_DecrRefCount(interp,
2958 script->token[i].objPtr);
2959 script->token[i].objPtr = foundObjPtr;
2960 }
2961 }
2962 }
2963 /* Try to share locally */
2964 for (i = 0; i < script->len; i++) {
2965 char *str = script->token[i].objPtr->bytes;
2966
2967 if (script->token[i].objPtr->refCount != 1) continue;
2968 if (strchr(str, ' ') || strchr(str, '\n')) continue;
2969 for (j = 0; j < script->len; j++) {
2970 if (script->token[i].objPtr !=
2971 script->token[j].objPtr &&
2972 Jim_StringEqObj(script->token[i].objPtr,
2973 script->token[j].objPtr, 0))
2974 {
2975 Jim_IncrRefCount(script->token[j].objPtr);
2976 Jim_DecrRefCount(interp,
2977 script->token[i].objPtr);
2978 script->token[i].objPtr =
2979 script->token[j].objPtr;
2980 }
2981 }
2982 }
2983 }
2984
2985 /* This method takes the string representation of an object
2986 * as a Tcl script, and generates the pre-parsed internal representation
2987 * of the script. */
2988 int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2989 {
2990 int scriptTextLen;
2991 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2992 struct JimParserCtx parser;
2993 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2994 ScriptToken *token;
2995 int args, tokens, start, end, i;
2996 int initialLineNumber;
2997 int propagateSourceInfo = 0;
2998
2999 script->len = 0;
3000 script->csLen = 0;
3001 script->commands = 0;
3002 script->token = NULL;
3003 script->cmdStruct = NULL;
3004 script->inUse = 1;
3005 /* Try to get information about filename / line number */
3006 if (objPtr->typePtr == &sourceObjType) {
3007 script->fileName =
3008 Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
3009 initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
3010 propagateSourceInfo = 1;
3011 } else {
3012 script->fileName = Jim_StrDup("?");
3013 initialLineNumber = 1;
3014 }
3015
3016 JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
3017 while(!JimParserEof(&parser)) {
3018 char *token;
3019 int len, type, linenr;
3020
3021 JimParseScript(&parser);
3022 token = JimParserGetToken(&parser, &len, &type, &linenr);
3023 ScriptObjAddToken(interp, script, token, len, type,
3024 propagateSourceInfo ? script->fileName : NULL,
3025 linenr);
3026 }
3027 token = script->token;
3028
3029 /* Compute the command structure array
3030 * (see the ScriptObj struct definition for more info) */
3031 start = 0; /* Current command start token index */
3032 end = -1; /* Current command end token index */
3033 while (1) {
3034 int expand = 0; /* expand flag. set to 1 on {expand} form. */
3035 int interpolation = 0; /* set to 1 if there is at least one
3036 argument of the command obtained via
3037 interpolation of more tokens. */
3038 /* Search for the end of command, while
3039 * count the number of args. */
3040 start = ++end;
3041 if (start >= script->len) break;
3042 args = 1; /* Number of args in current command */
3043 while (token[end].type != JIM_TT_EOL) {
3044 if (end == 0 || token[end-1].type == JIM_TT_SEP ||
3045 token[end-1].type == JIM_TT_EOL)
3046 {
3047 if (token[end].type == JIM_TT_STR &&
3048 token[end+1].type != JIM_TT_SEP &&
3049 token[end+1].type != JIM_TT_EOL &&
3050 (!strcmp(token[end].objPtr->bytes, "expand") ||
3051 !strcmp(token[end].objPtr->bytes, "*")))
3052 expand++;
3053 }
3054 if (token[end].type == JIM_TT_SEP)
3055 args++;
3056 end++;
3057 }
3058 interpolation = !((end-start+1) == args*2);
3059 /* Add the 'number of arguments' info into cmdstruct.
3060 * Negative value if there is list expansion involved. */
3061 if (expand)
3062 ScriptObjAddInt(script, -1);
3063 ScriptObjAddInt(script, args);
3064 /* Now add info about the number of tokens. */
3065 tokens = 0; /* Number of tokens in current argument. */
3066 expand = 0;
3067 for (i = start; i <= end; i++) {
3068 if (token[i].type == JIM_TT_SEP ||
3069 token[i].type == JIM_TT_EOL)
3070 {
3071 if (tokens == 1 && expand)
3072 expand = 0;
3073 ScriptObjAddInt(script,
3074 expand ? -tokens : tokens);
3075
3076 expand = 0;
3077 tokens = 0;
3078 continue;
3079 } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
3080 (!strcmp(token[i].objPtr->bytes, "expand") ||
3081 !strcmp(token[i].objPtr->bytes, "*")))
3082 {
3083 expand++;
3084 }
3085 tokens++;
3086 }
3087 }
3088 /* Perform literal sharing, but only for objects that appear
3089 * to be scripts written as literals inside the source code,
3090 * and not computed at runtime. Literal sharing is a costly
3091 * operation that should be done only against objects that
3092 * are likely to require compilation only the first time, and
3093 * then are executed multiple times. */
3094 if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
3095 Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
3096 if (bodyObjPtr->typePtr == &scriptObjType) {
3097 ScriptObj *bodyScript =
3098 bodyObjPtr->internalRep.ptr;
3099 ScriptShareLiterals(interp, script, bodyScript);
3100 }
3101 } else if (propagateSourceInfo) {
3102 ScriptShareLiterals(interp, script, NULL);
3103 }
3104 /* Free the old internal rep and set the new one. */
3105 Jim_FreeIntRep(interp, objPtr);
3106 Jim_SetIntRepPtr(objPtr, script);
3107 objPtr->typePtr = &scriptObjType;
3108 return JIM_OK;
3109 }
3110
3111 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
3112 {
3113 if (objPtr->typePtr != &scriptObjType) {
3114 SetScriptFromAny(interp, objPtr);
3115 }
3116 return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
3117 }
3118
3119 /* -----------------------------------------------------------------------------
3120 * Commands
3121 * ---------------------------------------------------------------------------*/
3122
3123 /* Commands HashTable Type.
3124 *
3125 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
3126 static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
3127 {
3128 Jim_Cmd *cmdPtr = (void*) val;
3129
3130 if (cmdPtr->cmdProc == NULL) {
3131 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3132 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3133 if (cmdPtr->staticVars) {
3134 Jim_FreeHashTable(cmdPtr->staticVars);
3135 Jim_Free(cmdPtr->staticVars);
3136 }
3137 } else if (cmdPtr->delProc != NULL) {
3138 /* If it was a C coded command, call the delProc if any */
3139 cmdPtr->delProc(interp, cmdPtr->privData);
3140 }
3141 Jim_Free(val);
3142 }
3143
3144 static Jim_HashTableType JimCommandsHashTableType = {
3145 JimStringCopyHTHashFunction, /* hash function */
3146 JimStringCopyHTKeyDup, /* key dup */
3147 NULL, /* val dup */
3148 JimStringCopyHTKeyCompare, /* key compare */
3149 JimStringCopyHTKeyDestructor, /* key destructor */
3150 Jim_CommandsHT_ValDestructor /* val destructor */
3151 };
3152
3153 /* ------------------------- Commands related functions --------------------- */
3154
3155 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
3156 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
3157 {
3158 Jim_HashEntry *he;
3159 Jim_Cmd *cmdPtr;
3160
3161 he = Jim_FindHashEntry(&interp->commands, cmdName);
3162 if (he == NULL) { /* New command to create */
3163 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3164 cmdPtr->cmdProc = cmdProc;
3165 cmdPtr->privData = privData;
3166 cmdPtr->delProc = delProc;
3167 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3168 } else {
3169 Jim_InterpIncrProcEpoch(interp);
3170 /* Free the arglist/body objects if it was a Tcl procedure */
3171 cmdPtr = he->val;
3172 if (cmdPtr->cmdProc == NULL) {
3173 Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
3174 Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
3175 if (cmdPtr->staticVars) {
3176 Jim_FreeHashTable(cmdPtr->staticVars);
3177 Jim_Free(cmdPtr->staticVars);
3178 }
3179 cmdPtr->staticVars = NULL;
3180 } else if (cmdPtr->delProc != NULL) {
3181 /* If it was a C coded command, call the delProc if any */
3182 cmdPtr->delProc(interp, cmdPtr->privData);
3183 }
3184 cmdPtr->cmdProc = cmdProc;
3185 cmdPtr->privData = privData;
3186 }
3187 /* There is no need to increment the 'proc epoch' because
3188 * creation of a new procedure can never affect existing
3189 * cached commands. We don't do negative caching. */
3190 return JIM_OK;
3191 }
3192
3193 int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
3194 Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
3195 int arityMin, int arityMax)
3196 {
3197 Jim_Cmd *cmdPtr;
3198
3199 cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
3200 cmdPtr->cmdProc = NULL; /* Not a C coded command */
3201 cmdPtr->argListObjPtr = argListObjPtr;
3202 cmdPtr->bodyObjPtr = bodyObjPtr;
3203 Jim_IncrRefCount(argListObjPtr);
3204 Jim_IncrRefCount(bodyObjPtr);
3205 cmdPtr->arityMin = arityMin;
3206 cmdPtr->arityMax = arityMax;
3207 cmdPtr->staticVars = NULL;
3208
3209 /* Create the statics hash table. */
3210 if (staticsListObjPtr) {
3211 int len, i;
3212
3213 Jim_ListLength(interp, staticsListObjPtr, &len);
3214 if (len != 0) {
3215 cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
3216 Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
3217 interp);
3218 for (i = 0; i < len; i++) {
3219 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
3220 Jim_Var *varPtr;
3221 int subLen;
3222
3223 Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
3224 /* Check if it's composed of two elements. */
3225 Jim_ListLength(interp, objPtr, &subLen);
3226 if (subLen == 1 || subLen == 2) {
3227 /* Try to get the variable value from the current
3228 * environment. */
3229 Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
3230 if (subLen == 1) {
3231 initObjPtr = Jim_GetVariable(interp, nameObjPtr,
3232 JIM_NONE);
3233 if (initObjPtr == NULL) {
3234 Jim_SetResult(interp,
3235 Jim_NewEmptyStringObj(interp));
3236 Jim_AppendStrings(interp, Jim_GetResult(interp),
3237 "variable for initialization of static \"",
3238 Jim_GetString(nameObjPtr, NULL),
3239 "\" not found in the local context",
3240 NULL);
3241 goto err;
3242 }
3243 } else {
3244 Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
3245 }
3246 varPtr = Jim_Alloc(sizeof(*varPtr));
3247 varPtr->objPtr = initObjPtr;
3248 Jim_IncrRefCount(initObjPtr);
3249 varPtr->linkFramePtr = NULL;
3250 if (Jim_AddHashEntry(cmdPtr->staticVars,
3251 Jim_GetString(nameObjPtr, NULL),
3252 varPtr) != JIM_OK)
3253 {
3254 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3255 Jim_AppendStrings(interp, Jim_GetResult(interp),
3256 "static variable name \"",
3257 Jim_GetString(objPtr, NULL), "\"",
3258 " duplicated in statics list", NULL);
3259 Jim_DecrRefCount(interp, initObjPtr);
3260 Jim_Free(varPtr);
3261 goto err;
3262 }
3263 } else {
3264 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3265 Jim_AppendStrings(interp, Jim_GetResult(interp),
3266 "too many fields in static specifier \"",
3267 objPtr, "\"", NULL);
3268 goto err;
3269 }
3270 }
3271 }
3272 }
3273
3274 /* Add the new command */
3275
3276 /* it may already exist, so we try to delete the old one */
3277 if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
3278 /* There was an old procedure with the same name, this requires
3279 * a 'proc epoch' update. */
3280 Jim_InterpIncrProcEpoch(interp);
3281 }
3282 /* If a procedure with the same name didn't existed there is no need
3283 * to increment the 'proc epoch' because creation of a new procedure
3284 * can never affect existing cached commands. We don't do
3285 * negative caching. */
3286 Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
3287 return JIM_OK;
3288
3289 err:
3290 Jim_FreeHashTable(cmdPtr->staticVars);
3291 Jim_Free(cmdPtr->staticVars);
3292 Jim_DecrRefCount(interp, argListObjPtr);
3293 Jim_DecrRefCount(interp, bodyObjPtr);
3294 Jim_Free(cmdPtr);
3295 return JIM_ERR;
3296 }
3297
3298 int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
3299 {
3300 if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
3301 return JIM_ERR;
3302 Jim_InterpIncrProcEpoch(interp);
3303 return JIM_OK;
3304 }
3305
3306 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
3307 const char *newName)
3308 {
3309 Jim_Cmd *cmdPtr;
3310 Jim_HashEntry *he;
3311 Jim_Cmd *copyCmdPtr;
3312
3313 if (newName[0] == '\0') /* Delete! */
3314 return Jim_DeleteCommand(interp, oldName);
3315 /* Rename */
3316 he = Jim_FindHashEntry(&interp->commands, oldName);
3317 if (he == NULL)
3318 return JIM_ERR; /* Invalid command name */
3319 cmdPtr = he->val;
3320 copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
3321 *copyCmdPtr = *cmdPtr;
3322 /* In order to avoid that a procedure will get arglist/body/statics
3323 * freed by the hash table methods, fake a C-coded command
3324 * setting cmdPtr->cmdProc as not NULL */
3325 cmdPtr->cmdProc = (void*)1;
3326 /* Also make sure delProc is NULL. */
3327 cmdPtr->delProc = NULL;
3328 /* Destroy the old command, and make sure the new is freed
3329 * as well. */
3330 Jim_DeleteHashEntry(&interp->commands, oldName);
3331 Jim_DeleteHashEntry(&interp->commands, newName);
3332 /* Now the new command. We are sure it can't fail because
3333 * the target name was already freed. */
3334 Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
3335 /* Increment the epoch */
3336 Jim_InterpIncrProcEpoch(interp);
3337 return JIM_OK;
3338 }
3339
3340 /* -----------------------------------------------------------------------------
3341 * Command object
3342 * ---------------------------------------------------------------------------*/
3343
3344 static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3345
3346 static Jim_ObjType commandObjType = {
3347 "command",
3348 NULL,
3349 NULL,
3350 NULL,
3351 JIM_TYPE_REFERENCES,
3352 };
3353
3354 int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3355 {
3356 Jim_HashEntry *he;
3357 const char *cmdName;
3358
3359 /* Get the string representation */
3360 cmdName = Jim_GetString(objPtr, NULL);
3361 /* Lookup this name into the commands hash table */
3362 he = Jim_FindHashEntry(&interp->commands, cmdName);
3363 if (he == NULL)
3364 return JIM_ERR;
3365
3366 /* Free the old internal repr and set the new one. */
3367 Jim_FreeIntRep(interp, objPtr);
3368 objPtr->typePtr = &commandObjType;
3369 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3370 objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3371 return JIM_OK;
3372 }
3373
3374 /* This function returns the command structure for the command name
3375 * stored in objPtr. It tries to specialize the objPtr to contain
3376 * a cached info instead to perform the lookup into the hash table
3377 * every time. The information cached may not be uptodate, in such
3378 * a case the lookup is performed and the cache updated. */
3379 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3380 {
3381 if ((objPtr->typePtr != &commandObjType ||
3382 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3383 SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3384 if (flags & JIM_ERRMSG) {
3385 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3386 Jim_AppendStrings(interp, Jim_GetResult(interp),
3387 "invalid command name \"", objPtr->bytes, "\"",
3388 NULL);
3389 }
3390 return NULL;
3391 }
3392 return objPtr->internalRep.cmdValue.cmdPtr;
3393 }
3394
3395 /* -----------------------------------------------------------------------------
3396 * Variables
3397 * ---------------------------------------------------------------------------*/
3398
3399 /* Variables HashTable Type.
3400 *
3401 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3402 static void JimVariablesHTValDestructor(void *interp, void *val)
3403 {
3404 Jim_Var *varPtr = (void*) val;
3405
3406 Jim_DecrRefCount(interp, varPtr->objPtr);
3407 Jim_Free(val);
3408 }
3409
3410 static Jim_HashTableType JimVariablesHashTableType = {
3411 JimStringCopyHTHashFunction, /* hash function */
3412 JimStringCopyHTKeyDup, /* key dup */
3413 NULL, /* val dup */
3414 JimStringCopyHTKeyCompare, /* key compare */
3415 JimStringCopyHTKeyDestructor, /* key destructor */
3416 JimVariablesHTValDestructor /* val destructor */
3417 };
3418
3419 /* -----------------------------------------------------------------------------
3420 * Variable object
3421 * ---------------------------------------------------------------------------*/
3422
3423 #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3424
3425 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3426
3427 static Jim_ObjType variableObjType = {
3428 "variable",
3429 NULL,
3430 NULL,
3431 NULL,
3432 JIM_TYPE_REFERENCES,
3433 };
3434
3435 /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3436 * is in the form "varname(key)". */
3437 static int Jim_NameIsDictSugar(const char *str, int len)
3438 {
3439 if (len == -1)
3440 len = strlen(str);
3441 if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3442 return 1;
3443 return 0;
3444 }
3445
3446 /* This method should be called only by the variable API.
3447 * It returns JIM_OK on success (variable already exists),
3448 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3449 * a variable name, but syntax glue for [dict] i.e. the last
3450 * character is ')' */
3451 int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3452 {
3453 Jim_HashEntry *he;
3454 const char *varName;
3455 int len;
3456
3457 /* Check if the object is already an uptodate variable */
3458 if (objPtr->typePtr == &variableObjType &&
3459 objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3460 return JIM_OK; /* nothing to do */
3461 /* Get the string representation */
3462 varName = Jim_GetString(objPtr, &len);
3463 /* Make sure it's not syntax glue to get/set dict. */
3464 if (Jim_NameIsDictSugar(varName, len))
3465 return JIM_DICT_SUGAR;
3466 /* Lookup this name into the variables hash table */
3467 he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3468 if (he == NULL) {
3469 /* Try with static vars. */
3470 if (interp->framePtr->staticVars == NULL)
3471 return JIM_ERR;
3472 if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3473 return JIM_ERR;
3474 }
3475 /* Free the old internal repr and set the new one. */
3476 Jim_FreeIntRep(interp, objPtr);
3477 objPtr->typePtr = &variableObjType;
3478 objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3479 objPtr->internalRep.varValue.varPtr = (void*)he->val;
3480 return JIM_OK;
3481 }
3482
3483 /* -------------------- Variables related functions ------------------------- */
3484 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3485 Jim_Obj *valObjPtr);
3486 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3487
3488 /* For now that's dummy. Variables lookup should be optimized
3489 * in many ways, with caching of lookups, and possibly with
3490 * a table of pre-allocated vars in every CallFrame for local vars.
3491 * All the caching should also have an 'epoch' mechanism similar
3492 * to the one used by Tcl for procedures lookup caching. */
3493
3494 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3495 {
3496 const char *name;
3497 Jim_Var *var;
3498 int err;
3499
3500 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3501 /* Check for [dict] syntax sugar. */
3502 if (err == JIM_DICT_SUGAR)
3503 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3504 /* New variable to create */
3505 name = Jim_GetString(nameObjPtr, NULL);
3506
3507 var = Jim_Alloc(sizeof(*var));
3508 var->objPtr = valObjPtr;
3509 Jim_IncrRefCount(valObjPtr);
3510 var->linkFramePtr = NULL;
3511 /* Insert the new variable */
3512 Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3513 /* Make the object int rep a variable */
3514 Jim_FreeIntRep(interp, nameObjPtr);
3515 nameObjPtr->typePtr = &variableObjType;
3516 nameObjPtr->internalRep.varValue.callFrameId =
3517 interp->framePtr->id;
3518 nameObjPtr->internalRep.varValue.varPtr = var;
3519 } else {
3520 var = nameObjPtr->internalRep.varValue.varPtr;
3521 if (var->linkFramePtr == NULL) {
3522 Jim_IncrRefCount(valObjPtr);
3523 Jim_DecrRefCount(interp, var->objPtr);
3524 var->objPtr = valObjPtr;
3525 } else { /* Else handle the link */
3526 Jim_CallFrame *savedCallFrame;
3527
3528 savedCallFrame = interp->framePtr;
3529 interp->framePtr = var->linkFramePtr;
3530 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3531 interp->framePtr = savedCallFrame;
3532 if (err != JIM_OK)
3533 return err;
3534 }
3535 }
3536 return JIM_OK;
3537 }
3538
3539 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3540 {
3541 Jim_Obj *nameObjPtr;
3542 int result;
3543
3544 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3545 Jim_IncrRefCount(nameObjPtr);
3546 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3547 Jim_DecrRefCount(interp, nameObjPtr);
3548 return result;
3549 }
3550
3551 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3552 {
3553 Jim_CallFrame *savedFramePtr;
3554 int result;
3555
3556 savedFramePtr = interp->framePtr;
3557 interp->framePtr = interp->topFramePtr;
3558 result = Jim_SetVariableStr(interp, name, objPtr);
3559 interp->framePtr = savedFramePtr;
3560 return result;
3561 }
3562
3563 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3564 {
3565 Jim_Obj *nameObjPtr, *valObjPtr;
3566 int result;
3567
3568 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3569 valObjPtr = Jim_NewStringObj(interp, val, -1);
3570 Jim_IncrRefCount(nameObjPtr);
3571 Jim_IncrRefCount(valObjPtr);
3572 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3573 Jim_DecrRefCount(interp, nameObjPtr);
3574 Jim_DecrRefCount(interp, valObjPtr);
3575 return result;
3576 }
3577
3578 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3579 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3580 {
3581 const char *varName;
3582 int len;
3583
3584 /* Check for cycles. */
3585 if (interp->framePtr == targetCallFrame) {
3586 Jim_Obj *objPtr = targetNameObjPtr;
3587 Jim_Var *varPtr;
3588 /* Cycles are only possible with 'uplevel 0' */
3589 while(1) {
3590 if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3591 Jim_SetResultString(interp,
3592 "can't upvar from variable to itself", -1);
3593 return JIM_ERR;
3594 }
3595 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3596 break;
3597 varPtr = objPtr->internalRep.varValue.varPtr;
3598 if (varPtr->linkFramePtr != targetCallFrame) break;
3599 objPtr = varPtr->objPtr;
3600 }
3601 }
3602 varName = Jim_GetString(nameObjPtr, &len);
3603 if (Jim_NameIsDictSugar(varName, len)) {
3604 Jim_SetResultString(interp,
3605 "Dict key syntax invalid as link source", -1);
3606 return JIM_ERR;
3607 }
3608 /* Perform the binding */
3609 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3610 /* We are now sure 'nameObjPtr' type is variableObjType */
3611 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3612 return JIM_OK;
3613 }
3614
3615 /* Return the Jim_Obj pointer associated with a variable name,
3616 * or NULL if the variable was not found in the current context.
3617 * The same optimization discussed in the comment to the
3618 * 'SetVariable' function should apply here. */
3619 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3620 {
3621 int err;
3622
3623 /* All the rest is handled here */
3624 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3625 /* Check for [dict] syntax sugar. */
3626 if (err == JIM_DICT_SUGAR)
3627 return JimDictSugarGet(interp, nameObjPtr);
3628 if (flags & JIM_ERRMSG) {
3629 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3630 Jim_AppendStrings(interp, Jim_GetResult(interp),
3631 "can't read \"", nameObjPtr->bytes,
3632 "\": no such variable", NULL);
3633 }
3634 return NULL;
3635 } else {
3636 Jim_Var *varPtr;
3637 Jim_Obj *objPtr;
3638 Jim_CallFrame *savedCallFrame;
3639
3640 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3641 if (varPtr->linkFramePtr == NULL)
3642 return varPtr->objPtr;
3643 /* The variable is a link? Resolve it. */
3644 savedCallFrame = interp->framePtr;
3645 interp->framePtr = varPtr->linkFramePtr;
3646 objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3647 if (objPtr == NULL && flags & JIM_ERRMSG) {
3648 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3649 Jim_AppendStrings(interp, Jim_GetResult(interp),
3650 "can't read \"", nameObjPtr->bytes,
3651 "\": no such variable", NULL);
3652 }
3653 interp->framePtr = savedCallFrame;
3654 return objPtr;
3655 }
3656 }
3657
3658 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3659 int flags)
3660 {
3661 Jim_CallFrame *savedFramePtr;
3662 Jim_Obj *objPtr;
3663
3664 savedFramePtr = interp->framePtr;
3665 interp->framePtr = interp->topFramePtr;
3666 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3667 interp->framePtr = savedFramePtr;
3668
3669 return objPtr;
3670 }
3671
3672 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3673 {
3674 Jim_Obj *nameObjPtr, *varObjPtr;
3675
3676 nameObjPtr = Jim_NewStringObj(interp, name, -1);
3677 Jim_IncrRefCount(nameObjPtr);
3678 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3679 Jim_DecrRefCount(interp, nameObjPtr);
3680 return varObjPtr;
3681 }
3682
3683 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3684 int flags)
3685 {
3686 Jim_CallFrame *savedFramePtr;
3687 Jim_Obj *objPtr;
3688
3689 savedFramePtr = interp->framePtr;
3690 interp->framePtr = interp->topFramePtr;
3691 objPtr = Jim_GetVariableStr(interp, name, flags);
3692 interp->framePtr = savedFramePtr;
3693
3694 return objPtr;
3695 }
3696
3697 /* Unset a variable.
3698 * Note: On success unset invalidates all the variable objects created
3699 * in the current call frame incrementing. */
3700 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3701 {
3702 const char *name;
3703 Jim_Var *varPtr;
3704 int err;
3705
3706 if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3707 /* Check for [dict] syntax sugar. */
3708 if (err == JIM_DICT_SUGAR)
3709 return JimDictSugarSet(interp, nameObjPtr, NULL);
3710 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3711 Jim_AppendStrings(interp, Jim_GetResult(interp),
3712 "can't unset \"", nameObjPtr->bytes,
3713 "\": no such variable", NULL);
3714 return JIM_ERR; /* var not found */
3715 }
3716 varPtr = nameObjPtr->internalRep.varValue.varPtr;
3717 /* If it's a link call UnsetVariable recursively */
3718 if (varPtr->linkFramePtr) {
3719 int retval;
3720
3721 Jim_CallFrame *savedCallFrame;
3722
3723 savedCallFrame = interp->framePtr;