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