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, ¤tVal) != 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, ¤tVal) == 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 |
|
|
}
|