You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

12844 lines
402 KiB

  1. /* Jim - A small embeddable Tcl interpreter
  2. *
  3. * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
  4. * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
  5. * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
  6. * Copyright 2008,2009 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
  7. * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
  8. * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
  9. * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
  10. * Copyright 2008 Steve Bennett <steveb@workware.net.au>
  11. * Copyright 2009 Nico Coesel <ncoesel@dealogic.nl>
  12. * Copyright 2009 Zachary T Welch zw@superlucidity.net
  13. * Copyright 2009 David Brownell
  14. *
  15. * The FreeBSD license
  16. *
  17. * Redistribution and use in source and binary forms, with or without
  18. * modification, are permitted provided that the following conditions
  19. * are met:
  20. *
  21. * 1. Redistributions of source code must retain the above copyright
  22. * notice, this list of conditions and the following disclaimer.
  23. * 2. Redistributions in binary form must reproduce the above
  24. * copyright notice, this list of conditions and the following
  25. * disclaimer in the documentation and/or other materials
  26. * provided with the distribution.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
  29. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  30. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  31. * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  32. * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  33. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  34. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  35. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  36. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  37. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  38. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  39. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  40. *
  41. * The views and conclusions contained in the software and documentation
  42. * are those of the authors and should not be interpreted as representing
  43. * official policies, either expressed or implied, of the Jim Tcl Project.
  44. **/
  45. #ifdef HAVE_CONFIG_H
  46. #include "config.h"
  47. #endif
  48. #define __JIM_CORE__
  49. #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
  50. #ifdef __ECOS
  51. #include <pkgconf/jimtcl.h>
  52. #include <stdio.h>
  53. #include <stdlib.h>
  54. typedef CYG_ADDRWORD intptr_t;
  55. #include <string.h>
  56. #include <stdarg.h>
  57. #include <ctype.h>
  58. #include <limits.h>
  59. #include <assert.h>
  60. #include <errno.h>
  61. #include <time.h>
  62. #endif
  63. #ifndef JIM_ANSIC
  64. #define JIM_DYNLIB /* Dynamic library support for UNIX and WIN32 */
  65. #endif /* JIM_ANSIC */
  66. #include <stdarg.h>
  67. #include <limits.h>
  68. /* Include the platform dependent libraries for
  69. * dynamic loading of libraries. */
  70. #ifdef JIM_DYNLIB
  71. #if defined(_WIN32) || defined(WIN32)
  72. #ifndef WIN32
  73. #define WIN32 1
  74. #endif
  75. #ifndef STRICT
  76. #define STRICT
  77. #endif
  78. #define WIN32_LEAN_AND_MEAN
  79. #include <windows.h>
  80. #if _MSC_VER >= 1000
  81. #pragma warning(disable:4146)
  82. #endif /* _MSC_VER */
  83. #else
  84. #include <dlfcn.h>
  85. #endif /* WIN32 */
  86. #endif /* JIM_DYNLIB */
  87. #ifdef __ECOS
  88. #include <cyg/jimtcl/jim.h>
  89. #else
  90. #include "jim.h"
  91. #endif
  92. #ifdef HAVE_BACKTRACE
  93. #include <execinfo.h>
  94. #endif
  95. /* -----------------------------------------------------------------------------
  96. * Global variables
  97. * ---------------------------------------------------------------------------*/
  98. /* A shared empty string for the objects string representation.
  99. * Jim_InvalidateStringRep knows about it and don't try to free. */
  100. static char *JimEmptyStringRep = (char*) "";
  101. /* -----------------------------------------------------------------------------
  102. * Required prototypes of not exported functions
  103. * ---------------------------------------------------------------------------*/
  104. static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf);
  105. static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags);
  106. static void JimRegisterCoreApi(Jim_Interp *interp);
  107. static Jim_HashTableType *getJimVariablesHashTableType(void);
  108. /* -----------------------------------------------------------------------------
  109. * Utility functions
  110. * ---------------------------------------------------------------------------*/
  111. static char *
  112. jim_vasprintf(const char *fmt, va_list ap)
  113. {
  114. #ifndef HAVE_VASPRINTF
  115. /* yucky way */
  116. static char buf[2048];
  117. vsnprintf(buf, sizeof(buf), fmt, ap);
  118. /* garentee termination */
  119. buf[sizeof(buf)-1] = 0;
  120. #else
  121. char *buf;
  122. int result;
  123. result = vasprintf(&buf, fmt, ap);
  124. if (result < 0) exit(-1);
  125. #endif
  126. return buf;
  127. }
  128. static void
  129. jim_vasprintf_done(void *buf)
  130. {
  131. #ifndef HAVE_VASPRINTF
  132. (void)(buf);
  133. #else
  134. free(buf);
  135. #endif
  136. }
  137. /*
  138. * Convert a string to a jim_wide INTEGER.
  139. * This function originates from BSD.
  140. *
  141. * Ignores `locale' stuff. Assumes that the upper and lower case
  142. * alphabets and digits are each contiguous.
  143. */
  144. #ifdef HAVE_LONG_LONG_INT
  145. #define JimIsAscii(c) (((c) & ~0x7f) == 0)
  146. static jim_wide JimStrtoll(const char *nptr, char **endptr, register int base)
  147. {
  148. register const char *s;
  149. register unsigned jim_wide acc;
  150. register unsigned char c;
  151. register unsigned jim_wide qbase, cutoff;
  152. register int neg, any, cutlim;
  153. /*
  154. * Skip white space and pick up leading +/- sign if any.
  155. * If base is 0, allow 0x for hex and 0 for octal, else
  156. * assume decimal; if base is already 16, allow 0x.
  157. */
  158. s = nptr;
  159. do {
  160. c = *s++;
  161. } while (isspace(c));
  162. if (c == '-') {
  163. neg = 1;
  164. c = *s++;
  165. } else {
  166. neg = 0;
  167. if (c == '+')
  168. c = *s++;
  169. }
  170. if ((base == 0 || base == 16) &&
  171. c == '0' && (*s == 'x' || *s == 'X')) {
  172. c = s[1];
  173. s += 2;
  174. base = 16;
  175. }
  176. if (base == 0)
  177. base = c == '0' ? 8 : 10;
  178. /*
  179. * Compute the cutoff value between legal numbers and illegal
  180. * numbers. That is the largest legal value, divided by the
  181. * base. An input number that is greater than this value, if
  182. * followed by a legal input character, is too big. One that
  183. * is equal to this value may be valid or not; the limit
  184. * between valid and invalid numbers is then based on the last
  185. * digit. For instance, if the range for quads is
  186. * [-9223372036854775808..9223372036854775807] and the input base
  187. * is 10, cutoff will be set to 922337203685477580 and cutlim to
  188. * either 7 (neg == 0) or 8 (neg == 1), meaning that if we have
  189. * accumulated a value > 922337203685477580, or equal but the
  190. * next digit is > 7 (or 8), the number is too big, and we will
  191. * return a range error.
  192. *
  193. * Set any if any `digits' consumed; make it negative to indicate
  194. * overflow.
  195. */
  196. qbase = (unsigned)base;
  197. cutoff = neg ? (unsigned jim_wide)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
  198. : LLONG_MAX;
  199. cutlim = (int)(cutoff % qbase);
  200. cutoff /= qbase;
  201. for (acc = 0, any = 0;; c = *s++) {
  202. if (!JimIsAscii(c))
  203. break;
  204. if (isdigit(c))
  205. c -= '0';
  206. else if (isalpha(c))
  207. c -= isupper(c) ? 'A' - 10 : 'a' - 10;
  208. else
  209. break;
  210. if (c >= base)
  211. break;
  212. if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
  213. any = -1;
  214. else {
  215. any = 1;
  216. acc *= qbase;
  217. acc += c;
  218. }
  219. }
  220. if (any < 0) {
  221. acc = neg ? LLONG_MIN : LLONG_MAX;
  222. errno = ERANGE;
  223. } else if (neg)
  224. acc = -acc;
  225. if (endptr != 0)
  226. *endptr = (char *)(any ? s - 1 : nptr);
  227. return (acc);
  228. }
  229. #endif
  230. /* Glob-style pattern matching. */
  231. static int JimStringMatch(const char *pattern, int patternLen,
  232. const char *string, int stringLen, int nocase)
  233. {
  234. while (patternLen) {
  235. switch (pattern[0]) {
  236. case '*':
  237. while (pattern[1] == '*') {
  238. pattern++;
  239. patternLen--;
  240. }
  241. if (patternLen == 1)
  242. return 1; /* match */
  243. while (stringLen) {
  244. if (JimStringMatch(pattern + 1, patternLen-1,
  245. string, stringLen, nocase))
  246. return 1; /* match */
  247. string++;
  248. stringLen--;
  249. }
  250. return 0; /* no match */
  251. break;
  252. case '?':
  253. if (stringLen == 0)
  254. return 0; /* no match */
  255. string++;
  256. stringLen--;
  257. break;
  258. case '[':
  259. {
  260. int not, match;
  261. pattern++;
  262. patternLen--;
  263. not = pattern[0] == '^';
  264. if (not) {
  265. pattern++;
  266. patternLen--;
  267. }
  268. match = 0;
  269. while (1) {
  270. if (pattern[0] == '\\') {
  271. pattern++;
  272. patternLen--;
  273. if (pattern[0] == string[0])
  274. match = 1;
  275. } else if (pattern[0] == ']') {
  276. break;
  277. } else if (patternLen == 0) {
  278. pattern--;
  279. patternLen++;
  280. break;
  281. } else if (pattern[1] == '-' && patternLen >= 3) {
  282. int start = pattern[0];
  283. int end = pattern[2];
  284. int c = string[0];
  285. if (start > end) {
  286. int t = start;
  287. start = end;
  288. end = t;
  289. }
  290. if (nocase) {
  291. start = tolower(start);
  292. end = tolower(end);
  293. c = tolower(c);
  294. }
  295. pattern += 2;
  296. patternLen -= 2;
  297. if (c >= start && c <= end)
  298. match = 1;
  299. } else {
  300. if (!nocase) {
  301. if (pattern[0] == string[0])
  302. match = 1;
  303. } else {
  304. if (tolower((int)pattern[0]) == tolower((int)string[0]))
  305. match = 1;
  306. }
  307. }
  308. pattern++;
  309. patternLen--;
  310. }
  311. if (not)
  312. match = !match;
  313. if (!match)
  314. return 0; /* no match */
  315. string++;
  316. stringLen--;
  317. break;
  318. }
  319. case '\\':
  320. if (patternLen >= 2) {
  321. pattern++;
  322. patternLen--;
  323. }
  324. /* fall through */
  325. default:
  326. if (!nocase) {
  327. if (pattern[0] != string[0])
  328. return 0; /* no match */
  329. } else {
  330. if (tolower((int)pattern[0]) != tolower((int)string[0]))
  331. return 0; /* no match */
  332. }
  333. string++;
  334. stringLen--;
  335. break;
  336. }
  337. pattern++;
  338. patternLen--;
  339. if (stringLen == 0) {
  340. while (*pattern == '*') {
  341. pattern++;
  342. patternLen--;
  343. }
  344. break;
  345. }
  346. }
  347. if (patternLen == 0 && stringLen == 0)
  348. return 1;
  349. return 0;
  350. }
  351. int JimStringCompare(const char *s1, int l1, const char *s2, int l2,
  352. int nocase)
  353. {
  354. unsigned char *u1 = (unsigned char*) s1, *u2 = (unsigned char*) s2;
  355. if (nocase == 0) {
  356. while (l1 && l2) {
  357. if (*u1 != *u2)
  358. return (int)*u1-*u2;
  359. u1++; u2++; l1--; l2--;
  360. }
  361. if (!l1 && !l2) return 0;
  362. return l1-l2;
  363. } else {
  364. while (l1 && l2) {
  365. if (tolower((int)*u1) != tolower((int)*u2))
  366. return tolower((int)*u1)-tolower((int)*u2);
  367. u1++; u2++; l1--; l2--;
  368. }
  369. if (!l1 && !l2) return 0;
  370. return l1-l2;
  371. }
  372. }
  373. /* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
  374. * The index of the first occurrence of s1 in s2 is returned.
  375. * If s1 is not found inside s2, -1 is returned. */
  376. int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int index)
  377. {
  378. int i;
  379. if (!l1 || !l2 || l1 > l2) return -1;
  380. if (index < 0) index = 0;
  381. s2 += index;
  382. for (i = index; i <= l2-l1; i++) {
  383. if (memcmp(s2, s1, l1) == 0)
  384. return i;
  385. s2++;
  386. }
  387. return -1;
  388. }
  389. int Jim_WideToString(char *buf, jim_wide wideValue)
  390. {
  391. const char *fmt = "%" JIM_WIDE_MODIFIER;
  392. return sprintf(buf, fmt, wideValue);
  393. }
  394. int Jim_StringToWide(const char *str, jim_wide *widePtr, int base)
  395. {
  396. char *endptr;
  397. #ifdef HAVE_LONG_LONG_INT
  398. *widePtr = JimStrtoll(str, &endptr, base);
  399. #else
  400. *widePtr = strtol(str, &endptr, base);
  401. #endif
  402. if ((str[0] == '\0') || (str == endptr))
  403. return JIM_ERR;
  404. if (endptr[0] != '\0') {
  405. while (*endptr) {
  406. if (!isspace((int)*endptr))
  407. return JIM_ERR;
  408. endptr++;
  409. }
  410. }
  411. return JIM_OK;
  412. }
  413. int Jim_StringToIndex(const char *str, int *intPtr)
  414. {
  415. char *endptr;
  416. *intPtr = strtol(str, &endptr, 10);
  417. if ((str[0] == '\0') || (str == endptr))
  418. return JIM_ERR;
  419. if (endptr[0] != '\0') {
  420. while (*endptr) {
  421. if (!isspace((int)*endptr))
  422. return JIM_ERR;
  423. endptr++;
  424. }
  425. }
  426. return JIM_OK;
  427. }
  428. /* The string representation of references has two features in order
  429. * to make the GC faster. The first is that every reference starts
  430. * with a non common character '~', in order to make the string matching
  431. * fater. The second is that the reference string rep his 32 characters
  432. * in length, this allows to avoid to check every object with a string
  433. * repr < 32, and usually there are many of this objects. */
  434. #define JIM_REFERENCE_SPACE (35 + JIM_REFERENCE_TAGLEN)
  435. static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id)
  436. {
  437. const char *fmt = "<reference.<%s>.%020" JIM_WIDE_MODIFIER ">";
  438. sprintf(buf, fmt, refPtr->tag, id);
  439. return JIM_REFERENCE_SPACE;
  440. }
  441. int Jim_DoubleToString(char *buf, double doubleValue)
  442. {
  443. char *s;
  444. int len;
  445. len = sprintf(buf, "%.17g", doubleValue);
  446. s = buf;
  447. while (*s) {
  448. if (*s == '.') return len;
  449. s++;
  450. }
  451. /* Add a final ".0" if it's a number. But not
  452. * for NaN or InF */
  453. if (isdigit((int)buf[0])
  454. || ((buf[0] == '-' || buf[0] == '+')
  455. && isdigit((int)buf[1]))) {
  456. s[0] = '.';
  457. s[1] = '0';
  458. s[2] = '\0';
  459. return len + 2;
  460. }
  461. return len;
  462. }
  463. int Jim_StringToDouble(const char *str, double *doublePtr)
  464. {
  465. char *endptr;
  466. *doublePtr = strtod(str, &endptr);
  467. if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr))
  468. return JIM_ERR;
  469. return JIM_OK;
  470. }
  471. static jim_wide JimPowWide(jim_wide b, jim_wide e)
  472. {
  473. jim_wide i, res = 1;
  474. if ((b == 0 && e != 0) || (e < 0)) return 0;
  475. for (i = 0; i < e; i++) {res *= b;}
  476. return res;
  477. }
  478. /* -----------------------------------------------------------------------------
  479. * Special functions
  480. * ---------------------------------------------------------------------------*/
  481. /* Note that 'interp' may be NULL if not available in the
  482. * context of the panic. It's only useful to get the error
  483. * file descriptor, it will default to stderr otherwise. */
  484. void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
  485. {
  486. va_list ap;
  487. va_start(ap, fmt);
  488. /*
  489. * Send it here first.. Assuming STDIO still works
  490. */
  491. fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: ");
  492. vfprintf(stderr, fmt, ap);
  493. fprintf(stderr, JIM_NL JIM_NL);
  494. va_end(ap);
  495. #ifdef HAVE_BACKTRACE
  496. {
  497. void *array[40];
  498. int size, i;
  499. char **strings;
  500. size = backtrace(array, 40);
  501. strings = backtrace_symbols(array, size);
  502. for (i = 0; i < size; i++)
  503. fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
  504. fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
  505. fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
  506. }
  507. #endif
  508. /* This may actually crash... we do it last */
  509. if (interp && interp->cookie_stderr) {
  510. Jim_fprintf(interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: ");
  511. Jim_vfprintf(interp, interp->cookie_stderr, fmt, ap);
  512. Jim_fprintf(interp, interp->cookie_stderr, JIM_NL JIM_NL);
  513. }
  514. abort();
  515. }
  516. /* -----------------------------------------------------------------------------
  517. * Memory allocation
  518. * ---------------------------------------------------------------------------*/
  519. /* Macro used for memory debugging.
  520. * In order for they to work you have to rename Jim_Alloc into _Jim_Alloc
  521. * and similary for Jim_Realloc and Jim_Free */
  522. #if 0
  523. #define Jim_Alloc(s) (printf("%s %d: Jim_Alloc(%d)\n",__FILE__,__LINE__,s),_Jim_Alloc(s))
  524. #define Jim_Free(p) (printf("%s %d: Jim_Free(%p)\n",__FILE__,__LINE__,p),_Jim_Free(p))
  525. #define Jim_Realloc(p,s) (printf("%s %d: Jim_Realloc(%p,%d)\n",__FILE__,__LINE__,p,s),_Jim_Realloc(p,s))
  526. #endif
  527. void *Jim_Alloc(int size)
  528. {
  529. /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
  530. if (size == 0)
  531. size = 1;
  532. void *p = malloc(size);
  533. if (p == NULL)
  534. Jim_Panic(NULL,"malloc: Out of memory");
  535. return p;
  536. }
  537. void Jim_Free(void *ptr) {
  538. free(ptr);
  539. }
  540. void *Jim_Realloc(void *ptr, int size)
  541. {
  542. /* We allocate zero length arrayes, etc. to use a single orthogonal codepath */
  543. if (size == 0)
  544. size = 1;
  545. void *p = realloc(ptr, size);
  546. if (p == NULL)
  547. Jim_Panic(NULL,"realloc: Out of memory");
  548. return p;
  549. }
  550. char *Jim_StrDup(const char *s)
  551. {
  552. int l = strlen(s);
  553. char *copy = Jim_Alloc(l + 1);
  554. memcpy(copy, s, l + 1);
  555. return copy;
  556. }
  557. char *Jim_StrDupLen(const char *s, int l)
  558. {
  559. char *copy = Jim_Alloc(l + 1);
  560. memcpy(copy, s, l + 1);
  561. copy[l] = 0; /* Just to be sure, original could be substring */
  562. return copy;
  563. }
  564. /* -----------------------------------------------------------------------------
  565. * Time related functions
  566. * ---------------------------------------------------------------------------*/
  567. /* Returns microseconds of CPU used since start. */
  568. static jim_wide JimClock(void)
  569. {
  570. #if (defined WIN32) && !(defined JIM_ANSIC)
  571. LARGE_INTEGER t, f;
  572. QueryPerformanceFrequency(&f);
  573. QueryPerformanceCounter(&t);
  574. return (long)((t.QuadPart * 1000000) / f.QuadPart);
  575. #else /* !WIN32 */
  576. clock_t clocks = clock();
  577. return (long)(clocks*(1000000/CLOCKS_PER_SEC));
  578. #endif /* WIN32 */
  579. }
  580. /* -----------------------------------------------------------------------------
  581. * Hash Tables
  582. * ---------------------------------------------------------------------------*/
  583. /* -------------------------- private prototypes ---------------------------- */
  584. static int JimExpandHashTableIfNeeded(Jim_HashTable *ht);
  585. static unsigned int JimHashTableNextPower(unsigned int size);
  586. static int JimInsertHashEntry(Jim_HashTable *ht, const void *key);
  587. /* -------------------------- hash functions -------------------------------- */
  588. /* Thomas Wang's 32 bit Mix Function */
  589. unsigned int Jim_IntHashFunction(unsigned int key)
  590. {
  591. key += ~(key << 15);
  592. key ^= (key >> 10);
  593. key += (key << 3);
  594. key ^= (key >> 6);
  595. key += ~(key << 11);
  596. key ^= (key >> 16);
  597. return key;
  598. }
  599. /* Identity hash function for integer keys */
  600. unsigned int Jim_IdentityHashFunction(unsigned int key)
  601. {
  602. return key;
  603. }
  604. /* Generic hash function (we are using to multiply by 9 and add the byte
  605. * as Tcl) */
  606. unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
  607. {
  608. unsigned int h = 0;
  609. while (len--)
  610. h += (h << 3)+*buf++;
  611. return h;
  612. }
  613. /* ----------------------------- API implementation ------------------------- */
  614. /* reset an hashtable already initialized with ht_init().
  615. * NOTE: This function should only called by ht_destroy(). */
  616. static void JimResetHashTable(Jim_HashTable *ht)
  617. {
  618. ht->table = NULL;
  619. ht->size = 0;
  620. ht->sizemask = 0;
  621. ht->used = 0;
  622. ht->collisions = 0;
  623. }
  624. /* Initialize the hash table */
  625. int Jim_InitHashTable(Jim_HashTable *ht, Jim_HashTableType *type,
  626. void *privDataPtr)
  627. {
  628. JimResetHashTable(ht);
  629. ht->type = type;
  630. ht->privdata = privDataPtr;
  631. return JIM_OK;
  632. }
  633. /* Resize the table to the minimal size that contains all the elements,
  634. * but with the invariant of a USER/BUCKETS ration near to <= 1 */
  635. int Jim_ResizeHashTable(Jim_HashTable *ht)
  636. {
  637. int minimal = ht->used;
  638. if (minimal < JIM_HT_INITIAL_SIZE)
  639. minimal = JIM_HT_INITIAL_SIZE;
  640. return Jim_ExpandHashTable(ht, minimal);
  641. }
  642. /* Expand or create the hashtable */
  643. int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
  644. {
  645. Jim_HashTable n; /* the new hashtable */
  646. unsigned int realsize = JimHashTableNextPower(size), i;
  647. /* the size is invalid if it is smaller than the number of
  648. * elements already inside the hashtable */
  649. if (ht->used >= size)
  650. return JIM_ERR;
  651. Jim_InitHashTable(&n, ht->type, ht->privdata);
  652. n.size = realsize;
  653. n.sizemask = realsize-1;
  654. n.table = Jim_Alloc(realsize*sizeof(Jim_HashEntry*));
  655. /* Initialize all the pointers to NULL */
  656. memset(n.table, 0, realsize*sizeof(Jim_HashEntry*));
  657. /* Copy all the elements from the old to the new table:
  658. * note that if the old hash table is empty ht->size is zero,
  659. * so Jim_ExpandHashTable just creates an hash table. */
  660. n.used = ht->used;
  661. for (i = 0; i < ht->size && ht->used > 0; i++) {
  662. Jim_HashEntry *he, *nextHe;
  663. if (ht->table[i] == NULL) continue;
  664. /* For each hash entry on this slot... */
  665. he = ht->table[i];
  666. while (he) {
  667. unsigned int h;
  668. nextHe = he->next;
  669. /* Get the new element index */
  670. h = Jim_HashKey(ht, he->key) & n.sizemask;
  671. he->next = n.table[h];
  672. n.table[h] = he;
  673. ht->used--;
  674. /* Pass to the next element */
  675. he = nextHe;
  676. }
  677. }
  678. assert(ht->used == 0);
  679. Jim_Free(ht->table);
  680. /* Remap the new hashtable in the old */
  681. *ht = n;
  682. return JIM_OK;
  683. }
  684. /* Add an element to the target hash table */
  685. int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
  686. {
  687. int index;
  688. Jim_HashEntry *entry;
  689. /* Get the index of the new element, or -1 if
  690. * the element already exists. */
  691. if ((index = JimInsertHashEntry(ht, key)) == -1)
  692. return JIM_ERR;
  693. /* Allocates the memory and stores key */
  694. entry = Jim_Alloc(sizeof(*entry));
  695. entry->next = ht->table[index];
  696. ht->table[index] = entry;
  697. /* Set the hash entry fields. */
  698. Jim_SetHashKey(ht, entry, key);
  699. Jim_SetHashVal(ht, entry, val);
  700. ht->used++;
  701. return JIM_OK;
  702. }
  703. /* Add an element, discarding the old if the key already exists */
  704. int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
  705. {
  706. Jim_HashEntry *entry;
  707. /* Try to add the element. If the key
  708. * does not exists Jim_AddHashEntry will suceed. */
  709. if (Jim_AddHashEntry(ht, key, val) == JIM_OK)
  710. return JIM_OK;
  711. /* It already exists, get the entry */
  712. entry = Jim_FindHashEntry(ht, key);
  713. /* Free the old value and set the new one */
  714. Jim_FreeEntryVal(ht, entry);
  715. Jim_SetHashVal(ht, entry, val);
  716. return JIM_OK;
  717. }
  718. /* Search and remove an element */
  719. int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
  720. {
  721. unsigned int h;
  722. Jim_HashEntry *he, *prevHe;
  723. if (ht->size == 0)
  724. return JIM_ERR;
  725. h = Jim_HashKey(ht, key) & ht->sizemask;
  726. he = ht->table[h];
  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. /* Destroy an entire hash table */
  747. int Jim_FreeHashTable(Jim_HashTable *ht)
  748. {
  749. unsigned int i;
  750. /* Free all the elements */
  751. for (i = 0; i < ht->size && ht->used > 0; i++) {
  752. Jim_HashEntry *he, *nextHe;
  753. if ((he = ht->table[i]) == NULL) continue;
  754. while (he) {
  755. nextHe = he->next;
  756. Jim_FreeEntryKey(ht, he);
  757. Jim_FreeEntryVal(ht, he);
  758. Jim_Free(he);
  759. ht->used--;
  760. he = nextHe;
  761. }
  762. }
  763. /* Free the table and the allocated cache structure */
  764. Jim_Free(ht->table);
  765. /* Re-initialize the table */
  766. JimResetHashTable(ht);
  767. return JIM_OK; /* never fails */
  768. }
  769. Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
  770. {
  771. Jim_HashEntry *he;
  772. unsigned int h;
  773. if (ht->size == 0) return NULL;
  774. h = Jim_HashKey(ht, key) & ht->sizemask;
  775. he = ht->table[h];
  776. while (he) {
  777. if (Jim_CompareHashKeys(ht, key, he->key))
  778. return he;
  779. he = he->next;
  780. }
  781. return NULL;
  782. }
  783. Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
  784. {
  785. Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
  786. iter->ht = ht;
  787. iter->index = -1;
  788. iter->entry = NULL;
  789. iter->nextEntry = NULL;
  790. return iter;
  791. }
  792. Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
  793. {
  794. while (1) {
  795. if (iter->entry == NULL) {
  796. iter->index++;
  797. if (iter->index >=
  798. (signed)iter->ht->size) break;
  799. iter->entry = iter->ht->table[iter->index];
  800. } else {
  801. iter->entry = iter->nextEntry;
  802. }
  803. if (iter->entry) {
  804. /* We need to save the 'next' here, the iterator user
  805. * may delete the entry we are returning. */
  806. iter->nextEntry = iter->entry->next;
  807. return iter->entry;
  808. }
  809. }
  810. return NULL;
  811. }
  812. /* ------------------------- private functions ------------------------------ */
  813. /* Expand the hash table if needed */
  814. static int JimExpandHashTableIfNeeded(Jim_HashTable *ht)
  815. {
  816. /* If the hash table is empty expand it to the intial size,
  817. * if the table is "full" dobule its size. */
  818. if (ht->size == 0)
  819. return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
  820. if (ht->size == ht->used)
  821. return Jim_ExpandHashTable(ht, ht->size*2);
  822. return JIM_OK;
  823. }
  824. /* Our hash table capability is a power of two */
  825. static unsigned int JimHashTableNextPower(unsigned int size)
  826. {
  827. unsigned int i = JIM_HT_INITIAL_SIZE;
  828. if (size >= 2147483648U)
  829. return 2147483648U;
  830. while (1) {
  831. if (i >= size)
  832. return i;
  833. i *= 2;
  834. }
  835. }
  836. /* Returns the index of a free slot that can be populated with
  837. * an hash entry for the given 'key'.
  838. * If the key already exists, -1 is returned. */
  839. static int JimInsertHashEntry(Jim_HashTable *ht, const void *key)
  840. {
  841. unsigned int h;
  842. Jim_HashEntry *he;
  843. /* Expand the hashtable if needed */
  844. if (JimExpandHashTableIfNeeded(ht) == JIM_ERR)
  845. return -1;
  846. /* Compute the key hash value */
  847. h = Jim_HashKey(ht, key) & ht->sizemask;
  848. /* Search if this slot does not already contain the given key */
  849. he = ht->table[h];
  850. while (he) {
  851. if (Jim_CompareHashKeys(ht, key, he->key))
  852. return -1;
  853. he = he->next;
  854. }
  855. return h;
  856. }
  857. /* ----------------------- StringCopy Hash Table Type ------------------------*/
  858. static unsigned int JimStringCopyHTHashFunction(const void *key)
  859. {
  860. return Jim_GenHashFunction(key, strlen(key));
  861. }
  862. static const void *JimStringCopyHTKeyDup(void *privdata, const void *key)
  863. {
  864. int len = strlen(key);
  865. char *copy = Jim_Alloc(len + 1);
  866. JIM_NOTUSED(privdata);
  867. memcpy(copy, key, len);
  868. copy[len] = '\0';
  869. return copy;
  870. }
  871. static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val)
  872. {
  873. int len = strlen(val);
  874. char *copy = Jim_Alloc(len + 1);
  875. JIM_NOTUSED(privdata);
  876. memcpy(copy, val, len);
  877. copy[len] = '\0';
  878. return copy;
  879. }
  880. static int JimStringCopyHTKeyCompare(void *privdata, const void *key1,
  881. const void *key2)
  882. {
  883. JIM_NOTUSED(privdata);
  884. return strcmp(key1, key2) == 0;
  885. }
  886. static void JimStringCopyHTKeyDestructor(void *privdata, const void *key)
  887. {
  888. JIM_NOTUSED(privdata);
  889. Jim_Free((void*)key); /* ATTENTION: const cast */
  890. }
  891. static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val)
  892. {
  893. JIM_NOTUSED(privdata);
  894. Jim_Free((void*)val); /* ATTENTION: const cast */
  895. }
  896. static Jim_HashTableType JimStringCopyHashTableType = {
  897. JimStringCopyHTHashFunction, /* hash function */
  898. JimStringCopyHTKeyDup, /* key dup */
  899. NULL, /* val dup */
  900. JimStringCopyHTKeyCompare, /* key compare */
  901. JimStringCopyHTKeyDestructor, /* key destructor */
  902. NULL /* val destructor */
  903. };
  904. /* This is like StringCopy but does not auto-duplicate the key.
  905. * It's used for intepreter's shared strings. */
  906. static Jim_HashTableType JimSharedStringsHashTableType = {
  907. JimStringCopyHTHashFunction, /* hash function */
  908. NULL, /* key dup */
  909. NULL, /* val dup */
  910. JimStringCopyHTKeyCompare, /* key compare */
  911. JimStringCopyHTKeyDestructor, /* key destructor */
  912. NULL /* val destructor */
  913. };
  914. /* This is like StringCopy but also automatically handle dynamic
  915. * allocated C strings as values. */
  916. static Jim_HashTableType JimStringKeyValCopyHashTableType = {
  917. JimStringCopyHTHashFunction, /* hash function */
  918. JimStringCopyHTKeyDup, /* key dup */
  919. JimStringKeyValCopyHTValDup, /* val dup */
  920. JimStringCopyHTKeyCompare, /* key compare */
  921. JimStringCopyHTKeyDestructor, /* key destructor */
  922. JimStringKeyValCopyHTValDestructor, /* val destructor */
  923. };
  924. typedef struct AssocDataValue {
  925. Jim_InterpDeleteProc *delProc;
  926. void *data;
  927. } AssocDataValue;
  928. static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
  929. {
  930. AssocDataValue *assocPtr = (AssocDataValue *)data;
  931. if (assocPtr->delProc != NULL)
  932. assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
  933. Jim_Free(data);
  934. }
  935. static Jim_HashTableType JimAssocDataHashTableType = {
  936. JimStringCopyHTHashFunction, /* hash function */
  937. JimStringCopyHTKeyDup, /* key dup */
  938. NULL, /* val dup */
  939. JimStringCopyHTKeyCompare, /* key compare */
  940. JimStringCopyHTKeyDestructor, /* key destructor */
  941. JimAssocDataHashTableValueDestructor /* val destructor */
  942. };
  943. /* -----------------------------------------------------------------------------
  944. * Stack - This is a simple generic stack implementation. It is used for
  945. * example in the 'expr' expression compiler.
  946. * ---------------------------------------------------------------------------*/
  947. void Jim_InitStack(Jim_Stack *stack)
  948. {
  949. stack->len = 0;
  950. stack->maxlen = 0;
  951. stack->vector = NULL;
  952. }
  953. void Jim_FreeStack(Jim_Stack *stack)
  954. {
  955. Jim_Free(stack->vector);
  956. }
  957. int Jim_StackLen(Jim_Stack *stack)
  958. {
  959. return stack->len;
  960. }
  961. void Jim_StackPush(Jim_Stack *stack, void *element) {
  962. int neededLen = stack->len + 1;
  963. if (neededLen > stack->maxlen) {
  964. stack->maxlen = neededLen*2;
  965. stack->vector = Jim_Realloc(stack->vector, sizeof(void*)*stack->maxlen);
  966. }
  967. stack->vector[stack->len] = element;
  968. stack->len++;
  969. }
  970. void *Jim_StackPop(Jim_Stack *stack)
  971. {
  972. if (stack->len == 0) return NULL;
  973. stack->len--;
  974. return stack->vector[stack->len];
  975. }
  976. void *Jim_StackPeek(Jim_Stack *stack)
  977. {
  978. if (stack->len == 0) return NULL;
  979. return stack->vector[stack->len-1];
  980. }
  981. void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr))
  982. {
  983. int i;
  984. for (i = 0; i < stack->len; i++)
  985. freeFunc(stack->vector[i]);
  986. }
  987. /* -----------------------------------------------------------------------------
  988. * Parser
  989. * ---------------------------------------------------------------------------*/
  990. /* Token types */
  991. #define JIM_TT_NONE -1 /* No token returned */
  992. #define JIM_TT_STR 0 /* simple string */
  993. #define JIM_TT_ESC 1 /* string that needs escape chars conversion */
  994. #define JIM_TT_VAR 2 /* var substitution */
  995. #define JIM_TT_DICTSUGAR 3 /* Syntax sugar for [dict get], $foo(bar) */
  996. #define JIM_TT_CMD 4 /* command substitution */
  997. #define JIM_TT_SEP 5 /* word separator */
  998. #define JIM_TT_EOL 6 /* line separator */
  999. /* Additional token types needed for expressions */
  1000. #define JIM_TT_SUBEXPR_START 7
  1001. #define JIM_TT_SUBEXPR_END 8
  1002. #define JIM_TT_EXPR_NUMBER 9
  1003. #define JIM_TT_EXPR_OPERATOR 10
  1004. /* Parser states */
  1005. #define JIM_PS_DEF 0 /* Default state */
  1006. #define JIM_PS_QUOTE 1 /* Inside "" */
  1007. /* Parser context structure. The same context is used both to parse
  1008. * Tcl scripts and lists. */
  1009. struct JimParserCtx {
  1010. const char *prg; /* Program text */
  1011. const char *p; /* Pointer to the point of the program we are parsing */
  1012. int len; /* Left length of 'prg' */
  1013. int linenr; /* Current line number */
  1014. const char *tstart;
  1015. const char *tend; /* Returned token is at tstart-tend in 'prg'. */
  1016. int tline; /* Line number of the returned token */
  1017. int tt; /* Token type */
  1018. int eof; /* Non zero if EOF condition is true. */
  1019. int state; /* Parser state */
  1020. int comment; /* Non zero if the next chars may be a comment. */
  1021. };
  1022. #define JimParserEof(c) ((c)->eof)
  1023. #define JimParserTstart(c) ((c)->tstart)
  1024. #define JimParserTend(c) ((c)->tend)
  1025. #define JimParserTtype(c) ((c)->tt)
  1026. #define JimParserTline(c) ((c)->tline)
  1027. static int JimParseScript(struct JimParserCtx *pc);
  1028. static int JimParseSep(struct JimParserCtx *pc);
  1029. static int JimParseEol(struct JimParserCtx *pc);
  1030. static int JimParseCmd(struct JimParserCtx *pc);
  1031. static int JimParseVar(struct JimParserCtx *pc);
  1032. static int JimParseBrace(struct JimParserCtx *pc);
  1033. static int JimParseStr(struct JimParserCtx *pc);
  1034. static int JimParseComment(struct JimParserCtx *pc);
  1035. static char *JimParserGetToken(struct JimParserCtx *pc,
  1036. int *lenPtr, int *typePtr, int *linePtr);
  1037. /* Initialize a parser context.
  1038. * 'prg' is a pointer to the program text, linenr is the line
  1039. * number of the first line contained in the program. */
  1040. void JimParserInit(struct JimParserCtx *pc, const char *prg,
  1041. int len, int linenr)
  1042. {
  1043. pc->prg = prg;
  1044. pc->p = prg;
  1045. pc->len = len;
  1046. pc->tstart = NULL;
  1047. pc->tend = NULL;
  1048. pc->tline = 0;
  1049. pc->tt = JIM_TT_NONE;
  1050. pc->eof = 0;
  1051. pc->state = JIM_PS_DEF;
  1052. pc->linenr = linenr;
  1053. pc->comment = 1;
  1054. }
  1055. int JimParseScript(struct JimParserCtx *pc)
  1056. {
  1057. while (1) { /* the while is used to reiterate with continue if needed */
  1058. if (!pc->len) {
  1059. pc->tstart = pc->p;
  1060. pc->tend = pc->p-1;
  1061. pc->tline = pc->linenr;
  1062. pc->tt = JIM_TT_EOL;
  1063. pc->eof = 1;
  1064. return JIM_OK;
  1065. }
  1066. switch (*(pc->p)) {
  1067. case '\\':
  1068. if (*(pc->p + 1) == '\n')
  1069. return JimParseSep(pc);
  1070. else {
  1071. pc->comment = 0;
  1072. return JimParseStr(pc);
  1073. }
  1074. break;
  1075. case ' ':
  1076. case '\t':
  1077. case '\r':
  1078. if (pc->state == JIM_PS_DEF)
  1079. return JimParseSep(pc);
  1080. else {
  1081. pc->comment = 0;
  1082. return JimParseStr(pc);
  1083. }
  1084. break;
  1085. case '\n':
  1086. case ';':
  1087. pc->comment = 1;
  1088. if (pc->state == JIM_PS_DEF)
  1089. return JimParseEol(pc);
  1090. else
  1091. return JimParseStr(pc);
  1092. break;
  1093. case '[':
  1094. pc->comment = 0;
  1095. return JimParseCmd(pc);
  1096. break;
  1097. case '$':
  1098. pc->comment = 0;
  1099. if (JimParseVar(pc) == JIM_ERR) {
  1100. pc->tstart = pc->tend = pc->p++; pc->len--;
  1101. pc->tline = pc->linenr;
  1102. pc->tt = JIM_TT_STR;
  1103. return JIM_OK;
  1104. } else
  1105. return JIM_OK;
  1106. break;
  1107. case '#':
  1108. if (pc->comment) {
  1109. JimParseComment(pc);
  1110. continue;
  1111. } else {
  1112. return JimParseStr(pc);
  1113. }
  1114. default:
  1115. pc->comment = 0;
  1116. return JimParseStr(pc);
  1117. break;
  1118. }
  1119. return JIM_OK;
  1120. }
  1121. }
  1122. int JimParseSep(struct JimParserCtx *pc)
  1123. {
  1124. pc->tstart = pc->p;
  1125. pc->tline = pc->linenr;
  1126. while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' ||
  1127. (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
  1128. if (*pc->p == '\\') {
  1129. pc->p++; pc->len--;
  1130. pc->linenr++;
  1131. }
  1132. pc->p++; pc->len--;
  1133. }
  1134. pc->tend = pc->p-1;
  1135. pc->tt = JIM_TT_SEP;
  1136. return JIM_OK;
  1137. }
  1138. int JimParseEol(struct JimParserCtx *pc)
  1139. {
  1140. pc->tstart = pc->p;
  1141. pc->tline = pc->linenr;
  1142. while (*pc->p == ' ' || *pc->p == '\n' ||
  1143. *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') {
  1144. if (*pc->p == '\n')
  1145. pc->linenr++;
  1146. pc->p++; pc->len--;
  1147. }
  1148. pc->tend = pc->p-1;
  1149. pc->tt = JIM_TT_EOL;
  1150. return JIM_OK;
  1151. }
  1152. /* Todo. Don't stop if ']' appears inside {} or quoted.
  1153. * Also should handle the case of puts [string length "]"] */
  1154. int JimParseCmd(struct JimParserCtx *pc)
  1155. {
  1156. int level = 1;
  1157. int blevel = 0;
  1158. pc->tstart = ++pc->p; pc->len--;
  1159. pc->tline = pc->linenr;
  1160. while (1) {
  1161. if (pc->len == 0) {
  1162. break;
  1163. } else if (*pc->p == '[' && blevel == 0) {
  1164. level++;
  1165. } else if (*pc->p == ']' && blevel == 0) {
  1166. level--;
  1167. if (!level) break;
  1168. } else if (*pc->p == '\\') {
  1169. pc->p++; pc->len--;
  1170. } else if (*pc->p == '{') {
  1171. blevel++;
  1172. } else if (*pc->p == '}') {
  1173. if (blevel != 0)
  1174. blevel--;
  1175. } else if (*pc->p == '\n')
  1176. pc->linenr++;
  1177. pc->p++; pc->len--;
  1178. }
  1179. pc->tend = pc->p-1;
  1180. pc->tt = JIM_TT_CMD;
  1181. if (*pc->p == ']') {
  1182. pc->p++; pc->len--;
  1183. }
  1184. return JIM_OK;
  1185. }
  1186. int JimParseVar(struct JimParserCtx *pc)
  1187. {
  1188. int brace = 0, stop = 0, ttype = JIM_TT_VAR;
  1189. pc->tstart = ++pc->p; pc->len--; /* skip the $ */
  1190. pc->tline = pc->linenr;
  1191. if (*pc->p == '{') {
  1192. pc->tstart = ++pc->p; pc->len--;
  1193. brace = 1;
  1194. }
  1195. if (brace) {
  1196. while (!stop) {
  1197. if (*pc->p == '}' || pc->len == 0) {
  1198. pc->tend = pc->p-1;
  1199. stop = 1;
  1200. if (pc->len == 0)
  1201. break;
  1202. }
  1203. else if (*pc->p == '\n')
  1204. pc->linenr++;
  1205. pc->p++; pc->len--;
  1206. }
  1207. } else {
  1208. /* Include leading colons */
  1209. while (*pc->p == ':') {
  1210. pc->p++;
  1211. pc->len--;
  1212. }
  1213. while (!stop) {
  1214. if (!((*pc->p >= 'a' && *pc->p <= 'z') ||
  1215. (*pc->p >= 'A' && *pc->p <= 'Z') ||
  1216. (*pc->p >= '0' && *pc->p <= '9') || *pc->p == '_'))
  1217. stop = 1;
  1218. else {
  1219. pc->p++; pc->len--;
  1220. }
  1221. }
  1222. /* Parse [dict get] syntax sugar. */
  1223. if (*pc->p == '(') {
  1224. while (*pc->p != ')' && pc->len) {
  1225. pc->p++; pc->len--;
  1226. if (*pc->p == '\\' && pc->len >= 2) {
  1227. pc->p += 2; pc->len -= 2;
  1228. }
  1229. }
  1230. if (*pc->p != '\0') {
  1231. pc->p++; pc->len--;
  1232. }
  1233. ttype = JIM_TT_DICTSUGAR;
  1234. }
  1235. pc->tend = pc->p-1;
  1236. }
  1237. /* Check if we parsed just the '$' character.
  1238. * That's not a variable so an error is returned
  1239. * to tell the state machine to consider this '$' just
  1240. * a string. */
  1241. if (pc->tstart == pc->p) {
  1242. pc->p--; pc->len++;
  1243. return JIM_ERR;
  1244. }
  1245. pc->tt = ttype;
  1246. return JIM_OK;
  1247. }
  1248. int JimParseBrace(struct JimParserCtx *pc)
  1249. {
  1250. int level = 1;
  1251. pc->tstart = ++pc->p; pc->len--;
  1252. pc->tline = pc->linenr;
  1253. while (1) {
  1254. if (*pc->p == '\\' && pc->len >= 2) {
  1255. pc->p++; pc->len--;
  1256. if (*pc->p == '\n')
  1257. pc->linenr++;
  1258. } else if (*pc->p == '{') {
  1259. level++;
  1260. } else if (pc->len == 0 || *pc->p == '}') {
  1261. level--;
  1262. if (pc->len == 0 || level == 0) {
  1263. pc->tend = pc->p-1;
  1264. if (pc->len != 0) {
  1265. pc->p++; pc->len--;
  1266. }
  1267. pc->tt = JIM_TT_STR;
  1268. return JIM_OK;
  1269. }
  1270. } else if (*pc->p == '\n') {
  1271. pc->linenr++;
  1272. }
  1273. pc->p++; pc->len--;
  1274. }
  1275. return JIM_OK; /* unreached */
  1276. }
  1277. int JimParseStr(struct JimParserCtx *pc)
  1278. {
  1279. int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
  1280. pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR);
  1281. if (newword && *pc->p == '{') {
  1282. return JimParseBrace(pc);
  1283. } else if (newword && *pc->p == '"') {
  1284. pc->state = JIM_PS_QUOTE;
  1285. pc->p++; pc->len--;
  1286. }
  1287. pc->tstart = pc->p;
  1288. pc->tline = pc->linenr;
  1289. while (1) {
  1290. if (pc->len == 0) {
  1291. pc->tend = pc->p-1;
  1292. pc->tt = JIM_TT_ESC;
  1293. return JIM_OK;
  1294. }
  1295. switch (*pc->p) {
  1296. case '\\':
  1297. if (pc->state == JIM_PS_DEF &&
  1298. *(pc->p + 1) == '\n') {
  1299. pc->tend = pc->p-1;
  1300. pc->tt = JIM_TT_ESC;
  1301. return JIM_OK;
  1302. }
  1303. if (pc->len >= 2) {
  1304. pc->p++; pc->len--;
  1305. }
  1306. break;
  1307. case '$':
  1308. case '[':
  1309. pc->tend = pc->p-1;
  1310. pc->tt = JIM_TT_ESC;
  1311. return JIM_OK;
  1312. case ' ':
  1313. case '\t':
  1314. case '\n':
  1315. case '\r':
  1316. case ';':
  1317. if (pc->state == JIM_PS_DEF) {
  1318. pc->tend = pc->p-1;
  1319. pc->tt = JIM_TT_ESC;
  1320. return JIM_OK;
  1321. } else if (*pc->p == '\n') {
  1322. pc->linenr++;
  1323. }
  1324. break;
  1325. case '"':
  1326. if (pc->state == JIM_PS_QUOTE) {
  1327. pc->tend = pc->p-1;
  1328. pc->tt = JIM_TT_ESC;
  1329. pc->p++; pc->len--;
  1330. pc->state = JIM_PS_DEF;
  1331. return JIM_OK;
  1332. }
  1333. break;
  1334. }
  1335. pc->p++; pc->len--;
  1336. }
  1337. return JIM_OK; /* unreached */
  1338. }
  1339. int JimParseComment(struct JimParserCtx *pc)
  1340. {
  1341. while (*pc->p) {
  1342. if (*pc->p == '\n') {
  1343. pc->linenr++;
  1344. if (*(pc->p-1) != '\\') {
  1345. pc->p++; pc->len--;
  1346. return JIM_OK;
  1347. }
  1348. }
  1349. pc->p++; pc->len--;
  1350. }
  1351. return JIM_OK;
  1352. }
  1353. /* xdigitval and odigitval are helper functions for JimParserGetToken() */
  1354. static int xdigitval(int c)
  1355. {
  1356. if (c >= '0' && c <= '9') return c-'0';
  1357. if (c >= 'a' && c <= 'f') return c-'a'+10;
  1358. if (c >= 'A' && c <= 'F') return c-'A'+10;
  1359. return -1;
  1360. }
  1361. static int odigitval(int c)
  1362. {
  1363. if (c >= '0' && c <= '7') return c-'0';
  1364. return -1;
  1365. }
  1366. /* Perform Tcl escape substitution of 's', storing the result
  1367. * string into 'dest'. The escaped string is guaranteed to
  1368. * be the same length or shorted than the source string.
  1369. * Slen is the length of the string at 's', if it's -1 the string
  1370. * length will be calculated by the function.
  1371. *
  1372. * The function returns the length of the resulting string. */
  1373. static int JimEscape(char *dest, const char *s, int slen)
  1374. {
  1375. char *p = dest;
  1376. int i, len;
  1377. if (slen == -1)
  1378. slen = strlen(s);
  1379. for (i = 0; i < slen; i++) {
  1380. switch (s[i]) {
  1381. case '\\':
  1382. switch (s[i + 1]) {
  1383. case 'a': *p++ = 0x7; i++; break;
  1384. case 'b': *p++ = 0x8; i++; break;
  1385. case 'f': *p++ = 0xc; i++; break;
  1386. case 'n': *p++ = 0xa; i++; break;
  1387. case 'r': *p++ = 0xd; i++; break;
  1388. case 't': *p++ = 0x9; i++; break;
  1389. case 'v': *p++ = 0xb; i++; break;
  1390. case '\0': *p++ = '\\'; i++; break;
  1391. case '\n': *p++ = ' '; i++; break;
  1392. default:
  1393. if (s[i + 1] == 'x') {
  1394. int val = 0;
  1395. int c = xdigitval(s[i + 2]);
  1396. if (c == -1) {
  1397. *p++ = 'x';
  1398. i++;
  1399. break;
  1400. }
  1401. val = c;
  1402. c = xdigitval(s[i + 3]);
  1403. if (c == -1) {
  1404. *p++ = val;
  1405. i += 2;
  1406. break;
  1407. }
  1408. val = (val*16) + c;
  1409. *p++ = val;
  1410. i += 3;
  1411. break;
  1412. } else if (s[i + 1] >= '0' && s[i + 1] <= '7')
  1413. {
  1414. int val = 0;
  1415. int c = odigitval(s[i + 1]);
  1416. val = c;
  1417. c = odigitval(s[i + 2]);
  1418. if (c == -1) {
  1419. *p++ = val;
  1420. i ++;
  1421. break;
  1422. }
  1423. val = (val*8) + c;
  1424. c = odigitval(s[i + 3]);
  1425. if (c == -1) {
  1426. *p++ = val;
  1427. i += 2;
  1428. break;
  1429. }
  1430. val = (val*8) + c;
  1431. *p++ = val;
  1432. i += 3;
  1433. } else {
  1434. *p++ = s[i + 1];
  1435. i++;
  1436. }
  1437. break;
  1438. }
  1439. break;
  1440. default:
  1441. *p++ = s[i];
  1442. break;
  1443. }
  1444. }
  1445. len = p-dest;
  1446. *p++ = '\0';
  1447. return len;
  1448. }
  1449. /* Returns a dynamically allocated copy of the current token in the
  1450. * parser context. The function perform conversion of escapes if
  1451. * the token is of type JIM_TT_ESC.
  1452. *
  1453. * Note that after the conversion, tokens that are grouped with
  1454. * braces in the source code, are always recognizable from the
  1455. * identical string obtained in a different way from the type.
  1456. *
  1457. * For exmple the string:
  1458. *
  1459. * {expand}$a
  1460. *
  1461. * will return as first token "expand", of type JIM_TT_STR
  1462. *
  1463. * While the string:
  1464. *
  1465. * expand$a
  1466. *
  1467. * will return as first token "expand", of type JIM_TT_ESC
  1468. */
  1469. char *JimParserGetToken(struct JimParserCtx *pc,
  1470. int *lenPtr, int *typePtr, int *linePtr)
  1471. {
  1472. const char *start, *end;
  1473. char *token;
  1474. int len;
  1475. start = JimParserTstart(pc);
  1476. end = JimParserTend(pc);
  1477. if (start > end) {
  1478. if (lenPtr) *lenPtr = 0;
  1479. if (typePtr) *typePtr = JimParserTtype(pc);
  1480. if (linePtr) *linePtr = JimParserTline(pc);
  1481. token = Jim_Alloc(1);
  1482. token[0] = '\0';
  1483. return token;
  1484. }
  1485. len = (end-start) + 1;
  1486. token = Jim_Alloc(len + 1);
  1487. if (JimParserTtype(pc) != JIM_TT_ESC) {
  1488. /* No escape conversion needed? Just copy it. */
  1489. memcpy(token, start, len);
  1490. token[len] = '\0';
  1491. } else {
  1492. /* Else convert the escape chars. */
  1493. len = JimEscape(token, start, len);
  1494. }
  1495. if (lenPtr) *lenPtr = len;
  1496. if (typePtr) *typePtr = JimParserTtype(pc);
  1497. if (linePtr) *linePtr = JimParserTline(pc);
  1498. return token;
  1499. }
  1500. /* The following functin is not really part of the parsing engine of Jim,
  1501. * but it somewhat related. Given an string and its length, it tries
  1502. * to guess if the script is complete or there are instead " " or { }
  1503. * open and not completed. This is useful for interactive shells
  1504. * implementation and for [info complete].
  1505. *
  1506. * If 'stateCharPtr' != NULL, the function stores ' ' on complete script,
  1507. * '{' on scripts incomplete missing one or more '}' to be balanced.
  1508. * '"' on scripts incomplete missing a '"' char.
  1509. *
  1510. * If the script is complete, 1 is returned, otherwise 0. */
  1511. int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
  1512. {
  1513. int level = 0;
  1514. int state = ' ';
  1515. while (len) {
  1516. switch (*s) {
  1517. case '\\':
  1518. if (len > 1)
  1519. s++;
  1520. break;
  1521. case '"':
  1522. if (state == ' ') {
  1523. state = '"';
  1524. } else if (state == '"') {
  1525. state = ' ';
  1526. }
  1527. break;
  1528. case '{':
  1529. if (state == '{') {
  1530. level++;
  1531. } else if (state == ' ') {
  1532. state = '{';
  1533. level++;
  1534. }
  1535. break;
  1536. case '}':
  1537. if (state == '{') {
  1538. level--;
  1539. if (level == 0)
  1540. state = ' ';
  1541. }
  1542. break;
  1543. }
  1544. s++;
  1545. len--;
  1546. }
  1547. if (stateCharPtr)
  1548. *stateCharPtr = state;
  1549. return state == ' ';
  1550. }
  1551. /* -----------------------------------------------------------------------------
  1552. * Tcl Lists parsing
  1553. * ---------------------------------------------------------------------------*/
  1554. static int JimParseListSep(struct JimParserCtx *pc);
  1555. static int JimParseListStr(struct JimParserCtx *pc);
  1556. int JimParseList(struct JimParserCtx *pc)
  1557. {
  1558. if (pc->len == 0) {
  1559. pc->tstart = pc->tend = pc->p;
  1560. pc->tline = pc->linenr;
  1561. pc->tt = JIM_TT_EOL;
  1562. pc->eof = 1;
  1563. return JIM_OK;
  1564. }
  1565. switch (*pc->p) {
  1566. case ' ':
  1567. case '\n':
  1568. case '\t':
  1569. case '\r':
  1570. if (pc->state == JIM_PS_DEF)
  1571. return JimParseListSep(pc);
  1572. else
  1573. return JimParseListStr(pc);
  1574. break;
  1575. default:
  1576. return JimParseListStr(pc);
  1577. break;
  1578. }
  1579. return JIM_OK;
  1580. }
  1581. int JimParseListSep(struct JimParserCtx *pc)
  1582. {
  1583. pc->tstart = pc->p;
  1584. pc->tline = pc->linenr;
  1585. while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n')
  1586. {
  1587. pc->p++; pc->len--;
  1588. }
  1589. pc->tend = pc->p-1;
  1590. pc->tt = JIM_TT_SEP;
  1591. return JIM_OK;
  1592. }
  1593. int JimParseListStr(struct JimParserCtx *pc)
  1594. {
  1595. int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
  1596. pc->tt == JIM_TT_NONE);
  1597. if (newword && *pc->p == '{') {
  1598. return JimParseBrace(pc);
  1599. } else if (newword && *pc->p == '"') {
  1600. pc->state = JIM_PS_QUOTE;
  1601. pc->p++; pc->len--;
  1602. }
  1603. pc->tstart = pc->p;
  1604. pc->tline = pc->linenr;
  1605. while (1) {
  1606. if (pc->len == 0) {
  1607. pc->tend = pc->p-1;
  1608. pc->tt = JIM_TT_ESC;
  1609. return JIM_OK;
  1610. }
  1611. switch (*pc->p) {
  1612. case '\\':
  1613. pc->p++; pc->len--;
  1614. break;
  1615. case ' ':
  1616. case '\t':
  1617. case '\n':
  1618. case '\r':
  1619. if (pc->state == JIM_PS_DEF) {
  1620. pc->tend = pc->p-1;
  1621. pc->tt = JIM_TT_ESC;
  1622. return JIM_OK;
  1623. } else if (*pc->p == '\n') {
  1624. pc->linenr++;
  1625. }
  1626. break;
  1627. case '"':
  1628. if (pc->state == JIM_PS_QUOTE) {
  1629. pc->tend = pc->p-1;
  1630. pc->tt = JIM_TT_ESC;
  1631. pc->p++; pc->len--;
  1632. pc->state = JIM_PS_DEF;
  1633. return JIM_OK;
  1634. }
  1635. break;
  1636. }
  1637. pc->p++; pc->len--;
  1638. }
  1639. return JIM_OK; /* unreached */
  1640. }
  1641. /* -----------------------------------------------------------------------------
  1642. * Jim_Obj related functions
  1643. * ---------------------------------------------------------------------------*/
  1644. /* Return a new initialized object. */
  1645. Jim_Obj *Jim_NewObj(Jim_Interp *interp)
  1646. {
  1647. Jim_Obj *objPtr;
  1648. /* -- Check if there are objects in the free list -- */
  1649. if (interp->freeList != NULL) {
  1650. /* -- Unlink the object from the free list -- */
  1651. objPtr = interp->freeList;
  1652. interp->freeList = objPtr->nextObjPtr;
  1653. } else {
  1654. /* -- No ready to use objects: allocate a new one -- */
  1655. objPtr = Jim_Alloc(sizeof(*objPtr));
  1656. }
  1657. /* Object is returned with refCount of 0. Every
  1658. * kind of GC implemented should take care to don't try
  1659. * to scan objects with refCount == 0. */
  1660. objPtr->refCount = 0;
  1661. /* All the other fields are left not initialized to save time.
  1662. * The caller will probably want set they to the right
  1663. * value anyway. */
  1664. /* -- Put the object into the live list -- */
  1665. objPtr->prevObjPtr = NULL;
  1666. objPtr->nextObjPtr = interp->liveList;
  1667. if (interp->liveList)
  1668. interp->liveList->prevObjPtr = objPtr;
  1669. interp->liveList = objPtr;
  1670. return objPtr;
  1671. }
  1672. /* Free an object. Actually objects are never freed, but
  1673. * just moved to the free objects list, where they will be
  1674. * reused by Jim_NewObj(). */
  1675. void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
  1676. {
  1677. /* Check if the object was already freed, panic. */
  1678. if (objPtr->refCount != 0) {
  1679. Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
  1680. objPtr->refCount);
  1681. }
  1682. /* Free the internal representation */
  1683. Jim_FreeIntRep(interp, objPtr);
  1684. /* Free the string representation */
  1685. if (objPtr->bytes != NULL) {
  1686. if (objPtr->bytes != JimEmptyStringRep)
  1687. Jim_Free(objPtr->bytes);
  1688. }
  1689. /* Unlink the object from the live objects list */
  1690. if (objPtr->prevObjPtr)
  1691. objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
  1692. if (objPtr->nextObjPtr)
  1693. objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
  1694. if (interp->liveList == objPtr)
  1695. interp->liveList = objPtr->nextObjPtr;
  1696. /* Link the object into the free objects list */
  1697. objPtr->prevObjPtr = NULL;
  1698. objPtr->nextObjPtr = interp->freeList;
  1699. if (interp->freeList)
  1700. interp->freeList->prevObjPtr = objPtr;
  1701. interp->freeList = objPtr;
  1702. objPtr->refCount = -1;
  1703. }
  1704. /* Invalidate the string representation of an object. */
  1705. void Jim_InvalidateStringRep(Jim_Obj *objPtr)
  1706. {
  1707. if (objPtr->bytes != NULL) {
  1708. if (objPtr->bytes != JimEmptyStringRep)
  1709. Jim_Free(objPtr->bytes);
  1710. }
  1711. objPtr->bytes = NULL;
  1712. }
  1713. #define Jim_SetStringRep(o, b, l) \
  1714. do { (o)->bytes = b; (o)->length = l; } while (0)
  1715. /* Set the initial string representation for an object.
  1716. * Does not try to free an old one. */
  1717. void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length)
  1718. {
  1719. if (length == 0) {
  1720. objPtr->bytes = JimEmptyStringRep;
  1721. objPtr->length = 0;
  1722. } else {
  1723. objPtr->bytes = Jim_Alloc(length + 1);
  1724. objPtr->length = length;
  1725. memcpy(objPtr->bytes, bytes, length);
  1726. objPtr->bytes[length] = '\0';
  1727. }
  1728. }
  1729. /* Duplicate an object. The returned object has refcount = 0. */
  1730. Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
  1731. {
  1732. Jim_Obj *dupPtr;
  1733. dupPtr = Jim_NewObj(interp);
  1734. if (objPtr->bytes == NULL) {
  1735. /* Object does not have a valid string representation. */
  1736. dupPtr->bytes = NULL;
  1737. } else {
  1738. Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length);
  1739. }
  1740. if (objPtr->typePtr != NULL) {
  1741. if (objPtr->typePtr->dupIntRepProc == NULL) {
  1742. dupPtr->internalRep = objPtr->internalRep;
  1743. } else {
  1744. objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
  1745. }
  1746. dupPtr->typePtr = objPtr->typePtr;
  1747. } else {
  1748. dupPtr->typePtr = NULL;
  1749. }
  1750. return dupPtr;
  1751. }
  1752. /* Return the string representation for objPtr. If the object
  1753. * string representation is invalid, calls the method to create
  1754. * a new one starting from the internal representation of the object. */
  1755. const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
  1756. {
  1757. if (objPtr->bytes == NULL) {
  1758. /* Invalid string repr. Generate it. */
  1759. if (objPtr->typePtr->updateStringProc == NULL) {
  1760. Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
  1761. objPtr->typePtr->name);
  1762. }
  1763. objPtr->typePtr->updateStringProc(objPtr);
  1764. }
  1765. if (lenPtr)
  1766. *lenPtr = objPtr->length;
  1767. return objPtr->bytes;
  1768. }
  1769. /* Just returns the length of the object's string rep */
  1770. int Jim_Length(Jim_Obj *objPtr)
  1771. {
  1772. int len;
  1773. Jim_GetString(objPtr, &len);
  1774. return len;
  1775. }
  1776. /* -----------------------------------------------------------------------------
  1777. * String Object
  1778. * ---------------------------------------------------------------------------*/
  1779. static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
  1780. static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  1781. static Jim_ObjType stringObjType = {
  1782. "string",
  1783. NULL,
  1784. DupStringInternalRep,
  1785. NULL,
  1786. JIM_TYPE_REFERENCES,
  1787. };
  1788. void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
  1789. {
  1790. JIM_NOTUSED(interp);
  1791. /* This is a bit subtle: the only caller of this function
  1792. * should be Jim_DuplicateObj(), that will copy the
  1793. * string representaion. After the copy, the duplicated
  1794. * object will not have more room in teh buffer than
  1795. * srcPtr->length bytes. So we just set it to length. */
  1796. dupPtr->internalRep.strValue.maxLength = srcPtr->length;
  1797. }
  1798. int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
  1799. {
  1800. /* Get a fresh string representation. */
  1801. (void) Jim_GetString(objPtr, NULL);
  1802. /* Free any other internal representation. */
  1803. Jim_FreeIntRep(interp, objPtr);
  1804. /* Set it as string, i.e. just set the maxLength field. */
  1805. objPtr->typePtr = &stringObjType;
  1806. objPtr->internalRep.strValue.maxLength = objPtr->length;
  1807. return JIM_OK;
  1808. }
  1809. Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
  1810. {
  1811. Jim_Obj *objPtr = Jim_NewObj(interp);
  1812. if (len == -1)
  1813. len = strlen(s);
  1814. /* Alloc/Set the string rep. */
  1815. if (len == 0) {
  1816. objPtr->bytes = JimEmptyStringRep;
  1817. objPtr->length = 0;
  1818. } else {
  1819. objPtr->bytes = Jim_Alloc(len + 1);
  1820. objPtr->length = len;
  1821. memcpy(objPtr->bytes, s, len);
  1822. objPtr->bytes[len] = '\0';
  1823. }
  1824. /* No typePtr field for the vanilla string object. */
  1825. objPtr->typePtr = NULL;
  1826. return objPtr;
  1827. }
  1828. /* This version does not try to duplicate the 's' pointer, but
  1829. * use it directly. */
  1830. Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
  1831. {
  1832. Jim_Obj *objPtr = Jim_NewObj(interp);
  1833. if (len == -1)
  1834. len = strlen(s);
  1835. Jim_SetStringRep(objPtr, s, len);
  1836. objPtr->typePtr = NULL;
  1837. return objPtr;
  1838. }
  1839. /* Low-level string append. Use it only against objects
  1840. * of type "string". */
  1841. void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
  1842. {
  1843. int needlen;
  1844. if (len == -1)
  1845. len = strlen(str);
  1846. needlen = objPtr->length + len;
  1847. if (objPtr->internalRep.strValue.maxLength < needlen ||
  1848. objPtr->internalRep.strValue.maxLength == 0) {
  1849. if (objPtr->bytes == JimEmptyStringRep) {
  1850. objPtr->bytes = Jim_Alloc((needlen*2) + 1);
  1851. } else {
  1852. objPtr->bytes = Jim_Realloc(objPtr->bytes, (needlen*2) + 1);
  1853. }
  1854. objPtr->internalRep.strValue.maxLength = needlen*2;
  1855. }
  1856. memcpy(objPtr->bytes + objPtr->length, str, len);
  1857. objPtr->bytes[objPtr->length + len] = '\0';
  1858. objPtr->length += len;
  1859. }
  1860. /* Low-level wrapper to append an object. */
  1861. void StringAppendObj(Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
  1862. {
  1863. int len;
  1864. const char *str;
  1865. str = Jim_GetString(appendObjPtr, &len);
  1866. StringAppendString(objPtr, str, len);
  1867. }
  1868. /* Higher level API to append strings to objects. */
  1869. void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
  1870. int len)
  1871. {
  1872. if (Jim_IsShared(objPtr))
  1873. Jim_Panic(interp,"Jim_AppendString called with shared object");
  1874. if (objPtr->typePtr != &stringObjType)
  1875. SetStringFromAny(interp, objPtr);
  1876. StringAppendString(objPtr, str, len);
  1877. }
  1878. void Jim_AppendString_sprintf(Jim_Interp *interp, Jim_Obj *objPtr, const char *fmt, ...)
  1879. {
  1880. char *buf;
  1881. va_list ap;
  1882. va_start(ap, fmt);
  1883. buf = jim_vasprintf(fmt, ap);
  1884. va_end(ap);
  1885. if (buf) {
  1886. Jim_AppendString(interp, objPtr, buf, -1);
  1887. jim_vasprintf_done(buf);
  1888. }
  1889. }
  1890. void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr,
  1891. Jim_Obj *appendObjPtr)
  1892. {
  1893. int len;
  1894. const char *str;
  1895. str = Jim_GetString(appendObjPtr, &len);
  1896. Jim_AppendString(interp, objPtr, str, len);
  1897. }
  1898. void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
  1899. {
  1900. va_list ap;
  1901. if (objPtr->typePtr != &stringObjType)
  1902. SetStringFromAny(interp, objPtr);
  1903. va_start(ap, objPtr);
  1904. while (1) {
  1905. char *s = va_arg(ap, char*);
  1906. if (s == NULL) break;
  1907. Jim_AppendString(interp, objPtr, s, -1);
  1908. }
  1909. va_end(ap);
  1910. }
  1911. int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase)
  1912. {
  1913. const char *aStr, *bStr;
  1914. int aLen, bLen, i;
  1915. if (aObjPtr == bObjPtr) return 1;
  1916. aStr = Jim_GetString(aObjPtr, &aLen);
  1917. bStr = Jim_GetString(bObjPtr, &bLen);
  1918. if (aLen != bLen) return 0;
  1919. if (nocase == 0)
  1920. return memcmp(aStr, bStr, aLen) == 0;
  1921. for (i = 0; i < aLen; i++) {
  1922. if (tolower((int)aStr[i]) != tolower((int)bStr[i]))
  1923. return 0;
  1924. }
  1925. return 1;
  1926. }
  1927. int Jim_StringMatchObj(Jim_Obj *patternObjPtr, Jim_Obj *objPtr,
  1928. int nocase)
  1929. {
  1930. const char *pattern, *string;
  1931. int patternLen, stringLen;
  1932. pattern = Jim_GetString(patternObjPtr, &patternLen);
  1933. string = Jim_GetString(objPtr, &stringLen);
  1934. return JimStringMatch(pattern, patternLen, string, stringLen, nocase);
  1935. }
  1936. int Jim_StringCompareObj(Jim_Obj *firstObjPtr,
  1937. Jim_Obj *secondObjPtr, int nocase)
  1938. {
  1939. const char *s1, *s2;
  1940. int l1, l2;
  1941. s1 = Jim_GetString(firstObjPtr, &l1);
  1942. s2 = Jim_GetString(secondObjPtr, &l2);
  1943. return JimStringCompare(s1, l1, s2, l2, nocase);
  1944. }
  1945. /* Convert a range, as returned by Jim_GetRange(), into
  1946. * an absolute index into an object of the specified length.
  1947. * This function may return negative values, or values
  1948. * bigger or equal to the length of the list if the index
  1949. * is out of range. */
  1950. static int JimRelToAbsIndex(int len, int index)
  1951. {
  1952. if (index < 0)
  1953. return len + index;
  1954. return index;
  1955. }
  1956. /* Convert a pair of index as normalize by JimRelToAbsIndex(),
  1957. * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable
  1958. * for implementation of commands like [string range] and [lrange].
  1959. *
  1960. * The resulting range is guaranteed to address valid elements of
  1961. * the structure. */
  1962. static void JimRelToAbsRange(int len, int first, int last,
  1963. int *firstPtr, int *lastPtr, int *rangeLenPtr)
  1964. {
  1965. int rangeLen;
  1966. if (first > last) {
  1967. rangeLen = 0;
  1968. } else {
  1969. rangeLen = last-first + 1;
  1970. if (rangeLen) {
  1971. if (first < 0) {
  1972. rangeLen += first;
  1973. first = 0;
  1974. }
  1975. if (last >= len) {
  1976. rangeLen -= (last-(len-1));
  1977. last = len-1;
  1978. }
  1979. }
  1980. }
  1981. if (rangeLen < 0) rangeLen = 0;
  1982. *firstPtr = first;
  1983. *lastPtr = last;
  1984. *rangeLenPtr = rangeLen;
  1985. }
  1986. Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
  1987. Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
  1988. {
  1989. int first, last;
  1990. const char *str;
  1991. int len, rangeLen;
  1992. if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
  1993. Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
  1994. return NULL;
  1995. str = Jim_GetString(strObjPtr, &len);
  1996. first = JimRelToAbsIndex(len, first);
  1997. last = JimRelToAbsIndex(len, last);
  1998. JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
  1999. return Jim_NewStringObj(interp, str + first, rangeLen);
  2000. }
  2001. static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
  2002. {
  2003. char *buf;
  2004. int i;
  2005. if (strObjPtr->typePtr != &stringObjType) {
  2006. SetStringFromAny(interp, strObjPtr);
  2007. }
  2008. buf = Jim_Alloc(strObjPtr->length + 1);
  2009. memcpy(buf, strObjPtr->bytes, strObjPtr->length + 1);
  2010. for (i = 0; i < strObjPtr->length; i++)
  2011. buf[i] = tolower(buf[i]);
  2012. return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
  2013. }
  2014. static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
  2015. {
  2016. char *buf;
  2017. int i;
  2018. if (strObjPtr->typePtr != &stringObjType) {
  2019. SetStringFromAny(interp, strObjPtr);
  2020. }
  2021. buf = Jim_Alloc(strObjPtr->length + 1);
  2022. memcpy(buf, strObjPtr->bytes, strObjPtr->length + 1);
  2023. for (i = 0; i < strObjPtr->length; i++)
  2024. buf[i] = toupper(buf[i]);
  2025. return Jim_NewStringObjNoAlloc(interp, buf, strObjPtr->length);
  2026. }
  2027. /* This is the core of the [format] command.
  2028. * TODO: Lots of things work - via a hack
  2029. * However, no format item can be >= JIM_MAX_FMT
  2030. */
  2031. #define JIM_MAX_FMT 2048
  2032. static Jim_Obj *Jim_FormatString_Inner(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
  2033. int objc, Jim_Obj *const *objv, char *sprintf_buf)
  2034. {
  2035. const char *fmt, *_fmt;
  2036. int fmtLen;
  2037. Jim_Obj *resObjPtr;
  2038. fmt = Jim_GetString(fmtObjPtr, &fmtLen);
  2039. _fmt = fmt;
  2040. resObjPtr = Jim_NewStringObj(interp, "", 0);
  2041. while (fmtLen) {
  2042. const char *p = fmt;
  2043. char spec[2], c;
  2044. jim_wide wideValue;
  2045. double doubleValue;
  2046. /* we cheat and use Sprintf()! */
  2047. char fmt_str[100];
  2048. char *cp;
  2049. int width;
  2050. int ljust;
  2051. int zpad;
  2052. int spad;
  2053. int altfm;
  2054. int forceplus;
  2055. int prec;
  2056. int inprec;
  2057. int haveprec;
  2058. int accum;
  2059. while (*fmt != '%' && fmtLen) {
  2060. fmt++; fmtLen--;
  2061. }
  2062. Jim_AppendString(interp, resObjPtr, p, fmt-p);
  2063. if (fmtLen == 0)
  2064. break;
  2065. fmt++; fmtLen--; /* skip '%' */
  2066. zpad = 0;
  2067. spad = 0;
  2068. width = -1;
  2069. ljust = 0;
  2070. altfm = 0;
  2071. forceplus = 0;
  2072. inprec = 0;
  2073. haveprec = 0;
  2074. prec = -1; /* not found yet */
  2075. next_fmt:
  2076. if (fmtLen <= 0) {
  2077. break;
  2078. }
  2079. switch (*fmt) {
  2080. /* terminals */
  2081. case 'b': /* binary - not all printfs() do this */
  2082. case 's': /* string */
  2083. case 'i': /* integer */
  2084. case 'd': /* decimal */
  2085. case 'x': /* hex */
  2086. case 'X': /* CAP hex */
  2087. case 'c': /* char */
  2088. case 'o': /* octal */
  2089. case 'u': /* unsigned */
  2090. case 'f': /* float */
  2091. break;
  2092. /* non-terminals */
  2093. case '0': /* zero pad */
  2094. zpad = 1;
  2095. fmt++; fmtLen--;
  2096. goto next_fmt;
  2097. break;
  2098. case '+':
  2099. forceplus = 1;
  2100. fmt++; fmtLen--;
  2101. goto next_fmt;
  2102. break;
  2103. case ' ': /* sign space */
  2104. spad = 1;
  2105. fmt++; fmtLen--;
  2106. goto next_fmt;
  2107. break;
  2108. case '-':
  2109. ljust = 1;
  2110. fmt++; fmtLen--;
  2111. goto next_fmt;
  2112. break;
  2113. case '#':
  2114. altfm = 1;
  2115. fmt++; fmtLen--;
  2116. goto next_fmt;
  2117. case '.':
  2118. inprec = 1;
  2119. fmt++; fmtLen--;
  2120. goto next_fmt;
  2121. break;
  2122. case '1':
  2123. case '2':
  2124. case '3':
  2125. case '4':
  2126. case '5':
  2127. case '6':
  2128. case '7':
  2129. case '8':
  2130. case '9':
  2131. accum = 0;
  2132. while (isdigit(*fmt) && (fmtLen > 0)) {
  2133. accum = (accum * 10) + (*fmt - '0');
  2134. fmt++; fmtLen--;
  2135. }
  2136. if (inprec) {
  2137. haveprec = 1;
  2138. prec = accum;
  2139. } else {
  2140. width = accum;
  2141. }
  2142. goto next_fmt;
  2143. case '*':
  2144. /* suck up the next item as an integer */
  2145. fmt++; fmtLen--;
  2146. objc--;
  2147. if (objc <= 0) {
  2148. goto not_enough_args;
  2149. }
  2150. if (Jim_GetWide(interp,objv[0],&wideValue)== JIM_ERR) {
  2151. Jim_FreeNewObj(interp, resObjPtr);
  2152. return NULL;
  2153. }
  2154. if (inprec) {
  2155. haveprec = 1;
  2156. prec = wideValue;
  2157. if (prec < 0) {
  2158. /* man 3 printf says */
  2159. /* if prec is negative, it is zero */
  2160. prec = 0;
  2161. }
  2162. } else {
  2163. width = wideValue;
  2164. if (width < 0) {
  2165. ljust = 1;
  2166. width = -width;
  2167. }
  2168. }
  2169. objv++;
  2170. goto next_fmt;
  2171. break;
  2172. }
  2173. if (*fmt != '%') {
  2174. if (objc == 0) {
  2175. not_enough_args:
  2176. Jim_FreeNewObj(interp, resObjPtr);
  2177. Jim_SetResultString(interp,
  2178. "not enough arguments for all format specifiers", -1);
  2179. return NULL;
  2180. } else {
  2181. objc--;
  2182. }
  2183. }
  2184. /*
  2185. * Create the formatter
  2186. * cause we cheat and use sprintf()
  2187. */
  2188. cp = fmt_str;
  2189. *cp++ = '%';
  2190. if (altfm) {
  2191. *cp++ = '#';
  2192. }
  2193. if (forceplus) {
  2194. *cp++ = '+';
  2195. } else if (spad) {
  2196. /* PLUS overrides */
  2197. *cp++ = ' ';
  2198. }
  2199. if (ljust) {
  2200. *cp++ = '-';
  2201. }
  2202. if (zpad) {
  2203. *cp++ = '0';
  2204. }
  2205. if (width > 0) {
  2206. sprintf(cp, "%d", width);
  2207. /* skip ahead */
  2208. cp = strchr(cp,0);
  2209. }
  2210. /* did we find a period? */
  2211. if (inprec) {
  2212. /* then add it */
  2213. *cp++ = '.';
  2214. /* did something occur after the period? */
  2215. if (haveprec) {
  2216. sprintf(cp, "%d", prec);
  2217. }
  2218. cp = strchr(cp,0);
  2219. }
  2220. *cp = 0;
  2221. /* here we do the work */
  2222. /* actually - we make sprintf() do it for us */
  2223. switch (*fmt) {
  2224. case 's':
  2225. *cp++ = 's';
  2226. *cp = 0;
  2227. /* BUG: we do not handled embeded NULLs */
  2228. snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString(objv[0], NULL));
  2229. break;
  2230. case 'c':
  2231. *cp++ = 'c';
  2232. *cp = 0;
  2233. if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
  2234. Jim_FreeNewObj(interp, resObjPtr);
  2235. return NULL;
  2236. }
  2237. c = (char) wideValue;
  2238. snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, c);
  2239. break;
  2240. case 'f':
  2241. case 'F':
  2242. case 'g':
  2243. case 'G':
  2244. case 'e':
  2245. case 'E':
  2246. *cp++ = *fmt;
  2247. *cp = 0;
  2248. if (Jim_GetDouble(interp, objv[0], &doubleValue) == JIM_ERR) {
  2249. Jim_FreeNewObj(interp, resObjPtr);
  2250. return NULL;
  2251. }
  2252. snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue);
  2253. break;
  2254. case 'b':
  2255. case 'd':
  2256. case 'o':
  2257. case 'i':
  2258. case 'u':
  2259. case 'x':
  2260. case 'X':
  2261. /* jim widevaluse are 64bit */
  2262. if (sizeof(jim_wide) == sizeof(long long)) {
  2263. *cp++ = 'l';
  2264. *cp++ = 'l';
  2265. } else {
  2266. *cp++ = 'l';
  2267. }
  2268. *cp++ = *fmt;
  2269. *cp = 0;
  2270. if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) {
  2271. Jim_FreeNewObj(interp, resObjPtr);
  2272. return NULL;
  2273. }
  2274. snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue);
  2275. break;
  2276. case '%':
  2277. sprintf_buf[0] = '%';
  2278. sprintf_buf[1] = 0;
  2279. objv--; /* undo the objv++ below */
  2280. break;
  2281. default:
  2282. spec[0] = *fmt; spec[1] = '\0';
  2283. Jim_FreeNewObj(interp, resObjPtr);
  2284. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  2285. Jim_AppendStrings(interp, Jim_GetResult(interp),
  2286. "bad field specifier \"", spec, "\"", NULL);
  2287. return NULL;
  2288. }
  2289. /* force terminate */
  2290. #if 0
  2291. printf("FMT was: %s\n", fmt_str);
  2292. printf("RES was: |%s|\n", sprintf_buf);
  2293. #endif
  2294. sprintf_buf[ JIM_MAX_FMT - 1] = 0;
  2295. Jim_AppendString(interp, resObjPtr, sprintf_buf, strlen(sprintf_buf));
  2296. /* next obj */
  2297. objv++;
  2298. fmt++;
  2299. fmtLen--;
  2300. }
  2301. return resObjPtr;
  2302. }
  2303. Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr,
  2304. int objc, Jim_Obj *const *objv)
  2305. {
  2306. char *sprintf_buf = malloc(JIM_MAX_FMT);
  2307. Jim_Obj *t = Jim_FormatString_Inner(interp, fmtObjPtr, objc, objv, sprintf_buf);
  2308. free(sprintf_buf);
  2309. return t;
  2310. }
  2311. /* -----------------------------------------------------------------------------
  2312. * Compared String Object
  2313. * ---------------------------------------------------------------------------*/
  2314. /* This is strange object that allows to compare a C literal string
  2315. * with a Jim object in very short time if the same comparison is done
  2316. * multiple times. For example every time the [if] command is executed,
  2317. * Jim has to check if a given argument is "else". This comparions if
  2318. * the code has no errors are true most of the times, so we can cache
  2319. * inside the object the pointer of the string of the last matching
  2320. * comparison. Because most C compilers perform literal sharing,
  2321. * so that: char *x = "foo", char *y = "foo", will lead to x == y,
  2322. * this works pretty well even if comparisons are at different places
  2323. * inside the C code. */
  2324. static Jim_ObjType comparedStringObjType = {
  2325. "compared-string",
  2326. NULL,
  2327. NULL,
  2328. NULL,
  2329. JIM_TYPE_REFERENCES,
  2330. };
  2331. /* The only way this object is exposed to the API is via the following
  2332. * function. Returns true if the string and the object string repr.
  2333. * are the same, otherwise zero is returned.
  2334. *
  2335. * Note: this isn't binary safe, but it hardly needs to be.*/
  2336. int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr,
  2337. const char *str)
  2338. {
  2339. if (objPtr->typePtr == &comparedStringObjType &&
  2340. objPtr->internalRep.ptr == str)
  2341. return 1;
  2342. else {
  2343. const char *objStr = Jim_GetString(objPtr, NULL);
  2344. if (strcmp(str, objStr) != 0) return 0;
  2345. if (objPtr->typePtr != &comparedStringObjType) {
  2346. Jim_FreeIntRep(interp, objPtr);
  2347. objPtr->typePtr = &comparedStringObjType;
  2348. }
  2349. objPtr->internalRep.ptr = (char*)str; /*ATTENTION: const cast */
  2350. return 1;
  2351. }
  2352. }
  2353. int qsortCompareStringPointers(const void *a, const void *b)
  2354. {
  2355. char * const *sa = (char * const *)a;
  2356. char * const *sb = (char * const *)b;
  2357. return strcmp(*sa, *sb);
  2358. }
  2359. int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
  2360. const char * const *tablePtr, int *indexPtr, const char *name, int flags)
  2361. {
  2362. const char * const *entryPtr = NULL;
  2363. char **tablePtrSorted;
  2364. int i, count = 0;
  2365. *indexPtr = -1;
  2366. for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
  2367. if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
  2368. *indexPtr = i;
  2369. return JIM_OK;
  2370. }
  2371. count++; /* If nothing matches, this will reach the len of tablePtr */
  2372. }
  2373. if (flags & JIM_ERRMSG) {
  2374. if (name == NULL)
  2375. name = "option";
  2376. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  2377. Jim_AppendStrings(interp, Jim_GetResult(interp),
  2378. "bad ", name, " \"", Jim_GetString(objPtr, NULL), "\": must be one of ",
  2379. NULL);
  2380. tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
  2381. memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
  2382. qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
  2383. for (i = 0; i < count; i++) {
  2384. if (i + 1 == count && count > 1)
  2385. Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
  2386. Jim_AppendString(interp, Jim_GetResult(interp),
  2387. tablePtrSorted[i], -1);
  2388. if (i + 1 != count)
  2389. Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
  2390. }
  2391. Jim_Free(tablePtrSorted);
  2392. }
  2393. return JIM_ERR;
  2394. }
  2395. int Jim_GetNvp(Jim_Interp *interp,
  2396. Jim_Obj *objPtr,
  2397. const Jim_Nvp *nvp_table,
  2398. const Jim_Nvp ** result)
  2399. {
  2400. Jim_Nvp *n;
  2401. int e;
  2402. e = Jim_Nvp_name2value_obj(interp, nvp_table, objPtr, &n);
  2403. if (e == JIM_ERR) {
  2404. return e;
  2405. }
  2406. /* Success? found? */
  2407. if (n->name) {
  2408. /* remove const */
  2409. *result = (Jim_Nvp *)n;
  2410. return JIM_OK;
  2411. } else {
  2412. return JIM_ERR;
  2413. }
  2414. }
  2415. /* -----------------------------------------------------------------------------
  2416. * Source Object
  2417. *
  2418. * This object is just a string from the language point of view, but
  2419. * in the internal representation it contains the filename and line number
  2420. * where this given token was read. This information is used by
  2421. * Jim_EvalObj() if the object passed happens to be of type "source".
  2422. *
  2423. * This allows to propagate the information about line numbers and file
  2424. * names and give error messages with absolute line numbers.
  2425. *
  2426. * Note that this object uses shared strings for filenames, and the
  2427. * pointer to the filename together with the line number is taken into
  2428. * the space for the "inline" internal represenation of the Jim_Object,
  2429. * so there is almost memory zero-overhead.
  2430. *
  2431. * Also the object will be converted to something else if the given
  2432. * token it represents in the source file is not something to be
  2433. * evaluated (not a script), and will be specialized in some other way,
  2434. * so the time overhead is alzo null.
  2435. * ---------------------------------------------------------------------------*/
  2436. static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
  2437. static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
  2438. static Jim_ObjType sourceObjType = {
  2439. "source",
  2440. FreeSourceInternalRep,
  2441. DupSourceInternalRep,
  2442. NULL,
  2443. JIM_TYPE_REFERENCES,
  2444. };
  2445. void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
  2446. {
  2447. Jim_ReleaseSharedString(interp,
  2448. objPtr->internalRep.sourceValue.fileName);
  2449. }
  2450. void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
  2451. {
  2452. dupPtr->internalRep.sourceValue.fileName =
  2453. Jim_GetSharedString(interp,
  2454. srcPtr->internalRep.sourceValue.fileName);
  2455. dupPtr->internalRep.sourceValue.lineNumber =
  2456. dupPtr->internalRep.sourceValue.lineNumber;
  2457. dupPtr->typePtr = &sourceObjType;
  2458. }
  2459. static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
  2460. const char *fileName, int lineNumber)
  2461. {
  2462. if (Jim_IsShared(objPtr))
  2463. Jim_Panic(interp,"JimSetSourceInfo called with shared object");
  2464. if (objPtr->typePtr != NULL)
  2465. Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
  2466. objPtr->internalRep.sourceValue.fileName =
  2467. Jim_GetSharedString(interp, fileName);
  2468. objPtr->internalRep.sourceValue.lineNumber = lineNumber;
  2469. objPtr->typePtr = &sourceObjType;
  2470. }
  2471. /* -----------------------------------------------------------------------------
  2472. * Script Object
  2473. * ---------------------------------------------------------------------------*/
  2474. #define JIM_CMDSTRUCT_EXPAND -1
  2475. static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
  2476. static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
  2477. static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  2478. static Jim_ObjType scriptObjType = {
  2479. "script",
  2480. FreeScriptInternalRep,
  2481. DupScriptInternalRep,
  2482. NULL,
  2483. JIM_TYPE_REFERENCES,
  2484. };
  2485. /* The ScriptToken structure represents every token into a scriptObj.
  2486. * Every token contains an associated Jim_Obj that can be specialized
  2487. * by commands operating on it. */
  2488. typedef struct ScriptToken {
  2489. int type;
  2490. Jim_Obj *objPtr;
  2491. int linenr;
  2492. } ScriptToken;
  2493. /* This is the script object internal representation. An array of
  2494. * ScriptToken structures, with an associated command structure array.
  2495. * The command structure is a pre-computed representation of the
  2496. * command length and arguments structure as a simple liner array
  2497. * of integers.
  2498. *
  2499. * For example the script:
  2500. *
  2501. * puts hello
  2502. * set $i $x$y [foo]BAR
  2503. *
  2504. * will produce a ScriptObj with the following Tokens:
  2505. *
  2506. * ESC puts
  2507. * SEP
  2508. * ESC hello
  2509. * EOL
  2510. * ESC set
  2511. * EOL
  2512. * VAR i
  2513. * SEP
  2514. * VAR x
  2515. * VAR y
  2516. * SEP
  2517. * CMD foo
  2518. * ESC BAR
  2519. * EOL
  2520. *
  2521. * This is a description of the tokens, separators, and of lines.
  2522. * The command structure instead represents the number of arguments
  2523. * of every command, followed by the tokens of which every argument
  2524. * is composed. So for the example script, the cmdstruct array will
  2525. * contain:
  2526. *
  2527. * 2 1 1 4 1 1 2 2
  2528. *
  2529. * Because "puts hello" has two args (2), composed of single tokens (1 1)
  2530. * While "set $i $x$y [foo]BAR" has four (4) args, the first two
  2531. * composed of single tokens (1 1) and the last two of double tokens
  2532. * (2 2).
  2533. *
  2534. * The precomputation of the command structure makes Jim_Eval() faster,
  2535. * and simpler because there aren't dynamic lengths / allocations.
  2536. *
  2537. * -- {expand} handling --
  2538. *
  2539. * Expand is handled in a special way. When a command
  2540. * contains at least an argument with the {expand} prefix,
  2541. * the command structure presents a -1 before the integer
  2542. * describing the number of arguments. This is used in order
  2543. * to send the command exection to a different path in case
  2544. * of {expand} and guarantee a fast path for the more common
  2545. * case. Also, the integers describing the number of tokens
  2546. * are expressed with negative sign, to allow for fast check
  2547. * of what's an {expand}-prefixed argument and what not.
  2548. *
  2549. * For example the command:
  2550. *
  2551. * list {expand}{1 2}
  2552. *
  2553. * Will produce the following cmdstruct array:
  2554. *
  2555. * -1 2 1 -2
  2556. *
  2557. * -- the substFlags field of the structure --
  2558. *
  2559. * The scriptObj structure is used to represent both "script" objects
  2560. * and "subst" objects. In the second case, the cmdStruct related
  2561. * fields are not used at all, but there is an additional field used
  2562. * that is 'substFlags': this represents the flags used to turn
  2563. * the string into the intenral representation used to perform the
  2564. * substitution. If this flags are not what the application requires
  2565. * the scriptObj is created again. For example the script:
  2566. *
  2567. * subst -nocommands $string
  2568. * subst -novariables $string
  2569. *
  2570. * Will recreate the internal representation of the $string object
  2571. * two times.
  2572. */
  2573. typedef struct ScriptObj {
  2574. int len; /* Length as number of tokens. */
  2575. int commands; /* number of top-level commands in script. */
  2576. ScriptToken *token; /* Tokens array. */
  2577. int *cmdStruct; /* commands structure */
  2578. int csLen; /* length of the cmdStruct array. */
  2579. int substFlags; /* flags used for the compilation of "subst" objects */
  2580. int inUse; /* Used to share a ScriptObj. Currently
  2581. only used by Jim_EvalObj() as protection against
  2582. shimmering of the currently evaluated object. */
  2583. char *fileName;
  2584. } ScriptObj;
  2585. void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
  2586. {
  2587. int i;
  2588. struct ScriptObj *script = (void*) objPtr->internalRep.ptr;
  2589. if (!script)
  2590. return;
  2591. script->inUse--;
  2592. if (script->inUse != 0) return;
  2593. for (i = 0; i < script->len; i++) {
  2594. if (script->token[i].objPtr != NULL)
  2595. Jim_DecrRefCount(interp, script->token[i].objPtr);
  2596. }
  2597. Jim_Free(script->token);
  2598. Jim_Free(script->cmdStruct);
  2599. Jim_Free(script->fileName);
  2600. Jim_Free(script);
  2601. }
  2602. void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
  2603. {
  2604. JIM_NOTUSED(interp);
  2605. JIM_NOTUSED(srcPtr);
  2606. /* Just returns an simple string. */
  2607. dupPtr->typePtr = NULL;
  2608. }
  2609. /* Add a new token to the internal repr of a script object */
  2610. static void ScriptObjAddToken(Jim_Interp *interp, struct ScriptObj *script,
  2611. char *strtoken, int len, int type, char *filename, int linenr)
  2612. {
  2613. int prevtype;
  2614. struct ScriptToken *token;
  2615. prevtype = (script->len == 0) ? JIM_TT_EOL : \
  2616. script->token[script->len-1].type;
  2617. /* Skip tokens without meaning, like words separators
  2618. * following a word separator or an end of command and
  2619. * so on. */
  2620. if (prevtype == JIM_TT_EOL) {
  2621. if (type == JIM_TT_EOL || type == JIM_TT_SEP) {
  2622. Jim_Free(strtoken);
  2623. return;
  2624. }
  2625. } else if (prevtype == JIM_TT_SEP) {
  2626. if (type == JIM_TT_SEP) {
  2627. Jim_Free(strtoken);
  2628. return;
  2629. } else if (type == JIM_TT_EOL) {
  2630. /* If an EOL is following by a SEP, drop the previous
  2631. * separator. */
  2632. script->len--;
  2633. Jim_DecrRefCount(interp, script->token[script->len].objPtr);
  2634. }
  2635. } else if (prevtype != JIM_TT_EOL && prevtype != JIM_TT_SEP &&
  2636. type == JIM_TT_ESC && len == 0)
  2637. {
  2638. /* Don't add empty tokens used in interpolation */
  2639. Jim_Free(strtoken);
  2640. return;
  2641. }
  2642. /* Make space for a new istruction */
  2643. script->len++;
  2644. script->token = Jim_Realloc(script->token,
  2645. sizeof(ScriptToken)*script->len);
  2646. /* Initialize the new token */
  2647. token = script->token + (script->len-1);
  2648. token->type = type;
  2649. /* Every object is intially as a string, but the
  2650. * internal type may be specialized during execution of the
  2651. * script. */
  2652. token->objPtr = Jim_NewStringObjNoAlloc(interp, strtoken, len);
  2653. /* To add source info to SEP and EOL tokens is useless because
  2654. * they will never by called as arguments of Jim_EvalObj(). */
  2655. if (filename && type != JIM_TT_SEP && type != JIM_TT_EOL)
  2656. JimSetSourceInfo(interp, token->objPtr, filename, linenr);
  2657. Jim_IncrRefCount(token->objPtr);
  2658. token->linenr = linenr;
  2659. }
  2660. /* Add an integer into the command structure field of the script object. */
  2661. static void ScriptObjAddInt(struct ScriptObj *script, int val)
  2662. {
  2663. script->csLen++;
  2664. script->cmdStruct = Jim_Realloc(script->cmdStruct,
  2665. sizeof(int)*script->csLen);
  2666. script->cmdStruct[script->csLen-1] = val;
  2667. }
  2668. /* Search a Jim_Obj contained in 'script' with the same stinrg repr.
  2669. * of objPtr. Search nested script objects recursively. */
  2670. static Jim_Obj *ScriptSearchLiteral(Jim_Interp *interp, ScriptObj *script,
  2671. ScriptObj *scriptBarrier, Jim_Obj *objPtr)
  2672. {
  2673. int i;
  2674. for (i = 0; i < script->len; i++) {
  2675. if (script->token[i].objPtr != objPtr &&
  2676. Jim_StringEqObj(script->token[i].objPtr, objPtr, 0)) {
  2677. return script->token[i].objPtr;
  2678. }
  2679. /* Enter recursively on scripts only if the object
  2680. * is not the same as the one we are searching for
  2681. * shared occurrences. */
  2682. if (script->token[i].objPtr->typePtr == &scriptObjType &&
  2683. script->token[i].objPtr != objPtr) {
  2684. Jim_Obj *foundObjPtr;
  2685. ScriptObj *subScript =
  2686. script->token[i].objPtr->internalRep.ptr;
  2687. /* Don't recursively enter the script we are trying
  2688. * to make shared to avoid circular references. */
  2689. if (subScript == scriptBarrier) continue;
  2690. if (subScript != script) {
  2691. foundObjPtr =
  2692. ScriptSearchLiteral(interp, subScript,
  2693. scriptBarrier, objPtr);
  2694. if (foundObjPtr != NULL)
  2695. return foundObjPtr;
  2696. }
  2697. }
  2698. }
  2699. return NULL;
  2700. }
  2701. /* Share literals of a script recursively sharing sub-scripts literals. */
  2702. static void ScriptShareLiterals(Jim_Interp *interp, ScriptObj *script,
  2703. ScriptObj *topLevelScript)
  2704. {
  2705. int i, j;
  2706. return;
  2707. /* Try to share with toplevel object. */
  2708. if (topLevelScript != NULL) {
  2709. for (i = 0; i < script->len; i++) {
  2710. Jim_Obj *foundObjPtr;
  2711. char *str = script->token[i].objPtr->bytes;
  2712. if (script->token[i].objPtr->refCount != 1) continue;
  2713. if (script->token[i].objPtr->typePtr == &scriptObjType) continue;
  2714. if (strchr(str, ' ') || strchr(str, '\n')) continue;
  2715. foundObjPtr = ScriptSearchLiteral(interp,
  2716. topLevelScript,
  2717. script, /* barrier */
  2718. script->token[i].objPtr);
  2719. if (foundObjPtr != NULL) {
  2720. Jim_IncrRefCount(foundObjPtr);
  2721. Jim_DecrRefCount(interp,
  2722. script->token[i].objPtr);
  2723. script->token[i].objPtr = foundObjPtr;
  2724. }
  2725. }
  2726. }
  2727. /* Try to share locally */
  2728. for (i = 0; i < script->len; i++) {
  2729. char *str = script->token[i].objPtr->bytes;
  2730. if (script->token[i].objPtr->refCount != 1) continue;
  2731. if (strchr(str, ' ') || strchr(str, '\n')) continue;
  2732. for (j = 0; j < script->len; j++) {
  2733. if (script->token[i].objPtr !=
  2734. script->token[j].objPtr &&
  2735. Jim_StringEqObj(script->token[i].objPtr,
  2736. script->token[j].objPtr, 0))
  2737. {
  2738. Jim_IncrRefCount(script->token[j].objPtr);
  2739. Jim_DecrRefCount(interp,
  2740. script->token[i].objPtr);
  2741. script->token[i].objPtr =
  2742. script->token[j].objPtr;
  2743. }
  2744. }
  2745. }
  2746. }
  2747. /* This method takes the string representation of an object
  2748. * as a Tcl script, and generates the pre-parsed internal representation
  2749. * of the script. */
  2750. int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
  2751. {
  2752. int scriptTextLen;
  2753. const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
  2754. struct JimParserCtx parser;
  2755. struct ScriptObj *script = Jim_Alloc(sizeof(*script));
  2756. ScriptToken *token;
  2757. int args, tokens, start, end, i;
  2758. int initialLineNumber;
  2759. int propagateSourceInfo = 0;
  2760. script->len = 0;
  2761. script->csLen = 0;
  2762. script->commands = 0;
  2763. script->token = NULL;
  2764. script->cmdStruct = NULL;
  2765. script->inUse = 1;
  2766. /* Try to get information about filename / line number */
  2767. if (objPtr->typePtr == &sourceObjType) {
  2768. script->fileName =
  2769. Jim_StrDup(objPtr->internalRep.sourceValue.fileName);
  2770. initialLineNumber = objPtr->internalRep.sourceValue.lineNumber;
  2771. propagateSourceInfo = 1;
  2772. } else {
  2773. script->fileName = Jim_StrDup("");
  2774. initialLineNumber = 1;
  2775. }
  2776. JimParserInit(&parser, scriptText, scriptTextLen, initialLineNumber);
  2777. while (!JimParserEof(&parser)) {
  2778. char *token;
  2779. int len, type, linenr;
  2780. JimParseScript(&parser);
  2781. token = JimParserGetToken(&parser, &len, &type, &linenr);
  2782. ScriptObjAddToken(interp, script, token, len, type,
  2783. propagateSourceInfo ? script->fileName : NULL,
  2784. linenr);
  2785. }
  2786. token = script->token;
  2787. /* Compute the command structure array
  2788. * (see the ScriptObj struct definition for more info) */
  2789. start = 0; /* Current command start token index */
  2790. end = -1; /* Current command end token index */
  2791. while (1) {
  2792. int expand = 0; /* expand flag. set to 1 on {expand} form. */
  2793. int interpolation = 0; /* set to 1 if there is at least one
  2794. argument of the command obtained via
  2795. interpolation of more tokens. */
  2796. /* Search for the end of command, while
  2797. * count the number of args. */
  2798. start = ++end;
  2799. if (start >= script->len) break;
  2800. args = 1; /* Number of args in current command */
  2801. while (token[end].type != JIM_TT_EOL) {
  2802. if (end == 0 || token[end-1].type == JIM_TT_SEP ||
  2803. token[end-1].type == JIM_TT_EOL)
  2804. {
  2805. if (token[end].type == JIM_TT_STR &&
  2806. token[end + 1].type != JIM_TT_SEP &&
  2807. token[end + 1].type != JIM_TT_EOL &&
  2808. (!strcmp(token[end].objPtr->bytes, "expand") ||
  2809. !strcmp(token[end].objPtr->bytes, "*")))
  2810. expand++;
  2811. }
  2812. if (token[end].type == JIM_TT_SEP)
  2813. args++;
  2814. end++;
  2815. }
  2816. interpolation = !((end-start + 1) == args*2);
  2817. /* Add the 'number of arguments' info into cmdstruct.
  2818. * Negative value if there is list expansion involved. */
  2819. if (expand)
  2820. ScriptObjAddInt(script, -1);
  2821. ScriptObjAddInt(script, args);
  2822. /* Now add info about the number of tokens. */
  2823. tokens = 0; /* Number of tokens in current argument. */
  2824. expand = 0;
  2825. for (i = start; i <= end; i++) {
  2826. if (token[i].type == JIM_TT_SEP ||
  2827. token[i].type == JIM_TT_EOL)
  2828. {
  2829. if (tokens == 1 && expand)
  2830. expand = 0;
  2831. ScriptObjAddInt(script,
  2832. expand ? -tokens : tokens);
  2833. expand = 0;
  2834. tokens = 0;
  2835. continue;
  2836. } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
  2837. (!strcmp(token[i].objPtr->bytes, "expand") ||
  2838. !strcmp(token[i].objPtr->bytes, "*")))
  2839. {
  2840. expand++;
  2841. }
  2842. tokens++;
  2843. }
  2844. }
  2845. /* Perform literal sharing, but only for objects that appear
  2846. * to be scripts written as literals inside the source code,
  2847. * and not computed at runtime. Literal sharing is a costly
  2848. * operation that should be done only against objects that
  2849. * are likely to require compilation only the first time, and
  2850. * then are executed multiple times. */
  2851. if (propagateSourceInfo && interp->framePtr->procBodyObjPtr) {
  2852. Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
  2853. if (bodyObjPtr->typePtr == &scriptObjType) {
  2854. ScriptObj *bodyScript =
  2855. bodyObjPtr->internalRep.ptr;
  2856. ScriptShareLiterals(interp, script, bodyScript);
  2857. }
  2858. } else if (propagateSourceInfo) {
  2859. ScriptShareLiterals(interp, script, NULL);
  2860. }
  2861. /* Free the old internal rep and set the new one. */
  2862. Jim_FreeIntRep(interp, objPtr);
  2863. Jim_SetIntRepPtr(objPtr, script);
  2864. objPtr->typePtr = &scriptObjType;
  2865. return JIM_OK;
  2866. }
  2867. ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
  2868. {
  2869. if (objPtr->typePtr != &scriptObjType) {
  2870. SetScriptFromAny(interp, objPtr);
  2871. }
  2872. return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
  2873. }
  2874. /* -----------------------------------------------------------------------------
  2875. * Commands
  2876. * ---------------------------------------------------------------------------*/
  2877. /* Commands HashTable Type.
  2878. *
  2879. * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */
  2880. static void Jim_CommandsHT_ValDestructor(void *interp, void *val)
  2881. {
  2882. Jim_Cmd *cmdPtr = (void*) val;
  2883. if (cmdPtr->cmdProc == NULL) {
  2884. Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
  2885. Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
  2886. if (cmdPtr->staticVars) {
  2887. Jim_FreeHashTable(cmdPtr->staticVars);
  2888. Jim_Free(cmdPtr->staticVars);
  2889. }
  2890. } else if (cmdPtr->delProc != NULL) {
  2891. /* If it was a C coded command, call the delProc if any */
  2892. cmdPtr->delProc(interp, cmdPtr->privData);
  2893. }
  2894. Jim_Free(val);
  2895. }
  2896. static Jim_HashTableType JimCommandsHashTableType = {
  2897. JimStringCopyHTHashFunction, /* hash function */
  2898. JimStringCopyHTKeyDup, /* key dup */
  2899. NULL, /* val dup */
  2900. JimStringCopyHTKeyCompare, /* key compare */
  2901. JimStringCopyHTKeyDestructor, /* key destructor */
  2902. Jim_CommandsHT_ValDestructor /* val destructor */
  2903. };
  2904. /* ------------------------- Commands related functions --------------------- */
  2905. int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
  2906. Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
  2907. {
  2908. Jim_HashEntry *he;
  2909. Jim_Cmd *cmdPtr;
  2910. he = Jim_FindHashEntry(&interp->commands, cmdName);
  2911. if (he == NULL) { /* New command to create */
  2912. cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
  2913. Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
  2914. } else {
  2915. Jim_InterpIncrProcEpoch(interp);
  2916. /* Free the arglist/body objects if it was a Tcl procedure */
  2917. cmdPtr = he->val;
  2918. if (cmdPtr->cmdProc == NULL) {
  2919. Jim_DecrRefCount(interp, cmdPtr->argListObjPtr);
  2920. Jim_DecrRefCount(interp, cmdPtr->bodyObjPtr);
  2921. if (cmdPtr->staticVars) {
  2922. Jim_FreeHashTable(cmdPtr->staticVars);
  2923. Jim_Free(cmdPtr->staticVars);
  2924. }
  2925. cmdPtr->staticVars = NULL;
  2926. } else if (cmdPtr->delProc != NULL) {
  2927. /* If it was a C coded command, call the delProc if any */
  2928. cmdPtr->delProc(interp, cmdPtr->privData);
  2929. }
  2930. }
  2931. /* Store the new details for this proc */
  2932. cmdPtr->delProc = delProc;
  2933. cmdPtr->cmdProc = cmdProc;
  2934. cmdPtr->privData = privData;
  2935. /* There is no need to increment the 'proc epoch' because
  2936. * creation of a new procedure can never affect existing
  2937. * cached commands. We don't do negative caching. */
  2938. return JIM_OK;
  2939. }
  2940. int Jim_CreateProcedure(Jim_Interp *interp, const char *cmdName,
  2941. Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
  2942. int arityMin, int arityMax)
  2943. {
  2944. Jim_Cmd *cmdPtr;
  2945. cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
  2946. cmdPtr->cmdProc = NULL; /* Not a C coded command */
  2947. cmdPtr->argListObjPtr = argListObjPtr;
  2948. cmdPtr->bodyObjPtr = bodyObjPtr;
  2949. Jim_IncrRefCount(argListObjPtr);
  2950. Jim_IncrRefCount(bodyObjPtr);
  2951. cmdPtr->arityMin = arityMin;
  2952. cmdPtr->arityMax = arityMax;
  2953. cmdPtr->staticVars = NULL;
  2954. /* Create the statics hash table. */
  2955. if (staticsListObjPtr) {
  2956. int len, i;
  2957. Jim_ListLength(interp, staticsListObjPtr, &len);
  2958. if (len != 0) {
  2959. cmdPtr->staticVars = Jim_Alloc(sizeof(Jim_HashTable));
  2960. Jim_InitHashTable(cmdPtr->staticVars, getJimVariablesHashTableType(),
  2961. interp);
  2962. for (i = 0; i < len; i++) {
  2963. Jim_Obj *objPtr=NULL, *initObjPtr=NULL, *nameObjPtr=NULL;
  2964. Jim_Var *varPtr;
  2965. int subLen;
  2966. Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE);
  2967. /* Check if it's composed of two elements. */
  2968. Jim_ListLength(interp, objPtr, &subLen);
  2969. if (subLen == 1 || subLen == 2) {
  2970. /* Try to get the variable value from the current
  2971. * environment. */
  2972. Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE);
  2973. if (subLen == 1) {
  2974. initObjPtr = Jim_GetVariable(interp, nameObjPtr,
  2975. JIM_NONE);
  2976. if (initObjPtr == NULL) {
  2977. Jim_SetResult(interp,
  2978. Jim_NewEmptyStringObj(interp));
  2979. Jim_AppendStrings(interp, Jim_GetResult(interp),
  2980. "variable for initialization of static \"",
  2981. Jim_GetString(nameObjPtr, NULL),
  2982. "\" not found in the local context",
  2983. NULL);
  2984. goto err;
  2985. }
  2986. } else {
  2987. Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE);
  2988. }
  2989. varPtr = Jim_Alloc(sizeof(*varPtr));
  2990. varPtr->objPtr = initObjPtr;
  2991. Jim_IncrRefCount(initObjPtr);
  2992. varPtr->linkFramePtr = NULL;
  2993. if (Jim_AddHashEntry(cmdPtr->staticVars,
  2994. Jim_GetString(nameObjPtr, NULL),
  2995. varPtr) != JIM_OK)
  2996. {
  2997. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  2998. Jim_AppendStrings(interp, Jim_GetResult(interp),
  2999. "static variable name \"",
  3000. Jim_GetString(objPtr, NULL), "\"",
  3001. " duplicated in statics list", NULL);
  3002. Jim_DecrRefCount(interp, initObjPtr);
  3003. Jim_Free(varPtr);
  3004. goto err;
  3005. }
  3006. } else {
  3007. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3008. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3009. "too many fields in static specifier \"",
  3010. objPtr, "\"", NULL);
  3011. goto err;
  3012. }
  3013. }
  3014. }
  3015. }
  3016. /* Add the new command */
  3017. /* it may already exist, so we try to delete the old one */
  3018. if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
  3019. /* There was an old procedure with the same name, this requires
  3020. * a 'proc epoch' update. */
  3021. Jim_InterpIncrProcEpoch(interp);
  3022. }
  3023. /* If a procedure with the same name didn't existed there is no need
  3024. * to increment the 'proc epoch' because creation of a new procedure
  3025. * can never affect existing cached commands. We don't do
  3026. * negative caching. */
  3027. Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
  3028. return JIM_OK;
  3029. err:
  3030. Jim_FreeHashTable(cmdPtr->staticVars);
  3031. Jim_Free(cmdPtr->staticVars);
  3032. Jim_DecrRefCount(interp, argListObjPtr);
  3033. Jim_DecrRefCount(interp, bodyObjPtr);
  3034. Jim_Free(cmdPtr);
  3035. return JIM_ERR;
  3036. }
  3037. int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName)
  3038. {
  3039. if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR)
  3040. return JIM_ERR;
  3041. Jim_InterpIncrProcEpoch(interp);
  3042. return JIM_OK;
  3043. }
  3044. int Jim_RenameCommand(Jim_Interp *interp, const char *oldName,
  3045. const char *newName)
  3046. {
  3047. Jim_Cmd *cmdPtr;
  3048. Jim_HashEntry *he;
  3049. Jim_Cmd *copyCmdPtr;
  3050. if (newName[0] == '\0') /* Delete! */
  3051. return Jim_DeleteCommand(interp, oldName);
  3052. /* Rename */
  3053. he = Jim_FindHashEntry(&interp->commands, oldName);
  3054. if (he == NULL)
  3055. return JIM_ERR; /* Invalid command name */
  3056. cmdPtr = he->val;
  3057. copyCmdPtr = Jim_Alloc(sizeof(Jim_Cmd));
  3058. *copyCmdPtr = *cmdPtr;
  3059. /* In order to avoid that a procedure will get arglist/body/statics
  3060. * freed by the hash table methods, fake a C-coded command
  3061. * setting cmdPtr->cmdProc as not NULL */
  3062. cmdPtr->cmdProc = (void*)1;
  3063. /* Also make sure delProc is NULL. */
  3064. cmdPtr->delProc = NULL;
  3065. /* Destroy the old command, and make sure the new is freed
  3066. * as well. */
  3067. Jim_DeleteHashEntry(&interp->commands, oldName);
  3068. Jim_DeleteHashEntry(&interp->commands, newName);
  3069. /* Now the new command. We are sure it can't fail because
  3070. * the target name was already freed. */
  3071. Jim_AddHashEntry(&interp->commands, newName, copyCmdPtr);
  3072. /* Increment the epoch */
  3073. Jim_InterpIncrProcEpoch(interp);
  3074. return JIM_OK;
  3075. }
  3076. /* -----------------------------------------------------------------------------
  3077. * Command object
  3078. * ---------------------------------------------------------------------------*/
  3079. static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  3080. static Jim_ObjType commandObjType = {
  3081. "command",
  3082. NULL,
  3083. NULL,
  3084. NULL,
  3085. JIM_TYPE_REFERENCES,
  3086. };
  3087. int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
  3088. {
  3089. Jim_HashEntry *he;
  3090. const char *cmdName;
  3091. /* Get the string representation */
  3092. cmdName = Jim_GetString(objPtr, NULL);
  3093. /* Lookup this name into the commands hash table */
  3094. he = Jim_FindHashEntry(&interp->commands, cmdName);
  3095. if (he == NULL)
  3096. return JIM_ERR;
  3097. /* Free the old internal repr and set the new one. */
  3098. Jim_FreeIntRep(interp, objPtr);
  3099. objPtr->typePtr = &commandObjType;
  3100. objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
  3101. objPtr->internalRep.cmdValue.cmdPtr = (void*)he->val;
  3102. return JIM_OK;
  3103. }
  3104. /* This function returns the command structure for the command name
  3105. * stored in objPtr. It tries to specialize the objPtr to contain
  3106. * a cached info instead to perform the lookup into the hash table
  3107. * every time. The information cached may not be uptodate, in such
  3108. * a case the lookup is performed and the cache updated. */
  3109. Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
  3110. {
  3111. if ((objPtr->typePtr != &commandObjType ||
  3112. objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) &&
  3113. SetCommandFromAny(interp, objPtr) == JIM_ERR) {
  3114. if (flags & JIM_ERRMSG) {
  3115. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3116. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3117. "invalid command name \"", objPtr->bytes, "\"",
  3118. NULL);
  3119. }
  3120. return NULL;
  3121. }
  3122. return objPtr->internalRep.cmdValue.cmdPtr;
  3123. }
  3124. /* -----------------------------------------------------------------------------
  3125. * Variables
  3126. * ---------------------------------------------------------------------------*/
  3127. /* Variables HashTable Type.
  3128. *
  3129. * Keys are dynamic allocated strings, Values are Jim_Var structures. */
  3130. static void JimVariablesHTValDestructor(void *interp, void *val)
  3131. {
  3132. Jim_Var *varPtr = (void*) val;
  3133. Jim_DecrRefCount(interp, varPtr->objPtr);
  3134. Jim_Free(val);
  3135. }
  3136. static Jim_HashTableType JimVariablesHashTableType = {
  3137. JimStringCopyHTHashFunction, /* hash function */
  3138. JimStringCopyHTKeyDup, /* key dup */
  3139. NULL, /* val dup */
  3140. JimStringCopyHTKeyCompare, /* key compare */
  3141. JimStringCopyHTKeyDestructor, /* key destructor */
  3142. JimVariablesHTValDestructor /* val destructor */
  3143. };
  3144. static Jim_HashTableType *getJimVariablesHashTableType(void)
  3145. {
  3146. return &JimVariablesHashTableType;
  3147. }
  3148. /* -----------------------------------------------------------------------------
  3149. * Variable object
  3150. * ---------------------------------------------------------------------------*/
  3151. #define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */
  3152. static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  3153. static Jim_ObjType variableObjType = {
  3154. "variable",
  3155. NULL,
  3156. NULL,
  3157. NULL,
  3158. JIM_TYPE_REFERENCES,
  3159. };
  3160. /* Return true if the string "str" looks like syntax sugar for [dict]. I.e.
  3161. * is in the form "varname(key)". */
  3162. static int Jim_NameIsDictSugar(const char *str, int len)
  3163. {
  3164. if (len == -1)
  3165. len = strlen(str);
  3166. if (len && str[len-1] == ')' && strchr(str, '(') != NULL)
  3167. return 1;
  3168. return 0;
  3169. }
  3170. /* This method should be called only by the variable API.
  3171. * It returns JIM_OK on success (variable already exists),
  3172. * JIM_ERR if it does not exists, JIM_DICT_GLUE if it's not
  3173. * a variable name, but syntax glue for [dict] i.e. the last
  3174. * character is ')' */
  3175. int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
  3176. {
  3177. Jim_HashEntry *he;
  3178. const char *varName;
  3179. int len;
  3180. /* Check if the object is already an uptodate variable */
  3181. if (objPtr->typePtr == &variableObjType &&
  3182. objPtr->internalRep.varValue.callFrameId == interp->framePtr->id)
  3183. return JIM_OK; /* nothing to do */
  3184. /* Get the string representation */
  3185. varName = Jim_GetString(objPtr, &len);
  3186. /* Make sure it's not syntax glue to get/set dict. */
  3187. if (Jim_NameIsDictSugar(varName, len))
  3188. return JIM_DICT_SUGAR;
  3189. if (varName[0] == ':' && varName[1] == ':') {
  3190. he = Jim_FindHashEntry(&interp->topFramePtr->vars, varName + 2);
  3191. if (he == NULL) {
  3192. return JIM_ERR;
  3193. }
  3194. }
  3195. else {
  3196. /* Lookup this name into the variables hash table */
  3197. he = Jim_FindHashEntry(&interp->framePtr->vars, varName);
  3198. if (he == NULL) {
  3199. /* Try with static vars. */
  3200. if (interp->framePtr->staticVars == NULL)
  3201. return JIM_ERR;
  3202. if (!(he = Jim_FindHashEntry(interp->framePtr->staticVars, varName)))
  3203. return JIM_ERR;
  3204. }
  3205. }
  3206. /* Free the old internal repr and set the new one. */
  3207. Jim_FreeIntRep(interp, objPtr);
  3208. objPtr->typePtr = &variableObjType;
  3209. objPtr->internalRep.varValue.callFrameId = interp->framePtr->id;
  3210. objPtr->internalRep.varValue.varPtr = (void*)he->val;
  3211. return JIM_OK;
  3212. }
  3213. /* -------------------- Variables related functions ------------------------- */
  3214. static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr,
  3215. Jim_Obj *valObjPtr);
  3216. static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr);
  3217. /* For now that's dummy. Variables lookup should be optimized
  3218. * in many ways, with caching of lookups, and possibly with
  3219. * a table of pre-allocated vars in every CallFrame for local vars.
  3220. * All the caching should also have an 'epoch' mechanism similar
  3221. * to the one used by Tcl for procedures lookup caching. */
  3222. int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
  3223. {
  3224. const char *name;
  3225. Jim_Var *var;
  3226. int err;
  3227. if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
  3228. /* Check for [dict] syntax sugar. */
  3229. if (err == JIM_DICT_SUGAR)
  3230. return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
  3231. /* New variable to create */
  3232. name = Jim_GetString(nameObjPtr, NULL);
  3233. var = Jim_Alloc(sizeof(*var));
  3234. var->objPtr = valObjPtr;
  3235. Jim_IncrRefCount(valObjPtr);
  3236. var->linkFramePtr = NULL;
  3237. /* Insert the new variable */
  3238. if (name[0] == ':' && name[1] == ':') {
  3239. /* Into to the top evel frame */
  3240. Jim_AddHashEntry(&interp->topFramePtr->vars, name + 2, var);
  3241. }
  3242. else {
  3243. Jim_AddHashEntry(&interp->framePtr->vars, name, var);
  3244. }
  3245. /* Make the object int rep a variable */
  3246. Jim_FreeIntRep(interp, nameObjPtr);
  3247. nameObjPtr->typePtr = &variableObjType;
  3248. nameObjPtr->internalRep.varValue.callFrameId =
  3249. interp->framePtr->id;
  3250. nameObjPtr->internalRep.varValue.varPtr = var;
  3251. } else {
  3252. var = nameObjPtr->internalRep.varValue.varPtr;
  3253. if (var->linkFramePtr == NULL) {
  3254. Jim_IncrRefCount(valObjPtr);
  3255. Jim_DecrRefCount(interp, var->objPtr);
  3256. var->objPtr = valObjPtr;
  3257. } else { /* Else handle the link */
  3258. Jim_CallFrame *savedCallFrame;
  3259. savedCallFrame = interp->framePtr;
  3260. interp->framePtr = var->linkFramePtr;
  3261. err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
  3262. interp->framePtr = savedCallFrame;
  3263. if (err != JIM_OK)
  3264. return err;
  3265. }
  3266. }
  3267. return JIM_OK;
  3268. }
  3269. int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
  3270. {
  3271. Jim_Obj *nameObjPtr;
  3272. int result;
  3273. nameObjPtr = Jim_NewStringObj(interp, name, -1);
  3274. Jim_IncrRefCount(nameObjPtr);
  3275. result = Jim_SetVariable(interp, nameObjPtr, objPtr);
  3276. Jim_DecrRefCount(interp, nameObjPtr);
  3277. return result;
  3278. }
  3279. int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
  3280. {
  3281. Jim_CallFrame *savedFramePtr;
  3282. int result;
  3283. savedFramePtr = interp->framePtr;
  3284. interp->framePtr = interp->topFramePtr;
  3285. result = Jim_SetVariableStr(interp, name, objPtr);
  3286. interp->framePtr = savedFramePtr;
  3287. return result;
  3288. }
  3289. int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
  3290. {
  3291. Jim_Obj *nameObjPtr, *valObjPtr;
  3292. int result;
  3293. nameObjPtr = Jim_NewStringObj(interp, name, -1);
  3294. valObjPtr = Jim_NewStringObj(interp, val, -1);
  3295. Jim_IncrRefCount(nameObjPtr);
  3296. Jim_IncrRefCount(valObjPtr);
  3297. result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
  3298. Jim_DecrRefCount(interp, nameObjPtr);
  3299. Jim_DecrRefCount(interp, valObjPtr);
  3300. return result;
  3301. }
  3302. int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
  3303. Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
  3304. {
  3305. const char *varName;
  3306. int len;
  3307. /* Check for cycles. */
  3308. if (interp->framePtr == targetCallFrame) {
  3309. Jim_Obj *objPtr = targetNameObjPtr;
  3310. Jim_Var *varPtr;
  3311. /* Cycles are only possible with 'uplevel 0' */
  3312. while (1) {
  3313. if (Jim_StringEqObj(objPtr, nameObjPtr, 0)) {
  3314. Jim_SetResultString(interp,
  3315. "can't upvar from variable to itself", -1);
  3316. return JIM_ERR;
  3317. }
  3318. if (SetVariableFromAny(interp, objPtr) != JIM_OK)
  3319. break;
  3320. varPtr = objPtr->internalRep.varValue.varPtr;
  3321. if (varPtr->linkFramePtr != targetCallFrame) break;
  3322. objPtr = varPtr->objPtr;
  3323. }
  3324. }
  3325. varName = Jim_GetString(nameObjPtr, &len);
  3326. if (Jim_NameIsDictSugar(varName, len)) {
  3327. Jim_SetResultString(interp,
  3328. "Dict key syntax invalid as link source", -1);
  3329. return JIM_ERR;
  3330. }
  3331. /* Perform the binding */
  3332. Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
  3333. /* We are now sure 'nameObjPtr' type is variableObjType */
  3334. nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
  3335. return JIM_OK;
  3336. }
  3337. /* Return the Jim_Obj pointer associated with a variable name,
  3338. * or NULL if the variable was not found in the current context.
  3339. * The same optimization discussed in the comment to the
  3340. * 'SetVariable' function should apply here. */
  3341. Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
  3342. {
  3343. int err;
  3344. /* All the rest is handled here */
  3345. if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
  3346. /* Check for [dict] syntax sugar. */
  3347. if (err == JIM_DICT_SUGAR)
  3348. return JimDictSugarGet(interp, nameObjPtr);
  3349. if (flags & JIM_ERRMSG) {
  3350. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3351. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3352. "can't read \"", nameObjPtr->bytes,
  3353. "\": no such variable", NULL);
  3354. }
  3355. return NULL;
  3356. } else {
  3357. Jim_Var *varPtr;
  3358. Jim_Obj *objPtr;
  3359. Jim_CallFrame *savedCallFrame;
  3360. varPtr = nameObjPtr->internalRep.varValue.varPtr;
  3361. if (varPtr->linkFramePtr == NULL)
  3362. return varPtr->objPtr;
  3363. /* The variable is a link? Resolve it. */
  3364. savedCallFrame = interp->framePtr;
  3365. interp->framePtr = varPtr->linkFramePtr;
  3366. objPtr = Jim_GetVariable(interp, varPtr->objPtr, JIM_NONE);
  3367. if (objPtr == NULL && flags & JIM_ERRMSG) {
  3368. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3369. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3370. "can't read \"", nameObjPtr->bytes,
  3371. "\": no such variable", NULL);
  3372. }
  3373. interp->framePtr = savedCallFrame;
  3374. return objPtr;
  3375. }
  3376. }
  3377. Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr,
  3378. int flags)
  3379. {
  3380. Jim_CallFrame *savedFramePtr;
  3381. Jim_Obj *objPtr;
  3382. savedFramePtr = interp->framePtr;
  3383. interp->framePtr = interp->topFramePtr;
  3384. objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
  3385. interp->framePtr = savedFramePtr;
  3386. return objPtr;
  3387. }
  3388. Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
  3389. {
  3390. Jim_Obj *nameObjPtr, *varObjPtr;
  3391. nameObjPtr = Jim_NewStringObj(interp, name, -1);
  3392. Jim_IncrRefCount(nameObjPtr);
  3393. varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
  3394. Jim_DecrRefCount(interp, nameObjPtr);
  3395. return varObjPtr;
  3396. }
  3397. Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name,
  3398. int flags)
  3399. {
  3400. Jim_CallFrame *savedFramePtr;
  3401. Jim_Obj *objPtr;
  3402. savedFramePtr = interp->framePtr;
  3403. interp->framePtr = interp->topFramePtr;
  3404. objPtr = Jim_GetVariableStr(interp, name, flags);
  3405. interp->framePtr = savedFramePtr;
  3406. return objPtr;
  3407. }
  3408. /* Unset a variable.
  3409. * Note: On success unset invalidates all the variable objects created
  3410. * in the current call frame incrementing. */
  3411. int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
  3412. {
  3413. const char *name;
  3414. Jim_Var *varPtr;
  3415. int err;
  3416. if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) {
  3417. /* Check for [dict] syntax sugar. */
  3418. if (err == JIM_DICT_SUGAR)
  3419. return JimDictSugarSet(interp, nameObjPtr, NULL);
  3420. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3421. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3422. "can't unset \"", nameObjPtr->bytes,
  3423. "\": no such variable", NULL);
  3424. return JIM_ERR; /* var not found */
  3425. }
  3426. varPtr = nameObjPtr->internalRep.varValue.varPtr;
  3427. /* If it's a link call UnsetVariable recursively */
  3428. if (varPtr->linkFramePtr) {
  3429. int retval;
  3430. Jim_CallFrame *savedCallFrame;
  3431. savedCallFrame = interp->framePtr;
  3432. interp->framePtr = varPtr->linkFramePtr;
  3433. retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
  3434. interp->framePtr = savedCallFrame;
  3435. if (retval != JIM_OK && flags & JIM_ERRMSG) {
  3436. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3437. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3438. "can't unset \"", nameObjPtr->bytes,
  3439. "\": no such variable", NULL);
  3440. }
  3441. return retval;
  3442. } else {
  3443. name = Jim_GetString(nameObjPtr, NULL);
  3444. if (Jim_DeleteHashEntry(&interp->framePtr->vars, name)
  3445. != JIM_OK) return JIM_ERR;
  3446. /* Change the callframe id, invalidating var lookup caching */
  3447. JimChangeCallFrameId(interp, interp->framePtr);
  3448. return JIM_OK;
  3449. }
  3450. }
  3451. /* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */
  3452. /* Given a variable name for [dict] operation syntax sugar,
  3453. * this function returns two objects, the first with the name
  3454. * of the variable to set, and the second with the rispective key.
  3455. * For example "foo(bar)" will return objects with string repr. of
  3456. * "foo" and "bar".
  3457. *
  3458. * The returned objects have refcount = 1. The function can't fail. */
  3459. static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
  3460. Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
  3461. {
  3462. const char *str, *p;
  3463. char *t;
  3464. int len, keyLen, nameLen;
  3465. Jim_Obj *varObjPtr, *keyObjPtr;
  3466. str = Jim_GetString(objPtr, &len);
  3467. p = strchr(str, '(');
  3468. p++;
  3469. keyLen = len-((p-str) + 1);
  3470. nameLen = (p-str)-1;
  3471. /* Create the objects with the variable name and key. */
  3472. t = Jim_Alloc(nameLen + 1);
  3473. memcpy(t, str, nameLen);
  3474. t[nameLen] = '\0';
  3475. varObjPtr = Jim_NewStringObjNoAlloc(interp, t, nameLen);
  3476. t = Jim_Alloc(keyLen + 1);
  3477. memcpy(t, p, keyLen);
  3478. t[keyLen] = '\0';
  3479. keyObjPtr = Jim_NewStringObjNoAlloc(interp, t, keyLen);
  3480. Jim_IncrRefCount(varObjPtr);
  3481. Jim_IncrRefCount(keyObjPtr);
  3482. *varPtrPtr = varObjPtr;
  3483. *keyPtrPtr = keyObjPtr;
  3484. }
  3485. /* Helper of Jim_SetVariable() to deal with dict-syntax variable names.
  3486. * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */
  3487. static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr,
  3488. Jim_Obj *valObjPtr)
  3489. {
  3490. Jim_Obj *varObjPtr, *keyObjPtr;
  3491. int err = JIM_OK;
  3492. JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
  3493. err = Jim_SetDictKeysVector(interp, varObjPtr, &keyObjPtr, 1,
  3494. valObjPtr);
  3495. Jim_DecrRefCount(interp, varObjPtr);
  3496. Jim_DecrRefCount(interp, keyObjPtr);
  3497. return err;
  3498. }
  3499. /* Helper of Jim_GetVariable() to deal with dict-syntax variable names */
  3500. static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr)
  3501. {
  3502. Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
  3503. JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
  3504. dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
  3505. if (!dictObjPtr) {
  3506. resObjPtr = NULL;
  3507. goto err;
  3508. }
  3509. if (Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_ERRMSG)
  3510. != JIM_OK) {
  3511. resObjPtr = NULL;
  3512. }
  3513. err:
  3514. Jim_DecrRefCount(interp, varObjPtr);
  3515. Jim_DecrRefCount(interp, keyObjPtr);
  3516. return resObjPtr;
  3517. }
  3518. /* --------- $var(INDEX) substitution, using a specialized object ----------- */
  3519. static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
  3520. static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
  3521. Jim_Obj *dupPtr);
  3522. static Jim_ObjType dictSubstObjType = {
  3523. "dict-substitution",
  3524. FreeDictSubstInternalRep,
  3525. DupDictSubstInternalRep,
  3526. NULL,
  3527. JIM_TYPE_NONE,
  3528. };
  3529. void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
  3530. {
  3531. Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
  3532. Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
  3533. }
  3534. void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr,
  3535. Jim_Obj *dupPtr)
  3536. {
  3537. JIM_NOTUSED(interp);
  3538. dupPtr->internalRep.dictSubstValue.varNameObjPtr =
  3539. srcPtr->internalRep.dictSubstValue.varNameObjPtr;
  3540. dupPtr->internalRep.dictSubstValue.indexObjPtr =
  3541. srcPtr->internalRep.dictSubstValue.indexObjPtr;
  3542. dupPtr->typePtr = &dictSubstObjType;
  3543. }
  3544. /* This function is used to expand [dict get] sugar in the form
  3545. * of $var(INDEX). The function is mainly used by Jim_EvalObj()
  3546. * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an
  3547. * object that is *guaranteed* to be in the form VARNAME(INDEX).
  3548. * The 'index' part is [subst]ituted, and is used to lookup a key inside
  3549. * the [dict]ionary contained in variable VARNAME. */
  3550. Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
  3551. {
  3552. Jim_Obj *varObjPtr, *keyObjPtr, *dictObjPtr, *resObjPtr;
  3553. Jim_Obj *substKeyObjPtr = NULL;
  3554. if (objPtr->typePtr != &dictSubstObjType) {
  3555. JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
  3556. Jim_FreeIntRep(interp, objPtr);
  3557. objPtr->typePtr = &dictSubstObjType;
  3558. objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
  3559. objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
  3560. }
  3561. if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
  3562. &substKeyObjPtr, JIM_NONE)
  3563. != JIM_OK) {
  3564. substKeyObjPtr = NULL;
  3565. goto err;
  3566. }
  3567. Jim_IncrRefCount(substKeyObjPtr);
  3568. dictObjPtr = Jim_GetVariable(interp,
  3569. objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_ERRMSG);
  3570. if (!dictObjPtr) {
  3571. resObjPtr = NULL;
  3572. goto err;
  3573. }
  3574. if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
  3575. != JIM_OK) {
  3576. resObjPtr = NULL;
  3577. goto err;
  3578. }
  3579. err:
  3580. if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
  3581. return resObjPtr;
  3582. }
  3583. /* -----------------------------------------------------------------------------
  3584. * CallFrame
  3585. * ---------------------------------------------------------------------------*/
  3586. static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp)
  3587. {
  3588. Jim_CallFrame *cf;
  3589. if (interp->freeFramesList) {
  3590. cf = interp->freeFramesList;
  3591. interp->freeFramesList = cf->nextFramePtr;
  3592. } else {
  3593. cf = Jim_Alloc(sizeof(*cf));
  3594. cf->vars.table = NULL;
  3595. }
  3596. cf->id = interp->callFrameEpoch++;
  3597. cf->parentCallFrame = NULL;
  3598. cf->argv = NULL;
  3599. cf->argc = 0;
  3600. cf->procArgsObjPtr = NULL;
  3601. cf->procBodyObjPtr = NULL;
  3602. cf->nextFramePtr = NULL;
  3603. cf->staticVars = NULL;
  3604. if (cf->vars.table == NULL)
  3605. Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
  3606. return cf;
  3607. }
  3608. /* Used to invalidate every caching related to callframe stability. */
  3609. static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf)
  3610. {
  3611. cf->id = interp->callFrameEpoch++;
  3612. }
  3613. #define JIM_FCF_NONE 0 /* no flags */
  3614. #define JIM_FCF_NOHT 1 /* don't free the hash table */
  3615. static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf,
  3616. int flags)
  3617. {
  3618. if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr);
  3619. if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr);
  3620. if (!(flags & JIM_FCF_NOHT))
  3621. Jim_FreeHashTable(&cf->vars);
  3622. else {
  3623. int i;
  3624. Jim_HashEntry **table = cf->vars.table, *he;
  3625. for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
  3626. he = table[i];
  3627. while (he != NULL) {
  3628. Jim_HashEntry *nextEntry = he->next;
  3629. Jim_Var *varPtr = (void*) he->val;
  3630. Jim_DecrRefCount(interp, varPtr->objPtr);
  3631. Jim_Free(he->val);
  3632. Jim_Free((void*)he->key); /* ATTENTION: const cast */
  3633. Jim_Free(he);
  3634. table[i] = NULL;
  3635. he = nextEntry;
  3636. }
  3637. }
  3638. cf->vars.used = 0;
  3639. }
  3640. cf->nextFramePtr = interp->freeFramesList;
  3641. interp->freeFramesList = cf;
  3642. }
  3643. /* -----------------------------------------------------------------------------
  3644. * References
  3645. * ---------------------------------------------------------------------------*/
  3646. /* References HashTable Type.
  3647. *
  3648. * Keys are jim_wide integers, dynamically allocated for now but in the
  3649. * future it's worth to cache this 8 bytes objects. Values are poitners
  3650. * to Jim_References. */
  3651. static void JimReferencesHTValDestructor(void *interp, void *val)
  3652. {
  3653. Jim_Reference *refPtr = (void*) val;
  3654. Jim_DecrRefCount(interp, refPtr->objPtr);
  3655. if (refPtr->finalizerCmdNamePtr != NULL) {
  3656. Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
  3657. }
  3658. Jim_Free(val);
  3659. }
  3660. unsigned int JimReferencesHTHashFunction(const void *key)
  3661. {
  3662. /* Only the least significant bits are used. */
  3663. const jim_wide *widePtr = key;
  3664. unsigned int intValue = (unsigned int) *widePtr;
  3665. return Jim_IntHashFunction(intValue);
  3666. }
  3667. unsigned int JimReferencesHTDoubleHashFunction(const void *key)
  3668. {
  3669. /* Only the least significant bits are used. */
  3670. const jim_wide *widePtr = key;
  3671. unsigned int intValue = (unsigned int) *widePtr;
  3672. return intValue; /* identity function. */
  3673. }
  3674. const void *JimReferencesHTKeyDup(void *privdata, const void *key)
  3675. {
  3676. void *copy = Jim_Alloc(sizeof(jim_wide));
  3677. JIM_NOTUSED(privdata);
  3678. memcpy(copy, key, sizeof(jim_wide));
  3679. return copy;
  3680. }
  3681. int JimReferencesHTKeyCompare(void *privdata, const void *key1,
  3682. const void *key2)
  3683. {
  3684. JIM_NOTUSED(privdata);
  3685. return memcmp(key1, key2, sizeof(jim_wide)) == 0;
  3686. }
  3687. void JimReferencesHTKeyDestructor(void *privdata, const void *key)
  3688. {
  3689. JIM_NOTUSED(privdata);
  3690. Jim_Free((void*)key);
  3691. }
  3692. static Jim_HashTableType JimReferencesHashTableType = {
  3693. JimReferencesHTHashFunction, /* hash function */
  3694. JimReferencesHTKeyDup, /* key dup */
  3695. NULL, /* val dup */
  3696. JimReferencesHTKeyCompare, /* key compare */
  3697. JimReferencesHTKeyDestructor, /* key destructor */
  3698. JimReferencesHTValDestructor /* val destructor */
  3699. };
  3700. /* -----------------------------------------------------------------------------
  3701. * Reference object type and References API
  3702. * ---------------------------------------------------------------------------*/
  3703. static void UpdateStringOfReference(struct Jim_Obj *objPtr);
  3704. static Jim_ObjType referenceObjType = {
  3705. "reference",
  3706. NULL,
  3707. NULL,
  3708. UpdateStringOfReference,
  3709. JIM_TYPE_REFERENCES,
  3710. };
  3711. void UpdateStringOfReference(struct Jim_Obj *objPtr)
  3712. {
  3713. int len;
  3714. char buf[JIM_REFERENCE_SPACE + 1];
  3715. Jim_Reference *refPtr;
  3716. refPtr = objPtr->internalRep.refValue.refPtr;
  3717. len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id);
  3718. objPtr->bytes = Jim_Alloc(len + 1);
  3719. memcpy(objPtr->bytes, buf, len + 1);
  3720. objPtr->length = len;
  3721. }
  3722. /* returns true if 'c' is a valid reference tag character.
  3723. * i.e. inside the range [_a-zA-Z0-9] */
  3724. static int isrefchar(int c)
  3725. {
  3726. if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
  3727. (c >= '0' && c <= '9')) return 1;
  3728. return 0;
  3729. }
  3730. int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
  3731. {
  3732. jim_wide wideValue;
  3733. int i, len;
  3734. const char *str, *start, *end;
  3735. char refId[21];
  3736. Jim_Reference *refPtr;
  3737. Jim_HashEntry *he;
  3738. /* Get the string representation */
  3739. str = Jim_GetString(objPtr, &len);
  3740. /* Check if it looks like a reference */
  3741. if (len < JIM_REFERENCE_SPACE) goto badformat;
  3742. /* Trim spaces */
  3743. start = str;
  3744. end = str + len-1;
  3745. while (*start == ' ') start++;
  3746. while (*end == ' ' && end > start) end--;
  3747. if (end-start + 1 != JIM_REFERENCE_SPACE) goto badformat;
  3748. /* <reference.<1234567>.%020> */
  3749. if (memcmp(start, "<reference.<", 12) != 0) goto badformat;
  3750. if (start[12 + JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat;
  3751. /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */
  3752. for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
  3753. if (!isrefchar(start[12 + i])) goto badformat;
  3754. }
  3755. /* Extract info from the refernece. */
  3756. memcpy(refId, start + 14 + JIM_REFERENCE_TAGLEN, 20);
  3757. refId[20] = '\0';
  3758. /* Try to convert the ID into a jim_wide */
  3759. if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat;
  3760. /* Check if the reference really exists! */
  3761. he = Jim_FindHashEntry(&interp->references, &wideValue);
  3762. if (he == NULL) {
  3763. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3764. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3765. "Invalid reference ID \"", str, "\"", NULL);
  3766. return JIM_ERR;
  3767. }
  3768. refPtr = he->val;
  3769. /* Free the old internal repr and set the new one. */
  3770. Jim_FreeIntRep(interp, objPtr);
  3771. objPtr->typePtr = &referenceObjType;
  3772. objPtr->internalRep.refValue.id = wideValue;
  3773. objPtr->internalRep.refValue.refPtr = refPtr;
  3774. return JIM_OK;
  3775. badformat:
  3776. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  3777. Jim_AppendStrings(interp, Jim_GetResult(interp),
  3778. "expected reference but got \"", str, "\"", NULL);
  3779. return JIM_ERR;
  3780. }
  3781. /* Returns a new reference pointing to objPtr, having cmdNamePtr
  3782. * as finalizer command (or NULL if there is no finalizer).
  3783. * The returned reference object has refcount = 0. */
  3784. Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr,
  3785. Jim_Obj *cmdNamePtr)
  3786. {
  3787. struct Jim_Reference *refPtr;
  3788. jim_wide wideValue = interp->referenceNextId;
  3789. Jim_Obj *refObjPtr;
  3790. const char *tag;
  3791. int tagLen, i;
  3792. /* Perform the Garbage Collection if needed. */
  3793. Jim_CollectIfNeeded(interp);
  3794. refPtr = Jim_Alloc(sizeof(*refPtr));
  3795. refPtr->objPtr = objPtr;
  3796. Jim_IncrRefCount(objPtr);
  3797. refPtr->finalizerCmdNamePtr = cmdNamePtr;
  3798. if (cmdNamePtr)
  3799. Jim_IncrRefCount(cmdNamePtr);
  3800. Jim_AddHashEntry(&interp->references, &wideValue, refPtr);
  3801. refObjPtr = Jim_NewObj(interp);
  3802. refObjPtr->typePtr = &referenceObjType;
  3803. refObjPtr->bytes = NULL;
  3804. refObjPtr->internalRep.refValue.id = interp->referenceNextId;
  3805. refObjPtr->internalRep.refValue.refPtr = refPtr;
  3806. interp->referenceNextId++;
  3807. /* Set the tag. Trimmered at JIM_REFERENCE_TAGLEN. Everything
  3808. * that does not pass the 'isrefchar' test is replaced with '_' */
  3809. tag = Jim_GetString(tagPtr, &tagLen);
  3810. if (tagLen > JIM_REFERENCE_TAGLEN)
  3811. tagLen = JIM_REFERENCE_TAGLEN;
  3812. for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
  3813. if (i < tagLen)
  3814. refPtr->tag[i] = tag[i];
  3815. else
  3816. refPtr->tag[i] = '_';
  3817. }
  3818. refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
  3819. return refObjPtr;
  3820. }
  3821. Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
  3822. {
  3823. if (objPtr->typePtr != &referenceObjType &&
  3824. SetReferenceFromAny(interp, objPtr) == JIM_ERR)
  3825. return NULL;
  3826. return objPtr->internalRep.refValue.refPtr;
  3827. }
  3828. int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
  3829. {
  3830. Jim_Reference *refPtr;
  3831. if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
  3832. return JIM_ERR;
  3833. Jim_IncrRefCount(cmdNamePtr);
  3834. if (refPtr->finalizerCmdNamePtr)
  3835. Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
  3836. refPtr->finalizerCmdNamePtr = cmdNamePtr;
  3837. return JIM_OK;
  3838. }
  3839. int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
  3840. {
  3841. Jim_Reference *refPtr;
  3842. if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
  3843. return JIM_ERR;
  3844. *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
  3845. return JIM_OK;
  3846. }
  3847. /* -----------------------------------------------------------------------------
  3848. * References Garbage Collection
  3849. * ---------------------------------------------------------------------------*/
  3850. /* This the hash table type for the "MARK" phase of the GC */
  3851. static Jim_HashTableType JimRefMarkHashTableType = {
  3852. JimReferencesHTHashFunction, /* hash function */
  3853. JimReferencesHTKeyDup, /* key dup */
  3854. NULL, /* val dup */
  3855. JimReferencesHTKeyCompare, /* key compare */
  3856. JimReferencesHTKeyDestructor, /* key destructor */
  3857. NULL /* val destructor */
  3858. };
  3859. /* #define JIM_DEBUG_GC 1 */
  3860. /* Performs the garbage collection. */
  3861. int Jim_Collect(Jim_Interp *interp)
  3862. {
  3863. Jim_HashTable marks;
  3864. Jim_HashTableIterator *htiter;
  3865. Jim_HashEntry *he;
  3866. Jim_Obj *objPtr;
  3867. int collected = 0;
  3868. /* Avoid recursive calls */
  3869. if (interp->lastCollectId == -1) {
  3870. /* Jim_Collect() already running. Return just now. */
  3871. return 0;
  3872. }
  3873. interp->lastCollectId = -1;
  3874. /* Mark all the references found into the 'mark' hash table.
  3875. * The references are searched in every live object that
  3876. * is of a type that can contain references. */
  3877. Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL);
  3878. objPtr = interp->liveList;
  3879. while (objPtr) {
  3880. if (objPtr->typePtr == NULL ||
  3881. objPtr->typePtr->flags & JIM_TYPE_REFERENCES) {
  3882. const char *str, *p;
  3883. int len;
  3884. /* If the object is of type reference, to get the
  3885. * Id is simple... */
  3886. if (objPtr->typePtr == &referenceObjType) {
  3887. Jim_AddHashEntry(&marks,
  3888. &objPtr->internalRep.refValue.id, NULL);
  3889. #ifdef JIM_DEBUG_GC
  3890. Jim_fprintf(interp,interp->cookie_stdout,
  3891. "MARK (reference): %d refcount: %d" JIM_NL,
  3892. (int) objPtr->internalRep.refValue.id,
  3893. objPtr->refCount);
  3894. #endif
  3895. objPtr = objPtr->nextObjPtr;
  3896. continue;
  3897. }
  3898. /* Get the string repr of the object we want
  3899. * to scan for references. */
  3900. p = str = Jim_GetString(objPtr, &len);
  3901. /* Skip objects too little to contain references. */
  3902. if (len < JIM_REFERENCE_SPACE) {
  3903. objPtr = objPtr->nextObjPtr;
  3904. continue;
  3905. }
  3906. /* Extract references from the object string repr. */
  3907. while (1) {
  3908. int i;
  3909. jim_wide id;
  3910. char buf[21];
  3911. if ((p = strstr(p, "<reference.<")) == NULL)
  3912. break;
  3913. /* Check if it's a valid reference. */
  3914. if (len-(p-str) < JIM_REFERENCE_SPACE) break;
  3915. if (p[41] != '>' || p[19] != '>' || p[20] != '.') break;
  3916. for (i = 21; i <= 40; i++)
  3917. if (!isdigit((int)p[i]))
  3918. break;
  3919. /* Get the ID */
  3920. memcpy(buf, p + 21, 20);
  3921. buf[20] = '\0';
  3922. Jim_StringToWide(buf, &id, 10);
  3923. /* Ok, a reference for the given ID
  3924. * was found. Mark it. */
  3925. Jim_AddHashEntry(&marks, &id, NULL);
  3926. #ifdef JIM_DEBUG_GC
  3927. Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id);
  3928. #endif
  3929. p += JIM_REFERENCE_SPACE;
  3930. }
  3931. }
  3932. objPtr = objPtr->nextObjPtr;
  3933. }
  3934. /* Run the references hash table to destroy every reference that
  3935. * is not referenced outside (not present in the mark HT). */
  3936. htiter = Jim_GetHashTableIterator(&interp->references);
  3937. while ((he = Jim_NextHashEntry(htiter)) != NULL) {
  3938. const jim_wide *refId;
  3939. Jim_Reference *refPtr;
  3940. refId = he->key;
  3941. /* Check if in the mark phase we encountered
  3942. * this reference. */
  3943. if (Jim_FindHashEntry(&marks, refId) == NULL) {
  3944. #ifdef JIM_DEBUG_GC
  3945. Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId);
  3946. #endif
  3947. collected++;
  3948. /* Drop the reference, but call the
  3949. * finalizer first if registered. */
  3950. refPtr = he->val;
  3951. if (refPtr->finalizerCmdNamePtr) {
  3952. char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE + 1);
  3953. Jim_Obj *objv[3], *oldResult;
  3954. JimFormatReference(refstr, refPtr, *refId);
  3955. objv[0] = refPtr->finalizerCmdNamePtr;
  3956. objv[1] = Jim_NewStringObjNoAlloc(interp,
  3957. refstr, 32);
  3958. objv[2] = refPtr->objPtr;
  3959. Jim_IncrRefCount(objv[0]);
  3960. Jim_IncrRefCount(objv[1]);
  3961. Jim_IncrRefCount(objv[2]);
  3962. /* Drop the reference itself */
  3963. Jim_DeleteHashEntry(&interp->references, refId);
  3964. /* Call the finalizer. Errors ignored. */
  3965. oldResult = interp->result;
  3966. Jim_IncrRefCount(oldResult);
  3967. Jim_EvalObjVector(interp, 3, objv);
  3968. Jim_SetResult(interp, oldResult);
  3969. Jim_DecrRefCount(interp, oldResult);
  3970. Jim_DecrRefCount(interp, objv[0]);
  3971. Jim_DecrRefCount(interp, objv[1]);
  3972. Jim_DecrRefCount(interp, objv[2]);
  3973. } else {
  3974. Jim_DeleteHashEntry(&interp->references, refId);
  3975. }
  3976. }
  3977. }
  3978. Jim_FreeHashTableIterator(htiter);
  3979. Jim_FreeHashTable(&marks);
  3980. interp->lastCollectId = interp->referenceNextId;
  3981. interp->lastCollectTime = time(NULL);
  3982. return collected;
  3983. }
  3984. #define JIM_COLLECT_ID_PERIOD 5000
  3985. #define JIM_COLLECT_TIME_PERIOD 300
  3986. void Jim_CollectIfNeeded(Jim_Interp *interp)
  3987. {
  3988. jim_wide elapsedId;
  3989. int elapsedTime;
  3990. elapsedId = interp->referenceNextId - interp->lastCollectId;
  3991. elapsedTime = time(NULL) - interp->lastCollectTime;
  3992. if (elapsedId > JIM_COLLECT_ID_PERIOD ||
  3993. elapsedTime > JIM_COLLECT_TIME_PERIOD) {
  3994. Jim_Collect(interp);
  3995. }
  3996. }
  3997. /* -----------------------------------------------------------------------------
  3998. * Interpreter related functions
  3999. * ---------------------------------------------------------------------------*/
  4000. Jim_Interp *Jim_CreateInterp(void)
  4001. {
  4002. Jim_Interp *i = Jim_Alloc(sizeof(*i));
  4003. Jim_Obj *pathPtr;
  4004. i->errorLine = 0;
  4005. i->errorFileName = Jim_StrDup("");
  4006. i->numLevels = 0;
  4007. i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
  4008. i->returnCode = JIM_OK;
  4009. i->exitCode = 0;
  4010. i->procEpoch = 0;
  4011. i->callFrameEpoch = 0;
  4012. i->liveList = i->freeList = NULL;
  4013. i->scriptFileName = Jim_StrDup("");
  4014. i->referenceNextId = 0;
  4015. i->lastCollectId = 0;
  4016. i->lastCollectTime = time(NULL);
  4017. i->freeFramesList = NULL;
  4018. i->prngState = NULL;
  4019. i->evalRetcodeLevel = -1;
  4020. i->cookie_stdin = stdin;
  4021. i->cookie_stdout = stdout;
  4022. i->cookie_stderr = stderr;
  4023. i->cb_fwrite = ((size_t (*)(const void *, size_t, size_t, void *))(fwrite));
  4024. i->cb_fread = ((size_t (*)(void *, size_t, size_t, void *))(fread));
  4025. i->cb_vfprintf = ((int (*)(void *, const char *fmt, va_list))(vfprintf));
  4026. i->cb_fflush = ((int (*)(void *))(fflush));
  4027. i->cb_fgets = ((char * (*)(char *, int, void *))(fgets));
  4028. /* Note that we can create objects only after the
  4029. * interpreter liveList and freeList pointers are
  4030. * initialized to NULL. */
  4031. Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
  4032. Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
  4033. Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType,
  4034. NULL);
  4035. Jim_InitHashTable(&i->stub, &JimStringCopyHashTableType, NULL);
  4036. Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
  4037. Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL);
  4038. i->framePtr = i->topFramePtr = JimCreateCallFrame(i);
  4039. i->emptyObj = Jim_NewEmptyStringObj(i);
  4040. i->result = i->emptyObj;
  4041. i->stackTrace = Jim_NewListObj(i, NULL, 0);
  4042. i->unknown = Jim_NewStringObj(i, "unknown", -1);
  4043. i->unknown_called = 0;
  4044. Jim_IncrRefCount(i->emptyObj);
  4045. Jim_IncrRefCount(i->result);
  4046. Jim_IncrRefCount(i->stackTrace);
  4047. Jim_IncrRefCount(i->unknown);
  4048. /* Initialize key variables every interpreter should contain */
  4049. pathPtr = Jim_NewStringObj(i, "./", -1);
  4050. Jim_SetVariableStr(i, "jim_libpath", pathPtr);
  4051. Jim_SetVariableStrWithStr(i, "jim_interactive", "0");
  4052. /* Export the core API to extensions */
  4053. JimRegisterCoreApi(i);
  4054. return i;
  4055. }
  4056. /* This is the only function Jim exports directly without
  4057. * to use the STUB system. It is only used by embedders
  4058. * in order to get an interpreter with the Jim API pointers
  4059. * registered. */
  4060. Jim_Interp *ExportedJimCreateInterp(void)
  4061. {
  4062. return Jim_CreateInterp();
  4063. }
  4064. void Jim_FreeInterp(Jim_Interp *i)
  4065. {
  4066. Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf;
  4067. Jim_Obj *objPtr, *nextObjPtr;
  4068. Jim_DecrRefCount(i, i->emptyObj);
  4069. Jim_DecrRefCount(i, i->result);
  4070. Jim_DecrRefCount(i, i->stackTrace);
  4071. Jim_DecrRefCount(i, i->unknown);
  4072. Jim_Free((void*)i->errorFileName);
  4073. Jim_Free((void*)i->scriptFileName);
  4074. Jim_FreeHashTable(&i->commands);
  4075. Jim_FreeHashTable(&i->references);
  4076. Jim_FreeHashTable(&i->stub);
  4077. Jim_FreeHashTable(&i->assocData);
  4078. Jim_FreeHashTable(&i->packages);
  4079. Jim_Free(i->prngState);
  4080. /* Free the call frames list */
  4081. while (cf) {
  4082. prevcf = cf->parentCallFrame;
  4083. JimFreeCallFrame(i, cf, JIM_FCF_NONE);
  4084. cf = prevcf;
  4085. }
  4086. /* Check that the live object list is empty, otherwise
  4087. * there is a memory leak. */
  4088. if (i->liveList != NULL) {
  4089. Jim_Obj *objPtr = i->liveList;
  4090. Jim_fprintf(i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL);
  4091. Jim_fprintf(i, i->cookie_stdout,"Objects still in the free list:" JIM_NL);
  4092. while (objPtr) {
  4093. const char *type = objPtr->typePtr ?
  4094. objPtr->typePtr->name : "";
  4095. Jim_fprintf(i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
  4096. objPtr, type,
  4097. objPtr->bytes ? objPtr->bytes
  4098. : "(null)", objPtr->refCount);
  4099. if (objPtr->typePtr == &sourceObjType) {
  4100. Jim_fprintf(i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL,
  4101. objPtr->internalRep.sourceValue.fileName,
  4102. objPtr->internalRep.sourceValue.lineNumber);
  4103. }
  4104. objPtr = objPtr->nextObjPtr;
  4105. }
  4106. Jim_fprintf(i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL);
  4107. Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
  4108. }
  4109. /* Free all the freed objects. */
  4110. objPtr = i->freeList;
  4111. while (objPtr) {
  4112. nextObjPtr = objPtr->nextObjPtr;
  4113. Jim_Free(objPtr);
  4114. objPtr = nextObjPtr;
  4115. }
  4116. /* Free cached CallFrame structures */
  4117. cf = i->freeFramesList;
  4118. while (cf) {
  4119. nextcf = cf->nextFramePtr;
  4120. if (cf->vars.table != NULL)
  4121. Jim_Free(cf->vars.table);
  4122. Jim_Free(cf);
  4123. cf = nextcf;
  4124. }
  4125. /* Free the sharedString hash table. Make sure to free it
  4126. * after every other Jim_Object was freed. */
  4127. Jim_FreeHashTable(&i->sharedStrings);
  4128. /* Free the interpreter structure. */
  4129. Jim_Free(i);
  4130. }
  4131. /* Store the call frame relative to the level represented by
  4132. * levelObjPtr into *framePtrPtr. If levelObjPtr == NULL, the
  4133. * level is assumed to be '1'.
  4134. *
  4135. * If a newLevelptr int pointer is specified, the function stores
  4136. * the absolute level integer value of the new target callframe into
  4137. * *newLevelPtr. (this is used to adjust interp->numLevels
  4138. * in the implementation of [uplevel], so that [info level] will
  4139. * return a correct information).
  4140. *
  4141. * This function accepts the 'level' argument in the form
  4142. * of the commands [uplevel] and [upvar].
  4143. *
  4144. * For a function accepting a relative integer as level suitable
  4145. * for implementation of [info level ?level?] check the
  4146. * GetCallFrameByInteger() function. */
  4147. int Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
  4148. Jim_CallFrame **framePtrPtr, int *newLevelPtr)
  4149. {
  4150. long level;
  4151. const char *str;
  4152. Jim_CallFrame *framePtr;
  4153. if (newLevelPtr) *newLevelPtr = interp->numLevels;
  4154. if (levelObjPtr) {
  4155. str = Jim_GetString(levelObjPtr, NULL);
  4156. if (str[0] == '#') {
  4157. char *endptr;
  4158. /* speedup for the toplevel (level #0) */
  4159. if (str[1] == '0' && str[2] == '\0') {
  4160. if (newLevelPtr) *newLevelPtr = 0;
  4161. *framePtrPtr = interp->topFramePtr;
  4162. return JIM_OK;
  4163. }
  4164. level = strtol(str + 1, &endptr, 0);
  4165. if (str[1] == '\0' || endptr[0] != '\0' || level < 0)
  4166. goto badlevel;
  4167. /* An 'absolute' level is converted into the
  4168. * 'number of levels to go back' format. */
  4169. level = interp->numLevels - level;
  4170. if (level < 0) goto badlevel;
  4171. } else {
  4172. if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0)
  4173. goto badlevel;
  4174. }
  4175. } else {
  4176. str = "1"; /* Needed to format the error message. */
  4177. level = 1;
  4178. }
  4179. /* Lookup */
  4180. framePtr = interp->framePtr;
  4181. if (newLevelPtr) *newLevelPtr = (*newLevelPtr)-level;
  4182. while (level--) {
  4183. framePtr = framePtr->parentCallFrame;
  4184. if (framePtr == NULL) goto badlevel;
  4185. }
  4186. *framePtrPtr = framePtr;
  4187. return JIM_OK;
  4188. badlevel:
  4189. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  4190. Jim_AppendStrings(interp, Jim_GetResult(interp),
  4191. "bad level \"", str, "\"", NULL);
  4192. return JIM_ERR;
  4193. }
  4194. /* Similar to Jim_GetCallFrameByLevel() but the level is specified
  4195. * as a relative integer like in the [info level ?level?] command. */
  4196. static int JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr,
  4197. Jim_CallFrame **framePtrPtr)
  4198. {
  4199. jim_wide level;
  4200. jim_wide relLevel; /* level relative to the current one. */
  4201. Jim_CallFrame *framePtr;
  4202. if (Jim_GetWide(interp, levelObjPtr, &level) != JIM_OK)
  4203. goto badlevel;
  4204. if (level > 0) {
  4205. /* An 'absolute' level is converted into the
  4206. * 'number of levels to go back' format. */
  4207. relLevel = interp->numLevels - level;
  4208. } else {
  4209. relLevel = -level;
  4210. }
  4211. /* Lookup */
  4212. framePtr = interp->framePtr;
  4213. while (relLevel--) {
  4214. framePtr = framePtr->parentCallFrame;
  4215. if (framePtr == NULL) goto badlevel;
  4216. }
  4217. *framePtrPtr = framePtr;
  4218. return JIM_OK;
  4219. badlevel:
  4220. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  4221. Jim_AppendStrings(interp, Jim_GetResult(interp),
  4222. "bad level \"", Jim_GetString(levelObjPtr, NULL), "\"", NULL);
  4223. return JIM_ERR;
  4224. }
  4225. static void JimSetErrorFileName(Jim_Interp *interp, char *filename)
  4226. {
  4227. Jim_Free((void*)interp->errorFileName);
  4228. interp->errorFileName = Jim_StrDup(filename);
  4229. }
  4230. static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr)
  4231. {
  4232. interp->errorLine = linenr;
  4233. }
  4234. static void JimResetStackTrace(Jim_Interp *interp)
  4235. {
  4236. Jim_DecrRefCount(interp, interp->stackTrace);
  4237. interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
  4238. Jim_IncrRefCount(interp->stackTrace);
  4239. }
  4240. static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
  4241. const char *filename, int linenr)
  4242. {
  4243. /* No need to add this dummy entry to the stack trace */
  4244. if (strcmp(procname, "unknown") == 0) {
  4245. return;
  4246. }
  4247. if (Jim_IsShared(interp->stackTrace)) {
  4248. interp->stackTrace =
  4249. Jim_DuplicateObj(interp, interp->stackTrace);
  4250. Jim_IncrRefCount(interp->stackTrace);
  4251. }
  4252. Jim_ListAppendElement(interp, interp->stackTrace,
  4253. Jim_NewStringObj(interp, procname, -1));
  4254. Jim_ListAppendElement(interp, interp->stackTrace,
  4255. Jim_NewStringObj(interp, filename, -1));
  4256. Jim_ListAppendElement(interp, interp->stackTrace,
  4257. Jim_NewIntObj(interp, linenr));
  4258. }
  4259. int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data)
  4260. {
  4261. AssocDataValue *assocEntryPtr = (AssocDataValue *)Jim_Alloc(sizeof(AssocDataValue));
  4262. assocEntryPtr->delProc = delProc;
  4263. assocEntryPtr->data = data;
  4264. return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
  4265. }
  4266. void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
  4267. {
  4268. Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
  4269. if (entryPtr != NULL) {
  4270. AssocDataValue *assocEntryPtr = (AssocDataValue *)entryPtr->val;
  4271. return assocEntryPtr->data;
  4272. }
  4273. return NULL;
  4274. }
  4275. int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
  4276. {
  4277. return Jim_DeleteHashEntry(&interp->assocData, key);
  4278. }
  4279. int Jim_GetExitCode(Jim_Interp *interp) {
  4280. return interp->exitCode;
  4281. }
  4282. void *Jim_SetStdin(Jim_Interp *interp, void *fp)
  4283. {
  4284. if (fp != NULL) interp->cookie_stdin = fp;
  4285. return interp->cookie_stdin;
  4286. }
  4287. void *Jim_SetStdout(Jim_Interp *interp, void *fp)
  4288. {
  4289. if (fp != NULL) interp->cookie_stdout = fp;
  4290. return interp->cookie_stdout;
  4291. }
  4292. void *Jim_SetStderr(Jim_Interp *interp, void *fp)
  4293. {
  4294. if (fp != NULL) interp->cookie_stderr = fp;
  4295. return interp->cookie_stderr;
  4296. }
  4297. /* -----------------------------------------------------------------------------
  4298. * Shared strings.
  4299. * Every interpreter has an hash table where to put shared dynamically
  4300. * allocate strings that are likely to be used a lot of times.
  4301. * For example, in the 'source' object type, there is a pointer to
  4302. * the filename associated with that object. Every script has a lot
  4303. * of this objects with the identical file name, so it is wise to share
  4304. * this info.
  4305. *
  4306. * The API is trivial: Jim_GetSharedString(interp, "foobar")
  4307. * returns the pointer to the shared string. Every time a reference
  4308. * to the string is no longer used, the user should call
  4309. * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using
  4310. * a given string, it is removed from the hash table.
  4311. * ---------------------------------------------------------------------------*/
  4312. const char *Jim_GetSharedString(Jim_Interp *interp, const char *str)
  4313. {
  4314. Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
  4315. if (he == NULL) {
  4316. char *strCopy = Jim_StrDup(str);
  4317. Jim_AddHashEntry(&interp->sharedStrings, strCopy, (void*)1);
  4318. return strCopy;
  4319. } else {
  4320. intptr_t refCount = (intptr_t) he->val;
  4321. refCount++;
  4322. he->val = (void*) refCount;
  4323. return he->key;
  4324. }
  4325. }
  4326. void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str)
  4327. {
  4328. intptr_t refCount;
  4329. Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
  4330. if (he == NULL)
  4331. Jim_Panic(interp,"Jim_ReleaseSharedString called with "
  4332. "unknown shared string '%s'", str);
  4333. refCount = (intptr_t) he->val;
  4334. refCount--;
  4335. if (refCount == 0) {
  4336. Jim_DeleteHashEntry(&interp->sharedStrings, str);
  4337. } else {
  4338. he->val = (void*) refCount;
  4339. }
  4340. }
  4341. /* -----------------------------------------------------------------------------
  4342. * Integer object
  4343. * ---------------------------------------------------------------------------*/
  4344. #define JIM_INTEGER_SPACE 24
  4345. static void UpdateStringOfInt(struct Jim_Obj *objPtr);
  4346. static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
  4347. static Jim_ObjType intObjType = {
  4348. "int",
  4349. NULL,
  4350. NULL,
  4351. UpdateStringOfInt,
  4352. JIM_TYPE_NONE,
  4353. };
  4354. void UpdateStringOfInt(struct Jim_Obj *objPtr)
  4355. {
  4356. int len;
  4357. char buf[JIM_INTEGER_SPACE + 1];
  4358. len = Jim_WideToString(buf, objPtr->internalRep.wideValue);
  4359. objPtr->bytes = Jim_Alloc(len + 1);
  4360. memcpy(objPtr->bytes, buf, len + 1);
  4361. objPtr->length = len;
  4362. }
  4363. int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
  4364. {
  4365. jim_wide wideValue;
  4366. const char *str;
  4367. /* Get the string representation */
  4368. str = Jim_GetString(objPtr, NULL);
  4369. /* Try to convert into a jim_wide */
  4370. if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
  4371. if (flags & JIM_ERRMSG) {
  4372. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  4373. Jim_AppendStrings(interp, Jim_GetResult(interp),
  4374. "expected integer but got \"", str, "\"", NULL);
  4375. }
  4376. return JIM_ERR;
  4377. }
  4378. if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) &&
  4379. errno == ERANGE) {
  4380. Jim_SetResultString(interp,
  4381. "Integer value too big to be represented", -1);
  4382. return JIM_ERR;
  4383. }
  4384. /* Free the old internal repr and set the new one. */
  4385. Jim_FreeIntRep(interp, objPtr);
  4386. objPtr->typePtr = &intObjType;
  4387. objPtr->internalRep.wideValue = wideValue;
  4388. return JIM_OK;
  4389. }
  4390. int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr)
  4391. {
  4392. if (objPtr->typePtr != &intObjType &&
  4393. SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
  4394. return JIM_ERR;
  4395. *widePtr = objPtr->internalRep.wideValue;
  4396. return JIM_OK;
  4397. }
  4398. /* Get a wide but does not set an error if the format is bad. */
  4399. static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr,
  4400. jim_wide *widePtr)
  4401. {
  4402. if (objPtr->typePtr != &intObjType &&
  4403. SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
  4404. return JIM_ERR;
  4405. *widePtr = objPtr->internalRep.wideValue;
  4406. return JIM_OK;
  4407. }
  4408. int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
  4409. {
  4410. jim_wide wideValue;
  4411. int retval;
  4412. retval = Jim_GetWide(interp, objPtr, &wideValue);
  4413. if (retval == JIM_OK) {
  4414. *longPtr = (long) wideValue;
  4415. return JIM_OK;
  4416. }
  4417. return JIM_ERR;
  4418. }
  4419. void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
  4420. {
  4421. if (Jim_IsShared(objPtr))
  4422. Jim_Panic(interp,"Jim_SetWide called with shared object");
  4423. if (objPtr->typePtr != &intObjType) {
  4424. Jim_FreeIntRep(interp, objPtr);
  4425. objPtr->typePtr = &intObjType;
  4426. }
  4427. Jim_InvalidateStringRep(objPtr);
  4428. objPtr->internalRep.wideValue = wideValue;
  4429. }
  4430. Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
  4431. {
  4432. Jim_Obj *objPtr;
  4433. objPtr = Jim_NewObj(interp);
  4434. objPtr->typePtr = &intObjType;
  4435. objPtr->bytes = NULL;
  4436. objPtr->internalRep.wideValue = wideValue;
  4437. return objPtr;
  4438. }
  4439. /* -----------------------------------------------------------------------------
  4440. * Double object
  4441. * ---------------------------------------------------------------------------*/
  4442. #define JIM_DOUBLE_SPACE 30
  4443. static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
  4444. static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
  4445. static Jim_ObjType doubleObjType = {
  4446. "double",
  4447. NULL,
  4448. NULL,
  4449. UpdateStringOfDouble,
  4450. JIM_TYPE_NONE,
  4451. };
  4452. void UpdateStringOfDouble(struct Jim_Obj *objPtr)
  4453. {
  4454. int len;
  4455. char buf[JIM_DOUBLE_SPACE + 1];
  4456. len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue);
  4457. objPtr->bytes = Jim_Alloc(len + 1);
  4458. memcpy(objPtr->bytes, buf, len + 1);
  4459. objPtr->length = len;
  4460. }
  4461. int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
  4462. {
  4463. double doubleValue;
  4464. const char *str;
  4465. /* Get the string representation */
  4466. str = Jim_GetString(objPtr, NULL);
  4467. /* Try to convert into a double */
  4468. if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
  4469. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  4470. Jim_AppendStrings(interp, Jim_GetResult(interp),
  4471. "expected number but got '", str, "'", NULL);
  4472. return JIM_ERR;
  4473. }
  4474. /* Free the old internal repr and set the new one. */
  4475. Jim_FreeIntRep(interp, objPtr);
  4476. objPtr->typePtr = &doubleObjType;
  4477. objPtr->internalRep.doubleValue = doubleValue;
  4478. return JIM_OK;
  4479. }
  4480. int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
  4481. {
  4482. if (objPtr->typePtr != &doubleObjType &&
  4483. SetDoubleFromAny(interp, objPtr) == JIM_ERR)
  4484. return JIM_ERR;
  4485. *doublePtr = objPtr->internalRep.doubleValue;
  4486. return JIM_OK;
  4487. }
  4488. void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
  4489. {
  4490. if (Jim_IsShared(objPtr))
  4491. Jim_Panic(interp,"Jim_SetDouble called with shared object");
  4492. if (objPtr->typePtr != &doubleObjType) {
  4493. Jim_FreeIntRep(interp, objPtr);
  4494. objPtr->typePtr = &doubleObjType;
  4495. }
  4496. Jim_InvalidateStringRep(objPtr);
  4497. objPtr->internalRep.doubleValue = doubleValue;
  4498. }
  4499. Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
  4500. {
  4501. Jim_Obj *objPtr;
  4502. objPtr = Jim_NewObj(interp);
  4503. objPtr->typePtr = &doubleObjType;
  4504. objPtr->bytes = NULL;
  4505. objPtr->internalRep.doubleValue = doubleValue;
  4506. return objPtr;
  4507. }
  4508. /* -----------------------------------------------------------------------------
  4509. * List object
  4510. * ---------------------------------------------------------------------------*/
  4511. static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
  4512. static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
  4513. static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
  4514. static void UpdateStringOfList(struct Jim_Obj *objPtr);
  4515. static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  4516. /* Note that while the elements of the list may contain references,
  4517. * the list object itself can't. This basically means that the
  4518. * list object string representation as a whole can't contain references
  4519. * that are not presents in the single elements. */
  4520. static Jim_ObjType listObjType = {
  4521. "list",
  4522. FreeListInternalRep,
  4523. DupListInternalRep,
  4524. UpdateStringOfList,
  4525. JIM_TYPE_NONE,
  4526. };
  4527. void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
  4528. {
  4529. int i;
  4530. for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
  4531. Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
  4532. }
  4533. Jim_Free(objPtr->internalRep.listValue.ele);
  4534. }
  4535. void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
  4536. {
  4537. int i;
  4538. JIM_NOTUSED(interp);
  4539. dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
  4540. dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
  4541. dupPtr->internalRep.listValue.ele =
  4542. Jim_Alloc(sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.maxLen);
  4543. memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
  4544. sizeof(Jim_Obj*)*srcPtr->internalRep.listValue.len);
  4545. for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
  4546. Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
  4547. }
  4548. dupPtr->typePtr = &listObjType;
  4549. }
  4550. /* The following function checks if a given string can be encoded
  4551. * into a list element without any kind of quoting, surrounded by braces,
  4552. * or using escapes to quote. */
  4553. #define JIM_ELESTR_SIMPLE 0
  4554. #define JIM_ELESTR_BRACE 1
  4555. #define JIM_ELESTR_QUOTE 2
  4556. static int ListElementQuotingType(const char *s, int len)
  4557. {
  4558. int i, level, trySimple = 1;
  4559. /* Try with the SIMPLE case */
  4560. if (len == 0) return JIM_ELESTR_BRACE;
  4561. if (s[0] == '"' || s[0] == '{') {
  4562. trySimple = 0;
  4563. goto testbrace;
  4564. }
  4565. for (i = 0; i < len; i++) {
  4566. switch (s[i]) {
  4567. case ' ':
  4568. case '$':
  4569. case '"':
  4570. case '[':
  4571. case ']':
  4572. case ';':
  4573. case '\\':
  4574. case '\r':
  4575. case '\n':
  4576. case '\t':
  4577. case '\f':
  4578. case '\v':
  4579. trySimple = 0;
  4580. case '{':
  4581. case '}':
  4582. goto testbrace;
  4583. }
  4584. }
  4585. return JIM_ELESTR_SIMPLE;
  4586. testbrace:
  4587. /* Test if it's possible to do with braces */
  4588. if (s[len-1] == '\\' ||
  4589. s[len-1] == ']') return JIM_ELESTR_QUOTE;
  4590. level = 0;
  4591. for (i = 0; i < len; i++) {
  4592. switch (s[i]) {
  4593. case '{': level++; break;
  4594. case '}': level--;
  4595. if (level < 0) return JIM_ELESTR_QUOTE;
  4596. break;
  4597. case '\\':
  4598. if (s[i + 1] == '\n')
  4599. return JIM_ELESTR_QUOTE;
  4600. else
  4601. if (s[i + 1] != '\0') i++;
  4602. break;
  4603. }
  4604. }
  4605. if (level == 0) {
  4606. if (!trySimple) return JIM_ELESTR_BRACE;
  4607. for (i = 0; i < len; i++) {
  4608. switch (s[i]) {
  4609. case ' ':
  4610. case '$':
  4611. case '"':
  4612. case '[':
  4613. case ']':
  4614. case ';':
  4615. case '\\':
  4616. case '\r':
  4617. case '\n':
  4618. case '\t':
  4619. case '\f':
  4620. case '\v':
  4621. return JIM_ELESTR_BRACE;
  4622. break;
  4623. }
  4624. }
  4625. return JIM_ELESTR_SIMPLE;
  4626. }
  4627. return JIM_ELESTR_QUOTE;
  4628. }
  4629. /* Returns the malloc-ed representation of a string
  4630. * using backslash to quote special chars. */
  4631. char *BackslashQuoteString(const char *s, int len, int *qlenPtr)
  4632. {
  4633. char *q = Jim_Alloc(len*2 + 1), *p;
  4634. p = q;
  4635. while (*s) {
  4636. switch (*s) {
  4637. case ' ':
  4638. case '$':
  4639. case '"':
  4640. case '[':
  4641. case ']':
  4642. case '{':
  4643. case '}':
  4644. case ';':
  4645. case '\\':
  4646. *p++ = '\\';
  4647. *p++ = *s++;
  4648. break;
  4649. case '\n': *p++ = '\\'; *p++ = 'n'; s++; break;
  4650. case '\r': *p++ = '\\'; *p++ = 'r'; s++; break;
  4651. case '\t': *p++ = '\\'; *p++ = 't'; s++; break;
  4652. case '\f': *p++ = '\\'; *p++ = 'f'; s++; break;
  4653. case '\v': *p++ = '\\'; *p++ = 'v'; s++; break;
  4654. default:
  4655. *p++ = *s++;
  4656. break;
  4657. }
  4658. }
  4659. *p = '\0';
  4660. *qlenPtr = p-q;
  4661. return q;
  4662. }
  4663. void UpdateStringOfList(struct Jim_Obj *objPtr)
  4664. {
  4665. int i, bufLen, realLength;
  4666. const char *strRep;
  4667. char *p;
  4668. int *quotingType;
  4669. Jim_Obj **ele = objPtr->internalRep.listValue.ele;
  4670. /* (Over) Estimate the space needed. */
  4671. quotingType = Jim_Alloc(sizeof(int)*objPtr->internalRep.listValue.len + 1);
  4672. bufLen = 0;
  4673. for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
  4674. int len;
  4675. strRep = Jim_GetString(ele[i], &len);
  4676. quotingType[i] = ListElementQuotingType(strRep, len);
  4677. switch (quotingType[i]) {
  4678. case JIM_ELESTR_SIMPLE: bufLen += len; break;
  4679. case JIM_ELESTR_BRACE: bufLen += len + 2; break;
  4680. case JIM_ELESTR_QUOTE: bufLen += len*2; break;
  4681. }
  4682. bufLen++; /* elements separator. */
  4683. }
  4684. bufLen++;
  4685. /* Generate the string rep. */
  4686. p = objPtr->bytes = Jim_Alloc(bufLen + 1);
  4687. realLength = 0;
  4688. for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
  4689. int len, qlen;
  4690. const char *strRep = Jim_GetString(ele[i], &len);
  4691. char *q;
  4692. switch (quotingType[i]) {
  4693. case JIM_ELESTR_SIMPLE:
  4694. memcpy(p, strRep, len);
  4695. p += len;
  4696. realLength += len;
  4697. break;
  4698. case JIM_ELESTR_BRACE:
  4699. *p++ = '{';
  4700. memcpy(p, strRep, len);
  4701. p += len;
  4702. *p++ = '}';
  4703. realLength += len + 2;
  4704. break;
  4705. case JIM_ELESTR_QUOTE:
  4706. q = BackslashQuoteString(strRep, len, &qlen);
  4707. memcpy(p, q, qlen);
  4708. Jim_Free(q);
  4709. p += qlen;
  4710. realLength += qlen;
  4711. break;
  4712. }
  4713. /* Add a separating space */
  4714. if (i + 1 != objPtr->internalRep.listValue.len) {
  4715. *p++ = ' ';
  4716. realLength ++;
  4717. }
  4718. }
  4719. *p = '\0'; /* nul term. */
  4720. objPtr->length = realLength;
  4721. Jim_Free(quotingType);
  4722. }
  4723. int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
  4724. {
  4725. struct JimParserCtx parser;
  4726. const char *str;
  4727. int strLen;
  4728. /* Get the string representation */
  4729. str = Jim_GetString(objPtr, &strLen);
  4730. /* Free the old internal repr just now and initialize the
  4731. * new one just now. The string->list conversion can't fail. */
  4732. Jim_FreeIntRep(interp, objPtr);
  4733. objPtr->typePtr = &listObjType;
  4734. objPtr->internalRep.listValue.len = 0;
  4735. objPtr->internalRep.listValue.maxLen = 0;
  4736. objPtr->internalRep.listValue.ele = NULL;
  4737. /* Convert into a list */
  4738. JimParserInit(&parser, str, strLen, 1);
  4739. while (!JimParserEof(&parser)) {
  4740. char *token;
  4741. int tokenLen, type;
  4742. Jim_Obj *elementPtr;
  4743. JimParseList(&parser);
  4744. if (JimParserTtype(&parser) != JIM_TT_STR &&
  4745. JimParserTtype(&parser) != JIM_TT_ESC)
  4746. continue;
  4747. token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
  4748. elementPtr = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
  4749. ListAppendElement(objPtr, elementPtr);
  4750. }
  4751. return JIM_OK;
  4752. }
  4753. Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements,
  4754. int len)
  4755. {
  4756. Jim_Obj *objPtr;
  4757. int i;
  4758. objPtr = Jim_NewObj(interp);
  4759. objPtr->typePtr = &listObjType;
  4760. objPtr->bytes = NULL;
  4761. objPtr->internalRep.listValue.ele = NULL;
  4762. objPtr->internalRep.listValue.len = 0;
  4763. objPtr->internalRep.listValue.maxLen = 0;
  4764. for (i = 0; i < len; i++) {
  4765. ListAppendElement(objPtr, elements[i]);
  4766. }
  4767. return objPtr;
  4768. }
  4769. /* Return a vector of Jim_Obj with the elements of a Jim list, and the
  4770. * length of the vector. Note that the user of this function should make
  4771. * sure that the list object can't shimmer while the vector returned
  4772. * is in use, this vector is the one stored inside the internal representation
  4773. * of the list object. This function is not exported, extensions should
  4774. * always access to the List object elements using Jim_ListIndex(). */
  4775. static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *argc,
  4776. Jim_Obj ***listVec)
  4777. {
  4778. Jim_ListLength(interp, listObj, argc);
  4779. assert(listObj->typePtr == &listObjType);
  4780. *listVec = listObj->internalRep.listValue.ele;
  4781. }
  4782. /* ListSortElements type values */
  4783. enum {JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_ASCII_DECR,
  4784. JIM_LSORT_NOCASE_DECR};
  4785. /* Sort the internal rep of a list. */
  4786. static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
  4787. {
  4788. return Jim_StringCompareObj(*lhsObj, *rhsObj, 0);
  4789. }
  4790. static int ListSortStringDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
  4791. {
  4792. return Jim_StringCompareObj(*lhsObj, *rhsObj, 0) * -1;
  4793. }
  4794. static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
  4795. {
  4796. return Jim_StringCompareObj(*lhsObj, *rhsObj, 1);
  4797. }
  4798. static int ListSortStringNoCaseDecr(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
  4799. {
  4800. return Jim_StringCompareObj(*lhsObj, *rhsObj, 1) * -1;
  4801. }
  4802. /* Sort a list *in place*. MUST be called with non-shared objects. */
  4803. static void ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, int type)
  4804. {
  4805. typedef int (qsort_comparator)(const void *, const void *);
  4806. int (*fn)(Jim_Obj**, Jim_Obj**);
  4807. Jim_Obj **vector;
  4808. int len;
  4809. if (Jim_IsShared(listObjPtr))
  4810. Jim_Panic(interp,"Jim_ListSortElements called with shared object");
  4811. if (listObjPtr->typePtr != &listObjType)
  4812. SetListFromAny(interp, listObjPtr);
  4813. vector = listObjPtr->internalRep.listValue.ele;
  4814. len = listObjPtr->internalRep.listValue.len;
  4815. switch (type) {
  4816. case JIM_LSORT_ASCII: fn = ListSortString; break;
  4817. case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break;
  4818. case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr; break;
  4819. case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr; break;
  4820. default:
  4821. fn = NULL; /* avoid warning */
  4822. Jim_Panic(interp,"ListSort called with invalid sort type");
  4823. }
  4824. qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
  4825. Jim_InvalidateStringRep(listObjPtr);
  4826. }
  4827. /* This is the low-level function to append an element to a list.
  4828. * The higher-level Jim_ListAppendElement() performs shared object
  4829. * check and invalidate the string repr. This version is used
  4830. * in the internals of the List Object and is not exported.
  4831. *
  4832. * NOTE: this function can be called only against objects
  4833. * with internal type of List. */
  4834. void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
  4835. {
  4836. int requiredLen = listPtr->internalRep.listValue.len + 1;
  4837. if (requiredLen > listPtr->internalRep.listValue.maxLen) {
  4838. int maxLen = requiredLen * 2;
  4839. listPtr->internalRep.listValue.ele =
  4840. Jim_Realloc(listPtr->internalRep.listValue.ele,
  4841. sizeof(Jim_Obj*)*maxLen);
  4842. listPtr->internalRep.listValue.maxLen = maxLen;
  4843. }
  4844. listPtr->internalRep.listValue.ele[listPtr->internalRep.listValue.len] =
  4845. objPtr;
  4846. listPtr->internalRep.listValue.len ++;
  4847. Jim_IncrRefCount(objPtr);
  4848. }
  4849. /* This is the low-level function to insert elements into a list.
  4850. * The higher-level Jim_ListInsertElements() performs shared object
  4851. * check and invalidate the string repr. This version is used
  4852. * in the internals of the List Object and is not exported.
  4853. *
  4854. * NOTE: this function can be called only against objects
  4855. * with internal type of List. */
  4856. void ListInsertElements(Jim_Obj *listPtr, int index, int elemc,
  4857. Jim_Obj *const *elemVec)
  4858. {
  4859. int currentLen = listPtr->internalRep.listValue.len;
  4860. int requiredLen = currentLen + elemc;
  4861. int i;
  4862. Jim_Obj **point;
  4863. if (requiredLen > listPtr->internalRep.listValue.maxLen) {
  4864. int maxLen = requiredLen * 2;
  4865. listPtr->internalRep.listValue.ele =
  4866. Jim_Realloc(listPtr->internalRep.listValue.ele,
  4867. sizeof(Jim_Obj*)*maxLen);
  4868. listPtr->internalRep.listValue.maxLen = maxLen;
  4869. }
  4870. point = listPtr->internalRep.listValue.ele + index;
  4871. memmove(point + elemc, point, (currentLen-index) * sizeof(Jim_Obj*));
  4872. for (i = 0; i < elemc; ++i) {
  4873. point[i] = elemVec[i];
  4874. Jim_IncrRefCount(point[i]);
  4875. }
  4876. listPtr->internalRep.listValue.len += elemc;
  4877. }
  4878. /* Appends every element of appendListPtr into listPtr.
  4879. * Both have to be of the list type. */
  4880. void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
  4881. {
  4882. int i, oldLen = listPtr->internalRep.listValue.len;
  4883. int appendLen = appendListPtr->internalRep.listValue.len;
  4884. int requiredLen = oldLen + appendLen;
  4885. if (requiredLen > listPtr->internalRep.listValue.maxLen) {
  4886. int maxLen = requiredLen * 2;
  4887. listPtr->internalRep.listValue.ele =
  4888. Jim_Realloc(listPtr->internalRep.listValue.ele,
  4889. sizeof(Jim_Obj*)*maxLen);
  4890. listPtr->internalRep.listValue.maxLen = maxLen;
  4891. }
  4892. for (i = 0; i < appendLen; i++) {
  4893. Jim_Obj *objPtr = appendListPtr->internalRep.listValue.ele[i];
  4894. listPtr->internalRep.listValue.ele[oldLen + i] = objPtr;
  4895. Jim_IncrRefCount(objPtr);
  4896. }
  4897. listPtr->internalRep.listValue.len += appendLen;
  4898. }
  4899. void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
  4900. {
  4901. if (Jim_IsShared(listPtr))
  4902. Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
  4903. if (listPtr->typePtr != &listObjType)
  4904. SetListFromAny(interp, listPtr);
  4905. Jim_InvalidateStringRep(listPtr);
  4906. ListAppendElement(listPtr, objPtr);
  4907. }
  4908. void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
  4909. {
  4910. if (Jim_IsShared(listPtr))
  4911. Jim_Panic(interp,"Jim_ListAppendList called with shared object");
  4912. if (listPtr->typePtr != &listObjType)
  4913. SetListFromAny(interp, listPtr);
  4914. Jim_InvalidateStringRep(listPtr);
  4915. ListAppendList(listPtr, appendListPtr);
  4916. }
  4917. void Jim_ListLength(Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr)
  4918. {
  4919. if (listPtr->typePtr != &listObjType)
  4920. SetListFromAny(interp, listPtr);
  4921. *intPtr = listPtr->internalRep.listValue.len;
  4922. }
  4923. void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
  4924. int objc, Jim_Obj *const *objVec)
  4925. {
  4926. if (Jim_IsShared(listPtr))
  4927. Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
  4928. if (listPtr->typePtr != &listObjType)
  4929. SetListFromAny(interp, listPtr);
  4930. if (index >= 0 && index > listPtr->internalRep.listValue.len)
  4931. index = listPtr->internalRep.listValue.len;
  4932. else if (index < 0)
  4933. index = 0;
  4934. Jim_InvalidateStringRep(listPtr);
  4935. ListInsertElements(listPtr, index, objc, objVec);
  4936. }
  4937. int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
  4938. Jim_Obj **objPtrPtr, int flags)
  4939. {
  4940. if (listPtr->typePtr != &listObjType)
  4941. SetListFromAny(interp, listPtr);
  4942. if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
  4943. (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
  4944. if (flags & JIM_ERRMSG) {
  4945. Jim_SetResultString(interp,
  4946. "list index out of range", -1);
  4947. }
  4948. return JIM_ERR;
  4949. }
  4950. if (index < 0)
  4951. index = listPtr->internalRep.listValue.len + index;
  4952. *objPtrPtr = listPtr->internalRep.listValue.ele[index];
  4953. return JIM_OK;
  4954. }
  4955. static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int index,
  4956. Jim_Obj *newObjPtr, int flags)
  4957. {
  4958. if (listPtr->typePtr != &listObjType)
  4959. SetListFromAny(interp, listPtr);
  4960. if ((index >= 0 && index >= listPtr->internalRep.listValue.len) ||
  4961. (index < 0 && (-index-1) >= listPtr->internalRep.listValue.len)) {
  4962. if (flags & JIM_ERRMSG) {
  4963. Jim_SetResultString(interp,
  4964. "list index out of range", -1);
  4965. }
  4966. return JIM_ERR;
  4967. }
  4968. if (index < 0)
  4969. index = listPtr->internalRep.listValue.len + index;
  4970. Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[index]);
  4971. listPtr->internalRep.listValue.ele[index] = newObjPtr;
  4972. Jim_IncrRefCount(newObjPtr);
  4973. return JIM_OK;
  4974. }
  4975. /* Modify the list stored into the variable named 'varNamePtr'
  4976. * setting the element specified by the 'indexc' indexes objects in 'indexv',
  4977. * with the new element 'newObjptr'. */
  4978. int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
  4979. Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
  4980. {
  4981. Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
  4982. int shared, i, index;
  4983. varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
  4984. if (objPtr == NULL)
  4985. return JIM_ERR;
  4986. if ((shared = Jim_IsShared(objPtr)))
  4987. varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
  4988. for (i = 0; i < indexc-1; i++) {
  4989. listObjPtr = objPtr;
  4990. if (Jim_GetIndex(interp, indexv[i], &index) != JIM_OK)
  4991. goto err;
  4992. if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
  4993. JIM_ERRMSG) != JIM_OK) {
  4994. goto err;
  4995. }
  4996. if (Jim_IsShared(objPtr)) {
  4997. objPtr = Jim_DuplicateObj(interp, objPtr);
  4998. ListSetIndex(interp, listObjPtr, index, objPtr, JIM_NONE);
  4999. }
  5000. Jim_InvalidateStringRep(listObjPtr);
  5001. }
  5002. if (Jim_GetIndex(interp, indexv[indexc-1], &index) != JIM_OK)
  5003. goto err;
  5004. if (ListSetIndex(interp, objPtr, index, newObjPtr, JIM_ERRMSG) == JIM_ERR)
  5005. goto err;
  5006. Jim_InvalidateStringRep(objPtr);
  5007. Jim_InvalidateStringRep(varObjPtr);
  5008. if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
  5009. goto err;
  5010. Jim_SetResult(interp, varObjPtr);
  5011. return JIM_OK;
  5012. err:
  5013. if (shared) {
  5014. Jim_FreeNewObj(interp, varObjPtr);
  5015. }
  5016. return JIM_ERR;
  5017. }
  5018. Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
  5019. {
  5020. int i;
  5021. /* If all the objects in objv are lists without string rep.
  5022. * it's possible to return a list as result, that's the
  5023. * concatenation of all the lists. */
  5024. for (i = 0; i < objc; i++) {
  5025. if (objv[i]->typePtr != &listObjType || objv[i]->bytes)
  5026. break;
  5027. }
  5028. if (i == objc) {
  5029. Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
  5030. for (i = 0; i < objc; i++)
  5031. Jim_ListAppendList(interp, objPtr, objv[i]);
  5032. return objPtr;
  5033. } else {
  5034. /* Else... we have to glue strings together */
  5035. int len = 0, objLen;
  5036. char *bytes, *p;
  5037. /* Compute the length */
  5038. for (i = 0; i < objc; i++) {
  5039. Jim_GetString(objv[i], &objLen);
  5040. len += objLen;
  5041. }
  5042. if (objc) len += objc-1;
  5043. /* Create the string rep, and a stinrg object holding it. */
  5044. p = bytes = Jim_Alloc(len + 1);
  5045. for (i = 0; i < objc; i++) {
  5046. const char *s = Jim_GetString(objv[i], &objLen);
  5047. while (objLen && (*s == ' ' || *s == '\t' || *s == '\n'))
  5048. {
  5049. s++; objLen--; len--;
  5050. }
  5051. while (objLen && (s[objLen-1] == ' ' ||
  5052. s[objLen-1] == '\n' || s[objLen-1] == '\t')) {
  5053. objLen--; len--;
  5054. }
  5055. memcpy(p, s, objLen);
  5056. p += objLen;
  5057. if (objLen && i + 1 != objc) {
  5058. *p++ = ' ';
  5059. } else if (i + 1 != objc) {
  5060. /* Drop the space calcuated for this
  5061. * element that is instead null. */
  5062. len--;
  5063. }
  5064. }
  5065. *p = '\0';
  5066. return Jim_NewStringObjNoAlloc(interp, bytes, len);
  5067. }
  5068. }
  5069. /* Returns a list composed of the elements in the specified range.
  5070. * first and start are directly accepted as Jim_Objects and
  5071. * processed for the end?-index? case. */
  5072. Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
  5073. {
  5074. int first, last;
  5075. int len, rangeLen;
  5076. if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
  5077. Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
  5078. return NULL;
  5079. Jim_ListLength(interp, listObjPtr, &len); /* will convert into list */
  5080. first = JimRelToAbsIndex(len, first);
  5081. last = JimRelToAbsIndex(len, last);
  5082. JimRelToAbsRange(len, first, last, &first, &last, &rangeLen);
  5083. return Jim_NewListObj(interp,
  5084. listObjPtr->internalRep.listValue.ele + first, rangeLen);
  5085. }
  5086. /* -----------------------------------------------------------------------------
  5087. * Dict object
  5088. * ---------------------------------------------------------------------------*/
  5089. static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
  5090. static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
  5091. static void UpdateStringOfDict(struct Jim_Obj *objPtr);
  5092. static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  5093. /* Dict HashTable Type.
  5094. *
  5095. * Keys and Values are Jim objects. */
  5096. unsigned int JimObjectHTHashFunction(const void *key)
  5097. {
  5098. const char *str;
  5099. Jim_Obj *objPtr = (Jim_Obj*) key;
  5100. int len, h;
  5101. str = Jim_GetString(objPtr, &len);
  5102. h = Jim_GenHashFunction((unsigned char*)str, len);
  5103. return h;
  5104. }
  5105. int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
  5106. {
  5107. JIM_NOTUSED(privdata);
  5108. return Jim_StringEqObj((Jim_Obj*)key1, (Jim_Obj*)key2, 0);
  5109. }
  5110. static void JimObjectHTKeyValDestructor(void *interp, void *val)
  5111. {
  5112. Jim_Obj *objPtr = val;
  5113. Jim_DecrRefCount(interp, objPtr);
  5114. }
  5115. static Jim_HashTableType JimDictHashTableType = {
  5116. JimObjectHTHashFunction, /* hash function */
  5117. NULL, /* key dup */
  5118. NULL, /* val dup */
  5119. JimObjectHTKeyCompare, /* key compare */
  5120. (void(*)(void*, const void*)) /* ATTENTION: const cast */
  5121. JimObjectHTKeyValDestructor, /* key destructor */
  5122. JimObjectHTKeyValDestructor /* val destructor */
  5123. };
  5124. /* Note that while the elements of the dict may contain references,
  5125. * the list object itself can't. This basically means that the
  5126. * dict object string representation as a whole can't contain references
  5127. * that are not presents in the single elements. */
  5128. static Jim_ObjType dictObjType = {
  5129. "dict",
  5130. FreeDictInternalRep,
  5131. DupDictInternalRep,
  5132. UpdateStringOfDict,
  5133. JIM_TYPE_NONE,
  5134. };
  5135. void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
  5136. {
  5137. JIM_NOTUSED(interp);
  5138. Jim_FreeHashTable(objPtr->internalRep.ptr);
  5139. Jim_Free(objPtr->internalRep.ptr);
  5140. }
  5141. void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
  5142. {
  5143. Jim_HashTable *ht, *dupHt;
  5144. Jim_HashTableIterator *htiter;
  5145. Jim_HashEntry *he;
  5146. /* Create a new hash table */
  5147. ht = srcPtr->internalRep.ptr;
  5148. dupHt = Jim_Alloc(sizeof(*dupHt));
  5149. Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
  5150. if (ht->size != 0)
  5151. Jim_ExpandHashTable(dupHt, ht->size);
  5152. /* Copy every element from the source to the dup hash table */
  5153. htiter = Jim_GetHashTableIterator(ht);
  5154. while ((he = Jim_NextHashEntry(htiter)) != NULL) {
  5155. const Jim_Obj *keyObjPtr = he->key;
  5156. Jim_Obj *valObjPtr = he->val;
  5157. Jim_IncrRefCount((Jim_Obj*)keyObjPtr); /* ATTENTION: const cast */
  5158. Jim_IncrRefCount(valObjPtr);
  5159. Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
  5160. }
  5161. Jim_FreeHashTableIterator(htiter);
  5162. dupPtr->internalRep.ptr = dupHt;
  5163. dupPtr->typePtr = &dictObjType;
  5164. }
  5165. void UpdateStringOfDict(struct Jim_Obj *objPtr)
  5166. {
  5167. int i, bufLen, realLength;
  5168. const char *strRep;
  5169. char *p;
  5170. int *quotingType, objc;
  5171. Jim_HashTable *ht;
  5172. Jim_HashTableIterator *htiter;
  5173. Jim_HashEntry *he;
  5174. Jim_Obj **objv;
  5175. /* Trun the hash table into a flat vector of Jim_Objects. */
  5176. ht = objPtr->internalRep.ptr;
  5177. objc = ht->used*2;
  5178. objv = Jim_Alloc(objc*sizeof(Jim_Obj*));
  5179. htiter = Jim_GetHashTableIterator(ht);
  5180. i = 0;
  5181. while ((he = Jim_NextHashEntry(htiter)) != NULL) {
  5182. objv[i++] = (Jim_Obj*)he->key; /* ATTENTION: const cast */
  5183. objv[i++] = he->val;
  5184. }
  5185. Jim_FreeHashTableIterator(htiter);
  5186. /* (Over) Estimate the space needed. */
  5187. quotingType = Jim_Alloc(sizeof(int)*objc);
  5188. bufLen = 0;
  5189. for (i = 0; i < objc; i++) {
  5190. int len;
  5191. strRep = Jim_GetString(objv[i], &len);
  5192. quotingType[i] = ListElementQuotingType(strRep, len);
  5193. switch (quotingType[i]) {
  5194. case JIM_ELESTR_SIMPLE: bufLen += len; break;
  5195. case JIM_ELESTR_BRACE: bufLen += len + 2; break;
  5196. case JIM_ELESTR_QUOTE: bufLen += len*2; break;
  5197. }
  5198. bufLen++; /* elements separator. */
  5199. }
  5200. bufLen++;
  5201. /* Generate the string rep. */
  5202. p = objPtr->bytes = Jim_Alloc(bufLen + 1);
  5203. realLength = 0;
  5204. for (i = 0; i < objc; i++) {
  5205. int len, qlen;
  5206. const char *strRep = Jim_GetString(objv[i], &len);
  5207. char *q;
  5208. switch (quotingType[i]) {
  5209. case JIM_ELESTR_SIMPLE:
  5210. memcpy(p, strRep, len);
  5211. p += len;
  5212. realLength += len;
  5213. break;
  5214. case JIM_ELESTR_BRACE:
  5215. *p++ = '{';
  5216. memcpy(p, strRep, len);
  5217. p += len;
  5218. *p++ = '}';
  5219. realLength += len + 2;
  5220. break;
  5221. case JIM_ELESTR_QUOTE:
  5222. q = BackslashQuoteString(strRep, len, &qlen);
  5223. memcpy(p, q, qlen);
  5224. Jim_Free(q);
  5225. p += qlen;
  5226. realLength += qlen;
  5227. break;
  5228. }
  5229. /* Add a separating space */
  5230. if (i + 1 != objc) {
  5231. *p++ = ' ';
  5232. realLength ++;
  5233. }
  5234. }
  5235. *p = '\0'; /* nul term. */
  5236. objPtr->length = realLength;
  5237. Jim_Free(quotingType);
  5238. Jim_Free(objv);
  5239. }
  5240. int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
  5241. {
  5242. struct JimParserCtx parser;
  5243. Jim_HashTable *ht;
  5244. Jim_Obj *objv[2];
  5245. const char *str;
  5246. int i, strLen;
  5247. /* Get the string representation */
  5248. str = Jim_GetString(objPtr, &strLen);
  5249. /* Free the old internal repr just now and initialize the
  5250. * new one just now. The string->list conversion can't fail. */
  5251. Jim_FreeIntRep(interp, objPtr);
  5252. ht = Jim_Alloc(sizeof(*ht));
  5253. Jim_InitHashTable(ht, &JimDictHashTableType, interp);
  5254. objPtr->typePtr = &dictObjType;
  5255. objPtr->internalRep.ptr = ht;
  5256. /* Convert into a dict */
  5257. JimParserInit(&parser, str, strLen, 1);
  5258. i = 0;
  5259. while (!JimParserEof(&parser)) {
  5260. char *token;
  5261. int tokenLen, type;
  5262. JimParseList(&parser);
  5263. if (JimParserTtype(&parser) != JIM_TT_STR &&
  5264. JimParserTtype(&parser) != JIM_TT_ESC)
  5265. continue;
  5266. token = JimParserGetToken(&parser, &tokenLen, &type, NULL);
  5267. objv[i++] = Jim_NewStringObjNoAlloc(interp, token, tokenLen);
  5268. if (i == 2) {
  5269. i = 0;
  5270. Jim_IncrRefCount(objv[0]);
  5271. Jim_IncrRefCount(objv[1]);
  5272. if (Jim_AddHashEntry(ht, objv[0], objv[1]) != JIM_OK) {
  5273. Jim_HashEntry *he;
  5274. he = Jim_FindHashEntry(ht, objv[0]);
  5275. Jim_DecrRefCount(interp, objv[0]);
  5276. /* ATTENTION: const cast */
  5277. Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
  5278. he->val = objv[1];
  5279. }
  5280. }
  5281. }
  5282. if (i) {
  5283. Jim_FreeNewObj(interp, objv[0]);
  5284. objPtr->typePtr = NULL;
  5285. Jim_FreeHashTable(ht);
  5286. Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1);
  5287. return JIM_ERR;
  5288. }
  5289. return JIM_OK;
  5290. }
  5291. /* Dict object API */
  5292. /* Add an element to a dict. objPtr must be of the "dict" type.
  5293. * The higer-level exported function is Jim_DictAddElement().
  5294. * If an element with the specified key already exists, the value
  5295. * associated is replaced with the new one.
  5296. *
  5297. * if valueObjPtr == NULL, the key is instead removed if it exists. */
  5298. static void DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
  5299. Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
  5300. {
  5301. Jim_HashTable *ht = objPtr->internalRep.ptr;
  5302. if (valueObjPtr == NULL) { /* unset */
  5303. Jim_DeleteHashEntry(ht, keyObjPtr);
  5304. return;
  5305. }
  5306. Jim_IncrRefCount(keyObjPtr);
  5307. Jim_IncrRefCount(valueObjPtr);
  5308. if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) {
  5309. Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr);
  5310. Jim_DecrRefCount(interp, keyObjPtr);
  5311. /* ATTENTION: const cast */
  5312. Jim_DecrRefCount(interp, (Jim_Obj*)he->val);
  5313. he->val = valueObjPtr;
  5314. }
  5315. }
  5316. /* Add an element, higher-level interface for DictAddElement().
  5317. * If valueObjPtr == NULL, the key is removed if it exists. */
  5318. int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
  5319. Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
  5320. {
  5321. if (Jim_IsShared(objPtr))
  5322. Jim_Panic(interp,"Jim_DictAddElement called with shared object");
  5323. if (objPtr->typePtr != &dictObjType) {
  5324. if (SetDictFromAny(interp, objPtr) != JIM_OK)
  5325. return JIM_ERR;
  5326. }
  5327. DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
  5328. Jim_InvalidateStringRep(objPtr);
  5329. return JIM_OK;
  5330. }
  5331. Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
  5332. {
  5333. Jim_Obj *objPtr;
  5334. int i;
  5335. if (len % 2)
  5336. Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
  5337. objPtr = Jim_NewObj(interp);
  5338. objPtr->typePtr = &dictObjType;
  5339. objPtr->bytes = NULL;
  5340. objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
  5341. Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
  5342. for (i = 0; i < len; i += 2)
  5343. DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
  5344. return objPtr;
  5345. }
  5346. /* Return the value associated to the specified dict key */
  5347. int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
  5348. Jim_Obj **objPtrPtr, int flags)
  5349. {
  5350. Jim_HashEntry *he;
  5351. Jim_HashTable *ht;
  5352. if (dictPtr->typePtr != &dictObjType) {
  5353. if (SetDictFromAny(interp, dictPtr) != JIM_OK)
  5354. return JIM_ERR;
  5355. }
  5356. ht = dictPtr->internalRep.ptr;
  5357. if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
  5358. if (flags & JIM_ERRMSG) {
  5359. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  5360. Jim_AppendStrings(interp, Jim_GetResult(interp),
  5361. "key \"", Jim_GetString(keyPtr, NULL),
  5362. "\" not found in dictionary", NULL);
  5363. }
  5364. return JIM_ERR;
  5365. }
  5366. *objPtrPtr = he->val;
  5367. return JIM_OK;
  5368. }
  5369. /* Return the value associated to the specified dict keys */
  5370. int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
  5371. Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
  5372. {
  5373. Jim_Obj *objPtr = NULL;
  5374. int i;
  5375. if (keyc == 0) {
  5376. *objPtrPtr = dictPtr;
  5377. return JIM_OK;
  5378. }
  5379. for (i = 0; i < keyc; i++) {
  5380. if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags)
  5381. != JIM_OK)
  5382. return JIM_ERR;
  5383. dictPtr = objPtr;
  5384. }
  5385. *objPtrPtr = objPtr;
  5386. return JIM_OK;
  5387. }
  5388. /* Modify the dict stored into the variable named 'varNamePtr'
  5389. * setting the element specified by the 'keyc' keys objects in 'keyv',
  5390. * with the new value of the element 'newObjPtr'.
  5391. *
  5392. * If newObjPtr == NULL the operation is to remove the given key
  5393. * from the dictionary. */
  5394. int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
  5395. Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr)
  5396. {
  5397. Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
  5398. int shared, i;
  5399. varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
  5400. if (objPtr == NULL) {
  5401. if (newObjPtr == NULL) /* Cannot remove a key from non existing var */
  5402. return JIM_ERR;
  5403. varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
  5404. if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
  5405. Jim_FreeNewObj(interp, varObjPtr);
  5406. return JIM_ERR;
  5407. }
  5408. }
  5409. if ((shared = Jim_IsShared(objPtr)))
  5410. varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
  5411. for (i = 0; i < keyc-1; i++) {
  5412. dictObjPtr = objPtr;
  5413. /* Check if it's a valid dictionary */
  5414. if (dictObjPtr->typePtr != &dictObjType) {
  5415. if (SetDictFromAny(interp, dictObjPtr) != JIM_OK)
  5416. goto err;
  5417. }
  5418. /* Check if the given key exists. */
  5419. Jim_InvalidateStringRep(dictObjPtr);
  5420. if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
  5421. newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK)
  5422. {
  5423. /* This key exists at the current level.
  5424. * Make sure it's not shared!. */
  5425. if (Jim_IsShared(objPtr)) {
  5426. objPtr = Jim_DuplicateObj(interp, objPtr);
  5427. DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
  5428. }
  5429. } else {
  5430. /* Key not found. If it's an [unset] operation
  5431. * this is an error. Only the last key may not
  5432. * exist. */
  5433. if (newObjPtr == NULL)
  5434. goto err;
  5435. /* Otherwise set an empty dictionary
  5436. * as key's value. */
  5437. objPtr = Jim_NewDictObj(interp, NULL, 0);
  5438. DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
  5439. }
  5440. }
  5441. if (Jim_DictAddElement(interp, objPtr, keyv[keyc-1], newObjPtr)
  5442. != JIM_OK)
  5443. goto err;
  5444. Jim_InvalidateStringRep(objPtr);
  5445. Jim_InvalidateStringRep(varObjPtr);
  5446. if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
  5447. goto err;
  5448. Jim_SetResult(interp, varObjPtr);
  5449. return JIM_OK;
  5450. err:
  5451. if (shared) {
  5452. Jim_FreeNewObj(interp, varObjPtr);
  5453. }
  5454. return JIM_ERR;
  5455. }
  5456. /* -----------------------------------------------------------------------------
  5457. * Index object
  5458. * ---------------------------------------------------------------------------*/
  5459. static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
  5460. static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  5461. static Jim_ObjType indexObjType = {
  5462. "index",
  5463. NULL,
  5464. NULL,
  5465. UpdateStringOfIndex,
  5466. JIM_TYPE_NONE,
  5467. };
  5468. void UpdateStringOfIndex(struct Jim_Obj *objPtr)
  5469. {
  5470. int len;
  5471. char buf[JIM_INTEGER_SPACE + 1];
  5472. if (objPtr->internalRep.indexValue >= 0)
  5473. len = sprintf(buf, "%d", objPtr->internalRep.indexValue);
  5474. else if (objPtr->internalRep.indexValue == -1)
  5475. len = sprintf(buf, "end");
  5476. else {
  5477. len = sprintf(buf, "end%d", objPtr->internalRep.indexValue + 1);
  5478. }
  5479. objPtr->bytes = Jim_Alloc(len + 1);
  5480. memcpy(objPtr->bytes, buf, len + 1);
  5481. objPtr->length = len;
  5482. }
  5483. int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
  5484. {
  5485. int index, end = 0;
  5486. const char *str;
  5487. /* Get the string representation */
  5488. str = Jim_GetString(objPtr, NULL);
  5489. /* Try to convert into an index */
  5490. if (!strcmp(str, "end")) {
  5491. index = 0;
  5492. end = 1;
  5493. } else {
  5494. if (!strncmp(str, "end-", 4)) {
  5495. str += 4;
  5496. end = 1;
  5497. }
  5498. if (Jim_StringToIndex(str, &index) != JIM_OK) {
  5499. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  5500. Jim_AppendStrings(interp, Jim_GetResult(interp),
  5501. "bad index \"", Jim_GetString(objPtr, NULL), "\": "
  5502. "must be integer or end?-integer?", NULL);
  5503. return JIM_ERR;
  5504. }
  5505. }
  5506. if (end) {
  5507. if (index < 0)
  5508. index = INT_MAX;
  5509. else
  5510. index = -(index + 1);
  5511. } else if (index < 0)
  5512. index = -INT_MAX;
  5513. /* Free the old internal repr and set the new one. */
  5514. Jim_FreeIntRep(interp, objPtr);
  5515. objPtr->typePtr = &indexObjType;
  5516. objPtr->internalRep.indexValue = index;
  5517. return JIM_OK;
  5518. }
  5519. int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
  5520. {
  5521. /* Avoid shimmering if the object is an integer. */
  5522. if (objPtr->typePtr == &intObjType) {
  5523. jim_wide val = objPtr->internalRep.wideValue;
  5524. if (!(val < LONG_MIN) && !(val > LONG_MAX)) {
  5525. *indexPtr = (val < 0) ? -INT_MAX : (long)val;;
  5526. return JIM_OK;
  5527. }
  5528. }
  5529. if (objPtr->typePtr != &indexObjType &&
  5530. SetIndexFromAny(interp, objPtr) == JIM_ERR)
  5531. return JIM_ERR;
  5532. *indexPtr = objPtr->internalRep.indexValue;
  5533. return JIM_OK;
  5534. }
  5535. /* -----------------------------------------------------------------------------
  5536. * Return Code Object.
  5537. * ---------------------------------------------------------------------------*/
  5538. static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
  5539. static Jim_ObjType returnCodeObjType = {
  5540. "return-code",
  5541. NULL,
  5542. NULL,
  5543. NULL,
  5544. JIM_TYPE_NONE,
  5545. };
  5546. int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
  5547. {
  5548. const char *str;
  5549. int strLen, returnCode;
  5550. jim_wide wideValue;
  5551. /* Get the string representation */
  5552. str = Jim_GetString(objPtr, &strLen);
  5553. /* Try to convert into an integer */
  5554. if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
  5555. returnCode = (int) wideValue;
  5556. else if (!JimStringCompare(str, strLen, "ok", 2, JIM_NOCASE))
  5557. returnCode = JIM_OK;
  5558. else if (!JimStringCompare(str, strLen, "error", 5, JIM_NOCASE))
  5559. returnCode = JIM_ERR;
  5560. else if (!JimStringCompare(str, strLen, "return", 6, JIM_NOCASE))
  5561. returnCode = JIM_RETURN;
  5562. else if (!JimStringCompare(str, strLen, "break", 5, JIM_NOCASE))
  5563. returnCode = JIM_BREAK;
  5564. else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
  5565. returnCode = JIM_CONTINUE;
  5566. else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
  5567. returnCode = JIM_EVAL;
  5568. else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
  5569. returnCode = JIM_EXIT;
  5570. else {
  5571. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  5572. Jim_AppendStrings(interp, Jim_GetResult(interp),
  5573. "expected return code but got '", str, "'",
  5574. NULL);
  5575. return JIM_ERR;
  5576. }
  5577. /* Free the old internal repr and set the new one. */
  5578. Jim_FreeIntRep(interp, objPtr);
  5579. objPtr->typePtr = &returnCodeObjType;
  5580. objPtr->internalRep.returnCode = returnCode;
  5581. return JIM_OK;
  5582. }
  5583. int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
  5584. {
  5585. if (objPtr->typePtr != &returnCodeObjType &&
  5586. SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
  5587. return JIM_ERR;
  5588. *intPtr = objPtr->internalRep.returnCode;
  5589. return JIM_OK;
  5590. }
  5591. /* -----------------------------------------------------------------------------
  5592. * Expression Parsing
  5593. * ---------------------------------------------------------------------------*/
  5594. static int JimParseExprOperator(struct JimParserCtx *pc);
  5595. static int JimParseExprNumber(struct JimParserCtx *pc);
  5596. static int JimParseExprIrrational(struct JimParserCtx *pc);
  5597. /* Exrp's Stack machine operators opcodes. */
  5598. /* Binary operators (numbers) */
  5599. #define JIM_EXPROP_BINARY_NUM_FIRST 0 /* first */
  5600. #define JIM_EXPROP_MUL 0
  5601. #define JIM_EXPROP_DIV 1
  5602. #define JIM_EXPROP_MOD 2
  5603. #define JIM_EXPROP_SUB 3
  5604. #define JIM_EXPROP_ADD 4
  5605. #define JIM_EXPROP_LSHIFT 5
  5606. #define JIM_EXPROP_RSHIFT 6
  5607. #define JIM_EXPROP_ROTL 7
  5608. #define JIM_EXPROP_ROTR 8
  5609. #define JIM_EXPROP_LT 9
  5610. #define JIM_EXPROP_GT 10
  5611. #define JIM_EXPROP_LTE 11
  5612. #define JIM_EXPROP_GTE 12
  5613. #define JIM_EXPROP_NUMEQ 13
  5614. #define JIM_EXPROP_NUMNE 14
  5615. #define JIM_EXPROP_BITAND 15
  5616. #define JIM_EXPROP_BITXOR 16
  5617. #define JIM_EXPROP_BITOR 17
  5618. #define JIM_EXPROP_LOGICAND 18
  5619. #define JIM_EXPROP_LOGICOR 19
  5620. #define JIM_EXPROP_LOGICAND_LEFT 20
  5621. #define JIM_EXPROP_LOGICOR_LEFT 21
  5622. #define JIM_EXPROP_POW 22
  5623. #define JIM_EXPROP_BINARY_NUM_LAST 22 /* last */
  5624. /* Binary operators (strings) */
  5625. #define JIM_EXPROP_STREQ 23
  5626. #define JIM_EXPROP_STRNE 24
  5627. /* Unary operators (numbers) */
  5628. #define JIM_EXPROP_NOT 25
  5629. #define JIM_EXPROP_BITNOT 26
  5630. #define JIM_EXPROP_UNARYMINUS 27
  5631. #define JIM_EXPROP_UNARYPLUS 28
  5632. #define JIM_EXPROP_LOGICAND_RIGHT 29
  5633. #define JIM_EXPROP_LOGICOR_RIGHT 30
  5634. /* Ternary operators */
  5635. #define JIM_EXPROP_TERNARY 31
  5636. /* Operands */
  5637. #define JIM_EXPROP_NUMBER 32
  5638. #define JIM_EXPROP_COMMAND 33
  5639. #define JIM_EXPROP_VARIABLE 34
  5640. #define JIM_EXPROP_DICTSUGAR 35
  5641. #define JIM_EXPROP_SUBST 36
  5642. #define JIM_EXPROP_STRING 37
  5643. /* Operators table */
  5644. typedef struct Jim_ExprOperator {
  5645. const char *name;
  5646. int precedence;
  5647. int arity;
  5648. int opcode;
  5649. } Jim_ExprOperator;
  5650. /* name - precedence - arity - opcode */
  5651. static struct Jim_ExprOperator Jim_ExprOperators[] = {
  5652. {"!", 300, 1, JIM_EXPROP_NOT},
  5653. {"~", 300, 1, JIM_EXPROP_BITNOT},
  5654. {"unarymin", 300, 1, JIM_EXPROP_UNARYMINUS},
  5655. {"unaryplus", 300, 1, JIM_EXPROP_UNARYPLUS},
  5656. {"**", 250, 2, JIM_EXPROP_POW},
  5657. {"*", 200, 2, JIM_EXPROP_MUL},
  5658. {"/", 200, 2, JIM_EXPROP_DIV},
  5659. {"%", 200, 2, JIM_EXPROP_MOD},
  5660. {"-", 100, 2, JIM_EXPROP_SUB},
  5661. {"+", 100, 2, JIM_EXPROP_ADD},
  5662. {"<<<", 90, 3, JIM_EXPROP_ROTL},
  5663. {">>>", 90, 3, JIM_EXPROP_ROTR},
  5664. {"<<", 90, 2, JIM_EXPROP_LSHIFT},
  5665. {">>", 90, 2, JIM_EXPROP_RSHIFT},
  5666. {"<", 80, 2, JIM_EXPROP_LT},
  5667. {">", 80, 2, JIM_EXPROP_GT},
  5668. {"<=", 80, 2, JIM_EXPROP_LTE},
  5669. {">=", 80, 2, JIM_EXPROP_GTE},
  5670. {"==", 70, 2, JIM_EXPROP_NUMEQ},
  5671. {"!=", 70, 2, JIM_EXPROP_NUMNE},
  5672. {"eq", 60, 2, JIM_EXPROP_STREQ},
  5673. {"ne", 60, 2, JIM_EXPROP_STRNE},
  5674. {"&", 50, 2, JIM_EXPROP_BITAND},
  5675. {"^", 49, 2, JIM_EXPROP_BITXOR},
  5676. {"|", 48, 2, JIM_EXPROP_BITOR},
  5677. {"&&", 10, 2, JIM_EXPROP_LOGICAND},
  5678. {"||", 10, 2, JIM_EXPROP_LOGICOR},
  5679. {"?", 5, 3, JIM_EXPROP_TERNARY},
  5680. /* private operators */
  5681. {NULL, 10, 2, JIM_EXPROP_LOGICAND_LEFT},
  5682. {NULL, 10, 1, JIM_EXPROP_LOGICAND_RIGHT},
  5683. {NULL, 10, 2, JIM_EXPROP_LOGICOR_LEFT},
  5684. {NULL, 10, 1, JIM_EXPROP_LOGICOR_RIGHT},
  5685. };
  5686. #define JIM_EXPR_OPERATORS_NUM \
  5687. (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
  5688. int JimParseExpression(struct JimParserCtx *pc)
  5689. {
  5690. /* Discard spaces and quoted newline */
  5691. while (*(pc->p) == ' ' ||
  5692. *(pc->p) == '\t' ||
  5693. *(pc->p) == '\r' ||
  5694. *(pc->p) == '\n' ||
  5695. (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
  5696. pc->p++; pc->len--;
  5697. }
  5698. if (pc->len == 0) {
  5699. pc->tstart = pc->tend = pc->p;
  5700. pc->tline = pc->linenr;
  5701. pc->tt = JIM_TT_EOL;
  5702. pc->eof = 1;
  5703. return JIM_OK;
  5704. }
  5705. switch (*(pc->p)) {
  5706. case '(':
  5707. pc->tstart = pc->tend = pc->p;
  5708. pc->tline = pc->linenr;
  5709. pc->tt = JIM_TT_SUBEXPR_START;
  5710. pc->p++; pc->len--;
  5711. break;
  5712. case ')':
  5713. pc->tstart = pc->tend = pc->p;
  5714. pc->tline = pc->linenr;
  5715. pc->tt = JIM_TT_SUBEXPR_END;
  5716. pc->p++; pc->len--;
  5717. break;
  5718. case '[':
  5719. return JimParseCmd(pc);
  5720. break;
  5721. case '$':
  5722. if (JimParseVar(pc) == JIM_ERR)
  5723. return JimParseExprOperator(pc);
  5724. else
  5725. return JIM_OK;
  5726. break;
  5727. case '-':
  5728. if ((pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_EXPR_OPERATOR) &&
  5729. isdigit((int)*(pc->p + 1)))
  5730. return JimParseExprNumber(pc);
  5731. else
  5732. return JimParseExprOperator(pc);
  5733. break;
  5734. case '0': case '1': case '2': case '3': case '4':
  5735. case '5': case '6': case '7': case '8': case '9': case '.':
  5736. return JimParseExprNumber(pc);
  5737. break;
  5738. case '"':
  5739. case '{':
  5740. /* Here it's possible to reuse the List String parsing. */
  5741. pc->tt = JIM_TT_NONE; /* Make sure it's sensed as a new word. */
  5742. return JimParseListStr(pc);
  5743. break;
  5744. case 'N': case 'I':
  5745. case 'n': case 'i':
  5746. if (JimParseExprIrrational(pc) == JIM_ERR)
  5747. return JimParseExprOperator(pc);
  5748. break;
  5749. default:
  5750. return JimParseExprOperator(pc);
  5751. break;
  5752. }
  5753. return JIM_OK;
  5754. }
  5755. int JimParseExprNumber(struct JimParserCtx *pc)
  5756. {
  5757. int allowdot = 1;
  5758. int allowhex = 0;
  5759. pc->tstart = pc->p;
  5760. pc->tline = pc->linenr;
  5761. if (*pc->p == '-') {
  5762. pc->p++; pc->len--;
  5763. }
  5764. while (isdigit((int)*pc->p)
  5765. || (allowhex && isxdigit((int)*pc->p))
  5766. || (allowdot && *pc->p == '.')
  5767. || (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
  5768. (*pc->p == 'x' || *pc->p == 'X'))
  5769. )
  5770. {
  5771. if ((*pc->p == 'x') || (*pc->p == 'X')) {
  5772. allowhex = 1;
  5773. allowdot = 0;
  5774. }
  5775. if (*pc->p == '.')
  5776. allowdot = 0;
  5777. pc->p++; pc->len--;
  5778. if (!allowdot && *pc->p == 'e' && *(pc->p + 1) == '-') {
  5779. pc->p += 2; pc->len -= 2;
  5780. }
  5781. }
  5782. pc->tend = pc->p-1;
  5783. pc->tt = JIM_TT_EXPR_NUMBER;
  5784. return JIM_OK;
  5785. }
  5786. int JimParseExprIrrational(struct JimParserCtx *pc)
  5787. {
  5788. const char *Tokens[] = {"NaN", "nan", "NAN", "Inf", "inf", "INF", NULL};
  5789. const char **token;
  5790. for (token = Tokens; *token != NULL; token++) {
  5791. int len = strlen(*token);
  5792. if (strncmp(*token, pc->p, len) == 0) {
  5793. pc->tstart = pc->p;
  5794. pc->tend = pc->p + len - 1;
  5795. pc->p += len; pc->len -= len;
  5796. pc->tline = pc->linenr;
  5797. pc->tt = JIM_TT_EXPR_NUMBER;
  5798. return JIM_OK;
  5799. }
  5800. }
  5801. return JIM_ERR;
  5802. }
  5803. int JimParseExprOperator(struct JimParserCtx *pc)
  5804. {
  5805. int i;
  5806. int bestIdx = -1, bestLen = 0;
  5807. /* Try to get the longest match. */
  5808. for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
  5809. const char *opname;
  5810. int oplen;
  5811. opname = Jim_ExprOperators[i].name;
  5812. if (opname == NULL) continue;
  5813. oplen = strlen(opname);
  5814. if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
  5815. bestIdx = i;
  5816. bestLen = oplen;
  5817. }
  5818. }
  5819. if (bestIdx == -1) return JIM_ERR;
  5820. pc->tstart = pc->p;
  5821. pc->tend = pc->p + bestLen - 1;
  5822. pc->p += bestLen; pc->len -= bestLen;
  5823. pc->tline = pc->linenr;
  5824. pc->tt = JIM_TT_EXPR_OPERATOR;
  5825. return JIM_OK;
  5826. }
  5827. struct Jim_ExprOperator *JimExprOperatorInfo(const char *opname)
  5828. {
  5829. int i;
  5830. for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
  5831. if (Jim_ExprOperators[i].name &&
  5832. strcmp(opname, Jim_ExprOperators[i].name) == 0)
  5833. return &Jim_ExprOperators[i];
  5834. return NULL;
  5835. }
  5836. struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
  5837. {
  5838. int i;
  5839. for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++)
  5840. if (Jim_ExprOperators[i].opcode == opcode)
  5841. return &Jim_ExprOperators[i];
  5842. return NULL;
  5843. }
  5844. /* -----------------------------------------------------------------------------
  5845. * Expression Object
  5846. * ---------------------------------------------------------------------------*/
  5847. static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
  5848. static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
  5849. static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
  5850. static Jim_ObjType exprObjType = {
  5851. "expression",
  5852. FreeExprInternalRep,
  5853. DupExprInternalRep,
  5854. NULL,
  5855. JIM_TYPE_REFERENCES,
  5856. };
  5857. /* Expr bytecode structure */
  5858. typedef struct ExprByteCode {
  5859. int *opcode; /* Integer array of opcodes. */
  5860. Jim_Obj **obj; /* Array of associated Jim Objects. */
  5861. int len; /* Bytecode length */
  5862. int inUse; /* Used for sharing. */
  5863. } ExprByteCode;
  5864. void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
  5865. {
  5866. int i;
  5867. ExprByteCode *expr = (void*) objPtr->internalRep.ptr;
  5868. expr->inUse--;
  5869. if (expr->inUse != 0) return;
  5870. for (i = 0; i < expr->len; i++)
  5871. Jim_DecrRefCount(interp, expr->obj[i]);
  5872. Jim_Free(expr->opcode);
  5873. Jim_Free(expr->obj);
  5874. Jim_Free(expr);
  5875. }
  5876. void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
  5877. {
  5878. JIM_NOTUSED(interp);
  5879. JIM_NOTUSED(srcPtr);
  5880. /* Just returns an simple string. */
  5881. dupPtr->typePtr = NULL;
  5882. }
  5883. /* Add a new instruction to an expression bytecode structure. */
  5884. static void ExprObjAddInstr(Jim_Interp *interp, ExprByteCode *expr,
  5885. int opcode, char *str, int len)
  5886. {
  5887. expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len + 1));
  5888. expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len + 1));
  5889. expr->opcode[expr->len] = opcode;
  5890. expr->obj[expr->len] = Jim_NewStringObjNoAlloc(interp, str, len);
  5891. Jim_IncrRefCount(expr->obj[expr->len]);
  5892. expr->len++;
  5893. }
  5894. /* Check if an expr program looks correct. */
  5895. static int ExprCheckCorrectness(ExprByteCode *expr)
  5896. {
  5897. int i;
  5898. int stacklen = 0;
  5899. /* Try to check if there are stack underflows,
  5900. * and make sure at the end of the program there is
  5901. * a single result on the stack. */
  5902. for (i = 0; i < expr->len; i++) {
  5903. switch (expr->opcode[i]) {
  5904. case JIM_EXPROP_NUMBER:
  5905. case JIM_EXPROP_STRING:
  5906. case JIM_EXPROP_SUBST:
  5907. case JIM_EXPROP_VARIABLE:
  5908. case JIM_EXPROP_DICTSUGAR:
  5909. case JIM_EXPROP_COMMAND:
  5910. stacklen++;
  5911. break;
  5912. case JIM_EXPROP_NOT:
  5913. case JIM_EXPROP_BITNOT:
  5914. case JIM_EXPROP_UNARYMINUS:
  5915. case JIM_EXPROP_UNARYPLUS:
  5916. /* Unary operations */
  5917. if (stacklen < 1) return JIM_ERR;
  5918. break;
  5919. case JIM_EXPROP_ADD:
  5920. case JIM_EXPROP_SUB:
  5921. case JIM_EXPROP_MUL:
  5922. case JIM_EXPROP_DIV:
  5923. case JIM_EXPROP_MOD:
  5924. case JIM_EXPROP_LT:
  5925. case JIM_EXPROP_GT:
  5926. case JIM_EXPROP_LTE:
  5927. case JIM_EXPROP_GTE:
  5928. case JIM_EXPROP_ROTL:
  5929. case JIM_EXPROP_ROTR:
  5930. case JIM_EXPROP_LSHIFT:
  5931. case JIM_EXPROP_RSHIFT:
  5932. case JIM_EXPROP_NUMEQ:
  5933. case JIM_EXPROP_NUMNE:
  5934. case JIM_EXPROP_STREQ:
  5935. case JIM_EXPROP_STRNE:
  5936. case JIM_EXPROP_BITAND:
  5937. case JIM_EXPROP_BITXOR:
  5938. case JIM_EXPROP_BITOR:
  5939. case JIM_EXPROP_LOGICAND:
  5940. case JIM_EXPROP_LOGICOR:
  5941. case JIM_EXPROP_POW:
  5942. /* binary operations */
  5943. if (stacklen < 2) return JIM_ERR;
  5944. stacklen--;
  5945. break;
  5946. default:
  5947. Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
  5948. break;
  5949. }
  5950. }
  5951. if (stacklen != 1) return JIM_ERR;
  5952. return JIM_OK;
  5953. }
  5954. static void ExprShareLiterals(Jim_Interp *interp, ExprByteCode *expr,
  5955. ScriptObj *topLevelScript)
  5956. {
  5957. int i;
  5958. return;
  5959. for (i = 0; i < expr->len; i++) {
  5960. Jim_Obj *foundObjPtr;
  5961. if (expr->obj[i] == NULL) continue;
  5962. foundObjPtr = ScriptSearchLiteral(interp, topLevelScript,
  5963. NULL, expr->obj[i]);
  5964. if (foundObjPtr != NULL) {
  5965. Jim_IncrRefCount(foundObjPtr);
  5966. Jim_DecrRefCount(interp, expr->obj[i]);
  5967. expr->obj[i] = foundObjPtr;
  5968. }
  5969. }
  5970. }
  5971. /* This procedure converts every occurrence of || and && opereators
  5972. * in lazy unary versions.
  5973. *
  5974. * a b || is converted into:
  5975. *
  5976. * a <offset> |L b |R
  5977. *
  5978. * a b && is converted into:
  5979. *
  5980. * a <offset> &L b &R
  5981. *
  5982. * "|L" checks if 'a' is true:
  5983. * 1) if it is true pushes 1 and skips <offset> istructions to reach
  5984. * the opcode just after |R.
  5985. * 2) if it is false does nothing.
  5986. * "|R" checks if 'b' is true:
  5987. * 1) if it is true pushes 1, otherwise pushes 0.
  5988. *
  5989. * "&L" checks if 'a' is true:
  5990. * 1) if it is true does nothing.
  5991. * 2) If it is false pushes 0 and skips <offset> istructions to reach
  5992. * the opcode just after &R
  5993. * "&R" checks if 'a' is true:
  5994. * if it is true pushes 1, otherwise pushes 0.
  5995. */
  5996. static void ExprMakeLazy(Jim_Interp *interp, ExprByteCode *expr)
  5997. {
  5998. while (1) {
  5999. int index = -1, leftindex, arity, i, offset;
  6000. Jim_ExprOperator *op;
  6001. /* Search for || or && */
  6002. for (i = 0; i < expr->len; i++) {
  6003. if (expr->opcode[i] == JIM_EXPROP_LOGICAND ||
  6004. expr->opcode[i] == JIM_EXPROP_LOGICOR) {
  6005. index = i;
  6006. break;
  6007. }
  6008. }
  6009. if (index == -1) return;
  6010. /* Search for the end of the first operator */
  6011. leftindex = index-1;
  6012. arity = 1;
  6013. while (arity) {
  6014. switch (expr->opcode[leftindex]) {
  6015. case JIM_EXPROP_NUMBER:
  6016. case JIM_EXPROP_COMMAND:
  6017. case JIM_EXPROP_VARIABLE:
  6018. case JIM_EXPROP_DICTSUGAR:
  6019. case JIM_EXPROP_SUBST:
  6020. case JIM_EXPROP_STRING:
  6021. break;
  6022. default:
  6023. op = JimExprOperatorInfoByOpcode(expr->opcode[leftindex]);
  6024. if (op == NULL) {
  6025. Jim_Panic(interp,"Default reached in ExprMakeLazy()");
  6026. }
  6027. arity += op->arity;
  6028. break;
  6029. }
  6030. arity--;
  6031. leftindex--;
  6032. }
  6033. leftindex++;
  6034. expr->opcode = Jim_Realloc(expr->opcode, sizeof(int)*(expr->len + 2));
  6035. expr->obj = Jim_Realloc(expr->obj, sizeof(Jim_Obj*)*(expr->len + 2));
  6036. memmove(&expr->opcode[leftindex + 2], &expr->opcode[leftindex],
  6037. sizeof(int)*(expr->len-leftindex));
  6038. memmove(&expr->obj[leftindex + 2], &expr->obj[leftindex],
  6039. sizeof(Jim_Obj*)*(expr->len-leftindex));
  6040. expr->len += 2;
  6041. index += 2;
  6042. offset = (index-leftindex)-1;
  6043. Jim_DecrRefCount(interp, expr->obj[index]);
  6044. if (expr->opcode[index] == JIM_EXPROP_LOGICAND) {
  6045. expr->opcode[leftindex + 1] = JIM_EXPROP_LOGICAND_LEFT;
  6046. expr->opcode[index] = JIM_EXPROP_LOGICAND_RIGHT;
  6047. expr->obj[leftindex + 1] = Jim_NewStringObj(interp, "&L", -1);
  6048. expr->obj[index] = Jim_NewStringObj(interp, "&R", -1);
  6049. } else {
  6050. expr->opcode[leftindex + 1] = JIM_EXPROP_LOGICOR_LEFT;
  6051. expr->opcode[index] = JIM_EXPROP_LOGICOR_RIGHT;
  6052. expr->obj[leftindex + 1] = Jim_NewStringObj(interp, "|L", -1);
  6053. expr->obj[index] = Jim_NewStringObj(interp, "|R", -1);
  6054. }
  6055. expr->opcode[leftindex] = JIM_EXPROP_NUMBER;
  6056. expr->obj[leftindex] = Jim_NewIntObj(interp, offset);
  6057. Jim_IncrRefCount(expr->obj[index]);
  6058. Jim_IncrRefCount(expr->obj[leftindex]);
  6059. Jim_IncrRefCount(expr->obj[leftindex + 1]);
  6060. }
  6061. }
  6062. /* This method takes the string representation of an expression
  6063. * and generates a program for the Expr's stack-based VM. */
  6064. int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
  6065. {
  6066. int exprTextLen;
  6067. const char *exprText = Jim_GetString(objPtr, &exprTextLen);
  6068. struct JimParserCtx parser;
  6069. int i, shareLiterals;
  6070. ExprByteCode *expr = Jim_Alloc(sizeof(*expr));
  6071. Jim_Stack stack;
  6072. Jim_ExprOperator *op;
  6073. /* Perform literal sharing with the current procedure
  6074. * running only if this expression appears to be not generated
  6075. * at runtime. */
  6076. shareLiterals = objPtr->typePtr == &sourceObjType;
  6077. expr->opcode = NULL;
  6078. expr->obj = NULL;
  6079. expr->len = 0;
  6080. expr->inUse = 1;
  6081. Jim_InitStack(&stack);
  6082. JimParserInit(&parser, exprText, exprTextLen, 1);
  6083. while (!JimParserEof(&parser)) {
  6084. char *token;
  6085. int len, type;
  6086. if (JimParseExpression(&parser) != JIM_OK) {
  6087. Jim_SetResultString(interp, "Syntax error in expression", -1);
  6088. goto err;
  6089. }
  6090. token = JimParserGetToken(&parser, &len, &type, NULL);
  6091. if (type == JIM_TT_EOL) {
  6092. Jim_Free(token);
  6093. break;
  6094. }
  6095. switch (type) {
  6096. case JIM_TT_STR:
  6097. ExprObjAddInstr(interp, expr, JIM_EXPROP_STRING, token, len);
  6098. break;
  6099. case JIM_TT_ESC:
  6100. ExprObjAddInstr(interp, expr, JIM_EXPROP_SUBST, token, len);
  6101. break;
  6102. case JIM_TT_VAR:
  6103. ExprObjAddInstr(interp, expr, JIM_EXPROP_VARIABLE, token, len);
  6104. break;
  6105. case JIM_TT_DICTSUGAR:
  6106. ExprObjAddInstr(interp, expr, JIM_EXPROP_DICTSUGAR, token, len);
  6107. break;
  6108. case JIM_TT_CMD:
  6109. ExprObjAddInstr(interp, expr, JIM_EXPROP_COMMAND, token, len);
  6110. break;
  6111. case JIM_TT_EXPR_NUMBER:
  6112. ExprObjAddInstr(interp, expr, JIM_EXPROP_NUMBER, token, len);
  6113. break;
  6114. case JIM_TT_EXPR_OPERATOR:
  6115. op = JimExprOperatorInfo(token);
  6116. while (1) {
  6117. Jim_ExprOperator *stackTopOp;
  6118. if (Jim_StackPeek(&stack) != NULL) {
  6119. stackTopOp = JimExprOperatorInfo(Jim_StackPeek(&stack));
  6120. } else {
  6121. stackTopOp = NULL;
  6122. }
  6123. if (Jim_StackLen(&stack) && op->arity != 1 &&
  6124. stackTopOp && stackTopOp->precedence >= op->precedence)
  6125. {
  6126. ExprObjAddInstr(interp, expr, stackTopOp->opcode,
  6127. Jim_StackPeek(&stack), -1);
  6128. Jim_StackPop(&stack);
  6129. } else {
  6130. break;
  6131. }
  6132. }
  6133. Jim_StackPush(&stack, token);
  6134. break;
  6135. case JIM_TT_SUBEXPR_START:
  6136. Jim_StackPush(&stack, Jim_StrDup("("));
  6137. Jim_Free(token);
  6138. break;
  6139. case JIM_TT_SUBEXPR_END:
  6140. {
  6141. int found = 0;
  6142. while (Jim_StackLen(&stack)) {
  6143. char *opstr = Jim_StackPop(&stack);
  6144. if (!strcmp(opstr, "(")) {
  6145. Jim_Free(opstr);
  6146. found = 1;
  6147. break;
  6148. }
  6149. op = JimExprOperatorInfo(opstr);
  6150. ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
  6151. }
  6152. if (!found) {
  6153. Jim_SetResultString(interp,
  6154. "Unexpected close parenthesis", -1);
  6155. goto err;
  6156. }
  6157. }
  6158. Jim_Free(token);
  6159. break;
  6160. default:
  6161. Jim_Panic(interp,"Default reached in SetExprFromAny()");
  6162. break;
  6163. }
  6164. }
  6165. while (Jim_StackLen(&stack)) {
  6166. char *opstr = Jim_StackPop(&stack);
  6167. op = JimExprOperatorInfo(opstr);
  6168. if (op == NULL && !strcmp(opstr, "(")) {
  6169. Jim_Free(opstr);
  6170. Jim_SetResultString(interp, "Missing close parenthesis", -1);
  6171. goto err;
  6172. }
  6173. ExprObjAddInstr(interp, expr, op->opcode, opstr, -1);
  6174. }
  6175. /* Check program correctness. */
  6176. if (ExprCheckCorrectness(expr) != JIM_OK) {
  6177. Jim_SetResultString(interp, "Invalid expression", -1);
  6178. goto err;
  6179. }
  6180. /* Free the stack used for the compilation. */
  6181. Jim_FreeStackElements(&stack, Jim_Free);
  6182. Jim_FreeStack(&stack);
  6183. /* Convert || and && operators in unary |L |R and &L &R for lazyness */
  6184. ExprMakeLazy(interp, expr);
  6185. /* Perform literal sharing */
  6186. if (shareLiterals && interp->framePtr->procBodyObjPtr) {
  6187. Jim_Obj *bodyObjPtr = interp->framePtr->procBodyObjPtr;
  6188. if (bodyObjPtr->typePtr == &scriptObjType) {
  6189. ScriptObj *bodyScript = bodyObjPtr->internalRep.ptr;
  6190. ExprShareLiterals(interp, expr, bodyScript);
  6191. }
  6192. }
  6193. /* Free the old internal rep and set the new one. */
  6194. Jim_FreeIntRep(interp, objPtr);
  6195. Jim_SetIntRepPtr(objPtr, expr);
  6196. objPtr->typePtr = &exprObjType;
  6197. return JIM_OK;
  6198. err: /* we jump here on syntax/compile errors. */
  6199. Jim_FreeStackElements(&stack, Jim_Free);
  6200. Jim_FreeStack(&stack);
  6201. Jim_Free(expr->opcode);
  6202. for (i = 0; i < expr->len; i++) {
  6203. Jim_DecrRefCount(interp,expr->obj[i]);
  6204. }
  6205. Jim_Free(expr->obj);
  6206. Jim_Free(expr);
  6207. return JIM_ERR;
  6208. }
  6209. ExprByteCode *Jim_GetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
  6210. {
  6211. if (objPtr->typePtr != &exprObjType) {
  6212. if (SetExprFromAny(interp, objPtr) != JIM_OK)
  6213. return NULL;
  6214. }
  6215. return (ExprByteCode*) Jim_GetIntRepPtr(objPtr);
  6216. }
  6217. /* -----------------------------------------------------------------------------
  6218. * Expressions evaluation.
  6219. * Jim uses a specialized stack-based virtual machine for expressions,
  6220. * that takes advantage of the fact that expr's operators
  6221. * can't be redefined.
  6222. *
  6223. * Jim_EvalExpression() uses the bytecode compiled by
  6224. * SetExprFromAny() method of the "expression" object.
  6225. *
  6226. * On success a Tcl Object containing the result of the evaluation
  6227. * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
  6228. * returned.
  6229. * On error the function returns a retcode != to JIM_OK and set a suitable
  6230. * error on the interp.
  6231. * ---------------------------------------------------------------------------*/
  6232. #define JIM_EE_STATICSTACK_LEN 10
  6233. int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr,
  6234. Jim_Obj **exprResultPtrPtr)
  6235. {
  6236. ExprByteCode *expr;
  6237. Jim_Obj **stack, *staticStack[JIM_EE_STATICSTACK_LEN];
  6238. int stacklen = 0, i, error = 0, errRetCode = JIM_ERR;
  6239. Jim_IncrRefCount(exprObjPtr);
  6240. expr = Jim_GetExpression(interp, exprObjPtr);
  6241. if (!expr) {
  6242. Jim_DecrRefCount(interp, exprObjPtr);
  6243. return JIM_ERR; /* error in expression. */
  6244. }
  6245. /* In order to avoid that the internal repr gets freed due to
  6246. * shimmering of the exprObjPtr's object, we make the internal rep
  6247. * shared. */
  6248. expr->inUse++;
  6249. /* The stack-based expr VM itself */
  6250. /* Stack allocation. Expr programs have the feature that
  6251. * a program of length N can't require a stack longer than
  6252. * N. */
  6253. if (expr->len > JIM_EE_STATICSTACK_LEN)
  6254. stack = Jim_Alloc(sizeof(Jim_Obj*)*expr->len);
  6255. else
  6256. stack = staticStack;
  6257. /* Execute every istruction */
  6258. for (i = 0; i < expr->len; i++) {
  6259. Jim_Obj *A, *B, *objPtr;
  6260. jim_wide wA, wB, wC;
  6261. double dA, dB, dC;
  6262. const char *sA, *sB;
  6263. int Alen, Blen, retcode;
  6264. int opcode = expr->opcode[i];
  6265. if (opcode == JIM_EXPROP_NUMBER || opcode == JIM_EXPROP_STRING) {
  6266. stack[stacklen++] = expr->obj[i];
  6267. Jim_IncrRefCount(expr->obj[i]);
  6268. } else if (opcode == JIM_EXPROP_VARIABLE) {
  6269. objPtr = Jim_GetVariable(interp, expr->obj[i], JIM_ERRMSG);
  6270. if (objPtr == NULL) {
  6271. error = 1;
  6272. goto err;
  6273. }
  6274. stack[stacklen++] = objPtr;
  6275. Jim_IncrRefCount(objPtr);
  6276. } else if (opcode == JIM_EXPROP_SUBST) {
  6277. if ((retcode = Jim_SubstObj(interp, expr->obj[i],
  6278. &objPtr, JIM_NONE)) != JIM_OK)
  6279. {
  6280. error = 1;
  6281. errRetCode = retcode;
  6282. goto err;
  6283. }
  6284. stack[stacklen++] = objPtr;
  6285. Jim_IncrRefCount(objPtr);
  6286. } else if (opcode == JIM_EXPROP_DICTSUGAR) {
  6287. objPtr = Jim_ExpandDictSugar(interp, expr->obj[i]);
  6288. if (objPtr == NULL) {
  6289. error = 1;
  6290. goto err;
  6291. }
  6292. stack[stacklen++] = objPtr;
  6293. Jim_IncrRefCount(objPtr);
  6294. } else if (opcode == JIM_EXPROP_COMMAND) {
  6295. if ((retcode = Jim_EvalObj(interp, expr->obj[i])) != JIM_OK) {
  6296. error = 1;
  6297. errRetCode = retcode;
  6298. goto err;
  6299. }
  6300. stack[stacklen++] = interp->result;
  6301. Jim_IncrRefCount(interp->result);
  6302. } else if (opcode >= JIM_EXPROP_BINARY_NUM_FIRST &&
  6303. opcode <= JIM_EXPROP_BINARY_NUM_LAST)
  6304. {
  6305. /* Note that there isn't to increment the
  6306. * refcount of objects. the references are moved
  6307. * from stack to A and B. */
  6308. B = stack[--stacklen];
  6309. A = stack[--stacklen];
  6310. /* --- Integer --- */
  6311. if ((A->typePtr == &doubleObjType && !A->bytes) ||
  6312. (B->typePtr == &doubleObjType && !B->bytes) ||
  6313. JimGetWideNoErr(interp, A, &wA) != JIM_OK ||
  6314. JimGetWideNoErr(interp, B, &wB) != JIM_OK) {
  6315. goto trydouble;
  6316. }
  6317. Jim_DecrRefCount(interp, A);
  6318. Jim_DecrRefCount(interp, B);
  6319. switch (expr->opcode[i]) {
  6320. case JIM_EXPROP_ADD: wC = wA + wB; break;
  6321. case JIM_EXPROP_SUB: wC = wA-wB; break;
  6322. case JIM_EXPROP_MUL: wC = wA*wB; break;
  6323. case JIM_EXPROP_LT: wC = wA < wB; break;
  6324. case JIM_EXPROP_GT: wC = wA > wB; break;
  6325. case JIM_EXPROP_LTE: wC = wA <= wB; break;
  6326. case JIM_EXPROP_GTE: wC = wA >= wB; break;
  6327. case JIM_EXPROP_LSHIFT: wC = wA << wB; break;
  6328. case JIM_EXPROP_RSHIFT: wC = wA >> wB; break;
  6329. case JIM_EXPROP_NUMEQ: wC = wA == wB; break;
  6330. case JIM_EXPROP_NUMNE: wC = wA != wB; break;
  6331. case JIM_EXPROP_BITAND: wC = wA&wB; break;
  6332. case JIM_EXPROP_BITXOR: wC = wA^wB; break;
  6333. case JIM_EXPROP_BITOR: wC = wA | wB; break;
  6334. case JIM_EXPROP_POW: wC = JimPowWide(wA,wB); break;
  6335. case JIM_EXPROP_LOGICAND_LEFT:
  6336. if (wA == 0) {
  6337. i += (int)wB;
  6338. wC = 0;
  6339. } else {
  6340. continue;
  6341. }
  6342. break;
  6343. case JIM_EXPROP_LOGICOR_LEFT:
  6344. if (wA != 0) {
  6345. i += (int)wB;
  6346. wC = 1;
  6347. } else {
  6348. continue;
  6349. }
  6350. break;
  6351. case JIM_EXPROP_DIV:
  6352. if (wB == 0) goto divbyzero;
  6353. wC = wA/wB;
  6354. break;
  6355. case JIM_EXPROP_MOD:
  6356. if (wB == 0) goto divbyzero;
  6357. wC = wA%wB;
  6358. break;
  6359. case JIM_EXPROP_ROTL: {
  6360. /* uint32_t would be better. But not everyone has inttypes.h?*/
  6361. unsigned long uA = (unsigned long)wA;
  6362. #ifdef _MSC_VER
  6363. wC = _rotl(uA,(unsigned long)wB);
  6364. #else
  6365. const unsigned int S = sizeof(unsigned long) * 8;
  6366. wC = (unsigned long)((uA << wB) | (uA >> (S-wB)));
  6367. #endif
  6368. break;
  6369. }
  6370. case JIM_EXPROP_ROTR: {
  6371. unsigned long uA = (unsigned long)wA;
  6372. #ifdef _MSC_VER
  6373. wC = _rotr(uA,(unsigned long)wB);
  6374. #else
  6375. const unsigned int S = sizeof(unsigned long) * 8;
  6376. wC = (unsigned long)((uA >> wB) | (uA << (S-wB)));
  6377. #endif
  6378. break;
  6379. }
  6380. default:
  6381. wC = 0; /* avoid gcc warning */
  6382. break;
  6383. }
  6384. stack[stacklen] = Jim_NewIntObj(interp, wC);
  6385. Jim_IncrRefCount(stack[stacklen]);
  6386. stacklen++;
  6387. continue;
  6388. trydouble:
  6389. /* --- Double --- */
  6390. if (Jim_GetDouble(interp, A, &dA) != JIM_OK ||
  6391. Jim_GetDouble(interp, B, &dB) != JIM_OK) {
  6392. /* Hmmm! For compatibility, maybe convert != and == into ne and eq */
  6393. if (expr->opcode[i] == JIM_EXPROP_NUMNE) {
  6394. opcode = JIM_EXPROP_STRNE;
  6395. goto retry_as_string;
  6396. }
  6397. else if (expr->opcode[i] == JIM_EXPROP_NUMEQ) {
  6398. opcode = JIM_EXPROP_STREQ;
  6399. goto retry_as_string;
  6400. }
  6401. Jim_DecrRefCount(interp, A);
  6402. Jim_DecrRefCount(interp, B);
  6403. error = 1;
  6404. goto err;
  6405. }
  6406. Jim_DecrRefCount(interp, A);
  6407. Jim_DecrRefCount(interp, B);
  6408. switch (expr->opcode[i]) {
  6409. case JIM_EXPROP_ROTL:
  6410. case JIM_EXPROP_ROTR:
  6411. case JIM_EXPROP_LSHIFT:
  6412. case JIM_EXPROP_RSHIFT:
  6413. case JIM_EXPROP_BITAND:
  6414. case JIM_EXPROP_BITXOR:
  6415. case JIM_EXPROP_BITOR:
  6416. case JIM_EXPROP_MOD:
  6417. case JIM_EXPROP_POW:
  6418. Jim_SetResultString(interp,
  6419. "Got floating-point value where integer was expected", -1);
  6420. error = 1;
  6421. goto err;
  6422. case JIM_EXPROP_ADD: dC = dA + dB; break;
  6423. case JIM_EXPROP_SUB: dC = dA-dB; break;
  6424. case JIM_EXPROP_MUL: dC = dA*dB; break;
  6425. case JIM_EXPROP_LT: dC = dA < dB; break;
  6426. case JIM_EXPROP_GT: dC = dA > dB; break;
  6427. case JIM_EXPROP_LTE: dC = dA <= dB; break;
  6428. case JIM_EXPROP_GTE: dC = dA >= dB; break;
  6429. /* FIXME comparing floats for equality/inequality is bad juju */
  6430. case JIM_EXPROP_NUMEQ: dC = dA == dB; break;
  6431. case JIM_EXPROP_NUMNE: dC = dA != dB; break;
  6432. case JIM_EXPROP_LOGICAND_LEFT:
  6433. if (dA == 0) {
  6434. i += (int)dB;
  6435. dC = 0;
  6436. } else {
  6437. continue;
  6438. }
  6439. break;
  6440. case JIM_EXPROP_LOGICOR_LEFT:
  6441. if (dA != 0) {
  6442. i += (int)dB;
  6443. dC = 1;
  6444. } else {
  6445. continue;
  6446. }
  6447. break;
  6448. case JIM_EXPROP_DIV:
  6449. if (dB == 0) goto divbyzero;
  6450. dC = dA/dB;
  6451. break;
  6452. default:
  6453. dC = 0; /* avoid gcc warning */
  6454. break;
  6455. }
  6456. stack[stacklen] = Jim_NewDoubleObj(interp, dC);
  6457. Jim_IncrRefCount(stack[stacklen]);
  6458. stacklen++;
  6459. } else if (opcode == JIM_EXPROP_STREQ || opcode == JIM_EXPROP_STRNE) {
  6460. B = stack[--stacklen];
  6461. A = stack[--stacklen];
  6462. retry_as_string:
  6463. sA = Jim_GetString(A, &Alen);
  6464. sB = Jim_GetString(B, &Blen);
  6465. switch (opcode) {
  6466. case JIM_EXPROP_STREQ:
  6467. if (Alen == Blen && memcmp(sA, sB, Alen) ==0)
  6468. wC = 1;
  6469. else
  6470. wC = 0;
  6471. break;
  6472. case JIM_EXPROP_STRNE:
  6473. if (Alen != Blen || memcmp(sA, sB, Alen) != 0)
  6474. wC = 1;
  6475. else
  6476. wC = 0;
  6477. break;
  6478. default:
  6479. wC = 0; /* avoid gcc warning */
  6480. break;
  6481. }
  6482. Jim_DecrRefCount(interp, A);
  6483. Jim_DecrRefCount(interp, B);
  6484. stack[stacklen] = Jim_NewIntObj(interp, wC);
  6485. Jim_IncrRefCount(stack[stacklen]);
  6486. stacklen++;
  6487. } else if (opcode == JIM_EXPROP_NOT ||
  6488. opcode == JIM_EXPROP_BITNOT ||
  6489. opcode == JIM_EXPROP_LOGICAND_RIGHT ||
  6490. opcode == JIM_EXPROP_LOGICOR_RIGHT) {
  6491. /* Note that there isn't to increment the
  6492. * refcount of objects. the references are moved
  6493. * from stack to A and B. */
  6494. A = stack[--stacklen];
  6495. /* --- Integer --- */
  6496. if ((A->typePtr == &doubleObjType && !A->bytes) ||
  6497. JimGetWideNoErr(interp, A, &wA) != JIM_OK) {
  6498. goto trydouble_unary;
  6499. }
  6500. Jim_DecrRefCount(interp, A);
  6501. switch (expr->opcode[i]) {
  6502. case JIM_EXPROP_NOT: wC = !wA; break;
  6503. case JIM_EXPROP_BITNOT: wC = ~wA; break;
  6504. case JIM_EXPROP_LOGICAND_RIGHT:
  6505. case JIM_EXPROP_LOGICOR_RIGHT: wC = (wA != 0); break;
  6506. default:
  6507. wC = 0; /* avoid gcc warning */
  6508. break;
  6509. }
  6510. stack[stacklen] = Jim_NewIntObj(interp, wC);
  6511. Jim_IncrRefCount(stack[stacklen]);
  6512. stacklen++;
  6513. continue;
  6514. trydouble_unary:
  6515. /* --- Double --- */
  6516. if (Jim_GetDouble(interp, A, &dA) != JIM_OK) {
  6517. Jim_DecrRefCount(interp, A);
  6518. error = 1;
  6519. goto err;
  6520. }
  6521. Jim_DecrRefCount(interp, A);
  6522. switch (expr->opcode[i]) {
  6523. case JIM_EXPROP_NOT: dC = !dA; break;
  6524. case JIM_EXPROP_LOGICAND_RIGHT:
  6525. case JIM_EXPROP_LOGICOR_RIGHT: dC = (dA != 0); break;
  6526. case JIM_EXPROP_BITNOT:
  6527. Jim_SetResultString(interp,
  6528. "Got floating-point value where integer was expected", -1);
  6529. error = 1;
  6530. goto err;
  6531. break;
  6532. default:
  6533. dC = 0; /* avoid gcc warning */
  6534. break;
  6535. }
  6536. stack[stacklen] = Jim_NewDoubleObj(interp, dC);
  6537. Jim_IncrRefCount(stack[stacklen]);
  6538. stacklen++;
  6539. } else {
  6540. Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
  6541. }
  6542. }
  6543. err:
  6544. /* There is no need to decerement the inUse field because
  6545. * this reference is transfered back into the exprObjPtr. */
  6546. Jim_FreeIntRep(interp, exprObjPtr);
  6547. exprObjPtr->typePtr = &exprObjType;
  6548. Jim_SetIntRepPtr(exprObjPtr, expr);
  6549. Jim_DecrRefCount(interp, exprObjPtr);
  6550. if (!error) {
  6551. *exprResultPtrPtr = stack[0];
  6552. Jim_IncrRefCount(stack[0]);
  6553. errRetCode = JIM_OK;
  6554. }
  6555. for (i = 0; i < stacklen; i++) {
  6556. Jim_DecrRefCount(interp, stack[i]);
  6557. }
  6558. if (stack != staticStack)
  6559. Jim_Free(stack);
  6560. return errRetCode;
  6561. divbyzero:
  6562. error = 1;
  6563. Jim_SetResultString(interp, "Division by zero", -1);
  6564. goto err;
  6565. }
  6566. int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
  6567. {
  6568. int retcode;
  6569. jim_wide wideValue;
  6570. double doubleValue;
  6571. Jim_Obj *exprResultPtr;
  6572. retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
  6573. if (retcode != JIM_OK)
  6574. return retcode;
  6575. if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
  6576. if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK)
  6577. {
  6578. Jim_DecrRefCount(interp, exprResultPtr);
  6579. return JIM_ERR;
  6580. } else {
  6581. Jim_DecrRefCount(interp, exprResultPtr);
  6582. *boolPtr = doubleValue != 0;
  6583. return JIM_OK;
  6584. }
  6585. }
  6586. Jim_DecrRefCount(interp, exprResultPtr);
  6587. *boolPtr = wideValue != 0;
  6588. return JIM_OK;
  6589. }
  6590. /* -----------------------------------------------------------------------------
  6591. * ScanFormat String Object
  6592. * ---------------------------------------------------------------------------*/
  6593. /* This Jim_Obj will held a parsed representation of a format string passed to
  6594. * the Jim_ScanString command. For error diagnostics, the scanformat string has
  6595. * to be parsed in its entirely first and then, if correct, can be used for
  6596. * scanning. To avoid endless re-parsing, the parsed representation will be
  6597. * stored in an internal representation and re-used for performance reason. */
  6598. /* A ScanFmtPartDescr will held the information of /one/ part of the whole
  6599. * scanformat string. This part will later be used to extract information
  6600. * out from the string to be parsed by Jim_ScanString */
  6601. typedef struct ScanFmtPartDescr {
  6602. char type; /* Type of conversion (e.g. c, d, f) */
  6603. char modifier; /* Modify type (e.g. l - long, h - short */
  6604. size_t width; /* Maximal width of input to be converted */
  6605. int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
  6606. char *arg; /* Specification of a CHARSET conversion */
  6607. char *prefix; /* Prefix to be scanned literally before conversion */
  6608. } ScanFmtPartDescr;
  6609. /* The ScanFmtStringObj will held the internal representation of a scanformat
  6610. * string parsed and separated in part descriptions. Furthermore it contains
  6611. * the original string representation of the scanformat string to allow for
  6612. * fast update of the Jim_Obj's string representation part.
  6613. *
  6614. * As add-on the internal object representation add some scratch pad area
  6615. * for usage by Jim_ScanString to avoid endless allocating and freeing of
  6616. * memory for purpose of string scanning.
  6617. *
  6618. * The error member points to a static allocated string in case of a mal-
  6619. * formed scanformat string or it contains '0' (NULL) in case of a valid
  6620. * parse representation.
  6621. *
  6622. * The whole memory of the internal representation is allocated as a single
  6623. * area of memory that will be internally separated. So freeing and duplicating
  6624. * of such an object is cheap */
  6625. typedef struct ScanFmtStringObj {
  6626. jim_wide size; /* Size of internal repr in bytes */
  6627. char *stringRep; /* Original string representation */
  6628. size_t count; /* Number of ScanFmtPartDescr contained */
  6629. size_t convCount; /* Number of conversions that will assign */
  6630. size_t maxPos; /* Max position index if XPG3 is used */
  6631. const char *error; /* Ptr to error text (NULL if no error */
  6632. char *scratch; /* Some scratch pad used by Jim_ScanString */
  6633. ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */
  6634. } ScanFmtStringObj;
  6635. static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
  6636. static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
  6637. static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
  6638. static Jim_ObjType scanFmtStringObjType = {
  6639. "scanformatstring",
  6640. FreeScanFmtInternalRep,
  6641. DupScanFmtInternalRep,
  6642. UpdateStringOfScanFmt,
  6643. JIM_TYPE_NONE,
  6644. };
  6645. void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
  6646. {
  6647. JIM_NOTUSED(interp);
  6648. Jim_Free((char*)objPtr->internalRep.ptr);
  6649. objPtr->internalRep.ptr = 0;
  6650. }
  6651. void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
  6652. {
  6653. size_t size = (size_t)((ScanFmtStringObj*)srcPtr->internalRep.ptr)->size;
  6654. ScanFmtStringObj *newVec = (ScanFmtStringObj*)Jim_Alloc(size);
  6655. JIM_NOTUSED(interp);
  6656. memcpy(newVec, srcPtr->internalRep.ptr, size);
  6657. dupPtr->internalRep.ptr = newVec;
  6658. dupPtr->typePtr = &scanFmtStringObjType;
  6659. }
  6660. void UpdateStringOfScanFmt(Jim_Obj *objPtr)
  6661. {
  6662. char *bytes = ((ScanFmtStringObj*)objPtr->internalRep.ptr)->stringRep;
  6663. objPtr->bytes = Jim_StrDup(bytes);
  6664. objPtr->length = strlen(bytes);
  6665. }
  6666. /* SetScanFmtFromAny will parse a given string and create the internal
  6667. * representation of the format specification. In case of an error
  6668. * the error data member of the internal representation will be set
  6669. * to an descriptive error text and the function will be left with
  6670. * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat
  6671. * specification */
  6672. static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
  6673. {
  6674. ScanFmtStringObj *fmtObj;
  6675. char *buffer;
  6676. int maxCount, i, approxSize, lastPos = -1;
  6677. const char *fmt = objPtr->bytes;
  6678. int maxFmtLen = objPtr->length;
  6679. const char *fmtEnd = fmt + maxFmtLen;
  6680. int curr;
  6681. Jim_FreeIntRep(interp, objPtr);
  6682. /* Count how many conversions could take place maximally */
  6683. for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
  6684. if (fmt[i] == '%')
  6685. ++maxCount;
  6686. /* Calculate an approximation of the memory necessary */
  6687. approxSize = sizeof(ScanFmtStringObj) /* Size of the container */
  6688. + (maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */
  6689. + maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */
  6690. + maxFmtLen * sizeof(char) + 1 /* Original stringrep */
  6691. + maxFmtLen * sizeof(char) /* Arg for CHARSETs */
  6692. + (maxCount +1) * sizeof(char) /* '\0' for every partial */
  6693. + 1; /* safety byte */
  6694. fmtObj = (ScanFmtStringObj*)Jim_Alloc(approxSize);
  6695. memset(fmtObj, 0, approxSize);
  6696. fmtObj->size = approxSize;
  6697. fmtObj->maxPos = 0;
  6698. fmtObj->scratch = (char*)&fmtObj->descr[maxCount + 1];
  6699. fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
  6700. memcpy(fmtObj->stringRep, fmt, maxFmtLen);
  6701. buffer = fmtObj->stringRep + maxFmtLen + 1;
  6702. objPtr->internalRep.ptr = fmtObj;
  6703. objPtr->typePtr = &scanFmtStringObjType;
  6704. for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
  6705. int width = 0, skip;
  6706. ScanFmtPartDescr *descr = &fmtObj->descr[curr];
  6707. fmtObj->count++;
  6708. descr->width = 0; /* Assume width unspecified */
  6709. /* Overread and store any "literal" prefix */
  6710. if (*fmt != '%' || fmt[1] == '%') {
  6711. descr->type = 0;
  6712. descr->prefix = &buffer[i];
  6713. for (; fmt < fmtEnd; ++fmt) {
  6714. if (*fmt == '%') {
  6715. if (fmt[1] != '%') break;
  6716. ++fmt;
  6717. }
  6718. buffer[i++] = *fmt;
  6719. }
  6720. buffer[i++] = 0;
  6721. }
  6722. /* Skip the conversion introducing '%' sign */
  6723. ++fmt;
  6724. /* End reached due to non-conversion literal only? */
  6725. if (fmt >= fmtEnd)
  6726. goto done;
  6727. descr->pos = 0; /* Assume "natural" positioning */
  6728. if (*fmt == '*') {
  6729. descr->pos = -1; /* Okay, conversion will not be assigned */
  6730. ++fmt;
  6731. } else
  6732. fmtObj->convCount++; /* Otherwise count as assign-conversion */
  6733. /* Check if next token is a number (could be width or pos */
  6734. if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
  6735. fmt += skip;
  6736. /* Was the number a XPG3 position specifier? */
  6737. if (descr->pos != -1 && *fmt == '$') {
  6738. int prev;
  6739. ++fmt;
  6740. descr->pos = width;
  6741. width = 0;
  6742. /* Look if "natural" postioning and XPG3 one was mixed */
  6743. if ((lastPos == 0 && descr->pos > 0)
  6744. || (lastPos > 0 && descr->pos == 0)) {
  6745. fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
  6746. return JIM_ERR;
  6747. }
  6748. /* Look if this position was already used */
  6749. for (prev = 0; prev < curr; ++prev) {
  6750. if (fmtObj->descr[prev].pos == -1) continue;
  6751. if (fmtObj->descr[prev].pos == descr->pos) {
  6752. fmtObj->error = "same \"%n$\" conversion specifier "
  6753. "used more than once";
  6754. return JIM_ERR;
  6755. }
  6756. }
  6757. /* Try to find a width after the XPG3 specifier */
  6758. if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
  6759. descr->width = width;
  6760. fmt += skip;
  6761. }
  6762. if (descr->pos > 0 && (size_t)descr->pos > fmtObj->maxPos)
  6763. fmtObj->maxPos = descr->pos;
  6764. } else {
  6765. /* Number was not a XPG3, so it has to be a width */
  6766. descr->width = width;
  6767. }
  6768. }
  6769. /* If positioning mode was undetermined yet, fix this */
  6770. if (lastPos == -1)
  6771. lastPos = descr->pos;
  6772. /* Handle CHARSET conversion type ... */
  6773. if (*fmt == '[') {
  6774. int swapped = 1, beg = i, end, j;
  6775. descr->type = '[';
  6776. descr->arg = &buffer[i];
  6777. ++fmt;
  6778. if (*fmt == '^') buffer[i++] = *fmt++;
  6779. if (*fmt == ']') buffer[i++] = *fmt++;
  6780. while (*fmt && *fmt != ']') buffer[i++] = *fmt++;
  6781. if (*fmt != ']') {
  6782. fmtObj->error = "unmatched [ in format string";
  6783. return JIM_ERR;
  6784. }
  6785. end = i;
  6786. buffer[i++] = 0;
  6787. /* In case a range fence was given "backwards", swap it */
  6788. while (swapped) {
  6789. swapped = 0;
  6790. for (j = beg + 1; j < end-1; ++j) {
  6791. if (buffer[j] == '-' && buffer[j-1] > buffer[j + 1]) {
  6792. char tmp = buffer[j-1];
  6793. buffer[j-1] = buffer[j + 1];
  6794. buffer[j + 1] = tmp;
  6795. swapped = 1;
  6796. }
  6797. }
  6798. }
  6799. } else {
  6800. /* Remember any valid modifier if given */
  6801. if (strchr("hlL", *fmt) != 0)
  6802. descr->modifier = tolower((int)*fmt++);
  6803. descr->type = *fmt;
  6804. if (strchr("efgcsndoxui", *fmt) == 0) {
  6805. fmtObj->error = "bad scan conversion character";
  6806. return JIM_ERR;
  6807. } else if (*fmt == 'c' && descr->width != 0) {
  6808. fmtObj->error = "field width may not be specified in %c "
  6809. "conversion";
  6810. return JIM_ERR;
  6811. } else if (*fmt == 'u' && descr->modifier == 'l') {
  6812. fmtObj->error = "unsigned wide not supported";
  6813. return JIM_ERR;
  6814. }
  6815. }
  6816. curr++;
  6817. }
  6818. done:
  6819. if (fmtObj->convCount == 0) {
  6820. fmtObj->error = "no any conversion specifier given";
  6821. return JIM_ERR;
  6822. }
  6823. return JIM_OK;
  6824. }
  6825. /* Some accessor macros to allow lowlevel access to fields of internal repr */
  6826. #define FormatGetCnvCount(_fo_) \
  6827. ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
  6828. #define FormatGetMaxPos(_fo_) \
  6829. ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
  6830. #define FormatGetError(_fo_) \
  6831. ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
  6832. /* Some Bit testing/setting/cleaning routines. For now only used in handling
  6833. * charsets ([a-z123]) within scanning. Later on perhaps a base for a
  6834. * bitvector implementation in Jim? */
  6835. static int JimTestBit(const char *bitvec, char ch)
  6836. {
  6837. div_t pos = div(ch-1, 8);
  6838. return bitvec[pos.quot] & (1 << pos.rem);
  6839. }
  6840. static void JimSetBit(char *bitvec, char ch)
  6841. {
  6842. div_t pos = div(ch-1, 8);
  6843. bitvec[pos.quot] |= (1 << pos.rem);
  6844. }
  6845. #if 0 /* currently not used */
  6846. static void JimClearBit(char *bitvec, char ch)
  6847. {
  6848. div_t pos = div(ch-1, 8);
  6849. bitvec[pos.quot] &= ~(1 << pos.rem);
  6850. }
  6851. #endif
  6852. /* JimScanAString is used to scan an unspecified string that ends with
  6853. * next WS, or a string that is specified via a charset. The charset
  6854. * is currently implemented in a way to only allow for usage with
  6855. * ASCII. Whenever we will switch to UNICODE, another idea has to
  6856. * be born :-/
  6857. *
  6858. * FIXME: Works only with ASCII */
  6859. static Jim_Obj *
  6860. JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
  6861. {
  6862. size_t i;
  6863. Jim_Obj *result;
  6864. char charset[256/8 + 1]; /* A Charset may contain max 256 chars */
  6865. char *buffer = Jim_Alloc(strlen(str) + 1), *anchor = buffer;
  6866. /* First init charset to nothing or all, depending if a specified
  6867. * or an unspecified string has to be parsed */
  6868. memset(charset, (sdescr ? 0 : 255), sizeof(charset));
  6869. if (sdescr) {
  6870. /* There was a set description given, that means we are parsing
  6871. * a specified string. So we have to build a corresponding
  6872. * charset reflecting the description */
  6873. int notFlag = 0;
  6874. /* Should the set be negated at the end? */
  6875. if (*sdescr == '^') {
  6876. notFlag = 1;
  6877. ++sdescr;
  6878. }
  6879. /* Here '-' is meant literally and not to define a range */
  6880. if (*sdescr == '-') {
  6881. JimSetBit(charset, '-');
  6882. ++sdescr;
  6883. }
  6884. while (*sdescr) {
  6885. if (sdescr[1] == '-' && sdescr[2] != 0) {
  6886. /* Handle range definitions */
  6887. int i;
  6888. for (i = sdescr[0]; i <= sdescr[2]; ++i)
  6889. JimSetBit(charset, (char)i);
  6890. sdescr += 3;
  6891. } else {
  6892. /* Handle verbatim character definitions */
  6893. JimSetBit(charset, *sdescr++);
  6894. }
  6895. }
  6896. /* Negate the charset if there was a NOT given */
  6897. for (i = 0; notFlag && i < sizeof(charset); ++i)
  6898. charset[i] = ~charset[i];
  6899. }
  6900. /* And after all the mess above, the real work begin ... */
  6901. while (str && *str) {
  6902. if (!sdescr && isspace((int)*str))
  6903. break; /* EOS via WS if unspecified */
  6904. if (JimTestBit(charset, *str)) *buffer++ = *str++;
  6905. else break; /* EOS via mismatch if specified scanning */
  6906. }
  6907. *buffer = 0; /* Close the string properly ... */
  6908. result = Jim_NewStringObj(interp, anchor, -1);
  6909. Jim_Free(anchor); /* ... and free it afer usage */
  6910. return result;
  6911. }
  6912. /* ScanOneEntry will scan one entry out of the string passed as argument.
  6913. * It use the sscanf() function for this task. After extracting and
  6914. * converting of the value, the count of scanned characters will be
  6915. * returned of -1 in case of no conversion tool place and string was
  6916. * already scanned thru */
  6917. static int ScanOneEntry(Jim_Interp *interp, const char *str, long pos,
  6918. ScanFmtStringObj *fmtObj, long index, Jim_Obj **valObjPtr)
  6919. {
  6920. # define MAX_SIZE (sizeof(jim_wide) > sizeof(double) \
  6921. ? sizeof(jim_wide) \
  6922. : sizeof(double))
  6923. char buffer[MAX_SIZE];
  6924. char *value = buffer;
  6925. const char *tok;
  6926. const ScanFmtPartDescr *descr = &fmtObj->descr[index];
  6927. size_t sLen = strlen(&str[pos]), scanned = 0;
  6928. size_t anchor = pos;
  6929. int i;
  6930. /* First pessimiticly assume, we will not scan anything :-) */
  6931. *valObjPtr = 0;
  6932. if (descr->prefix) {
  6933. /* There was a prefix given before the conversion, skip it and adjust
  6934. * the string-to-be-parsed accordingly */
  6935. for (i = 0; str[pos] && descr->prefix[i]; ++i) {
  6936. /* If prefix require, skip WS */
  6937. if (isspace((int)descr->prefix[i]))
  6938. while (str[pos] && isspace((int)str[pos])) ++pos;
  6939. else if (descr->prefix[i] != str[pos])
  6940. break; /* Prefix do not match here, leave the loop */
  6941. else
  6942. ++pos; /* Prefix matched so far, next round */
  6943. }
  6944. if (str[pos] == 0)
  6945. return -1; /* All of str consumed: EOF condition */
  6946. else if (descr->prefix[i] != 0)
  6947. return 0; /* Not whole prefix consumed, no conversion possible */
  6948. }
  6949. /* For all but following conversion, skip leading WS */
  6950. if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
  6951. while (isspace((int)str[pos])) ++pos;
  6952. /* Determine how much skipped/scanned so far */
  6953. scanned = pos - anchor;
  6954. if (descr->type == 'n') {
  6955. /* Return pseudo conversion means: how much scanned so far? */
  6956. *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
  6957. } else if (str[pos] == 0) {
  6958. /* Cannot scan anything, as str is totally consumed */
  6959. return -1;
  6960. } else {
  6961. /* Processing of conversions follows ... */
  6962. if (descr->width > 0) {
  6963. /* Do not try to scan as fas as possible but only the given width.
  6964. * To ensure this, we copy the part that should be scanned. */
  6965. size_t tLen = descr->width > sLen ? sLen : descr->width;
  6966. tok = Jim_StrDupLen(&str[pos], tLen);
  6967. } else {
  6968. /* As no width was given, simply refer to the original string */
  6969. tok = &str[pos];
  6970. }
  6971. switch (descr->type) {
  6972. case 'c':
  6973. *valObjPtr = Jim_NewIntObj(interp, *tok);
  6974. scanned += 1;
  6975. break;
  6976. case 'd': case 'o': case 'x': case 'u': case 'i': {
  6977. jim_wide jwvalue = 0;
  6978. long lvalue = 0;
  6979. char *endp; /* Position where the number finished */
  6980. int base = descr->type == 'o' ? 8
  6981. : descr->type == 'x' ? 16
  6982. : descr->type == 'i' ? 0
  6983. : 10;
  6984. do {
  6985. /* Try to scan a number with the given base */
  6986. if (descr->modifier == 'l')
  6987. {
  6988. #ifdef HAVE_LONG_LONG_INT
  6989. jwvalue = JimStrtoll(tok, &endp, base),
  6990. #else
  6991. jwvalue = strtol(tok, &endp, base),
  6992. #endif
  6993. memcpy(value, &jwvalue, sizeof(jim_wide));
  6994. }
  6995. else
  6996. {
  6997. if (descr->type == 'u')
  6998. lvalue = strtoul(tok, &endp, base);
  6999. else
  7000. lvalue = strtol(tok, &endp, base);
  7001. memcpy(value, &lvalue, sizeof(lvalue));
  7002. }
  7003. /* If scanning failed, and base was undetermined, simply
  7004. * put it to 10 and try once more. This should catch the
  7005. * case where %i begin to parse a number prefix (e.g.
  7006. * '0x' but no further digits follows. This will be
  7007. * handled as a ZERO followed by a char 'x' by Tcl */
  7008. if (endp == tok && base == 0) base = 10;
  7009. else break;
  7010. } while (1);
  7011. if (endp != tok) {
  7012. /* There was some number sucessfully scanned! */
  7013. if (descr->modifier == 'l')
  7014. *valObjPtr = Jim_NewIntObj(interp, jwvalue);
  7015. else
  7016. *valObjPtr = Jim_NewIntObj(interp, lvalue);
  7017. /* Adjust the number-of-chars scanned so far */
  7018. scanned += endp - tok;
  7019. } else {
  7020. /* Nothing was scanned. We have to determine if this
  7021. * happened due to e.g. prefix mismatch or input str
  7022. * exhausted */
  7023. scanned = *tok ? 0 : -1;
  7024. }
  7025. break;
  7026. }
  7027. case 's': case '[': {
  7028. *valObjPtr = JimScanAString(interp, descr->arg, tok);
  7029. scanned += Jim_Length(*valObjPtr);
  7030. break;
  7031. }
  7032. case 'e': case 'f': case 'g': {
  7033. char *endp;
  7034. double dvalue = strtod(tok, &endp);
  7035. memcpy(value, &dvalue, sizeof(double));
  7036. if (endp != tok) {
  7037. /* There was some number sucessfully scanned! */
  7038. *valObjPtr = Jim_NewDoubleObj(interp, dvalue);
  7039. /* Adjust the number-of-chars scanned so far */
  7040. scanned += endp - tok;
  7041. } else {
  7042. /* Nothing was scanned. We have to determine if this
  7043. * happened due to e.g. prefix mismatch or input str
  7044. * exhausted */
  7045. scanned = *tok ? 0 : -1;
  7046. }
  7047. break;
  7048. }
  7049. }
  7050. /* If a substring was allocated (due to pre-defined width) do not
  7051. * forget to free it */
  7052. if (tok != &str[pos])
  7053. Jim_Free((char*)tok);
  7054. }
  7055. return scanned;
  7056. }
  7057. /* Jim_ScanString is the workhorse of string scanning. It will scan a given
  7058. * string and returns all converted (and not ignored) values in a list back
  7059. * to the caller. If an error occured, a NULL pointer will be returned */
  7060. Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr,
  7061. Jim_Obj *fmtObjPtr, int flags)
  7062. {
  7063. size_t i, pos;
  7064. int scanned = 1;
  7065. const char *str = Jim_GetString(strObjPtr, 0);
  7066. Jim_Obj *resultList = 0;
  7067. Jim_Obj **resultVec =NULL;
  7068. int resultc;
  7069. Jim_Obj *emptyStr = 0;
  7070. ScanFmtStringObj *fmtObj;
  7071. /* If format specification is not an object, convert it! */
  7072. if (fmtObjPtr->typePtr != &scanFmtStringObjType)
  7073. SetScanFmtFromAny(interp, fmtObjPtr);
  7074. fmtObj = (ScanFmtStringObj*)fmtObjPtr->internalRep.ptr;
  7075. /* Check if format specification was valid */
  7076. if (fmtObj->error != 0) {
  7077. if (flags & JIM_ERRMSG)
  7078. Jim_SetResultString(interp, fmtObj->error, -1);
  7079. return 0;
  7080. }
  7081. /* Allocate a new "shared" empty string for all unassigned conversions */
  7082. emptyStr = Jim_NewEmptyStringObj(interp);
  7083. Jim_IncrRefCount(emptyStr);
  7084. /* Create a list and fill it with empty strings up to max specified XPG3 */
  7085. resultList = Jim_NewListObj(interp, 0, 0);
  7086. if (fmtObj->maxPos > 0) {
  7087. for (i = 0; i < fmtObj->maxPos; ++i)
  7088. Jim_ListAppendElement(interp, resultList, emptyStr);
  7089. JimListGetElements(interp, resultList, &resultc, &resultVec);
  7090. }
  7091. /* Now handle every partial format description */
  7092. for (i = 0, pos = 0; i < fmtObj->count; ++i) {
  7093. ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
  7094. Jim_Obj *value = 0;
  7095. /* Only last type may be "literal" w/o conversion - skip it! */
  7096. if (descr->type == 0) continue;
  7097. /* As long as any conversion could be done, we will proceed */
  7098. if (scanned > 0)
  7099. scanned = ScanOneEntry(interp, str, pos, fmtObj, i, &value);
  7100. /* In case our first try results in EOF, we will leave */
  7101. if (scanned == -1 && i == 0)
  7102. goto eof;
  7103. /* Advance next pos-to-be-scanned for the amount scanned already */
  7104. pos += scanned;
  7105. /* value == 0 means no conversion took place so take empty string */
  7106. if (value == 0)
  7107. value = Jim_NewEmptyStringObj(interp);
  7108. /* If value is a non-assignable one, skip it */
  7109. if (descr->pos == -1) {
  7110. Jim_FreeNewObj(interp, value);
  7111. } else if (descr->pos == 0)
  7112. /* Otherwise append it to the result list if no XPG3 was given */
  7113. Jim_ListAppendElement(interp, resultList, value);
  7114. else if (resultVec[descr->pos-1] == emptyStr) {
  7115. /* But due to given XPG3, put the value into the corr. slot */
  7116. Jim_DecrRefCount(interp, resultVec[descr->pos-1]);
  7117. Jim_IncrRefCount(value);
  7118. resultVec[descr->pos-1] = value;
  7119. } else {
  7120. /* Otherwise, the slot was already used - free obj and ERROR */
  7121. Jim_FreeNewObj(interp, value);
  7122. goto err;
  7123. }
  7124. }
  7125. Jim_DecrRefCount(interp, emptyStr);
  7126. return resultList;
  7127. eof:
  7128. Jim_DecrRefCount(interp, emptyStr);
  7129. Jim_FreeNewObj(interp, resultList);
  7130. return (Jim_Obj*)EOF;
  7131. err:
  7132. Jim_DecrRefCount(interp, emptyStr);
  7133. Jim_FreeNewObj(interp, resultList);
  7134. return 0;
  7135. }
  7136. /* -----------------------------------------------------------------------------
  7137. * Pseudo Random Number Generation
  7138. * ---------------------------------------------------------------------------*/
  7139. static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
  7140. int seedLen);
  7141. /* Initialize the sbox with the numbers from 0 to 255 */
  7142. static void JimPrngInit(Jim_Interp *interp)
  7143. {
  7144. int i;
  7145. unsigned int seed[256];
  7146. interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
  7147. for (i = 0; i < 256; i++)
  7148. seed[i] = (rand() ^ time(NULL) ^ clock());
  7149. JimPrngSeed(interp, (unsigned char*) seed, sizeof(int)*256);
  7150. }
  7151. /* Generates N bytes of random data */
  7152. static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
  7153. {
  7154. Jim_PrngState *prng;
  7155. unsigned char *destByte = (unsigned char*) dest;
  7156. unsigned int si, sj, x;
  7157. /* initialization, only needed the first time */
  7158. if (interp->prngState == NULL)
  7159. JimPrngInit(interp);
  7160. prng = interp->prngState;
  7161. /* generates 'len' bytes of pseudo-random numbers */
  7162. for (x = 0; x < len; x++) {
  7163. prng->i = (prng->i + 1) & 0xff;
  7164. si = prng->sbox[prng->i];
  7165. prng->j = (prng->j + si) & 0xff;
  7166. sj = prng->sbox[prng->j];
  7167. prng->sbox[prng->i] = sj;
  7168. prng->sbox[prng->j] = si;
  7169. *destByte++ = prng->sbox[(si + sj)&0xff];
  7170. }
  7171. }
  7172. /* Re-seed the generator with user-provided bytes */
  7173. static void JimPrngSeed(Jim_Interp *interp, const unsigned char *seed,
  7174. int seedLen)
  7175. {
  7176. int i;
  7177. unsigned char buf[256];
  7178. Jim_PrngState *prng;
  7179. /* initialization, only needed the first time */
  7180. if (interp->prngState == NULL)
  7181. JimPrngInit(interp);
  7182. prng = interp->prngState;
  7183. /* Set the sbox[i] with i */
  7184. for (i = 0; i < 256; i++)
  7185. prng->sbox[i] = i;
  7186. /* Now use the seed to perform a random permutation of the sbox */
  7187. for (i = 0; i < seedLen; i++) {
  7188. unsigned char t;
  7189. t = prng->sbox[i&0xFF];
  7190. prng->sbox[i&0xFF] = prng->sbox[seed[i]];
  7191. prng->sbox[seed[i]] = t;
  7192. }
  7193. prng->i = prng->j = 0;
  7194. /* discard the first 256 bytes of stream. */
  7195. JimRandomBytes(interp, buf, 256);
  7196. }
  7197. /* -----------------------------------------------------------------------------
  7198. * Dynamic libraries support (WIN32 not supported)
  7199. * ---------------------------------------------------------------------------*/
  7200. #ifdef JIM_DYNLIB
  7201. #ifdef WIN32
  7202. #define RTLD_LAZY 0
  7203. void * dlopen(const char *path, int mode)
  7204. {
  7205. JIM_NOTUSED(mode);
  7206. return (void *)LoadLibraryA(path);
  7207. }
  7208. int dlclose(void *handle)
  7209. {
  7210. FreeLibrary((HANDLE)handle);
  7211. return 0;
  7212. }
  7213. void *dlsym(void *handle, const char *symbol)
  7214. {
  7215. return GetProcAddress((HMODULE)handle, symbol);
  7216. }
  7217. static char win32_dlerror_string[121];
  7218. const char *dlerror(void)
  7219. {
  7220. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
  7221. LANG_NEUTRAL, win32_dlerror_string, 120, NULL);
  7222. return win32_dlerror_string;
  7223. }
  7224. #endif /* WIN32 */
  7225. int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
  7226. {
  7227. Jim_Obj *libPathObjPtr;
  7228. int prefixc, i;
  7229. void *handle;
  7230. int (*onload)(Jim_Interp *interp);
  7231. libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
  7232. if (libPathObjPtr == NULL) {
  7233. prefixc = 0;
  7234. libPathObjPtr = NULL;
  7235. } else {
  7236. Jim_IncrRefCount(libPathObjPtr);
  7237. Jim_ListLength(interp, libPathObjPtr, &prefixc);
  7238. }
  7239. for (i = -1; i < prefixc; i++) {
  7240. if (i < 0) {
  7241. handle = dlopen(pathName, RTLD_LAZY);
  7242. } else {
  7243. FILE *fp;
  7244. char buf[JIM_PATH_LEN];
  7245. const char *prefix;
  7246. int prefixlen;
  7247. Jim_Obj *prefixObjPtr;
  7248. buf[0] = '\0';
  7249. if (Jim_ListIndex(interp, libPathObjPtr, i,
  7250. &prefixObjPtr, JIM_NONE) != JIM_OK)
  7251. continue;
  7252. prefix = Jim_GetString(prefixObjPtr, &prefixlen);
  7253. if (prefixlen + strlen(pathName) + 1 >= JIM_PATH_LEN)
  7254. continue;
  7255. if (*pathName == '/') {
  7256. strcpy(buf, pathName);
  7257. }
  7258. else if (prefixlen && prefix[prefixlen-1] == '/')
  7259. sprintf(buf, "%s%s", prefix, pathName);
  7260. else
  7261. sprintf(buf, "%s/%s", prefix, pathName);
  7262. fp = fopen(buf, "r");
  7263. if (fp == NULL)
  7264. continue;
  7265. fclose(fp);
  7266. handle = dlopen(buf, RTLD_LAZY);
  7267. }
  7268. if (handle == NULL) {
  7269. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  7270. Jim_AppendStrings(interp, Jim_GetResult(interp),
  7271. "error loading extension \"", pathName,
  7272. "\": ", dlerror(), NULL);
  7273. if (i < 0)
  7274. continue;
  7275. goto err;
  7276. }
  7277. if ((onload = dlsym(handle, "Jim_OnLoad")) == NULL) {
  7278. Jim_SetResultString(interp,
  7279. "No Jim_OnLoad symbol found on extension", -1);
  7280. goto err;
  7281. }
  7282. if (onload(interp) == JIM_ERR) {
  7283. dlclose(handle);
  7284. goto err;
  7285. }
  7286. Jim_SetEmptyResult(interp);
  7287. if (libPathObjPtr != NULL)
  7288. Jim_DecrRefCount(interp, libPathObjPtr);
  7289. return JIM_OK;
  7290. }
  7291. err:
  7292. if (libPathObjPtr != NULL)
  7293. Jim_DecrRefCount(interp, libPathObjPtr);
  7294. return JIM_ERR;
  7295. }
  7296. #else /* JIM_DYNLIB */
  7297. int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName)
  7298. {
  7299. JIM_NOTUSED(interp);
  7300. JIM_NOTUSED(pathName);
  7301. Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1);
  7302. return JIM_ERR;
  7303. }
  7304. #endif/* JIM_DYNLIB */
  7305. /* -----------------------------------------------------------------------------
  7306. * Packages handling
  7307. * ---------------------------------------------------------------------------*/
  7308. #define JIM_PKG_ANY_VERSION -1
  7309. /* Convert a string of the type "1.2" into an integer.
  7310. * MAJOR.MINOR is converted as MAJOR*100 + MINOR, so "1.2" is converted
  7311. * to the integer with value 102 */
  7312. static int JimPackageVersionToInt(Jim_Interp *interp, const char *v,
  7313. int *intPtr, int flags)
  7314. {
  7315. char *copy;
  7316. jim_wide major, minor;
  7317. char *majorStr, *minorStr, *p;
  7318. if (v[0] == '\0') {
  7319. *intPtr = JIM_PKG_ANY_VERSION;
  7320. return JIM_OK;
  7321. }
  7322. copy = Jim_StrDup(v);
  7323. p = strchr(copy, '.');
  7324. if (p == NULL) goto badfmt;
  7325. *p = '\0';
  7326. majorStr = copy;
  7327. minorStr = p + 1;
  7328. if (Jim_StringToWide(majorStr, &major, 10) != JIM_OK ||
  7329. Jim_StringToWide(minorStr, &minor, 10) != JIM_OK)
  7330. goto badfmt;
  7331. *intPtr = (int)(major*100 + minor);
  7332. Jim_Free(copy);
  7333. return JIM_OK;
  7334. badfmt:
  7335. Jim_Free(copy);
  7336. if (flags & JIM_ERRMSG) {
  7337. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  7338. Jim_AppendStrings(interp, Jim_GetResult(interp),
  7339. "invalid package version '", v, "'", NULL);
  7340. }
  7341. return JIM_ERR;
  7342. }
  7343. #define JIM_MATCHVER_EXACT (1 << JIM_PRIV_FLAG_SHIFT)
  7344. static int JimPackageMatchVersion(int needed, int actual, int flags)
  7345. {
  7346. if (needed == JIM_PKG_ANY_VERSION) return 1;
  7347. if (flags & JIM_MATCHVER_EXACT) {
  7348. return needed == actual;
  7349. } else {
  7350. return needed/100 == actual/100 && (needed <= actual);
  7351. }
  7352. }
  7353. int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver,
  7354. int flags)
  7355. {
  7356. int intVersion;
  7357. /* Check if the version format is ok */
  7358. if (JimPackageVersionToInt(interp, ver, &intVersion, JIM_ERRMSG) != JIM_OK)
  7359. return JIM_ERR;
  7360. /* If the package was already provided returns an error. */
  7361. if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
  7362. if (flags & JIM_ERRMSG) {
  7363. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  7364. Jim_AppendStrings(interp, Jim_GetResult(interp),
  7365. "package '", name, "' was already provided", NULL);
  7366. }
  7367. return JIM_ERR;
  7368. }
  7369. Jim_AddHashEntry(&interp->packages, name, (char*) ver);
  7370. return JIM_OK;
  7371. }
  7372. #ifndef JIM_ANSIC
  7373. #ifndef WIN32
  7374. # include <sys/types.h>
  7375. # include <dirent.h>
  7376. #else
  7377. # include <io.h>
  7378. /* Posix dirent.h compatiblity layer for WIN32.
  7379. * Copyright Kevlin Henney, 1997, 2003. All rights reserved.
  7380. * Copyright Salvatore Sanfilippo ,2005.
  7381. *
  7382. * Permission to use, copy, modify, and distribute this software and its
  7383. * documentation for any purpose is hereby granted without fee, provided
  7384. * that this copyright and permissions notice appear in all copies and
  7385. * derivatives.
  7386. *
  7387. * This software is supplied "as is" without express or implied warranty.
  7388. * This software was modified by Salvatore Sanfilippo for the Jim Interpreter.
  7389. */
  7390. struct dirent {
  7391. char *d_name;
  7392. };
  7393. typedef struct DIR {
  7394. long handle; /* -1 for failed rewind */
  7395. struct _finddata_t info;
  7396. struct dirent result; /* d_name null iff first time */
  7397. char *name; /* null-terminated char string */
  7398. } DIR;
  7399. DIR *opendir(const char *name)
  7400. {
  7401. DIR *dir = 0;
  7402. if (name && name[0]) {
  7403. size_t base_length = strlen(name);
  7404. const char *all = /* search pattern must end with suitable wildcard */
  7405. strchr("/\\", name[base_length - 1]) ? "*" : "/*";
  7406. if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
  7407. (dir->name = (char *) Jim_Alloc(base_length + strlen(all) + 1)) != 0)
  7408. {
  7409. strcat(strcpy(dir->name, name), all);
  7410. if ((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
  7411. dir->result.d_name = 0;
  7412. else { /* rollback */
  7413. Jim_Free(dir->name);
  7414. Jim_Free(dir);
  7415. dir = 0;
  7416. }
  7417. } else { /* rollback */
  7418. Jim_Free(dir);
  7419. dir = 0;
  7420. errno = ENOMEM;
  7421. }
  7422. } else {
  7423. errno = EINVAL;
  7424. }
  7425. return dir;
  7426. }
  7427. int closedir(DIR *dir)
  7428. {
  7429. int result = -1;
  7430. if (dir) {
  7431. if (dir->handle != -1)
  7432. result = _findclose(dir->handle);
  7433. Jim_Free(dir->name);
  7434. Jim_Free(dir);
  7435. }
  7436. if (result == -1) /* map all errors to EBADF */
  7437. errno = EBADF;
  7438. return result;
  7439. }
  7440. struct dirent *readdir(DIR *dir)
  7441. {
  7442. struct dirent *result = 0;
  7443. if (dir && dir->handle != -1) {
  7444. if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
  7445. result = &dir->result;
  7446. result->d_name = dir->info.name;
  7447. }
  7448. } else {
  7449. errno = EBADF;
  7450. }
  7451. return result;
  7452. }
  7453. #endif /* WIN32 */
  7454. static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
  7455. int prefixc, const char *pkgName, int pkgVer, int flags)
  7456. {
  7457. int bestVer = -1, i;
  7458. int pkgNameLen = strlen(pkgName);
  7459. char *bestPackage = NULL;
  7460. struct dirent *de;
  7461. for (i = 0; i < prefixc; i++) {
  7462. DIR *dir;
  7463. char buf[JIM_PATH_LEN];
  7464. int prefixLen;
  7465. if (prefixes[i] == NULL) continue;
  7466. strncpy(buf, prefixes[i], JIM_PATH_LEN);
  7467. buf[JIM_PATH_LEN-1] = '\0';
  7468. prefixLen = strlen(buf);
  7469. if (prefixLen && buf[prefixLen-1] == '/')
  7470. buf[prefixLen-1] = '\0';
  7471. if ((dir = opendir(buf)) == NULL) continue;
  7472. while ((de = readdir(dir)) != NULL) {
  7473. char *fileName = de->d_name;
  7474. int fileNameLen = strlen(fileName);
  7475. if (strncmp(fileName, "jim-", 4) == 0 &&
  7476. strncmp(fileName + 4, pkgName, pkgNameLen) == 0 &&
  7477. *(fileName + 4+pkgNameLen) == '-' &&
  7478. fileNameLen > 4 && /* note that this is not really useful */
  7479. (strncmp(fileName + fileNameLen-4, ".tcl", 4) == 0 ||
  7480. strncmp(fileName + fileNameLen-4, ".dll", 4) == 0 ||
  7481. strncmp(fileName + fileNameLen-3, ".so", 3) == 0))
  7482. {
  7483. char ver[6]; /* xx.yy < nulterm> */
  7484. char *p = strrchr(fileName, '.');
  7485. int verLen, fileVer;
  7486. verLen = p - (fileName + 4+pkgNameLen + 1);
  7487. if (verLen < 3 || verLen > 5) continue;
  7488. memcpy(ver, fileName + 4+pkgNameLen + 1, verLen);
  7489. ver[verLen] = '\0';
  7490. if (JimPackageVersionToInt(interp, ver, &fileVer, JIM_NONE)
  7491. != JIM_OK) continue;
  7492. if (JimPackageMatchVersion(pkgVer, fileVer, flags) &&
  7493. (bestVer == -1 || bestVer < fileVer))
  7494. {
  7495. bestVer = fileVer;
  7496. Jim_Free(bestPackage);
  7497. bestPackage = Jim_Alloc(strlen(buf) + strlen(fileName) + 2);
  7498. sprintf(bestPackage, "%s/%s", buf, fileName);
  7499. }
  7500. }
  7501. }
  7502. closedir(dir);
  7503. }
  7504. return bestPackage;
  7505. }
  7506. #else /* JIM_ANSIC */
  7507. static char *JimFindBestPackage(Jim_Interp *interp, char **prefixes,
  7508. int prefixc, const char *pkgName, int pkgVer, int flags)
  7509. {
  7510. JIM_NOTUSED(interp);
  7511. JIM_NOTUSED(prefixes);
  7512. JIM_NOTUSED(prefixc);
  7513. JIM_NOTUSED(pkgName);
  7514. JIM_NOTUSED(pkgVer);
  7515. JIM_NOTUSED(flags);
  7516. return NULL;
  7517. }
  7518. #endif /* JIM_ANSIC */
  7519. /* Search for a suitable package under every dir specified by jim_libpath
  7520. * and load it if possible. If a suitable package was loaded with success
  7521. * JIM_OK is returned, otherwise JIM_ERR is returned. */
  7522. static int JimLoadPackage(Jim_Interp *interp, const char *name, int ver,
  7523. int flags)
  7524. {
  7525. Jim_Obj *libPathObjPtr;
  7526. char **prefixes, *best;
  7527. int prefixc, i, retCode = JIM_OK;
  7528. libPathObjPtr = Jim_GetGlobalVariableStr(interp, "jim_libpath", JIM_NONE);
  7529. if (libPathObjPtr == NULL) {
  7530. prefixc = 0;
  7531. libPathObjPtr = NULL;
  7532. } else {
  7533. Jim_IncrRefCount(libPathObjPtr);
  7534. Jim_ListLength(interp, libPathObjPtr, &prefixc);
  7535. }
  7536. prefixes = Jim_Alloc(sizeof(char*)*prefixc);
  7537. for (i = 0; i < prefixc; i++) {
  7538. Jim_Obj *prefixObjPtr;
  7539. if (Jim_ListIndex(interp, libPathObjPtr, i,
  7540. &prefixObjPtr, JIM_NONE) != JIM_OK)
  7541. {
  7542. prefixes[i] = NULL;
  7543. continue;
  7544. }
  7545. prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
  7546. }
  7547. /* Scan every directory to find the "best" package. */
  7548. best = JimFindBestPackage(interp, prefixes, prefixc, name, ver, flags);
  7549. if (best != NULL) {
  7550. char *p = strrchr(best, '.');
  7551. /* Try to load/source it */
  7552. if (p && strcmp(p, ".tcl") == 0) {
  7553. retCode = Jim_EvalFile(interp, best);
  7554. } else {
  7555. retCode = Jim_LoadLibrary(interp, best);
  7556. }
  7557. } else {
  7558. retCode = JIM_ERR;
  7559. }
  7560. Jim_Free(best);
  7561. for (i = 0; i < prefixc; i++)
  7562. Jim_Free(prefixes[i]);
  7563. Jim_Free(prefixes);
  7564. if (libPathObjPtr)
  7565. Jim_DecrRefCount(interp, libPathObjPtr);
  7566. return retCode;
  7567. }
  7568. const char *Jim_PackageRequire(Jim_Interp *interp, const char *name,
  7569. const char *ver, int flags)
  7570. {
  7571. Jim_HashEntry *he;
  7572. int requiredVer;
  7573. /* Start with an empty error string */
  7574. Jim_SetResultString(interp, "", 0);
  7575. if (JimPackageVersionToInt(interp, ver, &requiredVer, JIM_ERRMSG) != JIM_OK)
  7576. return NULL;
  7577. he = Jim_FindHashEntry(&interp->packages, name);
  7578. if (he == NULL) {
  7579. /* Try to load the package. */
  7580. if (JimLoadPackage(interp, name, requiredVer, flags) == JIM_OK) {
  7581. he = Jim_FindHashEntry(&interp->packages, name);
  7582. if (he == NULL) {
  7583. return "?";
  7584. }
  7585. return he->val;
  7586. }
  7587. /* No way... return an error. */
  7588. if (flags & JIM_ERRMSG) {
  7589. int len;
  7590. Jim_GetString(Jim_GetResult(interp), &len);
  7591. Jim_AppendStrings(interp, Jim_GetResult(interp), len ? "\n" : "",
  7592. "Can't find package '", name, "'", NULL);
  7593. }
  7594. return NULL;
  7595. } else {
  7596. int actualVer;
  7597. if (JimPackageVersionToInt(interp, he->val, &actualVer, JIM_ERRMSG)
  7598. != JIM_OK)
  7599. {
  7600. return NULL;
  7601. }
  7602. /* Check if version matches. */
  7603. if (JimPackageMatchVersion(requiredVer, actualVer, flags) == 0) {
  7604. Jim_AppendStrings(interp, Jim_GetResult(interp),
  7605. "Package '", name, "' already loaded, but with version ",
  7606. he->val, NULL);
  7607. return NULL;
  7608. }
  7609. return he->val;
  7610. }
  7611. }
  7612. /* -----------------------------------------------------------------------------
  7613. * Eval
  7614. * ---------------------------------------------------------------------------*/
  7615. #define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */
  7616. #define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */
  7617. static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
  7618. Jim_Obj *const *argv);
  7619. /* Handle calls to the [unknown] command */
  7620. static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
  7621. {
  7622. Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN];
  7623. int retCode;
  7624. /* If JimUnknown() is recursively called (e.g. error in the unknown proc,
  7625. * done here
  7626. */
  7627. if (interp->unknown_called) {
  7628. return JIM_ERR;
  7629. }
  7630. /* If the [unknown] command does not exists returns
  7631. * just now */
  7632. if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
  7633. return JIM_ERR;
  7634. /* The object interp->unknown just contains
  7635. * the "unknown" string, it is used in order to
  7636. * avoid to lookup the unknown command every time
  7637. * but instread to cache the result. */
  7638. if (argc + 1 <= JIM_EVAL_SARGV_LEN)
  7639. v = sv;
  7640. else
  7641. v = Jim_Alloc(sizeof(Jim_Obj*)*(argc + 1));
  7642. /* Make a copy of the arguments vector, but shifted on
  7643. * the right of one position. The command name of the
  7644. * command will be instead the first argument of the
  7645. * [unknonw] call. */
  7646. memcpy(v + 1, argv, sizeof(Jim_Obj*)*argc);
  7647. v[0] = interp->unknown;
  7648. /* Call it */
  7649. interp->unknown_called++;
  7650. retCode = Jim_EvalObjVector(interp, argc + 1, v);
  7651. interp->unknown_called--;
  7652. /* Clean up */
  7653. if (v != sv)
  7654. Jim_Free(v);
  7655. return retCode;
  7656. }
  7657. /* Eval the object vector 'objv' composed of 'objc' elements.
  7658. * Every element is used as single argument.
  7659. * Jim_EvalObj() will call this function every time its object
  7660. * argument is of "list" type, with no string representation.
  7661. *
  7662. * This is possible because the string representation of a
  7663. * list object generated by the UpdateStringOfList is made
  7664. * in a way that ensures that every list element is a different
  7665. * command argument. */
  7666. int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
  7667. {
  7668. int i, retcode;
  7669. Jim_Cmd *cmdPtr;
  7670. /* Incr refcount of arguments. */
  7671. for (i = 0; i < objc; i++)
  7672. Jim_IncrRefCount(objv[i]);
  7673. /* Command lookup */
  7674. cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
  7675. if (cmdPtr == NULL) {
  7676. retcode = JimUnknown(interp, objc, objv);
  7677. } else {
  7678. /* Call it -- Make sure result is an empty object. */
  7679. Jim_SetEmptyResult(interp);
  7680. if (cmdPtr->cmdProc) {
  7681. interp->cmdPrivData = cmdPtr->privData;
  7682. retcode = cmdPtr->cmdProc(interp, objc, objv);
  7683. if (retcode == JIM_ERR_ADDSTACK) {
  7684. //JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
  7685. retcode = JIM_ERR;
  7686. }
  7687. } else {
  7688. retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
  7689. if (retcode == JIM_ERR) {
  7690. JimAppendStackTrace(interp,
  7691. Jim_GetString(objv[0], NULL), "", 1);
  7692. }
  7693. }
  7694. }
  7695. /* Decr refcount of arguments and return the retcode */
  7696. for (i = 0; i < objc; i++)
  7697. Jim_DecrRefCount(interp, objv[i]);
  7698. return retcode;
  7699. }
  7700. /* Interpolate the given tokens into a unique Jim_Obj returned by reference
  7701. * via *objPtrPtr. This function is only called by Jim_EvalObj().
  7702. * The returned object has refcount = 0. */
  7703. int Jim_InterpolateTokens(Jim_Interp *interp, ScriptToken *token,
  7704. int tokens, Jim_Obj **objPtrPtr)
  7705. {
  7706. int totlen = 0, i, retcode;
  7707. Jim_Obj **intv;
  7708. Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
  7709. Jim_Obj *objPtr;
  7710. char *s;
  7711. if (tokens <= JIM_EVAL_SINTV_LEN)
  7712. intv = sintv;
  7713. else
  7714. intv = Jim_Alloc(sizeof(Jim_Obj*)*
  7715. tokens);
  7716. /* Compute every token forming the argument
  7717. * in the intv objects vector. */
  7718. for (i = 0; i < tokens; i++) {
  7719. switch (token[i].type) {
  7720. case JIM_TT_ESC:
  7721. case JIM_TT_STR:
  7722. intv[i] = token[i].objPtr;
  7723. break;
  7724. case JIM_TT_VAR:
  7725. intv[i] = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
  7726. if (!intv[i]) {
  7727. retcode = JIM_ERR;
  7728. goto err;
  7729. }
  7730. break;
  7731. case JIM_TT_DICTSUGAR:
  7732. intv[i] = Jim_ExpandDictSugar(interp, token[i].objPtr);
  7733. if (!intv[i]) {
  7734. retcode = JIM_ERR;
  7735. goto err;
  7736. }
  7737. break;
  7738. case JIM_TT_CMD:
  7739. retcode = Jim_EvalObj(interp, token[i].objPtr);
  7740. if (retcode != JIM_OK)
  7741. goto err;
  7742. intv[i] = Jim_GetResult(interp);
  7743. break;
  7744. default:
  7745. Jim_Panic(interp,
  7746. "default token type reached "
  7747. "in Jim_InterpolateTokens().");
  7748. break;
  7749. }
  7750. Jim_IncrRefCount(intv[i]);
  7751. /* Make sure there is a valid
  7752. * string rep, and add the string
  7753. * length to the total legnth. */
  7754. Jim_GetString(intv[i], NULL);
  7755. totlen += intv[i]->length;
  7756. }
  7757. /* Concatenate every token in an unique
  7758. * object. */
  7759. objPtr = Jim_NewStringObjNoAlloc(interp,
  7760. NULL, 0);
  7761. s = objPtr->bytes = Jim_Alloc(totlen + 1);
  7762. objPtr->length = totlen;
  7763. for (i = 0; i < tokens; i++) {
  7764. memcpy(s, intv[i]->bytes, intv[i]->length);
  7765. s += intv[i]->length;
  7766. Jim_DecrRefCount(interp, intv[i]);
  7767. }
  7768. objPtr->bytes[totlen] = '\0';
  7769. /* Free the intv vector if not static. */
  7770. if (tokens > JIM_EVAL_SINTV_LEN)
  7771. Jim_Free(intv);
  7772. *objPtrPtr = objPtr;
  7773. return JIM_OK;
  7774. err:
  7775. i--;
  7776. for (; i >= 0; i--)
  7777. Jim_DecrRefCount(interp, intv[i]);
  7778. if (tokens > JIM_EVAL_SINTV_LEN)
  7779. Jim_Free(intv);
  7780. return retcode;
  7781. }
  7782. /* Helper of Jim_EvalObj() to perform argument expansion.
  7783. * Basically this function append an argument to 'argv'
  7784. * (and increments argc by reference accordingly), performing
  7785. * expansion of the list object if 'expand' is non-zero, or
  7786. * just adding objPtr to argv if 'expand' is zero. */
  7787. void Jim_ExpandArgument(Jim_Interp *interp, Jim_Obj ***argv,
  7788. int *argcPtr, int expand, Jim_Obj *objPtr)
  7789. {
  7790. if (!expand) {
  7791. (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr) + 1));
  7792. /* refcount of objPtr not incremented because
  7793. * we are actually transfering a reference from
  7794. * the old 'argv' to the expanded one. */
  7795. (*argv)[*argcPtr] = objPtr;
  7796. (*argcPtr)++;
  7797. } else {
  7798. int len, i;
  7799. Jim_ListLength(interp, objPtr, &len);
  7800. (*argv) = Jim_Realloc(*argv, sizeof(Jim_Obj*)*((*argcPtr) + len));
  7801. for (i = 0; i < len; i++) {
  7802. (*argv)[*argcPtr] = objPtr->internalRep.listValue.ele[i];
  7803. Jim_IncrRefCount(objPtr->internalRep.listValue.ele[i]);
  7804. (*argcPtr)++;
  7805. }
  7806. /* The original object reference is no longer needed,
  7807. * after the expansion it is no longer present on
  7808. * the argument vector, but the single elements are
  7809. * in its place. */
  7810. Jim_DecrRefCount(interp, objPtr);
  7811. }
  7812. }
  7813. int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
  7814. {
  7815. int i, j = 0, len;
  7816. ScriptObj *script;
  7817. ScriptToken *token;
  7818. int *cs; /* command structure array */
  7819. int retcode = JIM_OK;
  7820. Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL, *tmpObjPtr;
  7821. interp->errorFlag = 0;
  7822. /* If the object is of type "list" and there is no
  7823. * string representation for this object, we can call
  7824. * a specialized version of Jim_EvalObj() */
  7825. if (scriptObjPtr->typePtr == &listObjType &&
  7826. scriptObjPtr->internalRep.listValue.len &&
  7827. scriptObjPtr->bytes == NULL) {
  7828. Jim_IncrRefCount(scriptObjPtr);
  7829. retcode = Jim_EvalObjVector(interp,
  7830. scriptObjPtr->internalRep.listValue.len,
  7831. scriptObjPtr->internalRep.listValue.ele);
  7832. Jim_DecrRefCount(interp, scriptObjPtr);
  7833. return retcode;
  7834. }
  7835. Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */
  7836. script = Jim_GetScript(interp, scriptObjPtr);
  7837. /* Now we have to make sure the internal repr will not be
  7838. * freed on shimmering.
  7839. *
  7840. * Think for example to this:
  7841. *
  7842. * set x {llength $x; ... some more code ...}; eval $x
  7843. *
  7844. * In order to preserve the internal rep, we increment the
  7845. * inUse field of the script internal rep structure. */
  7846. script->inUse++;
  7847. token = script->token;
  7848. len = script->len;
  7849. cs = script->cmdStruct;
  7850. i = 0; /* 'i' is the current token index. */
  7851. /* Reset the interpreter result. This is useful to
  7852. * return the emtpy result in the case of empty program. */
  7853. Jim_SetEmptyResult(interp);
  7854. /* Execute every command sequentially, returns on
  7855. * error (i.e. if a command does not return JIM_OK) */
  7856. while (i < len) {
  7857. int expand = 0;
  7858. int argc = *cs++; /* Get the number of arguments */
  7859. Jim_Cmd *cmd;
  7860. /* Set the expand flag if needed. */
  7861. if (argc == -1) {
  7862. expand++;
  7863. argc = *cs++;
  7864. }
  7865. /* Allocate the arguments vector */
  7866. if (argc <= JIM_EVAL_SARGV_LEN)
  7867. argv = sargv;
  7868. else
  7869. argv = Jim_Alloc(sizeof(Jim_Obj*)*argc);
  7870. /* Populate the arguments objects. */
  7871. for (j = 0; j < argc; j++) {
  7872. int tokens = *cs++;
  7873. /* tokens is negative if expansion is needed.
  7874. * for this argument. */
  7875. if (tokens < 0) {
  7876. tokens = (-tokens)-1;
  7877. i++;
  7878. }
  7879. if (tokens == 1) {
  7880. /* Fast path if the token does not
  7881. * need interpolation */
  7882. switch (token[i].type) {
  7883. case JIM_TT_ESC:
  7884. case JIM_TT_STR:
  7885. argv[j] = token[i].objPtr;
  7886. break;
  7887. case JIM_TT_VAR:
  7888. tmpObjPtr = Jim_GetVariable(interp, token[i].objPtr,
  7889. JIM_ERRMSG);
  7890. if (!tmpObjPtr) {
  7891. retcode = JIM_ERR;
  7892. goto err;
  7893. }
  7894. argv[j] = tmpObjPtr;
  7895. break;
  7896. case JIM_TT_DICTSUGAR:
  7897. tmpObjPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
  7898. if (!tmpObjPtr) {
  7899. retcode = JIM_ERR;
  7900. goto err;
  7901. }
  7902. argv[j] = tmpObjPtr;
  7903. break;
  7904. case JIM_TT_CMD:
  7905. retcode = Jim_EvalObj(interp, token[i].objPtr);
  7906. if (retcode != JIM_OK)
  7907. goto err;
  7908. argv[j] = Jim_GetResult(interp);
  7909. break;
  7910. default:
  7911. Jim_Panic(interp,
  7912. "default token type reached "
  7913. "in Jim_EvalObj().");
  7914. break;
  7915. }
  7916. Jim_IncrRefCount(argv[j]);
  7917. i += 2;
  7918. } else {
  7919. /* For interpolation we call an helper
  7920. * function doing the work for us. */
  7921. if ((retcode = Jim_InterpolateTokens(interp,
  7922. token + i, tokens, &tmpObjPtr)) != JIM_OK)
  7923. {
  7924. goto err;
  7925. }
  7926. argv[j] = tmpObjPtr;
  7927. Jim_IncrRefCount(argv[j]);
  7928. i += tokens + 1;
  7929. }
  7930. }
  7931. /* Handle {expand} expansion */
  7932. if (expand) {
  7933. int *ecs = cs - argc;
  7934. int eargc = 0;
  7935. Jim_Obj **eargv = NULL;
  7936. for (j = 0; j < argc; j++) {
  7937. Jim_ExpandArgument(interp, &eargv, &eargc,
  7938. ecs[j] < 0, argv[j]);
  7939. }
  7940. if (argv != sargv)
  7941. Jim_Free(argv);
  7942. argc = eargc;
  7943. argv = eargv;
  7944. j = argc;
  7945. if (argc == 0) {
  7946. /* Nothing to do with zero args. */
  7947. Jim_Free(eargv);
  7948. continue;
  7949. }
  7950. }
  7951. /* Lookup the command to call */
  7952. cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG);
  7953. if (cmd != NULL) {
  7954. /* Call it -- Make sure result is an empty object. */
  7955. Jim_SetEmptyResult(interp);
  7956. if (cmd->cmdProc) {
  7957. interp->cmdPrivData = cmd->privData;
  7958. retcode = cmd->cmdProc(interp, argc, argv);
  7959. if ((retcode == JIM_ERR)||(retcode == JIM_ERR_ADDSTACK)) {
  7960. JimAppendStackTrace(interp, "", script->fileName, token[i-argc*2].linenr);
  7961. retcode = JIM_ERR;
  7962. }
  7963. } else {
  7964. retcode = JimCallProcedure(interp, cmd, argc, argv);
  7965. if (retcode == JIM_ERR) {
  7966. JimAppendStackTrace(interp,
  7967. Jim_GetString(argv[0], NULL), script->fileName,
  7968. token[i-argc*2].linenr);
  7969. }
  7970. }
  7971. } else {
  7972. /* Call [unknown] */
  7973. retcode = JimUnknown(interp, argc, argv);
  7974. if (retcode == JIM_ERR) {
  7975. JimAppendStackTrace(interp,
  7976. "", script->fileName,
  7977. token[i-argc*2].linenr);
  7978. }
  7979. }
  7980. if (retcode != JIM_OK) {
  7981. i -= argc*2; /* point to the command name. */
  7982. goto err;
  7983. }
  7984. /* Decrement the arguments count */
  7985. for (j = 0; j < argc; j++) {
  7986. Jim_DecrRefCount(interp, argv[j]);
  7987. }
  7988. if (argv != sargv) {
  7989. Jim_Free(argv);
  7990. argv = NULL;
  7991. }
  7992. }
  7993. /* Note that we don't have to decrement inUse, because the
  7994. * following code transfers our use of the reference again to
  7995. * the script object. */
  7996. j = 0; /* on normal termination, the argv array is already
  7997. Jim_DecrRefCount-ed. */
  7998. err:
  7999. /* Handle errors. */
  8000. if (retcode == JIM_ERR && !interp->errorFlag) {
  8001. interp->errorFlag = 1;
  8002. JimSetErrorFileName(interp, script->fileName);
  8003. JimSetErrorLineNumber(interp, token[i].linenr);
  8004. JimResetStackTrace(interp);
  8005. }
  8006. Jim_FreeIntRep(interp, scriptObjPtr);
  8007. scriptObjPtr->typePtr = &scriptObjType;
  8008. Jim_SetIntRepPtr(scriptObjPtr, script);
  8009. Jim_DecrRefCount(interp, scriptObjPtr);
  8010. for (i = 0; i < j; i++) {
  8011. Jim_DecrRefCount(interp, argv[i]);
  8012. }
  8013. if (argv != sargv)
  8014. Jim_Free(argv);
  8015. return retcode;
  8016. }
  8017. /* Call a procedure implemented in Tcl.
  8018. * It's possible to speed-up a lot this function, currently
  8019. * the callframes are not cached, but allocated and
  8020. * destroied every time. What is expecially costly is
  8021. * to create/destroy the local vars hash table every time.
  8022. *
  8023. * This can be fixed just implementing callframes caching
  8024. * in JimCreateCallFrame() and JimFreeCallFrame(). */
  8025. int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc,
  8026. Jim_Obj *const *argv)
  8027. {
  8028. int i, retcode;
  8029. Jim_CallFrame *callFramePtr;
  8030. int num_args;
  8031. /* Check arity */
  8032. if (argc < cmd->arityMin || (cmd->arityMax != -1 &&
  8033. argc > cmd->arityMax)) {
  8034. Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
  8035. Jim_AppendStrings(interp, objPtr,
  8036. "wrong # args: should be \"", Jim_GetString(argv[0], NULL),
  8037. (cmd->arityMin > 1) ? " " : "",
  8038. Jim_GetString(cmd->argListObjPtr, NULL), "\"", NULL);
  8039. Jim_SetResult(interp, objPtr);
  8040. return JIM_ERR;
  8041. }
  8042. /* Check if there are too nested calls */
  8043. if (interp->numLevels == interp->maxNestingDepth) {
  8044. Jim_SetResultString(interp,
  8045. "Too many nested calls. Infinite recursion?", -1);
  8046. return JIM_ERR;
  8047. }
  8048. /* Create a new callframe */
  8049. callFramePtr = JimCreateCallFrame(interp);
  8050. callFramePtr->parentCallFrame = interp->framePtr;
  8051. callFramePtr->argv = argv;
  8052. callFramePtr->argc = argc;
  8053. callFramePtr->procArgsObjPtr = cmd->argListObjPtr;
  8054. callFramePtr->procBodyObjPtr = cmd->bodyObjPtr;
  8055. callFramePtr->staticVars = cmd->staticVars;
  8056. Jim_IncrRefCount(cmd->argListObjPtr);
  8057. Jim_IncrRefCount(cmd->bodyObjPtr);
  8058. interp->framePtr = callFramePtr;
  8059. interp->numLevels ++;
  8060. /* Set arguments */
  8061. Jim_ListLength(interp, cmd->argListObjPtr, &num_args);
  8062. /* If last argument is 'args', don't set it here */
  8063. if (cmd->arityMax == -1) {
  8064. num_args--;
  8065. }
  8066. for (i = 0; i < num_args; i++) {
  8067. Jim_Obj *argObjPtr=NULL;
  8068. Jim_Obj *nameObjPtr=NULL;
  8069. Jim_Obj *valueObjPtr=NULL;
  8070. Jim_ListIndex(interp, cmd->argListObjPtr, i, &argObjPtr, JIM_NONE);
  8071. if (i + 1 >= cmd->arityMin) {
  8072. /* The name is the first element of the list */
  8073. Jim_ListIndex(interp, argObjPtr, 0, &nameObjPtr, JIM_NONE);
  8074. }
  8075. else {
  8076. /* The element arg is the name */
  8077. nameObjPtr = argObjPtr;
  8078. }
  8079. if (i + 1 >= argc) {
  8080. /* No more values, so use default */
  8081. /* The value is the second element of the list */
  8082. Jim_ListIndex(interp, argObjPtr, 1, &valueObjPtr, JIM_NONE);
  8083. }
  8084. else {
  8085. valueObjPtr = argv[i + 1];
  8086. }
  8087. Jim_SetVariable(interp, nameObjPtr, valueObjPtr);
  8088. }
  8089. /* Set optional arguments */
  8090. if (cmd->arityMax == -1) {
  8091. Jim_Obj *listObjPtr=NULL, *objPtr=NULL;
  8092. i++;
  8093. listObjPtr = Jim_NewListObj(interp, argv + i, argc-i);
  8094. Jim_ListIndex(interp, cmd->argListObjPtr, num_args, &objPtr, JIM_NONE);
  8095. Jim_SetVariable(interp, objPtr, listObjPtr);
  8096. }
  8097. /* Eval the body */
  8098. retcode = Jim_EvalObj(interp, cmd->bodyObjPtr);
  8099. /* Destroy the callframe */
  8100. interp->numLevels --;
  8101. interp->framePtr = interp->framePtr->parentCallFrame;
  8102. if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) {
  8103. JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE);
  8104. } else {
  8105. JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT);
  8106. }
  8107. /* Handle the JIM_EVAL return code */
  8108. if (retcode == JIM_EVAL && interp->evalRetcodeLevel != interp->numLevels) {
  8109. int savedLevel = interp->evalRetcodeLevel;
  8110. interp->evalRetcodeLevel = interp->numLevels;
  8111. while (retcode == JIM_EVAL) {
  8112. Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp);
  8113. Jim_IncrRefCount(resultScriptObjPtr);
  8114. retcode = Jim_EvalObj(interp, resultScriptObjPtr);
  8115. Jim_DecrRefCount(interp, resultScriptObjPtr);
  8116. }
  8117. interp->evalRetcodeLevel = savedLevel;
  8118. }
  8119. /* Handle the JIM_RETURN return code */
  8120. if (retcode == JIM_RETURN) {
  8121. retcode = interp->returnCode;
  8122. interp->returnCode = JIM_OK;
  8123. }
  8124. return retcode;
  8125. }
  8126. int Jim_Eval_Named(Jim_Interp *interp, const char *script, const char *filename, int lineno)
  8127. {
  8128. int retval;
  8129. Jim_Obj *scriptObjPtr;
  8130. scriptObjPtr = Jim_NewStringObj(interp, script, -1);
  8131. Jim_IncrRefCount(scriptObjPtr);
  8132. if (filename) {
  8133. JimSetSourceInfo(interp, scriptObjPtr, filename, lineno);
  8134. }
  8135. retval = Jim_EvalObj(interp, scriptObjPtr);
  8136. Jim_DecrRefCount(interp, scriptObjPtr);
  8137. return retval;
  8138. }
  8139. int Jim_Eval(Jim_Interp *interp, const char *script)
  8140. {
  8141. return Jim_Eval_Named(interp, script, NULL, 0);
  8142. }
  8143. /* Execute script in the scope of the global level */
  8144. int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
  8145. {
  8146. Jim_CallFrame *savedFramePtr;
  8147. int retval;
  8148. savedFramePtr = interp->framePtr;
  8149. interp->framePtr = interp->topFramePtr;
  8150. retval = Jim_Eval(interp, script);
  8151. interp->framePtr = savedFramePtr;
  8152. return retval;
  8153. }
  8154. int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
  8155. {
  8156. Jim_CallFrame *savedFramePtr;
  8157. int retval;
  8158. savedFramePtr = interp->framePtr;
  8159. interp->framePtr = interp->topFramePtr;
  8160. retval = Jim_EvalObj(interp, scriptObjPtr);
  8161. interp->framePtr = savedFramePtr;
  8162. /* Try to report the error (if any) via the bgerror proc */
  8163. if (retval != JIM_OK) {
  8164. Jim_Obj *objv[2];
  8165. objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
  8166. objv[1] = Jim_GetResult(interp);
  8167. Jim_IncrRefCount(objv[0]);
  8168. Jim_IncrRefCount(objv[1]);
  8169. if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
  8170. /* Report the error to stderr. */
  8171. Jim_fprintf(interp, interp->cookie_stderr, "Background error:" JIM_NL);
  8172. Jim_PrintErrorMessage(interp);
  8173. }
  8174. Jim_DecrRefCount(interp, objv[0]);
  8175. Jim_DecrRefCount(interp, objv[1]);
  8176. }
  8177. return retval;
  8178. }
  8179. int Jim_EvalFile(Jim_Interp *interp, const char *filename)
  8180. {
  8181. char *prg = NULL;
  8182. FILE *fp;
  8183. int nread, totread, maxlen, buflen;
  8184. int retval;
  8185. Jim_Obj *scriptObjPtr;
  8186. if ((fp = fopen(filename, "r")) == NULL) {
  8187. const int cwd_len = 2048;
  8188. char *cwd = malloc(cwd_len);
  8189. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  8190. if (!getcwd(cwd, cwd_len)) strcpy(cwd, "unknown");
  8191. Jim_AppendStrings(interp, Jim_GetResult(interp),
  8192. "Error loading script \"", filename, "\"",
  8193. " cwd: ", cwd,
  8194. " err: ", strerror(errno), NULL);
  8195. free(cwd);
  8196. return JIM_ERR;
  8197. }
  8198. buflen = 1024;
  8199. maxlen = totread = 0;
  8200. while (1) {
  8201. if (maxlen < totread + buflen + 1) {
  8202. maxlen = totread + buflen + 1;
  8203. prg = Jim_Realloc(prg, maxlen);
  8204. }
  8205. /* do not use Jim_fread() - this is really a file */
  8206. if ((nread = fread(prg + totread, 1, buflen, fp)) == 0) break;
  8207. totread += nread;
  8208. }
  8209. prg[totread] = '\0';
  8210. /* do not use Jim_fclose() - this is really a file */
  8211. fclose(fp);
  8212. scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread);
  8213. JimSetSourceInfo(interp, scriptObjPtr, filename, 1);
  8214. Jim_IncrRefCount(scriptObjPtr);
  8215. retval = Jim_EvalObj(interp, scriptObjPtr);
  8216. Jim_DecrRefCount(interp, scriptObjPtr);
  8217. return retval;
  8218. }
  8219. /* -----------------------------------------------------------------------------
  8220. * Subst
  8221. * ---------------------------------------------------------------------------*/
  8222. static int JimParseSubstStr(struct JimParserCtx *pc)
  8223. {
  8224. pc->tstart = pc->p;
  8225. pc->tline = pc->linenr;
  8226. while (*pc->p && *pc->p != '$' && *pc->p != '[') {
  8227. pc->p++; pc->len--;
  8228. }
  8229. pc->tend = pc->p-1;
  8230. pc->tt = JIM_TT_ESC;
  8231. return JIM_OK;
  8232. }
  8233. static int JimParseSubst(struct JimParserCtx *pc, int flags)
  8234. {
  8235. int retval;
  8236. if (pc->len == 0) {
  8237. pc->tstart = pc->tend = pc->p;
  8238. pc->tline = pc->linenr;
  8239. pc->tt = JIM_TT_EOL;
  8240. pc->eof = 1;
  8241. return JIM_OK;
  8242. }
  8243. switch (*pc->p) {
  8244. case '[':
  8245. retval = JimParseCmd(pc);
  8246. if (flags & JIM_SUBST_NOCMD) {
  8247. pc->tstart--;
  8248. pc->tend++;
  8249. pc->tt = (flags & JIM_SUBST_NOESC) ?
  8250. JIM_TT_STR : JIM_TT_ESC;
  8251. }
  8252. return retval;
  8253. break;
  8254. case '$':
  8255. if (JimParseVar(pc) == JIM_ERR) {
  8256. pc->tstart = pc->tend = pc->p++; pc->len--;
  8257. pc->tline = pc->linenr;
  8258. pc->tt = JIM_TT_STR;
  8259. } else {
  8260. if (flags & JIM_SUBST_NOVAR) {
  8261. pc->tstart--;
  8262. if (flags & JIM_SUBST_NOESC)
  8263. pc->tt = JIM_TT_STR;
  8264. else
  8265. pc->tt = JIM_TT_ESC;
  8266. if (*pc->tstart == '{') {
  8267. pc->tstart--;
  8268. if (*(pc->tend + 1))
  8269. pc->tend++;
  8270. }
  8271. }
  8272. }
  8273. break;
  8274. default:
  8275. retval = JimParseSubstStr(pc);
  8276. if (flags & JIM_SUBST_NOESC)
  8277. pc->tt = JIM_TT_STR;
  8278. return retval;
  8279. break;
  8280. }
  8281. return JIM_OK;
  8282. }
  8283. /* The subst object type reuses most of the data structures and functions
  8284. * of the script object. Script's data structures are a bit more complex
  8285. * for what is needed for [subst]itution tasks, but the reuse helps to
  8286. * deal with a single data structure at the cost of some more memory
  8287. * usage for substitutions. */
  8288. static Jim_ObjType substObjType = {
  8289. "subst",
  8290. FreeScriptInternalRep,
  8291. DupScriptInternalRep,
  8292. NULL,
  8293. JIM_TYPE_REFERENCES,
  8294. };
  8295. /* This method takes the string representation of an object
  8296. * as a Tcl string where to perform [subst]itution, and generates
  8297. * the pre-parsed internal representation. */
  8298. int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
  8299. {
  8300. int scriptTextLen;
  8301. const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
  8302. struct JimParserCtx parser;
  8303. struct ScriptObj *script = Jim_Alloc(sizeof(*script));
  8304. script->len = 0;
  8305. script->csLen = 0;
  8306. script->commands = 0;
  8307. script->token = NULL;
  8308. script->cmdStruct = NULL;
  8309. script->inUse = 1;
  8310. script->substFlags = flags;
  8311. script->fileName = NULL;
  8312. JimParserInit(&parser, scriptText, scriptTextLen, 1);
  8313. while (1) {
  8314. char *token;
  8315. int len, type, linenr;
  8316. JimParseSubst(&parser, flags);
  8317. if (JimParserEof(&parser)) break;
  8318. token = JimParserGetToken(&parser, &len, &type, &linenr);
  8319. ScriptObjAddToken(interp, script, token, len, type,
  8320. NULL, linenr);
  8321. }
  8322. /* Free the old internal rep and set the new one. */
  8323. Jim_FreeIntRep(interp, objPtr);
  8324. Jim_SetIntRepPtr(objPtr, script);
  8325. objPtr->typePtr = &scriptObjType;
  8326. return JIM_OK;
  8327. }
  8328. ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
  8329. {
  8330. struct ScriptObj *script = Jim_GetIntRepPtr(objPtr);
  8331. if (objPtr->typePtr != &substObjType || script->substFlags != flags)
  8332. SetSubstFromAny(interp, objPtr, flags);
  8333. return (ScriptObj*) Jim_GetIntRepPtr(objPtr);
  8334. }
  8335. /* Performs commands,variables,blackslashes substitution,
  8336. * storing the result object (with refcount 0) into
  8337. * resObjPtrPtr. */
  8338. int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr,
  8339. Jim_Obj **resObjPtrPtr, int flags)
  8340. {
  8341. ScriptObj *script;
  8342. ScriptToken *token;
  8343. int i, len, retcode = JIM_OK;
  8344. Jim_Obj *resObjPtr, *savedResultObjPtr;
  8345. script = Jim_GetSubst(interp, substObjPtr, flags);
  8346. #ifdef JIM_OPTIMIZATION
  8347. /* Fast path for a very common case with array-alike syntax,
  8348. * that's: $foo($bar) */
  8349. if (script->len == 1 && script->token[0].type == JIM_TT_VAR) {
  8350. Jim_Obj *varObjPtr = script->token[0].objPtr;
  8351. Jim_IncrRefCount(varObjPtr);
  8352. resObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
  8353. if (resObjPtr == NULL) {
  8354. Jim_DecrRefCount(interp, varObjPtr);
  8355. return JIM_ERR;
  8356. }
  8357. Jim_DecrRefCount(interp, varObjPtr);
  8358. *resObjPtrPtr = resObjPtr;
  8359. return JIM_OK;
  8360. }
  8361. #endif
  8362. Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
  8363. /* In order to preserve the internal rep, we increment the
  8364. * inUse field of the script internal rep structure. */
  8365. script->inUse++;
  8366. token = script->token;
  8367. len = script->len;
  8368. /* Save the interp old result, to set it again before
  8369. * to return. */
  8370. savedResultObjPtr = interp->result;
  8371. Jim_IncrRefCount(savedResultObjPtr);
  8372. /* Perform the substitution. Starts with an empty object
  8373. * and adds every token (performing the appropriate
  8374. * var/command/escape substitution). */
  8375. resObjPtr = Jim_NewStringObj(interp, "", 0);
  8376. for (i = 0; i < len; i++) {
  8377. Jim_Obj *objPtr;
  8378. switch (token[i].type) {
  8379. case JIM_TT_STR:
  8380. case JIM_TT_ESC:
  8381. Jim_AppendObj(interp, resObjPtr, token[i].objPtr);
  8382. break;
  8383. case JIM_TT_VAR:
  8384. objPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
  8385. if (objPtr == NULL) goto err;
  8386. Jim_IncrRefCount(objPtr);
  8387. Jim_AppendObj(interp, resObjPtr, objPtr);
  8388. Jim_DecrRefCount(interp, objPtr);
  8389. break;
  8390. case JIM_TT_DICTSUGAR:
  8391. objPtr = Jim_ExpandDictSugar(interp, token[i].objPtr);
  8392. if (!objPtr) {
  8393. retcode = JIM_ERR;
  8394. goto err;
  8395. }
  8396. break;
  8397. case JIM_TT_CMD:
  8398. if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
  8399. goto err;
  8400. Jim_AppendObj(interp, resObjPtr, interp->result);
  8401. break;
  8402. default:
  8403. Jim_Panic(interp,
  8404. "default token type (%d) reached "
  8405. "in Jim_SubstObj().", token[i].type);
  8406. break;
  8407. }
  8408. }
  8409. ok:
  8410. if (retcode == JIM_OK)
  8411. Jim_SetResult(interp, savedResultObjPtr);
  8412. Jim_DecrRefCount(interp, savedResultObjPtr);
  8413. /* Note that we don't have to decrement inUse, because the
  8414. * following code transfers our use of the reference again to
  8415. * the script object. */
  8416. Jim_FreeIntRep(interp, substObjPtr);
  8417. substObjPtr->typePtr = &scriptObjType;
  8418. Jim_SetIntRepPtr(substObjPtr, script);
  8419. Jim_DecrRefCount(interp, substObjPtr);
  8420. *resObjPtrPtr = resObjPtr;
  8421. return retcode;
  8422. err:
  8423. Jim_FreeNewObj(interp, resObjPtr);
  8424. retcode = JIM_ERR;
  8425. goto ok;
  8426. }
  8427. /* -----------------------------------------------------------------------------
  8428. * API Input/Export functions
  8429. * ---------------------------------------------------------------------------*/
  8430. int Jim_GetApi(Jim_Interp *interp, const char *funcname, void *targetPtrPtr)
  8431. {
  8432. Jim_HashEntry *he;
  8433. he = Jim_FindHashEntry(&interp->stub, funcname);
  8434. if (!he)
  8435. return JIM_ERR;
  8436. memcpy(targetPtrPtr, &he->val, sizeof(void*));
  8437. return JIM_OK;
  8438. }
  8439. int Jim_RegisterApi(Jim_Interp *interp, const char *funcname, void *funcptr)
  8440. {
  8441. return Jim_AddHashEntry(&interp->stub, funcname, funcptr);
  8442. }
  8443. #define JIM_REGISTER_API(name) \
  8444. Jim_RegisterApi(interp, "Jim_" #name, (void *)Jim_ ## name)
  8445. void JimRegisterCoreApi(Jim_Interp *interp)
  8446. {
  8447. interp->getApiFuncPtr = Jim_GetApi;
  8448. JIM_REGISTER_API(Alloc);
  8449. JIM_REGISTER_API(Free);
  8450. JIM_REGISTER_API(Eval);
  8451. JIM_REGISTER_API(Eval_Named);
  8452. JIM_REGISTER_API(EvalGlobal);
  8453. JIM_REGISTER_API(EvalFile);
  8454. JIM_REGISTER_API(EvalObj);
  8455. JIM_REGISTER_API(EvalObjBackground);
  8456. JIM_REGISTER_API(EvalObjVector);
  8457. JIM_REGISTER_API(InitHashTable);
  8458. JIM_REGISTER_API(ExpandHashTable);
  8459. JIM_REGISTER_API(AddHashEntry);
  8460. JIM_REGISTER_API(ReplaceHashEntry);
  8461. JIM_REGISTER_API(DeleteHashEntry);
  8462. JIM_REGISTER_API(FreeHashTable);
  8463. JIM_REGISTER_API(FindHashEntry);
  8464. JIM_REGISTER_API(ResizeHashTable);
  8465. JIM_REGISTER_API(GetHashTableIterator);
  8466. JIM_REGISTER_API(NextHashEntry);
  8467. JIM_REGISTER_API(NewObj);
  8468. JIM_REGISTER_API(FreeObj);
  8469. JIM_REGISTER_API(InvalidateStringRep);
  8470. JIM_REGISTER_API(InitStringRep);
  8471. JIM_REGISTER_API(DuplicateObj);
  8472. JIM_REGISTER_API(GetString);
  8473. JIM_REGISTER_API(Length);
  8474. JIM_REGISTER_API(InvalidateStringRep);
  8475. JIM_REGISTER_API(NewStringObj);
  8476. JIM_REGISTER_API(NewStringObjNoAlloc);
  8477. JIM_REGISTER_API(AppendString);
  8478. JIM_REGISTER_API(AppendString_sprintf);
  8479. JIM_REGISTER_API(AppendObj);
  8480. JIM_REGISTER_API(AppendStrings);
  8481. JIM_REGISTER_API(StringEqObj);
  8482. JIM_REGISTER_API(StringMatchObj);
  8483. JIM_REGISTER_API(StringRangeObj);
  8484. JIM_REGISTER_API(FormatString);
  8485. JIM_REGISTER_API(CompareStringImmediate);
  8486. JIM_REGISTER_API(NewReference);
  8487. JIM_REGISTER_API(GetReference);
  8488. JIM_REGISTER_API(SetFinalizer);
  8489. JIM_REGISTER_API(GetFinalizer);
  8490. JIM_REGISTER_API(CreateInterp);
  8491. JIM_REGISTER_API(FreeInterp);
  8492. JIM_REGISTER_API(GetExitCode);
  8493. JIM_REGISTER_API(SetStdin);
  8494. JIM_REGISTER_API(SetStdout);
  8495. JIM_REGISTER_API(SetStderr);
  8496. JIM_REGISTER_API(CreateCommand);
  8497. JIM_REGISTER_API(CreateProcedure);
  8498. JIM_REGISTER_API(DeleteCommand);
  8499. JIM_REGISTER_API(RenameCommand);
  8500. JIM_REGISTER_API(GetCommand);
  8501. JIM_REGISTER_API(SetVariable);
  8502. JIM_REGISTER_API(SetVariableStr);
  8503. JIM_REGISTER_API(SetGlobalVariableStr);
  8504. JIM_REGISTER_API(SetVariableStrWithStr);
  8505. JIM_REGISTER_API(SetVariableLink);
  8506. JIM_REGISTER_API(GetVariable);
  8507. JIM_REGISTER_API(GetCallFrameByLevel);
  8508. JIM_REGISTER_API(Collect);
  8509. JIM_REGISTER_API(CollectIfNeeded);
  8510. JIM_REGISTER_API(GetIndex);
  8511. JIM_REGISTER_API(NewListObj);
  8512. JIM_REGISTER_API(ListAppendElement);
  8513. JIM_REGISTER_API(ListAppendList);
  8514. JIM_REGISTER_API(ListLength);
  8515. JIM_REGISTER_API(ListIndex);
  8516. JIM_REGISTER_API(SetListIndex);
  8517. JIM_REGISTER_API(ConcatObj);
  8518. JIM_REGISTER_API(NewDictObj);
  8519. JIM_REGISTER_API(DictKey);
  8520. JIM_REGISTER_API(DictKeysVector);
  8521. JIM_REGISTER_API(GetIndex);
  8522. JIM_REGISTER_API(GetReturnCode);
  8523. JIM_REGISTER_API(EvalExpression);
  8524. JIM_REGISTER_API(GetBoolFromExpr);
  8525. JIM_REGISTER_API(GetWide);
  8526. JIM_REGISTER_API(GetLong);
  8527. JIM_REGISTER_API(SetWide);
  8528. JIM_REGISTER_API(NewIntObj);
  8529. JIM_REGISTER_API(GetDouble);
  8530. JIM_REGISTER_API(SetDouble);
  8531. JIM_REGISTER_API(NewDoubleObj);
  8532. JIM_REGISTER_API(WrongNumArgs);
  8533. JIM_REGISTER_API(SetDictKeysVector);
  8534. JIM_REGISTER_API(SubstObj);
  8535. JIM_REGISTER_API(RegisterApi);
  8536. JIM_REGISTER_API(PrintErrorMessage);
  8537. JIM_REGISTER_API(InteractivePrompt);
  8538. JIM_REGISTER_API(RegisterCoreCommands);
  8539. JIM_REGISTER_API(GetSharedString);
  8540. JIM_REGISTER_API(ReleaseSharedString);
  8541. JIM_REGISTER_API(Panic);
  8542. JIM_REGISTER_API(StrDup);
  8543. JIM_REGISTER_API(UnsetVariable);
  8544. JIM_REGISTER_API(GetVariableStr);
  8545. JIM_REGISTER_API(GetGlobalVariable);
  8546. JIM_REGISTER_API(GetGlobalVariableStr);
  8547. JIM_REGISTER_API(GetAssocData);
  8548. JIM_REGISTER_API(SetAssocData);
  8549. JIM_REGISTER_API(DeleteAssocData);
  8550. JIM_REGISTER_API(GetEnum);
  8551. JIM_REGISTER_API(ScriptIsComplete);
  8552. JIM_REGISTER_API(PackageRequire);
  8553. JIM_REGISTER_API(PackageProvide);
  8554. JIM_REGISTER_API(InitStack);
  8555. JIM_REGISTER_API(FreeStack);
  8556. JIM_REGISTER_API(StackLen);
  8557. JIM_REGISTER_API(StackPush);
  8558. JIM_REGISTER_API(StackPop);
  8559. JIM_REGISTER_API(StackPeek);
  8560. JIM_REGISTER_API(FreeStackElements);
  8561. JIM_REGISTER_API(fprintf);
  8562. JIM_REGISTER_API(vfprintf);
  8563. JIM_REGISTER_API(fwrite);
  8564. JIM_REGISTER_API(fread);
  8565. JIM_REGISTER_API(fflush);
  8566. JIM_REGISTER_API(fgets);
  8567. JIM_REGISTER_API(GetNvp);
  8568. JIM_REGISTER_API(Nvp_name2value);
  8569. JIM_REGISTER_API(Nvp_name2value_simple);
  8570. JIM_REGISTER_API(Nvp_name2value_obj);
  8571. JIM_REGISTER_API(Nvp_name2value_nocase);
  8572. JIM_REGISTER_API(Nvp_name2value_obj_nocase);
  8573. JIM_REGISTER_API(Nvp_value2name);
  8574. JIM_REGISTER_API(Nvp_value2name_simple);
  8575. JIM_REGISTER_API(Nvp_value2name_obj);
  8576. JIM_REGISTER_API(GetOpt_Setup);
  8577. JIM_REGISTER_API(GetOpt_Debug);
  8578. JIM_REGISTER_API(GetOpt_Obj);
  8579. JIM_REGISTER_API(GetOpt_String);
  8580. JIM_REGISTER_API(GetOpt_Double);
  8581. JIM_REGISTER_API(GetOpt_Wide);
  8582. JIM_REGISTER_API(GetOpt_Nvp);
  8583. JIM_REGISTER_API(GetOpt_NvpUnknown);
  8584. JIM_REGISTER_API(GetOpt_Enum);
  8585. JIM_REGISTER_API(Debug_ArgvString);
  8586. JIM_REGISTER_API(SetResult_sprintf);
  8587. JIM_REGISTER_API(SetResult_NvpUnknown);
  8588. }
  8589. /* -----------------------------------------------------------------------------
  8590. * Core commands utility functions
  8591. * ---------------------------------------------------------------------------*/
  8592. void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
  8593. const char *msg)
  8594. {
  8595. int i;
  8596. Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp);
  8597. Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1);
  8598. for (i = 0; i < argc; i++) {
  8599. Jim_AppendObj(interp, objPtr, argv[i]);
  8600. if (!(i + 1 == argc && msg[0] == '\0'))
  8601. Jim_AppendString(interp, objPtr, " ", 1);
  8602. }
  8603. Jim_AppendString(interp, objPtr, msg, -1);
  8604. Jim_AppendString(interp, objPtr, "\"", 1);
  8605. Jim_SetResult(interp, objPtr);
  8606. }
  8607. static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr)
  8608. {
  8609. Jim_HashTableIterator *htiter;
  8610. Jim_HashEntry *he;
  8611. Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
  8612. const char *pattern;
  8613. int patternLen=0;
  8614. pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
  8615. htiter = Jim_GetHashTableIterator(&interp->commands);
  8616. while ((he = Jim_NextHashEntry(htiter)) != NULL) {
  8617. if (pattern && !JimStringMatch(pattern, patternLen, he->key,
  8618. strlen((const char*)he->key), 0))
  8619. continue;
  8620. Jim_ListAppendElement(interp, listObjPtr,
  8621. Jim_NewStringObj(interp, he->key, -1));
  8622. }
  8623. Jim_FreeHashTableIterator(htiter);
  8624. return listObjPtr;
  8625. }
  8626. #define JIM_VARLIST_GLOBALS 0
  8627. #define JIM_VARLIST_LOCALS 1
  8628. #define JIM_VARLIST_VARS 2
  8629. static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr,
  8630. int mode)
  8631. {
  8632. Jim_HashTableIterator *htiter;
  8633. Jim_HashEntry *he;
  8634. Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
  8635. const char *pattern;
  8636. int patternLen=0;
  8637. pattern = patternObjPtr ? Jim_GetString(patternObjPtr, &patternLen) : NULL;
  8638. if (mode == JIM_VARLIST_GLOBALS) {
  8639. htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars);
  8640. } else {
  8641. /* For [info locals], if we are at top level an emtpy list
  8642. * is returned. I don't agree, but we aim at compatibility (SS) */
  8643. if (mode == JIM_VARLIST_LOCALS &&
  8644. interp->framePtr == interp->topFramePtr)
  8645. return listObjPtr;
  8646. htiter = Jim_GetHashTableIterator(&interp->framePtr->vars);
  8647. }
  8648. while ((he = Jim_NextHashEntry(htiter)) != NULL) {
  8649. Jim_Var *varPtr = (Jim_Var*) he->val;
  8650. if (mode == JIM_VARLIST_LOCALS) {
  8651. if (varPtr->linkFramePtr != NULL)
  8652. continue;
  8653. }
  8654. if (pattern && !JimStringMatch(pattern, patternLen, he->key,
  8655. strlen((const char*)he->key), 0))
  8656. continue;
  8657. Jim_ListAppendElement(interp, listObjPtr,
  8658. Jim_NewStringObj(interp, he->key, -1));
  8659. }
  8660. Jim_FreeHashTableIterator(htiter);
  8661. return listObjPtr;
  8662. }
  8663. static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
  8664. Jim_Obj **objPtrPtr)
  8665. {
  8666. Jim_CallFrame *targetCallFrame;
  8667. if (JimGetCallFrameByInteger(interp, levelObjPtr, &targetCallFrame)
  8668. != JIM_OK)
  8669. return JIM_ERR;
  8670. /* No proc call at toplevel callframe */
  8671. if (targetCallFrame == interp->topFramePtr) {
  8672. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  8673. Jim_AppendStrings(interp, Jim_GetResult(interp),
  8674. "bad level \"",
  8675. Jim_GetString(levelObjPtr, NULL), "\"", NULL);
  8676. return JIM_ERR;
  8677. }
  8678. *objPtrPtr = Jim_NewListObj(interp,
  8679. targetCallFrame->argv,
  8680. targetCallFrame->argc);
  8681. return JIM_OK;
  8682. }
  8683. /* -----------------------------------------------------------------------------
  8684. * Core commands
  8685. * ---------------------------------------------------------------------------*/
  8686. /* fake [puts] -- not the real puts, just for debugging. */
  8687. static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc,
  8688. Jim_Obj *const *argv)
  8689. {
  8690. const char *str;
  8691. int len, nonewline = 0;
  8692. if (argc != 2 && argc != 3) {
  8693. Jim_WrongNumArgs(interp, 1, argv, "-nonewline string");
  8694. return JIM_ERR;
  8695. }
  8696. if (argc == 3) {
  8697. if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline"))
  8698. {
  8699. Jim_SetResultString(interp, "The second argument must "
  8700. "be -nonewline", -1);
  8701. return JIM_OK;
  8702. } else {
  8703. nonewline = 1;
  8704. argv++;
  8705. }
  8706. }
  8707. str = Jim_GetString(argv[1], &len);
  8708. Jim_fwrite(interp, str, 1, len, interp->cookie_stdout);
  8709. if (!nonewline) Jim_fprintf(interp, interp->cookie_stdout, JIM_NL);
  8710. return JIM_OK;
  8711. }
  8712. /* Helper for [+] and [*] */
  8713. static int Jim_AddMulHelper(Jim_Interp *interp, int argc,
  8714. Jim_Obj *const *argv, int op)
  8715. {
  8716. jim_wide wideValue, res;
  8717. double doubleValue, doubleRes;
  8718. int i;
  8719. res = (op == JIM_EXPROP_ADD) ? 0 : 1;
  8720. for (i = 1; i < argc; i++) {
  8721. if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
  8722. goto trydouble;
  8723. if (op == JIM_EXPROP_ADD)
  8724. res += wideValue;
  8725. else
  8726. res *= wideValue;
  8727. }
  8728. Jim_SetResult(interp, Jim_NewIntObj(interp, res));
  8729. return JIM_OK;
  8730. trydouble:
  8731. doubleRes = (double) res;
  8732. for (;i < argc; i++) {
  8733. if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
  8734. return JIM_ERR;
  8735. if (op == JIM_EXPROP_ADD)
  8736. doubleRes += doubleValue;
  8737. else
  8738. doubleRes *= doubleValue;
  8739. }
  8740. Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
  8741. return JIM_OK;
  8742. }
  8743. /* Helper for [-] and [/] */
  8744. static int Jim_SubDivHelper(Jim_Interp *interp, int argc,
  8745. Jim_Obj *const *argv, int op)
  8746. {
  8747. jim_wide wideValue, res = 0;
  8748. double doubleValue, doubleRes = 0;
  8749. int i = 2;
  8750. if (argc < 2) {
  8751. Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
  8752. return JIM_ERR;
  8753. } else if (argc == 2) {
  8754. /* The arity = 2 case is different. For [- x] returns -x,
  8755. * while [/ x] returns 1/x. */
  8756. if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
  8757. if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
  8758. JIM_OK)
  8759. {
  8760. return JIM_ERR;
  8761. } else {
  8762. if (op == JIM_EXPROP_SUB)
  8763. doubleRes = -doubleValue;
  8764. else
  8765. doubleRes = 1.0/doubleValue;
  8766. Jim_SetResult(interp, Jim_NewDoubleObj(interp,
  8767. doubleRes));
  8768. return JIM_OK;
  8769. }
  8770. }
  8771. if (op == JIM_EXPROP_SUB) {
  8772. res = -wideValue;
  8773. Jim_SetResult(interp, Jim_NewIntObj(interp, res));
  8774. } else {
  8775. doubleRes = 1.0/wideValue;
  8776. Jim_SetResult(interp, Jim_NewDoubleObj(interp,
  8777. doubleRes));
  8778. }
  8779. return JIM_OK;
  8780. } else {
  8781. if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
  8782. if (Jim_GetDouble(interp, argv[1], &doubleRes)
  8783. != JIM_OK) {
  8784. return JIM_ERR;
  8785. } else {
  8786. goto trydouble;
  8787. }
  8788. }
  8789. }
  8790. for (i = 2; i < argc; i++) {
  8791. if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
  8792. doubleRes = (double) res;
  8793. goto trydouble;
  8794. }
  8795. if (op == JIM_EXPROP_SUB)
  8796. res -= wideValue;
  8797. else
  8798. res /= wideValue;
  8799. }
  8800. Jim_SetResult(interp, Jim_NewIntObj(interp, res));
  8801. return JIM_OK;
  8802. trydouble:
  8803. for (;i < argc; i++) {
  8804. if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
  8805. return JIM_ERR;
  8806. if (op == JIM_EXPROP_SUB)
  8807. doubleRes -= doubleValue;
  8808. else
  8809. doubleRes /= doubleValue;
  8810. }
  8811. Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
  8812. return JIM_OK;
  8813. }
  8814. /* [+] */
  8815. static int Jim_AddCoreCommand(Jim_Interp *interp, int argc,
  8816. Jim_Obj *const *argv)
  8817. {
  8818. return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
  8819. }
  8820. /* [*] */
  8821. static int Jim_MulCoreCommand(Jim_Interp *interp, int argc,
  8822. Jim_Obj *const *argv)
  8823. {
  8824. return Jim_AddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
  8825. }
  8826. /* [-] */
  8827. static int Jim_SubCoreCommand(Jim_Interp *interp, int argc,
  8828. Jim_Obj *const *argv)
  8829. {
  8830. return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
  8831. }
  8832. /* [/] */
  8833. static int Jim_DivCoreCommand(Jim_Interp *interp, int argc,
  8834. Jim_Obj *const *argv)
  8835. {
  8836. return Jim_SubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
  8837. }
  8838. /* [set] */
  8839. static int Jim_SetCoreCommand(Jim_Interp *interp, int argc,
  8840. Jim_Obj *const *argv)
  8841. {
  8842. if (argc != 2 && argc != 3) {
  8843. Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
  8844. return JIM_ERR;
  8845. }
  8846. if (argc == 2) {
  8847. Jim_Obj *objPtr;
  8848. objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
  8849. if (!objPtr)
  8850. return JIM_ERR;
  8851. Jim_SetResult(interp, objPtr);
  8852. return JIM_OK;
  8853. }
  8854. /* argc == 3 case. */
  8855. if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
  8856. return JIM_ERR;
  8857. Jim_SetResult(interp, argv[2]);
  8858. return JIM_OK;
  8859. }
  8860. /* [unset] */
  8861. static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc,
  8862. Jim_Obj *const *argv)
  8863. {
  8864. int i;
  8865. if (argc < 2) {
  8866. Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
  8867. return JIM_ERR;
  8868. }
  8869. for (i = 1; i < argc; i++) {
  8870. if (Jim_UnsetVariable(interp, argv[i], JIM_ERRMSG) != JIM_OK)
  8871. return JIM_ERR;
  8872. }
  8873. return JIM_OK;
  8874. }
  8875. /* [incr] */
  8876. static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc,
  8877. Jim_Obj *const *argv)
  8878. {
  8879. jim_wide wideValue, increment = 1;
  8880. Jim_Obj *intObjPtr;
  8881. if (argc != 2 && argc != 3) {
  8882. Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
  8883. return JIM_ERR;
  8884. }
  8885. if (argc == 3) {
  8886. if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
  8887. return JIM_ERR;
  8888. }
  8889. intObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
  8890. if (!intObjPtr) return JIM_ERR;
  8891. if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK)
  8892. return JIM_ERR;
  8893. if (Jim_IsShared(intObjPtr)) {
  8894. intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
  8895. if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
  8896. Jim_FreeNewObj(interp, intObjPtr);
  8897. return JIM_ERR;
  8898. }
  8899. } else {
  8900. Jim_SetWide(interp, intObjPtr, wideValue + increment);
  8901. /* The following step is required in order to invalidate the
  8902. * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */
  8903. if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
  8904. return JIM_ERR;
  8905. }
  8906. }
  8907. Jim_SetResult(interp, intObjPtr);
  8908. return JIM_OK;
  8909. }
  8910. /* [while] */
  8911. static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc,
  8912. Jim_Obj *const *argv)
  8913. {
  8914. if (argc != 3) {
  8915. Jim_WrongNumArgs(interp, 1, argv, "condition body");
  8916. return JIM_ERR;
  8917. }
  8918. /* Try to run a specialized version of while if the expression
  8919. * is in one of the following forms:
  8920. *
  8921. * $a < CONST, $a < $b
  8922. * $a <= CONST, $a <= $b
  8923. * $a > CONST, $a > $b
  8924. * $a >= CONST, $a >= $b
  8925. * $a != CONST, $a != $b
  8926. * $a == CONST, $a == $b
  8927. * $a
  8928. * !$a
  8929. * CONST
  8930. */
  8931. #ifdef JIM_OPTIMIZATION
  8932. {
  8933. ExprByteCode *expr;
  8934. Jim_Obj *varAObjPtr = NULL, *varBObjPtr = NULL, *objPtr;
  8935. int exprLen, retval;
  8936. /* STEP 1 -- Check if there are the conditions to run the specialized
  8937. * version of while */
  8938. if ((expr = Jim_GetExpression(interp, argv[1])) == NULL) goto noopt;
  8939. if (expr->len <= 0 || expr->len > 3) goto noopt;
  8940. switch (expr->len) {
  8941. case 1:
  8942. if (expr->opcode[0] != JIM_EXPROP_VARIABLE &&
  8943. expr->opcode[0] != JIM_EXPROP_NUMBER)
  8944. goto noopt;
  8945. break;
  8946. case 2:
  8947. if (expr->opcode[1] != JIM_EXPROP_NOT ||
  8948. expr->opcode[0] != JIM_EXPROP_VARIABLE)
  8949. goto noopt;
  8950. break;
  8951. case 3:
  8952. if (expr->opcode[0] != JIM_EXPROP_VARIABLE ||
  8953. (expr->opcode[1] != JIM_EXPROP_NUMBER &&
  8954. expr->opcode[1] != JIM_EXPROP_VARIABLE))
  8955. goto noopt;
  8956. switch (expr->opcode[2]) {
  8957. case JIM_EXPROP_LT:
  8958. case JIM_EXPROP_LTE:
  8959. case JIM_EXPROP_GT:
  8960. case JIM_EXPROP_GTE:
  8961. case JIM_EXPROP_NUMEQ:
  8962. case JIM_EXPROP_NUMNE:
  8963. /* nothing to do */
  8964. break;
  8965. default:
  8966. goto noopt;
  8967. }
  8968. break;
  8969. default:
  8970. Jim_Panic(interp,
  8971. "Unexpected default reached in Jim_WhileCoreCommand()");
  8972. break;
  8973. }
  8974. /* STEP 2 -- conditions meet. Initialization. Take different
  8975. * branches for different expression lengths. */
  8976. exprLen = expr->len;
  8977. if (exprLen == 1) {
  8978. jim_wide wideValue=0;
  8979. if (expr->opcode[0] == JIM_EXPROP_VARIABLE) {
  8980. varAObjPtr = expr->obj[0];
  8981. Jim_IncrRefCount(varAObjPtr);
  8982. } else {
  8983. if (Jim_GetWide(interp, expr->obj[0], &wideValue) != JIM_OK)
  8984. goto noopt;
  8985. }
  8986. while (1) {
  8987. if (varAObjPtr) {
  8988. if (!(objPtr =
  8989. Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
  8990. Jim_GetWide(interp, objPtr, &wideValue) != JIM_OK)
  8991. {
  8992. Jim_DecrRefCount(interp, varAObjPtr);
  8993. goto noopt;
  8994. }
  8995. }
  8996. if (!wideValue) break;
  8997. if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
  8998. switch (retval) {
  8999. case JIM_BREAK:
  9000. if (varAObjPtr)
  9001. Jim_DecrRefCount(interp, varAObjPtr);
  9002. goto out;
  9003. break;
  9004. case JIM_CONTINUE:
  9005. continue;
  9006. break;
  9007. default:
  9008. if (varAObjPtr)
  9009. Jim_DecrRefCount(interp, varAObjPtr);
  9010. return retval;
  9011. }
  9012. }
  9013. }
  9014. if (varAObjPtr)
  9015. Jim_DecrRefCount(interp, varAObjPtr);
  9016. } else if (exprLen == 3) {
  9017. jim_wide wideValueA, wideValueB=0, cmpRes = 0;
  9018. int cmpType = expr->opcode[2];
  9019. varAObjPtr = expr->obj[0];
  9020. Jim_IncrRefCount(varAObjPtr);
  9021. if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
  9022. varBObjPtr = expr->obj[1];
  9023. Jim_IncrRefCount(varBObjPtr);
  9024. } else {
  9025. if (Jim_GetWide(interp, expr->obj[1], &wideValueB) != JIM_OK)
  9026. goto noopt;
  9027. }
  9028. while (1) {
  9029. if (!(objPtr = Jim_GetVariable(interp, varAObjPtr, JIM_NONE)) ||
  9030. Jim_GetWide(interp, objPtr, &wideValueA) != JIM_OK)
  9031. {
  9032. Jim_DecrRefCount(interp, varAObjPtr);
  9033. if (varBObjPtr)
  9034. Jim_DecrRefCount(interp, varBObjPtr);
  9035. goto noopt;
  9036. }
  9037. if (varBObjPtr) {
  9038. if (!(objPtr =
  9039. Jim_GetVariable(interp, varBObjPtr, JIM_NONE)) ||
  9040. Jim_GetWide(interp, objPtr, &wideValueB) != JIM_OK)
  9041. {
  9042. Jim_DecrRefCount(interp, varAObjPtr);
  9043. Jim_DecrRefCount(interp, varBObjPtr);
  9044. goto noopt;
  9045. }
  9046. }
  9047. switch (cmpType) {
  9048. case JIM_EXPROP_LT:
  9049. cmpRes = wideValueA < wideValueB; break;
  9050. case JIM_EXPROP_LTE:
  9051. cmpRes = wideValueA <= wideValueB; break;
  9052. case JIM_EXPROP_GT:
  9053. cmpRes = wideValueA > wideValueB; break;
  9054. case JIM_EXPROP_GTE:
  9055. cmpRes = wideValueA >= wideValueB; break;
  9056. case JIM_EXPROP_NUMEQ:
  9057. cmpRes = wideValueA == wideValueB; break;
  9058. case JIM_EXPROP_NUMNE:
  9059. cmpRes = wideValueA != wideValueB; break;
  9060. }
  9061. if (!cmpRes) break;
  9062. if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
  9063. switch (retval) {
  9064. case JIM_BREAK:
  9065. Jim_DecrRefCount(interp, varAObjPtr);
  9066. if (varBObjPtr)
  9067. Jim_DecrRefCount(interp, varBObjPtr);
  9068. goto out;
  9069. break;
  9070. case JIM_CONTINUE:
  9071. continue;
  9072. break;
  9073. default:
  9074. Jim_DecrRefCount(interp, varAObjPtr);
  9075. if (varBObjPtr)
  9076. Jim_DecrRefCount(interp, varBObjPtr);
  9077. return retval;
  9078. }
  9079. }
  9080. }
  9081. Jim_DecrRefCount(interp, varAObjPtr);
  9082. if (varBObjPtr)
  9083. Jim_DecrRefCount(interp, varBObjPtr);
  9084. } else {
  9085. /* TODO: case for len == 2 */
  9086. goto noopt;
  9087. }
  9088. Jim_SetEmptyResult(interp);
  9089. return JIM_OK;
  9090. }
  9091. noopt:
  9092. #endif
  9093. /* The general purpose implementation of while starts here */
  9094. while (1) {
  9095. int boolean, retval;
  9096. if ((retval = Jim_GetBoolFromExpr(interp, argv[1],
  9097. &boolean)) != JIM_OK)
  9098. return retval;
  9099. if (!boolean) break;
  9100. if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
  9101. switch (retval) {
  9102. case JIM_BREAK:
  9103. goto out;
  9104. break;
  9105. case JIM_CONTINUE:
  9106. continue;
  9107. break;
  9108. default:
  9109. return retval;
  9110. }
  9111. }
  9112. }
  9113. out:
  9114. Jim_SetEmptyResult(interp);
  9115. return JIM_OK;
  9116. }
  9117. /* [for] */
  9118. static int Jim_ForCoreCommand(Jim_Interp *interp, int argc,
  9119. Jim_Obj *const *argv)
  9120. {
  9121. int retval;
  9122. if (argc != 5) {
  9123. Jim_WrongNumArgs(interp, 1, argv, "start test next body");
  9124. return JIM_ERR;
  9125. }
  9126. /* Check if the for is on the form:
  9127. * for {set i CONST} {$i < CONST} {incr i}
  9128. * for {set i CONST} {$i < $j} {incr i}
  9129. * for {set i CONST} {$i <= CONST} {incr i}
  9130. * for {set i CONST} {$i <= $j} {incr i}
  9131. * XXX: NOTE: if variable traces are implemented, this optimization
  9132. * need to be modified to check for the proc epoch at every variable
  9133. * update. */
  9134. #ifdef JIM_OPTIMIZATION
  9135. {
  9136. ScriptObj *initScript, *incrScript;
  9137. ExprByteCode *expr;
  9138. jim_wide start, stop=0, currentVal;
  9139. unsigned jim_wide procEpoch = interp->procEpoch;
  9140. Jim_Obj *varNamePtr, *stopVarNamePtr = NULL, *objPtr;
  9141. int cmpType;
  9142. struct Jim_Cmd *cmdPtr;
  9143. /* Do it only if there aren't shared arguments */
  9144. if (argv[1] == argv[2] || argv[2] == argv[3] || argv[1] == argv[3])
  9145. goto evalstart;
  9146. initScript = Jim_GetScript(interp, argv[1]);
  9147. expr = Jim_GetExpression(interp, argv[2]);
  9148. incrScript = Jim_GetScript(interp, argv[3]);
  9149. /* Ensure proper lengths to start */
  9150. if (initScript->len != 6) goto evalstart;
  9151. if (incrScript->len != 4) goto evalstart;
  9152. if (expr->len != 3) goto evalstart;
  9153. /* Ensure proper token types. */
  9154. if (initScript->token[2].type != JIM_TT_ESC ||
  9155. initScript->token[4].type != JIM_TT_ESC ||
  9156. incrScript->token[2].type != JIM_TT_ESC ||
  9157. expr->opcode[0] != JIM_EXPROP_VARIABLE ||
  9158. (expr->opcode[1] != JIM_EXPROP_NUMBER &&
  9159. expr->opcode[1] != JIM_EXPROP_VARIABLE) ||
  9160. (expr->opcode[2] != JIM_EXPROP_LT &&
  9161. expr->opcode[2] != JIM_EXPROP_LTE))
  9162. goto evalstart;
  9163. cmpType = expr->opcode[2];
  9164. /* Initialization command must be [set] */
  9165. cmdPtr = Jim_GetCommand(interp, initScript->token[0].objPtr, JIM_NONE);
  9166. if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_SetCoreCommand)
  9167. goto evalstart;
  9168. /* Update command must be incr */
  9169. cmdPtr = Jim_GetCommand(interp, incrScript->token[0].objPtr, JIM_NONE);
  9170. if (cmdPtr == NULL || cmdPtr->cmdProc != Jim_IncrCoreCommand)
  9171. goto evalstart;
  9172. /* set, incr, expression must be about the same variable */
  9173. if (!Jim_StringEqObj(initScript->token[2].objPtr,
  9174. incrScript->token[2].objPtr, 0))
  9175. goto evalstart;
  9176. if (!Jim_StringEqObj(initScript->token[2].objPtr,
  9177. expr->obj[0], 0))
  9178. goto evalstart;
  9179. /* Check that the initialization and comparison are valid integers */
  9180. if (Jim_GetWide(interp, initScript->token[4].objPtr, &start) == JIM_ERR)
  9181. goto evalstart;
  9182. if (expr->opcode[1] == JIM_EXPROP_NUMBER &&
  9183. Jim_GetWide(interp, expr->obj[1], &stop) == JIM_ERR)
  9184. {
  9185. goto evalstart;
  9186. }
  9187. /* Initialization */
  9188. varNamePtr = expr->obj[0];
  9189. if (expr->opcode[1] == JIM_EXPROP_VARIABLE) {
  9190. stopVarNamePtr = expr->obj[1];
  9191. Jim_IncrRefCount(stopVarNamePtr);
  9192. }
  9193. Jim_IncrRefCount(varNamePtr);
  9194. /* --- OPTIMIZED FOR --- */
  9195. /* Start to loop */
  9196. objPtr = Jim_NewIntObj(interp, start);
  9197. if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
  9198. Jim_DecrRefCount(interp, varNamePtr);
  9199. if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
  9200. Jim_FreeNewObj(interp, objPtr);
  9201. goto evalstart;
  9202. }
  9203. while (1) {
  9204. /* === Check condition === */
  9205. /* Common code: */
  9206. objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
  9207. if (objPtr == NULL ||
  9208. Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK)
  9209. {
  9210. Jim_DecrRefCount(interp, varNamePtr);
  9211. if (stopVarNamePtr) Jim_DecrRefCount(interp, stopVarNamePtr);
  9212. goto testcond;
  9213. }
  9214. /* Immediate or Variable? get the 'stop' value if the latter. */
  9215. if (stopVarNamePtr) {
  9216. objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
  9217. if (objPtr == NULL ||
  9218. Jim_GetWide(interp, objPtr, &stop) != JIM_OK)
  9219. {
  9220. Jim_DecrRefCount(interp, varNamePtr);
  9221. Jim_DecrRefCount(interp, stopVarNamePtr);
  9222. goto testcond;
  9223. }
  9224. }
  9225. if (cmpType == JIM_EXPROP_LT) {
  9226. if (currentVal >= stop) break;
  9227. } else {
  9228. if (currentVal > stop) break;
  9229. }
  9230. /* Eval body */
  9231. if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
  9232. switch (retval) {
  9233. case JIM_BREAK:
  9234. if (stopVarNamePtr)
  9235. Jim_DecrRefCount(interp, stopVarNamePtr);
  9236. Jim_DecrRefCount(interp, varNamePtr);
  9237. goto out;
  9238. case JIM_CONTINUE:
  9239. /* nothing to do */
  9240. break;
  9241. default:
  9242. if (stopVarNamePtr)
  9243. Jim_DecrRefCount(interp, stopVarNamePtr);
  9244. Jim_DecrRefCount(interp, varNamePtr);
  9245. return retval;
  9246. }
  9247. }
  9248. /* If there was a change in procedures/command continue
  9249. * with the usual [for] command implementation */
  9250. if (procEpoch != interp->procEpoch) {
  9251. if (stopVarNamePtr)
  9252. Jim_DecrRefCount(interp, stopVarNamePtr);
  9253. Jim_DecrRefCount(interp, varNamePtr);
  9254. goto evalnext;
  9255. }
  9256. /* Increment */
  9257. objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
  9258. if (objPtr->refCount == 1 && objPtr->typePtr == &intObjType) {
  9259. objPtr->internalRep.wideValue ++;
  9260. Jim_InvalidateStringRep(objPtr);
  9261. } else {
  9262. Jim_Obj *auxObjPtr;
  9263. if (Jim_GetWide(interp, objPtr, &currentVal) == JIM_ERR) {
  9264. if (stopVarNamePtr)
  9265. Jim_DecrRefCount(interp, stopVarNamePtr);
  9266. Jim_DecrRefCount(interp, varNamePtr);
  9267. goto evalnext;
  9268. }
  9269. auxObjPtr = Jim_NewIntObj(interp, currentVal + 1);
  9270. if (Jim_SetVariable(interp, varNamePtr, auxObjPtr) == JIM_ERR) {
  9271. if (stopVarNamePtr)
  9272. Jim_DecrRefCount(interp, stopVarNamePtr);
  9273. Jim_DecrRefCount(interp, varNamePtr);
  9274. Jim_FreeNewObj(interp, auxObjPtr);
  9275. goto evalnext;
  9276. }
  9277. }
  9278. }
  9279. if (stopVarNamePtr)
  9280. Jim_DecrRefCount(interp, stopVarNamePtr);
  9281. Jim_DecrRefCount(interp, varNamePtr);
  9282. Jim_SetEmptyResult(interp);
  9283. return JIM_OK;
  9284. }
  9285. #endif
  9286. evalstart:
  9287. /* Eval start */
  9288. if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
  9289. return retval;
  9290. while (1) {
  9291. int boolean;
  9292. testcond:
  9293. /* Test the condition */
  9294. if ((retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean))
  9295. != JIM_OK)
  9296. return retval;
  9297. if (!boolean) break;
  9298. /* Eval body */
  9299. if ((retval = Jim_EvalObj(interp, argv[4])) != JIM_OK) {
  9300. switch (retval) {
  9301. case JIM_BREAK:
  9302. goto out;
  9303. break;
  9304. case JIM_CONTINUE:
  9305. /* Nothing to do */
  9306. break;
  9307. default:
  9308. return retval;
  9309. }
  9310. }
  9311. evalnext:
  9312. /* Eval next */
  9313. if ((retval = Jim_EvalObj(interp, argv[3])) != JIM_OK) {
  9314. switch (retval) {
  9315. case JIM_BREAK:
  9316. goto out;
  9317. break;
  9318. case JIM_CONTINUE:
  9319. continue;
  9320. break;
  9321. default:
  9322. return retval;
  9323. }
  9324. }
  9325. }
  9326. out:
  9327. Jim_SetEmptyResult(interp);
  9328. return JIM_OK;
  9329. }
  9330. /* foreach + lmap implementation. */
  9331. static int JimForeachMapHelper(Jim_Interp *interp, int argc,
  9332. Jim_Obj *const *argv, int doMap)
  9333. {
  9334. int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd;
  9335. int nbrOfLoops = 0;
  9336. Jim_Obj *emptyStr, *script, *mapRes = NULL;
  9337. if (argc < 4 || argc % 2 != 0) {
  9338. Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
  9339. return JIM_ERR;
  9340. }
  9341. if (doMap) {
  9342. mapRes = Jim_NewListObj(interp, NULL, 0);
  9343. Jim_IncrRefCount(mapRes);
  9344. }
  9345. emptyStr = Jim_NewEmptyStringObj(interp);
  9346. Jim_IncrRefCount(emptyStr);
  9347. script = argv[argc-1]; /* Last argument is a script */
  9348. nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */
  9349. listsIdx = (int*)Jim_Alloc(nbrOfLists * sizeof(int));
  9350. listsEnd = (int*)Jim_Alloc(nbrOfLists*2 * sizeof(int));
  9351. /* Initialize iterators and remember max nbr elements each list */
  9352. memset(listsIdx, 0, nbrOfLists * sizeof(int));
  9353. /* Remember lengths of all lists and calculate how much rounds to loop */
  9354. for (i = 0; i < nbrOfLists*2; i += 2) {
  9355. div_t cnt;
  9356. int count;
  9357. Jim_ListLength(interp, argv[i + 1], &listsEnd[i]);
  9358. Jim_ListLength(interp, argv[i + 2], &listsEnd[i + 1]);
  9359. if (listsEnd[i] == 0) {
  9360. Jim_SetResultString(interp, "foreach varlist is empty", -1);
  9361. goto err;
  9362. }
  9363. cnt = div(listsEnd[i + 1], listsEnd[i]);
  9364. count = cnt.quot + (cnt.rem ? 1 : 0);
  9365. if (count > nbrOfLoops)
  9366. nbrOfLoops = count;
  9367. }
  9368. for (; nbrOfLoops-- > 0;) {
  9369. for (i = 0; i < nbrOfLists; ++i) {
  9370. int varIdx = 0, var = i * 2;
  9371. while (varIdx < listsEnd[var]) {
  9372. Jim_Obj *varName, *ele;
  9373. int lst = i * 2 + 1;
  9374. if (Jim_ListIndex(interp, argv[var + 1], varIdx, &varName, JIM_ERRMSG)
  9375. != JIM_OK)
  9376. goto err;
  9377. if (listsIdx[i] < listsEnd[lst]) {
  9378. if (Jim_ListIndex(interp, argv[lst + 1], listsIdx[i], &ele, JIM_ERRMSG)
  9379. != JIM_OK)
  9380. goto err;
  9381. if (Jim_SetVariable(interp, varName, ele) != JIM_OK) {
  9382. Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
  9383. goto err;
  9384. }
  9385. ++listsIdx[i]; /* Remember next iterator of current list */
  9386. } else if (Jim_SetVariable(interp, varName, emptyStr) != JIM_OK) {
  9387. Jim_SetResultString(interp, "couldn't set loop variable: ", -1);
  9388. goto err;
  9389. }
  9390. ++varIdx; /* Next variable */
  9391. }
  9392. }
  9393. switch (result = Jim_EvalObj(interp, script)) {
  9394. case JIM_OK:
  9395. if (doMap)
  9396. Jim_ListAppendElement(interp, mapRes, interp->result);
  9397. break;
  9398. case JIM_CONTINUE:
  9399. break;
  9400. case JIM_BREAK:
  9401. goto out;
  9402. break;
  9403. default:
  9404. goto err;
  9405. }
  9406. }
  9407. out:
  9408. result = JIM_OK;
  9409. if (doMap)
  9410. Jim_SetResult(interp, mapRes);
  9411. else
  9412. Jim_SetEmptyResult(interp);
  9413. err:
  9414. if (doMap)
  9415. Jim_DecrRefCount(interp, mapRes);
  9416. Jim_DecrRefCount(interp, emptyStr);
  9417. Jim_Free(listsIdx);
  9418. Jim_Free(listsEnd);
  9419. return result;
  9420. }
  9421. /* [foreach] */
  9422. static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc,
  9423. Jim_Obj *const *argv)
  9424. {
  9425. return JimForeachMapHelper(interp, argc, argv, 0);
  9426. }
  9427. /* [lmap] */
  9428. static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc,
  9429. Jim_Obj *const *argv)
  9430. {
  9431. return JimForeachMapHelper(interp, argc, argv, 1);
  9432. }
  9433. /* [if] */
  9434. static int Jim_IfCoreCommand(Jim_Interp *interp, int argc,
  9435. Jim_Obj *const *argv)
  9436. {
  9437. int boolean, retval, current = 1, falsebody = 0;
  9438. if (argc >= 3) {
  9439. while (1) {
  9440. /* Far not enough arguments given! */
  9441. if (current >= argc) goto err;
  9442. if ((retval = Jim_GetBoolFromExpr(interp,
  9443. argv[current++], &boolean))
  9444. != JIM_OK)
  9445. return retval;
  9446. /* There lacks something, isn't it? */
  9447. if (current >= argc) goto err;
  9448. if (Jim_CompareStringImmediate(interp, argv[current],
  9449. "then")) current++;
  9450. /* Tsk tsk, no then-clause? */
  9451. if (current >= argc) goto err;
  9452. if (boolean)
  9453. return Jim_EvalObj(interp, argv[current]);
  9454. /* Ok: no else-clause follows */
  9455. if (++current >= argc) {
  9456. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  9457. return JIM_OK;
  9458. }
  9459. falsebody = current++;
  9460. if (Jim_CompareStringImmediate(interp, argv[falsebody],
  9461. "else")) {
  9462. /* IIICKS - else-clause isn't last cmd? */
  9463. if (current != argc-1) goto err;
  9464. return Jim_EvalObj(interp, argv[current]);
  9465. } else if (Jim_CompareStringImmediate(interp,
  9466. argv[falsebody], "elseif"))
  9467. /* Ok: elseif follows meaning all the stuff
  9468. * again (how boring...) */
  9469. continue;
  9470. /* OOPS - else-clause is not last cmd?*/
  9471. else if (falsebody != argc-1)
  9472. goto err;
  9473. return Jim_EvalObj(interp, argv[falsebody]);
  9474. }
  9475. return JIM_OK;
  9476. }
  9477. err:
  9478. Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
  9479. return JIM_ERR;
  9480. }
  9481. enum {SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD, SWITCH_UNKNOWN};
  9482. /* [switch] */
  9483. static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc,
  9484. Jim_Obj *const *argv)
  9485. {
  9486. int retcode = JIM_ERR, matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
  9487. Jim_Obj *command = 0, *const *caseList = 0, *strObj;
  9488. Jim_Obj *script = 0;
  9489. if (argc < 3) goto wrongnumargs;
  9490. for (opt = 1; opt < argc; ++opt) {
  9491. const char *option = Jim_GetString(argv[opt], 0);
  9492. if (*option != '-') break;
  9493. else if (strncmp(option, "--", 2) == 0) { ++opt; break; }
  9494. else if (strncmp(option, "-exact", 2) == 0) matchOpt = SWITCH_EXACT;
  9495. else if (strncmp(option, "-glob", 2) == 0) matchOpt = SWITCH_GLOB;
  9496. else if (strncmp(option, "-regexp", 2) == 0) matchOpt = SWITCH_RE;
  9497. else if (strncmp(option, "-command", 2) == 0) { matchOpt = SWITCH_CMD;
  9498. if ((argc - opt) < 2) goto wrongnumargs;
  9499. command = argv[++opt];
  9500. } else {
  9501. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  9502. Jim_AppendStrings(interp, Jim_GetResult(interp),
  9503. "bad option \"", option, "\": must be -exact, -glob, "
  9504. "-regexp, -command procname or --", 0);
  9505. goto err;
  9506. }
  9507. if ((argc - opt) < 2) goto wrongnumargs;
  9508. }
  9509. strObj = argv[opt++];
  9510. patCount = argc - opt;
  9511. if (patCount == 1) {
  9512. Jim_Obj **vector;
  9513. JimListGetElements(interp, argv[opt], &patCount, &vector);
  9514. caseList = vector;
  9515. } else
  9516. caseList = &argv[opt];
  9517. if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs;
  9518. for (i = 0; script == 0 && i < patCount; i += 2) {
  9519. Jim_Obj *patObj = caseList[i];
  9520. if (!Jim_CompareStringImmediate(interp, patObj, "default")
  9521. || i < (patCount-2)) {
  9522. switch (matchOpt) {
  9523. case SWITCH_EXACT:
  9524. if (Jim_StringEqObj(strObj, patObj, 0))
  9525. script = caseList[i + 1];
  9526. break;
  9527. case SWITCH_GLOB:
  9528. if (Jim_StringMatchObj(patObj, strObj, 0))
  9529. script = caseList[i + 1];
  9530. break;
  9531. case SWITCH_RE:
  9532. command = Jim_NewStringObj(interp, "regexp", -1);
  9533. /* Fall thru intentionally */
  9534. case SWITCH_CMD: {
  9535. Jim_Obj *parms[] = {command, patObj, strObj};
  9536. int rc = Jim_EvalObjVector(interp, 3, parms);
  9537. long matching;
  9538. /* After the execution of a command we need to
  9539. * make sure to reconvert the object into a list
  9540. * again. Only for the single-list style [switch]. */
  9541. if (argc-opt == 1) {
  9542. Jim_Obj **vector;
  9543. JimListGetElements(interp, argv[opt], &patCount,
  9544. &vector);
  9545. caseList = vector;
  9546. }
  9547. /* command is here already decref'd */
  9548. if (rc != JIM_OK) {
  9549. retcode = rc;
  9550. goto err;
  9551. }
  9552. rc = Jim_GetLong(interp, Jim_GetResult(interp), &matching);
  9553. if (rc != JIM_OK) {
  9554. retcode = rc;
  9555. goto err;
  9556. }
  9557. if (matching)
  9558. script = caseList[i + 1];
  9559. break;
  9560. }
  9561. default:
  9562. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  9563. Jim_AppendStrings(interp, Jim_GetResult(interp),
  9564. "internal error: no such option implemented", 0);
  9565. goto err;
  9566. }
  9567. } else {
  9568. script = caseList[i + 1];
  9569. }
  9570. }
  9571. for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-");
  9572. i += 2)
  9573. script = caseList[i + 1];
  9574. if (script && Jim_CompareStringImmediate(interp, script, "-")) {
  9575. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  9576. Jim_AppendStrings(interp, Jim_GetResult(interp),
  9577. "no body specified for pattern \"",
  9578. Jim_GetString(caseList[i-2], 0), "\"", 0);
  9579. goto err;
  9580. }
  9581. retcode = JIM_OK;
  9582. Jim_SetEmptyResult(interp);
  9583. if (script != 0)
  9584. retcode = Jim_EvalObj(interp, script);
  9585. return retcode;
  9586. wrongnumargs:
  9587. Jim_WrongNumArgs(interp, 1, argv, "?options? string "
  9588. "pattern body ... ?default body? or "
  9589. "{pattern body ?pattern body ...?}");
  9590. err:
  9591. return retcode;
  9592. }
  9593. /* [list] */
  9594. static int Jim_ListCoreCommand(Jim_Interp *interp, int argc,
  9595. Jim_Obj *const *argv)
  9596. {
  9597. Jim_Obj *listObjPtr;
  9598. listObjPtr = Jim_NewListObj(interp, argv + 1, argc-1);
  9599. Jim_SetResult(interp, listObjPtr);
  9600. return JIM_OK;
  9601. }
  9602. /* [lindex] */
  9603. static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc,
  9604. Jim_Obj *const *argv)
  9605. {
  9606. Jim_Obj *objPtr, *listObjPtr;
  9607. int i;
  9608. int index;
  9609. if (argc < 3) {
  9610. Jim_WrongNumArgs(interp, 1, argv, "list index ?...?");
  9611. return JIM_ERR;
  9612. }
  9613. objPtr = argv[1];
  9614. Jim_IncrRefCount(objPtr);
  9615. for (i = 2; i < argc; i++) {
  9616. listObjPtr = objPtr;
  9617. if (Jim_GetIndex(interp, argv[i], &index) != JIM_OK) {
  9618. Jim_DecrRefCount(interp, listObjPtr);
  9619. return JIM_ERR;
  9620. }
  9621. if (Jim_ListIndex(interp, listObjPtr, index, &objPtr,
  9622. JIM_NONE) != JIM_OK) {
  9623. /* Returns an empty object if the index
  9624. * is out of range. */
  9625. Jim_DecrRefCount(interp, listObjPtr);
  9626. Jim_SetEmptyResult(interp);
  9627. return JIM_OK;
  9628. }
  9629. Jim_IncrRefCount(objPtr);
  9630. Jim_DecrRefCount(interp, listObjPtr);
  9631. }
  9632. Jim_SetResult(interp, objPtr);
  9633. Jim_DecrRefCount(interp, objPtr);
  9634. return JIM_OK;
  9635. }
  9636. /* [llength] */
  9637. static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc,
  9638. Jim_Obj *const *argv)
  9639. {
  9640. int len;
  9641. if (argc != 2) {
  9642. Jim_WrongNumArgs(interp, 1, argv, "list");
  9643. return JIM_ERR;
  9644. }
  9645. Jim_ListLength(interp, argv[1], &len);
  9646. Jim_SetResult(interp, Jim_NewIntObj(interp, len));
  9647. return JIM_OK;
  9648. }
  9649. /* [lappend] */
  9650. static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc,
  9651. Jim_Obj *const *argv)
  9652. {
  9653. Jim_Obj *listObjPtr;
  9654. int shared, i;
  9655. if (argc < 2) {
  9656. Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
  9657. return JIM_ERR;
  9658. }
  9659. listObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
  9660. if (!listObjPtr) {
  9661. /* Create the list if it does not exists */
  9662. listObjPtr = Jim_NewListObj(interp, NULL, 0);
  9663. if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
  9664. Jim_FreeNewObj(interp, listObjPtr);
  9665. return JIM_ERR;
  9666. }
  9667. }
  9668. shared = Jim_IsShared(listObjPtr);
  9669. if (shared)
  9670. listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
  9671. for (i = 2; i < argc; i++)
  9672. Jim_ListAppendElement(interp, listObjPtr, argv[i]);
  9673. if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
  9674. if (shared)
  9675. Jim_FreeNewObj(interp, listObjPtr);
  9676. return JIM_ERR;
  9677. }
  9678. Jim_SetResult(interp, listObjPtr);
  9679. return JIM_OK;
  9680. }
  9681. /* [linsert] */
  9682. static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc,
  9683. Jim_Obj *const *argv)
  9684. {
  9685. int index, len;
  9686. Jim_Obj *listPtr;
  9687. if (argc < 4) {
  9688. Jim_WrongNumArgs(interp, 1, argv, "list index element "
  9689. "?element ...?");
  9690. return JIM_ERR;
  9691. }
  9692. listPtr = argv[1];
  9693. if (Jim_IsShared(listPtr))
  9694. listPtr = Jim_DuplicateObj(interp, listPtr);
  9695. if (Jim_GetIndex(interp, argv[2], &index) != JIM_OK)
  9696. goto err;
  9697. Jim_ListLength(interp, listPtr, &len);
  9698. if (index >= len)
  9699. index = len;
  9700. else if (index < 0)
  9701. index = len + index + 1;
  9702. Jim_ListInsertElements(interp, listPtr, index, argc-3, &argv[3]);
  9703. Jim_SetResult(interp, listPtr);
  9704. return JIM_OK;
  9705. err:
  9706. if (listPtr != argv[1]) {
  9707. Jim_FreeNewObj(interp, listPtr);
  9708. }
  9709. return JIM_ERR;
  9710. }
  9711. /* [lset] */
  9712. static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc,
  9713. Jim_Obj *const *argv)
  9714. {
  9715. if (argc < 3) {
  9716. Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
  9717. return JIM_ERR;
  9718. } else if (argc == 3) {
  9719. if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
  9720. return JIM_ERR;
  9721. Jim_SetResult(interp, argv[2]);
  9722. return JIM_OK;
  9723. }
  9724. if (Jim_SetListIndex(interp, argv[1], argv + 2, argc-3, argv[argc-1])
  9725. == JIM_ERR) return JIM_ERR;
  9726. return JIM_OK;
  9727. }
  9728. /* [lsort] */
  9729. static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
  9730. {
  9731. const char *options[] = {
  9732. "-ascii", "-nocase", "-increasing", "-decreasing", NULL
  9733. };
  9734. enum {OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING};
  9735. Jim_Obj *resObj;
  9736. int i, lsortType = JIM_LSORT_ASCII; /* default sort type */
  9737. int decreasing = 0;
  9738. if (argc < 2) {
  9739. Jim_WrongNumArgs(interp, 1, argv, "?options? list");
  9740. return JIM_ERR;
  9741. }
  9742. for (i = 1; i < (argc-1); i++) {
  9743. int option;
  9744. if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG)
  9745. != JIM_OK)
  9746. return JIM_ERR;
  9747. switch (option) {
  9748. case OPT_ASCII: lsortType = JIM_LSORT_ASCII; break;
  9749. case OPT_NOCASE: lsortType = JIM_LSORT_NOCASE; break;
  9750. case OPT_INCREASING: decreasing = 0; break;
  9751. case OPT_DECREASING: decreasing = 1; break;
  9752. }
  9753. }
  9754. if (decreasing) {
  9755. switch (lsortType) {
  9756. case JIM_LSORT_ASCII: lsortType = JIM_LSORT_ASCII_DECR; break;
  9757. case JIM_LSORT_NOCASE: lsortType = JIM_LSORT_NOCASE_DECR; break;
  9758. }
  9759. }
  9760. resObj = Jim_DuplicateObj(interp, argv[argc-1]);
  9761. ListSortElements(interp, resObj, lsortType);
  9762. Jim_SetResult(interp, resObj);
  9763. return JIM_OK;
  9764. }
  9765. /* [append] */
  9766. static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc,
  9767. Jim_Obj *const *argv)
  9768. {
  9769. Jim_Obj *stringObjPtr;
  9770. int shared, i;
  9771. if (argc < 2) {
  9772. Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
  9773. return JIM_ERR;
  9774. }
  9775. if (argc == 2) {
  9776. stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
  9777. if (!stringObjPtr) return JIM_ERR;
  9778. } else {
  9779. stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_NONE);
  9780. if (!stringObjPtr) {
  9781. /* Create the string if it does not exists */
  9782. stringObjPtr = Jim_NewEmptyStringObj(interp);
  9783. if (Jim_SetVariable(interp, argv[1], stringObjPtr)
  9784. != JIM_OK) {
  9785. Jim_FreeNewObj(interp, stringObjPtr);
  9786. return JIM_ERR;
  9787. }
  9788. }
  9789. }
  9790. shared = Jim_IsShared(stringObjPtr);
  9791. if (shared)
  9792. stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
  9793. for (i = 2; i < argc; i++)
  9794. Jim_AppendObj(interp, stringObjPtr, argv[i]);
  9795. if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
  9796. if (shared)
  9797. Jim_FreeNewObj(interp, stringObjPtr);
  9798. return JIM_ERR;
  9799. }
  9800. Jim_SetResult(interp, stringObjPtr);
  9801. return JIM_OK;
  9802. }
  9803. /* [debug] */
  9804. static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc,
  9805. Jim_Obj *const *argv)
  9806. {
  9807. const char *options[] = {
  9808. "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
  9809. "exprbc",
  9810. NULL
  9811. };
  9812. enum {
  9813. OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN,
  9814. OPT_EXPRLEN, OPT_EXPRBC
  9815. };
  9816. int option;
  9817. if (argc < 2) {
  9818. Jim_WrongNumArgs(interp, 1, argv, "option ?...?");
  9819. return JIM_ERR;
  9820. }
  9821. if (Jim_GetEnum(interp, argv[1], options, &option, "option",
  9822. JIM_ERRMSG) != JIM_OK)
  9823. return JIM_ERR;
  9824. if (option == OPT_REFCOUNT) {
  9825. if (argc != 3) {
  9826. Jim_WrongNumArgs(interp, 2, argv, "object");
  9827. return JIM_ERR;
  9828. }
  9829. Jim_SetResult(interp, Jim_NewIntObj(interp, argv[2]->refCount));
  9830. return JIM_OK;
  9831. } else if (option == OPT_OBJCOUNT) {
  9832. int freeobj = 0, liveobj = 0;
  9833. char buf[256];
  9834. Jim_Obj *objPtr;
  9835. if (argc != 2) {
  9836. Jim_WrongNumArgs(interp, 2, argv, "");
  9837. return JIM_ERR;
  9838. }
  9839. /* Count the number of free objects. */
  9840. objPtr = interp->freeList;
  9841. while (objPtr) {
  9842. freeobj++;
  9843. objPtr = objPtr->nextObjPtr;
  9844. }
  9845. /* Count the number of live objects. */
  9846. objPtr = interp->liveList;
  9847. while (objPtr) {
  9848. liveobj++;
  9849. objPtr = objPtr->nextObjPtr;
  9850. }
  9851. /* Set the result string and return. */
  9852. sprintf(buf, "free %d used %d", freeobj, liveobj);
  9853. Jim_SetResultString(interp, buf, -1);
  9854. return JIM_OK;
  9855. } else if (option == OPT_OBJECTS) {
  9856. Jim_Obj *objPtr, *listObjPtr, *subListObjPtr;
  9857. /* Count the number of live objects. */
  9858. objPtr = interp->liveList;
  9859. listObjPtr = Jim_NewListObj(interp, NULL, 0);
  9860. while (objPtr) {
  9861. char buf[128];
  9862. const char *type = objPtr->typePtr ?
  9863. objPtr->typePtr->name : "";
  9864. subListObjPtr = Jim_NewListObj(interp, NULL, 0);
  9865. sprintf(buf, "%p", objPtr);
  9866. Jim_ListAppendElement(interp, subListObjPtr,
  9867. Jim_NewStringObj(interp, buf, -1));
  9868. Jim_ListAppendElement(interp, subListObjPtr,
  9869. Jim_NewStringObj(interp, type, -1));
  9870. Jim_ListAppendElement(interp, subListObjPtr,
  9871. Jim_NewIntObj(interp, objPtr->refCount));
  9872. Jim_ListAppendElement(interp, subListObjPtr, objPtr);
  9873. Jim_ListAppendElement(interp, listObjPtr, subListObjPtr);
  9874. objPtr = objPtr->nextObjPtr;
  9875. }
  9876. Jim_SetResult(interp, listObjPtr);
  9877. return JIM_OK;
  9878. } else if (option == OPT_INVSTR) {
  9879. Jim_Obj *objPtr;
  9880. if (argc != 3) {
  9881. Jim_WrongNumArgs(interp, 2, argv, "object");
  9882. return JIM_ERR;
  9883. }
  9884. objPtr = argv[2];
  9885. if (objPtr->typePtr != NULL)
  9886. Jim_InvalidateStringRep(objPtr);
  9887. Jim_SetEmptyResult(interp);
  9888. return JIM_OK;
  9889. } else if (option == OPT_SCRIPTLEN) {
  9890. ScriptObj *script;
  9891. if (argc != 3) {
  9892. Jim_WrongNumArgs(interp, 2, argv, "script");
  9893. return JIM_ERR;
  9894. }
  9895. script = Jim_GetScript(interp, argv[2]);
  9896. Jim_SetResult(interp, Jim_NewIntObj(interp, script->len));
  9897. return JIM_OK;
  9898. } else if (option == OPT_EXPRLEN) {
  9899. ExprByteCode *expr;
  9900. if (argc != 3) {
  9901. Jim_WrongNumArgs(interp, 2, argv, "expression");
  9902. return JIM_ERR;
  9903. }
  9904. expr = Jim_GetExpression(interp, argv[2]);
  9905. if (expr == NULL)
  9906. return JIM_ERR;
  9907. Jim_SetResult(interp, Jim_NewIntObj(interp, expr->len));
  9908. return JIM_OK;
  9909. } else if (option == OPT_EXPRBC) {
  9910. Jim_Obj *objPtr;
  9911. ExprByteCode *expr;
  9912. int i;
  9913. if (argc != 3) {
  9914. Jim_WrongNumArgs(interp, 2, argv, "expression");
  9915. return JIM_ERR;
  9916. }
  9917. expr = Jim_GetExpression(interp, argv[2]);
  9918. if (expr == NULL)
  9919. return JIM_ERR;
  9920. objPtr = Jim_NewListObj(interp, NULL, 0);
  9921. for (i = 0; i < expr->len; i++) {
  9922. const char *type;
  9923. Jim_ExprOperator *op;
  9924. switch (expr->opcode[i]) {
  9925. case JIM_EXPROP_NUMBER: type = "number"; break;
  9926. case JIM_EXPROP_COMMAND: type = "command"; break;
  9927. case JIM_EXPROP_VARIABLE: type = "variable"; break;
  9928. case JIM_EXPROP_DICTSUGAR: type = "dictsugar"; break;
  9929. case JIM_EXPROP_SUBST: type = "subst"; break;
  9930. case JIM_EXPROP_STRING: type = "string"; break;
  9931. default:
  9932. op = JimExprOperatorInfo(Jim_GetString(expr->obj[i], NULL));
  9933. if (op == NULL) {
  9934. type = "private";
  9935. } else {
  9936. type = "operator";
  9937. }
  9938. break;
  9939. }
  9940. Jim_ListAppendElement(interp, objPtr,
  9941. Jim_NewStringObj(interp, type, -1));
  9942. Jim_ListAppendElement(interp, objPtr, expr->obj[i]);
  9943. }
  9944. Jim_SetResult(interp, objPtr);
  9945. return JIM_OK;
  9946. } else {
  9947. Jim_SetResultString(interp,
  9948. "bad option. Valid options are refcount, "
  9949. "objcount, objects, invstr", -1);
  9950. return JIM_ERR;
  9951. }
  9952. return JIM_OK; /* unreached */
  9953. }
  9954. /* [eval] */
  9955. static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc,
  9956. Jim_Obj *const *argv)
  9957. {
  9958. if (argc == 2) {
  9959. return Jim_EvalObj(interp, argv[1]);
  9960. } else if (argc > 2) {
  9961. Jim_Obj *objPtr;
  9962. int retcode;
  9963. objPtr = Jim_ConcatObj(interp, argc-1, argv + 1);
  9964. Jim_IncrRefCount(objPtr);
  9965. retcode = Jim_EvalObj(interp, objPtr);
  9966. Jim_DecrRefCount(interp, objPtr);
  9967. return retcode;
  9968. } else {
  9969. Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
  9970. return JIM_ERR;
  9971. }
  9972. }
  9973. /* [uplevel] */
  9974. static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc,
  9975. Jim_Obj *const *argv)
  9976. {
  9977. if (argc >= 2) {
  9978. int retcode, newLevel, oldLevel;
  9979. Jim_CallFrame *savedCallFrame, *targetCallFrame;
  9980. Jim_Obj *objPtr;
  9981. const char *str;
  9982. /* Save the old callframe pointer */
  9983. savedCallFrame = interp->framePtr;
  9984. /* Lookup the target frame pointer */
  9985. str = Jim_GetString(argv[1], NULL);
  9986. if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#')
  9987. {
  9988. if (Jim_GetCallFrameByLevel(interp, argv[1],
  9989. &targetCallFrame,
  9990. &newLevel) != JIM_OK)
  9991. return JIM_ERR;
  9992. argc--;
  9993. argv++;
  9994. } else {
  9995. if (Jim_GetCallFrameByLevel(interp, NULL,
  9996. &targetCallFrame,
  9997. &newLevel) != JIM_OK)
  9998. return JIM_ERR;
  9999. }
  10000. if (argc < 2) {
  10001. argc++;
  10002. argv--;
  10003. Jim_WrongNumArgs(interp, 1, argv,
  10004. "?level? command ?arg ...?");
  10005. return JIM_ERR;
  10006. }
  10007. /* Eval the code in the target callframe. */
  10008. interp->framePtr = targetCallFrame;
  10009. oldLevel = interp->numLevels;
  10010. interp->numLevels = newLevel;
  10011. if (argc == 2) {
  10012. retcode = Jim_EvalObj(interp, argv[1]);
  10013. } else {
  10014. objPtr = Jim_ConcatObj(interp, argc-1, argv + 1);
  10015. Jim_IncrRefCount(objPtr);
  10016. retcode = Jim_EvalObj(interp, objPtr);
  10017. Jim_DecrRefCount(interp, objPtr);
  10018. }
  10019. interp->numLevels = oldLevel;
  10020. interp->framePtr = savedCallFrame;
  10021. return retcode;
  10022. } else {
  10023. Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
  10024. return JIM_ERR;
  10025. }
  10026. }
  10027. /* [expr] */
  10028. static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc,
  10029. Jim_Obj *const *argv)
  10030. {
  10031. Jim_Obj *exprResultPtr;
  10032. int retcode;
  10033. if (argc == 2) {
  10034. retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
  10035. } else if (argc > 2) {
  10036. Jim_Obj *objPtr;
  10037. objPtr = Jim_ConcatObj(interp, argc-1, argv + 1);
  10038. Jim_IncrRefCount(objPtr);
  10039. retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
  10040. Jim_DecrRefCount(interp, objPtr);
  10041. } else {
  10042. Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
  10043. return JIM_ERR;
  10044. }
  10045. if (retcode != JIM_OK) return retcode;
  10046. Jim_SetResult(interp, exprResultPtr);
  10047. Jim_DecrRefCount(interp, exprResultPtr);
  10048. return JIM_OK;
  10049. }
  10050. /* [break] */
  10051. static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc,
  10052. Jim_Obj *const *argv)
  10053. {
  10054. if (argc != 1) {
  10055. Jim_WrongNumArgs(interp, 1, argv, "");
  10056. return JIM_ERR;
  10057. }
  10058. return JIM_BREAK;
  10059. }
  10060. /* [continue] */
  10061. static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc,
  10062. Jim_Obj *const *argv)
  10063. {
  10064. if (argc != 1) {
  10065. Jim_WrongNumArgs(interp, 1, argv, "");
  10066. return JIM_ERR;
  10067. }
  10068. return JIM_CONTINUE;
  10069. }
  10070. /* [return] */
  10071. static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc,
  10072. Jim_Obj *const *argv)
  10073. {
  10074. if (argc == 1) {
  10075. return JIM_RETURN;
  10076. } else if (argc == 2) {
  10077. Jim_SetResult(interp, argv[1]);
  10078. interp->returnCode = JIM_OK;
  10079. return JIM_RETURN;
  10080. } else if (argc == 3 || argc == 4) {
  10081. int returnCode;
  10082. if (Jim_GetReturnCode(interp, argv[2], &returnCode) == JIM_ERR)
  10083. return JIM_ERR;
  10084. interp->returnCode = returnCode;
  10085. if (argc == 4)
  10086. Jim_SetResult(interp, argv[3]);
  10087. return JIM_RETURN;
  10088. } else {
  10089. Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?result?");
  10090. return JIM_ERR;
  10091. }
  10092. return JIM_RETURN; /* unreached */
  10093. }
  10094. /* [tailcall] */
  10095. static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc,
  10096. Jim_Obj *const *argv)
  10097. {
  10098. Jim_Obj *objPtr;
  10099. objPtr = Jim_NewListObj(interp, argv + 1, argc-1);
  10100. Jim_SetResult(interp, objPtr);
  10101. return JIM_EVAL;
  10102. }
  10103. /* [proc] */
  10104. static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc,
  10105. Jim_Obj *const *argv)
  10106. {
  10107. int argListLen;
  10108. int arityMin, arityMax;
  10109. if (argc != 4 && argc != 5) {
  10110. Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
  10111. return JIM_ERR;
  10112. }
  10113. Jim_ListLength(interp, argv[2], &argListLen);
  10114. arityMin = arityMax = argListLen + 1;
  10115. if (argListLen) {
  10116. const char *str;
  10117. int len;
  10118. Jim_Obj *argPtr=NULL;
  10119. /* Check for 'args' and adjust arityMin and arityMax if necessary */
  10120. Jim_ListIndex(interp, argv[2], argListLen-1, &argPtr, JIM_NONE);
  10121. str = Jim_GetString(argPtr, &len);
  10122. if (len == 4 && memcmp(str, "args", 4) == 0) {
  10123. arityMin--;
  10124. arityMax = -1;
  10125. }
  10126. /* Check for default arguments and reduce arityMin if necessary */
  10127. while (arityMin > 1) {
  10128. int len;
  10129. Jim_ListIndex(interp, argv[2], arityMin - 2, &argPtr, JIM_NONE);
  10130. Jim_ListLength(interp, argPtr, &len);
  10131. if (len != 2) {
  10132. /* No default argument */
  10133. break;
  10134. }
  10135. arityMin--;
  10136. }
  10137. }
  10138. if (argc == 4) {
  10139. return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
  10140. argv[2], NULL, argv[3], arityMin, arityMax);
  10141. } else {
  10142. return Jim_CreateProcedure(interp, Jim_GetString(argv[1], NULL),
  10143. argv[2], argv[3], argv[4], arityMin, arityMax);
  10144. }
  10145. }
  10146. /* [concat] */
  10147. static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc,
  10148. Jim_Obj *const *argv)
  10149. {
  10150. Jim_SetResult(interp, Jim_ConcatObj(interp, argc-1, argv + 1));
  10151. return JIM_OK;
  10152. }
  10153. /* [upvar] */
  10154. static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc,
  10155. Jim_Obj *const *argv)
  10156. {
  10157. const char *str;
  10158. int i;
  10159. Jim_CallFrame *targetCallFrame;
  10160. /* Lookup the target frame pointer */
  10161. str = Jim_GetString(argv[1], NULL);
  10162. if (argc > 3 &&
  10163. ((str[0] >= '0' && str[0] <= '9') || str[0] == '#'))
  10164. {
  10165. if (Jim_GetCallFrameByLevel(interp, argv[1],
  10166. &targetCallFrame, NULL) != JIM_OK)
  10167. return JIM_ERR;
  10168. argc--;
  10169. argv++;
  10170. } else {
  10171. if (Jim_GetCallFrameByLevel(interp, NULL,
  10172. &targetCallFrame, NULL) != JIM_OK)
  10173. return JIM_ERR;
  10174. }
  10175. /* Check for arity */
  10176. if (argc < 3 || ((argc-1)%2) != 0) {
  10177. Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
  10178. return JIM_ERR;
  10179. }
  10180. /* Now... for every other/local couple: */
  10181. for (i = 1; i < argc; i += 2) {
  10182. if (Jim_SetVariableLink(interp, argv[i + 1], argv[i],
  10183. targetCallFrame) != JIM_OK) return JIM_ERR;
  10184. }
  10185. return JIM_OK;
  10186. }
  10187. /* [global] */
  10188. static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc,
  10189. Jim_Obj *const *argv)
  10190. {
  10191. int i;
  10192. if (argc < 2) {
  10193. Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
  10194. return JIM_ERR;
  10195. }
  10196. /* Link every var to the toplevel having the same name */
  10197. if (interp->numLevels == 0) return JIM_OK; /* global at toplevel... */
  10198. for (i = 1; i < argc; i++) {
  10199. if (Jim_SetVariableLink(interp, argv[i], argv[i],
  10200. interp->topFramePtr) != JIM_OK) return JIM_ERR;
  10201. }
  10202. return JIM_OK;
  10203. }
  10204. /* does the [string map] operation. On error NULL is returned,
  10205. * otherwise a new string object with the result, having refcount = 0,
  10206. * is returned. */
  10207. static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
  10208. Jim_Obj *objPtr, int nocase)
  10209. {
  10210. int numMaps;
  10211. const char **key, *str, *noMatchStart = NULL;
  10212. Jim_Obj **value;
  10213. int *keyLen, strLen, i;
  10214. Jim_Obj *resultObjPtr;
  10215. Jim_ListLength(interp, mapListObjPtr, &numMaps);
  10216. if (numMaps % 2) {
  10217. Jim_SetResultString(interp,
  10218. "list must contain an even number of elements", -1);
  10219. return NULL;
  10220. }
  10221. /* Initialization */
  10222. numMaps /= 2;
  10223. key = Jim_Alloc(sizeof(char*)*numMaps);
  10224. keyLen = Jim_Alloc(sizeof(int)*numMaps);
  10225. value = Jim_Alloc(sizeof(Jim_Obj*)*numMaps);
  10226. resultObjPtr = Jim_NewStringObj(interp, "", 0);
  10227. for (i = 0; i < numMaps; i++) {
  10228. Jim_Obj *eleObjPtr=NULL;
  10229. Jim_ListIndex(interp, mapListObjPtr, i*2, &eleObjPtr, JIM_NONE);
  10230. key[i] = Jim_GetString(eleObjPtr, &keyLen[i]);
  10231. Jim_ListIndex(interp, mapListObjPtr, i*2 + 1, &eleObjPtr, JIM_NONE);
  10232. value[i] = eleObjPtr;
  10233. }
  10234. str = Jim_GetString(objPtr, &strLen);
  10235. /* Map it */
  10236. while (strLen) {
  10237. for (i = 0; i < numMaps; i++) {
  10238. if (strLen >= keyLen[i] && keyLen[i]) {
  10239. if (!JimStringCompare(str, keyLen[i], key[i], keyLen[i],
  10240. nocase))
  10241. {
  10242. if (noMatchStart) {
  10243. Jim_AppendString(interp, resultObjPtr,
  10244. noMatchStart, str-noMatchStart);
  10245. noMatchStart = NULL;
  10246. }
  10247. Jim_AppendObj(interp, resultObjPtr, value[i]);
  10248. str += keyLen[i];
  10249. strLen -= keyLen[i];
  10250. break;
  10251. }
  10252. }
  10253. }
  10254. if (i == numMaps) { /* no match */
  10255. if (noMatchStart == NULL)
  10256. noMatchStart = str;
  10257. str ++;
  10258. strLen --;
  10259. }
  10260. }
  10261. if (noMatchStart) {
  10262. Jim_AppendString(interp, resultObjPtr,
  10263. noMatchStart, str-noMatchStart);
  10264. }
  10265. Jim_Free((void*)key);
  10266. Jim_Free(keyLen);
  10267. Jim_Free(value);
  10268. return resultObjPtr;
  10269. }
  10270. /* [string] */
  10271. static int Jim_StringCoreCommand(Jim_Interp *interp, int argc,
  10272. Jim_Obj *const *argv)
  10273. {
  10274. int option;
  10275. const char *options[] = {
  10276. "length", "compare", "match", "equal", "range", "map", "repeat",
  10277. "index", "first", "tolower", "toupper", NULL
  10278. };
  10279. enum {
  10280. OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_RANGE,
  10281. OPT_MAP, OPT_REPEAT, OPT_INDEX, OPT_FIRST, OPT_TOLOWER, OPT_TOUPPER
  10282. };
  10283. if (argc < 2) {
  10284. Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
  10285. return JIM_ERR;
  10286. }
  10287. if (Jim_GetEnum(interp, argv[1], options, &option, "option",
  10288. JIM_ERRMSG) != JIM_OK)
  10289. return JIM_ERR;
  10290. if (option == OPT_LENGTH) {
  10291. int len;
  10292. if (argc != 3) {
  10293. Jim_WrongNumArgs(interp, 2, argv, "string");
  10294. return JIM_ERR;
  10295. }
  10296. Jim_GetString(argv[2], &len);
  10297. Jim_SetResult(interp, Jim_NewIntObj(interp, len));
  10298. return JIM_OK;
  10299. } else if (option == OPT_COMPARE) {
  10300. int nocase = 0;
  10301. if ((argc != 4 && argc != 5) ||
  10302. (argc == 5 && Jim_CompareStringImmediate(interp,
  10303. argv[2], "-nocase") == 0)) {
  10304. Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
  10305. return JIM_ERR;
  10306. }
  10307. if (argc == 5) {
  10308. nocase = 1;
  10309. argv++;
  10310. }
  10311. Jim_SetResult(interp, Jim_NewIntObj(interp,
  10312. Jim_StringCompareObj(argv[2],
  10313. argv[3], nocase)));
  10314. return JIM_OK;
  10315. } else if (option == OPT_MATCH) {
  10316. int nocase = 0;
  10317. if ((argc != 4 && argc != 5) ||
  10318. (argc == 5 && Jim_CompareStringImmediate(interp,
  10319. argv[2], "-nocase") == 0)) {
  10320. Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern "
  10321. "string");
  10322. return JIM_ERR;
  10323. }
  10324. if (argc == 5) {
  10325. nocase = 1;
  10326. argv++;
  10327. }
  10328. Jim_SetResult(interp,
  10329. Jim_NewIntObj(interp, Jim_StringMatchObj(argv[2],
  10330. argv[3], nocase)));
  10331. return JIM_OK;
  10332. } else if (option == OPT_EQUAL) {
  10333. if (argc != 4) {
  10334. Jim_WrongNumArgs(interp, 2, argv, "string1 string2");
  10335. return JIM_ERR;
  10336. }
  10337. Jim_SetResult(interp,
  10338. Jim_NewIntObj(interp, Jim_StringEqObj(argv[2],
  10339. argv[3], 0)));
  10340. return JIM_OK;
  10341. } else if (option == OPT_RANGE) {
  10342. Jim_Obj *objPtr;
  10343. if (argc != 5) {
  10344. Jim_WrongNumArgs(interp, 2, argv, "string first last");
  10345. return JIM_ERR;
  10346. }
  10347. objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
  10348. if (objPtr == NULL)
  10349. return JIM_ERR;
  10350. Jim_SetResult(interp, objPtr);
  10351. return JIM_OK;
  10352. } else if (option == OPT_MAP) {
  10353. int nocase = 0;
  10354. Jim_Obj *objPtr;
  10355. if ((argc != 4 && argc != 5) ||
  10356. (argc == 5 && Jim_CompareStringImmediate(interp,
  10357. argv[2], "-nocase") == 0)) {
  10358. Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList "
  10359. "string");
  10360. return JIM_ERR;
  10361. }
  10362. if (argc == 5) {
  10363. nocase = 1;
  10364. argv++;
  10365. }
  10366. objPtr = JimStringMap(interp, argv[2], argv[3], nocase);
  10367. if (objPtr == NULL)
  10368. return JIM_ERR;
  10369. Jim_SetResult(interp, objPtr);
  10370. return JIM_OK;
  10371. } else if (option == OPT_REPEAT) {
  10372. Jim_Obj *objPtr;
  10373. jim_wide count;
  10374. if (argc != 4) {
  10375. Jim_WrongNumArgs(interp, 2, argv, "string count");
  10376. return JIM_ERR;
  10377. }
  10378. if (Jim_GetWide(interp, argv[3], &count) != JIM_OK)
  10379. return JIM_ERR;
  10380. objPtr = Jim_NewStringObj(interp, "", 0);
  10381. while (count--) {
  10382. Jim_AppendObj(interp, objPtr, argv[2]);
  10383. }
  10384. Jim_SetResult(interp, objPtr);
  10385. return JIM_OK;
  10386. } else if (option == OPT_INDEX) {
  10387. int index, len;
  10388. const char *str;
  10389. if (argc != 4) {
  10390. Jim_WrongNumArgs(interp, 2, argv, "string index");
  10391. return JIM_ERR;
  10392. }
  10393. if (Jim_GetIndex(interp, argv[3], &index) != JIM_OK)
  10394. return JIM_ERR;
  10395. str = Jim_GetString(argv[2], &len);
  10396. if (index != INT_MIN && index != INT_MAX)
  10397. index = JimRelToAbsIndex(len, index);
  10398. if (index < 0 || index >= len) {
  10399. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  10400. return JIM_OK;
  10401. } else {
  10402. Jim_SetResult(interp, Jim_NewStringObj(interp, str + index, 1));
  10403. return JIM_OK;
  10404. }
  10405. } else if (option == OPT_FIRST) {
  10406. int index = 0, l1, l2;
  10407. const char *s1, *s2;
  10408. if (argc != 4 && argc != 5) {
  10409. Jim_WrongNumArgs(interp, 2, argv, "subString string ?startIndex?");
  10410. return JIM_ERR;
  10411. }
  10412. s1 = Jim_GetString(argv[2], &l1);
  10413. s2 = Jim_GetString(argv[3], &l2);
  10414. if (argc == 5) {
  10415. if (Jim_GetIndex(interp, argv[4], &index) != JIM_OK)
  10416. return JIM_ERR;
  10417. index = JimRelToAbsIndex(l2, index);
  10418. }
  10419. Jim_SetResult(interp, Jim_NewIntObj(interp,
  10420. JimStringFirst(s1, l1, s2, l2, index)));
  10421. return JIM_OK;
  10422. } else if (option == OPT_TOLOWER) {
  10423. if (argc != 3) {
  10424. Jim_WrongNumArgs(interp, 2, argv, "string");
  10425. return JIM_ERR;
  10426. }
  10427. Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
  10428. } else if (option == OPT_TOUPPER) {
  10429. if (argc != 3) {
  10430. Jim_WrongNumArgs(interp, 2, argv, "string");
  10431. return JIM_ERR;
  10432. }
  10433. Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
  10434. }
  10435. return JIM_OK;
  10436. }
  10437. /* [time] */
  10438. static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc,
  10439. Jim_Obj *const *argv)
  10440. {
  10441. long i, count = 1;
  10442. jim_wide start, elapsed;
  10443. char buf [256];
  10444. const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
  10445. if (argc < 2) {
  10446. Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
  10447. return JIM_ERR;
  10448. }
  10449. if (argc == 3) {
  10450. if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
  10451. return JIM_ERR;
  10452. }
  10453. if (count < 0)
  10454. return JIM_OK;
  10455. i = count;
  10456. start = JimClock();
  10457. while (i-- > 0) {
  10458. int retval;
  10459. if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK)
  10460. return retval;
  10461. }
  10462. elapsed = JimClock() - start;
  10463. sprintf(buf, fmt, elapsed/count);
  10464. Jim_SetResultString(interp, buf, -1);
  10465. return JIM_OK;
  10466. }
  10467. /* [exit] */
  10468. static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc,
  10469. Jim_Obj *const *argv)
  10470. {
  10471. long exitCode = 0;
  10472. if (argc > 2) {
  10473. Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
  10474. return JIM_ERR;
  10475. }
  10476. if (argc == 2) {
  10477. if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
  10478. return JIM_ERR;
  10479. }
  10480. interp->exitCode = exitCode;
  10481. return JIM_EXIT;
  10482. }
  10483. /* [catch] */
  10484. static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
  10485. Jim_Obj *const *argv)
  10486. {
  10487. int exitCode = 0;
  10488. if (argc != 2 && argc != 3) {
  10489. Jim_WrongNumArgs(interp, 1, argv, "script ?varName?");
  10490. return JIM_ERR;
  10491. }
  10492. exitCode = Jim_EvalObj(interp, argv[1]);
  10493. if (argc == 3) {
  10494. if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
  10495. != JIM_OK)
  10496. return JIM_ERR;
  10497. }
  10498. Jim_SetResult(interp, Jim_NewIntObj(interp, exitCode));
  10499. return JIM_OK;
  10500. }
  10501. /* [ref] */
  10502. static int Jim_RefCoreCommand(Jim_Interp *interp, int argc,
  10503. Jim_Obj *const *argv)
  10504. {
  10505. if (argc != 3 && argc != 4) {
  10506. Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
  10507. return JIM_ERR;
  10508. }
  10509. if (argc == 3) {
  10510. Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
  10511. } else {
  10512. Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2],
  10513. argv[3]));
  10514. }
  10515. return JIM_OK;
  10516. }
  10517. /* [getref] */
  10518. static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc,
  10519. Jim_Obj *const *argv)
  10520. {
  10521. Jim_Reference *refPtr;
  10522. if (argc != 2) {
  10523. Jim_WrongNumArgs(interp, 1, argv, "reference");
  10524. return JIM_ERR;
  10525. }
  10526. if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
  10527. return JIM_ERR;
  10528. Jim_SetResult(interp, refPtr->objPtr);
  10529. return JIM_OK;
  10530. }
  10531. /* [setref] */
  10532. static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc,
  10533. Jim_Obj *const *argv)
  10534. {
  10535. Jim_Reference *refPtr;
  10536. if (argc != 3) {
  10537. Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
  10538. return JIM_ERR;
  10539. }
  10540. if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
  10541. return JIM_ERR;
  10542. Jim_IncrRefCount(argv[2]);
  10543. Jim_DecrRefCount(interp, refPtr->objPtr);
  10544. refPtr->objPtr = argv[2];
  10545. Jim_SetResult(interp, argv[2]);
  10546. return JIM_OK;
  10547. }
  10548. /* [collect] */
  10549. static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc,
  10550. Jim_Obj *const *argv)
  10551. {
  10552. if (argc != 1) {
  10553. Jim_WrongNumArgs(interp, 1, argv, "");
  10554. return JIM_ERR;
  10555. }
  10556. Jim_SetResult(interp, Jim_NewIntObj(interp, Jim_Collect(interp)));
  10557. return JIM_OK;
  10558. }
  10559. /* [finalize] reference ?newValue? */
  10560. static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc,
  10561. Jim_Obj *const *argv)
  10562. {
  10563. if (argc != 2 && argc != 3) {
  10564. Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
  10565. return JIM_ERR;
  10566. }
  10567. if (argc == 2) {
  10568. Jim_Obj *cmdNamePtr;
  10569. if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
  10570. return JIM_ERR;
  10571. if (cmdNamePtr != NULL) /* otherwise the null string is returned. */
  10572. Jim_SetResult(interp, cmdNamePtr);
  10573. } else {
  10574. if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
  10575. return JIM_ERR;
  10576. Jim_SetResult(interp, argv[2]);
  10577. }
  10578. return JIM_OK;
  10579. }
  10580. /* TODO */
  10581. /* [info references] (list of all the references/finalizers) */
  10582. /* [rename] */
  10583. static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc,
  10584. Jim_Obj *const *argv)
  10585. {
  10586. const char *oldName, *newName;
  10587. if (argc != 3) {
  10588. Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
  10589. return JIM_ERR;
  10590. }
  10591. oldName = Jim_GetString(argv[1], NULL);
  10592. newName = Jim_GetString(argv[2], NULL);
  10593. if (Jim_RenameCommand(interp, oldName, newName) != JIM_OK) {
  10594. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  10595. Jim_AppendStrings(interp, Jim_GetResult(interp),
  10596. "can't rename \"", oldName, "\": ",
  10597. "command doesn't exist", NULL);
  10598. return JIM_ERR;
  10599. }
  10600. return JIM_OK;
  10601. }
  10602. /* [dict] */
  10603. static int Jim_DictCoreCommand(Jim_Interp *interp, int argc,
  10604. Jim_Obj *const *argv)
  10605. {
  10606. int option;
  10607. const char *options[] = {
  10608. "create", "get", "set", "unset", "exists", NULL
  10609. };
  10610. enum {
  10611. OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST
  10612. };
  10613. if (argc < 2) {
  10614. Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
  10615. return JIM_ERR;
  10616. }
  10617. if (Jim_GetEnum(interp, argv[1], options, &option, "option",
  10618. JIM_ERRMSG) != JIM_OK)
  10619. return JIM_ERR;
  10620. if (option == OPT_CREATE) {
  10621. Jim_Obj *objPtr;
  10622. if (argc % 2) {
  10623. Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
  10624. return JIM_ERR;
  10625. }
  10626. objPtr = Jim_NewDictObj(interp, argv + 2, argc-2);
  10627. Jim_SetResult(interp, objPtr);
  10628. return JIM_OK;
  10629. } else if (option == OPT_GET) {
  10630. Jim_Obj *objPtr;
  10631. if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc-3, &objPtr,
  10632. JIM_ERRMSG) != JIM_OK)
  10633. return JIM_ERR;
  10634. Jim_SetResult(interp, objPtr);
  10635. return JIM_OK;
  10636. } else if (option == OPT_SET) {
  10637. if (argc < 5) {
  10638. Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
  10639. return JIM_ERR;
  10640. }
  10641. return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc-4,
  10642. argv[argc-1]);
  10643. } else if (option == OPT_UNSET) {
  10644. if (argc < 4) {
  10645. Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
  10646. return JIM_ERR;
  10647. }
  10648. return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc-3,
  10649. NULL);
  10650. } else if (option == OPT_EXIST) {
  10651. Jim_Obj *objPtr;
  10652. int exists;
  10653. if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc-3, &objPtr,
  10654. JIM_ERRMSG) == JIM_OK)
  10655. exists = 1;
  10656. else
  10657. exists = 0;
  10658. Jim_SetResult(interp, Jim_NewIntObj(interp, exists));
  10659. return JIM_OK;
  10660. } else {
  10661. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  10662. Jim_AppendStrings(interp, Jim_GetResult(interp),
  10663. "bad option \"", Jim_GetString(argv[1], NULL), "\":",
  10664. " must be create, get, set", NULL);
  10665. return JIM_ERR;
  10666. }
  10667. return JIM_OK;
  10668. }
  10669. /* [load] */
  10670. static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc,
  10671. Jim_Obj *const *argv)
  10672. {
  10673. if (argc < 2) {
  10674. Jim_WrongNumArgs(interp, 1, argv, "libaryFile");
  10675. return JIM_ERR;
  10676. }
  10677. return Jim_LoadLibrary(interp, Jim_GetString(argv[1], NULL));
  10678. }
  10679. /* [subst] */
  10680. static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc,
  10681. Jim_Obj *const *argv)
  10682. {
  10683. int i, flags = 0;
  10684. Jim_Obj *objPtr;
  10685. if (argc < 2) {
  10686. Jim_WrongNumArgs(interp, 1, argv,
  10687. "?-nobackslashes? ?-nocommands? ?-novariables? string");
  10688. return JIM_ERR;
  10689. }
  10690. i = argc-2;
  10691. while (i--) {
  10692. if (Jim_CompareStringImmediate(interp, argv[i + 1],
  10693. "-nobackslashes"))
  10694. flags |= JIM_SUBST_NOESC;
  10695. else if (Jim_CompareStringImmediate(interp, argv[i + 1],
  10696. "-novariables"))
  10697. flags |= JIM_SUBST_NOVAR;
  10698. else if (Jim_CompareStringImmediate(interp, argv[i + 1],
  10699. "-nocommands"))
  10700. flags |= JIM_SUBST_NOCMD;
  10701. else {
  10702. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  10703. Jim_AppendStrings(interp, Jim_GetResult(interp),
  10704. "bad option \"", Jim_GetString(argv[i + 1], NULL),
  10705. "\": must be -nobackslashes, -nocommands, or "
  10706. "-novariables", NULL);
  10707. return JIM_ERR;
  10708. }
  10709. }
  10710. if (Jim_SubstObj(interp, argv[argc-1], &objPtr, flags) != JIM_OK)
  10711. return JIM_ERR;
  10712. Jim_SetResult(interp, objPtr);
  10713. return JIM_OK;
  10714. }
  10715. /* [info] */
  10716. static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
  10717. Jim_Obj *const *argv)
  10718. {
  10719. int cmd, result = JIM_OK;
  10720. static const char *commands[] = {
  10721. "body", "commands", "exists", "globals", "level", "locals",
  10722. "vars", "version", "complete", "args", "hostname", NULL
  10723. };
  10724. enum {INFO_BODY, INFO_COMMANDS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
  10725. INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_COMPLETE, INFO_ARGS, INFO_HOSTNAME};
  10726. if (argc < 2) {
  10727. Jim_WrongNumArgs(interp, 1, argv, "command ?args ...?");
  10728. return JIM_ERR;
  10729. }
  10730. if (Jim_GetEnum(interp, argv[1], commands, &cmd, "command", JIM_ERRMSG)
  10731. != JIM_OK) {
  10732. return JIM_ERR;
  10733. }
  10734. if (cmd == INFO_COMMANDS) {
  10735. if (argc != 2 && argc != 3) {
  10736. Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
  10737. return JIM_ERR;
  10738. }
  10739. if (argc == 3)
  10740. Jim_SetResult(interp,JimCommandsList(interp, argv[2]));
  10741. else
  10742. Jim_SetResult(interp, JimCommandsList(interp, NULL));
  10743. } else if (cmd == INFO_EXISTS) {
  10744. Jim_Obj *exists;
  10745. if (argc != 3) {
  10746. Jim_WrongNumArgs(interp, 2, argv, "varName");
  10747. return JIM_ERR;
  10748. }
  10749. exists = Jim_GetVariable(interp, argv[2], 0);
  10750. Jim_SetResult(interp, Jim_NewIntObj(interp, exists != 0));
  10751. } else if (cmd == INFO_GLOBALS || cmd == INFO_LOCALS || cmd == INFO_VARS) {
  10752. int mode;
  10753. switch (cmd) {
  10754. case INFO_GLOBALS: mode = JIM_VARLIST_GLOBALS; break;
  10755. case INFO_LOCALS: mode = JIM_VARLIST_LOCALS; break;
  10756. case INFO_VARS: mode = JIM_VARLIST_VARS; break;
  10757. default: mode = 0; /* avoid warning */; break;
  10758. }
  10759. if (argc != 2 && argc != 3) {
  10760. Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
  10761. return JIM_ERR;
  10762. }
  10763. if (argc == 3)
  10764. Jim_SetResult(interp,JimVariablesList(interp, argv[2], mode));
  10765. else
  10766. Jim_SetResult(interp, JimVariablesList(interp, NULL, mode));
  10767. } else if (cmd == INFO_LEVEL) {
  10768. Jim_Obj *objPtr;
  10769. switch (argc) {
  10770. case 2:
  10771. Jim_SetResult(interp,
  10772. Jim_NewIntObj(interp, interp->numLevels));
  10773. break;
  10774. case 3:
  10775. if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK)
  10776. return JIM_ERR;
  10777. Jim_SetResult(interp, objPtr);
  10778. break;
  10779. default:
  10780. Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
  10781. return JIM_ERR;
  10782. }
  10783. } else if (cmd == INFO_BODY || cmd == INFO_ARGS) {
  10784. Jim_Cmd *cmdPtr;
  10785. if (argc != 3) {
  10786. Jim_WrongNumArgs(interp, 2, argv, "procname");
  10787. return JIM_ERR;
  10788. }
  10789. if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL)
  10790. return JIM_ERR;
  10791. if (cmdPtr->cmdProc != NULL) {
  10792. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  10793. Jim_AppendStrings(interp, Jim_GetResult(interp),
  10794. "command \"", Jim_GetString(argv[2], NULL),
  10795. "\" is not a procedure", NULL);
  10796. return JIM_ERR;
  10797. }
  10798. if (cmd == INFO_BODY)
  10799. Jim_SetResult(interp, cmdPtr->bodyObjPtr);
  10800. else
  10801. Jim_SetResult(interp, cmdPtr->argListObjPtr);
  10802. } else if (cmd == INFO_VERSION) {
  10803. char buf[(JIM_INTEGER_SPACE * 2) + 1];
  10804. sprintf(buf, "%d.%d",
  10805. JIM_VERSION / 100, JIM_VERSION % 100);
  10806. Jim_SetResultString(interp, buf, -1);
  10807. } else if (cmd == INFO_COMPLETE) {
  10808. const char *s;
  10809. int len;
  10810. if (argc != 3) {
  10811. Jim_WrongNumArgs(interp, 2, argv, "script");
  10812. return JIM_ERR;
  10813. }
  10814. s = Jim_GetString(argv[2], &len);
  10815. Jim_SetResult(interp,
  10816. Jim_NewIntObj(interp, Jim_ScriptIsComplete(s, len, NULL)));
  10817. } else if (cmd == INFO_HOSTNAME) {
  10818. /* Redirect to os.hostname if it exists */
  10819. Jim_Obj *command = Jim_NewStringObj(interp, "os.gethostname", -1);
  10820. result = Jim_EvalObjVector(interp, 1, &command);
  10821. }
  10822. return result;
  10823. }
  10824. /* [split] */
  10825. static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc,
  10826. Jim_Obj *const *argv)
  10827. {
  10828. const char *str, *splitChars, *noMatchStart;
  10829. int splitLen, strLen, i;
  10830. Jim_Obj *resObjPtr;
  10831. if (argc != 2 && argc != 3) {
  10832. Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
  10833. return JIM_ERR;
  10834. }
  10835. /* Init */
  10836. if (argc == 2) {
  10837. splitChars = " \n\t\r";
  10838. splitLen = 4;
  10839. } else {
  10840. splitChars = Jim_GetString(argv[2], &splitLen);
  10841. }
  10842. str = Jim_GetString(argv[1], &strLen);
  10843. if (!strLen) return JIM_OK;
  10844. noMatchStart = str;
  10845. resObjPtr = Jim_NewListObj(interp, NULL, 0);
  10846. /* Split */
  10847. if (splitLen) {
  10848. while (strLen) {
  10849. for (i = 0; i < splitLen; i++) {
  10850. if (*str == splitChars[i]) {
  10851. Jim_Obj *objPtr;
  10852. objPtr = Jim_NewStringObj(interp, noMatchStart,
  10853. (str-noMatchStart));
  10854. Jim_ListAppendElement(interp, resObjPtr, objPtr);
  10855. noMatchStart = str + 1;
  10856. break;
  10857. }
  10858. }
  10859. str ++;
  10860. strLen --;
  10861. }
  10862. Jim_ListAppendElement(interp, resObjPtr,
  10863. Jim_NewStringObj(interp, noMatchStart, (str-noMatchStart)));
  10864. } else {
  10865. /* This handles the special case of splitchars eq {}. This
  10866. * is trivial but we want to perform object sharing as Tcl does. */
  10867. Jim_Obj *objCache[256];
  10868. const unsigned char *u = (unsigned char*) str;
  10869. memset(objCache, 0, sizeof(objCache));
  10870. for (i = 0; i < strLen; i++) {
  10871. int c = u[i];
  10872. if (objCache[c] == NULL)
  10873. objCache[c] = Jim_NewStringObj(interp, (char*)u + i, 1);
  10874. Jim_ListAppendElement(interp, resObjPtr, objCache[c]);
  10875. }
  10876. }
  10877. Jim_SetResult(interp, resObjPtr);
  10878. return JIM_OK;
  10879. }
  10880. /* [join] */
  10881. static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc,
  10882. Jim_Obj *const *argv)
  10883. {
  10884. const char *joinStr;
  10885. int joinStrLen, i, listLen;
  10886. Jim_Obj *resObjPtr;
  10887. if (argc != 2 && argc != 3) {
  10888. Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
  10889. return JIM_ERR;
  10890. }
  10891. /* Init */
  10892. if (argc == 2) {
  10893. joinStr = " ";
  10894. joinStrLen = 1;
  10895. } else {
  10896. joinStr = Jim_GetString(argv[2], &joinStrLen);
  10897. }
  10898. Jim_ListLength(interp, argv[1], &listLen);
  10899. resObjPtr = Jim_NewStringObj(interp, NULL, 0);
  10900. /* Split */
  10901. for (i = 0; i < listLen; i++) {
  10902. Jim_Obj *objPtr=NULL;
  10903. Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE);
  10904. Jim_AppendObj(interp, resObjPtr, objPtr);
  10905. if (i + 1 != listLen) {
  10906. Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
  10907. }
  10908. }
  10909. Jim_SetResult(interp, resObjPtr);
  10910. return JIM_OK;
  10911. }
  10912. /* [format] */
  10913. static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc,
  10914. Jim_Obj *const *argv)
  10915. {
  10916. Jim_Obj *objPtr;
  10917. if (argc < 2) {
  10918. Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
  10919. return JIM_ERR;
  10920. }
  10921. objPtr = Jim_FormatString(interp, argv[1], argc-2, argv + 2);
  10922. if (objPtr == NULL)
  10923. return JIM_ERR;
  10924. Jim_SetResult(interp, objPtr);
  10925. return JIM_OK;
  10926. }
  10927. /* [scan] */
  10928. static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc,
  10929. Jim_Obj *const *argv)
  10930. {
  10931. Jim_Obj *listPtr, **outVec;
  10932. int outc, i, count = 0;
  10933. if (argc < 3) {
  10934. Jim_WrongNumArgs(interp, 1, argv, "string formatString ?varName ...?");
  10935. return JIM_ERR;
  10936. }
  10937. if (argv[2]->typePtr != &scanFmtStringObjType)
  10938. SetScanFmtFromAny(interp, argv[2]);
  10939. if (FormatGetError(argv[2]) != 0) {
  10940. Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
  10941. return JIM_ERR;
  10942. }
  10943. if (argc > 3) {
  10944. int maxPos = FormatGetMaxPos(argv[2]);
  10945. int count = FormatGetCnvCount(argv[2]);
  10946. if (maxPos > argc-3) {
  10947. Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
  10948. return JIM_ERR;
  10949. } else if (count != 0 && count < argc-3) {
  10950. Jim_SetResultString(interp, "variable is not assigned by any "
  10951. "conversion specifiers", -1);
  10952. return JIM_ERR;
  10953. } else if (count > argc-3) {
  10954. Jim_SetResultString(interp, "different numbers of variable names and "
  10955. "field specifiers", -1);
  10956. return JIM_ERR;
  10957. }
  10958. }
  10959. listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
  10960. if (listPtr == 0)
  10961. return JIM_ERR;
  10962. if (argc > 3) {
  10963. int len = 0;
  10964. if (listPtr != 0 && listPtr != (Jim_Obj*)EOF)
  10965. Jim_ListLength(interp, listPtr, &len);
  10966. if (listPtr == (Jim_Obj*)EOF || len == 0) { // XXX
  10967. Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
  10968. return JIM_OK;
  10969. }
  10970. JimListGetElements(interp, listPtr, &outc, &outVec);
  10971. for (i = 0; i < outc; ++i) {
  10972. if (Jim_Length(outVec[i]) > 0) {
  10973. ++count;
  10974. if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK)
  10975. goto err;
  10976. }
  10977. }
  10978. Jim_FreeNewObj(interp, listPtr);
  10979. Jim_SetResult(interp, Jim_NewIntObj(interp, count));
  10980. } else {
  10981. if (listPtr == (Jim_Obj*)EOF) {
  10982. Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
  10983. return JIM_OK;
  10984. }
  10985. Jim_SetResult(interp, listPtr);
  10986. }
  10987. return JIM_OK;
  10988. err:
  10989. Jim_FreeNewObj(interp, listPtr);
  10990. return JIM_ERR;
  10991. }
  10992. /* [error] */
  10993. static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc,
  10994. Jim_Obj *const *argv)
  10995. {
  10996. if (argc != 2) {
  10997. Jim_WrongNumArgs(interp, 1, argv, "message");
  10998. return JIM_ERR;
  10999. }
  11000. Jim_SetResult(interp, argv[1]);
  11001. return JIM_ERR;
  11002. }
  11003. /* [lrange] */
  11004. static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc,
  11005. Jim_Obj *const *argv)
  11006. {
  11007. Jim_Obj *objPtr;
  11008. if (argc != 4) {
  11009. Jim_WrongNumArgs(interp, 1, argv, "list first last");
  11010. return JIM_ERR;
  11011. }
  11012. if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
  11013. return JIM_ERR;
  11014. Jim_SetResult(interp, objPtr);
  11015. return JIM_OK;
  11016. }
  11017. /* [env] */
  11018. static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc,
  11019. Jim_Obj *const *argv)
  11020. {
  11021. const char *key;
  11022. char *val;
  11023. if (argc == 1) {
  11024. #ifdef NEED_ENVIRON_EXTERN
  11025. extern char **environ;
  11026. #endif
  11027. int i;
  11028. Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
  11029. for (i = 0; environ[i]; i++) {
  11030. const char *equals = strchr(environ[i], '=');
  11031. if (equals) {
  11032. Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, environ[i], equals - environ[i]));
  11033. Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
  11034. }
  11035. }
  11036. Jim_SetResult(interp, listObjPtr);
  11037. return JIM_OK;
  11038. }
  11039. if (argc != 2) {
  11040. Jim_WrongNumArgs(interp, 1, argv, "varName");
  11041. return JIM_ERR;
  11042. }
  11043. key = Jim_GetString(argv[1], NULL);
  11044. val = getenv(key);
  11045. if (val == NULL) {
  11046. Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
  11047. Jim_AppendStrings(interp, Jim_GetResult(interp),
  11048. "environment variable \"",
  11049. key, "\" does not exist", NULL);
  11050. return JIM_ERR;
  11051. }
  11052. Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
  11053. return JIM_OK;
  11054. }
  11055. /* [source] */
  11056. static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc,
  11057. Jim_Obj *const *argv)
  11058. {
  11059. int retval;
  11060. if (argc != 2) {
  11061. Jim_WrongNumArgs(interp, 1, argv, "fileName");
  11062. return JIM_ERR;
  11063. }
  11064. retval = Jim_EvalFile(interp, Jim_GetString(argv[1], NULL));
  11065. if (retval == JIM_ERR) {
  11066. return JIM_ERR_ADDSTACK;
  11067. }
  11068. if (retval == JIM_RETURN)
  11069. return JIM_OK;
  11070. return retval;
  11071. }
  11072. /* [lreverse] */
  11073. static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc,
  11074. Jim_Obj *const *argv)
  11075. {
  11076. Jim_Obj *revObjPtr, **ele;
  11077. int len;
  11078. if (argc != 2) {
  11079. Jim_WrongNumArgs(interp, 1, argv, "list");
  11080. return JIM_ERR;
  11081. }
  11082. JimListGetElements(interp, argv[1], &len, &ele);
  11083. len--;
  11084. revObjPtr = Jim_NewListObj(interp, NULL, 0);
  11085. while (len >= 0)
  11086. ListAppendElement(revObjPtr, ele[len--]);
  11087. Jim_SetResult(interp, revObjPtr);
  11088. return JIM_OK;
  11089. }
  11090. static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
  11091. {
  11092. jim_wide len;
  11093. if (step == 0) return -1;
  11094. if (start == end) return 0;
  11095. else if (step > 0 && start > end) return -1;
  11096. else if (step < 0 && end > start) return -1;
  11097. len = end-start;
  11098. if (len < 0) len = -len; /* abs(len) */
  11099. if (step < 0) step = -step; /* abs(step) */
  11100. len = 1 + ((len-1)/step);
  11101. /* We can truncate safely to INT_MAX, the range command
  11102. * will always return an error for a such long range
  11103. * because Tcl lists can't be so long. */
  11104. if (len > INT_MAX) len = INT_MAX;
  11105. return (int)((len < 0) ? -1 : len);
  11106. }
  11107. /* [range] */
  11108. static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc,
  11109. Jim_Obj *const *argv)
  11110. {
  11111. jim_wide start = 0, end, step = 1;
  11112. int len, i;
  11113. Jim_Obj *objPtr;
  11114. if (argc < 2 || argc > 4) {
  11115. Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
  11116. return JIM_ERR;
  11117. }
  11118. if (argc == 2) {
  11119. if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
  11120. return JIM_ERR;
  11121. } else {
  11122. if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
  11123. Jim_GetWide(interp, argv[2], &end) != JIM_OK)
  11124. return JIM_ERR;
  11125. if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
  11126. return JIM_ERR;
  11127. }
  11128. if ((len = JimRangeLen(start, end, step)) == -1) {
  11129. Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
  11130. return JIM_ERR;
  11131. }
  11132. objPtr = Jim_NewListObj(interp, NULL, 0);
  11133. for (i = 0; i < len; i++)
  11134. ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i*step));
  11135. Jim_SetResult(interp, objPtr);
  11136. return JIM_OK;
  11137. }
  11138. /* [rand] */
  11139. static int Jim_RandCoreCommand(Jim_Interp *interp, int argc,
  11140. Jim_Obj *const *argv)
  11141. {
  11142. jim_wide min = 0, max =0, len, maxMul;
  11143. if (argc < 1 || argc > 3) {
  11144. Jim_WrongNumArgs(interp, 1, argv, "?min? max");
  11145. return JIM_ERR;
  11146. }
  11147. if (argc == 1) {
  11148. max = JIM_WIDE_MAX;
  11149. } else if (argc == 2) {
  11150. if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
  11151. return JIM_ERR;
  11152. } else if (argc == 3) {
  11153. if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
  11154. Jim_GetWide(interp, argv[2], &max) != JIM_OK)
  11155. return JIM_ERR;
  11156. }
  11157. len = max-min;
  11158. if (len < 0) {
  11159. Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
  11160. return JIM_ERR;
  11161. }
  11162. maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
  11163. while (1) {
  11164. jim_wide r;
  11165. JimRandomBytes(interp, &r, sizeof(jim_wide));
  11166. if (r < 0 || r >= maxMul) continue;
  11167. r = (len == 0) ? 0 : r%len;
  11168. Jim_SetResult(interp, Jim_NewIntObj(interp, min + r));
  11169. return JIM_OK;
  11170. }
  11171. }
  11172. /* [package] */
  11173. static int Jim_PackageCoreCommand(Jim_Interp *interp, int argc,
  11174. Jim_Obj *const *argv)
  11175. {
  11176. int option;
  11177. const char *options[] = {
  11178. "require", "provide", NULL
  11179. };
  11180. enum {OPT_REQUIRE, OPT_PROVIDE};
  11181. if (argc < 2) {
  11182. Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
  11183. return JIM_ERR;
  11184. }
  11185. if (Jim_GetEnum(interp, argv[1], options, &option, "option",
  11186. JIM_ERRMSG) != JIM_OK)
  11187. return JIM_ERR;
  11188. if (option == OPT_REQUIRE) {
  11189. int exact = 0;
  11190. const char *ver;
  11191. if (Jim_CompareStringImmediate(interp, argv[2], "-exact")) {
  11192. exact = 1;
  11193. argv++;
  11194. argc--;
  11195. }
  11196. if (argc != 3 && argc != 4) {
  11197. Jim_WrongNumArgs(interp, 2, argv, "?-exact? package ?version?");
  11198. return JIM_ERR;
  11199. }
  11200. ver = Jim_PackageRequire(interp, Jim_GetString(argv[2], NULL),
  11201. argc == 4 ? Jim_GetString(argv[3], NULL) : "",
  11202. JIM_ERRMSG);
  11203. if (ver == NULL)
  11204. return JIM_ERR_ADDSTACK;
  11205. Jim_SetResultString(interp, ver, -1);
  11206. } else if (option == OPT_PROVIDE) {
  11207. if (argc != 4) {
  11208. Jim_WrongNumArgs(interp, 2, argv, "package version");
  11209. return JIM_ERR;
  11210. }
  11211. return Jim_PackageProvide(interp, Jim_GetString(argv[2], NULL),
  11212. Jim_GetString(argv[3], NULL), JIM_ERRMSG);
  11213. }
  11214. return JIM_OK;
  11215. }
  11216. static struct {
  11217. const char *name;
  11218. Jim_CmdProc cmdProc;
  11219. } Jim_CoreCommandsTable[] = {
  11220. {"set", Jim_SetCoreCommand},
  11221. {"unset", Jim_UnsetCoreCommand},
  11222. {"puts", Jim_PutsCoreCommand},
  11223. {"+", Jim_AddCoreCommand},
  11224. {"*", Jim_MulCoreCommand},
  11225. {"-", Jim_SubCoreCommand},
  11226. {"/", Jim_DivCoreCommand},
  11227. {"incr", Jim_IncrCoreCommand},
  11228. {"while", Jim_WhileCoreCommand},
  11229. {"for", Jim_ForCoreCommand},
  11230. {"foreach", Jim_ForeachCoreCommand},
  11231. {"lmap", Jim_LmapCoreCommand},
  11232. {"if", Jim_IfCoreCommand},
  11233. {"switch", Jim_SwitchCoreCommand},
  11234. {"list", Jim_ListCoreCommand},
  11235. {"lindex", Jim_LindexCoreCommand},
  11236. {"lset", Jim_LsetCoreCommand},
  11237. {"llength", Jim_LlengthCoreCommand},
  11238. {"lappend", Jim_LappendCoreCommand},
  11239. {"linsert", Jim_LinsertCoreCommand},
  11240. {"lsort", Jim_LsortCoreCommand},
  11241. {"append", Jim_AppendCoreCommand},
  11242. {"debug", Jim_DebugCoreCommand},
  11243. {"eval", Jim_EvalCoreCommand},
  11244. {"uplevel", Jim_UplevelCoreCommand},
  11245. {"expr", Jim_ExprCoreCommand},
  11246. {"break", Jim_BreakCoreCommand},
  11247. {"continue", Jim_ContinueCoreCommand},
  11248. {"proc", Jim_ProcCoreCommand},
  11249. {"concat", Jim_ConcatCoreCommand},
  11250. {"return", Jim_ReturnCoreCommand},
  11251. {"upvar", Jim_UpvarCoreCommand},
  11252. {"global", Jim_GlobalCoreCommand},
  11253. {"string", Jim_StringCoreCommand},
  11254. {"time", Jim_TimeCoreCommand},
  11255. {"exit", Jim_ExitCoreCommand},
  11256. {"catch", Jim_CatchCoreCommand},
  11257. {"ref", Jim_RefCoreCommand},
  11258. {"getref", Jim_GetrefCoreCommand},
  11259. {"setref", Jim_SetrefCoreCommand},
  11260. {"finalize", Jim_FinalizeCoreCommand},
  11261. {"collect", Jim_CollectCoreCommand},
  11262. {"rename", Jim_RenameCoreCommand},
  11263. {"dict", Jim_DictCoreCommand},
  11264. {"load", Jim_LoadCoreCommand},
  11265. {"subst", Jim_SubstCoreCommand},
  11266. {"info", Jim_InfoCoreCommand},
  11267. {"split", Jim_SplitCoreCommand},
  11268. {"join", Jim_JoinCoreCommand},
  11269. {"format", Jim_FormatCoreCommand},
  11270. {"scan", Jim_ScanCoreCommand},
  11271. {"error", Jim_ErrorCoreCommand},
  11272. {"lrange", Jim_LrangeCoreCommand},
  11273. {"env", Jim_EnvCoreCommand},
  11274. {"source", Jim_SourceCoreCommand},
  11275. {"lreverse", Jim_LreverseCoreCommand},
  11276. {"range", Jim_RangeCoreCommand},
  11277. {"rand", Jim_RandCoreCommand},
  11278. {"package", Jim_PackageCoreCommand},
  11279. {"tailcall", Jim_TailcallCoreCommand},
  11280. {NULL, NULL},
  11281. };
  11282. /* Some Jim core command is actually a procedure written in Jim itself. */
  11283. static void Jim_RegisterCoreProcedures(Jim_Interp *interp)
  11284. {
  11285. Jim_Eval(interp, (char*)
  11286. "proc lambda {arglist args} {\n"
  11287. " set name [ref {} function lambdaFinalizer]\n"
  11288. " uplevel 1 [list proc $name $arglist {expand}$args]\n"
  11289. " return $name\n"
  11290. "}\n"
  11291. "proc lambdaFinalizer {name val} {\n"
  11292. " rename $name {}\n"
  11293. "}\n"
  11294. );
  11295. }
  11296. void Jim_RegisterCoreCommands(Jim_Interp *interp)
  11297. {
  11298. int i = 0;
  11299. while (Jim_CoreCommandsTable[i].name != NULL) {
  11300. Jim_CreateCommand(interp,
  11301. Jim_CoreCommandsTable[i].name,
  11302. Jim_CoreCommandsTable[i].cmdProc,
  11303. NULL, NULL);
  11304. i++;
  11305. }
  11306. Jim_RegisterCoreProcedures(interp);
  11307. }
  11308. /* -----------------------------------------------------------------------------
  11309. * Interactive prompt
  11310. * ---------------------------------------------------------------------------*/
  11311. void Jim_PrintErrorMessage(Jim_Interp *interp)
  11312. {
  11313. int len, i;
  11314. if (*interp->errorFileName) {
  11315. Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL " ",
  11316. interp->errorFileName, interp->errorLine);
  11317. }
  11318. Jim_fprintf(interp,interp->cookie_stderr, "%s" JIM_NL,
  11319. Jim_GetString(interp->result, NULL));
  11320. Jim_ListLength(interp, interp->stackTrace, &len);
  11321. for (i = len-3; i >= 0; i-= 3) {
  11322. Jim_Obj *objPtr=NULL;
  11323. const char *proc, *file, *line;
  11324. Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
  11325. proc = Jim_GetString(objPtr, NULL);
  11326. Jim_ListIndex(interp, interp->stackTrace, i + 1, &objPtr,
  11327. JIM_NONE);
  11328. file = Jim_GetString(objPtr, NULL);
  11329. Jim_ListIndex(interp, interp->stackTrace, i + 2, &objPtr,
  11330. JIM_NONE);
  11331. line = Jim_GetString(objPtr, NULL);
  11332. if (*proc) {
  11333. Jim_fprintf(interp, interp->cookie_stderr,
  11334. "in procedure '%s' ", proc);
  11335. }
  11336. if (*file) {
  11337. Jim_fprintf(interp, interp->cookie_stderr,
  11338. "called at file \"%s\", line %s",
  11339. file, line);
  11340. }
  11341. if (*file || *proc) {
  11342. Jim_fprintf(interp, interp->cookie_stderr, JIM_NL);
  11343. }
  11344. }
  11345. }
  11346. int Jim_InteractivePrompt(Jim_Interp *interp)
  11347. {
  11348. int retcode = JIM_OK;
  11349. Jim_Obj *scriptObjPtr;
  11350. Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, "
  11351. "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL,
  11352. JIM_VERSION / 100, JIM_VERSION % 100);
  11353. Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
  11354. while (1) {
  11355. char buf[1024];
  11356. const char *result;
  11357. const char *retcodestr[] = {
  11358. "ok", "error", "return", "break", "continue", "eval", "exit"
  11359. };
  11360. int reslen;
  11361. if (retcode != 0) {
  11362. if (retcode >= 2 && retcode <= 6)
  11363. Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]);
  11364. else
  11365. Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode);
  11366. } else
  11367. Jim_fprintf(interp, interp->cookie_stdout, ". ");
  11368. Jim_fflush(interp, interp->cookie_stdout);
  11369. scriptObjPtr = Jim_NewStringObj(interp, "", 0);
  11370. Jim_IncrRefCount(scriptObjPtr);
  11371. while (1) {
  11372. const char *str;
  11373. char state;
  11374. int len;
  11375. if (Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) {
  11376. Jim_DecrRefCount(interp, scriptObjPtr);
  11377. goto out;
  11378. }
  11379. Jim_AppendString(interp, scriptObjPtr, buf, -1);
  11380. str = Jim_GetString(scriptObjPtr, &len);
  11381. if (Jim_ScriptIsComplete(str, len, &state))
  11382. break;
  11383. Jim_fprintf(interp, interp->cookie_stdout, "%c> ", state);
  11384. Jim_fflush(interp, interp->cookie_stdout);
  11385. }
  11386. retcode = Jim_EvalObj(interp, scriptObjPtr);
  11387. Jim_DecrRefCount(interp, scriptObjPtr);
  11388. result = Jim_GetString(Jim_GetResult(interp), &reslen);
  11389. if (retcode == JIM_ERR) {
  11390. Jim_PrintErrorMessage(interp);
  11391. } else if (retcode == JIM_EXIT) {
  11392. exit(Jim_GetExitCode(interp));
  11393. } else {
  11394. if (reslen) {
  11395. Jim_fwrite(interp, result, 1, reslen, interp->cookie_stdout);
  11396. Jim_fprintf(interp,interp->cookie_stdout, JIM_NL);
  11397. }
  11398. }
  11399. }
  11400. out:
  11401. return 0;
  11402. }
  11403. /* -----------------------------------------------------------------------------
  11404. * Jim's idea of STDIO..
  11405. * ---------------------------------------------------------------------------*/
  11406. int Jim_fprintf(Jim_Interp *interp, void *cookie, const char *fmt, ...)
  11407. {
  11408. int r;
  11409. va_list ap;
  11410. va_start(ap,fmt);
  11411. r = Jim_vfprintf(interp, cookie, fmt,ap);
  11412. va_end(ap);
  11413. return r;
  11414. }
  11415. int Jim_vfprintf(Jim_Interp *interp, void *cookie, const char *fmt, va_list ap)
  11416. {
  11417. if ((interp == NULL) || (interp->cb_vfprintf == NULL)) {
  11418. errno = ENOTSUP;
  11419. return -1;
  11420. }
  11421. return (*(interp->cb_vfprintf))(cookie, fmt, ap);
  11422. }
  11423. size_t Jim_fwrite(Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie)
  11424. {
  11425. if ((interp == NULL) || (interp->cb_fwrite == NULL)) {
  11426. errno = ENOTSUP;
  11427. return 0;
  11428. }
  11429. return (*(interp->cb_fwrite))(ptr, size, n, cookie);
  11430. }
  11431. size_t Jim_fread(Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie)
  11432. {
  11433. if ((interp == NULL) || (interp->cb_fread == NULL)) {
  11434. errno = ENOTSUP;
  11435. return 0;
  11436. }
  11437. return (*(interp->cb_fread))(ptr, size, n, cookie);
  11438. }
  11439. int Jim_fflush(Jim_Interp *interp, void *cookie)
  11440. {
  11441. if ((interp == NULL) || (interp->cb_fflush == NULL)) {
  11442. /* pretend all is well */
  11443. return 0;
  11444. }
  11445. return (*(interp->cb_fflush))(cookie);
  11446. }
  11447. char* Jim_fgets(Jim_Interp *interp, char *s, int size, void *cookie)
  11448. {
  11449. if ((interp == NULL) || (interp->cb_fgets == NULL)) {
  11450. errno = ENOTSUP;
  11451. return NULL;
  11452. }
  11453. return (*(interp->cb_fgets))(s, size, cookie);
  11454. }
  11455. Jim_Nvp *
  11456. Jim_Nvp_name2value_simple(const Jim_Nvp *p, const char *name)
  11457. {
  11458. while (p->name) {
  11459. if (0 == strcmp(name, p->name)) {
  11460. break;
  11461. }
  11462. p++;
  11463. }
  11464. return ((Jim_Nvp *)(p));
  11465. }
  11466. Jim_Nvp *
  11467. Jim_Nvp_name2value_nocase_simple(const Jim_Nvp *p, const char *name)
  11468. {
  11469. while (p->name) {
  11470. if (0 == strcasecmp(name, p->name)) {
  11471. break;
  11472. }
  11473. p++;
  11474. }
  11475. return ((Jim_Nvp *)(p));
  11476. }
  11477. int
  11478. Jim_Nvp_name2value_obj(Jim_Interp *interp,
  11479. const Jim_Nvp *p,
  11480. Jim_Obj *o,
  11481. Jim_Nvp **result)
  11482. {
  11483. return Jim_Nvp_name2value(interp, p, Jim_GetString(o, NULL), result);
  11484. }
  11485. int
  11486. Jim_Nvp_name2value(Jim_Interp *interp,
  11487. const Jim_Nvp *_p,
  11488. const char *name,
  11489. Jim_Nvp **result)
  11490. {
  11491. const Jim_Nvp *p;
  11492. p = Jim_Nvp_name2value_simple(_p, name);
  11493. /* result */
  11494. if (result) {
  11495. *result = (Jim_Nvp *)(p);
  11496. }
  11497. /* found? */
  11498. if (p->name) {
  11499. return JIM_OK;
  11500. } else {
  11501. return JIM_ERR;
  11502. }
  11503. }
  11504. int
  11505. Jim_Nvp_name2value_obj_nocase(Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **puthere)
  11506. {
  11507. return Jim_Nvp_name2value_nocase(interp, p, Jim_GetString(o, NULL), puthere);
  11508. }
  11509. int
  11510. Jim_Nvp_name2value_nocase(Jim_Interp *interp, const Jim_Nvp *_p, const char *name, Jim_Nvp **puthere)
  11511. {
  11512. const Jim_Nvp *p;
  11513. p = Jim_Nvp_name2value_nocase_simple(_p, name);
  11514. if (puthere) {
  11515. *puthere = (Jim_Nvp *)(p);
  11516. }
  11517. /* found */
  11518. if (p->name) {
  11519. return JIM_OK;
  11520. } else {
  11521. return JIM_ERR;
  11522. }
  11523. }
  11524. int
  11525. Jim_Nvp_value2name_obj(Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **result)
  11526. {
  11527. int e;;
  11528. jim_wide w;
  11529. e = Jim_GetWide(interp, o, &w);
  11530. if (e != JIM_OK) {
  11531. return e;
  11532. }
  11533. return Jim_Nvp_value2name(interp, p, w, result);
  11534. }
  11535. Jim_Nvp *
  11536. Jim_Nvp_value2name_simple(const Jim_Nvp *p, int value)
  11537. {
  11538. while (p->name) {
  11539. if (value == p->value) {
  11540. break;
  11541. }
  11542. p++;
  11543. }
  11544. return ((Jim_Nvp *)(p));
  11545. }
  11546. int
  11547. Jim_Nvp_value2name(Jim_Interp *interp, const Jim_Nvp *_p, int value, Jim_Nvp **result)
  11548. {
  11549. const Jim_Nvp *p;
  11550. p = Jim_Nvp_value2name_simple(_p, value);
  11551. if (result) {
  11552. *result = (Jim_Nvp *)(p);
  11553. }
  11554. if (p->name) {
  11555. return JIM_OK;
  11556. } else {
  11557. return JIM_ERR;
  11558. }
  11559. }
  11560. int
  11561. Jim_GetOpt_Setup(Jim_GetOptInfo *p, Jim_Interp *interp, int argc, Jim_Obj * const * argv)
  11562. {
  11563. memset(p, 0, sizeof(*p));
  11564. p->interp = interp;
  11565. p->argc = argc;
  11566. p->argv = argv;
  11567. return JIM_OK;
  11568. }
  11569. void
  11570. Jim_GetOpt_Debug(Jim_GetOptInfo *p)
  11571. {
  11572. int x;
  11573. Jim_fprintf(p->interp, p->interp->cookie_stderr, "---args---\n");
  11574. for (x = 0 ; x < p->argc ; x++) {
  11575. Jim_fprintf(p->interp, p->interp->cookie_stderr,
  11576. "%2d) %s\n",
  11577. x,
  11578. Jim_GetString(p->argv[x], NULL));
  11579. }
  11580. Jim_fprintf(p->interp, p->interp->cookie_stderr, "-------\n");
  11581. }
  11582. int
  11583. Jim_GetOpt_Obj(Jim_GetOptInfo *goi, Jim_Obj **puthere)
  11584. {
  11585. Jim_Obj *o;
  11586. o = NULL; // failure
  11587. if (goi->argc) {
  11588. // success
  11589. o = goi->argv[0];
  11590. goi->argc -= 1;
  11591. goi->argv += 1;
  11592. }
  11593. if (puthere) {
  11594. *puthere = o;
  11595. }
  11596. if (o != NULL) {
  11597. return JIM_OK;
  11598. } else {
  11599. return JIM_ERR;
  11600. }
  11601. }
  11602. int
  11603. Jim_GetOpt_String(Jim_GetOptInfo *goi, char **puthere, int *len)
  11604. {
  11605. int r;
  11606. Jim_Obj *o;
  11607. const char *cp;
  11608. r = Jim_GetOpt_Obj(goi, &o);
  11609. if (r == JIM_OK) {
  11610. cp = Jim_GetString(o, len);
  11611. if (puthere) {
  11612. /* remove const */
  11613. *puthere = (char *)(cp);
  11614. }
  11615. }
  11616. return r;
  11617. }
  11618. int
  11619. Jim_GetOpt_Double(Jim_GetOptInfo *goi, double *puthere)
  11620. {
  11621. int r;
  11622. Jim_Obj *o;
  11623. double _safe;
  11624. if (puthere == NULL) {
  11625. puthere = &_safe;
  11626. }
  11627. r = Jim_GetOpt_Obj(goi, &o);
  11628. if (r == JIM_OK) {
  11629. r = Jim_GetDouble(goi->interp, o, puthere);
  11630. if (r != JIM_OK) {
  11631. Jim_SetResult_sprintf(goi->interp,
  11632. "not a number: %s",
  11633. Jim_GetString(o, NULL));
  11634. }
  11635. }
  11636. return r;
  11637. }
  11638. int
  11639. Jim_GetOpt_Wide(Jim_GetOptInfo *goi, jim_wide *puthere)
  11640. {
  11641. int r;
  11642. Jim_Obj *o;
  11643. jim_wide _safe;
  11644. if (puthere == NULL) {
  11645. puthere = &_safe;
  11646. }
  11647. r = Jim_GetOpt_Obj(goi, &o);
  11648. if (r == JIM_OK) {
  11649. r = Jim_GetWide(goi->interp, o, puthere);
  11650. }
  11651. return r;
  11652. }
  11653. int Jim_GetOpt_Nvp(Jim_GetOptInfo *goi,
  11654. const Jim_Nvp *nvp,
  11655. Jim_Nvp **puthere)
  11656. {
  11657. Jim_Nvp *_safe;
  11658. Jim_Obj *o;
  11659. int e;
  11660. if (puthere == NULL) {
  11661. puthere = &_safe;
  11662. }
  11663. e = Jim_GetOpt_Obj(goi, &o);
  11664. if (e == JIM_OK) {
  11665. e = Jim_Nvp_name2value_obj(goi->interp,
  11666. nvp,
  11667. o,
  11668. puthere);
  11669. }
  11670. return e;
  11671. }
  11672. void
  11673. Jim_GetOpt_NvpUnknown(Jim_GetOptInfo *goi,
  11674. const Jim_Nvp *nvptable,
  11675. int hadprefix)
  11676. {
  11677. if (hadprefix) {
  11678. Jim_SetResult_NvpUnknown(goi->interp,
  11679. goi->argv[-2],
  11680. goi->argv[-1],
  11681. nvptable);
  11682. } else {
  11683. Jim_SetResult_NvpUnknown(goi->interp,
  11684. NULL,
  11685. goi->argv[-1],
  11686. nvptable);
  11687. }
  11688. }
  11689. int
  11690. Jim_GetOpt_Enum(Jim_GetOptInfo *goi,
  11691. const char * const * lookup,
  11692. int *puthere)
  11693. {
  11694. int _safe;
  11695. Jim_Obj *o;
  11696. int e;
  11697. if (puthere == NULL) {
  11698. puthere = &_safe;
  11699. }
  11700. e = Jim_GetOpt_Obj(goi, &o);
  11701. if (e == JIM_OK) {
  11702. e = Jim_GetEnum(goi->interp,
  11703. o,
  11704. lookup,
  11705. puthere,
  11706. "option",
  11707. JIM_ERRMSG);
  11708. }
  11709. return e;
  11710. }
  11711. int
  11712. Jim_SetResult_sprintf(Jim_Interp *interp, const char *fmt,...)
  11713. {
  11714. va_list ap;
  11715. char *buf;
  11716. va_start(ap,fmt);
  11717. buf = jim_vasprintf(fmt, ap);
  11718. va_end(ap);
  11719. if (buf) {
  11720. Jim_SetResultString(interp, buf, -1);
  11721. jim_vasprintf_done(buf);
  11722. }
  11723. return JIM_OK;
  11724. }
  11725. void
  11726. Jim_SetResult_NvpUnknown(Jim_Interp *interp,
  11727. Jim_Obj *param_name,
  11728. Jim_Obj *param_value,
  11729. const Jim_Nvp *nvp)
  11730. {
  11731. if (param_name) {
  11732. Jim_SetResult_sprintf(interp,
  11733. "%s: Unknown: %s, try one of: ",
  11734. Jim_GetString(param_name, NULL),
  11735. Jim_GetString(param_value, NULL));
  11736. } else {
  11737. Jim_SetResult_sprintf(interp,
  11738. "Unknown param: %s, try one of: ",
  11739. Jim_GetString(param_value, NULL));
  11740. }
  11741. while (nvp->name) {
  11742. const char *a;
  11743. const char *b;
  11744. if ((nvp + 1)->name) {
  11745. a = nvp->name;
  11746. b = ", ";
  11747. } else {
  11748. a = "or ";
  11749. b = nvp->name;
  11750. }
  11751. Jim_AppendStrings(interp,
  11752. Jim_GetResult(interp),
  11753. a, b, NULL);
  11754. nvp++;
  11755. }
  11756. }
  11757. static Jim_Obj *debug_string_obj;
  11758. const char *
  11759. Jim_Debug_ArgvString(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
  11760. {
  11761. int x;
  11762. if (debug_string_obj) {
  11763. Jim_FreeObj(interp, debug_string_obj);
  11764. }
  11765. debug_string_obj = Jim_NewEmptyStringObj(interp);
  11766. for (x = 0 ; x < argc ; x++) {
  11767. Jim_AppendStrings(interp,
  11768. debug_string_obj,
  11769. Jim_GetString(argv[x], NULL),
  11770. " ",
  11771. NULL);
  11772. }
  11773. return Jim_GetString(debug_string_obj, NULL);
  11774. }