1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */ 2 #define JIM_COMPAT 3 #define JIM_ANSIC 4 #define JIM_REGEXP 5 #define HAVE_NO_AUTOCONF 6 #define JIM_TINY 7 #define _JIMAUTOCONF_H 8 #define TCL_LIBRARY "." 9 #define jim_ext_bootstrap 10 #define jim_ext_aio 11 #define jim_ext_readdir 12 #define jim_ext_regexp 13 #define jim_ext_file 14 #define jim_ext_glob 15 #define jim_ext_exec 16 #define jim_ext_clock 17 #define jim_ext_array 18 #define jim_ext_stdlib 19 #define jim_ext_tclcompat 20 #if defined(_MSC_VER) 21 #define TCL_PLATFORM_OS "windows" 22 #define TCL_PLATFORM_PLATFORM "windows" 23 #define TCL_PLATFORM_PATH_SEPARATOR ";" 24 #define HAVE_MKDIR_ONE_ARG 25 #define HAVE_SYSTEM 26 #elif defined(__MINGW32__) 27 #define TCL_PLATFORM_OS "mingw" 28 #define TCL_PLATFORM_PLATFORM "windows" 29 #define TCL_PLATFORM_PATH_SEPARATOR ";" 30 #define HAVE_MKDIR_ONE_ARG 31 #define HAVE_SYSTEM 32 #define HAVE_SYS_TIME_H 33 #define HAVE_DIRENT_H 34 #define HAVE_UNISTD_H 35 #define HAVE_UMASK 36 #include <sys/stat.h> 37 #ifndef S_IRWXG 38 #define S_IRWXG 0 39 #endif 40 #ifndef S_IRWXO 41 #define S_IRWXO 0 42 #endif 43 #else 44 #define TCL_PLATFORM_OS "unknown" 45 #define TCL_PLATFORM_PLATFORM "unix" 46 #define TCL_PLATFORM_PATH_SEPARATOR ":" 47 #ifdef _MINIX 48 #define vfork fork 49 #define _POSIX_SOURCE 50 #else 51 #define _GNU_SOURCE 52 #endif 53 #define HAVE_FORK 54 #define HAVE_WAITPID 55 #define HAVE_ISATTY 56 #define HAVE_MKSTEMP 57 #define HAVE_LINK 58 #define HAVE_SYS_TIME_H 59 #define HAVE_DIRENT_H 60 #define HAVE_UNISTD_H 61 #define HAVE_UMASK 62 #define HAVE_PIPE 63 #define _FILE_OFFSET_BITS 64 64 #endif 65 #define JIM_VERSION 84 66 #ifndef JIM_WIN32COMPAT_H 67 #define JIM_WIN32COMPAT_H 68 69 70 71 #ifdef __cplusplus 72 extern "C" { 73 #endif 74 75 76 #if defined(_WIN32) || defined(WIN32) 77 78 #define HAVE_DLOPEN 79 void *dlopen(const char *path, int mode); 80 int dlclose(void *handle); 81 void *dlsym(void *handle, const char *symbol); 82 char *dlerror(void); 83 84 85 #if defined(__MINGW32__) 86 #define JIM_SPRINTF_DOUBLE_NEEDS_FIX 87 #endif 88 89 #ifdef _MSC_VER 90 91 92 #if _MSC_VER >= 1000 93 #pragma warning(disable:4146) 94 #endif 95 96 #include <limits.h> 97 #define jim_wide _int64 98 #ifndef HAVE_LONG_LONG 99 #define HAVE_LONG_LONG 100 #endif 101 #ifndef LLONG_MAX 102 #define LLONG_MAX 9223372036854775807I64 103 #endif 104 #ifndef LLONG_MIN 105 #define LLONG_MIN (-LLONG_MAX - 1I64) 106 #endif 107 #define JIM_WIDE_MIN LLONG_MIN 108 #define JIM_WIDE_MAX LLONG_MAX 109 #define JIM_WIDE_MODIFIER "I64d" 110 #define strcasecmp _stricmp 111 #define strtoull _strtoui64 112 113 #include <io.h> 114 115 #include <winsock.h> 116 int gettimeofday(struct timeval *tv, void *unused); 117 118 #define HAVE_OPENDIR 119 struct dirent { 120 char *d_name; 121 }; 122 123 typedef struct DIR { 124 long handle; 125 struct _finddata_t info; 126 struct dirent result; 127 char *name; 128 } DIR; 129 130 DIR *opendir(const char *name); 131 int closedir(DIR *dir); 132 struct dirent *readdir(DIR *dir); 133 134 #endif 135 136 #endif 137 138 #ifdef __cplusplus 139 } 140 #endif 141 142 #endif 143 #ifndef UTF8_UTIL_H 144 #define UTF8_UTIL_H 145 146 #ifdef __cplusplus 147 extern "C" { 148 #endif 149 150 151 152 #define MAX_UTF8_LEN 4 153 154 int utf8_fromunicode(char *p, unsigned uc); 155 156 #ifndef JIM_UTF8 157 #include <ctype.h> 158 159 160 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B)) 161 #define utf8_strwidth(S, B) utf8_strlen((S), (B)) 162 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1) 163 #define utf8_getchars(CP, C) (*(CP) = (C), 1) 164 #define utf8_upper(C) toupper(C) 165 #define utf8_title(C) toupper(C) 166 #define utf8_lower(C) tolower(C) 167 #define utf8_index(C, I) (I) 168 #define utf8_charlen(C) 1 169 #define utf8_prev_len(S, L) 1 170 #define utf8_width(C) 1 171 172 #else 173 174 #endif 175 176 #ifdef __cplusplus 177 } 178 #endif 179 180 #endif 181 182 #ifndef __JIM__H 183 #define __JIM__H 184 185 #ifdef __cplusplus 186 extern "C" { 187 #endif 188 189 #include <time.h> 190 #include <limits.h> 191 #include <stdlib.h> 192 #include <stdarg.h> 193 194 195 #ifndef HAVE_NO_AUTOCONF 196 #endif 197 198 199 200 #ifndef jim_wide 201 # ifdef HAVE_LONG_LONG 202 # define jim_wide long long 203 # ifndef LLONG_MAX 204 # define LLONG_MAX 9223372036854775807LL 205 # endif 206 # ifndef LLONG_MIN 207 # define LLONG_MIN (-LLONG_MAX - 1LL) 208 # endif 209 # define JIM_WIDE_MIN LLONG_MIN 210 # define JIM_WIDE_MAX LLONG_MAX 211 # else 212 # define jim_wide long 213 # define JIM_WIDE_MIN LONG_MIN 214 # define JIM_WIDE_MAX LONG_MAX 215 # endif 216 217 218 # ifdef HAVE_LONG_LONG 219 # define JIM_WIDE_MODIFIER "lld" 220 # else 221 # define JIM_WIDE_MODIFIER "ld" 222 # define strtoull strtoul 223 # endif 224 #endif 225 226 #define UCHAR(c) ((unsigned char)(c)) 227 228 229 230 #define JIM_ABI_VERSION 101 231 232 #define JIM_OK 0 233 #define JIM_ERR 1 234 #define JIM_RETURN 2 235 #define JIM_BREAK 3 236 #define JIM_CONTINUE 4 237 #define JIM_SIGNAL 5 238 #define JIM_EXIT 6 239 240 #define JIM_EVAL 7 241 242 #define JIM_MAX_CALLFRAME_DEPTH 1000 243 #define JIM_MAX_EVAL_DEPTH 2000 244 245 246 #define JIM_PRIV_FLAG_SHIFT 20 247 248 #define JIM_NONE 0 249 #define JIM_ERRMSG 1 250 #define JIM_ENUM_ABBREV 2 251 #define JIM_UNSHARED 4 252 #define JIM_MUSTEXIST 8 253 #define JIM_NORESULT 16 254 255 256 #define JIM_SUBST_NOVAR 1 257 #define JIM_SUBST_NOCMD 2 258 #define JIM_SUBST_NOESC 4 259 #define JIM_SUBST_FLAG 128 260 261 262 #define JIM_CASESENS 0 263 #define JIM_NOCASE 1 264 #define JIM_OPT_END 2 265 266 267 #define JIM_PATH_LEN 1024 268 269 270 #define JIM_NOTUSED(V) ((void) V) 271 272 #define JIM_LIBPATH "auto_path" 273 #define JIM_INTERACTIVE "tcl_interactive" 274 275 276 typedef struct Jim_Stack { 277 int len; 278 int maxlen; 279 void **vector; 280 } Jim_Stack; 281 282 283 typedef struct Jim_HashEntry { 284 void *key; 285 union { 286 void *val; 287 int intval; 288 } u; 289 struct Jim_HashEntry *next; 290 } Jim_HashEntry; 291 292 typedef struct Jim_HashTableType { 293 unsigned int (*hashFunction)(const void *key); 294 void *(*keyDup)(void *privdata, const void *key); 295 void *(*valDup)(void *privdata, const void *obj); 296 int (*keyCompare)(void *privdata, const void *key1, const void *key2); 297 void (*keyDestructor)(void *privdata, void *key); 298 void (*valDestructor)(void *privdata, void *obj); 299 } Jim_HashTableType; 300 301 typedef struct Jim_HashTable { 302 Jim_HashEntry **table; 303 const Jim_HashTableType *type; 304 void *privdata; 305 unsigned int size; 306 unsigned int sizemask; 307 unsigned int used; 308 unsigned int collisions; 309 unsigned int uniq; 310 } Jim_HashTable; 311 312 typedef struct Jim_HashTableIterator { 313 Jim_HashTable *ht; 314 Jim_HashEntry *entry, *nextEntry; 315 int index; 316 } Jim_HashTableIterator; 317 318 319 #define JIM_HT_INITIAL_SIZE 16 320 321 322 #define Jim_FreeEntryVal(ht, entry) \ 323 if ((ht)->type->valDestructor) \ 324 (ht)->type->valDestructor((ht)->privdata, (entry)->u.val) 325 326 #define Jim_SetHashVal(ht, entry, _val_) do { \ 327 if ((ht)->type->valDup) \ 328 (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \ 329 else \ 330 (entry)->u.val = (_val_); \ 331 } while(0) 332 333 #define Jim_SetHashIntVal(ht, entry, _val_) (entry)->u.intval = (_val_) 334 335 #define Jim_FreeEntryKey(ht, entry) \ 336 if ((ht)->type->keyDestructor) \ 337 (ht)->type->keyDestructor((ht)->privdata, (entry)->key) 338 339 #define Jim_SetHashKey(ht, entry, _key_) do { \ 340 if ((ht)->type->keyDup) \ 341 (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \ 342 else \ 343 (entry)->key = (void *)(_key_); \ 344 } while(0) 345 346 #define Jim_CompareHashKeys(ht, key1, key2) \ 347 (((ht)->type->keyCompare) ? \ 348 (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \ 349 (key1) == (key2)) 350 351 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq) 352 353 #define Jim_GetHashEntryKey(he) ((he)->key) 354 #define Jim_GetHashEntryVal(he) ((he)->u.val) 355 #define Jim_GetHashEntryIntVal(he) ((he)->u.intval) 356 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions) 357 #define Jim_GetHashTableSize(ht) ((ht)->size) 358 #define Jim_GetHashTableUsed(ht) ((ht)->used) 359 360 361 typedef struct Jim_Obj { 362 char *bytes; 363 const struct Jim_ObjType *typePtr; 364 int refCount; 365 int length; 366 367 union { 368 369 jim_wide wideValue; 370 371 int intValue; 372 373 double doubleValue; 374 375 void *ptr; 376 377 struct { 378 void *ptr1; 379 void *ptr2; 380 } twoPtrValue; 381 382 struct { 383 void *ptr; 384 int int1; 385 int int2; 386 } ptrIntValue; 387 388 struct { 389 struct Jim_VarVal *vv; 390 unsigned long callFrameId; 391 int global; 392 } varValue; 393 394 struct { 395 struct Jim_Obj *nsObj; 396 struct Jim_Cmd *cmdPtr; 397 unsigned long procEpoch; 398 } cmdValue; 399 400 struct { 401 struct Jim_Obj **ele; 402 int len; 403 int maxLen; 404 } listValue; 405 406 struct Jim_Dict *dictValue; 407 408 struct { 409 int maxLength; 410 int charLength; 411 } strValue; 412 413 struct { 414 unsigned long id; 415 struct Jim_Reference *refPtr; 416 } refValue; 417 418 struct { 419 struct Jim_Obj *fileNameObj; 420 int lineNumber; 421 } sourceValue; 422 423 struct { 424 struct Jim_Obj *varNameObjPtr; 425 struct Jim_Obj *indexObjPtr; 426 } dictSubstValue; 427 struct { 428 int line; 429 int argc; 430 } scriptLineValue; 431 } internalRep; 432 struct Jim_Obj *prevObjPtr; 433 struct Jim_Obj *nextObjPtr; 434 } Jim_Obj; 435 436 437 #define Jim_IncrRefCount(objPtr) \ 438 ++(objPtr)->refCount 439 #define Jim_DecrRefCount(interp, objPtr) \ 440 if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr) 441 #define Jim_IsShared(objPtr) \ 442 ((objPtr)->refCount > 1) 443 444 #define Jim_FreeNewObj Jim_FreeObj 445 446 447 #define Jim_FreeIntRep(i,o) \ 448 if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \ 449 (o)->typePtr->freeIntRepProc(i, o) 450 451 452 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr 453 454 455 #define Jim_SetIntRepPtr(o, p) \ 456 (o)->internalRep.ptr = (p) 457 458 459 struct Jim_Interp; 460 461 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp, 462 struct Jim_Obj *objPtr); 463 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp, 464 struct Jim_Obj *srcPtr, Jim_Obj *dupPtr); 465 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr); 466 467 typedef struct Jim_ObjType { 468 const char *name; 469 Jim_FreeInternalRepProc *freeIntRepProc; 470 Jim_DupInternalRepProc *dupIntRepProc; 471 Jim_UpdateStringProc *updateStringProc; 472 int flags; 473 } Jim_ObjType; 474 475 476 #define JIM_TYPE_NONE 0 477 #define JIM_TYPE_REFERENCES 1 478 479 480 481 typedef struct Jim_CallFrame { 482 unsigned long id; 483 int level; 484 struct Jim_HashTable vars; 485 struct Jim_HashTable *staticVars; 486 struct Jim_CallFrame *parent; 487 Jim_Obj *const *argv; 488 int argc; 489 Jim_Obj *procArgsObjPtr; 490 Jim_Obj *procBodyObjPtr; 491 struct Jim_CallFrame *next; 492 Jim_Obj *nsObj; 493 Jim_Obj *unused_fileNameObj; 494 int unused_line; 495 Jim_Stack *localCommands; 496 struct Jim_Obj *tailcallObj; 497 struct Jim_Cmd *tailcallCmd; 498 } Jim_CallFrame; 499 500 501 typedef struct Jim_EvalFrame { 502 Jim_CallFrame *framePtr; 503 int level; 504 int procLevel; 505 struct Jim_Cmd *cmd; 506 struct Jim_EvalFrame *parent; 507 Jim_Obj *const *argv; 508 int argc; 509 Jim_Obj *scriptObj; 510 } Jim_EvalFrame; 511 512 typedef struct Jim_VarVal { 513 Jim_Obj *objPtr; 514 struct Jim_CallFrame *linkFramePtr; 515 int refCount; 516 } Jim_VarVal; 517 518 519 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc, 520 Jim_Obj *const *argv); 521 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData); 522 523 typedef struct Jim_Dict { 524 struct JimDictHashEntry { 525 int offset; 526 unsigned hash; 527 } *ht; 528 unsigned int size; 529 unsigned int sizemask; 530 unsigned int uniq; 531 Jim_Obj **table; 532 int len; 533 int maxLen; 534 unsigned int dummy; 535 } Jim_Dict; 536 537 typedef struct Jim_Cmd { 538 int inUse; 539 int isproc; 540 struct Jim_Cmd *prevCmd; 541 Jim_Obj *cmdNameObj; 542 union { 543 struct { 544 545 Jim_CmdProc *cmdProc; 546 Jim_DelCmdProc *delProc; 547 void *privData; 548 } native; 549 struct { 550 551 Jim_Obj *argListObjPtr; 552 Jim_Obj *bodyObjPtr; 553 Jim_HashTable *staticVars; 554 int argListLen; 555 int reqArity; 556 int optArity; 557 int argsPos; 558 int upcall; 559 struct Jim_ProcArg { 560 Jim_Obj *nameObjPtr; 561 Jim_Obj *defaultObjPtr; 562 } *arglist; 563 Jim_Obj *nsObj; 564 } proc; 565 } u; 566 } Jim_Cmd; 567 568 569 typedef struct Jim_PrngState { 570 unsigned char sbox[256]; 571 unsigned int i, j; 572 } Jim_PrngState; 573 574 typedef struct Jim_Interp { 575 Jim_Obj *result; 576 int unused_errorLine; 577 Jim_Obj *currentFilenameObj; 578 int break_level; 579 int maxCallFrameDepth; 580 int maxEvalDepth; 581 int evalDepth; 582 int returnCode; 583 int returnLevel; 584 int exitCode; 585 long id; 586 int signal_level; 587 jim_wide sigmask; 588 int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask); 589 Jim_CallFrame *framePtr; 590 Jim_CallFrame *topFramePtr; 591 struct Jim_HashTable commands; 592 unsigned long procEpoch; /* Incremented every time the result 593 of procedures names lookup caching 594 may no longer be valid. */ 595 unsigned long callFrameEpoch; /* Incremented every time a new 596 callframe is created. This id is used for the 597 'ID' field contained in the Jim_CallFrame 598 structure. */ 599 int local; 600 int quitting; 601 int safeexpr; 602 Jim_Obj *liveList; 603 Jim_Obj *freeList; 604 Jim_Obj *unused_currentScriptObj; 605 Jim_EvalFrame topEvalFrame; 606 Jim_EvalFrame *evalFrame; 607 int procLevel; 608 Jim_Obj * const *unused_argv; 609 Jim_Obj *nullScriptObj; 610 Jim_Obj *emptyObj; 611 Jim_Obj *trueObj; 612 Jim_Obj *falseObj; 613 unsigned long referenceNextId; 614 struct Jim_HashTable references; 615 unsigned long lastCollectId; /* reference max Id of the last GC 616 execution. It's set to ~0 while the collection 617 is running as sentinel to avoid to recursive 618 calls via the [collect] command inside 619 finalizers. */ 620 jim_wide lastCollectTime; 621 Jim_Obj *stackTrace; 622 Jim_Obj *errorProc; 623 Jim_Obj *unknown; 624 Jim_Obj *defer; 625 Jim_Obj *traceCmdObj; 626 int unknown_called; 627 int errorFlag; 628 void *cmdPrivData; /* Used to pass the private data pointer to 629 a command. It is set to what the user specified 630 via Jim_CreateCommand(). */ 631 632 Jim_Cmd *oldCmdCache; 633 int oldCmdCacheSize; 634 struct Jim_CallFrame *freeFramesList; 635 struct Jim_HashTable assocData; 636 Jim_PrngState *prngState; 637 struct Jim_HashTable packages; 638 Jim_Stack *loadHandles; 639 } Jim_Interp; 640 641 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l)) 642 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval)) 643 644 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b) 645 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj) 646 #define Jim_GetResult(i) ((i)->result) 647 #define Jim_CmdPrivData(i) ((i)->cmdPrivData) 648 649 #define Jim_SetResult(i,o) do { \ 650 Jim_Obj *_resultObjPtr_ = (o); \ 651 Jim_IncrRefCount(_resultObjPtr_); \ 652 Jim_DecrRefCount(i,(i)->result); \ 653 (i)->result = _resultObjPtr_; \ 654 } while(0) 655 656 657 #define Jim_GetId(i) (++(i)->id) 658 659 660 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference 661 string representation must be fixed length. */ 662 typedef struct Jim_Reference { 663 Jim_Obj *objPtr; 664 Jim_Obj *finalizerCmdNamePtr; 665 char tag[JIM_REFERENCE_TAGLEN+1]; 666 } Jim_Reference; 667 668 669 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0) 670 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter) 671 672 #define JIM_EXPORT extern 673 674 675 676 JIM_EXPORT void *(*Jim_Allocator)(void *ptr, size_t size); 677 678 #define Jim_Free(P) Jim_Allocator((P), 0) 679 #define Jim_Realloc(P, S) Jim_Allocator((P), (S)) 680 #define Jim_Alloc(S) Jim_Allocator(NULL, (S)) 681 JIM_EXPORT char * Jim_StrDup (const char *s); 682 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l); 683 684 685 JIM_EXPORT char **Jim_GetEnviron(void); 686 JIM_EXPORT void Jim_SetEnviron(char **env); 687 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file); 688 #ifndef CLOCK_REALTIME 689 # define CLOCK_REALTIME 0 690 #endif 691 #ifndef CLOCK_MONOTONIC 692 # define CLOCK_MONOTONIC 1 693 #endif 694 #ifndef CLOCK_MONOTONIC_RAW 695 # define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC 696 #endif 697 JIM_EXPORT jim_wide Jim_GetTimeUsec(unsigned type); 698 699 700 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script); 701 702 703 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script); 704 705 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S)) 706 707 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script); 708 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename); 709 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename); 710 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr); 711 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc, 712 Jim_Obj *const *objv); 713 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj); 714 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, 715 int objc, Jim_Obj *const *objv); 716 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov)) 717 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj); 718 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, 719 Jim_Obj **resObjPtrPtr, int flags); 720 721 722 JIM_EXPORT Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, 723 int *lineptr); 724 725 JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, 726 Jim_Obj *fileNameObj, int lineNumber); 727 728 729 730 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack); 731 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack); 732 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack); 733 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element); 734 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack); 735 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack); 736 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr)); 737 738 739 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht, 740 const Jim_HashTableType *type, void *privdata); 741 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht, 742 unsigned int size); 743 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key, 744 void *val); 745 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht, 746 const void *key, void *val); 747 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht, 748 const void *key); 749 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht); 750 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht, 751 const void *key); 752 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator 753 (Jim_HashTable *ht); 754 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry 755 (Jim_HashTableIterator *iter); 756 757 758 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); 759 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); 760 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); 761 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp, 762 Jim_Obj *objPtr); 763 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, 764 int *lenPtr); 765 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr); 766 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr); 767 768 769 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp, 770 const char *s, int len); 771 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, 772 const char *s, int charlen); 773 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp, 774 char *s, int len); 775 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr, 776 const char *str, int len); 777 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr, 778 Jim_Obj *appendObjPtr); 779 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp, 780 Jim_Obj *objPtr, ...); 781 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr); 782 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr, 783 Jim_Obj *objPtr, int nocase); 784 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp, 785 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, 786 Jim_Obj *lastObjPtr); 787 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp, 788 Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv); 789 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr, 790 Jim_Obj *fmtObjPtr, int flags); 791 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp, 792 Jim_Obj *objPtr, const char *str); 793 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, 794 Jim_Obj *secondObjPtr, int nocase); 795 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr); 796 797 798 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp, 799 Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr); 800 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp, 801 Jim_Obj *objPtr); 802 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr); 803 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr); 804 805 806 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void); 807 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i); 808 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp); 809 JIM_EXPORT const char *Jim_ReturnCode(int code); 810 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...); 811 812 813 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp); 814 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp, 815 const char *cmdName, Jim_CmdProc *cmdProc, void *privData, 816 Jim_DelCmdProc *delProc); 817 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp, 818 Jim_Obj *cmdNameObj); 819 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp, 820 Jim_Obj *oldNameObj, Jim_Obj *newNameObj); 821 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp, 822 Jim_Obj *objPtr, int flags); 823 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp, 824 Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr); 825 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp, 826 const char *name, Jim_Obj *objPtr); 827 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp, 828 const char *name, Jim_Obj *objPtr); 829 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp, 830 const char *name, const char *val); 831 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp, 832 Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr, 833 Jim_CallFrame *targetCallFrame); 834 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp, 835 Jim_Obj *nameObjPtr); 836 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp, 837 Jim_Obj *nameObjPtr, int flags); 838 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp, 839 Jim_Obj *nameObjPtr, int flags); 840 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp, 841 const char *name, int flags); 842 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp, 843 const char *name, int flags); 844 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp, 845 Jim_Obj *nameObjPtr, int flags); 846 847 848 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, 849 Jim_Obj *levelObjPtr); 850 851 852 JIM_EXPORT int Jim_Collect (Jim_Interp *interp); 853 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp); 854 855 856 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr, 857 int *indexPtr); 858 859 860 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp, 861 Jim_Obj *const *elements, int len); 862 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp, 863 Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec); 864 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp, 865 Jim_Obj *listPtr, Jim_Obj *objPtr); 866 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp, 867 Jim_Obj *listPtr, Jim_Obj *appendListPtr); 868 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr); 869 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt, 870 int listindex, Jim_Obj **objPtrPtr, int seterr); 871 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx); 872 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp, 873 Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc, 874 Jim_Obj *newObjPtr); 875 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc, 876 Jim_Obj *const *objv); 877 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp, 878 Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen); 879 880 881 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp, 882 Jim_Obj *const *elements, int len); 883 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr, 884 Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags); 885 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp, 886 Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc, 887 Jim_Obj **objPtrPtr, int flags); 888 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp, 889 Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc, 890 Jim_Obj *newObjPtr, int flags); 891 JIM_EXPORT Jim_Obj **Jim_DictPairs(Jim_Interp *interp, 892 Jim_Obj *dictPtr, int *len); 893 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, 894 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr); 895 896 #define JIM_DICTMATCH_KEYS 0x0001 897 #define JIM_DICTMATCH_VALUES 0x002 898 899 JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types); 900 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr); 901 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr); 902 JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv); 903 904 905 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, 906 int *intPtr); 907 908 909 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, 910 Jim_Obj *exprObjPtr); 911 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp, 912 Jim_Obj *exprObjPtr, int *boolPtr); 913 914 915 JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, 916 int *booleanPtr); 917 918 919 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr, 920 jim_wide *widePtr); 921 JIM_EXPORT int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, 922 jim_wide *widePtr); 923 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr, 924 long *longPtr); 925 #define Jim_NewWideObj Jim_NewIntObj 926 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp, 927 jim_wide wideValue); 928 929 930 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, 931 double *doublePtr); 932 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, 933 double doubleValue); 934 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue); 935 936 937 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc, 938 Jim_Obj *const *argv, const char *msg); 939 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr, 940 const char * const *tablePtr, int *indexPtr, const char *name, int flags); 941 JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, 942 const char *const *tablePtr); 943 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp, 944 Jim_Obj *scriptObj, char *stateCharPtr); 945 946 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len); 947 948 949 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data); 950 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key); 951 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key, 952 Jim_InterpDeleteProc *delProc, void *data); 953 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key); 954 JIM_EXPORT int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version); 955 956 957 958 959 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp, 960 const char *name, const char *ver, int flags); 961 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp, 962 const char *name, int flags); 963 #define Jim_PackageProvideCheck(INTERP, NAME) \ 964 if (Jim_CheckAbiVersion(INTERP, JIM_ABI_VERSION) == JIM_ERR || Jim_PackageProvide(INTERP, NAME, "1.0", JIM_ERRMSG)) \ 965 return JIM_ERR 966 967 968 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp); 969 970 971 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp); 972 JIM_EXPORT void Jim_HistoryLoad(const char *filename); 973 JIM_EXPORT void Jim_HistorySave(const char *filename); 974 JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt); 975 JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj); 976 JIM_EXPORT void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj); 977 JIM_EXPORT void Jim_HistoryAdd(const char *line); 978 JIM_EXPORT void Jim_HistoryShow(void); 979 JIM_EXPORT void Jim_HistorySetMaxLen(int length); 980 JIM_EXPORT int Jim_HistoryGetMaxLen(void); 981 982 983 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); 984 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); 985 JIM_EXPORT int Jim_IsBigEndian(void); 986 987 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask) 988 JIM_EXPORT void Jim_SignalSetIgnored(jim_wide mask); 989 990 991 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); 992 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); 993 994 995 JIM_EXPORT int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command); 996 997 998 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr); 999 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr); 1000 1001 #ifdef __cplusplus 1002 } 1003 #endif 1004 1005 #endif 1006 1007 #ifndef JIM_SUBCMD_H 1008 #define JIM_SUBCMD_H 1009 1010 1011 #ifdef __cplusplus 1012 extern "C" { 1013 #endif 1014 1015 1016 #define JIM_MODFLAG_HIDDEN 0x0001 1017 #define JIM_MODFLAG_FULLARGV 0x0002 1018 1019 1020 1021 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv); 1022 1023 typedef struct { 1024 const char *cmd; 1025 const char *args; 1026 jim_subcmd_function *function; 1027 short minargs; 1028 short maxargs; 1029 unsigned short flags; 1030 } jim_subcmd_type; 1031 1032 #define JIM_DEF_SUBCMD(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs } 1033 #define JIM_DEF_SUBCMD_HIDDEN(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs, JIM_MODFLAG_HIDDEN } 1034 1035 const jim_subcmd_type * 1036 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); 1037 1038 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); 1039 1040 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv); 1041 1042 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type *ct, Jim_Obj *subcmd); 1043 1044 #ifdef __cplusplus 1045 } 1046 #endif 1047 1048 #endif 1049 #ifndef JIMREGEXP_H 1050 #define JIMREGEXP_H 1051 1052 1053 #ifdef __cplusplus 1054 extern "C" { 1055 #endif 1056 1057 #include <stdlib.h> 1058 1059 typedef struct { 1060 int rm_so; 1061 int rm_eo; 1062 } regmatch_t; 1063 1064 1065 typedef struct regexp { 1066 1067 int re_nsub; 1068 1069 1070 int cflags; 1071 int err; 1072 int regstart; 1073 int reganch; 1074 int regmust; 1075 int regmlen; 1076 int *program; 1077 1078 1079 const char *regparse; 1080 int p; 1081 int proglen; 1082 1083 1084 int eflags; 1085 const char *start; 1086 const char *reginput; 1087 const char *regbol; 1088 1089 1090 regmatch_t *pmatch; 1091 int nmatch; 1092 } regexp; 1093 1094 typedef regexp regex_t; 1095 1096 #define REG_EXTENDED 0 1097 #define REG_NEWLINE 1 1098 #define REG_ICASE 2 1099 1100 #define REG_NOTBOL 16 1101 1102 enum { 1103 REG_NOERROR, 1104 REG_NOMATCH, 1105 REG_BADPAT, 1106 REG_ERR_NULL_ARGUMENT, 1107 REG_ERR_UNKNOWN, 1108 REG_ERR_TOO_BIG, 1109 REG_ERR_NOMEM, 1110 REG_ERR_TOO_MANY_PAREN, 1111 REG_ERR_UNMATCHED_PAREN, 1112 REG_ERR_UNMATCHED_BRACES, 1113 REG_ERR_BAD_COUNT, 1114 REG_ERR_JUNK_ON_END, 1115 REG_ERR_OPERAND_COULD_BE_EMPTY, 1116 REG_ERR_NESTED_COUNT, 1117 REG_ERR_INTERNAL, 1118 REG_ERR_COUNT_FOLLOWS_NOTHING, 1119 REG_ERR_INVALID_ESCAPE, 1120 REG_ERR_CORRUPTED, 1121 REG_ERR_NULL_CHAR, 1122 REG_ERR_UNMATCHED_BRACKET, 1123 REG_ERR_NUM 1124 }; 1125 1126 int jim_regcomp(regex_t *preg, const char *regex, int cflags); 1127 int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags); 1128 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); 1129 void jim_regfree(regex_t *preg); 1130 1131 #ifdef __cplusplus 1132 } 1133 #endif 1134 1135 #endif 1136 #ifndef JIM_SIGNAL_H 1137 #define JIM_SIGNAL_H 1138 1139 #ifdef __cplusplus 1140 extern "C" { 1141 #endif 1142 1143 const char *Jim_SignalId(int sig); 1144 1145 #ifdef __cplusplus 1146 } 1147 #endif 1148 1149 #endif 1150 #ifndef JIMIOCOMPAT_H 1151 #define JIMIOCOMPAT_H 1152 1153 1154 #include <stdio.h> 1155 #include <errno.h> 1156 #include <sys/stat.h> 1157 1158 1159 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg); 1160 1161 int Jim_OpenForWrite(const char *filename, int append); 1162 1163 int Jim_OpenForRead(const char *filename); 1164 1165 #if defined(__MINGW32__) || defined(_WIN32) 1166 #ifndef STRICT 1167 #define STRICT 1168 #endif 1169 #define WIN32_LEAN_AND_MEAN 1170 #include <windows.h> 1171 #include <fcntl.h> 1172 #include <io.h> 1173 #include <process.h> 1174 1175 typedef HANDLE phandle_t; 1176 #define JIM_BAD_PHANDLE INVALID_HANDLE_VALUE 1177 1178 1179 #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0) 1180 #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff) 1181 #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0) 1182 #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff) 1183 #define WNOHANG 1 1184 1185 int Jim_Errno(void); 1186 1187 long waitpid(phandle_t phandle, int *status, int nohang); 1188 1189 phandle_t JimWaitPid(long processid, int *status, int nohang); 1190 1191 long JimProcessPid(phandle_t phandle); 1192 1193 #define HAVE_PIPE 1194 #define pipe(P) _pipe((P), 0, O_NOINHERIT) 1195 1196 typedef struct __stat64 jim_stat_t; 1197 #define Jim_Stat _stat64 1198 #define Jim_FileStat _fstat64 1199 #define Jim_Lseek _lseeki64 1200 #define O_TEXT _O_TEXT 1201 #define O_BINARY _O_BINARY 1202 #define Jim_SetMode _setmode 1203 #ifndef STDIN_FILENO 1204 #define STDIN_FILENO 0 1205 #endif 1206 1207 #else 1208 #if defined(HAVE_STAT64) 1209 typedef struct stat64 jim_stat_t; 1210 #define Jim_Stat stat64 1211 #if defined(HAVE_FSTAT64) 1212 #define Jim_FileStat fstat64 1213 #endif 1214 #if defined(HAVE_LSTAT64) 1215 #define Jim_LinkStat lstat64 1216 #endif 1217 #else 1218 typedef struct stat jim_stat_t; 1219 #define Jim_Stat stat 1220 #if defined(HAVE_FSTAT) 1221 #define Jim_FileStat fstat 1222 #endif 1223 #if defined(HAVE_LSTAT) 1224 #define Jim_LinkStat lstat 1225 #endif 1226 #endif 1227 #if defined(HAVE_LSEEK64) 1228 #define Jim_Lseek lseek64 1229 #else 1230 #define Jim_Lseek lseek 1231 #endif 1232 1233 #if defined(HAVE_UNISTD_H) 1234 #include <unistd.h> 1235 #include <fcntl.h> 1236 #include <sys/wait.h> 1237 1238 typedef int phandle_t; 1239 #define Jim_Errno() errno 1240 #define JIM_BAD_PHANDLE -1 1241 #define JimProcessPid(PIDTYPE) (PIDTYPE) 1242 #define JimWaitPid waitpid 1243 1244 #ifndef HAVE_EXECVPE 1245 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV) 1246 #endif 1247 #endif 1248 1249 #ifndef O_TEXT 1250 #define O_TEXT 0 1251 #endif 1252 1253 #endif 1254 1255 # ifndef MAXPATHLEN 1256 # ifdef PATH_MAX 1257 # define MAXPATHLEN PATH_MAX 1258 # else 1259 # define MAXPATHLEN JIM_PATH_LEN 1260 # endif 1261 # endif 1262 1263 1264 int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb); 1265 1266 #endif 1267 int Jim_bootstrapInit(Jim_Interp *interp) 1268 { 1269 if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG)) 1270 return JIM_ERR; 1271 1272 return Jim_EvalSource(interp, "bootstrap.tcl", 1, 1273 "\n" 1274 "proc package {cmd args} {\n" 1275 " if {$cmd eq \"require\"} {\n" 1276 " foreach path $::auto_path {\n" 1277 " lassign $args pkg\n" 1278 " set pkgpath $path/$pkg.tcl\n" 1279 " if {$path eq \".\"} {\n" 1280 " set pkgpath $pkg.tcl\n" 1281 " }\n" 1282 " if {[file exists $pkgpath]} {\n" 1283 " tailcall uplevel #0 [list source $pkgpath]\n" 1284 " }\n" 1285 " }\n" 1286 " }\n" 1287 "}\n" 1288 "set tcl_platform(bootstrap) 1\n" 1289 ); 1290 } 1291 int Jim_initjimshInit(Jim_Interp *interp) 1292 { 1293 if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG)) 1294 return JIM_ERR; 1295 1296 return Jim_EvalSource(interp, "initjimsh.tcl", 1, 1297 "\n" 1298 "\n" 1299 "\n" 1300 "proc _jimsh_init {} {\n" 1301 " rename _jimsh_init {}\n" 1302 " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n" 1303 "\n" 1304 "\n" 1305 " if {[exists jim::argv0]} {\n" 1306 " if {[string match \"*/*\" $jim::argv0]} {\n" 1307 " set jim::exe [file join [pwd] $jim::argv0]\n" 1308 " } else {\n" 1309 " set jim::argv0 [file tail $jim::argv0]\n" 1310 " set path [split [env PATH \"\"] $tcl_platform(pathSeparator)]\n" 1311 " if {$tcl_platform(platform) eq \"windows\"} {\n" 1312 "\n" 1313 " set path [lmap p [list \"\" {*}$path] { string map {\\\\ /} $p }]\n" 1314 " }\n" 1315 " foreach p $path {\n" 1316 " set exec [file join [pwd] $p $jim::argv0]\n" 1317 " if {[file executable $exec]} {\n" 1318 " set jim::exe $exec\n" 1319 " break\n" 1320 " }\n" 1321 " }\n" 1322 " }\n" 1323 " }\n" 1324 "\n" 1325 "\n" 1326 " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n" 1327 " if {[exists jim::exe]} {\n" 1328 " lappend p [file dirname $jim::exe]\n" 1329 " }\n" 1330 " lappend p {*}$auto_path\n" 1331 " set auto_path $p\n" 1332 "\n" 1333 " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n" 1334 " foreach src {.jimrc jimrc.tcl} {\n" 1335 " if {[file exists [env HOME]/$src]} {\n" 1336 " uplevel #0 source [env HOME]/$src\n" 1337 " break\n" 1338 " }\n" 1339 " }\n" 1340 " }\n" 1341 " return \"\"\n" 1342 "}\n" 1343 "\n" 1344 "if {$tcl_platform(platform) eq \"windows\"} {\n" 1345 " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n" 1346 "}\n" 1347 "\n" 1348 "\n" 1349 "set tcl::autocomplete_commands {array clock debug dict file history info namespace package signal socket string tcl::prefix zlib}\n" 1350 "\n" 1351 "\n" 1352 "\n" 1353 "proc tcl::autocomplete {prefix} {\n" 1354 " if {[set space [string first \" \" $prefix]] != -1} {\n" 1355 " set cmd [string range $prefix 0 $space-1]\n" 1356 " if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n" 1357 " set arg [string range $prefix $space+1 end]\n" 1358 "\n" 1359 " return [lmap p [$cmd -commands] {\n" 1360 " if {![string match \"${arg}*\" $p]} continue\n" 1361 " function \"$cmd $p\"\n" 1362 " }]\n" 1363 " }\n" 1364 " }\n" 1365 "\n" 1366 " if {[string match \"source *\" $prefix]} {\n" 1367 " set path [string range $prefix 7 end]\n" 1368 " return [lmap p [glob -nocomplain \"${path}*\"] {\n" 1369 " function \"source $p\"\n" 1370 " }]\n" 1371 " }\n" 1372 "\n" 1373 " return [lmap p [lsort [info commands $prefix*]] {\n" 1374 " if {[string match \"* *\" $p]} {\n" 1375 " continue\n" 1376 " }\n" 1377 " function $p\n" 1378 " }]\n" 1379 "}\n" 1380 "\n" 1381 "\n" 1382 "set tcl::stdhint_commands {array clock debug dict file history info namespace package signal string zlib}\n" 1383 "\n" 1384 "set tcl::stdhint_cols {\n" 1385 " none {0}\n" 1386 " black {30}\n" 1387 " red {31}\n" 1388 " green {32}\n" 1389 " yellow {33}\n" 1390 " blue {34}\n" 1391 " purple {35}\n" 1392 " cyan {36}\n" 1393 " normal {37}\n" 1394 " grey {30 1}\n" 1395 " gray {30 1}\n" 1396 " lred {31 1}\n" 1397 " lgreen {32 1}\n" 1398 " lyellow {33 1}\n" 1399 " lblue {34 1}\n" 1400 " lpurple {35 1}\n" 1401 " lcyan {36 1}\n" 1402 " white {37 1}\n" 1403 "}\n" 1404 "\n" 1405 "\n" 1406 "set tcl::stdhint_col $tcl::stdhint_cols(lcyan)\n" 1407 "\n" 1408 "\n" 1409 "proc tcl::stdhint {string} {\n" 1410 " set result \"\"\n" 1411 " if {[llength $string] >= 2} {\n" 1412 " lassign $string cmd arg\n" 1413 " if {$cmd in $::tcl::stdhint_commands || [info channel $cmd] ne \"\"} {\n" 1414 " catch {\n" 1415 " set help [$cmd -help $arg]\n" 1416 " if {[string match \"Usage: $cmd *\" $help]} {\n" 1417 " set n [llength $string]\n" 1418 " set subcmd [lindex $help $n]\n" 1419 " incr n\n" 1420 " set hint [join [lrange $help $n end]]\n" 1421 " set prefix \"\"\n" 1422 " if {![string match \"* \" $string]} {\n" 1423 " if {$n == 3 && $subcmd ne $arg} {\n" 1424 "\n" 1425 " set prefix \"[string range $subcmd [string length $arg] end] \"\n" 1426 " } else {\n" 1427 " set prefix \" \"\n" 1428 " }\n" 1429 " }\n" 1430 " set result [list $prefix$hint {*}$::tcl::stdhint_col]\n" 1431 " }\n" 1432 " }\n" 1433 " }\n" 1434 " }\n" 1435 " return $result\n" 1436 "}\n" 1437 "\n" 1438 "_jimsh_init\n" 1439 ); 1440 } 1441 int Jim_globInit(Jim_Interp *interp) 1442 { 1443 if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG)) 1444 return JIM_ERR; 1445 1446 return Jim_EvalSource(interp, "glob.tcl", 1, 1447 "\n" 1448 "\n" 1449 "\n" 1450 "\n" 1451 "\n" 1452 "\n" 1453 "\n" 1454 "package require readdir\n" 1455 "\n" 1456 "\n" 1457 "proc glob.globdir {dir pattern} {\n" 1458 " if {[file exists $dir/$pattern]} {\n" 1459 "\n" 1460 " return [list $pattern]\n" 1461 " }\n" 1462 "\n" 1463 " set result {}\n" 1464 " set files [readdir $dir]\n" 1465 " lappend files . ..\n" 1466 "\n" 1467 " foreach name $files {\n" 1468 " if {[string match $pattern $name]} {\n" 1469 "\n" 1470 " if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n" 1471 " continue\n" 1472 " }\n" 1473 " lappend result $name\n" 1474 " }\n" 1475 " }\n" 1476 "\n" 1477 " return $result\n" 1478 "}\n" 1479 "\n" 1480 "\n" 1481 "\n" 1482 "\n" 1483 "proc glob.explode {pattern} {\n" 1484 " set oldexp {}\n" 1485 " set newexp {\"\"}\n" 1486 "\n" 1487 " while 1 {\n" 1488 " set oldexp $newexp\n" 1489 " set newexp {}\n" 1490 " set ob [string first \\{ $pattern]\n" 1491 " set cb [string first \\} $pattern]\n" 1492 "\n" 1493 " if {$ob < $cb && $ob != -1} {\n" 1494 " set mid [string range $pattern 0 $ob-1]\n" 1495 " set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n" 1496 " if {$pattern eq \"\"} {\n" 1497 " error \"unmatched open brace in glob pattern\"\n" 1498 " }\n" 1499 " set pattern [string range $pattern 1 end]\n" 1500 "\n" 1501 " foreach subs $subexp {\n" 1502 " foreach sub [split $subs ,] {\n" 1503 " foreach old $oldexp {\n" 1504 " lappend newexp $old$mid$sub\n" 1505 " }\n" 1506 " }\n" 1507 " }\n" 1508 " } elseif {$cb != -1} {\n" 1509 " set suf [string range $pattern 0 $cb-1]\n" 1510 " set rest [string range $pattern $cb end]\n" 1511 " break\n" 1512 " } else {\n" 1513 " set suf $pattern\n" 1514 " set rest \"\"\n" 1515 " break\n" 1516 " }\n" 1517 " }\n" 1518 "\n" 1519 " foreach old $oldexp {\n" 1520 " lappend newexp $old$suf\n" 1521 " }\n" 1522 " list $rest {*}$newexp\n" 1523 "}\n" 1524 "\n" 1525 "\n" 1526 "\n" 1527 "proc glob.glob {base pattern} {\n" 1528 " set dir [file dirname $pattern]\n" 1529 " if {$pattern eq $dir || $pattern eq \"\"} {\n" 1530 " return [list [file join $base $dir] $pattern]\n" 1531 " } elseif {$pattern eq [file tail $pattern]} {\n" 1532 " set dir \"\"\n" 1533 " }\n" 1534 "\n" 1535 "\n" 1536 " set dirlist [glob.glob $base $dir]\n" 1537 " set pattern [file tail $pattern]\n" 1538 "\n" 1539 "\n" 1540 " set result {}\n" 1541 " foreach {realdir dir} $dirlist {\n" 1542 " if {![file isdir $realdir]} {\n" 1543 " continue\n" 1544 " }\n" 1545 " if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n" 1546 " append dir /\n" 1547 " }\n" 1548 " foreach name [glob.globdir $realdir $pattern] {\n" 1549 " lappend result [file join $realdir $name] $dir$name\n" 1550 " }\n" 1551 " }\n" 1552 " return $result\n" 1553 "}\n" 1554 "\n" 1555 "\n" 1556 "\n" 1557 "\n" 1558 "\n" 1559 "\n" 1560 "\n" 1561 "\n" 1562 "\n" 1563 "\n" 1564 "\n" 1565 "\n" 1566 "proc glob {args} {\n" 1567 " set nocomplain 0\n" 1568 " set base \"\"\n" 1569 " set tails 0\n" 1570 "\n" 1571 " set n 0\n" 1572 " foreach arg $args {\n" 1573 " if {[info exists param]} {\n" 1574 " set $param $arg\n" 1575 " unset param\n" 1576 " incr n\n" 1577 " continue\n" 1578 " }\n" 1579 " switch -glob -- $arg {\n" 1580 " -d* {\n" 1581 " set switch $arg\n" 1582 " set param base\n" 1583 " }\n" 1584 " -n* {\n" 1585 " set nocomplain 1\n" 1586 " }\n" 1587 " -ta* {\n" 1588 " set tails 1\n" 1589 " }\n" 1590 " -- {\n" 1591 " incr n\n" 1592 " break\n" 1593 " }\n" 1594 " -* {\n" 1595 " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n" 1596 " }\n" 1597 " * {\n" 1598 " break\n" 1599 " }\n" 1600 " }\n" 1601 " incr n\n" 1602 " }\n" 1603 " if {[info exists param]} {\n" 1604 " return -code error \"missing argument to \\\"$switch\\\"\"\n" 1605 " }\n" 1606 " if {[llength $args] <= $n} {\n" 1607 " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n" 1608 " }\n" 1609 "\n" 1610 " set args [lrange $args $n end]\n" 1611 "\n" 1612 " set result {}\n" 1613 " foreach pattern $args {\n" 1614 " set escpattern [string map {\n" 1615 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n" 1616 " } $pattern]\n" 1617 " set patexps [lassign [glob.explode $escpattern] rest]\n" 1618 " if {$rest ne \"\"} {\n" 1619 " return -code error \"unmatched close brace in glob pattern\"\n" 1620 " }\n" 1621 " foreach patexp $patexps {\n" 1622 " set patexp [string map {\n" 1623 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n" 1624 " } $patexp]\n" 1625 " foreach {realname name} [glob.glob $base $patexp] {\n" 1626 " incr n\n" 1627 " if {$tails} {\n" 1628 " lappend result $name\n" 1629 " } else {\n" 1630 " lappend result [file join $base $name]\n" 1631 " }\n" 1632 " }\n" 1633 " }\n" 1634 " }\n" 1635 "\n" 1636 " if {!$nocomplain && [llength $result] == 0} {\n" 1637 " set s $(([llength $args] > 1) ? \"s\" : \"\")\n" 1638 " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n" 1639 " }\n" 1640 "\n" 1641 " return $result\n" 1642 "}\n" 1643 ); 1644 } 1645 int Jim_stdlibInit(Jim_Interp *interp) 1646 { 1647 if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG)) 1648 return JIM_ERR; 1649 1650 return Jim_EvalSource(interp, "stdlib.tcl", 1, 1651 "\n" 1652 "\n" 1653 "if {![exists -command ref]} {\n" 1654 "\n" 1655 " proc ref {args} {{count 0}} {\n" 1656 " format %08x [incr count]\n" 1657 " }\n" 1658 "}\n" 1659 "\n" 1660 "\n" 1661 "proc lambda {arglist args} {\n" 1662 " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n" 1663 "}\n" 1664 "\n" 1665 "proc lambda.finalizer {name val} {\n" 1666 " rename $name {}\n" 1667 "}\n" 1668 "\n" 1669 "\n" 1670 "proc curry {args} {\n" 1671 " alias [ref {} function lambda.finalizer] {*}$args\n" 1672 "}\n" 1673 "\n" 1674 "\n" 1675 "\n" 1676 "\n" 1677 "\n" 1678 "\n" 1679 "\n" 1680 "\n" 1681 "\n" 1682 "proc function {value} {\n" 1683 " return $value\n" 1684 "}\n" 1685 "\n" 1686 "\n" 1687 "proc stackdump {stacktrace} {\n" 1688 " set lines {}\n" 1689 " lappend lines \"Traceback (most recent call last):\"\n" 1690 " foreach {cmd l f p} [lreverse $stacktrace] {\n" 1691 " set line {}\n" 1692 " if {$f ne \"\"} {\n" 1693 " append line \" File \\\"$f\\\", line $l\"\n" 1694 " }\n" 1695 " if {$p ne \"\"} {\n" 1696 " append line \", in $p\"\n" 1697 " }\n" 1698 " if {$line ne \"\"} {\n" 1699 " lappend lines $line\n" 1700 " if {$cmd ne \"\"} {\n" 1701 " set nl [string first \\n $cmd 1]\n" 1702 " if {$nl >= 0} {\n" 1703 " set cmd [string range $cmd 0 $nl-1]...\n" 1704 " }\n" 1705 " lappend lines \" $cmd\"\n" 1706 " }\n" 1707 " }\n" 1708 " }\n" 1709 " if {[llength $lines] > 1} {\n" 1710 " return [join $lines \\n]\n" 1711 " }\n" 1712 "}\n" 1713 "\n" 1714 "\n" 1715 "\n" 1716 "proc defer {script} {\n" 1717 " upvar jim::defer v\n" 1718 " lappend v $script\n" 1719 "}\n" 1720 "\n" 1721 "\n" 1722 "\n" 1723 "proc errorInfo {msg {stacktrace \"\"}} {\n" 1724 " if {$stacktrace eq \"\"} {\n" 1725 "\n" 1726 " set stacktrace [info stacktrace]\n" 1727 " }\n" 1728 " lassign $stacktrace p f l cmd\n" 1729 " if {$f ne \"\"} {\n" 1730 " set result \"$f:$l: Error: \"\n" 1731 " }\n" 1732 " append result \"$msg\\n\"\n" 1733 " append result [stackdump $stacktrace]\n" 1734 "\n" 1735 "\n" 1736 " string trim $result\n" 1737 "}\n" 1738 "\n" 1739 "\n" 1740 "\n" 1741 "proc {info nameofexecutable} {} {\n" 1742 " if {[exists ::jim::exe]} {\n" 1743 " return $::jim::exe\n" 1744 " }\n" 1745 "}\n" 1746 "\n" 1747 "\n" 1748 "proc {dict update} {&varName args script} {\n" 1749 " set keys {}\n" 1750 " foreach {n v} $args {\n" 1751 " upvar $v var_$v\n" 1752 " if {[dict exists $varName $n]} {\n" 1753 " set var_$v [dict get $varName $n]\n" 1754 " }\n" 1755 " }\n" 1756 " catch {uplevel 1 $script} msg opts\n" 1757 " if {[info exists varName]} {\n" 1758 " foreach {n v} $args {\n" 1759 " if {[info exists var_$v]} {\n" 1760 " dict set varName $n [set var_$v]\n" 1761 " } else {\n" 1762 " dict unset varName $n\n" 1763 " }\n" 1764 " }\n" 1765 " }\n" 1766 " return {*}$opts $msg\n" 1767 "}\n" 1768 "\n" 1769 "proc {dict replace} {dictionary {args {key value}}} {\n" 1770 " if {[llength ${key value}] % 2} {\n" 1771 " tailcall {dict replace}\n" 1772 " }\n" 1773 " tailcall dict merge $dictionary ${key value}\n" 1774 "}\n" 1775 "\n" 1776 "\n" 1777 "proc {dict lappend} {varName key {args value}} {\n" 1778 " upvar $varName dict\n" 1779 " if {[exists dict] && [dict exists $dict $key]} {\n" 1780 " set list [dict get $dict $key]\n" 1781 " }\n" 1782 " lappend list {*}$value\n" 1783 " dict set dict $key $list\n" 1784 "}\n" 1785 "\n" 1786 "\n" 1787 "proc {dict append} {varName key {args value}} {\n" 1788 " upvar $varName dict\n" 1789 " if {[exists dict] && [dict exists $dict $key]} {\n" 1790 " set str [dict get $dict $key]\n" 1791 " }\n" 1792 " append str {*}$value\n" 1793 " dict set dict $key $str\n" 1794 "}\n" 1795 "\n" 1796 "\n" 1797 "proc {dict incr} {varName key {increment 1}} {\n" 1798 " upvar $varName dict\n" 1799 " if {[exists dict] && [dict exists $dict $key]} {\n" 1800 " set value [dict get $dict $key]\n" 1801 " }\n" 1802 " incr value $increment\n" 1803 " dict set dict $key $value\n" 1804 "}\n" 1805 "\n" 1806 "\n" 1807 "proc {dict remove} {dictionary {args key}} {\n" 1808 " foreach k $key {\n" 1809 " dict unset dictionary $k\n" 1810 " }\n" 1811 " return $dictionary\n" 1812 "}\n" 1813 "\n" 1814 "\n" 1815 "proc {dict for} {vars dictionary script} {\n" 1816 " if {[llength $vars] != 2} {\n" 1817 " return -code error \"must have exactly two variable names\"\n" 1818 " }\n" 1819 " dict size $dictionary\n" 1820 " tailcall foreach $vars $dictionary $script\n" 1821 "}\n" 1822 ); 1823 } 1824 int Jim_tclcompatInit(Jim_Interp *interp) 1825 { 1826 if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG)) 1827 return JIM_ERR; 1828 1829 return Jim_EvalSource(interp, "tclcompat.tcl", 1, 1830 "\n" 1831 "\n" 1832 "\n" 1833 "\n" 1834 "\n" 1835 "\n" 1836 "\n" 1837 "\n" 1838 "set env [env]\n" 1839 "\n" 1840 "\n" 1841 "if {[exists -command stdout]} {\n" 1842 "\n" 1843 " foreach p {gets flush close eof seek tell} {\n" 1844 " proc $p {chan args} {p} {\n" 1845 " tailcall $chan $p {*}$args\n" 1846 " }\n" 1847 " }\n" 1848 " unset p\n" 1849 "\n" 1850 "\n" 1851 "\n" 1852 " proc puts {{-nonewline {}} {chan stdout} msg} {\n" 1853 " if {${-nonewline} ni {-nonewline {}}} {\n" 1854 " tailcall ${-nonewline} puts $msg\n" 1855 " }\n" 1856 " tailcall $chan puts {*}${-nonewline} $msg\n" 1857 " }\n" 1858 "\n" 1859 "\n" 1860 "\n" 1861 "\n" 1862 "\n" 1863 " proc read {{-nonewline {}} chan} {\n" 1864 " if {${-nonewline} ni {-nonewline {}}} {\n" 1865 " tailcall ${-nonewline} read {*}${chan}\n" 1866 " }\n" 1867 " tailcall $chan read {*}${-nonewline}\n" 1868 " }\n" 1869 "\n" 1870 " proc fconfigure {f args} {\n" 1871 " foreach {n v} $args {\n" 1872 " switch -glob -- $n {\n" 1873 " -bl* {\n" 1874 " $f ndelay $(!$v)\n" 1875 " }\n" 1876 " -bu* {\n" 1877 " $f buffering $v\n" 1878 " }\n" 1879 " -tr* {\n" 1880 " $f translation $v\n" 1881 " }\n" 1882 " default {\n" 1883 " return -code error \"fconfigure: unknown option $n\"\n" 1884 " }\n" 1885 " }\n" 1886 " }\n" 1887 " }\n" 1888 "}\n" 1889 "\n" 1890 "\n" 1891 "proc fileevent {args} {\n" 1892 " tailcall {*}$args\n" 1893 "}\n" 1894 "\n" 1895 "\n" 1896 "\n" 1897 "proc parray {arrayname {pattern *} {puts puts}} {\n" 1898 " upvar $arrayname a\n" 1899 "\n" 1900 " set max 0\n" 1901 " foreach name [array names a $pattern]] {\n" 1902 " if {[string length $name] > $max} {\n" 1903 " set max [string length $name]\n" 1904 " }\n" 1905 " }\n" 1906 " incr max [string length $arrayname]\n" 1907 " incr max 2\n" 1908 " foreach name [lsort [array names a $pattern]] {\n" 1909 " $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n" 1910 " }\n" 1911 "}\n" 1912 "\n" 1913 "\n" 1914 "proc {file copy} {{force {}} source target} {\n" 1915 " try {\n" 1916 " if {$force ni {{} -force}} {\n" 1917 " error \"bad option \\\"$force\\\": should be -force\"\n" 1918 " }\n" 1919 "\n" 1920 " set in [open $source rb]\n" 1921 "\n" 1922 " if {[file exists $target]} {\n" 1923 " if {$force eq \"\"} {\n" 1924 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n" 1925 " }\n" 1926 "\n" 1927 " if {$source eq $target} {\n" 1928 " return\n" 1929 " }\n" 1930 "\n" 1931 "\n" 1932 " file stat $source ss\n" 1933 " file stat $target ts\n" 1934 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n" 1935 " return\n" 1936 " }\n" 1937 " }\n" 1938 " set out [open $target wb]\n" 1939 " $in copyto $out\n" 1940 " $out close\n" 1941 " } on error {msg opts} {\n" 1942 " incr opts(-level)\n" 1943 " return {*}$opts $msg\n" 1944 " } finally {\n" 1945 " catch {$in close}\n" 1946 " }\n" 1947 "}\n" 1948 "\n" 1949 "\n" 1950 "\n" 1951 "proc popen {cmd {mode r}} {\n" 1952 " lassign [pipe] r w\n" 1953 " try {\n" 1954 " if {[string match \"w*\" $mode]} {\n" 1955 " lappend cmd <@$r &\n" 1956 " set pids [exec {*}$cmd]\n" 1957 " $r close\n" 1958 " set f $w\n" 1959 " } else {\n" 1960 " lappend cmd >@$w &\n" 1961 " set pids [exec {*}$cmd]\n" 1962 " $w close\n" 1963 " set f $r\n" 1964 " }\n" 1965 " lambda {cmd args} {f pids} {\n" 1966 " if {$cmd eq \"pid\"} {\n" 1967 " return $pids\n" 1968 " }\n" 1969 " if {$cmd eq \"close\"} {\n" 1970 " $f close\n" 1971 "\n" 1972 " set retopts {}\n" 1973 " foreach p $pids {\n" 1974 " lassign [wait $p] status - rc\n" 1975 " if {$status eq \"CHILDSTATUS\"} {\n" 1976 " if {$rc == 0} {\n" 1977 " continue\n" 1978 " }\n" 1979 " set msg \"child process exited abnormally\"\n" 1980 " } else {\n" 1981 " set msg \"child killed: received signal\"\n" 1982 " }\n" 1983 " set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n" 1984 " }\n" 1985 " return {*}$retopts\n" 1986 " }\n" 1987 " tailcall $f $cmd {*}$args\n" 1988 " }\n" 1989 " } on error {error opts} {\n" 1990 " $r close\n" 1991 " $w close\n" 1992 " error $error\n" 1993 " }\n" 1994 "}\n" 1995 "\n" 1996 "\n" 1997 "local proc pid {{channelId {}}} {\n" 1998 " if {$channelId eq \"\"} {\n" 1999 " tailcall upcall pid\n" 2000 " }\n" 2001 " if {[catch {$channelId tell}]} {\n" 2002 " return -code error \"can not find channel named \\\"$channelId\\\"\"\n" 2003 " }\n" 2004 " if {[catch {$channelId pid} pids]} {\n" 2005 " return \"\"\n" 2006 " }\n" 2007 " return $pids\n" 2008 "}\n" 2009 "\n" 2010 "\n" 2011 "\n" 2012 "proc throw {code {msg \"\"}} {\n" 2013 " return -code $code $msg\n" 2014 "}\n" 2015 "\n" 2016 "\n" 2017 "proc {file delete force} {path} {\n" 2018 " foreach e [readdir $path] {\n" 2019 " file delete -force $path/$e\n" 2020 " }\n" 2021 " file delete $path\n" 2022 "}\n" 2023 ); 2024 } 2025 2026 2027 #include <stdio.h> 2028 #include <string.h> 2029 #include <errno.h> 2030 #include <fcntl.h> 2031 #include <assert.h> 2032 #ifdef HAVE_UNISTD_H 2033 #include <unistd.h> 2034 #include <sys/stat.h> 2035 #endif 2036 #ifdef HAVE_UTIL_H 2037 #include <util.h> 2038 #endif 2039 #ifdef HAVE_PTY_H 2040 #include <pty.h> 2041 #endif 2042 2043 2044 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H) 2045 #include <sys/socket.h> 2046 #include <netinet/in.h> 2047 #include <netinet/tcp.h> 2048 #include <arpa/inet.h> 2049 #include <netdb.h> 2050 #ifdef HAVE_SYS_UN_H 2051 #include <sys/un.h> 2052 #endif 2053 #define HAVE_SOCKETS 2054 #elif defined (__MINGW32__) 2055 2056 #endif 2057 2058 #if defined(JIM_SSL) 2059 #include <openssl/ssl.h> 2060 #include <openssl/err.h> 2061 #endif 2062 2063 #ifdef HAVE_TERMIOS_H 2064 #endif 2065 2066 2067 #define AIO_CMD_LEN 32 2068 #define AIO_DEFAULT_RBUF_LEN 256 2069 #define AIO_DEFAULT_WBUF_LIMIT (64 * 1024) 2070 2071 #define AIO_KEEPOPEN 1 2072 #define AIO_NODELETE 2 2073 #define AIO_EOF 4 2074 #define AIO_WBUF_NONE 8 2075 #define AIO_NONBLOCK 16 2076 2077 #define AIO_ONEREAD 32 2078 2079 enum wbuftype { 2080 WBUF_OPT_NONE, 2081 WBUF_OPT_LINE, 2082 WBUF_OPT_FULL, 2083 }; 2084 2085 #if defined(JIM_IPV6) 2086 #define IPV6 1 2087 #else 2088 #define IPV6 0 2089 #ifndef PF_INET6 2090 #define PF_INET6 0 2091 #endif 2092 #endif 2093 #if defined(HAVE_SYS_UN_H) && defined(PF_UNIX) 2094 #define UNIX_SOCKETS 1 2095 #else 2096 #define UNIX_SOCKETS 0 2097 #endif 2098 2099 2100 2101 2102 static int JimReadableTimeout(int fd, long ms) 2103 { 2104 #ifdef HAVE_SELECT 2105 int retval; 2106 struct timeval tv; 2107 fd_set rfds; 2108 2109 FD_ZERO(&rfds); 2110 2111 FD_SET(fd, &rfds); 2112 tv.tv_sec = ms / 1000; 2113 tv.tv_usec = (ms % 1000) * 1000; 2114 2115 retval = select(fd + 1, &rfds, NULL, NULL, ms == 0 ? NULL : &tv); 2116 2117 if (retval > 0) { 2118 return JIM_OK; 2119 } 2120 return JIM_ERR; 2121 #else 2122 return JIM_OK; 2123 #endif 2124 } 2125 2126 2127 struct AioFile; 2128 2129 typedef struct { 2130 int (*writer)(struct AioFile *af, const char *buf, int len); 2131 int (*reader)(struct AioFile *af, char *buf, int len, int pending); 2132 int (*error)(const struct AioFile *af); 2133 const char *(*strerror)(struct AioFile *af); 2134 int (*verify)(struct AioFile *af); 2135 } JimAioFopsType; 2136 2137 typedef struct AioFile 2138 { 2139 Jim_Obj *filename; 2140 int wbuft; 2141 int flags; 2142 long timeout; 2143 int fd; 2144 int addr_family; 2145 void *ssl; 2146 const JimAioFopsType *fops; 2147 Jim_Obj *readbuf; 2148 Jim_Obj *writebuf; 2149 char *rbuf; 2150 size_t rbuf_len; 2151 size_t wbuf_limit; 2152 } AioFile; 2153 2154 static void aio_consume(Jim_Obj *objPtr, int n); 2155 2156 static int stdio_writer(struct AioFile *af, const char *buf, int len) 2157 { 2158 int ret = write(af->fd, buf, len); 2159 if (ret < 0 && errno == EPIPE) { 2160 aio_consume(af->writebuf, Jim_Length(af->writebuf)); 2161 } 2162 return ret; 2163 } 2164 2165 static int stdio_reader(struct AioFile *af, char *buf, int len, int nb) 2166 { 2167 if (nb || af->timeout == 0 || JimReadableTimeout(af->fd, af->timeout) == JIM_OK) { 2168 2169 int ret; 2170 2171 errno = 0; 2172 ret = read(af->fd, buf, len); 2173 if (ret <= 0 && errno != EAGAIN && errno != EINTR) { 2174 af->flags |= AIO_EOF; 2175 } 2176 return ret; 2177 } 2178 errno = ETIMEDOUT; 2179 return -1; 2180 } 2181 2182 static int stdio_error(const AioFile *af) 2183 { 2184 if (af->flags & AIO_EOF) { 2185 return JIM_OK; 2186 } 2187 2188 switch (errno) { 2189 case EAGAIN: 2190 case EINTR: 2191 case ETIMEDOUT: 2192 #ifdef ECONNRESET 2193 case ECONNRESET: 2194 #endif 2195 #ifdef ECONNABORTED 2196 case ECONNABORTED: 2197 #endif 2198 return JIM_OK; 2199 default: 2200 return JIM_ERR; 2201 } 2202 } 2203 2204 static const char *stdio_strerror(struct AioFile *af) 2205 { 2206 return strerror(errno); 2207 } 2208 2209 static const JimAioFopsType stdio_fops = { 2210 stdio_writer, 2211 stdio_reader, 2212 stdio_error, 2213 stdio_strerror, 2214 NULL, 2215 }; 2216 2217 2218 static void aio_set_nonblocking(AioFile *af, int nb) 2219 { 2220 #ifdef O_NDELAY 2221 int old = !!(af->flags & AIO_NONBLOCK); 2222 if (old != nb) { 2223 int fmode = fcntl(af->fd, F_GETFL); 2224 if (nb) { 2225 fmode |= O_NDELAY; 2226 af->flags |= AIO_NONBLOCK; 2227 } 2228 else { 2229 fmode &= ~O_NDELAY; 2230 af->flags &= ~AIO_NONBLOCK; 2231 } 2232 (void)fcntl(af->fd, F_SETFL, fmode); 2233 } 2234 #endif 2235 } 2236 2237 static int aio_start_nonblocking(AioFile *af) 2238 { 2239 int old = !!(af->flags & AIO_NONBLOCK); 2240 if (af->timeout) { 2241 aio_set_nonblocking(af, 1); 2242 } 2243 return old; 2244 } 2245 2246 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); 2247 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename, 2248 const char *hdlfmt, int family, int flags); 2249 2250 2251 static const char *JimAioErrorString(AioFile *af) 2252 { 2253 if (af && af->fops) 2254 return af->fops->strerror(af); 2255 2256 return strerror(errno); 2257 } 2258 2259 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name) 2260 { 2261 AioFile *af = Jim_CmdPrivData(interp); 2262 2263 if (name) { 2264 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af)); 2265 } 2266 else { 2267 Jim_SetResultString(interp, JimAioErrorString(af), -1); 2268 } 2269 } 2270 2271 static int aio_eof(AioFile *af) 2272 { 2273 return af->flags & AIO_EOF; 2274 } 2275 2276 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af) 2277 { 2278 int ret = 0; 2279 if (!aio_eof(af)) { 2280 ret = af->fops->error(af); 2281 if (ret) { 2282 JimAioSetError(interp, af->filename); 2283 } 2284 } 2285 return ret; 2286 } 2287 2288 static void aio_consume(Jim_Obj *objPtr, int n) 2289 { 2290 assert(objPtr->bytes); 2291 assert(n <= objPtr->length); 2292 2293 2294 memmove(objPtr->bytes, objPtr->bytes + n, objPtr->length - n + 1); 2295 objPtr->length -= n; 2296 } 2297 2298 2299 static int aio_flush(Jim_Interp *interp, AioFile *af); 2300 2301 #ifdef jim_ext_eventloop 2302 static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask) 2303 { 2304 AioFile *af = clientData; 2305 2306 aio_flush(interp, af); 2307 if (Jim_Length(af->writebuf) == 0) { 2308 2309 return -1; 2310 } 2311 return 0; 2312 } 2313 #endif 2314 2315 2316 static int aio_flush(Jim_Interp *interp, AioFile *af) 2317 { 2318 int len; 2319 const char *pt = Jim_GetString(af->writebuf, &len); 2320 if (len) { 2321 int ret = af->fops->writer(af, pt, len); 2322 if (ret > 0) { 2323 2324 aio_consume(af->writebuf, ret); 2325 } 2326 if (ret < 0) { 2327 return JimCheckStreamError(interp, af); 2328 } 2329 if (Jim_Length(af->writebuf)) { 2330 #ifdef jim_ext_eventloop 2331 void *handler = Jim_FindFileHandler(interp, af->fd, JIM_EVENT_WRITABLE); 2332 if (handler == NULL) { 2333 Jim_CreateFileHandler(interp, af->fd, JIM_EVENT_WRITABLE, aio_autoflush, af, NULL); 2334 return JIM_OK; 2335 } 2336 else if (handler == af) { 2337 2338 return JIM_OK; 2339 } 2340 #endif 2341 2342 Jim_SetResultString(interp, "send buffer is full", -1); 2343 return JIM_ERR; 2344 } 2345 } 2346 return JIM_OK; 2347 } 2348 2349 static int aio_read_len(Jim_Interp *interp, AioFile *af, unsigned flags, int neededLen) 2350 { 2351 if (!af->readbuf) { 2352 af->readbuf = Jim_NewStringObj(interp, NULL, 0); 2353 } 2354 2355 if (neededLen >= 0) { 2356 neededLen -= Jim_Length(af->readbuf); 2357 if (neededLen <= 0) { 2358 return JIM_OK; 2359 } 2360 } 2361 2362 while (neededLen && !aio_eof(af)) { 2363 int retval; 2364 int readlen; 2365 2366 if (neededLen == -1) { 2367 readlen = af->rbuf_len; 2368 } 2369 else { 2370 readlen = (neededLen > af->rbuf_len ? af->rbuf_len : neededLen); 2371 } 2372 2373 if (!af->rbuf) { 2374 af->rbuf = Jim_Alloc(af->rbuf_len); 2375 } 2376 retval = af->fops->reader(af, af->rbuf, readlen, flags & AIO_NONBLOCK); 2377 if (retval > 0) { 2378 if (retval) { 2379 Jim_AppendString(interp, af->readbuf, af->rbuf, retval); 2380 } 2381 if (neededLen != -1) { 2382 neededLen -= retval; 2383 } 2384 if (flags & AIO_ONEREAD) { 2385 return JIM_OK; 2386 } 2387 continue; 2388 } 2389 if ((flags & AIO_ONEREAD) || JimCheckStreamError(interp, af)) { 2390 return JIM_ERR; 2391 } 2392 break; 2393 } 2394 2395 return JIM_OK; 2396 } 2397 2398 static Jim_Obj *aio_read_consume(Jim_Interp *interp, AioFile *af, int neededLen) 2399 { 2400 Jim_Obj *objPtr = NULL; 2401 2402 if (neededLen < 0 || af->readbuf == NULL || Jim_Length(af->readbuf) <= neededLen) { 2403 objPtr = af->readbuf; 2404 af->readbuf = NULL; 2405 } 2406 else if (af->readbuf) { 2407 2408 int len; 2409 const char *pt = Jim_GetString(af->readbuf, &len); 2410 2411 objPtr = Jim_NewStringObj(interp, pt, neededLen); 2412 aio_consume(af->readbuf, neededLen); 2413 } 2414 2415 return objPtr; 2416 } 2417 2418 static void JimAioDelProc(Jim_Interp *interp, void *privData) 2419 { 2420 AioFile *af = privData; 2421 2422 JIM_NOTUSED(interp); 2423 2424 2425 aio_flush(interp, af); 2426 Jim_DecrRefCount(interp, af->writebuf); 2427 2428 #if UNIX_SOCKETS 2429 if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) { 2430 2431 Jim_Obj *filenameObj = aio_sockname(interp, af->fd); 2432 if (filenameObj) { 2433 if (Jim_Length(filenameObj)) { 2434 remove(Jim_String(filenameObj)); 2435 } 2436 Jim_FreeNewObj(interp, filenameObj); 2437 } 2438 } 2439 #endif 2440 2441 Jim_DecrRefCount(interp, af->filename); 2442 2443 #ifdef jim_ext_eventloop 2444 2445 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION); 2446 #endif 2447 2448 #if defined(JIM_SSL) 2449 if (af->ssl != NULL) { 2450 SSL_free(af->ssl); 2451 } 2452 #endif 2453 if (!(af->flags & AIO_KEEPOPEN)) { 2454 close(af->fd); 2455 } 2456 if (af->readbuf) { 2457 Jim_FreeNewObj(interp, af->readbuf); 2458 } 2459 2460 Jim_Free(af->rbuf); 2461 Jim_Free(af); 2462 } 2463 2464 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2465 { 2466 AioFile *af = Jim_CmdPrivData(interp); 2467 int nonewline = 0; 2468 jim_wide neededLen = -1; 2469 static const char * const options[] = { "-pending", "-nonewline", NULL }; 2470 enum { OPT_PENDING, OPT_NONEWLINE }; 2471 int option; 2472 int nb; 2473 Jim_Obj *objPtr; 2474 2475 if (argc) { 2476 if (*Jim_String(argv[0]) == '-') { 2477 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { 2478 return JIM_ERR; 2479 } 2480 switch (option) { 2481 case OPT_PENDING: 2482 2483 break; 2484 case OPT_NONEWLINE: 2485 nonewline++; 2486 break; 2487 } 2488 } 2489 else { 2490 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK) 2491 return JIM_ERR; 2492 if (neededLen < 0) { 2493 Jim_SetResultString(interp, "invalid parameter: negative len", -1); 2494 return JIM_ERR; 2495 } 2496 } 2497 argc--; 2498 argv++; 2499 } 2500 if (argc) { 2501 return -1; 2502 } 2503 2504 2505 nb = aio_start_nonblocking(af); 2506 2507 if (aio_read_len(interp, af, nb ? AIO_NONBLOCK : 0, neededLen) != JIM_OK) { 2508 aio_set_nonblocking(af, nb); 2509 return JIM_ERR; 2510 } 2511 objPtr = aio_read_consume(interp, af, neededLen); 2512 2513 aio_set_nonblocking(af, nb); 2514 2515 if (objPtr) { 2516 if (nonewline) { 2517 int len; 2518 const char *s = Jim_GetString(objPtr, &len); 2519 2520 if (len > 0 && s[len - 1] == '\n') { 2521 objPtr->length--; 2522 objPtr->bytes[objPtr->length] = '\0'; 2523 } 2524 } 2525 Jim_SetResult(interp, objPtr); 2526 } 2527 else { 2528 Jim_SetEmptyResult(interp); 2529 } 2530 return JIM_OK; 2531 } 2532 2533 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command) 2534 { 2535 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG); 2536 2537 2538 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) { 2539 return ((AioFile *) cmdPtr->u.native.privData)->fd; 2540 } 2541 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command); 2542 return -1; 2543 } 2544 2545 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2546 { 2547 AioFile *af = Jim_CmdPrivData(interp); 2548 2549 2550 aio_flush(interp, af); 2551 2552 Jim_SetResultInt(interp, af->fd); 2553 2554 return JIM_OK; 2555 } 2556 2557 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2558 { 2559 AioFile *af = Jim_CmdPrivData(interp); 2560 jim_wide count = 0; 2561 jim_wide maxlen = JIM_WIDE_MAX; 2562 int ok = 1; 2563 Jim_Obj *objv[4]; 2564 2565 if (argc == 2) { 2566 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) { 2567 return JIM_ERR; 2568 } 2569 } 2570 2571 objv[0] = argv[0]; 2572 objv[1] = Jim_NewStringObj(interp, "flush", -1); 2573 if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) { 2574 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", argv[0]); 2575 return JIM_ERR; 2576 } 2577 2578 2579 objv[0] = argv[0]; 2580 objv[1] = Jim_NewStringObj(interp, "puts", -1); 2581 objv[2] = Jim_NewStringObj(interp, "-nonewline", -1); 2582 Jim_IncrRefCount(objv[1]); 2583 Jim_IncrRefCount(objv[2]); 2584 2585 while (count < maxlen) { 2586 jim_wide len = maxlen - count; 2587 if (len > af->rbuf_len) { 2588 len = af->rbuf_len; 2589 } 2590 if (aio_read_len(interp, af, 0, len) != JIM_OK) { 2591 ok = 0; 2592 break; 2593 } 2594 objv[3] = aio_read_consume(interp, af, len); 2595 count += Jim_Length(objv[3]); 2596 if (Jim_EvalObjVector(interp, 4, objv) != JIM_OK) { 2597 ok = 0; 2598 break; 2599 } 2600 if (aio_eof(af)) { 2601 break; 2602 } 2603 if (count >= 16384 && af->rbuf_len < 65536) { 2604 2605 af->rbuf_len = 65536; 2606 af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len); 2607 } 2608 } 2609 2610 Jim_DecrRefCount(interp, objv[1]); 2611 Jim_DecrRefCount(interp, objv[2]); 2612 2613 if (!ok) { 2614 return JIM_ERR; 2615 } 2616 2617 Jim_SetResultInt(interp, count); 2618 2619 return JIM_OK; 2620 } 2621 2622 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2623 { 2624 AioFile *af = Jim_CmdPrivData(interp); 2625 Jim_Obj *objPtr = NULL; 2626 int len; 2627 int nb; 2628 unsigned flags = AIO_ONEREAD; 2629 char *nl = NULL; 2630 int offset = 0; 2631 2632 errno = 0; 2633 2634 2635 nb = aio_start_nonblocking(af); 2636 if (nb) { 2637 flags |= AIO_NONBLOCK; 2638 } 2639 2640 while (!aio_eof(af)) { 2641 if (af->readbuf) { 2642 const char *pt = Jim_GetString(af->readbuf, &len); 2643 nl = memchr(pt + offset, '\n', len - offset); 2644 if (nl) { 2645 2646 objPtr = Jim_NewStringObj(interp, pt, nl - pt); 2647 2648 aio_consume(af->readbuf, nl - pt + 1); 2649 break; 2650 } 2651 offset = len; 2652 } 2653 2654 2655 if (aio_read_len(interp, af, flags, -1) != JIM_OK) { 2656 break; 2657 } 2658 } 2659 2660 aio_set_nonblocking(af, nb); 2661 2662 if (!nl && aio_eof(af) && af->readbuf) { 2663 2664 objPtr = af->readbuf; 2665 af->readbuf = NULL; 2666 } 2667 else if (!objPtr) { 2668 objPtr = Jim_NewStringObj(interp, NULL, 0); 2669 } 2670 2671 if (argc) { 2672 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) { 2673 Jim_FreeNewObj(interp, objPtr); 2674 return JIM_ERR; 2675 } 2676 2677 len = Jim_Length(objPtr); 2678 2679 if (!nl && len == 0) { 2680 2681 len = -1; 2682 } 2683 Jim_SetResultInt(interp, len); 2684 } 2685 else { 2686 Jim_SetResult(interp, objPtr); 2687 } 2688 return JIM_OK; 2689 } 2690 2691 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2692 { 2693 AioFile *af = Jim_CmdPrivData(interp); 2694 int wlen; 2695 const char *wdata; 2696 Jim_Obj *strObj; 2697 int wnow = 0; 2698 int nl = 1; 2699 2700 if (argc == 2) { 2701 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) { 2702 return -1; 2703 } 2704 strObj = argv[1]; 2705 nl = 0; 2706 } 2707 else { 2708 strObj = argv[0]; 2709 } 2710 2711 #ifdef JIM_MAINTAINER 2712 if (Jim_IsShared(af->writebuf)) { 2713 Jim_DecrRefCount(interp, af->writebuf); 2714 af->writebuf = Jim_DuplicateObj(interp, af->writebuf); 2715 Jim_IncrRefCount(af->writebuf); 2716 } 2717 #endif 2718 Jim_AppendObj(interp, af->writebuf, strObj); 2719 if (nl) { 2720 Jim_AppendString(interp, af->writebuf, "\n", 1); 2721 } 2722 2723 2724 wdata = Jim_GetString(af->writebuf, &wlen); 2725 switch (af->wbuft) { 2726 case WBUF_OPT_NONE: 2727 2728 wnow = 1; 2729 break; 2730 2731 case WBUF_OPT_LINE: 2732 2733 if (nl || memchr(wdata, '\n', wlen) != NULL) { 2734 wnow = 1; 2735 } 2736 break; 2737 2738 case WBUF_OPT_FULL: 2739 if (wlen >= af->wbuf_limit) { 2740 wnow = 1; 2741 } 2742 break; 2743 } 2744 2745 if (wnow) { 2746 return aio_flush(interp, af); 2747 } 2748 return JIM_OK; 2749 } 2750 2751 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2752 { 2753 #ifdef HAVE_ISATTY 2754 AioFile *af = Jim_CmdPrivData(interp); 2755 Jim_SetResultInt(interp, isatty(af->fd)); 2756 #else 2757 Jim_SetResultInt(interp, 0); 2758 #endif 2759 2760 return JIM_OK; 2761 } 2762 2763 2764 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2765 { 2766 AioFile *af = Jim_CmdPrivData(interp); 2767 return aio_flush(interp, af); 2768 } 2769 2770 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2771 { 2772 AioFile *af = Jim_CmdPrivData(interp); 2773 2774 Jim_SetResultInt(interp, !!aio_eof(af)); 2775 return JIM_OK; 2776 } 2777 2778 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2779 { 2780 AioFile *af = Jim_CmdPrivData(interp); 2781 if (argc == 3) { 2782 int option = -1; 2783 #if defined(HAVE_SOCKETS) 2784 static const char * const options[] = { "r", "w", "-nodelete", NULL }; 2785 enum { OPT_R, OPT_W, OPT_NODELETE }; 2786 2787 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { 2788 return JIM_ERR; 2789 } 2790 #endif 2791 switch (option) { 2792 #if defined(HAVE_SHUTDOWN) 2793 case OPT_R: 2794 case OPT_W: 2795 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) { 2796 return JIM_OK; 2797 } 2798 JimAioSetError(interp, NULL); 2799 return JIM_ERR; 2800 #endif 2801 #if UNIX_SOCKETS 2802 case OPT_NODELETE: 2803 if (af->addr_family == PF_UNIX) { 2804 af->flags |= AIO_NODELETE; 2805 break; 2806 } 2807 2808 #endif 2809 default: 2810 Jim_SetResultString(interp, "not supported", -1); 2811 return JIM_ERR; 2812 } 2813 } 2814 2815 2816 af->flags &= ~AIO_KEEPOPEN; 2817 2818 return Jim_DeleteCommand(interp, argv[0]); 2819 } 2820 2821 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2822 { 2823 AioFile *af = Jim_CmdPrivData(interp); 2824 int orig = SEEK_SET; 2825 jim_wide offset; 2826 2827 if (argc == 2) { 2828 if (Jim_CompareStringImmediate(interp, argv[1], "start")) 2829 orig = SEEK_SET; 2830 else if (Jim_CompareStringImmediate(interp, argv[1], "current")) 2831 orig = SEEK_CUR; 2832 else if (Jim_CompareStringImmediate(interp, argv[1], "end")) 2833 orig = SEEK_END; 2834 else { 2835 return -1; 2836 } 2837 } 2838 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) { 2839 return JIM_ERR; 2840 } 2841 if (orig != SEEK_CUR || offset != 0) { 2842 2843 aio_flush(interp, af); 2844 } 2845 if (Jim_Lseek(af->fd, offset, orig) == -1) { 2846 JimAioSetError(interp, af->filename); 2847 return JIM_ERR; 2848 } 2849 if (af->readbuf) { 2850 Jim_FreeNewObj(interp, af->readbuf); 2851 af->readbuf = NULL; 2852 } 2853 af->flags &= ~AIO_EOF; 2854 return JIM_OK; 2855 } 2856 2857 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2858 { 2859 AioFile *af = Jim_CmdPrivData(interp); 2860 2861 Jim_SetResultInt(interp, Jim_Lseek(af->fd, 0, SEEK_CUR)); 2862 return JIM_OK; 2863 } 2864 2865 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2866 { 2867 AioFile *af = Jim_CmdPrivData(interp); 2868 2869 Jim_SetResult(interp, af->filename); 2870 return JIM_OK; 2871 } 2872 2873 #ifdef O_NDELAY 2874 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2875 { 2876 AioFile *af = Jim_CmdPrivData(interp); 2877 2878 if (argc) { 2879 long nb; 2880 2881 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) { 2882 return JIM_ERR; 2883 } 2884 aio_set_nonblocking(af, nb); 2885 } 2886 Jim_SetResultInt(interp, (af->flags & AIO_NONBLOCK) ? 1 : 0); 2887 return JIM_OK; 2888 } 2889 #endif 2890 2891 2892 #ifdef HAVE_FSYNC 2893 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2894 { 2895 AioFile *af = Jim_CmdPrivData(interp); 2896 2897 if (aio_flush(interp, af) != JIM_OK) { 2898 return JIM_ERR; 2899 } 2900 fsync(af->fd); 2901 return JIM_OK; 2902 } 2903 #endif 2904 2905 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2906 { 2907 AioFile *af = Jim_CmdPrivData(interp); 2908 Jim_Obj *resultObj; 2909 2910 static const char * const options[] = { 2911 "none", 2912 "line", 2913 "full", 2914 NULL 2915 }; 2916 2917 if (argc) { 2918 if (Jim_GetEnum(interp, argv[0], options, &af->wbuft, NULL, JIM_ERRMSG) != JIM_OK) { 2919 return JIM_ERR; 2920 } 2921 2922 if (af->wbuft == WBUF_OPT_FULL && argc == 2) { 2923 long l; 2924 if (Jim_GetLong(interp, argv[1], &l) != JIM_OK || l <= 0) { 2925 return JIM_ERR; 2926 } 2927 af->wbuf_limit = l; 2928 } 2929 2930 if (af->wbuft == WBUF_OPT_NONE) { 2931 if (aio_flush(interp, af) != JIM_OK) { 2932 return JIM_ERR; 2933 } 2934 } 2935 2936 } 2937 2938 resultObj = Jim_NewListObj(interp, NULL, 0); 2939 Jim_ListAppendElement(interp, resultObj, Jim_NewStringObj(interp, options[af->wbuft], -1)); 2940 if (af->wbuft == WBUF_OPT_FULL) { 2941 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, af->wbuf_limit)); 2942 } 2943 Jim_SetResult(interp, resultObj); 2944 2945 return JIM_OK; 2946 } 2947 2948 static int aio_cmd_translation(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2949 { 2950 enum {OPT_BINARY, OPT_TEXT}; 2951 static const char * const options[] = { 2952 "binary", 2953 "text", 2954 NULL 2955 }; 2956 int opt; 2957 2958 if (Jim_GetEnum(interp, argv[0], options, &opt, NULL, JIM_ERRMSG) != JIM_OK) { 2959 return JIM_ERR; 2960 } 2961 #if defined(Jim_SetMode) 2962 else { 2963 AioFile *af = Jim_CmdPrivData(interp); 2964 Jim_SetMode(af->fd, opt == OPT_BINARY ? O_BINARY : O_TEXT); 2965 } 2966 #endif 2967 return JIM_OK; 2968 } 2969 2970 static int aio_cmd_readsize(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2971 { 2972 AioFile *af = Jim_CmdPrivData(interp); 2973 2974 if (argc) { 2975 long l; 2976 if (Jim_GetLong(interp, argv[0], &l) != JIM_OK || l <= 0) { 2977 return JIM_ERR; 2978 } 2979 af->rbuf_len = l; 2980 if (af->rbuf) { 2981 af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len); 2982 } 2983 } 2984 Jim_SetResultInt(interp, af->rbuf_len); 2985 2986 return JIM_OK; 2987 } 2988 2989 #ifdef jim_ext_eventloop 2990 static int aio_cmd_timeout(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 2991 { 2992 #ifdef HAVE_SELECT 2993 AioFile *af = Jim_CmdPrivData(interp); 2994 if (argc == 1) { 2995 if (Jim_GetLong(interp, argv[0], &af->timeout) != JIM_OK) { 2996 return JIM_ERR; 2997 } 2998 } 2999 Jim_SetResultInt(interp, af->timeout); 3000 return JIM_OK; 3001 #else 3002 Jim_SetResultString(interp, "timeout not supported", -1); 3003 return JIM_ERR; 3004 #endif 3005 } 3006 3007 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, 3008 int argc, Jim_Obj * const *argv) 3009 { 3010 if (argc == 0) { 3011 3012 Jim_Obj *objPtr = Jim_FindFileHandler(interp, af->fd, mask); 3013 if (objPtr) { 3014 Jim_SetResult(interp, objPtr); 3015 } 3016 return JIM_OK; 3017 } 3018 3019 3020 Jim_DeleteFileHandler(interp, af->fd, mask); 3021 3022 3023 if (Jim_Length(argv[0])) { 3024 Jim_CreateScriptFileHandler(interp, af->fd, mask, argv[0]); 3025 } 3026 3027 return JIM_OK; 3028 } 3029 3030 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3031 { 3032 AioFile *af = Jim_CmdPrivData(interp); 3033 3034 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, argc, argv); 3035 } 3036 3037 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3038 { 3039 AioFile *af = Jim_CmdPrivData(interp); 3040 3041 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, argc, argv); 3042 } 3043 3044 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3045 { 3046 AioFile *af = Jim_CmdPrivData(interp); 3047 3048 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, argc, argv); 3049 } 3050 #endif 3051 3052 #if defined(jim_ext_file) && defined(Jim_FileStat) 3053 static int aio_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3054 { 3055 jim_stat_t sb; 3056 AioFile *af = Jim_CmdPrivData(interp); 3057 3058 if (Jim_FileStat(af->fd, &sb) == -1) { 3059 JimAioSetError(interp, NULL); 3060 return JIM_ERR; 3061 } 3062 return Jim_FileStoreStatData(interp, argc == 0 ? NULL : argv[0], &sb); 3063 } 3064 #endif 3065 3066 3067 3068 3069 static const jim_subcmd_type aio_command_table[] = { 3070 { "read", 3071 "?-nonewline|len?", 3072 aio_cmd_read, 3073 0, 3074 2, 3075 3076 }, 3077 { "copyto", 3078 "handle ?size?", 3079 aio_cmd_copy, 3080 1, 3081 2, 3082 3083 }, 3084 { "getfd", 3085 NULL, 3086 aio_cmd_getfd, 3087 0, 3088 0, 3089 3090 }, 3091 { "gets", 3092 "?var?", 3093 aio_cmd_gets, 3094 0, 3095 1, 3096 3097 }, 3098 { "puts", 3099 "?-nonewline? str", 3100 aio_cmd_puts, 3101 1, 3102 2, 3103 3104 }, 3105 { "isatty", 3106 NULL, 3107 aio_cmd_isatty, 3108 0, 3109 0, 3110 3111 }, 3112 { "flush", 3113 NULL, 3114 aio_cmd_flush, 3115 0, 3116 0, 3117 3118 }, 3119 { "eof", 3120 NULL, 3121 aio_cmd_eof, 3122 0, 3123 0, 3124 3125 }, 3126 { "close", 3127 "?r(ead)|w(rite)?", 3128 aio_cmd_close, 3129 0, 3130 1, 3131 JIM_MODFLAG_FULLARGV, 3132 3133 }, 3134 { "seek", 3135 "offset ?start|current|end", 3136 aio_cmd_seek, 3137 1, 3138 2, 3139 3140 }, 3141 { "tell", 3142 NULL, 3143 aio_cmd_tell, 3144 0, 3145 0, 3146 3147 }, 3148 { "filename", 3149 NULL, 3150 aio_cmd_filename, 3151 0, 3152 0, 3153 3154 }, 3155 #ifdef O_NDELAY 3156 { "ndelay", 3157 "?0|1?", 3158 aio_cmd_ndelay, 3159 0, 3160 1, 3161 3162 }, 3163 #endif 3164 #ifdef HAVE_FSYNC 3165 { "sync", 3166 NULL, 3167 aio_cmd_sync, 3168 0, 3169 0, 3170 3171 }, 3172 #endif 3173 { "buffering", 3174 "?none|line|full? ?size?", 3175 aio_cmd_buffering, 3176 0, 3177 2, 3178 3179 }, 3180 { "translation", 3181 "binary|text", 3182 aio_cmd_translation, 3183 1, 3184 1, 3185 3186 }, 3187 { "readsize", 3188 "?size?", 3189 aio_cmd_readsize, 3190 0, 3191 1, 3192 3193 }, 3194 #if defined(jim_ext_file) && defined(Jim_FileStat) 3195 { "stat", 3196 "?var?", 3197 aio_cmd_stat, 3198 0, 3199 1, 3200 3201 }, 3202 #endif 3203 #ifdef jim_ext_eventloop 3204 { "readable", 3205 "?readable-script?", 3206 aio_cmd_readable, 3207 0, 3208 1, 3209 3210 }, 3211 { "writable", 3212 "?writable-script?", 3213 aio_cmd_writable, 3214 0, 3215 1, 3216 3217 }, 3218 { "onexception", 3219 "?exception-script?", 3220 aio_cmd_onexception, 3221 0, 3222 1, 3223 3224 }, 3225 { "timeout", 3226 "?ms?", 3227 aio_cmd_timeout, 3228 0, 3229 1, 3230 3231 }, 3232 #endif 3233 { NULL } 3234 }; 3235 3236 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3237 { 3238 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv); 3239 } 3240 3241 static int parse_posix_open_mode(Jim_Interp *interp, Jim_Obj *modeObj) 3242 { 3243 int i; 3244 int flags = 0; 3245 #ifndef O_NOCTTY 3246 3247 #define O_NOCTTY 0 3248 #endif 3249 static const char * const modetypes[] = { 3250 "RDONLY", "WRONLY", "RDWR", "APPEND", "BINARY", "CREAT", "EXCL", "NOCTTY", "TRUNC", NULL 3251 }; 3252 static const int modeflags[] = { 3253 O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, 0, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC, 3254 }; 3255 3256 for (i = 0; i < Jim_ListLength(interp, modeObj); i++) { 3257 int opt; 3258 Jim_Obj *objPtr = Jim_ListGetIndex(interp, modeObj, i); 3259 if (Jim_GetEnum(interp, objPtr, modetypes, &opt, "access mode", JIM_ERRMSG) != JIM_OK) { 3260 return -1; 3261 } 3262 flags |= modeflags[opt]; 3263 } 3264 return flags; 3265 } 3266 3267 static int parse_open_mode(Jim_Interp *interp, Jim_Obj *filenameObj, Jim_Obj *modeObj) 3268 { 3269 3270 int flags; 3271 const char *mode = Jim_String(modeObj); 3272 if (*mode == 'R' || *mode == 'W') { 3273 return parse_posix_open_mode(interp, modeObj); 3274 } 3275 if (*mode == 'r') { 3276 flags = O_RDONLY; 3277 } 3278 else if (*mode == 'w') { 3279 flags = O_WRONLY | O_CREAT | O_TRUNC; 3280 } 3281 else if (*mode == 'a') { 3282 flags = O_WRONLY | O_CREAT | O_APPEND; 3283 } 3284 else { 3285 Jim_SetResultFormatted(interp, "%s: invalid open mode '%s'", Jim_String(filenameObj), mode); 3286 return -1; 3287 } 3288 mode++; 3289 3290 if (*mode == 'b') { 3291 #ifdef O_BINARY 3292 flags |= O_BINARY; 3293 #endif 3294 mode++; 3295 } 3296 3297 if (*mode == 't') { 3298 #ifdef O_TEXT 3299 flags |= O_TEXT; 3300 #endif 3301 mode++; 3302 } 3303 3304 if (*mode == '+') { 3305 mode++; 3306 3307 flags &= ~(O_RDONLY | O_WRONLY); 3308 flags |= O_RDWR; 3309 } 3310 3311 if (*mode == 'x') { 3312 mode++; 3313 #ifdef O_EXCL 3314 flags |= O_EXCL; 3315 #endif 3316 } 3317 3318 if (*mode == 'F') { 3319 mode++; 3320 #ifdef O_LARGEFILE 3321 flags |= O_LARGEFILE; 3322 #endif 3323 } 3324 3325 if (*mode == 'e') { 3326 3327 mode++; 3328 } 3329 return flags; 3330 } 3331 3332 static int JimAioOpenCommand(Jim_Interp *interp, int argc, 3333 Jim_Obj *const *argv) 3334 { 3335 int openflags; 3336 const char *filename; 3337 int fd = -1; 3338 int n = 0; 3339 int flags = 0; 3340 3341 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-noclose")) { 3342 flags = AIO_KEEPOPEN; 3343 n++; 3344 } 3345 if (argc < 2 || argc > 3 + n) { 3346 Jim_WrongNumArgs(interp, 1, argv, "filename ?-noclose? ?mode?"); 3347 return JIM_ERR; 3348 } 3349 3350 filename = Jim_String(argv[1]); 3351 3352 #ifdef jim_ext_tclcompat 3353 { 3354 3355 3356 if (*filename == '|') { 3357 Jim_Obj *evalObj[3]; 3358 int i = 0; 3359 3360 evalObj[i++] = Jim_NewStringObj(interp, "::popen", -1); 3361 evalObj[i++] = Jim_NewStringObj(interp, filename + 1, -1); 3362 if (argc == 3 + n) { 3363 evalObj[i++] = argv[2 + n]; 3364 } 3365 3366 return Jim_EvalObjVector(interp, i, evalObj); 3367 } 3368 } 3369 #endif 3370 if (argc == 3 + n) { 3371 openflags = parse_open_mode(interp, argv[1], argv[2 + n]); 3372 if (openflags == -1) { 3373 return JIM_ERR; 3374 } 3375 } 3376 else { 3377 openflags = O_RDONLY; 3378 } 3379 fd = open(filename, openflags, 0666); 3380 if (fd < 0) { 3381 JimAioSetError(interp, argv[1]); 3382 return JIM_ERR; 3383 } 3384 3385 return JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags) ? JIM_OK : JIM_ERR; 3386 } 3387 3388 3389 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename, 3390 const char *hdlfmt, int family, int flags) 3391 { 3392 AioFile *af; 3393 char buf[AIO_CMD_LEN]; 3394 Jim_Obj *cmdname; 3395 3396 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp)); 3397 cmdname = Jim_NewStringObj(interp, buf, -1); 3398 if (!filename) { 3399 filename = cmdname; 3400 } 3401 Jim_IncrRefCount(filename); 3402 3403 3404 af = Jim_Alloc(sizeof(*af)); 3405 memset(af, 0, sizeof(*af)); 3406 af->filename = filename; 3407 af->fd = fd; 3408 af->addr_family = family; 3409 af->fops = &stdio_fops; 3410 af->ssl = NULL; 3411 if (flags & AIO_WBUF_NONE) { 3412 af->wbuft = WBUF_OPT_NONE; 3413 } 3414 else { 3415 #ifdef HAVE_ISATTY 3416 af->wbuft = isatty(af->fd) ? WBUF_OPT_LINE : WBUF_OPT_FULL; 3417 #else 3418 af->wbuft = WBUF_OPT_FULL; 3419 #endif 3420 } 3421 3422 #ifdef FD_CLOEXEC 3423 if ((flags & AIO_KEEPOPEN) == 0) { 3424 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC); 3425 } 3426 #endif 3427 aio_set_nonblocking(af, !!(flags & AIO_NONBLOCK)); 3428 3429 af->flags |= flags; 3430 3431 af->writebuf = Jim_NewStringObj(interp, NULL, 0); 3432 Jim_IncrRefCount(af->writebuf); 3433 af->wbuf_limit = AIO_DEFAULT_WBUF_LIMIT; 3434 af->rbuf_len = AIO_DEFAULT_RBUF_LEN; 3435 3436 3437 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc); 3438 3439 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, cmdname)); 3440 3441 return af; 3442 } 3443 3444 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY) 3445 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename, 3446 const char *hdlfmt, int family, int flags) 3447 { 3448 if (JimMakeChannel(interp, p[0], filename, hdlfmt, family, flags)) { 3449 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); 3450 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); 3451 if (JimMakeChannel(interp, p[1], filename, hdlfmt, family, flags)) { 3452 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); 3453 Jim_SetResult(interp, objPtr); 3454 return JIM_OK; 3455 } 3456 } 3457 3458 3459 close(p[0]); 3460 close(p[1]); 3461 JimAioSetError(interp, NULL); 3462 return JIM_ERR; 3463 } 3464 #endif 3465 3466 #ifdef HAVE_PIPE 3467 static int JimCreatePipe(Jim_Interp *interp, Jim_Obj *filenameObj, int flags) 3468 { 3469 int p[2]; 3470 3471 if (pipe(p) != 0) { 3472 JimAioSetError(interp, NULL); 3473 return JIM_ERR; 3474 } 3475 3476 return JimMakeChannelPair(interp, p, filenameObj, "aio.pipe%ld", 0, flags); 3477 } 3478 3479 3480 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3481 { 3482 if (argc != 1) { 3483 Jim_WrongNumArgs(interp, 1, argv, ""); 3484 return JIM_ERR; 3485 } 3486 return JimCreatePipe(interp, argv[0], 0); 3487 } 3488 #endif 3489 3490 #ifdef HAVE_OPENPTY 3491 static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3492 { 3493 int p[2]; 3494 char path[MAXPATHLEN]; 3495 3496 if (argc != 1) { 3497 Jim_WrongNumArgs(interp, 1, argv, ""); 3498 return JIM_ERR; 3499 } 3500 3501 if (openpty(&p[0], &p[1], path, NULL, NULL) != 0) { 3502 JimAioSetError(interp, NULL); 3503 return JIM_ERR; 3504 } 3505 3506 3507 return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0); 3508 return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0); 3509 } 3510 #endif 3511 3512 3513 3514 int Jim_aioInit(Jim_Interp *interp) 3515 { 3516 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG)) 3517 return JIM_ERR; 3518 3519 #if defined(JIM_SSL) 3520 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL); 3521 #endif 3522 3523 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL); 3524 #ifdef HAVE_SOCKETS 3525 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL); 3526 #endif 3527 #ifdef HAVE_PIPE 3528 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL); 3529 #endif 3530 3531 3532 JimMakeChannel(interp, fileno(stdin), NULL, "stdin", 0, AIO_KEEPOPEN); 3533 JimMakeChannel(interp, fileno(stdout), NULL, "stdout", 0, AIO_KEEPOPEN); 3534 JimMakeChannel(interp, fileno(stderr), NULL, "stderr", 0, AIO_KEEPOPEN | AIO_WBUF_NONE); 3535 3536 return JIM_OK; 3537 } 3538 3539 #include <errno.h> 3540 #include <stdio.h> 3541 #include <string.h> 3542 3543 3544 #ifdef HAVE_DIRENT_H 3545 #include <dirent.h> 3546 #endif 3547 3548 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3549 { 3550 const char *dirPath; 3551 DIR *dirPtr; 3552 struct dirent *entryPtr; 3553 int nocomplain = 0; 3554 3555 if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) { 3556 nocomplain = 1; 3557 } 3558 if (argc != 2 && !nocomplain) { 3559 Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath"); 3560 return JIM_ERR; 3561 } 3562 3563 dirPath = Jim_String(argv[1 + nocomplain]); 3564 3565 dirPtr = opendir(dirPath); 3566 if (dirPtr == NULL) { 3567 if (nocomplain) { 3568 return JIM_OK; 3569 } 3570 Jim_SetResultString(interp, strerror(errno), -1); 3571 return JIM_ERR; 3572 } 3573 else { 3574 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 3575 3576 while ((entryPtr = readdir(dirPtr)) != NULL) { 3577 if (entryPtr->d_name[0] == '.') { 3578 if (entryPtr->d_name[1] == '\0') { 3579 continue; 3580 } 3581 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0')) 3582 continue; 3583 } 3584 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1)); 3585 } 3586 closedir(dirPtr); 3587 3588 Jim_SetResult(interp, listObj); 3589 3590 return JIM_OK; 3591 } 3592 } 3593 3594 int Jim_readdirInit(Jim_Interp *interp) 3595 { 3596 Jim_PackageProvideCheck(interp, "readdir"); 3597 Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL); 3598 return JIM_OK; 3599 } 3600 3601 #include <stdlib.h> 3602 #include <string.h> 3603 3604 #if defined(JIM_REGEXP) 3605 #else 3606 #include <regex.h> 3607 #define jim_regcomp regcomp 3608 #define jim_regexec regexec 3609 #define jim_regerror regerror 3610 #define jim_regfree regfree 3611 #endif 3612 3613 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 3614 { 3615 jim_regfree(objPtr->internalRep.ptrIntValue.ptr); 3616 Jim_Free(objPtr->internalRep.ptrIntValue.ptr); 3617 } 3618 3619 static const Jim_ObjType regexpObjType = { 3620 "regexp", 3621 FreeRegexpInternalRep, 3622 NULL, 3623 NULL, 3624 JIM_TYPE_NONE 3625 }; 3626 3627 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags) 3628 { 3629 regex_t *compre; 3630 const char *pattern; 3631 int ret; 3632 3633 3634 if (objPtr->typePtr == ®expObjType && 3635 objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) { 3636 3637 return objPtr->internalRep.ptrIntValue.ptr; 3638 } 3639 3640 3641 3642 3643 pattern = Jim_String(objPtr); 3644 compre = Jim_Alloc(sizeof(regex_t)); 3645 3646 if ((ret = jim_regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) { 3647 char buf[100]; 3648 3649 jim_regerror(ret, compre, buf, sizeof(buf)); 3650 Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf); 3651 jim_regfree(compre); 3652 Jim_Free(compre); 3653 return NULL; 3654 } 3655 3656 Jim_FreeIntRep(interp, objPtr); 3657 3658 objPtr->typePtr = ®expObjType; 3659 objPtr->internalRep.ptrIntValue.int1 = flags; 3660 objPtr->internalRep.ptrIntValue.ptr = compre; 3661 3662 return compre; 3663 } 3664 3665 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3666 { 3667 int opt_indices = 0; 3668 int opt_all = 0; 3669 int opt_inline = 0; 3670 regex_t *regex; 3671 int match, i, j; 3672 int offset = 0; 3673 regmatch_t *pmatch = NULL; 3674 int source_len; 3675 int result = JIM_OK; 3676 const char *pattern; 3677 const char *source_str; 3678 int num_matches = 0; 3679 int num_vars; 3680 Jim_Obj *resultListObj = NULL; 3681 int regcomp_flags = 0; 3682 int eflags = 0; 3683 int option; 3684 enum { 3685 OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END 3686 }; 3687 static const char * const options[] = { 3688 "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL 3689 }; 3690 3691 if (argc < 3) { 3692 wrongNumArgs: 3693 Jim_WrongNumArgs(interp, 1, argv, 3694 "?-switch ...? exp string ?matchVar? ?subMatchVar ...?"); 3695 return JIM_ERR; 3696 } 3697 3698 for (i = 1; i < argc; i++) { 3699 const char *opt = Jim_String(argv[i]); 3700 3701 if (*opt != '-') { 3702 break; 3703 } 3704 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 3705 return JIM_ERR; 3706 } 3707 if (option == OPT_END) { 3708 i++; 3709 break; 3710 } 3711 switch (option) { 3712 case OPT_INDICES: 3713 opt_indices = 1; 3714 break; 3715 3716 case OPT_NOCASE: 3717 regcomp_flags |= REG_ICASE; 3718 break; 3719 3720 case OPT_LINE: 3721 regcomp_flags |= REG_NEWLINE; 3722 break; 3723 3724 case OPT_ALL: 3725 opt_all = 1; 3726 break; 3727 3728 case OPT_INLINE: 3729 opt_inline = 1; 3730 break; 3731 3732 case OPT_START: 3733 if (++i == argc) { 3734 goto wrongNumArgs; 3735 } 3736 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) { 3737 return JIM_ERR; 3738 } 3739 break; 3740 } 3741 } 3742 if (argc - i < 2) { 3743 goto wrongNumArgs; 3744 } 3745 3746 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags); 3747 if (!regex) { 3748 return JIM_ERR; 3749 } 3750 3751 pattern = Jim_String(argv[i]); 3752 source_str = Jim_GetString(argv[i + 1], &source_len); 3753 3754 num_vars = argc - i - 2; 3755 3756 if (opt_inline) { 3757 if (num_vars) { 3758 Jim_SetResultString(interp, "regexp match variables not allowed when using -inline", 3759 -1); 3760 result = JIM_ERR; 3761 goto done; 3762 } 3763 num_vars = regex->re_nsub + 1; 3764 } 3765 3766 pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch)); 3767 3768 if (offset) { 3769 if (offset < 0) { 3770 offset += source_len + 1; 3771 } 3772 if (offset > source_len) { 3773 source_str += source_len; 3774 } 3775 else if (offset > 0) { 3776 source_str += utf8_index(source_str, offset); 3777 } 3778 eflags |= REG_NOTBOL; 3779 } 3780 3781 if (opt_inline) { 3782 resultListObj = Jim_NewListObj(interp, NULL, 0); 3783 } 3784 3785 next_match: 3786 match = jim_regexec(regex, source_str, num_vars + 1, pmatch, eflags); 3787 if (match >= REG_BADPAT) { 3788 char buf[100]; 3789 3790 jim_regerror(match, regex, buf, sizeof(buf)); 3791 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf); 3792 result = JIM_ERR; 3793 goto done; 3794 } 3795 3796 if (match == REG_NOMATCH) { 3797 goto done; 3798 } 3799 3800 num_matches++; 3801 3802 if (opt_all && !opt_inline) { 3803 3804 goto try_next_match; 3805 } 3806 3807 3808 j = 0; 3809 for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) { 3810 Jim_Obj *resultObj; 3811 3812 if (opt_indices) { 3813 resultObj = Jim_NewListObj(interp, NULL, 0); 3814 } 3815 else { 3816 resultObj = Jim_NewStringObj(interp, "", 0); 3817 } 3818 3819 if (pmatch[j].rm_so == -1) { 3820 if (opt_indices) { 3821 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1)); 3822 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1)); 3823 } 3824 } 3825 else { 3826 if (opt_indices) { 3827 3828 int so = utf8_strlen(source_str, pmatch[j].rm_so); 3829 int eo = utf8_strlen(source_str, pmatch[j].rm_eo); 3830 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + so)); 3831 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + eo - 1)); 3832 } 3833 else { 3834 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so); 3835 } 3836 } 3837 3838 if (opt_inline) { 3839 Jim_ListAppendElement(interp, resultListObj, resultObj); 3840 } 3841 else { 3842 3843 result = Jim_SetVariable(interp, argv[i], resultObj); 3844 3845 if (result != JIM_OK) { 3846 Jim_FreeObj(interp, resultObj); 3847 break; 3848 } 3849 } 3850 } 3851 3852 try_next_match: 3853 if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) { 3854 if (pmatch[0].rm_eo) { 3855 offset += utf8_strlen(source_str, pmatch[0].rm_eo); 3856 source_str += pmatch[0].rm_eo; 3857 } 3858 else { 3859 source_str++; 3860 offset++; 3861 } 3862 if (*source_str) { 3863 eflags = REG_NOTBOL; 3864 goto next_match; 3865 } 3866 } 3867 3868 done: 3869 if (result == JIM_OK) { 3870 if (opt_inline) { 3871 Jim_SetResult(interp, resultListObj); 3872 } 3873 else { 3874 Jim_SetResultInt(interp, num_matches); 3875 } 3876 } 3877 3878 Jim_Free(pmatch); 3879 return result; 3880 } 3881 3882 #define MAX_SUB_MATCHES 50 3883 3884 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 3885 { 3886 int regcomp_flags = 0; 3887 int regexec_flags = 0; 3888 int opt_all = 0; 3889 int opt_command = 0; 3890 int offset = 0; 3891 regex_t *regex; 3892 const char *p; 3893 int result = JIM_OK; 3894 regmatch_t pmatch[MAX_SUB_MATCHES + 1]; 3895 int num_matches = 0; 3896 3897 int i, j, n; 3898 Jim_Obj *varname; 3899 Jim_Obj *resultObj; 3900 Jim_Obj *cmd_prefix = NULL; 3901 Jim_Obj *regcomp_obj = NULL; 3902 const char *source_str; 3903 int source_len; 3904 const char *replace_str = NULL; 3905 int replace_len; 3906 const char *pattern; 3907 int option; 3908 enum { 3909 OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END 3910 }; 3911 static const char * const options[] = { 3912 "-nocase", "-line", "-all", "-start", "-command", "--", NULL 3913 }; 3914 3915 if (argc < 4) { 3916 wrongNumArgs: 3917 Jim_WrongNumArgs(interp, 1, argv, 3918 "?-switch ...? exp string subSpec ?varName?"); 3919 return JIM_ERR; 3920 } 3921 3922 for (i = 1; i < argc; i++) { 3923 const char *opt = Jim_String(argv[i]); 3924 3925 if (*opt != '-') { 3926 break; 3927 } 3928 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 3929 return JIM_ERR; 3930 } 3931 if (option == OPT_END) { 3932 i++; 3933 break; 3934 } 3935 switch (option) { 3936 case OPT_NOCASE: 3937 regcomp_flags |= REG_ICASE; 3938 break; 3939 3940 case OPT_LINE: 3941 regcomp_flags |= REG_NEWLINE; 3942 break; 3943 3944 case OPT_ALL: 3945 opt_all = 1; 3946 break; 3947 3948 case OPT_START: 3949 if (++i == argc) { 3950 goto wrongNumArgs; 3951 } 3952 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) { 3953 return JIM_ERR; 3954 } 3955 break; 3956 3957 case OPT_COMMAND: 3958 opt_command = 1; 3959 break; 3960 } 3961 } 3962 if (argc - i != 3 && argc - i != 4) { 3963 goto wrongNumArgs; 3964 } 3965 3966 3967 regcomp_obj = Jim_DuplicateObj(interp, argv[i]); 3968 Jim_IncrRefCount(regcomp_obj); 3969 regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags); 3970 if (!regex) { 3971 Jim_DecrRefCount(interp, regcomp_obj); 3972 return JIM_ERR; 3973 } 3974 pattern = Jim_String(argv[i]); 3975 3976 source_str = Jim_GetString(argv[i + 1], &source_len); 3977 if (opt_command) { 3978 cmd_prefix = argv[i + 2]; 3979 if (Jim_ListLength(interp, cmd_prefix) == 0) { 3980 Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1); 3981 Jim_DecrRefCount(interp, regcomp_obj); 3982 return JIM_ERR; 3983 } 3984 Jim_IncrRefCount(cmd_prefix); 3985 } 3986 else { 3987 replace_str = Jim_GetString(argv[i + 2], &replace_len); 3988 } 3989 varname = argv[i + 3]; 3990 3991 3992 resultObj = Jim_NewStringObj(interp, "", 0); 3993 3994 if (offset) { 3995 if (offset < 0) { 3996 offset += source_len + 1; 3997 } 3998 if (offset > source_len) { 3999 offset = source_len; 4000 } 4001 else if (offset < 0) { 4002 offset = 0; 4003 } 4004 } 4005 4006 offset = utf8_index(source_str, offset); 4007 4008 4009 Jim_AppendString(interp, resultObj, source_str, offset); 4010 4011 4012 n = source_len - offset; 4013 p = source_str + offset; 4014 do { 4015 int match = jim_regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags); 4016 4017 if (match >= REG_BADPAT) { 4018 char buf[100]; 4019 4020 jim_regerror(match, regex, buf, sizeof(buf)); 4021 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf); 4022 return JIM_ERR; 4023 } 4024 if (match == REG_NOMATCH) { 4025 break; 4026 } 4027 4028 num_matches++; 4029 4030 Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so); 4031 4032 if (opt_command) { 4033 4034 Jim_Obj *cmdListObj = Jim_DuplicateObj(interp, cmd_prefix); 4035 for (j = 0; j < MAX_SUB_MATCHES; j++) { 4036 if (pmatch[j].rm_so == -1) { 4037 break; 4038 } 4039 else { 4040 Jim_Obj *srcObj = Jim_NewStringObj(interp, p + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so); 4041 Jim_ListAppendElement(interp, cmdListObj, srcObj); 4042 } 4043 } 4044 Jim_IncrRefCount(cmdListObj); 4045 4046 result = Jim_EvalObj(interp, cmdListObj); 4047 Jim_DecrRefCount(interp, cmdListObj); 4048 if (result != JIM_OK) { 4049 goto cmd_error; 4050 } 4051 Jim_AppendString(interp, resultObj, Jim_String(Jim_GetResult(interp)), -1); 4052 } 4053 else { 4054 4055 for (j = 0; j < replace_len; j++) { 4056 int idx; 4057 int c = replace_str[j]; 4058 4059 if (c == '&') { 4060 idx = 0; 4061 } 4062 else if (c == '\\' && j < replace_len) { 4063 c = replace_str[++j]; 4064 if ((c >= '0') && (c <= '9')) { 4065 idx = c - '0'; 4066 } 4067 else if ((c == '\\') || (c == '&')) { 4068 Jim_AppendString(interp, resultObj, replace_str + j, 1); 4069 continue; 4070 } 4071 else { 4072 Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2); 4073 continue; 4074 } 4075 } 4076 else { 4077 Jim_AppendString(interp, resultObj, replace_str + j, 1); 4078 continue; 4079 } 4080 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) { 4081 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so, 4082 pmatch[idx].rm_eo - pmatch[idx].rm_so); 4083 } 4084 } 4085 } 4086 4087 p += pmatch[0].rm_eo; 4088 n -= pmatch[0].rm_eo; 4089 4090 4091 if (!opt_all || n == 0) { 4092 break; 4093 } 4094 4095 4096 if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') { 4097 break; 4098 } 4099 4100 4101 if (pattern[0] == '\0' && n) { 4102 4103 Jim_AppendString(interp, resultObj, p, 1); 4104 p++; 4105 n--; 4106 } 4107 4108 if (pmatch[0].rm_eo == pmatch[0].rm_so) { 4109 4110 regexec_flags = REG_NOTBOL; 4111 } 4112 else { 4113 regexec_flags = 0; 4114 } 4115 4116 } while (n); 4117 4118 Jim_AppendString(interp, resultObj, p, -1); 4119 4120 cmd_error: 4121 if (result == JIM_OK) { 4122 4123 if (argc - i == 4) { 4124 result = Jim_SetVariable(interp, varname, resultObj); 4125 4126 if (result == JIM_OK) { 4127 Jim_SetResultInt(interp, num_matches); 4128 } 4129 else { 4130 Jim_FreeObj(interp, resultObj); 4131 } 4132 } 4133 else { 4134 Jim_SetResult(interp, resultObj); 4135 result = JIM_OK; 4136 } 4137 } 4138 else { 4139 Jim_FreeObj(interp, resultObj); 4140 } 4141 4142 if (opt_command) { 4143 Jim_DecrRefCount(interp, cmd_prefix); 4144 } 4145 4146 Jim_DecrRefCount(interp, regcomp_obj); 4147 4148 return result; 4149 } 4150 4151 int Jim_regexpInit(Jim_Interp *interp) 4152 { 4153 Jim_PackageProvideCheck(interp, "regexp"); 4154 Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL); 4155 Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL); 4156 return JIM_OK; 4157 } 4158 4159 #include <limits.h> 4160 #include <stdlib.h> 4161 #include <string.h> 4162 #include <stdio.h> 4163 #include <errno.h> 4164 4165 4166 #ifdef HAVE_UTIMES 4167 #include <sys/time.h> 4168 #endif 4169 #ifdef HAVE_UNISTD_H 4170 #include <unistd.h> 4171 #elif defined(_MSC_VER) 4172 #include <direct.h> 4173 #define F_OK 0 4174 #define W_OK 2 4175 #define R_OK 4 4176 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 4177 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 4178 #endif 4179 4180 #if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER) 4181 #define ISWINDOWS 1 4182 4183 #undef HAVE_SYMLINK 4184 #else 4185 #define ISWINDOWS 0 4186 #endif 4187 4188 4189 #if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC) 4190 #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000) 4191 #elif defined(HAVE_STRUCT_STAT_ST_MTIM) 4192 #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000) 4193 #endif 4194 4195 4196 static void JimFixPath(char *path) 4197 { 4198 if (ISWINDOWS) { 4199 4200 char *p = path; 4201 while ((p = strchr(p, '\\')) != NULL) { 4202 *p++ = '/'; 4203 } 4204 } 4205 } 4206 4207 4208 static const char *JimGetFileType(int mode) 4209 { 4210 if (S_ISREG(mode)) { 4211 return "file"; 4212 } 4213 else if (S_ISDIR(mode)) { 4214 return "directory"; 4215 } 4216 #ifdef S_ISCHR 4217 else if (S_ISCHR(mode)) { 4218 return "characterSpecial"; 4219 } 4220 #endif 4221 #ifdef S_ISBLK 4222 else if (S_ISBLK(mode)) { 4223 return "blockSpecial"; 4224 } 4225 #endif 4226 #ifdef S_ISFIFO 4227 else if (S_ISFIFO(mode)) { 4228 return "fifo"; 4229 } 4230 #endif 4231 #ifdef S_ISLNK 4232 else if (S_ISLNK(mode)) { 4233 return "link"; 4234 } 4235 #endif 4236 #ifdef S_ISSOCK 4237 else if (S_ISSOCK(mode)) { 4238 return "socket"; 4239 } 4240 #endif 4241 return "unknown"; 4242 } 4243 4244 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value) 4245 { 4246 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1)); 4247 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value)); 4248 } 4249 4250 int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb) 4251 { 4252 4253 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 4254 4255 AppendStatElement(interp, listObj, "dev", sb->st_dev); 4256 AppendStatElement(interp, listObj, "ino", sb->st_ino); 4257 AppendStatElement(interp, listObj, "mode", sb->st_mode); 4258 AppendStatElement(interp, listObj, "nlink", sb->st_nlink); 4259 AppendStatElement(interp, listObj, "uid", sb->st_uid); 4260 AppendStatElement(interp, listObj, "gid", sb->st_gid); 4261 AppendStatElement(interp, listObj, "size", sb->st_size); 4262 AppendStatElement(interp, listObj, "atime", sb->st_atime); 4263 AppendStatElement(interp, listObj, "mtime", sb->st_mtime); 4264 AppendStatElement(interp, listObj, "ctime", sb->st_ctime); 4265 #ifdef STAT_MTIME_US 4266 AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb)); 4267 #endif 4268 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1)); 4269 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1)); 4270 4271 4272 if (varName) { 4273 Jim_Obj *objPtr; 4274 objPtr = Jim_GetVariable(interp, varName, JIM_NONE); 4275 4276 if (objPtr) { 4277 Jim_Obj *objv[2]; 4278 4279 objv[0] = objPtr; 4280 objv[1] = listObj; 4281 4282 objPtr = Jim_DictMerge(interp, 2, objv); 4283 if (objPtr == NULL) { 4284 4285 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName); 4286 Jim_FreeNewObj(interp, listObj); 4287 return JIM_ERR; 4288 } 4289 4290 Jim_InvalidateStringRep(objPtr); 4291 4292 Jim_FreeNewObj(interp, listObj); 4293 listObj = objPtr; 4294 } 4295 Jim_SetVariable(interp, varName, listObj); 4296 } 4297 4298 4299 Jim_SetResult(interp, listObj); 4300 4301 return JIM_OK; 4302 } 4303 4304 static int JimPathLenNoTrailingSlashes(const char *path, int len) 4305 { 4306 int i; 4307 for (i = len; i > 1 && path[i - 1] == '/'; i--) { 4308 4309 if (ISWINDOWS && path[i - 2] == ':') { 4310 4311 break; 4312 } 4313 } 4314 return i; 4315 } 4316 4317 static Jim_Obj *JimStripTrailingSlashes(Jim_Interp *interp, Jim_Obj *objPtr) 4318 { 4319 int len = Jim_Length(objPtr); 4320 const char *path = Jim_String(objPtr); 4321 int i = JimPathLenNoTrailingSlashes(path, len); 4322 if (i != len) { 4323 objPtr = Jim_NewStringObj(interp, path, i); 4324 } 4325 Jim_IncrRefCount(objPtr); 4326 return objPtr; 4327 } 4328 4329 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4330 { 4331 Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]); 4332 const char *path = Jim_String(objPtr); 4333 const char *p = strrchr(path, '/'); 4334 4335 if (!p) { 4336 Jim_SetResultString(interp, ".", -1); 4337 } 4338 else if (p[1] == 0) { 4339 4340 Jim_SetResult(interp, objPtr); 4341 } 4342 else if (p == path) { 4343 Jim_SetResultString(interp, "/", -1); 4344 } 4345 else if (ISWINDOWS && p[-1] == ':') { 4346 4347 Jim_SetResultString(interp, path, p - path + 1); 4348 } 4349 else { 4350 4351 int len = JimPathLenNoTrailingSlashes(path, p - path); 4352 Jim_SetResultString(interp, path, len); 4353 } 4354 Jim_DecrRefCount(interp, objPtr); 4355 return JIM_OK; 4356 } 4357 4358 static int file_cmd_split(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4359 { 4360 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 4361 const char *path = Jim_String(argv[0]); 4362 4363 if (*path == '/') { 4364 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "/", 1)); 4365 } 4366 4367 while (1) { 4368 4369 while (*path == '/') { 4370 path++; 4371 } 4372 if (*path) { 4373 const char *pt = strchr(path, '/'); 4374 if (pt) { 4375 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, pt - path)); 4376 path = pt; 4377 continue; 4378 } 4379 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, -1)); 4380 } 4381 break; 4382 } 4383 Jim_SetResult(interp, listObj); 4384 return JIM_OK; 4385 } 4386 4387 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4388 { 4389 const char *path = Jim_String(argv[0]); 4390 const char *lastSlash = strrchr(path, '/'); 4391 const char *p = strrchr(path, '.'); 4392 4393 if (p == NULL || (lastSlash != NULL && lastSlash > p)) { 4394 Jim_SetResult(interp, argv[0]); 4395 } 4396 else { 4397 Jim_SetResultString(interp, path, p - path); 4398 } 4399 return JIM_OK; 4400 } 4401 4402 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4403 { 4404 Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]); 4405 const char *path = Jim_String(objPtr); 4406 const char *lastSlash = strrchr(path, '/'); 4407 const char *p = strrchr(path, '.'); 4408 4409 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) { 4410 p = ""; 4411 } 4412 Jim_SetResultString(interp, p, -1); 4413 Jim_DecrRefCount(interp, objPtr); 4414 return JIM_OK; 4415 } 4416 4417 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4418 { 4419 Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]); 4420 const char *path = Jim_String(objPtr); 4421 const char *lastSlash = strrchr(path, '/'); 4422 4423 if (lastSlash) { 4424 Jim_SetResultString(interp, lastSlash + 1, -1); 4425 } 4426 else { 4427 Jim_SetResult(interp, objPtr); 4428 } 4429 Jim_DecrRefCount(interp, objPtr); 4430 return JIM_OK; 4431 } 4432 4433 #ifndef HAVE_RESTRICT 4434 #define restrict 4435 #endif 4436 4437 static char *JimRealPath(const char *restrict path, char *restrict resolved_path, size_t len) 4438 { 4439 #if defined(HAVE__FULLPATH) 4440 return _fullpath(resolved_path, path, len); 4441 #elif defined(HAVE_REALPATH) 4442 return realpath(path, resolved_path); 4443 #else 4444 return NULL; 4445 #endif 4446 } 4447 4448 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4449 { 4450 const char *path = Jim_String(argv[0]); 4451 char *newname = Jim_Alloc(MAXPATHLEN); 4452 4453 if (JimRealPath(path, newname, MAXPATHLEN)) { 4454 JimFixPath(newname); 4455 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1)); 4456 return JIM_OK; 4457 } 4458 Jim_Free(newname); 4459 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno)); 4460 return JIM_ERR; 4461 } 4462 4463 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4464 { 4465 int i; 4466 char *newname = Jim_Alloc(MAXPATHLEN + 1); 4467 char *last = newname; 4468 4469 *newname = 0; 4470 4471 4472 for (i = 0; i < argc; i++) { 4473 int len; 4474 const char *part = Jim_GetString(argv[i], &len); 4475 4476 if (*part == '/') { 4477 4478 last = newname; 4479 } 4480 else if (ISWINDOWS && strchr(part, ':')) { 4481 4482 last = newname; 4483 } 4484 else if (part[0] == '.') { 4485 if (part[1] == '/') { 4486 part += 2; 4487 len -= 2; 4488 } 4489 else if (part[1] == 0 && last != newname) { 4490 4491 continue; 4492 } 4493 } 4494 4495 4496 if (last != newname && last[-1] != '/') { 4497 *last++ = '/'; 4498 } 4499 4500 if (len) { 4501 if (last + len - newname >= MAXPATHLEN) { 4502 Jim_Free(newname); 4503 Jim_SetResultString(interp, "Path too long", -1); 4504 return JIM_ERR; 4505 } 4506 memcpy(last, part, len); 4507 last += len; 4508 } 4509 4510 4511 if (last > newname + 1 && last[-1] == '/') { 4512 4513 if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) { 4514 *--last = 0; 4515 } 4516 } 4517 } 4518 4519 *last = 0; 4520 4521 4522 4523 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname)); 4524 4525 return JIM_OK; 4526 } 4527 4528 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode) 4529 { 4530 Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1); 4531 4532 return JIM_OK; 4533 } 4534 4535 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4536 { 4537 return file_access(interp, argv[0], R_OK); 4538 } 4539 4540 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4541 { 4542 return file_access(interp, argv[0], W_OK); 4543 } 4544 4545 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4546 { 4547 #ifdef X_OK 4548 return file_access(interp, argv[0], X_OK); 4549 #else 4550 4551 Jim_SetResultBool(interp, 1); 4552 return JIM_OK; 4553 #endif 4554 } 4555 4556 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4557 { 4558 return file_access(interp, argv[0], F_OK); 4559 } 4560 4561 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4562 { 4563 int force = Jim_CompareStringImmediate(interp, argv[0], "-force"); 4564 4565 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) { 4566 argc--; 4567 argv++; 4568 } 4569 4570 while (argc--) { 4571 const char *path = Jim_String(argv[0]); 4572 4573 if (unlink(path) == -1 && errno != ENOENT) { 4574 if (rmdir(path) == -1) { 4575 4576 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) { 4577 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path, 4578 strerror(errno)); 4579 return JIM_ERR; 4580 } 4581 } 4582 } 4583 argv++; 4584 } 4585 return JIM_OK; 4586 } 4587 4588 #ifdef HAVE_MKDIR_ONE_ARG 4589 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME) 4590 #else 4591 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755) 4592 #endif 4593 4594 static int mkdir_all(char *path) 4595 { 4596 int ok = 1; 4597 4598 4599 goto first; 4600 4601 while (ok--) { 4602 4603 { 4604 char *slash = strrchr(path, '/'); 4605 4606 if (slash && slash != path) { 4607 *slash = 0; 4608 if (mkdir_all(path) != 0) { 4609 return -1; 4610 } 4611 *slash = '/'; 4612 } 4613 } 4614 first: 4615 if (MKDIR_DEFAULT(path) == 0) { 4616 return 0; 4617 } 4618 if (errno == ENOENT) { 4619 4620 continue; 4621 } 4622 4623 if (errno == EEXIST) { 4624 jim_stat_t sb; 4625 4626 if (Jim_Stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { 4627 return 0; 4628 } 4629 4630 errno = EEXIST; 4631 } 4632 4633 break; 4634 } 4635 return -1; 4636 } 4637 4638 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4639 { 4640 while (argc--) { 4641 char *path = Jim_StrDup(Jim_String(argv[0])); 4642 int rc = mkdir_all(path); 4643 4644 Jim_Free(path); 4645 if (rc != 0) { 4646 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0], 4647 strerror(errno)); 4648 return JIM_ERR; 4649 } 4650 argv++; 4651 } 4652 return JIM_OK; 4653 } 4654 4655 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4656 { 4657 int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0); 4658 4659 if (fd < 0) { 4660 return JIM_ERR; 4661 } 4662 close(fd); 4663 4664 return JIM_OK; 4665 } 4666 4667 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4668 { 4669 const char *source; 4670 const char *dest; 4671 int force = 0; 4672 4673 if (argc == 3) { 4674 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) { 4675 return -1; 4676 } 4677 force++; 4678 argv++; 4679 argc--; 4680 } 4681 4682 source = Jim_String(argv[0]); 4683 dest = Jim_String(argv[1]); 4684 4685 if (!force && access(dest, F_OK) == 0) { 4686 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0], 4687 argv[1]); 4688 return JIM_ERR; 4689 } 4690 #if ISWINDOWS 4691 if (access(dest, F_OK) == 0) { 4692 4693 remove(dest); 4694 } 4695 #endif 4696 if (rename(source, dest) != 0) { 4697 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1], 4698 strerror(errno)); 4699 return JIM_ERR; 4700 } 4701 4702 return JIM_OK; 4703 } 4704 4705 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK) 4706 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4707 { 4708 int ret; 4709 const char *source; 4710 const char *dest; 4711 static const char * const options[] = { "-hard", "-symbolic", NULL }; 4712 enum { OPT_HARD, OPT_SYMBOLIC, }; 4713 int option = OPT_HARD; 4714 4715 if (argc == 3) { 4716 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) { 4717 return JIM_ERR; 4718 } 4719 argv++; 4720 argc--; 4721 } 4722 4723 dest = Jim_String(argv[0]); 4724 source = Jim_String(argv[1]); 4725 4726 if (option == OPT_HARD) { 4727 ret = link(source, dest); 4728 } 4729 else { 4730 ret = symlink(source, dest); 4731 } 4732 4733 if (ret != 0) { 4734 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1], 4735 strerror(errno)); 4736 return JIM_ERR; 4737 } 4738 4739 return JIM_OK; 4740 } 4741 #endif 4742 4743 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb) 4744 { 4745 const char *path = Jim_String(filename); 4746 4747 if (Jim_Stat(path, sb) == -1) { 4748 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno)); 4749 return JIM_ERR; 4750 } 4751 return JIM_OK; 4752 } 4753 4754 #ifdef Jim_LinkStat 4755 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb) 4756 { 4757 const char *path = Jim_String(filename); 4758 4759 if (Jim_LinkStat(path, sb) == -1) { 4760 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno)); 4761 return JIM_ERR; 4762 } 4763 return JIM_OK; 4764 } 4765 #else 4766 #define file_lstat file_stat 4767 #endif 4768 4769 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4770 { 4771 jim_stat_t sb; 4772 4773 if (file_stat(interp, argv[0], &sb) != JIM_OK) { 4774 return JIM_ERR; 4775 } 4776 Jim_SetResultInt(interp, sb.st_atime); 4777 return JIM_OK; 4778 } 4779 4780 static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us) 4781 { 4782 #ifdef HAVE_UTIMES 4783 struct timeval times[2]; 4784 4785 times[1].tv_sec = times[0].tv_sec = us / 1000000; 4786 times[1].tv_usec = times[0].tv_usec = us % 1000000; 4787 4788 if (utimes(filename, times) != 0) { 4789 Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno)); 4790 return JIM_ERR; 4791 } 4792 return JIM_OK; 4793 #else 4794 Jim_SetResultString(interp, "Not implemented", -1); 4795 return JIM_ERR; 4796 #endif 4797 } 4798 4799 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4800 { 4801 jim_stat_t sb; 4802 4803 if (argc == 2) { 4804 jim_wide secs; 4805 if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) { 4806 return JIM_ERR; 4807 } 4808 return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000); 4809 } 4810 if (file_stat(interp, argv[0], &sb) != JIM_OK) { 4811 return JIM_ERR; 4812 } 4813 Jim_SetResultInt(interp, sb.st_mtime); 4814 return JIM_OK; 4815 } 4816 4817 #ifdef STAT_MTIME_US 4818 static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4819 { 4820 jim_stat_t sb; 4821 4822 if (argc == 2) { 4823 jim_wide us; 4824 if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) { 4825 return JIM_ERR; 4826 } 4827 return JimSetFileTimes(interp, Jim_String(argv[0]), us); 4828 } 4829 if (file_stat(interp, argv[0], &sb) != JIM_OK) { 4830 return JIM_ERR; 4831 } 4832 Jim_SetResultInt(interp, STAT_MTIME_US(sb)); 4833 return JIM_OK; 4834 } 4835 #endif 4836 4837 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4838 { 4839 return Jim_EvalPrefix(interp, "file copy", argc, argv); 4840 } 4841 4842 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4843 { 4844 jim_stat_t sb; 4845 4846 if (file_stat(interp, argv[0], &sb) != JIM_OK) { 4847 return JIM_ERR; 4848 } 4849 Jim_SetResultInt(interp, sb.st_size); 4850 return JIM_OK; 4851 } 4852 4853 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4854 { 4855 jim_stat_t sb; 4856 int ret = 0; 4857 4858 if (file_stat(interp, argv[0], &sb) == JIM_OK) { 4859 ret = S_ISDIR(sb.st_mode); 4860 } 4861 Jim_SetResultInt(interp, ret); 4862 return JIM_OK; 4863 } 4864 4865 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4866 { 4867 jim_stat_t sb; 4868 int ret = 0; 4869 4870 if (file_stat(interp, argv[0], &sb) == JIM_OK) { 4871 ret = S_ISREG(sb.st_mode); 4872 } 4873 Jim_SetResultInt(interp, ret); 4874 return JIM_OK; 4875 } 4876 4877 #ifdef HAVE_GETEUID 4878 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4879 { 4880 jim_stat_t sb; 4881 int ret = 0; 4882 4883 if (file_stat(interp, argv[0], &sb) == JIM_OK) { 4884 ret = (geteuid() == sb.st_uid); 4885 } 4886 Jim_SetResultInt(interp, ret); 4887 return JIM_OK; 4888 } 4889 #endif 4890 4891 #if defined(HAVE_READLINK) 4892 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4893 { 4894 const char *path = Jim_String(argv[0]); 4895 char *linkValue = Jim_Alloc(MAXPATHLEN + 1); 4896 4897 int linkLength = readlink(path, linkValue, MAXPATHLEN); 4898 4899 if (linkLength == -1) { 4900 Jim_Free(linkValue); 4901 Jim_SetResultFormatted(interp, "could not read link \"%#s\": %s", argv[0], strerror(errno)); 4902 return JIM_ERR; 4903 } 4904 linkValue[linkLength] = 0; 4905 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength)); 4906 return JIM_OK; 4907 } 4908 #endif 4909 4910 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4911 { 4912 jim_stat_t sb; 4913 4914 if (file_lstat(interp, argv[0], &sb) != JIM_OK) { 4915 return JIM_ERR; 4916 } 4917 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1); 4918 return JIM_OK; 4919 } 4920 4921 #ifdef Jim_LinkStat 4922 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4923 { 4924 jim_stat_t sb; 4925 4926 if (file_lstat(interp, argv[0], &sb) != JIM_OK) { 4927 return JIM_ERR; 4928 } 4929 return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb); 4930 } 4931 #else 4932 #define file_cmd_lstat file_cmd_stat 4933 #endif 4934 4935 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 4936 { 4937 jim_stat_t sb; 4938 4939 if (file_stat(interp, argv[0], &sb) != JIM_OK) { 4940 return JIM_ERR; 4941 } 4942 return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb); 4943 } 4944 4945 static const jim_subcmd_type file_command_table[] = { 4946 { "atime", 4947 "name", 4948 file_cmd_atime, 4949 1, 4950 1, 4951 4952 }, 4953 { "mtime", 4954 "name ?time?", 4955 file_cmd_mtime, 4956 1, 4957 2, 4958 4959 }, 4960 #ifdef STAT_MTIME_US 4961 { "mtimeus", 4962 "name ?time?", 4963 file_cmd_mtimeus, 4964 1, 4965 2, 4966 4967 }, 4968 #endif 4969 { "copy", 4970 "?-force? source dest", 4971 file_cmd_copy, 4972 2, 4973 3, 4974 4975 }, 4976 { "dirname", 4977 "name", 4978 file_cmd_dirname, 4979 1, 4980 1, 4981 4982 }, 4983 { "rootname", 4984 "name", 4985 file_cmd_rootname, 4986 1, 4987 1, 4988 4989 }, 4990 { "extension", 4991 "name", 4992 file_cmd_extension, 4993 1, 4994 1, 4995 4996 }, 4997 { "tail", 4998 "name", 4999 file_cmd_tail, 5000 1, 5001 1, 5002 5003 }, 5004 { "split", 5005 "name", 5006 file_cmd_split, 5007 1, 5008 1, 5009 5010 }, 5011 { "normalize", 5012 "name", 5013 file_cmd_normalize, 5014 1, 5015 1, 5016 5017 }, 5018 { "join", 5019 "name ?name ...?", 5020 file_cmd_join, 5021 1, 5022 -1, 5023 5024 }, 5025 { "readable", 5026 "name", 5027 file_cmd_readable, 5028 1, 5029 1, 5030 5031 }, 5032 { "writable", 5033 "name", 5034 file_cmd_writable, 5035 1, 5036 1, 5037 5038 }, 5039 { "executable", 5040 "name", 5041 file_cmd_executable, 5042 1, 5043 1, 5044 5045 }, 5046 { "exists", 5047 "name", 5048 file_cmd_exists, 5049 1, 5050 1, 5051 5052 }, 5053 { "delete", 5054 "?-force|--? name ...", 5055 file_cmd_delete, 5056 1, 5057 -1, 5058 5059 }, 5060 { "mkdir", 5061 "dir ...", 5062 file_cmd_mkdir, 5063 1, 5064 -1, 5065 5066 }, 5067 { "tempfile", 5068 "?template?", 5069 file_cmd_tempfile, 5070 0, 5071 1, 5072 5073 }, 5074 { "rename", 5075 "?-force? source dest", 5076 file_cmd_rename, 5077 2, 5078 3, 5079 5080 }, 5081 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK) 5082 { "link", 5083 "?-symbolic|-hard? newname target", 5084 file_cmd_link, 5085 2, 5086 3, 5087 5088 }, 5089 #endif 5090 #if defined(HAVE_READLINK) 5091 { "readlink", 5092 "name", 5093 file_cmd_readlink, 5094 1, 5095 1, 5096 5097 }, 5098 #endif 5099 { "size", 5100 "name", 5101 file_cmd_size, 5102 1, 5103 1, 5104 5105 }, 5106 { "stat", 5107 "name ?var?", 5108 file_cmd_stat, 5109 1, 5110 2, 5111 5112 }, 5113 { "lstat", 5114 "name ?var?", 5115 file_cmd_lstat, 5116 1, 5117 2, 5118 5119 }, 5120 { "type", 5121 "name", 5122 file_cmd_type, 5123 1, 5124 1, 5125 5126 }, 5127 #ifdef HAVE_GETEUID 5128 { "owned", 5129 "name", 5130 file_cmd_owned, 5131 1, 5132 1, 5133 5134 }, 5135 #endif 5136 { "isdirectory", 5137 "name", 5138 file_cmd_isdirectory, 5139 1, 5140 1, 5141 5142 }, 5143 { "isfile", 5144 "name", 5145 file_cmd_isfile, 5146 1, 5147 1, 5148 5149 }, 5150 { 5151 NULL 5152 } 5153 }; 5154 5155 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 5156 { 5157 const char *path; 5158 5159 if (argc != 2) { 5160 Jim_WrongNumArgs(interp, 1, argv, "dirname"); 5161 return JIM_ERR; 5162 } 5163 5164 path = Jim_String(argv[1]); 5165 5166 if (chdir(path) != 0) { 5167 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path, 5168 strerror(errno)); 5169 return JIM_ERR; 5170 } 5171 return JIM_OK; 5172 } 5173 5174 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 5175 { 5176 char *cwd = Jim_Alloc(MAXPATHLEN); 5177 5178 if (getcwd(cwd, MAXPATHLEN) == NULL) { 5179 Jim_SetResultString(interp, "Failed to get pwd", -1); 5180 Jim_Free(cwd); 5181 return JIM_ERR; 5182 } 5183 JimFixPath(cwd); 5184 Jim_SetResultString(interp, cwd, -1); 5185 5186 Jim_Free(cwd); 5187 return JIM_OK; 5188 } 5189 5190 int Jim_fileInit(Jim_Interp *interp) 5191 { 5192 Jim_PackageProvideCheck(interp, "file"); 5193 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL); 5194 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL); 5195 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL); 5196 return JIM_OK; 5197 } 5198 5199 #include <string.h> 5200 #include <ctype.h> 5201 5202 5203 #if (!(defined(HAVE_VFORK) || defined(HAVE_FORK)) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__) 5204 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 5205 { 5206 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp); 5207 int i, j; 5208 int rc; 5209 5210 5211 for (i = 1; i < argc; i++) { 5212 int len; 5213 const char *arg = Jim_GetString(argv[i], &len); 5214 5215 if (i > 1) { 5216 Jim_AppendString(interp, cmdlineObj, " ", 1); 5217 } 5218 if (strpbrk(arg, "\\\" ") == NULL) { 5219 5220 Jim_AppendString(interp, cmdlineObj, arg, len); 5221 continue; 5222 } 5223 5224 Jim_AppendString(interp, cmdlineObj, "\"", 1); 5225 for (j = 0; j < len; j++) { 5226 if (arg[j] == '\\' || arg[j] == '"') { 5227 Jim_AppendString(interp, cmdlineObj, "\\", 1); 5228 } 5229 Jim_AppendString(interp, cmdlineObj, &arg[j], 1); 5230 } 5231 Jim_AppendString(interp, cmdlineObj, "\"", 1); 5232 } 5233 rc = system(Jim_String(cmdlineObj)); 5234 5235 Jim_FreeNewObj(interp, cmdlineObj); 5236 5237 if (rc) { 5238 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); 5239 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); 5240 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0)); 5241 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc)); 5242 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); 5243 return JIM_ERR; 5244 } 5245 5246 return JIM_OK; 5247 } 5248 5249 int Jim_execInit(Jim_Interp *interp) 5250 { 5251 Jim_PackageProvideCheck(interp, "exec"); 5252 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL); 5253 return JIM_OK; 5254 } 5255 #else 5256 5257 5258 #include <errno.h> 5259 #include <signal.h> 5260 #include <sys/stat.h> 5261 5262 struct WaitInfoTable; 5263 5264 static char **JimOriginalEnviron(void); 5265 static char **JimSaveEnv(char **env); 5266 static void JimRestoreEnv(char **env); 5267 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, 5268 phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr); 5269 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr); 5270 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj); 5271 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv); 5272 5273 #if defined(__MINGW32__) 5274 static phandle_t JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId); 5275 #endif 5276 5277 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr) 5278 { 5279 int len; 5280 const char *s = Jim_GetString(objPtr, &len); 5281 5282 if (len > 0 && s[len - 1] == '\n') { 5283 objPtr->length--; 5284 objPtr->bytes[objPtr->length] = '\0'; 5285 } 5286 } 5287 5288 static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj) 5289 { 5290 char buf[256]; 5291 int ret = 0; 5292 5293 while (1) { 5294 int retval = read(fd, buf, sizeof(buf)); 5295 if (retval > 0) { 5296 ret = 1; 5297 Jim_AppendString(interp, strObj, buf, retval); 5298 } 5299 if (retval <= 0) { 5300 break; 5301 } 5302 } 5303 close(fd); 5304 return ret; 5305 } 5306 5307 static char **JimBuildEnv(Jim_Interp *interp) 5308 { 5309 int i; 5310 int size; 5311 int num; 5312 int n; 5313 char **envptr; 5314 char *envdata; 5315 5316 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE); 5317 5318 if (!objPtr) { 5319 return JimOriginalEnviron(); 5320 } 5321 5322 5323 5324 num = Jim_ListLength(interp, objPtr); 5325 if (num % 2) { 5326 5327 num--; 5328 } 5329 size = Jim_Length(objPtr) + 2; 5330 5331 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size); 5332 envdata = (char *)&envptr[num / 2 + 1]; 5333 5334 n = 0; 5335 for (i = 0; i < num; i += 2) { 5336 const char *s1, *s2; 5337 Jim_Obj *elemObj; 5338 5339 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE); 5340 s1 = Jim_String(elemObj); 5341 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE); 5342 s2 = Jim_String(elemObj); 5343 5344 envptr[n] = envdata; 5345 envdata += sprintf(envdata, "%s=%s", s1, s2); 5346 envdata++; 5347 n++; 5348 } 5349 envptr[n] = NULL; 5350 *envdata = 0; 5351 5352 return envptr; 5353 } 5354 5355 static void JimFreeEnv(char **env, char **original_environ) 5356 { 5357 if (env != original_environ) { 5358 Jim_Free(env); 5359 } 5360 } 5361 5362 static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj) 5363 { 5364 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); 5365 5366 if (pid <= 0) { 5367 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1)); 5368 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid)); 5369 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1)); 5370 } 5371 else if (WIFEXITED(waitStatus)) { 5372 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); 5373 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid)); 5374 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus))); 5375 } 5376 else { 5377 const char *type; 5378 const char *action; 5379 const char *signame; 5380 5381 if (WIFSIGNALED(waitStatus)) { 5382 type = "CHILDKILLED"; 5383 action = "killed"; 5384 signame = Jim_SignalId(WTERMSIG(waitStatus)); 5385 } 5386 else { 5387 type = "CHILDSUSP"; 5388 action = "suspended"; 5389 signame = "none"; 5390 } 5391 5392 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1)); 5393 5394 if (errStrObj) { 5395 Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL); 5396 } 5397 5398 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid)); 5399 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1)); 5400 } 5401 return errorCode; 5402 } 5403 5404 static int JimCheckWaitStatus(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj) 5405 { 5406 if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) { 5407 return JIM_OK; 5408 } 5409 Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj)); 5410 5411 return JIM_ERR; 5412 } 5413 5414 5415 struct WaitInfo 5416 { 5417 phandle_t phandle; 5418 int status; 5419 int flags; 5420 }; 5421 5422 5423 struct WaitInfoTable { 5424 struct WaitInfo *info; 5425 int size; 5426 int used; 5427 int refcount; 5428 }; 5429 5430 5431 #define WI_DETACHED 2 5432 5433 #define WAIT_TABLE_GROW_BY 4 5434 5435 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData) 5436 { 5437 struct WaitInfoTable *table = privData; 5438 5439 if (--table->refcount == 0) { 5440 Jim_Free(table->info); 5441 Jim_Free(table); 5442 } 5443 } 5444 5445 static struct WaitInfoTable *JimAllocWaitInfoTable(void) 5446 { 5447 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table)); 5448 table->info = NULL; 5449 table->size = table->used = 0; 5450 table->refcount = 1; 5451 5452 return table; 5453 } 5454 5455 static int JimWaitRemove(struct WaitInfoTable *table, phandle_t phandle) 5456 { 5457 int i; 5458 5459 5460 for (i = 0; i < table->used; i++) { 5461 if (phandle == table->info[i].phandle) { 5462 if (i != table->used - 1) { 5463 table->info[i] = table->info[table->used - 1]; 5464 } 5465 table->used--; 5466 return 0; 5467 } 5468 } 5469 return -1; 5470 } 5471 5472 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 5473 { 5474 int outputId; 5475 int errorId; 5476 phandle_t *pidPtr; 5477 int numPids, result; 5478 int child_siginfo = 1; 5479 Jim_Obj *childErrObj; 5480 Jim_Obj *errStrObj; 5481 struct WaitInfoTable *table = Jim_CmdPrivData(interp); 5482 5483 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) { 5484 Jim_Obj *listObj; 5485 int i; 5486 5487 argc--; 5488 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL); 5489 if (numPids < 0) { 5490 return JIM_ERR; 5491 } 5492 5493 listObj = Jim_NewListObj(interp, NULL, 0); 5494 for (i = 0; i < numPids; i++) { 5495 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, JimProcessPid(pidPtr[i]))); 5496 } 5497 Jim_SetResult(interp, listObj); 5498 JimDetachPids(table, numPids, pidPtr); 5499 Jim_Free(pidPtr); 5500 return JIM_OK; 5501 } 5502 5503 numPids = 5504 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId); 5505 5506 if (numPids < 0) { 5507 return JIM_ERR; 5508 } 5509 5510 result = JIM_OK; 5511 5512 errStrObj = Jim_NewStringObj(interp, "", 0); 5513 5514 5515 if (outputId != -1) { 5516 if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) { 5517 result = JIM_ERR; 5518 Jim_SetResultErrno(interp, "error reading from output pipe"); 5519 } 5520 } 5521 5522 5523 childErrObj = Jim_NewStringObj(interp, "", 0); 5524 Jim_IncrRefCount(childErrObj); 5525 5526 if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) { 5527 result = JIM_ERR; 5528 } 5529 5530 if (errorId != -1) { 5531 int ret; 5532 Jim_Lseek(errorId, 0, SEEK_SET); 5533 ret = JimAppendStreamToString(interp, errorId, errStrObj); 5534 if (ret < 0) { 5535 Jim_SetResultErrno(interp, "error reading from error pipe"); 5536 result = JIM_ERR; 5537 } 5538 else if (ret > 0) { 5539 5540 child_siginfo = 0; 5541 } 5542 } 5543 5544 if (child_siginfo) { 5545 5546 Jim_AppendObj(interp, errStrObj, childErrObj); 5547 } 5548 Jim_DecrRefCount(interp, childErrObj); 5549 5550 5551 Jim_RemoveTrailingNewline(errStrObj); 5552 5553 5554 Jim_SetResult(interp, errStrObj); 5555 5556 return result; 5557 } 5558 5559 static long JimWaitForProcess(struct WaitInfoTable *table, phandle_t phandle, int *statusPtr) 5560 { 5561 if (JimWaitRemove(table, phandle) == 0) { 5562 5563 return waitpid(phandle, statusPtr, 0); 5564 } 5565 5566 5567 return -1; 5568 } 5569 5570 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr) 5571 { 5572 int j; 5573 5574 for (j = 0; j < numPids; j++) { 5575 5576 int i; 5577 for (i = 0; i < table->used; i++) { 5578 if (pidPtr[j] == table->info[i].phandle) { 5579 table->info[i].flags |= WI_DETACHED; 5580 break; 5581 } 5582 } 5583 } 5584 } 5585 5586 static int JimGetChannelFd(Jim_Interp *interp, const char *name) 5587 { 5588 Jim_Obj *objv[2]; 5589 5590 objv[0] = Jim_NewStringObj(interp, name, -1); 5591 objv[1] = Jim_NewStringObj(interp, "getfd", -1); 5592 5593 if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) { 5594 jim_wide fd; 5595 if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) { 5596 return fd; 5597 } 5598 } 5599 return -1; 5600 } 5601 5602 static void JimReapDetachedPids(struct WaitInfoTable *table) 5603 { 5604 struct WaitInfo *waitPtr; 5605 int count; 5606 int dest; 5607 5608 if (!table) { 5609 return; 5610 } 5611 5612 waitPtr = table->info; 5613 dest = 0; 5614 for (count = table->used; count > 0; waitPtr++, count--) { 5615 if (waitPtr->flags & WI_DETACHED) { 5616 int status; 5617 long pid = waitpid(waitPtr->phandle, &status, WNOHANG); 5618 if (pid > 0) { 5619 5620 table->used--; 5621 continue; 5622 } 5623 } 5624 if (waitPtr != &table->info[dest]) { 5625 table->info[dest] = *waitPtr; 5626 } 5627 dest++; 5628 } 5629 } 5630 5631 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 5632 { 5633 struct WaitInfoTable *table = Jim_CmdPrivData(interp); 5634 int nohang = 0; 5635 long pid; 5636 phandle_t phandle; 5637 int status; 5638 Jim_Obj *errCodeObj; 5639 5640 5641 if (argc == 1) { 5642 JimReapDetachedPids(table); 5643 return JIM_OK; 5644 } 5645 5646 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) { 5647 nohang = 1; 5648 } 5649 if (argc != nohang + 2) { 5650 Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?"); 5651 return JIM_ERR; 5652 } 5653 if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) { 5654 return JIM_ERR; 5655 } 5656 5657 5658 phandle = JimWaitPid(pid, &status, nohang ? WNOHANG : 0); 5659 if (phandle == JIM_BAD_PHANDLE) { 5660 pid = -1; 5661 } 5662 #ifndef __MINGW32__ 5663 else if (pid < 0) { 5664 pid = phandle; 5665 } 5666 #endif 5667 5668 errCodeObj = JimMakeErrorCode(interp, pid, status, NULL); 5669 5670 if (phandle != JIM_BAD_PHANDLE && (WIFEXITED(status) || WIFSIGNALED(status))) { 5671 5672 JimWaitRemove(table, phandle); 5673 } 5674 Jim_SetResult(interp, errCodeObj); 5675 return JIM_OK; 5676 } 5677 5678 static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 5679 { 5680 if (argc != 1) { 5681 Jim_WrongNumArgs(interp, 1, argv, ""); 5682 return JIM_ERR; 5683 } 5684 5685 Jim_SetResultInt(interp, (jim_wide)getpid()); 5686 return JIM_OK; 5687 } 5688 5689 static int 5690 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr, 5691 int *inPipePtr, int *outPipePtr, int *errFilePtr) 5692 { 5693 phandle_t *pidPtr = NULL; /* Points to alloc-ed array holding all 5694 * the pids of child processes. */ 5695 int numPids = 0; /* Actual number of processes that exist 5696 * at *pidPtr right now. */ 5697 int cmdCount; /* Count of number of distinct commands 5698 * found in argc/argv. */ 5699 const char *input = NULL; /* Describes input for pipeline, depending 5700 * on "inputFile". NULL means take input 5701 * from stdin/pipe. */ 5702 int input_len = 0; 5703 5704 #define FILE_NAME 0 5705 #define FILE_APPEND 1 5706 #define FILE_HANDLE 2 5707 #define FILE_TEXT 3 5708 5709 int inputFile = FILE_NAME; /* 1 means input is name of input file. 5710 * 2 means input is filehandle name. 5711 * 0 means input holds actual 5712 * text to be input to command. */ 5713 5714 int outputFile = FILE_NAME; /* 0 means output is the name of output file. 5715 * 1 means output is the name of output file, and append. 5716 * 2 means output is filehandle name. 5717 * All this is ignored if output is NULL 5718 */ 5719 int errorFile = FILE_NAME; /* 0 means error is the name of error file. 5720 * 1 means error is the name of error file, and append. 5721 * 2 means error is filehandle name. 5722 * All this is ignored if error is NULL 5723 */ 5724 const char *output = NULL; /* Holds name of output file to pipe to, 5725 * or NULL if output goes to stdout/pipe. */ 5726 const char *error = NULL; /* Holds name of stderr file to pipe to, 5727 * or NULL if stderr goes to stderr/pipe. */ 5728 int inputId = -1; 5729 int outputId = -1; 5730 int errorId = -1; 5731 int lastOutputId = -1; 5732 int pipeIds[2]; 5733 int firstArg, lastArg; /* Indexes of first and last arguments in 5734 * current command. */ 5735 int lastBar; 5736 int i; 5737 phandle_t phandle; 5738 char **save_environ; 5739 #if defined(HAVE_EXECVPE) && !defined(__MINGW32__) 5740 char **child_environ; 5741 #endif 5742 struct WaitInfoTable *table = Jim_CmdPrivData(interp); 5743 5744 5745 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1)); 5746 int arg_count = 0; 5747 5748 if (inPipePtr != NULL) { 5749 *inPipePtr = -1; 5750 } 5751 if (outPipePtr != NULL) { 5752 *outPipePtr = -1; 5753 } 5754 if (errFilePtr != NULL) { 5755 *errFilePtr = -1; 5756 } 5757 pipeIds[0] = pipeIds[1] = -1; 5758 5759 cmdCount = 1; 5760 lastBar = -1; 5761 for (i = 0; i < argc; i++) { 5762 const char *arg = Jim_String(argv[i]); 5763 5764 if (arg[0] == '<') { 5765 inputFile = FILE_NAME; 5766 input = arg + 1; 5767 if (*input == '<') { 5768 inputFile = FILE_TEXT; 5769 input_len = Jim_Length(argv[i]) - 2; 5770 input++; 5771 } 5772 else if (*input == '@') { 5773 inputFile = FILE_HANDLE; 5774 input++; 5775 } 5776 5777 if (!*input && ++i < argc) { 5778 input = Jim_GetString(argv[i], &input_len); 5779 } 5780 } 5781 else if (arg[0] == '>') { 5782 int dup_error = 0; 5783 5784 outputFile = FILE_NAME; 5785 5786 output = arg + 1; 5787 if (*output == '>') { 5788 outputFile = FILE_APPEND; 5789 output++; 5790 } 5791 if (*output == '&') { 5792 5793 output++; 5794 dup_error = 1; 5795 } 5796 if (*output == '@') { 5797 outputFile = FILE_HANDLE; 5798 output++; 5799 } 5800 if (!*output && ++i < argc) { 5801 output = Jim_String(argv[i]); 5802 } 5803 if (dup_error) { 5804 errorFile = outputFile; 5805 error = output; 5806 } 5807 } 5808 else if (arg[0] == '2' && arg[1] == '>') { 5809 error = arg + 2; 5810 errorFile = FILE_NAME; 5811 5812 if (*error == '@') { 5813 errorFile = FILE_HANDLE; 5814 error++; 5815 } 5816 else if (*error == '>') { 5817 errorFile = FILE_APPEND; 5818 error++; 5819 } 5820 if (!*error && ++i < argc) { 5821 error = Jim_String(argv[i]); 5822 } 5823 } 5824 else { 5825 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) { 5826 if (i == lastBar + 1 || i == argc - 1) { 5827 Jim_SetResultString(interp, "illegal use of | or |& in command", -1); 5828 goto badargs; 5829 } 5830 lastBar = i; 5831 cmdCount++; 5832 } 5833 5834 arg_array[arg_count++] = (char *)arg; 5835 continue; 5836 } 5837 5838 if (i >= argc) { 5839 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg); 5840 goto badargs; 5841 } 5842 } 5843 5844 if (arg_count == 0) { 5845 Jim_SetResultString(interp, "didn't specify command to execute", -1); 5846 badargs: 5847 Jim_Free(arg_array); 5848 return -1; 5849 } 5850 5851 5852 save_environ = JimSaveEnv(JimBuildEnv(interp)); 5853 5854 if (input != NULL) { 5855 if (inputFile == FILE_TEXT) { 5856 inputId = Jim_MakeTempFile(interp, NULL, 1); 5857 if (inputId == -1) { 5858 goto error; 5859 } 5860 if (write(inputId, input, input_len) != input_len) { 5861 Jim_SetResultErrno(interp, "couldn't write temp file"); 5862 close(inputId); 5863 goto error; 5864 } 5865 Jim_Lseek(inputId, 0L, SEEK_SET); 5866 } 5867 else if (inputFile == FILE_HANDLE) { 5868 int fd = JimGetChannelFd(interp, input); 5869 5870 if (fd < 0) { 5871 goto error; 5872 } 5873 inputId = dup(fd); 5874 } 5875 else { 5876 inputId = Jim_OpenForRead(input); 5877 if (inputId == -1) { 5878 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno())); 5879 goto error; 5880 } 5881 } 5882 } 5883 else if (inPipePtr != NULL) { 5884 if (pipe(pipeIds) != 0) { 5885 Jim_SetResultErrno(interp, "couldn't create input pipe for command"); 5886 goto error; 5887 } 5888 inputId = pipeIds[0]; 5889 *inPipePtr = pipeIds[1]; 5890 pipeIds[0] = pipeIds[1] = -1; 5891 } 5892 5893 if (output != NULL) { 5894 if (outputFile == FILE_HANDLE) { 5895 int fd = JimGetChannelFd(interp, output); 5896 if (fd < 0) { 5897 goto error; 5898 } 5899 lastOutputId = dup(fd); 5900 } 5901 else { 5902 lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND); 5903 if (lastOutputId == -1) { 5904 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno())); 5905 goto error; 5906 } 5907 } 5908 } 5909 else if (outPipePtr != NULL) { 5910 if (pipe(pipeIds) != 0) { 5911 Jim_SetResultErrno(interp, "couldn't create output pipe"); 5912 goto error; 5913 } 5914 lastOutputId = pipeIds[1]; 5915 *outPipePtr = pipeIds[0]; 5916 pipeIds[0] = pipeIds[1] = -1; 5917 } 5918 5919 if (error != NULL) { 5920 if (errorFile == FILE_HANDLE) { 5921 if (strcmp(error, "1") == 0) { 5922 5923 if (lastOutputId != -1) { 5924 errorId = dup(lastOutputId); 5925 } 5926 else { 5927 5928 error = "stdout"; 5929 } 5930 } 5931 if (errorId == -1) { 5932 int fd = JimGetChannelFd(interp, error); 5933 if (fd < 0) { 5934 goto error; 5935 } 5936 errorId = dup(fd); 5937 } 5938 } 5939 else { 5940 errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND); 5941 if (errorId == -1) { 5942 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno())); 5943 goto error; 5944 } 5945 } 5946 } 5947 else if (errFilePtr != NULL) { 5948 errorId = Jim_MakeTempFile(interp, NULL, 1); 5949 if (errorId == -1) { 5950 goto error; 5951 } 5952 *errFilePtr = dup(errorId); 5953 } 5954 5955 5956 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr)); 5957 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) { 5958 int pipe_dup_err = 0; 5959 int origErrorId = errorId; 5960 5961 for (lastArg = firstArg; lastArg < arg_count; lastArg++) { 5962 if (strcmp(arg_array[lastArg], "|") == 0) { 5963 break; 5964 } 5965 if (strcmp(arg_array[lastArg], "|&") == 0) { 5966 pipe_dup_err = 1; 5967 break; 5968 } 5969 } 5970 5971 if (lastArg == firstArg) { 5972 Jim_SetResultString(interp, "missing command to exec", -1); 5973 goto error; 5974 } 5975 5976 5977 arg_array[lastArg] = NULL; 5978 if (lastArg == arg_count) { 5979 outputId = lastOutputId; 5980 lastOutputId = -1; 5981 } 5982 else { 5983 if (pipe(pipeIds) != 0) { 5984 Jim_SetResultErrno(interp, "couldn't create pipe"); 5985 goto error; 5986 } 5987 outputId = pipeIds[1]; 5988 } 5989 5990 5991 if (pipe_dup_err) { 5992 errorId = outputId; 5993 } 5994 5995 5996 5997 #ifdef __MINGW32__ 5998 phandle = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId); 5999 if (phandle == JIM_BAD_PHANDLE) { 6000 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]); 6001 goto error; 6002 } 6003 #else 6004 i = strlen(arg_array[firstArg]); 6005 6006 #ifdef HAVE_EXECVPE 6007 child_environ = Jim_GetEnviron(); 6008 #endif 6009 #ifdef HAVE_VFORK 6010 phandle = vfork(); 6011 #else 6012 phandle = fork(); 6013 #endif 6014 if (phandle < 0) { 6015 Jim_SetResultErrno(interp, "couldn't fork child process"); 6016 goto error; 6017 } 6018 if (phandle == 0) { 6019 6020 6021 if (inputId != -1 && inputId != fileno(stdin)) { 6022 dup2(inputId, fileno(stdin)); 6023 close(inputId); 6024 } 6025 if (outputId != -1 && outputId != fileno(stdout)) { 6026 dup2(outputId, fileno(stdout)); 6027 if (outputId != errorId) { 6028 close(outputId); 6029 } 6030 } 6031 if (errorId != -1 && errorId != fileno(stderr)) { 6032 dup2(errorId, fileno(stderr)); 6033 close(errorId); 6034 } 6035 6036 if (outPipePtr && *outPipePtr != -1) { 6037 close(*outPipePtr); 6038 } 6039 if (errFilePtr && *errFilePtr != -1) { 6040 close(*errFilePtr); 6041 } 6042 if (pipeIds[0] != -1) { 6043 close(pipeIds[0]); 6044 } 6045 if (lastOutputId != -1) { 6046 close(lastOutputId); 6047 } 6048 6049 execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ); 6050 6051 if (write(fileno(stderr), "couldn't exec \"", 15) && 6052 write(fileno(stderr), arg_array[firstArg], i) && 6053 write(fileno(stderr), "\"\n", 2)) { 6054 6055 } 6056 #ifdef JIM_MAINTAINER 6057 { 6058 6059 static char *const false_argv[2] = {"false", NULL}; 6060 execvp(false_argv[0],false_argv); 6061 } 6062 #endif 6063 _exit(127); 6064 } 6065 #endif 6066 6067 6068 6069 if (table->used == table->size) { 6070 table->size += WAIT_TABLE_GROW_BY; 6071 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info)); 6072 } 6073 6074 table->info[table->used].phandle = phandle; 6075 table->info[table->used].flags = 0; 6076 table->used++; 6077 6078 pidPtr[numPids] = phandle; 6079 6080 6081 errorId = origErrorId; 6082 6083 6084 if (inputId != -1) { 6085 close(inputId); 6086 } 6087 if (outputId != -1) { 6088 close(outputId); 6089 } 6090 inputId = pipeIds[0]; 6091 pipeIds[0] = pipeIds[1] = -1; 6092 } 6093 *pidArrayPtr = pidPtr; 6094 6095 6096 cleanup: 6097 if (inputId != -1) { 6098 close(inputId); 6099 } 6100 if (lastOutputId != -1) { 6101 close(lastOutputId); 6102 } 6103 if (errorId != -1) { 6104 close(errorId); 6105 } 6106 Jim_Free(arg_array); 6107 6108 JimRestoreEnv(save_environ); 6109 6110 return numPids; 6111 6112 6113 error: 6114 if ((inPipePtr != NULL) && (*inPipePtr != -1)) { 6115 close(*inPipePtr); 6116 *inPipePtr = -1; 6117 } 6118 if ((outPipePtr != NULL) && (*outPipePtr != -1)) { 6119 close(*outPipePtr); 6120 *outPipePtr = -1; 6121 } 6122 if ((errFilePtr != NULL) && (*errFilePtr != -1)) { 6123 close(*errFilePtr); 6124 *errFilePtr = -1; 6125 } 6126 if (pipeIds[0] != -1) { 6127 close(pipeIds[0]); 6128 } 6129 if (pipeIds[1] != -1) { 6130 close(pipeIds[1]); 6131 } 6132 if (pidPtr != NULL) { 6133 for (i = 0; i < numPids; i++) { 6134 if (pidPtr[i] != JIM_BAD_PHANDLE) { 6135 JimDetachPids(table, 1, &pidPtr[i]); 6136 } 6137 } 6138 Jim_Free(pidPtr); 6139 } 6140 numPids = -1; 6141 goto cleanup; 6142 } 6143 6144 6145 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj) 6146 { 6147 struct WaitInfoTable *table = Jim_CmdPrivData(interp); 6148 int result = JIM_OK; 6149 int i; 6150 6151 6152 for (i = 0; i < numPids; i++) { 6153 int waitStatus = 0; 6154 long pid = JimWaitForProcess(table, pidPtr[i], &waitStatus); 6155 if (pid > 0) { 6156 if (JimCheckWaitStatus(interp, pid, waitStatus, errStrObj) != JIM_OK) { 6157 result = JIM_ERR; 6158 } 6159 } 6160 } 6161 Jim_Free(pidPtr); 6162 6163 return result; 6164 } 6165 6166 int Jim_execInit(Jim_Interp *interp) 6167 { 6168 struct WaitInfoTable *waitinfo; 6169 6170 Jim_PackageProvideCheck(interp, "exec"); 6171 6172 waitinfo = JimAllocWaitInfoTable(); 6173 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable); 6174 waitinfo->refcount++; 6175 Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable); 6176 Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0); 6177 6178 return JIM_OK; 6179 } 6180 6181 #if defined(__MINGW32__) 6182 6183 6184 static int 6185 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH]) 6186 { 6187 int i; 6188 static char extensions[][5] = {".exe", "", ".bat"}; 6189 6190 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { 6191 snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]); 6192 6193 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) { 6194 continue; 6195 } 6196 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) { 6197 continue; 6198 } 6199 return 0; 6200 } 6201 6202 return -1; 6203 } 6204 6205 static char **JimSaveEnv(char **env) 6206 { 6207 return env; 6208 } 6209 6210 static void JimRestoreEnv(char **env) 6211 { 6212 JimFreeEnv(env, Jim_GetEnviron()); 6213 } 6214 6215 static char **JimOriginalEnviron(void) 6216 { 6217 return NULL; 6218 } 6219 6220 static Jim_Obj * 6221 JimWinBuildCommandLine(Jim_Interp *interp, char **argv) 6222 { 6223 char *start, *special; 6224 int quote, i; 6225 6226 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0); 6227 6228 for (i = 0; argv[i]; i++) { 6229 if (i > 0) { 6230 Jim_AppendString(interp, strObj, " ", 1); 6231 } 6232 6233 if (argv[i][0] == '\0') { 6234 quote = 1; 6235 } 6236 else { 6237 quote = 0; 6238 for (start = argv[i]; *start != '\0'; start++) { 6239 if (isspace(UCHAR(*start))) { 6240 quote = 1; 6241 break; 6242 } 6243 } 6244 } 6245 if (quote) { 6246 Jim_AppendString(interp, strObj, "\"" , 1); 6247 } 6248 6249 start = argv[i]; 6250 for (special = argv[i]; ; ) { 6251 if ((*special == '\\') && (special[1] == '\\' || 6252 special[1] == '"' || (quote && special[1] == '\0'))) { 6253 Jim_AppendString(interp, strObj, start, special - start); 6254 start = special; 6255 while (1) { 6256 special++; 6257 if (*special == '"' || (quote && *special == '\0')) { 6258 6259 Jim_AppendString(interp, strObj, start, special - start); 6260 break; 6261 } 6262 if (*special != '\\') { 6263 break; 6264 } 6265 } 6266 Jim_AppendString(interp, strObj, start, special - start); 6267 start = special; 6268 } 6269 if (*special == '"') { 6270 if (special == start) { 6271 Jim_AppendString(interp, strObj, "\"", 1); 6272 } 6273 else { 6274 Jim_AppendString(interp, strObj, start, special - start); 6275 } 6276 Jim_AppendString(interp, strObj, "\\\"", 2); 6277 start = special + 1; 6278 } 6279 if (*special == '\0') { 6280 break; 6281 } 6282 special++; 6283 } 6284 Jim_AppendString(interp, strObj, start, special - start); 6285 if (quote) { 6286 Jim_AppendString(interp, strObj, "\"", 1); 6287 } 6288 } 6289 return strObj; 6290 } 6291 6292 static phandle_t 6293 JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId) 6294 { 6295 STARTUPINFO startInfo; 6296 PROCESS_INFORMATION procInfo; 6297 HANDLE hProcess; 6298 char execPath[MAX_PATH]; 6299 phandle_t phandle = INVALID_HANDLE_VALUE; 6300 Jim_Obj *cmdLineObj; 6301 char *winenv; 6302 6303 if (JimWinFindExecutable(argv[0], execPath) < 0) { 6304 return phandle; 6305 } 6306 argv[0] = execPath; 6307 6308 hProcess = GetCurrentProcess(); 6309 cmdLineObj = JimWinBuildCommandLine(interp, argv); 6310 6311 6312 ZeroMemory(&startInfo, sizeof(startInfo)); 6313 startInfo.cb = sizeof(startInfo); 6314 startInfo.dwFlags = STARTF_USESTDHANDLES; 6315 startInfo.hStdInput = INVALID_HANDLE_VALUE; 6316 startInfo.hStdOutput= INVALID_HANDLE_VALUE; 6317 startInfo.hStdError = INVALID_HANDLE_VALUE; 6318 6319 if (inputId == -1) { 6320 inputId = _fileno(stdin); 6321 } 6322 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput, 6323 0, TRUE, DUPLICATE_SAME_ACCESS); 6324 if (startInfo.hStdInput == INVALID_HANDLE_VALUE) { 6325 goto end; 6326 } 6327 6328 if (outputId == -1) { 6329 outputId = _fileno(stdout); 6330 } 6331 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput, 6332 0, TRUE, DUPLICATE_SAME_ACCESS); 6333 if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) { 6334 goto end; 6335 } 6336 6337 6338 if (errorId == -1) { 6339 errorId = _fileno(stderr); 6340 } 6341 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError, 6342 0, TRUE, DUPLICATE_SAME_ACCESS); 6343 if (startInfo.hStdError == INVALID_HANDLE_VALUE) { 6344 goto end; 6345 } 6346 6347 if (env == NULL) { 6348 6349 winenv = NULL; 6350 } 6351 else if (env[0] == NULL) { 6352 winenv = (char *)"\0"; 6353 } 6354 else { 6355 winenv = env[0]; 6356 } 6357 6358 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE, 6359 0, winenv, NULL, &startInfo, &procInfo)) { 6360 goto end; 6361 } 6362 6363 6364 WaitForInputIdle(procInfo.hProcess, 5000); 6365 CloseHandle(procInfo.hThread); 6366 6367 phandle = procInfo.hProcess; 6368 6369 end: 6370 Jim_FreeNewObj(interp, cmdLineObj); 6371 if (startInfo.hStdInput != INVALID_HANDLE_VALUE) { 6372 CloseHandle(startInfo.hStdInput); 6373 } 6374 if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) { 6375 CloseHandle(startInfo.hStdOutput); 6376 } 6377 if (startInfo.hStdError != INVALID_HANDLE_VALUE) { 6378 CloseHandle(startInfo.hStdError); 6379 } 6380 return phandle; 6381 } 6382 6383 #else 6384 6385 static char **JimOriginalEnviron(void) 6386 { 6387 return Jim_GetEnviron(); 6388 } 6389 6390 static char **JimSaveEnv(char **env) 6391 { 6392 char **saveenv = Jim_GetEnviron(); 6393 Jim_SetEnviron(env); 6394 return saveenv; 6395 } 6396 6397 static void JimRestoreEnv(char **env) 6398 { 6399 JimFreeEnv(Jim_GetEnviron(), env); 6400 Jim_SetEnviron(env); 6401 } 6402 #endif 6403 #endif 6404 6405 6406 #include <stdlib.h> 6407 #include <string.h> 6408 #include <stdio.h> 6409 #include <time.h> 6410 6411 6412 #ifdef HAVE_SYS_TIME_H 6413 #include <sys/time.h> 6414 #endif 6415 6416 struct clock_options { 6417 int gmt; 6418 const char *format; 6419 }; 6420 6421 static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts) 6422 { 6423 static const char * const options[] = { "-gmt", "-format", NULL }; 6424 enum { OPT_GMT, OPT_FORMAT, }; 6425 int i; 6426 6427 for (i = 0; i < argc; i += 2) { 6428 int option; 6429 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 6430 return JIM_ERR; 6431 } 6432 switch (option) { 6433 case OPT_GMT: 6434 if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) { 6435 return JIM_ERR; 6436 } 6437 break; 6438 case OPT_FORMAT: 6439 opts->format = Jim_String(argv[i + 1]); 6440 break; 6441 } 6442 } 6443 return JIM_OK; 6444 } 6445 6446 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6447 { 6448 6449 char buf[100]; 6450 time_t t; 6451 jim_wide seconds; 6452 struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" }; 6453 struct tm *tm; 6454 6455 if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) { 6456 return JIM_ERR; 6457 } 6458 if (argc % 2 == 0) { 6459 return -1; 6460 } 6461 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) { 6462 return JIM_ERR; 6463 } 6464 6465 t = seconds; 6466 tm = options.gmt ? gmtime(&t) : localtime(&t); 6467 6468 if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) { 6469 Jim_SetResultString(interp, "format string too long or invalid time", -1); 6470 return JIM_ERR; 6471 } 6472 6473 Jim_SetResultString(interp, buf, -1); 6474 6475 return JIM_OK; 6476 } 6477 6478 #ifdef HAVE_STRPTIME 6479 static time_t jim_timegm(const struct tm *tm) 6480 { 6481 int m = tm->tm_mon + 1; 6482 int y = 1900 + tm->tm_year - (m <= 2); 6483 int era = (y >= 0 ? y : y - 399) / 400; 6484 unsigned yoe = (unsigned)(y - era * 400); 6485 unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1; 6486 unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; 6487 long days = (era * 146097 + (int)doe - 719468); 6488 int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec; 6489 6490 return days * 24 * 60 * 60 + secs; 6491 } 6492 6493 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6494 { 6495 char *pt; 6496 struct tm tm; 6497 time_t now = time(NULL); 6498 6499 struct clock_options options = { 0, NULL }; 6500 6501 if (argc % 2 == 0) { 6502 return -1; 6503 } 6504 6505 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) { 6506 return JIM_ERR; 6507 } 6508 if (options.format == NULL) { 6509 return -1; 6510 } 6511 6512 localtime_r(&now, &tm); 6513 6514 pt = strptime(Jim_String(argv[0]), options.format, &tm); 6515 if (pt == 0 || *pt != 0) { 6516 Jim_SetResultString(interp, "Failed to parse time according to format", -1); 6517 return JIM_ERR; 6518 } 6519 6520 6521 tm.tm_isdst = options.gmt ? 0 : -1; 6522 Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm)); 6523 6524 return JIM_OK; 6525 } 6526 #endif 6527 6528 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6529 { 6530 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000000); 6531 return JIM_OK; 6532 } 6533 6534 static int clock_cmd_clicks(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6535 { 6536 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW)); 6537 return JIM_OK; 6538 } 6539 6540 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6541 { 6542 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME)); 6543 return JIM_OK; 6544 } 6545 6546 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6547 { 6548 Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000); 6549 return JIM_OK; 6550 } 6551 6552 static const jim_subcmd_type clock_command_table[] = { 6553 { "clicks", 6554 NULL, 6555 clock_cmd_clicks, 6556 0, 6557 0, 6558 6559 }, 6560 { "format", 6561 "seconds ?-format string? ?-gmt boolean?", 6562 clock_cmd_format, 6563 1, 6564 5, 6565 6566 }, 6567 { "microseconds", 6568 NULL, 6569 clock_cmd_micros, 6570 0, 6571 0, 6572 6573 }, 6574 { "milliseconds", 6575 NULL, 6576 clock_cmd_millis, 6577 0, 6578 0, 6579 6580 }, 6581 #ifdef HAVE_STRPTIME 6582 { "scan", 6583 "str -format format ?-gmt boolean?", 6584 clock_cmd_scan, 6585 3, 6586 5, 6587 6588 }, 6589 #endif 6590 { "seconds", 6591 NULL, 6592 clock_cmd_seconds, 6593 0, 6594 0, 6595 6596 }, 6597 { NULL } 6598 }; 6599 6600 int Jim_clockInit(Jim_Interp *interp) 6601 { 6602 Jim_PackageProvideCheck(interp, "clock"); 6603 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL); 6604 return JIM_OK; 6605 } 6606 6607 #include <limits.h> 6608 #include <stdlib.h> 6609 #include <string.h> 6610 #include <stdio.h> 6611 #include <errno.h> 6612 6613 6614 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6615 { 6616 6617 Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED); 6618 Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1); 6619 return JIM_OK; 6620 } 6621 6622 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6623 { 6624 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); 6625 Jim_Obj *patternObj; 6626 6627 if (!objPtr) { 6628 return JIM_OK; 6629 } 6630 6631 patternObj = (argc == 1) ? NULL : argv[1]; 6632 6633 6634 if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) { 6635 if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) { 6636 6637 Jim_SetResult(interp, objPtr); 6638 return JIM_OK; 6639 } 6640 } 6641 6642 return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES); 6643 } 6644 6645 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6646 { 6647 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); 6648 6649 if (!objPtr) { 6650 return JIM_OK; 6651 } 6652 6653 return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS); 6654 } 6655 6656 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6657 { 6658 int i; 6659 int len; 6660 Jim_Obj *resultObj; 6661 Jim_Obj *objPtr; 6662 Jim_Obj **dictValuesObj; 6663 6664 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) { 6665 6666 Jim_UnsetVariable(interp, argv[0], JIM_NONE); 6667 return JIM_OK; 6668 } 6669 6670 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); 6671 6672 if (objPtr == NULL) { 6673 6674 return JIM_OK; 6675 } 6676 6677 dictValuesObj = Jim_DictPairs(interp, objPtr, &len); 6678 if (dictValuesObj == NULL) { 6679 6680 Jim_SetResultString(interp, "", -1); 6681 return JIM_OK; 6682 } 6683 6684 6685 resultObj = Jim_NewDictObj(interp, NULL, 0); 6686 6687 for (i = 0; i < len; i += 2) { 6688 if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) { 6689 Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]); 6690 } 6691 } 6692 6693 Jim_SetVariable(interp, argv[0], resultObj); 6694 return JIM_OK; 6695 } 6696 6697 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6698 { 6699 Jim_Obj *objPtr; 6700 int len = 0; 6701 6702 6703 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); 6704 if (objPtr) { 6705 len = Jim_DictSize(interp, objPtr); 6706 if (len < 0) { 6707 6708 Jim_SetResultInt(interp, 0); 6709 return JIM_OK; 6710 } 6711 } 6712 6713 Jim_SetResultInt(interp, len); 6714 6715 return JIM_OK; 6716 } 6717 6718 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6719 { 6720 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); 6721 if (objPtr) { 6722 return Jim_DictInfo(interp, objPtr); 6723 } 6724 Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL); 6725 return JIM_ERR; 6726 } 6727 6728 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 6729 { 6730 int i; 6731 int len; 6732 Jim_Obj *listObj = argv[1]; 6733 Jim_Obj *dictObj; 6734 6735 len = Jim_ListLength(interp, listObj); 6736 if (len % 2) { 6737 Jim_SetResultString(interp, "list must have an even number of elements", -1); 6738 return JIM_ERR; 6739 } 6740 6741 dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED); 6742 if (!dictObj) { 6743 6744 return Jim_SetVariable(interp, argv[0], listObj); 6745 } 6746 else if (Jim_DictSize(interp, dictObj) < 0) { 6747 return JIM_ERR; 6748 } 6749 6750 if (Jim_IsShared(dictObj)) { 6751 dictObj = Jim_DuplicateObj(interp, dictObj); 6752 } 6753 6754 for (i = 0; i < len; i += 2) { 6755 Jim_Obj *nameObj; 6756 Jim_Obj *valueObj; 6757 6758 Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE); 6759 Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE); 6760 6761 Jim_DictAddElement(interp, dictObj, nameObj, valueObj); 6762 } 6763 return Jim_SetVariable(interp, argv[0], dictObj); 6764 } 6765 6766 static const jim_subcmd_type array_command_table[] = { 6767 { "exists", 6768 "arrayName", 6769 array_cmd_exists, 6770 1, 6771 1, 6772 6773 }, 6774 { "get", 6775 "arrayName ?pattern?", 6776 array_cmd_get, 6777 1, 6778 2, 6779 6780 }, 6781 { "names", 6782 "arrayName ?pattern?", 6783 array_cmd_names, 6784 1, 6785 2, 6786 6787 }, 6788 { "set", 6789 "arrayName list", 6790 array_cmd_set, 6791 2, 6792 2, 6793 6794 }, 6795 { "size", 6796 "arrayName", 6797 array_cmd_size, 6798 1, 6799 1, 6800 6801 }, 6802 { "stat", 6803 "arrayName", 6804 array_cmd_stat, 6805 1, 6806 1, 6807 6808 }, 6809 { "unset", 6810 "arrayName ?pattern?", 6811 array_cmd_unset, 6812 1, 6813 2, 6814 6815 }, 6816 { NULL 6817 } 6818 }; 6819 6820 int Jim_arrayInit(Jim_Interp *interp) 6821 { 6822 Jim_PackageProvideCheck(interp, "array"); 6823 Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL); 6824 return JIM_OK; 6825 } 6826 int Jim_InitStaticExtensions(Jim_Interp *interp) 6827 { 6828 extern int Jim_bootstrapInit(Jim_Interp *); 6829 extern int Jim_aioInit(Jim_Interp *); 6830 extern int Jim_readdirInit(Jim_Interp *); 6831 extern int Jim_regexpInit(Jim_Interp *); 6832 extern int Jim_fileInit(Jim_Interp *); 6833 extern int Jim_globInit(Jim_Interp *); 6834 extern int Jim_execInit(Jim_Interp *); 6835 extern int Jim_clockInit(Jim_Interp *); 6836 extern int Jim_arrayInit(Jim_Interp *); 6837 extern int Jim_stdlibInit(Jim_Interp *); 6838 extern int Jim_tclcompatInit(Jim_Interp *); 6839 Jim_bootstrapInit(interp); 6840 Jim_aioInit(interp); 6841 Jim_readdirInit(interp); 6842 Jim_regexpInit(interp); 6843 Jim_fileInit(interp); 6844 Jim_globInit(interp); 6845 Jim_execInit(interp); 6846 Jim_clockInit(interp); 6847 Jim_arrayInit(interp); 6848 Jim_stdlibInit(interp); 6849 Jim_tclcompatInit(interp); 6850 return JIM_OK; 6851 } 6852 #ifndef JIM_TINY 6853 #define JIM_OPTIMIZATION 6854 #endif 6855 6856 #include <stdio.h> 6857 #include <stdlib.h> 6858 6859 #include <string.h> 6860 #include <stdarg.h> 6861 #include <ctype.h> 6862 #include <limits.h> 6863 #include <assert.h> 6864 #include <errno.h> 6865 #include <time.h> 6866 #include <setjmp.h> 6867 6868 6869 #ifdef HAVE_SYS_TIME_H 6870 #include <sys/time.h> 6871 #endif 6872 #ifdef HAVE_EXECINFO_H 6873 #include <execinfo.h> 6874 #endif 6875 #ifdef HAVE_CRT_EXTERNS_H 6876 #include <crt_externs.h> 6877 #endif 6878 6879 6880 #include <math.h> 6881 6882 6883 6884 6885 6886 #ifndef TCL_LIBRARY 6887 #define TCL_LIBRARY "." 6888 #endif 6889 #ifndef TCL_PLATFORM_OS 6890 #define TCL_PLATFORM_OS "unknown" 6891 #endif 6892 #ifndef TCL_PLATFORM_PLATFORM 6893 #define TCL_PLATFORM_PLATFORM "unknown" 6894 #endif 6895 #ifndef TCL_PLATFORM_PATH_SEPARATOR 6896 #define TCL_PLATFORM_PATH_SEPARATOR ":" 6897 #endif 6898 6899 6900 6901 6902 6903 6904 6905 #ifdef JIM_MAINTAINER 6906 #define JIM_DEBUG_COMMAND 6907 #define JIM_DEBUG_PANIC 6908 #endif 6909 6910 6911 6912 #define JIM_INTEGER_SPACE 24 6913 6914 #if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST) 6915 static const char *jim_tt_name(int type); 6916 #endif 6917 6918 #ifdef JIM_DEBUG_PANIC 6919 static void JimPanicDump(int fail_condition, const char *fmt, ...); 6920 #define JimPanic(X) JimPanicDump X 6921 #else 6922 #define JimPanic(X) 6923 #endif 6924 6925 #ifdef JIM_OPTIMIZATION 6926 static int JimIsWide(Jim_Obj *objPtr); 6927 #define JIM_IF_OPTIM(X) X 6928 #else 6929 #define JIM_IF_OPTIM(X) 6930 #endif 6931 6932 6933 static char JimEmptyStringRep[] = ""; 6934 6935 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action); 6936 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, 6937 int flags); 6938 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *const *indexv, int indexc, 6939 Jim_Obj **resultObj, int flags); 6940 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands); 6941 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); 6942 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); 6943 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, 6944 const char *prefix, const char *const *tablePtr, const char *name); 6945 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv); 6946 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); 6947 static int JimSign(jim_wide w); 6948 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen); 6949 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len); 6950 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv); 6951 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr); 6952 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 6953 6954 #define JIM_DICT_SUGAR 100 6955 6956 6957 6958 6959 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue 6960 6961 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none") 6962 6963 static int utf8_tounicode_case(const char *s, int *uc, int upper) 6964 { 6965 int l = utf8_tounicode(s, uc); 6966 if (upper) { 6967 *uc = utf8_upper(*uc); 6968 } 6969 return l; 6970 } 6971 6972 static Jim_Obj *JimPushInterpObjImpl(Jim_Obj **iop, Jim_Obj *no) 6973 { 6974 Jim_Obj *io = *iop; 6975 Jim_IncrRefCount(no); 6976 *iop = no; 6977 return io; 6978 } 6979 6980 #define JimPushInterpObj(IO, NO) JimPushInterpObjImpl(&(IO), NO) 6981 #define JimPopInterpObj(I, IO, SO) do { Jim_DecrRefCount(I, IO); IO = SO; } while (0) 6982 6983 6984 #define JIM_CHARSET_SCAN 2 6985 #define JIM_CHARSET_GLOB 0 6986 6987 static const char *JimCharsetMatch(const char *pattern, int plen, int c, int flags) 6988 { 6989 int not = 0; 6990 int pchar; 6991 int match = 0; 6992 int nocase = 0; 6993 int n; 6994 6995 if (flags & JIM_NOCASE) { 6996 nocase++; 6997 c = utf8_upper(c); 6998 } 6999 7000 if (flags & JIM_CHARSET_SCAN) { 7001 if (*pattern == '^') { 7002 not++; 7003 pattern++; 7004 plen--; 7005 } 7006 7007 7008 if (*pattern == ']') { 7009 goto first; 7010 } 7011 } 7012 7013 while (plen && *pattern != ']') { 7014 7015 if (pattern[0] == '\\') { 7016 first: 7017 n = utf8_tounicode_case(pattern, &pchar, nocase); 7018 pattern += n; 7019 plen -= n; 7020 } 7021 else { 7022 7023 int start; 7024 int end; 7025 7026 n = utf8_tounicode_case(pattern, &start, nocase); 7027 pattern += n; 7028 plen -= n; 7029 if (pattern[0] == '-' && plen > 1) { 7030 7031 n = 1 + utf8_tounicode_case(pattern + 1, &end, nocase); 7032 pattern += n; 7033 plen -= n; 7034 7035 7036 if ((c >= start && c <= end) || (c >= end && c <= start)) { 7037 match = 1; 7038 } 7039 continue; 7040 } 7041 pchar = start; 7042 } 7043 7044 if (pchar == c) { 7045 match = 1; 7046 } 7047 } 7048 if (not) { 7049 match = !match; 7050 } 7051 7052 return match ? pattern : NULL; 7053 } 7054 7055 7056 7057 static int JimGlobMatch(const char *pattern, int plen, const char *string, int slen, int nocase) 7058 { 7059 int c; 7060 int pchar; 7061 int n; 7062 const char *p; 7063 while (plen) { 7064 switch (pattern[0]) { 7065 case '*': 7066 while (pattern[1] == '*' && plen) { 7067 pattern++; 7068 plen--; 7069 } 7070 pattern++; 7071 plen--; 7072 if (!plen) { 7073 return 1; 7074 } 7075 while (slen) { 7076 7077 if (JimGlobMatch(pattern, plen, string, slen, nocase)) 7078 return 1; 7079 n = utf8_tounicode(string, &c); 7080 string += n; 7081 slen -= n; 7082 } 7083 return 0; 7084 7085 case '?': 7086 n = utf8_tounicode(string, &c); 7087 string += n; 7088 slen -= n; 7089 break; 7090 7091 case '[': { 7092 n = utf8_tounicode(string, &c); 7093 string += n; 7094 slen -= n; 7095 p = JimCharsetMatch(pattern + 1, plen - 1, c, nocase ? JIM_NOCASE : 0); 7096 if (!p) { 7097 return 0; 7098 } 7099 plen -= p - pattern; 7100 pattern = p; 7101 7102 if (!plen) { 7103 7104 continue; 7105 } 7106 break; 7107 } 7108 case '\\': 7109 if (pattern[1]) { 7110 pattern++; 7111 plen--; 7112 } 7113 7114 default: 7115 n = utf8_tounicode_case(string, &c, nocase); 7116 string += n; 7117 slen -= n; 7118 utf8_tounicode_case(pattern, &pchar, nocase); 7119 if (pchar != c) { 7120 return 0; 7121 } 7122 break; 7123 } 7124 n = utf8_tounicode_case(pattern, &pchar, nocase); 7125 pattern += n; 7126 plen -= n; 7127 if (!slen) { 7128 while (*pattern == '*' && plen) { 7129 pattern++; 7130 plen--; 7131 } 7132 break; 7133 } 7134 } 7135 if (!plen && !slen) { 7136 return 1; 7137 } 7138 return 0; 7139 } 7140 7141 static int JimStringCompareUtf8(const char *s1, int l1, const char *s2, int l2, int nocase) 7142 { 7143 int minlen = l1; 7144 if (l2 < l1) { 7145 minlen = l2; 7146 } 7147 while (minlen) { 7148 int c1, c2; 7149 s1 += utf8_tounicode_case(s1, &c1, nocase); 7150 s2 += utf8_tounicode_case(s2, &c2, nocase); 7151 if (c1 != c2) { 7152 return JimSign(c1 - c2); 7153 } 7154 minlen--; 7155 } 7156 7157 if (l1 < l2) { 7158 return -1; 7159 } 7160 if (l1 > l2) { 7161 return 1; 7162 } 7163 return 0; 7164 } 7165 7166 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx) 7167 { 7168 int i; 7169 int l1bytelen; 7170 7171 if (!l1 || !l2 || l1 > l2) { 7172 return -1; 7173 } 7174 if (idx < 0) 7175 idx = 0; 7176 s2 += utf8_index(s2, idx); 7177 7178 l1bytelen = utf8_index(s1, l1); 7179 7180 for (i = idx; i <= l2 - l1; i++) { 7181 int c; 7182 if (memcmp(s2, s1, l1bytelen) == 0) { 7183 return i; 7184 } 7185 s2 += utf8_tounicode(s2, &c); 7186 } 7187 return -1; 7188 } 7189 7190 static int JimStringLast(const char *s1, int l1, const char *s2, int l2) 7191 { 7192 const char *p; 7193 7194 if (!l1 || !l2 || l1 > l2) 7195 return -1; 7196 7197 7198 for (p = s2 + l2 - 1; p != s2 - 1; p--) { 7199 if (*p == *s1 && memcmp(s1, p, l1) == 0) { 7200 return p - s2; 7201 } 7202 } 7203 return -1; 7204 } 7205 7206 #ifdef JIM_UTF8 7207 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2) 7208 { 7209 int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2)); 7210 if (n > 0) { 7211 n = utf8_strlen(s2, n); 7212 } 7213 return n; 7214 } 7215 #endif 7216 7217 static int JimCheckConversion(const char *str, const char *endptr) 7218 { 7219 if (str[0] == '\0' || str == endptr) { 7220 return JIM_ERR; 7221 } 7222 7223 if (endptr[0] != '\0') { 7224 while (*endptr) { 7225 if (!isspace(UCHAR(*endptr))) { 7226 return JIM_ERR; 7227 } 7228 endptr++; 7229 } 7230 } 7231 return JIM_OK; 7232 } 7233 7234 static int JimNumberBase(const char *str, int *base, int *sign) 7235 { 7236 int i = 0; 7237 7238 *base = 0; 7239 7240 while (isspace(UCHAR(str[i]))) { 7241 i++; 7242 } 7243 7244 if (str[i] == '-') { 7245 *sign = -1; 7246 i++; 7247 } 7248 else { 7249 if (str[i] == '+') { 7250 i++; 7251 } 7252 *sign = 1; 7253 } 7254 7255 if (str[i] != '0') { 7256 7257 return 0; 7258 } 7259 7260 7261 switch (str[i + 1]) { 7262 case 'x': case 'X': *base = 16; break; 7263 case 'o': case 'O': *base = 8; break; 7264 case 'b': case 'B': *base = 2; break; 7265 case 'd': case 'D': *base = 10; break; 7266 default: return 0; 7267 } 7268 i += 2; 7269 7270 if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) { 7271 7272 return i; 7273 } 7274 7275 *base = 0; 7276 return 0; 7277 } 7278 7279 static long jim_strtol(const char *str, char **endptr) 7280 { 7281 int sign; 7282 int base; 7283 int i = JimNumberBase(str, &base, &sign); 7284 7285 if (base != 0) { 7286 long value = strtol(str + i, endptr, base); 7287 if (endptr == NULL || *endptr != str + i) { 7288 return value * sign; 7289 } 7290 } 7291 7292 7293 return strtol(str, endptr, 10); 7294 } 7295 7296 7297 static jim_wide jim_strtoull(const char *str, char **endptr) 7298 { 7299 #ifdef HAVE_LONG_LONG 7300 int sign; 7301 int base; 7302 int i = JimNumberBase(str, &base, &sign); 7303 7304 if (base != 0) { 7305 jim_wide value = strtoull(str + i, endptr, base); 7306 if (endptr == NULL || *endptr != str + i) { 7307 return value * sign; 7308 } 7309 } 7310 7311 7312 return strtoull(str, endptr, 10); 7313 #else 7314 return (unsigned long)jim_strtol(str, endptr); 7315 #endif 7316 } 7317 7318 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base) 7319 { 7320 char *endptr; 7321 7322 if (base) { 7323 *widePtr = strtoull(str, &endptr, base); 7324 } 7325 else { 7326 *widePtr = jim_strtoull(str, &endptr); 7327 } 7328 7329 return JimCheckConversion(str, endptr); 7330 } 7331 7332 int Jim_StringToDouble(const char *str, double *doublePtr) 7333 { 7334 char *endptr; 7335 7336 7337 errno = 0; 7338 7339 *doublePtr = strtod(str, &endptr); 7340 7341 return JimCheckConversion(str, endptr); 7342 } 7343 7344 static jim_wide JimPowWide(jim_wide b, jim_wide e) 7345 { 7346 jim_wide res = 1; 7347 7348 7349 if (b == 1) { 7350 7351 return 1; 7352 } 7353 if (e < 0) { 7354 if (b != -1) { 7355 return 0; 7356 } 7357 e = -e; 7358 } 7359 while (e) 7360 { 7361 if (e & 1) { 7362 res *= b; 7363 } 7364 e >>= 1; 7365 b *= b; 7366 } 7367 return res; 7368 } 7369 7370 #ifdef JIM_DEBUG_PANIC 7371 static void JimPanicDump(int condition, const char *fmt, ...) 7372 { 7373 va_list ap; 7374 7375 if (!condition) { 7376 return; 7377 } 7378 7379 va_start(ap, fmt); 7380 7381 fprintf(stderr, "\nJIM INTERPRETER PANIC: "); 7382 vfprintf(stderr, fmt, ap); 7383 fprintf(stderr, "\n\n"); 7384 va_end(ap); 7385 7386 #if defined(HAVE_BACKTRACE) 7387 { 7388 void *array[40]; 7389 int size, i; 7390 char **strings; 7391 7392 size = backtrace(array, 40); 7393 strings = backtrace_symbols(array, size); 7394 for (i = 0; i < size; i++) 7395 fprintf(stderr, "[backtrace] %s\n", strings[i]); 7396 fprintf(stderr, "[backtrace] Include the above lines and the output\n"); 7397 fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n"); 7398 } 7399 #endif 7400 7401 exit(1); 7402 } 7403 #endif 7404 7405 7406 void *JimDefaultAllocator(void *ptr, size_t size) 7407 { 7408 if (size == 0) { 7409 free(ptr); 7410 return NULL; 7411 } 7412 else if (ptr) { 7413 return realloc(ptr, size); 7414 } 7415 else { 7416 return malloc(size); 7417 } 7418 } 7419 7420 void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator; 7421 7422 char *Jim_StrDup(const char *s) 7423 { 7424 return Jim_StrDupLen(s, strlen(s)); 7425 } 7426 7427 char *Jim_StrDupLen(const char *s, int l) 7428 { 7429 char *copy = Jim_Alloc(l + 1); 7430 7431 memcpy(copy, s, l); 7432 copy[l] = 0; 7433 return copy; 7434 } 7435 7436 7437 jim_wide Jim_GetTimeUsec(unsigned type) 7438 { 7439 long long now; 7440 struct timeval tv; 7441 7442 #if defined(HAVE_CLOCK_GETTIME) 7443 struct timespec ts; 7444 7445 if (clock_gettime(type, &ts) == 0) { 7446 now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000; 7447 } 7448 else 7449 #endif 7450 { 7451 gettimeofday(&tv, NULL); 7452 7453 now = tv.tv_sec * 1000000LL + tv.tv_usec; 7454 } 7455 7456 return now; 7457 } 7458 7459 7460 7461 7462 7463 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht); 7464 static unsigned int JimHashTableNextPower(unsigned int size); 7465 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace); 7466 7467 7468 7469 7470 unsigned int Jim_IntHashFunction(unsigned int key) 7471 { 7472 key += ~(key << 15); 7473 key ^= (key >> 10); 7474 key += (key << 3); 7475 key ^= (key >> 6); 7476 key += ~(key << 11); 7477 key ^= (key >> 16); 7478 return key; 7479 } 7480 7481 7482 unsigned int Jim_GenHashFunction(const unsigned char *string, int length) 7483 { 7484 unsigned result = 0; 7485 string += length; 7486 while (length--) { 7487 result += (result << 3) + (unsigned char)(*--string); 7488 } 7489 return result; 7490 } 7491 7492 7493 7494 static void JimResetHashTable(Jim_HashTable *ht) 7495 { 7496 ht->table = NULL; 7497 ht->size = 0; 7498 ht->sizemask = 0; 7499 ht->used = 0; 7500 ht->collisions = 0; 7501 #ifdef JIM_RANDOMISE_HASH 7502 ht->uniq = (rand() ^ time(NULL) ^ clock()); 7503 #else 7504 ht->uniq = 0; 7505 #endif 7506 } 7507 7508 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter) 7509 { 7510 iter->ht = ht; 7511 iter->index = -1; 7512 iter->entry = NULL; 7513 iter->nextEntry = NULL; 7514 } 7515 7516 7517 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr) 7518 { 7519 JimResetHashTable(ht); 7520 ht->type = type; 7521 ht->privdata = privDataPtr; 7522 return JIM_OK; 7523 } 7524 7525 7526 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size) 7527 { 7528 Jim_HashTable n; 7529 unsigned int realsize = JimHashTableNextPower(size), i; 7530 7531 if (size <= ht->used) 7532 return; 7533 7534 Jim_InitHashTable(&n, ht->type, ht->privdata); 7535 n.size = realsize; 7536 n.sizemask = realsize - 1; 7537 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *)); 7538 7539 n.uniq = ht->uniq; 7540 7541 7542 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *)); 7543 7544 n.used = ht->used; 7545 for (i = 0; ht->used > 0; i++) { 7546 Jim_HashEntry *he, *nextHe; 7547 7548 if (ht->table[i] == NULL) 7549 continue; 7550 7551 7552 he = ht->table[i]; 7553 while (he) { 7554 unsigned int h; 7555 7556 nextHe = he->next; 7557 7558 h = Jim_HashKey(ht, he->key) & n.sizemask; 7559 he->next = n.table[h]; 7560 n.table[h] = he; 7561 ht->used--; 7562 7563 he = nextHe; 7564 } 7565 } 7566 assert(ht->used == 0); 7567 Jim_Free(ht->table); 7568 7569 7570 *ht = n; 7571 } 7572 7573 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val) 7574 { 7575 Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);; 7576 if (entry == NULL) 7577 return JIM_ERR; 7578 7579 7580 Jim_SetHashKey(ht, entry, key); 7581 Jim_SetHashVal(ht, entry, val); 7582 return JIM_OK; 7583 } 7584 7585 7586 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val) 7587 { 7588 int existed; 7589 Jim_HashEntry *entry; 7590 7591 entry = JimInsertHashEntry(ht, key, 1); 7592 if (entry->key) { 7593 if (ht->type->valDestructor && ht->type->valDup) { 7594 void *newval = ht->type->valDup(ht->privdata, val); 7595 ht->type->valDestructor(ht->privdata, entry->u.val); 7596 entry->u.val = newval; 7597 } 7598 else { 7599 Jim_FreeEntryVal(ht, entry); 7600 Jim_SetHashVal(ht, entry, val); 7601 } 7602 existed = 1; 7603 } 7604 else { 7605 7606 Jim_SetHashKey(ht, entry, key); 7607 Jim_SetHashVal(ht, entry, val); 7608 existed = 0; 7609 } 7610 7611 return existed; 7612 } 7613 7614 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key) 7615 { 7616 if (ht->used) { 7617 unsigned int h = Jim_HashKey(ht, key) & ht->sizemask; 7618 Jim_HashEntry *prevHe = NULL; 7619 Jim_HashEntry *he = ht->table[h]; 7620 7621 while (he) { 7622 if (Jim_CompareHashKeys(ht, key, he->key)) { 7623 7624 if (prevHe) 7625 prevHe->next = he->next; 7626 else 7627 ht->table[h] = he->next; 7628 ht->used--; 7629 Jim_FreeEntryKey(ht, he); 7630 Jim_FreeEntryVal(ht, he); 7631 Jim_Free(he); 7632 return JIM_OK; 7633 } 7634 prevHe = he; 7635 he = he->next; 7636 } 7637 } 7638 7639 return JIM_ERR; 7640 } 7641 7642 void Jim_ClearHashTable(Jim_HashTable *ht) 7643 { 7644 unsigned int i; 7645 7646 7647 for (i = 0; ht->used > 0; i++) { 7648 Jim_HashEntry *he, *nextHe; 7649 7650 he = ht->table[i]; 7651 while (he) { 7652 nextHe = he->next; 7653 Jim_FreeEntryKey(ht, he); 7654 Jim_FreeEntryVal(ht, he); 7655 Jim_Free(he); 7656 ht->used--; 7657 he = nextHe; 7658 } 7659 ht->table[i] = NULL; 7660 } 7661 } 7662 7663 int Jim_FreeHashTable(Jim_HashTable *ht) 7664 { 7665 Jim_ClearHashTable(ht); 7666 7667 Jim_Free(ht->table); 7668 7669 JimResetHashTable(ht); 7670 return JIM_OK; 7671 } 7672 7673 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key) 7674 { 7675 Jim_HashEntry *he; 7676 unsigned int h; 7677 7678 if (ht->used == 0) 7679 return NULL; 7680 h = Jim_HashKey(ht, key) & ht->sizemask; 7681 he = ht->table[h]; 7682 while (he) { 7683 if (Jim_CompareHashKeys(ht, key, he->key)) 7684 return he; 7685 he = he->next; 7686 } 7687 return NULL; 7688 } 7689 7690 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht) 7691 { 7692 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter)); 7693 JimInitHashTableIterator(ht, iter); 7694 return iter; 7695 } 7696 7697 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter) 7698 { 7699 while (1) { 7700 if (iter->entry == NULL) { 7701 iter->index++; 7702 if (iter->index >= (signed)iter->ht->size) 7703 break; 7704 iter->entry = iter->ht->table[iter->index]; 7705 } 7706 else { 7707 iter->entry = iter->nextEntry; 7708 } 7709 if (iter->entry) { 7710 iter->nextEntry = iter->entry->next; 7711 return iter->entry; 7712 } 7713 } 7714 return NULL; 7715 } 7716 7717 7718 7719 7720 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht) 7721 { 7722 if (ht->size == 0) 7723 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE); 7724 if (ht->size == ht->used) 7725 Jim_ExpandHashTable(ht, ht->size * 2); 7726 } 7727 7728 7729 static unsigned int JimHashTableNextPower(unsigned int size) 7730 { 7731 unsigned int i = JIM_HT_INITIAL_SIZE; 7732 7733 if (size >= 2147483648U) 7734 return 2147483648U; 7735 while (1) { 7736 if (i >= size) 7737 return i; 7738 i *= 2; 7739 } 7740 } 7741 7742 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace) 7743 { 7744 unsigned int h; 7745 Jim_HashEntry *he; 7746 7747 7748 JimExpandHashTableIfNeeded(ht); 7749 7750 7751 h = Jim_HashKey(ht, key) & ht->sizemask; 7752 7753 he = ht->table[h]; 7754 while (he) { 7755 if (Jim_CompareHashKeys(ht, key, he->key)) 7756 return replace ? he : NULL; 7757 he = he->next; 7758 } 7759 7760 7761 he = Jim_Alloc(sizeof(*he)); 7762 he->next = ht->table[h]; 7763 ht->table[h] = he; 7764 ht->used++; 7765 he->key = NULL; 7766 7767 return he; 7768 } 7769 7770 7771 7772 static unsigned int JimStringCopyHTHashFunction(const void *key) 7773 { 7774 return Jim_GenHashFunction(key, strlen(key)); 7775 } 7776 7777 static void *JimStringCopyHTDup(void *privdata, const void *key) 7778 { 7779 return Jim_StrDup(key); 7780 } 7781 7782 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2) 7783 { 7784 return strcmp(key1, key2) == 0; 7785 } 7786 7787 static void JimStringCopyHTKeyDestructor(void *privdata, void *key) 7788 { 7789 Jim_Free(key); 7790 } 7791 7792 static const Jim_HashTableType JimPackageHashTableType = { 7793 JimStringCopyHTHashFunction, 7794 JimStringCopyHTDup, 7795 NULL, 7796 JimStringCopyHTKeyCompare, 7797 JimStringCopyHTKeyDestructor, 7798 NULL 7799 }; 7800 7801 typedef struct AssocDataValue 7802 { 7803 Jim_InterpDeleteProc *delProc; 7804 void *data; 7805 } AssocDataValue; 7806 7807 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data) 7808 { 7809 AssocDataValue *assocPtr = (AssocDataValue *) data; 7810 7811 if (assocPtr->delProc != NULL) 7812 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data); 7813 Jim_Free(data); 7814 } 7815 7816 static const Jim_HashTableType JimAssocDataHashTableType = { 7817 JimStringCopyHTHashFunction, 7818 JimStringCopyHTDup, 7819 NULL, 7820 JimStringCopyHTKeyCompare, 7821 JimStringCopyHTKeyDestructor, 7822 JimAssocDataHashTableValueDestructor 7823 }; 7824 7825 void Jim_InitStack(Jim_Stack *stack) 7826 { 7827 stack->len = 0; 7828 stack->maxlen = 0; 7829 stack->vector = NULL; 7830 } 7831 7832 void Jim_FreeStack(Jim_Stack *stack) 7833 { 7834 Jim_Free(stack->vector); 7835 } 7836 7837 int Jim_StackLen(Jim_Stack *stack) 7838 { 7839 return stack->len; 7840 } 7841 7842 void Jim_StackPush(Jim_Stack *stack, void *element) 7843 { 7844 int neededLen = stack->len + 1; 7845 7846 if (neededLen > stack->maxlen) { 7847 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2; 7848 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen); 7849 } 7850 stack->vector[stack->len] = element; 7851 stack->len++; 7852 } 7853 7854 void *Jim_StackPop(Jim_Stack *stack) 7855 { 7856 if (stack->len == 0) 7857 return NULL; 7858 stack->len--; 7859 return stack->vector[stack->len]; 7860 } 7861 7862 void *Jim_StackPeek(Jim_Stack *stack) 7863 { 7864 if (stack->len == 0) 7865 return NULL; 7866 return stack->vector[stack->len - 1]; 7867 } 7868 7869 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr)) 7870 { 7871 int i; 7872 7873 for (i = 0; i < stack->len; i++) 7874 freeFunc(stack->vector[i]); 7875 } 7876 7877 7878 7879 #define JIM_TT_NONE 0 7880 #define JIM_TT_STR 1 7881 #define JIM_TT_ESC 2 7882 #define JIM_TT_VAR 3 7883 #define JIM_TT_DICTSUGAR 4 7884 #define JIM_TT_CMD 5 7885 7886 #define JIM_TT_SEP 6 7887 #define JIM_TT_EOL 7 7888 #define JIM_TT_EOF 8 7889 7890 #define JIM_TT_LINE 9 7891 #define JIM_TT_WORD 10 7892 7893 7894 #define JIM_TT_SUBEXPR_START 11 7895 #define JIM_TT_SUBEXPR_END 12 7896 #define JIM_TT_SUBEXPR_COMMA 13 7897 #define JIM_TT_EXPR_INT 14 7898 #define JIM_TT_EXPR_DOUBLE 15 7899 #define JIM_TT_EXPR_BOOLEAN 16 7900 7901 #define JIM_TT_EXPRSUGAR 17 7902 7903 7904 #define JIM_TT_EXPR_OP 20 7905 7906 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF) 7907 7908 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA) 7909 7910 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP) 7911 7912 struct JimParseMissing { 7913 int ch; 7914 int line; 7915 }; 7916 7917 struct JimParserCtx 7918 { 7919 const char *p; 7920 int len; 7921 int linenr; 7922 const char *tstart; 7923 const char *tend; 7924 int tline; 7925 int tt; 7926 int eof; 7927 int inquote; 7928 int comment; 7929 struct JimParseMissing missing; 7930 const char *errmsg; 7931 }; 7932 7933 static int JimParseScript(struct JimParserCtx *pc); 7934 static int JimParseSep(struct JimParserCtx *pc); 7935 static int JimParseEol(struct JimParserCtx *pc); 7936 static int JimParseCmd(struct JimParserCtx *pc); 7937 static int JimParseQuote(struct JimParserCtx *pc); 7938 static int JimParseVar(struct JimParserCtx *pc); 7939 static int JimParseBrace(struct JimParserCtx *pc); 7940 static int JimParseStr(struct JimParserCtx *pc); 7941 static int JimParseComment(struct JimParserCtx *pc); 7942 static void JimParseSubCmd(struct JimParserCtx *pc); 7943 static int JimParseSubQuote(struct JimParserCtx *pc); 7944 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc); 7945 7946 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr) 7947 { 7948 pc->p = prg; 7949 pc->len = len; 7950 pc->tstart = NULL; 7951 pc->tend = NULL; 7952 pc->tline = 0; 7953 pc->tt = JIM_TT_NONE; 7954 pc->eof = 0; 7955 pc->inquote = 0; 7956 pc->linenr = linenr; 7957 pc->comment = 1; 7958 pc->missing.ch = ' '; 7959 pc->missing.line = linenr; 7960 } 7961 7962 static int JimParseScript(struct JimParserCtx *pc) 7963 { 7964 while (1) { 7965 if (!pc->len) { 7966 pc->tstart = pc->p; 7967 pc->tend = pc->p - 1; 7968 pc->tline = pc->linenr; 7969 pc->tt = JIM_TT_EOL; 7970 if (pc->inquote) { 7971 pc->missing.ch = '"'; 7972 } 7973 pc->eof = 1; 7974 return JIM_OK; 7975 } 7976 switch (*(pc->p)) { 7977 case '\\': 7978 if (*(pc->p + 1) == '\n' && !pc->inquote) { 7979 return JimParseSep(pc); 7980 } 7981 pc->comment = 0; 7982 return JimParseStr(pc); 7983 case ' ': 7984 case '\t': 7985 case '\r': 7986 case '\f': 7987 if (!pc->inquote) 7988 return JimParseSep(pc); 7989 pc->comment = 0; 7990 return JimParseStr(pc); 7991 case '\n': 7992 case ';': 7993 pc->comment = 1; 7994 if (!pc->inquote) 7995 return JimParseEol(pc); 7996 return JimParseStr(pc); 7997 case '[': 7998 pc->comment = 0; 7999 return JimParseCmd(pc); 8000 case '$': 8001 pc->comment = 0; 8002 if (JimParseVar(pc) == JIM_ERR) { 8003 8004 pc->tstart = pc->tend = pc->p++; 8005 pc->len--; 8006 pc->tt = JIM_TT_ESC; 8007 } 8008 return JIM_OK; 8009 case '#': 8010 if (pc->comment) { 8011 JimParseComment(pc); 8012 continue; 8013 } 8014 return JimParseStr(pc); 8015 default: 8016 pc->comment = 0; 8017 return JimParseStr(pc); 8018 } 8019 return JIM_OK; 8020 } 8021 } 8022 8023 static int JimParseSep(struct JimParserCtx *pc) 8024 { 8025 pc->tstart = pc->p; 8026 pc->tline = pc->linenr; 8027 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) { 8028 if (*pc->p == '\n') { 8029 break; 8030 } 8031 if (*pc->p == '\\') { 8032 pc->p++; 8033 pc->len--; 8034 pc->linenr++; 8035 } 8036 pc->p++; 8037 pc->len--; 8038 } 8039 pc->tend = pc->p - 1; 8040 pc->tt = JIM_TT_SEP; 8041 return JIM_OK; 8042 } 8043 8044 static int JimParseEol(struct JimParserCtx *pc) 8045 { 8046 pc->tstart = pc->p; 8047 pc->tline = pc->linenr; 8048 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') { 8049 if (*pc->p == '\n') 8050 pc->linenr++; 8051 pc->p++; 8052 pc->len--; 8053 } 8054 pc->tend = pc->p - 1; 8055 pc->tt = JIM_TT_EOL; 8056 return JIM_OK; 8057 } 8058 8059 8060 static void JimParseSubBrace(struct JimParserCtx *pc) 8061 { 8062 int level = 1; 8063 8064 8065 pc->p++; 8066 pc->len--; 8067 while (pc->len) { 8068 switch (*pc->p) { 8069 case '\\': 8070 if (pc->len > 1) { 8071 if (*++pc->p == '\n') { 8072 pc->linenr++; 8073 } 8074 pc->len--; 8075 } 8076 break; 8077 8078 case '{': 8079 level++; 8080 break; 8081 8082 case '}': 8083 if (--level == 0) { 8084 pc->tend = pc->p - 1; 8085 pc->p++; 8086 pc->len--; 8087 return; 8088 } 8089 break; 8090 8091 case '\n': 8092 pc->linenr++; 8093 break; 8094 } 8095 pc->p++; 8096 pc->len--; 8097 } 8098 pc->missing.ch = '{'; 8099 pc->missing.line = pc->tline; 8100 pc->tend = pc->p - 1; 8101 } 8102 8103 static int JimParseSubQuote(struct JimParserCtx *pc) 8104 { 8105 int tt = JIM_TT_STR; 8106 int line = pc->tline; 8107 8108 8109 pc->p++; 8110 pc->len--; 8111 while (pc->len) { 8112 switch (*pc->p) { 8113 case '\\': 8114 if (pc->len > 1) { 8115 if (*++pc->p == '\n') { 8116 pc->linenr++; 8117 } 8118 pc->len--; 8119 tt = JIM_TT_ESC; 8120 } 8121 break; 8122 8123 case '"': 8124 pc->tend = pc->p - 1; 8125 pc->p++; 8126 pc->len--; 8127 return tt; 8128 8129 case '[': 8130 JimParseSubCmd(pc); 8131 tt = JIM_TT_ESC; 8132 continue; 8133 8134 case '\n': 8135 pc->linenr++; 8136 break; 8137 8138 case '$': 8139 tt = JIM_TT_ESC; 8140 break; 8141 } 8142 pc->p++; 8143 pc->len--; 8144 } 8145 pc->missing.ch = '"'; 8146 pc->missing.line = line; 8147 pc->tend = pc->p - 1; 8148 return tt; 8149 } 8150 8151 static void JimParseSubCmd(struct JimParserCtx *pc) 8152 { 8153 int level = 1; 8154 int startofword = 1; 8155 int line = pc->tline; 8156 8157 8158 pc->p++; 8159 pc->len--; 8160 while (pc->len) { 8161 switch (*pc->p) { 8162 case '\\': 8163 if (pc->len > 1) { 8164 if (*++pc->p == '\n') { 8165 pc->linenr++; 8166 } 8167 pc->len--; 8168 } 8169 break; 8170 8171 case '[': 8172 level++; 8173 break; 8174 8175 case ']': 8176 if (--level == 0) { 8177 pc->tend = pc->p - 1; 8178 pc->p++; 8179 pc->len--; 8180 return; 8181 } 8182 break; 8183 8184 case '"': 8185 if (startofword) { 8186 JimParseSubQuote(pc); 8187 if (pc->missing.ch == '"') { 8188 return; 8189 } 8190 continue; 8191 } 8192 break; 8193 8194 case '{': 8195 JimParseSubBrace(pc); 8196 startofword = 0; 8197 continue; 8198 8199 case '\n': 8200 pc->linenr++; 8201 break; 8202 } 8203 startofword = isspace(UCHAR(*pc->p)); 8204 pc->p++; 8205 pc->len--; 8206 } 8207 pc->missing.ch = '['; 8208 pc->missing.line = line; 8209 pc->tend = pc->p - 1; 8210 } 8211 8212 static int JimParseBrace(struct JimParserCtx *pc) 8213 { 8214 pc->tstart = pc->p + 1; 8215 pc->tline = pc->linenr; 8216 pc->tt = JIM_TT_STR; 8217 JimParseSubBrace(pc); 8218 return JIM_OK; 8219 } 8220 8221 static int JimParseCmd(struct JimParserCtx *pc) 8222 { 8223 pc->tstart = pc->p + 1; 8224 pc->tline = pc->linenr; 8225 pc->tt = JIM_TT_CMD; 8226 JimParseSubCmd(pc); 8227 return JIM_OK; 8228 } 8229 8230 static int JimParseQuote(struct JimParserCtx *pc) 8231 { 8232 pc->tstart = pc->p + 1; 8233 pc->tline = pc->linenr; 8234 pc->tt = JimParseSubQuote(pc); 8235 return JIM_OK; 8236 } 8237 8238 static int JimParseVar(struct JimParserCtx *pc) 8239 { 8240 8241 pc->p++; 8242 pc->len--; 8243 8244 #ifdef EXPRSUGAR_BRACKET 8245 if (*pc->p == '[') { 8246 8247 JimParseCmd(pc); 8248 pc->tt = JIM_TT_EXPRSUGAR; 8249 return JIM_OK; 8250 } 8251 #endif 8252 8253 pc->tstart = pc->p; 8254 pc->tt = JIM_TT_VAR; 8255 pc->tline = pc->linenr; 8256 8257 if (*pc->p == '{') { 8258 pc->tstart = ++pc->p; 8259 pc->len--; 8260 8261 while (pc->len && *pc->p != '}') { 8262 if (*pc->p == '\n') { 8263 pc->linenr++; 8264 } 8265 pc->p++; 8266 pc->len--; 8267 } 8268 pc->tend = pc->p - 1; 8269 if (pc->len) { 8270 pc->p++; 8271 pc->len--; 8272 } 8273 } 8274 else { 8275 while (1) { 8276 8277 if (pc->p[0] == ':' && pc->p[1] == ':') { 8278 while (*pc->p == ':') { 8279 pc->p++; 8280 pc->len--; 8281 } 8282 continue; 8283 } 8284 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) { 8285 pc->p++; 8286 pc->len--; 8287 continue; 8288 } 8289 break; 8290 } 8291 8292 if (*pc->p == '(') { 8293 int count = 1; 8294 const char *paren = NULL; 8295 8296 pc->tt = JIM_TT_DICTSUGAR; 8297 8298 while (count && pc->len) { 8299 pc->p++; 8300 pc->len--; 8301 if (*pc->p == '\\' && pc->len >= 1) { 8302 pc->p++; 8303 pc->len--; 8304 } 8305 else if (*pc->p == '(') { 8306 count++; 8307 } 8308 else if (*pc->p == ')') { 8309 paren = pc->p; 8310 count--; 8311 } 8312 } 8313 if (count == 0) { 8314 pc->p++; 8315 pc->len--; 8316 } 8317 else if (paren) { 8318 8319 paren++; 8320 pc->len += (pc->p - paren); 8321 pc->p = paren; 8322 } 8323 #ifndef EXPRSUGAR_BRACKET 8324 if (*pc->tstart == '(') { 8325 pc->tt = JIM_TT_EXPRSUGAR; 8326 } 8327 #endif 8328 } 8329 pc->tend = pc->p - 1; 8330 } 8331 if (pc->tstart == pc->p) { 8332 pc->p--; 8333 pc->len++; 8334 return JIM_ERR; 8335 } 8336 return JIM_OK; 8337 } 8338 8339 static int JimParseStr(struct JimParserCtx *pc) 8340 { 8341 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL || 8342 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) { 8343 8344 if (*pc->p == '{') { 8345 return JimParseBrace(pc); 8346 } 8347 if (*pc->p == '"') { 8348 pc->inquote = 1; 8349 pc->p++; 8350 pc->len--; 8351 8352 pc->missing.line = pc->tline; 8353 } 8354 } 8355 pc->tstart = pc->p; 8356 pc->tline = pc->linenr; 8357 while (1) { 8358 if (pc->len == 0) { 8359 if (pc->inquote) { 8360 pc->missing.ch = '"'; 8361 } 8362 pc->tend = pc->p - 1; 8363 pc->tt = JIM_TT_ESC; 8364 return JIM_OK; 8365 } 8366 switch (*pc->p) { 8367 case '\\': 8368 if (!pc->inquote && *(pc->p + 1) == '\n') { 8369 pc->tend = pc->p - 1; 8370 pc->tt = JIM_TT_ESC; 8371 return JIM_OK; 8372 } 8373 if (pc->len >= 2) { 8374 if (*(pc->p + 1) == '\n') { 8375 pc->linenr++; 8376 } 8377 pc->p++; 8378 pc->len--; 8379 } 8380 else if (pc->len == 1) { 8381 8382 pc->missing.ch = '\\'; 8383 } 8384 break; 8385 case '(': 8386 8387 if (pc->len > 1 && pc->p[1] != '$') { 8388 break; 8389 } 8390 8391 case ')': 8392 8393 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) { 8394 if (pc->p == pc->tstart) { 8395 8396 pc->p++; 8397 pc->len--; 8398 } 8399 pc->tend = pc->p - 1; 8400 pc->tt = JIM_TT_ESC; 8401 return JIM_OK; 8402 } 8403 break; 8404 8405 case '$': 8406 case '[': 8407 pc->tend = pc->p - 1; 8408 pc->tt = JIM_TT_ESC; 8409 return JIM_OK; 8410 case ' ': 8411 case '\t': 8412 case '\n': 8413 case '\r': 8414 case '\f': 8415 case ';': 8416 if (!pc->inquote) { 8417 pc->tend = pc->p - 1; 8418 pc->tt = JIM_TT_ESC; 8419 return JIM_OK; 8420 } 8421 else if (*pc->p == '\n') { 8422 pc->linenr++; 8423 } 8424 break; 8425 case '"': 8426 if (pc->inquote) { 8427 pc->tend = pc->p - 1; 8428 pc->tt = JIM_TT_ESC; 8429 pc->p++; 8430 pc->len--; 8431 pc->inquote = 0; 8432 return JIM_OK; 8433 } 8434 break; 8435 } 8436 pc->p++; 8437 pc->len--; 8438 } 8439 return JIM_OK; 8440 } 8441 8442 static int JimParseComment(struct JimParserCtx *pc) 8443 { 8444 while (*pc->p) { 8445 if (*pc->p == '\\') { 8446 pc->p++; 8447 pc->len--; 8448 if (pc->len == 0) { 8449 pc->missing.ch = '\\'; 8450 return JIM_OK; 8451 } 8452 if (*pc->p == '\n') { 8453 pc->linenr++; 8454 } 8455 } 8456 else if (*pc->p == '\n') { 8457 pc->p++; 8458 pc->len--; 8459 pc->linenr++; 8460 break; 8461 } 8462 pc->p++; 8463 pc->len--; 8464 } 8465 return JIM_OK; 8466 } 8467 8468 8469 static int xdigitval(int c) 8470 { 8471 if (c >= '0' && c <= '9') 8472 return c - '0'; 8473 if (c >= 'a' && c <= 'f') 8474 return c - 'a' + 10; 8475 if (c >= 'A' && c <= 'F') 8476 return c - 'A' + 10; 8477 return -1; 8478 } 8479 8480 static int odigitval(int c) 8481 { 8482 if (c >= '0' && c <= '7') 8483 return c - '0'; 8484 return -1; 8485 } 8486 8487 static int JimEscape(char *dest, const char *s, int slen) 8488 { 8489 char *p = dest; 8490 int i, len; 8491 8492 for (i = 0; i < slen; i++) { 8493 switch (s[i]) { 8494 case '\\': 8495 switch (s[i + 1]) { 8496 case 'a': 8497 *p++ = 0x7; 8498 i++; 8499 break; 8500 case 'b': 8501 *p++ = 0x8; 8502 i++; 8503 break; 8504 case 'f': 8505 *p++ = 0xc; 8506 i++; 8507 break; 8508 case 'n': 8509 *p++ = 0xa; 8510 i++; 8511 break; 8512 case 'r': 8513 *p++ = 0xd; 8514 i++; 8515 break; 8516 case 't': 8517 *p++ = 0x9; 8518 i++; 8519 break; 8520 case 'u': 8521 case 'U': 8522 case 'x': 8523 { 8524 unsigned val = 0; 8525 int k; 8526 int maxchars = 2; 8527 8528 i++; 8529 8530 if (s[i] == 'U') { 8531 maxchars = 8; 8532 } 8533 else if (s[i] == 'u') { 8534 if (s[i + 1] == '{') { 8535 maxchars = 6; 8536 i++; 8537 } 8538 else { 8539 maxchars = 4; 8540 } 8541 } 8542 8543 for (k = 0; k < maxchars; k++) { 8544 int c = xdigitval(s[i + k + 1]); 8545 if (c == -1) { 8546 break; 8547 } 8548 val = (val << 4) | c; 8549 } 8550 8551 if (s[i] == '{') { 8552 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') { 8553 8554 i--; 8555 k = 0; 8556 } 8557 else { 8558 8559 k++; 8560 } 8561 } 8562 if (k) { 8563 8564 if (s[i] == 'x') { 8565 *p++ = val; 8566 } 8567 else { 8568 p += utf8_fromunicode(p, val); 8569 } 8570 i += k; 8571 break; 8572 } 8573 8574 *p++ = s[i]; 8575 } 8576 break; 8577 case 'v': 8578 *p++ = 0xb; 8579 i++; 8580 break; 8581 case '\0': 8582 *p++ = '\\'; 8583 i++; 8584 break; 8585 case '\n': 8586 8587 *p++ = ' '; 8588 do { 8589 i++; 8590 } while (s[i + 1] == ' ' || s[i + 1] == '\t'); 8591 break; 8592 case '0': 8593 case '1': 8594 case '2': 8595 case '3': 8596 case '4': 8597 case '5': 8598 case '6': 8599 case '7': 8600 8601 { 8602 int val = 0; 8603 int c = odigitval(s[i + 1]); 8604 8605 val = c; 8606 c = odigitval(s[i + 2]); 8607 if (c == -1) { 8608 *p++ = val; 8609 i++; 8610 break; 8611 } 8612 val = (val * 8) + c; 8613 c = odigitval(s[i + 3]); 8614 if (c == -1) { 8615 *p++ = val; 8616 i += 2; 8617 break; 8618 } 8619 val = (val * 8) + c; 8620 *p++ = val; 8621 i += 3; 8622 } 8623 break; 8624 default: 8625 *p++ = s[i + 1]; 8626 i++; 8627 break; 8628 } 8629 break; 8630 default: 8631 *p++ = s[i]; 8632 break; 8633 } 8634 } 8635 len = p - dest; 8636 *p = '\0'; 8637 return len; 8638 } 8639 8640 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc) 8641 { 8642 const char *start, *end; 8643 char *token; 8644 int len; 8645 8646 start = pc->tstart; 8647 end = pc->tend; 8648 len = (end - start) + 1; 8649 if (len < 0) { 8650 len = 0; 8651 } 8652 token = Jim_Alloc(len + 1); 8653 if (pc->tt != JIM_TT_ESC) { 8654 8655 memcpy(token, start, len); 8656 token[len] = '\0'; 8657 } 8658 else { 8659 8660 len = JimEscape(token, start, len); 8661 } 8662 8663 return Jim_NewStringObjNoAlloc(interp, token, len); 8664 } 8665 8666 static int JimParseListSep(struct JimParserCtx *pc); 8667 static int JimParseListStr(struct JimParserCtx *pc); 8668 static int JimParseListQuote(struct JimParserCtx *pc); 8669 8670 static int JimParseList(struct JimParserCtx *pc) 8671 { 8672 if (isspace(UCHAR(*pc->p))) { 8673 return JimParseListSep(pc); 8674 } 8675 switch (*pc->p) { 8676 case '"': 8677 return JimParseListQuote(pc); 8678 8679 case '{': 8680 return JimParseBrace(pc); 8681 8682 default: 8683 if (pc->len) { 8684 return JimParseListStr(pc); 8685 } 8686 break; 8687 } 8688 8689 pc->tstart = pc->tend = pc->p; 8690 pc->tline = pc->linenr; 8691 pc->tt = JIM_TT_EOL; 8692 pc->eof = 1; 8693 return JIM_OK; 8694 } 8695 8696 static int JimParseListSep(struct JimParserCtx *pc) 8697 { 8698 pc->tstart = pc->p; 8699 pc->tline = pc->linenr; 8700 while (isspace(UCHAR(*pc->p))) { 8701 if (*pc->p == '\n') { 8702 pc->linenr++; 8703 } 8704 pc->p++; 8705 pc->len--; 8706 } 8707 pc->tend = pc->p - 1; 8708 pc->tt = JIM_TT_SEP; 8709 return JIM_OK; 8710 } 8711 8712 static int JimParseListQuote(struct JimParserCtx *pc) 8713 { 8714 pc->p++; 8715 pc->len--; 8716 8717 pc->tstart = pc->p; 8718 pc->tline = pc->linenr; 8719 pc->tt = JIM_TT_STR; 8720 8721 while (pc->len) { 8722 switch (*pc->p) { 8723 case '\\': 8724 pc->tt = JIM_TT_ESC; 8725 if (--pc->len == 0) { 8726 8727 pc->tend = pc->p; 8728 return JIM_OK; 8729 } 8730 pc->p++; 8731 break; 8732 case '\n': 8733 pc->linenr++; 8734 break; 8735 case '"': 8736 pc->tend = pc->p - 1; 8737 pc->p++; 8738 pc->len--; 8739 return JIM_OK; 8740 } 8741 pc->p++; 8742 pc->len--; 8743 } 8744 8745 pc->tend = pc->p - 1; 8746 return JIM_OK; 8747 } 8748 8749 static int JimParseListStr(struct JimParserCtx *pc) 8750 { 8751 pc->tstart = pc->p; 8752 pc->tline = pc->linenr; 8753 pc->tt = JIM_TT_STR; 8754 8755 while (pc->len) { 8756 if (isspace(UCHAR(*pc->p))) { 8757 pc->tend = pc->p - 1; 8758 return JIM_OK; 8759 } 8760 if (*pc->p == '\\') { 8761 if (--pc->len == 0) { 8762 8763 pc->tend = pc->p; 8764 return JIM_OK; 8765 } 8766 pc->tt = JIM_TT_ESC; 8767 pc->p++; 8768 } 8769 pc->p++; 8770 pc->len--; 8771 } 8772 pc->tend = pc->p - 1; 8773 return JIM_OK; 8774 } 8775 8776 8777 8778 Jim_Obj *Jim_NewObj(Jim_Interp *interp) 8779 { 8780 Jim_Obj *objPtr; 8781 8782 8783 if (interp->freeList != NULL) { 8784 8785 objPtr = interp->freeList; 8786 interp->freeList = objPtr->nextObjPtr; 8787 } 8788 else { 8789 8790 objPtr = Jim_Alloc(sizeof(*objPtr)); 8791 } 8792 8793 objPtr->refCount = 0; 8794 8795 8796 objPtr->prevObjPtr = NULL; 8797 objPtr->nextObjPtr = interp->liveList; 8798 if (interp->liveList) 8799 interp->liveList->prevObjPtr = objPtr; 8800 interp->liveList = objPtr; 8801 8802 return objPtr; 8803 } 8804 8805 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr) 8806 { 8807 8808 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr, 8809 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>")); 8810 8811 8812 Jim_FreeIntRep(interp, objPtr); 8813 8814 if (objPtr->bytes != NULL) { 8815 if (objPtr->bytes != JimEmptyStringRep) 8816 Jim_Free(objPtr->bytes); 8817 } 8818 8819 if (objPtr->prevObjPtr) 8820 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr; 8821 if (objPtr->nextObjPtr) 8822 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr; 8823 if (interp->liveList == objPtr) 8824 interp->liveList = objPtr->nextObjPtr; 8825 #ifdef JIM_DISABLE_OBJECT_POOL 8826 Jim_Free(objPtr); 8827 #else 8828 8829 objPtr->prevObjPtr = NULL; 8830 objPtr->nextObjPtr = interp->freeList; 8831 if (interp->freeList) 8832 interp->freeList->prevObjPtr = objPtr; 8833 interp->freeList = objPtr; 8834 objPtr->refCount = -1; 8835 #endif 8836 } 8837 8838 8839 void Jim_InvalidateStringRep(Jim_Obj *objPtr) 8840 { 8841 if (objPtr->bytes != NULL) { 8842 if (objPtr->bytes != JimEmptyStringRep) 8843 Jim_Free(objPtr->bytes); 8844 } 8845 objPtr->bytes = NULL; 8846 } 8847 8848 8849 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr) 8850 { 8851 Jim_Obj *dupPtr; 8852 8853 dupPtr = Jim_NewObj(interp); 8854 if (objPtr->bytes == NULL) { 8855 8856 dupPtr->bytes = NULL; 8857 } 8858 else if (objPtr->length == 0) { 8859 dupPtr->bytes = JimEmptyStringRep; 8860 dupPtr->length = 0; 8861 dupPtr->typePtr = NULL; 8862 return dupPtr; 8863 } 8864 else { 8865 dupPtr->bytes = Jim_Alloc(objPtr->length + 1); 8866 dupPtr->length = objPtr->length; 8867 8868 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1); 8869 } 8870 8871 8872 dupPtr->typePtr = objPtr->typePtr; 8873 if (objPtr->typePtr != NULL) { 8874 if (objPtr->typePtr->dupIntRepProc == NULL) { 8875 dupPtr->internalRep = objPtr->internalRep; 8876 } 8877 else { 8878 8879 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr); 8880 } 8881 } 8882 return dupPtr; 8883 } 8884 8885 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr) 8886 { 8887 if (objPtr->bytes == NULL) { 8888 8889 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); 8890 objPtr->typePtr->updateStringProc(objPtr); 8891 } 8892 if (lenPtr) 8893 *lenPtr = objPtr->length; 8894 return objPtr->bytes; 8895 } 8896 8897 8898 int Jim_Length(Jim_Obj *objPtr) 8899 { 8900 if (objPtr->bytes == NULL) { 8901 8902 Jim_GetString(objPtr, NULL); 8903 } 8904 return objPtr->length; 8905 } 8906 8907 8908 const char *Jim_String(Jim_Obj *objPtr) 8909 { 8910 if (objPtr->bytes == NULL) { 8911 8912 Jim_GetString(objPtr, NULL); 8913 } 8914 return objPtr->bytes; 8915 } 8916 8917 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str) 8918 { 8919 objPtr->bytes = Jim_StrDup(str); 8920 objPtr->length = strlen(str); 8921 } 8922 8923 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 8924 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 8925 8926 static const Jim_ObjType dictSubstObjType = { 8927 "dict-substitution", 8928 FreeDictSubstInternalRep, 8929 DupDictSubstInternalRep, 8930 NULL, 8931 JIM_TYPE_NONE, 8932 }; 8933 8934 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 8935 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 8936 8937 static const Jim_ObjType interpolatedObjType = { 8938 "interpolated", 8939 FreeInterpolatedInternalRep, 8940 DupInterpolatedInternalRep, 8941 NULL, 8942 JIM_TYPE_NONE, 8943 }; 8944 8945 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 8946 { 8947 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); 8948 } 8949 8950 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 8951 { 8952 8953 dupPtr->internalRep = srcPtr->internalRep; 8954 8955 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr); 8956 } 8957 8958 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 8959 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 8960 8961 static const Jim_ObjType stringObjType = { 8962 "string", 8963 NULL, 8964 DupStringInternalRep, 8965 NULL, 8966 JIM_TYPE_REFERENCES, 8967 }; 8968 8969 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 8970 { 8971 JIM_NOTUSED(interp); 8972 8973 dupPtr->internalRep.strValue.maxLength = srcPtr->length; 8974 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength; 8975 } 8976 8977 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 8978 { 8979 if (objPtr->typePtr != &stringObjType) { 8980 8981 if (objPtr->bytes == NULL) { 8982 8983 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); 8984 objPtr->typePtr->updateStringProc(objPtr); 8985 } 8986 8987 Jim_FreeIntRep(interp, objPtr); 8988 8989 objPtr->typePtr = &stringObjType; 8990 objPtr->internalRep.strValue.maxLength = objPtr->length; 8991 8992 objPtr->internalRep.strValue.charLength = -1; 8993 } 8994 return JIM_OK; 8995 } 8996 8997 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr) 8998 { 8999 #ifdef JIM_UTF8 9000 SetStringFromAny(interp, objPtr); 9001 9002 if (objPtr->internalRep.strValue.charLength < 0) { 9003 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length); 9004 } 9005 return objPtr->internalRep.strValue.charLength; 9006 #else 9007 return Jim_Length(objPtr); 9008 #endif 9009 } 9010 9011 9012 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len) 9013 { 9014 Jim_Obj *objPtr = Jim_NewObj(interp); 9015 9016 9017 if (len == -1) 9018 len = strlen(s); 9019 9020 if (len == 0) { 9021 objPtr->bytes = JimEmptyStringRep; 9022 } 9023 else { 9024 objPtr->bytes = Jim_StrDupLen(s, len); 9025 } 9026 objPtr->length = len; 9027 9028 9029 objPtr->typePtr = NULL; 9030 return objPtr; 9031 } 9032 9033 9034 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen) 9035 { 9036 #ifdef JIM_UTF8 9037 9038 int bytelen = utf8_index(s, charlen); 9039 9040 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen); 9041 9042 9043 objPtr->typePtr = &stringObjType; 9044 objPtr->internalRep.strValue.maxLength = bytelen; 9045 objPtr->internalRep.strValue.charLength = charlen; 9046 9047 return objPtr; 9048 #else 9049 return Jim_NewStringObj(interp, s, charlen); 9050 #endif 9051 } 9052 9053 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len) 9054 { 9055 Jim_Obj *objPtr = Jim_NewObj(interp); 9056 9057 objPtr->bytes = s; 9058 objPtr->length = (len == -1) ? strlen(s) : len; 9059 objPtr->typePtr = NULL; 9060 return objPtr; 9061 } 9062 9063 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len) 9064 { 9065 int needlen; 9066 9067 if (len == -1) 9068 len = strlen(str); 9069 needlen = objPtr->length + len; 9070 if (objPtr->internalRep.strValue.maxLength < needlen || 9071 objPtr->internalRep.strValue.maxLength == 0) { 9072 needlen *= 2; 9073 9074 if (needlen < 7) { 9075 needlen = 7; 9076 } 9077 if (objPtr->bytes == JimEmptyStringRep) { 9078 objPtr->bytes = Jim_Alloc(needlen + 1); 9079 } 9080 else { 9081 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1); 9082 } 9083 objPtr->internalRep.strValue.maxLength = needlen; 9084 } 9085 memcpy(objPtr->bytes + objPtr->length, str, len); 9086 objPtr->bytes[objPtr->length + len] = '\0'; 9087 9088 if (objPtr->internalRep.strValue.charLength >= 0) { 9089 9090 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len); 9091 } 9092 objPtr->length += len; 9093 } 9094 9095 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len) 9096 { 9097 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object")); 9098 SetStringFromAny(interp, objPtr); 9099 StringAppendString(objPtr, str, len); 9100 } 9101 9102 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr) 9103 { 9104 int len; 9105 const char *str = Jim_GetString(appendObjPtr, &len); 9106 Jim_AppendString(interp, objPtr, str, len); 9107 } 9108 9109 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...) 9110 { 9111 va_list ap; 9112 9113 SetStringFromAny(interp, objPtr); 9114 va_start(ap, objPtr); 9115 while (1) { 9116 const char *s = va_arg(ap, const char *); 9117 9118 if (s == NULL) 9119 break; 9120 Jim_AppendString(interp, objPtr, s, -1); 9121 } 9122 va_end(ap); 9123 } 9124 9125 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr) 9126 { 9127 if (aObjPtr == bObjPtr) { 9128 return 1; 9129 } 9130 else { 9131 int Alen, Blen; 9132 const char *sA = Jim_GetString(aObjPtr, &Alen); 9133 const char *sB = Jim_GetString(bObjPtr, &Blen); 9134 9135 return Alen == Blen && *sA == *sB && memcmp(sA, sB, Alen) == 0; 9136 } 9137 } 9138 9139 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase) 9140 { 9141 int plen, slen; 9142 const char *pattern = Jim_GetString(patternObjPtr, &plen); 9143 const char *string = Jim_GetString(objPtr, &slen); 9144 return JimGlobMatch(pattern, plen, string, slen, nocase); 9145 } 9146 9147 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase) 9148 { 9149 const char *s1 = Jim_String(firstObjPtr); 9150 int l1 = Jim_Utf8Length(interp, firstObjPtr); 9151 const char *s2 = Jim_String(secondObjPtr); 9152 int l2 = Jim_Utf8Length(interp, secondObjPtr); 9153 return JimStringCompareUtf8(s1, l1, s2, l2, nocase); 9154 } 9155 9156 static int JimRelToAbsIndex(int len, int idx) 9157 { 9158 if (idx < 0 && idx > -INT_MAX) 9159 return len + idx; 9160 return idx; 9161 } 9162 9163 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr) 9164 { 9165 int rangeLen; 9166 9167 if (*firstPtr > *lastPtr) { 9168 rangeLen = 0; 9169 } 9170 else { 9171 rangeLen = *lastPtr - *firstPtr + 1; 9172 if (rangeLen) { 9173 if (*firstPtr < 0) { 9174 rangeLen += *firstPtr; 9175 *firstPtr = 0; 9176 } 9177 if (*lastPtr >= len) { 9178 rangeLen -= (*lastPtr - (len - 1)); 9179 *lastPtr = len - 1; 9180 } 9181 } 9182 } 9183 if (rangeLen < 0) 9184 rangeLen = 0; 9185 9186 *rangeLenPtr = rangeLen; 9187 } 9188 9189 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, 9190 int len, int *first, int *last, int *range) 9191 { 9192 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) { 9193 return JIM_ERR; 9194 } 9195 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) { 9196 return JIM_ERR; 9197 } 9198 *first = JimRelToAbsIndex(len, *first); 9199 *last = JimRelToAbsIndex(len, *last); 9200 JimRelToAbsRange(len, first, last, range); 9201 return JIM_OK; 9202 } 9203 9204 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp, 9205 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr) 9206 { 9207 int first, last; 9208 const char *str; 9209 int rangeLen; 9210 int bytelen; 9211 9212 str = Jim_GetString(strObjPtr, &bytelen); 9213 9214 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) { 9215 return NULL; 9216 } 9217 9218 if (first == 0 && rangeLen == bytelen) { 9219 return strObjPtr; 9220 } 9221 return Jim_NewStringObj(interp, str + first, rangeLen); 9222 } 9223 9224 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp, 9225 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr) 9226 { 9227 #ifdef JIM_UTF8 9228 int first, last; 9229 const char *str; 9230 int len, rangeLen; 9231 int bytelen; 9232 9233 str = Jim_GetString(strObjPtr, &bytelen); 9234 len = Jim_Utf8Length(interp, strObjPtr); 9235 9236 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) { 9237 return NULL; 9238 } 9239 9240 if (first == 0 && rangeLen == len) { 9241 return strObjPtr; 9242 } 9243 if (len == bytelen) { 9244 9245 return Jim_NewStringObj(interp, str + first, rangeLen); 9246 } 9247 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen); 9248 #else 9249 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr); 9250 #endif 9251 } 9252 9253 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp, 9254 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj) 9255 { 9256 int first, last; 9257 const char *str; 9258 int len, rangeLen; 9259 Jim_Obj *objPtr; 9260 9261 len = Jim_Utf8Length(interp, strObjPtr); 9262 9263 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) { 9264 return NULL; 9265 } 9266 9267 if (last < first) { 9268 return strObjPtr; 9269 } 9270 9271 str = Jim_String(strObjPtr); 9272 9273 9274 objPtr = Jim_NewStringObjUtf8(interp, str, first); 9275 9276 9277 if (newStrObj) { 9278 Jim_AppendObj(interp, objPtr, newStrObj); 9279 } 9280 9281 9282 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1); 9283 9284 return objPtr; 9285 } 9286 9287 static void JimStrCopyUpperLower(char *dest, const char *str, int uc) 9288 { 9289 while (*str) { 9290 int c; 9291 str += utf8_tounicode(str, &c); 9292 dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c)); 9293 } 9294 *dest = 0; 9295 } 9296 9297 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr) 9298 { 9299 char *buf; 9300 int len; 9301 const char *str; 9302 9303 str = Jim_GetString(strObjPtr, &len); 9304 9305 #ifdef JIM_UTF8 9306 len *= 2; 9307 #endif 9308 buf = Jim_Alloc(len + 1); 9309 JimStrCopyUpperLower(buf, str, 0); 9310 return Jim_NewStringObjNoAlloc(interp, buf, -1); 9311 } 9312 9313 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr) 9314 { 9315 char *buf; 9316 const char *str; 9317 int len; 9318 9319 str = Jim_GetString(strObjPtr, &len); 9320 9321 #ifdef JIM_UTF8 9322 len *= 2; 9323 #endif 9324 buf = Jim_Alloc(len + 1); 9325 JimStrCopyUpperLower(buf, str, 1); 9326 return Jim_NewStringObjNoAlloc(interp, buf, -1); 9327 } 9328 9329 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr) 9330 { 9331 char *buf, *p; 9332 int len; 9333 int c; 9334 const char *str; 9335 9336 str = Jim_GetString(strObjPtr, &len); 9337 9338 #ifdef JIM_UTF8 9339 len *= 2; 9340 #endif 9341 buf = p = Jim_Alloc(len + 1); 9342 9343 str += utf8_tounicode(str, &c); 9344 p += utf8_getchars(p, utf8_title(c)); 9345 9346 JimStrCopyUpperLower(p, str, 0); 9347 9348 return Jim_NewStringObjNoAlloc(interp, buf, -1); 9349 } 9350 9351 static const char *utf8_memchr(const char *str, int len, int c) 9352 { 9353 #ifdef JIM_UTF8 9354 while (len) { 9355 int sc; 9356 int n = utf8_tounicode(str, &sc); 9357 if (sc == c) { 9358 return str; 9359 } 9360 str += n; 9361 len -= n; 9362 } 9363 return NULL; 9364 #else 9365 return memchr(str, c, len); 9366 #endif 9367 } 9368 9369 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen) 9370 { 9371 while (len) { 9372 int c; 9373 int n = utf8_tounicode(str, &c); 9374 9375 if (utf8_memchr(trimchars, trimlen, c) == NULL) { 9376 9377 break; 9378 } 9379 str += n; 9380 len -= n; 9381 } 9382 return str; 9383 } 9384 9385 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen) 9386 { 9387 str += len; 9388 9389 while (len) { 9390 int c; 9391 int n = utf8_prev_len(str, len); 9392 9393 len -= n; 9394 str -= n; 9395 9396 n = utf8_tounicode(str, &c); 9397 9398 if (utf8_memchr(trimchars, trimlen, c) == NULL) { 9399 return str + n; 9400 } 9401 } 9402 9403 return NULL; 9404 } 9405 9406 static const char default_trim_chars[] = " \t\n\r"; 9407 9408 static int default_trim_chars_len = sizeof(default_trim_chars); 9409 9410 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) 9411 { 9412 int len; 9413 const char *str = Jim_GetString(strObjPtr, &len); 9414 const char *trimchars = default_trim_chars; 9415 int trimcharslen = default_trim_chars_len; 9416 const char *newstr; 9417 9418 if (trimcharsObjPtr) { 9419 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen); 9420 } 9421 9422 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen); 9423 if (newstr == str) { 9424 return strObjPtr; 9425 } 9426 9427 return Jim_NewStringObj(interp, newstr, len - (newstr - str)); 9428 } 9429 9430 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) 9431 { 9432 int len; 9433 const char *trimchars = default_trim_chars; 9434 int trimcharslen = default_trim_chars_len; 9435 const char *nontrim; 9436 9437 if (trimcharsObjPtr) { 9438 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen); 9439 } 9440 9441 SetStringFromAny(interp, strObjPtr); 9442 9443 len = Jim_Length(strObjPtr); 9444 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen); 9445 9446 if (nontrim == NULL) { 9447 9448 return Jim_NewEmptyStringObj(interp); 9449 } 9450 if (nontrim == strObjPtr->bytes + len) { 9451 9452 return strObjPtr; 9453 } 9454 9455 if (Jim_IsShared(strObjPtr)) { 9456 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes)); 9457 } 9458 else { 9459 9460 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0; 9461 strObjPtr->length = (nontrim - strObjPtr->bytes); 9462 } 9463 9464 return strObjPtr; 9465 } 9466 9467 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) 9468 { 9469 9470 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr); 9471 9472 9473 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr); 9474 9475 9476 if (objPtr != strObjPtr && objPtr->refCount == 0) { 9477 9478 Jim_FreeNewObj(interp, objPtr); 9479 } 9480 9481 return strObjPtr; 9482 } 9483 9484 9485 #ifdef HAVE_ISASCII 9486 #define jim_isascii isascii 9487 #else 9488 static int jim_isascii(int c) 9489 { 9490 return !(c & ~0x7f); 9491 } 9492 #endif 9493 9494 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict) 9495 { 9496 static const char * const strclassnames[] = { 9497 "integer", "alpha", "alnum", "ascii", "digit", 9498 "double", "lower", "upper", "space", "xdigit", 9499 "control", "print", "graph", "punct", "boolean", 9500 NULL 9501 }; 9502 enum { 9503 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT, 9504 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT, 9505 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN, 9506 }; 9507 int strclass; 9508 int len; 9509 int i; 9510 const char *str; 9511 int (*isclassfunc)(int c) = NULL; 9512 9513 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 9514 return JIM_ERR; 9515 } 9516 9517 str = Jim_GetString(strObjPtr, &len); 9518 if (len == 0) { 9519 Jim_SetResultBool(interp, !strict); 9520 return JIM_OK; 9521 } 9522 9523 switch (strclass) { 9524 case STR_IS_INTEGER: 9525 { 9526 jim_wide w; 9527 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK); 9528 return JIM_OK; 9529 } 9530 9531 case STR_IS_DOUBLE: 9532 { 9533 double d; 9534 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE); 9535 return JIM_OK; 9536 } 9537 9538 case STR_IS_BOOLEAN: 9539 { 9540 int b; 9541 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK); 9542 return JIM_OK; 9543 } 9544 9545 case STR_IS_ALPHA: isclassfunc = isalpha; break; 9546 case STR_IS_ALNUM: isclassfunc = isalnum; break; 9547 case STR_IS_ASCII: isclassfunc = jim_isascii; break; 9548 case STR_IS_DIGIT: isclassfunc = isdigit; break; 9549 case STR_IS_LOWER: isclassfunc = islower; break; 9550 case STR_IS_UPPER: isclassfunc = isupper; break; 9551 case STR_IS_SPACE: isclassfunc = isspace; break; 9552 case STR_IS_XDIGIT: isclassfunc = isxdigit; break; 9553 case STR_IS_CONTROL: isclassfunc = iscntrl; break; 9554 case STR_IS_PRINT: isclassfunc = isprint; break; 9555 case STR_IS_GRAPH: isclassfunc = isgraph; break; 9556 case STR_IS_PUNCT: isclassfunc = ispunct; break; 9557 default: 9558 return JIM_ERR; 9559 } 9560 9561 for (i = 0; i < len; i++) { 9562 if (!isclassfunc(UCHAR(str[i]))) { 9563 Jim_SetResultBool(interp, 0); 9564 return JIM_OK; 9565 } 9566 } 9567 Jim_SetResultBool(interp, 1); 9568 return JIM_OK; 9569 } 9570 9571 9572 9573 static const Jim_ObjType comparedStringObjType = { 9574 "compared-string", 9575 NULL, 9576 NULL, 9577 NULL, 9578 JIM_TYPE_REFERENCES, 9579 }; 9580 9581 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str) 9582 { 9583 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) { 9584 return 1; 9585 } 9586 else { 9587 if (strcmp(str, Jim_String(objPtr)) != 0) 9588 return 0; 9589 9590 if (objPtr->typePtr != &comparedStringObjType) { 9591 Jim_FreeIntRep(interp, objPtr); 9592 objPtr->typePtr = &comparedStringObjType; 9593 } 9594 objPtr->internalRep.ptr = (char *)str; 9595 return 1; 9596 } 9597 } 9598 9599 static int qsortCompareStringPointers(const void *a, const void *b) 9600 { 9601 char *const *sa = (char *const *)a; 9602 char *const *sb = (char *const *)b; 9603 9604 return strcmp(*sa, *sb); 9605 } 9606 9607 9608 9609 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 9610 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 9611 9612 static const Jim_ObjType sourceObjType = { 9613 "source", 9614 FreeSourceInternalRep, 9615 DupSourceInternalRep, 9616 NULL, 9617 JIM_TYPE_REFERENCES, 9618 }; 9619 9620 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 9621 { 9622 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj); 9623 } 9624 9625 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 9626 { 9627 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue; 9628 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); 9629 } 9630 9631 static const Jim_ObjType scriptLineObjType = { 9632 "scriptline", 9633 NULL, 9634 NULL, 9635 NULL, 9636 JIM_NONE, 9637 }; 9638 9639 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line) 9640 { 9641 Jim_Obj *objPtr; 9642 9643 #ifdef DEBUG_SHOW_SCRIPT 9644 char buf[100]; 9645 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc); 9646 objPtr = Jim_NewStringObj(interp, buf, -1); 9647 #else 9648 objPtr = Jim_NewEmptyStringObj(interp); 9649 #endif 9650 objPtr->typePtr = &scriptLineObjType; 9651 objPtr->internalRep.scriptLineValue.argc = argc; 9652 objPtr->internalRep.scriptLineValue.line = line; 9653 9654 return objPtr; 9655 } 9656 9657 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 9658 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 9659 9660 static const Jim_ObjType scriptObjType = { 9661 "script", 9662 FreeScriptInternalRep, 9663 DupScriptInternalRep, 9664 NULL, 9665 JIM_TYPE_NONE, 9666 }; 9667 9668 typedef struct ScriptToken 9669 { 9670 Jim_Obj *objPtr; 9671 int type; 9672 } ScriptToken; 9673 9674 typedef struct ScriptObj 9675 { 9676 ScriptToken *token; 9677 Jim_Obj *fileNameObj; 9678 int len; 9679 int substFlags; 9680 int inUse; /* Used to share a ScriptObj. Currently 9681 only used by Jim_EvalObj() as protection against 9682 shimmering of the currently evaluated object. */ 9683 int firstline; 9684 int linenr; 9685 int missing; 9686 } ScriptObj; 9687 9688 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 9689 static int JimParseCheckMissing(Jim_Interp *interp, int ch); 9690 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr); 9691 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script); 9692 9693 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 9694 { 9695 int i; 9696 struct ScriptObj *script = (void *)objPtr->internalRep.ptr; 9697 9698 if (--script->inUse != 0) 9699 return; 9700 for (i = 0; i < script->len; i++) { 9701 Jim_DecrRefCount(interp, script->token[i].objPtr); 9702 } 9703 Jim_Free(script->token); 9704 Jim_DecrRefCount(interp, script->fileNameObj); 9705 Jim_Free(script); 9706 } 9707 9708 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 9709 { 9710 JIM_NOTUSED(interp); 9711 JIM_NOTUSED(srcPtr); 9712 9713 dupPtr->typePtr = NULL; 9714 } 9715 9716 typedef struct 9717 { 9718 const char *token; 9719 int len; 9720 int type; 9721 int line; 9722 } ParseToken; 9723 9724 typedef struct 9725 { 9726 9727 ParseToken *list; 9728 int size; 9729 int count; 9730 ParseToken static_list[20]; 9731 } ParseTokenList; 9732 9733 static void ScriptTokenListInit(ParseTokenList *tokenlist) 9734 { 9735 tokenlist->list = tokenlist->static_list; 9736 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken); 9737 tokenlist->count = 0; 9738 } 9739 9740 static void ScriptTokenListFree(ParseTokenList *tokenlist) 9741 { 9742 if (tokenlist->list != tokenlist->static_list) { 9743 Jim_Free(tokenlist->list); 9744 } 9745 } 9746 9747 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type, 9748 int line) 9749 { 9750 ParseToken *t; 9751 9752 if (tokenlist->count == tokenlist->size) { 9753 9754 tokenlist->size *= 2; 9755 if (tokenlist->list != tokenlist->static_list) { 9756 tokenlist->list = 9757 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list)); 9758 } 9759 else { 9760 9761 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list)); 9762 memcpy(tokenlist->list, tokenlist->static_list, 9763 tokenlist->count * sizeof(*tokenlist->list)); 9764 } 9765 } 9766 t = &tokenlist->list[tokenlist->count++]; 9767 t->token = token; 9768 t->len = len; 9769 t->type = type; 9770 t->line = line; 9771 } 9772 9773 static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t) 9774 { 9775 int expand = 1; 9776 int count = 0; 9777 9778 9779 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) { 9780 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) { 9781 9782 expand = -1; 9783 t++; 9784 } 9785 else { 9786 if (script->missing == ' ') { 9787 9788 script->missing = '}'; 9789 script->linenr = t[1].line; 9790 } 9791 } 9792 } 9793 9794 9795 while (!TOKEN_IS_SEP(t->type)) { 9796 t++; 9797 count++; 9798 } 9799 9800 return count * expand; 9801 } 9802 9803 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t) 9804 { 9805 Jim_Obj *objPtr; 9806 9807 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) { 9808 9809 int len = t->len; 9810 char *str = Jim_Alloc(len + 1); 9811 len = JimEscape(str, t->token, len); 9812 objPtr = Jim_NewStringObjNoAlloc(interp, str, len); 9813 } 9814 else { 9815 objPtr = Jim_NewStringObj(interp, t->token, t->len); 9816 } 9817 return objPtr; 9818 } 9819 9820 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, 9821 ParseTokenList *tokenlist) 9822 { 9823 int i; 9824 struct ScriptToken *token; 9825 9826 int lineargs = 0; 9827 9828 ScriptToken *linefirst; 9829 int count; 9830 int linenr; 9831 9832 #ifdef DEBUG_SHOW_SCRIPT_TOKENS 9833 printf("==== Tokens ====\n"); 9834 for (i = 0; i < tokenlist->count; i++) { 9835 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type), 9836 tokenlist->list[i].len, tokenlist->list[i].token); 9837 } 9838 #endif 9839 9840 9841 count = tokenlist->count; 9842 for (i = 0; i < tokenlist->count; i++) { 9843 if (tokenlist->list[i].type == JIM_TT_EOL) { 9844 count++; 9845 } 9846 } 9847 linenr = script->firstline = tokenlist->list[0].line; 9848 9849 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count); 9850 9851 9852 linefirst = token++; 9853 9854 for (i = 0; i < tokenlist->count; ) { 9855 9856 int wordtokens; 9857 9858 9859 while (tokenlist->list[i].type == JIM_TT_SEP) { 9860 i++; 9861 } 9862 9863 wordtokens = JimCountWordTokens(script, tokenlist->list + i); 9864 9865 if (wordtokens == 0) { 9866 9867 if (lineargs) { 9868 linefirst->type = JIM_TT_LINE; 9869 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr); 9870 Jim_IncrRefCount(linefirst->objPtr); 9871 9872 9873 lineargs = 0; 9874 linefirst = token++; 9875 } 9876 i++; 9877 continue; 9878 } 9879 else if (wordtokens != 1) { 9880 9881 token->type = JIM_TT_WORD; 9882 token->objPtr = Jim_NewIntObj(interp, wordtokens); 9883 Jim_IncrRefCount(token->objPtr); 9884 token++; 9885 if (wordtokens < 0) { 9886 9887 i++; 9888 wordtokens = -wordtokens - 1; 9889 lineargs--; 9890 } 9891 } 9892 9893 if (lineargs == 0) { 9894 9895 linenr = tokenlist->list[i].line; 9896 } 9897 lineargs++; 9898 9899 9900 while (wordtokens--) { 9901 const ParseToken *t = &tokenlist->list[i++]; 9902 9903 token->type = t->type; 9904 token->objPtr = JimMakeScriptObj(interp, t); 9905 Jim_IncrRefCount(token->objPtr); 9906 9907 Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line); 9908 token++; 9909 } 9910 } 9911 9912 if (lineargs == 0) { 9913 token--; 9914 } 9915 9916 script->len = token - script->token; 9917 9918 JimPanic((script->len >= count, "allocated script array is too short")); 9919 9920 #ifdef DEBUG_SHOW_SCRIPT 9921 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj)); 9922 for (i = 0; i < script->len; i++) { 9923 const ScriptToken *t = &script->token[i]; 9924 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); 9925 } 9926 #endif 9927 9928 } 9929 9930 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr) 9931 { 9932 ScriptObj *script = JimGetScript(interp, scriptObj); 9933 if (stateCharPtr) { 9934 *stateCharPtr = script->missing; 9935 } 9936 return script->missing == ' ' || script->missing == '}'; 9937 } 9938 9939 static int JimParseCheckMissing(Jim_Interp *interp, int ch) 9940 { 9941 const char *msg; 9942 9943 switch (ch) { 9944 case '\\': 9945 case ' ': 9946 return JIM_OK; 9947 9948 case '[': 9949 msg = "unmatched \"[\""; 9950 break; 9951 case '{': 9952 msg = "missing close-brace"; 9953 break; 9954 case '}': 9955 msg = "extra characters after close-brace"; 9956 break; 9957 case '"': 9958 default: 9959 msg = "missing quote"; 9960 break; 9961 } 9962 9963 Jim_SetResultString(interp, msg, -1); 9964 return JIM_ERR; 9965 } 9966 9967 Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr) 9968 { 9969 int line; 9970 Jim_Obj *fileNameObj; 9971 9972 if (objPtr->typePtr == &sourceObjType) { 9973 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; 9974 line = objPtr->internalRep.sourceValue.lineNumber; 9975 } 9976 else if (objPtr->typePtr == &scriptObjType) { 9977 ScriptObj *script = JimGetScript(interp, objPtr); 9978 fileNameObj = script->fileNameObj; 9979 line = script->firstline; 9980 } 9981 else { 9982 fileNameObj = interp->emptyObj; 9983 line = 1; 9984 } 9985 *lineptr = line; 9986 return fileNameObj; 9987 } 9988 9989 void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, 9990 Jim_Obj *fileNameObj, int lineNumber) 9991 { 9992 JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object")); 9993 Jim_FreeIntRep(interp, objPtr); 9994 Jim_IncrRefCount(fileNameObj); 9995 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj; 9996 objPtr->internalRep.sourceValue.lineNumber = lineNumber; 9997 objPtr->typePtr = &sourceObjType; 9998 } 9999 10000 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, 10001 ParseTokenList *tokenlist) 10002 { 10003 int i; 10004 struct ScriptToken *token; 10005 10006 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count); 10007 10008 for (i = 0; i < tokenlist->count; i++) { 10009 const ParseToken *t = &tokenlist->list[i]; 10010 10011 10012 token->type = t->type; 10013 token->objPtr = JimMakeScriptObj(interp, t); 10014 Jim_IncrRefCount(token->objPtr); 10015 token++; 10016 } 10017 10018 script->len = i; 10019 } 10020 10021 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 10022 { 10023 int scriptTextLen; 10024 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); 10025 struct JimParserCtx parser; 10026 struct ScriptObj *script; 10027 ParseTokenList tokenlist; 10028 Jim_Obj *fileNameObj; 10029 int line; 10030 10031 10032 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line); 10033 10034 10035 ScriptTokenListInit(&tokenlist); 10036 10037 JimParserInit(&parser, scriptText, scriptTextLen, line); 10038 while (!parser.eof) { 10039 JimParseScript(&parser); 10040 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, 10041 parser.tline); 10042 } 10043 10044 10045 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0); 10046 10047 10048 script = Jim_Alloc(sizeof(*script)); 10049 memset(script, 0, sizeof(*script)); 10050 script->inUse = 1; 10051 script->fileNameObj = fileNameObj; 10052 Jim_IncrRefCount(script->fileNameObj); 10053 script->missing = parser.missing.ch; 10054 script->linenr = parser.missing.line; 10055 10056 ScriptObjAddTokens(interp, script, &tokenlist); 10057 10058 10059 ScriptTokenListFree(&tokenlist); 10060 10061 10062 Jim_FreeIntRep(interp, objPtr); 10063 Jim_SetIntRepPtr(objPtr, script); 10064 objPtr->typePtr = &scriptObjType; 10065 } 10066 10067 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr) 10068 { 10069 if (objPtr == interp->emptyObj) { 10070 10071 objPtr = interp->nullScriptObj; 10072 } 10073 10074 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) { 10075 JimSetScriptFromAny(interp, objPtr); 10076 } 10077 10078 return (ScriptObj *)Jim_GetIntRepPtr(objPtr); 10079 } 10080 10081 void Jim_InterpIncrProcEpoch(Jim_Interp *interp) 10082 { 10083 interp->procEpoch++; 10084 10085 10086 while (interp->oldCmdCache) { 10087 Jim_Cmd *next = interp->oldCmdCache->prevCmd; 10088 Jim_Free(interp->oldCmdCache); 10089 interp->oldCmdCache = next; 10090 } 10091 interp->oldCmdCacheSize = 0; 10092 } 10093 10094 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr) 10095 { 10096 cmdPtr->inUse++; 10097 } 10098 10099 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr) 10100 { 10101 if (--cmdPtr->inUse == 0) { 10102 if (cmdPtr->isproc) { 10103 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr); 10104 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr); 10105 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj); 10106 if (cmdPtr->u.proc.staticVars) { 10107 Jim_FreeHashTable(cmdPtr->u.proc.staticVars); 10108 Jim_Free(cmdPtr->u.proc.staticVars); 10109 } 10110 } 10111 else { 10112 10113 if (cmdPtr->u.native.delProc) { 10114 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData); 10115 } 10116 } 10117 if (cmdPtr->prevCmd) { 10118 10119 JimDecrCmdRefCount(interp, cmdPtr->prevCmd); 10120 } 10121 10122 cmdPtr->prevCmd = interp->oldCmdCache; 10123 interp->oldCmdCache = cmdPtr; 10124 if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) { 10125 Jim_InterpIncrProcEpoch(interp); 10126 } 10127 } 10128 } 10129 10130 static void JimIncrVarRef(Jim_VarVal *vv) 10131 { 10132 vv->refCount++; 10133 } 10134 10135 static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv) 10136 { 10137 assert(vv->refCount > 0); 10138 if (--vv->refCount == 0) { 10139 if (vv->objPtr) { 10140 Jim_DecrRefCount(interp, vv->objPtr); 10141 } 10142 Jim_Free(vv); 10143 } 10144 } 10145 10146 static void JimVariablesHTValDestructor(void *interp, void *val) 10147 { 10148 JimDecrVarRef(interp, val); 10149 } 10150 10151 static unsigned int JimObjectHTHashFunction(const void *key) 10152 { 10153 Jim_Obj *keyObj = (Jim_Obj *)key; 10154 int length; 10155 const char *string; 10156 10157 #ifdef JIM_OPTIMIZATION 10158 if (JimIsWide(keyObj) && keyObj->bytes == NULL) { 10159 10160 jim_wide objValue = JimWideValue(keyObj); 10161 if (objValue > INT_MIN && objValue < INT_MAX) { 10162 unsigned result = 0; 10163 unsigned value = (unsigned)objValue; 10164 10165 if (objValue < 0) { 10166 value = (unsigned)-objValue; 10167 } 10168 10169 10170 do { 10171 result += (result << 3) + (value % 10 + '0'); 10172 value /= 10; 10173 } while (value); 10174 10175 if (objValue < 0) { 10176 result += (result << 3) + '-'; 10177 } 10178 return result; 10179 } 10180 } 10181 #endif 10182 string = Jim_GetString(keyObj, &length); 10183 return Jim_GenHashFunction((const unsigned char *)string, length); 10184 } 10185 10186 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2) 10187 { 10188 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2); 10189 } 10190 10191 static void *JimObjectHTKeyValDup(void *privdata, const void *val) 10192 { 10193 Jim_IncrRefCount((Jim_Obj *)val); 10194 return (void *)val; 10195 } 10196 10197 static void JimObjectHTKeyValDestructor(void *interp, void *val) 10198 { 10199 Jim_DecrRefCount(interp, (Jim_Obj *)val); 10200 } 10201 10202 10203 static void *JimVariablesHTValDup(void *privdata, const void *val) 10204 { 10205 JimIncrVarRef((Jim_VarVal *)val); 10206 return (void *)val; 10207 } 10208 10209 static const Jim_HashTableType JimVariablesHashTableType = { 10210 JimObjectHTHashFunction, 10211 JimObjectHTKeyValDup, 10212 JimVariablesHTValDup, 10213 JimObjectHTKeyCompare, 10214 JimObjectHTKeyValDestructor, 10215 JimVariablesHTValDestructor 10216 }; 10217 10218 10219 static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length) 10220 { 10221 int len; 10222 const char *str = Jim_GetString(objPtr, &len); 10223 if (len >= 2 && str[0] == ':' && str[1] == ':') { 10224 while (len && *str == ':') { 10225 len--; 10226 str++; 10227 } 10228 } 10229 *length = len; 10230 return str; 10231 } 10232 10233 static unsigned int JimCommandsHT_HashFunction(const void *key) 10234 { 10235 int len; 10236 const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len); 10237 return Jim_GenHashFunction((const unsigned char *)str, len); 10238 } 10239 10240 static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2) 10241 { 10242 int len1, len2; 10243 const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1); 10244 const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2); 10245 return len1 == len2 && *str1 == *str2 && memcmp(str1, str2, len1) == 0; 10246 } 10247 10248 static void JimCommandsHT_ValDestructor(void *interp, void *val) 10249 { 10250 JimDecrCmdRefCount(interp, val); 10251 } 10252 10253 static const Jim_HashTableType JimCommandsHashTableType = { 10254 JimCommandsHT_HashFunction, 10255 JimObjectHTKeyValDup, 10256 NULL, 10257 JimCommandsHT_KeyCompare, 10258 JimObjectHTKeyValDestructor, 10259 JimCommandsHT_ValDestructor 10260 }; 10261 10262 10263 10264 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr) 10265 { 10266 #ifdef jim_ext_namespace 10267 Jim_Obj *resultObj; 10268 10269 const char *name = Jim_String(nameObjPtr); 10270 if (name[0] == ':' && name[1] == ':') { 10271 return nameObjPtr; 10272 } 10273 Jim_IncrRefCount(nameObjPtr); 10274 resultObj = Jim_NewStringObj(interp, "::", -1); 10275 Jim_AppendObj(interp, resultObj, nameObjPtr); 10276 Jim_DecrRefCount(interp, nameObjPtr); 10277 10278 return resultObj; 10279 #else 10280 return nameObjPtr; 10281 #endif 10282 } 10283 10284 static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr) 10285 { 10286 #ifdef jim_ext_namespace 10287 if (Jim_Length(interp->framePtr->nsObj)) { 10288 int len; 10289 const char *name = Jim_GetString(objPtr, &len); 10290 if (len < 2 || name[0] != ':' || name[1] != ':') { 10291 10292 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj); 10293 Jim_AppendStrings(interp, objPtr, "::", name, NULL); 10294 } 10295 } 10296 #endif 10297 Jim_IncrRefCount(objPtr); 10298 return objPtr; 10299 } 10300 10301 static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd) 10302 { 10303 JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name")); 10304 10305 if (interp->local) { 10306 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr); 10307 if (he) { 10308 10309 cmd->prevCmd = Jim_GetHashEntryVal(he); 10310 Jim_SetHashVal(&interp->commands, he, cmd); 10311 10312 Jim_InterpIncrProcEpoch(interp); 10313 return; 10314 } 10315 } 10316 10317 10318 10319 Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd); 10320 } 10321 10322 int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj, 10323 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc) 10324 { 10325 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr)); 10326 10327 10328 memset(cmdPtr, 0, sizeof(*cmdPtr)); 10329 cmdPtr->inUse = 1; 10330 cmdPtr->u.native.delProc = delProc; 10331 cmdPtr->u.native.cmdProc = cmdProc; 10332 cmdPtr->u.native.privData = privData; 10333 10334 Jim_IncrRefCount(cmdNameObj); 10335 JimCreateCommand(interp, cmdNameObj, cmdPtr); 10336 Jim_DecrRefCount(interp, cmdNameObj); 10337 10338 return JIM_OK; 10339 } 10340 10341 10342 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr, 10343 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc) 10344 { 10345 return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc); 10346 } 10347 10348 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr) 10349 { 10350 int len, i; 10351 10352 len = Jim_ListLength(interp, staticsListObjPtr); 10353 if (len == 0) { 10354 return JIM_OK; 10355 } 10356 10357 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable)); 10358 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp); 10359 for (i = 0; i < len; i++) { 10360 Jim_Obj *initObjPtr = NULL; 10361 Jim_Obj *nameObjPtr; 10362 Jim_VarVal *vv = NULL; 10363 Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i); 10364 int subLen = Jim_ListLength(interp, objPtr); 10365 int byref = 0; 10366 10367 10368 if (subLen != 1 && subLen != 2) { 10369 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"", 10370 objPtr); 10371 return JIM_ERR; 10372 } 10373 10374 nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0); 10375 10376 10377 if (subLen == 1) { 10378 int len; 10379 const char *pt = Jim_GetString(nameObjPtr, &len); 10380 if (*pt == '&') { 10381 10382 nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1); 10383 byref = 1; 10384 } 10385 } 10386 Jim_IncrRefCount(nameObjPtr); 10387 10388 if (subLen == 1) { 10389 switch (SetVariableFromAny(interp, nameObjPtr)) { 10390 case JIM_DICT_SUGAR: 10391 10392 if (byref) { 10393 Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr); 10394 } 10395 else { 10396 Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr); 10397 } 10398 Jim_DecrRefCount(interp, nameObjPtr); 10399 return JIM_ERR; 10400 10401 case JIM_OK: 10402 if (byref) { 10403 vv = nameObjPtr->internalRep.varValue.vv; 10404 } 10405 else { 10406 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE); 10407 } 10408 break; 10409 10410 case JIM_ERR: 10411 10412 Jim_SetResultFormatted(interp, 10413 "variable for initialization of static \"%#s\" not found in the local context", 10414 nameObjPtr); 10415 Jim_DecrRefCount(interp, nameObjPtr); 10416 return JIM_ERR; 10417 } 10418 } 10419 else { 10420 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1); 10421 } 10422 10423 if (vv == NULL) { 10424 vv = Jim_Alloc(sizeof(*vv)); 10425 vv->objPtr = initObjPtr; 10426 Jim_IncrRefCount(vv->objPtr); 10427 vv->linkFramePtr = NULL; 10428 vv->refCount = 0; 10429 } 10430 10431 if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) { 10432 Jim_SetResultFormatted(interp, 10433 "static variable name \"%#s\" duplicated in statics list", nameObjPtr); 10434 JimIncrVarRef(vv); 10435 JimDecrVarRef(interp, vv); 10436 Jim_DecrRefCount(interp, nameObjPtr); 10437 return JIM_ERR; 10438 } 10439 10440 Jim_DecrRefCount(interp, nameObjPtr); 10441 } 10442 return JIM_OK; 10443 } 10444 10445 10446 #ifdef jim_ext_namespace 10447 static const char *Jim_memrchr(const char *p, int c, int len) 10448 { 10449 int i; 10450 for (i = len; i > 0; i--) { 10451 if (p[i] == c) { 10452 return p + i; 10453 } 10454 } 10455 return NULL; 10456 } 10457 #endif 10458 10459 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr) 10460 { 10461 #ifdef jim_ext_namespace 10462 if (cmdPtr->isproc) { 10463 int len; 10464 const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len); 10465 10466 const char *pt = Jim_memrchr(cmdname, ':', len); 10467 if (pt && pt != cmdname && pt[-1] == ':') { 10468 pt++; 10469 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj); 10470 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2); 10471 Jim_IncrRefCount(cmdPtr->u.proc.nsObj); 10472 10473 Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname)); 10474 if (Jim_FindHashEntry(&interp->commands, tempObj)) { 10475 10476 Jim_InterpIncrProcEpoch(interp); 10477 } 10478 Jim_FreeNewObj(interp, tempObj); 10479 } 10480 } 10481 #endif 10482 } 10483 10484 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr, 10485 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj) 10486 { 10487 Jim_Cmd *cmdPtr; 10488 int argListLen; 10489 int i; 10490 10491 argListLen = Jim_ListLength(interp, argListObjPtr); 10492 10493 10494 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen); 10495 assert(cmdPtr); 10496 memset(cmdPtr, 0, sizeof(*cmdPtr)); 10497 cmdPtr->inUse = 1; 10498 cmdPtr->isproc = 1; 10499 cmdPtr->u.proc.argListObjPtr = argListObjPtr; 10500 cmdPtr->u.proc.argListLen = argListLen; 10501 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr; 10502 cmdPtr->u.proc.argsPos = -1; 10503 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1); 10504 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj; 10505 Jim_IncrRefCount(argListObjPtr); 10506 Jim_IncrRefCount(bodyObjPtr); 10507 Jim_IncrRefCount(cmdPtr->u.proc.nsObj); 10508 10509 10510 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) { 10511 goto err; 10512 } 10513 10514 10515 10516 for (i = 0; i < argListLen; i++) { 10517 Jim_Obj *argPtr; 10518 Jim_Obj *nameObjPtr; 10519 Jim_Obj *defaultObjPtr; 10520 int len; 10521 10522 10523 argPtr = Jim_ListGetIndex(interp, argListObjPtr, i); 10524 len = Jim_ListLength(interp, argPtr); 10525 if (len == 0) { 10526 Jim_SetResultString(interp, "argument with no name", -1); 10527 err: 10528 JimDecrCmdRefCount(interp, cmdPtr); 10529 return NULL; 10530 } 10531 if (len > 2) { 10532 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr); 10533 goto err; 10534 } 10535 10536 if (len == 2) { 10537 10538 nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0); 10539 defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1); 10540 } 10541 else { 10542 10543 nameObjPtr = argPtr; 10544 defaultObjPtr = NULL; 10545 } 10546 10547 10548 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) { 10549 if (cmdPtr->u.proc.argsPos >= 0) { 10550 Jim_SetResultString(interp, "'args' specified more than once", -1); 10551 goto err; 10552 } 10553 cmdPtr->u.proc.argsPos = i; 10554 } 10555 else { 10556 if (len == 2) { 10557 cmdPtr->u.proc.optArity++; 10558 } 10559 else { 10560 cmdPtr->u.proc.reqArity++; 10561 } 10562 } 10563 10564 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr; 10565 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr; 10566 } 10567 10568 return cmdPtr; 10569 } 10570 10571 int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj) 10572 { 10573 int ret = JIM_OK; 10574 10575 nameObj = JimQualifyName(interp, nameObj); 10576 10577 if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) { 10578 Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj); 10579 ret = JIM_ERR; 10580 } 10581 Jim_DecrRefCount(interp, nameObj); 10582 10583 return ret; 10584 } 10585 10586 int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj) 10587 { 10588 int ret = JIM_ERR; 10589 Jim_HashEntry *he; 10590 Jim_Cmd *cmdPtr; 10591 10592 if (Jim_Length(newNameObj) == 0) { 10593 return Jim_DeleteCommand(interp, oldNameObj); 10594 } 10595 10596 10597 10598 oldNameObj = JimQualifyName(interp, oldNameObj); 10599 newNameObj = JimQualifyName(interp, newNameObj); 10600 10601 10602 he = Jim_FindHashEntry(&interp->commands, oldNameObj); 10603 if (he == NULL) { 10604 Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj); 10605 } 10606 else if (Jim_FindHashEntry(&interp->commands, newNameObj)) { 10607 Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj); 10608 } 10609 else { 10610 cmdPtr = Jim_GetHashEntryVal(he); 10611 if (cmdPtr->prevCmd) { 10612 Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj); 10613 } 10614 else { 10615 10616 JimIncrCmdRefCount(cmdPtr); 10617 JimUpdateProcNamespace(interp, cmdPtr, newNameObj); 10618 Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr); 10619 10620 10621 Jim_DeleteHashEntry(&interp->commands, oldNameObj); 10622 10623 10624 Jim_InterpIncrProcEpoch(interp); 10625 10626 ret = JIM_OK; 10627 } 10628 } 10629 10630 Jim_DecrRefCount(interp, oldNameObj); 10631 Jim_DecrRefCount(interp, newNameObj); 10632 10633 return ret; 10634 } 10635 10636 10637 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 10638 { 10639 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj); 10640 } 10641 10642 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 10643 { 10644 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue; 10645 dupPtr->typePtr = srcPtr->typePtr; 10646 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj); 10647 } 10648 10649 static const Jim_ObjType commandObjType = { 10650 "command", 10651 FreeCommandInternalRep, 10652 DupCommandInternalRep, 10653 NULL, 10654 JIM_TYPE_REFERENCES, 10655 }; 10656 10657 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 10658 { 10659 Jim_Cmd *cmd; 10660 10661 if (objPtr->typePtr == &commandObjType 10662 && objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch 10663 #ifdef jim_ext_namespace 10664 && Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj) 10665 #endif 10666 && objPtr->internalRep.cmdValue.cmdPtr->inUse) { 10667 10668 cmd = objPtr->internalRep.cmdValue.cmdPtr; 10669 } 10670 else { 10671 Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr); 10672 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj); 10673 #ifdef jim_ext_namespace 10674 if (he == NULL && Jim_Length(interp->framePtr->nsObj)) { 10675 he = Jim_FindHashEntry(&interp->commands, objPtr); 10676 } 10677 #endif 10678 if (he == NULL) { 10679 if (flags & JIM_ERRMSG) { 10680 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr); 10681 } 10682 Jim_DecrRefCount(interp, qualifiedNameObj); 10683 return NULL; 10684 } 10685 cmd = Jim_GetHashEntryVal(he); 10686 10687 cmd->cmdNameObj = Jim_GetHashEntryKey(he); 10688 10689 10690 Jim_FreeIntRep(interp, objPtr); 10691 objPtr->typePtr = &commandObjType; 10692 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch; 10693 objPtr->internalRep.cmdValue.cmdPtr = cmd; 10694 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj; 10695 Jim_IncrRefCount(interp->framePtr->nsObj); 10696 Jim_DecrRefCount(interp, qualifiedNameObj); 10697 } 10698 while (cmd->u.proc.upcall) { 10699 cmd = cmd->prevCmd; 10700 } 10701 return cmd; 10702 } 10703 10704 10705 10706 static const Jim_ObjType variableObjType = { 10707 "variable", 10708 NULL, 10709 NULL, 10710 NULL, 10711 JIM_TYPE_REFERENCES, 10712 }; 10713 10714 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 10715 { 10716 const char *varName; 10717 Jim_CallFrame *framePtr; 10718 int global; 10719 int len; 10720 Jim_VarVal *vv; 10721 10722 10723 if (objPtr->typePtr == &variableObjType) { 10724 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr; 10725 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) { 10726 10727 return JIM_OK; 10728 } 10729 10730 } 10731 else if (objPtr->typePtr == &dictSubstObjType) { 10732 return JIM_DICT_SUGAR; 10733 } 10734 10735 varName = Jim_GetString(objPtr, &len); 10736 10737 10738 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) { 10739 return JIM_DICT_SUGAR; 10740 } 10741 10742 if (varName[0] == ':' && varName[1] == ':') { 10743 while (*varName == ':') { 10744 varName++; 10745 len--; 10746 } 10747 global = 1; 10748 framePtr = interp->topFramePtr; 10749 10750 Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len); 10751 vv = JimFindVariable(&framePtr->vars, tempObj); 10752 Jim_FreeNewObj(interp, tempObj); 10753 } 10754 else { 10755 global = 0; 10756 framePtr = interp->framePtr; 10757 10758 vv = JimFindVariable(&framePtr->vars, objPtr); 10759 if (vv == NULL && framePtr->staticVars) { 10760 10761 vv = JimFindVariable(framePtr->staticVars, objPtr); 10762 } 10763 } 10764 10765 if (vv == NULL) { 10766 return JIM_ERR; 10767 } 10768 10769 10770 Jim_FreeIntRep(interp, objPtr); 10771 objPtr->typePtr = &variableObjType; 10772 objPtr->internalRep.varValue.callFrameId = framePtr->id; 10773 objPtr->internalRep.varValue.vv = vv; 10774 objPtr->internalRep.varValue.global = global; 10775 return JIM_OK; 10776 } 10777 10778 10779 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr); 10780 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags); 10781 10782 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv) 10783 { 10784 return Jim_AddHashEntry(ht, nameObjPtr, vv); 10785 } 10786 10787 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr) 10788 { 10789 Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr); 10790 if (he) { 10791 return (Jim_VarVal *)Jim_GetHashEntryVal(he); 10792 } 10793 return NULL; 10794 } 10795 10796 static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr) 10797 { 10798 return Jim_DeleteHashEntry(ht, nameObjPtr); 10799 } 10800 10801 static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr) 10802 { 10803 const char *name; 10804 Jim_CallFrame *framePtr; 10805 int global; 10806 int len; 10807 10808 10809 Jim_VarVal *vv = Jim_Alloc(sizeof(*vv)); 10810 10811 vv->objPtr = valObjPtr; 10812 Jim_IncrRefCount(valObjPtr); 10813 vv->linkFramePtr = NULL; 10814 vv->refCount = 0; 10815 10816 name = Jim_GetString(nameObjPtr, &len); 10817 if (name[0] == ':' && name[1] == ':') { 10818 while (*name == ':') { 10819 name++; 10820 len--; 10821 } 10822 framePtr = interp->topFramePtr; 10823 global = 1; 10824 JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv); 10825 } 10826 else { 10827 framePtr = interp->framePtr; 10828 global = 0; 10829 JimSetNewVariable(&framePtr->vars, nameObjPtr, vv); 10830 } 10831 10832 10833 Jim_FreeIntRep(interp, nameObjPtr); 10834 nameObjPtr->typePtr = &variableObjType; 10835 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id; 10836 nameObjPtr->internalRep.varValue.vv = vv; 10837 nameObjPtr->internalRep.varValue.global = global; 10838 10839 return vv; 10840 } 10841 10842 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr) 10843 { 10844 int err; 10845 Jim_VarVal *vv; 10846 10847 switch (SetVariableFromAny(interp, nameObjPtr)) { 10848 case JIM_DICT_SUGAR: 10849 return JimDictSugarSet(interp, nameObjPtr, valObjPtr); 10850 10851 case JIM_ERR: 10852 JimCreateVariable(interp, nameObjPtr, valObjPtr); 10853 break; 10854 10855 case JIM_OK: 10856 vv = nameObjPtr->internalRep.varValue.vv; 10857 if (vv->linkFramePtr == NULL) { 10858 Jim_IncrRefCount(valObjPtr); 10859 Jim_DecrRefCount(interp, vv->objPtr); 10860 vv->objPtr = valObjPtr; 10861 } 10862 else { 10863 Jim_CallFrame *savedCallFrame; 10864 10865 savedCallFrame = interp->framePtr; 10866 interp->framePtr = vv->linkFramePtr; 10867 err = Jim_SetVariable(interp, vv->objPtr, valObjPtr); 10868 interp->framePtr = savedCallFrame; 10869 if (err != JIM_OK) 10870 return err; 10871 } 10872 } 10873 return JIM_OK; 10874 } 10875 10876 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr) 10877 { 10878 Jim_Obj *nameObjPtr; 10879 int result; 10880 10881 nameObjPtr = Jim_NewStringObj(interp, name, -1); 10882 Jim_IncrRefCount(nameObjPtr); 10883 result = Jim_SetVariable(interp, nameObjPtr, objPtr); 10884 Jim_DecrRefCount(interp, nameObjPtr); 10885 return result; 10886 } 10887 10888 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr) 10889 { 10890 Jim_CallFrame *savedFramePtr; 10891 int result; 10892 10893 savedFramePtr = interp->framePtr; 10894 interp->framePtr = interp->topFramePtr; 10895 result = Jim_SetVariableStr(interp, name, objPtr); 10896 interp->framePtr = savedFramePtr; 10897 return result; 10898 } 10899 10900 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val) 10901 { 10902 Jim_Obj *valObjPtr; 10903 int result; 10904 10905 valObjPtr = Jim_NewStringObj(interp, val, -1); 10906 Jim_IncrRefCount(valObjPtr); 10907 result = Jim_SetVariableStr(interp, name, valObjPtr); 10908 Jim_DecrRefCount(interp, valObjPtr); 10909 return result; 10910 } 10911 10912 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr, 10913 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame) 10914 { 10915 const char *varName; 10916 const char *targetName; 10917 Jim_CallFrame *framePtr; 10918 Jim_VarVal *vv; 10919 int len; 10920 int varnamelen; 10921 10922 10923 switch (SetVariableFromAny(interp, nameObjPtr)) { 10924 case JIM_DICT_SUGAR: 10925 10926 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr); 10927 return JIM_ERR; 10928 10929 case JIM_OK: 10930 vv = nameObjPtr->internalRep.varValue.vv; 10931 10932 if (vv->linkFramePtr == NULL) { 10933 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr); 10934 return JIM_ERR; 10935 } 10936 10937 10938 vv->linkFramePtr = NULL; 10939 break; 10940 } 10941 10942 10943 10944 varName = Jim_GetString(nameObjPtr, &varnamelen); 10945 10946 if (varName[0] == ':' && varName[1] == ':') { 10947 while (*varName == ':') { 10948 varName++; 10949 varnamelen--; 10950 } 10951 10952 framePtr = interp->topFramePtr; 10953 } 10954 else { 10955 framePtr = interp->framePtr; 10956 } 10957 10958 targetName = Jim_GetString(targetNameObjPtr, &len); 10959 if (targetName[0] == ':' && targetName[1] == ':') { 10960 while (*targetName == ':') { 10961 targetName++; 10962 len--; 10963 } 10964 targetNameObjPtr = Jim_NewStringObj(interp, targetName, len); 10965 targetCallFrame = interp->topFramePtr; 10966 } 10967 Jim_IncrRefCount(targetNameObjPtr); 10968 10969 if (framePtr->level < targetCallFrame->level) { 10970 Jim_SetResultFormatted(interp, 10971 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable", 10972 nameObjPtr); 10973 Jim_DecrRefCount(interp, targetNameObjPtr); 10974 return JIM_ERR; 10975 } 10976 10977 10978 if (framePtr == targetCallFrame) { 10979 Jim_Obj *objPtr = targetNameObjPtr; 10980 10981 10982 while (1) { 10983 if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) { 10984 Jim_SetResultString(interp, "can't upvar from variable to itself", -1); 10985 Jim_DecrRefCount(interp, targetNameObjPtr); 10986 return JIM_ERR; 10987 } 10988 if (SetVariableFromAny(interp, objPtr) != JIM_OK) 10989 break; 10990 vv = objPtr->internalRep.varValue.vv; 10991 if (vv->linkFramePtr != targetCallFrame) 10992 break; 10993 objPtr = vv->objPtr; 10994 } 10995 } 10996 10997 10998 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr); 10999 11000 nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame; 11001 Jim_DecrRefCount(interp, targetNameObjPtr); 11002 return JIM_OK; 11003 } 11004 11005 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) 11006 { 11007 if (interp->safeexpr) { 11008 return nameObjPtr; 11009 } 11010 switch (SetVariableFromAny(interp, nameObjPtr)) { 11011 case JIM_OK:{ 11012 Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv; 11013 11014 if (vv->linkFramePtr == NULL) { 11015 return vv->objPtr; 11016 } 11017 else { 11018 Jim_Obj *objPtr; 11019 11020 11021 Jim_CallFrame *savedCallFrame = interp->framePtr; 11022 11023 interp->framePtr = vv->linkFramePtr; 11024 objPtr = Jim_GetVariable(interp, vv->objPtr, flags); 11025 interp->framePtr = savedCallFrame; 11026 if (objPtr) { 11027 return objPtr; 11028 } 11029 11030 } 11031 } 11032 break; 11033 11034 case JIM_DICT_SUGAR: 11035 11036 return JimDictSugarGet(interp, nameObjPtr, flags); 11037 } 11038 if (flags & JIM_ERRMSG) { 11039 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr); 11040 } 11041 return NULL; 11042 } 11043 11044 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) 11045 { 11046 Jim_CallFrame *savedFramePtr; 11047 Jim_Obj *objPtr; 11048 11049 savedFramePtr = interp->framePtr; 11050 interp->framePtr = interp->topFramePtr; 11051 objPtr = Jim_GetVariable(interp, nameObjPtr, flags); 11052 interp->framePtr = savedFramePtr; 11053 11054 return objPtr; 11055 } 11056 11057 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags) 11058 { 11059 Jim_Obj *nameObjPtr, *varObjPtr; 11060 11061 nameObjPtr = Jim_NewStringObj(interp, name, -1); 11062 Jim_IncrRefCount(nameObjPtr); 11063 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags); 11064 Jim_DecrRefCount(interp, nameObjPtr); 11065 return varObjPtr; 11066 } 11067 11068 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags) 11069 { 11070 Jim_CallFrame *savedFramePtr; 11071 Jim_Obj *objPtr; 11072 11073 savedFramePtr = interp->framePtr; 11074 interp->framePtr = interp->topFramePtr; 11075 objPtr = Jim_GetVariableStr(interp, name, flags); 11076 interp->framePtr = savedFramePtr; 11077 11078 return objPtr; 11079 } 11080 11081 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) 11082 { 11083 Jim_VarVal *vv; 11084 int retval; 11085 Jim_CallFrame *framePtr; 11086 11087 retval = SetVariableFromAny(interp, nameObjPtr); 11088 if (retval == JIM_DICT_SUGAR) { 11089 11090 return JimDictSugarSet(interp, nameObjPtr, NULL); 11091 } 11092 else if (retval == JIM_OK) { 11093 vv = nameObjPtr->internalRep.varValue.vv; 11094 11095 11096 if (vv->linkFramePtr) { 11097 framePtr = interp->framePtr; 11098 interp->framePtr = vv->linkFramePtr; 11099 retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE); 11100 interp->framePtr = framePtr; 11101 } 11102 else { 11103 if (nameObjPtr->internalRep.varValue.global) { 11104 int len; 11105 const char *name = Jim_GetString(nameObjPtr, &len); 11106 while (*name == ':') { 11107 name++; 11108 len--; 11109 } 11110 framePtr = interp->topFramePtr; 11111 Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len); 11112 retval = JimUnsetVariable(&framePtr->vars, tempObj); 11113 Jim_FreeNewObj(interp, tempObj); 11114 } 11115 else { 11116 framePtr = interp->framePtr; 11117 retval = JimUnsetVariable(&framePtr->vars, nameObjPtr); 11118 } 11119 11120 if (retval == JIM_OK) { 11121 11122 framePtr->id = interp->callFrameEpoch++; 11123 } 11124 } 11125 } 11126 if (retval != JIM_OK && (flags & JIM_ERRMSG)) { 11127 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr); 11128 } 11129 return retval; 11130 } 11131 11132 11133 11134 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr, 11135 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr) 11136 { 11137 const char *str, *p; 11138 int len, keyLen; 11139 Jim_Obj *varObjPtr, *keyObjPtr; 11140 11141 str = Jim_GetString(objPtr, &len); 11142 11143 p = strchr(str, '('); 11144 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str)); 11145 11146 varObjPtr = Jim_NewStringObj(interp, str, p - str); 11147 11148 p++; 11149 keyLen = (str + len) - p; 11150 if (str[len - 1] == ')') { 11151 keyLen--; 11152 } 11153 11154 11155 keyObjPtr = Jim_NewStringObj(interp, p, keyLen); 11156 11157 Jim_IncrRefCount(varObjPtr); 11158 Jim_IncrRefCount(keyObjPtr); 11159 *varPtrPtr = varObjPtr; 11160 *keyPtrPtr = keyObjPtr; 11161 } 11162 11163 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr) 11164 { 11165 int err; 11166 11167 SetDictSubstFromAny(interp, objPtr); 11168 11169 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, 11170 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST); 11171 11172 if (err == JIM_OK) { 11173 11174 Jim_SetEmptyResult(interp); 11175 } 11176 else { 11177 if (!valObjPtr) { 11178 11179 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) { 11180 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array", 11181 objPtr); 11182 return err; 11183 } 11184 } 11185 11186 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array", 11187 (valObjPtr ? "set" : "unset"), objPtr); 11188 } 11189 return err; 11190 } 11191 11192 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr, 11193 Jim_Obj *keyObjPtr, int flags) 11194 { 11195 Jim_Obj *dictObjPtr; 11196 Jim_Obj *resObjPtr = NULL; 11197 int ret; 11198 11199 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG); 11200 if (!dictObjPtr) { 11201 return NULL; 11202 } 11203 11204 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE); 11205 if (ret != JIM_OK) { 11206 Jim_SetResultFormatted(interp, 11207 "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr, 11208 ret < 0 ? "variable isn't" : "no such element in"); 11209 } 11210 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) { 11211 11212 Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr)); 11213 } 11214 11215 return resObjPtr; 11216 } 11217 11218 11219 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 11220 { 11221 SetDictSubstFromAny(interp, objPtr); 11222 11223 return JimDictExpandArrayVariable(interp, 11224 objPtr->internalRep.dictSubstValue.varNameObjPtr, 11225 objPtr->internalRep.dictSubstValue.indexObjPtr, flags); 11226 } 11227 11228 11229 11230 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 11231 { 11232 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr); 11233 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); 11234 } 11235 11236 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 11237 { 11238 11239 dupPtr->internalRep = srcPtr->internalRep; 11240 11241 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr); 11242 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr); 11243 } 11244 11245 11246 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 11247 { 11248 if (objPtr->typePtr != &dictSubstObjType) { 11249 Jim_Obj *varObjPtr, *keyObjPtr; 11250 11251 if (objPtr->typePtr == &interpolatedObjType) { 11252 11253 11254 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr; 11255 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr; 11256 11257 Jim_IncrRefCount(varObjPtr); 11258 Jim_IncrRefCount(keyObjPtr); 11259 } 11260 else { 11261 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr); 11262 } 11263 11264 Jim_FreeIntRep(interp, objPtr); 11265 objPtr->typePtr = &dictSubstObjType; 11266 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr; 11267 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr; 11268 } 11269 } 11270 11271 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr) 11272 { 11273 Jim_Obj *resObjPtr = NULL; 11274 Jim_Obj *substKeyObjPtr = NULL; 11275 11276 if (interp->safeexpr) { 11277 return objPtr; 11278 } 11279 11280 SetDictSubstFromAny(interp, objPtr); 11281 11282 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr, 11283 &substKeyObjPtr, JIM_NONE) 11284 != JIM_OK) { 11285 return NULL; 11286 } 11287 Jim_IncrRefCount(substKeyObjPtr); 11288 resObjPtr = 11289 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, 11290 substKeyObjPtr, 0); 11291 Jim_DecrRefCount(interp, substKeyObjPtr); 11292 11293 return resObjPtr; 11294 } 11295 11296 11297 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj) 11298 { 11299 Jim_CallFrame *cf; 11300 11301 if (interp->freeFramesList) { 11302 cf = interp->freeFramesList; 11303 interp->freeFramesList = cf->next; 11304 11305 cf->argv = NULL; 11306 cf->argc = 0; 11307 cf->procArgsObjPtr = NULL; 11308 cf->procBodyObjPtr = NULL; 11309 cf->next = NULL; 11310 cf->staticVars = NULL; 11311 cf->localCommands = NULL; 11312 cf->tailcallObj = NULL; 11313 cf->tailcallCmd = NULL; 11314 } 11315 else { 11316 cf = Jim_Alloc(sizeof(*cf)); 11317 memset(cf, 0, sizeof(*cf)); 11318 11319 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp); 11320 } 11321 11322 cf->id = interp->callFrameEpoch++; 11323 cf->parent = parent; 11324 cf->level = parent ? parent->level + 1 : 0; 11325 cf->nsObj = nsObj; 11326 Jim_IncrRefCount(nsObj); 11327 11328 return cf; 11329 } 11330 11331 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands) 11332 { 11333 11334 if (localCommands) { 11335 Jim_Obj *cmdNameObj; 11336 11337 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) { 11338 Jim_HashTable *ht = &interp->commands; 11339 Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj); 11340 if (he) { 11341 Jim_Cmd *cmd = Jim_GetHashEntryVal(he); 11342 if (cmd->prevCmd) { 11343 Jim_Cmd *prevCmd = cmd->prevCmd; 11344 cmd->prevCmd = NULL; 11345 11346 11347 JimDecrCmdRefCount(interp, cmd); 11348 11349 11350 Jim_SetHashVal(ht, he, prevCmd); 11351 } 11352 else { 11353 Jim_DeleteHashEntry(ht, cmdNameObj); 11354 } 11355 } 11356 Jim_DecrRefCount(interp, cmdNameObj); 11357 } 11358 Jim_FreeStack(localCommands); 11359 Jim_Free(localCommands); 11360 } 11361 return JIM_OK; 11362 } 11363 11364 static int JimInvokeDefer(Jim_Interp *interp, int retcode) 11365 { 11366 Jim_Obj *objPtr; 11367 11368 11369 if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) { 11370 return retcode; 11371 } 11372 objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE); 11373 11374 if (objPtr) { 11375 int ret = JIM_OK; 11376 int i; 11377 int listLen = Jim_ListLength(interp, objPtr); 11378 Jim_Obj *resultObjPtr; 11379 11380 Jim_IncrRefCount(objPtr); 11381 11382 resultObjPtr = Jim_GetResult(interp); 11383 Jim_IncrRefCount(resultObjPtr); 11384 Jim_SetEmptyResult(interp); 11385 11386 11387 for (i = listLen; i > 0; i--) { 11388 11389 Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1); 11390 ret = Jim_EvalObj(interp, scriptObjPtr); 11391 if (ret != JIM_OK) { 11392 break; 11393 } 11394 } 11395 11396 if (ret == JIM_OK || retcode == JIM_ERR) { 11397 11398 Jim_SetResult(interp, resultObjPtr); 11399 } 11400 else { 11401 retcode = ret; 11402 } 11403 11404 Jim_DecrRefCount(interp, resultObjPtr); 11405 Jim_DecrRefCount(interp, objPtr); 11406 } 11407 return retcode; 11408 } 11409 11410 #define JIM_FCF_FULL 0 11411 #define JIM_FCF_REUSE 1 11412 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action) 11413 { 11414 JimDeleteLocalProcs(interp, cf->localCommands); 11415 11416 if (cf->procArgsObjPtr) 11417 Jim_DecrRefCount(interp, cf->procArgsObjPtr); 11418 if (cf->procBodyObjPtr) 11419 Jim_DecrRefCount(interp, cf->procBodyObjPtr); 11420 Jim_DecrRefCount(interp, cf->nsObj); 11421 if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE) 11422 Jim_FreeHashTable(&cf->vars); 11423 else { 11424 Jim_ClearHashTable(&cf->vars); 11425 } 11426 cf->next = interp->freeFramesList; 11427 interp->freeFramesList = cf; 11428 } 11429 11430 11431 11432 int Jim_IsBigEndian(void) 11433 { 11434 union { 11435 unsigned short s; 11436 unsigned char c[2]; 11437 } uval = {0x0102}; 11438 11439 return uval.c[0] == 1; 11440 } 11441 11442 11443 Jim_Interp *Jim_CreateInterp(void) 11444 { 11445 Jim_Interp *i = Jim_Alloc(sizeof(*i)); 11446 11447 memset(i, 0, sizeof(*i)); 11448 11449 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH; 11450 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH; 11451 i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 11452 11453 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i); 11454 #ifdef JIM_REFERENCES 11455 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i); 11456 #endif 11457 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i); 11458 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL); 11459 i->emptyObj = Jim_NewEmptyStringObj(i); 11460 i->trueObj = Jim_NewIntObj(i, 1); 11461 i->falseObj = Jim_NewIntObj(i, 0); 11462 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj); 11463 i->result = i->emptyObj; 11464 i->stackTrace = Jim_NewListObj(i, NULL, 0); 11465 i->unknown = Jim_NewStringObj(i, "unknown", -1); 11466 i->defer = Jim_NewStringObj(i, "jim::defer", -1); 11467 i->errorProc = i->emptyObj; 11468 i->nullScriptObj = Jim_NewEmptyStringObj(i); 11469 i->evalFrame = &i->topEvalFrame; 11470 i->currentFilenameObj = Jim_NewEmptyStringObj(i); 11471 Jim_IncrRefCount(i->emptyObj); 11472 Jim_IncrRefCount(i->result); 11473 Jim_IncrRefCount(i->stackTrace); 11474 Jim_IncrRefCount(i->unknown); 11475 Jim_IncrRefCount(i->defer); 11476 Jim_IncrRefCount(i->nullScriptObj); 11477 Jim_IncrRefCount(i->errorProc); 11478 Jim_IncrRefCount(i->trueObj); 11479 Jim_IncrRefCount(i->falseObj); 11480 Jim_IncrRefCount(i->currentFilenameObj); 11481 11482 11483 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY); 11484 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0"); 11485 11486 Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim"); 11487 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS); 11488 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM); 11489 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR); 11490 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian"); 11491 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0"); 11492 Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0"); 11493 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *))); 11494 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide))); 11495 Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4)); 11496 11497 return i; 11498 } 11499 11500 void Jim_FreeInterp(Jim_Interp *i) 11501 { 11502 Jim_CallFrame *cf, *cfx; 11503 11504 Jim_Obj *objPtr, *nextObjPtr; 11505 11506 i->quitting = 1; 11507 11508 11509 for (cf = i->framePtr; cf; cf = cfx) { 11510 11511 JimInvokeDefer(i, JIM_OK); 11512 cfx = cf->parent; 11513 JimFreeCallFrame(i, cf, JIM_FCF_FULL); 11514 } 11515 11516 11517 Jim_FreeHashTable(&i->commands); 11518 11519 Jim_DecrRefCount(i, i->emptyObj); 11520 Jim_DecrRefCount(i, i->trueObj); 11521 Jim_DecrRefCount(i, i->falseObj); 11522 Jim_DecrRefCount(i, i->result); 11523 Jim_DecrRefCount(i, i->stackTrace); 11524 Jim_DecrRefCount(i, i->errorProc); 11525 Jim_DecrRefCount(i, i->unknown); 11526 Jim_DecrRefCount(i, i->defer); 11527 Jim_DecrRefCount(i, i->nullScriptObj); 11528 Jim_DecrRefCount(i, i->currentFilenameObj); 11529 11530 11531 Jim_InterpIncrProcEpoch(i); 11532 11533 #ifdef JIM_REFERENCES 11534 Jim_FreeHashTable(&i->references); 11535 #endif 11536 Jim_FreeHashTable(&i->packages); 11537 Jim_Free(i->prngState); 11538 Jim_FreeHashTable(&i->assocData); 11539 if (i->traceCmdObj) { 11540 Jim_DecrRefCount(i, i->traceCmdObj); 11541 } 11542 11543 #ifdef JIM_MAINTAINER 11544 if (i->liveList != NULL) { 11545 objPtr = i->liveList; 11546 11547 printf("\n-------------------------------------\n"); 11548 printf("Objects still in the free list:\n"); 11549 while (objPtr) { 11550 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string"; 11551 Jim_String(objPtr); 11552 11553 if (objPtr->bytes && strlen(objPtr->bytes) > 20) { 11554 printf("%p (%d) %-10s: '%.20s...'\n", 11555 (void *)objPtr, objPtr->refCount, type, objPtr->bytes); 11556 } 11557 else { 11558 printf("%p (%d) %-10s: '%s'\n", 11559 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)"); 11560 } 11561 if (objPtr->typePtr == &sourceObjType) { 11562 printf("FILE %s LINE %d\n", 11563 Jim_String(objPtr->internalRep.sourceValue.fileNameObj), 11564 objPtr->internalRep.sourceValue.lineNumber); 11565 } 11566 objPtr = objPtr->nextObjPtr; 11567 } 11568 printf("-------------------------------------\n\n"); 11569 JimPanic((1, "Live list non empty freeing the interpreter! Leak?")); 11570 } 11571 #endif 11572 11573 11574 objPtr = i->freeList; 11575 while (objPtr) { 11576 nextObjPtr = objPtr->nextObjPtr; 11577 Jim_Free(objPtr); 11578 objPtr = nextObjPtr; 11579 } 11580 11581 11582 for (cf = i->freeFramesList; cf; cf = cfx) { 11583 cfx = cf->next; 11584 if (cf->vars.table) 11585 Jim_FreeHashTable(&cf->vars); 11586 Jim_Free(cf); 11587 } 11588 11589 11590 Jim_Free(i); 11591 } 11592 11593 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr) 11594 { 11595 long level; 11596 const char *str; 11597 Jim_CallFrame *framePtr; 11598 11599 if (levelObjPtr) { 11600 str = Jim_String(levelObjPtr); 11601 if (str[0] == '#') { 11602 char *endptr; 11603 11604 level = jim_strtol(str + 1, &endptr); 11605 if (str[1] == '\0' || endptr[0] != '\0') { 11606 level = -1; 11607 } 11608 } 11609 else { 11610 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) { 11611 level = -1; 11612 } 11613 else { 11614 11615 level = interp->framePtr->level - level; 11616 } 11617 } 11618 } 11619 else { 11620 str = "1"; 11621 level = interp->framePtr->level - 1; 11622 } 11623 11624 if (level == 0) { 11625 return interp->topFramePtr; 11626 } 11627 if (level > 0) { 11628 11629 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) { 11630 if (framePtr->level == level) { 11631 return framePtr; 11632 } 11633 } 11634 } 11635 11636 Jim_SetResultFormatted(interp, "bad level \"%s\"", str); 11637 return NULL; 11638 } 11639 11640 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level) 11641 { 11642 Jim_CallFrame *framePtr; 11643 11644 if (level == 0) { 11645 return interp->framePtr; 11646 } 11647 11648 if (level < 0) { 11649 11650 level = interp->framePtr->level + level; 11651 } 11652 11653 if (level > 0) { 11654 11655 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) { 11656 if (framePtr->level == level) { 11657 return framePtr; 11658 } 11659 } 11660 } 11661 return NULL; 11662 } 11663 11664 static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel) 11665 { 11666 Jim_EvalFrame *evalFrame; 11667 11668 if (proclevel == 0) { 11669 return interp->evalFrame; 11670 } 11671 11672 if (proclevel < 0) { 11673 11674 proclevel = interp->procLevel + proclevel; 11675 } 11676 11677 if (proclevel >= 0) { 11678 11679 for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) { 11680 if (evalFrame->procLevel == proclevel) { 11681 return evalFrame; 11682 } 11683 } 11684 } 11685 return NULL; 11686 } 11687 11688 static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame) 11689 { 11690 if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) { 11691 Jim_EvalFrame *e; 11692 for (e = frame->parent; e; e = e->parent) { 11693 if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) { 11694 break; 11695 } 11696 } 11697 if (e && e->cmd && e->cmd->cmdNameObj) { 11698 return e->cmd->cmdNameObj; 11699 } 11700 } 11701 return NULL; 11702 } 11703 11704 static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj) 11705 { 11706 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame); 11707 Jim_Obj *fileNameObj = interp->emptyObj; 11708 int linenr = 1; 11709 11710 if (frame->scriptObj) { 11711 ScriptObj *script = JimGetScript(interp, frame->scriptObj); 11712 fileNameObj = script->fileNameObj; 11713 linenr = script->linenr; 11714 } 11715 11716 Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj); 11717 Jim_ListAppendElement(interp, listObj, fileNameObj); 11718 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr)); 11719 Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc)); 11720 } 11721 11722 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj) 11723 { 11724 11725 Jim_IncrRefCount(stackTraceObj); 11726 Jim_DecrRefCount(interp, interp->stackTrace); 11727 interp->stackTrace = stackTraceObj; 11728 interp->errorFlag = 1; 11729 } 11730 11731 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script) 11732 { 11733 if (!interp->errorFlag) { 11734 int i; 11735 Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0); 11736 11737 if (interp->procLevel == 0 && script) { 11738 Jim_ListAppendElement(interp, stackTrace, interp->emptyObj); 11739 Jim_ListAppendElement(interp, stackTrace, script->fileNameObj); 11740 Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr)); 11741 Jim_ListAppendElement(interp, stackTrace, interp->emptyObj); 11742 } 11743 else { 11744 for (i = 0; i <= interp->procLevel; i++) { 11745 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i); 11746 if (frame) { 11747 JimAddStackFrame(interp, frame, stackTrace); 11748 } 11749 } 11750 } 11751 JimSetStackTrace(interp, stackTrace); 11752 } 11753 } 11754 11755 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc, 11756 void *data) 11757 { 11758 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue)); 11759 11760 assocEntryPtr->delProc = delProc; 11761 assocEntryPtr->data = data; 11762 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr); 11763 } 11764 11765 void *Jim_GetAssocData(Jim_Interp *interp, const char *key) 11766 { 11767 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key); 11768 11769 if (entryPtr != NULL) { 11770 AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr); 11771 return assocEntryPtr->data; 11772 } 11773 return NULL; 11774 } 11775 11776 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key) 11777 { 11778 return Jim_DeleteHashEntry(&interp->assocData, key); 11779 } 11780 11781 int Jim_GetExitCode(Jim_Interp *interp) 11782 { 11783 return interp->exitCode; 11784 } 11785 11786 static void UpdateStringOfInt(struct Jim_Obj *objPtr); 11787 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); 11788 11789 static const Jim_ObjType intObjType = { 11790 "int", 11791 NULL, 11792 NULL, 11793 UpdateStringOfInt, 11794 JIM_TYPE_NONE, 11795 }; 11796 11797 static const Jim_ObjType coercedDoubleObjType = { 11798 "coerced-double", 11799 NULL, 11800 NULL, 11801 UpdateStringOfInt, 11802 JIM_TYPE_NONE, 11803 }; 11804 11805 11806 static void UpdateStringOfInt(struct Jim_Obj *objPtr) 11807 { 11808 char buf[JIM_INTEGER_SPACE + 1]; 11809 jim_wide wideValue = JimWideValue(objPtr); 11810 int pos = 0; 11811 11812 if (wideValue == 0) { 11813 buf[pos++] = '0'; 11814 } 11815 else { 11816 char tmp[JIM_INTEGER_SPACE]; 11817 int num = 0; 11818 int i; 11819 11820 if (wideValue < 0) { 11821 buf[pos++] = '-'; 11822 i = wideValue % 10; 11823 tmp[num++] = (i > 0) ? (10 - i) : -i; 11824 wideValue /= -10; 11825 } 11826 11827 while (wideValue) { 11828 tmp[num++] = wideValue % 10; 11829 wideValue /= 10; 11830 } 11831 11832 for (i = 0; i < num; i++) { 11833 buf[pos++] = '0' + tmp[num - i - 1]; 11834 } 11835 } 11836 buf[pos] = 0; 11837 11838 JimSetStringBytes(objPtr, buf); 11839 } 11840 11841 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 11842 { 11843 jim_wide wideValue; 11844 const char *str; 11845 11846 if (objPtr->typePtr == &coercedDoubleObjType) { 11847 11848 objPtr->typePtr = &intObjType; 11849 return JIM_OK; 11850 } 11851 11852 11853 str = Jim_String(objPtr); 11854 11855 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) { 11856 if (flags & JIM_ERRMSG) { 11857 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr); 11858 } 11859 return JIM_ERR; 11860 } 11861 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) { 11862 Jim_SetResultString(interp, "Integer value too big to be represented", -1); 11863 return JIM_ERR; 11864 } 11865 11866 Jim_FreeIntRep(interp, objPtr); 11867 objPtr->typePtr = &intObjType; 11868 objPtr->internalRep.wideValue = wideValue; 11869 return JIM_OK; 11870 } 11871 11872 #ifdef JIM_OPTIMIZATION 11873 static int JimIsWide(Jim_Obj *objPtr) 11874 { 11875 return objPtr->typePtr == &intObjType; 11876 } 11877 #endif 11878 11879 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) 11880 { 11881 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR) 11882 return JIM_ERR; 11883 *widePtr = JimWideValue(objPtr); 11884 return JIM_OK; 11885 } 11886 11887 int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) 11888 { 11889 int ret = JIM_OK; 11890 11891 if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) { 11892 SetIntFromAny(interp, objPtr, 0); 11893 } 11894 if (objPtr->typePtr == &intObjType) { 11895 *widePtr = JimWideValue(objPtr); 11896 } 11897 else { 11898 JimPanic((interp->safeexpr, "interp->safeexpr is set")); 11899 interp->safeexpr++; 11900 ret = Jim_EvalExpression(interp, objPtr); 11901 interp->safeexpr--; 11902 11903 if (ret == JIM_OK) { 11904 ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr); 11905 } 11906 if (ret != JIM_OK) { 11907 Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr); 11908 } 11909 } 11910 return ret; 11911 } 11912 11913 11914 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) 11915 { 11916 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR) 11917 return JIM_ERR; 11918 *widePtr = JimWideValue(objPtr); 11919 return JIM_OK; 11920 } 11921 11922 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr) 11923 { 11924 jim_wide wideValue; 11925 int retval; 11926 11927 retval = Jim_GetWide(interp, objPtr, &wideValue); 11928 if (retval == JIM_OK) { 11929 *longPtr = (long)wideValue; 11930 return JIM_OK; 11931 } 11932 return JIM_ERR; 11933 } 11934 11935 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue) 11936 { 11937 Jim_Obj *objPtr; 11938 11939 objPtr = Jim_NewObj(interp); 11940 objPtr->typePtr = &intObjType; 11941 objPtr->bytes = NULL; 11942 objPtr->internalRep.wideValue = wideValue; 11943 return objPtr; 11944 } 11945 11946 #define JIM_DOUBLE_SPACE 30 11947 11948 static void UpdateStringOfDouble(struct Jim_Obj *objPtr); 11949 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr); 11950 11951 static const Jim_ObjType doubleObjType = { 11952 "double", 11953 NULL, 11954 NULL, 11955 UpdateStringOfDouble, 11956 JIM_TYPE_NONE, 11957 }; 11958 11959 #if !HAVE_DECL_ISNAN 11960 #undef isnan 11961 #define isnan(X) ((X) != (X)) 11962 #endif 11963 #if !HAVE_DECL_ISINF 11964 #undef isinf 11965 #define isinf(X) (1.0 / (X) == 0.0) 11966 #endif 11967 11968 static void UpdateStringOfDouble(struct Jim_Obj *objPtr) 11969 { 11970 double value = objPtr->internalRep.doubleValue; 11971 11972 if (isnan(value)) { 11973 JimSetStringBytes(objPtr, "NaN"); 11974 return; 11975 } 11976 if (isinf(value)) { 11977 if (value < 0) { 11978 JimSetStringBytes(objPtr, "-Inf"); 11979 } 11980 else { 11981 JimSetStringBytes(objPtr, "Inf"); 11982 } 11983 return; 11984 } 11985 { 11986 char buf[JIM_DOUBLE_SPACE + 1]; 11987 int i; 11988 int len = sprintf(buf, "%.12g", value); 11989 11990 11991 for (i = 0; i < len; i++) { 11992 if (buf[i] == '.' || buf[i] == 'e') { 11993 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX) 11994 char *e = strchr(buf, 'e'); 11995 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') { 11996 11997 e += 2; 11998 memmove(e, e + 1, len - (e - buf)); 11999 } 12000 #endif 12001 break; 12002 } 12003 } 12004 if (buf[i] == '\0') { 12005 buf[i++] = '.'; 12006 buf[i++] = '0'; 12007 buf[i] = '\0'; 12008 } 12009 JimSetStringBytes(objPtr, buf); 12010 } 12011 } 12012 12013 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 12014 { 12015 double doubleValue; 12016 jim_wide wideValue; 12017 const char *str; 12018 12019 #ifdef HAVE_LONG_LONG 12020 12021 #define MIN_INT_IN_DOUBLE -(1LL << 53) 12022 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1) 12023 12024 if (objPtr->typePtr == &intObjType 12025 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE 12026 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) { 12027 12028 12029 objPtr->typePtr = &coercedDoubleObjType; 12030 return JIM_OK; 12031 } 12032 #endif 12033 str = Jim_String(objPtr); 12034 12035 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) { 12036 12037 Jim_FreeIntRep(interp, objPtr); 12038 objPtr->typePtr = &coercedDoubleObjType; 12039 objPtr->internalRep.wideValue = wideValue; 12040 return JIM_OK; 12041 } 12042 else { 12043 12044 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) { 12045 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr); 12046 return JIM_ERR; 12047 } 12048 12049 Jim_FreeIntRep(interp, objPtr); 12050 } 12051 objPtr->typePtr = &doubleObjType; 12052 objPtr->internalRep.doubleValue = doubleValue; 12053 return JIM_OK; 12054 } 12055 12056 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr) 12057 { 12058 if (objPtr->typePtr == &coercedDoubleObjType) { 12059 *doublePtr = JimWideValue(objPtr); 12060 return JIM_OK; 12061 } 12062 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR) 12063 return JIM_ERR; 12064 12065 if (objPtr->typePtr == &coercedDoubleObjType) { 12066 *doublePtr = JimWideValue(objPtr); 12067 } 12068 else { 12069 *doublePtr = objPtr->internalRep.doubleValue; 12070 } 12071 return JIM_OK; 12072 } 12073 12074 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue) 12075 { 12076 Jim_Obj *objPtr; 12077 12078 objPtr = Jim_NewObj(interp); 12079 objPtr->typePtr = &doubleObjType; 12080 objPtr->bytes = NULL; 12081 objPtr->internalRep.doubleValue = doubleValue; 12082 return objPtr; 12083 } 12084 12085 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); 12086 12087 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr) 12088 { 12089 if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR) 12090 return JIM_ERR; 12091 *booleanPtr = (int) JimWideValue(objPtr); 12092 return JIM_OK; 12093 } 12094 12095 static const char * const jim_true_false_strings[8] = { 12096 "1", "true", "yes", "on", 12097 "0", "false", "no", "off" 12098 }; 12099 12100 static const int jim_true_false_lens[8] = { 12101 1, 4, 3, 2, 12102 1, 5, 2, 3, 12103 }; 12104 12105 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 12106 { 12107 int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings, 12108 sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings)); 12109 if (index < 0) { 12110 if (flags & JIM_ERRMSG) { 12111 Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr); 12112 } 12113 return JIM_ERR; 12114 } 12115 12116 12117 Jim_FreeIntRep(interp, objPtr); 12118 objPtr->typePtr = &intObjType; 12119 12120 objPtr->internalRep.wideValue = index < 4 ? 1 : 0; 12121 return JIM_OK; 12122 } 12123 12124 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec); 12125 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr); 12126 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 12127 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 12128 static void UpdateStringOfList(struct Jim_Obj *objPtr); 12129 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 12130 12131 static const Jim_ObjType listObjType = { 12132 "list", 12133 FreeListInternalRep, 12134 DupListInternalRep, 12135 UpdateStringOfList, 12136 JIM_TYPE_NONE, 12137 }; 12138 12139 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 12140 { 12141 int i; 12142 12143 for (i = 0; i < objPtr->internalRep.listValue.len; i++) { 12144 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]); 12145 } 12146 Jim_Free(objPtr->internalRep.listValue.ele); 12147 } 12148 12149 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 12150 { 12151 int i; 12152 12153 JIM_NOTUSED(interp); 12154 12155 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len; 12156 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen; 12157 dupPtr->internalRep.listValue.ele = 12158 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen); 12159 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele, 12160 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len); 12161 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) { 12162 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]); 12163 } 12164 dupPtr->typePtr = &listObjType; 12165 } 12166 12167 #define JIM_ELESTR_SIMPLE 0 12168 #define JIM_ELESTR_BRACE 1 12169 #define JIM_ELESTR_QUOTE 2 12170 static unsigned char ListElementQuotingType(const char *s, int len) 12171 { 12172 int i, level, blevel, trySimple = 1; 12173 12174 12175 if (len == 0) 12176 return JIM_ELESTR_BRACE; 12177 if (s[0] == '"' || s[0] == '{') { 12178 trySimple = 0; 12179 goto testbrace; 12180 } 12181 for (i = 0; i < len; i++) { 12182 switch (s[i]) { 12183 case ' ': 12184 case '$': 12185 case '"': 12186 case '[': 12187 case ']': 12188 case ';': 12189 case '\\': 12190 case '\r': 12191 case '\n': 12192 case '\t': 12193 case '\f': 12194 case '\v': 12195 trySimple = 0; 12196 12197 case '{': 12198 case '}': 12199 goto testbrace; 12200 } 12201 } 12202 return JIM_ELESTR_SIMPLE; 12203 12204 testbrace: 12205 12206 if (s[len - 1] == '\\') 12207 return JIM_ELESTR_QUOTE; 12208 level = 0; 12209 blevel = 0; 12210 for (i = 0; i < len; i++) { 12211 switch (s[i]) { 12212 case '{': 12213 level++; 12214 break; 12215 case '}': 12216 level--; 12217 if (level < 0) 12218 return JIM_ELESTR_QUOTE; 12219 break; 12220 case '[': 12221 blevel++; 12222 break; 12223 case ']': 12224 blevel--; 12225 break; 12226 case '\\': 12227 if (s[i + 1] == '\n') 12228 return JIM_ELESTR_QUOTE; 12229 else if (s[i + 1] != '\0') 12230 i++; 12231 break; 12232 } 12233 } 12234 if (blevel < 0) { 12235 return JIM_ELESTR_QUOTE; 12236 } 12237 12238 if (level == 0) { 12239 if (!trySimple) 12240 return JIM_ELESTR_BRACE; 12241 for (i = 0; i < len; i++) { 12242 switch (s[i]) { 12243 case ' ': 12244 case '$': 12245 case '"': 12246 case '[': 12247 case ']': 12248 case ';': 12249 case '\\': 12250 case '\r': 12251 case '\n': 12252 case '\t': 12253 case '\f': 12254 case '\v': 12255 return JIM_ELESTR_BRACE; 12256 break; 12257 } 12258 } 12259 return JIM_ELESTR_SIMPLE; 12260 } 12261 return JIM_ELESTR_QUOTE; 12262 } 12263 12264 static int BackslashQuoteString(const char *s, int len, char *q) 12265 { 12266 char *p = q; 12267 12268 while (len--) { 12269 switch (*s) { 12270 case ' ': 12271 case '$': 12272 case '"': 12273 case '[': 12274 case ']': 12275 case '{': 12276 case '}': 12277 case ';': 12278 case '\\': 12279 *p++ = '\\'; 12280 *p++ = *s++; 12281 break; 12282 case '\n': 12283 *p++ = '\\'; 12284 *p++ = 'n'; 12285 s++; 12286 break; 12287 case '\r': 12288 *p++ = '\\'; 12289 *p++ = 'r'; 12290 s++; 12291 break; 12292 case '\t': 12293 *p++ = '\\'; 12294 *p++ = 't'; 12295 s++; 12296 break; 12297 case '\f': 12298 *p++ = '\\'; 12299 *p++ = 'f'; 12300 s++; 12301 break; 12302 case '\v': 12303 *p++ = '\\'; 12304 *p++ = 'v'; 12305 s++; 12306 break; 12307 default: 12308 *p++ = *s++; 12309 break; 12310 } 12311 } 12312 *p = '\0'; 12313 12314 return p - q; 12315 } 12316 12317 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc) 12318 { 12319 #define STATIC_QUOTING_LEN 32 12320 int i, bufLen, realLength; 12321 const char *strRep; 12322 char *p; 12323 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN]; 12324 12325 12326 if (objc > STATIC_QUOTING_LEN) { 12327 quotingType = Jim_Alloc(objc); 12328 } 12329 else { 12330 quotingType = staticQuoting; 12331 } 12332 bufLen = 0; 12333 for (i = 0; i < objc; i++) { 12334 int len; 12335 12336 strRep = Jim_GetString(objv[i], &len); 12337 quotingType[i] = ListElementQuotingType(strRep, len); 12338 switch (quotingType[i]) { 12339 case JIM_ELESTR_SIMPLE: 12340 if (i != 0 || strRep[0] != '#') { 12341 bufLen += len; 12342 break; 12343 } 12344 12345 quotingType[i] = JIM_ELESTR_BRACE; 12346 12347 case JIM_ELESTR_BRACE: 12348 bufLen += len + 2; 12349 break; 12350 case JIM_ELESTR_QUOTE: 12351 bufLen += len * 2; 12352 break; 12353 } 12354 bufLen++; 12355 } 12356 bufLen++; 12357 12358 12359 p = objPtr->bytes = Jim_Alloc(bufLen + 1); 12360 realLength = 0; 12361 for (i = 0; i < objc; i++) { 12362 int len, qlen; 12363 12364 strRep = Jim_GetString(objv[i], &len); 12365 12366 switch (quotingType[i]) { 12367 case JIM_ELESTR_SIMPLE: 12368 memcpy(p, strRep, len); 12369 p += len; 12370 realLength += len; 12371 break; 12372 case JIM_ELESTR_BRACE: 12373 *p++ = '{'; 12374 memcpy(p, strRep, len); 12375 p += len; 12376 *p++ = '}'; 12377 realLength += len + 2; 12378 break; 12379 case JIM_ELESTR_QUOTE: 12380 if (i == 0 && strRep[0] == '#') { 12381 *p++ = '\\'; 12382 realLength++; 12383 } 12384 qlen = BackslashQuoteString(strRep, len, p); 12385 p += qlen; 12386 realLength += qlen; 12387 break; 12388 } 12389 12390 if (i + 1 != objc) { 12391 *p++ = ' '; 12392 realLength++; 12393 } 12394 } 12395 *p = '\0'; 12396 objPtr->length = realLength; 12397 12398 if (quotingType != staticQuoting) { 12399 Jim_Free(quotingType); 12400 } 12401 } 12402 12403 static void UpdateStringOfList(struct Jim_Obj *objPtr) 12404 { 12405 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len); 12406 } 12407 12408 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 12409 { 12410 struct JimParserCtx parser; 12411 const char *str; 12412 int strLen; 12413 Jim_Obj *fileNameObj; 12414 int linenr; 12415 12416 if (objPtr->typePtr == &listObjType) { 12417 return JIM_OK; 12418 } 12419 12420 12421 if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) { 12422 Jim_Dict *dict = objPtr->internalRep.dictValue; 12423 12424 12425 objPtr->typePtr = &listObjType; 12426 objPtr->internalRep.listValue.len = dict->len; 12427 objPtr->internalRep.listValue.maxLen = dict->maxLen; 12428 objPtr->internalRep.listValue.ele = dict->table; 12429 12430 12431 Jim_Free(dict->ht); 12432 12433 12434 Jim_Free(dict); 12435 return JIM_OK; 12436 } 12437 12438 12439 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr); 12440 Jim_IncrRefCount(fileNameObj); 12441 12442 12443 str = Jim_GetString(objPtr, &strLen); 12444 12445 Jim_FreeIntRep(interp, objPtr); 12446 objPtr->typePtr = &listObjType; 12447 objPtr->internalRep.listValue.len = 0; 12448 objPtr->internalRep.listValue.maxLen = 0; 12449 objPtr->internalRep.listValue.ele = NULL; 12450 12451 12452 if (strLen) { 12453 JimParserInit(&parser, str, strLen, linenr); 12454 while (!parser.eof) { 12455 Jim_Obj *elementPtr; 12456 12457 JimParseList(&parser); 12458 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC) 12459 continue; 12460 elementPtr = JimParserGetTokenObj(interp, &parser); 12461 Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline); 12462 ListAppendElement(objPtr, elementPtr); 12463 } 12464 } 12465 Jim_DecrRefCount(interp, fileNameObj); 12466 return JIM_OK; 12467 } 12468 12469 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) 12470 { 12471 Jim_Obj *objPtr; 12472 12473 objPtr = Jim_NewObj(interp); 12474 objPtr->typePtr = &listObjType; 12475 objPtr->bytes = NULL; 12476 objPtr->internalRep.listValue.ele = NULL; 12477 objPtr->internalRep.listValue.len = 0; 12478 objPtr->internalRep.listValue.maxLen = 0; 12479 12480 if (len) { 12481 ListInsertElements(objPtr, 0, len, elements); 12482 } 12483 12484 return objPtr; 12485 } 12486 12487 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen, 12488 Jim_Obj ***listVec) 12489 { 12490 *listLen = Jim_ListLength(interp, listObj); 12491 *listVec = listObj->internalRep.listValue.ele; 12492 } 12493 12494 12495 static int JimSign(jim_wide w) 12496 { 12497 if (w == 0) { 12498 return 0; 12499 } 12500 else if (w < 0) { 12501 return -1; 12502 } 12503 return 1; 12504 } 12505 12506 12507 struct lsort_info { 12508 jmp_buf jmpbuf; 12509 Jim_Obj *command; 12510 Jim_Interp *interp; 12511 enum { 12512 JIM_LSORT_ASCII, 12513 JIM_LSORT_NOCASE, 12514 JIM_LSORT_INTEGER, 12515 JIM_LSORT_REAL, 12516 JIM_LSORT_COMMAND, 12517 JIM_LSORT_DICT 12518 } type; 12519 int order; 12520 Jim_Obj **indexv; 12521 int indexc; 12522 int unique; 12523 int (*subfn)(Jim_Obj **, Jim_Obj **); 12524 }; 12525 12526 static struct lsort_info *sort_info; 12527 12528 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12529 { 12530 Jim_Obj *lObj, *rObj; 12531 12532 if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK || 12533 Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) { 12534 longjmp(sort_info->jmpbuf, JIM_ERR); 12535 } 12536 return sort_info->subfn(&lObj, &rObj); 12537 } 12538 12539 12540 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12541 { 12542 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; 12543 } 12544 12545 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12546 { 12547 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order; 12548 } 12549 12550 static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12551 { 12552 12553 const char *left = Jim_String(*lhsObj); 12554 const char *right = Jim_String(*rhsObj); 12555 12556 while (1) { 12557 if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) { 12558 12559 jim_wide lint, rint; 12560 char *lend, *rend; 12561 lint = jim_strtoull(left, &lend); 12562 rint = jim_strtoull(right, &rend); 12563 if (lint != rint) { 12564 return JimSign(lint - rint) * sort_info->order; 12565 } 12566 if (lend -left != rend - right) { 12567 return JimSign((lend - left) - (rend - right)) * sort_info->order; 12568 } 12569 left = lend; 12570 right = rend; 12571 } 12572 else { 12573 int cl, cr; 12574 left += utf8_tounicode_case(left, &cl, 1); 12575 right += utf8_tounicode_case(right, &cr, 1); 12576 if (cl != cr) { 12577 return JimSign(cl - cr) * sort_info->order; 12578 } 12579 if (cl == 0) { 12580 12581 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; 12582 } 12583 } 12584 } 12585 } 12586 12587 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12588 { 12589 jim_wide lhs = 0, rhs = 0; 12590 12591 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK || 12592 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { 12593 longjmp(sort_info->jmpbuf, JIM_ERR); 12594 } 12595 12596 return JimSign(lhs - rhs) * sort_info->order; 12597 } 12598 12599 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12600 { 12601 double lhs = 0, rhs = 0; 12602 12603 if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK || 12604 Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { 12605 longjmp(sort_info->jmpbuf, JIM_ERR); 12606 } 12607 if (lhs == rhs) { 12608 return 0; 12609 } 12610 if (lhs > rhs) { 12611 return sort_info->order; 12612 } 12613 return -sort_info->order; 12614 } 12615 12616 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12617 { 12618 Jim_Obj *compare_script; 12619 int rc; 12620 12621 jim_wide ret = 0; 12622 12623 12624 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command); 12625 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj); 12626 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj); 12627 12628 rc = Jim_EvalObj(sort_info->interp, compare_script); 12629 12630 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) { 12631 longjmp(sort_info->jmpbuf, rc); 12632 } 12633 12634 return JimSign(ret) * sort_info->order; 12635 } 12636 12637 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs)) 12638 { 12639 int src; 12640 int dst = 0; 12641 Jim_Obj **ele = listObjPtr->internalRep.listValue.ele; 12642 12643 for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) { 12644 if (comp(&ele[dst], &ele[src]) == 0) { 12645 12646 Jim_DecrRefCount(sort_info->interp, ele[dst]); 12647 } 12648 else { 12649 12650 dst++; 12651 } 12652 ele[dst] = ele[src]; 12653 } 12654 12655 12656 dst++; 12657 if (dst < listObjPtr->internalRep.listValue.len) { 12658 ele[dst] = ele[src]; 12659 } 12660 12661 12662 listObjPtr->internalRep.listValue.len = dst; 12663 } 12664 12665 12666 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info) 12667 { 12668 struct lsort_info *prev_info; 12669 12670 typedef int (qsort_comparator) (const void *, const void *); 12671 int (*fn) (Jim_Obj **, Jim_Obj **); 12672 Jim_Obj **vector; 12673 int len; 12674 int rc; 12675 12676 JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object")); 12677 SetListFromAny(interp, listObjPtr); 12678 12679 12680 prev_info = sort_info; 12681 sort_info = info; 12682 12683 vector = listObjPtr->internalRep.listValue.ele; 12684 len = listObjPtr->internalRep.listValue.len; 12685 switch (info->type) { 12686 case JIM_LSORT_ASCII: 12687 fn = ListSortString; 12688 break; 12689 case JIM_LSORT_NOCASE: 12690 fn = ListSortStringNoCase; 12691 break; 12692 case JIM_LSORT_INTEGER: 12693 fn = ListSortInteger; 12694 break; 12695 case JIM_LSORT_REAL: 12696 fn = ListSortReal; 12697 break; 12698 case JIM_LSORT_COMMAND: 12699 fn = ListSortCommand; 12700 break; 12701 case JIM_LSORT_DICT: 12702 fn = ListSortDict; 12703 break; 12704 default: 12705 fn = NULL; 12706 JimPanic((1, "ListSort called with invalid sort type")); 12707 return -1; 12708 } 12709 12710 if (info->indexc) { 12711 12712 info->subfn = fn; 12713 fn = ListSortIndexHelper; 12714 } 12715 12716 if ((rc = setjmp(info->jmpbuf)) == 0) { 12717 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn); 12718 12719 if (info->unique && len > 1) { 12720 ListRemoveDuplicates(listObjPtr, fn); 12721 } 12722 12723 Jim_InvalidateStringRep(listObjPtr); 12724 } 12725 sort_info = prev_info; 12726 12727 return rc; 12728 } 12729 12730 12731 static void ListEnsureLength(Jim_Obj *listPtr, int idx) 12732 { 12733 assert(idx >= 0); 12734 if (idx >= listPtr->internalRep.listValue.maxLen) { 12735 if (idx < 4) { 12736 12737 idx = 4; 12738 } 12739 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele, 12740 sizeof(Jim_Obj *) * idx); 12741 12742 listPtr->internalRep.listValue.maxLen = idx; 12743 } 12744 } 12745 12746 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec) 12747 { 12748 int currentLen = listPtr->internalRep.listValue.len; 12749 int requiredLen = currentLen + elemc; 12750 int i; 12751 Jim_Obj **point; 12752 12753 if (elemc == 0) { 12754 12755 return; 12756 } 12757 12758 if (requiredLen > listPtr->internalRep.listValue.maxLen) { 12759 if (currentLen) { 12760 12761 requiredLen *= 2; 12762 } 12763 ListEnsureLength(listPtr, requiredLen); 12764 } 12765 if (idx < 0) { 12766 idx = currentLen; 12767 } 12768 point = listPtr->internalRep.listValue.ele + idx; 12769 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *)); 12770 for (i = 0; i < elemc; ++i) { 12771 point[i] = elemVec[i]; 12772 Jim_IncrRefCount(point[i]); 12773 } 12774 listPtr->internalRep.listValue.len += elemc; 12775 } 12776 12777 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr) 12778 { 12779 ListInsertElements(listPtr, -1, 1, &objPtr); 12780 } 12781 12782 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr) 12783 { 12784 ListInsertElements(listPtr, -1, 12785 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele); 12786 } 12787 12788 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr) 12789 { 12790 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object")); 12791 SetListFromAny(interp, listPtr); 12792 Jim_InvalidateStringRep(listPtr); 12793 ListAppendElement(listPtr, objPtr); 12794 } 12795 12796 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr) 12797 { 12798 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object")); 12799 SetListFromAny(interp, listPtr); 12800 SetListFromAny(interp, appendListPtr); 12801 Jim_InvalidateStringRep(listPtr); 12802 ListAppendList(listPtr, appendListPtr); 12803 } 12804 12805 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr) 12806 { 12807 SetListFromAny(interp, objPtr); 12808 return objPtr->internalRep.listValue.len; 12809 } 12810 12811 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx, 12812 int objc, Jim_Obj *const *objVec) 12813 { 12814 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object")); 12815 SetListFromAny(interp, listPtr); 12816 if (idx >= 0 && idx > listPtr->internalRep.listValue.len) 12817 idx = listPtr->internalRep.listValue.len; 12818 else if (idx < 0) 12819 idx = 0; 12820 Jim_InvalidateStringRep(listPtr); 12821 ListInsertElements(listPtr, idx, objc, objVec); 12822 } 12823 12824 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx) 12825 { 12826 SetListFromAny(interp, listPtr); 12827 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) || 12828 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) { 12829 return NULL; 12830 } 12831 if (idx < 0) 12832 idx = listPtr->internalRep.listValue.len + idx; 12833 return listPtr->internalRep.listValue.ele[idx]; 12834 } 12835 12836 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags) 12837 { 12838 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx); 12839 if (*objPtrPtr == NULL) { 12840 if (flags & JIM_ERRMSG) { 12841 Jim_SetResultString(interp, "list index out of range", -1); 12842 } 12843 return JIM_ERR; 12844 } 12845 return JIM_OK; 12846 } 12847 12848 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, 12849 Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags) 12850 { 12851 int i; 12852 int static_idxes[5]; 12853 int *idxes = static_idxes; 12854 int ret = JIM_OK; 12855 12856 if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) { 12857 idxes = Jim_Alloc(indexc * sizeof(*idxes)); 12858 } 12859 12860 for (i = 0; i < indexc; i++) { 12861 ret = Jim_GetIndex(interp, indexv[i], &idxes[i]); 12862 if (ret != JIM_OK) { 12863 goto err; 12864 } 12865 } 12866 12867 for (i = 0; i < indexc; i++) { 12868 Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]); 12869 if (!objPtr) { 12870 if (flags & JIM_ERRMSG) { 12871 if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) { 12872 Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]); 12873 } 12874 else { 12875 Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr); 12876 } 12877 } 12878 return -1; 12879 } 12880 listPtr = objPtr; 12881 } 12882 *resultObj = listPtr; 12883 err: 12884 if (idxes != static_idxes) 12885 Jim_Free(idxes); 12886 return ret; 12887 } 12888 12889 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, 12890 Jim_Obj *newObjPtr, int flags) 12891 { 12892 SetListFromAny(interp, listPtr); 12893 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) || 12894 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) { 12895 if (flags & JIM_ERRMSG) { 12896 Jim_SetResultString(interp, "list index out of range", -1); 12897 } 12898 return JIM_ERR; 12899 } 12900 if (idx < 0) 12901 idx = listPtr->internalRep.listValue.len + idx; 12902 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]); 12903 listPtr->internalRep.listValue.ele[idx] = newObjPtr; 12904 Jim_IncrRefCount(newObjPtr); 12905 return JIM_OK; 12906 } 12907 12908 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr, 12909 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr) 12910 { 12911 Jim_Obj *varObjPtr, *objPtr, *listObjPtr; 12912 int shared, i, idx; 12913 12914 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED); 12915 if (objPtr == NULL) 12916 return JIM_ERR; 12917 if ((shared = Jim_IsShared(objPtr))) 12918 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr); 12919 for (i = 0; i < indexc - 1; i++) { 12920 listObjPtr = objPtr; 12921 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK) 12922 goto err; 12923 12924 objPtr = Jim_ListGetIndex(interp, listObjPtr, idx); 12925 if (objPtr == NULL) { 12926 Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]); 12927 goto err; 12928 } 12929 if (Jim_IsShared(objPtr)) { 12930 objPtr = Jim_DuplicateObj(interp, objPtr); 12931 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE); 12932 } 12933 Jim_InvalidateStringRep(listObjPtr); 12934 } 12935 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK) 12936 goto err; 12937 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR) 12938 goto err; 12939 Jim_InvalidateStringRep(objPtr); 12940 Jim_InvalidateStringRep(varObjPtr); 12941 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) 12942 goto err; 12943 Jim_SetResult(interp, varObjPtr); 12944 return JIM_OK; 12945 err: 12946 if (shared) { 12947 Jim_FreeNewObj(interp, varObjPtr); 12948 } 12949 return JIM_ERR; 12950 } 12951 12952 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen) 12953 { 12954 int i; 12955 int listLen = Jim_ListLength(interp, listObjPtr); 12956 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp); 12957 12958 for (i = 0; i < listLen; ) { 12959 Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i)); 12960 if (++i != listLen) { 12961 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen); 12962 } 12963 } 12964 return resObjPtr; 12965 } 12966 12967 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 12968 { 12969 int i; 12970 12971 for (i = 0; i < objc; i++) { 12972 if (!Jim_IsList(objv[i])) 12973 break; 12974 } 12975 if (i == objc) { 12976 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); 12977 12978 for (i = 0; i < objc; i++) 12979 ListAppendList(objPtr, objv[i]); 12980 return objPtr; 12981 } 12982 else { 12983 12984 int len = 0, objLen; 12985 char *bytes, *p; 12986 12987 12988 for (i = 0; i < objc; i++) { 12989 len += Jim_Length(objv[i]); 12990 } 12991 if (objc) 12992 len += objc - 1; 12993 12994 p = bytes = Jim_Alloc(len + 1); 12995 for (i = 0; i < objc; i++) { 12996 const char *s = Jim_GetString(objv[i], &objLen); 12997 12998 12999 while (objLen && isspace(UCHAR(*s))) { 13000 s++; 13001 objLen--; 13002 len--; 13003 } 13004 13005 while (objLen && isspace(UCHAR(s[objLen - 1]))) { 13006 13007 if (objLen > 1 && s[objLen - 2] == '\\') { 13008 break; 13009 } 13010 objLen--; 13011 len--; 13012 } 13013 memcpy(p, s, objLen); 13014 p += objLen; 13015 if (i + 1 != objc) { 13016 if (objLen) 13017 *p++ = ' '; 13018 else { 13019 len--; 13020 } 13021 } 13022 } 13023 *p = '\0'; 13024 return Jim_NewStringObjNoAlloc(interp, bytes, len); 13025 } 13026 } 13027 13028 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, 13029 Jim_Obj *lastObjPtr) 13030 { 13031 int first, last; 13032 int len, rangeLen; 13033 13034 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK || 13035 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK) 13036 return NULL; 13037 len = Jim_ListLength(interp, listObjPtr); 13038 first = JimRelToAbsIndex(len, first); 13039 last = JimRelToAbsIndex(len, last); 13040 JimRelToAbsRange(len, &first, &last, &rangeLen); 13041 if (first == 0 && last == len) { 13042 return listObjPtr; 13043 } 13044 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen); 13045 } 13046 13047 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 13048 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 13049 static void UpdateStringOfDict(struct Jim_Obj *objPtr); 13050 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 13051 13052 13053 static const Jim_ObjType dictObjType = { 13054 "dict", 13055 FreeDictInternalRep, 13056 DupDictInternalRep, 13057 UpdateStringOfDict, 13058 JIM_TYPE_NONE, 13059 }; 13060 13061 static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict) 13062 { 13063 int i; 13064 for (i = 0; i < dict->len; i++) { 13065 Jim_DecrRefCount(interp, dict->table[i]); 13066 } 13067 Jim_Free(dict->table); 13068 Jim_Free(dict->ht); 13069 Jim_Free(dict); 13070 } 13071 13072 enum { 13073 DICT_HASH_FIND = -1, 13074 DICT_HASH_REMOVE = -2, 13075 DICT_HASH_ADD = -3, 13076 }; 13077 13078 static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset) 13079 { 13080 unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq); 13081 unsigned idx = h & dict->sizemask; 13082 int tvoffset = 0; 13083 unsigned peturb = h; 13084 unsigned first_removed = ~0; 13085 13086 if (dict->len) { 13087 while ((tvoffset = dict->ht[idx].offset)) { 13088 if (tvoffset == -1) { 13089 if (first_removed == ~0) { 13090 first_removed = idx; 13091 } 13092 } 13093 else if (dict->ht[idx].hash == h) { 13094 if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) { 13095 break; 13096 } 13097 } 13098 13099 peturb >>= 5; 13100 idx = (5 * idx + 1 + peturb) & dict->sizemask; 13101 } 13102 } 13103 13104 switch (op_tvoffset) { 13105 case DICT_HASH_FIND: 13106 13107 break; 13108 case DICT_HASH_REMOVE: 13109 if (tvoffset) { 13110 13111 dict->ht[idx].offset = -1; 13112 dict->dummy++; 13113 } 13114 13115 break; 13116 case DICT_HASH_ADD: 13117 if (tvoffset == 0) { 13118 13119 if (first_removed != ~0) { 13120 idx = first_removed; 13121 dict->dummy--; 13122 } 13123 dict->ht[idx].offset = dict->len + 1; 13124 dict->ht[idx].hash = h; 13125 } 13126 13127 break; 13128 default: 13129 assert(tvoffset); 13130 13131 dict->ht[idx].offset = op_tvoffset; 13132 break; 13133 } 13134 13135 return tvoffset; 13136 } 13137 13138 static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size) 13139 { 13140 int i; 13141 struct JimDictHashEntry *prevht = dict->ht; 13142 int prevsize = dict->size; 13143 13144 dict->size = JimHashTableNextPower(size); 13145 dict->sizemask = dict->size - 1; 13146 13147 13148 dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht)); 13149 memset(dict->ht, 0, dict->size * sizeof(*dict->ht)); 13150 13151 13152 for (i = 0; i < prevsize; i++) { 13153 if (prevht[i].offset > 0) { 13154 13155 unsigned h = prevht[i].hash; 13156 unsigned idx = h & dict->sizemask; 13157 unsigned peturb = h; 13158 13159 while (dict->ht[idx].offset) { 13160 peturb >>= 5; 13161 idx = (5 * idx + 1 + peturb) & dict->sizemask; 13162 } 13163 dict->ht[idx].offset = prevht[i].offset; 13164 dict->ht[idx].hash = h; 13165 } 13166 } 13167 Jim_Free(prevht); 13168 } 13169 13170 static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr) 13171 { 13172 if (dict->size <= dict->len + dict->dummy) { 13173 JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8); 13174 } 13175 return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD); 13176 } 13177 13178 static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size) 13179 { 13180 Jim_Dict *dict = Jim_Alloc(sizeof(*dict)); 13181 memset(dict, 0, sizeof(*dict)); 13182 13183 if (ht_size) { 13184 JimDictExpandHashTable(dict, ht_size); 13185 } 13186 if (table_size) { 13187 dict->table = Jim_Alloc(table_size * sizeof(*dict->table)); 13188 dict->maxLen = table_size; 13189 } 13190 #ifdef JIM_RANDOMISE_HASH 13191 dict->uniq = (rand() ^ time(NULL) ^ clock()); 13192 #endif 13193 return dict; 13194 } 13195 13196 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 13197 { 13198 JimFreeDict(interp, objPtr->internalRep.dictValue); 13199 } 13200 13201 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 13202 { 13203 Jim_Dict *oldDict = srcPtr->internalRep.dictValue; 13204 int i; 13205 13206 13207 Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size); 13208 13209 13210 for (i = 0; i < oldDict->len; i++) { 13211 newDict->table[i] = oldDict->table[i]; 13212 Jim_IncrRefCount(newDict->table[i]); 13213 } 13214 newDict->len = oldDict->len; 13215 13216 13217 newDict->uniq = oldDict->uniq; 13218 13219 13220 memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size); 13221 13222 dupPtr->internalRep.dictValue = newDict; 13223 dupPtr->typePtr = &dictObjType; 13224 } 13225 13226 static void UpdateStringOfDict(struct Jim_Obj *objPtr) 13227 { 13228 JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len); 13229 } 13230 13231 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 13232 { 13233 int listlen; 13234 13235 if (objPtr->typePtr == &dictObjType) { 13236 return JIM_OK; 13237 } 13238 13239 if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) { 13240 Jim_String(objPtr); 13241 } 13242 13243 listlen = Jim_ListLength(interp, objPtr); 13244 if (listlen % 2) { 13245 Jim_SetResultString(interp, "missing value to go with key", -1); 13246 return JIM_ERR; 13247 } 13248 else { 13249 13250 Jim_Dict *dict = JimDictNew(interp, 0, listlen); 13251 int i; 13252 13253 13254 dict->table = objPtr->internalRep.listValue.ele; 13255 dict->maxLen = objPtr->internalRep.listValue.maxLen; 13256 13257 13258 for (i = 0; i < listlen; i += 2) { 13259 int tvoffset = JimDictAdd(dict, dict->table[i]); 13260 if (tvoffset) { 13261 13262 13263 Jim_DecrRefCount(interp, dict->table[tvoffset]); 13264 13265 dict->table[tvoffset] = dict->table[i + 1]; 13266 13267 Jim_DecrRefCount(interp, dict->table[i]); 13268 } 13269 else { 13270 if (dict->len != i) { 13271 dict->table[dict->len++] = dict->table[i]; 13272 dict->table[dict->len++] = dict->table[i + 1]; 13273 } 13274 else { 13275 dict->len += 2; 13276 } 13277 } 13278 } 13279 13280 objPtr->typePtr = &dictObjType; 13281 objPtr->internalRep.dictValue = dict; 13282 13283 return JIM_OK; 13284 } 13285 } 13286 13287 13288 13289 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, 13290 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) 13291 { 13292 Jim_Dict *dict = objPtr->internalRep.dictValue; 13293 if (valueObjPtr == NULL) { 13294 13295 int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE); 13296 if (tvoffset) { 13297 13298 Jim_DecrRefCount(interp, dict->table[tvoffset - 1]); 13299 Jim_DecrRefCount(interp, dict->table[tvoffset]); 13300 dict->len -= 2; 13301 if (tvoffset != dict->len + 1) { 13302 13303 dict->table[tvoffset - 1] = dict->table[dict->len]; 13304 dict->table[tvoffset] = dict->table[dict->len + 1]; 13305 13306 13307 JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset); 13308 } 13309 return JIM_OK; 13310 } 13311 return JIM_ERR; 13312 } 13313 else { 13314 13315 int tvoffset = JimDictAdd(dict, keyObjPtr); 13316 if (tvoffset) { 13317 13318 Jim_IncrRefCount(valueObjPtr); 13319 Jim_DecrRefCount(interp, dict->table[tvoffset]); 13320 dict->table[tvoffset] = valueObjPtr; 13321 } 13322 else { 13323 if (dict->maxLen == dict->len) { 13324 13325 if (dict->maxLen < 4) { 13326 dict->maxLen = 4; 13327 } 13328 else { 13329 dict->maxLen *= 2; 13330 } 13331 dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table)); 13332 } 13333 Jim_IncrRefCount(keyObjPtr); 13334 Jim_IncrRefCount(valueObjPtr); 13335 13336 dict->table[dict->len++] = keyObjPtr; 13337 dict->table[dict->len++] = valueObjPtr; 13338 13339 } 13340 return JIM_OK; 13341 } 13342 } 13343 13344 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, 13345 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) 13346 { 13347 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object")); 13348 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 13349 return JIM_ERR; 13350 } 13351 Jim_InvalidateStringRep(objPtr); 13352 return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr); 13353 } 13354 13355 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) 13356 { 13357 Jim_Obj *objPtr; 13358 int i; 13359 13360 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even")); 13361 13362 objPtr = Jim_NewObj(interp); 13363 objPtr->typePtr = &dictObjType; 13364 objPtr->bytes = NULL; 13365 13366 objPtr->internalRep.dictValue = JimDictNew(interp, len, len); 13367 for (i = 0; i < len; i += 2) 13368 DictAddElement(interp, objPtr, elements[i], elements[i + 1]); 13369 return objPtr; 13370 } 13371 13372 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr, 13373 Jim_Obj **objPtrPtr, int flags) 13374 { 13375 int tvoffset; 13376 Jim_Dict *dict; 13377 13378 if (SetDictFromAny(interp, dictPtr) != JIM_OK) { 13379 return -1; 13380 } 13381 dict = dictPtr->internalRep.dictValue; 13382 tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND); 13383 if (tvoffset == 0) { 13384 if (flags & JIM_ERRMSG) { 13385 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr); 13386 } 13387 return JIM_ERR; 13388 } 13389 *objPtrPtr = dict->table[tvoffset]; 13390 return JIM_OK; 13391 } 13392 13393 Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len) 13394 { 13395 13396 if (Jim_IsList(dictPtr)) { 13397 Jim_Obj **table; 13398 JimListGetElements(interp, dictPtr, len, &table); 13399 if (*len % 2 == 0) { 13400 return table; 13401 } 13402 13403 } 13404 if (SetDictFromAny(interp, dictPtr) != JIM_OK) { 13405 13406 *len = 1; 13407 return NULL; 13408 } 13409 *len = dictPtr->internalRep.dictValue->len; 13410 return dictPtr->internalRep.dictValue->table; 13411 } 13412 13413 13414 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr, 13415 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags) 13416 { 13417 int i; 13418 13419 if (keyc == 0) { 13420 *objPtrPtr = dictPtr; 13421 return JIM_OK; 13422 } 13423 13424 for (i = 0; i < keyc; i++) { 13425 Jim_Obj *objPtr; 13426 13427 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags); 13428 if (rc != JIM_OK) { 13429 return rc; 13430 } 13431 dictPtr = objPtr; 13432 } 13433 *objPtrPtr = dictPtr; 13434 return JIM_OK; 13435 } 13436 13437 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr, 13438 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags) 13439 { 13440 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr; 13441 int shared, i; 13442 13443 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags); 13444 if (objPtr == NULL) { 13445 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) { 13446 13447 return JIM_ERR; 13448 } 13449 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0); 13450 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) { 13451 Jim_FreeNewObj(interp, varObjPtr); 13452 return JIM_ERR; 13453 } 13454 } 13455 if ((shared = Jim_IsShared(objPtr))) 13456 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr); 13457 for (i = 0; i < keyc; i++) { 13458 dictObjPtr = objPtr; 13459 13460 13461 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) { 13462 goto err; 13463 } 13464 13465 if (i == keyc - 1) { 13466 13467 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) { 13468 if (newObjPtr || (flags & JIM_MUSTEXIST)) { 13469 goto err; 13470 } 13471 } 13472 break; 13473 } 13474 13475 13476 Jim_InvalidateStringRep(dictObjPtr); 13477 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr, 13478 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) { 13479 if (Jim_IsShared(objPtr)) { 13480 objPtr = Jim_DuplicateObj(interp, objPtr); 13481 DictAddElement(interp, dictObjPtr, keyv[i], objPtr); 13482 } 13483 } 13484 else { 13485 if (newObjPtr == NULL) { 13486 goto err; 13487 } 13488 objPtr = Jim_NewDictObj(interp, NULL, 0); 13489 DictAddElement(interp, dictObjPtr, keyv[i], objPtr); 13490 } 13491 } 13492 13493 Jim_InvalidateStringRep(objPtr); 13494 Jim_InvalidateStringRep(varObjPtr); 13495 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) { 13496 goto err; 13497 } 13498 13499 if (!(flags & JIM_NORESULT)) { 13500 Jim_SetResult(interp, varObjPtr); 13501 } 13502 return JIM_OK; 13503 err: 13504 if (shared) { 13505 Jim_FreeNewObj(interp, varObjPtr); 13506 } 13507 return JIM_ERR; 13508 } 13509 13510 static void UpdateStringOfIndex(struct Jim_Obj *objPtr); 13511 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 13512 13513 static const Jim_ObjType indexObjType = { 13514 "index", 13515 NULL, 13516 NULL, 13517 UpdateStringOfIndex, 13518 JIM_TYPE_NONE, 13519 }; 13520 13521 static void UpdateStringOfIndex(struct Jim_Obj *objPtr) 13522 { 13523 if (objPtr->internalRep.intValue == -1) { 13524 JimSetStringBytes(objPtr, "end"); 13525 } 13526 else { 13527 char buf[JIM_INTEGER_SPACE + 1]; 13528 if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) { 13529 sprintf(buf, "%d", objPtr->internalRep.intValue); 13530 } 13531 else { 13532 13533 sprintf(buf, "end%d", objPtr->internalRep.intValue + 1); 13534 } 13535 JimSetStringBytes(objPtr, buf); 13536 } 13537 } 13538 13539 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 13540 { 13541 jim_wide idx; 13542 int end = 0; 13543 const char *str; 13544 Jim_Obj *exprObj = objPtr; 13545 13546 JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object")); 13547 13548 13549 str = Jim_String(objPtr); 13550 13551 13552 if (strncmp(str, "end", 3) == 0) { 13553 end = 1; 13554 str += 3; 13555 idx = 0; 13556 switch (*str) { 13557 case '\0': 13558 exprObj = NULL; 13559 break; 13560 13561 case '-': 13562 case '+': 13563 exprObj = Jim_NewStringObj(interp, str, -1); 13564 break; 13565 13566 default: 13567 goto badindex; 13568 } 13569 } 13570 if (exprObj) { 13571 int ret; 13572 Jim_IncrRefCount(exprObj); 13573 ret = Jim_GetWideExpr(interp, exprObj, &idx); 13574 Jim_DecrRefCount(interp, exprObj); 13575 if (ret != JIM_OK) { 13576 goto badindex; 13577 } 13578 } 13579 13580 if (end) { 13581 if (idx > 0) { 13582 idx = INT_MAX; 13583 } 13584 else { 13585 13586 idx--; 13587 } 13588 } 13589 else if (idx < 0) { 13590 idx = -INT_MAX; 13591 } 13592 13593 13594 Jim_FreeIntRep(interp, objPtr); 13595 objPtr->typePtr = &indexObjType; 13596 objPtr->internalRep.intValue = idx; 13597 return JIM_OK; 13598 13599 badindex: 13600 Jim_SetResultFormatted(interp, 13601 "bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr); 13602 return JIM_ERR; 13603 } 13604 13605 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr) 13606 { 13607 13608 if (objPtr->typePtr == &intObjType) { 13609 jim_wide val = JimWideValue(objPtr); 13610 13611 if (val < 0) 13612 *indexPtr = -INT_MAX; 13613 else if (val > INT_MAX) 13614 *indexPtr = INT_MAX; 13615 else 13616 *indexPtr = (int)val; 13617 return JIM_OK; 13618 } 13619 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR) 13620 return JIM_ERR; 13621 *indexPtr = objPtr->internalRep.intValue; 13622 return JIM_OK; 13623 } 13624 13625 13626 13627 static const char * const jimReturnCodes[] = { 13628 "ok", 13629 "error", 13630 "return", 13631 "break", 13632 "continue", 13633 "signal", 13634 "exit", 13635 "eval", 13636 NULL 13637 }; 13638 13639 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1) 13640 13641 static const Jim_ObjType returnCodeObjType = { 13642 "return-code", 13643 NULL, 13644 NULL, 13645 NULL, 13646 JIM_TYPE_NONE, 13647 }; 13648 13649 const char *Jim_ReturnCode(int code) 13650 { 13651 if (code < 0 || code >= (int)jimReturnCodesSize) { 13652 return "?"; 13653 } 13654 else { 13655 return jimReturnCodes[code]; 13656 } 13657 } 13658 13659 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 13660 { 13661 int returnCode; 13662 jim_wide wideValue; 13663 13664 13665 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR) 13666 returnCode = (int)wideValue; 13667 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) { 13668 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr); 13669 return JIM_ERR; 13670 } 13671 13672 Jim_FreeIntRep(interp, objPtr); 13673 objPtr->typePtr = &returnCodeObjType; 13674 objPtr->internalRep.intValue = returnCode; 13675 return JIM_OK; 13676 } 13677 13678 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr) 13679 { 13680 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR) 13681 return JIM_ERR; 13682 *intPtr = objPtr->internalRep.intValue; 13683 return JIM_OK; 13684 } 13685 13686 static int JimParseExprOperator(struct JimParserCtx *pc); 13687 static int JimParseExprNumber(struct JimParserCtx *pc); 13688 static int JimParseExprIrrational(struct JimParserCtx *pc); 13689 static int JimParseExprBoolean(struct JimParserCtx *pc); 13690 13691 13692 enum 13693 { 13694 13695 13696 13697 JIM_EXPROP_MUL = JIM_TT_EXPR_OP, 13698 JIM_EXPROP_DIV, 13699 JIM_EXPROP_MOD, 13700 JIM_EXPROP_SUB, 13701 JIM_EXPROP_ADD, 13702 JIM_EXPROP_LSHIFT, 13703 JIM_EXPROP_RSHIFT, 13704 JIM_EXPROP_ROTL, 13705 JIM_EXPROP_ROTR, 13706 JIM_EXPROP_LT, 13707 JIM_EXPROP_GT, 13708 JIM_EXPROP_LTE, 13709 JIM_EXPROP_GTE, 13710 JIM_EXPROP_NUMEQ, 13711 JIM_EXPROP_NUMNE, 13712 JIM_EXPROP_BITAND, 13713 JIM_EXPROP_BITXOR, 13714 JIM_EXPROP_BITOR, 13715 JIM_EXPROP_LOGICAND, 13716 JIM_EXPROP_LOGICOR, 13717 JIM_EXPROP_TERNARY, 13718 JIM_EXPROP_COLON, 13719 JIM_EXPROP_POW, 13720 13721 13722 JIM_EXPROP_STREQ, 13723 JIM_EXPROP_STRNE, 13724 JIM_EXPROP_STRIN, 13725 JIM_EXPROP_STRNI, 13726 JIM_EXPROP_STRLT, 13727 JIM_EXPROP_STRGT, 13728 JIM_EXPROP_STRLE, 13729 JIM_EXPROP_STRGE, 13730 13731 13732 JIM_EXPROP_NOT, 13733 JIM_EXPROP_BITNOT, 13734 JIM_EXPROP_UNARYMINUS, 13735 JIM_EXPROP_UNARYPLUS, 13736 13737 13738 JIM_EXPROP_FUNC_INT, 13739 JIM_EXPROP_FUNC_WIDE, 13740 JIM_EXPROP_FUNC_ABS, 13741 JIM_EXPROP_FUNC_DOUBLE, 13742 JIM_EXPROP_FUNC_ROUND, 13743 JIM_EXPROP_FUNC_RAND, 13744 JIM_EXPROP_FUNC_SRAND, 13745 13746 13747 JIM_EXPROP_FUNC_SIN, 13748 JIM_EXPROP_FUNC_COS, 13749 JIM_EXPROP_FUNC_TAN, 13750 JIM_EXPROP_FUNC_ASIN, 13751 JIM_EXPROP_FUNC_ACOS, 13752 JIM_EXPROP_FUNC_ATAN, 13753 JIM_EXPROP_FUNC_ATAN2, 13754 JIM_EXPROP_FUNC_SINH, 13755 JIM_EXPROP_FUNC_COSH, 13756 JIM_EXPROP_FUNC_TANH, 13757 JIM_EXPROP_FUNC_CEIL, 13758 JIM_EXPROP_FUNC_FLOOR, 13759 JIM_EXPROP_FUNC_EXP, 13760 JIM_EXPROP_FUNC_LOG, 13761 JIM_EXPROP_FUNC_LOG10, 13762 JIM_EXPROP_FUNC_SQRT, 13763 JIM_EXPROP_FUNC_POW, 13764 JIM_EXPROP_FUNC_HYPOT, 13765 JIM_EXPROP_FUNC_FMOD, 13766 }; 13767 13768 struct JimExprNode { 13769 int type; 13770 struct Jim_Obj *objPtr; 13771 13772 struct JimExprNode *left; 13773 struct JimExprNode *right; 13774 struct JimExprNode *ternary; 13775 }; 13776 13777 13778 typedef struct Jim_ExprOperator 13779 { 13780 const char *name; 13781 int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode); 13782 unsigned char precedence; 13783 unsigned char arity; 13784 unsigned char attr; 13785 unsigned char namelen; 13786 } Jim_ExprOperator; 13787 13788 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr); 13789 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node); 13790 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node); 13791 13792 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node) 13793 { 13794 int intresult = 1; 13795 int rc, bA = 0; 13796 double dA, dC = 0; 13797 jim_wide wA, wC = 0; 13798 Jim_Obj *A; 13799 13800 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 13801 return rc; 13802 } 13803 13804 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) { 13805 switch (node->type) { 13806 case JIM_EXPROP_FUNC_INT: 13807 case JIM_EXPROP_FUNC_WIDE: 13808 case JIM_EXPROP_FUNC_ROUND: 13809 case JIM_EXPROP_UNARYPLUS: 13810 wC = wA; 13811 break; 13812 case JIM_EXPROP_FUNC_DOUBLE: 13813 dC = wA; 13814 intresult = 0; 13815 break; 13816 case JIM_EXPROP_FUNC_ABS: 13817 wC = wA >= 0 ? wA : -wA; 13818 break; 13819 case JIM_EXPROP_UNARYMINUS: 13820 wC = -wA; 13821 break; 13822 case JIM_EXPROP_NOT: 13823 wC = !wA; 13824 break; 13825 default: 13826 abort(); 13827 } 13828 } 13829 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) { 13830 switch (node->type) { 13831 case JIM_EXPROP_FUNC_INT: 13832 case JIM_EXPROP_FUNC_WIDE: 13833 wC = dA; 13834 break; 13835 case JIM_EXPROP_FUNC_ROUND: 13836 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5); 13837 break; 13838 case JIM_EXPROP_FUNC_DOUBLE: 13839 case JIM_EXPROP_UNARYPLUS: 13840 dC = dA; 13841 intresult = 0; 13842 break; 13843 case JIM_EXPROP_FUNC_ABS: 13844 #ifdef JIM_MATH_FUNCTIONS 13845 dC = fabs(dA); 13846 #else 13847 dC = dA >= 0 ? dA : -dA; 13848 #endif 13849 intresult = 0; 13850 break; 13851 case JIM_EXPROP_UNARYMINUS: 13852 dC = -dA; 13853 intresult = 0; 13854 break; 13855 case JIM_EXPROP_NOT: 13856 wC = !dA; 13857 break; 13858 default: 13859 abort(); 13860 } 13861 } 13862 else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) { 13863 switch (node->type) { 13864 case JIM_EXPROP_NOT: 13865 wC = !bA; 13866 break; 13867 case JIM_EXPROP_UNARYPLUS: 13868 case JIM_EXPROP_UNARYMINUS: 13869 rc = JIM_ERR; 13870 Jim_SetResultFormatted(interp, 13871 "can't use non-numeric string as operand of \"%s\"", 13872 node->type == JIM_EXPROP_UNARYPLUS ? "+" : "-"); 13873 break; 13874 default: 13875 abort(); 13876 } 13877 } 13878 13879 if (rc == JIM_OK) { 13880 if (intresult) { 13881 Jim_SetResultInt(interp, wC); 13882 } 13883 else { 13884 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); 13885 } 13886 } 13887 13888 Jim_DecrRefCount(interp, A); 13889 13890 return rc; 13891 } 13892 13893 static double JimRandDouble(Jim_Interp *interp) 13894 { 13895 unsigned long x; 13896 JimRandomBytes(interp, &x, sizeof(x)); 13897 13898 return (double)x / (double)~0UL; 13899 } 13900 13901 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node) 13902 { 13903 jim_wide wA; 13904 Jim_Obj *A; 13905 int rc; 13906 13907 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 13908 return rc; 13909 } 13910 13911 rc = Jim_GetWide(interp, A, &wA); 13912 if (rc == JIM_OK) { 13913 switch (node->type) { 13914 case JIM_EXPROP_BITNOT: 13915 Jim_SetResultInt(interp, ~wA); 13916 break; 13917 case JIM_EXPROP_FUNC_SRAND: 13918 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA)); 13919 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp))); 13920 break; 13921 default: 13922 abort(); 13923 } 13924 } 13925 13926 Jim_DecrRefCount(interp, A); 13927 13928 return rc; 13929 } 13930 13931 static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node) 13932 { 13933 JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()")); 13934 13935 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp))); 13936 13937 return JIM_OK; 13938 } 13939 13940 #ifdef JIM_MATH_FUNCTIONS 13941 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node) 13942 { 13943 int rc; 13944 double dA, dC; 13945 Jim_Obj *A; 13946 13947 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 13948 return rc; 13949 } 13950 13951 rc = Jim_GetDouble(interp, A, &dA); 13952 if (rc == JIM_OK) { 13953 switch (node->type) { 13954 case JIM_EXPROP_FUNC_SIN: 13955 dC = sin(dA); 13956 break; 13957 case JIM_EXPROP_FUNC_COS: 13958 dC = cos(dA); 13959 break; 13960 case JIM_EXPROP_FUNC_TAN: 13961 dC = tan(dA); 13962 break; 13963 case JIM_EXPROP_FUNC_ASIN: 13964 dC = asin(dA); 13965 break; 13966 case JIM_EXPROP_FUNC_ACOS: 13967 dC = acos(dA); 13968 break; 13969 case JIM_EXPROP_FUNC_ATAN: 13970 dC = atan(dA); 13971 break; 13972 case JIM_EXPROP_FUNC_SINH: 13973 dC = sinh(dA); 13974 break; 13975 case JIM_EXPROP_FUNC_COSH: 13976 dC = cosh(dA); 13977 break; 13978 case JIM_EXPROP_FUNC_TANH: 13979 dC = tanh(dA); 13980 break; 13981 case JIM_EXPROP_FUNC_CEIL: 13982 dC = ceil(dA); 13983 break; 13984 case JIM_EXPROP_FUNC_FLOOR: 13985 dC = floor(dA); 13986 break; 13987 case JIM_EXPROP_FUNC_EXP: 13988 dC = exp(dA); 13989 break; 13990 case JIM_EXPROP_FUNC_LOG: 13991 dC = log(dA); 13992 break; 13993 case JIM_EXPROP_FUNC_LOG10: 13994 dC = log10(dA); 13995 break; 13996 case JIM_EXPROP_FUNC_SQRT: 13997 dC = sqrt(dA); 13998 break; 13999 default: 14000 abort(); 14001 } 14002 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); 14003 } 14004 14005 Jim_DecrRefCount(interp, A); 14006 14007 return rc; 14008 } 14009 #endif 14010 14011 14012 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node) 14013 { 14014 jim_wide wA, wB; 14015 int rc; 14016 Jim_Obj *A, *B; 14017 14018 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 14019 return rc; 14020 } 14021 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { 14022 Jim_DecrRefCount(interp, A); 14023 return rc; 14024 } 14025 14026 rc = JIM_ERR; 14027 14028 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) { 14029 jim_wide wC; 14030 14031 rc = JIM_OK; 14032 14033 switch (node->type) { 14034 case JIM_EXPROP_LSHIFT: 14035 wC = wA << wB; 14036 break; 14037 case JIM_EXPROP_RSHIFT: 14038 wC = wA >> wB; 14039 break; 14040 case JIM_EXPROP_BITAND: 14041 wC = wA & wB; 14042 break; 14043 case JIM_EXPROP_BITXOR: 14044 wC = wA ^ wB; 14045 break; 14046 case JIM_EXPROP_BITOR: 14047 wC = wA | wB; 14048 break; 14049 case JIM_EXPROP_MOD: 14050 if (wB == 0) { 14051 wC = 0; 14052 Jim_SetResultString(interp, "Division by zero", -1); 14053 rc = JIM_ERR; 14054 } 14055 else { 14056 int negative = 0; 14057 14058 if (wB < 0) { 14059 wB = -wB; 14060 wA = -wA; 14061 negative = 1; 14062 } 14063 wC = wA % wB; 14064 if (wC < 0) { 14065 wC += wB; 14066 } 14067 if (negative) { 14068 wC = -wC; 14069 } 14070 } 14071 break; 14072 case JIM_EXPROP_ROTL: 14073 case JIM_EXPROP_ROTR:{ 14074 14075 unsigned long uA = (unsigned long)wA; 14076 unsigned long uB = (unsigned long)wB; 14077 const unsigned int S = sizeof(unsigned long) * 8; 14078 14079 14080 uB %= S; 14081 14082 if (node->type == JIM_EXPROP_ROTR) { 14083 uB = S - uB; 14084 } 14085 wC = (unsigned long)(uA << uB) | (uA >> (S - uB)); 14086 break; 14087 } 14088 default: 14089 abort(); 14090 } 14091 Jim_SetResultInt(interp, wC); 14092 } 14093 14094 Jim_DecrRefCount(interp, A); 14095 Jim_DecrRefCount(interp, B); 14096 14097 return rc; 14098 } 14099 14100 14101 14102 static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node) 14103 { 14104 int rc = JIM_OK; 14105 double dA, dB, dC = 0; 14106 jim_wide wA, wB, wC = 0; 14107 Jim_Obj *A, *B; 14108 14109 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 14110 return rc; 14111 } 14112 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { 14113 Jim_DecrRefCount(interp, A); 14114 return rc; 14115 } 14116 14117 if ((A->typePtr != &doubleObjType || A->bytes) && 14118 (B->typePtr != &doubleObjType || B->bytes) && 14119 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) { 14120 14121 14122 14123 switch (node->type) { 14124 case JIM_EXPROP_POW: 14125 case JIM_EXPROP_FUNC_POW: 14126 if (wA == 0 && wB < 0) { 14127 Jim_SetResultString(interp, "exponentiation of zero by negative power", -1); 14128 rc = JIM_ERR; 14129 goto done; 14130 } 14131 wC = JimPowWide(wA, wB); 14132 goto intresult; 14133 case JIM_EXPROP_ADD: 14134 wC = wA + wB; 14135 goto intresult; 14136 case JIM_EXPROP_SUB: 14137 wC = wA - wB; 14138 goto intresult; 14139 case JIM_EXPROP_MUL: 14140 wC = wA * wB; 14141 goto intresult; 14142 case JIM_EXPROP_DIV: 14143 if (wB == 0) { 14144 Jim_SetResultString(interp, "Division by zero", -1); 14145 rc = JIM_ERR; 14146 goto done; 14147 } 14148 else { 14149 if (wB < 0) { 14150 wB = -wB; 14151 wA = -wA; 14152 } 14153 wC = wA / wB; 14154 if (wA % wB < 0) { 14155 wC--; 14156 } 14157 goto intresult; 14158 } 14159 case JIM_EXPROP_LT: 14160 wC = wA < wB; 14161 goto intresult; 14162 case JIM_EXPROP_GT: 14163 wC = wA > wB; 14164 goto intresult; 14165 case JIM_EXPROP_LTE: 14166 wC = wA <= wB; 14167 goto intresult; 14168 case JIM_EXPROP_GTE: 14169 wC = wA >= wB; 14170 goto intresult; 14171 case JIM_EXPROP_NUMEQ: 14172 wC = wA == wB; 14173 goto intresult; 14174 case JIM_EXPROP_NUMNE: 14175 wC = wA != wB; 14176 goto intresult; 14177 } 14178 } 14179 if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) { 14180 switch (node->type) { 14181 #ifndef JIM_MATH_FUNCTIONS 14182 case JIM_EXPROP_POW: 14183 case JIM_EXPROP_FUNC_POW: 14184 case JIM_EXPROP_FUNC_ATAN2: 14185 case JIM_EXPROP_FUNC_HYPOT: 14186 case JIM_EXPROP_FUNC_FMOD: 14187 Jim_SetResultString(interp, "unsupported", -1); 14188 rc = JIM_ERR; 14189 goto done; 14190 #else 14191 case JIM_EXPROP_POW: 14192 case JIM_EXPROP_FUNC_POW: 14193 dC = pow(dA, dB); 14194 goto doubleresult; 14195 case JIM_EXPROP_FUNC_ATAN2: 14196 dC = atan2(dA, dB); 14197 goto doubleresult; 14198 case JIM_EXPROP_FUNC_HYPOT: 14199 dC = hypot(dA, dB); 14200 goto doubleresult; 14201 case JIM_EXPROP_FUNC_FMOD: 14202 dC = fmod(dA, dB); 14203 goto doubleresult; 14204 #endif 14205 case JIM_EXPROP_ADD: 14206 dC = dA + dB; 14207 goto doubleresult; 14208 case JIM_EXPROP_SUB: 14209 dC = dA - dB; 14210 goto doubleresult; 14211 case JIM_EXPROP_MUL: 14212 dC = dA * dB; 14213 goto doubleresult; 14214 case JIM_EXPROP_DIV: 14215 if (dB == 0) { 14216 #ifdef INFINITY 14217 dC = dA < 0 ? -INFINITY : INFINITY; 14218 #else 14219 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL); 14220 #endif 14221 } 14222 else { 14223 dC = dA / dB; 14224 } 14225 goto doubleresult; 14226 case JIM_EXPROP_LT: 14227 wC = dA < dB; 14228 goto intresult; 14229 case JIM_EXPROP_GT: 14230 wC = dA > dB; 14231 goto intresult; 14232 case JIM_EXPROP_LTE: 14233 wC = dA <= dB; 14234 goto intresult; 14235 case JIM_EXPROP_GTE: 14236 wC = dA >= dB; 14237 goto intresult; 14238 case JIM_EXPROP_NUMEQ: 14239 wC = dA == dB; 14240 goto intresult; 14241 case JIM_EXPROP_NUMNE: 14242 wC = dA != dB; 14243 goto intresult; 14244 } 14245 } 14246 else { 14247 14248 14249 14250 int i = Jim_StringCompareObj(interp, A, B, 0); 14251 14252 switch (node->type) { 14253 case JIM_EXPROP_LT: 14254 wC = i < 0; 14255 goto intresult; 14256 case JIM_EXPROP_GT: 14257 wC = i > 0; 14258 goto intresult; 14259 case JIM_EXPROP_LTE: 14260 wC = i <= 0; 14261 goto intresult; 14262 case JIM_EXPROP_GTE: 14263 wC = i >= 0; 14264 goto intresult; 14265 case JIM_EXPROP_NUMEQ: 14266 wC = i == 0; 14267 goto intresult; 14268 case JIM_EXPROP_NUMNE: 14269 wC = i != 0; 14270 goto intresult; 14271 } 14272 } 14273 14274 rc = JIM_ERR; 14275 done: 14276 Jim_DecrRefCount(interp, A); 14277 Jim_DecrRefCount(interp, B); 14278 return rc; 14279 intresult: 14280 Jim_SetResultInt(interp, wC); 14281 goto done; 14282 doubleresult: 14283 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); 14284 goto done; 14285 } 14286 14287 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj) 14288 { 14289 int listlen; 14290 int i; 14291 14292 listlen = Jim_ListLength(interp, listObjPtr); 14293 for (i = 0; i < listlen; i++) { 14294 if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) { 14295 return 1; 14296 } 14297 } 14298 return 0; 14299 } 14300 14301 14302 14303 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node) 14304 { 14305 Jim_Obj *A, *B; 14306 jim_wide wC; 14307 int comp, rc; 14308 14309 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 14310 return rc; 14311 } 14312 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { 14313 Jim_DecrRefCount(interp, A); 14314 return rc; 14315 } 14316 14317 switch (node->type) { 14318 case JIM_EXPROP_STREQ: 14319 case JIM_EXPROP_STRNE: 14320 wC = Jim_StringEqObj(A, B); 14321 if (node->type == JIM_EXPROP_STRNE) { 14322 wC = !wC; 14323 } 14324 break; 14325 case JIM_EXPROP_STRLT: 14326 case JIM_EXPROP_STRGT: 14327 case JIM_EXPROP_STRLE: 14328 case JIM_EXPROP_STRGE: 14329 comp = Jim_StringCompareObj(interp, A, B, 0); 14330 if (node->type == JIM_EXPROP_STRLT) { 14331 wC = comp == -1; 14332 } else if (node->type == JIM_EXPROP_STRGT) { 14333 wC = comp == 1; 14334 } else if (node->type == JIM_EXPROP_STRLE) { 14335 wC = comp == -1 || comp == 0; 14336 } else { 14337 wC = comp == 0 || comp == 1; 14338 } 14339 break; 14340 case JIM_EXPROP_STRIN: 14341 wC = JimSearchList(interp, B, A); 14342 break; 14343 case JIM_EXPROP_STRNI: 14344 wC = !JimSearchList(interp, B, A); 14345 break; 14346 default: 14347 abort(); 14348 } 14349 Jim_SetResultInt(interp, wC); 14350 14351 Jim_DecrRefCount(interp, A); 14352 Jim_DecrRefCount(interp, B); 14353 14354 return rc; 14355 } 14356 14357 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj) 14358 { 14359 long l; 14360 double d; 14361 int b; 14362 int ret = -1; 14363 14364 14365 Jim_IncrRefCount(obj); 14366 14367 if (Jim_GetLong(interp, obj, &l) == JIM_OK) { 14368 ret = (l != 0); 14369 } 14370 else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) { 14371 ret = (d != 0); 14372 } 14373 else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) { 14374 ret = (b != 0); 14375 } 14376 14377 Jim_DecrRefCount(interp, obj); 14378 return ret; 14379 } 14380 14381 static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node) 14382 { 14383 14384 int result = JimExprGetTermBoolean(interp, node->left); 14385 14386 if (result == 1) { 14387 14388 result = JimExprGetTermBoolean(interp, node->right); 14389 } 14390 if (result == -1) { 14391 return JIM_ERR; 14392 } 14393 Jim_SetResultInt(interp, result); 14394 return JIM_OK; 14395 } 14396 14397 static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node) 14398 { 14399 14400 int result = JimExprGetTermBoolean(interp, node->left); 14401 14402 if (result == 0) { 14403 14404 result = JimExprGetTermBoolean(interp, node->right); 14405 } 14406 if (result == -1) { 14407 return JIM_ERR; 14408 } 14409 Jim_SetResultInt(interp, result); 14410 return JIM_OK; 14411 } 14412 14413 static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node) 14414 { 14415 14416 int result = JimExprGetTermBoolean(interp, node->left); 14417 14418 if (result == 1) { 14419 14420 return JimExprEvalTermNode(interp, node->right); 14421 } 14422 else if (result == 0) { 14423 14424 return JimExprEvalTermNode(interp, node->ternary); 14425 } 14426 14427 return JIM_ERR; 14428 } 14429 14430 enum 14431 { 14432 OP_FUNC = 0x0001, 14433 OP_RIGHT_ASSOC = 0x0002, 14434 }; 14435 14436 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1} 14437 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0) 14438 14439 static const struct Jim_ExprOperator Jim_ExprOperators[] = { 14440 OPRINIT("*", 110, 2, JimExprOpBin), 14441 OPRINIT("/", 110, 2, JimExprOpBin), 14442 OPRINIT("%", 110, 2, JimExprOpIntBin), 14443 14444 OPRINIT("-", 100, 2, JimExprOpBin), 14445 OPRINIT("+", 100, 2, JimExprOpBin), 14446 14447 OPRINIT("<<", 90, 2, JimExprOpIntBin), 14448 OPRINIT(">>", 90, 2, JimExprOpIntBin), 14449 14450 OPRINIT("<<<", 90, 2, JimExprOpIntBin), 14451 OPRINIT(">>>", 90, 2, JimExprOpIntBin), 14452 14453 OPRINIT("<", 80, 2, JimExprOpBin), 14454 OPRINIT(">", 80, 2, JimExprOpBin), 14455 OPRINIT("<=", 80, 2, JimExprOpBin), 14456 OPRINIT(">=", 80, 2, JimExprOpBin), 14457 14458 OPRINIT("==", 70, 2, JimExprOpBin), 14459 OPRINIT("!=", 70, 2, JimExprOpBin), 14460 14461 OPRINIT("&", 50, 2, JimExprOpIntBin), 14462 OPRINIT("^", 49, 2, JimExprOpIntBin), 14463 OPRINIT("|", 48, 2, JimExprOpIntBin), 14464 14465 OPRINIT("&&", 10, 2, JimExprOpAnd), 14466 OPRINIT("||", 9, 2, JimExprOpOr), 14467 OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC), 14468 OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC), 14469 14470 14471 OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC), 14472 14473 OPRINIT("eq", 60, 2, JimExprOpStrBin), 14474 OPRINIT("ne", 60, 2, JimExprOpStrBin), 14475 14476 OPRINIT("in", 55, 2, JimExprOpStrBin), 14477 OPRINIT("ni", 55, 2, JimExprOpStrBin), 14478 14479 OPRINIT("lt", 75, 2, JimExprOpStrBin), 14480 OPRINIT("gt", 75, 2, JimExprOpStrBin), 14481 OPRINIT("le", 75, 2, JimExprOpStrBin), 14482 OPRINIT("ge", 75, 2, JimExprOpStrBin), 14483 14484 OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), 14485 OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC), 14486 OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), 14487 OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), 14488 14489 14490 14491 OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC), 14492 OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC), 14493 OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC), 14494 OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC), 14495 OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC), 14496 OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC), 14497 OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC), 14498 14499 #ifdef JIM_MATH_FUNCTIONS 14500 OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14501 OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14502 OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14503 OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14504 OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14505 OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14506 OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC), 14507 OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14508 OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14509 OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14510 OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14511 OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14512 OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14513 OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14514 OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14515 OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14516 OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC), 14517 OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC), 14518 OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC), 14519 #endif 14520 }; 14521 #undef OPRINIT 14522 #undef OPRINIT_ATTR 14523 14524 #define JIM_EXPR_OPERATORS_NUM \ 14525 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) 14526 14527 static int JimParseExpression(struct JimParserCtx *pc) 14528 { 14529 pc->errmsg = NULL; 14530 14531 while (1) { 14532 14533 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) { 14534 if (*pc->p == '\n') { 14535 pc->linenr++; 14536 } 14537 pc->p++; 14538 pc->len--; 14539 } 14540 14541 if (*pc->p == '#') { 14542 JimParseComment(pc); 14543 14544 continue; 14545 } 14546 break; 14547 } 14548 14549 14550 pc->tline = pc->linenr; 14551 pc->tstart = pc->p; 14552 14553 if (pc->len == 0) { 14554 pc->tend = pc->p; 14555 pc->tt = JIM_TT_EOL; 14556 pc->eof = 1; 14557 return JIM_OK; 14558 } 14559 switch (*(pc->p)) { 14560 case '(': 14561 pc->tt = JIM_TT_SUBEXPR_START; 14562 goto singlechar; 14563 case ')': 14564 pc->tt = JIM_TT_SUBEXPR_END; 14565 goto singlechar; 14566 case ',': 14567 pc->tt = JIM_TT_SUBEXPR_COMMA; 14568 singlechar: 14569 pc->tend = pc->p; 14570 pc->p++; 14571 pc->len--; 14572 break; 14573 case '[': 14574 return JimParseCmd(pc); 14575 case '$': 14576 if (JimParseVar(pc) == JIM_ERR) 14577 return JimParseExprOperator(pc); 14578 else { 14579 14580 if (pc->tt == JIM_TT_EXPRSUGAR) { 14581 pc->errmsg = "nesting expr in expr is not allowed"; 14582 return JIM_ERR; 14583 } 14584 return JIM_OK; 14585 } 14586 break; 14587 case '0': 14588 case '1': 14589 case '2': 14590 case '3': 14591 case '4': 14592 case '5': 14593 case '6': 14594 case '7': 14595 case '8': 14596 case '9': 14597 case '.': 14598 return JimParseExprNumber(pc); 14599 case '"': 14600 return JimParseQuote(pc); 14601 case '{': 14602 return JimParseBrace(pc); 14603 14604 case 'N': 14605 case 'I': 14606 case 'n': 14607 case 'i': 14608 if (JimParseExprIrrational(pc) == JIM_ERR) 14609 if (JimParseExprBoolean(pc) == JIM_ERR) 14610 return JimParseExprOperator(pc); 14611 break; 14612 case 't': 14613 case 'f': 14614 case 'o': 14615 case 'y': 14616 if (JimParseExprBoolean(pc) == JIM_ERR) 14617 return JimParseExprOperator(pc); 14618 break; 14619 default: 14620 return JimParseExprOperator(pc); 14621 break; 14622 } 14623 return JIM_OK; 14624 } 14625 14626 static int JimParseExprNumber(struct JimParserCtx *pc) 14627 { 14628 char *end; 14629 14630 14631 pc->tt = JIM_TT_EXPR_INT; 14632 14633 jim_strtoull(pc->p, (char **)&pc->p); 14634 14635 if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) { 14636 if (strtod(pc->tstart, &end)) { } 14637 if (end == pc->tstart) 14638 return JIM_ERR; 14639 if (end > pc->p) { 14640 14641 pc->tt = JIM_TT_EXPR_DOUBLE; 14642 pc->p = end; 14643 } 14644 } 14645 pc->tend = pc->p - 1; 14646 pc->len -= (pc->p - pc->tstart); 14647 return JIM_OK; 14648 } 14649 14650 static int JimParseExprIrrational(struct JimParserCtx *pc) 14651 { 14652 const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL }; 14653 int i; 14654 14655 for (i = 0; irrationals[i]; i++) { 14656 const char *irr = irrationals[i]; 14657 14658 if (strncmp(irr, pc->p, 3) == 0) { 14659 pc->p += 3; 14660 pc->len -= 3; 14661 pc->tend = pc->p - 1; 14662 pc->tt = JIM_TT_EXPR_DOUBLE; 14663 return JIM_OK; 14664 } 14665 } 14666 return JIM_ERR; 14667 } 14668 14669 static int JimParseExprBoolean(struct JimParserCtx *pc) 14670 { 14671 int i; 14672 for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) { 14673 if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) { 14674 pc->p += jim_true_false_lens[i]; 14675 pc->len -= jim_true_false_lens[i]; 14676 pc->tend = pc->p - 1; 14677 pc->tt = JIM_TT_EXPR_BOOLEAN; 14678 return JIM_OK; 14679 } 14680 } 14681 return JIM_ERR; 14682 } 14683 14684 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode) 14685 { 14686 static Jim_ExprOperator dummy_op; 14687 if (opcode < JIM_TT_EXPR_OP) { 14688 return &dummy_op; 14689 } 14690 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP]; 14691 } 14692 14693 static int JimParseExprOperator(struct JimParserCtx *pc) 14694 { 14695 int i; 14696 const struct Jim_ExprOperator *bestOp = NULL; 14697 int bestLen = 0; 14698 14699 14700 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { 14701 const struct Jim_ExprOperator *op = &Jim_ExprOperators[i]; 14702 14703 if (op->name[0] != pc->p[0]) { 14704 continue; 14705 } 14706 14707 if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) { 14708 bestOp = op; 14709 bestLen = op->namelen; 14710 } 14711 } 14712 if (bestOp == NULL) { 14713 return JIM_ERR; 14714 } 14715 14716 14717 if (bestOp->attr & OP_FUNC) { 14718 const char *p = pc->p + bestLen; 14719 int len = pc->len - bestLen; 14720 14721 while (len && isspace(UCHAR(*p))) { 14722 len--; 14723 p++; 14724 } 14725 if (*p != '(') { 14726 pc->errmsg = "function requires parentheses"; 14727 return JIM_ERR; 14728 } 14729 } 14730 pc->tend = pc->p + bestLen - 1; 14731 pc->p += bestLen; 14732 pc->len -= bestLen; 14733 14734 pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP; 14735 return JIM_OK; 14736 } 14737 14738 14739 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 14740 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 14741 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 14742 14743 static const Jim_ObjType exprObjType = { 14744 "expression", 14745 FreeExprInternalRep, 14746 DupExprInternalRep, 14747 NULL, 14748 JIM_TYPE_NONE, 14749 }; 14750 14751 14752 struct ExprTree 14753 { 14754 struct JimExprNode *expr; 14755 struct JimExprNode *nodes; 14756 int len; 14757 int inUse; 14758 }; 14759 14760 static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num) 14761 { 14762 int i; 14763 for (i = 0; i < num; i++) { 14764 if (nodes[i].objPtr) { 14765 Jim_DecrRefCount(interp, nodes[i].objPtr); 14766 } 14767 } 14768 Jim_Free(nodes); 14769 } 14770 14771 static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr) 14772 { 14773 ExprTreeFreeNodes(interp, expr->nodes, expr->len); 14774 Jim_Free(expr); 14775 } 14776 14777 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 14778 { 14779 struct ExprTree *expr = (void *)objPtr->internalRep.ptr; 14780 14781 if (expr) { 14782 if (--expr->inUse != 0) { 14783 return; 14784 } 14785 14786 ExprTreeFree(interp, expr); 14787 } 14788 } 14789 14790 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 14791 { 14792 JIM_NOTUSED(interp); 14793 JIM_NOTUSED(srcPtr); 14794 14795 14796 dupPtr->typePtr = NULL; 14797 } 14798 14799 struct ExprBuilder { 14800 int parencount; 14801 int level; 14802 ParseToken *token; 14803 ParseToken *first_token; 14804 Jim_Stack stack; 14805 Jim_Obj *exprObjPtr; 14806 Jim_Obj *fileNameObj; 14807 struct JimExprNode *nodes; 14808 struct JimExprNode *next; 14809 }; 14810 14811 #ifdef DEBUG_SHOW_EXPR 14812 static void JimShowExprNode(struct JimExprNode *node, int level) 14813 { 14814 int i; 14815 for (i = 0; i < level; i++) { 14816 printf(" "); 14817 } 14818 if (TOKEN_IS_EXPR_OP(node->type)) { 14819 printf("%s\n", jim_tt_name(node->type)); 14820 if (node->left) { 14821 JimShowExprNode(node->left, level + 1); 14822 } 14823 if (node->right) { 14824 JimShowExprNode(node->right, level + 1); 14825 } 14826 if (node->ternary) { 14827 JimShowExprNode(node->ternary, level + 1); 14828 } 14829 } 14830 else { 14831 printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr)); 14832 } 14833 } 14834 #endif 14835 14836 #define EXPR_UNTIL_CLOSE 0x0001 14837 #define EXPR_FUNC_ARGS 0x0002 14838 #define EXPR_TERNARY 0x0004 14839 14840 static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) { 14841 int rc; 14842 struct JimExprNode *node; 14843 14844 int exp_stacklen = builder->stack.len + exp_numterms; 14845 14846 if (builder->level++ > 200) { 14847 Jim_SetResultString(interp, "Expression too complex", -1); 14848 return JIM_ERR; 14849 } 14850 14851 while (builder->token->type != JIM_TT_EOL) { 14852 ParseToken *t = builder->token++; 14853 int prevtt; 14854 14855 if (t == builder->first_token) { 14856 prevtt = JIM_TT_NONE; 14857 } 14858 else { 14859 prevtt = t[-1].type; 14860 } 14861 14862 if (t->type == JIM_TT_SUBEXPR_START) { 14863 if (builder->stack.len == exp_stacklen) { 14864 Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr); 14865 return JIM_ERR; 14866 } 14867 builder->parencount++; 14868 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1); 14869 if (rc != JIM_OK) { 14870 return rc; 14871 } 14872 14873 } 14874 else if (t->type == JIM_TT_SUBEXPR_END) { 14875 if (!(flags & EXPR_UNTIL_CLOSE)) { 14876 if (builder->stack.len == exp_stacklen && builder->level > 1) { 14877 builder->token--; 14878 builder->level--; 14879 return JIM_OK; 14880 } 14881 Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr); 14882 return JIM_ERR; 14883 } 14884 builder->parencount--; 14885 if (builder->stack.len == exp_stacklen) { 14886 14887 break; 14888 } 14889 } 14890 else if (t->type == JIM_TT_SUBEXPR_COMMA) { 14891 if (!(flags & EXPR_FUNC_ARGS)) { 14892 if (builder->stack.len == exp_stacklen) { 14893 14894 builder->token--; 14895 builder->level--; 14896 return JIM_OK; 14897 } 14898 Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr); 14899 return JIM_ERR; 14900 } 14901 else { 14902 14903 if (builder->stack.len > exp_stacklen) { 14904 Jim_SetResultFormatted(interp, "too many arguments to math function"); 14905 return JIM_ERR; 14906 } 14907 } 14908 14909 } 14910 else if (t->type == JIM_EXPROP_COLON) { 14911 if (!(flags & EXPR_TERNARY)) { 14912 if (builder->level != 1) { 14913 14914 builder->token--; 14915 builder->level--; 14916 return JIM_OK; 14917 } 14918 Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr); 14919 return JIM_ERR; 14920 } 14921 if (builder->stack.len == exp_stacklen) { 14922 14923 builder->token--; 14924 builder->level--; 14925 return JIM_OK; 14926 } 14927 14928 } 14929 else if (TOKEN_IS_EXPR_OP(t->type)) { 14930 const struct Jim_ExprOperator *op; 14931 14932 14933 if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) { 14934 if (t->type == JIM_EXPROP_SUB) { 14935 t->type = JIM_EXPROP_UNARYMINUS; 14936 } 14937 else if (t->type == JIM_EXPROP_ADD) { 14938 t->type = JIM_EXPROP_UNARYPLUS; 14939 } 14940 } 14941 14942 op = JimExprOperatorInfoByOpcode(t->type); 14943 14944 if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) { 14945 14946 builder->token--; 14947 break; 14948 } 14949 14950 if (op->attr & OP_FUNC) { 14951 if (builder->token->type != JIM_TT_SUBEXPR_START) { 14952 Jim_SetResultString(interp, "missing arguments for math function", -1); 14953 return JIM_ERR; 14954 } 14955 builder->token++; 14956 if (op->arity == 0) { 14957 if (builder->token->type != JIM_TT_SUBEXPR_END) { 14958 Jim_SetResultString(interp, "too many arguments for math function", -1); 14959 return JIM_ERR; 14960 } 14961 builder->token++; 14962 goto noargs; 14963 } 14964 builder->parencount++; 14965 14966 14967 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity); 14968 } 14969 else if (t->type == JIM_EXPROP_TERNARY) { 14970 14971 rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2); 14972 } 14973 else { 14974 rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1); 14975 } 14976 14977 if (rc != JIM_OK) { 14978 return rc; 14979 } 14980 14981 noargs: 14982 node = builder->next++; 14983 node->type = t->type; 14984 14985 if (op->arity >= 3) { 14986 node->ternary = Jim_StackPop(&builder->stack); 14987 if (node->ternary == NULL) { 14988 goto missingoperand; 14989 } 14990 } 14991 if (op->arity >= 2) { 14992 node->right = Jim_StackPop(&builder->stack); 14993 if (node->right == NULL) { 14994 goto missingoperand; 14995 } 14996 } 14997 if (op->arity >= 1) { 14998 node->left = Jim_StackPop(&builder->stack); 14999 if (node->left == NULL) { 15000 missingoperand: 15001 Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr); 15002 builder->next--; 15003 return JIM_ERR; 15004 15005 } 15006 } 15007 15008 15009 Jim_StackPush(&builder->stack, node); 15010 } 15011 else { 15012 Jim_Obj *objPtr = NULL; 15013 15014 15015 15016 15017 if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) { 15018 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr); 15019 return JIM_ERR; 15020 } 15021 15022 15023 if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) { 15024 char *endptr; 15025 if (t->type == JIM_TT_EXPR_INT) { 15026 objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr)); 15027 } 15028 else { 15029 objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr)); 15030 } 15031 if (endptr != t->token + t->len) { 15032 15033 Jim_FreeNewObj(interp, objPtr); 15034 objPtr = NULL; 15035 } 15036 } 15037 15038 if (!objPtr) { 15039 15040 objPtr = Jim_NewStringObj(interp, t->token, t->len); 15041 if (t->type == JIM_TT_CMD) { 15042 15043 Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line); 15044 } 15045 } 15046 15047 15048 node = builder->next++; 15049 node->objPtr = objPtr; 15050 Jim_IncrRefCount(node->objPtr); 15051 node->type = t->type; 15052 Jim_StackPush(&builder->stack, node); 15053 } 15054 } 15055 15056 if (builder->stack.len == exp_stacklen) { 15057 builder->level--; 15058 return JIM_OK; 15059 } 15060 15061 if ((flags & EXPR_FUNC_ARGS)) { 15062 Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many"); 15063 } 15064 else { 15065 if (builder->stack.len < exp_stacklen) { 15066 if (builder->level == 0) { 15067 Jim_SetResultFormatted(interp, "empty expression"); 15068 } 15069 else { 15070 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr); 15071 } 15072 } 15073 else { 15074 Jim_SetResultFormatted(interp, "extra terms after expression"); 15075 } 15076 } 15077 15078 return JIM_ERR; 15079 } 15080 15081 static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj) 15082 { 15083 struct ExprTree *expr; 15084 struct ExprBuilder builder; 15085 int rc; 15086 struct JimExprNode *top = NULL; 15087 15088 builder.parencount = 0; 15089 builder.level = 0; 15090 builder.token = builder.first_token = tokenlist->list; 15091 builder.exprObjPtr = exprObjPtr; 15092 builder.fileNameObj = fileNameObj; 15093 15094 builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1)); 15095 memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1)); 15096 builder.next = builder.nodes; 15097 Jim_InitStack(&builder.stack); 15098 15099 rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1); 15100 15101 if (rc == JIM_OK) { 15102 top = Jim_StackPop(&builder.stack); 15103 15104 if (builder.parencount) { 15105 Jim_SetResultString(interp, "missing close parenthesis", -1); 15106 rc = JIM_ERR; 15107 } 15108 } 15109 15110 15111 Jim_FreeStack(&builder.stack); 15112 15113 if (rc != JIM_OK) { 15114 ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes); 15115 return NULL; 15116 } 15117 15118 expr = Jim_Alloc(sizeof(*expr)); 15119 expr->inUse = 1; 15120 expr->expr = top; 15121 expr->nodes = builder.nodes; 15122 expr->len = builder.next - builder.nodes; 15123 15124 assert(expr->len <= tokenlist->count - 1); 15125 15126 return expr; 15127 } 15128 15129 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 15130 { 15131 int exprTextLen; 15132 const char *exprText; 15133 struct JimParserCtx parser; 15134 struct ExprTree *expr; 15135 ParseTokenList tokenlist; 15136 int line; 15137 Jim_Obj *fileNameObj; 15138 int rc = JIM_ERR; 15139 15140 15141 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line); 15142 Jim_IncrRefCount(fileNameObj); 15143 15144 exprText = Jim_GetString(objPtr, &exprTextLen); 15145 15146 15147 ScriptTokenListInit(&tokenlist); 15148 15149 JimParserInit(&parser, exprText, exprTextLen, line); 15150 while (!parser.eof) { 15151 if (JimParseExpression(&parser) != JIM_OK) { 15152 ScriptTokenListFree(&tokenlist); 15153 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr); 15154 if (parser.errmsg) { 15155 Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL); 15156 } 15157 expr = NULL; 15158 goto err; 15159 } 15160 15161 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, 15162 parser.tline); 15163 } 15164 15165 #ifdef DEBUG_SHOW_EXPR_TOKENS 15166 { 15167 int i; 15168 printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj)); 15169 for (i = 0; i < tokenlist.count; i++) { 15170 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type), 15171 tokenlist.list[i].len, tokenlist.list[i].token); 15172 } 15173 } 15174 #endif 15175 15176 if (tokenlist.count <= 1) { 15177 Jim_SetResultString(interp, "empty expression", -1); 15178 rc = JIM_ERR; 15179 } 15180 else { 15181 rc = JimParseCheckMissing(interp, parser.missing.ch); 15182 } 15183 if (rc != JIM_OK) { 15184 ScriptTokenListFree(&tokenlist); 15185 Jim_DecrRefCount(interp, fileNameObj); 15186 return rc; 15187 } 15188 15189 15190 expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj); 15191 15192 15193 ScriptTokenListFree(&tokenlist); 15194 15195 if (!expr) { 15196 goto err; 15197 } 15198 15199 #ifdef DEBUG_SHOW_EXPR 15200 printf("==== Expr ====\n"); 15201 JimShowExprNode(expr->expr, 0); 15202 #endif 15203 15204 rc = JIM_OK; 15205 15206 err: 15207 15208 Jim_DecrRefCount(interp, fileNameObj); 15209 Jim_FreeIntRep(interp, objPtr); 15210 Jim_SetIntRepPtr(objPtr, expr); 15211 objPtr->typePtr = &exprObjType; 15212 return rc; 15213 } 15214 15215 static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr) 15216 { 15217 if (objPtr->typePtr != &exprObjType) { 15218 if (SetExprFromAny(interp, objPtr) != JIM_OK) { 15219 return NULL; 15220 } 15221 } 15222 return (struct ExprTree *) Jim_GetIntRepPtr(objPtr); 15223 } 15224 15225 #ifdef JIM_OPTIMIZATION 15226 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node) 15227 { 15228 if (node->type == JIM_TT_EXPR_INT) 15229 return node->objPtr; 15230 else if (node->type == JIM_TT_VAR) 15231 return Jim_GetVariable(interp, node->objPtr, JIM_NONE); 15232 else if (node->type == JIM_TT_DICTSUGAR) 15233 return JimExpandDictSugar(interp, node->objPtr); 15234 else 15235 return NULL; 15236 } 15237 #endif 15238 15239 15240 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node) 15241 { 15242 if (TOKEN_IS_EXPR_OP(node->type)) { 15243 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type); 15244 return op->funcop(interp, node); 15245 } 15246 else { 15247 Jim_Obj *objPtr; 15248 15249 15250 switch (node->type) { 15251 case JIM_TT_EXPR_INT: 15252 case JIM_TT_EXPR_DOUBLE: 15253 case JIM_TT_EXPR_BOOLEAN: 15254 case JIM_TT_STR: 15255 Jim_SetResult(interp, node->objPtr); 15256 return JIM_OK; 15257 15258 case JIM_TT_VAR: 15259 objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG); 15260 if (objPtr) { 15261 Jim_SetResult(interp, objPtr); 15262 return JIM_OK; 15263 } 15264 return JIM_ERR; 15265 15266 case JIM_TT_DICTSUGAR: 15267 objPtr = JimExpandDictSugar(interp, node->objPtr); 15268 if (objPtr) { 15269 Jim_SetResult(interp, objPtr); 15270 return JIM_OK; 15271 } 15272 return JIM_ERR; 15273 15274 case JIM_TT_ESC: 15275 if (interp->safeexpr) { 15276 return JIM_ERR; 15277 } 15278 if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) { 15279 Jim_SetResult(interp, objPtr); 15280 return JIM_OK; 15281 } 15282 return JIM_ERR; 15283 15284 case JIM_TT_CMD: 15285 if (interp->safeexpr) { 15286 return JIM_ERR; 15287 } 15288 return Jim_EvalObj(interp, node->objPtr); 15289 15290 default: 15291 15292 return JIM_ERR; 15293 } 15294 } 15295 } 15296 15297 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr) 15298 { 15299 int rc = JimExprEvalTermNode(interp, node); 15300 if (rc == JIM_OK) { 15301 *objPtrPtr = Jim_GetResult(interp); 15302 Jim_IncrRefCount(*objPtrPtr); 15303 } 15304 return rc; 15305 } 15306 15307 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node) 15308 { 15309 if (JimExprEvalTermNode(interp, node) == JIM_OK) { 15310 return ExprBool(interp, Jim_GetResult(interp)); 15311 } 15312 return -1; 15313 } 15314 15315 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr) 15316 { 15317 struct ExprTree *expr; 15318 int retcode = JIM_OK; 15319 15320 Jim_IncrRefCount(exprObjPtr); 15321 expr = JimGetExpression(interp, exprObjPtr); 15322 if (!expr) { 15323 retcode = JIM_ERR; 15324 goto done; 15325 } 15326 15327 #ifdef JIM_OPTIMIZATION 15328 if (!interp->safeexpr) { 15329 Jim_Obj *objPtr; 15330 15331 15332 switch (expr->len) { 15333 case 1: 15334 objPtr = JimExprIntValOrVar(interp, expr->expr); 15335 if (objPtr) { 15336 Jim_SetResult(interp, objPtr); 15337 goto done; 15338 } 15339 break; 15340 15341 case 2: 15342 if (expr->expr->type == JIM_EXPROP_NOT) { 15343 objPtr = JimExprIntValOrVar(interp, expr->expr->left); 15344 15345 if (objPtr && JimIsWide(objPtr)) { 15346 Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj); 15347 goto done; 15348 } 15349 } 15350 break; 15351 15352 case 3: 15353 objPtr = JimExprIntValOrVar(interp, expr->expr->left); 15354 if (objPtr && JimIsWide(objPtr)) { 15355 Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right); 15356 if (objPtr2 && JimIsWide(objPtr2)) { 15357 jim_wide wideValueA = JimWideValue(objPtr); 15358 jim_wide wideValueB = JimWideValue(objPtr2); 15359 int cmpRes; 15360 switch (expr->expr->type) { 15361 case JIM_EXPROP_LT: 15362 cmpRes = wideValueA < wideValueB; 15363 break; 15364 case JIM_EXPROP_LTE: 15365 cmpRes = wideValueA <= wideValueB; 15366 break; 15367 case JIM_EXPROP_GT: 15368 cmpRes = wideValueA > wideValueB; 15369 break; 15370 case JIM_EXPROP_GTE: 15371 cmpRes = wideValueA >= wideValueB; 15372 break; 15373 case JIM_EXPROP_NUMEQ: 15374 cmpRes = wideValueA == wideValueB; 15375 break; 15376 case JIM_EXPROP_NUMNE: 15377 cmpRes = wideValueA != wideValueB; 15378 break; 15379 default: 15380 goto noopt; 15381 } 15382 Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj); 15383 goto done; 15384 } 15385 } 15386 break; 15387 } 15388 } 15389 noopt: 15390 #endif 15391 15392 expr->inUse++; 15393 15394 15395 retcode = JimExprEvalTermNode(interp, expr->expr); 15396 15397 15398 Jim_FreeIntRep(interp, exprObjPtr); 15399 exprObjPtr->typePtr = &exprObjType; 15400 Jim_SetIntRepPtr(exprObjPtr, expr); 15401 15402 done: 15403 Jim_DecrRefCount(interp, exprObjPtr); 15404 15405 return retcode; 15406 } 15407 15408 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr) 15409 { 15410 int retcode = Jim_EvalExpression(interp, exprObjPtr); 15411 15412 if (retcode == JIM_OK) { 15413 switch (ExprBool(interp, Jim_GetResult(interp))) { 15414 case 0: 15415 *boolPtr = 0; 15416 break; 15417 15418 case 1: 15419 *boolPtr = 1; 15420 break; 15421 15422 case -1: 15423 retcode = JIM_ERR; 15424 break; 15425 } 15426 } 15427 return retcode; 15428 } 15429 15430 15431 15432 15433 typedef struct ScanFmtPartDescr 15434 { 15435 const char *arg; 15436 const char *prefix; 15437 size_t width; 15438 int pos; 15439 char type; 15440 char modifier; 15441 } ScanFmtPartDescr; 15442 15443 15444 typedef struct ScanFmtStringObj 15445 { 15446 jim_wide size; 15447 char *stringRep; 15448 size_t count; 15449 size_t convCount; 15450 size_t maxPos; 15451 const char *error; 15452 char *scratch; 15453 ScanFmtPartDescr descr[1]; 15454 } ScanFmtStringObj; 15455 15456 15457 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 15458 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 15459 static void UpdateStringOfScanFmt(Jim_Obj *objPtr); 15460 15461 static const Jim_ObjType scanFmtStringObjType = { 15462 "scanformatstring", 15463 FreeScanFmtInternalRep, 15464 DupScanFmtInternalRep, 15465 UpdateStringOfScanFmt, 15466 JIM_TYPE_NONE, 15467 }; 15468 15469 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 15470 { 15471 JIM_NOTUSED(interp); 15472 Jim_Free((char *)objPtr->internalRep.ptr); 15473 objPtr->internalRep.ptr = 0; 15474 } 15475 15476 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 15477 { 15478 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size; 15479 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size); 15480 15481 JIM_NOTUSED(interp); 15482 memcpy(newVec, srcPtr->internalRep.ptr, size); 15483 dupPtr->internalRep.ptr = newVec; 15484 dupPtr->typePtr = &scanFmtStringObjType; 15485 } 15486 15487 static void UpdateStringOfScanFmt(Jim_Obj *objPtr) 15488 { 15489 JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep); 15490 } 15491 15492 15493 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 15494 { 15495 ScanFmtStringObj *fmtObj; 15496 char *buffer; 15497 int maxCount, i, approxSize, lastPos = -1; 15498 const char *fmt = Jim_String(objPtr); 15499 int maxFmtLen = Jim_Length(objPtr); 15500 const char *fmtEnd = fmt + maxFmtLen; 15501 int curr; 15502 15503 Jim_FreeIntRep(interp, objPtr); 15504 15505 for (i = 0, maxCount = 0; i < maxFmtLen; ++i) 15506 if (fmt[i] == '%') 15507 ++maxCount; 15508 15509 approxSize = sizeof(ScanFmtStringObj) 15510 +(maxCount + 1) * sizeof(ScanFmtPartDescr) 15511 +maxFmtLen * sizeof(char) + 3 + 1 15512 + maxFmtLen * sizeof(char) + 1 15513 + maxFmtLen * sizeof(char) 15514 +(maxCount + 1) * sizeof(char) 15515 +1; 15516 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize); 15517 memset(fmtObj, 0, approxSize); 15518 fmtObj->size = approxSize; 15519 fmtObj->maxPos = 0; 15520 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1]; 15521 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1; 15522 memcpy(fmtObj->stringRep, fmt, maxFmtLen); 15523 buffer = fmtObj->stringRep + maxFmtLen + 1; 15524 objPtr->internalRep.ptr = fmtObj; 15525 objPtr->typePtr = &scanFmtStringObjType; 15526 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) { 15527 int width = 0, skip; 15528 ScanFmtPartDescr *descr = &fmtObj->descr[curr]; 15529 15530 fmtObj->count++; 15531 descr->width = 0; 15532 15533 if (*fmt != '%' || fmt[1] == '%') { 15534 descr->type = 0; 15535 descr->prefix = &buffer[i]; 15536 for (; fmt < fmtEnd; ++fmt) { 15537 if (*fmt == '%') { 15538 if (fmt[1] != '%') 15539 break; 15540 ++fmt; 15541 } 15542 buffer[i++] = *fmt; 15543 } 15544 buffer[i++] = 0; 15545 } 15546 15547 ++fmt; 15548 15549 if (fmt >= fmtEnd) 15550 goto done; 15551 descr->pos = 0; 15552 if (*fmt == '*') { 15553 descr->pos = -1; 15554 ++fmt; 15555 } 15556 else 15557 fmtObj->convCount++; 15558 15559 if (sscanf(fmt, "%d%n", &width, &skip) == 1) { 15560 fmt += skip; 15561 15562 if (descr->pos != -1 && *fmt == '$') { 15563 int prev; 15564 15565 ++fmt; 15566 descr->pos = width; 15567 width = 0; 15568 15569 if ((lastPos == 0 && descr->pos > 0) 15570 || (lastPos > 0 && descr->pos == 0)) { 15571 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers"; 15572 return JIM_ERR; 15573 } 15574 15575 for (prev = 0; prev < curr; ++prev) { 15576 if (fmtObj->descr[prev].pos == -1) 15577 continue; 15578 if (fmtObj->descr[prev].pos == descr->pos) { 15579 fmtObj->error = 15580 "variable is assigned by multiple \"%n$\" conversion specifiers"; 15581 return JIM_ERR; 15582 } 15583 } 15584 if (descr->pos < 0) { 15585 fmtObj->error = 15586 "\"%n$\" conversion specifier is negative"; 15587 return JIM_ERR; 15588 } 15589 15590 if (sscanf(fmt, "%d%n", &width, &skip) == 1) { 15591 descr->width = width; 15592 fmt += skip; 15593 } 15594 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos) 15595 fmtObj->maxPos = descr->pos; 15596 } 15597 else { 15598 15599 descr->width = width; 15600 } 15601 } 15602 15603 if (lastPos == -1) 15604 lastPos = descr->pos; 15605 15606 if (*fmt == '[') { 15607 int swapped = 1, beg = i, end, j; 15608 15609 descr->type = '['; 15610 descr->arg = &buffer[i]; 15611 ++fmt; 15612 if (*fmt == '^') 15613 buffer[i++] = *fmt++; 15614 if (*fmt == ']') 15615 buffer[i++] = *fmt++; 15616 while (*fmt && *fmt != ']') 15617 buffer[i++] = *fmt++; 15618 if (*fmt != ']') { 15619 fmtObj->error = "unmatched [ in format string"; 15620 return JIM_ERR; 15621 } 15622 end = i; 15623 buffer[i++] = 0; 15624 15625 while (swapped) { 15626 swapped = 0; 15627 for (j = beg + 1; j < end - 1; ++j) { 15628 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) { 15629 char tmp = buffer[j - 1]; 15630 15631 buffer[j - 1] = buffer[j + 1]; 15632 buffer[j + 1] = tmp; 15633 swapped = 1; 15634 } 15635 } 15636 } 15637 } 15638 else { 15639 15640 if (fmt < fmtEnd && strchr("hlL", *fmt)) 15641 descr->modifier = tolower((int)*fmt++); 15642 15643 if (fmt >= fmtEnd) { 15644 fmtObj->error = "missing scan conversion character"; 15645 return JIM_ERR; 15646 } 15647 15648 descr->type = *fmt; 15649 if (strchr("efgcsndoxui", *fmt) == 0) { 15650 fmtObj->error = "bad scan conversion character"; 15651 return JIM_ERR; 15652 } 15653 else if (*fmt == 'c' && descr->width != 0) { 15654 fmtObj->error = "field width may not be specified in %c " "conversion"; 15655 return JIM_ERR; 15656 } 15657 else if (*fmt == 'u' && descr->modifier == 'l') { 15658 fmtObj->error = "unsigned wide not supported"; 15659 return JIM_ERR; 15660 } 15661 } 15662 curr++; 15663 } 15664 done: 15665 return JIM_OK; 15666 } 15667 15668 15669 15670 #define FormatGetCnvCount(_fo_) \ 15671 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount 15672 #define FormatGetMaxPos(_fo_) \ 15673 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos 15674 #define FormatGetError(_fo_) \ 15675 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error 15676 15677 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str) 15678 { 15679 char *buffer = Jim_StrDup(str); 15680 char *p = buffer; 15681 15682 while (*str) { 15683 int c; 15684 int n; 15685 15686 if (!sdescr && isspace(UCHAR(*str))) 15687 break; 15688 15689 n = utf8_tounicode(str, &c); 15690 if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN)) 15691 break; 15692 while (n--) 15693 *p++ = *str++; 15694 } 15695 *p = 0; 15696 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer); 15697 } 15698 15699 15700 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen, 15701 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr) 15702 { 15703 const char *tok; 15704 const ScanFmtPartDescr *descr = &fmtObj->descr[idx]; 15705 size_t scanned = 0; 15706 size_t anchor = pos; 15707 int i; 15708 Jim_Obj *tmpObj = NULL; 15709 15710 15711 *valObjPtr = 0; 15712 if (descr->prefix) { 15713 for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) { 15714 15715 if (isspace(UCHAR(descr->prefix[i]))) 15716 while (pos < str_bytelen && isspace(UCHAR(str[pos]))) 15717 ++pos; 15718 else if (descr->prefix[i] != str[pos]) 15719 break; 15720 else 15721 ++pos; 15722 } 15723 if (pos >= str_bytelen) { 15724 return -1; 15725 } 15726 else if (descr->prefix[i] != 0) 15727 return 0; 15728 } 15729 15730 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n') 15731 while (isspace(UCHAR(str[pos]))) 15732 ++pos; 15733 15734 15735 scanned = pos - anchor; 15736 15737 15738 if (descr->type == 'n') { 15739 15740 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned); 15741 } 15742 else if (pos >= str_bytelen) { 15743 15744 return -1; 15745 } 15746 else if (descr->type == 'c') { 15747 int c; 15748 scanned += utf8_tounicode(&str[pos], &c); 15749 *valObjPtr = Jim_NewIntObj(interp, c); 15750 return scanned; 15751 } 15752 else { 15753 15754 if (descr->width > 0) { 15755 size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos); 15756 size_t tLen = descr->width > sLen ? sLen : descr->width; 15757 15758 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen); 15759 tok = tmpObj->bytes; 15760 } 15761 else { 15762 15763 tok = &str[pos]; 15764 } 15765 switch (descr->type) { 15766 case 'd': 15767 case 'o': 15768 case 'x': 15769 case 'u': 15770 case 'i':{ 15771 char *endp; 15772 jim_wide w; 15773 15774 int base = descr->type == 'o' ? 8 15775 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10; 15776 15777 15778 if (base == 0) { 15779 w = jim_strtoull(tok, &endp); 15780 } 15781 else { 15782 w = strtoull(tok, &endp, base); 15783 } 15784 15785 if (endp != tok) { 15786 15787 *valObjPtr = Jim_NewIntObj(interp, w); 15788 15789 15790 scanned += endp - tok; 15791 } 15792 else { 15793 scanned = *tok ? 0 : -1; 15794 } 15795 break; 15796 } 15797 case 's': 15798 case '[':{ 15799 *valObjPtr = JimScanAString(interp, descr->arg, tok); 15800 scanned += Jim_Length(*valObjPtr); 15801 break; 15802 } 15803 case 'e': 15804 case 'f': 15805 case 'g':{ 15806 char *endp; 15807 double value = strtod(tok, &endp); 15808 15809 if (endp != tok) { 15810 15811 *valObjPtr = Jim_NewDoubleObj(interp, value); 15812 15813 scanned += endp - tok; 15814 } 15815 else { 15816 scanned = *tok ? 0 : -1; 15817 } 15818 break; 15819 } 15820 } 15821 if (tmpObj) { 15822 Jim_FreeNewObj(interp, tmpObj); 15823 } 15824 } 15825 return scanned; 15826 } 15827 15828 15829 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags) 15830 { 15831 size_t i, pos; 15832 int scanned = 1; 15833 const char *str = Jim_String(strObjPtr); 15834 int str_bytelen = Jim_Length(strObjPtr); 15835 Jim_Obj *resultList = 0; 15836 Jim_Obj **resultVec = 0; 15837 int resultc; 15838 Jim_Obj *emptyStr = 0; 15839 ScanFmtStringObj *fmtObj; 15840 15841 15842 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format")); 15843 15844 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr; 15845 15846 if (fmtObj->error != 0) { 15847 if (flags & JIM_ERRMSG) 15848 Jim_SetResultString(interp, fmtObj->error, -1); 15849 return 0; 15850 } 15851 15852 emptyStr = Jim_NewEmptyStringObj(interp); 15853 Jim_IncrRefCount(emptyStr); 15854 15855 resultList = Jim_NewListObj(interp, NULL, 0); 15856 if (fmtObj->maxPos > 0) { 15857 for (i = 0; i < fmtObj->maxPos; ++i) 15858 Jim_ListAppendElement(interp, resultList, emptyStr); 15859 JimListGetElements(interp, resultList, &resultc, &resultVec); 15860 } 15861 15862 for (i = 0, pos = 0; i < fmtObj->count; ++i) { 15863 ScanFmtPartDescr *descr = &(fmtObj->descr[i]); 15864 Jim_Obj *value = 0; 15865 15866 15867 if (descr->type == 0) 15868 continue; 15869 15870 if (scanned > 0) 15871 scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value); 15872 15873 if (scanned == -1 && i == 0) 15874 goto eof; 15875 15876 pos += scanned; 15877 15878 15879 if (value == 0) 15880 value = Jim_NewEmptyStringObj(interp); 15881 15882 if (descr->pos == -1) { 15883 Jim_FreeNewObj(interp, value); 15884 } 15885 else if (descr->pos == 0) 15886 15887 Jim_ListAppendElement(interp, resultList, value); 15888 else if (resultVec[descr->pos - 1] == emptyStr) { 15889 15890 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]); 15891 Jim_IncrRefCount(value); 15892 resultVec[descr->pos - 1] = value; 15893 } 15894 else { 15895 15896 Jim_FreeNewObj(interp, value); 15897 goto err; 15898 } 15899 } 15900 Jim_DecrRefCount(interp, emptyStr); 15901 return resultList; 15902 eof: 15903 Jim_DecrRefCount(interp, emptyStr); 15904 Jim_FreeNewObj(interp, resultList); 15905 return (Jim_Obj *)EOF; 15906 err: 15907 Jim_DecrRefCount(interp, emptyStr); 15908 Jim_FreeNewObj(interp, resultList); 15909 return 0; 15910 } 15911 15912 15913 static void JimPrngInit(Jim_Interp *interp) 15914 { 15915 #define PRNG_SEED_SIZE 256 15916 int i; 15917 unsigned int *seed; 15918 time_t t = time(NULL); 15919 15920 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState)); 15921 15922 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed)); 15923 for (i = 0; i < PRNG_SEED_SIZE; i++) { 15924 seed[i] = (rand() ^ t ^ clock()); 15925 } 15926 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed)); 15927 Jim_Free(seed); 15928 } 15929 15930 15931 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len) 15932 { 15933 Jim_PrngState *prng; 15934 unsigned char *destByte = (unsigned char *)dest; 15935 unsigned int si, sj, x; 15936 15937 15938 if (interp->prngState == NULL) 15939 JimPrngInit(interp); 15940 prng = interp->prngState; 15941 15942 for (x = 0; x < len; x++) { 15943 prng->i = (prng->i + 1) & 0xff; 15944 si = prng->sbox[prng->i]; 15945 prng->j = (prng->j + si) & 0xff; 15946 sj = prng->sbox[prng->j]; 15947 prng->sbox[prng->i] = sj; 15948 prng->sbox[prng->j] = si; 15949 *destByte++ = prng->sbox[(si + sj) & 0xff]; 15950 } 15951 } 15952 15953 15954 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen) 15955 { 15956 int i; 15957 Jim_PrngState *prng; 15958 15959 15960 if (interp->prngState == NULL) 15961 JimPrngInit(interp); 15962 prng = interp->prngState; 15963 15964 15965 for (i = 0; i < 256; i++) 15966 prng->sbox[i] = i; 15967 15968 for (i = 0; i < seedLen; i++) { 15969 unsigned char t; 15970 15971 t = prng->sbox[i & 0xFF]; 15972 prng->sbox[i & 0xFF] = prng->sbox[seed[i]]; 15973 prng->sbox[seed[i]] = t; 15974 } 15975 prng->i = prng->j = 0; 15976 15977 for (i = 0; i < 256; i += seedLen) { 15978 JimRandomBytes(interp, seed, seedLen); 15979 } 15980 } 15981 15982 15983 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 15984 { 15985 jim_wide wideValue, increment = 1; 15986 Jim_Obj *intObjPtr; 15987 15988 if (argc != 2 && argc != 3) { 15989 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?"); 15990 return JIM_ERR; 15991 } 15992 if (argc == 3) { 15993 if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK) 15994 return JIM_ERR; 15995 } 15996 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); 15997 if (!intObjPtr) { 15998 15999 wideValue = 0; 16000 } 16001 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) { 16002 return JIM_ERR; 16003 } 16004 if (!intObjPtr || Jim_IsShared(intObjPtr)) { 16005 intObjPtr = Jim_NewIntObj(interp, wideValue + increment); 16006 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) { 16007 Jim_FreeNewObj(interp, intObjPtr); 16008 return JIM_ERR; 16009 } 16010 } 16011 else { 16012 16013 Jim_InvalidateStringRep(intObjPtr); 16014 JimWideValue(intObjPtr) = wideValue + increment; 16015 16016 if (argv[1]->typePtr != &variableObjType) { 16017 16018 Jim_SetVariable(interp, argv[1], intObjPtr); 16019 } 16020 } 16021 Jim_SetResult(interp, intObjPtr); 16022 return JIM_OK; 16023 } 16024 16025 16026 #define JIM_EVAL_SARGV_LEN 8 16027 #define JIM_EVAL_SINTV_LEN 8 16028 16029 static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv) 16030 { 16031 JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object")); 16032 16033 int ret; 16034 Jim_Obj *nargv[7]; 16035 Jim_Obj *traceCmdObj = interp->traceCmdObj; 16036 Jim_Obj *resultObj = Jim_GetResult(interp); 16037 ScriptObj *script = NULL; 16038 16039 16040 16041 if (interp->evalFrame->scriptObj) { 16042 script = JimGetScript(interp, interp->evalFrame->scriptObj); 16043 } 16044 16045 nargv[0] = traceCmdObj; 16046 nargv[1] = Jim_NewStringObj(interp, type, -1); 16047 nargv[2] = script ? script->fileNameObj : interp->emptyObj; 16048 nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1); 16049 nargv[4] = resultObj; 16050 nargv[5] = argv[0]; 16051 nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1); 16052 16053 16054 interp->traceCmdObj = NULL; 16055 16056 Jim_IncrRefCount(resultObj); 16057 ret = Jim_EvalObjVector(interp, 7, nargv); 16058 Jim_DecrRefCount(interp, resultObj); 16059 16060 if (ret == JIM_OK || ret == JIM_RETURN) { 16061 16062 interp->traceCmdObj = traceCmdObj; 16063 Jim_SetEmptyResult(interp); 16064 ret = JIM_OK; 16065 } 16066 else { 16067 16068 Jim_DecrRefCount(interp, traceCmdObj); 16069 } 16070 return ret; 16071 } 16072 16073 16074 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 16075 { 16076 int retcode; 16077 16078 if (interp->unknown_called > 50) { 16079 return JIM_ERR; 16080 } 16081 16082 16083 16084 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL) 16085 return JIM_ERR; 16086 16087 interp->unknown_called++; 16088 16089 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv); 16090 interp->unknown_called--; 16091 16092 return retcode; 16093 } 16094 16095 static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj) 16096 { 16097 memset(frame, 0, sizeof(*frame)); 16098 frame->parent = interp->evalFrame; 16099 frame->level = frame->parent->level + 1; 16100 frame->procLevel = interp->procLevel; 16101 frame->framePtr = interp->framePtr; 16102 if (scriptObj) { 16103 frame->scriptObj = scriptObj; 16104 } 16105 else { 16106 frame->scriptObj = frame->parent->scriptObj; 16107 } 16108 interp->evalFrame = frame; 16109 #if 0 16110 if (frame->scriptObj) { 16111 printf("script: %.*s\n", 20, Jim_String(frame->scriptObj)); 16112 } 16113 #endif 16114 } 16115 16116 static void JimPopEvalFrame(Jim_Interp *interp) 16117 { 16118 interp->evalFrame = interp->evalFrame->parent; 16119 } 16120 16121 16122 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 16123 { 16124 int retcode; 16125 Jim_Cmd *cmdPtr; 16126 void *prevPrivData; 16127 Jim_Obj *tailcallObj = NULL; 16128 16129 #if 0 16130 printf("invoke"); 16131 int j; 16132 for (j = 0; j < objc; j++) { 16133 printf(" '%s'", Jim_String(objv[j])); 16134 } 16135 printf("\n"); 16136 #endif 16137 16138 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG); 16139 if (cmdPtr == NULL) { 16140 return JimUnknown(interp, objc, objv); 16141 } 16142 JimIncrCmdRefCount(cmdPtr); 16143 16144 if (interp->evalDepth == interp->maxEvalDepth) { 16145 Jim_SetResultString(interp, "Infinite eval recursion", -1); 16146 retcode = JIM_ERR; 16147 goto out; 16148 } 16149 interp->evalDepth++; 16150 prevPrivData = interp->cmdPrivData; 16151 16152 tailcall: 16153 16154 interp->evalFrame->argc = objc; 16155 interp->evalFrame->argv = objv; 16156 interp->evalFrame->cmd = cmdPtr; 16157 16158 if (!interp->traceCmdObj || 16159 (retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) { 16160 16161 Jim_SetEmptyResult(interp); 16162 if (cmdPtr->isproc) { 16163 retcode = JimCallProcedure(interp, cmdPtr, objc, objv); 16164 } 16165 else { 16166 interp->cmdPrivData = cmdPtr->u.native.privData; 16167 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); 16168 } 16169 if (retcode == JIM_ERR) { 16170 JimSetErrorStack(interp, NULL); 16171 } 16172 } 16173 16174 if (tailcallObj) { 16175 16176 Jim_DecrRefCount(interp, tailcallObj); 16177 tailcallObj = NULL; 16178 } 16179 16180 16181 interp->evalFrame->argc = 0; 16182 interp->evalFrame->argv = NULL; 16183 16184 16185 if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) { 16186 JimDecrCmdRefCount(interp, cmdPtr); 16187 16188 16189 cmdPtr = interp->framePtr->tailcallCmd; 16190 interp->framePtr->tailcallCmd = NULL; 16191 tailcallObj = interp->framePtr->tailcallObj; 16192 interp->framePtr->tailcallObj = NULL; 16193 objc = tailcallObj->internalRep.listValue.len; 16194 objv = tailcallObj->internalRep.listValue.ele; 16195 goto tailcall; 16196 } 16197 16198 interp->cmdPrivData = prevPrivData; 16199 interp->evalDepth--; 16200 16201 out: 16202 JimDecrCmdRefCount(interp, cmdPtr); 16203 16204 if (retcode == JIM_ERR) { 16205 JimSetErrorStack(interp, NULL); 16206 } 16207 16208 if (interp->framePtr->tailcallObj) { 16209 JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd); 16210 Jim_DecrRefCount(interp, interp->framePtr->tailcallObj); 16211 interp->framePtr->tailcallCmd = NULL; 16212 interp->framePtr->tailcallObj = NULL; 16213 } 16214 16215 return retcode; 16216 } 16217 16218 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 16219 { 16220 int i, retcode; 16221 Jim_EvalFrame frame; 16222 16223 16224 for (i = 0; i < objc; i++) 16225 Jim_IncrRefCount(objv[i]); 16226 16227 16228 JimPushEvalFrame(interp, &frame, NULL); 16229 16230 retcode = JimInvokeCommand(interp, objc, objv); 16231 16232 JimPopEvalFrame(interp); 16233 16234 16235 for (i = 0; i < objc; i++) 16236 Jim_DecrRefCount(interp, objv[i]); 16237 16238 return retcode; 16239 } 16240 16241 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv) 16242 { 16243 int ret; 16244 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv)); 16245 16246 nargv[0] = prefix; 16247 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc); 16248 ret = Jim_EvalObjVector(interp, objc + 1, nargv); 16249 Jim_Free(nargv); 16250 return ret; 16251 } 16252 16253 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr) 16254 { 16255 Jim_Obj *objPtr; 16256 int ret = JIM_ERR; 16257 16258 switch (token->type) { 16259 case JIM_TT_STR: 16260 case JIM_TT_ESC: 16261 objPtr = token->objPtr; 16262 break; 16263 case JIM_TT_VAR: 16264 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG); 16265 break; 16266 case JIM_TT_DICTSUGAR: 16267 objPtr = JimExpandDictSugar(interp, token->objPtr); 16268 break; 16269 case JIM_TT_EXPRSUGAR: 16270 ret = Jim_EvalExpression(interp, token->objPtr); 16271 if (ret == JIM_OK) { 16272 objPtr = Jim_GetResult(interp); 16273 } 16274 else { 16275 objPtr = NULL; 16276 } 16277 break; 16278 case JIM_TT_CMD: 16279 ret = Jim_EvalObj(interp, token->objPtr); 16280 if (ret == JIM_OK || ret == JIM_RETURN) { 16281 objPtr = interp->result; 16282 } else { 16283 16284 objPtr = NULL; 16285 } 16286 break; 16287 default: 16288 JimPanic((1, 16289 "default token type (%d) reached " "in Jim_SubstObj().", token->type)); 16290 objPtr = NULL; 16291 break; 16292 } 16293 if (objPtr) { 16294 *objPtrPtr = objPtr; 16295 return JIM_OK; 16296 } 16297 return ret; 16298 } 16299 16300 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags) 16301 { 16302 int totlen = 0, i; 16303 Jim_Obj **intv; 16304 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN]; 16305 Jim_Obj *objPtr; 16306 char *s; 16307 16308 if (tokens <= JIM_EVAL_SINTV_LEN) 16309 intv = sintv; 16310 else 16311 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens); 16312 16313 for (i = 0; i < tokens; i++) { 16314 switch (JimSubstOneToken(interp, &token[i], &intv[i])) { 16315 case JIM_OK: 16316 case JIM_RETURN: 16317 break; 16318 case JIM_BREAK: 16319 if (flags & JIM_SUBST_FLAG) { 16320 16321 tokens = i; 16322 continue; 16323 } 16324 16325 16326 case JIM_CONTINUE: 16327 if (flags & JIM_SUBST_FLAG) { 16328 intv[i] = NULL; 16329 continue; 16330 } 16331 16332 16333 default: 16334 while (i--) { 16335 Jim_DecrRefCount(interp, intv[i]); 16336 } 16337 if (intv != sintv) { 16338 Jim_Free(intv); 16339 } 16340 return NULL; 16341 } 16342 Jim_IncrRefCount(intv[i]); 16343 Jim_String(intv[i]); 16344 totlen += intv[i]->length; 16345 } 16346 16347 16348 if (tokens == 1 && intv[0] && intv == sintv) { 16349 16350 intv[0]->refCount--; 16351 return intv[0]; 16352 } 16353 16354 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0); 16355 16356 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC 16357 && token[2].type == JIM_TT_VAR) { 16358 16359 objPtr->typePtr = &interpolatedObjType; 16360 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr; 16361 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2]; 16362 Jim_IncrRefCount(intv[2]); 16363 } 16364 else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) { 16365 16366 int line; 16367 Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line); 16368 Jim_SetSourceInfo(interp, objPtr, fileNameObj, line); 16369 } 16370 16371 16372 s = objPtr->bytes = Jim_Alloc(totlen + 1); 16373 objPtr->length = totlen; 16374 for (i = 0; i < tokens; i++) { 16375 if (intv[i]) { 16376 memcpy(s, intv[i]->bytes, intv[i]->length); 16377 s += intv[i]->length; 16378 Jim_DecrRefCount(interp, intv[i]); 16379 } 16380 } 16381 objPtr->bytes[totlen] = '\0'; 16382 16383 if (intv != sintv) { 16384 Jim_Free(intv); 16385 } 16386 16387 return objPtr; 16388 } 16389 16390 16391 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr) 16392 { 16393 int retcode = JIM_OK; 16394 Jim_EvalFrame frame; 16395 16396 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list.")); 16397 16398 JimPushEvalFrame(interp, &frame, NULL); 16399 16400 if (listPtr->internalRep.listValue.len) { 16401 Jim_IncrRefCount(listPtr); 16402 retcode = JimInvokeCommand(interp, 16403 listPtr->internalRep.listValue.len, 16404 listPtr->internalRep.listValue.ele); 16405 Jim_DecrRefCount(interp, listPtr); 16406 } 16407 16408 JimPopEvalFrame(interp); 16409 16410 return retcode; 16411 } 16412 16413 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr) 16414 { 16415 SetListFromAny(interp, listPtr); 16416 return JimEvalObjList(interp, listPtr); 16417 } 16418 16419 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr) 16420 { 16421 int i; 16422 ScriptObj *script; 16423 ScriptToken *token; 16424 int retcode = JIM_OK; 16425 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL; 16426 Jim_EvalFrame frame; 16427 16428 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { 16429 return JimEvalObjList(interp, scriptObjPtr); 16430 } 16431 16432 Jim_IncrRefCount(scriptObjPtr); 16433 script = JimGetScript(interp, scriptObjPtr); 16434 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) { 16435 JimSetErrorStack(interp, script); 16436 Jim_DecrRefCount(interp, scriptObjPtr); 16437 return JIM_ERR; 16438 } 16439 16440 Jim_SetEmptyResult(interp); 16441 16442 token = script->token; 16443 16444 #ifdef JIM_OPTIMIZATION 16445 if (script->len == 0) { 16446 Jim_DecrRefCount(interp, scriptObjPtr); 16447 return JIM_OK; 16448 } 16449 if (script->len == 3 16450 && token[1].objPtr->typePtr == &commandObjType 16451 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0 16452 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand 16453 && token[2].objPtr->typePtr == &variableObjType) { 16454 16455 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE); 16456 16457 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { 16458 JimWideValue(objPtr)++; 16459 Jim_InvalidateStringRep(objPtr); 16460 Jim_DecrRefCount(interp, scriptObjPtr); 16461 Jim_SetResult(interp, objPtr); 16462 return JIM_OK; 16463 } 16464 } 16465 #endif 16466 16467 script->inUse++; 16468 16469 JimPushEvalFrame(interp, &frame, scriptObjPtr); 16470 16471 16472 interp->errorFlag = 0; 16473 argv = sargv; 16474 16475 for (i = 0; i < script->len && retcode == JIM_OK; ) { 16476 int argc; 16477 int j; 16478 16479 16480 argc = token[i].objPtr->internalRep.scriptLineValue.argc; 16481 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line; 16482 16483 16484 if (argc > JIM_EVAL_SARGV_LEN) 16485 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc); 16486 16487 16488 i++; 16489 16490 for (j = 0; j < argc; j++) { 16491 long wordtokens = 1; 16492 int expand = 0; 16493 Jim_Obj *wordObjPtr = NULL; 16494 16495 if (token[i].type == JIM_TT_WORD) { 16496 wordtokens = JimWideValue(token[i++].objPtr); 16497 if (wordtokens < 0) { 16498 expand = 1; 16499 wordtokens = -wordtokens; 16500 } 16501 } 16502 16503 if (wordtokens == 1) { 16504 16505 switch (token[i].type) { 16506 case JIM_TT_ESC: 16507 case JIM_TT_STR: 16508 wordObjPtr = token[i].objPtr; 16509 break; 16510 case JIM_TT_VAR: 16511 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG); 16512 break; 16513 case JIM_TT_EXPRSUGAR: 16514 retcode = Jim_EvalExpression(interp, token[i].objPtr); 16515 if (retcode == JIM_OK) { 16516 wordObjPtr = Jim_GetResult(interp); 16517 } 16518 else { 16519 wordObjPtr = NULL; 16520 } 16521 break; 16522 case JIM_TT_DICTSUGAR: 16523 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr); 16524 break; 16525 case JIM_TT_CMD: 16526 retcode = Jim_EvalObj(interp, token[i].objPtr); 16527 if (retcode == JIM_OK) { 16528 wordObjPtr = Jim_GetResult(interp); 16529 } 16530 break; 16531 default: 16532 JimPanic((1, "default token type reached " "in Jim_EvalObj().")); 16533 } 16534 } 16535 else { 16536 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE); 16537 } 16538 16539 if (!wordObjPtr) { 16540 if (retcode == JIM_OK) { 16541 retcode = JIM_ERR; 16542 } 16543 break; 16544 } 16545 16546 Jim_IncrRefCount(wordObjPtr); 16547 i += wordtokens; 16548 16549 if (!expand) { 16550 argv[j] = wordObjPtr; 16551 } 16552 else { 16553 16554 int len = Jim_ListLength(interp, wordObjPtr); 16555 int newargc = argc + len - 1; 16556 int k; 16557 16558 if (len > 1) { 16559 if (argv == sargv) { 16560 if (newargc > JIM_EVAL_SARGV_LEN) { 16561 argv = Jim_Alloc(sizeof(*argv) * newargc); 16562 memcpy(argv, sargv, sizeof(*argv) * j); 16563 } 16564 } 16565 else { 16566 16567 argv = Jim_Realloc(argv, sizeof(*argv) * newargc); 16568 } 16569 } 16570 16571 16572 for (k = 0; k < len; k++) { 16573 argv[j++] = wordObjPtr->internalRep.listValue.ele[k]; 16574 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]); 16575 } 16576 16577 Jim_DecrRefCount(interp, wordObjPtr); 16578 16579 16580 j--; 16581 argc += len - 1; 16582 } 16583 } 16584 16585 if (retcode == JIM_OK && argc) { 16586 16587 retcode = JimInvokeCommand(interp, argc, argv); 16588 16589 if (Jim_CheckSignal(interp)) { 16590 retcode = JIM_SIGNAL; 16591 } 16592 } 16593 16594 16595 while (j-- > 0) { 16596 Jim_DecrRefCount(interp, argv[j]); 16597 } 16598 16599 if (argv != sargv) { 16600 Jim_Free(argv); 16601 argv = sargv; 16602 } 16603 } 16604 16605 16606 if (retcode == JIM_ERR) { 16607 JimSetErrorStack(interp, NULL); 16608 } 16609 16610 JimPopEvalFrame(interp); 16611 16612 Jim_FreeIntRep(interp, scriptObjPtr); 16613 scriptObjPtr->typePtr = &scriptObjType; 16614 Jim_SetIntRepPtr(scriptObjPtr, script); 16615 Jim_DecrRefCount(interp, scriptObjPtr); 16616 16617 return retcode; 16618 } 16619 16620 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj) 16621 { 16622 int retcode; 16623 16624 const char *varname = Jim_String(argNameObj); 16625 if (*varname == '&') { 16626 16627 Jim_Obj *objPtr; 16628 Jim_CallFrame *savedCallFrame = interp->framePtr; 16629 16630 interp->framePtr = interp->framePtr->parent; 16631 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG); 16632 interp->framePtr = savedCallFrame; 16633 if (!objPtr) { 16634 return JIM_ERR; 16635 } 16636 16637 16638 objPtr = Jim_NewStringObj(interp, varname + 1, -1); 16639 Jim_IncrRefCount(objPtr); 16640 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent); 16641 Jim_DecrRefCount(interp, objPtr); 16642 } 16643 else { 16644 retcode = Jim_SetVariable(interp, argNameObj, argValObj); 16645 } 16646 return retcode; 16647 } 16648 16649 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd) 16650 { 16651 16652 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0); 16653 int i; 16654 16655 for (i = 0; i < cmd->u.proc.argListLen; i++) { 16656 Jim_AppendString(interp, argmsg, " ", 1); 16657 16658 if (i == cmd->u.proc.argsPos) { 16659 if (cmd->u.proc.arglist[i].defaultObjPtr) { 16660 16661 Jim_AppendString(interp, argmsg, "?", 1); 16662 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr); 16663 Jim_AppendString(interp, argmsg, " ...?", -1); 16664 } 16665 else { 16666 16667 Jim_AppendString(interp, argmsg, "?arg ...?", -1); 16668 } 16669 } 16670 else { 16671 if (cmd->u.proc.arglist[i].defaultObjPtr) { 16672 Jim_AppendString(interp, argmsg, "?", 1); 16673 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr); 16674 Jim_AppendString(interp, argmsg, "?", 1); 16675 } 16676 else { 16677 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr); 16678 if (*arg == '&') { 16679 arg++; 16680 } 16681 Jim_AppendString(interp, argmsg, arg, -1); 16682 } 16683 } 16684 } 16685 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg); 16686 } 16687 16688 #ifdef jim_ext_namespace 16689 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj) 16690 { 16691 Jim_CallFrame *callFramePtr; 16692 int retcode; 16693 16694 16695 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj); 16696 callFramePtr->argv = interp->evalFrame->argv; 16697 callFramePtr->argc = interp->evalFrame->argc; 16698 callFramePtr->procArgsObjPtr = NULL; 16699 callFramePtr->procBodyObjPtr = scriptObj; 16700 callFramePtr->staticVars = NULL; 16701 Jim_IncrRefCount(scriptObj); 16702 interp->framePtr = callFramePtr; 16703 16704 16705 if (interp->framePtr->level == interp->maxCallFrameDepth) { 16706 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1); 16707 retcode = JIM_ERR; 16708 } 16709 else { 16710 16711 retcode = Jim_EvalObj(interp, scriptObj); 16712 } 16713 16714 16715 interp->framePtr = interp->framePtr->parent; 16716 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE); 16717 16718 return retcode; 16719 } 16720 #endif 16721 16722 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv) 16723 { 16724 Jim_CallFrame *callFramePtr; 16725 int i, d, retcode, optargs; 16726 16727 16728 if (argc - 1 < cmd->u.proc.reqArity || 16729 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) { 16730 JimSetProcWrongArgs(interp, argv[0], cmd); 16731 return JIM_ERR; 16732 } 16733 16734 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) { 16735 16736 return JIM_OK; 16737 } 16738 16739 16740 if (interp->framePtr->level == interp->maxCallFrameDepth) { 16741 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1); 16742 return JIM_ERR; 16743 } 16744 16745 16746 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj); 16747 callFramePtr->argv = argv; 16748 callFramePtr->argc = argc; 16749 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr; 16750 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr; 16751 callFramePtr->staticVars = cmd->u.proc.staticVars; 16752 16753 interp->procLevel++; 16754 16755 Jim_IncrRefCount(cmd->u.proc.argListObjPtr); 16756 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr); 16757 interp->framePtr = callFramePtr; 16758 16759 16760 optargs = (argc - 1 - cmd->u.proc.reqArity); 16761 16762 16763 i = 1; 16764 for (d = 0; d < cmd->u.proc.argListLen; d++) { 16765 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr; 16766 if (d == cmd->u.proc.argsPos) { 16767 16768 Jim_Obj *listObjPtr; 16769 int argsLen = 0; 16770 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) { 16771 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity); 16772 } 16773 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen); 16774 16775 16776 if (cmd->u.proc.arglist[d].defaultObjPtr) { 16777 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr; 16778 } 16779 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr); 16780 if (retcode != JIM_OK) { 16781 goto badargset; 16782 } 16783 16784 i += argsLen; 16785 continue; 16786 } 16787 16788 16789 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) { 16790 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]); 16791 } 16792 else { 16793 16794 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr); 16795 } 16796 if (retcode != JIM_OK) { 16797 goto badargset; 16798 } 16799 } 16800 16801 if (interp->traceCmdObj == NULL || 16802 (retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) { 16803 16804 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr); 16805 } 16806 16807 badargset: 16808 16809 16810 retcode = JimInvokeDefer(interp, retcode); 16811 interp->framePtr = interp->framePtr->parent; 16812 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE); 16813 16814 16815 if (retcode == JIM_RETURN) { 16816 if (--interp->returnLevel <= 0) { 16817 retcode = interp->returnCode; 16818 interp->returnCode = JIM_OK; 16819 interp->returnLevel = 0; 16820 } 16821 } 16822 interp->procLevel--; 16823 16824 return retcode; 16825 } 16826 16827 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script) 16828 { 16829 int retval; 16830 Jim_Obj *scriptObjPtr; 16831 16832 scriptObjPtr = Jim_NewStringObj(interp, script, -1); 16833 Jim_IncrRefCount(scriptObjPtr); 16834 if (filename) { 16835 Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno); 16836 } 16837 retval = Jim_EvalObj(interp, scriptObjPtr); 16838 Jim_DecrRefCount(interp, scriptObjPtr); 16839 return retval; 16840 } 16841 16842 int Jim_Eval(Jim_Interp *interp, const char *script) 16843 { 16844 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1)); 16845 } 16846 16847 16848 int Jim_EvalGlobal(Jim_Interp *interp, const char *script) 16849 { 16850 int retval; 16851 Jim_CallFrame *savedFramePtr = interp->framePtr; 16852 16853 interp->framePtr = interp->topFramePtr; 16854 retval = Jim_Eval(interp, script); 16855 interp->framePtr = savedFramePtr; 16856 16857 return retval; 16858 } 16859 16860 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename) 16861 { 16862 int retval; 16863 Jim_CallFrame *savedFramePtr = interp->framePtr; 16864 16865 interp->framePtr = interp->topFramePtr; 16866 retval = Jim_EvalFile(interp, filename); 16867 interp->framePtr = savedFramePtr; 16868 16869 return retval; 16870 } 16871 16872 #include <sys/stat.h> 16873 16874 static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename) 16875 { 16876 jim_stat_t sb; 16877 int fd; 16878 char *buf; 16879 int readlen; 16880 16881 if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) { 16882 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno)); 16883 return NULL; 16884 } 16885 buf = Jim_Alloc(sb.st_size + 1); 16886 readlen = read(fd, buf, sb.st_size); 16887 close(fd); 16888 if (readlen < 0) { 16889 Jim_Free(buf); 16890 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno)); 16891 return NULL; 16892 } 16893 else { 16894 Jim_Obj *objPtr; 16895 buf[readlen] = 0; 16896 16897 objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen); 16898 16899 return objPtr; 16900 } 16901 } 16902 16903 16904 int Jim_EvalFile(Jim_Interp *interp, const char *filename) 16905 { 16906 Jim_Obj *filenameObj; 16907 Jim_Obj *oldFilenameObj; 16908 Jim_Obj *scriptObjPtr; 16909 int retcode; 16910 16911 scriptObjPtr = JimReadTextFile(interp, filename); 16912 if (!scriptObjPtr) { 16913 return JIM_ERR; 16914 } 16915 16916 filenameObj = Jim_NewStringObj(interp, filename, -1); 16917 Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1); 16918 16919 oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj); 16920 16921 retcode = Jim_EvalObj(interp, scriptObjPtr); 16922 16923 JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj); 16924 16925 16926 if (retcode == JIM_RETURN) { 16927 if (--interp->returnLevel <= 0) { 16928 retcode = interp->returnCode; 16929 interp->returnCode = JIM_OK; 16930 interp->returnLevel = 0; 16931 } 16932 } 16933 16934 return retcode; 16935 } 16936 16937 static void JimParseSubst(struct JimParserCtx *pc, int flags) 16938 { 16939 pc->tstart = pc->p; 16940 pc->tline = pc->linenr; 16941 16942 if (pc->len == 0) { 16943 pc->tend = pc->p; 16944 pc->tt = JIM_TT_EOL; 16945 pc->eof = 1; 16946 return; 16947 } 16948 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) { 16949 JimParseCmd(pc); 16950 return; 16951 } 16952 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) { 16953 if (JimParseVar(pc) == JIM_OK) { 16954 return; 16955 } 16956 16957 pc->tstart = pc->p; 16958 16959 pc->p++; 16960 pc->len--; 16961 } 16962 while (pc->len) { 16963 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) { 16964 break; 16965 } 16966 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) { 16967 break; 16968 } 16969 if (*pc->p == '\\' && pc->len > 1) { 16970 pc->p++; 16971 pc->len--; 16972 } 16973 pc->p++; 16974 pc->len--; 16975 } 16976 pc->tend = pc->p - 1; 16977 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC; 16978 } 16979 16980 16981 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags) 16982 { 16983 int scriptTextLen; 16984 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); 16985 struct JimParserCtx parser; 16986 struct ScriptObj *script = Jim_Alloc(sizeof(*script)); 16987 ParseTokenList tokenlist; 16988 16989 16990 ScriptTokenListInit(&tokenlist); 16991 16992 JimParserInit(&parser, scriptText, scriptTextLen, 1); 16993 while (1) { 16994 JimParseSubst(&parser, flags); 16995 if (parser.eof) { 16996 16997 break; 16998 } 16999 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, 17000 parser.tline); 17001 } 17002 17003 17004 script->inUse = 1; 17005 script->substFlags = flags; 17006 script->fileNameObj = interp->emptyObj; 17007 Jim_IncrRefCount(script->fileNameObj); 17008 SubstObjAddTokens(interp, script, &tokenlist); 17009 17010 17011 ScriptTokenListFree(&tokenlist); 17012 17013 #ifdef DEBUG_SHOW_SUBST 17014 { 17015 int i; 17016 17017 printf("==== Subst ====\n"); 17018 for (i = 0; i < script->len; i++) { 17019 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type), 17020 Jim_String(script->token[i].objPtr)); 17021 } 17022 } 17023 #endif 17024 17025 17026 Jim_FreeIntRep(interp, objPtr); 17027 Jim_SetIntRepPtr(objPtr, script); 17028 objPtr->typePtr = &scriptObjType; 17029 return JIM_OK; 17030 } 17031 17032 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 17033 { 17034 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags) 17035 SetSubstFromAny(interp, objPtr, flags); 17036 return (ScriptObj *) Jim_GetIntRepPtr(objPtr); 17037 } 17038 17039 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags) 17040 { 17041 ScriptObj *script; 17042 17043 JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object")); 17044 17045 script = Jim_GetSubst(interp, substObjPtr, flags); 17046 17047 Jim_IncrRefCount(substObjPtr); 17048 script->inUse++; 17049 17050 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags); 17051 17052 script->inUse--; 17053 Jim_DecrRefCount(interp, substObjPtr); 17054 if (*resObjPtrPtr == NULL) { 17055 return JIM_ERR; 17056 } 17057 return JIM_OK; 17058 } 17059 17060 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg) 17061 { 17062 Jim_Obj *objPtr; 17063 Jim_Obj *listObjPtr; 17064 17065 JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0")); 17066 17067 listObjPtr = Jim_NewListObj(interp, argv, argc); 17068 17069 if (msg && *msg) { 17070 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1)); 17071 } 17072 Jim_IncrRefCount(listObjPtr); 17073 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1); 17074 Jim_DecrRefCount(interp, listObjPtr); 17075 17076 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr); 17077 } 17078 17079 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, 17080 Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type); 17081 17082 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL) 17083 17084 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr, 17085 JimHashtableIteratorCallbackType *callback, int type) 17086 { 17087 Jim_HashEntry *he; 17088 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); 17089 17090 17091 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) { 17092 he = Jim_FindHashEntry(ht, patternObjPtr); 17093 if (he) { 17094 callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he), 17095 patternObjPtr, type); 17096 } 17097 } 17098 else { 17099 Jim_HashTableIterator htiter; 17100 JimInitHashTableIterator(ht, &htiter); 17101 while ((he = Jim_NextHashEntry(&htiter)) != NULL) { 17102 callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he), 17103 patternObjPtr, type); 17104 } 17105 } 17106 return listObjPtr; 17107 } 17108 17109 17110 #define JIM_CMDLIST_COMMANDS 0 17111 #define JIM_CMDLIST_PROCS 1 17112 #define JIM_CMDLIST_CHANNELS 2 17113 17114 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, 17115 Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type) 17116 { 17117 Jim_Cmd *cmdPtr = (Jim_Cmd *)value; 17118 17119 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) { 17120 17121 return; 17122 } 17123 17124 Jim_IncrRefCount(keyObj); 17125 17126 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) { 17127 int match = 1; 17128 if (patternObj) { 17129 int plen, slen; 17130 const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen); 17131 const char *str = Jim_GetStringNoQualifier(keyObj, &slen); 17132 #ifdef JIM_NO_INTROSPECTION 17133 17134 match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0); 17135 #else 17136 match = JimGlobMatch(pattern, plen, str, slen, 0); 17137 #endif 17138 } 17139 if (match) { 17140 Jim_ListAppendElement(interp, listObjPtr, keyObj); 17141 } 17142 } 17143 Jim_DecrRefCount(interp, keyObj); 17144 } 17145 17146 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type) 17147 { 17148 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type); 17149 } 17150 17151 17152 #define JIM_VARLIST_GLOBALS 0 17153 #define JIM_VARLIST_LOCALS 1 17154 #define JIM_VARLIST_VARS 2 17155 #define JIM_VARLIST_MASK 0x000f 17156 17157 #define JIM_VARLIST_VALUES 0x1000 17158 17159 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, 17160 Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type) 17161 { 17162 Jim_VarVal *vv = (Jim_VarVal *)value; 17163 17164 if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) { 17165 if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) { 17166 Jim_ListAppendElement(interp, listObjPtr, keyObj); 17167 if (type & JIM_VARLIST_VALUES) { 17168 Jim_ListAppendElement(interp, listObjPtr, vv->objPtr); 17169 } 17170 } 17171 } 17172 } 17173 17174 17175 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode) 17176 { 17177 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) { 17178 return interp->emptyObj; 17179 } 17180 else { 17181 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr; 17182 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, 17183 mode); 17184 } 17185 } 17186 17187 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr) 17188 { 17189 long level; 17190 17191 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) { 17192 Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level); 17193 if (targetCallFrame && targetCallFrame != interp->topFramePtr) { 17194 #ifdef JIM_NO_INTROSPECTION 17195 17196 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1); 17197 #else 17198 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc); 17199 #endif 17200 return JIM_OK; 17201 } 17202 } 17203 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); 17204 return JIM_ERR; 17205 } 17206 17207 static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr) 17208 { 17209 long level; 17210 17211 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) { 17212 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level); 17213 if (frame) { 17214 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 17215 17216 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1)); 17217 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1)); 17218 if (frame->scriptObj) { 17219 ScriptObj *script = JimGetScript(interp, frame->scriptObj); 17220 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1)); 17221 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr)); 17222 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1)); 17223 Jim_ListAppendElement(interp, listObj, script->fileNameObj); 17224 } 17225 #ifndef JIM_NO_INTROSPECTION 17226 { 17227 Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc); 17228 17229 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1)); 17230 Jim_ListAppendElement(interp, listObj, cmdObj); 17231 } 17232 #endif 17233 { 17234 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame); 17235 if (procNameObj) { 17236 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1)); 17237 Jim_ListAppendElement(interp, listObj, procNameObj); 17238 } 17239 } 17240 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1)); 17241 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level)); 17242 17243 *objPtrPtr = listObj; 17244 return JIM_OK; 17245 } 17246 } 17247 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); 17248 return JIM_ERR; 17249 } 17250 17251 17252 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17253 { 17254 if (argc != 2 && argc != 3) { 17255 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string"); 17256 return JIM_ERR; 17257 } 17258 if (argc == 3) { 17259 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) { 17260 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1); 17261 return JIM_ERR; 17262 } 17263 else { 17264 fputs(Jim_String(argv[2]), stdout); 17265 } 17266 } 17267 else { 17268 puts(Jim_String(argv[1])); 17269 } 17270 return JIM_OK; 17271 } 17272 17273 17274 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) 17275 { 17276 jim_wide wideValue, res; 17277 double doubleValue, doubleRes; 17278 int i; 17279 17280 res = (op == JIM_EXPROP_ADD) ? 0 : 1; 17281 17282 for (i = 1; i < argc; i++) { 17283 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) 17284 goto trydouble; 17285 if (op == JIM_EXPROP_ADD) 17286 res += wideValue; 17287 else 17288 res *= wideValue; 17289 } 17290 Jim_SetResultInt(interp, res); 17291 return JIM_OK; 17292 trydouble: 17293 doubleRes = (double)res; 17294 for (; i < argc; i++) { 17295 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK) 17296 return JIM_ERR; 17297 if (op == JIM_EXPROP_ADD) 17298 doubleRes += doubleValue; 17299 else 17300 doubleRes *= doubleValue; 17301 } 17302 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17303 return JIM_OK; 17304 } 17305 17306 17307 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) 17308 { 17309 jim_wide wideValue, res = 0; 17310 double doubleValue, doubleRes = 0; 17311 int i = 2; 17312 17313 if (argc < 2) { 17314 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?"); 17315 return JIM_ERR; 17316 } 17317 else if (argc == 2) { 17318 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) { 17319 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) { 17320 return JIM_ERR; 17321 } 17322 else { 17323 if (op == JIM_EXPROP_SUB) 17324 doubleRes = -doubleValue; 17325 else 17326 doubleRes = 1.0 / doubleValue; 17327 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17328 return JIM_OK; 17329 } 17330 } 17331 if (op == JIM_EXPROP_SUB) { 17332 res = -wideValue; 17333 Jim_SetResultInt(interp, res); 17334 } 17335 else { 17336 doubleRes = 1.0 / wideValue; 17337 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17338 } 17339 return JIM_OK; 17340 } 17341 else { 17342 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) { 17343 if (Jim_GetDouble(interp, argv[1], &doubleRes) 17344 != JIM_OK) { 17345 return JIM_ERR; 17346 } 17347 else { 17348 goto trydouble; 17349 } 17350 } 17351 } 17352 for (i = 2; i < argc; i++) { 17353 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) { 17354 doubleRes = (double)res; 17355 goto trydouble; 17356 } 17357 if (op == JIM_EXPROP_SUB) 17358 res -= wideValue; 17359 else { 17360 if (wideValue == 0) { 17361 Jim_SetResultString(interp, "Division by zero", -1); 17362 return JIM_ERR; 17363 } 17364 res /= wideValue; 17365 } 17366 } 17367 Jim_SetResultInt(interp, res); 17368 return JIM_OK; 17369 trydouble: 17370 for (; i < argc; i++) { 17371 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK) 17372 return JIM_ERR; 17373 if (op == JIM_EXPROP_SUB) 17374 doubleRes -= doubleValue; 17375 else 17376 doubleRes /= doubleValue; 17377 } 17378 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17379 return JIM_OK; 17380 } 17381 17382 17383 17384 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17385 { 17386 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD); 17387 } 17388 17389 17390 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17391 { 17392 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL); 17393 } 17394 17395 17396 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17397 { 17398 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB); 17399 } 17400 17401 17402 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17403 { 17404 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV); 17405 } 17406 17407 17408 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17409 { 17410 if (argc != 2 && argc != 3) { 17411 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?"); 17412 return JIM_ERR; 17413 } 17414 if (argc == 2) { 17415 Jim_Obj *objPtr; 17416 17417 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); 17418 if (!objPtr) 17419 return JIM_ERR; 17420 Jim_SetResult(interp, objPtr); 17421 return JIM_OK; 17422 } 17423 17424 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) 17425 return JIM_ERR; 17426 Jim_SetResult(interp, argv[2]); 17427 return JIM_OK; 17428 } 17429 17430 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17431 { 17432 int i = 1; 17433 int complain = 1; 17434 17435 while (i < argc) { 17436 if (Jim_CompareStringImmediate(interp, argv[i], "--")) { 17437 i++; 17438 break; 17439 } 17440 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) { 17441 complain = 0; 17442 i++; 17443 continue; 17444 } 17445 break; 17446 } 17447 17448 while (i < argc) { 17449 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK 17450 && complain) { 17451 return JIM_ERR; 17452 } 17453 i++; 17454 } 17455 17456 Jim_SetEmptyResult(interp); 17457 return JIM_OK; 17458 } 17459 17460 static int JimCheckLoopRetcode(Jim_Interp *interp, int retval) 17461 { 17462 if (retval == JIM_BREAK || retval == JIM_CONTINUE) { 17463 if (--interp->break_level > 0) { 17464 return 1; 17465 } 17466 } 17467 return 0; 17468 } 17469 17470 17471 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17472 { 17473 if (argc != 3) { 17474 Jim_WrongNumArgs(interp, 1, argv, "condition body"); 17475 return JIM_ERR; 17476 } 17477 17478 17479 while (1) { 17480 int boolean = 0, retval; 17481 17482 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK) 17483 return retval; 17484 if (!boolean) 17485 break; 17486 17487 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) { 17488 if (JimCheckLoopRetcode(interp, retval)) { 17489 return retval; 17490 } 17491 switch (retval) { 17492 case JIM_BREAK: 17493 goto out; 17494 case JIM_CONTINUE: 17495 continue; 17496 default: 17497 return retval; 17498 } 17499 } 17500 } 17501 out: 17502 Jim_SetEmptyResult(interp); 17503 return JIM_OK; 17504 } 17505 17506 17507 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17508 { 17509 int retval; 17510 int boolean = 1; 17511 int immediate = 0; 17512 Jim_Obj *varNamePtr = NULL; 17513 Jim_Obj *stopVarNamePtr = NULL; 17514 17515 if (argc != 5) { 17516 Jim_WrongNumArgs(interp, 1, argv, "start test next body"); 17517 return JIM_ERR; 17518 } 17519 17520 17521 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) { 17522 return retval; 17523 } 17524 17525 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); 17526 17527 17528 #ifdef JIM_OPTIMIZATION 17529 if (retval == JIM_OK && boolean) { 17530 ScriptObj *incrScript; 17531 struct ExprTree *expr; 17532 jim_wide stop, currentVal; 17533 Jim_Obj *objPtr; 17534 int cmpOffset; 17535 17536 17537 expr = JimGetExpression(interp, argv[2]); 17538 incrScript = JimGetScript(interp, argv[3]); 17539 17540 17541 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) { 17542 goto evalstart; 17543 } 17544 17545 if (incrScript->token[1].type != JIM_TT_ESC) { 17546 goto evalstart; 17547 } 17548 17549 if (expr->expr->type == JIM_EXPROP_LT) { 17550 cmpOffset = 0; 17551 } 17552 else if (expr->expr->type == JIM_EXPROP_LTE) { 17553 cmpOffset = 1; 17554 } 17555 else { 17556 goto evalstart; 17557 } 17558 17559 if (expr->expr->left->type != JIM_TT_VAR) { 17560 goto evalstart; 17561 } 17562 17563 if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) { 17564 goto evalstart; 17565 } 17566 17567 17568 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) { 17569 goto evalstart; 17570 } 17571 17572 17573 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) { 17574 goto evalstart; 17575 } 17576 17577 17578 if (expr->expr->right->type == JIM_TT_EXPR_INT) { 17579 if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) { 17580 goto evalstart; 17581 } 17582 } 17583 else { 17584 stopVarNamePtr = expr->expr->right->objPtr; 17585 Jim_IncrRefCount(stopVarNamePtr); 17586 17587 stop = 0; 17588 } 17589 17590 17591 varNamePtr = expr->expr->left->objPtr; 17592 Jim_IncrRefCount(varNamePtr); 17593 17594 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE); 17595 if (objPtr == NULL || Jim_GetWide(interp, objPtr, ¤tVal) != JIM_OK) { 17596 goto testcond; 17597 } 17598 17599 17600 while (retval == JIM_OK) { 17601 17602 17603 17604 17605 if (stopVarNamePtr) { 17606 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE); 17607 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) { 17608 goto testcond; 17609 } 17610 } 17611 17612 if (currentVal >= stop + cmpOffset) { 17613 break; 17614 } 17615 17616 17617 retval = Jim_EvalObj(interp, argv[4]); 17618 if (JimCheckLoopRetcode(interp, retval)) { 17619 immediate++; 17620 goto out; 17621 } 17622 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17623 retval = JIM_OK; 17624 17625 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG); 17626 17627 17628 if (objPtr == NULL) { 17629 retval = JIM_ERR; 17630 goto out; 17631 } 17632 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { 17633 currentVal = ++JimWideValue(objPtr); 17634 Jim_InvalidateStringRep(objPtr); 17635 } 17636 else { 17637 if (Jim_GetWide(interp, objPtr, ¤tVal) != JIM_OK || 17638 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp, 17639 ++currentVal)) != JIM_OK) { 17640 goto evalnext; 17641 } 17642 } 17643 } 17644 } 17645 goto out; 17646 } 17647 evalstart: 17648 #endif 17649 17650 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) { 17651 17652 retval = Jim_EvalObj(interp, argv[4]); 17653 if (JimCheckLoopRetcode(interp, retval)) { 17654 immediate++; 17655 break; 17656 } 17657 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17658 17659 JIM_IF_OPTIM(evalnext:) 17660 retval = Jim_EvalObj(interp, argv[3]); 17661 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17662 17663 JIM_IF_OPTIM(testcond:) 17664 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); 17665 } 17666 } 17667 } 17668 JIM_IF_OPTIM(out:) 17669 if (stopVarNamePtr) { 17670 Jim_DecrRefCount(interp, stopVarNamePtr); 17671 } 17672 if (varNamePtr) { 17673 Jim_DecrRefCount(interp, varNamePtr); 17674 } 17675 17676 if (!immediate) { 17677 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) { 17678 Jim_SetEmptyResult(interp); 17679 return JIM_OK; 17680 } 17681 } 17682 17683 return retval; 17684 } 17685 17686 17687 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17688 { 17689 int retval; 17690 jim_wide i; 17691 jim_wide limit = 0; 17692 jim_wide incr = 1; 17693 Jim_Obj *bodyObjPtr; 17694 17695 if (argc < 4 || argc > 6) { 17696 Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body"); 17697 return JIM_ERR; 17698 } 17699 17700 retval = Jim_GetWideExpr(interp, argv[2], &i); 17701 if (argc > 4 && retval == JIM_OK) { 17702 retval = Jim_GetWideExpr(interp, argv[3], &limit); 17703 } 17704 if (argc > 5 && retval == JIM_OK) { 17705 Jim_GetWideExpr(interp, argv[4], &incr); 17706 } 17707 if (retval != JIM_OK) { 17708 return retval; 17709 } 17710 if (argc == 4) { 17711 limit = i; 17712 i = 0; 17713 } 17714 bodyObjPtr = argv[argc - 1]; 17715 17716 retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i)); 17717 17718 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) { 17719 retval = Jim_EvalObj(interp, bodyObjPtr); 17720 if (JimCheckLoopRetcode(interp, retval)) { 17721 return retval; 17722 } 17723 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17724 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); 17725 17726 retval = JIM_OK; 17727 17728 17729 i += incr; 17730 17731 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { 17732 if (argv[1]->typePtr != &variableObjType) { 17733 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { 17734 return JIM_ERR; 17735 } 17736 } 17737 JimWideValue(objPtr) = i; 17738 Jim_InvalidateStringRep(objPtr); 17739 17740 if (argv[1]->typePtr != &variableObjType) { 17741 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { 17742 retval = JIM_ERR; 17743 break; 17744 } 17745 } 17746 } 17747 else { 17748 objPtr = Jim_NewIntObj(interp, i); 17749 retval = Jim_SetVariable(interp, argv[1], objPtr); 17750 if (retval != JIM_OK) { 17751 Jim_FreeNewObj(interp, objPtr); 17752 } 17753 } 17754 } 17755 } 17756 17757 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) { 17758 Jim_SetEmptyResult(interp); 17759 return JIM_OK; 17760 } 17761 return retval; 17762 } 17763 17764 typedef struct { 17765 Jim_Obj *objPtr; 17766 int idx; 17767 } Jim_ListIter; 17768 17769 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr) 17770 { 17771 iter->objPtr = objPtr; 17772 iter->idx = 0; 17773 } 17774 17775 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter) 17776 { 17777 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) { 17778 return NULL; 17779 } 17780 return iter->objPtr->internalRep.listValue.ele[iter->idx++]; 17781 } 17782 17783 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter) 17784 { 17785 return iter->idx >= Jim_ListLength(interp, iter->objPtr); 17786 } 17787 17788 17789 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap) 17790 { 17791 int result = JIM_OK; 17792 int i, numargs; 17793 Jim_ListIter twoiters[2]; 17794 Jim_ListIter *iters; 17795 Jim_Obj *script; 17796 Jim_Obj *resultObj; 17797 17798 if (argc < 4 || argc % 2 != 0) { 17799 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script"); 17800 return JIM_ERR; 17801 } 17802 script = argv[argc - 1]; 17803 numargs = (argc - 1 - 1); 17804 17805 if (numargs == 2) { 17806 iters = twoiters; 17807 } 17808 else { 17809 iters = Jim_Alloc(numargs * sizeof(*iters)); 17810 } 17811 for (i = 0; i < numargs; i++) { 17812 JimListIterInit(&iters[i], argv[i + 1]); 17813 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) { 17814 result = JIM_ERR; 17815 } 17816 } 17817 if (result != JIM_OK) { 17818 Jim_SetResultString(interp, "foreach varlist is empty", -1); 17819 goto empty_varlist; 17820 } 17821 17822 if (doMap) { 17823 resultObj = Jim_NewListObj(interp, NULL, 0); 17824 } 17825 else { 17826 resultObj = interp->emptyObj; 17827 } 17828 Jim_IncrRefCount(resultObj); 17829 17830 while (1) { 17831 17832 for (i = 0; i < numargs; i += 2) { 17833 if (!JimListIterDone(interp, &iters[i + 1])) { 17834 break; 17835 } 17836 } 17837 if (i == numargs) { 17838 17839 break; 17840 } 17841 17842 17843 for (i = 0; i < numargs; i += 2) { 17844 Jim_Obj *varName; 17845 17846 17847 JimListIterInit(&iters[i], argv[i + 1]); 17848 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) { 17849 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]); 17850 if (!valObj) { 17851 17852 valObj = interp->emptyObj; 17853 } 17854 17855 Jim_IncrRefCount(valObj); 17856 result = Jim_SetVariable(interp, varName, valObj); 17857 Jim_DecrRefCount(interp, valObj); 17858 if (result != JIM_OK) { 17859 goto err; 17860 } 17861 } 17862 } 17863 result = Jim_EvalObj(interp, script); 17864 if (JimCheckLoopRetcode(interp, result)) { 17865 goto err; 17866 } 17867 switch (result) { 17868 case JIM_OK: 17869 if (doMap) { 17870 Jim_ListAppendElement(interp, resultObj, interp->result); 17871 } 17872 break; 17873 case JIM_CONTINUE: 17874 break; 17875 case JIM_BREAK: 17876 goto out; 17877 default: 17878 goto err; 17879 } 17880 } 17881 out: 17882 result = JIM_OK; 17883 Jim_SetResult(interp, resultObj); 17884 err: 17885 Jim_DecrRefCount(interp, resultObj); 17886 empty_varlist: 17887 if (numargs > 2) { 17888 Jim_Free(iters); 17889 } 17890 return result; 17891 } 17892 17893 17894 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17895 { 17896 return JimForeachMapHelper(interp, argc, argv, 0); 17897 } 17898 17899 17900 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17901 { 17902 return JimForeachMapHelper(interp, argc, argv, 1); 17903 } 17904 17905 17906 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17907 { 17908 int result = JIM_ERR; 17909 int i; 17910 Jim_ListIter iter; 17911 Jim_Obj *resultObj; 17912 17913 if (argc < 2) { 17914 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?"); 17915 return JIM_ERR; 17916 } 17917 17918 JimListIterInit(&iter, argv[1]); 17919 17920 for (i = 2; i < argc; i++) { 17921 Jim_Obj *valObj = JimListIterNext(interp, &iter); 17922 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj); 17923 if (result != JIM_OK) { 17924 return result; 17925 } 17926 } 17927 17928 resultObj = Jim_NewListObj(interp, NULL, 0); 17929 while (!JimListIterDone(interp, &iter)) { 17930 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter)); 17931 } 17932 17933 Jim_SetResult(interp, resultObj); 17934 17935 return JIM_OK; 17936 } 17937 17938 17939 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17940 { 17941 int boolean, retval, current = 1, falsebody = 0; 17942 17943 if (argc >= 3) { 17944 while (1) { 17945 17946 if (current >= argc) 17947 goto err; 17948 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean)) 17949 != JIM_OK) 17950 return retval; 17951 17952 if (current >= argc) 17953 goto err; 17954 if (Jim_CompareStringImmediate(interp, argv[current], "then")) 17955 current++; 17956 17957 if (current >= argc) 17958 goto err; 17959 if (boolean) 17960 return Jim_EvalObj(interp, argv[current]); 17961 17962 if (++current >= argc) { 17963 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); 17964 return JIM_OK; 17965 } 17966 falsebody = current++; 17967 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) { 17968 17969 if (current != argc - 1) 17970 goto err; 17971 return Jim_EvalObj(interp, argv[current]); 17972 } 17973 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif")) 17974 continue; 17975 17976 else if (falsebody != argc - 1) 17977 goto err; 17978 return Jim_EvalObj(interp, argv[falsebody]); 17979 } 17980 return JIM_OK; 17981 } 17982 err: 17983 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody"); 17984 return JIM_ERR; 17985 } 17986 17987 17988 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj, 17989 Jim_Obj *stringObj, int flags) 17990 { 17991 Jim_Obj *parms[5]; 17992 int argc = 0; 17993 long eq; 17994 int rc; 17995 17996 parms[argc++] = commandObj; 17997 if (flags & JIM_NOCASE) { 17998 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1); 17999 } 18000 if (flags & JIM_OPT_END) { 18001 parms[argc++] = Jim_NewStringObj(interp, "--", -1); 18002 } 18003 parms[argc++] = patternObj; 18004 parms[argc++] = stringObj; 18005 18006 rc = Jim_EvalObjVector(interp, argc, parms); 18007 18008 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) { 18009 eq = -rc; 18010 } 18011 18012 return eq; 18013 } 18014 18015 18016 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18017 { 18018 enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD }; 18019 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i; 18020 int match_flags = 0; 18021 Jim_Obj *command = NULL, *scriptObj = NULL, *strObj; 18022 Jim_Obj **caseList; 18023 18024 if (argc < 3) { 18025 wrongnumargs: 18026 Jim_WrongNumArgs(interp, 1, argv, "?options? string " 18027 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}"); 18028 return JIM_ERR; 18029 } 18030 for (opt = 1; opt < argc; ++opt) { 18031 const char *option = Jim_String(argv[opt]); 18032 18033 if (*option != '-') 18034 break; 18035 else if (strncmp(option, "--", 2) == 0) { 18036 ++opt; 18037 break; 18038 } 18039 else if (strncmp(option, "-exact", 2) == 0) 18040 matchOpt = SWITCH_EXACT; 18041 else if (strncmp(option, "-glob", 2) == 0) 18042 matchOpt = SWITCH_GLOB; 18043 else if (strncmp(option, "-regexp", 2) == 0) { 18044 matchOpt = SWITCH_RE; 18045 match_flags |= JIM_OPT_END; 18046 } 18047 else if (strncmp(option, "-command", 2) == 0) { 18048 matchOpt = SWITCH_CMD; 18049 if ((argc - opt) < 2) 18050 goto wrongnumargs; 18051 command = argv[++opt]; 18052 } 18053 else { 18054 Jim_SetResultFormatted(interp, 18055 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --", 18056 argv[opt]); 18057 return JIM_ERR; 18058 } 18059 if ((argc - opt) < 2) 18060 goto wrongnumargs; 18061 } 18062 strObj = argv[opt++]; 18063 patCount = argc - opt; 18064 if (patCount == 1) { 18065 JimListGetElements(interp, argv[opt], &patCount, &caseList); 18066 } 18067 else 18068 caseList = (Jim_Obj **)&argv[opt]; 18069 if (patCount == 0 || patCount % 2 != 0) 18070 goto wrongnumargs; 18071 for (i = 0; scriptObj == NULL && i < patCount; i += 2) { 18072 Jim_Obj *patObj = caseList[i]; 18073 18074 if (!Jim_CompareStringImmediate(interp, patObj, "default") 18075 || i < (patCount - 2)) { 18076 switch (matchOpt) { 18077 case SWITCH_EXACT: 18078 if (Jim_StringEqObj(strObj, patObj)) 18079 scriptObj = caseList[i + 1]; 18080 break; 18081 case SWITCH_GLOB: 18082 if (Jim_StringMatchObj(interp, patObj, strObj, 0)) 18083 scriptObj = caseList[i + 1]; 18084 break; 18085 case SWITCH_RE: 18086 command = Jim_NewStringObj(interp, "regexp", -1); 18087 18088 case SWITCH_CMD:{ 18089 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags); 18090 18091 if (argc - opt == 1) { 18092 JimListGetElements(interp, argv[opt], &patCount, &caseList); 18093 } 18094 18095 if (rc < 0) { 18096 return -rc; 18097 } 18098 if (rc) 18099 scriptObj = caseList[i + 1]; 18100 break; 18101 } 18102 } 18103 } 18104 else { 18105 scriptObj = caseList[i + 1]; 18106 } 18107 } 18108 for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2) 18109 scriptObj = caseList[i + 1]; 18110 if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) { 18111 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]); 18112 return JIM_ERR; 18113 } 18114 Jim_SetEmptyResult(interp); 18115 if (scriptObj) { 18116 return Jim_EvalObj(interp, scriptObj); 18117 } 18118 return JIM_OK; 18119 } 18120 18121 18122 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18123 { 18124 Jim_Obj *listObjPtr; 18125 18126 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1); 18127 Jim_SetResult(interp, listObjPtr); 18128 return JIM_OK; 18129 } 18130 18131 18132 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18133 { 18134 Jim_Obj *objPtr; 18135 int ret; 18136 18137 if (argc < 2) { 18138 Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?"); 18139 return JIM_ERR; 18140 } 18141 ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE); 18142 if (ret < 0) { 18143 ret = JIM_OK; 18144 Jim_SetEmptyResult(interp); 18145 } 18146 else if (ret == JIM_OK) { 18147 Jim_SetResult(interp, objPtr); 18148 } 18149 return ret; 18150 } 18151 18152 18153 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18154 { 18155 if (argc != 2) { 18156 Jim_WrongNumArgs(interp, 1, argv, "list"); 18157 return JIM_ERR; 18158 } 18159 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1])); 18160 return JIM_OK; 18161 } 18162 18163 18164 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18165 { 18166 static const char * const options[] = { 18167 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command", 18168 "-stride", "-index", NULL 18169 }; 18170 enum 18171 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE, 18172 OPT_COMMAND, OPT_STRIDE, OPT_INDEX }; 18173 int i; 18174 int opt_bool = 0; 18175 int opt_not = 0; 18176 int opt_all = 0; 18177 int opt_inline = 0; 18178 int opt_match = OPT_EXACT; 18179 int listlen; 18180 int rc = JIM_OK; 18181 Jim_Obj *listObjPtr = NULL; 18182 Jim_Obj *commandObj = NULL; 18183 Jim_Obj *indexObj = NULL; 18184 int match_flags = 0; 18185 long stride = 1; 18186 18187 if (argc < 3) { 18188 wrongargs: 18189 Jim_WrongNumArgs(interp, 1, argv, 18190 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value"); 18191 return JIM_ERR; 18192 } 18193 18194 for (i = 1; i < argc - 2; i++) { 18195 int option; 18196 18197 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { 18198 return JIM_ERR; 18199 } 18200 switch (option) { 18201 case OPT_BOOL: 18202 opt_bool = 1; 18203 opt_inline = 0; 18204 break; 18205 case OPT_NOT: 18206 opt_not = 1; 18207 break; 18208 case OPT_NOCASE: 18209 match_flags |= JIM_NOCASE; 18210 break; 18211 case OPT_INLINE: 18212 opt_inline = 1; 18213 opt_bool = 0; 18214 break; 18215 case OPT_ALL: 18216 opt_all = 1; 18217 break; 18218 case OPT_REGEXP: 18219 opt_match = option; 18220 match_flags |= JIM_OPT_END; 18221 break; 18222 case OPT_COMMAND: 18223 if (i >= argc - 2) { 18224 goto wrongargs; 18225 } 18226 commandObj = argv[++i]; 18227 18228 case OPT_EXACT: 18229 case OPT_GLOB: 18230 opt_match = option; 18231 break; 18232 case OPT_INDEX: 18233 if (i >= argc - 2) { 18234 goto wrongargs; 18235 } 18236 indexObj = argv[++i]; 18237 break; 18238 case OPT_STRIDE: 18239 if (i >= argc - 2) { 18240 goto wrongargs; 18241 } 18242 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) { 18243 return JIM_ERR; 18244 } 18245 if (stride < 1) { 18246 Jim_SetResultString(interp, "stride length must be at least 1", -1); 18247 return JIM_ERR; 18248 } 18249 break; 18250 } 18251 } 18252 18253 argc -= i; 18254 if (argc < 2) { 18255 goto wrongargs; 18256 } 18257 argv += i; 18258 18259 listlen = Jim_ListLength(interp, argv[0]); 18260 if (listlen % stride) { 18261 Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1); 18262 return JIM_ERR; 18263 } 18264 18265 if (opt_all) { 18266 listObjPtr = Jim_NewListObj(interp, NULL, 0); 18267 } 18268 if (opt_match == OPT_REGEXP) { 18269 commandObj = Jim_NewStringObj(interp, "regexp", -1); 18270 } 18271 if (commandObj) { 18272 Jim_IncrRefCount(commandObj); 18273 } 18274 18275 for (i = 0; i < listlen; i += stride) { 18276 int eq = 0; 18277 Jim_Obj *searchListObj; 18278 Jim_Obj *objPtr; 18279 int offset; 18280 18281 if (indexObj) { 18282 int indexlen = Jim_ListLength(interp, indexObj); 18283 if (stride == 1) { 18284 searchListObj = Jim_ListGetIndex(interp, argv[0], i); 18285 } 18286 else { 18287 searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride); 18288 } 18289 Jim_IncrRefCount(searchListObj); 18290 rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG); 18291 if (rc != JIM_OK) { 18292 Jim_DecrRefCount(interp, searchListObj); 18293 rc = JIM_ERR; 18294 goto done; 18295 } 18296 18297 offset = 0; 18298 } 18299 else { 18300 18301 searchListObj = argv[0]; 18302 offset = i; 18303 objPtr = Jim_ListGetIndex(interp, searchListObj, i); 18304 Jim_IncrRefCount(searchListObj); 18305 } 18306 18307 switch (opt_match) { 18308 case OPT_EXACT: 18309 eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0; 18310 break; 18311 18312 case OPT_GLOB: 18313 eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags); 18314 break; 18315 18316 case OPT_REGEXP: 18317 case OPT_COMMAND: 18318 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags); 18319 if (eq < 0) { 18320 Jim_DecrRefCount(interp, searchListObj); 18321 rc = JIM_ERR; 18322 goto done; 18323 } 18324 break; 18325 } 18326 18327 18328 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) { 18329 Jim_Obj *resultObj; 18330 18331 if (opt_bool) { 18332 resultObj = Jim_NewIntObj(interp, eq ^ opt_not); 18333 } 18334 else if (!opt_inline) { 18335 resultObj = Jim_NewIntObj(interp, i); 18336 } 18337 else if (stride == 1) { 18338 resultObj = objPtr; 18339 } 18340 else if (opt_all) { 18341 18342 ListInsertElements(listObjPtr, -1, stride, 18343 searchListObj->internalRep.listValue.ele + offset); 18344 18345 resultObj = NULL; 18346 } 18347 else { 18348 resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride); 18349 } 18350 18351 if (opt_all) { 18352 18353 if (stride == 1) { 18354 Jim_ListAppendElement(interp, listObjPtr, resultObj); 18355 } 18356 } 18357 else { 18358 Jim_SetResult(interp, resultObj); 18359 Jim_DecrRefCount(interp, searchListObj); 18360 goto done; 18361 } 18362 } 18363 Jim_DecrRefCount(interp, searchListObj); 18364 } 18365 18366 if (opt_all) { 18367 Jim_SetResult(interp, listObjPtr); 18368 listObjPtr = NULL; 18369 } 18370 else { 18371 18372 if (opt_bool) { 18373 Jim_SetResultBool(interp, opt_not); 18374 } 18375 else if (!opt_inline) { 18376 Jim_SetResultInt(interp, -1); 18377 } 18378 } 18379 18380 done: 18381 if (listObjPtr) { 18382 Jim_FreeNewObj(interp, listObjPtr); 18383 } 18384 if (commandObj) { 18385 Jim_DecrRefCount(interp, commandObj); 18386 } 18387 return rc; 18388 } 18389 18390 18391 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18392 { 18393 Jim_Obj *listObjPtr; 18394 int new_obj = 0; 18395 int i; 18396 18397 if (argc < 2) { 18398 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?"); 18399 return JIM_ERR; 18400 } 18401 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); 18402 if (!listObjPtr) { 18403 18404 listObjPtr = Jim_NewListObj(interp, NULL, 0); 18405 new_obj = 1; 18406 } 18407 else if (Jim_IsShared(listObjPtr)) { 18408 listObjPtr = Jim_DuplicateObj(interp, listObjPtr); 18409 new_obj = 1; 18410 } 18411 for (i = 2; i < argc; i++) 18412 Jim_ListAppendElement(interp, listObjPtr, argv[i]); 18413 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) { 18414 if (new_obj) 18415 Jim_FreeNewObj(interp, listObjPtr); 18416 return JIM_ERR; 18417 } 18418 Jim_SetResult(interp, listObjPtr); 18419 return JIM_OK; 18420 } 18421 18422 18423 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18424 { 18425 int idx, len; 18426 Jim_Obj *listPtr; 18427 18428 if (argc < 3) { 18429 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?"); 18430 return JIM_ERR; 18431 } 18432 listPtr = argv[1]; 18433 if (Jim_IsShared(listPtr)) 18434 listPtr = Jim_DuplicateObj(interp, listPtr); 18435 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK) 18436 goto err; 18437 len = Jim_ListLength(interp, listPtr); 18438 if (idx >= len) 18439 idx = len; 18440 else if (idx < 0) 18441 idx = len + idx + 1; 18442 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]); 18443 Jim_SetResult(interp, listPtr); 18444 return JIM_OK; 18445 err: 18446 if (listPtr != argv[1]) { 18447 Jim_FreeNewObj(interp, listPtr); 18448 } 18449 return JIM_ERR; 18450 } 18451 18452 18453 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18454 { 18455 int first, last, len, rangeLen; 18456 Jim_Obj *listObj; 18457 Jim_Obj *newListObj; 18458 18459 if (argc < 4) { 18460 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?"); 18461 return JIM_ERR; 18462 } 18463 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK || 18464 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) { 18465 return JIM_ERR; 18466 } 18467 18468 listObj = argv[1]; 18469 len = Jim_ListLength(interp, listObj); 18470 18471 first = JimRelToAbsIndex(len, first); 18472 last = JimRelToAbsIndex(len, last); 18473 JimRelToAbsRange(len, &first, &last, &rangeLen); 18474 18475 18476 if (first > len) { 18477 first = len; 18478 } 18479 18480 18481 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first); 18482 18483 18484 ListInsertElements(newListObj, -1, argc - 4, argv + 4); 18485 18486 18487 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen); 18488 18489 Jim_SetResult(interp, newListObj); 18490 return JIM_OK; 18491 } 18492 18493 18494 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18495 { 18496 if (argc < 3) { 18497 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value"); 18498 return JIM_ERR; 18499 } 18500 else if (argc == 3) { 18501 18502 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) 18503 return JIM_ERR; 18504 Jim_SetResult(interp, argv[2]); 18505 return JIM_OK; 18506 } 18507 return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]); 18508 } 18509 18510 18511 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[]) 18512 { 18513 static const char * const options[] = { 18514 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", 18515 "-stride", "-dictionary", NULL 18516 }; 18517 enum { 18518 OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE, 18519 OPT_STRIDE, OPT_DICT 18520 }; 18521 Jim_Obj *resObj; 18522 int i; 18523 int retCode; 18524 int shared; 18525 long stride = 1; 18526 Jim_Obj **elements; 18527 int listlen; 18528 18529 struct lsort_info info; 18530 18531 if (argc < 2) { 18532 wrongargs: 18533 Jim_WrongNumArgs(interp, 1, argv, "?options? list"); 18534 return JIM_ERR; 18535 } 18536 18537 info.type = JIM_LSORT_ASCII; 18538 info.order = 1; 18539 info.indexc = 0; 18540 info.unique = 0; 18541 info.command = NULL; 18542 info.interp = interp; 18543 18544 for (i = 1; i < (argc - 1); i++) { 18545 int option; 18546 18547 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) 18548 != JIM_OK) 18549 return JIM_ERR; 18550 switch (option) { 18551 case OPT_ASCII: 18552 info.type = JIM_LSORT_ASCII; 18553 break; 18554 case OPT_DICT: 18555 info.type = JIM_LSORT_DICT; 18556 break; 18557 case OPT_NOCASE: 18558 info.type = JIM_LSORT_NOCASE; 18559 break; 18560 case OPT_INTEGER: 18561 info.type = JIM_LSORT_INTEGER; 18562 break; 18563 case OPT_REAL: 18564 info.type = JIM_LSORT_REAL; 18565 break; 18566 case OPT_INCREASING: 18567 info.order = 1; 18568 break; 18569 case OPT_DECREASING: 18570 info.order = -1; 18571 break; 18572 case OPT_UNIQUE: 18573 info.unique = 1; 18574 break; 18575 case OPT_COMMAND: 18576 if (i >= (argc - 2)) { 18577 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1); 18578 return JIM_ERR; 18579 } 18580 info.type = JIM_LSORT_COMMAND; 18581 info.command = argv[i + 1]; 18582 i++; 18583 break; 18584 case OPT_STRIDE: 18585 if (i >= argc - 2) { 18586 goto wrongargs; 18587 } 18588 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) { 18589 return JIM_ERR; 18590 } 18591 if (stride < 2) { 18592 Jim_SetResultString(interp, "stride length must be at least 2", -1); 18593 return JIM_ERR; 18594 } 18595 break; 18596 case OPT_INDEX: 18597 if (i >= (argc - 2)) { 18598 badindex: 18599 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1); 18600 return JIM_ERR; 18601 } 18602 JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv); 18603 if (info.indexc == 0) { 18604 goto badindex; 18605 } 18606 i++; 18607 break; 18608 } 18609 } 18610 resObj = argv[argc - 1]; 18611 JimListGetElements(interp, resObj, &listlen, &elements); 18612 if (listlen <= 1) { 18613 18614 Jim_SetResult(interp, resObj); 18615 return JIM_OK; 18616 } 18617 18618 if (stride > 1) { 18619 Jim_Obj *tmpListObj; 18620 int i; 18621 18622 if (listlen % stride) { 18623 Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1); 18624 return JIM_ERR; 18625 } 18626 18627 tmpListObj = Jim_NewListObj(interp, NULL, 0); 18628 Jim_IncrRefCount(tmpListObj); 18629 for (i = 0; i < listlen; i += stride) { 18630 Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride)); 18631 } 18632 retCode = ListSortElements(interp, tmpListObj, &info); 18633 if (retCode == JIM_OK) { 18634 resObj = Jim_NewListObj(interp, NULL, 0); 18635 18636 for (i = 0; i < listlen; i += stride) { 18637 Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride)); 18638 } 18639 Jim_SetResult(interp, resObj); 18640 } 18641 Jim_DecrRefCount(interp, tmpListObj); 18642 } 18643 else { 18644 if ((shared = Jim_IsShared(resObj))) { 18645 resObj = Jim_DuplicateObj(interp, resObj); 18646 } 18647 retCode = ListSortElements(interp, resObj, &info); 18648 if (retCode == JIM_OK) { 18649 Jim_SetResult(interp, resObj); 18650 } 18651 else if (shared) { 18652 Jim_FreeNewObj(interp, resObj); 18653 } 18654 } 18655 return retCode; 18656 } 18657 18658 18659 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18660 { 18661 Jim_Obj *stringObjPtr; 18662 int i; 18663 18664 if (argc < 2) { 18665 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?"); 18666 return JIM_ERR; 18667 } 18668 if (argc == 2) { 18669 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); 18670 if (!stringObjPtr) 18671 return JIM_ERR; 18672 } 18673 else { 18674 int new_obj = 0; 18675 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); 18676 if (!stringObjPtr) { 18677 18678 stringObjPtr = Jim_NewEmptyStringObj(interp); 18679 new_obj = 1; 18680 } 18681 else if (Jim_IsShared(stringObjPtr)) { 18682 new_obj = 1; 18683 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr); 18684 } 18685 for (i = 2; i < argc; i++) { 18686 Jim_AppendObj(interp, stringObjPtr, argv[i]); 18687 } 18688 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) { 18689 if (new_obj) { 18690 Jim_FreeNewObj(interp, stringObjPtr); 18691 } 18692 return JIM_ERR; 18693 } 18694 } 18695 Jim_SetResult(interp, stringObjPtr); 18696 return JIM_OK; 18697 } 18698 18699 18700 18701 18702 18703 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18704 { 18705 int rc; 18706 18707 if (argc < 2) { 18708 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?"); 18709 return JIM_ERR; 18710 } 18711 18712 if (argc == 2) { 18713 rc = Jim_EvalObj(interp, argv[1]); 18714 } 18715 else { 18716 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); 18717 } 18718 18719 return rc; 18720 } 18721 18722 18723 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18724 { 18725 if (argc >= 2) { 18726 int retcode; 18727 Jim_CallFrame *savedCallFrame, *targetCallFrame; 18728 const char *str; 18729 18730 18731 savedCallFrame = interp->framePtr; 18732 18733 18734 str = Jim_String(argv[1]); 18735 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') { 18736 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]); 18737 argc--; 18738 argv++; 18739 } 18740 else { 18741 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL); 18742 } 18743 if (targetCallFrame == NULL) { 18744 return JIM_ERR; 18745 } 18746 if (argc < 2) { 18747 Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?"); 18748 return JIM_ERR; 18749 } 18750 18751 interp->framePtr = targetCallFrame; 18752 if (argc == 2) { 18753 retcode = Jim_EvalObj(interp, argv[1]); 18754 } 18755 else { 18756 retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); 18757 } 18758 interp->framePtr = savedCallFrame; 18759 return retcode; 18760 } 18761 else { 18762 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?"); 18763 return JIM_ERR; 18764 } 18765 } 18766 18767 18768 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18769 { 18770 int retcode; 18771 18772 if (argc == 2) { 18773 retcode = Jim_EvalExpression(interp, argv[1]); 18774 } 18775 #ifndef JIM_COMPAT 18776 else { 18777 Jim_WrongNumArgs(interp, 1, argv, "expression"); 18778 retcode = JIM_ERR; 18779 } 18780 #else 18781 else if (argc > 2) { 18782 Jim_Obj *objPtr; 18783 18784 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1); 18785 Jim_IncrRefCount(objPtr); 18786 retcode = Jim_EvalExpression(interp, objPtr); 18787 Jim_DecrRefCount(interp, objPtr); 18788 } 18789 else { 18790 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?"); 18791 return JIM_ERR; 18792 } 18793 #endif 18794 return retcode; 18795 } 18796 18797 static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode) 18798 { 18799 if (argc != 1 && argc != 2) { 18800 Jim_WrongNumArgs(interp, 1, argv, "?level?"); 18801 return JIM_ERR; 18802 } 18803 if (argc == 2) { 18804 long level; 18805 int ret = Jim_GetLong(interp, argv[1], &level); 18806 if (ret != JIM_OK) { 18807 return ret; 18808 } 18809 interp->break_level = level; 18810 } 18811 return retcode; 18812 } 18813 18814 18815 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18816 { 18817 return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK); 18818 } 18819 18820 18821 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18822 { 18823 return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE); 18824 } 18825 18826 18827 static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18828 { 18829 Jim_Obj *listObj; 18830 int i; 18831 jim_wide skip = 0; 18832 jim_wide last = 0; 18833 18834 if (argc > 1) { 18835 if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) { 18836 return JIM_ERR; 18837 } 18838 } 18839 if (argc > 2) { 18840 if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) { 18841 return JIM_ERR; 18842 } 18843 } 18844 18845 listObj = Jim_NewListObj(interp, NULL, 0); 18846 for (i = skip; i <= interp->procLevel; i++) { 18847 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i); 18848 if (frame->procLevel < last) { 18849 break; 18850 } 18851 JimAddStackFrame(interp, frame, listObj); 18852 } 18853 Jim_SetResult(interp, listObj); 18854 return JIM_OK; 18855 } 18856 18857 18858 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18859 { 18860 int i; 18861 Jim_Obj *stackTraceObj = NULL; 18862 Jim_Obj *errorCodeObj = NULL; 18863 int returnCode = JIM_OK; 18864 long level = 1; 18865 18866 for (i = 1; i < argc - 1; i += 2) { 18867 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) { 18868 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) { 18869 return JIM_ERR; 18870 } 18871 } 18872 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) { 18873 stackTraceObj = argv[i + 1]; 18874 } 18875 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) { 18876 errorCodeObj = argv[i + 1]; 18877 } 18878 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) { 18879 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) { 18880 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]); 18881 return JIM_ERR; 18882 } 18883 } 18884 else { 18885 break; 18886 } 18887 } 18888 18889 if (i != argc - 1 && i != argc) { 18890 Jim_WrongNumArgs(interp, 1, argv, 18891 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?"); 18892 } 18893 18894 18895 if (stackTraceObj && returnCode == JIM_ERR) { 18896 JimSetStackTrace(interp, stackTraceObj); 18897 } 18898 18899 if (errorCodeObj && returnCode == JIM_ERR) { 18900 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj); 18901 } 18902 interp->returnCode = returnCode; 18903 interp->returnLevel = level; 18904 18905 if (i == argc - 1) { 18906 Jim_SetResult(interp, argv[i]); 18907 } 18908 return level == 0 ? returnCode : JIM_RETURN; 18909 } 18910 18911 18912 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18913 { 18914 if (interp->framePtr->level == 0) { 18915 Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1); 18916 return JIM_ERR; 18917 } 18918 else if (argc >= 2) { 18919 18920 Jim_CallFrame *cf = interp->framePtr->parent; 18921 18922 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); 18923 if (cmdPtr == NULL) { 18924 return JIM_ERR; 18925 } 18926 18927 JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd")); 18928 18929 18930 JimIncrCmdRefCount(cmdPtr); 18931 cf->tailcallCmd = cmdPtr; 18932 18933 18934 JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj")); 18935 18936 cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1); 18937 Jim_IncrRefCount(cf->tailcallObj); 18938 18939 18940 return JIM_EVAL; 18941 } 18942 return JIM_OK; 18943 } 18944 18945 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18946 { 18947 Jim_Obj *cmdList; 18948 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp); 18949 18950 18951 cmdList = Jim_DuplicateObj(interp, prefixListObj); 18952 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1); 18953 18954 return JimEvalObjList(interp, cmdList); 18955 } 18956 18957 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData) 18958 { 18959 Jim_Obj *prefixListObj = privData; 18960 Jim_DecrRefCount(interp, prefixListObj); 18961 } 18962 18963 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18964 { 18965 Jim_Obj *prefixListObj; 18966 18967 if (argc < 3) { 18968 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?"); 18969 return JIM_ERR; 18970 } 18971 18972 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2); 18973 Jim_IncrRefCount(prefixListObj); 18974 Jim_SetResult(interp, argv[1]); 18975 18976 return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete); 18977 } 18978 18979 18980 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18981 { 18982 Jim_Cmd *cmd; 18983 18984 if (argc != 4 && argc != 5) { 18985 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body"); 18986 return JIM_ERR; 18987 } 18988 18989 if (argc == 4) { 18990 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL); 18991 } 18992 else { 18993 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL); 18994 } 18995 18996 if (cmd) { 18997 18998 Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]); 18999 JimCreateCommand(interp, nameObjPtr, cmd); 19000 19001 19002 JimUpdateProcNamespace(interp, cmd, nameObjPtr); 19003 Jim_DecrRefCount(interp, nameObjPtr); 19004 19005 19006 Jim_SetResult(interp, argv[1]); 19007 return JIM_OK; 19008 } 19009 return JIM_ERR; 19010 } 19011 19012 19013 static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19014 { 19015 if (argc != 2) { 19016 Jim_WrongNumArgs(interp, 1, argv, "callback"); 19017 return JIM_ERR; 19018 } 19019 19020 if (interp->traceCmdObj) { 19021 Jim_DecrRefCount(interp, interp->traceCmdObj); 19022 interp->traceCmdObj = NULL; 19023 } 19024 19025 if (Jim_Length(argv[1])) { 19026 19027 interp->traceCmdObj = argv[1]; 19028 Jim_IncrRefCount(interp->traceCmdObj); 19029 } 19030 return JIM_OK; 19031 } 19032 19033 19034 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19035 { 19036 int retcode; 19037 19038 if (argc < 2) { 19039 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?"); 19040 return JIM_ERR; 19041 } 19042 19043 19044 interp->local++; 19045 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); 19046 interp->local--; 19047 19048 19049 19050 if (retcode == 0) { 19051 Jim_Obj *cmdNameObj = Jim_GetResult(interp); 19052 19053 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) { 19054 return JIM_ERR; 19055 } 19056 if (interp->framePtr->localCommands == NULL) { 19057 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands)); 19058 Jim_InitStack(interp->framePtr->localCommands); 19059 } 19060 Jim_IncrRefCount(cmdNameObj); 19061 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj); 19062 } 19063 19064 return retcode; 19065 } 19066 19067 19068 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19069 { 19070 if (argc < 2) { 19071 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?"); 19072 return JIM_ERR; 19073 } 19074 else { 19075 int retcode; 19076 19077 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); 19078 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) { 19079 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]); 19080 return JIM_ERR; 19081 } 19082 19083 cmdPtr->u.proc.upcall++; 19084 JimIncrCmdRefCount(cmdPtr); 19085 19086 19087 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); 19088 19089 19090 cmdPtr->u.proc.upcall--; 19091 JimDecrCmdRefCount(interp, cmdPtr); 19092 19093 return retcode; 19094 } 19095 } 19096 19097 19098 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19099 { 19100 if (argc < 2) { 19101 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?"); 19102 return JIM_ERR; 19103 } 19104 else { 19105 int ret; 19106 Jim_Cmd *cmd; 19107 Jim_Obj *argListObjPtr; 19108 Jim_Obj *bodyObjPtr; 19109 Jim_Obj *nsObj = NULL; 19110 Jim_Obj **nargv; 19111 19112 int len = Jim_ListLength(interp, argv[1]); 19113 if (len != 2 && len != 3) { 19114 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]); 19115 return JIM_ERR; 19116 } 19117 19118 if (len == 3) { 19119 #ifdef jim_ext_namespace 19120 19121 nsObj = Jim_ListGetIndex(interp, argv[1], 2); 19122 #else 19123 Jim_SetResultString(interp, "namespaces not enabled", -1); 19124 return JIM_ERR; 19125 #endif 19126 } 19127 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0); 19128 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1); 19129 19130 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj); 19131 19132 if (cmd) { 19133 19134 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv)); 19135 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1); 19136 Jim_IncrRefCount(nargv[0]); 19137 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv)); 19138 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv); 19139 Jim_DecrRefCount(interp, nargv[0]); 19140 Jim_Free(nargv); 19141 19142 JimDecrCmdRefCount(interp, cmd); 19143 return ret; 19144 } 19145 return JIM_ERR; 19146 } 19147 } 19148 19149 19150 19151 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19152 { 19153 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); 19154 return JIM_OK; 19155 } 19156 19157 19158 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19159 { 19160 int i; 19161 Jim_CallFrame *targetCallFrame; 19162 19163 19164 if (argc > 3 && (argc % 2 == 0)) { 19165 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]); 19166 argc--; 19167 argv++; 19168 } 19169 else { 19170 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL); 19171 } 19172 if (targetCallFrame == NULL) { 19173 return JIM_ERR; 19174 } 19175 19176 19177 if (argc < 3) { 19178 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?"); 19179 return JIM_ERR; 19180 } 19181 19182 19183 for (i = 1; i < argc; i += 2) { 19184 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK) 19185 return JIM_ERR; 19186 } 19187 return JIM_OK; 19188 } 19189 19190 19191 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19192 { 19193 int i; 19194 19195 if (argc < 2) { 19196 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?"); 19197 return JIM_ERR; 19198 } 19199 19200 if (interp->framePtr->level == 0) 19201 return JIM_OK; 19202 for (i = 1; i < argc; i++) { 19203 19204 const char *name = Jim_String(argv[i]); 19205 if (name[0] != ':' || name[1] != ':') { 19206 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK) 19207 return JIM_ERR; 19208 } 19209 } 19210 return JIM_OK; 19211 } 19212 19213 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr, 19214 Jim_Obj *objPtr, int nocase) 19215 { 19216 int numMaps; 19217 const char *str, *noMatchStart = NULL; 19218 int strLen, i; 19219 Jim_Obj *resultObjPtr; 19220 19221 numMaps = Jim_ListLength(interp, mapListObjPtr); 19222 if (numMaps % 2) { 19223 Jim_SetResultString(interp, "list must contain an even number of elements", -1); 19224 return NULL; 19225 } 19226 19227 str = Jim_String(objPtr); 19228 strLen = Jim_Utf8Length(interp, objPtr); 19229 19230 19231 resultObjPtr = Jim_NewStringObj(interp, "", 0); 19232 while (strLen) { 19233 for (i = 0; i < numMaps; i += 2) { 19234 Jim_Obj *eachObjPtr; 19235 const char *k; 19236 int kl; 19237 19238 eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i); 19239 k = Jim_String(eachObjPtr); 19240 kl = Jim_Utf8Length(interp, eachObjPtr); 19241 19242 if (strLen >= kl && kl) { 19243 int rc; 19244 rc = JimStringCompareUtf8(str, kl, k, kl, nocase); 19245 if (rc == 0) { 19246 if (noMatchStart) { 19247 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); 19248 noMatchStart = NULL; 19249 } 19250 Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1)); 19251 str += utf8_index(str, kl); 19252 strLen -= kl; 19253 break; 19254 } 19255 } 19256 } 19257 if (i == numMaps) { 19258 int c; 19259 if (noMatchStart == NULL) 19260 noMatchStart = str; 19261 str += utf8_tounicode(str, &c); 19262 strLen--; 19263 } 19264 } 19265 if (noMatchStart) { 19266 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); 19267 } 19268 return resultObjPtr; 19269 } 19270 19271 19272 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19273 { 19274 int len; 19275 int opt_case = 1; 19276 int option; 19277 static const char * const nocase_options[] = { 19278 "-nocase", NULL 19279 }; 19280 static const char * const nocase_length_options[] = { 19281 "-nocase", "-length", NULL 19282 }; 19283 19284 enum { 19285 OPT_BYTELENGTH, 19286 OPT_BYTERANGE, 19287 OPT_CAT, 19288 OPT_COMPARE, 19289 OPT_EQUAL, 19290 OPT_FIRST, 19291 OPT_INDEX, 19292 OPT_IS, 19293 OPT_LAST, 19294 OPT_LENGTH, 19295 OPT_MAP, 19296 OPT_MATCH, 19297 OPT_RANGE, 19298 OPT_REPEAT, 19299 OPT_REPLACE, 19300 OPT_REVERSE, 19301 OPT_TOLOWER, 19302 OPT_TOTITLE, 19303 OPT_TOUPPER, 19304 OPT_TRIM, 19305 OPT_TRIMLEFT, 19306 OPT_TRIMRIGHT, 19307 OPT_COUNT 19308 }; 19309 static const jim_subcmd_type cmds[OPT_COUNT + 1] = { 19310 JIM_DEF_SUBCMD("bytelength", "string", 1, 1), 19311 JIM_DEF_SUBCMD("byterange", "string first last", 3, 3), 19312 JIM_DEF_SUBCMD("cat", "?...?", 0, -1), 19313 JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5), 19314 JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5), 19315 JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3), 19316 JIM_DEF_SUBCMD("index", "string index", 2, 2), 19317 JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3), 19318 JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3), 19319 JIM_DEF_SUBCMD("length","string", 1, 1), 19320 JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3), 19321 JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3), 19322 JIM_DEF_SUBCMD("range", "string first last", 3, 3), 19323 JIM_DEF_SUBCMD("repeat", "string count", 2, 2), 19324 JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4), 19325 JIM_DEF_SUBCMD("reverse", "string", 1, 1), 19326 JIM_DEF_SUBCMD("tolower", "string", 1, 1), 19327 JIM_DEF_SUBCMD("totitle", "string", 1, 1), 19328 JIM_DEF_SUBCMD("toupper", "string", 1, 1), 19329 JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2), 19330 JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2), 19331 JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2), 19332 { NULL } 19333 }; 19334 const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv); 19335 if (!ct) { 19336 return JIM_ERR; 19337 } 19338 if (ct->function) { 19339 19340 return ct->function(interp, argc, argv); 19341 } 19342 19343 option = ct - cmds; 19344 19345 switch (option) { 19346 case OPT_LENGTH: 19347 Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2])); 19348 return JIM_OK; 19349 19350 case OPT_BYTELENGTH: 19351 Jim_SetResultInt(interp, Jim_Length(argv[2])); 19352 return JIM_OK; 19353 19354 case OPT_CAT:{ 19355 Jim_Obj *objPtr; 19356 if (argc == 3) { 19357 19358 objPtr = argv[2]; 19359 } 19360 else { 19361 int i; 19362 19363 objPtr = Jim_NewStringObj(interp, "", 0); 19364 19365 for (i = 2; i < argc; i++) { 19366 Jim_AppendObj(interp, objPtr, argv[i]); 19367 } 19368 } 19369 Jim_SetResult(interp, objPtr); 19370 return JIM_OK; 19371 } 19372 19373 case OPT_COMPARE: 19374 case OPT_EQUAL: 19375 { 19376 19377 long opt_length = -1; 19378 int n = argc - 4; 19379 int i = 2; 19380 while (n > 0) { 19381 int subopt; 19382 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL, 19383 JIM_ENUM_ABBREV) != JIM_OK) { 19384 badcompareargs: 19385 Jim_SubCmdArgError(interp, ct, argv[0]); 19386 return JIM_ERR; 19387 } 19388 if (subopt == 0) { 19389 19390 opt_case = 0; 19391 n--; 19392 } 19393 else { 19394 19395 if (n < 2) { 19396 goto badcompareargs; 19397 } 19398 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) { 19399 return JIM_ERR; 19400 } 19401 n -= 2; 19402 } 19403 } 19404 if (n) { 19405 goto badcompareargs; 19406 } 19407 argv += argc - 2; 19408 if (opt_length < 0 && option != OPT_COMPARE && opt_case) { 19409 19410 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1])); 19411 } 19412 else { 19413 const char *s1 = Jim_String(argv[0]); 19414 int l1 = Jim_Utf8Length(interp, argv[0]); 19415 const char *s2 = Jim_String(argv[1]); 19416 int l2 = Jim_Utf8Length(interp, argv[1]); 19417 if (opt_length >= 0) { 19418 if (l1 > opt_length) { 19419 l1 = opt_length; 19420 } 19421 if (l2 > opt_length) { 19422 l2 = opt_length; 19423 } 19424 } 19425 n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case); 19426 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0); 19427 } 19428 return JIM_OK; 19429 } 19430 19431 case OPT_MATCH: 19432 if (argc != 4 && 19433 (argc != 5 || 19434 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL, 19435 JIM_ENUM_ABBREV) != JIM_OK)) { 19436 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string"); 19437 return JIM_ERR; 19438 } 19439 if (opt_case == 0) { 19440 argv++; 19441 } 19442 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case)); 19443 return JIM_OK; 19444 19445 case OPT_MAP:{ 19446 Jim_Obj *objPtr; 19447 19448 if (argc != 4 && 19449 (argc != 5 || 19450 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL, 19451 JIM_ENUM_ABBREV) != JIM_OK)) { 19452 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string"); 19453 return JIM_ERR; 19454 } 19455 19456 if (opt_case == 0) { 19457 argv++; 19458 } 19459 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case); 19460 if (objPtr == NULL) { 19461 return JIM_ERR; 19462 } 19463 Jim_SetResult(interp, objPtr); 19464 return JIM_OK; 19465 } 19466 19467 case OPT_RANGE:{ 19468 Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]); 19469 if (objPtr == NULL) { 19470 return JIM_ERR; 19471 } 19472 Jim_SetResult(interp, objPtr); 19473 return JIM_OK; 19474 } 19475 19476 case OPT_BYTERANGE:{ 19477 Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]); 19478 if (objPtr == NULL) { 19479 return JIM_ERR; 19480 } 19481 Jim_SetResult(interp, objPtr); 19482 return JIM_OK; 19483 } 19484 19485 case OPT_REPLACE:{ 19486 Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL); 19487 if (objPtr == NULL) { 19488 return JIM_ERR; 19489 } 19490 Jim_SetResult(interp, objPtr); 19491 return JIM_OK; 19492 } 19493 19494 19495 case OPT_REPEAT:{ 19496 Jim_Obj *objPtr; 19497 jim_wide count; 19498 19499 if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) { 19500 return JIM_ERR; 19501 } 19502 objPtr = Jim_NewStringObj(interp, "", 0); 19503 if (count > 0) { 19504 while (count--) { 19505 Jim_AppendObj(interp, objPtr, argv[2]); 19506 } 19507 } 19508 Jim_SetResult(interp, objPtr); 19509 return JIM_OK; 19510 } 19511 19512 case OPT_REVERSE:{ 19513 char *buf, *p; 19514 const char *str; 19515 int i; 19516 19517 str = Jim_GetString(argv[2], &len); 19518 buf = Jim_Alloc(len + 1); 19519 assert(buf); 19520 p = buf + len; 19521 *p = 0; 19522 for (i = 0; i < len; ) { 19523 int c; 19524 int l = utf8_tounicode(str, &c); 19525 memcpy(p - l, str, l); 19526 p -= l; 19527 i += l; 19528 str += l; 19529 } 19530 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); 19531 return JIM_OK; 19532 } 19533 19534 case OPT_INDEX:{ 19535 int idx; 19536 const char *str; 19537 19538 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) { 19539 return JIM_ERR; 19540 } 19541 str = Jim_String(argv[2]); 19542 len = Jim_Utf8Length(interp, argv[2]); 19543 idx = JimRelToAbsIndex(len, idx); 19544 if (idx < 0 || idx >= len || str == NULL) { 19545 Jim_SetResultString(interp, "", 0); 19546 } 19547 else if (len == Jim_Length(argv[2])) { 19548 19549 Jim_SetResultString(interp, str + idx, 1); 19550 } 19551 else { 19552 int c; 19553 int i = utf8_index(str, idx); 19554 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c)); 19555 } 19556 return JIM_OK; 19557 } 19558 19559 case OPT_FIRST: 19560 case OPT_LAST:{ 19561 int idx = 0, l1, l2; 19562 const char *s1, *s2; 19563 19564 s1 = Jim_String(argv[2]); 19565 s2 = Jim_String(argv[3]); 19566 l1 = Jim_Utf8Length(interp, argv[2]); 19567 l2 = Jim_Utf8Length(interp, argv[3]); 19568 if (argc == 5) { 19569 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) { 19570 return JIM_ERR; 19571 } 19572 idx = JimRelToAbsIndex(l2, idx); 19573 if (idx < 0) { 19574 idx = 0; 19575 } 19576 } 19577 else if (option == OPT_LAST) { 19578 idx = l2; 19579 } 19580 if (option == OPT_FIRST) { 19581 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx)); 19582 } 19583 else { 19584 #ifdef JIM_UTF8 19585 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx)); 19586 #else 19587 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx)); 19588 #endif 19589 } 19590 return JIM_OK; 19591 } 19592 19593 case OPT_TRIM: 19594 Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL)); 19595 return JIM_OK; 19596 case OPT_TRIMLEFT: 19597 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL)); 19598 return JIM_OK; 19599 case OPT_TRIMRIGHT:{ 19600 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL)); 19601 return JIM_OK; 19602 } 19603 19604 case OPT_TOLOWER: 19605 Jim_SetResult(interp, JimStringToLower(interp, argv[2])); 19606 return JIM_OK; 19607 case OPT_TOUPPER: 19608 Jim_SetResult(interp, JimStringToUpper(interp, argv[2])); 19609 return JIM_OK; 19610 case OPT_TOTITLE: 19611 Jim_SetResult(interp, JimStringToTitle(interp, argv[2])); 19612 return JIM_OK; 19613 19614 case OPT_IS: 19615 if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) { 19616 Jim_SubCmdArgError(interp, ct, argv[0]); 19617 return JIM_ERR; 19618 } 19619 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5); 19620 } 19621 return JIM_OK; 19622 } 19623 19624 19625 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19626 { 19627 long i, count = 1; 19628 jim_wide start, elapsed; 19629 19630 if (argc < 2) { 19631 Jim_WrongNumArgs(interp, 1, argv, "script ?count?"); 19632 return JIM_ERR; 19633 } 19634 if (argc == 3) { 19635 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK) 19636 return JIM_ERR; 19637 } 19638 if (count < 0) 19639 return JIM_OK; 19640 i = count; 19641 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 19642 while (i-- > 0) { 19643 int retval; 19644 19645 retval = Jim_EvalObj(interp, argv[1]); 19646 if (retval != JIM_OK) { 19647 return retval; 19648 } 19649 } 19650 elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start; 19651 if (elapsed < count * 10) { 19652 Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count)); 19653 } 19654 else { 19655 Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count); 19656 } 19657 Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1); 19658 return JIM_OK; 19659 } 19660 19661 19662 static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19663 { 19664 long us = 0; 19665 jim_wide start, delta, overhead; 19666 Jim_Obj *objPtr; 19667 double us_per_iter; 19668 int count; 19669 int n; 19670 19671 if (argc < 2) { 19672 Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?"); 19673 return JIM_ERR; 19674 } 19675 if (argc == 3) { 19676 if (Jim_GetLong(interp, argv[2], &us) != JIM_OK) 19677 return JIM_ERR; 19678 us *= 1000; 19679 } 19680 if (us < 1) { 19681 19682 us = 1000 * 1000; 19683 } 19684 19685 19686 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 19687 count = 0; 19688 do { 19689 int retval = Jim_EvalObj(interp, argv[1]); 19690 delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start; 19691 if (retval != JIM_OK) { 19692 return retval; 19693 } 19694 count++; 19695 } while (delta < us); 19696 19697 19698 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 19699 n = 0; 19700 do { 19701 int retval = Jim_EvalObj(interp, interp->nullScriptObj); 19702 overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start; 19703 if (retval != JIM_OK) { 19704 return retval; 19705 } 19706 n++; 19707 } while (n < count); 19708 19709 delta -= overhead; 19710 19711 us_per_iter = (double)delta / count; 19712 objPtr = Jim_NewListObj(interp, NULL, 0); 19713 19714 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1)); 19715 Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter)); 19716 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1)); 19717 Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter)); 19718 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1)); 19719 Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count)); 19720 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1)); 19721 Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta)); 19722 Jim_SetResult(interp, objPtr); 19723 return JIM_OK; 19724 } 19725 19726 19727 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19728 { 19729 long exitCode = 0; 19730 19731 if (argc > 2) { 19732 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?"); 19733 return JIM_ERR; 19734 } 19735 if (argc == 2) { 19736 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK) 19737 return JIM_ERR; 19738 Jim_SetResult(interp, argv[1]); 19739 } 19740 interp->exitCode = exitCode; 19741 return JIM_EXIT; 19742 } 19743 19744 static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc) 19745 { 19746 int len = Jim_ListLength(interp, retcodeListObj); 19747 int i; 19748 for (i = 0; i < len; i++) { 19749 int returncode; 19750 if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) { 19751 return JIM_ERR; 19752 } 19753 if (rc == returncode) { 19754 return JIM_OK; 19755 } 19756 } 19757 return -1; 19758 } 19759 19760 19761 static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv) 19762 { 19763 static const char * const wrongargs_catchtry[2] = { 19764 "?-?no?code ... --? script ?resultVarName? ?optionVarName?", 19765 "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?" 19766 }; 19767 int exitCode = 0; 19768 int i; 19769 int sig = 0; 19770 int ok; 19771 Jim_Obj *finallyScriptObj = NULL; 19772 Jim_Obj *msgVarObj = NULL; 19773 Jim_Obj *optsVarObj = NULL; 19774 Jim_Obj *handlerScriptObj = NULL; 19775 Jim_Obj *errorCodeObj; 19776 int idx; 19777 19778 19779 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL); 19780 static const int max_ignore_code = sizeof(ignore_mask) * 8; 19781 19782 JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper")); 19783 19784 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1)); 19785 19786 for (i = 1; i < argc - 1; i++) { 19787 const char *arg = Jim_String(argv[i]); 19788 jim_wide option; 19789 int ignore; 19790 19791 19792 if (strcmp(arg, "--") == 0) { 19793 i++; 19794 break; 19795 } 19796 if (*arg != '-') { 19797 break; 19798 } 19799 19800 if (strncmp(arg, "-no", 3) == 0) { 19801 arg += 3; 19802 ignore = 1; 19803 } 19804 else { 19805 arg++; 19806 ignore = 0; 19807 } 19808 19809 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) { 19810 option = -1; 19811 } 19812 if (option < 0) { 19813 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize); 19814 } 19815 if (option < 0) { 19816 goto wrongargs; 19817 } 19818 19819 if (ignore) { 19820 ignore_mask |= ((jim_wide)1 << option); 19821 } 19822 else { 19823 ignore_mask &= (~((jim_wide)1 << option)); 19824 } 19825 } 19826 19827 idx = i; 19828 19829 if (argc - idx < 1) { 19830 wrongargs: 19831 Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]); 19832 return JIM_ERR; 19833 } 19834 19835 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { 19836 sig++; 19837 } 19838 19839 interp->signal_level += sig; 19840 if (Jim_CheckSignal(interp)) { 19841 19842 exitCode = JIM_SIGNAL; 19843 } 19844 else { 19845 exitCode = Jim_EvalObj(interp, argv[idx]); 19846 19847 interp->errorFlag = 0; 19848 } 19849 interp->signal_level -= sig; 19850 19851 errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE); 19852 19853 idx++; 19854 if (istry) { 19855 while (idx < argc) { 19856 int option; 19857 int ret; 19858 static const char * const try_options[] = { "on", "trap", "finally", NULL }; 19859 enum { TRY_ON, TRY_TRAP, TRY_FINALLY, }; 19860 19861 if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) { 19862 return JIM_ERR; 19863 } 19864 switch (option) { 19865 case TRY_ON: 19866 case TRY_TRAP: 19867 if (idx + 4 > argc) { 19868 goto wrongargs; 19869 } 19870 if (option == TRY_ON) { 19871 ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode); 19872 if (ret > JIM_OK) { 19873 goto wrongargs; 19874 } 19875 } 19876 else if (errorCodeObj) { 19877 int len = Jim_ListLength(interp, argv[idx + 1]); 19878 19879 if (len > Jim_ListLength(interp, errorCodeObj)) { 19880 19881 ret = -1; 19882 } 19883 else { 19884 int i; 19885 ret = JIM_OK; 19886 19887 for (i = 0; i < len; i++) { 19888 Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i); 19889 Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i); 19890 if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) { 19891 ret = -1; 19892 break; 19893 } 19894 } 19895 } 19896 } 19897 else { 19898 19899 ret = -1; 19900 } 19901 19902 if (ret == JIM_OK && handlerScriptObj == NULL) { 19903 msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0); 19904 optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1); 19905 handlerScriptObj = argv[idx + 3]; 19906 } 19907 idx += 4; 19908 break; 19909 case TRY_FINALLY: 19910 if (idx + 2 != argc) { 19911 goto wrongargs; 19912 } 19913 finallyScriptObj = argv[idx + 1]; 19914 idx += 2; 19915 break; 19916 } 19917 } 19918 } 19919 else { 19920 if (argc - idx >= 1) { 19921 msgVarObj = argv[idx]; 19922 idx++; 19923 if (argc - idx >= 1) { 19924 optsVarObj = argv[idx]; 19925 idx++; 19926 } 19927 } 19928 } 19929 19930 19931 if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) { 19932 19933 if (finallyScriptObj) { 19934 Jim_EvalObj(interp, finallyScriptObj); 19935 } 19936 return exitCode; 19937 } 19938 19939 if (sig && exitCode == JIM_SIGNAL) { 19940 19941 if (interp->signal_set_result) { 19942 interp->signal_set_result(interp, interp->sigmask); 19943 } 19944 else if (!istry) { 19945 Jim_SetResultInt(interp, interp->sigmask); 19946 } 19947 interp->sigmask = 0; 19948 } 19949 19950 ok = 1; 19951 if (msgVarObj && Jim_Length(msgVarObj)) { 19952 if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) { 19953 ok = 0; 19954 } 19955 } 19956 if (ok && optsVarObj && Jim_Length(optsVarObj)) { 19957 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0); 19958 19959 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1)); 19960 Jim_ListAppendElement(interp, optListObj, 19961 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode)); 19962 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1)); 19963 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel)); 19964 if (exitCode == JIM_ERR) { 19965 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo", 19966 -1)); 19967 Jim_ListAppendElement(interp, optListObj, interp->stackTrace); 19968 19969 if (errorCodeObj) { 19970 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1)); 19971 Jim_ListAppendElement(interp, optListObj, errorCodeObj); 19972 } 19973 } 19974 if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) { 19975 ok = 0; 19976 } 19977 } 19978 if (ok && handlerScriptObj) { 19979 19980 exitCode = Jim_EvalObj(interp, handlerScriptObj); 19981 } 19982 19983 if (finallyScriptObj) { 19984 19985 Jim_Obj *prevResultObj = Jim_GetResult(interp); 19986 Jim_IncrRefCount(prevResultObj); 19987 int ret = Jim_EvalObj(interp, finallyScriptObj); 19988 if (ret == JIM_OK) { 19989 Jim_SetResult(interp, prevResultObj); 19990 } 19991 else { 19992 exitCode = ret; 19993 } 19994 Jim_DecrRefCount(interp, prevResultObj); 19995 } 19996 if (!istry) { 19997 Jim_SetResultInt(interp, exitCode); 19998 exitCode = JIM_OK; 19999 } 20000 return exitCode; 20001 } 20002 20003 20004 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20005 { 20006 return JimCatchTryHelper(interp, 0, argc, argv); 20007 } 20008 20009 20010 static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20011 { 20012 return JimCatchTryHelper(interp, 1, argc, argv); 20013 } 20014 20015 20016 20017 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20018 { 20019 if (argc != 3) { 20020 Jim_WrongNumArgs(interp, 1, argv, "oldName newName"); 20021 return JIM_ERR; 20022 } 20023 20024 return Jim_RenameCommand(interp, argv[1], argv[2]); 20025 } 20026 20027 #define JIM_DICTMATCH_KEYS 0x0001 20028 #define JIM_DICTMATCH_VALUES 0x002 20029 20030 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types) 20031 { 20032 Jim_Obj *listObjPtr; 20033 Jim_Dict *dict; 20034 int i; 20035 20036 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 20037 return JIM_ERR; 20038 } 20039 dict = objPtr->internalRep.dictValue; 20040 20041 listObjPtr = Jim_NewListObj(interp, NULL, 0); 20042 20043 for (i = 0; i < dict->len; i += 2 ) { 20044 Jim_Obj *keyObj = dict->table[i]; 20045 Jim_Obj *valObj = dict->table[i + 1]; 20046 if (patternObj) { 20047 Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj; 20048 if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) { 20049 20050 continue; 20051 } 20052 } 20053 if (return_types & JIM_DICTMATCH_KEYS) { 20054 Jim_ListAppendElement(interp, listObjPtr, keyObj); 20055 } 20056 if (return_types & JIM_DICTMATCH_VALUES) { 20057 Jim_ListAppendElement(interp, listObjPtr, valObj); 20058 } 20059 } 20060 20061 Jim_SetResult(interp, listObjPtr); 20062 return JIM_OK; 20063 } 20064 20065 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr) 20066 { 20067 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 20068 return -1; 20069 } 20070 return objPtr->internalRep.dictValue->len / 2; 20071 } 20072 20073 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 20074 { 20075 Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0); 20076 int i; 20077 20078 JimPanic((objc == 0, "Jim_DictMerge called with objc=0")); 20079 20080 20081 20082 for (i = 0; i < objc; i++) { 20083 Jim_Obj **table; 20084 int tablelen; 20085 int j; 20086 20087 table = Jim_DictPairs(interp, objv[i], &tablelen); 20088 if (tablelen && !table) { 20089 Jim_FreeNewObj(interp, objPtr); 20090 return NULL; 20091 } 20092 for (j = 0; j < tablelen; j += 2) { 20093 DictAddElement(interp, objPtr, table[j], table[j + 1]); 20094 } 20095 } 20096 return objPtr; 20097 } 20098 20099 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr) 20100 { 20101 char buffer[100]; 20102 Jim_Obj *output; 20103 Jim_Dict *dict; 20104 20105 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 20106 return JIM_ERR; 20107 } 20108 20109 dict = objPtr->internalRep.dictValue; 20110 20111 20112 snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size); 20113 output = Jim_NewStringObj(interp, buffer, -1); 20114 Jim_SetResult(interp, output); 20115 return JIM_OK; 20116 } 20117 20118 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv) 20119 { 20120 Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1); 20121 20122 Jim_AppendString(interp, prefixObj, " ", 1); 20123 Jim_AppendString(interp, prefixObj, subcmd, -1); 20124 20125 return Jim_EvalObjPrefix(interp, prefixObj, argc, argv); 20126 } 20127 20128 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj) 20129 { 20130 int i; 20131 Jim_Obj *objPtr; 20132 Jim_Obj *dictObj; 20133 Jim_Obj **dictValues; 20134 int len; 20135 int ret = JIM_OK; 20136 20137 20138 dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG); 20139 if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) { 20140 return JIM_ERR; 20141 } 20142 20143 dictValues = Jim_DictPairs(interp, objPtr, &len); 20144 if (len && dictValues == NULL) { 20145 return JIM_ERR; 20146 } 20147 for (i = 0; i < len; i += 2) { 20148 if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) { 20149 return JIM_ERR; 20150 } 20151 } 20152 20153 20154 if (Jim_Length(scriptObj)) { 20155 ret = Jim_EvalObj(interp, scriptObj); 20156 20157 20158 if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) { 20159 20160 Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1)); 20161 for (i = 0; i < keyc; i++) { 20162 newkeyv[i] = keyv[i]; 20163 } 20164 20165 for (i = 0; i < len; i += 2) { 20166 20167 if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) { 20168 20169 objPtr = Jim_GetVariable(interp, dictValues[i], 0); 20170 newkeyv[keyc] = dictValues[i]; 20171 Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT); 20172 } 20173 } 20174 Jim_Free(newkeyv); 20175 } 20176 } 20177 20178 return ret; 20179 } 20180 20181 20182 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20183 { 20184 Jim_Obj *objPtr; 20185 int types = JIM_DICTMATCH_KEYS; 20186 20187 enum { 20188 OPT_CREATE, 20189 OPT_GET, 20190 OPT_GETDEF, 20191 OPT_GETWITHDEFAULT, 20192 OPT_SET, 20193 OPT_UNSET, 20194 OPT_EXISTS, 20195 OPT_KEYS, 20196 OPT_SIZE, 20197 OPT_INFO, 20198 OPT_MERGE, 20199 OPT_WITH, 20200 OPT_APPEND, 20201 OPT_LAPPEND, 20202 OPT_INCR, 20203 OPT_REMOVE, 20204 OPT_VALUES, 20205 OPT_FOR, 20206 OPT_REPLACE, 20207 OPT_UPDATE, 20208 OPT_COUNT 20209 }; 20210 static const jim_subcmd_type cmds[OPT_COUNT + 1] = { 20211 JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2), 20212 JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1), 20213 JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1), 20214 JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1), 20215 JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1), 20216 JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1), 20217 JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1), 20218 JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2), 20219 JIM_DEF_SUBCMD("size", "dictionary", 1, 1), 20220 JIM_DEF_SUBCMD("info", "dictionary", 1, 1), 20221 JIM_DEF_SUBCMD("merge", "?...?", 0, -1), 20222 JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1), 20223 JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1), 20224 JIM_DEF_SUBCMD("lappend", "varName key ?value ...?", 2, -1), 20225 JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3), 20226 JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1), 20227 JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2), 20228 JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3), 20229 JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1), 20230 JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1), 20231 { NULL } 20232 }; 20233 const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv); 20234 if (!ct) { 20235 return JIM_ERR; 20236 } 20237 if (ct->function) { 20238 20239 return ct->function(interp, argc, argv); 20240 } 20241 20242 20243 switch (ct - cmds) { 20244 case OPT_GET: 20245 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, 20246 JIM_ERRMSG) != JIM_OK) { 20247 return JIM_ERR; 20248 } 20249 Jim_SetResult(interp, objPtr); 20250 return JIM_OK; 20251 20252 case OPT_GETDEF: 20253 case OPT_GETWITHDEFAULT:{ 20254 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG); 20255 if (rc == -1) { 20256 20257 return JIM_ERR; 20258 } 20259 if (rc == JIM_ERR) { 20260 Jim_SetResult(interp, argv[argc - 1]); 20261 } 20262 else { 20263 Jim_SetResult(interp, objPtr); 20264 } 20265 return JIM_OK; 20266 } 20267 20268 case OPT_SET: 20269 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG | JIM_UNSHARED); 20270 20271 case OPT_EXISTS:{ 20272 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE); 20273 if (rc < 0) { 20274 return JIM_ERR; 20275 } 20276 Jim_SetResultBool(interp, rc == JIM_OK); 20277 return JIM_OK; 20278 } 20279 20280 case OPT_UNSET: 20281 if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_UNSHARED) != JIM_OK) { 20282 return JIM_ERR; 20283 } 20284 return JIM_OK; 20285 20286 case OPT_VALUES: 20287 types = JIM_DICTMATCH_VALUES; 20288 20289 case OPT_KEYS: 20290 return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types); 20291 20292 case OPT_SIZE: 20293 if (Jim_DictSize(interp, argv[2]) < 0) { 20294 return JIM_ERR; 20295 } 20296 Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2])); 20297 return JIM_OK; 20298 20299 case OPT_MERGE: 20300 if (argc == 2) { 20301 return JIM_OK; 20302 } 20303 objPtr = Jim_DictMerge(interp, argc - 2, argv + 2); 20304 if (objPtr == NULL) { 20305 return JIM_ERR; 20306 } 20307 Jim_SetResult(interp, objPtr); 20308 return JIM_OK; 20309 20310 case OPT_CREATE: 20311 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2); 20312 Jim_SetResult(interp, objPtr); 20313 return JIM_OK; 20314 20315 case OPT_INFO: 20316 return Jim_DictInfo(interp, argv[2]); 20317 20318 case OPT_WITH: 20319 return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]); 20320 20321 case OPT_UPDATE: 20322 if (argc < 6 || argc % 2) { 20323 20324 argc = 2; 20325 } 20326 20327 default: 20328 return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2); 20329 } 20330 } 20331 20332 20333 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20334 { 20335 static const char * const options[] = { 20336 "-nobackslashes", "-nocommands", "-novariables", NULL 20337 }; 20338 enum 20339 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES }; 20340 int i; 20341 int flags = JIM_SUBST_FLAG; 20342 Jim_Obj *objPtr; 20343 20344 if (argc < 2) { 20345 Jim_WrongNumArgs(interp, 1, argv, "?options? string"); 20346 return JIM_ERR; 20347 } 20348 for (i = 1; i < (argc - 1); i++) { 20349 int option; 20350 20351 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, 20352 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 20353 return JIM_ERR; 20354 } 20355 switch (option) { 20356 case OPT_NOBACKSLASHES: 20357 flags |= JIM_SUBST_NOESC; 20358 break; 20359 case OPT_NOCOMMANDS: 20360 flags |= JIM_SUBST_NOCMD; 20361 break; 20362 case OPT_NOVARIABLES: 20363 flags |= JIM_SUBST_NOVAR; 20364 break; 20365 } 20366 } 20367 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) { 20368 return JIM_ERR; 20369 } 20370 Jim_SetResult(interp, objPtr); 20371 return JIM_OK; 20372 } 20373 20374 #ifdef jim_ext_namespace 20375 static int JimIsGlobalNamespace(Jim_Obj *objPtr) 20376 { 20377 int len; 20378 const char *str = Jim_GetString(objPtr, &len); 20379 return len >= 2 && str[0] == ':' && str[1] == ':'; 20380 } 20381 #endif 20382 20383 20384 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20385 { 20386 Jim_Obj *objPtr; 20387 int mode = 0; 20388 20389 20390 enum { 20391 INFO_ALIAS, 20392 INFO_ARGS, 20393 INFO_BODY, 20394 INFO_CHANNELS, 20395 INFO_COMMANDS, 20396 INFO_COMPLETE, 20397 INFO_EXISTS, 20398 INFO_FRAME, 20399 INFO_GLOBALS, 20400 INFO_HOSTNAME, 20401 INFO_LEVEL, 20402 INFO_LOCALS, 20403 INFO_NAMEOFEXECUTABLE, 20404 INFO_PATCHLEVEL, 20405 INFO_PROCS, 20406 INFO_REFERENCES, 20407 INFO_RETURNCODES, 20408 INFO_SCRIPT, 20409 INFO_SOURCE, 20410 INFO_STACKTRACE, 20411 INFO_STATICS, 20412 INFO_VARS, 20413 INFO_VERSION, 20414 INFO_COUNT 20415 }; 20416 static const jim_subcmd_type cmds[INFO_COUNT + 1] = { 20417 JIM_DEF_SUBCMD("alias", "command", 1, 1), 20418 JIM_DEF_SUBCMD("args", "procname", 1, 1), 20419 JIM_DEF_SUBCMD("body", "procname", 1, 1), 20420 JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1), 20421 JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1), 20422 JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2), 20423 JIM_DEF_SUBCMD("exists", "varName", 1, 1), 20424 JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1), 20425 JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1), 20426 JIM_DEF_SUBCMD("hostname", NULL, 0, 0), 20427 JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1), 20428 JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1), 20429 JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0), 20430 JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0), 20431 JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1), 20432 JIM_DEF_SUBCMD("references", NULL, 0, 0), 20433 JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1), 20434 JIM_DEF_SUBCMD("script", "?filename?", 0, 1), 20435 JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3), 20436 JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0), 20437 JIM_DEF_SUBCMD("statics", "procname", 1, 1), 20438 JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1), 20439 JIM_DEF_SUBCMD("version", NULL, 0, 0), 20440 { NULL } 20441 }; 20442 const jim_subcmd_type *ct; 20443 #ifdef jim_ext_namespace 20444 int nons = 0; 20445 20446 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) { 20447 20448 argc--; 20449 argv++; 20450 nons = 1; 20451 } 20452 #endif 20453 ct = Jim_ParseSubCmd(interp, cmds, argc, argv); 20454 if (!ct) { 20455 return JIM_ERR; 20456 } 20457 if (ct->function) { 20458 20459 return ct->function(interp, argc, argv); 20460 } 20461 20462 int option = ct - cmds; 20463 20464 switch (option) { 20465 case INFO_EXISTS: 20466 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL); 20467 return JIM_OK; 20468 20469 case INFO_ALIAS:{ 20470 Jim_Cmd *cmdPtr; 20471 20472 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) { 20473 return JIM_ERR; 20474 } 20475 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) { 20476 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]); 20477 return JIM_ERR; 20478 } 20479 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData); 20480 return JIM_OK; 20481 } 20482 20483 case INFO_CHANNELS: 20484 mode++; 20485 #ifndef jim_ext_aio 20486 Jim_SetResultString(interp, "aio not enabled", -1); 20487 return JIM_ERR; 20488 #endif 20489 20490 case INFO_PROCS: 20491 mode++; 20492 20493 case INFO_COMMANDS: 20494 20495 #ifdef jim_ext_namespace 20496 if (!nons) { 20497 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) { 20498 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1); 20499 } 20500 } 20501 #endif 20502 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode)); 20503 return JIM_OK; 20504 20505 case INFO_VARS: 20506 mode++; 20507 20508 case INFO_LOCALS: 20509 mode++; 20510 20511 case INFO_GLOBALS: 20512 20513 #ifdef jim_ext_namespace 20514 if (!nons) { 20515 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) { 20516 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1); 20517 } 20518 } 20519 #endif 20520 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode)); 20521 return JIM_OK; 20522 20523 case INFO_SCRIPT: 20524 if (argc == 3) { 20525 Jim_IncrRefCount(argv[2]); 20526 Jim_DecrRefCount(interp, interp->currentFilenameObj); 20527 interp->currentFilenameObj = argv[2]; 20528 } 20529 Jim_SetResult(interp, interp->currentFilenameObj); 20530 return JIM_OK; 20531 20532 case INFO_SOURCE:{ 20533 Jim_Obj *resObjPtr; 20534 Jim_Obj *fileNameObj; 20535 20536 if (argc == 4) { 20537 Jim_SubCmdArgError(interp, ct, argv[0]); 20538 return JIM_ERR; 20539 } 20540 if (argc == 5) { 20541 jim_wide line; 20542 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) { 20543 return JIM_ERR; 20544 } 20545 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2])); 20546 Jim_SetSourceInfo(interp, resObjPtr, argv[3], line); 20547 } 20548 else { 20549 int line; 20550 fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line); 20551 resObjPtr = Jim_NewListObj(interp, NULL, 0); 20552 Jim_ListAppendElement(interp, resObjPtr, fileNameObj); 20553 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line)); 20554 } 20555 Jim_SetResult(interp, resObjPtr); 20556 return JIM_OK; 20557 } 20558 20559 case INFO_STACKTRACE: 20560 Jim_SetResult(interp, interp->stackTrace); 20561 return JIM_OK; 20562 20563 case INFO_LEVEL: 20564 if (argc == 2) { 20565 Jim_SetResultInt(interp, interp->framePtr->level); 20566 } 20567 else { 20568 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) { 20569 return JIM_ERR; 20570 } 20571 Jim_SetResult(interp, objPtr); 20572 } 20573 return JIM_OK; 20574 20575 case INFO_FRAME: 20576 if (argc == 2) { 20577 Jim_SetResultInt(interp, interp->procLevel + 1); 20578 } 20579 else { 20580 if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) { 20581 return JIM_ERR; 20582 } 20583 Jim_SetResult(interp, objPtr); 20584 } 20585 return JIM_OK; 20586 20587 case INFO_BODY: 20588 case INFO_STATICS: 20589 case INFO_ARGS:{ 20590 Jim_Cmd *cmdPtr; 20591 20592 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) { 20593 return JIM_ERR; 20594 } 20595 if (!cmdPtr->isproc) { 20596 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]); 20597 return JIM_ERR; 20598 } 20599 switch (option) { 20600 #ifdef JIM_NO_INTROSPECTION 20601 default: 20602 Jim_SetResultString(interp, "unsupported", -1); 20603 return JIM_ERR; 20604 #else 20605 case INFO_BODY: 20606 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr); 20607 break; 20608 case INFO_ARGS: 20609 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr); 20610 break; 20611 #endif 20612 case INFO_STATICS: 20613 if (cmdPtr->u.proc.staticVars) { 20614 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars, 20615 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES)); 20616 } 20617 break; 20618 } 20619 return JIM_OK; 20620 } 20621 20622 case INFO_VERSION: 20623 case INFO_PATCHLEVEL:{ 20624 char buf[(JIM_INTEGER_SPACE * 2) + 1]; 20625 20626 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100); 20627 Jim_SetResultString(interp, buf, -1); 20628 return JIM_OK; 20629 } 20630 20631 case INFO_COMPLETE: { 20632 char missing; 20633 20634 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing)); 20635 if (missing != ' ' && argc == 4) { 20636 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1)); 20637 } 20638 return JIM_OK; 20639 } 20640 20641 case INFO_HOSTNAME: 20642 20643 return Jim_Eval(interp, "os.gethostname"); 20644 20645 case INFO_NAMEOFEXECUTABLE: 20646 20647 return Jim_Eval(interp, "{info nameofexecutable}"); 20648 20649 case INFO_RETURNCODES: 20650 if (argc == 2) { 20651 int i; 20652 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); 20653 20654 for (i = 0; jimReturnCodes[i]; i++) { 20655 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i)); 20656 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, 20657 jimReturnCodes[i], -1)); 20658 } 20659 20660 Jim_SetResult(interp, listObjPtr); 20661 } 20662 else if (argc == 3) { 20663 long code; 20664 const char *name; 20665 20666 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) { 20667 return JIM_ERR; 20668 } 20669 name = Jim_ReturnCode(code); 20670 if (*name == '?') { 20671 Jim_SetResultInt(interp, code); 20672 } 20673 else { 20674 Jim_SetResultString(interp, name, -1); 20675 } 20676 } 20677 return JIM_OK; 20678 case INFO_REFERENCES: 20679 #ifdef JIM_REFERENCES 20680 return JimInfoReferences(interp, argc, argv); 20681 #else 20682 Jim_SetResultString(interp, "not supported", -1); 20683 return JIM_ERR; 20684 #endif 20685 default: 20686 abort(); 20687 } 20688 } 20689 20690 20691 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20692 { 20693 Jim_Obj *objPtr; 20694 int result = 0; 20695 20696 static const char * const options[] = { 20697 "-command", "-proc", "-alias", "-var", NULL 20698 }; 20699 enum 20700 { 20701 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR 20702 }; 20703 int option; 20704 20705 if (argc == 2) { 20706 option = OPT_VAR; 20707 objPtr = argv[1]; 20708 } 20709 else if (argc == 3) { 20710 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 20711 return JIM_ERR; 20712 } 20713 objPtr = argv[2]; 20714 } 20715 else { 20716 Jim_WrongNumArgs(interp, 1, argv, "?option? name"); 20717 return JIM_ERR; 20718 } 20719 20720 if (option == OPT_VAR) { 20721 result = Jim_GetVariable(interp, objPtr, 0) != NULL; 20722 } 20723 else { 20724 20725 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE); 20726 20727 if (cmd) { 20728 switch (option) { 20729 case OPT_COMMAND: 20730 result = 1; 20731 break; 20732 20733 case OPT_ALIAS: 20734 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd; 20735 break; 20736 20737 case OPT_PROC: 20738 result = cmd->isproc; 20739 break; 20740 } 20741 } 20742 } 20743 Jim_SetResultBool(interp, result); 20744 return JIM_OK; 20745 } 20746 20747 20748 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20749 { 20750 const char *str, *splitChars, *noMatchStart; 20751 int splitLen, strLen; 20752 Jim_Obj *resObjPtr; 20753 int c; 20754 int len; 20755 20756 if (argc != 2 && argc != 3) { 20757 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?"); 20758 return JIM_ERR; 20759 } 20760 20761 str = Jim_GetString(argv[1], &len); 20762 if (len == 0) { 20763 return JIM_OK; 20764 } 20765 strLen = Jim_Utf8Length(interp, argv[1]); 20766 20767 20768 if (argc == 2) { 20769 splitChars = " \n\t\r"; 20770 splitLen = 4; 20771 } 20772 else { 20773 splitChars = Jim_String(argv[2]); 20774 splitLen = Jim_Utf8Length(interp, argv[2]); 20775 } 20776 20777 noMatchStart = str; 20778 resObjPtr = Jim_NewListObj(interp, NULL, 0); 20779 20780 20781 if (splitLen) { 20782 Jim_Obj *objPtr; 20783 while (strLen--) { 20784 const char *sc = splitChars; 20785 int scLen = splitLen; 20786 int sl = utf8_tounicode(str, &c); 20787 while (scLen--) { 20788 int pc; 20789 sc += utf8_tounicode(sc, &pc); 20790 if (c == pc) { 20791 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart)); 20792 Jim_ListAppendElement(interp, resObjPtr, objPtr); 20793 noMatchStart = str + sl; 20794 break; 20795 } 20796 } 20797 str += sl; 20798 } 20799 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart)); 20800 Jim_ListAppendElement(interp, resObjPtr, objPtr); 20801 } 20802 else { 20803 Jim_Obj **commonObj = NULL; 20804 #define NUM_COMMON (128 - 9) 20805 while (strLen--) { 20806 int n = utf8_tounicode(str, &c); 20807 #ifdef JIM_OPTIMIZATION 20808 if (c >= 9 && c < 128) { 20809 20810 c -= 9; 20811 if (!commonObj) { 20812 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON); 20813 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON); 20814 } 20815 if (!commonObj[c]) { 20816 commonObj[c] = Jim_NewStringObj(interp, str, 1); 20817 } 20818 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]); 20819 str++; 20820 continue; 20821 } 20822 #endif 20823 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1)); 20824 str += n; 20825 } 20826 Jim_Free(commonObj); 20827 } 20828 20829 Jim_SetResult(interp, resObjPtr); 20830 return JIM_OK; 20831 } 20832 20833 20834 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20835 { 20836 const char *joinStr; 20837 int joinStrLen; 20838 20839 if (argc != 2 && argc != 3) { 20840 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?"); 20841 return JIM_ERR; 20842 } 20843 20844 if (argc == 2) { 20845 joinStr = " "; 20846 joinStrLen = 1; 20847 } 20848 else { 20849 joinStr = Jim_GetString(argv[2], &joinStrLen); 20850 } 20851 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen)); 20852 return JIM_OK; 20853 } 20854 20855 20856 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20857 { 20858 Jim_Obj *objPtr; 20859 20860 if (argc < 2) { 20861 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?"); 20862 return JIM_ERR; 20863 } 20864 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2); 20865 if (objPtr == NULL) 20866 return JIM_ERR; 20867 Jim_SetResult(interp, objPtr); 20868 return JIM_OK; 20869 } 20870 20871 20872 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20873 { 20874 Jim_Obj *listPtr, **outVec; 20875 int outc, i; 20876 20877 if (argc < 3) { 20878 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?"); 20879 return JIM_ERR; 20880 } 20881 if (argv[2]->typePtr != &scanFmtStringObjType) 20882 SetScanFmtFromAny(interp, argv[2]); 20883 if (FormatGetError(argv[2]) != 0) { 20884 Jim_SetResultString(interp, FormatGetError(argv[2]), -1); 20885 return JIM_ERR; 20886 } 20887 if (argc > 3) { 20888 int maxPos = FormatGetMaxPos(argv[2]); 20889 int count = FormatGetCnvCount(argv[2]); 20890 20891 if (maxPos > argc - 3) { 20892 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1); 20893 return JIM_ERR; 20894 } 20895 else if (count > argc - 3) { 20896 Jim_SetResultString(interp, "different numbers of variable names and " 20897 "field specifiers", -1); 20898 return JIM_ERR; 20899 } 20900 else if (count < argc - 3) { 20901 Jim_SetResultString(interp, "variable is not assigned by any " 20902 "conversion specifiers", -1); 20903 return JIM_ERR; 20904 } 20905 } 20906 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG); 20907 if (listPtr == 0) 20908 return JIM_ERR; 20909 if (argc > 3) { 20910 int rc = JIM_OK; 20911 int count = 0; 20912 20913 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) { 20914 int len = Jim_ListLength(interp, listPtr); 20915 20916 if (len != 0) { 20917 JimListGetElements(interp, listPtr, &outc, &outVec); 20918 for (i = 0; i < outc; ++i) { 20919 if (Jim_Length(outVec[i]) > 0) { 20920 ++count; 20921 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) { 20922 rc = JIM_ERR; 20923 } 20924 } 20925 } 20926 } 20927 Jim_FreeNewObj(interp, listPtr); 20928 } 20929 else { 20930 count = -1; 20931 } 20932 if (rc == JIM_OK) { 20933 Jim_SetResultInt(interp, count); 20934 } 20935 return rc; 20936 } 20937 else { 20938 if (listPtr == (Jim_Obj *)EOF) { 20939 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0)); 20940 return JIM_OK; 20941 } 20942 Jim_SetResult(interp, listPtr); 20943 } 20944 return JIM_OK; 20945 } 20946 20947 20948 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20949 { 20950 if (argc != 2 && argc != 3) { 20951 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?"); 20952 return JIM_ERR; 20953 } 20954 Jim_SetResult(interp, argv[1]); 20955 if (argc == 3) { 20956 JimSetStackTrace(interp, argv[2]); 20957 return JIM_ERR; 20958 } 20959 return JIM_ERR; 20960 } 20961 20962 20963 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20964 { 20965 Jim_Obj *objPtr; 20966 20967 if (argc != 4) { 20968 Jim_WrongNumArgs(interp, 1, argv, "list first last"); 20969 return JIM_ERR; 20970 } 20971 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL) 20972 return JIM_ERR; 20973 Jim_SetResult(interp, objPtr); 20974 return JIM_OK; 20975 } 20976 20977 20978 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20979 { 20980 Jim_Obj *objPtr; 20981 jim_wide count; 20982 20983 if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) { 20984 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?"); 20985 return JIM_ERR; 20986 } 20987 if (count == 0 || argc == 2) { 20988 Jim_SetEmptyResult(interp); 20989 return JIM_OK; 20990 } 20991 20992 argc -= 2; 20993 argv += 2; 20994 20995 objPtr = Jim_NewListObj(interp, NULL, 0); 20996 ListEnsureLength(objPtr, argc * count); 20997 while (count--) { 20998 ListInsertElements(objPtr, -1, argc, argv); 20999 } 21000 21001 Jim_SetResult(interp, objPtr); 21002 return JIM_OK; 21003 } 21004 21005 char **Jim_GetEnviron(void) 21006 { 21007 #if defined(HAVE__NSGETENVIRON) 21008 return *_NSGetEnviron(); 21009 #elif defined(_environ) 21010 return _environ; 21011 #else 21012 #if !defined(NO_ENVIRON_EXTERN) 21013 extern char **environ; 21014 #endif 21015 return environ; 21016 #endif 21017 } 21018 21019 void Jim_SetEnviron(char **env) 21020 { 21021 #if defined(HAVE__NSGETENVIRON) 21022 *_NSGetEnviron() = env; 21023 #elif defined(_environ) 21024 _environ = env; 21025 #else 21026 #if !defined(NO_ENVIRON_EXTERN) 21027 extern char **environ; 21028 #endif 21029 21030 environ = env; 21031 #endif 21032 } 21033 21034 21035 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21036 { 21037 const char *key; 21038 const char *val; 21039 21040 if (argc == 1) { 21041 char **e = Jim_GetEnviron(); 21042 21043 int i; 21044 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); 21045 21046 for (i = 0; e[i]; i++) { 21047 const char *equals = strchr(e[i], '='); 21048 21049 if (equals) { 21050 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i], 21051 equals - e[i])); 21052 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1)); 21053 } 21054 } 21055 21056 Jim_SetResult(interp, listObjPtr); 21057 return JIM_OK; 21058 } 21059 21060 if (argc > 3) { 21061 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?"); 21062 return JIM_ERR; 21063 } 21064 key = Jim_String(argv[1]); 21065 val = getenv(key); 21066 if (val == NULL) { 21067 if (argc < 3) { 21068 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]); 21069 return JIM_ERR; 21070 } 21071 val = Jim_String(argv[2]); 21072 } 21073 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1)); 21074 return JIM_OK; 21075 } 21076 21077 21078 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21079 { 21080 int retval; 21081 21082 if (argc != 2) { 21083 Jim_WrongNumArgs(interp, 1, argv, "fileName"); 21084 return JIM_ERR; 21085 } 21086 retval = Jim_EvalFile(interp, Jim_String(argv[1])); 21087 if (retval == JIM_RETURN) 21088 return JIM_OK; 21089 return retval; 21090 } 21091 21092 21093 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21094 { 21095 Jim_Obj *revObjPtr, **ele; 21096 int len; 21097 21098 if (argc != 2) { 21099 Jim_WrongNumArgs(interp, 1, argv, "list"); 21100 return JIM_ERR; 21101 } 21102 JimListGetElements(interp, argv[1], &len, &ele); 21103 revObjPtr = Jim_NewListObj(interp, NULL, 0); 21104 ListEnsureLength(revObjPtr, len); 21105 len--; 21106 while (len >= 0) 21107 ListAppendElement(revObjPtr, ele[len--]); 21108 Jim_SetResult(interp, revObjPtr); 21109 return JIM_OK; 21110 } 21111 21112 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step) 21113 { 21114 jim_wide len; 21115 21116 if (step == 0) 21117 return -1; 21118 if (start == end) 21119 return 0; 21120 else if (step > 0 && start > end) 21121 return -1; 21122 else if (step < 0 && end > start) 21123 return -1; 21124 len = end - start; 21125 if (len < 0) 21126 len = -len; 21127 if (step < 0) 21128 step = -step; 21129 len = 1 + ((len - 1) / step); 21130 if (len > INT_MAX) 21131 len = INT_MAX; 21132 return (int)((len < 0) ? -1 : len); 21133 } 21134 21135 21136 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21137 { 21138 jim_wide start = 0, end, step = 1; 21139 int len, i; 21140 Jim_Obj *objPtr; 21141 21142 if (argc < 2 || argc > 4) { 21143 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?"); 21144 return JIM_ERR; 21145 } 21146 if (argc == 2) { 21147 if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK) 21148 return JIM_ERR; 21149 } 21150 else { 21151 if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK || 21152 Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK) 21153 return JIM_ERR; 21154 if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK) 21155 return JIM_ERR; 21156 } 21157 if ((len = JimRangeLen(start, end, step)) == -1) { 21158 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1); 21159 return JIM_ERR; 21160 } 21161 objPtr = Jim_NewListObj(interp, NULL, 0); 21162 ListEnsureLength(objPtr, len); 21163 for (i = 0; i < len; i++) 21164 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step)); 21165 Jim_SetResult(interp, objPtr); 21166 return JIM_OK; 21167 } 21168 21169 21170 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21171 { 21172 jim_wide min = 0, max = 0, len, maxMul; 21173 21174 if (argc < 1 || argc > 3) { 21175 Jim_WrongNumArgs(interp, 1, argv, "?min? max"); 21176 return JIM_ERR; 21177 } 21178 if (argc == 1) { 21179 max = JIM_WIDE_MAX; 21180 } else if (argc == 2) { 21181 if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK) 21182 return JIM_ERR; 21183 } else if (argc == 3) { 21184 if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK || 21185 Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK) 21186 return JIM_ERR; 21187 } 21188 len = max-min; 21189 if (len < 0) { 21190 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1); 21191 return JIM_ERR; 21192 } 21193 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0); 21194 while (1) { 21195 jim_wide r; 21196 21197 JimRandomBytes(interp, &r, sizeof(jim_wide)); 21198 if (r < 0 || r >= maxMul) continue; 21199 r = (len == 0) ? 0 : r%len; 21200 Jim_SetResultInt(interp, min+r); 21201 return JIM_OK; 21202 } 21203 } 21204 21205 static const struct { 21206 const char *name; 21207 Jim_CmdProc *cmdProc; 21208 } Jim_CoreCommandsTable[] = { 21209 {"alias", Jim_AliasCoreCommand}, 21210 {"set", Jim_SetCoreCommand}, 21211 {"unset", Jim_UnsetCoreCommand}, 21212 {"puts", Jim_PutsCoreCommand}, 21213 {"+", Jim_AddCoreCommand}, 21214 {"*", Jim_MulCoreCommand}, 21215 {"-", Jim_SubCoreCommand}, 21216 {"/", Jim_DivCoreCommand}, 21217 {"incr", Jim_IncrCoreCommand}, 21218 {"while", Jim_WhileCoreCommand}, 21219 {"loop", Jim_LoopCoreCommand}, 21220 {"for", Jim_ForCoreCommand}, 21221 {"foreach", Jim_ForeachCoreCommand}, 21222 {"lmap", Jim_LmapCoreCommand}, 21223 {"lassign", Jim_LassignCoreCommand}, 21224 {"if", Jim_IfCoreCommand}, 21225 {"switch", Jim_SwitchCoreCommand}, 21226 {"list", Jim_ListCoreCommand}, 21227 {"lindex", Jim_LindexCoreCommand}, 21228 {"lset", Jim_LsetCoreCommand}, 21229 {"lsearch", Jim_LsearchCoreCommand}, 21230 {"llength", Jim_LlengthCoreCommand}, 21231 {"lappend", Jim_LappendCoreCommand}, 21232 {"linsert", Jim_LinsertCoreCommand}, 21233 {"lreplace", Jim_LreplaceCoreCommand}, 21234 {"lsort", Jim_LsortCoreCommand}, 21235 {"append", Jim_AppendCoreCommand}, 21236 {"eval", Jim_EvalCoreCommand}, 21237 {"uplevel", Jim_UplevelCoreCommand}, 21238 {"expr", Jim_ExprCoreCommand}, 21239 {"break", Jim_BreakCoreCommand}, 21240 {"continue", Jim_ContinueCoreCommand}, 21241 {"proc", Jim_ProcCoreCommand}, 21242 {"xtrace", Jim_XtraceCoreCommand}, 21243 {"concat", Jim_ConcatCoreCommand}, 21244 {"return", Jim_ReturnCoreCommand}, 21245 {"upvar", Jim_UpvarCoreCommand}, 21246 {"global", Jim_GlobalCoreCommand}, 21247 {"string", Jim_StringCoreCommand}, 21248 {"time", Jim_TimeCoreCommand}, 21249 {"timerate", Jim_TimeRateCoreCommand}, 21250 {"exit", Jim_ExitCoreCommand}, 21251 {"catch", Jim_CatchCoreCommand}, 21252 {"try", Jim_TryCoreCommand}, 21253 #ifdef JIM_REFERENCES 21254 {"ref", Jim_RefCoreCommand}, 21255 {"getref", Jim_GetrefCoreCommand}, 21256 {"setref", Jim_SetrefCoreCommand}, 21257 {"finalize", Jim_FinalizeCoreCommand}, 21258 {"collect", Jim_CollectCoreCommand}, 21259 #endif 21260 {"rename", Jim_RenameCoreCommand}, 21261 {"dict", Jim_DictCoreCommand}, 21262 {"subst", Jim_SubstCoreCommand}, 21263 {"info", Jim_InfoCoreCommand}, 21264 {"exists", Jim_ExistsCoreCommand}, 21265 {"split", Jim_SplitCoreCommand}, 21266 {"join", Jim_JoinCoreCommand}, 21267 {"format", Jim_FormatCoreCommand}, 21268 {"scan", Jim_ScanCoreCommand}, 21269 {"error", Jim_ErrorCoreCommand}, 21270 {"lrange", Jim_LrangeCoreCommand}, 21271 {"lrepeat", Jim_LrepeatCoreCommand}, 21272 {"env", Jim_EnvCoreCommand}, 21273 {"source", Jim_SourceCoreCommand}, 21274 {"lreverse", Jim_LreverseCoreCommand}, 21275 {"range", Jim_RangeCoreCommand}, 21276 {"rand", Jim_RandCoreCommand}, 21277 {"tailcall", Jim_TailcallCoreCommand}, 21278 {"local", Jim_LocalCoreCommand}, 21279 {"upcall", Jim_UpcallCoreCommand}, 21280 {"apply", Jim_ApplyCoreCommand}, 21281 {"stacktrace", Jim_StacktraceCoreCommand}, 21282 {NULL, NULL}, 21283 }; 21284 21285 void Jim_RegisterCoreCommands(Jim_Interp *interp) 21286 { 21287 int i = 0; 21288 21289 while (Jim_CoreCommandsTable[i].name != NULL) { 21290 Jim_CreateCommand(interp, 21291 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL); 21292 i++; 21293 } 21294 } 21295 21296 void Jim_MakeErrorMessage(Jim_Interp *interp) 21297 { 21298 Jim_Obj *argv[2]; 21299 21300 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1); 21301 argv[1] = interp->result; 21302 21303 Jim_EvalObjVector(interp, 2, argv); 21304 } 21305 21306 static char **JimSortStringTable(const char *const *tablePtr) 21307 { 21308 int count; 21309 char **tablePtrSorted; 21310 21311 21312 for (count = 0; tablePtr[count]; count++) { 21313 } 21314 21315 21316 tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1)); 21317 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count); 21318 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers); 21319 tablePtrSorted[count] = NULL; 21320 21321 return tablePtrSorted; 21322 } 21323 21324 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, 21325 const char *prefix, const char *const *tablePtr, const char *name) 21326 { 21327 char **tablePtrSorted; 21328 int i; 21329 21330 if (name == NULL) { 21331 name = "option"; 21332 } 21333 21334 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg); 21335 tablePtrSorted = JimSortStringTable(tablePtr); 21336 for (i = 0; tablePtrSorted[i]; i++) { 21337 if (tablePtrSorted[i + 1] == NULL && i > 0) { 21338 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1); 21339 } 21340 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL); 21341 if (tablePtrSorted[i + 1]) { 21342 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1); 21343 } 21344 } 21345 Jim_Free(tablePtrSorted); 21346 } 21347 21348 21349 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr) 21350 { 21351 if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) { 21352 int i; 21353 char **tablePtrSorted = JimSortStringTable(tablePtr); 21354 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); 21355 for (i = 0; tablePtrSorted[i]; i++) { 21356 Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1)); 21357 } 21358 Jim_Free(tablePtrSorted); 21359 return JIM_OK; 21360 } 21361 return JIM_ERR; 21362 } 21363 21364 static const Jim_ObjType getEnumObjType = { 21365 "get-enum", 21366 NULL, 21367 NULL, 21368 NULL, 21369 JIM_TYPE_REFERENCES 21370 }; 21371 21372 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr, 21373 const char *const *tablePtr, int *indexPtr, const char *name, int flags) 21374 { 21375 const char *bad = "bad "; 21376 const char *const *entryPtr = NULL; 21377 int i; 21378 int match = -1; 21379 int arglen; 21380 const char *arg; 21381 21382 if (objPtr->typePtr == &getEnumObjType) { 21383 if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) { 21384 *indexPtr = objPtr->internalRep.ptrIntValue.int2; 21385 return JIM_OK; 21386 } 21387 } 21388 21389 arg = Jim_GetString(objPtr, &arglen); 21390 21391 *indexPtr = -1; 21392 21393 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) { 21394 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) { 21395 21396 match = i; 21397 goto found; 21398 } 21399 if (flags & JIM_ENUM_ABBREV) { 21400 if (strncmp(arg, *entryPtr, arglen) == 0) { 21401 if (*arg == '-' && arglen == 1) { 21402 break; 21403 } 21404 if (match >= 0) { 21405 bad = "ambiguous "; 21406 goto ambiguous; 21407 } 21408 match = i; 21409 } 21410 } 21411 } 21412 21413 21414 if (match >= 0) { 21415 found: 21416 21417 Jim_FreeIntRep(interp, objPtr); 21418 objPtr->typePtr = &getEnumObjType; 21419 objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr; 21420 objPtr->internalRep.ptrIntValue.int1 = flags; 21421 objPtr->internalRep.ptrIntValue.int2 = match; 21422 21423 *indexPtr = match; 21424 return JIM_OK; 21425 } 21426 21427 ambiguous: 21428 if (flags & JIM_ERRMSG) { 21429 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name); 21430 } 21431 return JIM_ERR; 21432 } 21433 21434 int Jim_FindByName(const char *name, const char * const array[], size_t len) 21435 { 21436 int i; 21437 21438 for (i = 0; i < (int)len; i++) { 21439 if (array[i] && strcmp(array[i], name) == 0) { 21440 return i; 21441 } 21442 } 21443 return -1; 21444 } 21445 21446 int Jim_IsDict(Jim_Obj *objPtr) 21447 { 21448 return objPtr->typePtr == &dictObjType; 21449 } 21450 21451 int Jim_IsList(Jim_Obj *objPtr) 21452 { 21453 return objPtr->typePtr == &listObjType; 21454 } 21455 21456 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...) 21457 { 21458 21459 int len = strlen(format); 21460 int extra = 0; 21461 int n = 0; 21462 const char *params[5]; 21463 int nobjparam = 0; 21464 Jim_Obj *objparam[5]; 21465 char *buf; 21466 va_list args; 21467 int i; 21468 21469 va_start(args, format); 21470 21471 for (i = 0; i < len && n < 5; i++) { 21472 int l; 21473 21474 if (strncmp(format + i, "%s", 2) == 0) { 21475 params[n] = va_arg(args, char *); 21476 21477 l = strlen(params[n]); 21478 } 21479 else if (strncmp(format + i, "%#s", 3) == 0) { 21480 Jim_Obj *objPtr = va_arg(args, Jim_Obj *); 21481 21482 params[n] = Jim_GetString(objPtr, &l); 21483 objparam[nobjparam++] = objPtr; 21484 Jim_IncrRefCount(objPtr); 21485 } 21486 else { 21487 if (format[i] == '%') { 21488 i++; 21489 } 21490 continue; 21491 } 21492 n++; 21493 extra += l; 21494 } 21495 21496 len += extra; 21497 buf = Jim_Alloc(len + 1); 21498 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]); 21499 21500 va_end(args); 21501 21502 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); 21503 21504 for (i = 0; i < nobjparam; i++) { 21505 Jim_DecrRefCount(interp, objparam[i]); 21506 } 21507 } 21508 21509 int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version) 21510 { 21511 if (abi_version != JIM_ABI_VERSION) { 21512 Jim_SetResultString(interp, "ABI version mismatch", -1); 21513 return JIM_ERR; 21514 } 21515 return JIM_OK; 21516 } 21517 21518 21519 #ifndef jim_ext_package 21520 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags) 21521 { 21522 return JIM_OK; 21523 } 21524 #endif 21525 #ifndef jim_ext_aio 21526 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj) 21527 { 21528 return -1; 21529 } 21530 #endif 21531 21532 21533 #include <stdio.h> 21534 #include <string.h> 21535 21536 21537 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21538 { 21539 21540 return JIM_OK; 21541 } 21542 21543 static const jim_subcmd_type dummy_subcmd = { 21544 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN 21545 }; 21546 21547 static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep) 21548 { 21549 21550 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 21551 Jim_Obj *sortCmd[2]; 21552 21553 for (; ct->cmd; ct++) { 21554 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { 21555 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1)); 21556 } 21557 } 21558 21559 21560 sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1); 21561 sortCmd[1] = listObj; 21562 21563 if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) { 21564 return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep)); 21565 } 21566 21567 return Jim_GetResult(interp); 21568 } 21569 21570 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type, 21571 Jim_Obj *cmd, Jim_Obj *subcmd) 21572 { 21573 Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type, 21574 subcmd, subcmd_cmd_list(interp, command_table, ", ")); 21575 } 21576 21577 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc, 21578 Jim_Obj *const *argv) 21579 { 21580 Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s", 21581 argv[0], subcmd_cmd_list(interp, command_table, ", ")); 21582 } 21583 21584 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd) 21585 { 21586 if (cmd) { 21587 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL); 21588 } 21589 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL); 21590 if (ct->args && *ct->args) { 21591 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL); 21592 } 21593 } 21594 21595 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd) 21596 { 21597 Jim_SetResultString(interp, "wrong # args: should be \"", -1); 21598 add_cmd_usage(interp, ct, subcmd); 21599 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); 21600 } 21601 21602 static const Jim_ObjType subcmdLookupObjType = { 21603 "subcmd-lookup", 21604 NULL, 21605 NULL, 21606 NULL, 21607 JIM_TYPE_REFERENCES 21608 }; 21609 21610 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table, 21611 int argc, Jim_Obj *const *argv) 21612 { 21613 const jim_subcmd_type *ct; 21614 const jim_subcmd_type *partial = 0; 21615 int cmdlen; 21616 Jim_Obj *cmd; 21617 const char *cmdstr; 21618 int help = 0; 21619 int argsok = 1; 21620 21621 if (argc < 2) { 21622 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n" 21623 "Use \"%#s -help ?command?\" for help", argv[0], argv[0]); 21624 return 0; 21625 } 21626 21627 cmd = argv[1]; 21628 21629 21630 if (cmd->typePtr == &subcmdLookupObjType) { 21631 if (cmd->internalRep.ptrIntValue.ptr == command_table) { 21632 ct = command_table + cmd->internalRep.ptrIntValue.int1; 21633 goto found; 21634 } 21635 } 21636 21637 21638 if (Jim_CompareStringImmediate(interp, cmd, "-help")) { 21639 if (argc == 2) { 21640 21641 show_cmd_usage(interp, command_table, argc, argv); 21642 return &dummy_subcmd; 21643 } 21644 help = 1; 21645 21646 21647 cmd = argv[2]; 21648 } 21649 21650 21651 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) { 21652 Jim_SetResult(interp, subcmd_cmd_list(interp, command_table, " ")); 21653 return &dummy_subcmd; 21654 } 21655 21656 cmdstr = Jim_GetString(cmd, &cmdlen); 21657 21658 for (ct = command_table; ct->cmd; ct++) { 21659 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) { 21660 21661 break; 21662 } 21663 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) { 21664 if (partial) { 21665 21666 if (help) { 21667 21668 show_cmd_usage(interp, command_table, argc, argv); 21669 return &dummy_subcmd; 21670 } 21671 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]); 21672 return 0; 21673 } 21674 partial = ct; 21675 } 21676 continue; 21677 } 21678 21679 21680 if (partial && !ct->cmd) { 21681 ct = partial; 21682 } 21683 21684 if (!ct->cmd) { 21685 21686 if (help) { 21687 21688 show_cmd_usage(interp, command_table, argc, argv); 21689 return &dummy_subcmd; 21690 } 21691 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]); 21692 return 0; 21693 } 21694 21695 if (help) { 21696 Jim_SetResultString(interp, "Usage: ", -1); 21697 21698 add_cmd_usage(interp, ct, argv[0]); 21699 return &dummy_subcmd; 21700 } 21701 21702 21703 Jim_FreeIntRep(interp, cmd); 21704 cmd->typePtr = &subcmdLookupObjType; 21705 cmd->internalRep.ptrIntValue.ptr = (void *)command_table; 21706 cmd->internalRep.ptrIntValue.int1 = ct - command_table; 21707 21708 found: 21709 21710 21711 if (argc - 2 < ct->minargs) { 21712 argsok = 0; 21713 } 21714 else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) { 21715 argsok = 0; 21716 } 21717 else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) { 21718 21719 argsok = 0; 21720 } 21721 if (!argsok) { 21722 Jim_SetResultString(interp, "wrong # args: should be \"", -1); 21723 21724 add_cmd_usage(interp, ct, argv[0]); 21725 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); 21726 21727 return 0; 21728 } 21729 21730 21731 return ct; 21732 } 21733 21734 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv) 21735 { 21736 int ret = JIM_ERR; 21737 21738 if (ct) { 21739 if (ct->flags & JIM_MODFLAG_FULLARGV) { 21740 ret = ct->function(interp, argc, argv); 21741 } 21742 else { 21743 ret = ct->function(interp, argc - 2, argv + 2); 21744 } 21745 if (ret < 0) { 21746 Jim_SubCmdArgError(interp, ct, argv[0]); 21747 ret = JIM_ERR; 21748 } 21749 } 21750 return ret; 21751 } 21752 21753 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21754 { 21755 const jim_subcmd_type *ct = 21756 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv); 21757 21758 return Jim_CallSubCmd(interp, ct, argc, argv); 21759 } 21760 21761 #include <ctype.h> 21762 #include <stdlib.h> 21763 #include <string.h> 21764 #include <stdio.h> 21765 #include <assert.h> 21766 21767 21768 int utf8_fromunicode(char *p, unsigned uc) 21769 { 21770 if (uc <= 0x7f) { 21771 *p = uc; 21772 return 1; 21773 } 21774 else if (uc <= 0x7ff) { 21775 *p++ = 0xc0 | ((uc & 0x7c0) >> 6); 21776 *p = 0x80 | (uc & 0x3f); 21777 return 2; 21778 } 21779 else if (uc <= 0xffff) { 21780 *p++ = 0xe0 | ((uc & 0xf000) >> 12); 21781 *p++ = 0x80 | ((uc & 0xfc0) >> 6); 21782 *p = 0x80 | (uc & 0x3f); 21783 return 3; 21784 } 21785 21786 else { 21787 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18); 21788 *p++ = 0x80 | ((uc & 0x3f000) >> 12); 21789 *p++ = 0x80 | ((uc & 0xfc0) >> 6); 21790 *p = 0x80 | (uc & 0x3f); 21791 return 4; 21792 } 21793 } 21794 21795 #include <ctype.h> 21796 #include <string.h> 21797 #include <stdio.h> 21798 21799 21800 #define JIM_INTEGER_SPACE 24 21801 #define MAX_FLOAT_WIDTH 320 21802 21803 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv) 21804 { 21805 const char *span, *format, *formatEnd, *msg; 21806 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0; 21807 static const char * const mixedXPG = 21808 "cannot mix \"%\" and \"%n$\" conversion specifiers"; 21809 static const char * const badIndex[2] = { 21810 "not enough arguments for all format specifiers", 21811 "\"%n$\" argument index out of range" 21812 }; 21813 int formatLen; 21814 Jim_Obj *resultPtr; 21815 21816 char *num_buffer = NULL; 21817 int num_buffer_size = 0; 21818 21819 span = format = Jim_GetString(fmtObjPtr, &formatLen); 21820 formatEnd = format + formatLen; 21821 resultPtr = Jim_NewEmptyStringObj(interp); 21822 21823 while (format != formatEnd) { 21824 char *end; 21825 int gotMinus, sawFlag; 21826 int gotPrecision, useShort; 21827 long width, precision; 21828 int newXpg; 21829 int ch; 21830 int step; 21831 int doubleType; 21832 char pad = ' '; 21833 char spec[2*JIM_INTEGER_SPACE + 12]; 21834 char *p; 21835 21836 int formatted_chars; 21837 int formatted_bytes; 21838 const char *formatted_buf; 21839 21840 step = utf8_tounicode(format, &ch); 21841 format += step; 21842 if (ch != '%') { 21843 numBytes += step; 21844 continue; 21845 } 21846 if (numBytes) { 21847 Jim_AppendString(interp, resultPtr, span, numBytes); 21848 numBytes = 0; 21849 } 21850 21851 21852 step = utf8_tounicode(format, &ch); 21853 if (ch == '%') { 21854 span = format; 21855 numBytes = step; 21856 format += step; 21857 continue; 21858 } 21859 21860 21861 newXpg = 0; 21862 if (isdigit(ch)) { 21863 int position = strtoul(format, &end, 10); 21864 if (*end == '$') { 21865 newXpg = 1; 21866 objIndex = position - 1; 21867 format = end + 1; 21868 step = utf8_tounicode(format, &ch); 21869 } 21870 } 21871 if (newXpg) { 21872 if (gotSequential) { 21873 msg = mixedXPG; 21874 goto errorMsg; 21875 } 21876 gotXpg = 1; 21877 } else { 21878 if (gotXpg) { 21879 msg = mixedXPG; 21880 goto errorMsg; 21881 } 21882 gotSequential = 1; 21883 } 21884 if ((objIndex < 0) || (objIndex >= objc)) { 21885 msg = badIndex[gotXpg]; 21886 goto errorMsg; 21887 } 21888 21889 p = spec; 21890 *p++ = '%'; 21891 21892 gotMinus = 0; 21893 sawFlag = 1; 21894 do { 21895 switch (ch) { 21896 case '-': 21897 gotMinus = 1; 21898 break; 21899 case '0': 21900 pad = ch; 21901 break; 21902 case ' ': 21903 case '+': 21904 case '#': 21905 break; 21906 default: 21907 sawFlag = 0; 21908 continue; 21909 } 21910 *p++ = ch; 21911 format += step; 21912 step = utf8_tounicode(format, &ch); 21913 21914 } while (sawFlag && (p - spec <= 5)); 21915 21916 21917 width = 0; 21918 if (isdigit(ch)) { 21919 width = strtoul(format, &end, 10); 21920 format = end; 21921 step = utf8_tounicode(format, &ch); 21922 } else if (ch == '*') { 21923 if (objIndex >= objc - 1) { 21924 msg = badIndex[gotXpg]; 21925 goto errorMsg; 21926 } 21927 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) { 21928 goto error; 21929 } 21930 if (width < 0) { 21931 width = -width; 21932 if (!gotMinus) { 21933 *p++ = '-'; 21934 gotMinus = 1; 21935 } 21936 } 21937 objIndex++; 21938 format += step; 21939 step = utf8_tounicode(format, &ch); 21940 } 21941 21942 21943 gotPrecision = precision = 0; 21944 if (ch == '.') { 21945 gotPrecision = 1; 21946 format += step; 21947 step = utf8_tounicode(format, &ch); 21948 } 21949 if (isdigit(ch)) { 21950 precision = strtoul(format, &end, 10); 21951 format = end; 21952 step = utf8_tounicode(format, &ch); 21953 } else if (ch == '*') { 21954 if (objIndex >= objc - 1) { 21955 msg = badIndex[gotXpg]; 21956 goto errorMsg; 21957 } 21958 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) { 21959 goto error; 21960 } 21961 21962 21963 if (precision < 0) { 21964 precision = 0; 21965 } 21966 objIndex++; 21967 format += step; 21968 step = utf8_tounicode(format, &ch); 21969 } 21970 21971 21972 useShort = 0; 21973 if (ch == 'h') { 21974 useShort = 1; 21975 format += step; 21976 step = utf8_tounicode(format, &ch); 21977 } else if (ch == 'l') { 21978 21979 format += step; 21980 step = utf8_tounicode(format, &ch); 21981 if (ch == 'l') { 21982 format += step; 21983 step = utf8_tounicode(format, &ch); 21984 } 21985 } 21986 21987 format += step; 21988 span = format; 21989 21990 21991 if (ch == 'i') { 21992 ch = 'd'; 21993 } 21994 21995 doubleType = 0; 21996 21997 switch (ch) { 21998 case '\0': 21999 msg = "format string ended in middle of field specifier"; 22000 goto errorMsg; 22001 case 's': { 22002 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes); 22003 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]); 22004 if (gotPrecision && (precision < formatted_chars)) { 22005 22006 formatted_chars = precision; 22007 formatted_bytes = utf8_index(formatted_buf, precision); 22008 } 22009 break; 22010 } 22011 case 'c': { 22012 jim_wide code; 22013 22014 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) { 22015 goto error; 22016 } 22017 22018 formatted_bytes = utf8_getchars(spec, code); 22019 formatted_buf = spec; 22020 formatted_chars = 1; 22021 break; 22022 } 22023 case 'b': { 22024 unsigned jim_wide w; 22025 int length; 22026 int i; 22027 int j; 22028 22029 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) { 22030 goto error; 22031 } 22032 length = sizeof(w) * 8; 22033 22034 22035 22036 if (num_buffer_size < length + 1) { 22037 num_buffer_size = length + 1; 22038 num_buffer = Jim_Realloc(num_buffer, num_buffer_size); 22039 } 22040 22041 j = 0; 22042 for (i = length; i > 0; ) { 22043 i--; 22044 if (w & ((unsigned jim_wide)1 << i)) { 22045 num_buffer[j++] = '1'; 22046 } 22047 else if (j || i == 0) { 22048 num_buffer[j++] = '0'; 22049 } 22050 } 22051 num_buffer[j] = 0; 22052 formatted_chars = formatted_bytes = j; 22053 formatted_buf = num_buffer; 22054 break; 22055 } 22056 22057 case 'e': 22058 case 'E': 22059 case 'f': 22060 case 'g': 22061 case 'G': 22062 doubleType = 1; 22063 22064 case 'd': 22065 case 'u': 22066 case 'o': 22067 case 'x': 22068 case 'X': { 22069 jim_wide w; 22070 double d; 22071 int length; 22072 22073 22074 if (width) { 22075 p += sprintf(p, "%ld", width); 22076 } 22077 if (gotPrecision) { 22078 p += sprintf(p, ".%ld", precision); 22079 } 22080 22081 22082 if (doubleType) { 22083 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) { 22084 goto error; 22085 } 22086 length = MAX_FLOAT_WIDTH; 22087 } 22088 else { 22089 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) { 22090 goto error; 22091 } 22092 length = JIM_INTEGER_SPACE; 22093 if (useShort) { 22094 if (ch == 'd') { 22095 w = (short)w; 22096 } 22097 else { 22098 w = (unsigned short)w; 22099 } 22100 } 22101 *p++ = 'l'; 22102 #ifdef HAVE_LONG_LONG 22103 if (sizeof(long long) == sizeof(jim_wide)) { 22104 *p++ = 'l'; 22105 } 22106 #endif 22107 } 22108 22109 *p++ = (char) ch; 22110 *p = '\0'; 22111 22112 22113 if (width > 10000 || length > 10000 || precision > 10000) { 22114 Jim_SetResultString(interp, "format too long", -1); 22115 goto error; 22116 } 22117 22118 22119 22120 if (width > length) { 22121 length = width; 22122 } 22123 if (gotPrecision) { 22124 length += precision; 22125 } 22126 22127 22128 if (num_buffer_size < length + 1) { 22129 num_buffer_size = length + 1; 22130 num_buffer = Jim_Realloc(num_buffer, num_buffer_size); 22131 } 22132 22133 if (doubleType) { 22134 snprintf(num_buffer, length + 1, spec, d); 22135 } 22136 else { 22137 formatted_bytes = snprintf(num_buffer, length + 1, spec, w); 22138 } 22139 formatted_chars = formatted_bytes = strlen(num_buffer); 22140 formatted_buf = num_buffer; 22141 break; 22142 } 22143 22144 default: { 22145 22146 spec[0] = ch; 22147 spec[1] = '\0'; 22148 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec); 22149 goto error; 22150 } 22151 } 22152 22153 if (!gotMinus) { 22154 while (formatted_chars < width) { 22155 Jim_AppendString(interp, resultPtr, &pad, 1); 22156 formatted_chars++; 22157 } 22158 } 22159 22160 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes); 22161 22162 while (formatted_chars < width) { 22163 Jim_AppendString(interp, resultPtr, &pad, 1); 22164 formatted_chars++; 22165 } 22166 22167 objIndex += gotSequential; 22168 } 22169 if (numBytes) { 22170 Jim_AppendString(interp, resultPtr, span, numBytes); 22171 } 22172 22173 Jim_Free(num_buffer); 22174 return resultPtr; 22175 22176 errorMsg: 22177 Jim_SetResultString(interp, msg, -1); 22178 error: 22179 Jim_FreeNewObj(interp, resultPtr); 22180 Jim_Free(num_buffer); 22181 return NULL; 22182 } 22183 22184 22185 #if defined(JIM_REGEXP) 22186 #include <stdio.h> 22187 #include <ctype.h> 22188 #include <stdlib.h> 22189 #include <string.h> 22190 22191 22192 22193 #define REG_MAX_PAREN 100 22194 22195 22196 22197 #define END 0 22198 #define BOL 1 22199 #define EOL 2 22200 #define ANY 3 22201 #define ANYOF 4 22202 #define ANYBUT 5 22203 #define BRANCH 6 22204 #define BACK 7 22205 #define EXACTLY 8 22206 #define NOTHING 9 22207 #define REP 10 22208 #define REPMIN 11 22209 #define REPX 12 22210 #define REPXMIN 13 22211 #define BOLX 14 22212 #define EOLX 15 22213 #define WORDA 16 22214 #define WORDZ 17 22215 22216 #define OPENNC 1000 22217 #define OPEN 1001 22218 22219 22220 22221 22222 #define CLOSENC 2000 22223 #define CLOSE 2001 22224 #define CLOSE_END (CLOSE+REG_MAX_PAREN) 22225 22226 #define REG_MAGIC 0xFADED00D 22227 22228 22229 #define OP(preg, p) (preg->program[p]) 22230 #define NEXT(preg, p) (preg->program[p + 1]) 22231 #define OPERAND(p) ((p) + 2) 22232 22233 22234 22235 22236 #define FAIL(R,M) { (R)->err = (M); return (M); } 22237 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{') 22238 #define META "^$.[()|?{+*" 22239 22240 #define HASWIDTH 1 22241 #define SIMPLE 2 22242 #define SPSTART 4 22243 #define WORST 0 22244 22245 #define MAX_REP_COUNT 1000000 22246 22247 static int reg(regex_t *preg, int paren, int *flagp ); 22248 static int regpiece(regex_t *preg, int *flagp ); 22249 static int regbranch(regex_t *preg, int *flagp ); 22250 static int regatom(regex_t *preg, int *flagp ); 22251 static int regnode(regex_t *preg, int op ); 22252 static int regnext(regex_t *preg, int p ); 22253 static void regc(regex_t *preg, int b ); 22254 static int reginsert(regex_t *preg, int op, int size, int opnd ); 22255 static void regtail(regex_t *preg, int p, int val); 22256 static void regoptail(regex_t *preg, int p, int val ); 22257 static int regopsize(regex_t *preg, int p ); 22258 22259 static int reg_range_find(const int *string, int c); 22260 static const char *str_find(const char *string, int c, int nocase); 22261 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase); 22262 22263 22264 #ifdef DEBUG 22265 static int regnarrate = 0; 22266 static void regdump(regex_t *preg); 22267 static const char *regprop( int op ); 22268 #endif 22269 22270 22271 static int str_int_len(const int *seq) 22272 { 22273 int n = 0; 22274 while (*seq++) { 22275 n++; 22276 } 22277 return n; 22278 } 22279 22280 int jim_regcomp(regex_t *preg, const char *exp, int cflags) 22281 { 22282 int scan; 22283 int longest; 22284 unsigned len; 22285 int flags; 22286 22287 #ifdef DEBUG 22288 fprintf(stderr, "Compiling: '%s'\n", exp); 22289 #endif 22290 memset(preg, 0, sizeof(*preg)); 22291 22292 if (exp == NULL) 22293 FAIL(preg, REG_ERR_NULL_ARGUMENT); 22294 22295 22296 preg->cflags = cflags; 22297 preg->regparse = exp; 22298 22299 22300 preg->proglen = (strlen(exp) + 1) * 5; 22301 preg->program = malloc(preg->proglen * sizeof(int)); 22302 if (preg->program == NULL) 22303 FAIL(preg, REG_ERR_NOMEM); 22304 22305 regc(preg, REG_MAGIC); 22306 if (reg(preg, 0, &flags) == 0) { 22307 return preg->err; 22308 } 22309 22310 22311 if (preg->re_nsub >= REG_MAX_PAREN) 22312 FAIL(preg,REG_ERR_TOO_BIG); 22313 22314 22315 preg->regstart = 0; 22316 preg->reganch = 0; 22317 preg->regmust = 0; 22318 preg->regmlen = 0; 22319 scan = 1; 22320 if (OP(preg, regnext(preg, scan)) == END) { 22321 scan = OPERAND(scan); 22322 22323 22324 if (OP(preg, scan) == EXACTLY) { 22325 preg->regstart = preg->program[OPERAND(scan)]; 22326 } 22327 else if (OP(preg, scan) == BOL) 22328 preg->reganch++; 22329 22330 if (flags&SPSTART) { 22331 longest = 0; 22332 len = 0; 22333 for (; scan != 0; scan = regnext(preg, scan)) { 22334 if (OP(preg, scan) == EXACTLY) { 22335 int plen = str_int_len(preg->program + OPERAND(scan)); 22336 if (plen >= len) { 22337 longest = OPERAND(scan); 22338 len = plen; 22339 } 22340 } 22341 } 22342 preg->regmust = longest; 22343 preg->regmlen = len; 22344 } 22345 } 22346 22347 #ifdef DEBUG 22348 regdump(preg); 22349 #endif 22350 22351 return 0; 22352 } 22353 22354 static int reg(regex_t *preg, int paren, int *flagp ) 22355 { 22356 int ret; 22357 int br; 22358 int ender; 22359 int parno = 0; 22360 int flags; 22361 22362 *flagp = HASWIDTH; 22363 22364 22365 if (paren) { 22366 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') { 22367 22368 preg->regparse += 2; 22369 parno = -1; 22370 } 22371 else { 22372 parno = ++preg->re_nsub; 22373 } 22374 ret = regnode(preg, OPEN+parno); 22375 } else 22376 ret = 0; 22377 22378 22379 br = regbranch(preg, &flags); 22380 if (br == 0) 22381 return 0; 22382 if (ret != 0) 22383 regtail(preg, ret, br); 22384 else 22385 ret = br; 22386 if (!(flags&HASWIDTH)) 22387 *flagp &= ~HASWIDTH; 22388 *flagp |= flags&SPSTART; 22389 while (*preg->regparse == '|') { 22390 preg->regparse++; 22391 br = regbranch(preg, &flags); 22392 if (br == 0) 22393 return 0; 22394 regtail(preg, ret, br); 22395 if (!(flags&HASWIDTH)) 22396 *flagp &= ~HASWIDTH; 22397 *flagp |= flags&SPSTART; 22398 } 22399 22400 22401 ender = regnode(preg, (paren) ? CLOSE+parno : END); 22402 regtail(preg, ret, ender); 22403 22404 22405 for (br = ret; br != 0; br = regnext(preg, br)) 22406 regoptail(preg, br, ender); 22407 22408 22409 if (paren && *preg->regparse++ != ')') { 22410 preg->err = REG_ERR_UNMATCHED_PAREN; 22411 return 0; 22412 } else if (!paren && *preg->regparse != '\0') { 22413 if (*preg->regparse == ')') { 22414 preg->err = REG_ERR_UNMATCHED_PAREN; 22415 return 0; 22416 } else { 22417 preg->err = REG_ERR_JUNK_ON_END; 22418 return 0; 22419 } 22420 } 22421 22422 return(ret); 22423 } 22424 22425 static int regbranch(regex_t *preg, int *flagp ) 22426 { 22427 int ret; 22428 int chain; 22429 int latest; 22430 int flags; 22431 22432 *flagp = WORST; 22433 22434 ret = regnode(preg, BRANCH); 22435 chain = 0; 22436 while (*preg->regparse != '\0' && *preg->regparse != ')' && 22437 *preg->regparse != '|') { 22438 latest = regpiece(preg, &flags); 22439 if (latest == 0) 22440 return 0; 22441 *flagp |= flags&HASWIDTH; 22442 if (chain == 0) { 22443 *flagp |= flags&SPSTART; 22444 } 22445 else { 22446 regtail(preg, chain, latest); 22447 } 22448 chain = latest; 22449 } 22450 if (chain == 0) 22451 (void) regnode(preg, NOTHING); 22452 22453 return(ret); 22454 } 22455 22456 static int regpiece(regex_t *preg, int *flagp) 22457 { 22458 int ret; 22459 char op; 22460 int next; 22461 int flags; 22462 int min; 22463 int max; 22464 22465 ret = regatom(preg, &flags); 22466 if (ret == 0) 22467 return 0; 22468 22469 op = *preg->regparse; 22470 if (!ISMULT(op)) { 22471 *flagp = flags; 22472 return(ret); 22473 } 22474 22475 if (!(flags&HASWIDTH) && op != '?') { 22476 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY; 22477 return 0; 22478 } 22479 22480 22481 if (op == '{') { 22482 char *end; 22483 22484 min = strtoul(preg->regparse + 1, &end, 10); 22485 if (end == preg->regparse + 1) { 22486 preg->err = REG_ERR_BAD_COUNT; 22487 return 0; 22488 } 22489 if (*end == '}') { 22490 max = min; 22491 } 22492 else if (*end == '\0') { 22493 preg->err = REG_ERR_UNMATCHED_BRACES; 22494 return 0; 22495 } 22496 else { 22497 preg->regparse = end; 22498 max = strtoul(preg->regparse + 1, &end, 10); 22499 if (*end != '}') { 22500 preg->err = REG_ERR_UNMATCHED_BRACES; 22501 return 0; 22502 } 22503 } 22504 if (end == preg->regparse + 1) { 22505 max = MAX_REP_COUNT; 22506 } 22507 else if (max < min || max >= 100) { 22508 preg->err = REG_ERR_BAD_COUNT; 22509 return 0; 22510 } 22511 if (min >= 100) { 22512 preg->err = REG_ERR_BAD_COUNT; 22513 return 0; 22514 } 22515 22516 preg->regparse = strchr(preg->regparse, '}'); 22517 } 22518 else { 22519 min = (op == '+'); 22520 max = (op == '?' ? 1 : MAX_REP_COUNT); 22521 } 22522 22523 if (preg->regparse[1] == '?') { 22524 preg->regparse++; 22525 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret); 22526 } 22527 else { 22528 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret); 22529 } 22530 preg->program[ret + 2] = max; 22531 preg->program[ret + 3] = min; 22532 preg->program[ret + 4] = 0; 22533 22534 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART); 22535 22536 if (!(flags & SIMPLE)) { 22537 int back = regnode(preg, BACK); 22538 regtail(preg, back, ret); 22539 regtail(preg, next, back); 22540 } 22541 22542 preg->regparse++; 22543 if (ISMULT(*preg->regparse)) { 22544 preg->err = REG_ERR_NESTED_COUNT; 22545 return 0; 22546 } 22547 22548 return ret; 22549 } 22550 22551 static void reg_addrange(regex_t *preg, int lower, int upper) 22552 { 22553 if (lower > upper) { 22554 reg_addrange(preg, upper, lower); 22555 } 22556 22557 regc(preg, upper - lower + 1); 22558 regc(preg, lower); 22559 } 22560 22561 static void reg_addrange_str(regex_t *preg, const char *str) 22562 { 22563 while (*str) { 22564 reg_addrange(preg, *str, *str); 22565 str++; 22566 } 22567 } 22568 22569 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper) 22570 { 22571 int l = utf8_tounicode(s, uc); 22572 if (upper) { 22573 *uc = utf8_upper(*uc); 22574 } 22575 return l; 22576 } 22577 22578 static int hexdigitval(int c) 22579 { 22580 if (c >= '0' && c <= '9') 22581 return c - '0'; 22582 if (c >= 'a' && c <= 'f') 22583 return c - 'a' + 10; 22584 if (c >= 'A' && c <= 'F') 22585 return c - 'A' + 10; 22586 return -1; 22587 } 22588 22589 static int parse_hex(const char *s, int n, int *uc) 22590 { 22591 int val = 0; 22592 int k; 22593 22594 for (k = 0; k < n; k++) { 22595 int c = hexdigitval(*s++); 22596 if (c == -1) { 22597 break; 22598 } 22599 val = (val << 4) | c; 22600 } 22601 if (k) { 22602 *uc = val; 22603 } 22604 return k; 22605 } 22606 22607 static int reg_decode_escape(const char *s, int *ch) 22608 { 22609 int n; 22610 const char *s0 = s; 22611 22612 *ch = *s++; 22613 22614 switch (*ch) { 22615 case 'b': *ch = '\b'; break; 22616 case 'e': *ch = 27; break; 22617 case 'f': *ch = '\f'; break; 22618 case 'n': *ch = '\n'; break; 22619 case 'r': *ch = '\r'; break; 22620 case 't': *ch = '\t'; break; 22621 case 'v': *ch = '\v'; break; 22622 case 'u': 22623 if (*s == '{') { 22624 22625 n = parse_hex(s + 1, 6, ch); 22626 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) { 22627 s += n + 2; 22628 } 22629 else { 22630 22631 *ch = 'u'; 22632 } 22633 } 22634 else if ((n = parse_hex(s, 4, ch)) > 0) { 22635 s += n; 22636 } 22637 break; 22638 case 'U': 22639 if ((n = parse_hex(s, 8, ch)) > 0) { 22640 s += n; 22641 } 22642 break; 22643 case 'x': 22644 if ((n = parse_hex(s, 2, ch)) > 0) { 22645 s += n; 22646 } 22647 break; 22648 case '\0': 22649 s--; 22650 *ch = '\\'; 22651 break; 22652 } 22653 return s - s0; 22654 } 22655 22656 static int regatom(regex_t *preg, int *flagp) 22657 { 22658 int ret; 22659 int flags; 22660 int nocase = (preg->cflags & REG_ICASE); 22661 22662 int ch; 22663 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase); 22664 22665 *flagp = WORST; 22666 22667 preg->regparse += n; 22668 switch (ch) { 22669 22670 case '^': 22671 ret = regnode(preg, BOL); 22672 break; 22673 case '$': 22674 ret = regnode(preg, EOL); 22675 break; 22676 case '.': 22677 ret = regnode(preg, ANY); 22678 *flagp |= HASWIDTH|SIMPLE; 22679 break; 22680 case '[': { 22681 const char *pattern = preg->regparse; 22682 22683 if (*pattern == '^') { 22684 ret = regnode(preg, ANYBUT); 22685 pattern++; 22686 } else 22687 ret = regnode(preg, ANYOF); 22688 22689 22690 if (*pattern == ']' || *pattern == '-') { 22691 reg_addrange(preg, *pattern, *pattern); 22692 pattern++; 22693 } 22694 22695 while (*pattern != ']') { 22696 22697 int start; 22698 int end; 22699 22700 enum { 22701 CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER, 22702 CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT, 22703 CC_NUM 22704 }; 22705 int cc; 22706 22707 if (!*pattern) { 22708 preg->err = REG_ERR_UNMATCHED_BRACKET; 22709 return 0; 22710 } 22711 22712 pattern += reg_utf8_tounicode_case(pattern, &start, nocase); 22713 if (start == '\\') { 22714 22715 switch (*pattern) { 22716 case 's': 22717 pattern++; 22718 cc = CC_SPACE; 22719 goto cc_switch; 22720 case 'd': 22721 pattern++; 22722 cc = CC_DIGIT; 22723 goto cc_switch; 22724 case 'w': 22725 pattern++; 22726 reg_addrange(preg, '_', '_'); 22727 cc = CC_ALNUM; 22728 goto cc_switch; 22729 } 22730 pattern += reg_decode_escape(pattern, &start); 22731 if (start == 0) { 22732 preg->err = REG_ERR_NULL_CHAR; 22733 return 0; 22734 } 22735 if (start == '\\' && *pattern == 0) { 22736 preg->err = REG_ERR_INVALID_ESCAPE; 22737 return 0; 22738 } 22739 } 22740 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') { 22741 22742 pattern += utf8_tounicode(pattern, &end); 22743 pattern += reg_utf8_tounicode_case(pattern, &end, nocase); 22744 if (end == '\\') { 22745 pattern += reg_decode_escape(pattern, &end); 22746 if (end == 0) { 22747 preg->err = REG_ERR_NULL_CHAR; 22748 return 0; 22749 } 22750 if (end == '\\' && *pattern == 0) { 22751 preg->err = REG_ERR_INVALID_ESCAPE; 22752 return 0; 22753 } 22754 } 22755 22756 reg_addrange(preg, start, end); 22757 continue; 22758 } 22759 if (start == '[' && pattern[0] == ':') { 22760 static const char *character_class[] = { 22761 ":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:", 22762 ":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:", 22763 }; 22764 22765 for (cc = 0; cc < CC_NUM; cc++) { 22766 n = strlen(character_class[cc]); 22767 if (strncmp(pattern, character_class[cc], n) == 0) { 22768 if (pattern[n] != ']') { 22769 preg->err = REG_ERR_UNMATCHED_BRACKET; 22770 return 0; 22771 } 22772 22773 pattern += n + 1; 22774 break; 22775 } 22776 } 22777 if (cc != CC_NUM) { 22778 cc_switch: 22779 switch (cc) { 22780 case CC_ALNUM: 22781 reg_addrange(preg, '0', '9'); 22782 22783 case CC_ALPHA: 22784 if ((preg->cflags & REG_ICASE) == 0) { 22785 reg_addrange(preg, 'a', 'z'); 22786 } 22787 reg_addrange(preg, 'A', 'Z'); 22788 break; 22789 case CC_SPACE: 22790 reg_addrange_str(preg, " \t\r\n\f\v"); 22791 break; 22792 case CC_BLANK: 22793 reg_addrange_str(preg, " \t"); 22794 break; 22795 case CC_UPPER: 22796 reg_addrange(preg, 'A', 'Z'); 22797 break; 22798 case CC_LOWER: 22799 reg_addrange(preg, 'a', 'z'); 22800 break; 22801 case CC_XDIGIT: 22802 reg_addrange(preg, 'a', 'f'); 22803 reg_addrange(preg, 'A', 'F'); 22804 22805 case CC_DIGIT: 22806 reg_addrange(preg, '0', '9'); 22807 break; 22808 case CC_CNTRL: 22809 reg_addrange(preg, 0, 31); 22810 reg_addrange(preg, 127, 127); 22811 break; 22812 case CC_PRINT: 22813 reg_addrange(preg, ' ', '~'); 22814 break; 22815 case CC_GRAPH: 22816 reg_addrange(preg, '!', '~'); 22817 break; 22818 case CC_PUNCT: 22819 reg_addrange(preg, '!', '/'); 22820 reg_addrange(preg, ':', '@'); 22821 reg_addrange(preg, '[', '`'); 22822 reg_addrange(preg, '{', '~'); 22823 break; 22824 } 22825 continue; 22826 } 22827 } 22828 22829 reg_addrange(preg, start, start); 22830 } 22831 regc(preg, '\0'); 22832 22833 if (*pattern) { 22834 pattern++; 22835 } 22836 preg->regparse = pattern; 22837 22838 *flagp |= HASWIDTH|SIMPLE; 22839 } 22840 break; 22841 case '(': 22842 ret = reg(preg, 1, &flags); 22843 if (ret == 0) 22844 return 0; 22845 *flagp |= flags&(HASWIDTH|SPSTART); 22846 break; 22847 case '\0': 22848 case '|': 22849 case ')': 22850 preg->err = REG_ERR_INTERNAL; 22851 return 0; 22852 case '?': 22853 case '+': 22854 case '*': 22855 case '{': 22856 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING; 22857 return 0; 22858 case '\\': 22859 ch = *preg->regparse++; 22860 switch (ch) { 22861 case '\0': 22862 preg->err = REG_ERR_INVALID_ESCAPE; 22863 return 0; 22864 case 'A': 22865 ret = regnode(preg, BOLX); 22866 break; 22867 case 'Z': 22868 ret = regnode(preg, EOLX); 22869 break; 22870 case '<': 22871 case 'm': 22872 ret = regnode(preg, WORDA); 22873 break; 22874 case '>': 22875 case 'M': 22876 ret = regnode(preg, WORDZ); 22877 break; 22878 case 'd': 22879 case 'D': 22880 ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT); 22881 reg_addrange(preg, '0', '9'); 22882 regc(preg, '\0'); 22883 *flagp |= HASWIDTH|SIMPLE; 22884 break; 22885 case 'w': 22886 case 'W': 22887 ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT); 22888 if ((preg->cflags & REG_ICASE) == 0) { 22889 reg_addrange(preg, 'a', 'z'); 22890 } 22891 reg_addrange(preg, 'A', 'Z'); 22892 reg_addrange(preg, '0', '9'); 22893 reg_addrange(preg, '_', '_'); 22894 regc(preg, '\0'); 22895 *flagp |= HASWIDTH|SIMPLE; 22896 break; 22897 case 's': 22898 case 'S': 22899 ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT); 22900 reg_addrange_str(preg," \t\r\n\f\v"); 22901 regc(preg, '\0'); 22902 *flagp |= HASWIDTH|SIMPLE; 22903 break; 22904 22905 default: 22906 22907 22908 preg->regparse--; 22909 goto de_fault; 22910 } 22911 break; 22912 de_fault: 22913 default: { 22914 int added = 0; 22915 22916 22917 preg->regparse -= n; 22918 22919 ret = regnode(preg, EXACTLY); 22920 22921 22922 22923 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) { 22924 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE)); 22925 if (ch == '\\' && preg->regparse[n]) { 22926 if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) { 22927 22928 break; 22929 } 22930 n += reg_decode_escape(preg->regparse + n, &ch); 22931 if (ch == 0) { 22932 preg->err = REG_ERR_NULL_CHAR; 22933 return 0; 22934 } 22935 } 22936 22937 22938 if (ISMULT(preg->regparse[n])) { 22939 22940 if (added) { 22941 22942 break; 22943 } 22944 22945 regc(preg, ch); 22946 added++; 22947 preg->regparse += n; 22948 break; 22949 } 22950 22951 22952 regc(preg, ch); 22953 added++; 22954 preg->regparse += n; 22955 } 22956 regc(preg, '\0'); 22957 22958 *flagp |= HASWIDTH; 22959 if (added == 1) 22960 *flagp |= SIMPLE; 22961 break; 22962 } 22963 break; 22964 } 22965 22966 return(ret); 22967 } 22968 22969 static void reg_grow(regex_t *preg, int n) 22970 { 22971 if (preg->p + n >= preg->proglen) { 22972 preg->proglen = (preg->p + n) * 2; 22973 preg->program = realloc(preg->program, preg->proglen * sizeof(int)); 22974 } 22975 } 22976 22977 22978 static int regnode(regex_t *preg, int op) 22979 { 22980 reg_grow(preg, 2); 22981 22982 22983 preg->program[preg->p++] = op; 22984 preg->program[preg->p++] = 0; 22985 22986 22987 return preg->p - 2; 22988 } 22989 22990 static void regc(regex_t *preg, int b ) 22991 { 22992 reg_grow(preg, 1); 22993 preg->program[preg->p++] = b; 22994 } 22995 22996 static int reginsert(regex_t *preg, int op, int size, int opnd ) 22997 { 22998 reg_grow(preg, size); 22999 23000 23001 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd)); 23002 23003 memset(preg->program + opnd, 0, sizeof(int) * size); 23004 23005 preg->program[opnd] = op; 23006 23007 preg->p += size; 23008 23009 return opnd + size; 23010 } 23011 23012 static void regtail(regex_t *preg, int p, int val) 23013 { 23014 int scan; 23015 int temp; 23016 int offset; 23017 23018 23019 scan = p; 23020 for (;;) { 23021 temp = regnext(preg, scan); 23022 if (temp == 0) 23023 break; 23024 scan = temp; 23025 } 23026 23027 if (OP(preg, scan) == BACK) 23028 offset = scan - val; 23029 else 23030 offset = val - scan; 23031 23032 preg->program[scan + 1] = offset; 23033 } 23034 23035 23036 static void regoptail(regex_t *preg, int p, int val ) 23037 { 23038 23039 if (p != 0 && OP(preg, p) == BRANCH) { 23040 regtail(preg, OPERAND(p), val); 23041 } 23042 } 23043 23044 23045 static int regtry(regex_t *preg, const char *string ); 23046 static int regmatch(regex_t *preg, int prog); 23047 static int regrepeat(regex_t *preg, int p, int max); 23048 23049 int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) 23050 { 23051 const char *s; 23052 int scan; 23053 23054 23055 if (preg == NULL || preg->program == NULL || string == NULL) { 23056 return REG_ERR_NULL_ARGUMENT; 23057 } 23058 23059 23060 if (*preg->program != REG_MAGIC) { 23061 return REG_ERR_CORRUPTED; 23062 } 23063 23064 #ifdef DEBUG 23065 fprintf(stderr, "regexec: %s\n", string); 23066 regdump(preg); 23067 #endif 23068 23069 preg->eflags = eflags; 23070 preg->pmatch = pmatch; 23071 preg->nmatch = nmatch; 23072 preg->start = string; 23073 23074 23075 for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) { 23076 int op = OP(preg, scan); 23077 if (op == END) 23078 break; 23079 if (op == REPX || op == REPXMIN) 23080 preg->program[scan + 4] = 0; 23081 } 23082 23083 23084 if (preg->regmust != 0) { 23085 s = string; 23086 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) { 23087 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) { 23088 break; 23089 } 23090 s++; 23091 } 23092 if (s == NULL) 23093 return REG_NOMATCH; 23094 } 23095 23096 23097 preg->regbol = string; 23098 23099 23100 if (preg->reganch) { 23101 if (eflags & REG_NOTBOL) { 23102 23103 goto nextline; 23104 } 23105 while (1) { 23106 if (regtry(preg, string)) { 23107 return REG_NOERROR; 23108 } 23109 if (*string) { 23110 nextline: 23111 if (preg->cflags & REG_NEWLINE) { 23112 23113 string = strchr(string, '\n'); 23114 if (string) { 23115 preg->regbol = ++string; 23116 continue; 23117 } 23118 } 23119 } 23120 return REG_NOMATCH; 23121 } 23122 } 23123 23124 23125 s = string; 23126 if (preg->regstart != '\0') { 23127 23128 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) { 23129 if (regtry(preg, s)) 23130 return REG_NOERROR; 23131 s++; 23132 } 23133 } 23134 else 23135 23136 while (1) { 23137 if (regtry(preg, s)) 23138 return REG_NOERROR; 23139 if (*s == '\0') { 23140 break; 23141 } 23142 else { 23143 int c; 23144 s += utf8_tounicode(s, &c); 23145 } 23146 } 23147 23148 23149 return REG_NOMATCH; 23150 } 23151 23152 23153 static int regtry( regex_t *preg, const char *string ) 23154 { 23155 int i; 23156 23157 preg->reginput = string; 23158 23159 for (i = 0; i < preg->nmatch; i++) { 23160 preg->pmatch[i].rm_so = -1; 23161 preg->pmatch[i].rm_eo = -1; 23162 } 23163 if (regmatch(preg, 1)) { 23164 preg->pmatch[0].rm_so = string - preg->start; 23165 preg->pmatch[0].rm_eo = preg->reginput - preg->start; 23166 return(1); 23167 } else 23168 return(0); 23169 } 23170 23171 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase) 23172 { 23173 const char *s = string; 23174 while (proglen && *s) { 23175 int ch; 23176 int n = reg_utf8_tounicode_case(s, &ch, nocase); 23177 if (ch != *prog) { 23178 return -1; 23179 } 23180 prog++; 23181 s += n; 23182 proglen--; 23183 } 23184 if (proglen == 0) { 23185 return s - string; 23186 } 23187 return -1; 23188 } 23189 23190 static int reg_range_find(const int *range, int c) 23191 { 23192 while (*range) { 23193 23194 if (c >= range[1] && c <= (range[0] + range[1] - 1)) { 23195 return 1; 23196 } 23197 range += 2; 23198 } 23199 return 0; 23200 } 23201 23202 static const char *str_find(const char *string, int c, int nocase) 23203 { 23204 if (nocase) { 23205 23206 c = utf8_upper(c); 23207 } 23208 while (*string) { 23209 int ch; 23210 int n = reg_utf8_tounicode_case(string, &ch, nocase); 23211 if (c == ch) { 23212 return string; 23213 } 23214 string += n; 23215 } 23216 return NULL; 23217 } 23218 23219 static int reg_iseol(regex_t *preg, int ch) 23220 { 23221 if (preg->cflags & REG_NEWLINE) { 23222 return ch == '\0' || ch == '\n'; 23223 } 23224 else { 23225 return ch == '\0'; 23226 } 23227 } 23228 23229 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin) 23230 { 23231 int nextch = '\0'; 23232 const char *save; 23233 int no; 23234 int c; 23235 23236 int max = preg->program[scan + 2]; 23237 int min = preg->program[scan + 3]; 23238 int next = regnext(preg, scan); 23239 23240 if (OP(preg, next) == EXACTLY) { 23241 nextch = preg->program[OPERAND(next)]; 23242 } 23243 save = preg->reginput; 23244 no = regrepeat(preg, scan + 5, max); 23245 if (no < min) { 23246 return 0; 23247 } 23248 if (matchmin) { 23249 23250 max = no; 23251 no = min; 23252 } 23253 23254 while (1) { 23255 if (matchmin) { 23256 if (no > max) { 23257 break; 23258 } 23259 } 23260 else { 23261 if (no < min) { 23262 break; 23263 } 23264 } 23265 preg->reginput = save + utf8_index(save, no); 23266 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); 23267 23268 if (reg_iseol(preg, nextch) || c == nextch) { 23269 if (regmatch(preg, next)) { 23270 return(1); 23271 } 23272 } 23273 if (matchmin) { 23274 23275 no++; 23276 } 23277 else { 23278 23279 no--; 23280 } 23281 } 23282 return(0); 23283 } 23284 23285 static int regmatchrepeat(regex_t *preg, int scan, int matchmin) 23286 { 23287 int *scanpt = preg->program + scan; 23288 23289 int max = scanpt[2]; 23290 int min = scanpt[3]; 23291 23292 23293 if (scanpt[4] < min) { 23294 23295 scanpt[4]++; 23296 if (regmatch(preg, scan + 5)) { 23297 return 1; 23298 } 23299 scanpt[4]--; 23300 return 0; 23301 } 23302 if (scanpt[4] > max) { 23303 return 0; 23304 } 23305 23306 if (matchmin) { 23307 23308 if (regmatch(preg, regnext(preg, scan))) { 23309 return 1; 23310 } 23311 23312 scanpt[4]++; 23313 if (regmatch(preg, scan + 5)) { 23314 return 1; 23315 } 23316 scanpt[4]--; 23317 return 0; 23318 } 23319 23320 if (scanpt[4] < max) { 23321 scanpt[4]++; 23322 if (regmatch(preg, scan + 5)) { 23323 return 1; 23324 } 23325 scanpt[4]--; 23326 } 23327 23328 return regmatch(preg, regnext(preg, scan)); 23329 } 23330 23331 23332 static int regmatch(regex_t *preg, int prog) 23333 { 23334 int scan; 23335 int next; 23336 const char *save; 23337 23338 scan = prog; 23339 23340 #ifdef DEBUG 23341 if (scan != 0 && regnarrate) 23342 fprintf(stderr, "%s(\n", regprop(scan)); 23343 #endif 23344 while (scan != 0) { 23345 int n; 23346 int c; 23347 #ifdef DEBUG 23348 if (regnarrate) { 23349 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan))); 23350 } 23351 #endif 23352 next = regnext(preg, scan); 23353 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); 23354 23355 switch (OP(preg, scan)) { 23356 case BOLX: 23357 if ((preg->eflags & REG_NOTBOL)) { 23358 return(0); 23359 } 23360 23361 case BOL: 23362 if (preg->reginput != preg->regbol) { 23363 return(0); 23364 } 23365 break; 23366 case EOLX: 23367 if (c != 0) { 23368 23369 return 0; 23370 } 23371 break; 23372 case EOL: 23373 if (!reg_iseol(preg, c)) { 23374 return(0); 23375 } 23376 break; 23377 case WORDA: 23378 23379 if ((!isalnum(UCHAR(c))) && c != '_') 23380 return(0); 23381 23382 if (preg->reginput > preg->regbol && 23383 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_')) 23384 return(0); 23385 break; 23386 case WORDZ: 23387 23388 if (preg->reginput > preg->regbol) { 23389 23390 if (reg_iseol(preg, c) || !(isalnum(UCHAR(c)) || c == '_')) { 23391 c = preg->reginput[-1]; 23392 23393 if (isalnum(UCHAR(c)) || c == '_') { 23394 break; 23395 } 23396 } 23397 } 23398 23399 return(0); 23400 23401 case ANY: 23402 if (reg_iseol(preg, c)) 23403 return 0; 23404 preg->reginput += n; 23405 break; 23406 case EXACTLY: { 23407 int opnd; 23408 int len; 23409 int slen; 23410 23411 opnd = OPERAND(scan); 23412 len = str_int_len(preg->program + opnd); 23413 23414 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE); 23415 if (slen < 0) { 23416 return(0); 23417 } 23418 preg->reginput += slen; 23419 } 23420 break; 23421 case ANYOF: 23422 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) { 23423 return(0); 23424 } 23425 preg->reginput += n; 23426 break; 23427 case ANYBUT: 23428 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) { 23429 return(0); 23430 } 23431 preg->reginput += n; 23432 break; 23433 case NOTHING: 23434 break; 23435 case BACK: 23436 break; 23437 case BRANCH: 23438 if (OP(preg, next) != BRANCH) 23439 next = OPERAND(scan); 23440 else { 23441 do { 23442 save = preg->reginput; 23443 if (regmatch(preg, OPERAND(scan))) { 23444 return(1); 23445 } 23446 preg->reginput = save; 23447 scan = regnext(preg, scan); 23448 } while (scan != 0 && OP(preg, scan) == BRANCH); 23449 return(0); 23450 23451 } 23452 break; 23453 case REP: 23454 case REPMIN: 23455 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN); 23456 23457 case REPX: 23458 case REPXMIN: 23459 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN); 23460 23461 case END: 23462 return 1; 23463 23464 case OPENNC: 23465 case CLOSENC: 23466 return regmatch(preg, next); 23467 23468 default: 23469 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) { 23470 save = preg->reginput; 23471 if (regmatch(preg, next)) { 23472 if (OP(preg, scan) < CLOSE) { 23473 int no = OP(preg, scan) - OPEN; 23474 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) { 23475 preg->pmatch[no].rm_so = save - preg->start; 23476 } 23477 } 23478 else { 23479 int no = OP(preg, scan) - CLOSE; 23480 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) { 23481 preg->pmatch[no].rm_eo = save - preg->start; 23482 } 23483 } 23484 return(1); 23485 } 23486 23487 preg->reginput = save; 23488 return(0); 23489 } 23490 return REG_ERR_INTERNAL; 23491 } 23492 23493 scan = next; 23494 } 23495 23496 return REG_ERR_INTERNAL; 23497 } 23498 23499 static int regrepeat(regex_t *preg, int p, int max) 23500 { 23501 int count = 0; 23502 const char *scan; 23503 int opnd; 23504 int ch; 23505 int n; 23506 23507 scan = preg->reginput; 23508 opnd = OPERAND(p); 23509 switch (OP(preg, p)) { 23510 case ANY: 23511 while (!reg_iseol(preg, *scan) && count < max) { 23512 count++; 23513 scan += utf8_charlen(*scan); 23514 } 23515 break; 23516 case EXACTLY: 23517 while (count < max) { 23518 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); 23519 if (preg->program[opnd] != ch) { 23520 break; 23521 } 23522 count++; 23523 scan += n; 23524 } 23525 break; 23526 case ANYOF: 23527 while (count < max) { 23528 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); 23529 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) { 23530 break; 23531 } 23532 count++; 23533 scan += n; 23534 } 23535 break; 23536 case ANYBUT: 23537 while (count < max) { 23538 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); 23539 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) { 23540 break; 23541 } 23542 count++; 23543 scan += n; 23544 } 23545 break; 23546 default: 23547 preg->err = REG_ERR_INTERNAL; 23548 count = 0; 23549 break; 23550 } 23551 preg->reginput = scan; 23552 23553 return(count); 23554 } 23555 23556 static int regnext(regex_t *preg, int p ) 23557 { 23558 int offset; 23559 23560 offset = NEXT(preg, p); 23561 23562 if (offset == 0) 23563 return 0; 23564 23565 if (OP(preg, p) == BACK) 23566 return(p-offset); 23567 else 23568 return(p+offset); 23569 } 23570 23571 static int regopsize(regex_t *preg, int p ) 23572 { 23573 23574 switch (OP(preg, p)) { 23575 case REP: 23576 case REPMIN: 23577 case REPX: 23578 case REPXMIN: 23579 return 5; 23580 23581 case ANYOF: 23582 case ANYBUT: 23583 case EXACTLY: { 23584 int s = p + 2; 23585 while (preg->program[s++]) { 23586 } 23587 return s - p; 23588 } 23589 } 23590 return 2; 23591 } 23592 23593 23594 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) 23595 { 23596 static const char *error_strings[] = { 23597 "success", 23598 "no match", 23599 "bad pattern", 23600 "null argument", 23601 "unknown error", 23602 "too big", 23603 "out of memory", 23604 "too many ()", 23605 "parentheses () not balanced", 23606 "braces {} not balanced", 23607 "invalid repetition count(s)", 23608 "extra characters", 23609 "*+ of empty atom", 23610 "nested count", 23611 "internal error", 23612 "count follows nothing", 23613 "invalid escape \\ sequence", 23614 "corrupted program", 23615 "contains null char", 23616 "brackets [] not balanced", 23617 }; 23618 const char *err; 23619 23620 if (errcode < 0 || errcode >= REG_ERR_NUM) { 23621 err = "Bad error code"; 23622 } 23623 else { 23624 err = error_strings[errcode]; 23625 } 23626 23627 return snprintf(errbuf, errbuf_size, "%s", err); 23628 } 23629 23630 void jim_regfree(regex_t *preg) 23631 { 23632 free(preg->program); 23633 } 23634 23635 #endif 23636 #include <string.h> 23637 23638 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg) 23639 { 23640 Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno())); 23641 } 23642 23643 #if defined(_WIN32) || defined(WIN32) 23644 #include <sys/stat.h> 23645 23646 int Jim_Errno(void) 23647 { 23648 switch (GetLastError()) { 23649 case ERROR_FILE_NOT_FOUND: return ENOENT; 23650 case ERROR_PATH_NOT_FOUND: return ENOENT; 23651 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; 23652 case ERROR_ACCESS_DENIED: return EACCES; 23653 case ERROR_INVALID_HANDLE: return EBADF; 23654 case ERROR_BAD_ENVIRONMENT: return E2BIG; 23655 case ERROR_BAD_FORMAT: return ENOEXEC; 23656 case ERROR_INVALID_ACCESS: return EACCES; 23657 case ERROR_INVALID_DRIVE: return ENOENT; 23658 case ERROR_CURRENT_DIRECTORY: return EACCES; 23659 case ERROR_NOT_SAME_DEVICE: return EXDEV; 23660 case ERROR_NO_MORE_FILES: return ENOENT; 23661 case ERROR_WRITE_PROTECT: return EROFS; 23662 case ERROR_BAD_UNIT: return ENXIO; 23663 case ERROR_NOT_READY: return EBUSY; 23664 case ERROR_BAD_COMMAND: return EIO; 23665 case ERROR_CRC: return EIO; 23666 case ERROR_BAD_LENGTH: return EIO; 23667 case ERROR_SEEK: return EIO; 23668 case ERROR_WRITE_FAULT: return EIO; 23669 case ERROR_READ_FAULT: return EIO; 23670 case ERROR_GEN_FAILURE: return EIO; 23671 case ERROR_SHARING_VIOLATION: return EACCES; 23672 case ERROR_LOCK_VIOLATION: return EACCES; 23673 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE; 23674 case ERROR_HANDLE_DISK_FULL: return ENOSPC; 23675 case ERROR_NOT_SUPPORTED: return ENODEV; 23676 case ERROR_REM_NOT_LIST: return EBUSY; 23677 case ERROR_DUP_NAME: return EEXIST; 23678 case ERROR_BAD_NETPATH: return ENOENT; 23679 case ERROR_NETWORK_BUSY: return EBUSY; 23680 case ERROR_DEV_NOT_EXIST: return ENODEV; 23681 case ERROR_TOO_MANY_CMDS: return EAGAIN; 23682 case ERROR_ADAP_HDW_ERR: return EIO; 23683 case ERROR_BAD_NET_RESP: return EIO; 23684 case ERROR_UNEXP_NET_ERR: return EIO; 23685 case ERROR_NETNAME_DELETED: return ENOENT; 23686 case ERROR_NETWORK_ACCESS_DENIED: return EACCES; 23687 case ERROR_BAD_DEV_TYPE: return ENODEV; 23688 case ERROR_BAD_NET_NAME: return ENOENT; 23689 case ERROR_TOO_MANY_NAMES: return ENFILE; 23690 case ERROR_TOO_MANY_SESS: return EIO; 23691 case ERROR_SHARING_PAUSED: return EAGAIN; 23692 case ERROR_REDIR_PAUSED: return EAGAIN; 23693 case ERROR_FILE_EXISTS: return EEXIST; 23694 case ERROR_CANNOT_MAKE: return ENOSPC; 23695 case ERROR_OUT_OF_STRUCTURES: return ENFILE; 23696 case ERROR_ALREADY_ASSIGNED: return EEXIST; 23697 case ERROR_INVALID_PASSWORD: return EPERM; 23698 case ERROR_NET_WRITE_FAULT: return EIO; 23699 case ERROR_NO_PROC_SLOTS: return EAGAIN; 23700 case ERROR_DISK_CHANGE: return EXDEV; 23701 case ERROR_BROKEN_PIPE: return EPIPE; 23702 case ERROR_OPEN_FAILED: return ENOENT; 23703 case ERROR_DISK_FULL: return ENOSPC; 23704 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE; 23705 case ERROR_INVALID_TARGET_HANDLE: return EBADF; 23706 case ERROR_INVALID_NAME: return ENOENT; 23707 case ERROR_PROC_NOT_FOUND: return ESRCH; 23708 case ERROR_WAIT_NO_CHILDREN: return ECHILD; 23709 case ERROR_CHILD_NOT_COMPLETE: return ECHILD; 23710 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; 23711 case ERROR_SEEK_ON_DEVICE: return ESPIPE; 23712 case ERROR_BUSY_DRIVE: return EAGAIN; 23713 case ERROR_DIR_NOT_EMPTY: return EEXIST; 23714 case ERROR_NOT_LOCKED: return EACCES; 23715 case ERROR_BAD_PATHNAME: return ENOENT; 23716 case ERROR_LOCK_FAILED: return EACCES; 23717 case ERROR_ALREADY_EXISTS: return EEXIST; 23718 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; 23719 case ERROR_BAD_PIPE: return EPIPE; 23720 case ERROR_PIPE_BUSY: return EAGAIN; 23721 case ERROR_PIPE_NOT_CONNECTED: return EPIPE; 23722 case ERROR_DIRECTORY: return ENOTDIR; 23723 } 23724 return EINVAL; 23725 } 23726 23727 long JimProcessPid(phandle_t pid) 23728 { 23729 if (pid == INVALID_HANDLE_VALUE) { 23730 return -1; 23731 } 23732 return GetProcessId(pid); 23733 } 23734 23735 phandle_t JimWaitPid(long pid, int *status, int nohang) 23736 { 23737 if (pid > 0) { 23738 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid); 23739 if (h) { 23740 long pid = waitpid(h, status, nohang); 23741 CloseHandle(h); 23742 if (pid > 0) { 23743 return h; 23744 } 23745 } 23746 } 23747 return JIM_BAD_PHANDLE; 23748 } 23749 23750 long waitpid(phandle_t phandle, int *status, int nohang) 23751 { 23752 long pid; 23753 DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE); 23754 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) { 23755 23756 return -1; 23757 } 23758 GetExitCodeProcess(phandle, &ret); 23759 *status = ret; 23760 23761 pid = GetProcessId(phandle); 23762 CloseHandle(phandle); 23763 return pid; 23764 } 23765 23766 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file) 23767 { 23768 char name[MAX_PATH]; 23769 HANDLE handle; 23770 23771 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) { 23772 return -1; 23773 } 23774 23775 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, 23776 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0), 23777 NULL); 23778 23779 if (handle == INVALID_HANDLE_VALUE) { 23780 goto error; 23781 } 23782 23783 Jim_SetResultString(interp, name, -1); 23784 return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT); 23785 23786 error: 23787 Jim_SetResultErrno(interp, name); 23788 DeleteFile(name); 23789 return -1; 23790 } 23791 23792 int Jim_OpenForWrite(const char *filename, int append) 23793 { 23794 if (strcmp(filename, "/dev/null") == 0) { 23795 filename = "nul:"; 23796 } 23797 int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE); 23798 if (fd >= 0 && append) { 23799 23800 _lseek(fd, 0L, SEEK_END); 23801 } 23802 return fd; 23803 } 23804 23805 int Jim_OpenForRead(const char *filename) 23806 { 23807 if (strcmp(filename, "/dev/null") == 0) { 23808 filename = "nul:"; 23809 } 23810 return _open(filename, _O_RDONLY | _O_TEXT, 0); 23811 } 23812 23813 #elif defined(HAVE_UNISTD_H) 23814 23815 23816 23817 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file) 23818 { 23819 int fd; 23820 mode_t mask; 23821 Jim_Obj *filenameObj; 23822 23823 if (filename_template == NULL) { 23824 const char *tmpdir = getenv("TMPDIR"); 23825 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) { 23826 tmpdir = "/tmp/"; 23827 } 23828 filenameObj = Jim_NewStringObj(interp, tmpdir, -1); 23829 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') { 23830 Jim_AppendString(interp, filenameObj, "/", 1); 23831 } 23832 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1); 23833 } 23834 else { 23835 filenameObj = Jim_NewStringObj(interp, filename_template, -1); 23836 } 23837 23838 23839 #ifdef HAVE_UMASK 23840 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); 23841 #endif 23842 #ifdef HAVE_MKSTEMP 23843 fd = mkstemp(filenameObj->bytes); 23844 #else 23845 if (mktemp(filenameObj->bytes) == NULL) { 23846 fd = -1; 23847 } 23848 else { 23849 fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC); 23850 } 23851 #endif 23852 #ifdef HAVE_UMASK 23853 umask(mask); 23854 #endif 23855 if (fd < 0) { 23856 Jim_SetResultErrno(interp, Jim_String(filenameObj)); 23857 Jim_FreeNewObj(interp, filenameObj); 23858 return -1; 23859 } 23860 if (unlink_file) { 23861 remove(Jim_String(filenameObj)); 23862 } 23863 23864 Jim_SetResult(interp, filenameObj); 23865 return fd; 23866 } 23867 23868 int Jim_OpenForWrite(const char *filename, int append) 23869 { 23870 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666); 23871 } 23872 23873 int Jim_OpenForRead(const char *filename) 23874 { 23875 return open(filename, O_RDONLY, 0); 23876 } 23877 23878 #endif 23879 23880 #if defined(_WIN32) || defined(WIN32) 23881 #ifndef STRICT 23882 #define STRICT 23883 #endif 23884 #define WIN32_LEAN_AND_MEAN 23885 #include <windows.h> 23886 23887 #if defined(HAVE_DLOPEN_COMPAT) 23888 void *dlopen(const char *path, int mode) 23889 { 23890 JIM_NOTUSED(mode); 23891 23892 return (void *)LoadLibraryA(path); 23893 } 23894 23895 int dlclose(void *handle) 23896 { 23897 FreeLibrary((HANDLE)handle); 23898 return 0; 23899 } 23900 23901 void *dlsym(void *handle, const char *symbol) 23902 { 23903 return GetProcAddress((HMODULE)handle, symbol); 23904 } 23905 23906 char *dlerror(void) 23907 { 23908 static char msg[121]; 23909 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 23910 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL); 23911 return msg; 23912 } 23913 #endif 23914 23915 #ifdef _MSC_VER 23916 23917 #include <sys/timeb.h> 23918 23919 23920 int gettimeofday(struct timeval *tv, void *unused) 23921 { 23922 struct _timeb tb; 23923 23924 _ftime(&tb); 23925 tv->tv_sec = tb.time; 23926 tv->tv_usec = tb.millitm * 1000; 23927 23928 return 0; 23929 } 23930 23931 23932 DIR *opendir(const char *name) 23933 { 23934 DIR *dir = 0; 23935 23936 if (name && name[0]) { 23937 size_t base_length = strlen(name); 23938 const char *all = 23939 strchr("/\\", name[base_length - 1]) ? "*" : "/*"; 23940 23941 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 && 23942 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) { 23943 strcat(strcpy(dir->name, name), all); 23944 23945 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1) 23946 dir->result.d_name = 0; 23947 else { 23948 Jim_Free(dir->name); 23949 Jim_Free(dir); 23950 dir = 0; 23951 } 23952 } 23953 else { 23954 Jim_Free(dir); 23955 dir = 0; 23956 errno = ENOMEM; 23957 } 23958 } 23959 else { 23960 errno = EINVAL; 23961 } 23962 return dir; 23963 } 23964 23965 int closedir(DIR * dir) 23966 { 23967 int result = -1; 23968 23969 if (dir) { 23970 if (dir->handle != -1) 23971 result = _findclose(dir->handle); 23972 Jim_Free(dir->name); 23973 Jim_Free(dir); 23974 } 23975 if (result == -1) 23976 errno = EBADF; 23977 return result; 23978 } 23979 23980 struct dirent *readdir(DIR * dir) 23981 { 23982 struct dirent *result = 0; 23983 23984 if (dir && dir->handle != -1) { 23985 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { 23986 result = &dir->result; 23987 result->d_name = dir->info.name; 23988 } 23989 } 23990 else { 23991 errno = EBADF; 23992 } 23993 return result; 23994 } 23995 #endif 23996 #endif 23997 #include <stdio.h> 23998 #include <signal.h> 23999 24000 24001 24002 24003 24004 24005 #ifndef SIGPIPE 24006 #define SIGPIPE 13 24007 #endif 24008 #ifndef SIGINT 24009 #define SIGINT 2 24010 #endif 24011 24012 const char *Jim_SignalId(int sig) 24013 { 24014 static char buf[10]; 24015 switch (sig) { 24016 case SIGINT: return "SIGINT"; 24017 case SIGPIPE: return "SIGPIPE"; 24018 24019 } 24020 snprintf(buf, sizeof(buf), "%d", sig); 24021 return buf; 24022 } 24023 #ifndef JIM_BOOTSTRAP_LIB_ONLY 24024 #include <errno.h> 24025 #include <string.h> 24026 #include <stdio.h> 24027 24028 24029 #ifdef USE_LINENOISE 24030 #ifdef HAVE_UNISTD_H 24031 #include <unistd.h> 24032 #endif 24033 #ifdef HAVE_SYS_STAT_H 24034 #include <sys/stat.h> 24035 #endif 24036 #include "linenoise.h" 24037 #else 24038 #define MAX_LINE_LEN 512 24039 #endif 24040 24041 #ifdef USE_LINENOISE 24042 struct JimCompletionInfo { 24043 Jim_Interp *interp; 24044 Jim_Obj *completion_command; 24045 Jim_Obj *hints_command; 24046 24047 }; 24048 24049 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp); 24050 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata); 24051 static const char completion_callback_assoc_key[] = "interactive-completion"; 24052 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata); 24053 static void JimFreeHintsCallback(void *hint, void *userdata); 24054 #endif 24055 24056 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt) 24057 { 24058 #ifdef USE_LINENOISE 24059 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp); 24060 char *result; 24061 Jim_Obj *objPtr; 24062 long mlmode = 0; 24063 if (compinfo->completion_command) { 24064 linenoiseSetCompletionCallback(JimCompletionCallback, compinfo); 24065 } 24066 if (compinfo->hints_command) { 24067 linenoiseSetHintsCallback(JimHintsCallback, compinfo); 24068 linenoiseSetFreeHintsCallback(JimFreeHintsCallback); 24069 } 24070 objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE); 24071 if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) { 24072 linenoiseSetMultiLine(mlmode); 24073 } 24074 24075 result = linenoise(prompt); 24076 24077 linenoiseSetCompletionCallback(NULL, NULL); 24078 linenoiseSetHintsCallback(NULL, NULL); 24079 linenoiseSetFreeHintsCallback(NULL); 24080 return result; 24081 #else 24082 int len; 24083 char *line = Jim_Alloc(MAX_LINE_LEN); 24084 24085 fputs(prompt, stdout); 24086 fflush(stdout); 24087 24088 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) { 24089 Jim_Free(line); 24090 return NULL; 24091 } 24092 len = strlen(line); 24093 if (len && line[len - 1] == '\n') { 24094 line[len - 1] = '\0'; 24095 } 24096 return line; 24097 #endif 24098 } 24099 24100 void Jim_HistoryLoad(const char *filename) 24101 { 24102 #ifdef USE_LINENOISE 24103 linenoiseHistoryLoad(filename); 24104 #endif 24105 } 24106 24107 void Jim_HistoryAdd(const char *line) 24108 { 24109 #ifdef USE_LINENOISE 24110 linenoiseHistoryAdd(line); 24111 #endif 24112 } 24113 24114 void Jim_HistorySave(const char *filename) 24115 { 24116 #ifdef USE_LINENOISE 24117 #ifdef HAVE_UMASK 24118 mode_t mask; 24119 24120 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); 24121 #endif 24122 linenoiseHistorySave(filename); 24123 #ifdef HAVE_UMASK 24124 umask(mask); 24125 #endif 24126 #endif 24127 } 24128 24129 void Jim_HistoryShow(void) 24130 { 24131 #ifdef USE_LINENOISE 24132 24133 int i; 24134 int len; 24135 char **history = linenoiseHistory(&len); 24136 for (i = 0; i < len; i++) { 24137 printf("%4d %s\n", i + 1, history[i]); 24138 } 24139 #endif 24140 } 24141 24142 void Jim_HistorySetMaxLen(int length) 24143 { 24144 #ifdef USE_LINENOISE 24145 linenoiseHistorySetMaxLen(length); 24146 #endif 24147 } 24148 24149 int Jim_HistoryGetMaxLen(void) 24150 { 24151 #ifdef USE_LINENOISE 24152 return linenoiseHistoryGetMaxLen(); 24153 #endif 24154 return 0; 24155 } 24156 24157 #ifdef USE_LINENOISE 24158 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata) 24159 { 24160 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata; 24161 Jim_Obj *objv[2]; 24162 int ret; 24163 24164 objv[0] = info->completion_command; 24165 objv[1] = Jim_NewStringObj(info->interp, prefix, -1); 24166 24167 ret = Jim_EvalObjVector(info->interp, 2, objv); 24168 24169 24170 if (ret == JIM_OK) { 24171 int i; 24172 Jim_Obj *listObj = Jim_GetResult(info->interp); 24173 int len = Jim_ListLength(info->interp, listObj); 24174 for (i = 0; i < len; i++) { 24175 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i))); 24176 } 24177 } 24178 } 24179 24180 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata) 24181 { 24182 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata; 24183 Jim_Obj *objv[2]; 24184 int ret; 24185 char *result = NULL; 24186 24187 objv[0] = info->hints_command; 24188 objv[1] = Jim_NewStringObj(info->interp, prefix, -1); 24189 24190 ret = Jim_EvalObjVector(info->interp, 2, objv); 24191 24192 24193 if (ret == JIM_OK) { 24194 Jim_Obj *listObj = Jim_GetResult(info->interp); 24195 Jim_IncrRefCount(listObj); 24196 24197 int len = Jim_ListLength(info->interp, listObj); 24198 if (len >= 1) { 24199 long x; 24200 result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0))); 24201 if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) { 24202 *color = x; 24203 } 24204 if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) { 24205 *bold = x; 24206 } 24207 } 24208 Jim_DecrRefCount(info->interp, listObj); 24209 } 24210 return result; 24211 } 24212 24213 static void JimFreeHintsCallback(void *hint, void *userdata) 24214 { 24215 Jim_Free(hint); 24216 } 24217 24218 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data) 24219 { 24220 struct JimCompletionInfo *compinfo = data; 24221 24222 if (compinfo->completion_command) { 24223 Jim_DecrRefCount(interp, compinfo->completion_command); 24224 } 24225 if (compinfo->hints_command) { 24226 Jim_DecrRefCount(interp, compinfo->hints_command); 24227 } 24228 24229 Jim_Free(compinfo); 24230 } 24231 24232 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp) 24233 { 24234 struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key); 24235 if (compinfo == NULL) { 24236 compinfo = Jim_Alloc(sizeof(*compinfo)); 24237 compinfo->interp = interp; 24238 compinfo->completion_command = NULL; 24239 compinfo->hints_command = NULL; 24240 Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo); 24241 } 24242 return compinfo; 24243 } 24244 #endif 24245 24246 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj) 24247 { 24248 #ifdef USE_LINENOISE 24249 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp); 24250 24251 if (completionCommandObj) { 24252 24253 Jim_IncrRefCount(completionCommandObj); 24254 } 24255 if (compinfo->completion_command) { 24256 Jim_DecrRefCount(interp, compinfo->completion_command); 24257 } 24258 compinfo->completion_command = completionCommandObj; 24259 #endif 24260 } 24261 24262 void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj) 24263 { 24264 #ifdef USE_LINENOISE 24265 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp); 24266 24267 if (hintsCommandObj) { 24268 24269 Jim_IncrRefCount(hintsCommandObj); 24270 } 24271 if (compinfo->hints_command) { 24272 Jim_DecrRefCount(interp, compinfo->hints_command); 24273 } 24274 compinfo->hints_command = hintsCommandObj; 24275 #endif 24276 } 24277 24278 int Jim_InteractivePrompt(Jim_Interp *interp) 24279 { 24280 int retcode = JIM_OK; 24281 char *history_file = NULL; 24282 #ifdef USE_LINENOISE 24283 const char *home; 24284 24285 home = getenv("HOME"); 24286 if (home && isatty(STDIN_FILENO)) { 24287 int history_len = strlen(home) + sizeof("/.jim_history"); 24288 history_file = Jim_Alloc(history_len); 24289 snprintf(history_file, history_len, "%s/.jim_history", home); 24290 Jim_HistoryLoad(history_file); 24291 } 24292 24293 Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1)); 24294 Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1)); 24295 #endif 24296 24297 printf("Welcome to Jim version %d.%d\n", 24298 JIM_VERSION / 100, JIM_VERSION % 100); 24299 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1"); 24300 24301 while (1) { 24302 Jim_Obj *scriptObjPtr; 24303 const char *result; 24304 int reslen; 24305 char prompt[20]; 24306 24307 if (retcode != JIM_OK) { 24308 const char *retcodestr = Jim_ReturnCode(retcode); 24309 24310 if (*retcodestr == '?') { 24311 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode); 24312 } 24313 else { 24314 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr); 24315 } 24316 } 24317 else { 24318 strcpy(prompt, ". "); 24319 } 24320 24321 scriptObjPtr = Jim_NewStringObj(interp, "", 0); 24322 Jim_IncrRefCount(scriptObjPtr); 24323 while (1) { 24324 char state; 24325 char *line; 24326 24327 line = Jim_HistoryGetline(interp, prompt); 24328 if (line == NULL) { 24329 if (errno == EINTR) { 24330 continue; 24331 } 24332 Jim_DecrRefCount(interp, scriptObjPtr); 24333 retcode = JIM_OK; 24334 goto out; 24335 } 24336 if (Jim_Length(scriptObjPtr) != 0) { 24337 24338 Jim_AppendString(interp, scriptObjPtr, "\n", 1); 24339 } 24340 Jim_AppendString(interp, scriptObjPtr, line, -1); 24341 Jim_Free(line); 24342 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state)) 24343 break; 24344 24345 snprintf(prompt, sizeof(prompt), "%c> ", state); 24346 } 24347 #ifdef USE_LINENOISE 24348 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) { 24349 24350 Jim_HistoryShow(); 24351 Jim_DecrRefCount(interp, scriptObjPtr); 24352 continue; 24353 } 24354 24355 Jim_HistoryAdd(Jim_String(scriptObjPtr)); 24356 if (history_file) { 24357 Jim_HistorySave(history_file); 24358 } 24359 #endif 24360 retcode = Jim_EvalObj(interp, scriptObjPtr); 24361 Jim_DecrRefCount(interp, scriptObjPtr); 24362 24363 if (retcode == JIM_EXIT) { 24364 break; 24365 } 24366 if (retcode == JIM_ERR) { 24367 Jim_MakeErrorMessage(interp); 24368 } 24369 result = Jim_GetString(Jim_GetResult(interp), &reslen); 24370 if (reslen) { 24371 if (fwrite(result, reslen, 1, stdout) == 0) { 24372 24373 } 24374 putchar('\n'); 24375 } 24376 } 24377 out: 24378 Jim_Free(history_file); 24379 24380 return retcode; 24381 } 24382 24383 #include <stdio.h> 24384 #include <stdlib.h> 24385 #include <string.h> 24386 24387 24388 24389 extern int Jim_initjimshInit(Jim_Interp *interp); 24390 24391 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[]) 24392 { 24393 int n; 24394 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 24395 24396 24397 for (n = 0; n < argc; n++) { 24398 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1); 24399 24400 Jim_ListAppendElement(interp, listObj, obj); 24401 } 24402 24403 Jim_SetVariableStr(interp, "argv", listObj); 24404 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc)); 24405 } 24406 24407 static void JimPrintErrorMessage(Jim_Interp *interp) 24408 { 24409 Jim_MakeErrorMessage(interp); 24410 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); 24411 } 24412 24413 void usage(const char* executable_name) 24414 { 24415 printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); 24416 printf("Usage: %s\n", executable_name); 24417 printf("or : %s [options] [filename]\n", executable_name); 24418 printf("\n"); 24419 printf("Without options: Interactive mode\n"); 24420 printf("\n"); 24421 printf("Options:\n"); 24422 printf(" --version : prints the version string\n"); 24423 printf(" --help : prints this text\n"); 24424 printf(" -e CMD : executes command CMD\n"); 24425 printf(" NOTE: all subsequent options will be passed as arguments to the command\n"); 24426 printf(" [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n"); 24427 printf(" NOTE: all subsequent options will be passed to the script\n\n"); 24428 } 24429 24430 int main(int argc, char *const argv[]) 24431 { 24432 int retcode; 24433 Jim_Interp *interp; 24434 char *const orig_argv0 = argv[0]; 24435 24436 24437 if (argc > 1 && strcmp(argv[1], "--version") == 0) { 24438 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); 24439 return 0; 24440 } 24441 else if (argc > 1 && strcmp(argv[1], "--help") == 0) { 24442 usage(argv[0]); 24443 return 0; 24444 } 24445 24446 24447 interp = Jim_CreateInterp(); 24448 Jim_RegisterCoreCommands(interp); 24449 24450 24451 if (Jim_InitStaticExtensions(interp) != JIM_OK) { 24452 JimPrintErrorMessage(interp); 24453 } 24454 24455 Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0); 24456 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0"); 24457 #ifdef USE_LINENOISE 24458 Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1"); 24459 #else 24460 Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0"); 24461 #endif 24462 retcode = Jim_initjimshInit(interp); 24463 24464 if (argc == 1) { 24465 24466 if (retcode == JIM_ERR) { 24467 JimPrintErrorMessage(interp); 24468 } 24469 if (retcode != JIM_EXIT) { 24470 JimSetArgv(interp, 0, NULL); 24471 if (!isatty(STDIN_FILENO)) { 24472 24473 goto eval_stdin; 24474 } 24475 retcode = Jim_InteractivePrompt(interp); 24476 } 24477 } 24478 else { 24479 24480 if (argc > 2 && strcmp(argv[1], "-e") == 0) { 24481 24482 JimSetArgv(interp, argc - 3, argv + 3); 24483 retcode = Jim_Eval(interp, argv[2]); 24484 if (retcode != JIM_ERR) { 24485 int len; 24486 const char *msg = Jim_GetString(Jim_GetResult(interp), &len); 24487 if (fwrite(msg, len, 1, stdout) == 0) { 24488 24489 } 24490 putchar('\n'); 24491 } 24492 } 24493 else { 24494 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1)); 24495 JimSetArgv(interp, argc - 2, argv + 2); 24496 if (strcmp(argv[1], "-") == 0) { 24497 eval_stdin: 24498 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]"); 24499 } else { 24500 retcode = Jim_EvalFile(interp, argv[1]); 24501 } 24502 } 24503 if (retcode == JIM_ERR) { 24504 JimPrintErrorMessage(interp); 24505 } 24506 } 24507 if (retcode == JIM_EXIT) { 24508 retcode = Jim_GetExitCode(interp); 24509 } 24510 else if (retcode == JIM_ERR) { 24511 retcode = 1; 24512 } 24513 else { 24514 retcode = 0; 24515 } 24516 Jim_FreeInterp(interp); 24517 return retcode; 24518 } 24519 #endif 24520