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