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