OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [net/] [athttpd/] [current/] [src/] [jim.c] - Blame information for rev 867

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 786 skrzyp
/* Jim - A small embeddable Tcl interpreter
2
 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
3
 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
4
 *
5
 * $Id: jim.c,v 1.170 2006/11/06 21:48:57 antirez Exp $
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * A copy of the license is also included in the source distribution
14
 * of Jim, as a TXT file name called LICENSE.
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
 
23
#define __JIM_CORE__
24
#define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
25
 
26
#include <pkgconf/athttpd.h>
27
 
28
#ifndef JIM_ANSIC
29
#define JIM_DYNLIB      /* Dynamic library support for UNIX and WIN32 */
30
#endif /* JIM_ANSIC */
31
 
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include <stdarg.h>
36
#include <ctype.h>
37
#include <limits.h>
38
#include <assert.h>
39
#include <errno.h>
40
#include <time.h>
41
 
42
/* Include the platform dependent libraries for
43
 * dynamic loading of libraries. */
44
#ifdef JIM_DYNLIB
45
#if defined(_WIN32) || defined(WIN32)
46
#ifndef WIN32
47
#define WIN32 1
48
#endif
49
#define STRICT
50
#define WIN32_LEAN_AND_MEAN
51
#include <windows.h>
52
#if _MSC_VER >= 1000
53
#pragma warning(disable:4146)
54
#endif /* _MSC_VER */
55
#else
56
#include <dlfcn.h>
57
#endif /* WIN32 */
58
#endif /* JIM_DYNLIB */
59
 
60
#include <cyg/athttpd/jim.h>
61
 
62
#ifdef HAVE_BACKTRACE
63
#include <execinfo.h>
64
#endif
65
 
66
/* -----------------------------------------------------------------------------
67
 * Global variables
68
 * ---------------------------------------------------------------------------*/
69
 
70
/* A shared empty string for the objects string representation.
71
 * Jim_InvalidateStringRep knows about it and don't try to free. */
72
static char *JimEmptyStringRep = (char*) "";
73
 
74
/* -----------------------------------------------------------------------------
75
 * Required prototypes of not exported functions
76
 * ---------------------------------------------------------------------------*/
77
static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
78
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
79
static void JimRegisterCoreApi(Jim_Interp *interp);
80
 
81
static Jim_HashTableType JimVariablesHashTableType;
82
 
83
/* -----------------------------------------------------------------------------
84
 * Utility functions
85
 * ---------------------------------------------------------------------------*/
86
 
87
/*
88
 * Convert a string to a jim_wide INTEGER.
89
 * This function originates from BSD.
90
 *
91
 * Ignores `locale' stuff.  Assumes that the upper and lower case
92
 * alphabets and digits are each contiguous.
93
 */
94
#ifdef HAVE_LONG_LONG
95
#define JimIsAscii(c) (((c) & ~0x7f) == 0)
96
static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
97
{
98
    register const char *s;
99
    register unsigned jim_wide acc;
100
    register unsigned char c;
101
    register unsigned jim_wide qbase, cutoff;
102
    register int neg, any, cutlim;
103
 
104
    /*
105
     * Skip white space and pick up leading +/- sign if any.
106
     * If base is 0, allow 0x for hex and 0 for octal, else
107
     * assume decimal; if base is already 16, allow 0x.
108
     */
109
    s = nptr;
110
    do {
111
        c = *s++;
112
    } while (isspace(c));
113
    if (c == '-') {
114
        neg = 1;
115
        c = *s++;
116
    } else {
117
        neg = 0;
118
        if (c == '+')
119
            c = *s++;
120
    }
121
    if ((base == 0 || base == 16) &&
122
        c == '0' && (*s == 'x' || *s == 'X')) {
123
        c = s[1];
124
        s += 2;
125
        base = 16;
126
    }
127
    if (base == 0)
128
        base = c == '0' ? 8 : 10;
129
 
130
    /*
131
     * Compute the cutoff value between legal numbers and illegal
132
     * numbers.  That is the largest legal value, divided by the
133
     * base.  An input number that is greater than this value, if
134
     * followed by a legal input character, is too big.  One that
135
     * is equal to this value may be valid or not; the limit
136
     * between valid and invalid numbers is then based on the last
137
     * digit.  For instance, if the range for quads is
138
     * [-9223372036854775808..9223372036854775807] and the input base
139
     * is 10, cutoff will be set to 922337203685477580 and cutlim to
140
     * either 7 (neg==0) or 8 (neg==1), meaning that if we have
141
     * accumulated a value > 922337203685477580, or equal but the
142
     * next digit is > 7 (or 8), the number is too big, and we will
143
     * return a range error.
144
     *
145
     * Set any if any `digits' consumed; make it negative to indicate
146
     * overflow.
147
     */
148
    qbase = (unsigned)base;
149
    cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
150
        : LLONG_MAX;
151
    cutlim = (int)(cutoff % qbase);
152
    cutoff /= qbase;
153
    for (acc = 0, any = 0;; c = *s++) {
154
        if (!JimIsAscii(c))
155
            break;
156
        if (isdigit(c))
157
            c -= '0';
158
        else if (isalpha(c))
159
            c -= isupper(c) ? 'A' - 10 : 'a' - 10;
160
        else
161
            break;
162
        if (c >= base)
163
            break;
164
        if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
165
            any = -1;
166
        else {
167
            any = 1;
168
            acc *= qbase;
169
            acc += c;
170
        }
171
    }
172
    if (any < 0) {
173
        acc = neg ? LLONG_MIN : LLONG_MAX;
174
        errno = ERANGE;
175
    } else if (neg)
176
        acc = -acc;
177
    if (endptr != 0)
178
        *endptr = (char *)(any ? s - 1 : nptr);
179
    return (acc);
180
}
181
#endif
182
 
183
/* Glob-style pattern matching. */
184
static int JimStringMatch(const char *pattern, int patternLen,
185
        const char *string, int stringLen, int nocase)
186
{
187
    while(patternLen) {
188
        switch(pattern[0]) {
189
        case '*':
190
            while (pattern[1] == '*') {
191
                pattern++;
192
                patternLen--;
193
            }
194
            if (patternLen == 1)
195
                return 1; /* match */
196
            while(stringLen) {
197
                if (JimStringMatch(pattern+1, patternLen-1,
198
                            string, stringLen, nocase))
199
                    return 1; /* match */
200
                string++;
201
                stringLen--;
202
            }
203
            return 0; /* no match */
204
            break;
205
        case '?':
206
            if (stringLen == 0)
207
                return 0; /* no match */
208
            string++;
209
            stringLen--;
210
            break;
211
        case '[':
212
        {
213
            int not, match;
214
 
215
            pattern++;
216
            patternLen--;
217
            not = pattern[0] == '^';
218
            if (not) {
219
                pattern++;
220
                patternLen--;
221
            }
222
            match = 0;
223
            while(1) {
224
                if (pattern[0] == '\\') {
225
                    pattern++;
226
                    patternLen--;
227
                    if (pattern[0] == string[0])
228
                        match = 1;
229
                } else if (pattern[0] == ']') {
230
                    break;
231
                } else if (patternLen == 0) {
232
                    pattern--;
233
                    patternLen++;
234
                    break;
235
                } else if (pattern[1] == '-' && patternLen >= 3) {
236
                    int start = pattern[0];
237
                    int end = pattern[2];
238
                    int c = string[0];
239
                    if (start > end) {
240
                        int t = start;
241
                        start = end;
242
                        end = t;
243
                    }
244
                    if (nocase) {
245
                        start = tolower(start);
246
                        end = tolower(end);
247
                        c = tolower(c);
248
                    }
249
                    pattern += 2;
250
                    patternLen -= 2;
251
                    if (c >= start && c <= end)
252
                        match = 1;
253
                } else {
254
                    if (!nocase) {
255
                        if (pattern[0] == string[0])
256
                            match = 1;
257
                    } else {
258
                        if (tolower((int)pattern[0]) == tolower((int)string[0]))
259
                            match = 1;
260
                    }
261
                }
262
                pattern++;
263
                patternLen--;
264
            }
265
            if (not)
266
                match = !match;
267
            if (!match)
268
                return 0; /* no match */
269
            string++;
270
            stringLen--;
271
            break;
272
        }
273
        case '\\':
274
            if (patternLen >= 2) {
275
                pattern++;
276
                patternLen--;
277
            }
278
            /* fall through */
279
        default:
280
            if (!nocase) {
281
                if (pattern[0] != string[0])
282
                    return 0; /* no match */
283
            } else {
284
                if (tolower((int)pattern[0]) != tolower((int)string[0]))
285
                    return 0; /* no match */
286
            }
287
            string++;
288
            stringLen--;
289
            break;
290
        }
291
        pattern++;
292
        patternLen--;
293
        if (stringLen == 0) {
294
            while(*pattern == '*') {
295
                pattern++;
296
                patternLen--;
297
            }
298
            break;
299
        }
300
    }
301
    if (patternLen == 0 && stringLen == 0)
302
        return 1;
303
    return 0;
304
}
305
 
306
int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
307
        int nocase)
308
{
309
    unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
310
 
311
    if (nocase == 0) {
312
        while(l1 && l2) {
313
            if (*u1 != *u2)
314
                return (int)*u1-*u2;
315
            u1++; u2++; l1--; l2--;
316
        }
317
        if (!l1 && !l2) return 0;
318
        return l1-l2;
319
    } else {
320
        while(l1 && l2) {
321
            if (tolower((int)*u1) != tolower((int)*u2))
322
                return tolower((int)*u1)-tolower((int)*u2);
323
            u1++; u2++; l1--; l2--;
324
        }
325
        if (!l1 && !l2) return 0;
326
        return l1-l2;
327
    }
328
}
329
 
330
/* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
331
 * The index of the first occurrence of s1 in s2 is returned.
332
 * If s1 is not found inside s2, -1 is returned. */
333
int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
334
{
335
    int i;
336
 
337
    if (!l1 || !l2 || l1 > l2) return -1;
338
    if (index < 0) index = 0;
339
    s2 += index;
340
    for (i = index; i <= l2-l1; i++) {
341
        if (memcmp(s2, s1, l1) == 0)
342
            return i;
343
        s2++;
344
    }
345
    return -1;
346
}
347
 
348
int Jim_WideToString(char *buf, jim_wide wideValue)
349
{
350
    const char *fmt = "%" JIM_WIDE_MODIFIER;
351
    return sprintf(buf, fmt, wideValue);
352
}
353
 
354
int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
355
{
356
    char *endptr;
357
 
358
#ifdef HAVE_LONG_LONG
359
    *widePtr = JimStrtoll(str, &endptr, base);
360
#else
361
    *widePtr = strtol(str, &endptr, base);
362
#endif
363
    if (str[0] == '\0')
364
        return JIM_ERR;
365
    if (endptr[0] != '\0') {
366
        while(*endptr) {
367
            if (!isspace((int)*endptr))
368
                return JIM_ERR;
369
            endptr++;
370
        }
371
    }
372
    return JIM_OK;
373
}
374
 
375
int Jim_StringToIndex(const char *str, int *intPtr)
376
{
377
    char *endptr;
378
 
379
    *intPtr = strtol(str, &endptr, 10);
380
    if (str[0] == '\0')
381
        return JIM_ERR;
382
    if (endptr[0] != '\0') {
383
        while(*endptr) {
384
            if (!isspace((int)*endptr))
385
                return JIM_ERR;
386
            endptr++;
387
        }
388
    }
389
    return JIM_OK;
390
}
391
 
392
/* The string representation of references has two features in order
393
 * to make the GC faster. The first is that every reference starts
394
 * with a non common character '~', in order to make the string matching
395
 * fater. The second is that the reference string rep his 32 characters
396
 * in length, this allows to avoid to check every object with a string
397
 * repr < 32, and usually there are many of this objects. */
398
 
399
#define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
400
 
401
static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
402
{
403
    const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
404
    sprintf(buf, fmt, refPtr->tag, id);
405
    return JIM_REFERENCE_SPACE;
406
}
407
 
408
int Jim_DoubleToString(char *buf, double doubleValue)
409
{
410
    char *s;
411
    int len;
412
 
413
    len = sprintf(buf, "%.17g", doubleValue);
414
    s = buf;
415
    while(*s) {
416
        if (*s == '.') return len;
417
        s++;
418
    }
419
    /* Add a final ".0" if it's a number. But not
420
     * for NaN or InF */
421
    if (isdigit((int)buf[0])
422
        || ((buf[0] == '-' || buf[0] == '+')
423
            && isdigit((int)buf[1]))) {
424
        s[0] = '.';
425
        s[1] = '0';
426
        s[2] = '\0';
427
        return len+2;
428
    }
429
    return len;
430
}
431
 
432
int Jim_StringToDouble(const char *str, double *doublePtr)
433
{
434
    char *endptr;
435
 
436
    *doublePtr = strtod(str, &endptr);
437
    if (str[0] == '\0' || endptr[0] != '\0')
438
        return JIM_ERR;
439
    return JIM_OK;
440
}
441
 
442
static jim_wide JimPowWide(jim_wide b, jim_wide e)
443
{
444
    jim_wide i, res = 1;
445
    if ((b==0 && e!=0) || (e<0)) return 0;
446
    for(i=0; i<e; i++) {res *= b;}
447
    return res;
448
}
449
 
450
/* -----------------------------------------------------------------------------
451
 * Special functions
452
 * ---------------------------------------------------------------------------*/
453
 
454
/* Note that 'interp' may be NULL if not available in the
455
 * context of the panic. It's only useful to get the error
456
 * file descriptor, it will default to stderr otherwise. */
457
void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
458
{
459
    va_list ap;
460
    FILE *fp = interp ? interp->stderr : stderr;
461
 
462
    va_start(ap, fmt);
463
    fprintf(fp, JIM_NL "JIM INTERPRETER PANIC: ");
464
    vfprintf(fp, fmt, ap);
465
    fprintf(fp, JIM_NL JIM_NL);
466
    va_end(ap);
467
#ifdef HAVE_BACKTRACE
468
    {
469
        void *array[40];
470
        int size, i;
471
        char **strings;
472
 
473
        size = backtrace(array, 40);
474
        strings = backtrace_symbols(array, size);
475
        for (i = 0; i < size; i++)
476
            fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
477
        fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
478
        fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
479
    }
480
#endif
481
    abort();
482
}
483
 
484
/* -----------------------------------------------------------------------------
485
 * Memory allocation
486
 * ---------------------------------------------------------------------------*/
487
 
488
/* Macro used for memory debugging.
489
 * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
490
 * and similary for Jim_Realloc and Jim_Free */
491
#if 0
492
#define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
493
#define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
494
#define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
495
#endif
496
 
497
void *Jim_Alloc(int size)
498
{
499
    void *p = malloc(size);
500
    if (p == NULL)
501
        Jim_Panic(NULL,"Out of memory");
502
    return p;
503
}
504
 
505
void Jim_Free(void *ptr) {
506
    free(ptr);
507
}
508
 
509
void *Jim_Realloc(void *ptr, int size)
510
{
511
    void *p = realloc(ptr, size);
512
    if (p == NULL)
513
        Jim_Panic(NULL,"Out of memory");
514
    return p;
515
}
516
 
517
char *Jim_StrDup(const char *s)
518
{
519
    int l = strlen(s);
520
    char *copy = Jim_Alloc(l+1);
521
 
522
    memcpy(copy, s, l+1);
523
    return copy;
524
}
525
 
526
char *Jim_StrDupLen(const char *s, int l)
527
{
528
    char *copy = Jim_Alloc(l+1);
529
 
530
    memcpy(copy, s, l+1);
531
    copy[l] = 0;    /* Just to be sure, original could be substring */
532
    return copy;
533
}
534
 
535
/* -----------------------------------------------------------------------------
536
 * Time related functions
537
 * ---------------------------------------------------------------------------*/
538
/* Returns microseconds of CPU used since start. */
539
static jim_wide JimClock(void)
540
{
541
#if (defined WIN32) && !(defined JIM_ANSIC)
542
    LARGE_INTEGER t, f;
543
    QueryPerformanceFrequency(&f);
544
    QueryPerformanceCounter(&t);
545
    return (long)((t.QuadPart * 1000000) / f.QuadPart);
546
#else /* !WIN32 */
547
    clock_t clocks = clock();
548
 
549
    return (long)(clocks*(1000000/CLOCKS_PER_SEC));
550
#endif /* WIN32 */
551
}
552
 
553
/* -----------------------------------------------------------------------------
554
 * Hash Tables
555
 * ---------------------------------------------------------------------------*/
556
 
557
/* -------------------------- private prototypes ---------------------------- */
558
static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
559
static unsigned int JimHashTableNextPower(unsigned int size);
560
static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
561
 
562
/* -------------------------- hash functions -------------------------------- */
563
 
564
/* Thomas Wang's 32 bit Mix Function */
565
unsigned int Jim_IntHashFunction(unsigned int key)
566
{
567
    key += ~(key << 15);
568
    key ^=  (key >> 10);
569
    key +=  (key << 3);
570
    key ^=  (key >> 6);
571
    key += ~(key << 11);
572
    key ^=  (key >> 16);
573
    return key;
574
}
575
 
576
/* Identity hash function for integer keys */
577
unsigned int Jim_IdentityHashFunction(unsigned int key)
578
{
579
    return key;
580
}
581
 
582
/* Generic hash function (we are using to multiply by 9 and add the byte
583
 * as Tcl) */
584
unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
585
{
586
    unsigned int h = 0;
587
    while(len--)
588
        h += (h<<3)+*buf++;
589
    return h;
590
}
591
 
592
/* ----------------------------- API implementation ------------------------- */
593
/* reset an hashtable already initialized with ht_init().
594
 * NOTE: This function should only called by ht_destroy(). */
595
static void JimResetHashTable(Jim_HashTable *ht)
596
{
597
    ht->table = NULL;
598
    ht->size = 0;
599
    ht->sizemask = 0;
600
    ht->used = 0;
601
    ht->collisions = 0;
602
}
603
 
604
/* Initialize the hash table */
605
int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
606
        void *privDataPtr)
607
{
608
    JimResetHashTable(ht);
609
    ht->type = type;
610
    ht->privdata = privDataPtr;
611
    return JIM_OK;
612
}
613
 
614
/* Resize the table to the minimal size that contains all the elements,
615
 * but with the invariant of a USER/BUCKETS ration near to <= 1 */
616
int Jim_ResizeHashTable(Jim_HashTable *ht)
617
{
618
    int minimal = ht->used;
619
 
620
    if (minimal < JIM_HT_INITIAL_SIZE)
621
        minimal = JIM_HT_INITIAL_SIZE;
622
    return Jim_ExpandHashTable(ht, minimal);
623
}
624
 
625
/* Expand or create the hashtable */
626
int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
627
{
628
    Jim_HashTable n; /* the new hashtable */
629
    unsigned int realsize = JimHashTableNextPower(size), i;
630
 
631
    /* the size is invalid if it is smaller than the number of
632
     * elements already inside the hashtable */
633
    if (ht->used >= size)
634
        return JIM_ERR;
635
 
636
    Jim_InitHashTable(&n, ht->type, ht->privdata);
637
    n.size = realsize;
638
    n.sizemask = realsize-1;
639
    n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
640
 
641
    /* Initialize all the pointers to NULL */
642
    memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
643
 
644
    /* Copy all the elements from the old to the new table:
645
     * note that if the old hash table is empty ht->size is zero,
646
     * so Jim_ExpandHashTable just creates an hash table. */
647
    n.used = ht->used;
648
    for (i = 0; i < ht->size && ht->used > 0; i++) {
649
        Jim_HashEntry *he, *nextHe;
650
 
651
        if (ht->table[i] == NULL) continue;
652
 
653
        /* For each hash entry on this slot... */
654
        he = ht->table[i];
655
        while(he) {
656
            unsigned int h;
657
 
658
            nextHe = he->next;
659
            /* Get the new element index */
660
            h = Jim_HashKey(ht, he->key) & n.sizemask;
661
            he->next = n.table[h];
662
            n.table[h] = he;
663
            ht->used--;
664
            /* Pass to the next element */
665
            he = nextHe;
666
        }
667
    }
668
    assert(ht->used == 0);
669
    Jim_Free(ht->table);
670
 
671
    /* Remap the new hashtable in the old */
672
    *ht = n;
673
    return JIM_OK;
674
}
675
 
676
/* Add an element to the target hash table */
677
int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
678
{
679
    int index;
680
    Jim_HashEntry *entry;
681
 
682
    /* Get the index of the new element, or -1 if
683
     * the element already exists. */
684
    if ((index = JimInsertHashEntry(ht, key)) == -1)
685
        return JIM_ERR;
686
 
687
    /* Allocates the memory and stores key */
688
    entry = Jim_Alloc(sizeof(*entry));
689
    entry->next = ht->table[index];
690
    ht->table[index] = entry;
691
 
692
    /* Set the hash entry fields. */
693
    Jim_SetHashKey(ht, entry, key);
694
    Jim_SetHashVal(ht, entry, val);
695
    ht->used++;
696
    return JIM_OK;
697
}
698
 
699
/* Add an element, discarding the old if the key already exists */
700
int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
701
{
702
    Jim_HashEntry *entry;
703
 
704
    /* Try to add the element. If the key
705
     * does not exists Jim_AddHashEntry will suceed. */
706
    if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
707
        return JIM_OK;
708
    /* It already exists, get the entry */
709
    entry = Jim_FindHashEntry(ht, key);
710
    /* Free the old value and set the new one */
711
    Jim_FreeEntryVal(ht, entry);
712
    Jim_SetHashVal(ht, entry, val);
713
    return JIM_OK;
714
}
715
 
716
/* Search and remove an element */
717
int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
718
{
719
    unsigned int h;
720
    Jim_HashEntry *he, *prevHe;
721
 
722
    if (ht->size == 0)
723
        return JIM_ERR;
724
    h = Jim_HashKey(ht, key) & ht->sizemask;
725
    he = ht->table[h];
726
 
727
    prevHe = NULL;
728
    while(he) {
729
        if (Jim_CompareHashKeys(ht, key, he->key)) {
730
            /* Unlink the element from the list */
731
            if (prevHe)
732
                prevHe->next = he->next;
733
            else
734
                ht->table[h] = he->next;
735
            Jim_FreeEntryKey(ht, he);
736
            Jim_FreeEntryVal(ht, he);
737
            Jim_Free(he);
738
            ht->used--;
739
            return JIM_OK;
740
        }
741
        prevHe = he;
742
        he = he->next;
743
    }
744
    return JIM_ERR; /* not found */
745
}
746
 
747
/* Destroy an entire hash table */
748
int Jim_FreeHashTable(Jim_HashTable *ht)
749
{
750
    unsigned int i;
751
 
752
    /* Free all the elements */
753
    for (i = 0; i < ht->size && ht->used > 0; i++) {
754
        Jim_HashEntry *he, *nextHe;
755
 
756
        if ((he = ht->table[i]) == NULL) continue;
757
        while(he) {
758
            nextHe = he->next;
759
            Jim_FreeEntryKey(ht, he);
760
            Jim_FreeEntryVal(ht, he);
761
            Jim_Free(he);
762
            ht->used--;
763
            he = nextHe;
764
        }
765
    }
766
    /* Free the table and the allocated cache structure */
767
    Jim_Free(ht->table);
768
    /* Re-initialize the table */
769
    JimResetHashTable(ht);
770
    return JIM_OK; /* never fails */
771
}
772
 
773
Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
774
{
775
    Jim_HashEntry *he;
776
    unsigned int h;
777
 
778
    if (ht->size == 0) return NULL;
779
    h = Jim_HashKey(ht, key) & ht->sizemask;
780
    he = ht->table[h];
781
    while(he) {
782
        if (Jim_CompareHashKeys(ht, key, he->key))
783
            return he;
784
        he = he->next;
785
    }
786
    return NULL;
787
}
788
 
789
Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
790
{
791
    Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
792
 
793
    iter->ht = ht;
794
    iter->index = -1;
795
    iter->entry = NULL;
796
    iter->nextEntry = NULL;
797
    return iter;
798
}
799
 
800
Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
801
{
802
    while (1) {
803
        if (iter->entry == NULL) {
804
            iter->index++;
805
            if (iter->index >=
806
                    (signed)iter->ht->size) break;
807
            iter->entry = iter->ht->table[iter->index];
808
        } else {
809
            iter->entry = iter->nextEntry;
810
        }
811
        if (iter->entry) {
812
            /* We need to save the 'next' here, the iterator user
813
             * may delete the entry we are returning. */
814
            iter->nextEntry = iter->entry->next;
815
            return iter->entry;
816
        }
817
    }
818
    return NULL;
819
}
820
 
821
/* ------------------------- private functions ------------------------------ */
822
 
823
/* Expand the hash table if needed */
824
static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
825
{
826
    /* If the hash table is empty expand it to the intial size,
827
     * if the table is "full" dobule its size. */
828
    if (ht->size == 0)
829
        return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
830
    if (ht->size == ht->used)
831
        return Jim_ExpandHashTable(ht, ht->size*2);
832
    return JIM_OK;
833
}
834
 
835
/* Our hash table capability is a power of two */
836
static unsigned int JimHashTableNextPower(unsigned int size)
837
{
838
    unsigned int i = JIM_HT_INITIAL_SIZE;
839
 
840
    if (size >= 2147483648U)
841
        return 2147483648U;
842
    while(1) {
843
        if (i >= size)
844
            return i;
845
        i *= 2;
846
    }
847
}
848
 
849
/* Returns the index of a free slot that can be populated with
850
 * an hash entry for the given 'key'.
851
 * If the key already exists, -1 is returned. */
852
static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
853
{
854
    unsigned int h;
855
    Jim_HashEntry *he;
856
 
857
    /* Expand the hashtable if needed */
858
    if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
859
        return -1;
860
    /* Compute the key hash value */
861
    h = Jim_HashKey(ht, key) & ht->sizemask;
862
    /* Search if this slot does not already contain the given key */
863
    he = ht->table[h];
864
    while(he) {
865
        if (Jim_CompareHashKeys(ht, key, he->key))
866
            return -1;
867
        he = he->next;
868
    }
869
    return h;
870
}
871
 
872
/* ----------------------- StringCopy Hash Table Type ------------------------*/
873
 
874
static unsigned int JimStringCopyHTHashFunction(const void *key)
875
{
876
    return Jim_GenHashFunction(key, strlen(key));
877
}
878
 
879
static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
880
{
881
    int len = strlen(key);
882
    char *copy = Jim_Alloc(len+1);
883
    JIM_NOTUSED(privdata);
884
 
885
    memcpy(copy, key, len);
886
    copy[len] = '\0';
887
    return copy;
888
}
889
 
890
static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
891
{
892
    int len = strlen(val);
893
    char *copy = Jim_Alloc(len+1);
894
    JIM_NOTUSED(privdata);
895
 
896
    memcpy(copy, val, len);
897
    copy[len] = '\0';
898
    return copy;
899
}
900
 
901
static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
902
        const void *key2)
903
{
904
    JIM_NOTUSED(privdata);
905
 
906
    return strcmp(key1, key2) == 0;
907
}
908
 
909
static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
910
{
911
    JIM_NOTUSED(privdata);
912
 
913
    Jim_Free((void*)key); /* ATTENTION: const cast */
914
}
915
 
916
static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
917
{
918
    JIM_NOTUSED(privdata);
919
 
920
    Jim_Free((void*)val); /* ATTENTION: const cast */
921
}
922
 
923
static Jim_HashTableType JimStringCopyHashTableType = {
924
    JimStringCopyHTHashFunction,        /* hash function */
925
    JimStringCopyHTKeyDup,              /* key dup */
926
    NULL,                               /* val dup */
927
    JimStringCopyHTKeyCompare,          /* key compare */
928
    JimStringCopyHTKeyDestructor,       /* key destructor */
929
    NULL                                /* val destructor */
930
};
931
 
932
/* This is like StringCopy but does not auto-duplicate the key.
933
 * It's used for intepreter's shared strings. */
934
static Jim_HashTableType JimSharedStringsHashTableType = {
935
    JimStringCopyHTHashFunction,        /* hash function */
936
    NULL,                               /* key dup */
937
    NULL,                               /* val dup */
938
    JimStringCopyHTKeyCompare,          /* key compare */
939
    JimStringCopyHTKeyDestructor,       /* key destructor */
940
    NULL                                /* val destructor */
941
};
942
 
943
/* This is like StringCopy but also automatically handle dynamic
944
 * allocated C strings as values. */
945
static Jim_HashTableType JimStringKeyValCopyHashTableType = {
946
    JimStringCopyHTHashFunction,        /* hash function */
947
    JimStringCopyHTKeyDup,              /* key dup */
948
    JimStringKeyValCopyHTValDup,        /* val dup */
949
    JimStringCopyHTKeyCompare,          /* key compare */
950
    JimStringCopyHTKeyDestructor,       /* key destructor */
951
    JimStringKeyValCopyHTValDestructor, /* val destructor */
952
};
953
 
954
typedef struct AssocDataValue {
955
    Jim_InterpDeleteProc *delProc;
956
    void *data;
957
} AssocDataValue;
958
 
959
static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
960
{
961
    AssocDataValue *assocPtr = (AssocDataValue *)data;
962
    if (assocPtr->delProc != NULL)
963
        assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
964
    Jim_Free(data);
965
}
966
 
967
static Jim_HashTableType JimAssocDataHashTableType = {
968
    JimStringCopyHTHashFunction,         /* hash function */
969
    JimStringCopyHTKeyDup,               /* key dup */
970
    NULL,                                /* val dup */
971
    JimStringCopyHTKeyCompare,           /* key compare */
972
    JimStringCopyHTKeyDestructor,        /* key destructor */
973
    JimAssocDataHashTableValueDestructor /* val destructor */
974
};
975
 
976
/* -----------------------------------------------------------------------------
977
 * Stack - This is a simple generic stack implementation. It is used for
978
 * example in the 'expr' expression compiler.
979
 * ---------------------------------------------------------------------------*/
980
void Jim_InitStack(Jim_Stack *stack)
981
{
982
    stack->len = 0;
983
    stack->maxlen = 0;
984
    stack->vector = NULL;
985
}
986
 
987
void Jim_FreeStack(Jim_Stack *stack)
988
{
989
    Jim_Free(stack->vector);
990
}
991
 
992
int Jim_StackLen(Jim_Stack *stack)
993
{
994
    return stack->len;
995
}
996
 
997
void Jim_StackPush(Jim_Stack *stack, void *element) {
998
    int neededLen = stack->len+1;
999
    if (neededLen > stack->maxlen) {
1000
        stack->maxlen = neededLen*2;
1001
        stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
1002
    }
1003
    stack->vector[stack->len] = element;
1004
    stack->len++;
1005
}
1006
 
1007
void *Jim_StackPop(Jim_Stack *stack)
1008
{
1009
    if (stack->len == 0) return NULL;
1010
    stack->len--;
1011
    return stack->vector[stack->len];
1012
}
1013
 
1014
void *Jim_StackPeek(Jim_Stack *stack)
1015
{
1016
    if (stack->len == 0) return NULL;
1017
    return stack->vector[stack->len-1];
1018
}
1019
 
1020
void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
1021
{
1022
    int i;
1023
 
1024
    for (i = 0; i < stack->len; i++)
1025
        freeFunc(stack->vector[i]);
1026
}
1027
 
1028
/* -----------------------------------------------------------------------------
1029
 * Parser
1030
 * ---------------------------------------------------------------------------*/
1031
 
1032
/* Token types */
1033
#define JIM_TT_NONE -1        /* No token returned */
1034
#define JIM_TT_STR 0        /* simple string */
1035
#define JIM_TT_ESC 1        /* string that needs escape chars conversion */
1036
#define JIM_TT_VAR 2        /* var substitution */
1037
#define JIM_TT_DICTSUGAR 3    /* Syntax sugar for [dict get], $foo(bar) */
1038
#define JIM_TT_CMD 4        /* command substitution */
1039
#define JIM_TT_SEP 5        /* word separator */
1040
#define JIM_TT_EOL 6        /* line separator */
1041
 
1042
/* Additional token types needed for expressions */
1043
#define JIM_TT_SUBEXPR_START 7
1044
#define JIM_TT_SUBEXPR_END 8
1045
#define JIM_TT_EXPR_NUMBER 9
1046
#define JIM_TT_EXPR_OPERATOR 10
1047
 
1048
/* Parser states */
1049
#define JIM_PS_DEF 0        /* Default state */
1050
#define JIM_PS_QUOTE 1        /* Inside "" */
1051
 
1052
/* Parser context structure. The same context is used both to parse
1053
 * Tcl scripts and lists. */
1054
struct JimParserCtx {
1055
    const char *prg;     /* Program text */
1056
    const char *p;       /* Pointer to the point of the program we are parsing */
1057
    int len;             /* Left length of 'prg' */
1058
    int linenr;          /* Current line number */
1059
    const char *tstart;
1060
    const char *tend;    /* Returned token is at tstart-tend in 'prg'. */
1061
    int tline;           /* Line number of the returned token */
1062
    int tt;              /* Token type */
1063
    int eof;             /* Non zero if EOF condition is true. */
1064
    int state;           /* Parser state */
1065
    int comment;         /* Non zero if the next chars may be a comment. */
1066
};
1067
 
1068
#define JimParserEof(c) ((c)->eof)
1069
#define JimParserTstart(c) ((c)->tstart)
1070
#define JimParserTend(c) ((c)->tend)
1071
#define JimParserTtype(c) ((c)->tt)
1072
#define JimParserTline(c) ((c)->tline)
1073
 
1074
static int JimParseScript(struct JimParserCtx *pc);
1075
static int JimParseSep(struct JimParserCtx *pc);
1076
static int JimParseEol(struct JimParserCtx *pc);
1077
static int JimParseCmd(struct JimParserCtx *pc);
1078
static int JimParseVar(struct JimParserCtx *pc);
1079
static int JimParseBrace(struct JimParserCtx *pc);
1080
static int JimParseStr(struct JimParserCtx *pc);
1081
static int JimParseComment(struct JimParserCtx *pc);
1082
static char *JimParserGetToken(struct JimParserCtx *pc,
1083
        int *lenPtr, int *typePtr, int *linePtr);
1084
 
1085
/* Initialize a parser context.
1086
 * 'prg' is a pointer to the program text, linenr is the line
1087
 * number of the first line contained in the program. */
1088
void JimParserInit(struct JimParserCtx *pc, const char *prg,
1089
        int len, int linenr)
1090
{
1091
    pc->prg = prg;
1092
    pc->p = prg;
1093
    pc->len = len;
1094
    pc->tstart = NULL;
1095
    pc->tend = NULL;
1096
    pc->tline = 0;
1097
    pc->tt = JIM_TT_NONE;
1098
    pc->eof = 0;
1099
    pc->state = JIM_PS_DEF;
1100
    pc->linenr = linenr;
1101
    pc->comment = 1;
1102
}
1103
 
1104
int JimParseScript(struct JimParserCtx *pc)
1105
{
1106
    while(1) { /* the while is used to reiterate with continue if needed */
1107
        if (!pc->len) {
1108
            pc->tstart = pc->p;
1109
            pc->tend = pc->p-1;
1110
            pc->tline = pc->linenr;
1111
            pc->tt = JIM_TT_EOL;
1112
            pc->eof = 1;
1113
            return JIM_OK;
1114
        }
1115
        switch(*(pc->p)) {
1116
        case '\\':
1117
            if (*(pc->p+1) == '\n')
1118
                return JimParseSep(pc);
1119
            else {
1120
                pc->comment = 0;
1121
                return JimParseStr(pc);
1122
            }
1123
            break;
1124
        case ' ':
1125
        case '\t':
1126
        case '\r':
1127
            if (pc->state == JIM_PS_DEF)
1128
                return JimParseSep(pc);
1129
            else {
1130
                pc->comment = 0;
1131
                return JimParseStr(pc);
1132
            }
1133
            break;
1134
        case '\n':
1135
        case ';':
1136
            pc->comment = 1;
1137
            if (pc->state == JIM_PS_DEF)
1138
                return JimParseEol(pc);
1139
            else
1140
                return JimParseStr(pc);
1141
            break;
1142
        case '[':
1143
            pc->comment = 0;
1144
            return JimParseCmd(pc);
1145
            break;
1146
        case '$':
1147
            pc->comment = 0;
1148
            if (JimParseVar(pc) == JIM_ERR) {
1149
                pc->tstart = pc->tend = pc->p++; pc->len--;
1150
                pc->tline = pc->linenr;
1151
                pc->tt = JIM_TT_STR;
1152
                return JIM_OK;
1153
            } else
1154
                return JIM_OK;
1155
            break;
1156
        case '#':
1157
            if (pc->comment) {
1158
                JimParseComment(pc);
1159
                continue;
1160
            } else {
1161
                return JimParseStr(pc);
1162
            }
1163
        default:
1164
            pc->comment = 0;
1165
            return JimParseStr(pc);
1166
            break;
1167
        }
1168
        return JIM_OK;
1169
    }
1170
}
1171
 
1172
int JimParseSep(struct JimParserCtx *pc)
1173
{
1174
    pc->tstart = pc->p;
1175
    pc->tline = pc->linenr;
1176
    while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
1177
           (*pc->p == '\\' && *(pc->p+1) == '\n')) {
1178
        if (*pc->p == '\\') {
1179
            pc->p++; pc->len--;
1180
            pc->linenr++;
1181
        }
1182
        pc->p++; pc->len--;
1183
    }
1184
    pc->tend = pc->p-1;
1185
    pc->tt = JIM_TT_SEP;
1186
    return JIM_OK;
1187
}
1188
 
1189
int JimParseEol(struct JimParserCtx *pc)
1190
{
1191
    pc->tstart = pc->p;
1192
    pc->tline = pc->linenr;
1193
    while (*pc->p == ' ' || *pc->p == '\n' ||
1194
           *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
1195
        if (*pc->p == '\n')
1196
            pc->linenr++;
1197
        pc->p++; pc->len--;
1198
    }
1199
    pc->tend = pc->p-1;
1200
    pc->tt = JIM_TT_EOL;
1201
    return JIM_OK;
1202
}
1203
 
1204
/* Todo. Don't stop if ']' appears inside {} or quoted.
1205
 * Also should handle the case of puts [string length "]"] */
1206
int JimParseCmd(struct JimParserCtx *pc)
1207
{
1208
    int level = 1;
1209
    int blevel = 0;
1210
 
1211
    pc->tstart = ++pc->p; pc->len--;
1212
    pc->tline = pc->linenr;
1213
    while (1) {
1214
        if (pc->len == 0) {
1215
            break;
1216
        } else if (*pc->p == '[' && blevel == 0) {
1217
            level++;
1218
        } else if (*pc->p == ']' && blevel == 0) {
1219
            level--;
1220
            if (!level) break;
1221
        } else if (*pc->p == '\\') {
1222
            pc->p++; pc->len--;
1223
        } else if (*pc->p == '{') {
1224
            blevel++;
1225
        } else if (*pc->p == '}') {
1226
            if (blevel != 0)
1227
                blevel--;
1228
        } else if (*pc->p == '\n')
1229
            pc->linenr++;
1230
        pc->p++; pc->len--;
1231
    }
1232
    pc->tend = pc->p-1;
1233
    pc->tt = JIM_TT_CMD;
1234
    if (*pc->p == ']') {
1235
        pc->p++; pc->len--;
1236
    }
1237
    return JIM_OK;
1238
}
1239
 
1240
int JimParseVar(struct JimParserCtx *pc)
1241
{
1242
    int brace = 0, stop = 0, ttype = JIM_TT_VAR;
1243
 
1244
    pc->tstart = ++pc->p; pc->len--; /* skip the $ */
1245
    pc->tline = pc->linenr;
1246
    if (*pc->p == '{') {
1247
        pc->tstart = ++pc->p; pc->len--;
1248
        brace = 1;
1249
    }
1250
    if (brace) {
1251
        while (!stop) {
1252
            if (*pc->p == '}' || pc->len == 0) {
1253
                stop = 1;
1254
                if (pc->len == 0)
1255
                    continue;
1256
            }
1257
            else if (*pc->p == '\n')
1258
                pc->linenr++;
1259
            pc->p++; pc->len--;
1260
        }
1261
        if (pc->len == 0)
1262
            pc->tend = pc->p-1;
1263
        else
1264
            pc->tend = pc->p-2;
1265
    } else {
1266
        while (!stop) {
1267
            if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
1268
                (*pc->p >= 'A' && *pc->p <= 'Z') ||
1269
                (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
1270
                stop = 1;
1271
            else {
1272
                pc->p++; pc->len--;
1273
            }
1274
        }
1275
        /* Parse [dict get] syntax sugar. */
1276
        if (*pc->p == '(') {
1277
            while (*pc->p != ')' && pc->len) {
1278
                pc->p++; pc->len--;
1279
                if (*pc->p == '\\' && pc->len >= 2) {
1280
                    pc->p += 2; pc->len -= 2;
1281
                }
1282
            }
1283
            if (*pc->p != '\0') {
1284
                pc->p++; pc->len--;
1285
            }
1286
            ttype = JIM_TT_DICTSUGAR;
1287
        }
1288
        pc->tend = pc->p-1;
1289
    }
1290
    /* Check if we parsed just the '$' character.
1291
     * That's not a variable so an error is returned
1292
     * to tell the state machine to consider this '$' just
1293
     * a string. */
1294
    if (pc->tstart == pc->p) {
1295
        pc->p--; pc->len++;
1296
        return JIM_ERR;
1297
    }
1298
    pc->tt = ttype;
1299
    return JIM_OK;
1300
}
1301
 
1302
int JimParseBrace(struct JimParserCtx *pc)
1303
{
1304
    int level = 1;
1305
 
1306
    pc->tstart = ++pc->p; pc->len--;
1307
    pc->tline = pc->linenr;
1308
    while (1) {
1309
        if (*pc->p == '\\' && pc->len >= 2) {
1310
            pc->p++; pc->len--;
1311
            if (*pc->p == '\n')
1312
                pc->linenr++;
1313
        } else if (*pc->p == '{') {
1314
            level++;
1315
        } else if (pc->len == 0 || *pc->p == '}') {
1316
            level--;
1317
            if (pc->len == 0 || level == 0) {
1318
                pc->tend = pc->p-1;
1319
                if (pc->len != 0) {
1320
                    pc->p++; pc->len--;
1321
                }
1322
                pc->tt = JIM_TT_STR;
1323
                return JIM_OK;
1324
            }
1325
        } else if (*pc->p == '\n') {
1326
            pc->linenr++;
1327
        }
1328
        pc->p++; pc->len--;
1329
    }
1330
    return JIM_OK; /* unreached */
1331
}
1332
 
1333
int JimParseStr(struct JimParserCtx *pc)
1334
{
1335
    int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1336
            pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
1337
    if (newword && *pc->p == '{') {
1338
        return JimParseBrace(pc);
1339
    } else if (newword && *pc->p == '"') {
1340
        pc->state = JIM_PS_QUOTE;
1341
        pc->p++; pc->len--;
1342
    }
1343
    pc->tstart = pc->p;
1344
    pc->tline = pc->linenr;
1345
    while (1) {
1346
        if (pc->len == 0) {
1347
            pc->tend = pc->p-1;
1348
            pc->tt = JIM_TT_ESC;
1349
            return JIM_OK;
1350
        }
1351
        switch(*pc->p) {
1352
        case '\\':
1353
            if (pc->state == JIM_PS_DEF &&
1354
                *(pc->p+1) == '\n') {
1355
                pc->tend = pc->p-1;
1356
                pc->tt = JIM_TT_ESC;
1357
                return JIM_OK;
1358
            }
1359
            if (pc->len >= 2) {
1360
                pc->p++; pc->len--;
1361
            }
1362
            break;
1363
        case '$':
1364
        case '[':
1365
            pc->tend = pc->p-1;
1366
            pc->tt = JIM_TT_ESC;
1367
            return JIM_OK;
1368
        case ' ':
1369
        case '\t':
1370
        case '\n':
1371
        case '\r':
1372
        case ';':
1373
            if (pc->state == JIM_PS_DEF) {
1374
                pc->tend = pc->p-1;
1375
                pc->tt = JIM_TT_ESC;
1376
                return JIM_OK;
1377
            } else if (*pc->p == '\n') {
1378
                pc->linenr++;
1379
            }
1380
            break;
1381
        case '"':
1382
            if (pc->state == JIM_PS_QUOTE) {
1383
                pc->tend = pc->p-1;
1384
                pc->tt = JIM_TT_ESC;
1385
                pc->p++; pc->len--;
1386
                pc->state = JIM_PS_DEF;
1387
                return JIM_OK;
1388
            }
1389
            break;
1390
        }
1391
        pc->p++; pc->len--;
1392
    }
1393
    return JIM_OK; /* unreached */
1394
}
1395
 
1396
int JimParseComment(struct JimParserCtx *pc)
1397
{
1398
    while (*pc->p) {
1399
        if (*pc->p == '\n') {
1400
            pc->linenr++;
1401
            if (*(pc->p-1) != '\\') {
1402
                pc->p++; pc->len--;
1403
                return JIM_OK;
1404
            }
1405
        }
1406
        pc->p++; pc->len--;
1407
    }
1408
    return JIM_OK;
1409
}
1410
 
1411
/* xdigitval and odigitval are helper functions for JimParserGetToken() */
1412
static int xdigitval(int c)
1413
{
1414
    if (c >= '0' && c <= '9') return c-'0';
1415
    if (c >= 'a' && c <= 'f') return c-'a'+10;
1416
    if (c >= 'A' && c <= 'F') return c-'A'+10;
1417
    return -1;
1418
}
1419
 
1420
static int odigitval(int c)
1421
{
1422
    if (c >= '0' && c <= '7') return c-'0';
1423
    return -1;
1424
}
1425
 
1426
/* Perform Tcl escape substitution of 's', storing the result
1427
 * string into 'dest'. The escaped string is guaranteed to
1428
 * be the same length or shorted than the source string.
1429
 * Slen is the length of the string at 's', if it's -1 the string
1430
 * length will be calculated by the function.
1431
 *
1432
 * The function returns the length of the resulting string. */
1433
static int JimEscape(char *dest, const char *s, int slen)
1434
{
1435
    char *p = dest;
1436
    int i, len;
1437
 
1438
    if (slen == -1)
1439
        slen = strlen(s);
1440
 
1441
    for (i = 0; i < slen; i++) {
1442
        switch(s[i]) {
1443
        case '\\':
1444
            switch(s[i+1]) {
1445
            case 'a': *p++ = 0x7; i++; break;
1446
            case 'b': *p++ = 0x8; i++; break;
1447
            case 'f': *p++ = 0xc; i++; break;
1448
            case 'n': *p++ = 0xa; i++; break;
1449
            case 'r': *p++ = 0xd; i++; break;
1450
            case 't': *p++ = 0x9; i++; break;
1451
            case 'v': *p++ = 0xb; i++; break;
1452
            case '\0': *p++ = '\\'; i++; break;
1453
            case '\n': *p++ = ' '; i++; break;
1454
            default:
1455
                  if (s[i+1] == 'x') {
1456
                    int val = 0;
1457
                    int c = xdigitval(s[i+2]);
1458
                    if (c == -1) {
1459
                        *p++ = 'x';
1460
                        i++;
1461
                        break;
1462
                    }
1463
                    val = c;
1464
                    c = xdigitval(s[i+3]);
1465
                    if (c == -1) {
1466
                        *p++ = val;
1467
                        i += 2;
1468
                        break;
1469
                    }
1470
                    val = (val*16)+c;
1471
                    *p++ = val;
1472
                    i += 3;
1473
                    break;
1474
                  } else if (s[i+1] >= '0' && s[i+1] <= '7')
1475
                  {
1476
                    int val = 0;
1477
                    int c = odigitval(s[i+1]);
1478
                    val = c;
1479
                    c = odigitval(s[i+2]);
1480
                    if (c == -1) {
1481
                        *p++ = val;
1482
                        i ++;
1483
                        break;
1484
                    }
1485
                    val = (val*8)+c;
1486
                    c = odigitval(s[i+3]);
1487
                    if (c == -1) {
1488
                        *p++ = val;
1489
                        i += 2;
1490
                        break;
1491
                    }
1492
                    val = (val*8)+c;
1493
                    *p++ = val;
1494
                    i += 3;
1495
                  } else {
1496
                    *p++ = s[i+1];
1497
                    i++;
1498
                  }
1499
                  break;
1500
            }
1501
            break;
1502
        default:
1503
            *p++ = s[i];
1504
            break;
1505
        }
1506
    }
1507
    len = p-dest;
1508
    *p++ = '\0';
1509
    return len;
1510
}
1511
 
1512
/* Returns a dynamically allocated copy of the current token in the
1513
 * parser context. The function perform conversion of escapes if
1514
 * the token is of type JIM_TT_ESC.
1515
 *
1516
 * Note that after the conversion, tokens that are grouped with
1517
 * braces in the source code, are always recognizable from the
1518
 * identical string obtained in a different way from the type.
1519
 *
1520
 * For exmple the string:
1521
 *
1522
 * {expand}$a
1523
 *
1524
 * will return as first token "expand", of type JIM_TT_STR
1525
 *
1526
 * While the string:
1527
 *
1528
 * expand$a
1529
 *
1530
 * will return as first token "expand", of type JIM_TT_ESC
1531
 */
1532
char *JimParserGetToken(struct JimParserCtx *pc,
1533
        int *lenPtr, int *typePtr, int *linePtr)
1534
{
1535
    const char *start, *end;
1536
    char *token;
1537
    int len;
1538
 
1539
    start = JimParserTstart(pc);
1540
    end = JimParserTend(pc);
1541
    if (start > end) {
1542
        if (lenPtr) *lenPtr = 0;
1543
        if (typePtr) *typePtr = JimParserTtype(pc);
1544
        if (linePtr) *linePtr = JimParserTline(pc);
1545
        token = Jim_Alloc(1);
1546
        token[0] = '\0';
1547
        return token;
1548
    }
1549
    len = (end-start)+1;
1550
    token = Jim_Alloc(len+1);
1551
    if (JimParserTtype(pc) != JIM_TT_ESC) {
1552
        /* No escape conversion needed? Just copy it. */
1553
        memcpy(token, start, len);
1554
        token[len] = '\0';
1555
    } else {
1556
        /* Else convert the escape chars. */
1557
        len = JimEscape(token, start, len);
1558
    }
1559
    if (lenPtr) *lenPtr = len;
1560
    if (typePtr) *typePtr = JimParserTtype(pc);
1561
    if (linePtr) *linePtr = JimParserTline(pc);
1562
    return token;
1563
}
1564
 
1565
/* The following functin is not really part of the parsing engine of Jim,
1566
 * but it somewhat related. Given an string and its length, it tries
1567
 * to guess if the script is complete or there are instead " " or { }
1568
 * open and not completed. This is useful for interactive shells
1569
 * implementation and for [info complete].
1570
 *
1571
 * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
1572
 * '{' on scripts incomplete missing one or more '}' to be balanced.
1573
 * '"' on scripts incomplete missing a '"' char.
1574
 *
1575
 * If the script is complete, 1 is returned, otherwise 0. */
1576
int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
1577
{
1578
    int level = 0;
1579
    int state = ' ';
1580
 
1581
    while(len) {
1582
        switch (*s) {
1583
            case '\\':
1584
                if (len > 1)
1585
                    s++;
1586
                break;
1587
            case '"':
1588
                if (state == ' ') {
1589
                    state = '"';
1590
                } else if (state == '"') {
1591
                    state = ' ';
1592
                }
1593
                break;
1594
            case '{':
1595
                if (state == '{') {
1596
                    level++;
1597
                } else if (state == ' ') {
1598
                    state = '{';
1599
                    level++;
1600
                }
1601
                break;
1602
            case '}':
1603
                if (state == '{') {
1604
                    level--;
1605
                    if (level == 0)
1606
                        state = ' ';
1607
                }
1608
                break;
1609
        }
1610
        s++;
1611
        len--;
1612
    }
1613
    if (stateCharPtr)
1614
        *stateCharPtr = state;
1615
    return state == ' ';
1616
}
1617
 
1618
/* -----------------------------------------------------------------------------
1619
 * Tcl Lists parsing
1620
 * ---------------------------------------------------------------------------*/
1621
static int JimParseListSep(struct JimParserCtx *pc);
1622
static int JimParseListStr(struct JimParserCtx *pc);
1623
 
1624
int JimParseList(struct JimParserCtx *pc)
1625
{
1626
    if (pc->len == 0) {
1627
        pc->tstart = pc->tend = pc->p;
1628
        pc->tline = pc->linenr;
1629
        pc->tt = JIM_TT_EOL;
1630
        pc->eof = 1;
1631
        return JIM_OK;
1632
    }
1633
    switch(*pc->p) {
1634
    case ' ':
1635
    case '\n':
1636
    case '\t':
1637
    case '\r':
1638
        if (pc->state == JIM_PS_DEF)
1639
            return JimParseListSep(pc);
1640
        else
1641
            return JimParseListStr(pc);
1642
        break;
1643
    default:
1644
        return JimParseListStr(pc);
1645
        break;
1646
    }
1647
    return JIM_OK;
1648
}
1649
 
1650
int JimParseListSep(struct JimParserCtx *pc)
1651
{
1652
    pc->tstart = pc->p;
1653
    pc->tline = pc->linenr;
1654
    while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
1655
    {
1656
        pc->p++; pc->len--;
1657
    }
1658
    pc->tend = pc->p-1;
1659
    pc->tt = JIM_TT_SEP;
1660
    return JIM_OK;
1661
}
1662
 
1663
int JimParseListStr(struct JimParserCtx *pc)
1664
{
1665
    int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
1666
            pc->tt == JIM_TT_NONE);
1667
    if (newword && *pc->p == '{') {
1668
        return JimParseBrace(pc);
1669
    } else if (newword && *pc->p == '"') {
1670
        pc->state = JIM_PS_QUOTE;
1671
        pc->p++; pc->len--;
1672
    }
1673
    pc->tstart = pc->p;
1674
    pc->tline = pc->linenr;
1675
    while (1) {
1676
        if (pc->len == 0) {
1677
            pc->tend = pc->p-1;
1678
            pc->tt = JIM_TT_ESC;
1679
            return JIM_OK;
1680
        }
1681
        switch(*pc->p) {
1682
        case '\\':
1683
            pc->p++; pc->len--;
1684
            break;
1685
        case ' ':
1686
        case '\t':
1687
        case '\n':
1688
        case '\r':
1689
            if (pc->state == JIM_PS_DEF) {
1690
                pc->tend = pc->p-1;
1691
                pc->tt = JIM_TT_ESC;
1692
                return JIM_OK;
1693
            } else if (*pc->p == '\n') {
1694
                pc->linenr++;
1695
            }
1696
            break;
1697
        case '"':
1698
            if (pc->state == JIM_PS_QUOTE) {
1699
                pc->tend = pc->p-1;
1700
                pc->tt = JIM_TT_ESC;
1701
                pc->p++; pc->len--;
1702
                pc->state = JIM_PS_DEF;
1703
                return JIM_OK;
1704
            }
1705
            break;
1706
        }
1707
        pc->p++; pc->len--;
1708
    }
1709
    return JIM_OK; /* unreached */
1710
}
1711
 
1712
/* -----------------------------------------------------------------------------
1713
 * Jim_Obj related functions
1714
 * ---------------------------------------------------------------------------*/
1715
 
1716
/* Return a new initialized object. */
1717
Jim_Obj *Jim_NewObj(Jim_Interp *interp)
1718
{
1719
    Jim_Obj *objPtr;
1720
 
1721
    /* -- Check if there are objects in the free list -- */
1722
    if (interp->freeList != NULL) {
1723
        /* -- Unlink the object from the free list -- */
1724
        objPtr = interp->freeList;
1725
        interp->freeList = objPtr->nextObjPtr;
1726
    } else {
1727
        /* -- No ready to use objects: allocate a new one -- */
1728
        objPtr = Jim_Alloc(sizeof(*objPtr));
1729
    }
1730
 
1731
    /* Object is returned with refCount of 0. Every
1732
     * kind of GC implemented should take care to don't try
1733
     * to scan objects with refCount == 0. */
1734
    objPtr->refCount = 0;
1735
    /* All the other fields are left not initialized to save time.
1736
     * The caller will probably want set they to the right
1737
     * value anyway. */
1738
 
1739
    /* -- Put the object into the live list -- */
1740
    objPtr->prevObjPtr = NULL;
1741
    objPtr->nextObjPtr = interp->liveList;
1742
    if (interp->liveList)
1743
        interp->liveList->prevObjPtr = objPtr;
1744
    interp->liveList = objPtr;
1745
 
1746
    return objPtr;
1747
}
1748
 
1749
/* Free an object. Actually objects are never freed, but
1750
 * just moved to the free objects list, where they will be
1751
 * reused by Jim_NewObj(). */
1752
void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
1753
{
1754
    /* Check if the object was already freed, panic. */
1755
    if (objPtr->refCount != 0)  {
1756
        Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
1757
                objPtr->refCount);
1758
    }
1759
    /* Free the internal representation */
1760
    Jim_FreeIntRep(interp, objPtr);
1761
    /* Free the string representation */
1762
    if (objPtr->bytes != NULL) {
1763
        if (objPtr->bytes != JimEmptyStringRep)
1764
            Jim_Free(objPtr->bytes);
1765
    }
1766
    /* Unlink the object from the live objects list */
1767
    if (objPtr->prevObjPtr)
1768
        objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
1769
    if (objPtr->nextObjPtr)
1770
        objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
1771
    if (interp->liveList == objPtr)
1772
        interp->liveList = objPtr->nextObjPtr;
1773
    /* Link the object into the free objects list */
1774
    objPtr->prevObjPtr = NULL;
1775
    objPtr->nextObjPtr = interp->freeList;
1776
    if (interp->freeList)
1777
        interp->freeList->prevObjPtr = objPtr;
1778
    interp->freeList = objPtr;
1779
    objPtr->refCount = -1;
1780
}
1781
 
1782
/* Invalidate the string representation of an object. */
1783
void Jim_InvalidateStringRep(Jim_Obj *objPtr)
1784
{
1785
    if (objPtr->bytes != NULL) {
1786
        if (objPtr->bytes != JimEmptyStringRep)
1787
            Jim_Free(objPtr->bytes);
1788
    }
1789
    objPtr->bytes = NULL;
1790
}
1791
 
1792
#define Jim_SetStringRep(o, b, l) \
1793
    do { (o)->bytes = b; (o)->length = l; } while (0)
1794
 
1795
/* Set the initial string representation for an object.
1796
 * Does not try to free an old one. */
1797
void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
1798
{
1799
    if (length == 0) {
1800
        objPtr->bytes = JimEmptyStringRep;
1801
        objPtr->length = 0;
1802
    } else {
1803
        objPtr->bytes = Jim_Alloc(length+1);
1804
        objPtr->length = length;
1805
        memcpy(objPtr->bytes, bytes, length);
1806
        objPtr->bytes[length] = '\0';
1807
    }
1808
}
1809
 
1810
/* Duplicate an object. The returned object has refcount = 0. */
1811
Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
1812
{
1813
    Jim_Obj *dupPtr;
1814
 
1815
    dupPtr = Jim_NewObj(interp);
1816
    if (objPtr->bytes == NULL) {
1817
        /* Object does not have a valid string representation. */
1818
        dupPtr->bytes = NULL;
1819
    } else {
1820
        Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
1821
    }
1822
    if (objPtr->typePtr != NULL) {
1823
        if (objPtr->typePtr->dupIntRepProc == NULL) {
1824
            dupPtr->internalRep = objPtr->internalRep;
1825
        } else {
1826
            objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
1827
        }
1828
        dupPtr->typePtr = objPtr->typePtr;
1829
    } else {
1830
        dupPtr->typePtr = NULL;
1831
    }
1832
    return dupPtr;
1833
}
1834
 
1835
/* Return the string representation for objPtr. If the object
1836
 * string representation is invalid, calls the method to create
1837
 * a new one starting from the internal representation of the object. */
1838
const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
1839
{
1840
    if (objPtr->bytes == NULL) {
1841
        /* Invalid string repr. Generate it. */
1842
        if (objPtr->typePtr->updateStringProc == NULL) {
1843
            Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
1844
                objPtr->typePtr->name);
1845
        }
1846
        objPtr->typePtr->updateStringProc(objPtr);
1847
    }
1848
    if (lenPtr)
1849
        *lenPtr = objPtr->length;
1850
    return objPtr->bytes;
1851
}
1852
 
1853
/* Just returns the length of the object's string rep */
1854
int Jim_Length(Jim_Obj *objPtr)
1855
{
1856
    int len;
1857
 
1858
    Jim_GetString(objPtr, &len);
1859
    return len;
1860
}
1861
 
1862
/* -----------------------------------------------------------------------------
1863
 * String Object
1864
 * ---------------------------------------------------------------------------*/
1865
static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
1866
static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
1867
 
1868
static Jim_ObjType stringObjType = {
1869
    "string",
1870
    NULL,
1871
    DupStringInternalRep,
1872
    NULL,
1873
    JIM_TYPE_REFERENCES,
1874
};
1875
 
1876
void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1877
{
1878
    JIM_NOTUSED(interp);
1879
 
1880
    /* This is a bit subtle: the only caller of this function
1881
     * should be Jim_DuplicateObj(), that will copy the
1882
     * string representaion. After the copy, the duplicated
1883
     * object will not have more room in teh buffer than
1884
     * srcPtr->length bytes. So we just set it to length. */
1885
    dupPtr->internalRep.strValue.maxLength = srcPtr->length;
1886
}
1887
 
1888
int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
1889
{
1890
    /* Get a fresh string representation. */
1891
    (void) Jim_GetString(objPtr, NULL);
1892
    /* Free any other internal representation. */
1893
    Jim_FreeIntRep(interp, objPtr);
1894
    /* Set it as string, i.e. just set the maxLength field. */
1895
    objPtr->typePtr = &stringObjType;
1896
    objPtr->internalRep.strValue.maxLength = objPtr->length;
1897
    return JIM_OK;
1898
}
1899
 
1900
Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
1901
{
1902
    Jim_Obj *objPtr = Jim_NewObj(interp);
1903
 
1904
    if (len == -1)
1905
        len = strlen(s);
1906
    /* Alloc/Set the string rep. */
1907
    if (len == 0) {
1908
        objPtr->bytes = JimEmptyStringRep;
1909
        objPtr->length = 0;
1910
    } else {
1911
        objPtr->bytes = Jim_Alloc(len+1);
1912
        objPtr->length = len;
1913
        memcpy(objPtr->bytes, s, len);
1914
        objPtr->bytes[len] = '\0';
1915
    }
1916
 
1917
    /* No typePtr field for the vanilla string object. */
1918
    objPtr->typePtr = NULL;
1919
    return objPtr;
1920
}
1921
 
1922
/* This version does not try to duplicate the 's' pointer, but
1923
 * use it directly. */
1924
Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
1925
{
1926
    Jim_Obj *objPtr = Jim_NewObj(interp);
1927
 
1928
    if (len == -1)
1929
        len = strlen(s);
1930
    Jim_SetStringRep(objPtr, s, len);
1931
    objPtr->typePtr = NULL;
1932
    return objPtr;
1933
}
1934
 
1935
/* Low-level string append. Use it only against objects
1936
 * of type "string". */
1937
void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
1938
{
1939
    int needlen;
1940
 
1941
    if (len == -1)
1942
        len = strlen(str);
1943
    needlen = objPtr->length + len;
1944
    if (objPtr->internalRep.strValue.maxLength < needlen ||
1945
        objPtr->internalRep.strValue.maxLength == 0) {
1946
        if (objPtr->bytes == JimEmptyStringRep) {
1947
            objPtr->bytes = Jim_Alloc((needlen*2)+1);
1948
        } else {
1949
            objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2)+1);
1950
        }
1951
        objPtr->internalRep.strValue.maxLength = needlen*2;
1952
    }
1953
    memcpy(objPtr->bytes + objPtr->length, str, len);
1954
    objPtr->bytes[objPtr->length+len] = '\0';
1955
    objPtr->length += len;
1956
}
1957
 
1958
/* Low-level wrapper to append an object. */
1959
void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
1960
{
1961
    int len;
1962
    const char *str;
1963
 
1964
    str = Jim_GetString(appendObjPtr, &len);
1965
    StringAppendString(objPtr, str, len);
1966
}
1967
 
1968
/* Higher level API to append strings to objects. */
1969
void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
1970
        int len)
1971
{
1972
    if (Jim_IsShared(objPtr))
1973
        Jim_Panic(interp,"Jim_AppendString called with shared object");
1974
    if (objPtr->typePtr != &stringObjType)
1975
        SetStringFromAny(interp, objPtr);
1976
    StringAppendString(objPtr, str, len);
1977
}
1978
 
1979
void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
1980
        Jim_Obj *appendObjPtr)
1981
{
1982
    int len;
1983
    const char *str;
1984
 
1985
    str = Jim_GetString(appendObjPtr, &len);
1986
    Jim_AppendString(interp, objPtr, str, len);
1987
}
1988
 
1989
void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
1990
{
1991
    va_list ap;
1992
 
1993
    if (objPtr->typePtr != &stringObjType)
1994
        SetStringFromAny(interp, objPtr);
1995
    va_start(ap, objPtr);
1996
    while (1) {
1997
        char *s = va_arg(ap, char*);
1998
 
1999
        if (s == NULL) break;
2000
        Jim_AppendString(interp, objPtr, s, -1);
2001
    }
2002
    va_end(ap);
2003
}
2004
 
2005
int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
2006
{
2007
    const char *aStr, *bStr;
2008
    int aLen, bLen, i;
2009
 
2010
    if (aObjPtr == bObjPtr) return 1;
2011
    aStr = Jim_GetString(aObjPtr, &aLen);
2012
    bStr = Jim_GetString(bObjPtr, &bLen);
2013
    if (aLen != bLen) return 0;
2014
    if (nocase == 0)
2015
        return memcmp(aStr, bStr, aLen) == 0;
2016
    for (i = 0; i < aLen; i++) {
2017
        if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
2018
            return 0;
2019
    }
2020
    return 1;
2021
}
2022
 
2023
int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
2024
        int nocase)
2025
{
2026
    const char *pattern, *string;
2027
    int patternLen, stringLen;
2028
 
2029
    pattern = Jim_GetString(patternObjPtr, &patternLen);
2030
    string = Jim_GetString(objPtr, &stringLen);
2031
    return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
2032
}
2033
 
2034
int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
2035
        Jim_Obj *secondObjPtr, int nocase)
2036
{
2037
    const char *s1, *s2;
2038
    int l1, l2;
2039
 
2040
    s1 = Jim_GetString(firstObjPtr, &l1);
2041
    s2 = Jim_GetString(secondObjPtr, &l2);
2042
    return JimStringCompare(s1, l1, s2, l2, nocase);
2043
}
2044
 
2045
/* Convert a range, as returned by Jim_GetRange(), into
2046
 * an absolute index into an object of the specified length.
2047
 * This function may return negative values, or values
2048
 * bigger or equal to the length of the list if the index
2049
 * is out of range. */
2050
static int JimRelToAbsIndex(int len, int index)
2051
{
2052
    if (index < 0)
2053
        return len + index;
2054
    return index;
2055
}
2056
 
2057
/* Convert a pair of index as normalize by JimRelToAbsIndex(),
2058
 * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
2059
 * for implementation of commands like [string range] and [lrange].
2060
 *
2061
 * The resulting range is guaranteed to address valid elements of
2062
 * the structure. */
2063
static void JimRelToAbsRange(int len, int first, int last,
2064
        int *firstPtr, int *lastPtr, int *rangeLenPtr)
2065
{
2066
    int rangeLen;
2067
 
2068
    if (first > last) {
2069
        rangeLen = 0;
2070
    } else {
2071
        rangeLen = last-first+1;
2072
        if (rangeLen) {
2073
            if (first < 0) {
2074
                rangeLen += first;
2075
                first = 0;
2076
            }
2077
            if (last >= len) {
2078
                rangeLen -= (last-(len-1));
2079
                last = len-1;
2080
            }
2081
        }
2082
    }
2083
    if (rangeLen < 0) rangeLen = 0;
2084
 
2085
    *firstPtr = first;
2086
    *lastPtr = last;
2087
    *rangeLenPtr = rangeLen;
2088
}
2089
 
2090
Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
2091
        Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
2092
{
2093
    int first, last;
2094
    const char *str;
2095
    int len, rangeLen;
2096
 
2097
    if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
2098
        Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
2099
        return NULL;
2100
    str = Jim_GetString(strObjPtr, &len);
2101
    first = JimRelToAbsIndex(len, first);
2102
    last = JimRelToAbsIndex(len, last);
2103
    JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
2104
    return Jim_NewStringObj(interp, str+first, rangeLen);
2105
}
2106
 
2107
static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
2108
{
2109
    char *buf = Jim_Alloc(strObjPtr->length+1);
2110
    int i;
2111
 
2112
    memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2113
    for (i = 0; i < strObjPtr->length; i++)
2114
        buf[i] = tolower(buf[i]);
2115
    return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2116
}
2117
 
2118
static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
2119
{
2120
    char *buf = Jim_Alloc(strObjPtr->length+1);
2121
    int i;
2122
 
2123
    memcpy(buf, strObjPtr->bytes, strObjPtr->length+1);
2124
    for (i = 0; i < strObjPtr->length; i++)
2125
        buf[i] = toupper(buf[i]);
2126
    return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
2127
}
2128
 
2129
/* This is the core of the [format] command.
2130
 * TODO: Export it, make it real... for now only %s and %%
2131
 * specifiers supported. */
2132
Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
2133
        int objc, Jim_Obj *const *objv)
2134
{
2135
    const char *fmt;
2136
    int fmtLen;
2137
    Jim_Obj *resObjPtr;
2138
 
2139
    fmt = Jim_GetString(fmtObjPtr, &fmtLen);
2140
    resObjPtr = Jim_NewStringObj(interp, "", 0);
2141
    while (fmtLen) {
2142
        const char *p = fmt;
2143
        char spec[2], c;
2144
        jim_wide wideValue;
2145
 
2146
        while (*fmt != '%' && fmtLen) {
2147
            fmt++; fmtLen--;
2148
        }
2149
        Jim_AppendString(interp, resObjPtr, p, fmt-p);
2150
        if (fmtLen == 0)
2151
            break;
2152
        fmt++; fmtLen--; /* skip '%' */
2153
        if (*fmt != '%') {
2154
            if (objc == 0) {
2155
                Jim_FreeNewObj(interp, resObjPtr);
2156
                Jim_SetResultString(interp,
2157
                        "not enough arguments for all format specifiers", -1);
2158
                return NULL;
2159
            } else {
2160
                objc--;
2161
            }
2162
        }
2163
        switch(*fmt) {
2164
        case 's':
2165
            Jim_AppendObj(interp, resObjPtr, objv[0]);
2166
            objv++;
2167
            break;
2168
        case 'c':
2169
            if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
2170
                Jim_FreeNewObj(interp, resObjPtr);
2171
                return NULL;
2172
            }
2173
            c = (char) wideValue;
2174
            Jim_AppendString(interp, resObjPtr, &c, 1);
2175
            break;
2176
        case '%':
2177
            Jim_AppendString(interp, resObjPtr, "%" , 1);
2178
            break;
2179
        default:
2180
            spec[0] = *fmt; spec[1] = '\0';
2181
            Jim_FreeNewObj(interp, resObjPtr);
2182
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2183
            Jim_AppendStrings(interp, Jim_GetResult(interp),
2184
                    "bad field specifier \"",  spec, "\"", NULL);
2185
            return NULL;
2186
        }
2187
        fmt++;
2188
        fmtLen--;
2189
    }
2190
    return resObjPtr;
2191
}
2192
 
2193
/* -----------------------------------------------------------------------------
2194
 * Compared String Object
2195
 * ---------------------------------------------------------------------------*/
2196
 
2197
/* This is strange object that allows to compare a C literal string
2198
 * with a Jim object in very short time if the same comparison is done
2199
 * multiple times. For example every time the [if] command is executed,
2200
 * Jim has to check if a given argument is "else". This comparions if
2201
 * the code has no errors are true most of the times, so we can cache
2202
 * inside the object the pointer of the string of the last matching
2203
 * comparison. Because most C compilers perform literal sharing,
2204
 * so that: char *x = "foo", char *y = "foo", will lead to x == y,
2205
 * this works pretty well even if comparisons are at different places
2206
 * inside the C code. */
2207
 
2208
static Jim_ObjType comparedStringObjType = {
2209
    "compared-string",
2210
    NULL,
2211
    NULL,
2212
    NULL,
2213
    JIM_TYPE_REFERENCES,
2214
};
2215
 
2216
/* The only way this object is exposed to the API is via the following
2217
 * function. Returns true if the string and the object string repr.
2218
 * are the same, otherwise zero is returned.
2219
 *
2220
 * Note: this isn't binary safe, but it hardly needs to be.*/
2221
int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
2222
        const char *str)
2223
{
2224
    if (objPtr->typePtr == &comparedStringObjType &&
2225
        objPtr->internalRep.ptr == str)
2226
        return 1;
2227
    else {
2228
        const char *objStr = Jim_GetString(objPtr, NULL);
2229
        if (strcmp(str, objStr) != 0) return 0;
2230
        if (objPtr->typePtr != &comparedStringObjType) {
2231
            Jim_FreeIntRep(interp, objPtr);
2232
            objPtr->typePtr = &comparedStringObjType;
2233
        }
2234
        objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
2235
        return 1;
2236
    }
2237
}
2238
 
2239
int qsortCompareStringPointers(const void *a, const void *b)
2240
{
2241
    char * const *sa = (char * const *)a;
2242
    char * const *sb = (char * const *)b;
2243
    return strcmp(*sa, *sb);
2244
}
2245
 
2246
int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
2247
        const char **tablePtr, int *indexPtr, const char *name, int flags)
2248
{
2249
    const char **entryPtr = NULL;
2250
    char **tablePtrSorted;
2251
    int i, count = 0;
2252
 
2253
    *indexPtr = -1;
2254
    for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
2255
        if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
2256
            *indexPtr = i;
2257
            return JIM_OK;
2258
        }
2259
        count++; /* If nothing matches, this will reach the len of tablePtr */
2260
    }
2261
    if (flags & JIM_ERRMSG) {
2262
        if (name == NULL)
2263
            name = "option";
2264
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2265
        Jim_AppendStrings(interp, Jim_GetResult(interp),
2266
            "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
2267
            NULL);
2268
        tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
2269
        memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
2270
        qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
2271
        for (i = 0; i < count; i++) {
2272
            if (i+1 == count && count > 1)
2273
                Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
2274
            Jim_AppendString(interp, Jim_GetResult(interp),
2275
                    tablePtrSorted[i], -1);
2276
            if (i+1 != count)
2277
                Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
2278
        }
2279
        Jim_Free(tablePtrSorted);
2280
    }
2281
    return JIM_ERR;
2282
}
2283
 
2284
/* -----------------------------------------------------------------------------
2285
 * Source Object
2286
 *
2287
 * This object is just a string from the language point of view, but
2288
 * in the internal representation it contains the filename and line number
2289
 * where this given token was read. This information is used by
2290
 * Jim_EvalObj() if the object passed happens to be of type "source".
2291
 *
2292
 * This allows to propagate the information about line numbers and file
2293
 * names and give error messages with absolute line numbers.
2294
 *
2295
 * Note that this object uses shared strings for filenames, and the
2296
 * pointer to the filename together with the line number is taken into
2297
 * the space for the "inline" internal represenation of the Jim_Object,
2298
 * so there is almost memory zero-overhead.
2299
 *
2300
 * Also the object will be converted to something else if the given
2301
 * token it represents in the source file is not something to be
2302
 * evaluated (not a script), and will be specialized in some other way,
2303
 * so the time overhead is alzo null.
2304
 * ---------------------------------------------------------------------------*/
2305
 
2306
static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2307
static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2308
 
2309
static Jim_ObjType sourceObjType = {
2310
    "source",
2311
    FreeSourceInternalRep,
2312
    DupSourceInternalRep,
2313
    NULL,
2314
    JIM_TYPE_REFERENCES,
2315
};
2316
 
2317
void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2318
{
2319
    Jim_ReleaseSharedString(interp,
2320
            objPtr->internalRep.sourceValue.fileName);
2321
}
2322
 
2323
void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2324
{
2325
    dupPtr->internalRep.sourceValue.fileName =
2326
        Jim_GetSharedString(interp,
2327
                srcPtr->internalRep.sourceValue.fileName);
2328
    dupPtr->internalRep.sourceValue.lineNumber =
2329
        dupPtr->internalRep.sourceValue.lineNumber;
2330
    dupPtr->typePtr = &sourceObjType;
2331
}
2332
 
2333
static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
2334
        const char *fileName, int lineNumber)
2335
{
2336
    if (Jim_IsShared(objPtr))
2337
        Jim_Panic(interp,"JimSetSourceInfo called with shared object");
2338
    if (objPtr->typePtr != NULL)
2339
        Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
2340
    objPtr->internalRep.sourceValue.fileName =
2341
        Jim_GetSharedString(interp, fileName);
2342
    objPtr->internalRep.sourceValue.lineNumber = lineNumber;
2343
    objPtr->typePtr = &sourceObjType;
2344
}
2345
 
2346
/* -----------------------------------------------------------------------------
2347
 * Script Object
2348
 * ---------------------------------------------------------------------------*/
2349
 
2350
#define JIM_CMDSTRUCT_EXPAND -1
2351
 
2352
static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
2353
static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
2354
static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
2355
 
2356
static Jim_ObjType scriptObjType = {
2357
    "script",
2358
    FreeScriptInternalRep,
2359
    DupScriptInternalRep,
2360
    NULL,
2361
    JIM_TYPE_REFERENCES,
2362
};
2363
 
2364
/* The ScriptToken structure represents every token into a scriptObj.
2365
 * Every token contains an associated Jim_Obj that can be specialized
2366
 * by commands operating on it. */
2367
typedef struct ScriptToken {
2368
    int type;
2369
    Jim_Obj *objPtr;
2370
    int linenr;
2371
} ScriptToken;
2372
 
2373
/* This is the script object internal representation. An array of
2374
 * ScriptToken structures, with an associated command structure array.
2375
 * The command structure is a pre-computed representation of the
2376
 * command length and arguments structure as a simple liner array
2377
 * of integers.
2378
 *
2379
 * For example the script:
2380
 *
2381
 * puts hello
2382
 * set $i $x$y [foo]BAR
2383
 *
2384
 * will produce a ScriptObj with the following Tokens:
2385
 *
2386
 * ESC puts
2387
 * SEP
2388
 * ESC hello
2389
 * EOL
2390
 * ESC set
2391
 * EOL
2392
 * VAR i
2393
 * SEP
2394
 * VAR x
2395
 * VAR y
2396
 * SEP
2397
 * CMD foo
2398
 * ESC BAR
2399
 * EOL
2400
 *
2401
 * This is a description of the tokens, separators, and of lines.
2402
 * The command structure instead represents the number of arguments
2403
 * of every command, followed by the tokens of which every argument
2404
 * is composed. So for the example script, the cmdstruct array will
2405
 * contain:
2406
 *
2407
 * 2 1 1 4 1 1 2 2
2408
 *
2409
 * Because "puts hello" has two args (2), composed of single tokens (1 1)
2410
 * While "set $i $x$y [foo]BAR" has four (4) args, the first two
2411
 * composed of single tokens (1 1) and the last two of double tokens
2412
 * (2 2).
2413
 *
2414
 * The precomputation of the command structure makes Jim_Eval() faster,
2415
 * and simpler because there aren't dynamic lengths / allocations.
2416
 *
2417
 * -- {expand} handling --
2418
 *
2419
 * Expand is handled in a special way. When a command
2420
 * contains at least an argument with the {expand} prefix,
2421
 * the command structure presents a -1 before the integer
2422
 * describing the number of arguments. This is used in order
2423
 * to send the command exection to a different path in case
2424
 * of {expand} and guarantee a fast path for the more common
2425
 * case. Also, the integers describing the number of tokens
2426
 * are expressed with negative sign, to allow for fast check
2427
 * of what's an {expand}-prefixed argument and what not.
2428
 *
2429
 * For example the command:
2430
 *
2431
 * list {expand}{1 2}
2432
 *
2433
 * Will produce the following cmdstruct array:
2434
 *
2435
 * -1 2 1 -2
2436
 *
2437
 * -- the substFlags field of the structure --
2438
 *
2439
 * The scriptObj structure is used to represent both "script" objects
2440
 * and "subst" objects. In the second case, the cmdStruct related
2441
 * fields are not used at all, but there is an additional field used
2442
 * that is 'substFlags': this represents the flags used to turn
2443
 * the string into the intenral representation used to perform the
2444
 * substitution. If this flags are not what the application requires
2445
 * the scriptObj is created again. For example the script:
2446
 *
2447
 * subst -nocommands $string
2448
 * subst -novariables $string
2449
 *
2450
 * Will recreate the internal representation of the $string object
2451
 * two times.
2452
 */
2453
typedef struct ScriptObj {
2454
    int len; /* Length as number of tokens. */
2455
    int commands; /* number of top-level commands in script. */
2456
    ScriptToken *token; /* Tokens array. */
2457
    int *cmdStruct; /* commands structure */
2458
    int csLen; /* length of the cmdStruct array. */
2459
    int substFlags; /* flags used for the compilation of "subst" objects */
2460
    int inUse; /* Used to share a ScriptObj. Currently
2461
              only used by Jim_EvalObj() as protection against
2462
              shimmering of the currently evaluated object. */
2463
    char *fileName;
2464
} ScriptObj;
2465
 
2466
void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2467
{
2468
    int i;
2469
    struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
2470
 
2471
    script->inUse--;
2472
    if (script->inUse != 0) return;
2473
    for (i = 0; i < script->len; i++) {
2474
        if (script->token[i].objPtr != NULL)
2475
            Jim_DecrRefCount(interp, script->token[i].objPtr);
2476
    }
2477
    Jim_Free(script->token);
2478
    Jim_Free(script->cmdStruct);
2479
    Jim_Free(script->fileName);
2480
    Jim_Free(script);
2481
}
2482
 
2483
void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
2484
{
2485
    JIM_NOTUSED(interp);
2486
    JIM_NOTUSED(srcPtr);
2487
 
2488
    /* Just returns an simple string. */
2489
    dupPtr->typePtr = NULL;
2490
}
2491
 
2492
/* Add a new token to the internal repr of a script object */
2493
static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
2494
        char *strtoken, int len, int type, char *filename, int linenr)
2495
{
2496
    int prevtype;
2497
    struct ScriptToken *token;
2498
 
2499
    prevtype = (script->len == 0) ? JIM_TT_EOL : \
2500
        script->token[script->len-1].type;
2501
    /* Skip tokens without meaning, like words separators
2502
     * following a word separator or an end of command and
2503
     * so on. */
2504
    if (prevtype == JIM_TT_EOL) {
2505
        if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
2506
            Jim_Free(strtoken);
2507
            return;
2508
        }
2509
    } else if (prevtype == JIM_TT_SEP) {
2510
        if (type == JIM_TT_SEP) {
2511
            Jim_Free(strtoken);
2512
            return;
2513
        } else if (type == JIM_TT_EOL) {
2514
            /* If an EOL is following by a SEP, drop the previous
2515
             * separator. */
2516
            script->len--;
2517
            Jim_DecrRefCount(interp, script->token[script->len].objPtr);
2518
        }
2519
    } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
2520
            type == JIM_TT_ESC && len == 0)
2521
    {
2522
        /* Don't add empty tokens used in interpolation */
2523
        Jim_Free(strtoken);
2524
        return;
2525
    }
2526
    /* Make space for a new istruction */
2527
    script->len++;
2528
    script->token = Jim_Realloc(script->token,
2529
            sizeof(ScriptToken)*script->len);
2530
    /* Initialize the new token */
2531
    token = script->token+(script->len-1);
2532
    token->type = type;
2533
    /* Every object is intially as a string, but the
2534
     * internal type may be specialized during execution of the
2535
     * script. */
2536
    token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
2537
    /* To add source info to SEP and EOL tokens is useless because
2538
     * they will never by called as arguments of Jim_EvalObj(). */
2539
    if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
2540
        JimSetSourceInfo(interp, token->objPtr, filename, linenr);
2541
    Jim_IncrRefCount(token->objPtr);
2542
    token->linenr = linenr;
2543
}
2544
 
2545
/* Add an integer into the command structure field of the script object. */
2546
static void ScriptObjAddInt(struct ScriptObj *script, int val)
2547
{
2548
    script->csLen++;
2549
    script->cmdStruct = Jim_Realloc(script->cmdStruct,
2550
                    sizeof(int)*script->csLen);
2551
    script->cmdStruct[script->csLen-1] = val;
2552
}
2553
 
2554
/* Search a Jim_Obj contained in 'script' with the same stinrg repr.
2555
 * of objPtr. Search nested script objects recursively. */
2556
static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
2557
        ScriptObj *scriptBarrier, Jim_Obj *objPtr)
2558
{
2559
    int i;
2560
 
2561
    for (i = 0; i < script->len; i++) {
2562
        if (script->token[i].objPtr != objPtr &&
2563
            Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
2564
            return script->token[i].objPtr;
2565
        }
2566
        /* Enter recursively on scripts only if the object
2567
         * is not the same as the one we are searching for
2568
         * shared occurrences. */
2569
        if (script->token[i].objPtr->typePtr == &scriptObjType &&
2570
            script->token[i].objPtr != objPtr) {
2571
            Jim_Obj *foundObjPtr;
2572
 
2573
            ScriptObj *subScript =
2574
                script->token[i].objPtr->internalRep.ptr;
2575
            /* Don't recursively enter the script we are trying
2576
             * to make shared to avoid circular references. */
2577
            if (subScript == scriptBarrier) continue;
2578
            if (subScript != script) {
2579
                foundObjPtr =
2580
                    ScriptSearchLiteral(interp, subScript,
2581
                            scriptBarrier, objPtr);
2582
                if (foundObjPtr != NULL)
2583
                    return foundObjPtr;
2584
            }
2585
        }
2586
    }
2587
    return NULL;
2588
}
2589
 
2590
/* Share literals of a script recursively sharing sub-scripts literals. */
2591
static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
2592
        ScriptObj *topLevelScript)
2593
{
2594
    int i, j;
2595
 
2596
    return;
2597
    /* Try to share with toplevel object. */
2598
    if (topLevelScript != NULL) {
2599
        for (i = 0; i < script->len; i++) {
2600
            Jim_Obj *foundObjPtr;
2601
            char *str = script->token[i].objPtr->bytes;
2602
 
2603
            if (script->token[i].objPtr->refCount != 1) continue;
2604
            if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
2605
            if (strchr(str, ' ') || strchr(str, '\n')) continue;
2606
            foundObjPtr = ScriptSearchLiteral(interp,
2607
                    topLevelScript,
2608
                    script, /* barrier */
2609
                    script->token[i].objPtr);
2610
            if (foundObjPtr != NULL) {
2611
                Jim_IncrRefCount(foundObjPtr);
2612
                Jim_DecrRefCount(interp,
2613
                        script->token[i].objPtr);
2614
                script->token[i].objPtr = foundObjPtr;
2615
            }
2616
        }
2617
    }
2618
    /* Try to share locally */
2619
    for (i = 0; i < script->len; i++) {
2620
        char *str = script->token[i].objPtr->bytes;
2621
 
2622
        if (script->token[i].objPtr->refCount != 1) continue;
2623
        if (strchr(str, ' ') || strchr(str, '\n')) continue;
2624
        for (j = 0; j < script->len; j++) {
2625
            if (script->token[i].objPtr !=
2626
                    script->token[j].objPtr &&
2627
                Jim_StringEqObj(script->token[i].objPtr,
2628
                            script->token[j].objPtr, 0))
2629
            {
2630
                Jim_IncrRefCount(script->token[j].objPtr);
2631
                Jim_DecrRefCount(interp,
2632
                        script->token[i].objPtr);
2633
                script->token[i].objPtr =
2634
                    script->token[j].objPtr;
2635
            }
2636
        }
2637
    }
2638
}
2639
 
2640
/* This method takes the string representation of an object
2641
 * as a Tcl script, and generates the pre-parsed internal representation
2642
 * of the script. */
2643
int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
2644
{
2645
    int scriptTextLen;
2646
    const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
2647
    struct JimParserCtx parser;
2648
    struct ScriptObj *script = Jim_Alloc(sizeof(*script));
2649
    ScriptToken *token;
2650
    int args, tokens, start, end, i;
2651
    int initialLineNumber;
2652
    int propagateSourceInfo = 0;
2653
 
2654
    script->len = 0;
2655
    script->csLen = 0;
2656
    script->commands = 0;
2657
    script->token = NULL;
2658
    script->cmdStruct = NULL;
2659
    script->inUse = 1;
2660
    /* Try to get information about filename / line number */
2661
    if (objPtr->typePtr == &sourceObjType) {
2662
        script->fileName =
2663
            Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
2664
        initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
2665
        propagateSourceInfo = 1;
2666
    } else {
2667
        script->fileName = Jim_StrDup("?");
2668
        initialLineNumber = 1;
2669
    }
2670
 
2671
    JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
2672
    while(!JimParserEof(&parser)) {
2673
        char *token;
2674
        int len, type, linenr;
2675
 
2676
        JimParseScript(&parser);
2677
        token = JimParserGetToken(&parser, &len, &type, &linenr);
2678
        ScriptObjAddToken(interp, script, token, len, type,
2679
                propagateSourceInfo ? script->fileName : NULL,
2680
                linenr);
2681
    }
2682
    token = script->token;
2683
 
2684
    /* Compute the command structure array
2685
     * (see the ScriptObj struct definition for more info) */
2686
    start = 0; /* Current command start token index */
2687
    end = -1; /* Current command end token index */
2688
    while (1) {
2689
        int expand = 0; /* expand flag. set to 1 on {expand} form. */
2690
        int interpolation = 0; /* set to 1 if there is at least one
2691
                      argument of the command obtained via
2692
                      interpolation of more tokens. */
2693
        /* Search for the end of command, while
2694
         * count the number of args. */
2695
        start = ++end;
2696
        if (start >= script->len) break;
2697
        args = 1; /* Number of args in current command */
2698
        while (token[end].type != JIM_TT_EOL) {
2699
            if (end == 0 || token[end-1].type == JIM_TT_SEP ||
2700
                    token[end-1].type == JIM_TT_EOL)
2701
            {
2702
                if (token[end].type == JIM_TT_STR &&
2703
                    token[end+1].type != JIM_TT_SEP &&
2704
                    token[end+1].type != JIM_TT_EOL &&
2705
                    (!strcmp(token[end].objPtr->bytes, "expand") ||
2706
                     !strcmp(token[end].objPtr->bytes, "*")))
2707
                    expand++;
2708
            }
2709
            if (token[end].type == JIM_TT_SEP)
2710
                args++;
2711
            end++;
2712
        }
2713
        interpolation = !((end-start+1) == args*2);
2714
        /* Add the 'number of arguments' info into cmdstruct.
2715
         * Negative value if there is list expansion involved. */
2716
        if (expand)
2717
            ScriptObjAddInt(script, -1);
2718
        ScriptObjAddInt(script, args);
2719
        /* Now add info about the number of tokens. */
2720
        tokens = 0; /* Number of tokens in current argument. */
2721
        expand = 0;
2722
        for (i = start; i <= end; i++) {
2723
            if (token[i].type == JIM_TT_SEP ||
2724
                token[i].type == JIM_TT_EOL)
2725
            {
2726
                if (tokens == 1 && expand)
2727
                    expand = 0;
2728
                ScriptObjAddInt(script,
2729
                        expand ? -tokens : tokens);
2730
 
2731
                expand = 0;
2732
                tokens = 0;
2733
                continue;
2734
            } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
2735
                   (!strcmp(token[i].objPtr->bytes, "expand") ||
2736
                    !strcmp(token[i].objPtr->bytes, "*")))
2737
            {
2738
                expand++;
2739
            }
2740
            tokens++;
2741
        }
2742
    }
2743
    /* Perform literal sharing, but only for objects that appear
2744
     * to be scripts written as literals inside the source code,
2745
     * and not computed at runtime. Literal sharing is a costly
2746
     * operation that should be done only against objects that
2747
     * are likely to require compilation only the first time, and
2748
     * then are executed multiple times. */
2749
    if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
2750
        Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
2751
        if (bodyObjPtr->typePtr == &scriptObjType) {
2752
            ScriptObj *bodyScript =
2753
                bodyObjPtr->internalRep.ptr;
2754
            ScriptShareLiterals(interp, script, bodyScript);
2755
        }
2756
    } else if (propagateSourceInfo) {
2757
        ScriptShareLiterals(interp, script, NULL);
2758
    }
2759
    /* Free the old internal rep and set the new one. */
2760
    Jim_FreeIntRep(interp, objPtr);
2761
    Jim_SetIntRepPtr(objPtr, script);
2762
    objPtr->typePtr = &scriptObjType;
2763
    return JIM_OK;
2764
}
2765
 
2766
ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
2767
{
2768
    if (objPtr->typePtr != &scriptObjType) {
2769
        SetScriptFromAny(interp, objPtr);
2770
    }
2771
    return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
2772
}
2773
 
2774
/* -----------------------------------------------------------------------------
2775
 * Commands
2776
 * ---------------------------------------------------------------------------*/
2777
 
2778
/* Commands HashTable Type.
2779
 *
2780
 * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
2781
static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
2782
{
2783
    Jim_Cmd *cmdPtr = (void*) val;
2784
 
2785
    if (cmdPtr->cmdProc == NULL) {
2786
        Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2787
        Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2788
        if (cmdPtr->staticVars) {
2789
            Jim_FreeHashTable(cmdPtr->staticVars);
2790
            Jim_Free(cmdPtr->staticVars);
2791
        }
2792
    } else if (cmdPtr->delProc != NULL) {
2793
            /* If it was a C coded command, call the delProc if any */
2794
            cmdPtr->delProc(interp, cmdPtr->privData);
2795
    }
2796
    Jim_Free(val);
2797
}
2798
 
2799
static Jim_HashTableType JimCommandsHashTableType = {
2800
    JimStringCopyHTHashFunction,        /* hash function */
2801
    JimStringCopyHTKeyDup,        /* key dup */
2802
    NULL,                    /* val dup */
2803
    JimStringCopyHTKeyCompare,        /* key compare */
2804
    JimStringCopyHTKeyDestructor,        /* key destructor */
2805
    Jim_CommandsHT_ValDestructor        /* val destructor */
2806
};
2807
 
2808
/* ------------------------- Commands related functions --------------------- */
2809
 
2810
int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
2811
        Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
2812
{
2813
    Jim_HashEntry *he;
2814
    Jim_Cmd *cmdPtr;
2815
 
2816
    he = Jim_FindHashEntry(&interp->commands, cmdName);
2817
    if (he == NULL) { /* New command to create */
2818
        cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2819
        cmdPtr->cmdProc = cmdProc;
2820
        cmdPtr->privData = privData;
2821
        cmdPtr->delProc = delProc;
2822
        Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2823
    } else {
2824
        Jim_InterpIncrProcEpoch(interp);
2825
        /* Free the arglist/body objects if it was a Tcl procedure */
2826
        cmdPtr = he->val;
2827
        if (cmdPtr->cmdProc == NULL) {
2828
            Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
2829
            Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
2830
            if (cmdPtr->staticVars) {
2831
                Jim_FreeHashTable(cmdPtr->staticVars);
2832
                Jim_Free(cmdPtr->staticVars);
2833
            }
2834
            cmdPtr->staticVars = NULL;
2835
        } else if (cmdPtr->delProc != NULL) {
2836
            /* If it was a C coded command, call the delProc if any */
2837
            cmdPtr->delProc(interp, cmdPtr->privData);
2838
        }
2839
        cmdPtr->cmdProc = cmdProc;
2840
        cmdPtr->privData = privData;
2841
    }
2842
    /* There is no need to increment the 'proc epoch' because
2843
     * creation of a new procedure can never affect existing
2844
     * cached commands. We don't do negative caching. */
2845
    return JIM_OK;
2846
}
2847
 
2848
int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
2849
        Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
2850
        int arityMin, int arityMax)
2851
{
2852
    Jim_Cmd *cmdPtr;
2853
 
2854
    cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
2855
    cmdPtr->cmdProc = NULL; /* Not a C coded command */
2856
    cmdPtr->argListObjPtr = argListObjPtr;
2857
    cmdPtr->bodyObjPtr = bodyObjPtr;
2858
    Jim_IncrRefCount(argListObjPtr);
2859
    Jim_IncrRefCount(bodyObjPtr);
2860
    cmdPtr->arityMin = arityMin;
2861
    cmdPtr->arityMax = arityMax;
2862
    cmdPtr->staticVars = NULL;
2863
 
2864
    /* Create the statics hash table. */
2865
    if (staticsListObjPtr) {
2866
        int len, i;
2867
 
2868
        Jim_ListLength(interp, staticsListObjPtr, &len);
2869
        if (len != 0) {
2870
            cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
2871
            Jim_InitHashTable(cmdPtr->staticVars, &JimVariablesHashTableType,
2872
                    interp);
2873
            for (i = 0; i < len; i++) {
2874
                Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
2875
                Jim_Var *varPtr;
2876
                int subLen;
2877
 
2878
                Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
2879
                /* Check if it's composed of two elements. */
2880
                Jim_ListLength(interp, objPtr, &subLen);
2881
                if (subLen == 1 || subLen == 2) {
2882
                    /* Try to get the variable value from the current
2883
                     * environment. */
2884
                    Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
2885
                    if (subLen == 1) {
2886
                        initObjPtr = Jim_GetVariable(interp, nameObjPtr,
2887
                                JIM_NONE);
2888
                        if (initObjPtr == NULL) {
2889
                            Jim_SetResult(interp,
2890
                                    Jim_NewEmptyStringObj(interp));
2891
                            Jim_AppendStrings(interp, Jim_GetResult(interp),
2892
                                "variable for initialization of static \"",
2893
                                Jim_GetString(nameObjPtr, NULL),
2894
                                "\" not found in the local context",
2895
                                NULL);
2896
                            goto err;
2897
                        }
2898
                    } else {
2899
                        Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
2900
                    }
2901
                    varPtr = Jim_Alloc(sizeof(*varPtr));
2902
                    varPtr->objPtr = initObjPtr;
2903
                    Jim_IncrRefCount(initObjPtr);
2904
                    varPtr->linkFramePtr = NULL;
2905
                    if (Jim_AddHashEntry(cmdPtr->staticVars,
2906
                            Jim_GetString(nameObjPtr, NULL),
2907
                            varPtr) != JIM_OK)
2908
                    {
2909
                        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2910
                        Jim_AppendStrings(interp, Jim_GetResult(interp),
2911
                            "static variable name \"",
2912
                            Jim_GetString(objPtr, NULL), "\"",
2913
                            " duplicated in statics list", NULL);
2914
                        Jim_DecrRefCount(interp, initObjPtr);
2915
                        Jim_Free(varPtr);
2916
                        goto err;
2917
                    }
2918
                } else {
2919
                    Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
2920
                    Jim_AppendStrings(interp, Jim_GetResult(interp),
2921
                        "too many fields in static specifier \"",
2922
                        objPtr, "\"", NULL);
2923
                    goto err;
2924
                }
2925
            }
2926
        }
2927
    }
2928
 
2929
    /* Add the new command */
2930
 
2931
    /* it may already exist, so we try to delete the old one */
2932
    if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
2933
        /* There was an old procedure with the same name, this requires
2934
         * a 'proc epoch' update. */
2935
        Jim_InterpIncrProcEpoch(interp);
2936
    }
2937
    /* If a procedure with the same name didn't existed there is no need
2938
     * to increment the 'proc epoch' because creation of a new procedure
2939
     * can never affect existing cached commands. We don't do
2940
     * negative caching. */
2941
    Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
2942
    return JIM_OK;
2943
 
2944
err:
2945
    Jim_FreeHashTable(cmdPtr->staticVars);
2946
    Jim_Free(cmdPtr->staticVars);
2947
    Jim_DecrRefCount(interp, argListObjPtr);
2948
    Jim_DecrRefCount(interp, bodyObjPtr);
2949
    Jim_Free(cmdPtr);
2950
    return JIM_ERR;
2951
}
2952
 
2953
int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
2954
{
2955
    if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
2956
        return JIM_ERR;
2957
    Jim_InterpIncrProcEpoch(interp);
2958
    return JIM_OK;
2959
}
2960
 
2961
int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
2962
        const char *newName)
2963
{
2964
    Jim_Cmd *cmdPtr;
2965
    Jim_HashEntry *he;
2966
    Jim_Cmd *copyCmdPtr;
2967
 
2968
    if (newName[0] == '\0') /* Delete! */
2969
        return Jim_DeleteCommand(interp, oldName);
2970
    /* Rename */
2971
    he = Jim_FindHashEntry(&interp->commands, oldName);
2972
    if (he == NULL)
2973
        return JIM_ERR; /* Invalid command name */
2974
    cmdPtr = he->val;
2975
    copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
2976
    *copyCmdPtr = *cmdPtr;
2977
    /* In order to avoid that a procedure will get arglist/body/statics
2978
     * freed by the hash table methods, fake a C-coded command
2979
     * setting cmdPtr->cmdProc as not NULL */
2980
    cmdPtr->cmdProc = (void*)1;
2981
    /* Also make sure delProc is NULL. */
2982
    cmdPtr->delProc = NULL;
2983
    /* Destroy the old command, and make sure the new is freed
2984
     * as well. */
2985
    Jim_DeleteHashEntry(&interp->commands, oldName);
2986
    Jim_DeleteHashEntry(&interp->commands, newName);
2987
    /* Now the new command. We are sure it can't fail because
2988
     * the target name was already freed. */
2989
    Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
2990
    /* Increment the epoch */
2991
    Jim_InterpIncrProcEpoch(interp);
2992
    return JIM_OK;
2993
}
2994
 
2995
/* -----------------------------------------------------------------------------
2996
 * Command object
2997
 * ---------------------------------------------------------------------------*/
2998
 
2999
static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3000
 
3001
static Jim_ObjType commandObjType = {
3002
    "command",
3003
    NULL,
3004
    NULL,
3005
    NULL,
3006
    JIM_TYPE_REFERENCES,
3007
};
3008
 
3009
int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3010
{
3011
    Jim_HashEntry *he;
3012
    const char *cmdName;
3013
 
3014
    /* Get the string representation */
3015
    cmdName = Jim_GetString(objPtr, NULL);
3016
    /* Lookup this name into the commands hash table */
3017
    he = Jim_FindHashEntry(&interp->commands, cmdName);
3018
    if (he == NULL)
3019
        return JIM_ERR;
3020
 
3021
    /* Free the old internal repr and set the new one. */
3022
    Jim_FreeIntRep(interp, objPtr);
3023
    objPtr->typePtr = &commandObjType;
3024
    objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
3025
    objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
3026
    return JIM_OK;
3027
}
3028
 
3029
/* This function returns the command structure for the command name
3030
 * stored in objPtr. It tries to specialize the objPtr to contain
3031
 * a cached info instead to perform the lookup into the hash table
3032
 * every time. The information cached may not be uptodate, in such
3033
 * a case the lookup is performed and the cache updated. */
3034
Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
3035
{
3036
    if ((objPtr->typePtr != &commandObjType ||
3037
        objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
3038
        SetCommandFromAny(interp, objPtr) == JIM_ERR) {
3039
        if (flags & JIM_ERRMSG) {
3040
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3041
            Jim_AppendStrings(interp, Jim_GetResult(interp),
3042
                "invalid command name \"", objPtr->bytes, "\"",
3043
                NULL);
3044
        }
3045
        return NULL;
3046
    }
3047
    return objPtr->internalRep.cmdValue.cmdPtr;
3048
}
3049
 
3050
/* -----------------------------------------------------------------------------
3051
 * Variables
3052
 * ---------------------------------------------------------------------------*/
3053
 
3054
/* Variables HashTable Type.
3055
 *
3056
 * Keys are dynamic allocated strings, Values are Jim_Var structures. */
3057
static void JimVariablesHTValDestructor(void *interp, void *val)
3058
{
3059
    Jim_Var *varPtr = (void*) val;
3060
 
3061
    Jim_DecrRefCount(interp, varPtr->objPtr);
3062
    Jim_Free(val);
3063
}
3064
 
3065
static Jim_HashTableType JimVariablesHashTableType = {
3066
    JimStringCopyHTHashFunction,        /* hash function */
3067
    JimStringCopyHTKeyDup,              /* key dup */
3068
    NULL,                               /* val dup */
3069
    JimStringCopyHTKeyCompare,        /* key compare */
3070
    JimStringCopyHTKeyDestructor,     /* key destructor */
3071
    JimVariablesHTValDestructor       /* val destructor */
3072
};
3073
 
3074
/* -----------------------------------------------------------------------------
3075
 * Variable object
3076
 * ---------------------------------------------------------------------------*/
3077
 
3078
#define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
3079
 
3080
static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
3081
 
3082
static Jim_ObjType variableObjType = {
3083
    "variable",
3084
    NULL,
3085
    NULL,
3086
    NULL,
3087
    JIM_TYPE_REFERENCES,
3088
};
3089
 
3090
/* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
3091
 * is in the form "varname(key)". */
3092
static int Jim_NameIsDictSugar(const char *str, int len)
3093
{
3094
    if (len == -1)
3095
        len = strlen(str);
3096
    if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
3097
        return 1;
3098
    return 0;
3099
}
3100
 
3101
/* This method should be called only by the variable API.
3102
 * It returns JIM_OK on success (variable already exists),
3103
 * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
3104
 * a variable name, but syntax glue for [dict] i.e. the last
3105
 * character is ')' */
3106
int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
3107
{
3108
    Jim_HashEntry *he;
3109
    const char *varName;
3110
    int len;
3111
 
3112
    /* Check if the object is already an uptodate variable */
3113
    if (objPtr->typePtr == &variableObjType &&
3114
        objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
3115
        return JIM_OK; /* nothing to do */
3116
    /* Get the string representation */
3117
    varName = Jim_GetString(objPtr, &len);
3118
    /* Make sure it's not syntax glue to get/set dict. */
3119
    if (Jim_NameIsDictSugar(varName, len))
3120
            return JIM_DICT_SUGAR;
3121
    /* Lookup this name into the variables hash table */
3122
    he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
3123
    if (he == NULL) {
3124
        /* Try with static vars. */
3125
        if (interp->framePtr->staticVars == NULL)
3126
            return JIM_ERR;
3127
        if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
3128
            return JIM_ERR;
3129
    }
3130
    /* Free the old internal repr and set the new one. */
3131
    Jim_FreeIntRep(interp, objPtr);
3132
    objPtr->typePtr = &variableObjType;
3133
    objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
3134
    objPtr->internalRep.varValue.varPtr = (void*)he->val;
3135
    return JIM_OK;
3136
}
3137
 
3138
/* -------------------- Variables related functions ------------------------- */
3139
static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
3140
        Jim_Obj *valObjPtr);
3141
static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
3142
 
3143
/* For now that's dummy. Variables lookup should be optimized
3144
 * in many ways, with caching of lookups, and possibly with
3145
 * a table of pre-allocated vars in every CallFrame for local vars.
3146
 * All the caching should also have an 'epoch' mechanism similar
3147
 * to the one used by Tcl for procedures lookup caching. */
3148
 
3149
int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
3150
{
3151
    const char *name;
3152
    Jim_Var *var;
3153
    int err;
3154
 
3155
    if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3156
        /* Check for [dict] syntax sugar. */
3157
        if (err == JIM_DICT_SUGAR)
3158
            return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
3159
        /* New variable to create */
3160
        name = Jim_GetString(nameObjPtr, NULL);
3161
 
3162
        var = Jim_Alloc(sizeof(*var));
3163
        var->objPtr = valObjPtr;
3164
        Jim_IncrRefCount(valObjPtr);
3165
        var->linkFramePtr = NULL;
3166
        /* Insert the new variable */
3167
        Jim_AddHashEntry(&interp->framePtr->vars, name, var);
3168
        /* Make the object int rep a variable */
3169
        Jim_FreeIntRep(interp, nameObjPtr);
3170
        nameObjPtr->typePtr = &variableObjType;
3171
        nameObjPtr->internalRep.varValue.callFrameId =
3172
            interp->framePtr->id;
3173
        nameObjPtr->internalRep.varValue.varPtr = var;
3174
    } else {
3175
        var = nameObjPtr->internalRep.varValue.varPtr;
3176
        if (var->linkFramePtr == NULL) {
3177
            Jim_IncrRefCount(valObjPtr);
3178
            Jim_DecrRefCount(interp, var->objPtr);
3179
            var->objPtr = valObjPtr;
3180
        } else { /* Else handle the link */
3181
            Jim_CallFrame *savedCallFrame;
3182
 
3183
            savedCallFrame = interp->framePtr;
3184
            interp->framePtr = var->linkFramePtr;
3185
            err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
3186
            interp->framePtr = savedCallFrame;
3187
            if (err != JIM_OK)
3188
                return err;
3189
        }
3190
    }
3191
    return JIM_OK;
3192
}
3193
 
3194
int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3195
{
3196
    Jim_Obj *nameObjPtr;
3197
    int result;
3198
 
3199
    nameObjPtr = Jim_NewStringObj(interp, name, -1);
3200
    Jim_IncrRefCount(nameObjPtr);
3201
    result = Jim_SetVariable(interp, nameObjPtr, objPtr);
3202
    Jim_DecrRefCount(interp, nameObjPtr);
3203
    return result;
3204
}
3205
 
3206
int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
3207
{
3208
    Jim_CallFrame *savedFramePtr;
3209
    int result;
3210
 
3211
    savedFramePtr = interp->framePtr;
3212
    interp->framePtr = interp->topFramePtr;
3213
    result = Jim_SetVariableStr(interp, name, objPtr);
3214
    interp->framePtr = savedFramePtr;
3215
    return result;
3216
}
3217
 
3218
int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
3219
{
3220
    Jim_Obj *nameObjPtr, *valObjPtr;
3221
    int result;
3222
 
3223
    nameObjPtr = Jim_NewStringObj(interp, name, -1);
3224
    valObjPtr = Jim_NewStringObj(interp, val, -1);
3225
    Jim_IncrRefCount(nameObjPtr);
3226
    Jim_IncrRefCount(valObjPtr);
3227
    result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
3228
    Jim_DecrRefCount(interp, nameObjPtr);
3229
    Jim_DecrRefCount(interp, valObjPtr);
3230
    return result;
3231
}
3232
 
3233
int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3234
        Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
3235
{
3236
    const char *varName;
3237
    int len;
3238
 
3239
    /* Check for cycles. */
3240
    if (interp->framePtr == targetCallFrame) {
3241
        Jim_Obj *objPtr = targetNameObjPtr;
3242
        Jim_Var *varPtr;
3243
        /* Cycles are only possible with 'uplevel 0' */
3244
        while(1) {
3245
            if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
3246
                Jim_SetResultString(interp,
3247
                    "can't upvar from variable to itself", -1);
3248
                return JIM_ERR;
3249
            }
3250
            if (SetVariableFromAny(interp, objPtr) != JIM_OK)
3251
                break;
3252
            varPtr = objPtr->internalRep.varValue.varPtr;
3253
            if (varPtr->linkFramePtr != targetCallFrame) break;
3254
            objPtr = varPtr->objPtr;
3255
        }
3256
    }
3257
    varName = Jim_GetString(nameObjPtr, &len);
3258
    if (Jim_NameIsDictSugar(varName, len)) {
3259
        Jim_SetResultString(interp,
3260
            "Dict key syntax invalid as link source", -1);
3261
        return JIM_ERR;
3262
    }
3263
    /* Perform the binding */
3264
    Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
3265
    /* We are now sure 'nameObjPtr' type is variableObjType */
3266
    nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
3267
    return JIM_OK;
3268
}
3269
 
3270
/* Return the Jim_Obj pointer associated with a variable name,
3271
 * or NULL if the variable was not found in the current context.
3272
 * The same optimization discussed in the comment to the
3273
 * 'SetVariable' function should apply here. */
3274
Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3275
{
3276
    int err;
3277
 
3278
    /* All the rest is handled here */
3279
    if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3280
        /* Check for [dict] syntax sugar. */
3281
        if (err == JIM_DICT_SUGAR)
3282
            return JimDictSugarGet(interp, nameObjPtr);
3283
        if (flags & JIM_ERRMSG) {
3284
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3285
            Jim_AppendStrings(interp, Jim_GetResult(interp),
3286
                "can't read \"", nameObjPtr->bytes,
3287
                "\": no such variable", NULL);
3288
        }
3289
        return NULL;
3290
    } else {
3291
        Jim_Var *varPtr;
3292
        Jim_Obj *objPtr;
3293
        Jim_CallFrame *savedCallFrame;
3294
 
3295
        varPtr = nameObjPtr->internalRep.varValue.varPtr;
3296
        if (varPtr->linkFramePtr == NULL)
3297
            return varPtr->objPtr;
3298
        /* The variable is a link? Resolve it. */
3299
        savedCallFrame = interp->framePtr;
3300
        interp->framePtr = varPtr->linkFramePtr;
3301
        objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
3302
        if (objPtr == NULL && flags & JIM_ERRMSG) {
3303
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3304
            Jim_AppendStrings(interp, Jim_GetResult(interp),
3305
                "can't read \"", nameObjPtr->bytes,
3306
                "\": no such variable", NULL);
3307
        }
3308
        interp->framePtr = savedCallFrame;
3309
        return objPtr;
3310
    }
3311
}
3312
 
3313
Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
3314
        int flags)
3315
{
3316
    Jim_CallFrame *savedFramePtr;
3317
    Jim_Obj *objPtr;
3318
 
3319
    savedFramePtr = interp->framePtr;
3320
    interp->framePtr = interp->topFramePtr;
3321
    objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3322
    interp->framePtr = savedFramePtr;
3323
 
3324
    return objPtr;
3325
}
3326
 
3327
Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
3328
{
3329
    Jim_Obj *nameObjPtr, *varObjPtr;
3330
 
3331
    nameObjPtr = Jim_NewStringObj(interp, name, -1);
3332
    Jim_IncrRefCount(nameObjPtr);
3333
    varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
3334
    Jim_DecrRefCount(interp, nameObjPtr);
3335
    return varObjPtr;
3336
}
3337
 
3338
Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
3339
        int flags)
3340
{
3341
    Jim_CallFrame *savedFramePtr;
3342
    Jim_Obj *objPtr;
3343
 
3344
    savedFramePtr = interp->framePtr;
3345
    interp->framePtr = interp->topFramePtr;
3346
    objPtr = Jim_GetVariableStr(interp, name, flags);
3347
    interp->framePtr = savedFramePtr;
3348
 
3349
    return objPtr;
3350
}
3351
 
3352
/* Unset a variable.
3353
 * Note: On success unset invalidates all the variable objects created
3354
 * in the current call frame incrementing. */
3355
int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
3356
{
3357
    const char *name;
3358
    Jim_Var *varPtr;
3359
    int err;
3360
 
3361
    if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
3362
        /* Check for [dict] syntax sugar. */
3363
        if (err == JIM_DICT_SUGAR)
3364
            return JimDictSugarSet(interp, nameObjPtr, NULL);
3365
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3366
        Jim_AppendStrings(interp, Jim_GetResult(interp),
3367
            "can't unset \"", nameObjPtr->bytes,
3368
            "\": no such variable", NULL);
3369
        return JIM_ERR; /* var not found */
3370
    }
3371
    varPtr = nameObjPtr->internalRep.varValue.varPtr;
3372
    /* If it's a link call UnsetVariable recursively */
3373
    if (varPtr->linkFramePtr) {
3374
        int retval;
3375
 
3376
        Jim_CallFrame *savedCallFrame;
3377
 
3378
        savedCallFrame = interp->framePtr;
3379
        interp->framePtr = varPtr->linkFramePtr;
3380
        retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
3381
        interp->framePtr = savedCallFrame;
3382
        if (retval != JIM_OK && flags & JIM_ERRMSG) {
3383
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3384
            Jim_AppendStrings(interp, Jim_GetResult(interp),
3385
                "can't unset \"", nameObjPtr->bytes,
3386
                "\": no such variable", NULL);
3387
        }
3388
        return retval;
3389
    } else {
3390
        name = Jim_GetString(nameObjPtr, NULL);
3391
        if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
3392
                != JIM_OK) return JIM_ERR;
3393
        /* Change the callframe id, invalidating var lookup caching */
3394
        JimChangeCallFrameId(interp, interp->framePtr);
3395
        return JIM_OK;
3396
    }
3397
}
3398
 
3399
/* ----------  Dict syntax sugar (similar to array Tcl syntax) -------------- */
3400
 
3401
/* Given a variable name for [dict] operation syntax sugar,
3402
 * this function returns two objects, the first with the name
3403
 * of the variable to set, and the second with the rispective key.
3404
 * For example "foo(bar)" will return objects with string repr. of
3405
 * "foo" and "bar".
3406
 *
3407
 * The returned objects have refcount = 1. The function can't fail. */
3408
static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
3409
        Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
3410
{
3411
    const char *str, *p;
3412
    char *t;
3413
    int len, keyLen, nameLen;
3414
    Jim_Obj *varObjPtr, *keyObjPtr;
3415
 
3416
    str = Jim_GetString(objPtr, &len);
3417
    p = strchr(str, '(');
3418
    p++;
3419
    keyLen = len-((p-str)+1);
3420
    nameLen = (p-str)-1;
3421
    /* Create the objects with the variable name and key. */
3422
    t = Jim_Alloc(nameLen+1);
3423
    memcpy(t, str, nameLen);
3424
    t[nameLen] = '\0';
3425
    varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
3426
 
3427
    t = Jim_Alloc(keyLen+1);
3428
    memcpy(t, p, keyLen);
3429
    t[keyLen] = '\0';
3430
    keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
3431
 
3432
    Jim_IncrRefCount(varObjPtr);
3433
    Jim_IncrRefCount(keyObjPtr);
3434
    *varPtrPtr = varObjPtr;
3435
    *keyPtrPtr = keyObjPtr;
3436
}
3437
 
3438
/* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
3439
 * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
3440
static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
3441
        Jim_Obj *valObjPtr)
3442
{
3443
    Jim_Obj *varObjPtr, *keyObjPtr;
3444
    int err = JIM_OK;
3445
 
3446
    JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3447
    err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
3448
            valObjPtr);
3449
    Jim_DecrRefCount(interp, varObjPtr);
3450
    Jim_DecrRefCount(interp, keyObjPtr);
3451
    return err;
3452
}
3453
 
3454
/* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
3455
static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
3456
{
3457
    Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3458
 
3459
    JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3460
    dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
3461
    if (!dictObjPtr) {
3462
        resObjPtr = NULL;
3463
        goto err;
3464
    }
3465
    if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
3466
            != JIM_OK) {
3467
        resObjPtr = NULL;
3468
    }
3469
err:
3470
    Jim_DecrRefCount(interp, varObjPtr);
3471
    Jim_DecrRefCount(interp, keyObjPtr);
3472
    return resObjPtr;
3473
}
3474
 
3475
/* --------- $var(INDEX) substitution, using a specialized object ----------- */
3476
 
3477
static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
3478
static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3479
        Jim_Obj *dupPtr);
3480
 
3481
static Jim_ObjType dictSubstObjType = {
3482
    "dict-substitution",
3483
    FreeDictSubstInternalRep,
3484
    DupDictSubstInternalRep,
3485
    NULL,
3486
    JIM_TYPE_NONE,
3487
};
3488
 
3489
void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
3490
{
3491
    Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
3492
    Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
3493
}
3494
 
3495
void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
3496
        Jim_Obj *dupPtr)
3497
{
3498
    JIM_NOTUSED(interp);
3499
 
3500
    dupPtr->internalRep.dictSubstValue.varNameObjPtr =
3501
        srcPtr->internalRep.dictSubstValue.varNameObjPtr;
3502
    dupPtr->internalRep.dictSubstValue.indexObjPtr =
3503
        srcPtr->internalRep.dictSubstValue.indexObjPtr;
3504
    dupPtr->typePtr = &dictSubstObjType;
3505
}
3506
 
3507
/* This function is used to expand [dict get] sugar in the form
3508
 * of $var(INDEX). The function is mainly used by Jim_EvalObj()
3509
 * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
3510
 * object that is *guaranteed* to be in the form VARNAME(INDEX).
3511
 * The 'index' part is [subst]ituted, and is used to lookup a key inside
3512
 * the [dict]ionary contained in variable VARNAME. */
3513
Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
3514
{
3515
    Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
3516
    Jim_Obj *substKeyObjPtr = NULL;
3517
 
3518
    if (objPtr->typePtr != &dictSubstObjType) {
3519
        JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
3520
        Jim_FreeIntRep(interp, objPtr);
3521
        objPtr->typePtr = &dictSubstObjType;
3522
        objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
3523
        objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
3524
    }
3525
    if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
3526
                &substKeyObjPtr, JIM_NONE)
3527
            != JIM_OK) {
3528
        substKeyObjPtr = NULL;
3529
        goto err;
3530
    }
3531
    Jim_IncrRefCount(substKeyObjPtr);
3532
    dictObjPtr = Jim_GetVariable(interp,
3533
            objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
3534
    if (!dictObjPtr) {
3535
        resObjPtr = NULL;
3536
        goto err;
3537
    }
3538
    if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
3539
            != JIM_OK) {
3540
        resObjPtr = NULL;
3541
        goto err;
3542
    }
3543
err:
3544
    if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
3545
    return resObjPtr;
3546
}
3547
 
3548
/* -----------------------------------------------------------------------------
3549
 * CallFrame
3550
 * ---------------------------------------------------------------------------*/
3551
 
3552
static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
3553
{
3554
    Jim_CallFrame *cf;
3555
    if (interp->freeFramesList) {
3556
        cf = interp->freeFramesList;
3557
        interp->freeFramesList = cf->nextFramePtr;
3558
    } else {
3559
        cf = Jim_Alloc(sizeof(*cf));
3560
        cf->vars.table = NULL;
3561
    }
3562
 
3563
    cf->id = interp->callFrameEpoch++;
3564
    cf->parentCallFrame = NULL;
3565
    cf->argv = NULL;
3566
    cf->argc = 0;
3567
    cf->procArgsObjPtr = NULL;
3568
    cf->procBodyObjPtr = NULL;
3569
    cf->nextFramePtr = NULL;
3570
    cf->staticVars = NULL;
3571
    if (cf->vars.table == NULL)
3572
        Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
3573
    return cf;
3574
}
3575
 
3576
/* Used to invalidate every caching related to callframe stability. */
3577
static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
3578
{
3579
    cf->id = interp->callFrameEpoch++;
3580
}
3581
 
3582
#define JIM_FCF_NONE 0 /* no flags */
3583
#define JIM_FCF_NOHT 1 /* don't free the hash table */
3584
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
3585
        int flags)
3586
{
3587
    if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
3588
    if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
3589
    if (!(flags & JIM_FCF_NOHT))
3590
        Jim_FreeHashTable(&cf->vars);
3591
    else {
3592
        int i;
3593
        Jim_HashEntry **table = cf->vars.table, *he;
3594
 
3595
        for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
3596
            he = table[i];
3597
            while (he != NULL) {
3598
                Jim_HashEntry *nextEntry = he->next;
3599
                Jim_Var *varPtr = (void*) he->val;
3600
 
3601
                Jim_DecrRefCount(interp, varPtr->objPtr);
3602
                Jim_Free(he->val);
3603
                Jim_Free((void*)he->key); /* ATTENTION: const cast */
3604
                Jim_Free(he);
3605
                table[i] = NULL;
3606
                he = nextEntry;
3607
            }
3608
        }
3609
        cf->vars.used = 0;
3610
    }
3611
    cf->nextFramePtr = interp->freeFramesList;
3612
    interp->freeFramesList = cf;
3613
}
3614
 
3615
/* -----------------------------------------------------------------------------
3616
 * References
3617
 * ---------------------------------------------------------------------------*/
3618
 
3619
/* References HashTable Type.
3620
 *
3621
 * Keys are jim_wide integers, dynamically allocated for now but in the
3622
 * future it's worth to cache this 8 bytes objects. Values are poitners
3623
 * to Jim_References. */
3624
static void JimReferencesHTValDestructor(void *interp, void *val)
3625
{
3626
    Jim_Reference *refPtr = (void*) val;
3627
 
3628
    Jim_DecrRefCount(interp, refPtr->objPtr);
3629
    if (refPtr->finalizerCmdNamePtr != NULL) {
3630
        Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3631
    }
3632
    Jim_Free(val);
3633
}
3634
 
3635
unsigned int JimReferencesHTHashFunction(const void *key)
3636
{
3637
    /* Only the least significant bits are used. */
3638
    const jim_wide *widePtr = key;
3639
    unsigned int intValue = (unsigned int) *widePtr;
3640
    return Jim_IntHashFunction(intValue);
3641
}
3642
 
3643
unsigned int JimReferencesHTDoubleHashFunction(const void *key)
3644
{
3645
    /* Only the least significant bits are used. */
3646
    const jim_wide *widePtr = key;
3647
    unsigned int intValue = (unsigned int) *widePtr;
3648
    return intValue; /* identity function. */
3649
}
3650
 
3651
const void *JimReferencesHTKeyDup(void *privdata, const void *key)
3652
{
3653
    void *copy = Jim_Alloc(sizeof(jim_wide));
3654
    JIM_NOTUSED(privdata);
3655
 
3656
    memcpy(copy, key, sizeof(jim_wide));
3657
    return copy;
3658
}
3659
 
3660
int JimReferencesHTKeyCompare(void *privdata, const void *key1,
3661
        const void *key2)
3662
{
3663
    JIM_NOTUSED(privdata);
3664
 
3665
    return memcmp(key1, key2, sizeof(jim_wide)) == 0;
3666
}
3667
 
3668
void JimReferencesHTKeyDestructor(void *privdata, const void *key)
3669
{
3670
    JIM_NOTUSED(privdata);
3671
 
3672
    Jim_Free((void*)key);
3673
}
3674
 
3675
static Jim_HashTableType JimReferencesHashTableType = {
3676
    JimReferencesHTHashFunction,    /* hash function */
3677
    JimReferencesHTKeyDup,          /* key dup */
3678
    NULL,                           /* val dup */
3679
    JimReferencesHTKeyCompare,      /* key compare */
3680
    JimReferencesHTKeyDestructor,   /* key destructor */
3681
    JimReferencesHTValDestructor    /* val destructor */
3682
};
3683
 
3684
/* -----------------------------------------------------------------------------
3685
 * Reference object type and References API
3686
 * ---------------------------------------------------------------------------*/
3687
 
3688
static void UpdateStringOfReference(struct Jim_Obj *objPtr);
3689
 
3690
static Jim_ObjType referenceObjType = {
3691
    "reference",
3692
    NULL,
3693
    NULL,
3694
    UpdateStringOfReference,
3695
    JIM_TYPE_REFERENCES,
3696
};
3697
 
3698
void UpdateStringOfReference(struct Jim_Obj *objPtr)
3699
{
3700
    int len;
3701
    char buf[JIM_REFERENCE_SPACE+1];
3702
    Jim_Reference *refPtr;
3703
 
3704
    refPtr = objPtr->internalRep.refValue.refPtr;
3705
    len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
3706
    objPtr->bytes = Jim_Alloc(len+1);
3707
    memcpy(objPtr->bytes, buf, len+1);
3708
    objPtr->length = len;
3709
}
3710
 
3711
/* returns true if 'c' is a valid reference tag character.
3712
 * i.e. inside the range [_a-zA-Z0-9] */
3713
static int isrefchar(int c)
3714
{
3715
    if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3716
        (c >= '0' && c <= '9')) return 1;
3717
    return 0;
3718
}
3719
 
3720
int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
3721
{
3722
    jim_wide wideValue;
3723
    int i, len;
3724
    const char *str, *start, *end;
3725
    char refId[21];
3726
    Jim_Reference *refPtr;
3727
    Jim_HashEntry *he;
3728
 
3729
    /* Get the string representation */
3730
    str = Jim_GetString(objPtr, &len);
3731
    /* Check if it looks like a reference */
3732
    if (len < JIM_REFERENCE_SPACE) goto badformat;
3733
    /* Trim spaces */
3734
    start = str;
3735
    end = str+len-1;
3736
    while (*start == ' ') start++;
3737
    while (*end == ' ' && end > start) end--;
3738
    if (end-start+1 != JIM_REFERENCE_SPACE) goto badformat;
3739
    /* <reference.<1234567>.%020> */
3740
    if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
3741
    if (start[12+JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
3742
    /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
3743
    for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3744
        if (!isrefchar(start[12+i])) goto badformat;
3745
    }
3746
    /* Extract info from the refernece. */
3747
    memcpy(refId, start+14+JIM_REFERENCE_TAGLEN, 20);
3748
    refId[20] = '\0';
3749
    /* Try to convert the ID into a jim_wide */
3750
    if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
3751
    /* Check if the reference really exists! */
3752
    he = Jim_FindHashEntry(&interp->references, &wideValue);
3753
    if (he == NULL) {
3754
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3755
        Jim_AppendStrings(interp, Jim_GetResult(interp),
3756
                "Invalid reference ID \"", str, "\"", NULL);
3757
        return JIM_ERR;
3758
    }
3759
    refPtr = he->val;
3760
    /* Free the old internal repr and set the new one. */
3761
    Jim_FreeIntRep(interp, objPtr);
3762
    objPtr->typePtr = &referenceObjType;
3763
    objPtr->internalRep.refValue.id = wideValue;
3764
    objPtr->internalRep.refValue.refPtr = refPtr;
3765
    return JIM_OK;
3766
 
3767
badformat:
3768
    Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3769
    Jim_AppendStrings(interp, Jim_GetResult(interp),
3770
            "expected reference but got \"", str, "\"", NULL);
3771
    return JIM_ERR;
3772
}
3773
 
3774
/* Returns a new reference pointing to objPtr, having cmdNamePtr
3775
 * as finalizer command (or NULL if there is no finalizer).
3776
 * The returned reference object has refcount = 0. */
3777
Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
3778
        Jim_Obj *cmdNamePtr)
3779
{
3780
    struct Jim_Reference *refPtr;
3781
    jim_wide wideValue = interp->referenceNextId;
3782
    Jim_Obj *refObjPtr;
3783
    const char *tag;
3784
    int tagLen, i;
3785
 
3786
    /* Perform the Garbage Collection if needed. */
3787
    Jim_CollectIfNeeded(interp);
3788
 
3789
    refPtr = Jim_Alloc(sizeof(*refPtr));
3790
    refPtr->objPtr = objPtr;
3791
    Jim_IncrRefCount(objPtr);
3792
    refPtr->finalizerCmdNamePtr = cmdNamePtr;
3793
    if (cmdNamePtr)
3794
        Jim_IncrRefCount(cmdNamePtr);
3795
    Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
3796
    refObjPtr = Jim_NewObj(interp);
3797
    refObjPtr->typePtr = &referenceObjType;
3798
    refObjPtr->bytes = NULL;
3799
    refObjPtr->internalRep.refValue.id = interp->referenceNextId;
3800
    refObjPtr->internalRep.refValue.refPtr = refPtr;
3801
    interp->referenceNextId++;
3802
    /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
3803
     * that does not pass the 'isrefchar' test is replaced with '_' */
3804
    tag = Jim_GetString(tagPtr, &tagLen);
3805
    if (tagLen > JIM_REFERENCE_TAGLEN)
3806
        tagLen = JIM_REFERENCE_TAGLEN;
3807
    for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
3808
        if (i < tagLen)
3809
            refPtr->tag[i] = tag[i];
3810
        else
3811
            refPtr->tag[i] = '_';
3812
    }
3813
    refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
3814
    return refObjPtr;
3815
}
3816
 
3817
Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
3818
{
3819
    if (objPtr->typePtr != &referenceObjType &&
3820
        SetReferenceFromAny(interp, objPtr) == JIM_ERR)
3821
        return NULL;
3822
    return objPtr->internalRep.refValue.refPtr;
3823
}
3824
 
3825
int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
3826
{
3827
    Jim_Reference *refPtr;
3828
 
3829
    if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3830
        return JIM_ERR;
3831
    Jim_IncrRefCount(cmdNamePtr);
3832
    if (refPtr->finalizerCmdNamePtr)
3833
        Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
3834
    refPtr->finalizerCmdNamePtr = cmdNamePtr;
3835
    return JIM_OK;
3836
}
3837
 
3838
int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
3839
{
3840
    Jim_Reference *refPtr;
3841
 
3842
    if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
3843
        return JIM_ERR;
3844
    *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
3845
    return JIM_OK;
3846
}
3847
 
3848
/* -----------------------------------------------------------------------------
3849
 * References Garbage Collection
3850
 * ---------------------------------------------------------------------------*/
3851
 
3852
/* This the hash table type for the "MARK" phase of the GC */
3853
static Jim_HashTableType JimRefMarkHashTableType = {
3854
    JimReferencesHTHashFunction,    /* hash function */
3855
    JimReferencesHTKeyDup,          /* key dup */
3856
    NULL,                           /* val dup */
3857
    JimReferencesHTKeyCompare,      /* key compare */
3858
    JimReferencesHTKeyDestructor,   /* key destructor */
3859
    NULL                            /* val destructor */
3860
};
3861
 
3862
/* #define JIM_DEBUG_GC 1 */
3863
 
3864
/* Performs the garbage collection. */
3865
int Jim_Collect(Jim_Interp *interp)
3866
{
3867
    Jim_HashTable marks;
3868
    Jim_HashTableIterator *htiter;
3869
    Jim_HashEntry *he;
3870
    Jim_Obj *objPtr;
3871
    int collected = 0;
3872
 
3873
    /* Avoid recursive calls */
3874
    if (interp->lastCollectId == -1) {
3875
        /* Jim_Collect() already running. Return just now. */
3876
        return 0;
3877
    }
3878
    interp->lastCollectId = -1;
3879
 
3880
    /* Mark all the references found into the 'mark' hash table.
3881
     * The references are searched in every live object that
3882
     * is of a type that can contain references. */
3883
    Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
3884
    objPtr = interp->liveList;
3885
    while(objPtr) {
3886
        if (objPtr->typePtr == NULL ||
3887
            objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
3888
            const char *str, *p;
3889
            int len;
3890
 
3891
            /* If the object is of type reference, to get the
3892
             * Id is simple... */
3893
            if (objPtr->typePtr == &referenceObjType) {
3894
                Jim_AddHashEntry(&marks,
3895
                    &objPtr->internalRep.refValue.id, NULL);
3896
#ifdef JIM_DEBUG_GC
3897
                fprintf(interp->stdout,
3898
                    "MARK (reference): %d refcount: %d" JIM_NL,
3899
                    (int) objPtr->internalRep.refValue.id,
3900
                    objPtr->refCount);
3901
#endif
3902
                objPtr = objPtr->nextObjPtr;
3903
                continue;
3904
            }
3905
            /* Get the string repr of the object we want
3906
             * to scan for references. */
3907
            p = str = Jim_GetString(objPtr, &len);
3908
            /* Skip objects too little to contain references. */
3909
            if (len < JIM_REFERENCE_SPACE) {
3910
                objPtr = objPtr->nextObjPtr;
3911
                continue;
3912
            }
3913
            /* Extract references from the object string repr. */
3914
            while(1) {
3915
                int i;
3916
                jim_wide id;
3917
                char buf[21];
3918
 
3919
                if ((p = strstr(p, "<reference.<")) == NULL)
3920
                    break;
3921
                /* Check if it's a valid reference. */
3922
                if (len-(p-str) < JIM_REFERENCE_SPACE) break;
3923
                if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
3924
                for (i = 21; i <= 40; i++)
3925
                    if (!isdigit((int)p[i]))
3926
                        break;
3927
                /* Get the ID */
3928
                memcpy(buf, p+21, 20);
3929
                buf[20] = '\0';
3930
                Jim_StringToWide(buf, &id, 10);
3931
 
3932
                /* Ok, a reference for the given ID
3933
                 * was found. Mark it. */
3934
                Jim_AddHashEntry(&marks, &id, NULL);
3935
#ifdef JIM_DEBUG_GC
3936
                fprintf(interp->stdout,"MARK: %d" JIM_NL, (int)id);
3937
#endif
3938
                p += JIM_REFERENCE_SPACE;
3939
            }
3940
        }
3941
        objPtr = objPtr->nextObjPtr;
3942
    }
3943
 
3944
    /* Run the references hash table to destroy every reference that
3945
     * is not referenced outside (not present in the mark HT). */
3946
    htiter = Jim_GetHashTableIterator(&interp->references);
3947
    while ((he = Jim_NextHashEntry(htiter)) != NULL) {
3948
        const jim_wide *refId;
3949
        Jim_Reference *refPtr;
3950
 
3951
        refId = he->key;
3952
        /* Check if in the mark phase we encountered
3953
         * this reference. */
3954
        if (Jim_FindHashEntry(&marks, refId) == NULL) {
3955
#ifdef JIM_DEBUG_GC
3956
            fprintf(interp->stdout,"COLLECTING %d" JIM_NL, (int)*refId);
3957
#endif
3958
            collected++;
3959
            /* Drop the reference, but call the
3960
             * finalizer first if registered. */
3961
            refPtr = he->val;
3962
            if (refPtr->finalizerCmdNamePtr) {
3963
                char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE+1);
3964
                Jim_Obj *objv[3], *oldResult;
3965
 
3966
                JimFormatReference(refstr, refPtr, *refId);
3967
 
3968
                objv[0] = refPtr->finalizerCmdNamePtr;
3969
                objv[1] = Jim_NewStringObjNoAlloc(interp,
3970
                        refstr, 32);
3971
                objv[2] = refPtr->objPtr;
3972
                Jim_IncrRefCount(objv[0]);
3973
                Jim_IncrRefCount(objv[1]);
3974
                Jim_IncrRefCount(objv[2]);
3975
 
3976
                /* Drop the reference itself */
3977
                Jim_DeleteHashEntry(&interp->references, refId);
3978
 
3979
                /* Call the finalizer. Errors ignored. */
3980
                oldResult = interp->result;
3981
                Jim_IncrRefCount(oldResult);
3982
                Jim_EvalObjVector(interp, 3, objv);
3983
                Jim_SetResult(interp, oldResult);
3984
                Jim_DecrRefCount(interp, oldResult);
3985
 
3986
                Jim_DecrRefCount(interp, objv[0]);
3987
                Jim_DecrRefCount(interp, objv[1]);
3988
                Jim_DecrRefCount(interp, objv[2]);
3989
            } else {
3990
                Jim_DeleteHashEntry(&interp->references, refId);
3991
            }
3992
        }
3993
    }
3994
    Jim_FreeHashTableIterator(htiter);
3995
    Jim_FreeHashTable(&marks);
3996
    interp->lastCollectId = interp->referenceNextId;
3997
    interp->lastCollectTime = time(NULL);
3998
    return collected;
3999
}
4000
 
4001
#define JIM_COLLECT_ID_PERIOD 5000
4002
#define JIM_COLLECT_TIME_PERIOD 300
4003
 
4004
void Jim_CollectIfNeeded(Jim_Interp *interp)
4005
{
4006
    jim_wide elapsedId;
4007
    int elapsedTime;
4008
 
4009
    elapsedId = interp->referenceNextId - interp->lastCollectId;
4010
    elapsedTime = time(NULL) - interp->lastCollectTime;
4011
 
4012
 
4013
    if (elapsedId > JIM_COLLECT_ID_PERIOD ||
4014
        elapsedTime > JIM_COLLECT_TIME_PERIOD) {
4015
        Jim_Collect(interp);
4016
    }
4017
}
4018
 
4019
/* -----------------------------------------------------------------------------
4020
 * Interpreter related functions
4021
 * ---------------------------------------------------------------------------*/
4022
 
4023
Jim_Interp *Jim_CreateInterp(void)
4024
{
4025
    Jim_Interp *i = Jim_Alloc(sizeof(*i));
4026
    Jim_Obj *pathPtr;
4027
 
4028
    i->errorLine = 0;
4029
    i->errorFileName = Jim_StrDup("");
4030
    i->numLevels = 0;
4031
    i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
4032
    i->returnCode = JIM_OK;
4033
    i->exitCode = 0;
4034
    i->procEpoch = 0;
4035
    i->callFrameEpoch = 0;
4036
    i->liveList = i->freeList = NULL;
4037
    i->scriptFileName = Jim_StrDup("");
4038
    i->referenceNextId = 0;
4039
    i->lastCollectId = 0;
4040
    i->lastCollectTime = time(NULL);
4041
    i->freeFramesList = NULL;
4042
    i->prngState = NULL;
4043
    i->evalRetcodeLevel = -1;
4044
    i->stdin = stdin;
4045
    i->stdout = stdout;
4046
    i->stderr = stderr;
4047
 
4048
    /* Note that we can create objects only after the
4049
     * interpreter liveList and freeList pointers are
4050
     * initialized to NULL. */
4051
    Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
4052
    Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
4053
    Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
4054
            NULL);
4055
    Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
4056
    Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
4057
    Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
4058
    i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
4059
    i->emptyObj = Jim_NewEmptyStringObj(i);
4060
    i->result = i->emptyObj;
4061
    i->stackTrace = Jim_NewListObj(i, NULL, 0);
4062
    i->unknown = Jim_NewStringObj(i, "unknown", -1);
4063
    Jim_IncrRefCount(i->emptyObj);
4064
    Jim_IncrRefCount(i->result);
4065
    Jim_IncrRefCount(i->stackTrace);
4066
    Jim_IncrRefCount(i->unknown);
4067
 
4068
    /* Initialize key variables every interpreter should contain */
4069
    pathPtr = Jim_NewStringObj(i, "./", -1);
4070
    Jim_SetVariableStr(i, "jim_libpath", pathPtr);
4071
    Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
4072
 
4073
    /* Export the core API to extensions */
4074
    JimRegisterCoreApi(i);
4075
    return i;
4076
}
4077
 
4078
/* This is the only function Jim exports directly without
4079
 * to use the STUB system. It is only used by embedders
4080
 * in order to get an interpreter with the Jim API pointers
4081
 * registered. */
4082
Jim_Interp *ExportedJimCreateInterp(void)
4083
{
4084
    return Jim_CreateInterp();
4085
}
4086
 
4087
void Jim_FreeInterp(Jim_Interp *i)
4088
{
4089
    Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
4090
    Jim_Obj *objPtr, *nextObjPtr;
4091
 
4092
    Jim_DecrRefCount(i, i->emptyObj);
4093
    Jim_DecrRefCount(i, i->result);
4094
    Jim_DecrRefCount(i, i->stackTrace);
4095
    Jim_DecrRefCount(i, i->unknown);
4096
    Jim_Free((void*)i->errorFileName);
4097
    Jim_Free((void*)i->scriptFileName);
4098
    Jim_FreeHashTable(&i->commands);
4099
    Jim_FreeHashTable(&i->references);
4100
    Jim_FreeHashTable(&i->stub);
4101
    Jim_FreeHashTable(&i->assocData);
4102
    Jim_FreeHashTable(&i->packages);
4103
    Jim_Free(i->prngState);
4104
    /* Free the call frames list */
4105
    while(cf) {
4106
        prevcf = cf->parentCallFrame;
4107
        JimFreeCallFrame(i, cf, JIM_FCF_NONE);
4108
        cf = prevcf;
4109
    }
4110
    /* Check that the live object list is empty, otherwise
4111
     * there is a memory leak. */
4112
    if (i->liveList != NULL) {
4113
        Jim_Obj *objPtr = i->liveList;
4114
 
4115
        fprintf(i->stdout,JIM_NL "-------------------------------------" JIM_NL);
4116
        fprintf(i->stdout,"Objects still in the free list:" JIM_NL);
4117
        while(objPtr) {
4118
            const char *type = objPtr->typePtr ?
4119
                objPtr->typePtr->name : "";
4120
            fprintf(i->stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
4121
                    objPtr, type,
4122
                    objPtr->bytes ? objPtr->bytes
4123
                    : "(null)", objPtr->refCount);
4124
            if (objPtr->typePtr == &sourceObjType) {
4125
                fprintf(i->stdout, "FILE %s LINE %d" JIM_NL,
4126
                objPtr->internalRep.sourceValue.fileName,
4127
                objPtr->internalRep.sourceValue.lineNumber);
4128
            }
4129
            objPtr = objPtr->nextObjPtr;
4130
        }
4131
        fprintf(stdout, "-------------------------------------" JIM_NL JIM_NL);
4132
        Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
4133
    }
4134
    /* Free all the freed objects. */
4135
    objPtr = i->freeList;
4136
    while (objPtr) {
4137
        nextObjPtr = objPtr->nextObjPtr;
4138
        Jim_Free(objPtr);
4139
        objPtr = nextObjPtr;
4140
    }
4141
    /* Free cached CallFrame structures */
4142
    cf = i->freeFramesList;
4143
    while(cf) {
4144
        nextcf = cf->nextFramePtr;
4145
        if (cf->vars.table != NULL)
4146
            Jim_Free(cf->vars.table);
4147
        Jim_Free(cf);
4148
        cf = nextcf;
4149
    }
4150
    /* Free the sharedString hash table. Make sure to free it
4151
     * after every other Jim_Object was freed. */
4152
    Jim_FreeHashTable(&i->sharedStrings);
4153
    /* Free the interpreter structure. */
4154
    Jim_Free(i);
4155
}
4156
 
4157
/* Store the call frame relative to the level represented by
4158
 * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
4159
 * level is assumed to be '1'.
4160
 *
4161
 * If a newLevelptr int pointer is specified, the function stores
4162
 * the absolute level integer value of the new target callframe into
4163
 * *newLevelPtr. (this is used to adjust interp->numLevels
4164
 * in the implementation of [uplevel], so that [info level] will
4165
 * return a correct information).
4166
 *
4167
 * This function accepts the 'level' argument in the form
4168
 * of the commands [uplevel] and [upvar].
4169
 *
4170
 * For a function accepting a relative integer as level suitable
4171
 * for implementation of [info level ?level?] check the
4172
 * GetCallFrameByInteger() function. */
4173
int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4174
        Jim_CallFrame **framePtrPtr, int *newLevelPtr)
4175
{
4176
    long level;
4177
    const char *str;
4178
    Jim_CallFrame *framePtr;
4179
 
4180
    if (newLevelPtr) *newLevelPtr = interp->numLevels;
4181
    if (levelObjPtr) {
4182
        str = Jim_GetString(levelObjPtr, NULL);
4183
        if (str[0] == '#') {
4184
            char *endptr;
4185
            /* speedup for the toplevel (level #0) */
4186
            if (str[1] == '0' && str[2] == '\0') {
4187
                if (newLevelPtr) *newLevelPtr = 0;
4188
                *framePtrPtr = interp->topFramePtr;
4189
                return JIM_OK;
4190
            }
4191
 
4192
            level = strtol(str+1, &endptr, 0);
4193
            if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
4194
                goto badlevel;
4195
            /* An 'absolute' level is converted into the
4196
             * 'number of levels to go back' format. */
4197
            level = interp->numLevels - level;
4198
            if (level < 0) goto badlevel;
4199
        } else {
4200
            if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
4201
                goto badlevel;
4202
        }
4203
    } else {
4204
        str = "1"; /* Needed to format the error message. */
4205
        level = 1;
4206
    }
4207
    /* Lookup */
4208
    framePtr = interp->framePtr;
4209
    if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
4210
    while (level--) {
4211
        framePtr = framePtr->parentCallFrame;
4212
        if (framePtr == NULL) goto badlevel;
4213
    }
4214
    *framePtrPtr = framePtr;
4215
    return JIM_OK;
4216
badlevel:
4217
    Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4218
    Jim_AppendStrings(interp, Jim_GetResult(interp),
4219
            "bad level \"", str, "\"", NULL);
4220
    return JIM_ERR;
4221
}
4222
 
4223
/* Similar to Jim_GetCallFrameByLevel() but the level is specified
4224
 * as a relative integer like in the [info level ?level?] command. */
4225
static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
4226
        Jim_CallFrame **framePtrPtr)
4227
{
4228
    jim_wide level;
4229
    jim_wide relLevel; /* level relative to the current one. */
4230
    Jim_CallFrame *framePtr;
4231
 
4232
    if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
4233
        goto badlevel;
4234
    if (level > 0) {
4235
        /* An 'absolute' level is converted into the
4236
         * 'number of levels to go back' format. */
4237
        relLevel = interp->numLevels - level;
4238
    } else {
4239
        relLevel = -level;
4240
    }
4241
    /* Lookup */
4242
    framePtr = interp->framePtr;
4243
    while (relLevel--) {
4244
        framePtr = framePtr->parentCallFrame;
4245
        if (framePtr == NULL) goto badlevel;
4246
    }
4247
    *framePtrPtr = framePtr;
4248
    return JIM_OK;
4249
badlevel:
4250
    Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4251
    Jim_AppendStrings(interp, Jim_GetResult(interp),
4252
            "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
4253
    return JIM_ERR;
4254
}
4255
 
4256
static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
4257
{
4258
    Jim_Free((void*)interp->errorFileName);
4259
    interp->errorFileName = Jim_StrDup(filename);
4260
}
4261
 
4262
static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
4263
{
4264
    interp->errorLine = linenr;
4265
}
4266
 
4267
static void JimResetStackTrace(Jim_Interp *interp)
4268
{
4269
    Jim_DecrRefCount(interp, interp->stackTrace);
4270
    interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
4271
    Jim_IncrRefCount(interp->stackTrace);
4272
}
4273
 
4274
static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
4275
        const char *filename, int linenr)
4276
{
4277
    if (Jim_IsShared(interp->stackTrace)) {
4278
        interp->stackTrace =
4279
            Jim_DuplicateObj(interp, interp->stackTrace);
4280
        Jim_IncrRefCount(interp->stackTrace);
4281
    }
4282
    Jim_ListAppendElement(interp, interp->stackTrace,
4283
            Jim_NewStringObj(interp, procname, -1));
4284
    Jim_ListAppendElement(interp, interp->stackTrace,
4285
            Jim_NewStringObj(interp, filename, -1));
4286
    Jim_ListAppendElement(interp, interp->stackTrace,
4287
            Jim_NewIntObj(interp, linenr));
4288
}
4289
 
4290
int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
4291
{
4292
    AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
4293
    assocEntryPtr->delProc = delProc;
4294
    assocEntryPtr->data = data;
4295
    return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
4296
}
4297
 
4298
void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
4299
{
4300
    Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
4301
    if (entryPtr != NULL) {
4302
        AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
4303
        return assocEntryPtr->data;
4304
    }
4305
    return NULL;
4306
}
4307
 
4308
int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
4309
{
4310
    return Jim_DeleteHashEntry(&interp->assocData, key);
4311
}
4312
 
4313
int Jim_GetExitCode(Jim_Interp *interp) {
4314
    return interp->exitCode;
4315
}
4316
 
4317
FILE *Jim_SetStdin(Jim_Interp *interp, FILE *fp)
4318
{
4319
    if (fp != NULL) interp->stdin = fp;
4320
    return interp->stdin;
4321
}
4322
 
4323
FILE *Jim_SetStdout(Jim_Interp *interp, FILE *fp)
4324
{
4325
    if (fp != NULL) interp->stdout = fp;
4326
    return interp->stdout;
4327
}
4328
 
4329
FILE *Jim_SetStderr(Jim_Interp *interp, FILE *fp)
4330
{
4331
    if (fp != NULL) interp->stderr = fp;
4332
    return interp->stderr;
4333
}
4334
 
4335
/* -----------------------------------------------------------------------------
4336
 * Shared strings.
4337
 * Every interpreter has an hash table where to put shared dynamically
4338
 * allocate strings that are likely to be used a lot of times.
4339
 * For example, in the 'source' object type, there is a pointer to
4340
 * the filename associated with that object. Every script has a lot
4341
 * of this objects with the identical file name, so it is wise to share
4342
 * this info.
4343
 *
4344
 * The API is trivial: Jim_GetSharedString(interp, "foobar")
4345
 * returns the pointer to the shared string. Every time a reference
4346
 * to the string is no longer used, the user should call
4347
 * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
4348
 * a given string, it is removed from the hash table.
4349
 * ---------------------------------------------------------------------------*/
4350
const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
4351
{
4352
    Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4353
 
4354
    if (he == NULL) {
4355
        char *strCopy = Jim_StrDup(str);
4356
 
4357
        Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
4358
        return strCopy;
4359
    } else {
4360
        long refCount = (long) he->val;
4361
 
4362
        refCount++;
4363
        he->val = (void*) refCount;
4364
        return he->key;
4365
    }
4366
}
4367
 
4368
void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
4369
{
4370
    long refCount;
4371
    Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
4372
 
4373
    if (he == NULL)
4374
        Jim_Panic(interp,"Jim_ReleaseSharedString called with "
4375
              "unknown shared string '%s'", str);
4376
    refCount = (long) he->val;
4377
    refCount--;
4378
    if (refCount == 0) {
4379
        Jim_DeleteHashEntry(&interp->sharedStrings, str);
4380
    } else {
4381
        he->val = (void*) refCount;
4382
    }
4383
}
4384
 
4385
/* -----------------------------------------------------------------------------
4386
 * Integer object
4387
 * ---------------------------------------------------------------------------*/
4388
#define JIM_INTEGER_SPACE 24
4389
 
4390
static void UpdateStringOfInt(struct Jim_Obj *objPtr);
4391
static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
4392
 
4393
static Jim_ObjType intObjType = {
4394
    "int",
4395
    NULL,
4396
    NULL,
4397
    UpdateStringOfInt,
4398
    JIM_TYPE_NONE,
4399
};
4400
 
4401
void UpdateStringOfInt(struct Jim_Obj *objPtr)
4402
{
4403
    int len;
4404
    char buf[JIM_INTEGER_SPACE+1];
4405
 
4406
    len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
4407
    objPtr->bytes = Jim_Alloc(len+1);
4408
    memcpy(objPtr->bytes, buf, len+1);
4409
    objPtr->length = len;
4410
}
4411
 
4412
int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
4413
{
4414
    jim_wide wideValue;
4415
    const char *str;
4416
 
4417
    /* Get the string representation */
4418
    str = Jim_GetString(objPtr, NULL);
4419
    /* Try to convert into a jim_wide */
4420
    if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
4421
        if (flags & JIM_ERRMSG) {
4422
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4423
            Jim_AppendStrings(interp, Jim_GetResult(interp),
4424
                    "expected integer but got \"", str, "\"", NULL);
4425
        }
4426
        return JIM_ERR;
4427
    }
4428
    if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
4429
        errno == ERANGE) {
4430
        Jim_SetResultString(interp,
4431
            "Integer value too big to be represented", -1);
4432
        return JIM_ERR;
4433
    }
4434
    /* Free the old internal repr and set the new one. */
4435
    Jim_FreeIntRep(interp, objPtr);
4436
    objPtr->typePtr = &intObjType;
4437
    objPtr->internalRep.wideValue = wideValue;
4438
    return JIM_OK;
4439
}
4440
 
4441
int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
4442
{
4443
    if (objPtr->typePtr != &intObjType &&
4444
        SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
4445
        return JIM_ERR;
4446
    *widePtr = objPtr->internalRep.wideValue;
4447
    return JIM_OK;
4448
}
4449
 
4450
/* Get a wide but does not set an error if the format is bad. */
4451
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
4452
        jim_wide *widePtr)
4453
{
4454
    if (objPtr->typePtr != &intObjType &&
4455
        SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
4456
        return JIM_ERR;
4457
    *widePtr = objPtr->internalRep.wideValue;
4458
    return JIM_OK;
4459
}
4460
 
4461
int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
4462
{
4463
    jim_wide wideValue;
4464
    int retval;
4465
 
4466
    retval = Jim_GetWide(interp, objPtr, &wideValue);
4467
    if (retval == JIM_OK) {
4468
        *longPtr = (long) wideValue;
4469
        return JIM_OK;
4470
    }
4471
    return JIM_ERR;
4472
}
4473
 
4474
void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
4475
{
4476
    if (Jim_IsShared(objPtr))
4477
        Jim_Panic(interp,"Jim_SetWide called with shared object");
4478
    if (objPtr->typePtr != &intObjType) {
4479
        Jim_FreeIntRep(interp, objPtr);
4480
        objPtr->typePtr = &intObjType;
4481
    }
4482
    Jim_InvalidateStringRep(objPtr);
4483
    objPtr->internalRep.wideValue = wideValue;
4484
}
4485
 
4486
Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
4487
{
4488
    Jim_Obj *objPtr;
4489
 
4490
    objPtr = Jim_NewObj(interp);
4491
    objPtr->typePtr = &intObjType;
4492
    objPtr->bytes = NULL;
4493
    objPtr->internalRep.wideValue = wideValue;
4494
    return objPtr;
4495
}
4496
 
4497
/* -----------------------------------------------------------------------------
4498
 * Double object
4499
 * ---------------------------------------------------------------------------*/
4500
#define JIM_DOUBLE_SPACE 30
4501
 
4502
static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
4503
static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
4504
 
4505
static Jim_ObjType doubleObjType = {
4506
    "double",
4507
    NULL,
4508
    NULL,
4509
    UpdateStringOfDouble,
4510
    JIM_TYPE_NONE,
4511
};
4512
 
4513
void UpdateStringOfDouble(struct Jim_Obj *objPtr)
4514
{
4515
    int len;
4516
    char buf[JIM_DOUBLE_SPACE+1];
4517
 
4518
    len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
4519
    objPtr->bytes = Jim_Alloc(len+1);
4520
    memcpy(objPtr->bytes, buf, len+1);
4521
    objPtr->length = len;
4522
}
4523
 
4524
int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
4525
{
4526
    double doubleValue;
4527
    const char *str;
4528
 
4529
    /* Get the string representation */
4530
    str = Jim_GetString(objPtr, NULL);
4531
    /* Try to convert into a double */
4532
    if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
4533
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
4534
        Jim_AppendStrings(interp, Jim_GetResult(interp),
4535
                "expected number but got '", str, "'", NULL);
4536
        return JIM_ERR;
4537
    }
4538
    /* Free the old internal repr and set the new one. */
4539
    Jim_FreeIntRep(interp, objPtr);
4540
    objPtr->typePtr = &doubleObjType;
4541
    objPtr->internalRep.doubleValue = doubleValue;
4542
    return JIM_OK;
4543
}
4544
 
4545
int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
4546
{
4547
    if (objPtr->typePtr != &doubleObjType &&
4548
        SetDoubleFromAny(interp, objPtr) == JIM_ERR)
4549
        return JIM_ERR;
4550
    *doublePtr = objPtr->internalRep.doubleValue;
4551
    return JIM_OK;
4552
}
4553
 
4554
void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
4555
{
4556
    if (Jim_IsShared(objPtr))
4557
        Jim_Panic(interp,"Jim_SetDouble called with shared object");
4558
    if (objPtr->typePtr != &doubleObjType) {
4559
        Jim_FreeIntRep(interp, objPtr);
4560
        objPtr->typePtr = &doubleObjType;
4561
    }
4562
    Jim_InvalidateStringRep(objPtr);
4563
    objPtr->internalRep.doubleValue = doubleValue;
4564
}
4565
 
4566
Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
4567
{
4568
    Jim_Obj *objPtr;
4569
 
4570
    objPtr = Jim_NewObj(interp);
4571
    objPtr->typePtr = &doubleObjType;
4572
    objPtr->bytes = NULL;
4573
    objPtr->internalRep.doubleValue = doubleValue;
4574
    return objPtr;
4575
}
4576
 
4577
/* -----------------------------------------------------------------------------
4578
 * List object
4579
 * ---------------------------------------------------------------------------*/
4580
static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
4581
static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
4582
static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
4583
static void UpdateStringOfList(struct Jim_Obj *objPtr);
4584
static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
4585
 
4586
/* Note that while the elements of the list may contain references,
4587
 * the list object itself can't. This basically means that the
4588
 * list object string representation as a whole can't contain references
4589
 * that are not presents in the single elements. */
4590
static Jim_ObjType listObjType = {
4591
    "list",
4592
    FreeListInternalRep,
4593
    DupListInternalRep,
4594
    UpdateStringOfList,
4595
    JIM_TYPE_NONE,
4596
};
4597
 
4598
void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
4599
{
4600
    int i;
4601
 
4602
    for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4603
        Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
4604
    }
4605
    Jim_Free(objPtr->internalRep.listValue.ele);
4606
}
4607
 
4608
void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
4609
{
4610
    int i;
4611
    JIM_NOTUSED(interp);
4612
 
4613
    dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
4614
    dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
4615
    dupPtr->internalRep.listValue.ele =
4616
        Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
4617
    memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
4618
            sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
4619
    for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
4620
        Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
4621
    }
4622
    dupPtr->typePtr = &listObjType;
4623
}
4624
 
4625
/* The following function checks if a given string can be encoded
4626
 * into a list element without any kind of quoting, surrounded by braces,
4627
 * or using escapes to quote. */
4628
#define JIM_ELESTR_SIMPLE 0
4629
#define JIM_ELESTR_BRACE 1
4630
#define JIM_ELESTR_QUOTE 2
4631
static int ListElementQuotingType(const char *s, int len)
4632
{
4633
    int i, level, trySimple = 1;
4634
 
4635
    /* Try with the SIMPLE case */
4636
    if (len == 0) return JIM_ELESTR_BRACE;
4637
    if (s[0] == '"' || s[0] == '{') {
4638
        trySimple = 0;
4639
        goto testbrace;
4640
    }
4641
    for (i = 0; i < len; i++) {
4642
        switch(s[i]) {
4643
        case ' ':
4644
        case '$':
4645
        case '"':
4646
        case '[':
4647
        case ']':
4648
        case ';':
4649
        case '\\':
4650
        case '\r':
4651
        case '\n':
4652
        case '\t':
4653
        case '\f':
4654
        case '\v':
4655
            trySimple = 0;
4656
        case '{':
4657
        case '}':
4658
            goto testbrace;
4659
        }
4660
    }
4661
    return JIM_ELESTR_SIMPLE;
4662
 
4663
testbrace:
4664
    /* Test if it's possible to do with braces */
4665
    if (s[len-1] == '\\' ||
4666
        s[len-1] == ']') return JIM_ELESTR_QUOTE;
4667
    level = 0;
4668
    for (i = 0; i < len; i++) {
4669
        switch(s[i]) {
4670
        case '{': level++; break;
4671
        case '}': level--;
4672
              if (level < 0) return JIM_ELESTR_QUOTE;
4673
              break;
4674
        case '\\':
4675
              if (s[i+1] == '\n')
4676
                  return JIM_ELESTR_QUOTE;
4677
              else
4678
                  if (s[i+1] != '\0') i++;
4679
              break;
4680
        }
4681
    }
4682
    if (level == 0) {
4683
        if (!trySimple) return JIM_ELESTR_BRACE;
4684
        for (i = 0; i < len; i++) {
4685
            switch(s[i]) {
4686
            case ' ':
4687
            case '$':
4688
            case '"':
4689
            case '[':
4690
            case ']':
4691
            case ';':
4692
            case '\\':
4693
            case '\r':
4694
            case '\n':
4695
            case '\t':
4696
            case '\f':
4697
            case '\v':
4698
                return JIM_ELESTR_BRACE;
4699
                break;
4700
            }
4701
        }
4702
        return JIM_ELESTR_SIMPLE;
4703
    }
4704
    return JIM_ELESTR_QUOTE;
4705
}
4706
 
4707
/* Returns the malloc-ed representation of a string
4708
 * using backslash to quote special chars. */
4709
char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
4710
{
4711
    char *q = Jim_Alloc(len*2+1), *p;
4712
 
4713
    p = q;
4714
    while(*s) {
4715
        switch (*s) {
4716
        case ' ':
4717
        case '$':
4718
        case '"':
4719
        case '[':
4720
        case ']':
4721
        case '{':
4722
        case '}':
4723
        case ';':
4724
        case '\\':
4725
            *p++ = '\\';
4726
            *p++ = *s++;
4727
            break;
4728
        case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
4729
        case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
4730
        case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
4731
        case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
4732
        case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
4733
        default:
4734
            *p++ = *s++;
4735
            break;
4736
        }
4737
    }
4738
    *p = '\0';
4739
    *qlenPtr = p-q;
4740
    return q;
4741
}
4742
 
4743
void UpdateStringOfList(struct Jim_Obj *objPtr)
4744
{
4745
    int i, bufLen, realLength;
4746
    const char *strRep;
4747
    char *p;
4748
    int *quotingType;
4749
    Jim_Obj **ele = objPtr->internalRep.listValue.ele;
4750
 
4751
    /* (Over) Estimate the space needed. */
4752
    quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len+1);
4753
    bufLen = 0;
4754
    for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4755
        int len;
4756
 
4757
        strRep = Jim_GetString(ele[i], &len);
4758
        quotingType[i] = ListElementQuotingType(strRep, len);
4759
        switch (quotingType[i]) {
4760
        case JIM_ELESTR_SIMPLE: bufLen += len; break;
4761
        case JIM_ELESTR_BRACE: bufLen += len+2; break;
4762
        case JIM_ELESTR_QUOTE: bufLen += len*2; break;
4763
        }
4764
        bufLen++; /* elements separator. */
4765
    }
4766
    bufLen++;
4767
 
4768
    /* Generate the string rep. */
4769
    p = objPtr->bytes = Jim_Alloc(bufLen+1);
4770
    realLength = 0;
4771
    for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
4772
        int len, qlen;
4773
        const char *strRep = Jim_GetString(ele[i], &len);
4774
        char *q;
4775
 
4776
        switch(quotingType[i]) {
4777
        case JIM_ELESTR_SIMPLE:
4778
            memcpy(p, strRep, len);
4779
            p += len;
4780
            realLength += len;
4781
            break;
4782
        case JIM_ELESTR_BRACE:
4783
            *p++ = '{';
4784
            memcpy(p, strRep, len);
4785
            p += len;
4786
            *p++ = '}';
4787
            realLength += len+2;
4788
            break;
4789
        case JIM_ELESTR_QUOTE:
4790
            q = BackslashQuoteString(strRep, len, &qlen);
4791
            memcpy(p, q, qlen);
4792
            Jim_Free(q);
4793
            p += qlen;
4794
            realLength += qlen;
4795
            break;
4796
        }
4797
        /* Add a separating space */
4798
        if (i+1 != objPtr->internalRep.listValue.len) {
4799
            *p++ = ' ';
4800
            realLength ++;
4801
        }
4802
    }
4803
    *p = '\0'; /* nul term. */
4804
    objPtr->length = realLength;
4805
    Jim_Free(quotingType);
4806
}
4807
 
4808
int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
4809
{
4810
    struct JimParserCtx parser;
4811
    const char *str;
4812
    int strLen;
4813
 
4814
    /* Get the string representation */
4815
    str = Jim_GetString(objPtr, &strLen);
4816
 
4817
    /* Free the old internal repr just now and initialize the
4818
     * new one just now. The string->list conversion can't fail. */
4819
    Jim_FreeIntRep(interp, objPtr);
4820
    objPtr->typePtr = &listObjType;
4821
    objPtr->internalRep.listValue.len = 0;
4822
    objPtr->internalRep.listValue.maxLen = 0;
4823
    objPtr->internalRep.listValue.ele = NULL;
4824
 
4825
    /* Convert into a list */
4826
    JimParserInit(&parser, str, strLen, 1);
4827
    while(!JimParserEof(&parser)) {
4828
        char *token;
4829
        int tokenLen, type;
4830
        Jim_Obj *elementPtr;
4831
 
4832
        JimParseList(&parser);
4833
        if (JimParserTtype(&parser) != JIM_TT_STR &&
4834
            JimParserTtype(&parser) != JIM_TT_ESC)
4835
            continue;
4836
        token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
4837
        elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
4838
        ListAppendElement(objPtr, elementPtr);
4839
    }
4840
    return JIM_OK;
4841
}
4842
 
4843
Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
4844
        int len)
4845
{
4846
    Jim_Obj *objPtr;
4847
    int i;
4848
 
4849
    objPtr = Jim_NewObj(interp);
4850
    objPtr->typePtr = &listObjType;
4851
    objPtr->bytes = NULL;
4852
    objPtr->internalRep.listValue.ele = NULL;
4853
    objPtr->internalRep.listValue.len = 0;
4854
    objPtr->internalRep.listValue.maxLen = 0;
4855
    for (i = 0; i < len; i++) {
4856
        ListAppendElement(objPtr, elements[i]);
4857
    }
4858
    return objPtr;
4859
}
4860
 
4861
/* Return a vector of Jim_Obj with the elements of a Jim list, and the
4862
 * length of the vector. Note that the user of this function should make
4863
 * sure that the list object can't shimmer while the vector returned
4864
 * is in use, this vector is the one stored inside the internal representation
4865
 * of the list object. This function is not exported, extensions should
4866
 * always access to the List object elements using Jim_ListIndex(). */
4867
static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
4868
        Jim_Obj ***listVec)
4869
{
4870
    Jim_ListLength(interp, listObj, argc);
4871
    assert(listObj->typePtr == &listObjType);
4872
    *listVec = listObj->internalRep.listValue.ele;
4873
}
4874
 
4875
/* ListSortElements type values */
4876
enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
4877
      JIM_LSORT_NOCASE_DECR};
4878
 
4879
/* Sort the internal rep of a list. */
4880
static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4881
{
4882
    return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
4883
}
4884
 
4885
static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4886
{
4887
    return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
4888
}
4889
 
4890
static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4891
{
4892
    return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
4893
}
4894
 
4895
static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
4896
{
4897
    return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
4898
}
4899
 
4900
/* Sort a list *in place*. MUST be called with non-shared objects. */
4901
static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
4902
{
4903
    typedef int (qsort_comparator)(const void *, const void *);
4904
    int (*fn)(Jim_Obj**, Jim_Obj**);
4905
    Jim_Obj **vector;
4906
    int len;
4907
 
4908
    if (Jim_IsShared(listObjPtr))
4909
        Jim_Panic(interp,"Jim_ListSortElements called with shared object");
4910
    if (listObjPtr->typePtr != &listObjType)
4911
        SetListFromAny(interp, listObjPtr);
4912
 
4913
    vector = listObjPtr->internalRep.listValue.ele;
4914
    len = listObjPtr->internalRep.listValue.len;
4915
    switch (type) {
4916
        case JIM_LSORT_ASCII: fn = ListSortString;  break;
4917
        case JIM_LSORT_NOCASE: fn = ListSortStringNoCase;  break;
4918
        case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr;  break;
4919
        case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr;  break;
4920
        default:
4921
            fn = NULL; /* avoid warning */
4922
            Jim_Panic(interp,"ListSort called with invalid sort type");
4923
    }
4924
    qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
4925
    Jim_InvalidateStringRep(listObjPtr);
4926
}
4927
 
4928
/* This is the low-level function to append an element to a list.
4929
 * The higher-level Jim_ListAppendElement() performs shared object
4930
 * check and invalidate the string repr. This version is used
4931
 * in the internals of the List Object and is not exported.
4932
 *
4933
 * NOTE: this function can be called only against objects
4934
 * with internal type of List. */
4935
void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
4936
{
4937
    int requiredLen = listPtr->internalRep.listValue.len + 1;
4938
 
4939
    if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4940
        int maxLen = requiredLen * 2;
4941
 
4942
        listPtr->internalRep.listValue.ele =
4943
            Jim_Realloc(listPtr->internalRep.listValue.ele,
4944
                    sizeof(Jim_Obj*)*maxLen);
4945
        listPtr->internalRep.listValue.maxLen = maxLen;
4946
    }
4947
    listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
4948
        objPtr;
4949
    listPtr->internalRep.listValue.len ++;
4950
    Jim_IncrRefCount(objPtr);
4951
}
4952
 
4953
/* This is the low-level function to insert elements into a list.
4954
 * The higher-level Jim_ListInsertElements() performs shared object
4955
 * check and invalidate the string repr. This version is used
4956
 * in the internals of the List Object and is not exported.
4957
 *
4958
 * NOTE: this function can be called only against objects
4959
 * with internal type of List. */
4960
void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
4961
        Jim_Obj *const *elemVec)
4962
{
4963
    int currentLen = listPtr->internalRep.listValue.len;
4964
    int requiredLen = currentLen + elemc;
4965
    int i;
4966
    Jim_Obj **point;
4967
 
4968
    if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4969
        int maxLen = requiredLen * 2;
4970
 
4971
        listPtr->internalRep.listValue.ele =
4972
            Jim_Realloc(listPtr->internalRep.listValue.ele,
4973
                    sizeof(Jim_Obj*)*maxLen);
4974
        listPtr->internalRep.listValue.maxLen = maxLen;
4975
    }
4976
    point = listPtr->internalRep.listValue.ele + index;
4977
    memmove(point+elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
4978
    for (i=0; i < elemc; ++i) {
4979
        point[i] = elemVec[i];
4980
        Jim_IncrRefCount(point[i]);
4981
    }
4982
    listPtr->internalRep.listValue.len += elemc;
4983
}
4984
 
4985
/* Appends every element of appendListPtr into listPtr.
4986
 * Both have to be of the list type. */
4987
void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
4988
{
4989
    int i, oldLen = listPtr->internalRep.listValue.len;
4990
    int appendLen = appendListPtr->internalRep.listValue.len;
4991
    int requiredLen = oldLen + appendLen;
4992
 
4993
    if (requiredLen > listPtr->internalRep.listValue.maxLen) {
4994
        int maxLen = requiredLen * 2;
4995
 
4996
        listPtr->internalRep.listValue.ele =
4997
            Jim_Realloc(listPtr->internalRep.listValue.ele,
4998
                    sizeof(Jim_Obj*)*maxLen);
4999
        listPtr->internalRep.listValue.maxLen = maxLen;
5000
    }
5001
    for (i = 0; i < appendLen; i++) {
5002
        Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
5003
        listPtr->internalRep.listValue.ele[oldLen+i] = objPtr;
5004
        Jim_IncrRefCount(objPtr);
5005
    }
5006
    listPtr->internalRep.listValue.len += appendLen;
5007
}
5008
 
5009
void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
5010
{
5011
    if (Jim_IsShared(listPtr))
5012
        Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
5013
    if (listPtr->typePtr != &listObjType)
5014
        SetListFromAny(interp, listPtr);
5015
    Jim_InvalidateStringRep(listPtr);
5016
    ListAppendElement(listPtr, objPtr);
5017
}
5018
 
5019
void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
5020
{
5021
    if (Jim_IsShared(listPtr))
5022
        Jim_Panic(interp,"Jim_ListAppendList called with shared object");
5023
    if (listPtr->typePtr != &listObjType)
5024
        SetListFromAny(interp, listPtr);
5025
    Jim_InvalidateStringRep(listPtr);
5026
    ListAppendList(listPtr, appendListPtr);
5027
}
5028
 
5029
void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
5030
{
5031
    if (listPtr->typePtr != &listObjType)
5032
        SetListFromAny(interp, listPtr);
5033
    *intPtr = listPtr->internalRep.listValue.len;
5034
}
5035
 
5036
void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5037
        int objc, Jim_Obj *const *objVec)
5038
{
5039
    if (Jim_IsShared(listPtr))
5040
        Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
5041
    if (listPtr->typePtr != &listObjType)
5042
        SetListFromAny(interp, listPtr);
5043
    if (index >= 0 && index > listPtr->internalRep.listValue.len)
5044
        index = listPtr->internalRep.listValue.len;
5045
    else if (index < 0 )
5046
        index = 0;
5047
    Jim_InvalidateStringRep(listPtr);
5048
    ListInsertElements(listPtr, index, objc, objVec);
5049
}
5050
 
5051
int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5052
        Jim_Obj **objPtrPtr, int flags)
5053
{
5054
    if (listPtr->typePtr != &listObjType)
5055
        SetListFromAny(interp, listPtr);
5056
    if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5057
        (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5058
        if (flags & JIM_ERRMSG) {
5059
            Jim_SetResultString(interp,
5060
                "list index out of range", -1);
5061
        }
5062
        return JIM_ERR;
5063
    }
5064
    if (index < 0)
5065
        index = listPtr->internalRep.listValue.len+index;
5066
    *objPtrPtr = listPtr->internalRep.listValue.ele[index];
5067
    return JIM_OK;
5068
}
5069
 
5070
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
5071
        Jim_Obj *newObjPtr, int flags)
5072
{
5073
    if (listPtr->typePtr != &listObjType)
5074
        SetListFromAny(interp, listPtr);
5075
    if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
5076
        (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
5077
        if (flags & JIM_ERRMSG) {
5078
            Jim_SetResultString(interp,
5079
                "list index out of range", -1);
5080
        }
5081
        return JIM_ERR;
5082
    }
5083
    if (index < 0)
5084
        index = listPtr->internalRep.listValue.len+index;
5085
    Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
5086
    listPtr->internalRep.listValue.ele[index] = newObjPtr;
5087
    Jim_IncrRefCount(newObjPtr);
5088
    return JIM_OK;
5089
}
5090
 
5091
/* Modify the list stored into the variable named 'varNamePtr'
5092
 * setting the element specified by the 'indexc' indexes objects in 'indexv',
5093
 * with the new element 'newObjptr'. */
5094
int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
5095
        Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
5096
{
5097
    Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
5098
    int shared, i, index;
5099
 
5100
    varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5101
    if (objPtr == NULL)
5102
        return JIM_ERR;
5103
    if ((shared = Jim_IsShared(objPtr)))
5104
        varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5105
    for (i = 0; i < indexc-1; i++) {
5106
        listObjPtr = objPtr;
5107
        if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
5108
            goto err;
5109
        if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
5110
                    JIM_ERRMSG) != JIM_OK) {
5111
            goto err;
5112
        }
5113
        if (Jim_IsShared(objPtr)) {
5114
            objPtr = Jim_DuplicateObj(interp, objPtr);
5115
            ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
5116
        }
5117
        Jim_InvalidateStringRep(listObjPtr);
5118
    }
5119
    if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
5120
        goto err;
5121
    if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
5122
        goto err;
5123
    Jim_InvalidateStringRep(objPtr);
5124
    Jim_InvalidateStringRep(varObjPtr);
5125
    if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5126
        goto err;
5127
    Jim_SetResult(interp, varObjPtr);
5128
    return JIM_OK;
5129
err:
5130
    if (shared) {
5131
        Jim_FreeNewObj(interp, varObjPtr);
5132
    }
5133
    return JIM_ERR;
5134
}
5135
 
5136
Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
5137
{
5138
    int i;
5139
 
5140
    /* If all the objects in objv are lists without string rep.
5141
     * it's possible to return a list as result, that's the
5142
     * concatenation of all the lists. */
5143
    for (i = 0; i < objc; i++) {
5144
        if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
5145
            break;
5146
    }
5147
    if (i == objc) {
5148
        Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
5149
        for (i = 0; i < objc; i++)
5150
            Jim_ListAppendList(interp, objPtr, objv[i]);
5151
        return objPtr;
5152
    } else {
5153
        /* Else... we have to glue strings together */
5154
        int len = 0, objLen;
5155
        char *bytes, *p;
5156
 
5157
        /* Compute the length */
5158
        for (i = 0; i < objc; i++) {
5159
            Jim_GetString(objv[i], &objLen);
5160
            len += objLen;
5161
        }
5162
        if (objc) len += objc-1;
5163
        /* Create the string rep, and a stinrg object holding it. */
5164
        p = bytes = Jim_Alloc(len+1);
5165
        for (i = 0; i < objc; i++) {
5166
            const char *s = Jim_GetString(objv[i], &objLen);
5167
            while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
5168
            {
5169
                s++; objLen--; len--;
5170
            }
5171
            while (objLen && (s[objLen-1] == ' ' ||
5172
                s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
5173
                objLen--; len--;
5174
            }
5175
            memcpy(p, s, objLen);
5176
            p += objLen;
5177
            if (objLen && i+1 != objc) {
5178
                *p++ = ' ';
5179
            } else if (i+1 != objc) {
5180
                /* Drop the space calcuated for this
5181
                 * element that is instead null. */
5182
                len--;
5183
            }
5184
        }
5185
        *p = '\0';
5186
        return Jim_NewStringObjNoAlloc(interp, bytes, len);
5187
    }
5188
}
5189
 
5190
/* Returns a list composed of the elements in the specified range.
5191
 * first and start are directly accepted as Jim_Objects and
5192
 * processed for the end?-index? case. */
5193
Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
5194
{
5195
    int first, last;
5196
    int len, rangeLen;
5197
 
5198
    if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
5199
        Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
5200
        return NULL;
5201
    Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
5202
    first = JimRelToAbsIndex(len, first);
5203
    last = JimRelToAbsIndex(len, last);
5204
    JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
5205
    return Jim_NewListObj(interp,
5206
            listObjPtr->internalRep.listValue.ele+first, rangeLen);
5207
}
5208
 
5209
/* -----------------------------------------------------------------------------
5210
 * Dict object
5211
 * ---------------------------------------------------------------------------*/
5212
static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
5213
static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
5214
static void UpdateStringOfDict(struct Jim_Obj *objPtr);
5215
static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5216
 
5217
/* Dict HashTable Type.
5218
 *
5219
 * Keys and Values are Jim objects. */
5220
 
5221
unsigned int JimObjectHTHashFunction(const void *key)
5222
{
5223
    const char *str;
5224
    Jim_Obj *objPtr = (Jim_Obj*) key;
5225
    int len, h;
5226
 
5227
    str = Jim_GetString(objPtr, &len);
5228
    h = Jim_GenHashFunction((unsigned char*)str, len);
5229
    return h;
5230
}
5231
 
5232
int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
5233
{
5234
    JIM_NOTUSED(privdata);
5235
 
5236
    return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
5237
}
5238
 
5239
static void JimObjectHTKeyValDestructor(void *interp, void *val)
5240
{
5241
    Jim_Obj *objPtr = val;
5242
 
5243
    Jim_DecrRefCount(interp, objPtr);
5244
}
5245
 
5246
static Jim_HashTableType JimDictHashTableType = {
5247
    JimObjectHTHashFunction,            /* hash function */
5248
    NULL,                               /* key dup */
5249
    NULL,                               /* val dup */
5250
    JimObjectHTKeyCompare,              /* key compare */
5251
    (void(*)(void*, const void*))       /* ATTENTION: const cast */
5252
        JimObjectHTKeyValDestructor,    /* key destructor */
5253
    JimObjectHTKeyValDestructor         /* val destructor */
5254
};
5255
 
5256
/* Note that while the elements of the dict may contain references,
5257
 * the list object itself can't. This basically means that the
5258
 * dict object string representation as a whole can't contain references
5259
 * that are not presents in the single elements. */
5260
static Jim_ObjType dictObjType = {
5261
    "dict",
5262
    FreeDictInternalRep,
5263
    DupDictInternalRep,
5264
    UpdateStringOfDict,
5265
    JIM_TYPE_NONE,
5266
};
5267
 
5268
void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
5269
{
5270
    JIM_NOTUSED(interp);
5271
 
5272
    Jim_FreeHashTable(objPtr->internalRep.ptr);
5273
    Jim_Free(objPtr->internalRep.ptr);
5274
}
5275
 
5276
void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
5277
{
5278
    Jim_HashTable *ht, *dupHt;
5279
    Jim_HashTableIterator *htiter;
5280
    Jim_HashEntry *he;
5281
 
5282
    /* Create a new hash table */
5283
    ht = srcPtr->internalRep.ptr;
5284
    dupHt = Jim_Alloc(sizeof(*dupHt));
5285
    Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
5286
    if (ht->size != 0)
5287
        Jim_ExpandHashTable(dupHt, ht->size);
5288
    /* Copy every element from the source to the dup hash table */
5289
    htiter = Jim_GetHashTableIterator(ht);
5290
    while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5291
        const Jim_Obj *keyObjPtr = he->key;
5292
        Jim_Obj *valObjPtr = he->val;
5293
 
5294
        Jim_IncrRefCount((Jim_Obj*)keyObjPtr);  /* ATTENTION: const cast */
5295
        Jim_IncrRefCount(valObjPtr);
5296
        Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
5297
    }
5298
    Jim_FreeHashTableIterator(htiter);
5299
 
5300
    dupPtr->internalRep.ptr = dupHt;
5301
    dupPtr->typePtr = &dictObjType;
5302
}
5303
 
5304
void UpdateStringOfDict(struct Jim_Obj *objPtr)
5305
{
5306
    int i, bufLen, realLength;
5307
    const char *strRep;
5308
    char *p;
5309
    int *quotingType, objc;
5310
    Jim_HashTable *ht;
5311
    Jim_HashTableIterator *htiter;
5312
    Jim_HashEntry *he;
5313
    Jim_Obj **objv;
5314
 
5315
    /* Trun the hash table into a flat vector of Jim_Objects. */
5316
    ht = objPtr->internalRep.ptr;
5317
    objc = ht->used*2;
5318
    objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
5319
    htiter = Jim_GetHashTableIterator(ht);
5320
    i = 0;
5321
    while ((he = Jim_NextHashEntry(htiter)) != NULL) {
5322
        objv[i++] = (Jim_Obj*)he->key;  /* ATTENTION: const cast */
5323
        objv[i++] = he->val;
5324
    }
5325
    Jim_FreeHashTableIterator(htiter);
5326
    /* (Over) Estimate the space needed. */
5327
    quotingType = Jim_Alloc(sizeof(int)*objc);
5328
    bufLen = 0;
5329
    for (i = 0; i < objc; i++) {
5330
        int len;
5331
 
5332
        strRep = Jim_GetString(objv[i], &len);
5333
        quotingType[i] = ListElementQuotingType(strRep, len);
5334
        switch (quotingType[i]) {
5335
        case JIM_ELESTR_SIMPLE: bufLen += len; break;
5336
        case JIM_ELESTR_BRACE: bufLen += len+2; break;
5337
        case JIM_ELESTR_QUOTE: bufLen += len*2; break;
5338
        }
5339
        bufLen++; /* elements separator. */
5340
    }
5341
    bufLen++;
5342
 
5343
    /* Generate the string rep. */
5344
    p = objPtr->bytes = Jim_Alloc(bufLen+1);
5345
    realLength = 0;
5346
    for (i = 0; i < objc; i++) {
5347
        int len, qlen;
5348
        const char *strRep = Jim_GetString(objv[i], &len);
5349
        char *q;
5350
 
5351
        switch(quotingType[i]) {
5352
        case JIM_ELESTR_SIMPLE:
5353
            memcpy(p, strRep, len);
5354
            p += len;
5355
            realLength += len;
5356
            break;
5357
        case JIM_ELESTR_BRACE:
5358
            *p++ = '{';
5359
            memcpy(p, strRep, len);
5360
            p += len;
5361
            *p++ = '}';
5362
            realLength += len+2;
5363
            break;
5364
        case JIM_ELESTR_QUOTE:
5365
            q = BackslashQuoteString(strRep, len, &qlen);
5366
            memcpy(p, q, qlen);
5367
            Jim_Free(q);
5368
            p += qlen;
5369
            realLength += qlen;
5370
            break;
5371
        }
5372
        /* Add a separating space */
5373
        if (i+1 != objc) {
5374
            *p++ = ' ';
5375
            realLength ++;
5376
        }
5377
    }
5378
    *p = '\0'; /* nul term. */
5379
    objPtr->length = realLength;
5380
    Jim_Free(quotingType);
5381
    Jim_Free(objv);
5382
}
5383
 
5384
int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
5385
{
5386
    struct JimParserCtx parser;
5387
    Jim_HashTable *ht;
5388
    Jim_Obj *objv[2];
5389
    const char *str;
5390
    int i, strLen;
5391
 
5392
    /* Get the string representation */
5393
    str = Jim_GetString(objPtr, &strLen);
5394
 
5395
    /* Free the old internal repr just now and initialize the
5396
     * new one just now. The string->list conversion can't fail. */
5397
    Jim_FreeIntRep(interp, objPtr);
5398
    ht = Jim_Alloc(sizeof(*ht));
5399
    Jim_InitHashTable(ht, &JimDictHashTableType, interp);
5400
    objPtr->typePtr = &dictObjType;
5401
    objPtr->internalRep.ptr = ht;
5402
 
5403
    /* Convert into a dict */
5404
    JimParserInit(&parser, str, strLen, 1);
5405
    i = 0;
5406
    while(!JimParserEof(&parser)) {
5407
        char *token;
5408
        int tokenLen, type;
5409
 
5410
        JimParseList(&parser);
5411
        if (JimParserTtype(&parser) != JIM_TT_STR &&
5412
            JimParserTtype(&parser) != JIM_TT_ESC)
5413
            continue;
5414
        token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
5415
        objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
5416
        if (i == 2) {
5417
            i = 0;
5418
            Jim_IncrRefCount(objv[0]);
5419
            Jim_IncrRefCount(objv[1]);
5420
            if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
5421
                Jim_HashEntry *he;
5422
                he = Jim_FindHashEntry(ht, objv[0]);
5423
                Jim_DecrRefCount(interp, objv[0]);
5424
                /* ATTENTION: const cast */
5425
                Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5426
                he->val = objv[1];
5427
            }
5428
        }
5429
    }
5430
    if (i) {
5431
        Jim_FreeNewObj(interp, objv[0]);
5432
        objPtr->typePtr = NULL;
5433
        Jim_FreeHashTable(ht);
5434
        Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
5435
        return JIM_ERR;
5436
    }
5437
    return JIM_OK;
5438
}
5439
 
5440
/* Dict object API */
5441
 
5442
/* Add an element to a dict. objPtr must be of the "dict" type.
5443
 * The higer-level exported function is Jim_DictAddElement().
5444
 * If an element with the specified key already exists, the value
5445
 * associated is replaced with the new one.
5446
 *
5447
 * if valueObjPtr == NULL, the key is instead removed if it exists. */
5448
static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5449
        Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5450
{
5451
    Jim_HashTable *ht = objPtr->internalRep.ptr;
5452
 
5453
    if (valueObjPtr == NULL) { /* unset */
5454
        Jim_DeleteHashEntry(ht, keyObjPtr);
5455
        return;
5456
    }
5457
    Jim_IncrRefCount(keyObjPtr);
5458
    Jim_IncrRefCount(valueObjPtr);
5459
    if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
5460
        Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
5461
        Jim_DecrRefCount(interp, keyObjPtr);
5462
        /* ATTENTION: const cast */
5463
        Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
5464
        he->val = valueObjPtr;
5465
    }
5466
}
5467
 
5468
/* Add an element, higher-level interface for DictAddElement().
5469
 * If valueObjPtr == NULL, the key is removed if it exists. */
5470
int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
5471
        Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
5472
{
5473
    if (Jim_IsShared(objPtr))
5474
        Jim_Panic(interp,"Jim_DictAddElement called with shared object");
5475
    if (objPtr->typePtr != &dictObjType) {
5476
        if (SetDictFromAny(interp, objPtr) != JIM_OK)
5477
            return JIM_ERR;
5478
    }
5479
    DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
5480
    Jim_InvalidateStringRep(objPtr);
5481
    return JIM_OK;
5482
}
5483
 
5484
Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
5485
{
5486
    Jim_Obj *objPtr;
5487
    int i;
5488
 
5489
    if (len % 2)
5490
        Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
5491
 
5492
    objPtr = Jim_NewObj(interp);
5493
    objPtr->typePtr = &dictObjType;
5494
    objPtr->bytes = NULL;
5495
    objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
5496
    Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
5497
    for (i = 0; i < len; i += 2)
5498
        DictAddElement(interp, objPtr, elements[i], elements[i+1]);
5499
    return objPtr;
5500
}
5501
 
5502
/* Return the value associated to the specified dict key */
5503
int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
5504
        Jim_Obj **objPtrPtr, int flags)
5505
{
5506
    Jim_HashEntry *he;
5507
    Jim_HashTable *ht;
5508
 
5509
    if (dictPtr->typePtr != &dictObjType) {
5510
        if (SetDictFromAny(interp, dictPtr) != JIM_OK)
5511
            return JIM_ERR;
5512
    }
5513
    ht = dictPtr->internalRep.ptr;
5514
    if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
5515
        if (flags & JIM_ERRMSG) {
5516
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5517
            Jim_AppendStrings(interp, Jim_GetResult(interp),
5518
                    "key \"", Jim_GetString(keyPtr, NULL),
5519
                    "\" not found in dictionary", NULL);
5520
        }
5521
        return JIM_ERR;
5522
    }
5523
    *objPtrPtr = he->val;
5524
    return JIM_OK;
5525
}
5526
 
5527
/* Return the value associated to the specified dict keys */
5528
int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
5529
        Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
5530
{
5531
    Jim_Obj *objPtr;
5532
    int i;
5533
 
5534
    if (keyc == 0) {
5535
        *objPtrPtr = dictPtr;
5536
        return JIM_OK;
5537
    }
5538
 
5539
    for (i = 0; i < keyc; i++) {
5540
        if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
5541
                != JIM_OK)
5542
            return JIM_ERR;
5543
        dictPtr = objPtr;
5544
    }
5545
    *objPtrPtr = objPtr;
5546
    return JIM_OK;
5547
}
5548
 
5549
/* Modify the dict stored into the variable named 'varNamePtr'
5550
 * setting the element specified by the 'keyc' keys objects in 'keyv',
5551
 * with the new value of the element 'newObjPtr'.
5552
 *
5553
 * If newObjPtr == NULL the operation is to remove the given key
5554
 * from the dictionary. */
5555
int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
5556
        Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
5557
{
5558
    Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
5559
    int shared, i;
5560
 
5561
    varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
5562
    if (objPtr == NULL) {
5563
        if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
5564
            return JIM_ERR;
5565
        varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
5566
        if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
5567
            Jim_FreeNewObj(interp, varObjPtr);
5568
            return JIM_ERR;
5569
        }
5570
    }
5571
    if ((shared = Jim_IsShared(objPtr)))
5572
        varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
5573
    for (i = 0; i < keyc-1; i++) {
5574
        dictObjPtr = objPtr;
5575
 
5576
        /* Check if it's a valid dictionary */
5577
        if (dictObjPtr->typePtr != &dictObjType) {
5578
            if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
5579
                goto err;
5580
        }
5581
        /* Check if the given key exists. */
5582
        Jim_InvalidateStringRep(dictObjPtr);
5583
        if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
5584
            newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
5585
        {
5586
            /* This key exists at the current level.
5587
             * Make sure it's not shared!. */
5588
            if (Jim_IsShared(objPtr)) {
5589
                objPtr = Jim_DuplicateObj(interp, objPtr);
5590
                DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5591
            }
5592
        } else {
5593
            /* Key not found. If it's an [unset] operation
5594
             * this is an error. Only the last key may not
5595
             * exist. */
5596
            if (newObjPtr == NULL)
5597
                goto err;
5598
            /* Otherwise set an empty dictionary
5599
             * as key's value. */
5600
            objPtr = Jim_NewDictObj(interp, NULL, 0);
5601
            DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
5602
        }
5603
    }
5604
    if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
5605
            != JIM_OK)
5606
        goto err;
5607
    Jim_InvalidateStringRep(objPtr);
5608
    Jim_InvalidateStringRep(varObjPtr);
5609
    if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
5610
        goto err;
5611
    Jim_SetResult(interp, varObjPtr);
5612
    return JIM_OK;
5613
err:
5614
    if (shared) {
5615
        Jim_FreeNewObj(interp, varObjPtr);
5616
    }
5617
    return JIM_ERR;
5618
}
5619
 
5620
/* -----------------------------------------------------------------------------
5621
 * Index object
5622
 * ---------------------------------------------------------------------------*/
5623
static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
5624
static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
5625
 
5626
static Jim_ObjType indexObjType = {
5627
    "index",
5628
    NULL,
5629
    NULL,
5630
    UpdateStringOfIndex,
5631
    JIM_TYPE_NONE,
5632
};
5633
 
5634
void UpdateStringOfIndex(struct Jim_Obj *objPtr)
5635
{
5636
    int len;
5637
    char buf[JIM_INTEGER_SPACE+1];
5638
 
5639
    if (objPtr->internalRep.indexValue >= 0)
5640
        len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
5641
    else if (objPtr->internalRep.indexValue == -1)
5642
        len = sprintf(buf, "end");
5643
    else {
5644
        len = sprintf(buf, "end%d", objPtr->internalRep.indexValue+1);
5645
    }
5646
    objPtr->bytes = Jim_Alloc(len+1);
5647
    memcpy(objPtr->bytes, buf, len+1);
5648
    objPtr->length = len;
5649
}
5650
 
5651
int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5652
{
5653
    int index, end = 0;
5654
    const char *str;
5655
 
5656
    /* Get the string representation */
5657
    str = Jim_GetString(objPtr, NULL);
5658
    /* Try to convert into an index */
5659
    if (!strcmp(str, "end")) {
5660
        index = 0;
5661
        end = 1;
5662
    } else {
5663
        if (!strncmp(str, "end-", 4)) {
5664
            str += 4;
5665
            end = 1;
5666
        }
5667
        if (Jim_StringToIndex(str, &index) != JIM_OK) {
5668
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5669
            Jim_AppendStrings(interp, Jim_GetResult(interp),
5670
                    "bad index \"", Jim_GetString(objPtr, NULL), "\": "
5671
                    "must be integer or end?-integer?", NULL);
5672
            return JIM_ERR;
5673
        }
5674
    }
5675
    if (end) {
5676
        if (index < 0)
5677
            index = INT_MAX;
5678
        else
5679
            index = -(index+1);
5680
    } else if (!end && index < 0)
5681
        index = -INT_MAX;
5682
    /* Free the old internal repr and set the new one. */
5683
    Jim_FreeIntRep(interp, objPtr);
5684
    objPtr->typePtr = &indexObjType;
5685
    objPtr->internalRep.indexValue = index;
5686
    return JIM_OK;
5687
}
5688
 
5689
int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
5690
{
5691
    /* Avoid shimmering if the object is an integer. */
5692
    if (objPtr->typePtr == &intObjType) {
5693
        jim_wide val = objPtr->internalRep.wideValue;
5694
        if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
5695
            *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
5696
            return JIM_OK;
5697
        }
5698
    }
5699
    if (objPtr->typePtr != &indexObjType &&
5700
        SetIndexFromAny(interp, objPtr) == JIM_ERR)
5701
        return JIM_ERR;
5702
    *indexPtr = objPtr->internalRep.indexValue;
5703
    return JIM_OK;
5704
}
5705
 
5706
/* -----------------------------------------------------------------------------
5707
 * Return Code Object.
5708
 * ---------------------------------------------------------------------------*/
5709
 
5710
static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5711
 
5712
static Jim_ObjType returnCodeObjType = {
5713
    "return-code",
5714
    NULL,
5715
    NULL,
5716
    NULL,
5717
    JIM_TYPE_NONE,
5718
};
5719
 
5720
int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
5721
{
5722
    const char *str;
5723
    int strLen, returnCode;
5724
    jim_wide wideValue;
5725
 
5726
    /* Get the string representation */
5727
    str = Jim_GetString(objPtr, &strLen);
5728
    /* Try to convert into an integer */
5729
    if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
5730
        returnCode = (int) wideValue;
5731
    else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
5732
        returnCode = JIM_OK;
5733
    else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
5734
        returnCode = JIM_ERR;
5735
    else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
5736
        returnCode = JIM_RETURN;
5737
    else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
5738
        returnCode = JIM_BREAK;
5739
    else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
5740
        returnCode = JIM_CONTINUE;
5741
    else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
5742
        returnCode = JIM_EVAL;
5743
    else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
5744
        returnCode = JIM_EXIT;
5745
    else {
5746
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
5747
        Jim_AppendStrings(interp, Jim_GetResult(interp),
5748
                "expected return code but got '", str, "'",
5749
                NULL);
5750
        return JIM_ERR;
5751
    }
5752
    /* Free the old internal repr and set the new one. */
5753
    Jim_FreeIntRep(interp, objPtr);
5754
    objPtr->typePtr = &returnCodeObjType;
5755
    objPtr->internalRep.returnCode = returnCode;
5756
    return JIM_OK;
5757
}
5758
 
5759
int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
5760
{
5761
    if (objPtr->typePtr != &returnCodeObjType &&
5762
        SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
5763
        return JIM_ERR;
5764
    *intPtr = objPtr->internalRep.returnCode;
5765
    return JIM_OK;
5766
}
5767
 
5768
/* -----------------------------------------------------------------------------
5769
 * Expression Parsing
5770
 * ---------------------------------------------------------------------------*/
5771
static int JimParseExprOperator(struct JimParserCtx *pc);
5772
static int JimParseExprNumber(struct JimParserCtx *pc);
5773
static int JimParseExprIrrational(struct JimParserCtx *pc);
5774
 
5775
/* Exrp's Stack machine operators opcodes. */
5776
 
5777
/* Binary operators (numbers) */
5778
#define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
5779
#define JIM_EXPROP_MUL 0
5780
#define JIM_EXPROP_DIV 1
5781
#define JIM_EXPROP_MOD 2
5782
#define JIM_EXPROP_SUB 3
5783
#define JIM_EXPROP_ADD 4
5784
#define JIM_EXPROP_LSHIFT 5
5785
#define JIM_EXPROP_RSHIFT 6
5786
#define JIM_EXPROP_ROTL 7
5787
#define JIM_EXPROP_ROTR 8
5788
#define JIM_EXPROP_LT 9
5789
#define JIM_EXPROP_GT 10
5790
#define JIM_EXPROP_LTE 11
5791
#define JIM_EXPROP_GTE 12
5792
#define JIM_EXPROP_NUMEQ 13
5793
#define JIM_EXPROP_NUMNE 14
5794
#define JIM_EXPROP_BITAND 15
5795
#define JIM_EXPROP_BITXOR 16
5796
#define JIM_EXPROP_BITOR 17
5797
#define JIM_EXPROP_LOGICAND 18
5798
#define JIM_EXPROP_LOGICOR 19
5799
#define JIM_EXPROP_LOGICAND_LEFT 20
5800
#define JIM_EXPROP_LOGICOR_LEFT 21
5801
#define JIM_EXPROP_POW 22
5802
#define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
5803
 
5804
/* Binary operators (strings) */
5805
#define JIM_EXPROP_STREQ 23
5806
#define JIM_EXPROP_STRNE 24
5807
 
5808
/* Unary operators (numbers) */
5809
#define JIM_EXPROP_NOT 25
5810
#define JIM_EXPROP_BITNOT 26
5811
#define JIM_EXPROP_UNARYMINUS 27
5812
#define JIM_EXPROP_UNARYPLUS 28
5813
#define JIM_EXPROP_LOGICAND_RIGHT 29
5814
#define JIM_EXPROP_LOGICOR_RIGHT 30
5815
 
5816
/* Ternary operators */
5817
#define JIM_EXPROP_TERNARY 31
5818
 
5819
/* Operands */
5820
#define JIM_EXPROP_NUMBER 32
5821
#define JIM_EXPROP_COMMAND 33
5822
#define JIM_EXPROP_VARIABLE 34
5823
#define JIM_EXPROP_DICTSUGAR 35
5824
#define JIM_EXPROP_SUBST 36
5825
#define JIM_EXPROP_STRING 37
5826
 
5827
/* Operators table */
5828
typedef struct Jim_ExprOperator {
5829
    const char *name;
5830
    int precedence;
5831
    int arity;
5832
    int opcode;
5833
} Jim_ExprOperator;
5834
 
5835
/* name - precedence - arity - opcode */
5836
static struct Jim_ExprOperator Jim_ExprOperators[] = {
5837
    {"!", 300, 1, JIM_EXPROP_NOT},
5838
    {"~", 300, 1, JIM_EXPROP_BITNOT},
5839
    {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
5840
    {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
5841
 
5842
    {"**", 250, 2, JIM_EXPROP_POW},
5843
 
5844
    {"*", 200, 2, JIM_EXPROP_MUL},
5845
    {"/", 200, 2, JIM_EXPROP_DIV},
5846
    {"%", 200, 2, JIM_EXPROP_MOD},
5847
 
5848
    {"-", 100, 2, JIM_EXPROP_SUB},
5849
    {"+", 100, 2, JIM_EXPROP_ADD},
5850
 
5851
    {"<<<", 90, 3, JIM_EXPROP_ROTL},
5852
    {">>>", 90, 3, JIM_EXPROP_ROTR},
5853
    {"<<", 90, 2, JIM_EXPROP_LSHIFT},
5854
    {">>", 90, 2, JIM_EXPROP_RSHIFT},
5855
 
5856
    {"<",  80, 2, JIM_EXPROP_LT},
5857
    {">",  80, 2, JIM_EXPROP_GT},
5858
    {"<=", 80, 2, JIM_EXPROP_LTE},
5859
    {">=", 80, 2, JIM_EXPROP_GTE},
5860
 
5861
    {"==", 70, 2, JIM_EXPROP_NUMEQ},
5862
    {"!=", 70, 2, JIM_EXPROP_NUMNE},
5863
 
5864
    {"eq", 60, 2, JIM_EXPROP_STREQ},
5865
    {"ne", 60, 2, JIM_EXPROP_STRNE},
5866
 
5867
    {"&", 50, 2, JIM_EXPROP_BITAND},
5868
    {"^", 49, 2, JIM_EXPROP_BITXOR},
5869
    {"|", 48, 2, JIM_EXPROP_BITOR},
5870
 
5871
    {"&&", 10, 2, JIM_EXPROP_LOGICAND},
5872
    {"||", 10, 2, JIM_EXPROP_LOGICOR},
5873
 
5874
    {"?", 5, 3, JIM_EXPROP_TERNARY},
5875
    /* private operators */
5876
    {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
5877
    {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
5878
    {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
5879
    {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
5880
};
5881
 
5882
#define JIM_EXPR_OPERATORS_NUM \
5883
    (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
5884
 
5885
int JimParseExpression(struct JimParserCtx *pc)
5886
{
5887
    /* Discard spaces and quoted newline */
5888
    while(*(pc->p) == ' ' ||
5889
          *(pc->p) == '\t' ||
5890
          *(pc->p) == '\r' ||
5891
          *(pc->p) == '\n' ||
5892
            (*(pc->p) == '\\' && *(pc->p+1) == '\n')) {
5893
        pc->p++; pc->len--;
5894
    }
5895
 
5896
    if (pc->len == 0) {
5897
        pc->tstart = pc->tend = pc->p;
5898
        pc->tline = pc->linenr;
5899
        pc->tt = JIM_TT_EOL;
5900
        pc->eof = 1;
5901
        return JIM_OK;
5902
    }
5903
    switch(*(pc->p)) {
5904
    case '(':
5905
        pc->tstart = pc->tend = pc->p;
5906
        pc->tline = pc->linenr;
5907
        pc->tt = JIM_TT_SUBEXPR_START;
5908
        pc->p++; pc->len--;
5909
        break;
5910
    case ')':
5911
        pc->tstart = pc->tend = pc->p;
5912
        pc->tline = pc->linenr;
5913
        pc->tt = JIM_TT_SUBEXPR_END;
5914
        pc->p++; pc->len--;
5915
        break;
5916
    case '[':
5917
        return JimParseCmd(pc);
5918
        break;
5919
    case '$':
5920
        if (JimParseVar(pc) == JIM_ERR)
5921
            return JimParseExprOperator(pc);
5922
        else
5923
            return JIM_OK;
5924
        break;
5925
    case '-':
5926
        if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
5927
            isdigit((int)*(pc->p+1)))
5928
            return JimParseExprNumber(pc);
5929
        else
5930
            return JimParseExprOperator(pc);
5931
        break;
5932
    case '0': case '1': case '2': case '3': case '4':
5933
    case '5': case '6': case '7': case '8': case '9': case '.':
5934
        return JimParseExprNumber(pc);
5935
        break;
5936
    case '"':
5937
    case '{':
5938
        /* Here it's possible to reuse the List String parsing. */
5939
        pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
5940
        return JimParseListStr(pc);
5941
        break;
5942
    case 'N': case 'I':
5943
    case 'n': case 'i':
5944
        if (JimParseExprIrrational(pc) == JIM_ERR)
5945
            return JimParseExprOperator(pc);
5946
        break;
5947
    default:
5948
        return JimParseExprOperator(pc);
5949
        break;
5950
    }
5951
    return JIM_OK;
5952
}
5953
 
5954
int JimParseExprNumber(struct JimParserCtx *pc)
5955
{
5956
    int allowdot = 1;
5957
 
5958
    pc->tstart = pc->p;
5959
    pc->tline = pc->linenr;
5960
    if (*pc->p == '-') {
5961
        pc->p++; pc->len--;
5962
    }
5963
    while (isdigit((int)*pc->p) || (allowdot && *pc->p == '.') ||
5964
           (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
5965
              (*pc->p == 'x' || *pc->p == 'X')))
5966
    {
5967
        if (*pc->p == '.')
5968
            allowdot = 0;
5969
        pc->p++; pc->len--;
5970
        if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
5971
            pc->p += 2; pc->len -= 2;
5972
        }
5973
    }
5974
    pc->tend = pc->p-1;
5975
    pc->tt = JIM_TT_EXPR_NUMBER;
5976
    return JIM_OK;
5977
}
5978
 
5979
int JimParseExprIrrational(struct JimParserCtx *pc)
5980
{
5981
    const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
5982
    const char **token;
5983
    for (token = Tokens; *token != NULL; token++) {
5984
        int len = strlen(*token);
5985
        if (strncmp(*token, pc->p, len) == 0) {
5986
            pc->tstart = pc->p;
5987
            pc->tend = pc->p + len - 1;
5988
            pc->p += len; pc->len -= len;
5989
            pc->tline = pc->linenr;
5990
            pc->tt = JIM_TT_EXPR_NUMBER;
5991
            return JIM_OK;
5992
        }
5993
    }
5994
    return JIM_ERR;
5995
}
5996
 
5997
int JimParseExprOperator(struct JimParserCtx *pc)
5998
{
5999
    int i;
6000
    int bestIdx = -1, bestLen = 0;
6001
 
6002
    /* Try to get the longest match. */
6003
    for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
6004
        const char *opname;
6005
        int oplen;
6006
 
6007
        opname = Jim_ExprOperators[i].name;
6008
        if (opname == NULL) continue;
6009
        oplen = strlen(opname);
6010
 
6011
        if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
6012
            bestIdx = i;
6013
            bestLen = oplen;
6014
        }
6015
    }
6016
    if (bestIdx == -1) return JIM_ERR;
6017
    pc->tstart = pc->p;
6018
    pc->tend = pc->p + bestLen - 1;
6019
    pc->p += bestLen; pc->len -= bestLen;
6020
    pc->tline = pc->linenr;
6021
    pc->tt = JIM_TT_EXPR_OPERATOR;
6022
    return JIM_OK;
6023
}
6024
 
6025
struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
6026
{
6027
    int i;
6028
    for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6029
        if (Jim_ExprOperators[i].name &&
6030
            strcmp(opname, Jim_ExprOperators[i].name) == 0)
6031
            return &Jim_ExprOperators[i];
6032
    return NULL;
6033
}
6034
 
6035
struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
6036
{
6037
    int i;
6038
    for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
6039
        if (Jim_ExprOperators[i].opcode == opcode)
6040
            return &Jim_ExprOperators[i];
6041
    return NULL;
6042
}
6043
 
6044
/* -----------------------------------------------------------------------------
6045
 * Expression Object
6046
 * ---------------------------------------------------------------------------*/
6047
static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6048
static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6049
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
6050
 
6051
static Jim_ObjType exprObjType = {
6052
    "expression",
6053
    FreeExprInternalRep,
6054
    DupExprInternalRep,
6055
    NULL,
6056
    JIM_TYPE_REFERENCES,
6057
};
6058
 
6059
/* Expr bytecode structure */
6060
typedef struct ExprByteCode {
6061
    int *opcode;        /* Integer array of opcodes. */
6062
    Jim_Obj **obj;      /* Array of associated Jim Objects. */
6063
    int len;            /* Bytecode length */
6064
    int inUse;          /* Used for sharing. */
6065
} ExprByteCode;
6066
 
6067
void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6068
{
6069
    int i;
6070
    ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
6071
 
6072
    expr->inUse--;
6073
    if (expr->inUse != 0) return;
6074
    for (i = 0; i < expr->len; i++)
6075
        Jim_DecrRefCount(interp, expr->obj[i]);
6076
    Jim_Free(expr->opcode);
6077
    Jim_Free(expr->obj);
6078
    Jim_Free(expr);
6079
}
6080
 
6081
void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6082
{
6083
    JIM_NOTUSED(interp);
6084
    JIM_NOTUSED(srcPtr);
6085
 
6086
    /* Just returns an simple string. */
6087
    dupPtr->typePtr = NULL;
6088
}
6089
 
6090
/* Add a new instruction to an expression bytecode structure. */
6091
static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
6092
        int opcode, char *str, int len)
6093
{
6094
    expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+1));
6095
    expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+1));
6096
    expr->opcode[expr->len] = opcode;
6097
    expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
6098
    Jim_IncrRefCount(expr->obj[expr->len]);
6099
    expr->len++;
6100
}
6101
 
6102
/* Check if an expr program looks correct. */
6103
static int ExprCheckCorrectness(ExprByteCode *expr)
6104
{
6105
    int i;
6106
    int stacklen = 0;
6107
 
6108
    /* Try to check if there are stack underflows,
6109
     * and make sure at the end of the program there is
6110
     * a single result on the stack. */
6111
    for (i = 0; i < expr->len; i++) {
6112
        switch(expr->opcode[i]) {
6113
        case JIM_EXPROP_NUMBER:
6114
        case JIM_EXPROP_STRING:
6115
        case JIM_EXPROP_SUBST:
6116
        case JIM_EXPROP_VARIABLE:
6117
        case JIM_EXPROP_DICTSUGAR:
6118
        case JIM_EXPROP_COMMAND:
6119
            stacklen++;
6120
            break;
6121
        case JIM_EXPROP_NOT:
6122
        case JIM_EXPROP_BITNOT:
6123
        case JIM_EXPROP_UNARYMINUS:
6124
        case JIM_EXPROP_UNARYPLUS:
6125
            /* Unary operations */
6126
            if (stacklen < 1) return JIM_ERR;
6127
            break;
6128
        case JIM_EXPROP_ADD:
6129
        case JIM_EXPROP_SUB:
6130
        case JIM_EXPROP_MUL:
6131
        case JIM_EXPROP_DIV:
6132
        case JIM_EXPROP_MOD:
6133
        case JIM_EXPROP_LT:
6134
        case JIM_EXPROP_GT:
6135
        case JIM_EXPROP_LTE:
6136
        case JIM_EXPROP_GTE:
6137
        case JIM_EXPROP_ROTL:
6138
        case JIM_EXPROP_ROTR:
6139
        case JIM_EXPROP_LSHIFT:
6140
        case JIM_EXPROP_RSHIFT:
6141
        case JIM_EXPROP_NUMEQ:
6142
        case JIM_EXPROP_NUMNE:
6143
        case JIM_EXPROP_STREQ:
6144
        case JIM_EXPROP_STRNE:
6145
        case JIM_EXPROP_BITAND:
6146
        case JIM_EXPROP_BITXOR:
6147
        case JIM_EXPROP_BITOR:
6148
        case JIM_EXPROP_LOGICAND:
6149
        case JIM_EXPROP_LOGICOR:
6150
        case JIM_EXPROP_POW:
6151
            /* binary operations */
6152
            if (stacklen < 2) return JIM_ERR;
6153
            stacklen--;
6154
            break;
6155
        default:
6156
            Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
6157
            break;
6158
        }
6159
    }
6160
    if (stacklen != 1) return JIM_ERR;
6161
    return JIM_OK;
6162
}
6163
 
6164
static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
6165
        ScriptObj *topLevelScript)
6166
{
6167
    int i;
6168
 
6169
    return;
6170
    for (i = 0; i < expr->len; i++) {
6171
        Jim_Obj *foundObjPtr;
6172
 
6173
        if (expr->obj[i] == NULL) continue;
6174
        foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
6175
                NULL, expr->obj[i]);
6176
        if (foundObjPtr != NULL) {
6177
            Jim_IncrRefCount(foundObjPtr);
6178
            Jim_DecrRefCount(interp, expr->obj[i]);
6179
            expr->obj[i] = foundObjPtr;
6180
        }
6181
    }
6182
}
6183
 
6184
/* This procedure converts every occurrence of || and && opereators
6185
 * in lazy unary versions.
6186
 *
6187
 * a b || is converted into:
6188
 *
6189
 * a <offset> |L b |R
6190
 *
6191
 * a b && is converted into:
6192
 *
6193
 * a <offset> &L b &R
6194
 *
6195
 * "|L" checks if 'a' is true:
6196
 *   1) if it is true pushes 1 and skips <offset> istructions to reach
6197
 *      the opcode just after |R.
6198
 *   2) if it is false does nothing.
6199
 * "|R" checks if 'b' is true:
6200
 *   1) if it is true pushes 1, otherwise pushes 0.
6201
 *
6202
 * "&L" checks if 'a' is true:
6203
 *   1) if it is true does nothing.
6204
 *   2) If it is false pushes 0 and skips <offset> istructions to reach
6205
 *      the opcode just after &R
6206
 * "&R" checks if 'a' is true:
6207
 *      if it is true pushes 1, otherwise pushes 0.
6208
 */
6209
static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
6210
{
6211
    while (1) {
6212
        int index = -1, leftindex, arity, i, offset;
6213
        Jim_ExprOperator *op;
6214
 
6215
        /* Search for || or && */
6216
        for (i = 0; i < expr->len; i++) {
6217
            if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
6218
                expr->opcode[i] == JIM_EXPROP_LOGICOR) {
6219
                index = i;
6220
                break;
6221
            }
6222
        }
6223
        if (index == -1) return;
6224
        /* Search for the end of the first operator */
6225
        leftindex = index-1;
6226
        arity = 1;
6227
        while(arity) {
6228
            switch(expr->opcode[leftindex]) {
6229
            case JIM_EXPROP_NUMBER:
6230
            case JIM_EXPROP_COMMAND:
6231
            case JIM_EXPROP_VARIABLE:
6232
            case JIM_EXPROP_DICTSUGAR:
6233
            case JIM_EXPROP_SUBST:
6234
            case JIM_EXPROP_STRING:
6235
                break;
6236
            default:
6237
                op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
6238
                if (op == NULL) {
6239
                    Jim_Panic(interp,"Default reached in ExprMakeLazy()");
6240
                }
6241
                arity += op->arity;
6242
                break;
6243
            }
6244
            arity--;
6245
            leftindex--;
6246
        }
6247
        leftindex++;
6248
        expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len+2));
6249
        expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len+2));
6250
        memmove(&expr->opcode[leftindex+2], &expr->opcode[leftindex],
6251
                sizeof(int)*(expr->len-leftindex));
6252
        memmove(&expr->obj[leftindex+2], &expr->obj[leftindex],
6253
                sizeof(Jim_Obj*)*(expr->len-leftindex));
6254
        expr->len += 2;
6255
        index += 2;
6256
        offset = (index-leftindex)-1;
6257
        Jim_DecrRefCount(interp, expr->obj[index]);
6258
        if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
6259
            expr->opcode[leftindex+1] = JIM_EXPROP_LOGICAND_LEFT;
6260
            expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
6261
            expr->obj[leftindex+1] = Jim_NewStringObj(interp, "&L", -1);
6262
            expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
6263
        } else {
6264
            expr->opcode[leftindex+1] = JIM_EXPROP_LOGICOR_LEFT;
6265
            expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
6266
            expr->obj[leftindex+1] = Jim_NewStringObj(interp, "|L", -1);
6267
            expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
6268
        }
6269
        expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
6270
        expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
6271
        Jim_IncrRefCount(expr->obj[index]);
6272
        Jim_IncrRefCount(expr->obj[leftindex]);
6273
        Jim_IncrRefCount(expr->obj[leftindex+1]);
6274
    }
6275
}
6276
 
6277
/* This method takes the string representation of an expression
6278
 * and generates a program for the Expr's stack-based VM. */
6279
int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
6280
{
6281
    int exprTextLen;
6282
    const char *exprText = Jim_GetString(objPtr, &exprTextLen);
6283
    struct JimParserCtx parser;
6284
    int i, shareLiterals;
6285
    ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
6286
    Jim_Stack stack;
6287
    Jim_ExprOperator *op;
6288
 
6289
    /* Perform literal sharing with the current procedure
6290
     * running only if this expression appears to be not generated
6291
     * at runtime. */
6292
    shareLiterals = objPtr->typePtr == &sourceObjType;
6293
 
6294
    expr->opcode = NULL;
6295
    expr->obj = NULL;
6296
    expr->len = 0;
6297
    expr->inUse = 1;
6298
 
6299
    Jim_InitStack(&stack);
6300
    JimParserInit(&parser, exprText, exprTextLen, 1);
6301
    while(!JimParserEof(&parser)) {
6302
        char *token;
6303
        int len, type;
6304
 
6305
        if (JimParseExpression(&parser) != JIM_OK) {
6306
            Jim_SetResultString(interp, "Syntax error in expression", -1);
6307
            goto err;
6308
        }
6309
        token = JimParserGetToken(&parser, &len, &type, NULL);
6310
        if (type == JIM_TT_EOL) {
6311
            Jim_Free(token);
6312
            break;
6313
        }
6314
        switch(type) {
6315
        case JIM_TT_STR:
6316
            ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
6317
            break;
6318
        case JIM_TT_ESC:
6319
            ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
6320
            break;
6321
        case JIM_TT_VAR:
6322
            ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
6323
            break;
6324
        case JIM_TT_DICTSUGAR:
6325
            ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
6326
            break;
6327
        case JIM_TT_CMD:
6328
            ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
6329
            break;
6330
        case JIM_TT_EXPR_NUMBER:
6331
            ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
6332
            break;
6333
        case JIM_TT_EXPR_OPERATOR:
6334
            op = JimExprOperatorInfo(token);
6335
            while(1) {
6336
                Jim_ExprOperator *stackTopOp;
6337
 
6338
                if (Jim_StackPeek(&stack) != NULL) {
6339
                    stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
6340
                } else {
6341
                    stackTopOp = NULL;
6342
                }
6343
                if (Jim_StackLen(&stack) && op->arity != 1 &&
6344
                    stackTopOp && stackTopOp->precedence >= op->precedence)
6345
                {
6346
                    ExprObjAddInstr(interp, expr, stackTopOp->opcode,
6347
                        Jim_StackPeek(&stack), -1);
6348
                    Jim_StackPop(&stack);
6349
                } else {
6350
                    break;
6351
                }
6352
            }
6353
            Jim_StackPush(&stack, token);
6354
            break;
6355
        case JIM_TT_SUBEXPR_START:
6356
            Jim_StackPush(&stack, Jim_StrDup("("));
6357
            Jim_Free(token);
6358
            break;
6359
        case JIM_TT_SUBEXPR_END:
6360
            {
6361
                int found = 0;
6362
                while(Jim_StackLen(&stack)) {
6363
                    char *opstr = Jim_StackPop(&stack);
6364
                    if (!strcmp(opstr, "(")) {
6365
                        Jim_Free(opstr);
6366
                        found = 1;
6367
                        break;
6368
                    }
6369
                    op = JimExprOperatorInfo(opstr);
6370
                    ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6371
                }
6372
                if (!found) {
6373
                    Jim_SetResultString(interp,
6374
                        "Unexpected close parenthesis", -1);
6375
                    goto err;
6376
                }
6377
            }
6378
            Jim_Free(token);
6379
            break;
6380
        default:
6381
            Jim_Panic(interp,"Default reached in SetExprFromAny()");
6382
            break;
6383
        }
6384
    }
6385
    while (Jim_StackLen(&stack)) {
6386
        char *opstr = Jim_StackPop(&stack);
6387
        op = JimExprOperatorInfo(opstr);
6388
        if (op == NULL && !strcmp(opstr, "(")) {
6389
            Jim_Free(opstr);
6390
            Jim_SetResultString(interp, "Missing close parenthesis", -1);
6391
            goto err;
6392
        }
6393
        ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
6394
    }
6395
    /* Check program correctness. */
6396
    if (ExprCheckCorrectness(expr) != JIM_OK) {
6397
        Jim_SetResultString(interp, "Invalid expression", -1);
6398
        goto err;
6399
    }
6400
 
6401
    /* Free the stack used for the compilation. */
6402
    Jim_FreeStackElements(&stack, Jim_Free);
6403
    Jim_FreeStack(&stack);
6404
 
6405
    /* Convert || and && operators in unary |L |R and &L &R for lazyness */
6406
    ExprMakeLazy(interp, expr);
6407
 
6408
    /* Perform literal sharing */
6409
    if (shareLiterals && interp->framePtr->procBodyObjPtr) {
6410
        Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
6411
        if (bodyObjPtr->typePtr == &scriptObjType) {
6412
            ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
6413
            ExprShareLiterals(interp, expr, bodyScript);
6414
        }
6415
    }
6416
 
6417
    /* Free the old internal rep and set the new one. */
6418
    Jim_FreeIntRep(interp, objPtr);
6419
    Jim_SetIntRepPtr(objPtr, expr);
6420
    objPtr->typePtr = &exprObjType;
6421
    return JIM_OK;
6422
 
6423
err:    /* we jump here on syntax/compile errors. */
6424
    Jim_FreeStackElements(&stack, Jim_Free);
6425
    Jim_FreeStack(&stack);
6426
    Jim_Free(expr->opcode);
6427
    for (i = 0; i < expr->len; i++) {
6428
        Jim_DecrRefCount(interp,expr->obj[i]);
6429
    }
6430
    Jim_Free(expr->obj);
6431
    Jim_Free(expr);
6432
    return JIM_ERR;
6433
}
6434
 
6435
ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
6436
{
6437
    if (objPtr->typePtr != &exprObjType) {
6438
        if (SetExprFromAny(interp, objPtr) != JIM_OK)
6439
            return NULL;
6440
    }
6441
    return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
6442
}
6443
 
6444
/* -----------------------------------------------------------------------------
6445
 * Expressions evaluation.
6446
 * Jim uses a specialized stack-based virtual machine for expressions,
6447
 * that takes advantage of the fact that expr's operators
6448
 * can't be redefined.
6449
 *
6450
 * Jim_EvalExpression() uses the bytecode compiled by
6451
 * SetExprFromAny() method of the "expression" object.
6452
 *
6453
 * On success a Tcl Object containing the result of the evaluation
6454
 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
6455
 * returned.
6456
 * On error the function returns a retcode != to JIM_OK and set a suitable
6457
 * error on the interp.
6458
 * ---------------------------------------------------------------------------*/
6459
#define JIM_EE_STATICSTACK_LEN 10
6460
 
6461
int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
6462
        Jim_Obj **exprResultPtrPtr)
6463
{
6464
    ExprByteCode *expr;
6465
    Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
6466
    int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
6467
 
6468
    Jim_IncrRefCount(exprObjPtr);
6469
    expr = Jim_GetExpression(interp, exprObjPtr);
6470
    if (!expr) {
6471
        Jim_DecrRefCount(interp, exprObjPtr);
6472
        return JIM_ERR; /* error in expression. */
6473
    }
6474
    /* In order to avoid that the internal repr gets freed due to
6475
     * shimmering of the exprObjPtr's object, we make the internal rep
6476
     * shared. */
6477
    expr->inUse++;
6478
 
6479
    /* The stack-based expr VM itself */
6480
 
6481
    /* Stack allocation. Expr programs have the feature that
6482
     * a program of length N can't require a stack longer than
6483
     * N. */
6484
    if (expr->len > JIM_EE_STATICSTACK_LEN)
6485
        stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
6486
    else
6487
        stack = staticStack;
6488
 
6489
    /* Execute every istruction */
6490
    for (i = 0; i < expr->len; i++) {
6491
        Jim_Obj *A, *B, *objPtr;
6492
        jim_wide wA, wB, wC;
6493
        double dA, dB, dC;
6494
        const char *sA, *sB;
6495
        int Alen, Blen, retcode;
6496
        int opcode = expr->opcode[i];
6497
 
6498
        if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
6499
            stack[stacklen++] = expr->obj[i];
6500
            Jim_IncrRefCount(expr->obj[i]);
6501
        } else if (opcode == JIM_EXPROP_VARIABLE) {
6502
            objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
6503
            if (objPtr == NULL) {
6504
                error = 1;
6505
                goto err;
6506
            }
6507
            stack[stacklen++] = objPtr;
6508
            Jim_IncrRefCount(objPtr);
6509
        } else if (opcode == JIM_EXPROP_SUBST) {
6510
            if ((retcode = Jim_SubstObj(interp, expr->obj[i],
6511
                        &objPtr, JIM_NONE)) != JIM_OK)
6512
            {
6513
                error = 1;
6514
                errRetCode = retcode;
6515
                goto err;
6516
            }
6517
            stack[stacklen++] = objPtr;
6518
            Jim_IncrRefCount(objPtr);
6519
        } else if (opcode == JIM_EXPROP_DICTSUGAR) {
6520
            objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
6521
            if (objPtr == NULL) {
6522
                error = 1;
6523
                goto err;
6524
            }
6525
            stack[stacklen++] = objPtr;
6526
            Jim_IncrRefCount(objPtr);
6527
        } else if (opcode == JIM_EXPROP_COMMAND) {
6528
            if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
6529
                error = 1;
6530
                errRetCode = retcode;
6531
                goto err;
6532
            }
6533
            stack[stacklen++] = interp->result;
6534
            Jim_IncrRefCount(interp->result);
6535
        } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
6536
                   opcode <= JIM_EXPROP_BINARY_NUM_LAST)
6537
        {
6538
            /* Note that there isn't to increment the
6539
             * refcount of objects. the references are moved
6540
             * from stack to A and B. */
6541
            B = stack[--stacklen];
6542
            A = stack[--stacklen];
6543
 
6544
            /* --- Integer --- */
6545
            if ((A->typePtr == &doubleObjType && !A->bytes) ||
6546
                (B->typePtr == &doubleObjType && !B->bytes) ||
6547
                JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
6548
                JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
6549
                goto trydouble;
6550
            }
6551
            Jim_DecrRefCount(interp, A);
6552
            Jim_DecrRefCount(interp, B);
6553
            switch(expr->opcode[i]) {
6554
            case JIM_EXPROP_ADD: wC = wA+wB; break;
6555
            case JIM_EXPROP_SUB: wC = wA-wB; break;
6556
            case JIM_EXPROP_MUL: wC = wA*wB; break;
6557
            case JIM_EXPROP_LT: wC = wA<wB; break;
6558
            case JIM_EXPROP_GT: wC = wA>wB; break;
6559
            case JIM_EXPROP_LTE: wC = wA<=wB; break;
6560
            case JIM_EXPROP_GTE: wC = wA>=wB; break;
6561
            case JIM_EXPROP_LSHIFT: wC = wA<<wB; break;
6562
            case JIM_EXPROP_RSHIFT: wC = wA>>wB; break;
6563
            case JIM_EXPROP_NUMEQ: wC = wA==wB; break;
6564
            case JIM_EXPROP_NUMNE: wC = wA!=wB; break;
6565
            case JIM_EXPROP_BITAND: wC = wA&wB; break;
6566
            case JIM_EXPROP_BITXOR: wC = wA^wB; break;
6567
            case JIM_EXPROP_BITOR: wC = wA|wB; break;
6568
            case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
6569
            case JIM_EXPROP_LOGICAND_LEFT:
6570
                if (wA == 0) {
6571
                    i += (int)wB;
6572
                    wC = 0;
6573
                } else {
6574
                    continue;
6575
                }
6576
                break;
6577
            case JIM_EXPROP_LOGICOR_LEFT:
6578
                if (wA != 0) {
6579
                    i += (int)wB;
6580
                    wC = 1;
6581
                } else {
6582
                    continue;
6583
                }
6584
                break;
6585
            case JIM_EXPROP_DIV:
6586
                if (wB == 0) goto divbyzero;
6587
                wC = wA/wB;
6588
                break;
6589
            case JIM_EXPROP_MOD:
6590
                if (wB == 0) goto divbyzero;
6591
                wC = wA%wB;
6592
                break;
6593
            case JIM_EXPROP_ROTL: {
6594
                /* uint32_t would be better. But not everyone has inttypes.h?*/
6595
                unsigned long uA = (unsigned long)wA;
6596
#ifdef _MSC_VER
6597
                wC = _rotl(uA,(unsigned long)wB);
6598
#else
6599
                const unsigned int S = sizeof(unsigned long) * 8;
6600
                wC = (unsigned long)((uA<<wB)|(uA>>(S-wB)));
6601
#endif
6602
                break;
6603
            }
6604
            case JIM_EXPROP_ROTR: {
6605
                unsigned long uA = (unsigned long)wA;
6606
#ifdef _MSC_VER
6607
                wC = _rotr(uA,(unsigned long)wB);
6608
#else
6609
                const unsigned int S = sizeof(unsigned long) * 8;
6610
                wC = (unsigned long)((uA>>wB)|(uA<<(S-wB)));
6611
#endif
6612
                break;
6613
            }
6614
 
6615
            default:
6616
                wC = 0; /* avoid gcc warning */
6617
                break;
6618
            }
6619
            stack[stacklen] = Jim_NewIntObj(interp, wC);
6620
            Jim_IncrRefCount(stack[stacklen]);
6621
            stacklen++;
6622
            continue;
6623
trydouble:
6624
            /* --- Double --- */
6625
            if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
6626
                Jim_GetDouble(interp, B, &dB) != JIM_OK) {
6627
                Jim_DecrRefCount(interp, A);
6628
                Jim_DecrRefCount(interp, B);
6629
                error = 1;
6630
                goto err;
6631
            }
6632
            Jim_DecrRefCount(interp, A);
6633
            Jim_DecrRefCount(interp, B);
6634
            switch(expr->opcode[i]) {
6635
            case JIM_EXPROP_ROTL:
6636
            case JIM_EXPROP_ROTR:
6637
            case JIM_EXPROP_LSHIFT:
6638
            case JIM_EXPROP_RSHIFT:
6639
            case JIM_EXPROP_BITAND:
6640
            case JIM_EXPROP_BITXOR:
6641
            case JIM_EXPROP_BITOR:
6642
            case JIM_EXPROP_MOD:
6643
            case JIM_EXPROP_POW:
6644
                Jim_SetResultString(interp,
6645
                    "Got floating-point value where integer was expected", -1);
6646
                error = 1;
6647
                goto err;
6648
                break;
6649
            case JIM_EXPROP_ADD: dC = dA+dB; break;
6650
            case JIM_EXPROP_SUB: dC = dA-dB; break;
6651
            case JIM_EXPROP_MUL: dC = dA*dB; break;
6652
            case JIM_EXPROP_LT: dC = dA<dB; break;
6653
            case JIM_EXPROP_GT: dC = dA>dB; break;
6654
            case JIM_EXPROP_LTE: dC = dA<=dB; break;
6655
            case JIM_EXPROP_GTE: dC = dA>=dB; break;
6656
            case JIM_EXPROP_NUMEQ: dC = dA==dB; break;
6657
            case JIM_EXPROP_NUMNE: dC = dA!=dB; break;
6658
            case JIM_EXPROP_LOGICAND_LEFT:
6659
                if (dA == 0) {
6660
                    i += (int)dB;
6661
                    dC = 0;
6662
                } else {
6663
                    continue;
6664
                }
6665
                break;
6666
            case JIM_EXPROP_LOGICOR_LEFT:
6667
                if (dA != 0) {
6668
                    i += (int)dB;
6669
                    dC = 1;
6670
                } else {
6671
                    continue;
6672
                }
6673
                break;
6674
            case JIM_EXPROP_DIV:
6675
                if (dB == 0) goto divbyzero;
6676
                dC = dA/dB;
6677
                break;
6678
            default:
6679
                dC = 0; /* avoid gcc warning */
6680
                break;
6681
            }
6682
            stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6683
            Jim_IncrRefCount(stack[stacklen]);
6684
            stacklen++;
6685
        } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
6686
            B = stack[--stacklen];
6687
            A = stack[--stacklen];
6688
            sA = Jim_GetString(A, &Alen);
6689
            sB = Jim_GetString(B, &Blen);
6690
            switch(expr->opcode[i]) {
6691
            case JIM_EXPROP_STREQ:
6692
                if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
6693
                    wC = 1;
6694
                else
6695
                    wC = 0;
6696
                break;
6697
            case JIM_EXPROP_STRNE:
6698
                if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
6699
                    wC = 1;
6700
                else
6701
                    wC = 0;
6702
                break;
6703
            default:
6704
                wC = 0; /* avoid gcc warning */
6705
                break;
6706
            }
6707
            Jim_DecrRefCount(interp, A);
6708
            Jim_DecrRefCount(interp, B);
6709
            stack[stacklen] = Jim_NewIntObj(interp, wC);
6710
            Jim_IncrRefCount(stack[stacklen]);
6711
            stacklen++;
6712
        } else if (opcode == JIM_EXPROP_NOT ||
6713
                   opcode == JIM_EXPROP_BITNOT ||
6714
                   opcode == JIM_EXPROP_LOGICAND_RIGHT ||
6715
                   opcode == JIM_EXPROP_LOGICOR_RIGHT) {
6716
            /* Note that there isn't to increment the
6717
             * refcount of objects. the references are moved
6718
             * from stack to A and B. */
6719
            A = stack[--stacklen];
6720
 
6721
            /* --- Integer --- */
6722
            if ((A->typePtr == &doubleObjType && !A->bytes) ||
6723
                JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
6724
                goto trydouble_unary;
6725
            }
6726
            Jim_DecrRefCount(interp, A);
6727
            switch(expr->opcode[i]) {
6728
            case JIM_EXPROP_NOT: wC = !wA; break;
6729
            case JIM_EXPROP_BITNOT: wC = ~wA; break;
6730
            case JIM_EXPROP_LOGICAND_RIGHT:
6731
            case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
6732
            default:
6733
                wC = 0; /* avoid gcc warning */
6734
                break;
6735
            }
6736
            stack[stacklen] = Jim_NewIntObj(interp, wC);
6737
            Jim_IncrRefCount(stack[stacklen]);
6738
            stacklen++;
6739
            continue;
6740
trydouble_unary:
6741
            /* --- Double --- */
6742
            if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
6743
                Jim_DecrRefCount(interp, A);
6744
                error = 1;
6745
                goto err;
6746
            }
6747
            Jim_DecrRefCount(interp, A);
6748
            switch(expr->opcode[i]) {
6749
            case JIM_EXPROP_NOT: dC = !dA; break;
6750
            case JIM_EXPROP_LOGICAND_RIGHT:
6751
            case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
6752
            case JIM_EXPROP_BITNOT:
6753
                Jim_SetResultString(interp,
6754
                    "Got floating-point value where integer was expected", -1);
6755
                error = 1;
6756
                goto err;
6757
                break;
6758
            default:
6759
                dC = 0; /* avoid gcc warning */
6760
                break;
6761
            }
6762
            stack[stacklen] = Jim_NewDoubleObj(interp, dC);
6763
            Jim_IncrRefCount(stack[stacklen]);
6764
            stacklen++;
6765
        } else {
6766
            Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
6767
        }
6768
    }
6769
err:
6770
    /* There is no need to decerement the inUse field because
6771
     * this reference is transfered back into the exprObjPtr. */
6772
    Jim_FreeIntRep(interp, exprObjPtr);
6773
    exprObjPtr->typePtr = &exprObjType;
6774
    Jim_SetIntRepPtr(exprObjPtr, expr);
6775
    Jim_DecrRefCount(interp, exprObjPtr);
6776
    if (!error) {
6777
        *exprResultPtrPtr = stack[0];
6778
        Jim_IncrRefCount(stack[0]);
6779
        errRetCode = JIM_OK;
6780
    }
6781
    for (i = 0; i < stacklen; i++) {
6782
        Jim_DecrRefCount(interp, stack[i]);
6783
    }
6784
    if (stack != staticStack)
6785
        Jim_Free(stack);
6786
    return errRetCode;
6787
divbyzero:
6788
    error = 1;
6789
    Jim_SetResultString(interp, "Division by zero", -1);
6790
    goto err;
6791
}
6792
 
6793
int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
6794
{
6795
    int retcode;
6796
    jim_wide wideValue;
6797
    double doubleValue;
6798
    Jim_Obj *exprResultPtr;
6799
 
6800
    retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
6801
    if (retcode != JIM_OK)
6802
        return retcode;
6803
    if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
6804
        if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
6805
        {
6806
            Jim_DecrRefCount(interp, exprResultPtr);
6807
            return JIM_ERR;
6808
        } else {
6809
            Jim_DecrRefCount(interp, exprResultPtr);
6810
            *boolPtr = doubleValue != 0;
6811
            return JIM_OK;
6812
        }
6813
    }
6814
    Jim_DecrRefCount(interp, exprResultPtr);
6815
    *boolPtr = wideValue != 0;
6816
    return JIM_OK;
6817
}
6818
 
6819
/* -----------------------------------------------------------------------------
6820
 * ScanFormat String Object
6821
 * ---------------------------------------------------------------------------*/
6822
 
6823
/* This Jim_Obj will held a parsed representation of a format string passed to
6824
 * the Jim_ScanString command. For error diagnostics, the scanformat string has
6825
 * to be parsed in its entirely first and then, if correct, can be used for
6826
 * scanning. To avoid endless re-parsing, the parsed representation will be
6827
 * stored in an internal representation and re-used for performance reason. */
6828
 
6829
/* A ScanFmtPartDescr will held the information of /one/ part of the whole
6830
 * scanformat string. This part will later be used to extract information
6831
 * out from the string to be parsed by Jim_ScanString */
6832
 
6833
typedef struct ScanFmtPartDescr {
6834
    char type;         /* Type of conversion (e.g. c, d, f) */
6835
    char modifier;     /* Modify type (e.g. l - long, h - short */
6836
    size_t  width;     /* Maximal width of input to be converted */
6837
    int  pos;          /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
6838
    char *arg;         /* Specification of a CHARSET conversion */
6839
    char *prefix;      /* Prefix to be scanned literally before conversion */
6840
} ScanFmtPartDescr;
6841
 
6842
/* The ScanFmtStringObj will held the internal representation of a scanformat
6843
 * string parsed and separated in part descriptions. Furthermore it contains
6844
 * the original string representation of the scanformat string to allow for
6845
 * fast update of the Jim_Obj's string representation part.
6846
 *
6847
 * As add-on the internal object representation add some scratch pad area
6848
 * for usage by Jim_ScanString to avoid endless allocating and freeing of
6849
 * memory for purpose of string scanning.
6850
 *
6851
 * The error member points to a static allocated string in case of a mal-
6852
 * formed scanformat string or it contains '0' (NULL) in case of a valid
6853
 * parse representation.
6854
 *
6855
 * The whole memory of the internal representation is allocated as a single
6856
 * area of memory that will be internally separated. So freeing and duplicating
6857
 * of such an object is cheap */
6858
 
6859
typedef struct ScanFmtStringObj {
6860
    jim_wide        size;         /* Size of internal repr in bytes */
6861
    char            *stringRep;   /* Original string representation */
6862
    size_t          count;        /* Number of ScanFmtPartDescr contained */
6863
    size_t          convCount;    /* Number of conversions that will assign */
6864
    size_t          maxPos;       /* Max position index if XPG3 is used */
6865
    const char      *error;       /* Ptr to error text (NULL if no error */
6866
    char            *scratch;     /* Some scratch pad used by Jim_ScanString */
6867
    ScanFmtPartDescr descr[1];    /* The vector of partial descriptions */
6868
} ScanFmtStringObj;
6869
 
6870
 
6871
static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
6872
static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
6873
static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
6874
 
6875
static Jim_ObjType scanFmtStringObjType = {
6876
    "scanformatstring",
6877
    FreeScanFmtInternalRep,
6878
    DupScanFmtInternalRep,
6879
    UpdateStringOfScanFmt,
6880
    JIM_TYPE_NONE,
6881
};
6882
 
6883
void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
6884
{
6885
    JIM_NOTUSED(interp);
6886
    Jim_Free((char*)objPtr->internalRep.ptr);
6887
    objPtr->internalRep.ptr = 0;
6888
}
6889
 
6890
void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
6891
{
6892
    size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
6893
    ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
6894
 
6895
    JIM_NOTUSED(interp);
6896
    memcpy(newVec, srcPtr->internalRep.ptr, size);
6897
    dupPtr->internalRep.ptr = newVec;
6898
    dupPtr->typePtr = &scanFmtStringObjType;
6899
}
6900
 
6901
void UpdateStringOfScanFmt(Jim_Obj *objPtr)
6902
{
6903
    char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
6904
 
6905
    objPtr->bytes = Jim_StrDup(bytes);
6906
    objPtr->length = strlen(bytes);
6907
}
6908
 
6909
/* SetScanFmtFromAny will parse a given string and create the internal
6910
 * representation of the format specification. In case of an error
6911
 * the error data member of the internal representation will be set
6912
 * to an descriptive error text and the function will be left with
6913
 * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
6914
 * specification */
6915
 
6916
static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
6917
{
6918
    ScanFmtStringObj *fmtObj;
6919
    char *buffer;
6920
    int maxCount, i, approxSize, lastPos = -1;
6921
    const char *fmt = objPtr->bytes;
6922
    int maxFmtLen = objPtr->length;
6923
    const char *fmtEnd = fmt + maxFmtLen;
6924
    int curr;
6925
 
6926
    Jim_FreeIntRep(interp, objPtr);
6927
    /* Count how many conversions could take place maximally */
6928
    for (i=0, maxCount=0; i < maxFmtLen; ++i)
6929
        if (fmt[i] == '%')
6930
            ++maxCount;
6931
    /* Calculate an approximation of the memory necessary */
6932
    approxSize = sizeof(ScanFmtStringObj)           /* Size of the container */
6933
        + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
6934
        + maxFmtLen * sizeof(char) + 3 + 1          /* Scratch + "%n" + '\0' */
6935
        + maxFmtLen * sizeof(char) + 1              /* Original stringrep */
6936
        + maxFmtLen * sizeof(char)                  /* Arg for CHARSETs */
6937
        + (maxCount +1) * sizeof(char)              /* '\0' for every partial */
6938
        + 1;                                        /* safety byte */
6939
    fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
6940
    memset(fmtObj, 0, approxSize);
6941
    fmtObj->size = approxSize;
6942
    fmtObj->maxPos = 0;
6943
    fmtObj->scratch = (char*)&fmtObj->descr[maxCount+1];
6944
    fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
6945
    memcpy(fmtObj->stringRep, fmt, maxFmtLen);
6946
    buffer = fmtObj->stringRep + maxFmtLen + 1;
6947
    objPtr->internalRep.ptr = fmtObj;
6948
    objPtr->typePtr = &scanFmtStringObjType;
6949
    for (i=0, curr=0; fmt < fmtEnd; ++fmt) {
6950
        int width=0, skip;
6951
        ScanFmtPartDescr *descr = &fmtObj->descr[curr];
6952
        fmtObj->count++;
6953
        descr->width = 0;                   /* Assume width unspecified */
6954
        /* Overread and store any "literal" prefix */
6955
        if (*fmt != '%' || fmt[1] == '%') {
6956
            descr->type = 0;
6957
            descr->prefix = &buffer[i];
6958
            for (; fmt < fmtEnd; ++fmt) {
6959
                if (*fmt == '%') {
6960
                    if (fmt[1] != '%') break;
6961
                    ++fmt;
6962
                }
6963
                buffer[i++] = *fmt;
6964
            }
6965
            buffer[i++] = 0;
6966
        }
6967
        /* Skip the conversion introducing '%' sign */
6968
        ++fmt;
6969
        /* End reached due to non-conversion literal only? */
6970
        if (fmt >= fmtEnd)
6971
            goto done;
6972
        descr->pos = 0;                     /* Assume "natural" positioning */
6973
        if (*fmt == '*') {
6974
            descr->pos = -1;       /* Okay, conversion will not be assigned */
6975
            ++fmt;
6976
        } else
6977
            fmtObj->convCount++;    /* Otherwise count as assign-conversion */
6978
        /* Check if next token is a number (could be width or pos */
6979
        if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
6980
            fmt += skip;
6981
            /* Was the number a XPG3 position specifier? */
6982
            if (descr->pos != -1 && *fmt == '$') {
6983
                int prev;
6984
                ++fmt;
6985
                descr->pos = width;
6986
                width = 0;
6987
                /* Look if "natural" postioning and XPG3 one was mixed */
6988
                if ((lastPos == 0 && descr->pos > 0)
6989
                        || (lastPos > 0 && descr->pos == 0)) {
6990
                    fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
6991
                    return JIM_ERR;
6992
                }
6993
                /* Look if this position was already used */
6994
                for (prev=0; prev < curr; ++prev) {
6995
                    if (fmtObj->descr[prev].pos == -1) continue;
6996
                    if (fmtObj->descr[prev].pos == descr->pos) {
6997
                        fmtObj->error = "same \"%n$\" conversion specifier "
6998
                            "used more than once";
6999
                        return JIM_ERR;
7000
                    }
7001
                }
7002
                /* Try to find a width after the XPG3 specifier */
7003
                if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
7004
                    descr->width = width;
7005
                    fmt += skip;
7006
                }
7007
                if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
7008
                    fmtObj->maxPos = descr->pos;
7009
            } else {
7010
                /* Number was not a XPG3, so it has to be a width */
7011
                descr->width = width;
7012
            }
7013
        }
7014
        /* If positioning mode was undetermined yet, fix this */
7015
        if (lastPos == -1)
7016
            lastPos = descr->pos;
7017
        /* Handle CHARSET conversion type ... */
7018
        if (*fmt == '[') {
7019
            int swapped = 1, beg = i, end, j;
7020
            descr->type = '[';
7021
            descr->arg = &buffer[i];
7022
            ++fmt;
7023
            if (*fmt == '^') buffer[i++] = *fmt++;
7024
            if (*fmt == ']') buffer[i++] = *fmt++;
7025
            while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
7026
            if (*fmt != ']') {
7027
                fmtObj->error = "unmatched [ in format string";
7028
                return JIM_ERR;
7029
            }
7030
            end = i;
7031
            buffer[i++] = 0;
7032
            /* In case a range fence was given "backwards", swap it */
7033
            while (swapped) {
7034
                swapped = 0;
7035
                for (j=beg+1; j < end-1; ++j) {
7036
                    if (buffer[j] == '-' && buffer[j-1] > buffer[j+1]) {
7037
                        char tmp = buffer[j-1];
7038
                        buffer[j-1] = buffer[j+1];
7039
                        buffer[j+1] = tmp;
7040
                        swapped = 1;
7041
                    }
7042
                }
7043
            }
7044
        } else {
7045
            /* Remember any valid modifier if given */
7046
            if (strchr("hlL", *fmt) != 0)
7047
                descr->modifier = tolower((int)*fmt++);
7048
 
7049
            descr->type = *fmt;
7050
            if (strchr("efgcsndoxui", *fmt) == 0) {
7051
                fmtObj->error = "bad scan conversion character";
7052
                return JIM_ERR;
7053
            } else if (*fmt == 'c' && descr->width != 0) {
7054
                fmtObj->error = "field width may not be specified in %c "
7055
                    "conversion";
7056
                return JIM_ERR;
7057
            } else if (*fmt == 'u' && descr->modifier == 'l') {
7058
                fmtObj->error = "unsigned wide not supported";
7059
                return JIM_ERR;
7060
            }
7061
        }
7062
        curr++;
7063
    }
7064
done:
7065
    if (fmtObj->convCount == 0) {
7066
        fmtObj->error = "no any conversion specifier given";
7067
        return JIM_ERR;
7068
    }
7069
    return JIM_OK;
7070
}
7071
 
7072
/* Some accessor macros to allow lowlevel access to fields of internal repr */
7073
 
7074
#define FormatGetCnvCount(_fo_) \
7075
    ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
7076
#define FormatGetMaxPos(_fo_) \
7077
    ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
7078
#define FormatGetError(_fo_) \
7079
    ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
7080
 
7081
/* Some Bit testing/setting/cleaning routines. For now only used in handling
7082
 * charsets ([a-z123]) within scanning. Later on perhaps a base for a
7083
 * bitvector implementation in Jim? */
7084
 
7085
static int JimTestBit(const char *bitvec, char ch)
7086
{
7087
    div_t pos = div(ch-1, 8);
7088
    return bitvec[pos.quot] & (1 << pos.rem);
7089
}
7090
 
7091
static void JimSetBit(char *bitvec, char ch)
7092
{
7093
    div_t pos = div(ch-1, 8);
7094
    bitvec[pos.quot] |= (1 << pos.rem);
7095
}
7096
 
7097
#if 0 /* currently not used */
7098
static void JimClearBit(char *bitvec, char ch)
7099
{
7100
    div_t pos = div(ch-1, 8);
7101
    bitvec[pos.quot] &= ~(1 << pos.rem);
7102
}
7103
#endif
7104
 
7105
/* JimScanAString is used to scan an unspecified string that ends with
7106
 * next WS, or a string that is specified via a charset. The charset
7107
 * is currently implemented in a way to only allow for usage with
7108
 * ASCII. Whenever we will switch to UNICODE, another idea has to
7109
 * be born :-/
7110
 *
7111
 * FIXME: Works only with ASCII */
7112
 
7113
static Jim_Obj *
7114
JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
7115
{
7116
    size_t i;
7117
    Jim_Obj *result;
7118
    char charset[256/8+1];  /* A Charset may contain max 256 chars */
7119
    char *buffer = Jim_Alloc(strlen(str)+1), *anchor = buffer;
7120
 
7121
    /* First init charset to nothing or all, depending if a specified
7122
     * or an unspecified string has to be parsed */
7123
    memset(charset, (sdescr ? 0 : 255), sizeof(charset));
7124
    if (sdescr) {
7125
        /* There was a set description given, that means we are parsing
7126
         * a specified string. So we have to build a corresponding
7127
         * charset reflecting the description */
7128
        int notFlag = 0;
7129
        /* Should the set be negated at the end? */
7130
        if (*sdescr == '^') {
7131
            notFlag = 1;
7132
            ++sdescr;
7133
        }
7134
        /* Here '-' is meant literally and not to define a range */
7135
        if (*sdescr == '-') {
7136
            JimSetBit(charset, '-');
7137
            ++sdescr;
7138
        }
7139
        while (*sdescr) {
7140
            if (sdescr[1] == '-' && sdescr[2] != 0) {
7141
                /* Handle range definitions */
7142
                int i;
7143
                for (i=sdescr[0]; i <= sdescr[2]; ++i)
7144
                    JimSetBit(charset, i);
7145
                sdescr += 3;
7146
            } else {
7147
                /* Handle verbatim character definitions */
7148
                JimSetBit(charset, *sdescr++);
7149
            }
7150
        }
7151
        /* Negate the charset if there was a NOT given */
7152
        for (i=0; notFlag && i < sizeof(charset); ++i)
7153
            charset[i] = ~charset[i];
7154
    }
7155
    /* And after all the mess above, the real work begin ... */
7156
    while (str && *str) {
7157
        if (!sdescr && isspace((int)*str))
7158
            break; /* EOS via WS if unspecified */
7159
        if (JimTestBit(charset, *str)) *buffer++ = *str++;
7160
        else break;             /* EOS via mismatch if specified scanning */
7161
    }
7162
    *buffer = 0;                /* Close the string properly ... */
7163
    result = Jim_NewStringObj(interp, anchor, -1);
7164
    Jim_Free(anchor);           /* ... and free it afer usage */
7165
    return result;
7166
}
7167
 
7168
/* ScanOneEntry will scan one entry out of the string passed as argument.
7169
 * It use the sscanf() function for this task. After extracting and
7170
 * converting of the value, the count of scanned characters will be
7171
 * returned of -1 in case of no conversion tool place and string was
7172
 * already scanned thru */
7173
 
7174
static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
7175
        ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
7176
{
7177
#   define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
7178
        ? sizeof(jim_wide)                             \
7179
        : sizeof(double))
7180
    char buffer[MAX_SIZE];
7181
    char *value = buffer;
7182
    const char *tok;
7183
    const ScanFmtPartDescr *descr = &fmtObj->descr[index];
7184
    size_t sLen = strlen(&str[pos]), scanned = 0;
7185
    size_t anchor = pos;
7186
    int i;
7187
 
7188
    /* First pessimiticly assume, we will not scan anything :-) */
7189
    *valObjPtr = 0;
7190
    if (descr->prefix) {
7191
        /* There was a prefix given before the conversion, skip it and adjust
7192
         * the string-to-be-parsed accordingly */
7193
        for (i=0; str[pos] && descr->prefix[i]; ++i) {
7194
            /* If prefix require, skip WS */
7195
            if (isspace((int)descr->prefix[i]))
7196
                while (str[pos] && isspace((int)str[pos])) ++pos;
7197
            else if (descr->prefix[i] != str[pos])
7198
                break;  /* Prefix do not match here, leave the loop */
7199
            else
7200
                ++pos;  /* Prefix matched so far, next round */
7201
        }
7202
        if (str[pos] == 0)
7203
            return -1;  /* All of str consumed: EOF condition */
7204
        else if (descr->prefix[i] != 0)
7205
            return 0;   /* Not whole prefix consumed, no conversion possible */
7206
    }
7207
    /* For all but following conversion, skip leading WS */
7208
    if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
7209
        while (isspace((int)str[pos])) ++pos;
7210
    /* Determine how much skipped/scanned so far */
7211
    scanned = pos - anchor;
7212
    if (descr->type == 'n') {
7213
        /* Return pseudo conversion means: how much scanned so far? */
7214
        *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
7215
    } else if (str[pos] == 0) {
7216
        /* Cannot scan anything, as str is totally consumed */
7217
        return -1;
7218
    } else {
7219
        /* Processing of conversions follows ... */
7220
        if (descr->width > 0) {
7221
            /* Do not try to scan as fas as possible but only the given width.
7222
             * To ensure this, we copy the part that should be scanned. */
7223
            size_t tLen = descr->width > sLen ? sLen : descr->width;
7224
            tok = Jim_StrDupLen(&str[pos], tLen);
7225
        } else {
7226
            /* As no width was given, simply refer to the original string */
7227
            tok = &str[pos];
7228
        }
7229
        switch (descr->type) {
7230
            case 'c':
7231
                *valObjPtr = Jim_NewIntObj(interp, *tok);
7232
                scanned += 1;
7233
                break;
7234
            case 'd': case 'o': case 'x': case 'u': case 'i': {
7235
                char *endp;  /* Position where the number finished */
7236
                int base = descr->type == 'o' ? 8
7237
                    : descr->type == 'x' ? 16
7238
                    : descr->type == 'i' ? 0
7239
                    : 10;
7240
 
7241
                do {
7242
                    /* Try to scan a number with the given base */
7243
                    if (descr->modifier == 'l')
7244
#ifdef HAVE_LONG_LONG
7245
                      *(jim_wide*)value = JimStrtoll(tok, &endp, base);
7246
#else
7247
                      *(jim_wide*)value = strtol(tok, &endp, base);
7248
#endif
7249
                    else
7250
                      if (descr->type == 'u')
7251
                        *(long*)value = strtoul(tok, &endp, base);
7252
                      else
7253
                        *(long*)value = strtol(tok, &endp, base);
7254
                    /* If scanning failed, and base was undetermined, simply
7255
                     * put it to 10 and try once more. This should catch the
7256
                     * case where %i begin to parse a number prefix (e.g.
7257
                     * '0x' but no further digits follows. This will be
7258
                     * handled as a ZERO followed by a char 'x' by Tcl */
7259
                    if (endp == tok && base == 0) base = 10;
7260
                    else break;
7261
                } while (1);
7262
                if (endp != tok) {
7263
                    /* There was some number sucessfully scanned! */
7264
                    if (descr->modifier == 'l')
7265
                        *valObjPtr = Jim_NewIntObj(interp, *(jim_wide*)value);
7266
                    else
7267
                        *valObjPtr = Jim_NewIntObj(interp, *(long*)value);
7268
                    /* Adjust the number-of-chars scanned so far */
7269
                    scanned += endp - tok;
7270
                } else {
7271
                    /* Nothing was scanned. We have to determine if this
7272
                     * happened due to e.g. prefix mismatch or input str
7273
                     * exhausted */
7274
                    scanned = *tok ? 0 : -1;
7275
                }
7276
                break;
7277
            }
7278
            case 's': case '[': {
7279
                *valObjPtr = JimScanAString(interp, descr->arg, tok);
7280
                scanned += Jim_Length(*valObjPtr);
7281
                break;
7282
            }
7283
            case 'e': case 'f': case 'g': {
7284
                char *endp;
7285
 
7286
                *(double*)value = strtod(tok, &endp);
7287
                if (endp != tok) {
7288
                    /* There was some number sucessfully scanned! */
7289
                    *valObjPtr = Jim_NewDoubleObj(interp, *(double*)value);
7290
                    /* Adjust the number-of-chars scanned so far */
7291
                    scanned += endp - tok;
7292
                } else {
7293
                    /* Nothing was scanned. We have to determine if this
7294
                     * happened due to e.g. prefix mismatch or input str
7295
                     * exhausted */
7296
                    scanned = *tok ? 0 : -1;
7297
                }
7298
                break;
7299
            }
7300
        }
7301
        /* If a substring was allocated (due to pre-defined width) do not
7302
         * forget to free it */
7303
        if (tok != &str[pos])
7304
            Jim_Free((char*)tok);
7305
    }
7306
    return scanned;
7307
}
7308
 
7309
/* Jim_ScanString is the workhorse of string scanning. It will scan a given
7310
 * string and returns all converted (and not ignored) values in a list back
7311
 * to the caller. If an error occured, a NULL pointer will be returned */
7312
 
7313
Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
7314
        Jim_Obj *fmtObjPtr, int flags)
7315
{
7316
    size_t i, pos;
7317
    int scanned = 1;
7318
    const char *str = Jim_GetString(strObjPtr, 0);
7319
    Jim_Obj *resultList = 0;
7320
    Jim_Obj **resultVec;
7321
    int resultc;
7322
    Jim_Obj *emptyStr = 0;
7323
    ScanFmtStringObj *fmtObj;
7324
 
7325
    /* If format specification is not an object, convert it! */
7326
    if (fmtObjPtr->typePtr != &scanFmtStringObjType)
7327
        SetScanFmtFromAny(interp, fmtObjPtr);
7328
    fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
7329
    /* Check if format specification was valid */
7330
    if (fmtObj->error != 0) {
7331
        if (flags & JIM_ERRMSG)
7332
            Jim_SetResultString(interp, fmtObj->error, -1);
7333
        return 0;
7334
    }
7335
    /* Allocate a new "shared" empty string for all unassigned conversions */
7336
    emptyStr = Jim_NewEmptyStringObj(interp);
7337
    Jim_IncrRefCount(emptyStr);
7338
    /* Create a list and fill it with empty strings up to max specified XPG3 */
7339
    resultList = Jim_NewListObj(interp, 0, 0);
7340
    if (fmtObj->maxPos > 0) {
7341
        for (i=0; i < fmtObj->maxPos; ++i)
7342
            Jim_ListAppendElement(interp, resultList, emptyStr);
7343
        JimListGetElements(interp, resultList, &resultc, &resultVec);
7344
    }
7345
    /* Now handle every partial format description */
7346
    for (i=0, pos=0; i < fmtObj->count; ++i) {
7347
        ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
7348
        Jim_Obj *value = 0;
7349
        /* Only last type may be "literal" w/o conversion - skip it! */
7350
        if (descr->type == 0) continue;
7351
        /* As long as any conversion could be done, we will proceed */
7352
        if (scanned > 0)
7353
            scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
7354
        /* In case our first try results in EOF, we will leave */
7355
        if (scanned == -1 && i == 0)
7356
            goto eof;
7357
        /* Advance next pos-to-be-scanned for the amount scanned already */
7358
        pos += scanned;
7359
        /* value == 0 means no conversion took place so take empty string */
7360
        if (value == 0)
7361
            value = Jim_NewEmptyStringObj(interp);
7362
        /* If value is a non-assignable one, skip it */
7363
        if (descr->pos == -1) {
7364
            Jim_FreeNewObj(interp, value);
7365
        } else if (descr->pos == 0)
7366
            /* Otherwise append it to the result list if no XPG3 was given */
7367
            Jim_ListAppendElement(interp, resultList, value);
7368
        else if (resultVec[descr->pos-1] == emptyStr) {
7369
            /* But due to given XPG3, put the value into the corr. slot */
7370
            Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
7371
            Jim_IncrRefCount(value);
7372
            resultVec[descr->pos-1] = value;
7373
        } else {
7374
            /* Otherwise, the slot was already used - free obj and ERROR */
7375
            Jim_FreeNewObj(interp, value);
7376
            goto err;
7377
        }
7378
    }
7379
    Jim_DecrRefCount(interp, emptyStr);
7380
    return resultList;
7381
eof:
7382
    Jim_DecrRefCount(interp, emptyStr);
7383
    Jim_FreeNewObj(interp, resultList);
7384
    return (Jim_Obj*)EOF;
7385
err:
7386
    Jim_DecrRefCount(interp, emptyStr);
7387
    Jim_FreeNewObj(interp, resultList);
7388
    return 0;
7389
}
7390
 
7391
/* -----------------------------------------------------------------------------
7392
 * Pseudo Random Number Generation
7393
 * ---------------------------------------------------------------------------*/
7394
static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7395
        int seedLen);
7396
 
7397
/* Initialize the sbox with the numbers from 0 to 255 */
7398
static void JimPrngInit(Jim_Interp *interp)
7399
{
7400
    int i;
7401
    unsigned int seed[256];
7402
 
7403
    interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
7404
    for (i = 0; i < 256; i++)
7405
        seed[i] = (rand() ^ time(NULL) ^ clock());
7406
    JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
7407
}
7408
 
7409
/* Generates N bytes of random data */
7410
static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
7411
{
7412
    Jim_PrngState *prng;
7413
    unsigned char *destByte = (unsigned char*) dest;
7414
    unsigned int si, sj, x;
7415
 
7416
    /* initialization, only needed the first time */
7417
    if (interp->prngState == NULL)
7418
        JimPrngInit(interp);
7419
    prng = interp->prngState;
7420
    /* generates 'len' bytes of pseudo-random numbers */
7421
    for (x = 0; x < len; x++) {
7422
        prng->i = (prng->i+1) & 0xff;
7423
        si = prng->sbox[prng->i];
7424
        prng->j = (prng->j + si) & 0xff;
7425
        sj = prng->sbox[prng->j];
7426
        prng->sbox[prng->i] = sj;
7427
        prng->sbox[prng->j] = si;
7428
        *destByte++ = prng->sbox[(si+sj)&0xff];
7429
    }
7430
}
7431
 
7432
/* Re-seed the generator with user-provided bytes */
7433
static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
7434
        int seedLen)
7435
{
7436
    int i;
7437
    unsigned char buf[256];
7438
    Jim_PrngState *prng;
7439
 
7440
    /* initialization, only needed the first time */
7441
    if (interp->prngState == NULL)
7442
        JimPrngInit(interp);
7443
    prng = interp->prngState;
7444
 
7445
    /* Set the sbox[i] with i */
7446
    for (i = 0; i < 256; i++)
7447
        prng->sbox[i] = i;
7448
    /* Now use the seed to perform a random permutation of the sbox */
7449
    for (i = 0; i < seedLen; i++) {
7450
        unsigned char t;
7451
 
7452
        t = prng->sbox[i&0xFF];
7453
        prng->sbox[i&0xFF] = prng->sbox[seed[i]];
7454
        prng->sbox[seed[i]] = t;
7455
    }
7456
    prng->i = prng->j = 0;
7457
    /* discard the first 256 bytes of stream. */
7458
    JimRandomBytes(interp, buf, 256);
7459
}
7460
 
7461
/* -----------------------------------------------------------------------------
7462
 * Dynamic libraries support (WIN32 not supported)
7463
 * ---------------------------------------------------------------------------*/
7464
 
7465
#ifdef JIM_DYNLIB
7466
#ifdef WIN32
7467
#define RTLD_LAZY 0
7468
void * dlopen(const char *path, int mode)
7469
{
7470
    JIM_NOTUSED(mode);
7471
 
7472
    return (void *)LoadLibraryA(path);
7473
}
7474
int dlclose(void *handle)
7475
{
7476
    FreeLibrary((HANDLE)handle);
7477
    return 0;
7478
}
7479
void *dlsym(void *handle, const char *symbol)
7480
{
7481
    return GetProcAddress((HMODULE)handle, symbol);
7482
}
7483
static char win32_dlerror_string[121];
7484
const char *dlerror()
7485
{
7486
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
7487
                   LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
7488
    return win32_dlerror_string;
7489
}
7490
#endif /* WIN32 */
7491
 
7492
int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7493
{
7494
    Jim_Obj *libPathObjPtr;
7495
    int prefixc, i;
7496
    void *handle;
7497
    int (*onload)(Jim_Interp *interp);
7498
 
7499
    libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7500
    if (libPathObjPtr == NULL) {
7501
        prefixc = 0;
7502
        libPathObjPtr = NULL;
7503
    } else {
7504
        Jim_IncrRefCount(libPathObjPtr);
7505
        Jim_ListLength(interp, libPathObjPtr, &prefixc);
7506
    }
7507
 
7508
    for (i = -1; i < prefixc; i++) {
7509
        if (i < 0) {
7510
            handle = dlopen(pathName, RTLD_LAZY);
7511
        } else {
7512
            FILE *fp;
7513
            char buf[JIM_PATH_LEN];
7514
            const char *prefix;
7515
            int prefixlen;
7516
            Jim_Obj *prefixObjPtr;
7517
 
7518
            buf[0] = '\0';
7519
            if (Jim_ListIndex(interp, libPathObjPtr, i,
7520
                    &prefixObjPtr, JIM_NONE) != JIM_OK)
7521
                continue;
7522
            prefix = Jim_GetString(prefixObjPtr, NULL);
7523
            prefixlen = strlen(prefix);
7524
            if (prefixlen+strlen(pathName)+1 >= JIM_PATH_LEN)
7525
                continue;
7526
            if (prefixlen && prefix[prefixlen-1] == '/')
7527
                sprintf(buf, "%s%s", prefix, pathName);
7528
            else
7529
                sprintf(buf, "%s/%s", prefix, pathName);
7530
            fp = fopen(buf, "r");
7531
            if (fp == NULL)
7532
                continue;
7533
            fclose(fp);
7534
            handle = dlopen(buf, RTLD_LAZY);
7535
        }
7536
        if (handle == NULL) {
7537
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7538
            Jim_AppendStrings(interp, Jim_GetResult(interp),
7539
                "error loading extension \"", pathName,
7540
                "\": ", dlerror(), NULL);
7541
            if (i < 0)
7542
                continue;
7543
            goto err;
7544
        }
7545
        if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
7546
            Jim_SetResultString(interp,
7547
                    "No Jim_OnLoad symbol found on extension", -1);
7548
            goto err;
7549
        }
7550
        if (onload(interp) == JIM_ERR) {
7551
            dlclose(handle);
7552
            goto err;
7553
        }
7554
        Jim_SetEmptyResult(interp);
7555
        if (libPathObjPtr != NULL)
7556
            Jim_DecrRefCount(interp, libPathObjPtr);
7557
        return JIM_OK;
7558
    }
7559
err:
7560
    if (libPathObjPtr != NULL)
7561
        Jim_DecrRefCount(interp, libPathObjPtr);
7562
    return JIM_ERR;
7563
}
7564
#else /* JIM_DYNLIB */
7565
int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
7566
{
7567
    JIM_NOTUSED(interp);
7568
    JIM_NOTUSED(pathName);
7569
 
7570
    Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
7571
    return JIM_ERR;
7572
}
7573
#endif/* JIM_DYNLIB */
7574
 
7575
/* -----------------------------------------------------------------------------
7576
 * Packages handling
7577
 * ---------------------------------------------------------------------------*/
7578
 
7579
#define JIM_PKG_ANY_VERSION -1
7580
 
7581
/* Convert a string of the type "1.2" into an integer.
7582
 * MAJOR.MINOR is converted as MAJOR*100+MINOR, so "1.2" is converted
7583
 * to the integer with value 102 */
7584
static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
7585
        int *intPtr, int flags)
7586
{
7587
    char *copy;
7588
    jim_wide major, minor;
7589
    char *majorStr, *minorStr, *p;
7590
 
7591
    if (v[0] == '\0') {
7592
        *intPtr = JIM_PKG_ANY_VERSION;
7593
        return JIM_OK;
7594
    }
7595
 
7596
    copy = Jim_StrDup(v);
7597
    p = strchr(copy, '.');
7598
    if (p == NULL) goto badfmt;
7599
    *p = '\0';
7600
    majorStr = copy;
7601
    minorStr = p+1;
7602
 
7603
    if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
7604
        Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
7605
        goto badfmt;
7606
    *intPtr = (int)(major*100+minor);
7607
    Jim_Free(copy);
7608
    return JIM_OK;
7609
 
7610
badfmt:
7611
    Jim_Free(copy);
7612
    if (flags & JIM_ERRMSG) {
7613
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7614
        Jim_AppendStrings(interp, Jim_GetResult(interp),
7615
                "invalid package version '", v, "'", NULL);
7616
    }
7617
    return JIM_ERR;
7618
}
7619
 
7620
#define JIM_MATCHVER_EXACT (1<<JIM_PRIV_FLAG_SHIFT)
7621
static int JimPackageMatchVersion(int needed, int actual, int flags)
7622
{
7623
    if (needed == JIM_PKG_ANY_VERSION) return 1;
7624
    if (flags & JIM_MATCHVER_EXACT) {
7625
        return needed == actual;
7626
    } else {
7627
        return needed/100 == actual/100 && (needed <= actual);
7628
    }
7629
}
7630
 
7631
int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
7632
        int flags)
7633
{
7634
    int intVersion;
7635
    /* Check if the version format is ok */
7636
    if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
7637
        return JIM_ERR;
7638
    /* If the package was already provided returns an error. */
7639
    if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
7640
        if (flags & JIM_ERRMSG) {
7641
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7642
            Jim_AppendStrings(interp, Jim_GetResult(interp),
7643
                    "package '", name, "' was already provided", NULL);
7644
        }
7645
        return JIM_ERR;
7646
    }
7647
    Jim_AddHashEntry(&interp->packages, name, (char*) ver);
7648
    return JIM_OK;
7649
}
7650
 
7651
#ifndef JIM_ANSIC
7652
 
7653
#ifndef WIN32
7654
# include <sys/types.h>
7655
# include <dirent.h>
7656
#else
7657
# include <io.h>
7658
/* Posix dirent.h compatiblity layer for WIN32.
7659
 * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
7660
 * Copyright Salvatore Sanfilippo ,2005.
7661
 *
7662
 * Permission to use, copy, modify, and distribute this software and its
7663
 * documentation for any purpose is hereby granted without fee, provided
7664
 * that this copyright and permissions notice appear in all copies and
7665
 * derivatives.
7666
 *
7667
 * This software is supplied "as is" without express or implied warranty.
7668
 * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
7669
 */
7670
 
7671
struct dirent {
7672
    char *d_name;
7673
};
7674
 
7675
typedef struct DIR {
7676
    long                handle; /* -1 for failed rewind */
7677
    struct _finddata_t  info;
7678
    struct dirent       result; /* d_name null iff first time */
7679
    char                *name;  /* null-terminated char string */
7680
} DIR;
7681
 
7682
DIR *opendir(const char *name)
7683
{
7684
    DIR *dir = 0;
7685
 
7686
    if(name && name[0]) {
7687
        size_t base_length = strlen(name);
7688
        const char *all = /* search pattern must end with suitable wildcard */
7689
            strchr("/\\", name[base_length - 1]) ? "*" : "/*";
7690
 
7691
        if((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
7692
           (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
7693
        {
7694
            strcat(strcpy(dir->name, name), all);
7695
 
7696
            if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
7697
                dir->result.d_name = 0;
7698
            else { /* rollback */
7699
                Jim_Free(dir->name);
7700
                Jim_Free(dir);
7701
                dir = 0;
7702
            }
7703
        } else { /* rollback */
7704
            Jim_Free(dir);
7705
            dir   = 0;
7706
            errno = ENOMEM;
7707
        }
7708
    } else {
7709
        errno = EINVAL;
7710
    }
7711
    return dir;
7712
}
7713
 
7714
int closedir(DIR *dir)
7715
{
7716
    int result = -1;
7717
 
7718
    if(dir) {
7719
        if(dir->handle != -1)
7720
            result = _findclose(dir->handle);
7721
        Jim_Free(dir->name);
7722
        Jim_Free(dir);
7723
    }
7724
    if(result == -1) /* map all errors to EBADF */
7725
        errno = EBADF;
7726
    return result;
7727
}
7728
 
7729
struct dirent *readdir(DIR *dir)
7730
{
7731
    struct dirent *result = 0;
7732
 
7733
    if(dir && dir->handle != -1) {
7734
        if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
7735
            result         = &dir->result;
7736
            result->d_name = dir->info.name;
7737
        }
7738
    } else {
7739
        errno = EBADF;
7740
    }
7741
    return result;
7742
}
7743
 
7744
#endif /* WIN32 */
7745
 
7746
static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7747
        int prefixc, const char *pkgName, int pkgVer, int flags)
7748
{
7749
    int bestVer = -1, i;
7750
    int pkgNameLen = strlen(pkgName);
7751
    char *bestPackage = NULL;
7752
    struct dirent *de;
7753
 
7754
    for (i = 0; i < prefixc; i++) {
7755
        DIR *dir;
7756
        char buf[JIM_PATH_LEN];
7757
        int prefixLen;
7758
 
7759
        if (prefixes[i] == NULL) continue;
7760
        strncpy(buf, prefixes[i], JIM_PATH_LEN);
7761
        buf[JIM_PATH_LEN-1] = '\0';
7762
        prefixLen = strlen(buf);
7763
        if (prefixLen && buf[prefixLen-1] == '/')
7764
            buf[prefixLen-1] = '\0';
7765
 
7766
        if ((dir = opendir(buf)) == NULL) continue;
7767
        while ((de = readdir(dir)) != NULL) {
7768
            char *fileName = de->d_name;
7769
            int fileNameLen = strlen(fileName);
7770
 
7771
            if (strncmp(fileName, "jim-", 4) == 0 &&
7772
                strncmp(fileName+4, pkgName, pkgNameLen) == 0 &&
7773
                *(fileName+4+pkgNameLen) == '-' &&
7774
                fileNameLen > 4 && /* note that this is not really useful */
7775
                (strncmp(fileName+fileNameLen-4, ".tcl", 4) == 0 ||
7776
                 strncmp(fileName+fileNameLen-4, ".dll", 4) == 0 ||
7777
                 strncmp(fileName+fileNameLen-3, ".so", 3) == 0))
7778
            {
7779
                char ver[6]; /* xx.yy<nulterm> */
7780
                char *p = strrchr(fileName, '.');
7781
                int verLen, fileVer;
7782
 
7783
                verLen = p - (fileName+4+pkgNameLen+1);
7784
                if (verLen < 3 || verLen > 5) continue;
7785
                memcpy(ver, fileName+4+pkgNameLen+1, verLen);
7786
                ver[verLen] = '\0';
7787
                if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
7788
                        != JIM_OK) continue;
7789
                if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
7790
                    (bestVer == -1 || bestVer < fileVer))
7791
                {
7792
                    bestVer = fileVer;
7793
                    Jim_Free(bestPackage);
7794
                    bestPackage = Jim_Alloc(strlen(buf)+strlen(fileName)+2);
7795
                    sprintf(bestPackage, "%s/%s", buf, fileName);
7796
                }
7797
            }
7798
        }
7799
        closedir(dir);
7800
    }
7801
    return bestPackage;
7802
}
7803
 
7804
#else /* JIM_ANSIC */
7805
 
7806
static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
7807
        int prefixc, const char *pkgName, int pkgVer, int flags)
7808
{
7809
    JIM_NOTUSED(interp);
7810
    JIM_NOTUSED(prefixes);
7811
    JIM_NOTUSED(prefixc);
7812
    JIM_NOTUSED(pkgName);
7813
    JIM_NOTUSED(pkgVer);
7814
    JIM_NOTUSED(flags);
7815
    return NULL;
7816
}
7817
 
7818
#endif /* JIM_ANSIC */
7819
 
7820
/* Search for a suitable package under every dir specified by jim_libpath
7821
 * and load it if possible. If a suitable package was loaded with success
7822
 * JIM_OK is returned, otherwise JIM_ERR is returned. */
7823
static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
7824
        int flags)
7825
{
7826
    Jim_Obj *libPathObjPtr;
7827
    char **prefixes, *best;
7828
    int prefixc, i, retCode = JIM_OK;
7829
 
7830
    libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
7831
    if (libPathObjPtr == NULL) {
7832
        prefixc = 0;
7833
        libPathObjPtr = NULL;
7834
    } else {
7835
        Jim_IncrRefCount(libPathObjPtr);
7836
        Jim_ListLength(interp, libPathObjPtr, &prefixc);
7837
    }
7838
 
7839
    prefixes = Jim_Alloc(sizeof(char*)*prefixc);
7840
    for (i = 0; i < prefixc; i++) {
7841
            Jim_Obj *prefixObjPtr;
7842
            if (Jim_ListIndex(interp, libPathObjPtr, i,
7843
                    &prefixObjPtr, JIM_NONE) != JIM_OK)
7844
            {
7845
                prefixes[i] = NULL;
7846
                continue;
7847
            }
7848
            prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
7849
    }
7850
    /* Scan every directory to find the "best" package. */
7851
    best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
7852
    if (best != NULL) {
7853
        char *p = strrchr(best, '.');
7854
        /* Try to load/source it */
7855
        if (p && strcmp(p, ".tcl") == 0) {
7856
            retCode = Jim_EvalFile(interp, best);
7857
        } else {
7858
            retCode = Jim_LoadLibrary(interp, best);
7859
        }
7860
    } else {
7861
        retCode = JIM_ERR;
7862
    }
7863
    Jim_Free(best);
7864
    for (i = 0; i < prefixc; i++)
7865
        Jim_Free(prefixes[i]);
7866
    Jim_Free(prefixes);
7867
    if (libPathObjPtr)
7868
        Jim_DecrRefCount(interp, libPathObjPtr);
7869
    return retCode;
7870
}
7871
 
7872
const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
7873
        const char *ver, int flags)
7874
{
7875
    Jim_HashEntry *he;
7876
    int requiredVer;
7877
 
7878
    if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
7879
        return NULL;
7880
    he = Jim_FindHashEntry(&interp->packages, name);
7881
    if (he == NULL) {
7882
        /* Try to load the package. */
7883
        if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
7884
            he = Jim_FindHashEntry(&interp->packages, name);
7885
            if (he == NULL) {
7886
                return "?";
7887
            }
7888
            return he->val;
7889
        }
7890
        /* No way... return an error. */
7891
        if (flags & JIM_ERRMSG) {
7892
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7893
            Jim_AppendStrings(interp, Jim_GetResult(interp),
7894
                    "Can't find package '", name, "'", NULL);
7895
        }
7896
        return NULL;
7897
    } else {
7898
        int actualVer;
7899
        if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
7900
                != JIM_OK)
7901
        {
7902
            return NULL;
7903
        }
7904
        /* Check if version matches. */
7905
        if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
7906
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
7907
            Jim_AppendStrings(interp, Jim_GetResult(interp),
7908
                    "Package '", name, "' already loaded, but with version ",
7909
                    he->val, NULL);
7910
            return NULL;
7911
        }
7912
        return he->val;
7913
    }
7914
}
7915
 
7916
/* -----------------------------------------------------------------------------
7917
 * Eval
7918
 * ---------------------------------------------------------------------------*/
7919
#define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
7920
#define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
7921
 
7922
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
7923
        Jim_Obj *const *argv);
7924
 
7925
/* Handle calls to the [unknown] command */
7926
static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
7927
{
7928
    Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
7929
    int retCode;
7930
 
7931
    /* If the [unknown] command does not exists returns
7932
     * just now */
7933
    if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
7934
        return JIM_ERR;
7935
 
7936
    /* The object interp->unknown just contains
7937
     * the "unknown" string, it is used in order to
7938
     * avoid to lookup the unknown command every time
7939
     * but instread to cache the result. */
7940
    if (argc+1 <= JIM_EVAL_SARGV_LEN)
7941
        v = sv;
7942
    else
7943
        v = Jim_Alloc(sizeof(Jim_Obj*)*(argc+1));
7944
    /* Make a copy of the arguments vector, but shifted on
7945
     * the right of one position. The command name of the
7946
     * command will be instead the first argument of the
7947
     * [unknonw] call. */
7948
    memcpy(v+1, argv, sizeof(Jim_Obj*)*argc);
7949
    v[0] = interp->unknown;
7950
    /* Call it */
7951
    retCode = Jim_EvalObjVector(interp, argc+1, v);
7952
    /* Clean up */
7953
    if (v != sv)
7954
        Jim_Free(v);
7955
    return retCode;
7956
}
7957
 
7958
/* Eval the object vector 'objv' composed of 'objc' elements.
7959
 * Every element is used as single argument.
7960
 * Jim_EvalObj() will call this function every time its object
7961
 * argument is of "list" type, with no string representation.
7962
 *
7963
 * This is possible because the string representation of a
7964
 * list object generated by the UpdateStringOfList is made
7965
 * in a way that ensures that every list element is a different
7966
 * command argument. */
7967
int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
7968
{
7969
    int i, retcode;
7970
    Jim_Cmd *cmdPtr;
7971
 
7972
    /* Incr refcount of arguments. */
7973
    for (i = 0; i < objc; i++)
7974
        Jim_IncrRefCount(objv[i]);
7975
    /* Command lookup */
7976
    cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
7977
    if (cmdPtr == NULL) {
7978
        retcode = JimUnknown(interp, objc, objv);
7979
    } else {
7980
        /* Call it -- Make sure result is an empty object. */
7981
        Jim_SetEmptyResult(interp);
7982
        if (cmdPtr->cmdProc) {
7983
            interp->cmdPrivData = cmdPtr->privData;
7984
            retcode = cmdPtr->cmdProc(interp, objc, objv);
7985
        } else {
7986
            retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
7987
            if (retcode == JIM_ERR) {
7988
                JimAppendStackTrace(interp,
7989
                    Jim_GetString(objv[0], NULL), "?", 1);
7990
            }
7991
        }
7992
    }
7993
    /* Decr refcount of arguments and return the retcode */
7994
    for (i = 0; i < objc; i++)
7995
        Jim_DecrRefCount(interp, objv[i]);
7996
    return retcode;
7997
}
7998
 
7999
/* Interpolate the given tokens into a unique Jim_Obj returned by reference
8000
 * via *objPtrPtr. This function is only called by Jim_EvalObj().
8001
 * The returned object has refcount = 0. */
8002
int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
8003
        int tokens, Jim_Obj **objPtrPtr)
8004
{
8005
    int totlen = 0, i, retcode;
8006
    Jim_Obj **intv;
8007
    Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
8008
    Jim_Obj *objPtr;
8009
    char *s;
8010
 
8011
    if (tokens <= JIM_EVAL_SINTV_LEN)
8012
        intv = sintv;
8013
    else
8014
        intv = Jim_Alloc(sizeof(Jim_Obj*)*
8015
                tokens);
8016
    /* Compute every token forming the argument
8017
     * in the intv objects vector. */
8018
    for (i = 0; i < tokens; i++) {
8019
        switch(token[i].type) {
8020
        case JIM_TT_ESC:
8021
        case JIM_TT_STR:
8022
            intv[i] = token[i].objPtr;
8023
            break;
8024
        case JIM_TT_VAR:
8025
            intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8026
            if (!intv[i]) {
8027
                retcode = JIM_ERR;
8028
                goto err;
8029
            }
8030
            break;
8031
        case JIM_TT_DICTSUGAR:
8032
            intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
8033
            if (!intv[i]) {
8034
                retcode = JIM_ERR;
8035
                goto err;
8036
            }
8037
            break;
8038
        case JIM_TT_CMD:
8039
            retcode = Jim_EvalObj(interp, token[i].objPtr);
8040
            if (retcode != JIM_OK)
8041
                goto err;
8042
            intv[i] = Jim_GetResult(interp);
8043
            break;
8044
        default:
8045
            Jim_Panic(interp,
8046
              "default token type reached "
8047
              "in Jim_InterpolateTokens().");
8048
            break;
8049
        }
8050
        Jim_IncrRefCount(intv[i]);
8051
        /* Make sure there is a valid
8052
         * string rep, and add the string
8053
         * length to the total legnth. */
8054
        Jim_GetString(intv[i], NULL);
8055
        totlen += intv[i]->length;
8056
    }
8057
    /* Concatenate every token in an unique
8058
     * object. */
8059
    objPtr = Jim_NewStringObjNoAlloc(interp,
8060
            NULL, 0);
8061
    s = objPtr->bytes = Jim_Alloc(totlen+1);
8062
    objPtr->length = totlen;
8063
    for (i = 0; i < tokens; i++) {
8064
        memcpy(s, intv[i]->bytes, intv[i]->length);
8065
        s += intv[i]->length;
8066
        Jim_DecrRefCount(interp, intv[i]);
8067
    }
8068
    objPtr->bytes[totlen] = '\0';
8069
    /* Free the intv vector if not static. */
8070
    if (tokens > JIM_EVAL_SINTV_LEN)
8071
        Jim_Free(intv);
8072
    *objPtrPtr = objPtr;
8073
    return JIM_OK;
8074
err:
8075
    i--;
8076
    for (; i >= 0; i--)
8077
        Jim_DecrRefCount(interp, intv[i]);
8078
    if (tokens > JIM_EVAL_SINTV_LEN)
8079
        Jim_Free(intv);
8080
    return retcode;
8081
}
8082
 
8083
/* Helper of Jim_EvalObj() to perform argument expansion.
8084
 * Basically this function append an argument to 'argv'
8085
 * (and increments argc by reference accordingly), performing
8086
 * expansion of the list object if 'expand' is non-zero, or
8087
 * just adding objPtr to argv if 'expand' is zero. */
8088
void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
8089
        int *argcPtr, int expand, Jim_Obj *objPtr)
8090
{
8091
    if (!expand) {
8092
        (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+1));
8093
        /* refcount of objPtr not incremented because
8094
         * we are actually transfering a reference from
8095
         * the old 'argv' to the expanded one. */
8096
        (*argv)[*argcPtr] = objPtr;
8097
        (*argcPtr)++;
8098
    } else {
8099
        int len, i;
8100
 
8101
        Jim_ListLength(interp, objPtr, &len);
8102
        (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr)+len));
8103
        for (i = 0; i < len; i++) {
8104
            (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
8105
            Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
8106
            (*argcPtr)++;
8107
        }
8108
        /* The original object reference is no longer needed,
8109
         * after the expansion it is no longer present on
8110
         * the argument vector, but the single elements are
8111
         * in its place. */
8112
        Jim_DecrRefCount(interp, objPtr);
8113
    }
8114
}
8115
 
8116
int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8117
{
8118
    int i, j = 0, len;
8119
    ScriptObj *script;
8120
    ScriptToken *token;
8121
    int *cs; /* command structure array */
8122
    int retcode = JIM_OK;
8123
    Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
8124
 
8125
    interp->errorFlag = 0;
8126
 
8127
    /* If the object is of type "list" and there is no
8128
     * string representation for this object, we can call
8129
     * a specialized version of Jim_EvalObj() */
8130
    if (scriptObjPtr->typePtr == &listObjType &&
8131
        scriptObjPtr->internalRep.listValue.len &&
8132
        scriptObjPtr->bytes == NULL) {
8133
        Jim_IncrRefCount(scriptObjPtr);
8134
        retcode = Jim_EvalObjVector(interp,
8135
                scriptObjPtr->internalRep.listValue.len,
8136
                scriptObjPtr->internalRep.listValue.ele);
8137
        Jim_DecrRefCount(interp, scriptObjPtr);
8138
        return retcode;
8139
    }
8140
 
8141
    Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
8142
    script = Jim_GetScript(interp, scriptObjPtr);
8143
    /* Now we have to make sure the internal repr will not be
8144
     * freed on shimmering.
8145
     *
8146
     * Think for example to this:
8147
     *
8148
     * set x {llength $x; ... some more code ...}; eval $x
8149
     *
8150
     * In order to preserve the internal rep, we increment the
8151
     * inUse field of the script internal rep structure. */
8152
    script->inUse++;
8153
 
8154
    token = script->token;
8155
    len = script->len;
8156
    cs = script->cmdStruct;
8157
    i = 0; /* 'i' is the current token index. */
8158
 
8159
    /* Reset the interpreter result. This is useful to
8160
     * return the emtpy result in the case of empty program. */
8161
    Jim_SetEmptyResult(interp);
8162
 
8163
    /* Execute every command sequentially, returns on
8164
     * error (i.e. if a command does not return JIM_OK) */
8165
    while (i < len) {
8166
        int expand = 0;
8167
        int argc = *cs++; /* Get the number of arguments */
8168
        Jim_Cmd *cmd;
8169
 
8170
        /* Set the expand flag if needed. */
8171
        if (argc == -1) {
8172
            expand++;
8173
            argc = *cs++;
8174
        }
8175
        /* Allocate the arguments vector */
8176
        if (argc <= JIM_EVAL_SARGV_LEN)
8177
            argv = sargv;
8178
        else
8179
            argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
8180
        /* Populate the arguments objects. */
8181
        for (j = 0; j < argc; j++) {
8182
            int tokens = *cs++;
8183
 
8184
            /* tokens is negative if expansion is needed.
8185
             * for this argument. */
8186
            if (tokens < 0) {
8187
                tokens = (-tokens)-1;
8188
                i++;
8189
            }
8190
            if (tokens == 1) {
8191
                /* Fast path if the token does not
8192
                 * need interpolation */
8193
                switch(token[i].type) {
8194
                case JIM_TT_ESC:
8195
                case JIM_TT_STR:
8196
                    argv[j] = token[i].objPtr;
8197
                    break;
8198
                case JIM_TT_VAR:
8199
                    tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
8200
                            JIM_ERRMSG);
8201
                    if (!tmpObjPtr) {
8202
                        retcode = JIM_ERR;
8203
                        goto err;
8204
                    }
8205
                    argv[j] = tmpObjPtr;
8206
                    break;
8207
                case JIM_TT_DICTSUGAR:
8208
                    tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
8209
                    if (!tmpObjPtr) {
8210
                        retcode = JIM_ERR;
8211
                        goto err;
8212
                    }
8213
                    argv[j] = tmpObjPtr;
8214
                    break;
8215
                case JIM_TT_CMD:
8216
                    retcode = Jim_EvalObj(interp, token[i].objPtr);
8217
                    if (retcode != JIM_OK)
8218
                        goto err;
8219
                    argv[j] = Jim_GetResult(interp);
8220
                    break;
8221
                default:
8222
                    Jim_Panic(interp,
8223
                      "default token type reached "
8224
                      "in Jim_EvalObj().");
8225
                    break;
8226
                }
8227
                Jim_IncrRefCount(argv[j]);
8228
                i += 2;
8229
            } else {
8230
                /* For interpolation we call an helper
8231
                 * function doing the work for us. */
8232
                if ((retcode = Jim_InterpolateTokens(interp,
8233
                        token+i, tokens, &tmpObjPtr)) != JIM_OK)
8234
                {
8235
                    goto err;
8236
                }
8237
                argv[j] = tmpObjPtr;
8238
                Jim_IncrRefCount(argv[j]);
8239
                i += tokens+1;
8240
            }
8241
        }
8242
        /* Handle {expand} expansion */
8243
        if (expand) {
8244
            int *ecs = cs - argc;
8245
            int eargc = 0;
8246
            Jim_Obj **eargv = NULL;
8247
 
8248
            for (j = 0; j < argc; j++) {
8249
                Jim_ExpandArgument( interp, &eargv, &eargc,
8250
                        ecs[j] < 0, argv[j]);
8251
            }
8252
            if (argv != sargv)
8253
                Jim_Free(argv);
8254
            argc = eargc;
8255
            argv = eargv;
8256
            j = argc;
8257
            if (argc == 0) {
8258
                /* Nothing to do with zero args. */
8259
                Jim_Free(eargv);
8260
                continue;
8261
            }
8262
        }
8263
        /* Lookup the command to call */
8264
        cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
8265
        if (cmd != NULL) {
8266
            /* Call it -- Make sure result is an empty object. */
8267
            Jim_SetEmptyResult(interp);
8268
            if (cmd->cmdProc) {
8269
                interp->cmdPrivData = cmd->privData;
8270
                retcode = cmd->cmdProc(interp, argc, argv);
8271
            } else {
8272
                retcode = JimCallProcedure(interp, cmd, argc, argv);
8273
                if (retcode == JIM_ERR) {
8274
                    JimAppendStackTrace(interp,
8275
                        Jim_GetString(argv[0], NULL), script->fileName,
8276
                        token[i-argc*2].linenr);
8277
                }
8278
            }
8279
        } else {
8280
            /* Call [unknown] */
8281
            retcode = JimUnknown(interp, argc, argv);
8282
        }
8283
        if (retcode != JIM_OK) {
8284
            i -= argc*2; /* point to the command name. */
8285
            goto err;
8286
        }
8287
        /* Decrement the arguments count */
8288
        for (j = 0; j < argc; j++) {
8289
            Jim_DecrRefCount(interp, argv[j]);
8290
        }
8291
 
8292
        if (argv != sargv) {
8293
            Jim_Free(argv);
8294
            argv = NULL;
8295
        }
8296
    }
8297
    /* Note that we don't have to decrement inUse, because the
8298
     * following code transfers our use of the reference again to
8299
     * the script object. */
8300
    j = 0; /* on normal termination, the argv array is already
8301
          Jim_DecrRefCount-ed. */
8302
err:
8303
    /* Handle errors. */
8304
    if (retcode == JIM_ERR && !interp->errorFlag) {
8305
        interp->errorFlag = 1;
8306
        JimSetErrorFileName(interp, script->fileName);
8307
        JimSetErrorLineNumber(interp, token[i].linenr);
8308
        JimResetStackTrace(interp);
8309
    }
8310
    Jim_FreeIntRep(interp, scriptObjPtr);
8311
    scriptObjPtr->typePtr = &scriptObjType;
8312
    Jim_SetIntRepPtr(scriptObjPtr, script);
8313
    Jim_DecrRefCount(interp, scriptObjPtr);
8314
    for (i = 0; i < j; i++) {
8315
        Jim_DecrRefCount(interp, argv[i]);
8316
    }
8317
    if (argv != sargv)
8318
        Jim_Free(argv);
8319
    return retcode;
8320
}
8321
 
8322
/* Call a procedure implemented in Tcl.
8323
 * It's possible to speed-up a lot this function, currently
8324
 * the callframes are not cached, but allocated and
8325
 * destroied every time. What is expecially costly is
8326
 * to create/destroy the local vars hash table every time.
8327
 *
8328
 * This can be fixed just implementing callframes caching
8329
 * in JimCreateCallFrame() and JimFreeCallFrame(). */
8330
int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
8331
        Jim_Obj *const *argv)
8332
{
8333
    int i, retcode;
8334
    Jim_CallFrame *callFramePtr;
8335
 
8336
    /* Check arity */
8337
    if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
8338
        argc > cmd->arityMax)) {
8339
        Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8340
        Jim_AppendStrings(interp, objPtr,
8341
            "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
8342
            (cmd->arityMin > 1) ? " " : "",
8343
            Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
8344
        Jim_SetResult(interp, objPtr);
8345
        return JIM_ERR;
8346
    }
8347
    /* Check if there are too nested calls */
8348
    if (interp->numLevels == interp->maxNestingDepth) {
8349
        Jim_SetResultString(interp,
8350
            "Too many nested calls. Infinite recursion?", -1);
8351
        return JIM_ERR;
8352
    }
8353
    /* Create a new callframe */
8354
    callFramePtr = JimCreateCallFrame(interp);
8355
    callFramePtr->parentCallFrame = interp->framePtr;
8356
    callFramePtr->argv = argv;
8357
    callFramePtr->argc = argc;
8358
    callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
8359
    callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
8360
    callFramePtr->staticVars = cmd->staticVars;
8361
    Jim_IncrRefCount(cmd->argListObjPtr);
8362
    Jim_IncrRefCount(cmd->bodyObjPtr);
8363
    interp->framePtr = callFramePtr;
8364
    interp->numLevels ++;
8365
    /* Set arguments */
8366
    for (i = 0; i < cmd->arityMin-1; i++) {
8367
        Jim_Obj *objPtr;
8368
 
8369
        Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8370
        Jim_SetVariable(interp, objPtr, argv[i+1]);
8371
    }
8372
    if (cmd->arityMax == -1) {
8373
        Jim_Obj *listObjPtr, *objPtr;
8374
 
8375
        listObjPtr = Jim_NewListObj(interp, argv+cmd->arityMin,
8376
                argc-cmd->arityMin);
8377
        Jim_ListIndex(interp, cmd->argListObjPtr, i, &objPtr, JIM_NONE);
8378
        Jim_SetVariable(interp, objPtr, listObjPtr);
8379
    }
8380
    /* Eval the body */
8381
    retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
8382
 
8383
    /* Destroy the callframe */
8384
    interp->numLevels --;
8385
    interp->framePtr = interp->framePtr->parentCallFrame;
8386
    if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
8387
        JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
8388
    } else {
8389
        JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
8390
    }
8391
    /* Handle the JIM_EVAL return code */
8392
    if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
8393
        int savedLevel = interp->evalRetcodeLevel;
8394
 
8395
        interp->evalRetcodeLevel = interp->numLevels;
8396
        while (retcode == JIM_EVAL) {
8397
            Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
8398
            Jim_IncrRefCount(resultScriptObjPtr);
8399
            retcode = Jim_EvalObj(interp, resultScriptObjPtr);
8400
            Jim_DecrRefCount(interp, resultScriptObjPtr);
8401
        }
8402
        interp->evalRetcodeLevel = savedLevel;
8403
    }
8404
    /* Handle the JIM_RETURN return code */
8405
    if (retcode == JIM_RETURN) {
8406
        retcode = interp->returnCode;
8407
        interp->returnCode = JIM_OK;
8408
    }
8409
    return retcode;
8410
}
8411
 
8412
int Jim_Eval(Jim_Interp *interp, const char *script)
8413
{
8414
    Jim_Obj *scriptObjPtr = Jim_NewStringObj(interp, script, -1);
8415
    int retval;
8416
 
8417
    Jim_IncrRefCount(scriptObjPtr);
8418
    retval = Jim_EvalObj(interp, scriptObjPtr);
8419
    Jim_DecrRefCount(interp, scriptObjPtr);
8420
    return retval;
8421
}
8422
 
8423
/* Execute script in the scope of the global level */
8424
int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
8425
{
8426
    Jim_CallFrame *savedFramePtr;
8427
    int retval;
8428
 
8429
    savedFramePtr = interp->framePtr;
8430
    interp->framePtr = interp->topFramePtr;
8431
    retval = Jim_Eval(interp, script);
8432
    interp->framePtr = savedFramePtr;
8433
    return retval;
8434
}
8435
 
8436
int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
8437
{
8438
    Jim_CallFrame *savedFramePtr;
8439
    int retval;
8440
 
8441
    savedFramePtr = interp->framePtr;
8442
    interp->framePtr = interp->topFramePtr;
8443
    retval = Jim_EvalObj(interp, scriptObjPtr);
8444
    interp->framePtr = savedFramePtr;
8445
    /* Try to report the error (if any) via the bgerror proc */
8446
    if (retval != JIM_OK) {
8447
        Jim_Obj *objv[2];
8448
 
8449
        objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
8450
        objv[1] = Jim_GetResult(interp);
8451
        Jim_IncrRefCount(objv[0]);
8452
        Jim_IncrRefCount(objv[1]);
8453
        if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
8454
            /* Report the error to stderr. */
8455
            fprintf(interp->stderr, "Background error:" JIM_NL);
8456
            Jim_PrintErrorMessage(interp);
8457
        }
8458
        Jim_DecrRefCount(interp, objv[0]);
8459
        Jim_DecrRefCount(interp, objv[1]);
8460
    }
8461
    return retval;
8462
}
8463
 
8464
int Jim_EvalFile(Jim_Interp *interp, const char *filename)
8465
{
8466
    char *prg = NULL;
8467
    FILE *fp;
8468
    int nread, totread, maxlen, buflen;
8469
    int retval;
8470
    Jim_Obj *scriptObjPtr;
8471
 
8472
    if ((fp = fopen(filename, "r")) == NULL) {
8473
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8474
        Jim_AppendStrings(interp, Jim_GetResult(interp),
8475
            "Error loading script \"", filename, "\": ",
8476
            strerror(errno), NULL);
8477
        return JIM_ERR;
8478
    }
8479
    buflen = 1024;
8480
    maxlen = totread = 0;
8481
    while (1) {
8482
        if (maxlen < totread+buflen+1) {
8483
            maxlen = totread+buflen+1;
8484
            prg = Jim_Realloc(prg, maxlen);
8485
        }
8486
        if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break;
8487
        totread += nread;
8488
    }
8489
    prg[totread] = '\0';
8490
    fclose(fp);
8491
 
8492
    scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
8493
    JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
8494
    Jim_IncrRefCount(scriptObjPtr);
8495
    retval = Jim_EvalObj(interp, scriptObjPtr);
8496
    Jim_DecrRefCount(interp, scriptObjPtr);
8497
    return retval;
8498
}
8499
 
8500
/* -----------------------------------------------------------------------------
8501
 * Subst
8502
 * ---------------------------------------------------------------------------*/
8503
static int JimParseSubstStr(struct JimParserCtx *pc)
8504
{
8505
    pc->tstart = pc->p;
8506
    pc->tline = pc->linenr;
8507
    while (*pc->p && *pc->p != '$' && *pc->p != '[') {
8508
        pc->p++; pc->len--;
8509
    }
8510
    pc->tend = pc->p-1;
8511
    pc->tt = JIM_TT_ESC;
8512
    return JIM_OK;
8513
}
8514
 
8515
static int JimParseSubst(struct JimParserCtx *pc, int flags)
8516
{
8517
    int retval;
8518
 
8519
    if (pc->len == 0) {
8520
        pc->tstart = pc->tend = pc->p;
8521
        pc->tline = pc->linenr;
8522
        pc->tt = JIM_TT_EOL;
8523
        pc->eof = 1;
8524
        return JIM_OK;
8525
    }
8526
    switch(*pc->p) {
8527
    case '[':
8528
        retval = JimParseCmd(pc);
8529
        if (flags & JIM_SUBST_NOCMD) {
8530
            pc->tstart--;
8531
            pc->tend++;
8532
            pc->tt = (flags & JIM_SUBST_NOESC) ?
8533
                JIM_TT_STR : JIM_TT_ESC;
8534
        }
8535
        return retval;
8536
        break;
8537
    case '$':
8538
        if (JimParseVar(pc) == JIM_ERR) {
8539
            pc->tstart = pc->tend = pc->p++; pc->len--;
8540
            pc->tline = pc->linenr;
8541
            pc->tt = JIM_TT_STR;
8542
        } else {
8543
            if (flags & JIM_SUBST_NOVAR) {
8544
                pc->tstart--;
8545
                if (flags & JIM_SUBST_NOESC)
8546
                    pc->tt = JIM_TT_STR;
8547
                else
8548
                    pc->tt = JIM_TT_ESC;
8549
                if (*pc->tstart == '{') {
8550
                    pc->tstart--;
8551
                    if (*(pc->tend+1))
8552
                        pc->tend++;
8553
                }
8554
            }
8555
        }
8556
        break;
8557
    default:
8558
        retval = JimParseSubstStr(pc);
8559
        if (flags & JIM_SUBST_NOESC)
8560
            pc->tt = JIM_TT_STR;
8561
        return retval;
8562
        break;
8563
    }
8564
    return JIM_OK;
8565
}
8566
 
8567
/* The subst object type reuses most of the data structures and functions
8568
 * of the script object. Script's data structures are a bit more complex
8569
 * for what is needed for [subst]itution tasks, but the reuse helps to
8570
 * deal with a single data structure at the cost of some more memory
8571
 * usage for substitutions. */
8572
static Jim_ObjType substObjType = {
8573
    "subst",
8574
    FreeScriptInternalRep,
8575
    DupScriptInternalRep,
8576
    NULL,
8577
    JIM_TYPE_REFERENCES,
8578
};
8579
 
8580
/* This method takes the string representation of an object
8581
 * as a Tcl string where to perform [subst]itution, and generates
8582
 * the pre-parsed internal representation. */
8583
int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
8584
{
8585
    int scriptTextLen;
8586
    const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8587
    struct JimParserCtx parser;
8588
    struct ScriptObj *script = Jim_Alloc(sizeof(*script));
8589
 
8590
    script->len = 0;
8591
    script->csLen = 0;
8592
    script->commands = 0;
8593
    script->token = NULL;
8594
    script->cmdStruct = NULL;
8595
    script->inUse = 1;
8596
    script->substFlags = flags;
8597
    script->fileName = NULL;
8598
 
8599
    JimParserInit(&parser, scriptText, scriptTextLen, 1);
8600
    while(1) {
8601
        char *token;
8602
        int len, type, linenr;
8603
 
8604
        JimParseSubst(&parser, flags);
8605
        if (JimParserEof(&parser)) break;
8606
        token = JimParserGetToken(&parser, &len, &type, &linenr);
8607
        ScriptObjAddToken(interp, script, token, len, type,
8608
                NULL, linenr);
8609
    }
8610
    /* Free the old internal rep and set the new one. */
8611
    Jim_FreeIntRep(interp, objPtr);
8612
    Jim_SetIntRepPtr(objPtr, script);
8613
    objPtr->typePtr = &scriptObjType;
8614
    return JIM_OK;
8615
}
8616
 
8617
ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
8618
{
8619
    struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
8620
 
8621
    if (objPtr->typePtr != &substObjType || script->substFlags != flags)
8622
        SetSubstFromAny(interp, objPtr, flags);
8623
    return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
8624
}
8625
 
8626
/* Performs commands,variables,blackslashes substitution,
8627
 * storing the result object (with refcount 0) into
8628
 * resObjPtrPtr. */
8629
int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
8630
        Jim_Obj **resObjPtrPtr, int flags)
8631
{
8632
    ScriptObj *script;
8633
    ScriptToken *token;
8634
    int i, len, retcode = JIM_OK;
8635
    Jim_Obj *resObjPtr, *savedResultObjPtr;
8636
 
8637
    script = Jim_GetSubst(interp, substObjPtr, flags);
8638
#ifdef JIM_OPTIMIZATION
8639
    /* Fast path for a very common case with array-alike syntax,
8640
     * that's: $foo($bar) */
8641
    if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
8642
        Jim_Obj *varObjPtr = script->token[0].objPtr;
8643
 
8644
        Jim_IncrRefCount(varObjPtr);
8645
        resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
8646
        if (resObjPtr == NULL) {
8647
            Jim_DecrRefCount(interp, varObjPtr);
8648
            return JIM_ERR;
8649
        }
8650
        Jim_DecrRefCount(interp, varObjPtr);
8651
        *resObjPtrPtr = resObjPtr;
8652
        return JIM_OK;
8653
    }
8654
#endif
8655
 
8656
    Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
8657
    /* In order to preserve the internal rep, we increment the
8658
     * inUse field of the script internal rep structure. */
8659
    script->inUse++;
8660
 
8661
    token = script->token;
8662
    len = script->len;
8663
 
8664
    /* Save the interp old result, to set it again before
8665
     * to return. */
8666
    savedResultObjPtr = interp->result;
8667
    Jim_IncrRefCount(savedResultObjPtr);
8668
 
8669
    /* Perform the substitution. Starts with an empty object
8670
     * and adds every token (performing the appropriate
8671
     * var/command/escape substitution). */
8672
    resObjPtr = Jim_NewStringObj(interp, "", 0);
8673
    for (i = 0; i < len; i++) {
8674
        Jim_Obj *objPtr;
8675
 
8676
        switch(token[i].type) {
8677
        case JIM_TT_STR:
8678
        case JIM_TT_ESC:
8679
            Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
8680
            break;
8681
        case JIM_TT_VAR:
8682
            objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
8683
            if (objPtr == NULL) goto err;
8684
            Jim_IncrRefCount(objPtr);
8685
            Jim_AppendObj(interp, resObjPtr, objPtr);
8686
            Jim_DecrRefCount(interp, objPtr);
8687
            break;
8688
        case JIM_TT_CMD:
8689
            if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
8690
                goto err;
8691
            Jim_AppendObj(interp, resObjPtr, interp->result);
8692
            break;
8693
        default:
8694
            Jim_Panic(interp,
8695
              "default token type (%d) reached "
8696
              "in Jim_SubstObj().", token[i].type);
8697
            break;
8698
        }
8699
    }
8700
ok:
8701
    if (retcode == JIM_OK)
8702
        Jim_SetResult(interp, savedResultObjPtr);
8703
    Jim_DecrRefCount(interp, savedResultObjPtr);
8704
    /* Note that we don't have to decrement inUse, because the
8705
     * following code transfers our use of the reference again to
8706
     * the script object. */
8707
    Jim_FreeIntRep(interp, substObjPtr);
8708
    substObjPtr->typePtr = &scriptObjType;
8709
    Jim_SetIntRepPtr(substObjPtr, script);
8710
    Jim_DecrRefCount(interp, substObjPtr);
8711
    *resObjPtrPtr = resObjPtr;
8712
    return retcode;
8713
err:
8714
    Jim_FreeNewObj(interp, resObjPtr);
8715
    retcode = JIM_ERR;
8716
    goto ok;
8717
}
8718
 
8719
/* -----------------------------------------------------------------------------
8720
 * API Input/Export functions
8721
 * ---------------------------------------------------------------------------*/
8722
 
8723
int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
8724
{
8725
    Jim_HashEntry *he;
8726
 
8727
    he = Jim_FindHashEntry(&interp->stub, funcname);
8728
    if (!he)
8729
        return JIM_ERR;
8730
    memcpy(targetPtrPtr, &he->val, sizeof(void*));
8731
    return JIM_OK;
8732
}
8733
 
8734
int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
8735
{
8736
    return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
8737
}
8738
 
8739
#define JIM_REGISTER_API(name) \
8740
    Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
8741
 
8742
void JimRegisterCoreApi(Jim_Interp *interp)
8743
{
8744
  interp->getApiFuncPtr = Jim_GetApi;
8745
  JIM_REGISTER_API(Alloc);
8746
  JIM_REGISTER_API(Free);
8747
  JIM_REGISTER_API(Eval);
8748
  JIM_REGISTER_API(EvalGlobal);
8749
  JIM_REGISTER_API(EvalFile);
8750
  JIM_REGISTER_API(EvalObj);
8751
  JIM_REGISTER_API(EvalObjBackground);
8752
  JIM_REGISTER_API(EvalObjVector);
8753
  JIM_REGISTER_API(InitHashTable);
8754
  JIM_REGISTER_API(ExpandHashTable);
8755
  JIM_REGISTER_API(AddHashEntry);
8756
  JIM_REGISTER_API(ReplaceHashEntry);
8757
  JIM_REGISTER_API(DeleteHashEntry);
8758
  JIM_REGISTER_API(FreeHashTable);
8759
  JIM_REGISTER_API(FindHashEntry);
8760
  JIM_REGISTER_API(ResizeHashTable);
8761
  JIM_REGISTER_API(GetHashTableIterator);
8762
  JIM_REGISTER_API(NextHashEntry);
8763
  JIM_REGISTER_API(NewObj);
8764
  JIM_REGISTER_API(FreeObj);
8765
  JIM_REGISTER_API(InvalidateStringRep);
8766
  JIM_REGISTER_API(InitStringRep);
8767
  JIM_REGISTER_API(DuplicateObj);
8768
  JIM_REGISTER_API(GetString);
8769
  JIM_REGISTER_API(Length);
8770
  JIM_REGISTER_API(InvalidateStringRep);
8771
  JIM_REGISTER_API(NewStringObj);
8772
  JIM_REGISTER_API(NewStringObjNoAlloc);
8773
  JIM_REGISTER_API(AppendString);
8774
  JIM_REGISTER_API(AppendObj);
8775
  JIM_REGISTER_API(AppendStrings);
8776
  JIM_REGISTER_API(StringEqObj);
8777
  JIM_REGISTER_API(StringMatchObj);
8778
  JIM_REGISTER_API(StringRangeObj);
8779
  JIM_REGISTER_API(FormatString);
8780
  JIM_REGISTER_API(CompareStringImmediate);
8781
  JIM_REGISTER_API(NewReference);
8782
  JIM_REGISTER_API(GetReference);
8783
  JIM_REGISTER_API(SetFinalizer);
8784
  JIM_REGISTER_API(GetFinalizer);
8785
  JIM_REGISTER_API(CreateInterp);
8786
  JIM_REGISTER_API(FreeInterp);
8787
  JIM_REGISTER_API(GetExitCode);
8788
  JIM_REGISTER_API(SetStdin);
8789
  JIM_REGISTER_API(SetStdout);
8790
  JIM_REGISTER_API(SetStderr);
8791
  JIM_REGISTER_API(CreateCommand);
8792
  JIM_REGISTER_API(CreateProcedure);
8793
  JIM_REGISTER_API(DeleteCommand);
8794
  JIM_REGISTER_API(RenameCommand);
8795
  JIM_REGISTER_API(GetCommand);
8796
  JIM_REGISTER_API(SetVariable);
8797
  JIM_REGISTER_API(SetVariableStr);
8798
  JIM_REGISTER_API(SetGlobalVariableStr);
8799
  JIM_REGISTER_API(SetVariableStrWithStr);
8800
  JIM_REGISTER_API(SetVariableLink);
8801
  JIM_REGISTER_API(GetVariable);
8802
  JIM_REGISTER_API(GetCallFrameByLevel);
8803
  JIM_REGISTER_API(Collect);
8804
  JIM_REGISTER_API(CollectIfNeeded);
8805
  JIM_REGISTER_API(GetIndex);
8806
  JIM_REGISTER_API(NewListObj);
8807
  JIM_REGISTER_API(ListAppendElement);
8808
  JIM_REGISTER_API(ListAppendList);
8809
  JIM_REGISTER_API(ListLength);
8810
  JIM_REGISTER_API(ListIndex);
8811
  JIM_REGISTER_API(SetListIndex);
8812
  JIM_REGISTER_API(ConcatObj);
8813
  JIM_REGISTER_API(NewDictObj);
8814
  JIM_REGISTER_API(DictKey);
8815
  JIM_REGISTER_API(DictKeysVector);
8816
  JIM_REGISTER_API(GetIndex);
8817
  JIM_REGISTER_API(GetReturnCode);
8818
  JIM_REGISTER_API(EvalExpression);
8819
  JIM_REGISTER_API(GetBoolFromExpr);
8820
  JIM_REGISTER_API(GetWide);
8821
  JIM_REGISTER_API(GetLong);
8822
  JIM_REGISTER_API(SetWide);
8823
  JIM_REGISTER_API(NewIntObj);
8824
  JIM_REGISTER_API(GetDouble);
8825
  JIM_REGISTER_API(SetDouble);
8826
  JIM_REGISTER_API(NewDoubleObj);
8827
  JIM_REGISTER_API(WrongNumArgs);
8828
  JIM_REGISTER_API(SetDictKeysVector);
8829
  JIM_REGISTER_API(SubstObj);
8830
  JIM_REGISTER_API(RegisterApi);
8831
  JIM_REGISTER_API(PrintErrorMessage);
8832
  JIM_REGISTER_API(InteractivePrompt);
8833
  JIM_REGISTER_API(RegisterCoreCommands);
8834
  JIM_REGISTER_API(GetSharedString);
8835
  JIM_REGISTER_API(ReleaseSharedString);
8836
  JIM_REGISTER_API(Panic);
8837
  JIM_REGISTER_API(StrDup);
8838
  JIM_REGISTER_API(UnsetVariable);
8839
  JIM_REGISTER_API(GetVariableStr);
8840
  JIM_REGISTER_API(GetGlobalVariable);
8841
  JIM_REGISTER_API(GetGlobalVariableStr);
8842
  JIM_REGISTER_API(GetAssocData);
8843
  JIM_REGISTER_API(SetAssocData);
8844
  JIM_REGISTER_API(DeleteAssocData);
8845
  JIM_REGISTER_API(GetEnum);
8846
  JIM_REGISTER_API(ScriptIsComplete);
8847
  JIM_REGISTER_API(PackageRequire);
8848
  JIM_REGISTER_API(PackageProvide);
8849
  JIM_REGISTER_API(InitStack);
8850
  JIM_REGISTER_API(FreeStack);
8851
  JIM_REGISTER_API(StackLen);
8852
  JIM_REGISTER_API(StackPush);
8853
  JIM_REGISTER_API(StackPop);
8854
  JIM_REGISTER_API(StackPeek);
8855
  JIM_REGISTER_API(FreeStackElements);
8856
}
8857
 
8858
/* -----------------------------------------------------------------------------
8859
 * Core commands utility functions
8860
 * ---------------------------------------------------------------------------*/
8861
void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
8862
        const char *msg)
8863
{
8864
    int i;
8865
    Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
8866
 
8867
    Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
8868
    for (i = 0; i < argc; i++) {
8869
        Jim_AppendObj(interp, objPtr, argv[i]);
8870
        if (!(i+1 == argc && msg[0] == '\0'))
8871
            Jim_AppendString(interp, objPtr, " ", 1);
8872
    }
8873
    Jim_AppendString(interp, objPtr, msg, -1);
8874
    Jim_AppendString(interp, objPtr, "\"", 1);
8875
    Jim_SetResult(interp, objPtr);
8876
}
8877
 
8878
static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
8879
{
8880
    Jim_HashTableIterator *htiter;
8881
    Jim_HashEntry *he;
8882
    Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
8883
    const char *pattern;
8884
    int patternLen;
8885
 
8886
    pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
8887
    htiter = Jim_GetHashTableIterator(&interp->commands);
8888
    while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8889
        if (pattern && !JimStringMatch(pattern, patternLen, he->key,
8890
                    strlen((const char*)he->key), 0))
8891
            continue;
8892
        Jim_ListAppendElement(interp, listObjPtr,
8893
                Jim_NewStringObj(interp, he->key, -1));
8894
    }
8895
    Jim_FreeHashTableIterator(htiter);
8896
    return listObjPtr;
8897
}
8898
 
8899
#define JIM_VARLIST_GLOBALS 0
8900
#define JIM_VARLIST_LOCALS 1
8901
#define JIM_VARLIST_VARS 2
8902
 
8903
static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
8904
        int mode)
8905
{
8906
    Jim_HashTableIterator *htiter;
8907
    Jim_HashEntry *he;
8908
    Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
8909
    const char *pattern;
8910
    int patternLen;
8911
 
8912
    pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
8913
    if (mode == JIM_VARLIST_GLOBALS) {
8914
        htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
8915
    } else {
8916
        /* For [info locals], if we are at top level an emtpy list
8917
         * is returned. I don't agree, but we aim at compatibility (SS) */
8918
        if (mode == JIM_VARLIST_LOCALS &&
8919
            interp->framePtr == interp->topFramePtr)
8920
            return listObjPtr;
8921
        htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
8922
    }
8923
    while ((he = Jim_NextHashEntry(htiter)) != NULL) {
8924
        Jim_Var *varPtr = (Jim_Var*) he->val;
8925
        if (mode == JIM_VARLIST_LOCALS) {
8926
            if (varPtr->linkFramePtr != NULL)
8927
                continue;
8928
        }
8929
        if (pattern && !JimStringMatch(pattern, patternLen, he->key,
8930
                    strlen((const char*)he->key), 0))
8931
            continue;
8932
        Jim_ListAppendElement(interp, listObjPtr,
8933
                Jim_NewStringObj(interp, he->key, -1));
8934
    }
8935
    Jim_FreeHashTableIterator(htiter);
8936
    return listObjPtr;
8937
}
8938
 
8939
static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
8940
        Jim_Obj **objPtrPtr)
8941
{
8942
    Jim_CallFrame *targetCallFrame;
8943
 
8944
    if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
8945
            != JIM_OK)
8946
        return JIM_ERR;
8947
    /* No proc call at toplevel callframe */
8948
    if (targetCallFrame == interp->topFramePtr) {
8949
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
8950
        Jim_AppendStrings(interp, Jim_GetResult(interp),
8951
                "bad level \"",
8952
                Jim_GetString(levelObjPtr, NULL), "\"", NULL);
8953
        return JIM_ERR;
8954
    }
8955
    *objPtrPtr = Jim_NewListObj(interp,
8956
            targetCallFrame->argv,
8957
            targetCallFrame->argc);
8958
    return JIM_OK;
8959
}
8960
 
8961
/* -----------------------------------------------------------------------------
8962
 * Core commands
8963
 * ---------------------------------------------------------------------------*/
8964
 
8965
/* fake [puts] -- not the real puts, just for debugging. */
8966
static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
8967
        Jim_Obj *const *argv)
8968
{
8969
    const char *str;
8970
    int len, nonewline = 0;
8971
 
8972
    if (argc != 2 && argc != 3) {
8973
        Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
8974
        return JIM_ERR;
8975
    }
8976
    if (argc == 3) {
8977
        if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
8978
        {
8979
            Jim_SetResultString(interp, "The second argument must "
8980
                    "be -nonewline", -1);
8981
            return JIM_OK;
8982
        } else {
8983
            nonewline = 1;
8984
            argv++;
8985
        }
8986
    }
8987
    str = Jim_GetString(argv[1], &len);
8988
    fwrite(str, 1, len, interp->stdout);
8989
    if (!nonewline) fprintf(interp->stdout, JIM_NL);
8990
    return JIM_OK;
8991
}
8992
 
8993
/* Helper for [+] and [*] */
8994
static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
8995
        Jim_Obj *const *argv, int op)
8996
{
8997
    jim_wide wideValue, res;
8998
    double doubleValue, doubleRes;
8999
    int i;
9000
 
9001
    res = (op == JIM_EXPROP_ADD) ? 0 : 1;
9002
 
9003
    for (i = 1; i < argc; i++) {
9004
        if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
9005
            goto trydouble;
9006
        if (op == JIM_EXPROP_ADD)
9007
            res += wideValue;
9008
        else
9009
            res *= wideValue;
9010
    }
9011
    Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9012
    return JIM_OK;
9013
trydouble:
9014
    doubleRes = (double) res;
9015
    for (;i < argc; i++) {
9016
        if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9017
            return JIM_ERR;
9018
        if (op == JIM_EXPROP_ADD)
9019
            doubleRes += doubleValue;
9020
        else
9021
            doubleRes *= doubleValue;
9022
    }
9023
    Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9024
    return JIM_OK;
9025
}
9026
 
9027
/* Helper for [-] and [/] */
9028
static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
9029
        Jim_Obj *const *argv, int op)
9030
{
9031
    jim_wide wideValue, res = 0;
9032
    double doubleValue, doubleRes = 0;
9033
    int i = 2;
9034
 
9035
    if (argc < 2) {
9036
        Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
9037
        return JIM_ERR;
9038
    } else if (argc == 2) {
9039
        /* The arity = 2 case is different. For [- x] returns -x,
9040
         * while [/ x] returns 1/x. */
9041
        if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
9042
            if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
9043
                    JIM_OK)
9044
            {
9045
                return JIM_ERR;
9046
            } else {
9047
                if (op == JIM_EXPROP_SUB)
9048
                    doubleRes = -doubleValue;
9049
                else
9050
                    doubleRes = 1.0/doubleValue;
9051
                Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9052
                            doubleRes));
9053
                return JIM_OK;
9054
            }
9055
        }
9056
        if (op == JIM_EXPROP_SUB) {
9057
            res = -wideValue;
9058
            Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9059
        } else {
9060
            doubleRes = 1.0/wideValue;
9061
            Jim_SetResult(interp, Jim_NewDoubleObj(interp,
9062
                        doubleRes));
9063
        }
9064
        return JIM_OK;
9065
    } else {
9066
        if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
9067
            if (Jim_GetDouble(interp, argv[1], &doubleRes)
9068
                    != JIM_OK) {
9069
                return JIM_ERR;
9070
            } else {
9071
                goto trydouble;
9072
            }
9073
        }
9074
    }
9075
    for (i = 2; i < argc; i++) {
9076
        if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
9077
            doubleRes = (double) res;
9078
            goto trydouble;
9079
        }
9080
        if (op == JIM_EXPROP_SUB)
9081
            res -= wideValue;
9082
        else
9083
            res /= wideValue;
9084
    }
9085
    Jim_SetResult(interp, Jim_NewIntObj(interp, res));
9086
    return JIM_OK;
9087
trydouble:
9088
    for (;i < argc; i++) {
9089
        if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
9090
            return JIM_ERR;
9091
        if (op == JIM_EXPROP_SUB)
9092
            doubleRes -= doubleValue;
9093
        else
9094
            doubleRes /= doubleValue;
9095
    }
9096
    Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
9097
    return JIM_OK;
9098
}
9099
 
9100
 
9101
/* [+] */
9102
static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
9103
        Jim_Obj *const *argv)
9104
{
9105
    return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
9106
}
9107
 
9108
/* [*] */
9109
static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
9110
        Jim_Obj *const *argv)
9111
{
9112
    return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
9113
}
9114
 
9115
/* [-] */
9116
static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
9117
        Jim_Obj *const *argv)
9118
{
9119
    return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
9120
}
9121
 
9122
/* [/] */
9123
static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
9124
        Jim_Obj *const *argv)
9125
{
9126
    return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
9127
}
9128
 
9129
/* [set] */
9130
static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
9131
        Jim_Obj *const *argv)
9132
{
9133
    if (argc != 2 && argc != 3) {
9134
        Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
9135
        return JIM_ERR;
9136
    }
9137
    if (argc == 2) {
9138
        Jim_Obj *objPtr;
9139
        objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9140
        if (!objPtr)
9141
            return JIM_ERR;
9142
        Jim_SetResult(interp, objPtr);
9143
        return JIM_OK;
9144
    }
9145
    /* argc == 3 case. */
9146
    if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
9147
        return JIM_ERR;
9148
    Jim_SetResult(interp, argv[2]);
9149
    return JIM_OK;
9150
}
9151
 
9152
/* [unset] */
9153
static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
9154
        Jim_Obj *const *argv)
9155
{
9156
    int i;
9157
 
9158
    if (argc < 2) {
9159
        Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
9160
        return JIM_ERR;
9161
    }
9162
    for (i = 1; i < argc; i++) {
9163
        if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
9164
            return JIM_ERR;
9165
    }
9166
    return JIM_OK;
9167
}
9168
 
9169
/* [incr] */
9170
static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
9171
        Jim_Obj *const *argv)
9172
{
9173
    jim_wide wideValue, increment = 1;
9174
    Jim_Obj *intObjPtr;
9175
 
9176
    if (argc != 2 && argc != 3) {
9177
        Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
9178
        return JIM_ERR;
9179
    }
9180
    if (argc == 3) {
9181
        if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
9182
            return JIM_ERR;
9183
    }
9184
    intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
9185
    if (!intObjPtr) return JIM_ERR;
9186
    if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
9187
        return JIM_ERR;
9188
    if (Jim_IsShared(intObjPtr)) {
9189
        intObjPtr = Jim_NewIntObj(interp, wideValue+increment);
9190
        if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9191
            Jim_FreeNewObj(interp, intObjPtr);
9192
            return JIM_ERR;
9193
        }
9194
    } else {
9195
        Jim_SetWide(interp, intObjPtr, wideValue+increment);
9196
        /* The following step is required in order to invalidate the
9197
         * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
9198
        if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
9199
            return JIM_ERR;
9200
        }
9201
    }
9202
    Jim_SetResult(interp, intObjPtr);
9203
    return JIM_OK;
9204
}
9205
 
9206
/* [while] */
9207
static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
9208
        Jim_Obj *const *argv)
9209
{
9210
    if (argc != 3) {
9211
        Jim_WrongNumArgs(interp, 1, argv, "condition body");
9212
        return JIM_ERR;
9213
    }
9214
    /* Try to run a specialized version of while if the expression
9215
     * is in one of the following forms:
9216
     *
9217
     *   $a < CONST, $a < $b
9218
     *   $a <= CONST, $a <= $b
9219
     *   $a > CONST, $a > $b
9220
     *   $a >= CONST, $a >= $b
9221
     *   $a != CONST, $a != $b
9222
     *   $a == CONST, $a == $b
9223
     *   $a
9224
     *   !$a
9225
     *   CONST
9226
     */
9227
 
9228
#ifdef JIM_OPTIMIZATION
9229
    {
9230
        ExprByteCode *expr;
9231
        Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
9232
        int exprLen, retval;
9233
 
9234
        /* STEP 1 -- Check if there are the conditions to run the specialized
9235
         * version of while */
9236
 
9237
        if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
9238
        if (expr->len <= 0 || expr->len > 3) goto noopt;
9239
        switch(expr->len) {
9240
        case 1:
9241
            if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
9242
                expr->opcode[0] != JIM_EXPROP_NUMBER)
9243
                goto noopt;
9244
            break;
9245
        case 2:
9246
            if (expr->opcode[1] != JIM_EXPROP_NOT ||
9247
                expr->opcode[0] != JIM_EXPROP_VARIABLE)
9248
                goto noopt;
9249
            break;
9250
        case 3:
9251
            if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9252
                (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9253
                 expr->opcode[1] != JIM_EXPROP_VARIABLE))
9254
                goto noopt;
9255
            switch(expr->opcode[2]) {
9256
            case JIM_EXPROP_LT:
9257
            case JIM_EXPROP_LTE:
9258
            case JIM_EXPROP_GT:
9259
            case JIM_EXPROP_GTE:
9260
            case JIM_EXPROP_NUMEQ:
9261
            case JIM_EXPROP_NUMNE:
9262
                /* nothing to do */
9263
                break;
9264
            default:
9265
                goto noopt;
9266
            }
9267
            break;
9268
        default:
9269
            Jim_Panic(interp,
9270
                "Unexpected default reached in Jim_WhileCoreCommand()");
9271
            break;
9272
        }
9273
 
9274
        /* STEP 2 -- conditions meet. Initialization. Take different
9275
         * branches for different expression lengths. */
9276
        exprLen = expr->len;
9277
 
9278
        if (exprLen == 1) {
9279
            jim_wide wideValue;
9280
 
9281
            if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
9282
                varAObjPtr = expr->obj[0];
9283
                Jim_IncrRefCount(varAObjPtr);
9284
            } else {
9285
                if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
9286
                    goto noopt;
9287
            }
9288
            while (1) {
9289
                if (varAObjPtr) {
9290
                    if (!(objPtr =
9291
                               Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9292
                        Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
9293
                    {
9294
                        Jim_DecrRefCount(interp, varAObjPtr);
9295
                        goto noopt;
9296
                    }
9297
                }
9298
                if (!wideValue) break;
9299
                if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9300
                    switch(retval) {
9301
                    case JIM_BREAK:
9302
                        if (varAObjPtr)
9303
                            Jim_DecrRefCount(interp, varAObjPtr);
9304
                        goto out;
9305
                        break;
9306
                    case JIM_CONTINUE:
9307
                        continue;
9308
                        break;
9309
                    default:
9310
                        if (varAObjPtr)
9311
                            Jim_DecrRefCount(interp, varAObjPtr);
9312
                        return retval;
9313
                    }
9314
                }
9315
            }
9316
            if (varAObjPtr)
9317
                Jim_DecrRefCount(interp, varAObjPtr);
9318
        } else if (exprLen == 3) {
9319
            jim_wide wideValueA, wideValueB, cmpRes = 0;
9320
            int cmpType = expr->opcode[2];
9321
 
9322
            varAObjPtr = expr->obj[0];
9323
            Jim_IncrRefCount(varAObjPtr);
9324
            if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9325
                varBObjPtr = expr->obj[1];
9326
                Jim_IncrRefCount(varBObjPtr);
9327
            } else {
9328
                if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
9329
                    goto noopt;
9330
            }
9331
            while (1) {
9332
                if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
9333
                    Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
9334
                {
9335
                    Jim_DecrRefCount(interp, varAObjPtr);
9336
                    if (varBObjPtr)
9337
                        Jim_DecrRefCount(interp, varBObjPtr);
9338
                    goto noopt;
9339
                }
9340
                if (varBObjPtr) {
9341
                    if (!(objPtr =
9342
                               Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
9343
                        Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
9344
                    {
9345
                        Jim_DecrRefCount(interp, varAObjPtr);
9346
                        if (varBObjPtr)
9347
                            Jim_DecrRefCount(interp, varBObjPtr);
9348
                        goto noopt;
9349
                    }
9350
                }
9351
                switch(cmpType) {
9352
                case JIM_EXPROP_LT:
9353
                    cmpRes = wideValueA < wideValueB; break;
9354
                case JIM_EXPROP_LTE:
9355
                    cmpRes = wideValueA <= wideValueB; break;
9356
                case JIM_EXPROP_GT:
9357
                    cmpRes = wideValueA > wideValueB; break;
9358
                case JIM_EXPROP_GTE:
9359
                    cmpRes = wideValueA >= wideValueB; break;
9360
                case JIM_EXPROP_NUMEQ:
9361
                    cmpRes = wideValueA == wideValueB; break;
9362
                case JIM_EXPROP_NUMNE:
9363
                    cmpRes = wideValueA != wideValueB; break;
9364
                }
9365
                if (!cmpRes) break;
9366
                if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9367
                    switch(retval) {
9368
                    case JIM_BREAK:
9369
                        Jim_DecrRefCount(interp, varAObjPtr);
9370
                        if (varBObjPtr)
9371
                            Jim_DecrRefCount(interp, varBObjPtr);
9372
                        goto out;
9373
                        break;
9374
                    case JIM_CONTINUE:
9375
                        continue;
9376
                        break;
9377
                    default:
9378
                        Jim_DecrRefCount(interp, varAObjPtr);
9379
                        if (varBObjPtr)
9380
                            Jim_DecrRefCount(interp, varBObjPtr);
9381
                        return retval;
9382
                    }
9383
                }
9384
            }
9385
            Jim_DecrRefCount(interp, varAObjPtr);
9386
            if (varBObjPtr)
9387
                Jim_DecrRefCount(interp, varBObjPtr);
9388
        } else {
9389
            /* TODO: case for len == 2 */
9390
            goto noopt;
9391
        }
9392
        Jim_SetEmptyResult(interp);
9393
        return JIM_OK;
9394
    }
9395
noopt:
9396
#endif
9397
 
9398
    /* The general purpose implementation of while starts here */
9399
    while (1) {
9400
        int boolean, retval;
9401
 
9402
        if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
9403
                        &boolean)) != JIM_OK)
9404
            return retval;
9405
        if (!boolean) break;
9406
        if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
9407
            switch(retval) {
9408
            case JIM_BREAK:
9409
                goto out;
9410
                break;
9411
            case JIM_CONTINUE:
9412
                continue;
9413
                break;
9414
            default:
9415
                return retval;
9416
            }
9417
        }
9418
    }
9419
out:
9420
    Jim_SetEmptyResult(interp);
9421
    return JIM_OK;
9422
}
9423
 
9424
/* [for] */
9425
static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
9426
        Jim_Obj *const *argv)
9427
{
9428
    int retval;
9429
 
9430
    if (argc != 5) {
9431
        Jim_WrongNumArgs(interp, 1, argv, "start test next body");
9432
        return JIM_ERR;
9433
    }
9434
    /* Check if the for is on the form:
9435
     *      for {set i CONST} {$i < CONST} {incr i}
9436
     *      for {set i CONST} {$i < $j} {incr i}
9437
     *      for {set i CONST} {$i <= CONST} {incr i}
9438
     *      for {set i CONST} {$i <= $j} {incr i}
9439
     * XXX: NOTE: if variable traces are implemented, this optimization
9440
     * need to be modified to check for the proc epoch at every variable
9441
     * update. */
9442
#ifdef JIM_OPTIMIZATION
9443
    {
9444
        ScriptObj *initScript, *incrScript;
9445
        ExprByteCode *expr;
9446
        jim_wide start, stop, currentVal;
9447
        unsigned jim_wide procEpoch = interp->procEpoch;
9448
        Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
9449
        int cmpType;
9450
        struct Jim_Cmd *cmdPtr;
9451
 
9452
        /* Do it only if there aren't shared arguments */
9453
        if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
9454
            goto evalstart;
9455
        initScript = Jim_GetScript(interp, argv[1]);
9456
        expr = Jim_GetExpression(interp, argv[2]);
9457
        incrScript = Jim_GetScript(interp, argv[3]);
9458
 
9459
        /* Ensure proper lengths to start */
9460
        if (initScript->len != 6) goto evalstart;
9461
        if (incrScript->len != 4) goto evalstart;
9462
        if (expr->len != 3) goto evalstart;
9463
        /* Ensure proper token types. */
9464
        if (initScript->token[2].type != JIM_TT_ESC ||
9465
            initScript->token[4].type != JIM_TT_ESC ||
9466
            incrScript->token[2].type != JIM_TT_ESC ||
9467
            expr->opcode[0] != JIM_EXPROP_VARIABLE ||
9468
            (expr->opcode[1] != JIM_EXPROP_NUMBER &&
9469
             expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
9470
            (expr->opcode[2] != JIM_EXPROP_LT &&
9471
             expr->opcode[2] != JIM_EXPROP_LTE))
9472
            goto evalstart;
9473
        cmpType = expr->opcode[2];
9474
        /* Initialization command must be [set] */
9475
        cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
9476
        if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
9477
            goto evalstart;
9478
        /* Update command must be incr */
9479
        cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
9480
        if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
9481
            goto evalstart;
9482
        /* set, incr, expression must be about the same variable */
9483
        if (!Jim_StringEqObj(initScript->token[2].objPtr,
9484
                            incrScript->token[2].objPtr, 0))
9485
            goto evalstart;
9486
        if (!Jim_StringEqObj(initScript->token[2].objPtr,
9487
                            expr->obj[0], 0))
9488
            goto evalstart;
9489
        /* Check that the initialization and comparison are valid integers */
9490
        if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
9491
            goto evalstart;
9492
        if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
9493
            Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
9494
        {
9495
            goto evalstart;
9496
        }
9497
 
9498
        /* Initialization */
9499
        varNamePtr = expr->obj[0];
9500
        if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
9501
            stopVarNamePtr = expr->obj[1];
9502
            Jim_IncrRefCount(stopVarNamePtr);
9503
        }
9504
        Jim_IncrRefCount(varNamePtr);
9505
 
9506
        /* --- OPTIMIZED FOR --- */
9507
        /* Start to loop */
9508
        objPtr = Jim_NewIntObj(interp, start);
9509
        if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
9510
            Jim_DecrRefCount(interp, varNamePtr);
9511
            if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9512
            Jim_FreeNewObj(interp, objPtr);
9513
            goto evalstart;
9514
        }
9515
        while (1) {
9516
            /* === Check condition === */
9517
            /* Common code: */
9518
            objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
9519
            if (objPtr == NULL ||
9520
                Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
9521
            {
9522
                Jim_DecrRefCount(interp, varNamePtr);
9523
                if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
9524
                goto testcond;
9525
            }
9526
            /* Immediate or Variable? get the 'stop' value if the latter. */
9527
            if (stopVarNamePtr) {
9528
                objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
9529
                if (objPtr == NULL ||
9530
                    Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
9531
                {
9532
                    Jim_DecrRefCount(interp, varNamePtr);
9533
                    Jim_DecrRefCount(interp, stopVarNamePtr);
9534
                    goto testcond;
9535
                }
9536
            }
9537
            if (cmpType == JIM_EXPROP_LT) {
9538
                if (currentVal >= stop) break;
9539
            } else {
9540
                if (currentVal > stop) break;
9541
            }
9542
            /* Eval body */
9543
            if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9544
                switch(retval) {
9545
                case JIM_BREAK:
9546
                    if (stopVarNamePtr)
9547
                        Jim_DecrRefCount(interp, stopVarNamePtr);
9548
                    Jim_DecrRefCount(interp, varNamePtr);
9549
                    goto out;
9550
                case JIM_CONTINUE:
9551
                    /* nothing to do */
9552
                    break;
9553
                default:
9554
                    if (stopVarNamePtr)
9555
                        Jim_DecrRefCount(interp, stopVarNamePtr);
9556
                    Jim_DecrRefCount(interp, varNamePtr);
9557
                    return retval;
9558
                }
9559
            }
9560
            /* If there was a change in procedures/command continue
9561
             * with the usual [for] command implementation */
9562
            if (procEpoch != interp->procEpoch) {
9563
                if (stopVarNamePtr)
9564
                    Jim_DecrRefCount(interp, stopVarNamePtr);
9565
                Jim_DecrRefCount(interp, varNamePtr);
9566
                goto evalnext;
9567
            }
9568
            /* Increment */
9569
            objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
9570
            if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
9571
                objPtr->internalRep.wideValue ++;
9572
                Jim_InvalidateStringRep(objPtr);
9573
            } else {
9574
                Jim_Obj *auxObjPtr;
9575
 
9576
                if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
9577
                    if (stopVarNamePtr)
9578
                        Jim_DecrRefCount(interp, stopVarNamePtr);
9579
                    Jim_DecrRefCount(interp, varNamePtr);
9580
                    goto evalnext;
9581
                }
9582
                auxObjPtr = Jim_NewIntObj(interp, currentVal+1);
9583
                if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
9584
                    if (stopVarNamePtr)
9585
                        Jim_DecrRefCount(interp, stopVarNamePtr);
9586
                    Jim_DecrRefCount(interp, varNamePtr);
9587
                    Jim_FreeNewObj(interp, auxObjPtr);
9588
                    goto evalnext;
9589
                }
9590
            }
9591
        }
9592
        if (stopVarNamePtr)
9593
            Jim_DecrRefCount(interp, stopVarNamePtr);
9594
        Jim_DecrRefCount(interp, varNamePtr);
9595
        Jim_SetEmptyResult(interp);
9596
        return JIM_OK;
9597
    }
9598
#endif
9599
evalstart:
9600
    /* Eval start */
9601
    if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
9602
        return retval;
9603
    while (1) {
9604
        int boolean;
9605
testcond:
9606
        /* Test the condition */
9607
        if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
9608
                != JIM_OK)
9609
            return retval;
9610
        if (!boolean) break;
9611
        /* Eval body */
9612
        if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
9613
            switch(retval) {
9614
            case JIM_BREAK:
9615
                goto out;
9616
                break;
9617
            case JIM_CONTINUE:
9618
                /* Nothing to do */
9619
                break;
9620
            default:
9621
                return retval;
9622
            }
9623
        }
9624
evalnext:
9625
        /* Eval next */
9626
        if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
9627
            switch(retval) {
9628
            case JIM_BREAK:
9629
                goto out;
9630
                break;
9631
            case JIM_CONTINUE:
9632
                continue;
9633
                break;
9634
            default:
9635
                return retval;
9636
            }
9637
        }
9638
    }
9639
out:
9640
    Jim_SetEmptyResult(interp);
9641
    return JIM_OK;
9642
}
9643
 
9644
/* foreach + lmap implementation. */
9645
static int JimForeachMapHelper(Jim_Interp *interp, int argc,
9646
        Jim_Obj *const *argv, int doMap)
9647
{
9648
    int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
9649
    int nbrOfLoops = 0;
9650
    Jim_Obj *emptyStr, *script, *mapRes = NULL;
9651
 
9652
    if (argc < 4 || argc % 2 != 0) {
9653
        Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
9654
        return JIM_ERR;
9655
    }
9656
    if (doMap) {
9657
        mapRes = Jim_NewListObj(interp, NULL, 0);
9658
        Jim_IncrRefCount(mapRes);
9659
    }
9660
    emptyStr = Jim_NewEmptyStringObj(interp);
9661
    Jim_IncrRefCount(emptyStr);
9662
    script = argv[argc-1];            /* Last argument is a script */
9663
    nbrOfLists = (argc - 1 - 1) / 2;  /* argc - 'foreach' - script */
9664
    listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
9665
    listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
9666
    /* Initialize iterators and remember max nbr elements each list */
9667
    memset(listsIdx, 0, nbrOfLists * sizeof(int));
9668
    /* Remember lengths of all lists and calculate how much rounds to loop */
9669
    for (i=0; i < nbrOfLists*2; i += 2) {
9670
        div_t cnt;
9671
        int count;
9672
        Jim_ListLength(interp, argv[i+1], &listsEnd[i]);
9673
        Jim_ListLength(interp, argv[i+2], &listsEnd[i+1]);
9674
        if (listsEnd[i] == 0) {
9675
            Jim_SetResultString(interp, "foreach varlist is empty", -1);
9676
            goto err;
9677
        }
9678
        cnt = div(listsEnd[i+1], listsEnd[i]);
9679
        count = cnt.quot + (cnt.rem ? 1 : 0);
9680
        if (count > nbrOfLoops)
9681
            nbrOfLoops = count;
9682
    }
9683
    for (; nbrOfLoops-- > 0; ) {
9684
        for (i=0; i < nbrOfLists; ++i) {
9685
            int varIdx = 0, var = i * 2;
9686
            while (varIdx < listsEnd[var]) {
9687
                Jim_Obj *varName, *ele;
9688
                int lst = i * 2 + 1;
9689
                if (Jim_ListIndex(interp, argv[var+1], varIdx, &varName, JIM_ERRMSG)
9690
                        != JIM_OK)
9691
                        goto err;
9692
                if (listsIdx[i] < listsEnd[lst]) {
9693
                    if (Jim_ListIndex(interp, argv[lst+1], listsIdx[i], &ele, JIM_ERRMSG)
9694
                        != JIM_OK)
9695
                        goto err;
9696
                    if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
9697
                        Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9698
                        goto err;
9699
                    }
9700
                    ++listsIdx[i];  /* Remember next iterator of current list */
9701
                } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
9702
                    Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
9703
                    goto err;
9704
                }
9705
                ++varIdx;  /* Next variable */
9706
            }
9707
        }
9708
        switch (result = Jim_EvalObj(interp, script)) {
9709
            case JIM_OK:
9710
                if (doMap)
9711
                    Jim_ListAppendElement(interp, mapRes, interp->result);
9712
                break;
9713
            case JIM_CONTINUE:
9714
                break;
9715
            case JIM_BREAK:
9716
                goto out;
9717
                break;
9718
            default:
9719
                goto err;
9720
        }
9721
    }
9722
out:
9723
    result = JIM_OK;
9724
    if (doMap)
9725
        Jim_SetResult(interp, mapRes);
9726
    else
9727
        Jim_SetEmptyResult(interp);
9728
err:
9729
    if (doMap)
9730
        Jim_DecrRefCount(interp, mapRes);
9731
    Jim_DecrRefCount(interp, emptyStr);
9732
    Jim_Free(listsIdx);
9733
    Jim_Free(listsEnd);
9734
    return result;
9735
}
9736
 
9737
/* [foreach] */
9738
static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
9739
        Jim_Obj *const *argv)
9740
{
9741
    return JimForeachMapHelper(interp, argc, argv, 0);
9742
}
9743
 
9744
/* [lmap] */
9745
static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
9746
        Jim_Obj *const *argv)
9747
{
9748
    return JimForeachMapHelper(interp, argc, argv, 1);
9749
}
9750
 
9751
/* [if] */
9752
static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
9753
        Jim_Obj *const *argv)
9754
{
9755
    int boolean, retval, current = 1, falsebody = 0;
9756
    if (argc >= 3) {
9757
        while (1) {
9758
            /* Far not enough arguments given! */
9759
            if (current >= argc) goto err;
9760
            if ((retval = Jim_GetBoolFromExpr(interp,
9761
                        argv[current++], &boolean))
9762
                    != JIM_OK)
9763
                return retval;
9764
            /* There lacks something, isn't it? */
9765
            if (current >= argc) goto err;
9766
            if (Jim_CompareStringImmediate(interp, argv[current],
9767
                        "then")) current++;
9768
            /* Tsk tsk, no then-clause? */
9769
            if (current >= argc) goto err;
9770
            if (boolean)
9771
                return Jim_EvalObj(interp, argv[current]);
9772
             /* Ok: no else-clause follows */
9773
            if (++current >= argc) return JIM_OK;
9774
            falsebody = current++;
9775
            if (Jim_CompareStringImmediate(interp, argv[falsebody],
9776
                        "else")) {
9777
                /* IIICKS - else-clause isn't last cmd? */
9778
                if (current != argc-1) goto err;
9779
                return Jim_EvalObj(interp, argv[current]);
9780
            } else if (Jim_CompareStringImmediate(interp,
9781
                        argv[falsebody], "elseif"))
9782
                /* Ok: elseif follows meaning all the stuff
9783
                 * again (how boring...) */
9784
                continue;
9785
            /* OOPS - else-clause is not last cmd?*/
9786
            else if (falsebody != argc-1)
9787
                goto err;
9788
            return Jim_EvalObj(interp, argv[falsebody]);
9789
        }
9790
        return JIM_OK;
9791
    }
9792
err:
9793
    Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
9794
    return JIM_ERR;
9795
}
9796
 
9797
enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
9798
 
9799
/* [switch] */
9800
static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
9801
        Jim_Obj *const *argv)
9802
{
9803
    int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt=1, patCount, i;
9804
    Jim_Obj *command = 0, *const *caseList = 0, *strObj;
9805
    Jim_Obj *script = 0;
9806
    if (argc < 3) goto wrongnumargs;
9807
    for (opt=1; opt < argc; ++opt) {
9808
        const char *option = Jim_GetString(argv[opt], 0);
9809
        if (*option != '-') break;
9810
        else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
9811
        else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
9812
        else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
9813
        else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
9814
        else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
9815
            if ((argc - opt) < 2) goto wrongnumargs;
9816
            command = argv[++opt];
9817
        } else {
9818
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9819
            Jim_AppendStrings(interp, Jim_GetResult(interp),
9820
                "bad option \"", option, "\": must be -exact, -glob, "
9821
                "-regexp, -command procname or --", 0);
9822
            goto err;
9823
        }
9824
        if ((argc - opt) < 2) goto wrongnumargs;
9825
    }
9826
    strObj = argv[opt++];
9827
    patCount = argc - opt;
9828
    if (patCount == 1) {
9829
        Jim_Obj **vector;
9830
        JimListGetElements(interp, argv[opt], &patCount, &vector);
9831
        caseList = vector;
9832
    } else
9833
        caseList = &argv[opt];
9834
    if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
9835
    for (i=0; script == 0 && i < patCount; i += 2) {
9836
        Jim_Obj *patObj = caseList[i];
9837
        if (!Jim_CompareStringImmediate(interp, patObj, "default")
9838
            || i < (patCount-2)) {
9839
            switch (matchOpt) {
9840
                case SWITCH_EXACT:
9841
                    if (Jim_StringEqObj(strObj, patObj, 0))
9842
                        script = caseList[i+1];
9843
                    break;
9844
                case SWITCH_GLOB:
9845
                    if (Jim_StringMatchObj(patObj, strObj, 0))
9846
                        script = caseList[i+1];
9847
                    break;
9848
                case SWITCH_RE:
9849
                    command = Jim_NewStringObj(interp, "regexp", -1);
9850
                    /* Fall thru intentionally */
9851
                case SWITCH_CMD: {
9852
                    Jim_Obj *parms[] = {command, patObj, strObj};
9853
                    int rc = Jim_EvalObjVector(interp, 3, parms);
9854
                    long matching;
9855
                    /* After the execution of a command we need to
9856
                     * make sure to reconvert the object into a list
9857
                     * again. Only for the single-list style [switch]. */
9858
                    if (argc-opt == 1) {
9859
                        Jim_Obj **vector;
9860
                        JimListGetElements(interp, argv[opt], &patCount,
9861
                                &vector);
9862
                        caseList = vector;
9863
                    }
9864
                    /* command is here already decref'd */
9865
                    if (rc != JIM_OK) {
9866
                        retcode = rc;
9867
                        goto err;
9868
                    }
9869
                    rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
9870
                    if (rc != JIM_OK) {
9871
                        retcode = rc;
9872
                        goto err;
9873
                    }
9874
                    if (matching)
9875
                        script = caseList[i+1];
9876
                    break;
9877
                }
9878
                default:
9879
                    Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9880
                    Jim_AppendStrings(interp, Jim_GetResult(interp),
9881
                        "internal error: no such option implemented", 0);
9882
                    goto err;
9883
            }
9884
        } else {
9885
          script = caseList[i+1];
9886
        }
9887
    }
9888
    for(; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
9889
        i += 2)
9890
        script = caseList[i+1];
9891
    if (script && Jim_CompareStringImmediate(interp, script, "-")) {
9892
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
9893
        Jim_AppendStrings(interp, Jim_GetResult(interp),
9894
            "no body specified for pattern \"",
9895
            Jim_GetString(caseList[i-2], 0), "\"", 0);
9896
        goto err;
9897
    }
9898
    retcode = JIM_OK;
9899
    Jim_SetEmptyResult(interp);
9900
    if (script != 0)
9901
        retcode = Jim_EvalObj(interp, script);
9902
    return retcode;
9903
wrongnumargs:
9904
    Jim_WrongNumArgs(interp, 1, argv, "?options? string "
9905
        "pattern body ... ?default body?   or   "
9906
        "{pattern body ?pattern body ...?}");
9907
err:
9908
    return retcode;
9909
}
9910
 
9911
/* [list] */
9912
static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
9913
        Jim_Obj *const *argv)
9914
{
9915
    Jim_Obj *listObjPtr;
9916
 
9917
    listObjPtr = Jim_NewListObj(interp, argv+1, argc-1);
9918
    Jim_SetResult(interp, listObjPtr);
9919
    return JIM_OK;
9920
}
9921
 
9922
/* [lindex] */
9923
static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
9924
        Jim_Obj *const *argv)
9925
{
9926
    Jim_Obj *objPtr, *listObjPtr;
9927
    int i;
9928
    int index;
9929
 
9930
    if (argc < 3) {
9931
        Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
9932
        return JIM_ERR;
9933
    }
9934
    objPtr = argv[1];
9935
    Jim_IncrRefCount(objPtr);
9936
    for (i = 2; i < argc; i++) {
9937
        listObjPtr = objPtr;
9938
        if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
9939
            Jim_DecrRefCount(interp, listObjPtr);
9940
            return JIM_ERR;
9941
        }
9942
        if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
9943
                    JIM_NONE) != JIM_OK) {
9944
            /* Returns an empty object if the index
9945
             * is out of range. */
9946
            Jim_DecrRefCount(interp, listObjPtr);
9947
            Jim_SetEmptyResult(interp);
9948
            return JIM_OK;
9949
        }
9950
        Jim_IncrRefCount(objPtr);
9951
        Jim_DecrRefCount(interp, listObjPtr);
9952
    }
9953
    Jim_SetResult(interp, objPtr);
9954
    Jim_DecrRefCount(interp, objPtr);
9955
    return JIM_OK;
9956
}
9957
 
9958
/* [llength] */
9959
static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
9960
        Jim_Obj *const *argv)
9961
{
9962
    int len;
9963
 
9964
    if (argc != 2) {
9965
        Jim_WrongNumArgs(interp, 1, argv, "list");
9966
        return JIM_ERR;
9967
    }
9968
    Jim_ListLength(interp, argv[1], &len);
9969
    Jim_SetResult(interp, Jim_NewIntObj(interp, len));
9970
    return JIM_OK;
9971
}
9972
 
9973
/* [lappend] */
9974
static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
9975
        Jim_Obj *const *argv)
9976
{
9977
    Jim_Obj *listObjPtr;
9978
    int shared, i;
9979
 
9980
    if (argc < 2) {
9981
        Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
9982
        return JIM_ERR;
9983
    }
9984
    listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
9985
    if (!listObjPtr) {
9986
        /* Create the list if it does not exists */
9987
        listObjPtr = Jim_NewListObj(interp, NULL, 0);
9988
        if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
9989
            Jim_FreeNewObj(interp, listObjPtr);
9990
            return JIM_ERR;
9991
        }
9992
    }
9993
    shared = Jim_IsShared(listObjPtr);
9994
    if (shared)
9995
        listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
9996
    for (i = 2; i < argc; i++)
9997
        Jim_ListAppendElement(interp, listObjPtr, argv[i]);
9998
    if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
9999
        if (shared)
10000
            Jim_FreeNewObj(interp, listObjPtr);
10001
        return JIM_ERR;
10002
    }
10003
    Jim_SetResult(interp, listObjPtr);
10004
    return JIM_OK;
10005
}
10006
 
10007
/* [linsert] */
10008
static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
10009
        Jim_Obj *const *argv)
10010
{
10011
    int index, len;
10012
    Jim_Obj *listPtr;
10013
 
10014
    if (argc < 4) {
10015
        Jim_WrongNumArgs(interp, 1, argv, "list index element "
10016
            "?element ...?");
10017
        return JIM_ERR;
10018
    }
10019
    listPtr = argv[1];
10020
    if (Jim_IsShared(listPtr))
10021
        listPtr = Jim_DuplicateObj(interp, listPtr);
10022
    if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
10023
        goto err;
10024
    Jim_ListLength(interp, listPtr, &len);
10025
    if (index >= len)
10026
        index = len;
10027
    else if (index < 0)
10028
        index = len + index + 1;
10029
    Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
10030
    Jim_SetResult(interp, listPtr);
10031
    return JIM_OK;
10032
err:
10033
    if (listPtr != argv[1]) {
10034
        Jim_FreeNewObj(interp, listPtr);
10035
    }
10036
    return JIM_ERR;
10037
}
10038
 
10039
/* [lset] */
10040
static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
10041
        Jim_Obj *const *argv)
10042
{
10043
    if (argc < 3) {
10044
        Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
10045
        return JIM_ERR;
10046
    } else if (argc == 3) {
10047
        if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
10048
            return JIM_ERR;
10049
        Jim_SetResult(interp, argv[2]);
10050
        return JIM_OK;
10051
    }
10052
    if (Jim_SetListIndex(interp, argv[1], argv+2, argc-3, argv[argc-1])
10053
            == JIM_ERR) return JIM_ERR;
10054
    return JIM_OK;
10055
}
10056
 
10057
/* [lsort] */
10058
static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
10059
{
10060
    const char *options[] = {
10061
        "-ascii", "-nocase", "-increasing", "-decreasing", NULL
10062
    };
10063
    enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
10064
    Jim_Obj *resObj;
10065
    int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
10066
    int decreasing = 0;
10067
 
10068
    if (argc < 2) {
10069
        Jim_WrongNumArgs(interp, 1, argv, "?options? list");
10070
        return JIM_ERR;
10071
    }
10072
    for (i = 1; i < (argc-1); i++) {
10073
        int option;
10074
 
10075
        if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
10076
                != JIM_OK)
10077
            return JIM_ERR;
10078
        switch(option) {
10079
        case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
10080
        case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
10081
        case OPT_INCREASING: decreasing = 0; break;
10082
        case OPT_DECREASING: decreasing = 1; break;
10083
        }
10084
    }
10085
    if (decreasing) {
10086
        switch(lsortType) {
10087
        case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
10088
        case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
10089
        }
10090
    }
10091
    resObj = Jim_DuplicateObj(interp, argv[argc-1]);
10092
    ListSortElements(interp, resObj, lsortType);
10093
    Jim_SetResult(interp, resObj);
10094
    return JIM_OK;
10095
}
10096
 
10097
/* [append] */
10098
static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
10099
        Jim_Obj *const *argv)
10100
{
10101
    Jim_Obj *stringObjPtr;
10102
    int shared, i;
10103
 
10104
    if (argc < 2) {
10105
        Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
10106
        return JIM_ERR;
10107
    }
10108
    if (argc == 2) {
10109
        stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
10110
        if (!stringObjPtr) return JIM_ERR;
10111
    } else {
10112
        stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
10113
        if (!stringObjPtr) {
10114
            /* Create the string if it does not exists */
10115
            stringObjPtr = Jim_NewEmptyStringObj(interp);
10116
            if (Jim_SetVariable(interp, argv[1], stringObjPtr)
10117
                    != JIM_OK) {
10118
                Jim_FreeNewObj(interp, stringObjPtr);
10119
                return JIM_ERR;
10120
            }
10121
        }
10122
    }
10123
    shared = Jim_IsShared(stringObjPtr);
10124
    if (shared)
10125
        stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
10126
    for (i = 2; i < argc; i++)
10127
        Jim_AppendObj(interp, stringObjPtr, argv[i]);
10128
    if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
10129
        if (shared)
10130
            Jim_FreeNewObj(interp, stringObjPtr);
10131
        return JIM_ERR;
10132
    }
10133
    Jim_SetResult(interp, stringObjPtr);
10134
    return JIM_OK;
10135
}
10136
 
10137
/* [debug] */
10138
static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
10139
        Jim_Obj *const *argv)
10140
{
10141
    const char *options[] = {
10142
        "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
10143
        "exprbc",
10144
        NULL
10145
    };
10146
    enum {
10147
        OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
10148
        OPT_EXPRLEN, OPT_EXPRBC
10149
    };
10150
    int option;
10151
 
10152
    if (argc < 2) {
10153
        Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
10154
        return JIM_ERR;
10155
    }
10156
    if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10157
                JIM_ERRMSG) != JIM_OK)
10158
        return JIM_ERR;
10159
    if (option == OPT_REFCOUNT) {
10160
        if (argc != 3) {
10161
            Jim_WrongNumArgs(interp, 2, argv, "object");
10162
            return JIM_ERR;
10163
        }
10164
        Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
10165
        return JIM_OK;
10166
    } else if (option == OPT_OBJCOUNT) {
10167
        int freeobj = 0, liveobj = 0;
10168
        char buf[256];
10169
        Jim_Obj *objPtr;
10170
 
10171
        if (argc != 2) {
10172
            Jim_WrongNumArgs(interp, 2, argv, "");
10173
            return JIM_ERR;
10174
        }
10175
        /* Count the number of free objects. */
10176
        objPtr = interp->freeList;
10177
        while (objPtr) {
10178
            freeobj++;
10179
            objPtr = objPtr->nextObjPtr;
10180
        }
10181
        /* Count the number of live objects. */
10182
        objPtr = interp->liveList;
10183
        while (objPtr) {
10184
            liveobj++;
10185
            objPtr = objPtr->nextObjPtr;
10186
        }
10187
        /* Set the result string and return. */
10188
        sprintf(buf, "free %d used %d", freeobj, liveobj);
10189
        Jim_SetResultString(interp, buf, -1);
10190
        return JIM_OK;
10191
    } else if (option == OPT_OBJECTS) {
10192
        Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
10193
        /* Count the number of live objects. */
10194
        objPtr = interp->liveList;
10195
        listObjPtr = Jim_NewListObj(interp, NULL, 0);
10196
        while (objPtr) {
10197
            char buf[128];
10198
            const char *type = objPtr->typePtr ?
10199
                objPtr->typePtr->name : "";
10200
            subListObjPtr = Jim_NewListObj(interp, NULL, 0);
10201
            sprintf(buf, "%p", objPtr);
10202
            Jim_ListAppendElement(interp, subListObjPtr,
10203
                Jim_NewStringObj(interp, buf, -1));
10204
            Jim_ListAppendElement(interp, subListObjPtr,
10205
                Jim_NewStringObj(interp, type, -1));
10206
            Jim_ListAppendElement(interp, subListObjPtr,
10207
                Jim_NewIntObj(interp, objPtr->refCount));
10208
            Jim_ListAppendElement(interp, subListObjPtr, objPtr);
10209
            Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
10210
            objPtr = objPtr->nextObjPtr;
10211
        }
10212
        Jim_SetResult(interp, listObjPtr);
10213
        return JIM_OK;
10214
    } else if (option == OPT_INVSTR) {
10215
        Jim_Obj *objPtr;
10216
 
10217
        if (argc != 3) {
10218
            Jim_WrongNumArgs(interp, 2, argv, "object");
10219
            return JIM_ERR;
10220
        }
10221
        objPtr = argv[2];
10222
        if (objPtr->typePtr != NULL)
10223
            Jim_InvalidateStringRep(objPtr);
10224
        Jim_SetEmptyResult(interp);
10225
        return JIM_OK;
10226
    } else if (option == OPT_SCRIPTLEN) {
10227
        ScriptObj *script;
10228
        if (argc != 3) {
10229
            Jim_WrongNumArgs(interp, 2, argv, "script");
10230
            return JIM_ERR;
10231
        }
10232
        script = Jim_GetScript(interp, argv[2]);
10233
        Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
10234
        return JIM_OK;
10235
    } else if (option == OPT_EXPRLEN) {
10236
        ExprByteCode *expr;
10237
        if (argc != 3) {
10238
            Jim_WrongNumArgs(interp, 2, argv, "expression");
10239
            return JIM_ERR;
10240
        }
10241
        expr = Jim_GetExpression(interp, argv[2]);
10242
        if (expr == NULL)
10243
            return JIM_ERR;
10244
        Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
10245
        return JIM_OK;
10246
    } else if (option == OPT_EXPRBC) {
10247
        Jim_Obj *objPtr;
10248
        ExprByteCode *expr;
10249
        int i;
10250
 
10251
        if (argc != 3) {
10252
            Jim_WrongNumArgs(interp, 2, argv, "expression");
10253
            return JIM_ERR;
10254
        }
10255
        expr = Jim_GetExpression(interp, argv[2]);
10256
        if (expr == NULL)
10257
            return JIM_ERR;
10258
        objPtr = Jim_NewListObj(interp, NULL, 0);
10259
        for (i = 0; i < expr->len; i++) {
10260
            const char *type;
10261
            Jim_ExprOperator *op;
10262
 
10263
            switch(expr->opcode[i]) {
10264
            case JIM_EXPROP_NUMBER: type = "number"; break;
10265
            case JIM_EXPROP_COMMAND: type = "command"; break;
10266
            case JIM_EXPROP_VARIABLE: type = "variable"; break;
10267
            case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
10268
            case JIM_EXPROP_SUBST: type = "subst"; break;
10269
            case JIM_EXPROP_STRING: type = "string"; break;
10270
            default:
10271
                op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
10272
                if (op == NULL) {
10273
                    type = "private";
10274
                } else {
10275
                    type = "operator";
10276
                }
10277
                break;
10278
            }
10279
            Jim_ListAppendElement(interp, objPtr,
10280
                    Jim_NewStringObj(interp, type, -1));
10281
            Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
10282
        }
10283
        Jim_SetResult(interp, objPtr);
10284
        return JIM_OK;
10285
    } else {
10286
        Jim_SetResultString(interp,
10287
            "bad option. Valid options are refcount, "
10288
            "objcount, objects, invstr", -1);
10289
        return JIM_ERR;
10290
    }
10291
    return JIM_OK; /* unreached */
10292
}
10293
 
10294
/* [eval] */
10295
static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
10296
        Jim_Obj *const *argv)
10297
{
10298
    if (argc == 2) {
10299
        return Jim_EvalObj(interp, argv[1]);
10300
    } else if (argc > 2) {
10301
        Jim_Obj *objPtr;
10302
        int retcode;
10303
 
10304
        objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10305
        Jim_IncrRefCount(objPtr);
10306
        retcode = Jim_EvalObj(interp, objPtr);
10307
        Jim_DecrRefCount(interp, objPtr);
10308
        return retcode;
10309
    } else {
10310
        Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
10311
        return JIM_ERR;
10312
    }
10313
}
10314
 
10315
/* [uplevel] */
10316
static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
10317
        Jim_Obj *const *argv)
10318
{
10319
    if (argc >= 2) {
10320
        int retcode, newLevel, oldLevel;
10321
        Jim_CallFrame *savedCallFrame, *targetCallFrame;
10322
        Jim_Obj *objPtr;
10323
        const char *str;
10324
 
10325
        /* Save the old callframe pointer */
10326
        savedCallFrame = interp->framePtr;
10327
 
10328
        /* Lookup the target frame pointer */
10329
        str = Jim_GetString(argv[1], NULL);
10330
        if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
10331
        {
10332
            if (Jim_GetCallFrameByLevel(interp, argv[1],
10333
                        &targetCallFrame,
10334
                        &newLevel) != JIM_OK)
10335
                return JIM_ERR;
10336
            argc--;
10337
            argv++;
10338
        } else {
10339
            if (Jim_GetCallFrameByLevel(interp, NULL,
10340
                        &targetCallFrame,
10341
                        &newLevel) != JIM_OK)
10342
                return JIM_ERR;
10343
        }
10344
        if (argc < 2) {
10345
            argc++;
10346
            argv--;
10347
            Jim_WrongNumArgs(interp, 1, argv,
10348
                    "?level? command ?arg ...?");
10349
            return JIM_ERR;
10350
        }
10351
        /* Eval the code in the target callframe. */
10352
        interp->framePtr = targetCallFrame;
10353
        oldLevel = interp->numLevels;
10354
        interp->numLevels = newLevel;
10355
        if (argc == 2) {
10356
            retcode = Jim_EvalObj(interp, argv[1]);
10357
        } else {
10358
            objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10359
            Jim_IncrRefCount(objPtr);
10360
            retcode = Jim_EvalObj(interp, objPtr);
10361
            Jim_DecrRefCount(interp, objPtr);
10362
        }
10363
        interp->numLevels = oldLevel;
10364
        interp->framePtr = savedCallFrame;
10365
        return retcode;
10366
    } else {
10367
        Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
10368
        return JIM_ERR;
10369
    }
10370
}
10371
 
10372
/* [expr] */
10373
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
10374
        Jim_Obj *const *argv)
10375
{
10376
    Jim_Obj *exprResultPtr;
10377
    int retcode;
10378
 
10379
    if (argc == 2) {
10380
        retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
10381
    } else if (argc > 2) {
10382
        Jim_Obj *objPtr;
10383
 
10384
        objPtr = Jim_ConcatObj(interp, argc-1, argv+1);
10385
        Jim_IncrRefCount(objPtr);
10386
        retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
10387
        Jim_DecrRefCount(interp, objPtr);
10388
    } else {
10389
        Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
10390
        return JIM_ERR;
10391
    }
10392
    if (retcode != JIM_OK) return retcode;
10393
    Jim_SetResult(interp, exprResultPtr);
10394
    Jim_DecrRefCount(interp, exprResultPtr);
10395
    return JIM_OK;
10396
}
10397
 
10398
/* [break] */
10399
static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
10400
        Jim_Obj *const *argv)
10401
{
10402
    if (argc != 1) {
10403
        Jim_WrongNumArgs(interp, 1, argv, "");
10404
        return JIM_ERR;
10405
    }
10406
    return JIM_BREAK;
10407
}
10408
 
10409
/* [continue] */
10410
static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
10411
        Jim_Obj *const *argv)
10412
{
10413
    if (argc != 1) {
10414
        Jim_WrongNumArgs(interp, 1, argv, "");
10415
        return JIM_ERR;
10416
    }
10417
    return JIM_CONTINUE;
10418
}
10419
 
10420
/* [return] */
10421
static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
10422
        Jim_Obj *const *argv)
10423
{
10424
    if (argc == 1) {
10425
        return JIM_RETURN;
10426
    } else if (argc == 2) {
10427
        Jim_SetResult(interp, argv[1]);
10428
        interp->returnCode = JIM_OK;
10429
        return JIM_RETURN;
10430
    } else if (argc == 3 || argc == 4) {
10431
        int returnCode;
10432
        if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
10433
            return JIM_ERR;
10434
        interp->returnCode = returnCode;
10435
        if (argc == 4)
10436
            Jim_SetResult(interp, argv[3]);
10437
        return JIM_RETURN;
10438
    } else {
10439
        Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
10440
        return JIM_ERR;
10441
    }
10442
    return JIM_RETURN; /* unreached */
10443
}
10444
 
10445
/* [tailcall] */
10446
static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
10447
        Jim_Obj *const *argv)
10448
{
10449
    Jim_Obj *objPtr;
10450
 
10451
    objPtr = Jim_NewListObj(interp, argv+1, argc-1);
10452
    Jim_SetResult(interp, objPtr);
10453
    return JIM_EVAL;
10454
}
10455
 
10456
/* [proc] */
10457
static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
10458
        Jim_Obj *const *argv)
10459
{
10460
    int argListLen;
10461
    int arityMin, arityMax;
10462
 
10463
    if (argc != 4 && argc != 5) {
10464
        Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
10465
        return JIM_ERR;
10466
    }
10467
    Jim_ListLength(interp, argv[2], &argListLen);
10468
    arityMin = arityMax = argListLen+1;
10469
    if (argListLen) {
10470
        const char *str;
10471
        int len;
10472
        Jim_Obj *lastArgPtr;
10473
 
10474
        Jim_ListIndex(interp, argv[2], argListLen-1, &lastArgPtr, JIM_NONE);
10475
        str = Jim_GetString(lastArgPtr, &len);
10476
        if (len == 4 && memcmp(str, "args", 4) == 0) {
10477
            arityMin--;
10478
            arityMax = -1;
10479
        }
10480
    }
10481
    if (argc == 4) {
10482
        return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10483
                argv[2], NULL, argv[3], arityMin, arityMax);
10484
    } else {
10485
        return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
10486
                argv[2], argv[3], argv[4], arityMin, arityMax);
10487
    }
10488
}
10489
 
10490
/* [concat] */
10491
static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
10492
        Jim_Obj *const *argv)
10493
{
10494
    Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv+1));
10495
    return JIM_OK;
10496
}
10497
 
10498
/* [upvar] */
10499
static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
10500
        Jim_Obj *const *argv)
10501
{
10502
    const char *str;
10503
    int i;
10504
    Jim_CallFrame *targetCallFrame;
10505
 
10506
    /* Lookup the target frame pointer */
10507
    str = Jim_GetString(argv[1], NULL);
10508
    if (argc > 3 &&
10509
        ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
10510
    {
10511
        if (Jim_GetCallFrameByLevel(interp, argv[1],
10512
                    &targetCallFrame, NULL) != JIM_OK)
10513
            return JIM_ERR;
10514
        argc--;
10515
        argv++;
10516
    } else {
10517
        if (Jim_GetCallFrameByLevel(interp, NULL,
10518
                    &targetCallFrame, NULL) != JIM_OK)
10519
            return JIM_ERR;
10520
    }
10521
    /* Check for arity */
10522
    if (argc < 3 || ((argc-1)%2) != 0) {
10523
        Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
10524
        return JIM_ERR;
10525
    }
10526
    /* Now... for every other/local couple: */
10527
    for (i = 1; i < argc; i += 2) {
10528
        if (Jim_SetVariableLink(interp, argv[i+1], argv[i],
10529
                targetCallFrame) != JIM_OK) return JIM_ERR;
10530
    }
10531
    return JIM_OK;
10532
}
10533
 
10534
/* [global] */
10535
static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
10536
        Jim_Obj *const *argv)
10537
{
10538
    int i;
10539
 
10540
    if (argc < 2) {
10541
        Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
10542
        return JIM_ERR;
10543
    }
10544
    /* Link every var to the toplevel having the same name */
10545
    if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
10546
    for (i = 1; i < argc; i++) {
10547
        if (Jim_SetVariableLink(interp, argv[i], argv[i],
10548
                interp->topFramePtr) != JIM_OK) return JIM_ERR;
10549
    }
10550
    return JIM_OK;
10551
}
10552
 
10553
/* does the [string map] operation. On error NULL is returned,
10554
 * otherwise a new string object with the result, having refcount = 0,
10555
 * is returned. */
10556
static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
10557
        Jim_Obj *objPtr, int nocase)
10558
{
10559
    int numMaps;
10560
    const char **key, *str, *noMatchStart = NULL;
10561
    Jim_Obj **value;
10562
    int *keyLen, strLen, i;
10563
    Jim_Obj *resultObjPtr;
10564
 
10565
    Jim_ListLength(interp, mapListObjPtr, &numMaps);
10566
    if (numMaps % 2) {
10567
        Jim_SetResultString(interp,
10568
                "list must contain an even number of elements", -1);
10569
        return NULL;
10570
    }
10571
    /* Initialization */
10572
    numMaps /= 2;
10573
    key = Jim_Alloc(sizeof(char*)*numMaps);
10574
    keyLen = Jim_Alloc(sizeof(int)*numMaps);
10575
    value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
10576
    resultObjPtr = Jim_NewStringObj(interp, "", 0);
10577
    for (i = 0; i < numMaps; i++) {
10578
        Jim_Obj *eleObjPtr;
10579
 
10580
        Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
10581
        key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
10582
        Jim_ListIndex(interp, mapListObjPtr, i*2+1, &eleObjPtr, JIM_NONE);
10583
        value[i] = eleObjPtr;
10584
    }
10585
    str = Jim_GetString(objPtr, &strLen);
10586
    /* Map it */
10587
    while(strLen) {
10588
        for (i = 0; i < numMaps; i++) {
10589
            if (strLen >= keyLen[i] && keyLen[i]) {
10590
                if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
10591
                            nocase))
10592
                {
10593
                    if (noMatchStart) {
10594
                        Jim_AppendString(interp, resultObjPtr,
10595
                                noMatchStart, str-noMatchStart);
10596
                        noMatchStart = NULL;
10597
                    }
10598
                    Jim_AppendObj(interp, resultObjPtr, value[i]);
10599
                    str += keyLen[i];
10600
                    strLen -= keyLen[i];
10601
                    break;
10602
                }
10603
            }
10604
        }
10605
        if (i == numMaps) { /* no match */
10606
            if (noMatchStart == NULL)
10607
                noMatchStart = str;
10608
            str ++;
10609
            strLen --;
10610
        }
10611
    }
10612
    if (noMatchStart) {
10613
        Jim_AppendString(interp, resultObjPtr,
10614
            noMatchStart, str-noMatchStart);
10615
    }
10616
    Jim_Free((void*)key);
10617
    Jim_Free(keyLen);
10618
    Jim_Free(value);
10619
    return resultObjPtr;
10620
}
10621
 
10622
/* [string] */
10623
static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
10624
        Jim_Obj *const *argv)
10625
{
10626
    int option;
10627
    const char *options[] = {
10628
        "length", "compare", "match", "equal", "range", "map", "repeat",
10629
        "index", "first", "tolower", "toupper", NULL
10630
    };
10631
    enum {
10632
        OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
10633
        OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
10634
    };
10635
 
10636
    if (argc < 2) {
10637
        Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10638
        return JIM_ERR;
10639
    }
10640
    if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10641
                JIM_ERRMSG) != JIM_OK)
10642
        return JIM_ERR;
10643
 
10644
    if (option == OPT_LENGTH) {
10645
        int len;
10646
 
10647
        if (argc != 3) {
10648
            Jim_WrongNumArgs(interp, 2, argv, "string");
10649
            return JIM_ERR;
10650
        }
10651
        Jim_GetString(argv[2], &len);
10652
        Jim_SetResult(interp, Jim_NewIntObj(interp, len));
10653
        return JIM_OK;
10654
    } else if (option == OPT_COMPARE) {
10655
        int nocase = 0;
10656
        if ((argc != 4 && argc != 5) ||
10657
            (argc == 5 && Jim_CompareStringImmediate(interp,
10658
                argv[2], "-nocase") == 0)) {
10659
            Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10660
            return JIM_ERR;
10661
        }
10662
        if (argc == 5) {
10663
            nocase = 1;
10664
            argv++;
10665
        }
10666
        Jim_SetResult(interp, Jim_NewIntObj(interp,
10667
                    Jim_StringCompareObj(argv[2],
10668
                            argv[3], nocase)));
10669
        return JIM_OK;
10670
    } else if (option == OPT_MATCH) {
10671
        int nocase = 0;
10672
        if ((argc != 4 && argc != 5) ||
10673
            (argc == 5 && Jim_CompareStringImmediate(interp,
10674
                argv[2], "-nocase") == 0)) {
10675
            Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
10676
                    "string");
10677
            return JIM_ERR;
10678
        }
10679
        if (argc == 5) {
10680
            nocase = 1;
10681
            argv++;
10682
        }
10683
        Jim_SetResult(interp,
10684
            Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
10685
                    argv[3], nocase)));
10686
        return JIM_OK;
10687
    } else if (option == OPT_EQUAL) {
10688
        if (argc != 4) {
10689
            Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
10690
            return JIM_ERR;
10691
        }
10692
        Jim_SetResult(interp,
10693
            Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
10694
                    argv[3], 0)));
10695
        return JIM_OK;
10696
    } else if (option == OPT_RANGE) {
10697
        Jim_Obj *objPtr;
10698
 
10699
        if (argc != 5) {
10700
            Jim_WrongNumArgs(interp, 2, argv, "string first last");
10701
            return JIM_ERR;
10702
        }
10703
        objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
10704
        if (objPtr == NULL)
10705
            return JIM_ERR;
10706
        Jim_SetResult(interp, objPtr);
10707
        return JIM_OK;
10708
    } else if (option == OPT_MAP) {
10709
        int nocase = 0;
10710
        Jim_Obj *objPtr;
10711
 
10712
        if ((argc != 4 && argc != 5) ||
10713
            (argc == 5 && Jim_CompareStringImmediate(interp,
10714
                argv[2], "-nocase") == 0)) {
10715
            Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
10716
                    "string");
10717
            return JIM_ERR;
10718
        }
10719
        if (argc == 5) {
10720
            nocase = 1;
10721
            argv++;
10722
        }
10723
        objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
10724
        if (objPtr == NULL)
10725
            return JIM_ERR;
10726
        Jim_SetResult(interp, objPtr);
10727
        return JIM_OK;
10728
    } else if (option == OPT_REPEAT) {
10729
        Jim_Obj *objPtr;
10730
        jim_wide count;
10731
 
10732
        if (argc != 4) {
10733
            Jim_WrongNumArgs(interp, 2, argv, "string count");
10734
            return JIM_ERR;
10735
        }
10736
        if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
10737
            return JIM_ERR;
10738
        objPtr = Jim_NewStringObj(interp, "", 0);
10739
        while (count--) {
10740
            Jim_AppendObj(interp, objPtr, argv[2]);
10741
        }
10742
        Jim_SetResult(interp, objPtr);
10743
        return JIM_OK;
10744
    } else if (option == OPT_INDEX) {
10745
        int index, len;
10746
        const char *str;
10747
 
10748
        if (argc != 4) {
10749
            Jim_WrongNumArgs(interp, 2, argv, "string index");
10750
            return JIM_ERR;
10751
        }
10752
        if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
10753
            return JIM_ERR;
10754
        str = Jim_GetString(argv[2], &len);
10755
        if (index != INT_MIN && index != INT_MAX)
10756
            index = JimRelToAbsIndex(len, index);
10757
        if (index < 0 || index >= len) {
10758
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10759
            return JIM_OK;
10760
        } else {
10761
            Jim_SetResult(interp, Jim_NewStringObj(interp, str+index, 1));
10762
            return JIM_OK;
10763
        }
10764
    } else if (option == OPT_FIRST) {
10765
        int index = 0, l1, l2;
10766
        const char *s1, *s2;
10767
 
10768
        if (argc != 4 && argc != 5) {
10769
            Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
10770
            return JIM_ERR;
10771
        }
10772
        s1 = Jim_GetString(argv[2], &l1);
10773
        s2 = Jim_GetString(argv[3], &l2);
10774
        if (argc == 5) {
10775
            if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
10776
                return JIM_ERR;
10777
            index = JimRelToAbsIndex(l2, index);
10778
        }
10779
        Jim_SetResult(interp, Jim_NewIntObj(interp,
10780
                    JimStringFirst(s1, l1, s2, l2, index)));
10781
        return JIM_OK;
10782
    } else if (option == OPT_TOLOWER) {
10783
        if (argc != 3) {
10784
            Jim_WrongNumArgs(interp, 2, argv, "string");
10785
            return JIM_ERR;
10786
        }
10787
        Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
10788
    } else if (option == OPT_TOUPPER) {
10789
        if (argc != 3) {
10790
            Jim_WrongNumArgs(interp, 2, argv, "string");
10791
            return JIM_ERR;
10792
        }
10793
        Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
10794
    }
10795
    return JIM_OK;
10796
}
10797
 
10798
/* [time] */
10799
static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
10800
        Jim_Obj *const *argv)
10801
{
10802
    long i, count = 1;
10803
    jim_wide start, elapsed;
10804
    char buf [256];
10805
    const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
10806
 
10807
    if (argc < 2) {
10808
        Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
10809
        return JIM_ERR;
10810
    }
10811
    if (argc == 3) {
10812
        if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
10813
            return JIM_ERR;
10814
    }
10815
    if (count < 0)
10816
        return JIM_OK;
10817
    i = count;
10818
    start = JimClock();
10819
    while (i-- > 0) {
10820
        int retval;
10821
 
10822
        if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
10823
            return retval;
10824
    }
10825
    elapsed = JimClock() - start;
10826
    sprintf(buf, fmt, elapsed/count);
10827
    Jim_SetResultString(interp, buf, -1);
10828
    return JIM_OK;
10829
}
10830
 
10831
/* [exit] */
10832
static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
10833
        Jim_Obj *const *argv)
10834
{
10835
    long exitCode = 0;
10836
 
10837
    if (argc > 2) {
10838
        Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
10839
        return JIM_ERR;
10840
    }
10841
    if (argc == 2) {
10842
        if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
10843
            return JIM_ERR;
10844
    }
10845
    interp->exitCode = exitCode;
10846
    return JIM_EXIT;
10847
}
10848
 
10849
/* [catch] */
10850
static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
10851
        Jim_Obj *const *argv)
10852
{
10853
    int exitCode = 0;
10854
 
10855
    if (argc != 2 && argc != 3) {
10856
        Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
10857
        return JIM_ERR;
10858
    }
10859
    exitCode = Jim_EvalObj(interp, argv[1]);
10860
    if (argc == 3) {
10861
        if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
10862
                != JIM_OK)
10863
            return JIM_ERR;
10864
    }
10865
    Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
10866
    return JIM_OK;
10867
}
10868
 
10869
/* [ref] */
10870
static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
10871
        Jim_Obj *const *argv)
10872
{
10873
    if (argc != 3 && argc != 4) {
10874
        Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
10875
        return JIM_ERR;
10876
    }
10877
    if (argc == 3) {
10878
        Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
10879
    } else {
10880
        Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
10881
                    argv[3]));
10882
    }
10883
    return JIM_OK;
10884
}
10885
 
10886
/* [getref] */
10887
static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
10888
        Jim_Obj *const *argv)
10889
{
10890
    Jim_Reference *refPtr;
10891
 
10892
    if (argc != 2) {
10893
        Jim_WrongNumArgs(interp, 1, argv, "reference");
10894
        return JIM_ERR;
10895
    }
10896
    if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10897
        return JIM_ERR;
10898
    Jim_SetResult(interp, refPtr->objPtr);
10899
    return JIM_OK;
10900
}
10901
 
10902
/* [setref] */
10903
static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
10904
        Jim_Obj *const *argv)
10905
{
10906
    Jim_Reference *refPtr;
10907
 
10908
    if (argc != 3) {
10909
        Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
10910
        return JIM_ERR;
10911
    }
10912
    if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
10913
        return JIM_ERR;
10914
    Jim_IncrRefCount(argv[2]);
10915
    Jim_DecrRefCount(interp, refPtr->objPtr);
10916
    refPtr->objPtr = argv[2];
10917
    Jim_SetResult(interp, argv[2]);
10918
    return JIM_OK;
10919
}
10920
 
10921
/* [collect] */
10922
static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
10923
        Jim_Obj *const *argv)
10924
{
10925
    if (argc != 1) {
10926
        Jim_WrongNumArgs(interp, 1, argv, "");
10927
        return JIM_ERR;
10928
    }
10929
    Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
10930
    return JIM_OK;
10931
}
10932
 
10933
/* [finalize] reference ?newValue? */
10934
static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
10935
        Jim_Obj *const *argv)
10936
{
10937
    if (argc != 2 && argc != 3) {
10938
        Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
10939
        return JIM_ERR;
10940
    }
10941
    if (argc == 2) {
10942
        Jim_Obj *cmdNamePtr;
10943
 
10944
        if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
10945
            return JIM_ERR;
10946
        if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
10947
            Jim_SetResult(interp, cmdNamePtr);
10948
    } else {
10949
        if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
10950
            return JIM_ERR;
10951
        Jim_SetResult(interp, argv[2]);
10952
    }
10953
    return JIM_OK;
10954
}
10955
 
10956
/* TODO */
10957
/* [info references] (list of all the references/finalizers) */
10958
 
10959
/* [rename] */
10960
static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
10961
        Jim_Obj *const *argv)
10962
{
10963
    const char *oldName, *newName;
10964
 
10965
    if (argc != 3) {
10966
        Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
10967
        return JIM_ERR;
10968
    }
10969
    oldName = Jim_GetString(argv[1], NULL);
10970
    newName = Jim_GetString(argv[2], NULL);
10971
    if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
10972
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
10973
        Jim_AppendStrings(interp, Jim_GetResult(interp),
10974
            "can't rename \"", oldName, "\": ",
10975
            "command doesn't exist", NULL);
10976
        return JIM_ERR;
10977
    }
10978
    return JIM_OK;
10979
}
10980
 
10981
/* [dict] */
10982
static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
10983
        Jim_Obj *const *argv)
10984
{
10985
    int option;
10986
    const char *options[] = {
10987
        "create", "get", "set", "unset", "exists", NULL
10988
    };
10989
    enum {
10990
        OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
10991
    };
10992
 
10993
    if (argc < 2) {
10994
        Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
10995
        return JIM_ERR;
10996
    }
10997
 
10998
    if (Jim_GetEnum(interp, argv[1], options, &option, "option",
10999
                JIM_ERRMSG) != JIM_OK)
11000
        return JIM_ERR;
11001
 
11002
    if (option == OPT_CREATE) {
11003
        Jim_Obj *objPtr;
11004
 
11005
        if (argc % 2) {
11006
            Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
11007
            return JIM_ERR;
11008
        }
11009
        objPtr = Jim_NewDictObj(interp, argv+2, argc-2);
11010
        Jim_SetResult(interp, objPtr);
11011
        return JIM_OK;
11012
    } else if (option == OPT_GET) {
11013
        Jim_Obj *objPtr;
11014
 
11015
        if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11016
                JIM_ERRMSG) != JIM_OK)
11017
            return JIM_ERR;
11018
        Jim_SetResult(interp, objPtr);
11019
        return JIM_OK;
11020
    } else if (option == OPT_SET) {
11021
        if (argc < 5) {
11022
            Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
11023
            return JIM_ERR;
11024
        }
11025
        return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-4,
11026
                    argv[argc-1]);
11027
    } else if (option == OPT_UNSET) {
11028
        if (argc < 4) {
11029
            Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
11030
            return JIM_ERR;
11031
        }
11032
        return Jim_SetDictKeysVector(interp, argv[2], argv+3, argc-3,
11033
                    NULL);
11034
    } else if (option == OPT_EXIST) {
11035
        Jim_Obj *objPtr;
11036
        int exists;
11037
 
11038
        if (Jim_DictKeysVector(interp, argv[2], argv+3, argc-3, &objPtr,
11039
                JIM_ERRMSG) == JIM_OK)
11040
            exists = 1;
11041
        else
11042
            exists = 0;
11043
        Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
11044
        return JIM_OK;
11045
    } else {
11046
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11047
        Jim_AppendStrings(interp, Jim_GetResult(interp),
11048
            "bad option \"", Jim_GetString(argv[1], NULL), "\":",
11049
            " must be create, get, set", NULL);
11050
        return JIM_ERR;
11051
    }
11052
    return JIM_OK;
11053
}
11054
 
11055
/* [load] */
11056
static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
11057
        Jim_Obj *const *argv)
11058
{
11059
    if (argc < 2) {
11060
        Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
11061
        return JIM_ERR;
11062
    }
11063
    return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
11064
}
11065
 
11066
/* [subst] */
11067
static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
11068
        Jim_Obj *const *argv)
11069
{
11070
    int i, flags = 0;
11071
    Jim_Obj *objPtr;
11072
 
11073
    if (argc < 2) {
11074
        Jim_WrongNumArgs(interp, 1, argv,
11075
            "?-nobackslashes? ?-nocommands? ?-novariables? string");
11076
        return JIM_ERR;
11077
    }
11078
    i = argc-2;
11079
    while(i--) {
11080
        if (Jim_CompareStringImmediate(interp, argv[i+1],
11081
                    "-nobackslashes"))
11082
            flags |= JIM_SUBST_NOESC;
11083
        else if (Jim_CompareStringImmediate(interp, argv[i+1],
11084
                    "-novariables"))
11085
            flags |= JIM_SUBST_NOVAR;
11086
        else if (Jim_CompareStringImmediate(interp, argv[i+1],
11087
                    "-nocommands"))
11088
            flags |= JIM_SUBST_NOCMD;
11089
        else {
11090
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11091
            Jim_AppendStrings(interp, Jim_GetResult(interp),
11092
                "bad option \"", Jim_GetString(argv[i+1], NULL),
11093
                "\": must be -nobackslashes, -nocommands, or "
11094
                "-novariables", NULL);
11095
            return JIM_ERR;
11096
        }
11097
    }
11098
    if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
11099
        return JIM_ERR;
11100
    Jim_SetResult(interp, objPtr);
11101
    return JIM_OK;
11102
}
11103
 
11104
/* [info] */
11105
static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
11106
        Jim_Obj *const *argv)
11107
{
11108
    int cmd, result = JIM_OK;
11109
    static const char *commands[] = {
11110
        "body", "commands", "exists", "globals", "level", "locals",
11111
        "vars", "version", "complete", "args", NULL
11112
    };
11113
    enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
11114
          INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS};
11115
 
11116
    if (argc < 2) {
11117
        Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
11118
        return JIM_ERR;
11119
    }
11120
    if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
11121
        != JIM_OK) {
11122
        return JIM_ERR;
11123
    }
11124
 
11125
    if (cmd == INFO_COMMANDS) {
11126
        if (argc != 2 && argc != 3) {
11127
            Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11128
            return JIM_ERR;
11129
        }
11130
        if (argc == 3)
11131
            Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
11132
        else
11133
            Jim_SetResult(interp, JimCommandsList(interp, NULL));
11134
    } else if (cmd == INFO_EXISTS) {
11135
        Jim_Obj *exists;
11136
        if (argc != 3) {
11137
            Jim_WrongNumArgs(interp, 2, argv, "varName");
11138
            return JIM_ERR;
11139
        }
11140
        exists = Jim_GetVariable(interp, argv[2], 0);
11141
        Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
11142
    } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
11143
        int mode;
11144
        switch (cmd) {
11145
            case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
11146
            case INFO_LOCALS:  mode = JIM_VARLIST_LOCALS; break;
11147
            case INFO_VARS:    mode = JIM_VARLIST_VARS; break;
11148
            default: mode = 0; /* avoid warning */; break;
11149
        }
11150
        if (argc != 2 && argc != 3) {
11151
            Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
11152
            return JIM_ERR;
11153
        }
11154
        if (argc == 3)
11155
            Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
11156
        else
11157
            Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
11158
    } else if (cmd == INFO_LEVEL) {
11159
        Jim_Obj *objPtr;
11160
        switch (argc) {
11161
            case 2:
11162
                Jim_SetResult(interp,
11163
                              Jim_NewIntObj(interp, interp->numLevels));
11164
                break;
11165
            case 3:
11166
                if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
11167
                    return JIM_ERR;
11168
                Jim_SetResult(interp, objPtr);
11169
                break;
11170
            default:
11171
                Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
11172
                return JIM_ERR;
11173
        }
11174
    } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
11175
        Jim_Cmd *cmdPtr;
11176
 
11177
        if (argc != 3) {
11178
            Jim_WrongNumArgs(interp, 2, argv, "procname");
11179
            return JIM_ERR;
11180
        }
11181
        if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
11182
            return JIM_ERR;
11183
        if (cmdPtr->cmdProc != NULL) {
11184
            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11185
            Jim_AppendStrings(interp, Jim_GetResult(interp),
11186
                "command \"", Jim_GetString(argv[2], NULL),
11187
                "\" is not a procedure", NULL);
11188
            return JIM_ERR;
11189
        }
11190
        if (cmd == INFO_BODY)
11191
            Jim_SetResult(interp, cmdPtr->bodyObjPtr);
11192
        else
11193
            Jim_SetResult(interp, cmdPtr->argListObjPtr);
11194
    } else if (cmd == INFO_VERSION) {
11195
        char buf[(JIM_INTEGER_SPACE * 2) + 1];
11196
        sprintf(buf, "%d.%d",
11197
                JIM_VERSION / 100, JIM_VERSION % 100);
11198
        Jim_SetResultString(interp, buf, -1);
11199
    } else if (cmd == INFO_COMPLETE) {
11200
        const char *s;
11201
        int len;
11202
 
11203
        if (argc != 3) {
11204
            Jim_WrongNumArgs(interp, 2, argv, "script");
11205
            return JIM_ERR;
11206
        }
11207
        s = Jim_GetString(argv[2], &len);
11208
        Jim_SetResult(interp,
11209
                Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
11210
    }
11211
    return result;
11212
}
11213
 
11214
/* [split] */
11215
static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
11216
        Jim_Obj *const *argv)
11217
{
11218
    const char *str, *splitChars, *noMatchStart;
11219
    int splitLen, strLen, i;
11220
    Jim_Obj *resObjPtr;
11221
 
11222
    if (argc != 2 && argc != 3) {
11223
        Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
11224
        return JIM_ERR;
11225
    }
11226
    /* Init */
11227
    if (argc == 2) {
11228
        splitChars = " \n\t\r";
11229
        splitLen = 4;
11230
    } else {
11231
        splitChars = Jim_GetString(argv[2], &splitLen);
11232
    }
11233
    str = Jim_GetString(argv[1], &strLen);
11234
    if (!strLen) return JIM_OK;
11235
    noMatchStart = str;
11236
    resObjPtr = Jim_NewListObj(interp, NULL, 0);
11237
    /* Split */
11238
    if (splitLen) {
11239
        while (strLen) {
11240
            for (i = 0; i < splitLen; i++) {
11241
                if (*str == splitChars[i]) {
11242
                    Jim_Obj *objPtr;
11243
 
11244
                    objPtr = Jim_NewStringObj(interp, noMatchStart,
11245
                            (str-noMatchStart));
11246
                    Jim_ListAppendElement(interp, resObjPtr, objPtr);
11247
                    noMatchStart = str+1;
11248
                    break;
11249
                }
11250
            }
11251
            str ++;
11252
            strLen --;
11253
        }
11254
        Jim_ListAppendElement(interp, resObjPtr,
11255
                Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
11256
    } else {
11257
        /* This handles the special case of splitchars eq {}. This
11258
         * is trivial but we want to perform object sharing as Tcl does. */
11259
        Jim_Obj *objCache[256];
11260
        const unsigned char *u = (unsigned char*) str;
11261
        memset(objCache, 0, sizeof(objCache));
11262
        for (i = 0; i < strLen; i++) {
11263
            int c = u[i];
11264
 
11265
            if (objCache[c] == NULL)
11266
                objCache[c] = Jim_NewStringObj(interp, (char*)u+i, 1);
11267
            Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
11268
        }
11269
    }
11270
    Jim_SetResult(interp, resObjPtr);
11271
    return JIM_OK;
11272
}
11273
 
11274
/* [join] */
11275
static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
11276
        Jim_Obj *const *argv)
11277
{
11278
    const char *joinStr;
11279
    int joinStrLen, i, listLen;
11280
    Jim_Obj *resObjPtr;
11281
 
11282
    if (argc != 2 && argc != 3) {
11283
        Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
11284
        return JIM_ERR;
11285
    }
11286
    /* Init */
11287
    if (argc == 2) {
11288
        joinStr = " ";
11289
        joinStrLen = 1;
11290
    } else {
11291
        joinStr = Jim_GetString(argv[2], &joinStrLen);
11292
    }
11293
    Jim_ListLength(interp, argv[1], &listLen);
11294
    resObjPtr = Jim_NewStringObj(interp, NULL, 0);
11295
    /* Split */
11296
    for (i = 0; i < listLen; i++) {
11297
        Jim_Obj *objPtr;
11298
 
11299
        Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
11300
        Jim_AppendObj(interp, resObjPtr, objPtr);
11301
        if (i+1 != listLen) {
11302
            Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11303
        }
11304
    }
11305
    Jim_SetResult(interp, resObjPtr);
11306
    return JIM_OK;
11307
}
11308
 
11309
/* [format] */
11310
static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
11311
        Jim_Obj *const *argv)
11312
{
11313
    Jim_Obj *objPtr;
11314
 
11315
    if (argc < 2) {
11316
        Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
11317
        return JIM_ERR;
11318
    }
11319
    objPtr = Jim_FormatString(interp, argv[1], argc-2, argv+2);
11320
    if (objPtr == NULL)
11321
        return JIM_ERR;
11322
    Jim_SetResult(interp, objPtr);
11323
    return JIM_OK;
11324
}
11325
 
11326
/* [scan] */
11327
static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
11328
        Jim_Obj *const *argv)
11329
{
11330
    Jim_Obj *listPtr, **outVec;
11331
    int outc, i, count = 0;
11332
 
11333
    if (argc < 3) {
11334
        Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
11335
        return JIM_ERR;
11336
    }
11337
    if (argv[2]->typePtr != &scanFmtStringObjType)
11338
        SetScanFmtFromAny(interp, argv[2]);
11339
    if (FormatGetError(argv[2]) != 0) {
11340
        Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
11341
        return JIM_ERR;
11342
    }
11343
    if (argc > 3) {
11344
        int maxPos = FormatGetMaxPos(argv[2]);
11345
        int count = FormatGetCnvCount(argv[2]);
11346
        if (maxPos > argc-3) {
11347
            Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
11348
            return JIM_ERR;
11349
        } else if (count != 0 && count < argc-3) {
11350
            Jim_SetResultString(interp, "variable is not assigned by any "
11351
                "conversion specifiers", -1);
11352
            return JIM_ERR;
11353
        } else if (count > argc-3) {
11354
            Jim_SetResultString(interp, "different numbers of variable names and "
11355
                "field specifiers", -1);
11356
            return JIM_ERR;
11357
        }
11358
    }
11359
    listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
11360
    if (listPtr == 0)
11361
        return JIM_ERR;
11362
    if (argc > 3) {
11363
        int len = 0;
11364
        if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
11365
            Jim_ListLength(interp, listPtr, &len);
11366
        if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
11367
            Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
11368
            return JIM_OK;
11369
        }
11370
        JimListGetElements(interp, listPtr, &outc, &outVec);
11371
        for (i = 0; i < outc; ++i) {
11372
            if (Jim_Length(outVec[i]) > 0) {
11373
                ++count;
11374
                if (Jim_SetVariable(interp, argv[3+i], outVec[i]) != JIM_OK)
11375
                    goto err;
11376
            }
11377
        }
11378
        Jim_FreeNewObj(interp, listPtr);
11379
        Jim_SetResult(interp, Jim_NewIntObj(interp, count));
11380
    } else {
11381
        if (listPtr == (Jim_Obj*)EOF) {
11382
            Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
11383
            return JIM_OK;
11384
        }
11385
        Jim_SetResult(interp, listPtr);
11386
    }
11387
    return JIM_OK;
11388
err:
11389
    Jim_FreeNewObj(interp, listPtr);
11390
    return JIM_ERR;
11391
}
11392
 
11393
/* [error] */
11394
static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
11395
        Jim_Obj *const *argv)
11396
{
11397
    if (argc != 2) {
11398
        Jim_WrongNumArgs(interp, 1, argv, "message");
11399
        return JIM_ERR;
11400
    }
11401
    Jim_SetResult(interp, argv[1]);
11402
    return JIM_ERR;
11403
}
11404
 
11405
/* [lrange] */
11406
static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
11407
        Jim_Obj *const *argv)
11408
{
11409
    Jim_Obj *objPtr;
11410
 
11411
    if (argc != 4) {
11412
        Jim_WrongNumArgs(interp, 1, argv, "list first last");
11413
        return JIM_ERR;
11414
    }
11415
    if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
11416
        return JIM_ERR;
11417
    Jim_SetResult(interp, objPtr);
11418
    return JIM_OK;
11419
}
11420
 
11421
/* [env] */
11422
static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
11423
        Jim_Obj *const *argv)
11424
{
11425
    const char *key;
11426
    char *val;
11427
 
11428
    if (argc != 2) {
11429
        Jim_WrongNumArgs(interp, 1, argv, "varName");
11430
        return JIM_ERR;
11431
    }
11432
    key = Jim_GetString(argv[1], NULL);
11433
    val = getenv(key);
11434
    if (val == NULL) {
11435
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
11436
        Jim_AppendStrings(interp, Jim_GetResult(interp),
11437
                "environment variable \"",
11438
                key, "\" does not exist", NULL);
11439
        return JIM_ERR;
11440
    }
11441
    Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
11442
    return JIM_OK;
11443
}
11444
 
11445
/* [source] */
11446
static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
11447
        Jim_Obj *const *argv)
11448
{
11449
    int retval;
11450
 
11451
    if (argc != 2) {
11452
        Jim_WrongNumArgs(interp, 1, argv, "fileName");
11453
        return JIM_ERR;
11454
    }
11455
    retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
11456
    if (retval == JIM_RETURN)
11457
        return JIM_OK;
11458
    return retval;
11459
}
11460
 
11461
/* [lreverse] */
11462
static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
11463
        Jim_Obj *const *argv)
11464
{
11465
    Jim_Obj *revObjPtr, **ele;
11466
    int len;
11467
 
11468
    if (argc != 2) {
11469
        Jim_WrongNumArgs(interp, 1, argv, "list");
11470
        return JIM_ERR;
11471
    }
11472
    JimListGetElements(interp, argv[1], &len, &ele);
11473
    len--;
11474
    revObjPtr = Jim_NewListObj(interp, NULL, 0);
11475
    while (len >= 0)
11476
        ListAppendElement(revObjPtr, ele[len--]);
11477
    Jim_SetResult(interp, revObjPtr);
11478
    return JIM_OK;
11479
}
11480
 
11481
static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
11482
{
11483
    jim_wide len;
11484
 
11485
    if (step == 0) return -1;
11486
    if (start == end) return 0;
11487
    else if (step > 0 && start > end) return -1;
11488
    else if (step < 0 && end > start) return -1;
11489
    len = end-start;
11490
    if (len < 0) len = -len; /* abs(len) */
11491
    if (step < 0) step = -step; /* abs(step) */
11492
    len = 1 + ((len-1)/step);
11493
    /* We can truncate safely to INT_MAX, the range command
11494
     * will always return an error for a such long range
11495
     * because Tcl lists can't be so long. */
11496
    if (len > INT_MAX) len = INT_MAX;
11497
    return (int)((len < 0) ? -1 : len);
11498
}
11499
 
11500
/* [range] */
11501
static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
11502
        Jim_Obj *const *argv)
11503
{
11504
    jim_wide start = 0, end, step = 1;
11505
    int len, i;
11506
    Jim_Obj *objPtr;
11507
 
11508
    if (argc < 2 || argc > 4) {
11509
        Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
11510
        return JIM_ERR;
11511
    }
11512
    if (argc == 2) {
11513
        if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
11514
            return JIM_ERR;
11515
    } else {
11516
        if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
11517
            Jim_GetWide(interp, argv[2], &end) != JIM_OK)
11518
            return JIM_ERR;
11519
        if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
11520
            return JIM_ERR;
11521
    }
11522
    if ((len = JimRangeLen(start, end, step)) == -1) {
11523
        Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
11524
        return JIM_ERR;
11525
    }
11526
    objPtr = Jim_NewListObj(interp, NULL, 0);
11527
    for (i = 0; i < len; i++)
11528
        ListAppendElement(objPtr, Jim_NewIntObj(interp, start+i*step));
11529
    Jim_SetResult(interp, objPtr);
11530
    return JIM_OK;
11531
}
11532
 
11533
/* [rand] */
11534
static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
11535
        Jim_Obj *const *argv)
11536
{
11537
    jim_wide min = 0, max, len, maxMul;
11538
 
11539
    if (argc < 1 || argc > 3) {
11540
        Jim_WrongNumArgs(interp, 1, argv, "?min? max");
11541
        return JIM_ERR;
11542
    }
11543
    if (argc == 1) {
11544
        max = JIM_WIDE_MAX;
11545
    } else if (argc == 2) {
11546
        if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
11547
            return JIM_ERR;
11548
    } else if (argc == 3) {
11549
        if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
11550
            Jim_GetWide(interp, argv[2], &max) != JIM_OK)
11551
            return JIM_ERR;
11552
    }
11553
    len = max-min;
11554
    if (len < 0) {
11555
        Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
11556
        return JIM_ERR;
11557
    }
11558
    maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
11559
    while (1) {
11560
        jim_wide r;
11561
 
11562
        JimRandomBytes(interp, &r, sizeof(jim_wide));
11563
        if (r < 0 || r >= maxMul) continue;
11564
        r = (len == 0) ? 0 : r%len;
11565
        Jim_SetResult(interp, Jim_NewIntObj(interp, min+r));
11566
        return JIM_OK;
11567
    }
11568
}
11569
 
11570
/* [package] */
11571
static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
11572
        Jim_Obj *const *argv)
11573
{
11574
    int option;
11575
    const char *options[] = {
11576
        "require", "provide", NULL
11577
    };
11578
    enum {OPT_REQUIRE, OPT_PROVIDE};
11579
 
11580
    if (argc < 2) {
11581
        Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
11582
        return JIM_ERR;
11583
    }
11584
    if (Jim_GetEnum(interp, argv[1], options, &option, "option",
11585
                JIM_ERRMSG) != JIM_OK)
11586
        return JIM_ERR;
11587
 
11588
    if (option == OPT_REQUIRE) {
11589
        int exact = 0;
11590
        const char *ver;
11591
 
11592
        if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
11593
            exact = 1;
11594
            argv++;
11595
            argc--;
11596
        }
11597
        if (argc != 3 && argc != 4) {
11598
            Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
11599
            return JIM_ERR;
11600
        }
11601
        ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
11602
                argc == 4 ? Jim_GetString(argv[3], NULL) : "",
11603
                JIM_ERRMSG);
11604
        if (ver == NULL)
11605
            return JIM_ERR;
11606
        Jim_SetResultString(interp, ver, -1);
11607
    } else if (option == OPT_PROVIDE) {
11608
        if (argc != 4) {
11609
            Jim_WrongNumArgs(interp, 2, argv, "package version");
11610
            return JIM_ERR;
11611
        }
11612
        return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
11613
                    Jim_GetString(argv[3], NULL), JIM_ERRMSG);
11614
    }
11615
    return JIM_OK;
11616
}
11617
 
11618
static struct {
11619
    const char *name;
11620
    Jim_CmdProc cmdProc;
11621
} Jim_CoreCommandsTable[] = {
11622
    {"set", Jim_SetCoreCommand},
11623
    {"unset", Jim_UnsetCoreCommand},
11624
    {"puts", Jim_PutsCoreCommand},
11625
    {"+", Jim_AddCoreCommand},
11626
    {"*", Jim_MulCoreCommand},
11627
    {"-", Jim_SubCoreCommand},
11628
    {"/", Jim_DivCoreCommand},
11629
    {"incr", Jim_IncrCoreCommand},
11630
    {"while", Jim_WhileCoreCommand},
11631
    {"for", Jim_ForCoreCommand},
11632
    {"foreach", Jim_ForeachCoreCommand},
11633
    {"lmap", Jim_LmapCoreCommand},
11634
    {"if", Jim_IfCoreCommand},
11635
    {"switch", Jim_SwitchCoreCommand},
11636
    {"list", Jim_ListCoreCommand},
11637
    {"lindex", Jim_LindexCoreCommand},
11638
    {"lset", Jim_LsetCoreCommand},
11639
    {"llength", Jim_LlengthCoreCommand},
11640
    {"lappend", Jim_LappendCoreCommand},
11641
    {"linsert", Jim_LinsertCoreCommand},
11642
    {"lsort", Jim_LsortCoreCommand},
11643
    {"append", Jim_AppendCoreCommand},
11644
    {"debug", Jim_DebugCoreCommand},
11645
    {"eval", Jim_EvalCoreCommand},
11646
    {"uplevel", Jim_UplevelCoreCommand},
11647
    {"expr", Jim_ExprCoreCommand},
11648
    {"break", Jim_BreakCoreCommand},
11649
    {"continue", Jim_ContinueCoreCommand},
11650
    {"proc", Jim_ProcCoreCommand},
11651
    {"concat", Jim_ConcatCoreCommand},
11652
    {"return", Jim_ReturnCoreCommand},
11653
    {"upvar", Jim_UpvarCoreCommand},
11654
    {"global", Jim_GlobalCoreCommand},
11655
    {"string", Jim_StringCoreCommand},
11656
    {"time", Jim_TimeCoreCommand},
11657
    {"exit", Jim_ExitCoreCommand},
11658
    {"catch", Jim_CatchCoreCommand},
11659
    {"ref", Jim_RefCoreCommand},
11660
    {"getref", Jim_GetrefCoreCommand},
11661
    {"setref", Jim_SetrefCoreCommand},
11662
    {"finalize", Jim_FinalizeCoreCommand},
11663
    {"collect", Jim_CollectCoreCommand},
11664
    {"rename", Jim_RenameCoreCommand},
11665
    {"dict", Jim_DictCoreCommand},
11666
    {"load", Jim_LoadCoreCommand},
11667
    {"subst", Jim_SubstCoreCommand},
11668
    {"info", Jim_InfoCoreCommand},
11669
    {"split", Jim_SplitCoreCommand},
11670
    {"join", Jim_JoinCoreCommand},
11671
    {"format", Jim_FormatCoreCommand},
11672
    {"scan", Jim_ScanCoreCommand},
11673
    {"error", Jim_ErrorCoreCommand},
11674
    {"lrange", Jim_LrangeCoreCommand},
11675
    {"env", Jim_EnvCoreCommand},
11676
    {"source", Jim_SourceCoreCommand},
11677
    {"lreverse", Jim_LreverseCoreCommand},
11678
    {"range", Jim_RangeCoreCommand},
11679
    {"rand", Jim_RandCoreCommand},
11680
    {"package", Jim_PackageCoreCommand},
11681
    {"tailcall", Jim_TailcallCoreCommand},
11682
    {NULL, NULL},
11683
};
11684
 
11685
/* Some Jim core command is actually a procedure written in Jim itself. */
11686
static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
11687
{
11688
    Jim_Eval(interp, (char*)
11689
"proc lambda {arglist args} {\n"
11690
"    set name [ref {} function lambdaFinalizer]\n"
11691
"    uplevel 1 [list proc $name $arglist {expand}$args]\n"
11692
"    return $name\n"
11693
"}\n"
11694
"proc lambdaFinalizer {name val} {\n"
11695
"    rename $name {}\n"
11696
"}\n"
11697
    );
11698
}
11699
 
11700
void Jim_RegisterCoreCommands(Jim_Interp *interp)
11701
{
11702
    int i = 0;
11703
 
11704
    while(Jim_CoreCommandsTable[i].name != NULL) {
11705
        Jim_CreateCommand(interp,
11706
                Jim_CoreCommandsTable[i].name,
11707
                Jim_CoreCommandsTable[i].cmdProc,
11708
                NULL, NULL);
11709
        i++;
11710
    }
11711
    Jim_RegisterCoreProcedures(interp);
11712
}
11713
 
11714
/* -----------------------------------------------------------------------------
11715
 * Interactive prompt
11716
 * ---------------------------------------------------------------------------*/
11717
void Jim_PrintErrorMessage(Jim_Interp *interp)
11718
{
11719
    int len, i;
11720
 
11721
    fprintf(interp->stderr, "Runtime error, file \"%s\", line %d:" JIM_NL,
11722
            interp->errorFileName, interp->errorLine);
11723
    fprintf(interp->stderr, "    %s" JIM_NL,
11724
            Jim_GetString(interp->result, NULL));
11725
    Jim_ListLength(interp, interp->stackTrace, &len);
11726
    for (i = 0; i < len; i+= 3) {
11727
        Jim_Obj *objPtr;
11728
        const char *proc, *file, *line;
11729
 
11730
        Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
11731
        proc = Jim_GetString(objPtr, NULL);
11732
        Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
11733
                JIM_NONE);
11734
        file = Jim_GetString(objPtr, NULL);
11735
        Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
11736
                JIM_NONE);
11737
        line = Jim_GetString(objPtr, NULL);
11738
        fprintf(interp->stderr,
11739
                "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
11740
                proc, file, line);
11741
    }
11742
}
11743
 
11744
int Jim_InteractivePrompt(Jim_Interp *interp)
11745
{
11746
    int retcode = JIM_OK;
11747
    Jim_Obj *scriptObjPtr;
11748
 
11749
    fprintf(interp->stdout, "Welcome to Jim version %d.%d, "
11750
           "Copyright (c) 2005 Salvatore Sanfilippo" JIM_NL,
11751
           JIM_VERSION / 100, JIM_VERSION % 100);
11752
    fprintf(interp->stdout,
11753
            "CVS ID: $Id: jim.c,v 1.170 2006/11/06 21:48:57 antirez Exp $"
11754
            JIM_NL);
11755
    Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
11756
    while (1) {
11757
        char buf[1024];
11758
        const char *result;
11759
        const char *retcodestr[] = {
11760
            "ok", "error", "return", "break", "continue", "eval", "exit"
11761
        };
11762
        int reslen;
11763
 
11764
        if (retcode != 0) {
11765
            if (retcode >= 2 && retcode <= 6)
11766
                fprintf(interp->stdout, "[%s] . ", retcodestr[retcode]);
11767
            else
11768
                fprintf(interp->stdout, "[%d] . ", retcode);
11769
        } else
11770
            fprintf(interp->stdout, ". ");
11771
        fflush(interp->stdout);
11772
        scriptObjPtr = Jim_NewStringObj(interp, "", 0);
11773
        Jim_IncrRefCount(scriptObjPtr);
11774
        while(1) {
11775
            const char *str;
11776
            char state;
11777
            int len;
11778
 
11779
            if (fgets(buf, 1024, interp->stdin) == NULL) {
11780
                Jim_DecrRefCount(interp, scriptObjPtr);
11781
                goto out;
11782
            }
11783
            Jim_AppendString(interp, scriptObjPtr, buf, -1);
11784
            str = Jim_GetString(scriptObjPtr, &len);
11785
            if (Jim_ScriptIsComplete(str, len, &state))
11786
                break;
11787
            fprintf(interp->stdout, "%c> ", state);
11788
            fflush(stdout);
11789
        }
11790
        retcode = Jim_EvalObj(interp, scriptObjPtr);
11791
        Jim_DecrRefCount(interp, scriptObjPtr);
11792
        result = Jim_GetString(Jim_GetResult(interp), &reslen);
11793
        if (retcode == JIM_ERR) {
11794
            Jim_PrintErrorMessage(interp);
11795
        } else if (retcode == JIM_EXIT) {
11796
            exit(Jim_GetExitCode(interp));
11797
        } else {
11798
            if (reslen) {
11799
                fwrite(result, 1, reslen, interp->stdout);
11800
                fprintf(interp->stdout, JIM_NL);
11801
            }
11802
        }
11803
    }
11804
out:
11805
    return 0;
11806
}

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.