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 { 7413 void *p = realloc(ptr, size); 7414 if( p==0 ){ 7415 fprintf(stderr,"Out of memory\n"); 7416 exit(1); 7417 } 7418 return p; 7419 } 7420 } 7421 7422 void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator; 7423 7424 char *Jim_StrDup(const char *s) 7425 { 7426 return Jim_StrDupLen(s, strlen(s)); 7427 } 7428 7429 char *Jim_StrDupLen(const char *s, int l) 7430 { 7431 char *copy = Jim_Alloc(l + 1); 7432 7433 memcpy(copy, s, l); 7434 copy[l] = 0; 7435 return copy; 7436 } 7437 7438 7439 jim_wide Jim_GetTimeUsec(unsigned type) 7440 { 7441 long long now; 7442 struct timeval tv; 7443 7444 #if defined(HAVE_CLOCK_GETTIME) 7445 struct timespec ts; 7446 7447 if (clock_gettime(type, &ts) == 0) { 7448 now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000; 7449 } 7450 else 7451 #endif 7452 { 7453 gettimeofday(&tv, NULL); 7454 7455 now = tv.tv_sec * 1000000LL + tv.tv_usec; 7456 } 7457 7458 return now; 7459 } 7460 7461 7462 7463 7464 7465 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht); 7466 static unsigned int JimHashTableNextPower(unsigned int size); 7467 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace); 7468 7469 7470 7471 7472 unsigned int Jim_IntHashFunction(unsigned int key) 7473 { 7474 key += ~(key << 15); 7475 key ^= (key >> 10); 7476 key += (key << 3); 7477 key ^= (key >> 6); 7478 key += ~(key << 11); 7479 key ^= (key >> 16); 7480 return key; 7481 } 7482 7483 7484 unsigned int Jim_GenHashFunction(const unsigned char *string, int length) 7485 { 7486 unsigned result = 0; 7487 string += length; 7488 while (length--) { 7489 result += (result << 3) + (unsigned char)(*--string); 7490 } 7491 return result; 7492 } 7493 7494 7495 7496 static void JimResetHashTable(Jim_HashTable *ht) 7497 { 7498 ht->table = NULL; 7499 ht->size = 0; 7500 ht->sizemask = 0; 7501 ht->used = 0; 7502 ht->collisions = 0; 7503 #ifdef JIM_RANDOMISE_HASH 7504 ht->uniq = (rand() ^ time(NULL) ^ clock()); 7505 #else 7506 ht->uniq = 0; 7507 #endif 7508 } 7509 7510 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter) 7511 { 7512 iter->ht = ht; 7513 iter->index = -1; 7514 iter->entry = NULL; 7515 iter->nextEntry = NULL; 7516 } 7517 7518 7519 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr) 7520 { 7521 JimResetHashTable(ht); 7522 ht->type = type; 7523 ht->privdata = privDataPtr; 7524 return JIM_OK; 7525 } 7526 7527 7528 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size) 7529 { 7530 Jim_HashTable n; 7531 unsigned int realsize = JimHashTableNextPower(size), i; 7532 7533 if (size <= ht->used) 7534 return; 7535 7536 Jim_InitHashTable(&n, ht->type, ht->privdata); 7537 n.size = realsize; 7538 n.sizemask = realsize - 1; 7539 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *)); 7540 7541 n.uniq = ht->uniq; 7542 7543 7544 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *)); 7545 7546 n.used = ht->used; 7547 for (i = 0; ht->used > 0; i++) { 7548 Jim_HashEntry *he, *nextHe; 7549 7550 if (ht->table[i] == NULL) 7551 continue; 7552 7553 7554 he = ht->table[i]; 7555 while (he) { 7556 unsigned int h; 7557 7558 nextHe = he->next; 7559 7560 h = Jim_HashKey(ht, he->key) & n.sizemask; 7561 he->next = n.table[h]; 7562 n.table[h] = he; 7563 ht->used--; 7564 7565 he = nextHe; 7566 } 7567 } 7568 assert(ht->used == 0); 7569 Jim_Free(ht->table); 7570 7571 7572 *ht = n; 7573 } 7574 7575 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val) 7576 { 7577 Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);; 7578 if (entry == NULL) 7579 return JIM_ERR; 7580 7581 7582 Jim_SetHashKey(ht, entry, key); 7583 Jim_SetHashVal(ht, entry, val); 7584 return JIM_OK; 7585 } 7586 7587 7588 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val) 7589 { 7590 int existed; 7591 Jim_HashEntry *entry; 7592 7593 entry = JimInsertHashEntry(ht, key, 1); 7594 if (entry->key) { 7595 if (ht->type->valDestructor && ht->type->valDup) { 7596 void *newval = ht->type->valDup(ht->privdata, val); 7597 ht->type->valDestructor(ht->privdata, entry->u.val); 7598 entry->u.val = newval; 7599 } 7600 else { 7601 Jim_FreeEntryVal(ht, entry); 7602 Jim_SetHashVal(ht, entry, val); 7603 } 7604 existed = 1; 7605 } 7606 else { 7607 7608 Jim_SetHashKey(ht, entry, key); 7609 Jim_SetHashVal(ht, entry, val); 7610 existed = 0; 7611 } 7612 7613 return existed; 7614 } 7615 7616 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key) 7617 { 7618 if (ht->used) { 7619 unsigned int h = Jim_HashKey(ht, key) & ht->sizemask; 7620 Jim_HashEntry *prevHe = NULL; 7621 Jim_HashEntry *he = ht->table[h]; 7622 7623 while (he) { 7624 if (Jim_CompareHashKeys(ht, key, he->key)) { 7625 7626 if (prevHe) 7627 prevHe->next = he->next; 7628 else 7629 ht->table[h] = he->next; 7630 ht->used--; 7631 Jim_FreeEntryKey(ht, he); 7632 Jim_FreeEntryVal(ht, he); 7633 Jim_Free(he); 7634 return JIM_OK; 7635 } 7636 prevHe = he; 7637 he = he->next; 7638 } 7639 } 7640 7641 return JIM_ERR; 7642 } 7643 7644 void Jim_ClearHashTable(Jim_HashTable *ht) 7645 { 7646 unsigned int i; 7647 7648 7649 for (i = 0; ht->used > 0; i++) { 7650 Jim_HashEntry *he, *nextHe; 7651 7652 he = ht->table[i]; 7653 while (he) { 7654 nextHe = he->next; 7655 Jim_FreeEntryKey(ht, he); 7656 Jim_FreeEntryVal(ht, he); 7657 Jim_Free(he); 7658 ht->used--; 7659 he = nextHe; 7660 } 7661 ht->table[i] = NULL; 7662 } 7663 } 7664 7665 int Jim_FreeHashTable(Jim_HashTable *ht) 7666 { 7667 Jim_ClearHashTable(ht); 7668 7669 Jim_Free(ht->table); 7670 7671 JimResetHashTable(ht); 7672 return JIM_OK; 7673 } 7674 7675 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key) 7676 { 7677 Jim_HashEntry *he; 7678 unsigned int h; 7679 7680 if (ht->used == 0) 7681 return NULL; 7682 h = Jim_HashKey(ht, key) & ht->sizemask; 7683 he = ht->table[h]; 7684 while (he) { 7685 if (Jim_CompareHashKeys(ht, key, he->key)) 7686 return he; 7687 he = he->next; 7688 } 7689 return NULL; 7690 } 7691 7692 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht) 7693 { 7694 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter)); 7695 JimInitHashTableIterator(ht, iter); 7696 return iter; 7697 } 7698 7699 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter) 7700 { 7701 while (1) { 7702 if (iter->entry == NULL) { 7703 iter->index++; 7704 if (iter->index >= (signed)iter->ht->size) 7705 break; 7706 iter->entry = iter->ht->table[iter->index]; 7707 } 7708 else { 7709 iter->entry = iter->nextEntry; 7710 } 7711 if (iter->entry) { 7712 iter->nextEntry = iter->entry->next; 7713 return iter->entry; 7714 } 7715 } 7716 return NULL; 7717 } 7718 7719 7720 7721 7722 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht) 7723 { 7724 if (ht->size == 0) 7725 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE); 7726 if (ht->size == ht->used) 7727 Jim_ExpandHashTable(ht, ht->size * 2); 7728 } 7729 7730 7731 static unsigned int JimHashTableNextPower(unsigned int size) 7732 { 7733 unsigned int i = JIM_HT_INITIAL_SIZE; 7734 7735 if (size >= 2147483648U) 7736 return 2147483648U; 7737 while (1) { 7738 if (i >= size) 7739 return i; 7740 i *= 2; 7741 } 7742 } 7743 7744 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace) 7745 { 7746 unsigned int h; 7747 Jim_HashEntry *he; 7748 7749 7750 JimExpandHashTableIfNeeded(ht); 7751 7752 7753 h = Jim_HashKey(ht, key) & ht->sizemask; 7754 7755 he = ht->table[h]; 7756 while (he) { 7757 if (Jim_CompareHashKeys(ht, key, he->key)) 7758 return replace ? he : NULL; 7759 he = he->next; 7760 } 7761 7762 7763 he = Jim_Alloc(sizeof(*he)); 7764 he->next = ht->table[h]; 7765 ht->table[h] = he; 7766 ht->used++; 7767 he->key = NULL; 7768 7769 return he; 7770 } 7771 7772 7773 7774 static unsigned int JimStringCopyHTHashFunction(const void *key) 7775 { 7776 return Jim_GenHashFunction(key, strlen(key)); 7777 } 7778 7779 static void *JimStringCopyHTDup(void *privdata, const void *key) 7780 { 7781 return Jim_StrDup(key); 7782 } 7783 7784 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2) 7785 { 7786 return strcmp(key1, key2) == 0; 7787 } 7788 7789 static void JimStringCopyHTKeyDestructor(void *privdata, void *key) 7790 { 7791 Jim_Free(key); 7792 } 7793 7794 static const Jim_HashTableType JimPackageHashTableType = { 7795 JimStringCopyHTHashFunction, 7796 JimStringCopyHTDup, 7797 NULL, 7798 JimStringCopyHTKeyCompare, 7799 JimStringCopyHTKeyDestructor, 7800 NULL 7801 }; 7802 7803 typedef struct AssocDataValue 7804 { 7805 Jim_InterpDeleteProc *delProc; 7806 void *data; 7807 } AssocDataValue; 7808 7809 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data) 7810 { 7811 AssocDataValue *assocPtr = (AssocDataValue *) data; 7812 7813 if (assocPtr->delProc != NULL) 7814 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data); 7815 Jim_Free(data); 7816 } 7817 7818 static const Jim_HashTableType JimAssocDataHashTableType = { 7819 JimStringCopyHTHashFunction, 7820 JimStringCopyHTDup, 7821 NULL, 7822 JimStringCopyHTKeyCompare, 7823 JimStringCopyHTKeyDestructor, 7824 JimAssocDataHashTableValueDestructor 7825 }; 7826 7827 void Jim_InitStack(Jim_Stack *stack) 7828 { 7829 stack->len = 0; 7830 stack->maxlen = 0; 7831 stack->vector = NULL; 7832 } 7833 7834 void Jim_FreeStack(Jim_Stack *stack) 7835 { 7836 Jim_Free(stack->vector); 7837 } 7838 7839 int Jim_StackLen(Jim_Stack *stack) 7840 { 7841 return stack->len; 7842 } 7843 7844 void Jim_StackPush(Jim_Stack *stack, void *element) 7845 { 7846 int neededLen = stack->len + 1; 7847 7848 if (neededLen > stack->maxlen) { 7849 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2; 7850 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen); 7851 } 7852 stack->vector[stack->len] = element; 7853 stack->len++; 7854 } 7855 7856 void *Jim_StackPop(Jim_Stack *stack) 7857 { 7858 if (stack->len == 0) 7859 return NULL; 7860 stack->len--; 7861 return stack->vector[stack->len]; 7862 } 7863 7864 void *Jim_StackPeek(Jim_Stack *stack) 7865 { 7866 if (stack->len == 0) 7867 return NULL; 7868 return stack->vector[stack->len - 1]; 7869 } 7870 7871 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr)) 7872 { 7873 int i; 7874 7875 for (i = 0; i < stack->len; i++) 7876 freeFunc(stack->vector[i]); 7877 } 7878 7879 7880 7881 #define JIM_TT_NONE 0 7882 #define JIM_TT_STR 1 7883 #define JIM_TT_ESC 2 7884 #define JIM_TT_VAR 3 7885 #define JIM_TT_DICTSUGAR 4 7886 #define JIM_TT_CMD 5 7887 7888 #define JIM_TT_SEP 6 7889 #define JIM_TT_EOL 7 7890 #define JIM_TT_EOF 8 7891 7892 #define JIM_TT_LINE 9 7893 #define JIM_TT_WORD 10 7894 7895 7896 #define JIM_TT_SUBEXPR_START 11 7897 #define JIM_TT_SUBEXPR_END 12 7898 #define JIM_TT_SUBEXPR_COMMA 13 7899 #define JIM_TT_EXPR_INT 14 7900 #define JIM_TT_EXPR_DOUBLE 15 7901 #define JIM_TT_EXPR_BOOLEAN 16 7902 7903 #define JIM_TT_EXPRSUGAR 17 7904 7905 7906 #define JIM_TT_EXPR_OP 20 7907 7908 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF) 7909 7910 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA) 7911 7912 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP) 7913 7914 struct JimParseMissing { 7915 int ch; 7916 int line; 7917 }; 7918 7919 struct JimParserCtx 7920 { 7921 const char *p; 7922 int len; 7923 int linenr; 7924 const char *tstart; 7925 const char *tend; 7926 int tline; 7927 int tt; 7928 int eof; 7929 int inquote; 7930 int comment; 7931 struct JimParseMissing missing; 7932 const char *errmsg; 7933 }; 7934 7935 static int JimParseScript(struct JimParserCtx *pc); 7936 static int JimParseSep(struct JimParserCtx *pc); 7937 static int JimParseEol(struct JimParserCtx *pc); 7938 static int JimParseCmd(struct JimParserCtx *pc); 7939 static int JimParseQuote(struct JimParserCtx *pc); 7940 static int JimParseVar(struct JimParserCtx *pc); 7941 static int JimParseBrace(struct JimParserCtx *pc); 7942 static int JimParseStr(struct JimParserCtx *pc); 7943 static int JimParseComment(struct JimParserCtx *pc); 7944 static void JimParseSubCmd(struct JimParserCtx *pc); 7945 static int JimParseSubQuote(struct JimParserCtx *pc); 7946 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc); 7947 7948 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr) 7949 { 7950 pc->p = prg; 7951 pc->len = len; 7952 pc->tstart = NULL; 7953 pc->tend = NULL; 7954 pc->tline = 0; 7955 pc->tt = JIM_TT_NONE; 7956 pc->eof = 0; 7957 pc->inquote = 0; 7958 pc->linenr = linenr; 7959 pc->comment = 1; 7960 pc->missing.ch = ' '; 7961 pc->missing.line = linenr; 7962 } 7963 7964 static int JimParseScript(struct JimParserCtx *pc) 7965 { 7966 while (1) { 7967 if (!pc->len) { 7968 pc->tstart = pc->p; 7969 pc->tend = pc->p - 1; 7970 pc->tline = pc->linenr; 7971 pc->tt = JIM_TT_EOL; 7972 if (pc->inquote) { 7973 pc->missing.ch = '"'; 7974 } 7975 pc->eof = 1; 7976 return JIM_OK; 7977 } 7978 switch (*(pc->p)) { 7979 case '\\': 7980 if (*(pc->p + 1) == '\n' && !pc->inquote) { 7981 return JimParseSep(pc); 7982 } 7983 pc->comment = 0; 7984 return JimParseStr(pc); 7985 case ' ': 7986 case '\t': 7987 case '\r': 7988 case '\f': 7989 if (!pc->inquote) 7990 return JimParseSep(pc); 7991 pc->comment = 0; 7992 return JimParseStr(pc); 7993 case '\n': 7994 case ';': 7995 pc->comment = 1; 7996 if (!pc->inquote) 7997 return JimParseEol(pc); 7998 return JimParseStr(pc); 7999 case '[': 8000 pc->comment = 0; 8001 return JimParseCmd(pc); 8002 case '$': 8003 pc->comment = 0; 8004 if (JimParseVar(pc) == JIM_ERR) { 8005 8006 pc->tstart = pc->tend = pc->p++; 8007 pc->len--; 8008 pc->tt = JIM_TT_ESC; 8009 } 8010 return JIM_OK; 8011 case '#': 8012 if (pc->comment) { 8013 JimParseComment(pc); 8014 continue; 8015 } 8016 return JimParseStr(pc); 8017 default: 8018 pc->comment = 0; 8019 return JimParseStr(pc); 8020 } 8021 return JIM_OK; 8022 } 8023 } 8024 8025 static int JimParseSep(struct JimParserCtx *pc) 8026 { 8027 pc->tstart = pc->p; 8028 pc->tline = pc->linenr; 8029 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) { 8030 if (*pc->p == '\n') { 8031 break; 8032 } 8033 if (*pc->p == '\\') { 8034 pc->p++; 8035 pc->len--; 8036 pc->linenr++; 8037 } 8038 pc->p++; 8039 pc->len--; 8040 } 8041 pc->tend = pc->p - 1; 8042 pc->tt = JIM_TT_SEP; 8043 return JIM_OK; 8044 } 8045 8046 static int JimParseEol(struct JimParserCtx *pc) 8047 { 8048 pc->tstart = pc->p; 8049 pc->tline = pc->linenr; 8050 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') { 8051 if (*pc->p == '\n') 8052 pc->linenr++; 8053 pc->p++; 8054 pc->len--; 8055 } 8056 pc->tend = pc->p - 1; 8057 pc->tt = JIM_TT_EOL; 8058 return JIM_OK; 8059 } 8060 8061 8062 static void JimParseSubBrace(struct JimParserCtx *pc) 8063 { 8064 int level = 1; 8065 8066 8067 pc->p++; 8068 pc->len--; 8069 while (pc->len) { 8070 switch (*pc->p) { 8071 case '\\': 8072 if (pc->len > 1) { 8073 if (*++pc->p == '\n') { 8074 pc->linenr++; 8075 } 8076 pc->len--; 8077 } 8078 break; 8079 8080 case '{': 8081 level++; 8082 break; 8083 8084 case '}': 8085 if (--level == 0) { 8086 pc->tend = pc->p - 1; 8087 pc->p++; 8088 pc->len--; 8089 return; 8090 } 8091 break; 8092 8093 case '\n': 8094 pc->linenr++; 8095 break; 8096 } 8097 pc->p++; 8098 pc->len--; 8099 } 8100 pc->missing.ch = '{'; 8101 pc->missing.line = pc->tline; 8102 pc->tend = pc->p - 1; 8103 } 8104 8105 static int JimParseSubQuote(struct JimParserCtx *pc) 8106 { 8107 int tt = JIM_TT_STR; 8108 int line = pc->tline; 8109 8110 8111 pc->p++; 8112 pc->len--; 8113 while (pc->len) { 8114 switch (*pc->p) { 8115 case '\\': 8116 if (pc->len > 1) { 8117 if (*++pc->p == '\n') { 8118 pc->linenr++; 8119 } 8120 pc->len--; 8121 tt = JIM_TT_ESC; 8122 } 8123 break; 8124 8125 case '"': 8126 pc->tend = pc->p - 1; 8127 pc->p++; 8128 pc->len--; 8129 return tt; 8130 8131 case '[': 8132 JimParseSubCmd(pc); 8133 tt = JIM_TT_ESC; 8134 continue; 8135 8136 case '\n': 8137 pc->linenr++; 8138 break; 8139 8140 case '$': 8141 tt = JIM_TT_ESC; 8142 break; 8143 } 8144 pc->p++; 8145 pc->len--; 8146 } 8147 pc->missing.ch = '"'; 8148 pc->missing.line = line; 8149 pc->tend = pc->p - 1; 8150 return tt; 8151 } 8152 8153 static void JimParseSubCmd(struct JimParserCtx *pc) 8154 { 8155 int level = 1; 8156 int startofword = 1; 8157 int line = pc->tline; 8158 8159 8160 pc->p++; 8161 pc->len--; 8162 while (pc->len) { 8163 switch (*pc->p) { 8164 case '\\': 8165 if (pc->len > 1) { 8166 if (*++pc->p == '\n') { 8167 pc->linenr++; 8168 } 8169 pc->len--; 8170 } 8171 break; 8172 8173 case '[': 8174 level++; 8175 break; 8176 8177 case ']': 8178 if (--level == 0) { 8179 pc->tend = pc->p - 1; 8180 pc->p++; 8181 pc->len--; 8182 return; 8183 } 8184 break; 8185 8186 case '"': 8187 if (startofword) { 8188 JimParseSubQuote(pc); 8189 if (pc->missing.ch == '"') { 8190 return; 8191 } 8192 continue; 8193 } 8194 break; 8195 8196 case '{': 8197 JimParseSubBrace(pc); 8198 startofword = 0; 8199 continue; 8200 8201 case '\n': 8202 pc->linenr++; 8203 break; 8204 } 8205 startofword = isspace(UCHAR(*pc->p)); 8206 pc->p++; 8207 pc->len--; 8208 } 8209 pc->missing.ch = '['; 8210 pc->missing.line = line; 8211 pc->tend = pc->p - 1; 8212 } 8213 8214 static int JimParseBrace(struct JimParserCtx *pc) 8215 { 8216 pc->tstart = pc->p + 1; 8217 pc->tline = pc->linenr; 8218 pc->tt = JIM_TT_STR; 8219 JimParseSubBrace(pc); 8220 return JIM_OK; 8221 } 8222 8223 static int JimParseCmd(struct JimParserCtx *pc) 8224 { 8225 pc->tstart = pc->p + 1; 8226 pc->tline = pc->linenr; 8227 pc->tt = JIM_TT_CMD; 8228 JimParseSubCmd(pc); 8229 return JIM_OK; 8230 } 8231 8232 static int JimParseQuote(struct JimParserCtx *pc) 8233 { 8234 pc->tstart = pc->p + 1; 8235 pc->tline = pc->linenr; 8236 pc->tt = JimParseSubQuote(pc); 8237 return JIM_OK; 8238 } 8239 8240 static int JimParseVar(struct JimParserCtx *pc) 8241 { 8242 8243 pc->p++; 8244 pc->len--; 8245 8246 #ifdef EXPRSUGAR_BRACKET 8247 if (*pc->p == '[') { 8248 8249 JimParseCmd(pc); 8250 pc->tt = JIM_TT_EXPRSUGAR; 8251 return JIM_OK; 8252 } 8253 #endif 8254 8255 pc->tstart = pc->p; 8256 pc->tt = JIM_TT_VAR; 8257 pc->tline = pc->linenr; 8258 8259 if (*pc->p == '{') { 8260 pc->tstart = ++pc->p; 8261 pc->len--; 8262 8263 while (pc->len && *pc->p != '}') { 8264 if (*pc->p == '\n') { 8265 pc->linenr++; 8266 } 8267 pc->p++; 8268 pc->len--; 8269 } 8270 pc->tend = pc->p - 1; 8271 if (pc->len) { 8272 pc->p++; 8273 pc->len--; 8274 } 8275 } 8276 else { 8277 while (1) { 8278 8279 if (pc->p[0] == ':' && pc->p[1] == ':') { 8280 while (*pc->p == ':') { 8281 pc->p++; 8282 pc->len--; 8283 } 8284 continue; 8285 } 8286 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) { 8287 pc->p++; 8288 pc->len--; 8289 continue; 8290 } 8291 break; 8292 } 8293 8294 if (*pc->p == '(') { 8295 int count = 1; 8296 const char *paren = NULL; 8297 8298 pc->tt = JIM_TT_DICTSUGAR; 8299 8300 while (count && pc->len) { 8301 pc->p++; 8302 pc->len--; 8303 if (*pc->p == '\\' && pc->len >= 1) { 8304 pc->p++; 8305 pc->len--; 8306 } 8307 else if (*pc->p == '(') { 8308 count++; 8309 } 8310 else if (*pc->p == ')') { 8311 paren = pc->p; 8312 count--; 8313 } 8314 } 8315 if (count == 0) { 8316 pc->p++; 8317 pc->len--; 8318 } 8319 else if (paren) { 8320 8321 paren++; 8322 pc->len += (pc->p - paren); 8323 pc->p = paren; 8324 } 8325 #ifndef EXPRSUGAR_BRACKET 8326 if (*pc->tstart == '(') { 8327 pc->tt = JIM_TT_EXPRSUGAR; 8328 } 8329 #endif 8330 } 8331 pc->tend = pc->p - 1; 8332 } 8333 if (pc->tstart == pc->p) { 8334 pc->p--; 8335 pc->len++; 8336 return JIM_ERR; 8337 } 8338 return JIM_OK; 8339 } 8340 8341 static int JimParseStr(struct JimParserCtx *pc) 8342 { 8343 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL || 8344 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) { 8345 8346 if (*pc->p == '{') { 8347 return JimParseBrace(pc); 8348 } 8349 if (*pc->p == '"') { 8350 pc->inquote = 1; 8351 pc->p++; 8352 pc->len--; 8353 8354 pc->missing.line = pc->tline; 8355 } 8356 } 8357 pc->tstart = pc->p; 8358 pc->tline = pc->linenr; 8359 while (1) { 8360 if (pc->len == 0) { 8361 if (pc->inquote) { 8362 pc->missing.ch = '"'; 8363 } 8364 pc->tend = pc->p - 1; 8365 pc->tt = JIM_TT_ESC; 8366 return JIM_OK; 8367 } 8368 switch (*pc->p) { 8369 case '\\': 8370 if (!pc->inquote && *(pc->p + 1) == '\n') { 8371 pc->tend = pc->p - 1; 8372 pc->tt = JIM_TT_ESC; 8373 return JIM_OK; 8374 } 8375 if (pc->len >= 2) { 8376 if (*(pc->p + 1) == '\n') { 8377 pc->linenr++; 8378 } 8379 pc->p++; 8380 pc->len--; 8381 } 8382 else if (pc->len == 1) { 8383 8384 pc->missing.ch = '\\'; 8385 } 8386 break; 8387 case '(': 8388 8389 if (pc->len > 1 && pc->p[1] != '$') { 8390 break; 8391 } 8392 8393 case ')': 8394 8395 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) { 8396 if (pc->p == pc->tstart) { 8397 8398 pc->p++; 8399 pc->len--; 8400 } 8401 pc->tend = pc->p - 1; 8402 pc->tt = JIM_TT_ESC; 8403 return JIM_OK; 8404 } 8405 break; 8406 8407 case '$': 8408 case '[': 8409 pc->tend = pc->p - 1; 8410 pc->tt = JIM_TT_ESC; 8411 return JIM_OK; 8412 case ' ': 8413 case '\t': 8414 case '\n': 8415 case '\r': 8416 case '\f': 8417 case ';': 8418 if (!pc->inquote) { 8419 pc->tend = pc->p - 1; 8420 pc->tt = JIM_TT_ESC; 8421 return JIM_OK; 8422 } 8423 else if (*pc->p == '\n') { 8424 pc->linenr++; 8425 } 8426 break; 8427 case '"': 8428 if (pc->inquote) { 8429 pc->tend = pc->p - 1; 8430 pc->tt = JIM_TT_ESC; 8431 pc->p++; 8432 pc->len--; 8433 pc->inquote = 0; 8434 return JIM_OK; 8435 } 8436 break; 8437 } 8438 pc->p++; 8439 pc->len--; 8440 } 8441 return JIM_OK; 8442 } 8443 8444 static int JimParseComment(struct JimParserCtx *pc) 8445 { 8446 while (*pc->p) { 8447 if (*pc->p == '\\') { 8448 pc->p++; 8449 pc->len--; 8450 if (pc->len == 0) { 8451 pc->missing.ch = '\\'; 8452 return JIM_OK; 8453 } 8454 if (*pc->p == '\n') { 8455 pc->linenr++; 8456 } 8457 } 8458 else if (*pc->p == '\n') { 8459 pc->p++; 8460 pc->len--; 8461 pc->linenr++; 8462 break; 8463 } 8464 pc->p++; 8465 pc->len--; 8466 } 8467 return JIM_OK; 8468 } 8469 8470 8471 static int xdigitval(int c) 8472 { 8473 if (c >= '0' && c <= '9') 8474 return c - '0'; 8475 if (c >= 'a' && c <= 'f') 8476 return c - 'a' + 10; 8477 if (c >= 'A' && c <= 'F') 8478 return c - 'A' + 10; 8479 return -1; 8480 } 8481 8482 static int odigitval(int c) 8483 { 8484 if (c >= '0' && c <= '7') 8485 return c - '0'; 8486 return -1; 8487 } 8488 8489 static int JimEscape(char *dest, const char *s, int slen) 8490 { 8491 char *p = dest; 8492 int i, len; 8493 8494 for (i = 0; i < slen; i++) { 8495 switch (s[i]) { 8496 case '\\': 8497 switch (s[i + 1]) { 8498 case 'a': 8499 *p++ = 0x7; 8500 i++; 8501 break; 8502 case 'b': 8503 *p++ = 0x8; 8504 i++; 8505 break; 8506 case 'f': 8507 *p++ = 0xc; 8508 i++; 8509 break; 8510 case 'n': 8511 *p++ = 0xa; 8512 i++; 8513 break; 8514 case 'r': 8515 *p++ = 0xd; 8516 i++; 8517 break; 8518 case 't': 8519 *p++ = 0x9; 8520 i++; 8521 break; 8522 case 'u': 8523 case 'U': 8524 case 'x': 8525 { 8526 unsigned val = 0; 8527 int k; 8528 int maxchars = 2; 8529 8530 i++; 8531 8532 if (s[i] == 'U') { 8533 maxchars = 8; 8534 } 8535 else if (s[i] == 'u') { 8536 if (s[i + 1] == '{') { 8537 maxchars = 6; 8538 i++; 8539 } 8540 else { 8541 maxchars = 4; 8542 } 8543 } 8544 8545 for (k = 0; k < maxchars; k++) { 8546 int c = xdigitval(s[i + k + 1]); 8547 if (c == -1) { 8548 break; 8549 } 8550 val = (val << 4) | c; 8551 } 8552 8553 if (s[i] == '{') { 8554 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') { 8555 8556 i--; 8557 k = 0; 8558 } 8559 else { 8560 8561 k++; 8562 } 8563 } 8564 if (k) { 8565 8566 if (s[i] == 'x') { 8567 *p++ = val; 8568 } 8569 else { 8570 p += utf8_fromunicode(p, val); 8571 } 8572 i += k; 8573 break; 8574 } 8575 8576 *p++ = s[i]; 8577 } 8578 break; 8579 case 'v': 8580 *p++ = 0xb; 8581 i++; 8582 break; 8583 case '\0': 8584 *p++ = '\\'; 8585 i++; 8586 break; 8587 case '\n': 8588 8589 *p++ = ' '; 8590 do { 8591 i++; 8592 } while (s[i + 1] == ' ' || s[i + 1] == '\t'); 8593 break; 8594 case '0': 8595 case '1': 8596 case '2': 8597 case '3': 8598 case '4': 8599 case '5': 8600 case '6': 8601 case '7': 8602 8603 { 8604 int val = 0; 8605 int c = odigitval(s[i + 1]); 8606 8607 val = c; 8608 c = odigitval(s[i + 2]); 8609 if (c == -1) { 8610 *p++ = val; 8611 i++; 8612 break; 8613 } 8614 val = (val * 8) + c; 8615 c = odigitval(s[i + 3]); 8616 if (c == -1) { 8617 *p++ = val; 8618 i += 2; 8619 break; 8620 } 8621 val = (val * 8) + c; 8622 *p++ = val; 8623 i += 3; 8624 } 8625 break; 8626 default: 8627 *p++ = s[i + 1]; 8628 i++; 8629 break; 8630 } 8631 break; 8632 default: 8633 *p++ = s[i]; 8634 break; 8635 } 8636 } 8637 len = p - dest; 8638 *p = '\0'; 8639 return len; 8640 } 8641 8642 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc) 8643 { 8644 const char *start, *end; 8645 char *token; 8646 int len; 8647 8648 start = pc->tstart; 8649 end = pc->tend; 8650 len = (end - start) + 1; 8651 if (len < 0) { 8652 len = 0; 8653 } 8654 token = Jim_Alloc(len + 1); 8655 if (pc->tt != JIM_TT_ESC) { 8656 8657 memcpy(token, start, len); 8658 token[len] = '\0'; 8659 } 8660 else { 8661 8662 len = JimEscape(token, start, len); 8663 } 8664 8665 return Jim_NewStringObjNoAlloc(interp, token, len); 8666 } 8667 8668 static int JimParseListSep(struct JimParserCtx *pc); 8669 static int JimParseListStr(struct JimParserCtx *pc); 8670 static int JimParseListQuote(struct JimParserCtx *pc); 8671 8672 static int JimParseList(struct JimParserCtx *pc) 8673 { 8674 if (isspace(UCHAR(*pc->p))) { 8675 return JimParseListSep(pc); 8676 } 8677 switch (*pc->p) { 8678 case '"': 8679 return JimParseListQuote(pc); 8680 8681 case '{': 8682 return JimParseBrace(pc); 8683 8684 default: 8685 if (pc->len) { 8686 return JimParseListStr(pc); 8687 } 8688 break; 8689 } 8690 8691 pc->tstart = pc->tend = pc->p; 8692 pc->tline = pc->linenr; 8693 pc->tt = JIM_TT_EOL; 8694 pc->eof = 1; 8695 return JIM_OK; 8696 } 8697 8698 static int JimParseListSep(struct JimParserCtx *pc) 8699 { 8700 pc->tstart = pc->p; 8701 pc->tline = pc->linenr; 8702 while (isspace(UCHAR(*pc->p))) { 8703 if (*pc->p == '\n') { 8704 pc->linenr++; 8705 } 8706 pc->p++; 8707 pc->len--; 8708 } 8709 pc->tend = pc->p - 1; 8710 pc->tt = JIM_TT_SEP; 8711 return JIM_OK; 8712 } 8713 8714 static int JimParseListQuote(struct JimParserCtx *pc) 8715 { 8716 pc->p++; 8717 pc->len--; 8718 8719 pc->tstart = pc->p; 8720 pc->tline = pc->linenr; 8721 pc->tt = JIM_TT_STR; 8722 8723 while (pc->len) { 8724 switch (*pc->p) { 8725 case '\\': 8726 pc->tt = JIM_TT_ESC; 8727 if (--pc->len == 0) { 8728 8729 pc->tend = pc->p; 8730 return JIM_OK; 8731 } 8732 pc->p++; 8733 break; 8734 case '\n': 8735 pc->linenr++; 8736 break; 8737 case '"': 8738 pc->tend = pc->p - 1; 8739 pc->p++; 8740 pc->len--; 8741 return JIM_OK; 8742 } 8743 pc->p++; 8744 pc->len--; 8745 } 8746 8747 pc->tend = pc->p - 1; 8748 return JIM_OK; 8749 } 8750 8751 static int JimParseListStr(struct JimParserCtx *pc) 8752 { 8753 pc->tstart = pc->p; 8754 pc->tline = pc->linenr; 8755 pc->tt = JIM_TT_STR; 8756 8757 while (pc->len) { 8758 if (isspace(UCHAR(*pc->p))) { 8759 pc->tend = pc->p - 1; 8760 return JIM_OK; 8761 } 8762 if (*pc->p == '\\') { 8763 if (--pc->len == 0) { 8764 8765 pc->tend = pc->p; 8766 return JIM_OK; 8767 } 8768 pc->tt = JIM_TT_ESC; 8769 pc->p++; 8770 } 8771 pc->p++; 8772 pc->len--; 8773 } 8774 pc->tend = pc->p - 1; 8775 return JIM_OK; 8776 } 8777 8778 8779 8780 Jim_Obj *Jim_NewObj(Jim_Interp *interp) 8781 { 8782 Jim_Obj *objPtr; 8783 8784 8785 if (interp->freeList != NULL) { 8786 8787 objPtr = interp->freeList; 8788 interp->freeList = objPtr->nextObjPtr; 8789 } 8790 else { 8791 8792 objPtr = Jim_Alloc(sizeof(*objPtr)); 8793 } 8794 8795 objPtr->refCount = 0; 8796 8797 8798 objPtr->prevObjPtr = NULL; 8799 objPtr->nextObjPtr = interp->liveList; 8800 if (interp->liveList) 8801 interp->liveList->prevObjPtr = objPtr; 8802 interp->liveList = objPtr; 8803 8804 return objPtr; 8805 } 8806 8807 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr) 8808 { 8809 8810 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr, 8811 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>")); 8812 8813 8814 Jim_FreeIntRep(interp, objPtr); 8815 8816 if (objPtr->bytes != NULL) { 8817 if (objPtr->bytes != JimEmptyStringRep) 8818 Jim_Free(objPtr->bytes); 8819 } 8820 8821 if (objPtr->prevObjPtr) 8822 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr; 8823 if (objPtr->nextObjPtr) 8824 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr; 8825 if (interp->liveList == objPtr) 8826 interp->liveList = objPtr->nextObjPtr; 8827 #ifdef JIM_DISABLE_OBJECT_POOL 8828 Jim_Free(objPtr); 8829 #else 8830 8831 objPtr->prevObjPtr = NULL; 8832 objPtr->nextObjPtr = interp->freeList; 8833 if (interp->freeList) 8834 interp->freeList->prevObjPtr = objPtr; 8835 interp->freeList = objPtr; 8836 objPtr->refCount = -1; 8837 #endif 8838 } 8839 8840 8841 void Jim_InvalidateStringRep(Jim_Obj *objPtr) 8842 { 8843 if (objPtr->bytes != NULL) { 8844 if (objPtr->bytes != JimEmptyStringRep) 8845 Jim_Free(objPtr->bytes); 8846 } 8847 objPtr->bytes = NULL; 8848 } 8849 8850 8851 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr) 8852 { 8853 Jim_Obj *dupPtr; 8854 8855 dupPtr = Jim_NewObj(interp); 8856 if (objPtr->bytes == NULL) { 8857 8858 dupPtr->bytes = NULL; 8859 } 8860 else if (objPtr->length == 0) { 8861 dupPtr->bytes = JimEmptyStringRep; 8862 dupPtr->length = 0; 8863 dupPtr->typePtr = NULL; 8864 return dupPtr; 8865 } 8866 else { 8867 dupPtr->bytes = Jim_Alloc(objPtr->length + 1); 8868 dupPtr->length = objPtr->length; 8869 8870 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1); 8871 } 8872 8873 8874 dupPtr->typePtr = objPtr->typePtr; 8875 if (objPtr->typePtr != NULL) { 8876 if (objPtr->typePtr->dupIntRepProc == NULL) { 8877 dupPtr->internalRep = objPtr->internalRep; 8878 } 8879 else { 8880 8881 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr); 8882 } 8883 } 8884 return dupPtr; 8885 } 8886 8887 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr) 8888 { 8889 if (objPtr->bytes == NULL) { 8890 8891 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); 8892 objPtr->typePtr->updateStringProc(objPtr); 8893 } 8894 if (lenPtr) 8895 *lenPtr = objPtr->length; 8896 return objPtr->bytes; 8897 } 8898 8899 8900 int Jim_Length(Jim_Obj *objPtr) 8901 { 8902 if (objPtr->bytes == NULL) { 8903 8904 Jim_GetString(objPtr, NULL); 8905 } 8906 return objPtr->length; 8907 } 8908 8909 8910 const char *Jim_String(Jim_Obj *objPtr) 8911 { 8912 if (objPtr->bytes == NULL) { 8913 8914 Jim_GetString(objPtr, NULL); 8915 } 8916 return objPtr->bytes; 8917 } 8918 8919 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str) 8920 { 8921 objPtr->bytes = Jim_StrDup(str); 8922 objPtr->length = strlen(str); 8923 } 8924 8925 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 8926 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 8927 8928 static const Jim_ObjType dictSubstObjType = { 8929 "dict-substitution", 8930 FreeDictSubstInternalRep, 8931 DupDictSubstInternalRep, 8932 NULL, 8933 JIM_TYPE_NONE, 8934 }; 8935 8936 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 8937 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 8938 8939 static const Jim_ObjType interpolatedObjType = { 8940 "interpolated", 8941 FreeInterpolatedInternalRep, 8942 DupInterpolatedInternalRep, 8943 NULL, 8944 JIM_TYPE_NONE, 8945 }; 8946 8947 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 8948 { 8949 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); 8950 } 8951 8952 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 8953 { 8954 8955 dupPtr->internalRep = srcPtr->internalRep; 8956 8957 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr); 8958 } 8959 8960 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 8961 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 8962 8963 static const Jim_ObjType stringObjType = { 8964 "string", 8965 NULL, 8966 DupStringInternalRep, 8967 NULL, 8968 JIM_TYPE_REFERENCES, 8969 }; 8970 8971 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 8972 { 8973 JIM_NOTUSED(interp); 8974 8975 dupPtr->internalRep.strValue.maxLength = srcPtr->length; 8976 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength; 8977 } 8978 8979 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 8980 { 8981 if (objPtr->typePtr != &stringObjType) { 8982 8983 if (objPtr->bytes == NULL) { 8984 8985 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); 8986 objPtr->typePtr->updateStringProc(objPtr); 8987 } 8988 8989 Jim_FreeIntRep(interp, objPtr); 8990 8991 objPtr->typePtr = &stringObjType; 8992 objPtr->internalRep.strValue.maxLength = objPtr->length; 8993 8994 objPtr->internalRep.strValue.charLength = -1; 8995 } 8996 return JIM_OK; 8997 } 8998 8999 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr) 9000 { 9001 #ifdef JIM_UTF8 9002 SetStringFromAny(interp, objPtr); 9003 9004 if (objPtr->internalRep.strValue.charLength < 0) { 9005 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length); 9006 } 9007 return objPtr->internalRep.strValue.charLength; 9008 #else 9009 return Jim_Length(objPtr); 9010 #endif 9011 } 9012 9013 9014 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len) 9015 { 9016 Jim_Obj *objPtr = Jim_NewObj(interp); 9017 9018 9019 if (len == -1) 9020 len = strlen(s); 9021 9022 if (len == 0) { 9023 objPtr->bytes = JimEmptyStringRep; 9024 } 9025 else { 9026 objPtr->bytes = Jim_StrDupLen(s, len); 9027 } 9028 objPtr->length = len; 9029 9030 9031 objPtr->typePtr = NULL; 9032 return objPtr; 9033 } 9034 9035 9036 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen) 9037 { 9038 #ifdef JIM_UTF8 9039 9040 int bytelen = utf8_index(s, charlen); 9041 9042 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen); 9043 9044 9045 objPtr->typePtr = &stringObjType; 9046 objPtr->internalRep.strValue.maxLength = bytelen; 9047 objPtr->internalRep.strValue.charLength = charlen; 9048 9049 return objPtr; 9050 #else 9051 return Jim_NewStringObj(interp, s, charlen); 9052 #endif 9053 } 9054 9055 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len) 9056 { 9057 Jim_Obj *objPtr = Jim_NewObj(interp); 9058 9059 objPtr->bytes = s; 9060 objPtr->length = (len == -1) ? strlen(s) : len; 9061 objPtr->typePtr = NULL; 9062 return objPtr; 9063 } 9064 9065 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len) 9066 { 9067 int needlen; 9068 9069 if (len == -1) 9070 len = strlen(str); 9071 needlen = objPtr->length + len; 9072 if (objPtr->internalRep.strValue.maxLength < needlen || 9073 objPtr->internalRep.strValue.maxLength == 0) { 9074 needlen *= 2; 9075 9076 if (needlen < 7) { 9077 needlen = 7; 9078 } 9079 if (objPtr->bytes == JimEmptyStringRep) { 9080 objPtr->bytes = Jim_Alloc(needlen + 1); 9081 } 9082 else { 9083 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1); 9084 } 9085 objPtr->internalRep.strValue.maxLength = needlen; 9086 } 9087 memcpy(objPtr->bytes + objPtr->length, str, len); 9088 objPtr->bytes[objPtr->length + len] = '\0'; 9089 9090 if (objPtr->internalRep.strValue.charLength >= 0) { 9091 9092 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len); 9093 } 9094 objPtr->length += len; 9095 } 9096 9097 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len) 9098 { 9099 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object")); 9100 SetStringFromAny(interp, objPtr); 9101 StringAppendString(objPtr, str, len); 9102 } 9103 9104 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr) 9105 { 9106 int len; 9107 const char *str = Jim_GetString(appendObjPtr, &len); 9108 Jim_AppendString(interp, objPtr, str, len); 9109 } 9110 9111 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...) 9112 { 9113 va_list ap; 9114 9115 SetStringFromAny(interp, objPtr); 9116 va_start(ap, objPtr); 9117 while (1) { 9118 const char *s = va_arg(ap, const char *); 9119 9120 if (s == NULL) 9121 break; 9122 Jim_AppendString(interp, objPtr, s, -1); 9123 } 9124 va_end(ap); 9125 } 9126 9127 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr) 9128 { 9129 if (aObjPtr == bObjPtr) { 9130 return 1; 9131 } 9132 else { 9133 int Alen, Blen; 9134 const char *sA = Jim_GetString(aObjPtr, &Alen); 9135 const char *sB = Jim_GetString(bObjPtr, &Blen); 9136 9137 return Alen == Blen && memcmp(sA, sB, Alen) == 0; 9138 } 9139 } 9140 9141 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase) 9142 { 9143 int plen, slen; 9144 const char *pattern = Jim_GetString(patternObjPtr, &plen); 9145 const char *string = Jim_GetString(objPtr, &slen); 9146 return JimGlobMatch(pattern, plen, string, slen, nocase); 9147 } 9148 9149 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase) 9150 { 9151 const char *s1 = Jim_String(firstObjPtr); 9152 int l1 = Jim_Utf8Length(interp, firstObjPtr); 9153 const char *s2 = Jim_String(secondObjPtr); 9154 int l2 = Jim_Utf8Length(interp, secondObjPtr); 9155 return JimStringCompareUtf8(s1, l1, s2, l2, nocase); 9156 } 9157 9158 static int JimRelToAbsIndex(int len, int idx) 9159 { 9160 if (idx < 0 && idx > -INT_MAX) 9161 return len + idx; 9162 return idx; 9163 } 9164 9165 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr) 9166 { 9167 int rangeLen; 9168 9169 if (*firstPtr > *lastPtr) { 9170 rangeLen = 0; 9171 } 9172 else { 9173 rangeLen = *lastPtr - *firstPtr + 1; 9174 if (rangeLen) { 9175 if (*firstPtr < 0) { 9176 rangeLen += *firstPtr; 9177 *firstPtr = 0; 9178 } 9179 if (*lastPtr >= len) { 9180 rangeLen -= (*lastPtr - (len - 1)); 9181 *lastPtr = len - 1; 9182 } 9183 } 9184 } 9185 if (rangeLen < 0) 9186 rangeLen = 0; 9187 9188 *rangeLenPtr = rangeLen; 9189 } 9190 9191 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, 9192 int len, int *first, int *last, int *range) 9193 { 9194 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) { 9195 return JIM_ERR; 9196 } 9197 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) { 9198 return JIM_ERR; 9199 } 9200 *first = JimRelToAbsIndex(len, *first); 9201 *last = JimRelToAbsIndex(len, *last); 9202 JimRelToAbsRange(len, first, last, range); 9203 return JIM_OK; 9204 } 9205 9206 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp, 9207 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr) 9208 { 9209 int first, last; 9210 const char *str; 9211 int rangeLen; 9212 int bytelen; 9213 9214 str = Jim_GetString(strObjPtr, &bytelen); 9215 9216 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) { 9217 return NULL; 9218 } 9219 9220 if (first == 0 && rangeLen == bytelen) { 9221 return strObjPtr; 9222 } 9223 return Jim_NewStringObj(interp, str + first, rangeLen); 9224 } 9225 9226 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp, 9227 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr) 9228 { 9229 #ifdef JIM_UTF8 9230 int first, last; 9231 const char *str; 9232 int len, rangeLen; 9233 int bytelen; 9234 9235 str = Jim_GetString(strObjPtr, &bytelen); 9236 len = Jim_Utf8Length(interp, strObjPtr); 9237 9238 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) { 9239 return NULL; 9240 } 9241 9242 if (first == 0 && rangeLen == len) { 9243 return strObjPtr; 9244 } 9245 if (len == bytelen) { 9246 9247 return Jim_NewStringObj(interp, str + first, rangeLen); 9248 } 9249 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen); 9250 #else 9251 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr); 9252 #endif 9253 } 9254 9255 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp, 9256 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj) 9257 { 9258 int first, last; 9259 const char *str; 9260 int len, rangeLen; 9261 Jim_Obj *objPtr; 9262 9263 len = Jim_Utf8Length(interp, strObjPtr); 9264 9265 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) { 9266 return NULL; 9267 } 9268 9269 if (last < first) { 9270 return strObjPtr; 9271 } 9272 9273 str = Jim_String(strObjPtr); 9274 9275 9276 objPtr = Jim_NewStringObjUtf8(interp, str, first); 9277 9278 9279 if (newStrObj) { 9280 Jim_AppendObj(interp, objPtr, newStrObj); 9281 } 9282 9283 9284 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1); 9285 9286 return objPtr; 9287 } 9288 9289 static void JimStrCopyUpperLower(char *dest, const char *str, int uc) 9290 { 9291 while (*str) { 9292 int c; 9293 str += utf8_tounicode(str, &c); 9294 dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c)); 9295 } 9296 *dest = 0; 9297 } 9298 9299 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr) 9300 { 9301 char *buf; 9302 int len; 9303 const char *str; 9304 9305 str = Jim_GetString(strObjPtr, &len); 9306 9307 #ifdef JIM_UTF8 9308 len *= 2; 9309 #endif 9310 buf = Jim_Alloc(len + 1); 9311 JimStrCopyUpperLower(buf, str, 0); 9312 return Jim_NewStringObjNoAlloc(interp, buf, -1); 9313 } 9314 9315 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr) 9316 { 9317 char *buf; 9318 const char *str; 9319 int len; 9320 9321 str = Jim_GetString(strObjPtr, &len); 9322 9323 #ifdef JIM_UTF8 9324 len *= 2; 9325 #endif 9326 buf = Jim_Alloc(len + 1); 9327 JimStrCopyUpperLower(buf, str, 1); 9328 return Jim_NewStringObjNoAlloc(interp, buf, -1); 9329 } 9330 9331 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr) 9332 { 9333 char *buf, *p; 9334 int len; 9335 int c; 9336 const char *str; 9337 9338 str = Jim_GetString(strObjPtr, &len); 9339 9340 #ifdef JIM_UTF8 9341 len *= 2; 9342 #endif 9343 buf = p = Jim_Alloc(len + 1); 9344 9345 str += utf8_tounicode(str, &c); 9346 p += utf8_getchars(p, utf8_title(c)); 9347 9348 JimStrCopyUpperLower(p, str, 0); 9349 9350 return Jim_NewStringObjNoAlloc(interp, buf, -1); 9351 } 9352 9353 static const char *utf8_memchr(const char *str, int len, int c) 9354 { 9355 #ifdef JIM_UTF8 9356 while (len) { 9357 int sc; 9358 int n = utf8_tounicode(str, &sc); 9359 if (sc == c) { 9360 return str; 9361 } 9362 str += n; 9363 len -= n; 9364 } 9365 return NULL; 9366 #else 9367 return memchr(str, c, len); 9368 #endif 9369 } 9370 9371 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen) 9372 { 9373 while (len) { 9374 int c; 9375 int n = utf8_tounicode(str, &c); 9376 9377 if (utf8_memchr(trimchars, trimlen, c) == NULL) { 9378 9379 break; 9380 } 9381 str += n; 9382 len -= n; 9383 } 9384 return str; 9385 } 9386 9387 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen) 9388 { 9389 str += len; 9390 9391 while (len) { 9392 int c; 9393 int n = utf8_prev_len(str, len); 9394 9395 len -= n; 9396 str -= n; 9397 9398 n = utf8_tounicode(str, &c); 9399 9400 if (utf8_memchr(trimchars, trimlen, c) == NULL) { 9401 return str + n; 9402 } 9403 } 9404 9405 return NULL; 9406 } 9407 9408 static const char default_trim_chars[] = " \t\n\r"; 9409 9410 static int default_trim_chars_len = sizeof(default_trim_chars); 9411 9412 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) 9413 { 9414 int len; 9415 const char *str = Jim_GetString(strObjPtr, &len); 9416 const char *trimchars = default_trim_chars; 9417 int trimcharslen = default_trim_chars_len; 9418 const char *newstr; 9419 9420 if (trimcharsObjPtr) { 9421 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen); 9422 } 9423 9424 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen); 9425 if (newstr == str) { 9426 return strObjPtr; 9427 } 9428 9429 return Jim_NewStringObj(interp, newstr, len - (newstr - str)); 9430 } 9431 9432 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) 9433 { 9434 int len; 9435 const char *trimchars = default_trim_chars; 9436 int trimcharslen = default_trim_chars_len; 9437 const char *nontrim; 9438 9439 if (trimcharsObjPtr) { 9440 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen); 9441 } 9442 9443 SetStringFromAny(interp, strObjPtr); 9444 9445 len = Jim_Length(strObjPtr); 9446 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen); 9447 9448 if (nontrim == NULL) { 9449 9450 return Jim_NewEmptyStringObj(interp); 9451 } 9452 if (nontrim == strObjPtr->bytes + len) { 9453 9454 return strObjPtr; 9455 } 9456 9457 if (Jim_IsShared(strObjPtr)) { 9458 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes)); 9459 } 9460 else { 9461 9462 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0; 9463 strObjPtr->length = (nontrim - strObjPtr->bytes); 9464 } 9465 9466 return strObjPtr; 9467 } 9468 9469 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) 9470 { 9471 9472 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr); 9473 9474 9475 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr); 9476 9477 9478 if (objPtr != strObjPtr && objPtr->refCount == 0) { 9479 9480 Jim_FreeNewObj(interp, objPtr); 9481 } 9482 9483 return strObjPtr; 9484 } 9485 9486 9487 #ifdef HAVE_ISASCII 9488 #define jim_isascii isascii 9489 #else 9490 static int jim_isascii(int c) 9491 { 9492 return !(c & ~0x7f); 9493 } 9494 #endif 9495 9496 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict) 9497 { 9498 static const char * const strclassnames[] = { 9499 "integer", "alpha", "alnum", "ascii", "digit", 9500 "double", "lower", "upper", "space", "xdigit", 9501 "control", "print", "graph", "punct", "boolean", 9502 NULL 9503 }; 9504 enum { 9505 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT, 9506 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT, 9507 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN, 9508 }; 9509 int strclass; 9510 int len; 9511 int i; 9512 const char *str; 9513 int (*isclassfunc)(int c) = NULL; 9514 9515 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 9516 return JIM_ERR; 9517 } 9518 9519 str = Jim_GetString(strObjPtr, &len); 9520 if (len == 0) { 9521 Jim_SetResultBool(interp, !strict); 9522 return JIM_OK; 9523 } 9524 9525 switch (strclass) { 9526 case STR_IS_INTEGER: 9527 { 9528 jim_wide w; 9529 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK); 9530 return JIM_OK; 9531 } 9532 9533 case STR_IS_DOUBLE: 9534 { 9535 double d; 9536 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE); 9537 return JIM_OK; 9538 } 9539 9540 case STR_IS_BOOLEAN: 9541 { 9542 int b; 9543 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK); 9544 return JIM_OK; 9545 } 9546 9547 case STR_IS_ALPHA: isclassfunc = isalpha; break; 9548 case STR_IS_ALNUM: isclassfunc = isalnum; break; 9549 case STR_IS_ASCII: isclassfunc = jim_isascii; break; 9550 case STR_IS_DIGIT: isclassfunc = isdigit; break; 9551 case STR_IS_LOWER: isclassfunc = islower; break; 9552 case STR_IS_UPPER: isclassfunc = isupper; break; 9553 case STR_IS_SPACE: isclassfunc = isspace; break; 9554 case STR_IS_XDIGIT: isclassfunc = isxdigit; break; 9555 case STR_IS_CONTROL: isclassfunc = iscntrl; break; 9556 case STR_IS_PRINT: isclassfunc = isprint; break; 9557 case STR_IS_GRAPH: isclassfunc = isgraph; break; 9558 case STR_IS_PUNCT: isclassfunc = ispunct; break; 9559 default: 9560 return JIM_ERR; 9561 } 9562 9563 for (i = 0; i < len; i++) { 9564 if (!isclassfunc(UCHAR(str[i]))) { 9565 Jim_SetResultBool(interp, 0); 9566 return JIM_OK; 9567 } 9568 } 9569 Jim_SetResultBool(interp, 1); 9570 return JIM_OK; 9571 } 9572 9573 9574 9575 static const Jim_ObjType comparedStringObjType = { 9576 "compared-string", 9577 NULL, 9578 NULL, 9579 NULL, 9580 JIM_TYPE_REFERENCES, 9581 }; 9582 9583 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str) 9584 { 9585 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) { 9586 return 1; 9587 } 9588 else { 9589 if (strcmp(str, Jim_String(objPtr)) != 0) 9590 return 0; 9591 9592 if (objPtr->typePtr != &comparedStringObjType) { 9593 Jim_FreeIntRep(interp, objPtr); 9594 objPtr->typePtr = &comparedStringObjType; 9595 } 9596 objPtr->internalRep.ptr = (char *)str; 9597 return 1; 9598 } 9599 } 9600 9601 static int qsortCompareStringPointers(const void *a, const void *b) 9602 { 9603 char *const *sa = (char *const *)a; 9604 char *const *sb = (char *const *)b; 9605 9606 return strcmp(*sa, *sb); 9607 } 9608 9609 9610 9611 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 9612 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 9613 9614 static const Jim_ObjType sourceObjType = { 9615 "source", 9616 FreeSourceInternalRep, 9617 DupSourceInternalRep, 9618 NULL, 9619 JIM_TYPE_REFERENCES, 9620 }; 9621 9622 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 9623 { 9624 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj); 9625 } 9626 9627 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 9628 { 9629 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue; 9630 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); 9631 } 9632 9633 static const Jim_ObjType scriptLineObjType = { 9634 "scriptline", 9635 NULL, 9636 NULL, 9637 NULL, 9638 JIM_NONE, 9639 }; 9640 9641 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line) 9642 { 9643 Jim_Obj *objPtr; 9644 9645 #ifdef DEBUG_SHOW_SCRIPT 9646 char buf[100]; 9647 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc); 9648 objPtr = Jim_NewStringObj(interp, buf, -1); 9649 #else 9650 objPtr = Jim_NewEmptyStringObj(interp); 9651 #endif 9652 objPtr->typePtr = &scriptLineObjType; 9653 objPtr->internalRep.scriptLineValue.argc = argc; 9654 objPtr->internalRep.scriptLineValue.line = line; 9655 9656 return objPtr; 9657 } 9658 9659 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 9660 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 9661 9662 static const Jim_ObjType scriptObjType = { 9663 "script", 9664 FreeScriptInternalRep, 9665 DupScriptInternalRep, 9666 NULL, 9667 JIM_TYPE_NONE, 9668 }; 9669 9670 typedef struct ScriptToken 9671 { 9672 Jim_Obj *objPtr; 9673 int type; 9674 } ScriptToken; 9675 9676 typedef struct ScriptObj 9677 { 9678 ScriptToken *token; 9679 Jim_Obj *fileNameObj; 9680 int len; 9681 int substFlags; 9682 int inUse; /* Used to share a ScriptObj. Currently 9683 only used by Jim_EvalObj() as protection against 9684 shimmering of the currently evaluated object. */ 9685 int firstline; 9686 int linenr; 9687 int missing; 9688 } ScriptObj; 9689 9690 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 9691 static int JimParseCheckMissing(Jim_Interp *interp, int ch); 9692 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr); 9693 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script); 9694 9695 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 9696 { 9697 int i; 9698 struct ScriptObj *script = (void *)objPtr->internalRep.ptr; 9699 9700 if (--script->inUse != 0) 9701 return; 9702 for (i = 0; i < script->len; i++) { 9703 Jim_DecrRefCount(interp, script->token[i].objPtr); 9704 } 9705 Jim_Free(script->token); 9706 Jim_DecrRefCount(interp, script->fileNameObj); 9707 Jim_Free(script); 9708 } 9709 9710 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 9711 { 9712 JIM_NOTUSED(interp); 9713 JIM_NOTUSED(srcPtr); 9714 9715 dupPtr->typePtr = NULL; 9716 } 9717 9718 typedef struct 9719 { 9720 const char *token; 9721 int len; 9722 int type; 9723 int line; 9724 } ParseToken; 9725 9726 typedef struct 9727 { 9728 9729 ParseToken *list; 9730 int size; 9731 int count; 9732 ParseToken static_list[20]; 9733 } ParseTokenList; 9734 9735 static void ScriptTokenListInit(ParseTokenList *tokenlist) 9736 { 9737 tokenlist->list = tokenlist->static_list; 9738 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken); 9739 tokenlist->count = 0; 9740 } 9741 9742 static void ScriptTokenListFree(ParseTokenList *tokenlist) 9743 { 9744 if (tokenlist->list != tokenlist->static_list) { 9745 Jim_Free(tokenlist->list); 9746 } 9747 } 9748 9749 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type, 9750 int line) 9751 { 9752 ParseToken *t; 9753 9754 if (tokenlist->count == tokenlist->size) { 9755 9756 tokenlist->size *= 2; 9757 if (tokenlist->list != tokenlist->static_list) { 9758 tokenlist->list = 9759 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list)); 9760 } 9761 else { 9762 9763 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list)); 9764 memcpy(tokenlist->list, tokenlist->static_list, 9765 tokenlist->count * sizeof(*tokenlist->list)); 9766 } 9767 } 9768 t = &tokenlist->list[tokenlist->count++]; 9769 t->token = token; 9770 t->len = len; 9771 t->type = type; 9772 t->line = line; 9773 } 9774 9775 static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t) 9776 { 9777 int expand = 1; 9778 int count = 0; 9779 9780 9781 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) { 9782 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) { 9783 9784 expand = -1; 9785 t++; 9786 } 9787 else { 9788 if (script->missing == ' ') { 9789 9790 script->missing = '}'; 9791 script->linenr = t[1].line; 9792 } 9793 } 9794 } 9795 9796 9797 while (!TOKEN_IS_SEP(t->type)) { 9798 t++; 9799 count++; 9800 } 9801 9802 return count * expand; 9803 } 9804 9805 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t) 9806 { 9807 Jim_Obj *objPtr; 9808 9809 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) { 9810 9811 int len = t->len; 9812 char *str = Jim_Alloc(len + 1); 9813 len = JimEscape(str, t->token, len); 9814 objPtr = Jim_NewStringObjNoAlloc(interp, str, len); 9815 } 9816 else { 9817 objPtr = Jim_NewStringObj(interp, t->token, t->len); 9818 } 9819 return objPtr; 9820 } 9821 9822 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, 9823 ParseTokenList *tokenlist) 9824 { 9825 int i; 9826 struct ScriptToken *token; 9827 9828 int lineargs = 0; 9829 9830 ScriptToken *linefirst; 9831 int count; 9832 int linenr; 9833 9834 #ifdef DEBUG_SHOW_SCRIPT_TOKENS 9835 printf("==== Tokens ====\n"); 9836 for (i = 0; i < tokenlist->count; i++) { 9837 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type), 9838 tokenlist->list[i].len, tokenlist->list[i].token); 9839 } 9840 #endif 9841 9842 9843 count = tokenlist->count; 9844 for (i = 0; i < tokenlist->count; i++) { 9845 if (tokenlist->list[i].type == JIM_TT_EOL) { 9846 count++; 9847 } 9848 } 9849 linenr = script->firstline = tokenlist->list[0].line; 9850 9851 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count); 9852 9853 9854 linefirst = token++; 9855 9856 for (i = 0; i < tokenlist->count; ) { 9857 9858 int wordtokens; 9859 9860 9861 while (tokenlist->list[i].type == JIM_TT_SEP) { 9862 i++; 9863 } 9864 9865 wordtokens = JimCountWordTokens(script, tokenlist->list + i); 9866 9867 if (wordtokens == 0) { 9868 9869 if (lineargs) { 9870 linefirst->type = JIM_TT_LINE; 9871 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr); 9872 Jim_IncrRefCount(linefirst->objPtr); 9873 9874 9875 lineargs = 0; 9876 linefirst = token++; 9877 } 9878 i++; 9879 continue; 9880 } 9881 else if (wordtokens != 1) { 9882 9883 token->type = JIM_TT_WORD; 9884 token->objPtr = Jim_NewIntObj(interp, wordtokens); 9885 Jim_IncrRefCount(token->objPtr); 9886 token++; 9887 if (wordtokens < 0) { 9888 9889 i++; 9890 wordtokens = -wordtokens - 1; 9891 lineargs--; 9892 } 9893 } 9894 9895 if (lineargs == 0) { 9896 9897 linenr = tokenlist->list[i].line; 9898 } 9899 lineargs++; 9900 9901 9902 while (wordtokens--) { 9903 const ParseToken *t = &tokenlist->list[i++]; 9904 9905 token->type = t->type; 9906 token->objPtr = JimMakeScriptObj(interp, t); 9907 Jim_IncrRefCount(token->objPtr); 9908 9909 Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line); 9910 token++; 9911 } 9912 } 9913 9914 if (lineargs == 0) { 9915 token--; 9916 } 9917 9918 script->len = token - script->token; 9919 9920 JimPanic((script->len >= count, "allocated script array is too short")); 9921 9922 #ifdef DEBUG_SHOW_SCRIPT 9923 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj)); 9924 for (i = 0; i < script->len; i++) { 9925 const ScriptToken *t = &script->token[i]; 9926 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); 9927 } 9928 #endif 9929 9930 } 9931 9932 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr) 9933 { 9934 ScriptObj *script = JimGetScript(interp, scriptObj); 9935 if (stateCharPtr) { 9936 *stateCharPtr = script->missing; 9937 } 9938 return script->missing == ' ' || script->missing == '}'; 9939 } 9940 9941 static int JimParseCheckMissing(Jim_Interp *interp, int ch) 9942 { 9943 const char *msg; 9944 9945 switch (ch) { 9946 case '\\': 9947 case ' ': 9948 return JIM_OK; 9949 9950 case '[': 9951 msg = "unmatched \"[\""; 9952 break; 9953 case '{': 9954 msg = "missing close-brace"; 9955 break; 9956 case '}': 9957 msg = "extra characters after close-brace"; 9958 break; 9959 case '"': 9960 default: 9961 msg = "missing quote"; 9962 break; 9963 } 9964 9965 Jim_SetResultString(interp, msg, -1); 9966 return JIM_ERR; 9967 } 9968 9969 Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr) 9970 { 9971 int line; 9972 Jim_Obj *fileNameObj; 9973 9974 if (objPtr->typePtr == &sourceObjType) { 9975 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; 9976 line = objPtr->internalRep.sourceValue.lineNumber; 9977 } 9978 else if (objPtr->typePtr == &scriptObjType) { 9979 ScriptObj *script = JimGetScript(interp, objPtr); 9980 fileNameObj = script->fileNameObj; 9981 line = script->firstline; 9982 } 9983 else { 9984 fileNameObj = interp->emptyObj; 9985 line = 1; 9986 } 9987 *lineptr = line; 9988 return fileNameObj; 9989 } 9990 9991 void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, 9992 Jim_Obj *fileNameObj, int lineNumber) 9993 { 9994 JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object")); 9995 Jim_FreeIntRep(interp, objPtr); 9996 Jim_IncrRefCount(fileNameObj); 9997 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj; 9998 objPtr->internalRep.sourceValue.lineNumber = lineNumber; 9999 objPtr->typePtr = &sourceObjType; 10000 } 10001 10002 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, 10003 ParseTokenList *tokenlist) 10004 { 10005 int i; 10006 struct ScriptToken *token; 10007 10008 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count); 10009 10010 for (i = 0; i < tokenlist->count; i++) { 10011 const ParseToken *t = &tokenlist->list[i]; 10012 10013 10014 token->type = t->type; 10015 token->objPtr = JimMakeScriptObj(interp, t); 10016 Jim_IncrRefCount(token->objPtr); 10017 token++; 10018 } 10019 10020 script->len = i; 10021 } 10022 10023 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 10024 { 10025 int scriptTextLen; 10026 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); 10027 struct JimParserCtx parser; 10028 struct ScriptObj *script; 10029 ParseTokenList tokenlist; 10030 Jim_Obj *fileNameObj; 10031 int line; 10032 10033 10034 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line); 10035 10036 10037 ScriptTokenListInit(&tokenlist); 10038 10039 JimParserInit(&parser, scriptText, scriptTextLen, line); 10040 while (!parser.eof) { 10041 JimParseScript(&parser); 10042 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, 10043 parser.tline); 10044 } 10045 10046 10047 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0); 10048 10049 10050 script = Jim_Alloc(sizeof(*script)); 10051 memset(script, 0, sizeof(*script)); 10052 script->inUse = 1; 10053 script->fileNameObj = fileNameObj; 10054 Jim_IncrRefCount(script->fileNameObj); 10055 script->missing = parser.missing.ch; 10056 script->linenr = parser.missing.line; 10057 10058 ScriptObjAddTokens(interp, script, &tokenlist); 10059 10060 10061 ScriptTokenListFree(&tokenlist); 10062 10063 10064 Jim_FreeIntRep(interp, objPtr); 10065 Jim_SetIntRepPtr(objPtr, script); 10066 objPtr->typePtr = &scriptObjType; 10067 } 10068 10069 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr) 10070 { 10071 if (objPtr == interp->emptyObj) { 10072 10073 objPtr = interp->nullScriptObj; 10074 } 10075 10076 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) { 10077 JimSetScriptFromAny(interp, objPtr); 10078 } 10079 10080 return (ScriptObj *)Jim_GetIntRepPtr(objPtr); 10081 } 10082 10083 void Jim_InterpIncrProcEpoch(Jim_Interp *interp) 10084 { 10085 interp->procEpoch++; 10086 10087 10088 while (interp->oldCmdCache) { 10089 Jim_Cmd *next = interp->oldCmdCache->prevCmd; 10090 Jim_Free(interp->oldCmdCache); 10091 interp->oldCmdCache = next; 10092 } 10093 interp->oldCmdCacheSize = 0; 10094 } 10095 10096 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr) 10097 { 10098 cmdPtr->inUse++; 10099 } 10100 10101 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr) 10102 { 10103 if (--cmdPtr->inUse == 0) { 10104 if (cmdPtr->isproc) { 10105 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr); 10106 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr); 10107 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj); 10108 if (cmdPtr->u.proc.staticVars) { 10109 Jim_FreeHashTable(cmdPtr->u.proc.staticVars); 10110 Jim_Free(cmdPtr->u.proc.staticVars); 10111 } 10112 } 10113 else { 10114 10115 if (cmdPtr->u.native.delProc) { 10116 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData); 10117 } 10118 } 10119 if (cmdPtr->prevCmd) { 10120 10121 JimDecrCmdRefCount(interp, cmdPtr->prevCmd); 10122 } 10123 10124 cmdPtr->prevCmd = interp->oldCmdCache; 10125 interp->oldCmdCache = cmdPtr; 10126 if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) { 10127 Jim_InterpIncrProcEpoch(interp); 10128 } 10129 } 10130 } 10131 10132 static void JimIncrVarRef(Jim_VarVal *vv) 10133 { 10134 vv->refCount++; 10135 } 10136 10137 static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv) 10138 { 10139 assert(vv->refCount > 0); 10140 if (--vv->refCount == 0) { 10141 if (vv->objPtr) { 10142 Jim_DecrRefCount(interp, vv->objPtr); 10143 } 10144 Jim_Free(vv); 10145 } 10146 } 10147 10148 static void JimVariablesHTValDestructor(void *interp, void *val) 10149 { 10150 JimDecrVarRef(interp, val); 10151 } 10152 10153 static unsigned int JimObjectHTHashFunction(const void *key) 10154 { 10155 Jim_Obj *keyObj = (Jim_Obj *)key; 10156 int length; 10157 const char *string; 10158 10159 #ifdef JIM_OPTIMIZATION 10160 if (JimIsWide(keyObj) && keyObj->bytes == NULL) { 10161 10162 jim_wide objValue = JimWideValue(keyObj); 10163 if (objValue > INT_MIN && objValue < INT_MAX) { 10164 unsigned result = 0; 10165 unsigned value = (unsigned)objValue; 10166 10167 if (objValue < 0) { 10168 value = (unsigned)-objValue; 10169 } 10170 10171 10172 do { 10173 result += (result << 3) + (value % 10 + '0'); 10174 value /= 10; 10175 } while (value); 10176 10177 if (objValue < 0) { 10178 result += (result << 3) + '-'; 10179 } 10180 return result; 10181 } 10182 } 10183 #endif 10184 string = Jim_GetString(keyObj, &length); 10185 return Jim_GenHashFunction((const unsigned char *)string, length); 10186 } 10187 10188 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2) 10189 { 10190 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2); 10191 } 10192 10193 static void *JimObjectHTKeyValDup(void *privdata, const void *val) 10194 { 10195 Jim_IncrRefCount((Jim_Obj *)val); 10196 return (void *)val; 10197 } 10198 10199 static void JimObjectHTKeyValDestructor(void *interp, void *val) 10200 { 10201 Jim_DecrRefCount(interp, (Jim_Obj *)val); 10202 } 10203 10204 10205 static void *JimVariablesHTValDup(void *privdata, const void *val) 10206 { 10207 JimIncrVarRef((Jim_VarVal *)val); 10208 return (void *)val; 10209 } 10210 10211 static const Jim_HashTableType JimVariablesHashTableType = { 10212 JimObjectHTHashFunction, 10213 JimObjectHTKeyValDup, 10214 JimVariablesHTValDup, 10215 JimObjectHTKeyCompare, 10216 JimObjectHTKeyValDestructor, 10217 JimVariablesHTValDestructor 10218 }; 10219 10220 10221 static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length) 10222 { 10223 int len; 10224 const char *str = Jim_GetString(objPtr, &len); 10225 if (len >= 2 && str[0] == ':' && str[1] == ':') { 10226 while (len && *str == ':') { 10227 len--; 10228 str++; 10229 } 10230 } 10231 *length = len; 10232 return str; 10233 } 10234 10235 static unsigned int JimCommandsHT_HashFunction(const void *key) 10236 { 10237 int len; 10238 const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len); 10239 return Jim_GenHashFunction((const unsigned char *)str, len); 10240 } 10241 10242 static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2) 10243 { 10244 int len1, len2; 10245 const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1); 10246 const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2); 10247 return len1 == len2 && memcmp(str1, str2, len1) == 0; 10248 } 10249 10250 static void JimCommandsHT_ValDestructor(void *interp, void *val) 10251 { 10252 JimDecrCmdRefCount(interp, val); 10253 } 10254 10255 static const Jim_HashTableType JimCommandsHashTableType = { 10256 JimCommandsHT_HashFunction, 10257 JimObjectHTKeyValDup, 10258 NULL, 10259 JimCommandsHT_KeyCompare, 10260 JimObjectHTKeyValDestructor, 10261 JimCommandsHT_ValDestructor 10262 }; 10263 10264 10265 10266 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr) 10267 { 10268 #ifdef jim_ext_namespace 10269 Jim_Obj *resultObj; 10270 10271 const char *name = Jim_String(nameObjPtr); 10272 if (name[0] == ':' && name[1] == ':') { 10273 return nameObjPtr; 10274 } 10275 Jim_IncrRefCount(nameObjPtr); 10276 resultObj = Jim_NewStringObj(interp, "::", -1); 10277 Jim_AppendObj(interp, resultObj, nameObjPtr); 10278 Jim_DecrRefCount(interp, nameObjPtr); 10279 10280 return resultObj; 10281 #else 10282 return nameObjPtr; 10283 #endif 10284 } 10285 10286 static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr) 10287 { 10288 #ifdef jim_ext_namespace 10289 if (Jim_Length(interp->framePtr->nsObj)) { 10290 int len; 10291 const char *name = Jim_GetString(objPtr, &len); 10292 if (len < 2 || name[0] != ':' || name[1] != ':') { 10293 10294 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj); 10295 Jim_AppendStrings(interp, objPtr, "::", name, NULL); 10296 } 10297 } 10298 #endif 10299 Jim_IncrRefCount(objPtr); 10300 return objPtr; 10301 } 10302 10303 static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd) 10304 { 10305 JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name")); 10306 10307 if (interp->local) { 10308 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr); 10309 if (he) { 10310 10311 cmd->prevCmd = Jim_GetHashEntryVal(he); 10312 Jim_SetHashVal(&interp->commands, he, cmd); 10313 10314 Jim_InterpIncrProcEpoch(interp); 10315 return; 10316 } 10317 } 10318 10319 10320 10321 Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd); 10322 } 10323 10324 int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj, 10325 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc) 10326 { 10327 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr)); 10328 10329 10330 memset(cmdPtr, 0, sizeof(*cmdPtr)); 10331 cmdPtr->inUse = 1; 10332 cmdPtr->u.native.delProc = delProc; 10333 cmdPtr->u.native.cmdProc = cmdProc; 10334 cmdPtr->u.native.privData = privData; 10335 10336 Jim_IncrRefCount(cmdNameObj); 10337 JimCreateCommand(interp, cmdNameObj, cmdPtr); 10338 Jim_DecrRefCount(interp, cmdNameObj); 10339 10340 return JIM_OK; 10341 } 10342 10343 10344 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr, 10345 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc) 10346 { 10347 return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc); 10348 } 10349 10350 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr) 10351 { 10352 int len, i; 10353 10354 len = Jim_ListLength(interp, staticsListObjPtr); 10355 if (len == 0) { 10356 return JIM_OK; 10357 } 10358 10359 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable)); 10360 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp); 10361 for (i = 0; i < len; i++) { 10362 Jim_Obj *initObjPtr = NULL; 10363 Jim_Obj *nameObjPtr; 10364 Jim_VarVal *vv = NULL; 10365 Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i); 10366 int subLen = Jim_ListLength(interp, objPtr); 10367 int byref = 0; 10368 10369 10370 if (subLen != 1 && subLen != 2) { 10371 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"", 10372 objPtr); 10373 return JIM_ERR; 10374 } 10375 10376 nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0); 10377 10378 10379 if (subLen == 1) { 10380 int len; 10381 const char *pt = Jim_GetString(nameObjPtr, &len); 10382 if (*pt == '&') { 10383 10384 nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1); 10385 byref = 1; 10386 } 10387 } 10388 Jim_IncrRefCount(nameObjPtr); 10389 10390 if (subLen == 1) { 10391 switch (SetVariableFromAny(interp, nameObjPtr)) { 10392 case JIM_DICT_SUGAR: 10393 10394 if (byref) { 10395 Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr); 10396 } 10397 else { 10398 Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr); 10399 } 10400 Jim_DecrRefCount(interp, nameObjPtr); 10401 return JIM_ERR; 10402 10403 case JIM_OK: 10404 if (byref) { 10405 vv = nameObjPtr->internalRep.varValue.vv; 10406 } 10407 else { 10408 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE); 10409 } 10410 break; 10411 10412 case JIM_ERR: 10413 10414 Jim_SetResultFormatted(interp, 10415 "variable for initialization of static \"%#s\" not found in the local context", 10416 nameObjPtr); 10417 Jim_DecrRefCount(interp, nameObjPtr); 10418 return JIM_ERR; 10419 } 10420 } 10421 else { 10422 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1); 10423 } 10424 10425 if (vv == NULL) { 10426 vv = Jim_Alloc(sizeof(*vv)); 10427 vv->objPtr = initObjPtr; 10428 Jim_IncrRefCount(vv->objPtr); 10429 vv->linkFramePtr = NULL; 10430 vv->refCount = 0; 10431 } 10432 10433 if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) { 10434 Jim_SetResultFormatted(interp, 10435 "static variable name \"%#s\" duplicated in statics list", nameObjPtr); 10436 JimIncrVarRef(vv); 10437 JimDecrVarRef(interp, vv); 10438 Jim_DecrRefCount(interp, nameObjPtr); 10439 return JIM_ERR; 10440 } 10441 10442 Jim_DecrRefCount(interp, nameObjPtr); 10443 } 10444 return JIM_OK; 10445 } 10446 10447 10448 #ifdef jim_ext_namespace 10449 static const char *Jim_memrchr(const char *p, int c, int len) 10450 { 10451 int i; 10452 for (i = len; i > 0; i--) { 10453 if (p[i] == c) { 10454 return p + i; 10455 } 10456 } 10457 return NULL; 10458 } 10459 #endif 10460 10461 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr) 10462 { 10463 #ifdef jim_ext_namespace 10464 if (cmdPtr->isproc) { 10465 int len; 10466 const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len); 10467 10468 const char *pt = Jim_memrchr(cmdname, ':', len); 10469 if (pt && pt != cmdname && pt[-1] == ':') { 10470 pt++; 10471 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj); 10472 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2); 10473 Jim_IncrRefCount(cmdPtr->u.proc.nsObj); 10474 10475 Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname)); 10476 if (Jim_FindHashEntry(&interp->commands, tempObj)) { 10477 10478 Jim_InterpIncrProcEpoch(interp); 10479 } 10480 Jim_FreeNewObj(interp, tempObj); 10481 } 10482 } 10483 #endif 10484 } 10485 10486 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr, 10487 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj) 10488 { 10489 Jim_Cmd *cmdPtr; 10490 int argListLen; 10491 int i; 10492 10493 argListLen = Jim_ListLength(interp, argListObjPtr); 10494 10495 10496 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen); 10497 assert(cmdPtr); 10498 memset(cmdPtr, 0, sizeof(*cmdPtr)); 10499 cmdPtr->inUse = 1; 10500 cmdPtr->isproc = 1; 10501 cmdPtr->u.proc.argListObjPtr = argListObjPtr; 10502 cmdPtr->u.proc.argListLen = argListLen; 10503 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr; 10504 cmdPtr->u.proc.argsPos = -1; 10505 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1); 10506 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj; 10507 Jim_IncrRefCount(argListObjPtr); 10508 Jim_IncrRefCount(bodyObjPtr); 10509 Jim_IncrRefCount(cmdPtr->u.proc.nsObj); 10510 10511 10512 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) { 10513 goto err; 10514 } 10515 10516 10517 10518 for (i = 0; i < argListLen; i++) { 10519 Jim_Obj *argPtr; 10520 Jim_Obj *nameObjPtr; 10521 Jim_Obj *defaultObjPtr; 10522 int len; 10523 10524 10525 argPtr = Jim_ListGetIndex(interp, argListObjPtr, i); 10526 len = Jim_ListLength(interp, argPtr); 10527 if (len == 0) { 10528 Jim_SetResultString(interp, "argument with no name", -1); 10529 err: 10530 JimDecrCmdRefCount(interp, cmdPtr); 10531 return NULL; 10532 } 10533 if (len > 2) { 10534 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr); 10535 goto err; 10536 } 10537 10538 if (len == 2) { 10539 10540 nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0); 10541 defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1); 10542 } 10543 else { 10544 10545 nameObjPtr = argPtr; 10546 defaultObjPtr = NULL; 10547 } 10548 10549 10550 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) { 10551 if (cmdPtr->u.proc.argsPos >= 0) { 10552 Jim_SetResultString(interp, "'args' specified more than once", -1); 10553 goto err; 10554 } 10555 cmdPtr->u.proc.argsPos = i; 10556 } 10557 else { 10558 if (len == 2) { 10559 cmdPtr->u.proc.optArity++; 10560 } 10561 else { 10562 cmdPtr->u.proc.reqArity++; 10563 } 10564 } 10565 10566 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr; 10567 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr; 10568 } 10569 10570 return cmdPtr; 10571 } 10572 10573 int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj) 10574 { 10575 int ret = JIM_OK; 10576 10577 nameObj = JimQualifyName(interp, nameObj); 10578 10579 if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) { 10580 Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj); 10581 ret = JIM_ERR; 10582 } 10583 Jim_DecrRefCount(interp, nameObj); 10584 10585 return ret; 10586 } 10587 10588 int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj) 10589 { 10590 int ret = JIM_ERR; 10591 Jim_HashEntry *he; 10592 Jim_Cmd *cmdPtr; 10593 10594 if (Jim_Length(newNameObj) == 0) { 10595 return Jim_DeleteCommand(interp, oldNameObj); 10596 } 10597 10598 10599 10600 oldNameObj = JimQualifyName(interp, oldNameObj); 10601 newNameObj = JimQualifyName(interp, newNameObj); 10602 10603 10604 he = Jim_FindHashEntry(&interp->commands, oldNameObj); 10605 if (he == NULL) { 10606 Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj); 10607 } 10608 else if (Jim_FindHashEntry(&interp->commands, newNameObj)) { 10609 Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj); 10610 } 10611 else { 10612 cmdPtr = Jim_GetHashEntryVal(he); 10613 if (cmdPtr->prevCmd) { 10614 Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj); 10615 } 10616 else { 10617 10618 JimIncrCmdRefCount(cmdPtr); 10619 JimUpdateProcNamespace(interp, cmdPtr, newNameObj); 10620 Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr); 10621 10622 10623 Jim_DeleteHashEntry(&interp->commands, oldNameObj); 10624 10625 10626 Jim_InterpIncrProcEpoch(interp); 10627 10628 ret = JIM_OK; 10629 } 10630 } 10631 10632 Jim_DecrRefCount(interp, oldNameObj); 10633 Jim_DecrRefCount(interp, newNameObj); 10634 10635 return ret; 10636 } 10637 10638 10639 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 10640 { 10641 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj); 10642 } 10643 10644 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 10645 { 10646 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue; 10647 dupPtr->typePtr = srcPtr->typePtr; 10648 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj); 10649 } 10650 10651 static const Jim_ObjType commandObjType = { 10652 "command", 10653 FreeCommandInternalRep, 10654 DupCommandInternalRep, 10655 NULL, 10656 JIM_TYPE_REFERENCES, 10657 }; 10658 10659 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 10660 { 10661 Jim_Cmd *cmd; 10662 10663 if (objPtr->typePtr == &commandObjType 10664 && objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch 10665 #ifdef jim_ext_namespace 10666 && Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj) 10667 #endif 10668 && objPtr->internalRep.cmdValue.cmdPtr->inUse) { 10669 10670 cmd = objPtr->internalRep.cmdValue.cmdPtr; 10671 } 10672 else { 10673 Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr); 10674 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj); 10675 #ifdef jim_ext_namespace 10676 if (he == NULL && Jim_Length(interp->framePtr->nsObj)) { 10677 he = Jim_FindHashEntry(&interp->commands, objPtr); 10678 } 10679 #endif 10680 if (he == NULL) { 10681 if (flags & JIM_ERRMSG) { 10682 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr); 10683 } 10684 Jim_DecrRefCount(interp, qualifiedNameObj); 10685 return NULL; 10686 } 10687 cmd = Jim_GetHashEntryVal(he); 10688 10689 cmd->cmdNameObj = Jim_GetHashEntryKey(he); 10690 10691 10692 Jim_FreeIntRep(interp, objPtr); 10693 objPtr->typePtr = &commandObjType; 10694 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch; 10695 objPtr->internalRep.cmdValue.cmdPtr = cmd; 10696 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj; 10697 Jim_IncrRefCount(interp->framePtr->nsObj); 10698 Jim_DecrRefCount(interp, qualifiedNameObj); 10699 } 10700 while (cmd->u.proc.upcall) { 10701 cmd = cmd->prevCmd; 10702 } 10703 return cmd; 10704 } 10705 10706 10707 10708 static const Jim_ObjType variableObjType = { 10709 "variable", 10710 NULL, 10711 NULL, 10712 NULL, 10713 JIM_TYPE_REFERENCES, 10714 }; 10715 10716 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 10717 { 10718 const char *varName; 10719 Jim_CallFrame *framePtr; 10720 int global; 10721 int len; 10722 Jim_VarVal *vv; 10723 10724 10725 if (objPtr->typePtr == &variableObjType) { 10726 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr; 10727 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) { 10728 10729 return JIM_OK; 10730 } 10731 10732 } 10733 else if (objPtr->typePtr == &dictSubstObjType) { 10734 return JIM_DICT_SUGAR; 10735 } 10736 10737 varName = Jim_GetString(objPtr, &len); 10738 10739 10740 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) { 10741 return JIM_DICT_SUGAR; 10742 } 10743 10744 if (varName[0] == ':' && varName[1] == ':') { 10745 while (*varName == ':') { 10746 varName++; 10747 len--; 10748 } 10749 global = 1; 10750 framePtr = interp->topFramePtr; 10751 10752 Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len); 10753 vv = JimFindVariable(&framePtr->vars, tempObj); 10754 Jim_FreeNewObj(interp, tempObj); 10755 } 10756 else { 10757 global = 0; 10758 framePtr = interp->framePtr; 10759 10760 vv = JimFindVariable(&framePtr->vars, objPtr); 10761 if (vv == NULL && framePtr->staticVars) { 10762 10763 vv = JimFindVariable(framePtr->staticVars, objPtr); 10764 } 10765 } 10766 10767 if (vv == NULL) { 10768 return JIM_ERR; 10769 } 10770 10771 10772 Jim_FreeIntRep(interp, objPtr); 10773 objPtr->typePtr = &variableObjType; 10774 objPtr->internalRep.varValue.callFrameId = framePtr->id; 10775 objPtr->internalRep.varValue.vv = vv; 10776 objPtr->internalRep.varValue.global = global; 10777 return JIM_OK; 10778 } 10779 10780 10781 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr); 10782 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags); 10783 10784 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv) 10785 { 10786 return Jim_AddHashEntry(ht, nameObjPtr, vv); 10787 } 10788 10789 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr) 10790 { 10791 Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr); 10792 if (he) { 10793 return (Jim_VarVal *)Jim_GetHashEntryVal(he); 10794 } 10795 return NULL; 10796 } 10797 10798 static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr) 10799 { 10800 return Jim_DeleteHashEntry(ht, nameObjPtr); 10801 } 10802 10803 static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr) 10804 { 10805 const char *name; 10806 Jim_CallFrame *framePtr; 10807 int global; 10808 int len; 10809 10810 10811 Jim_VarVal *vv = Jim_Alloc(sizeof(*vv)); 10812 10813 vv->objPtr = valObjPtr; 10814 Jim_IncrRefCount(valObjPtr); 10815 vv->linkFramePtr = NULL; 10816 vv->refCount = 0; 10817 10818 name = Jim_GetString(nameObjPtr, &len); 10819 if (name[0] == ':' && name[1] == ':') { 10820 while (*name == ':') { 10821 name++; 10822 len--; 10823 } 10824 framePtr = interp->topFramePtr; 10825 global = 1; 10826 JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv); 10827 } 10828 else { 10829 framePtr = interp->framePtr; 10830 global = 0; 10831 JimSetNewVariable(&framePtr->vars, nameObjPtr, vv); 10832 } 10833 10834 10835 Jim_FreeIntRep(interp, nameObjPtr); 10836 nameObjPtr->typePtr = &variableObjType; 10837 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id; 10838 nameObjPtr->internalRep.varValue.vv = vv; 10839 nameObjPtr->internalRep.varValue.global = global; 10840 10841 return vv; 10842 } 10843 10844 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr) 10845 { 10846 int err; 10847 Jim_VarVal *vv; 10848 10849 switch (SetVariableFromAny(interp, nameObjPtr)) { 10850 case JIM_DICT_SUGAR: 10851 return JimDictSugarSet(interp, nameObjPtr, valObjPtr); 10852 10853 case JIM_ERR: 10854 JimCreateVariable(interp, nameObjPtr, valObjPtr); 10855 break; 10856 10857 case JIM_OK: 10858 vv = nameObjPtr->internalRep.varValue.vv; 10859 if (vv->linkFramePtr == NULL) { 10860 Jim_IncrRefCount(valObjPtr); 10861 Jim_DecrRefCount(interp, vv->objPtr); 10862 vv->objPtr = valObjPtr; 10863 } 10864 else { 10865 Jim_CallFrame *savedCallFrame; 10866 10867 savedCallFrame = interp->framePtr; 10868 interp->framePtr = vv->linkFramePtr; 10869 err = Jim_SetVariable(interp, vv->objPtr, valObjPtr); 10870 interp->framePtr = savedCallFrame; 10871 if (err != JIM_OK) 10872 return err; 10873 } 10874 } 10875 return JIM_OK; 10876 } 10877 10878 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr) 10879 { 10880 Jim_Obj *nameObjPtr; 10881 int result; 10882 10883 nameObjPtr = Jim_NewStringObj(interp, name, -1); 10884 Jim_IncrRefCount(nameObjPtr); 10885 result = Jim_SetVariable(interp, nameObjPtr, objPtr); 10886 Jim_DecrRefCount(interp, nameObjPtr); 10887 return result; 10888 } 10889 10890 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr) 10891 { 10892 Jim_CallFrame *savedFramePtr; 10893 int result; 10894 10895 savedFramePtr = interp->framePtr; 10896 interp->framePtr = interp->topFramePtr; 10897 result = Jim_SetVariableStr(interp, name, objPtr); 10898 interp->framePtr = savedFramePtr; 10899 return result; 10900 } 10901 10902 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val) 10903 { 10904 Jim_Obj *valObjPtr; 10905 int result; 10906 10907 valObjPtr = Jim_NewStringObj(interp, val, -1); 10908 Jim_IncrRefCount(valObjPtr); 10909 result = Jim_SetVariableStr(interp, name, valObjPtr); 10910 Jim_DecrRefCount(interp, valObjPtr); 10911 return result; 10912 } 10913 10914 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr, 10915 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame) 10916 { 10917 const char *varName; 10918 const char *targetName; 10919 Jim_CallFrame *framePtr; 10920 Jim_VarVal *vv; 10921 int len; 10922 int varnamelen; 10923 10924 10925 switch (SetVariableFromAny(interp, nameObjPtr)) { 10926 case JIM_DICT_SUGAR: 10927 10928 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr); 10929 return JIM_ERR; 10930 10931 case JIM_OK: 10932 vv = nameObjPtr->internalRep.varValue.vv; 10933 10934 if (vv->linkFramePtr == NULL) { 10935 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr); 10936 return JIM_ERR; 10937 } 10938 10939 10940 vv->linkFramePtr = NULL; 10941 break; 10942 } 10943 10944 10945 10946 varName = Jim_GetString(nameObjPtr, &varnamelen); 10947 10948 if (varName[0] == ':' && varName[1] == ':') { 10949 while (*varName == ':') { 10950 varName++; 10951 varnamelen--; 10952 } 10953 10954 framePtr = interp->topFramePtr; 10955 } 10956 else { 10957 framePtr = interp->framePtr; 10958 } 10959 10960 targetName = Jim_GetString(targetNameObjPtr, &len); 10961 if (targetName[0] == ':' && targetName[1] == ':') { 10962 while (*targetName == ':') { 10963 targetName++; 10964 len--; 10965 } 10966 targetNameObjPtr = Jim_NewStringObj(interp, targetName, len); 10967 targetCallFrame = interp->topFramePtr; 10968 } 10969 Jim_IncrRefCount(targetNameObjPtr); 10970 10971 if (framePtr->level < targetCallFrame->level) { 10972 Jim_SetResultFormatted(interp, 10973 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable", 10974 nameObjPtr); 10975 Jim_DecrRefCount(interp, targetNameObjPtr); 10976 return JIM_ERR; 10977 } 10978 10979 10980 if (framePtr == targetCallFrame) { 10981 Jim_Obj *objPtr = targetNameObjPtr; 10982 10983 10984 while (1) { 10985 if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) { 10986 Jim_SetResultString(interp, "can't upvar from variable to itself", -1); 10987 Jim_DecrRefCount(interp, targetNameObjPtr); 10988 return JIM_ERR; 10989 } 10990 if (SetVariableFromAny(interp, objPtr) != JIM_OK) 10991 break; 10992 vv = objPtr->internalRep.varValue.vv; 10993 if (vv->linkFramePtr != targetCallFrame) 10994 break; 10995 objPtr = vv->objPtr; 10996 } 10997 } 10998 10999 11000 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr); 11001 11002 nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame; 11003 Jim_DecrRefCount(interp, targetNameObjPtr); 11004 return JIM_OK; 11005 } 11006 11007 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) 11008 { 11009 if (interp->safeexpr) { 11010 return nameObjPtr; 11011 } 11012 switch (SetVariableFromAny(interp, nameObjPtr)) { 11013 case JIM_OK:{ 11014 Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv; 11015 11016 if (vv->linkFramePtr == NULL) { 11017 return vv->objPtr; 11018 } 11019 else { 11020 Jim_Obj *objPtr; 11021 11022 11023 Jim_CallFrame *savedCallFrame = interp->framePtr; 11024 11025 interp->framePtr = vv->linkFramePtr; 11026 objPtr = Jim_GetVariable(interp, vv->objPtr, flags); 11027 interp->framePtr = savedCallFrame; 11028 if (objPtr) { 11029 return objPtr; 11030 } 11031 11032 } 11033 } 11034 break; 11035 11036 case JIM_DICT_SUGAR: 11037 11038 return JimDictSugarGet(interp, nameObjPtr, flags); 11039 } 11040 if (flags & JIM_ERRMSG) { 11041 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr); 11042 } 11043 return NULL; 11044 } 11045 11046 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) 11047 { 11048 Jim_CallFrame *savedFramePtr; 11049 Jim_Obj *objPtr; 11050 11051 savedFramePtr = interp->framePtr; 11052 interp->framePtr = interp->topFramePtr; 11053 objPtr = Jim_GetVariable(interp, nameObjPtr, flags); 11054 interp->framePtr = savedFramePtr; 11055 11056 return objPtr; 11057 } 11058 11059 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags) 11060 { 11061 Jim_Obj *nameObjPtr, *varObjPtr; 11062 11063 nameObjPtr = Jim_NewStringObj(interp, name, -1); 11064 Jim_IncrRefCount(nameObjPtr); 11065 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags); 11066 Jim_DecrRefCount(interp, nameObjPtr); 11067 return varObjPtr; 11068 } 11069 11070 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags) 11071 { 11072 Jim_CallFrame *savedFramePtr; 11073 Jim_Obj *objPtr; 11074 11075 savedFramePtr = interp->framePtr; 11076 interp->framePtr = interp->topFramePtr; 11077 objPtr = Jim_GetVariableStr(interp, name, flags); 11078 interp->framePtr = savedFramePtr; 11079 11080 return objPtr; 11081 } 11082 11083 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) 11084 { 11085 Jim_VarVal *vv; 11086 int retval; 11087 Jim_CallFrame *framePtr; 11088 11089 retval = SetVariableFromAny(interp, nameObjPtr); 11090 if (retval == JIM_DICT_SUGAR) { 11091 11092 return JimDictSugarSet(interp, nameObjPtr, NULL); 11093 } 11094 else if (retval == JIM_OK) { 11095 vv = nameObjPtr->internalRep.varValue.vv; 11096 11097 11098 if (vv->linkFramePtr) { 11099 framePtr = interp->framePtr; 11100 interp->framePtr = vv->linkFramePtr; 11101 retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE); 11102 interp->framePtr = framePtr; 11103 } 11104 else { 11105 if (nameObjPtr->internalRep.varValue.global) { 11106 int len; 11107 const char *name = Jim_GetString(nameObjPtr, &len); 11108 while (*name == ':') { 11109 name++; 11110 len--; 11111 } 11112 framePtr = interp->topFramePtr; 11113 Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len); 11114 retval = JimUnsetVariable(&framePtr->vars, tempObj); 11115 Jim_FreeNewObj(interp, tempObj); 11116 } 11117 else { 11118 framePtr = interp->framePtr; 11119 retval = JimUnsetVariable(&framePtr->vars, nameObjPtr); 11120 } 11121 11122 if (retval == JIM_OK) { 11123 11124 framePtr->id = interp->callFrameEpoch++; 11125 } 11126 } 11127 } 11128 if (retval != JIM_OK && (flags & JIM_ERRMSG)) { 11129 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr); 11130 } 11131 return retval; 11132 } 11133 11134 11135 11136 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr, 11137 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr) 11138 { 11139 const char *str, *p; 11140 int len, keyLen; 11141 Jim_Obj *varObjPtr, *keyObjPtr; 11142 11143 str = Jim_GetString(objPtr, &len); 11144 11145 p = strchr(str, '('); 11146 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str)); 11147 11148 varObjPtr = Jim_NewStringObj(interp, str, p - str); 11149 11150 p++; 11151 keyLen = (str + len) - p; 11152 if (str[len - 1] == ')') { 11153 keyLen--; 11154 } 11155 11156 11157 keyObjPtr = Jim_NewStringObj(interp, p, keyLen); 11158 11159 Jim_IncrRefCount(varObjPtr); 11160 Jim_IncrRefCount(keyObjPtr); 11161 *varPtrPtr = varObjPtr; 11162 *keyPtrPtr = keyObjPtr; 11163 } 11164 11165 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr) 11166 { 11167 int err; 11168 11169 SetDictSubstFromAny(interp, objPtr); 11170 11171 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, 11172 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST); 11173 11174 if (err == JIM_OK) { 11175 11176 Jim_SetEmptyResult(interp); 11177 } 11178 else { 11179 if (!valObjPtr) { 11180 11181 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) { 11182 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array", 11183 objPtr); 11184 return err; 11185 } 11186 } 11187 11188 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array", 11189 (valObjPtr ? "set" : "unset"), objPtr); 11190 } 11191 return err; 11192 } 11193 11194 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr, 11195 Jim_Obj *keyObjPtr, int flags) 11196 { 11197 Jim_Obj *dictObjPtr; 11198 Jim_Obj *resObjPtr = NULL; 11199 int ret; 11200 11201 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG); 11202 if (!dictObjPtr) { 11203 return NULL; 11204 } 11205 11206 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE); 11207 if (ret != JIM_OK) { 11208 Jim_SetResultFormatted(interp, 11209 "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr, 11210 ret < 0 ? "variable isn't" : "no such element in"); 11211 } 11212 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) { 11213 11214 Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr)); 11215 } 11216 11217 return resObjPtr; 11218 } 11219 11220 11221 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 11222 { 11223 SetDictSubstFromAny(interp, objPtr); 11224 11225 return JimDictExpandArrayVariable(interp, 11226 objPtr->internalRep.dictSubstValue.varNameObjPtr, 11227 objPtr->internalRep.dictSubstValue.indexObjPtr, flags); 11228 } 11229 11230 11231 11232 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 11233 { 11234 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr); 11235 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); 11236 } 11237 11238 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 11239 { 11240 11241 dupPtr->internalRep = srcPtr->internalRep; 11242 11243 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr); 11244 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr); 11245 } 11246 11247 11248 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 11249 { 11250 if (objPtr->typePtr != &dictSubstObjType) { 11251 Jim_Obj *varObjPtr, *keyObjPtr; 11252 11253 if (objPtr->typePtr == &interpolatedObjType) { 11254 11255 11256 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr; 11257 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr; 11258 11259 Jim_IncrRefCount(varObjPtr); 11260 Jim_IncrRefCount(keyObjPtr); 11261 } 11262 else { 11263 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr); 11264 } 11265 11266 Jim_FreeIntRep(interp, objPtr); 11267 objPtr->typePtr = &dictSubstObjType; 11268 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr; 11269 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr; 11270 } 11271 } 11272 11273 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr) 11274 { 11275 Jim_Obj *resObjPtr = NULL; 11276 Jim_Obj *substKeyObjPtr = NULL; 11277 11278 if (interp->safeexpr) { 11279 return objPtr; 11280 } 11281 11282 SetDictSubstFromAny(interp, objPtr); 11283 11284 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr, 11285 &substKeyObjPtr, JIM_NONE) 11286 != JIM_OK) { 11287 return NULL; 11288 } 11289 Jim_IncrRefCount(substKeyObjPtr); 11290 resObjPtr = 11291 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, 11292 substKeyObjPtr, 0); 11293 Jim_DecrRefCount(interp, substKeyObjPtr); 11294 11295 return resObjPtr; 11296 } 11297 11298 11299 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj) 11300 { 11301 Jim_CallFrame *cf; 11302 11303 if (interp->freeFramesList) { 11304 cf = interp->freeFramesList; 11305 interp->freeFramesList = cf->next; 11306 11307 cf->argv = NULL; 11308 cf->argc = 0; 11309 cf->procArgsObjPtr = NULL; 11310 cf->procBodyObjPtr = NULL; 11311 cf->next = NULL; 11312 cf->staticVars = NULL; 11313 cf->localCommands = NULL; 11314 cf->tailcallObj = NULL; 11315 cf->tailcallCmd = NULL; 11316 } 11317 else { 11318 cf = Jim_Alloc(sizeof(*cf)); 11319 memset(cf, 0, sizeof(*cf)); 11320 11321 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp); 11322 } 11323 11324 cf->id = interp->callFrameEpoch++; 11325 cf->parent = parent; 11326 cf->level = parent ? parent->level + 1 : 0; 11327 cf->nsObj = nsObj; 11328 Jim_IncrRefCount(nsObj); 11329 11330 return cf; 11331 } 11332 11333 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands) 11334 { 11335 11336 if (localCommands) { 11337 Jim_Obj *cmdNameObj; 11338 11339 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) { 11340 Jim_HashTable *ht = &interp->commands; 11341 Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj); 11342 if (he) { 11343 Jim_Cmd *cmd = Jim_GetHashEntryVal(he); 11344 if (cmd->prevCmd) { 11345 Jim_Cmd *prevCmd = cmd->prevCmd; 11346 cmd->prevCmd = NULL; 11347 11348 11349 JimDecrCmdRefCount(interp, cmd); 11350 11351 11352 Jim_SetHashVal(ht, he, prevCmd); 11353 } 11354 else { 11355 Jim_DeleteHashEntry(ht, cmdNameObj); 11356 } 11357 } 11358 Jim_DecrRefCount(interp, cmdNameObj); 11359 } 11360 Jim_FreeStack(localCommands); 11361 Jim_Free(localCommands); 11362 } 11363 return JIM_OK; 11364 } 11365 11366 static int JimInvokeDefer(Jim_Interp *interp, int retcode) 11367 { 11368 Jim_Obj *objPtr; 11369 11370 11371 if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) { 11372 return retcode; 11373 } 11374 objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE); 11375 11376 if (objPtr) { 11377 int ret = JIM_OK; 11378 int i; 11379 int listLen = Jim_ListLength(interp, objPtr); 11380 Jim_Obj *resultObjPtr; 11381 11382 Jim_IncrRefCount(objPtr); 11383 11384 resultObjPtr = Jim_GetResult(interp); 11385 Jim_IncrRefCount(resultObjPtr); 11386 Jim_SetEmptyResult(interp); 11387 11388 11389 for (i = listLen; i > 0; i--) { 11390 11391 Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1); 11392 ret = Jim_EvalObj(interp, scriptObjPtr); 11393 if (ret != JIM_OK) { 11394 break; 11395 } 11396 } 11397 11398 if (ret == JIM_OK || retcode == JIM_ERR) { 11399 11400 Jim_SetResult(interp, resultObjPtr); 11401 } 11402 else { 11403 retcode = ret; 11404 } 11405 11406 Jim_DecrRefCount(interp, resultObjPtr); 11407 Jim_DecrRefCount(interp, objPtr); 11408 } 11409 return retcode; 11410 } 11411 11412 #define JIM_FCF_FULL 0 11413 #define JIM_FCF_REUSE 1 11414 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action) 11415 { 11416 JimDeleteLocalProcs(interp, cf->localCommands); 11417 11418 if (cf->procArgsObjPtr) 11419 Jim_DecrRefCount(interp, cf->procArgsObjPtr); 11420 if (cf->procBodyObjPtr) 11421 Jim_DecrRefCount(interp, cf->procBodyObjPtr); 11422 Jim_DecrRefCount(interp, cf->nsObj); 11423 if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE) 11424 Jim_FreeHashTable(&cf->vars); 11425 else { 11426 Jim_ClearHashTable(&cf->vars); 11427 } 11428 cf->next = interp->freeFramesList; 11429 interp->freeFramesList = cf; 11430 } 11431 11432 11433 11434 int Jim_IsBigEndian(void) 11435 { 11436 union { 11437 unsigned short s; 11438 unsigned char c[2]; 11439 } uval = {0x0102}; 11440 11441 return uval.c[0] == 1; 11442 } 11443 11444 11445 Jim_Interp *Jim_CreateInterp(void) 11446 { 11447 Jim_Interp *i = Jim_Alloc(sizeof(*i)); 11448 11449 memset(i, 0, sizeof(*i)); 11450 11451 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH; 11452 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH; 11453 i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 11454 11455 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i); 11456 #ifdef JIM_REFERENCES 11457 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i); 11458 #endif 11459 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i); 11460 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL); 11461 i->emptyObj = Jim_NewEmptyStringObj(i); 11462 i->trueObj = Jim_NewIntObj(i, 1); 11463 i->falseObj = Jim_NewIntObj(i, 0); 11464 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj); 11465 i->result = i->emptyObj; 11466 i->stackTrace = Jim_NewListObj(i, NULL, 0); 11467 i->unknown = Jim_NewStringObj(i, "unknown", -1); 11468 i->defer = Jim_NewStringObj(i, "jim::defer", -1); 11469 i->errorProc = i->emptyObj; 11470 i->nullScriptObj = Jim_NewEmptyStringObj(i); 11471 i->evalFrame = &i->topEvalFrame; 11472 i->currentFilenameObj = Jim_NewEmptyStringObj(i); 11473 Jim_IncrRefCount(i->emptyObj); 11474 Jim_IncrRefCount(i->result); 11475 Jim_IncrRefCount(i->stackTrace); 11476 Jim_IncrRefCount(i->unknown); 11477 Jim_IncrRefCount(i->defer); 11478 Jim_IncrRefCount(i->nullScriptObj); 11479 Jim_IncrRefCount(i->errorProc); 11480 Jim_IncrRefCount(i->trueObj); 11481 Jim_IncrRefCount(i->falseObj); 11482 Jim_IncrRefCount(i->currentFilenameObj); 11483 11484 11485 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY); 11486 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0"); 11487 11488 Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim"); 11489 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS); 11490 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM); 11491 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR); 11492 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian"); 11493 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0"); 11494 Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0"); 11495 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *))); 11496 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide))); 11497 Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4)); 11498 11499 return i; 11500 } 11501 11502 void Jim_FreeInterp(Jim_Interp *i) 11503 { 11504 Jim_CallFrame *cf, *cfx; 11505 11506 Jim_Obj *objPtr, *nextObjPtr; 11507 11508 i->quitting = 1; 11509 11510 11511 for (cf = i->framePtr; cf; cf = cfx) { 11512 11513 JimInvokeDefer(i, JIM_OK); 11514 cfx = cf->parent; 11515 JimFreeCallFrame(i, cf, JIM_FCF_FULL); 11516 } 11517 11518 11519 Jim_FreeHashTable(&i->commands); 11520 11521 Jim_DecrRefCount(i, i->emptyObj); 11522 Jim_DecrRefCount(i, i->trueObj); 11523 Jim_DecrRefCount(i, i->falseObj); 11524 Jim_DecrRefCount(i, i->result); 11525 Jim_DecrRefCount(i, i->stackTrace); 11526 Jim_DecrRefCount(i, i->errorProc); 11527 Jim_DecrRefCount(i, i->unknown); 11528 Jim_DecrRefCount(i, i->defer); 11529 Jim_DecrRefCount(i, i->nullScriptObj); 11530 Jim_DecrRefCount(i, i->currentFilenameObj); 11531 11532 11533 Jim_InterpIncrProcEpoch(i); 11534 11535 #ifdef JIM_REFERENCES 11536 Jim_FreeHashTable(&i->references); 11537 #endif 11538 Jim_FreeHashTable(&i->packages); 11539 Jim_Free(i->prngState); 11540 Jim_FreeHashTable(&i->assocData); 11541 if (i->traceCmdObj) { 11542 Jim_DecrRefCount(i, i->traceCmdObj); 11543 } 11544 11545 #ifdef JIM_MAINTAINER 11546 if (i->liveList != NULL) { 11547 objPtr = i->liveList; 11548 11549 printf("\n-------------------------------------\n"); 11550 printf("Objects still in the free list:\n"); 11551 while (objPtr) { 11552 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string"; 11553 Jim_String(objPtr); 11554 11555 if (objPtr->bytes && strlen(objPtr->bytes) > 20) { 11556 printf("%p (%d) %-10s: '%.20s...'\n", 11557 (void *)objPtr, objPtr->refCount, type, objPtr->bytes); 11558 } 11559 else { 11560 printf("%p (%d) %-10s: '%s'\n", 11561 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)"); 11562 } 11563 if (objPtr->typePtr == &sourceObjType) { 11564 printf("FILE %s LINE %d\n", 11565 Jim_String(objPtr->internalRep.sourceValue.fileNameObj), 11566 objPtr->internalRep.sourceValue.lineNumber); 11567 } 11568 objPtr = objPtr->nextObjPtr; 11569 } 11570 printf("-------------------------------------\n\n"); 11571 JimPanic((1, "Live list non empty freeing the interpreter! Leak?")); 11572 } 11573 #endif 11574 11575 11576 objPtr = i->freeList; 11577 while (objPtr) { 11578 nextObjPtr = objPtr->nextObjPtr; 11579 Jim_Free(objPtr); 11580 objPtr = nextObjPtr; 11581 } 11582 11583 11584 for (cf = i->freeFramesList; cf; cf = cfx) { 11585 cfx = cf->next; 11586 if (cf->vars.table) 11587 Jim_FreeHashTable(&cf->vars); 11588 Jim_Free(cf); 11589 } 11590 11591 11592 Jim_Free(i); 11593 } 11594 11595 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr) 11596 { 11597 long level; 11598 const char *str; 11599 Jim_CallFrame *framePtr; 11600 11601 if (levelObjPtr) { 11602 str = Jim_String(levelObjPtr); 11603 if (str[0] == '#') { 11604 char *endptr; 11605 11606 level = jim_strtol(str + 1, &endptr); 11607 if (str[1] == '\0' || endptr[0] != '\0') { 11608 level = -1; 11609 } 11610 } 11611 else { 11612 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) { 11613 level = -1; 11614 } 11615 else { 11616 11617 level = interp->framePtr->level - level; 11618 } 11619 } 11620 } 11621 else { 11622 str = "1"; 11623 level = interp->framePtr->level - 1; 11624 } 11625 11626 if (level == 0) { 11627 return interp->topFramePtr; 11628 } 11629 if (level > 0) { 11630 11631 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) { 11632 if (framePtr->level == level) { 11633 return framePtr; 11634 } 11635 } 11636 } 11637 11638 Jim_SetResultFormatted(interp, "bad level \"%s\"", str); 11639 return NULL; 11640 } 11641 11642 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level) 11643 { 11644 Jim_CallFrame *framePtr; 11645 11646 if (level == 0) { 11647 return interp->framePtr; 11648 } 11649 11650 if (level < 0) { 11651 11652 level = interp->framePtr->level + level; 11653 } 11654 11655 if (level > 0) { 11656 11657 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) { 11658 if (framePtr->level == level) { 11659 return framePtr; 11660 } 11661 } 11662 } 11663 return NULL; 11664 } 11665 11666 static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel) 11667 { 11668 Jim_EvalFrame *evalFrame; 11669 11670 if (proclevel == 0) { 11671 return interp->evalFrame; 11672 } 11673 11674 if (proclevel < 0) { 11675 11676 proclevel = interp->procLevel + proclevel; 11677 } 11678 11679 if (proclevel >= 0) { 11680 11681 for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) { 11682 if (evalFrame->procLevel == proclevel) { 11683 return evalFrame; 11684 } 11685 } 11686 } 11687 return NULL; 11688 } 11689 11690 static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame) 11691 { 11692 if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) { 11693 Jim_EvalFrame *e; 11694 for (e = frame->parent; e; e = e->parent) { 11695 if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) { 11696 break; 11697 } 11698 } 11699 if (e && e->cmd && e->cmd->cmdNameObj) { 11700 return e->cmd->cmdNameObj; 11701 } 11702 } 11703 return NULL; 11704 } 11705 11706 static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj) 11707 { 11708 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame); 11709 Jim_Obj *fileNameObj = interp->emptyObj; 11710 int linenr = 1; 11711 11712 if (frame->scriptObj) { 11713 ScriptObj *script = JimGetScript(interp, frame->scriptObj); 11714 fileNameObj = script->fileNameObj; 11715 linenr = script->linenr; 11716 } 11717 11718 Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj); 11719 Jim_ListAppendElement(interp, listObj, fileNameObj); 11720 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr)); 11721 Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc)); 11722 } 11723 11724 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj) 11725 { 11726 11727 Jim_IncrRefCount(stackTraceObj); 11728 Jim_DecrRefCount(interp, interp->stackTrace); 11729 interp->stackTrace = stackTraceObj; 11730 interp->errorFlag = 1; 11731 } 11732 11733 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script) 11734 { 11735 if (!interp->errorFlag) { 11736 int i; 11737 Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0); 11738 11739 if (interp->procLevel == 0 && script) { 11740 Jim_ListAppendElement(interp, stackTrace, interp->emptyObj); 11741 Jim_ListAppendElement(interp, stackTrace, script->fileNameObj); 11742 Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr)); 11743 Jim_ListAppendElement(interp, stackTrace, interp->emptyObj); 11744 } 11745 else { 11746 for (i = 0; i <= interp->procLevel; i++) { 11747 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i); 11748 if (frame) { 11749 JimAddStackFrame(interp, frame, stackTrace); 11750 } 11751 } 11752 } 11753 JimSetStackTrace(interp, stackTrace); 11754 } 11755 } 11756 11757 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc, 11758 void *data) 11759 { 11760 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue)); 11761 11762 assocEntryPtr->delProc = delProc; 11763 assocEntryPtr->data = data; 11764 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr); 11765 } 11766 11767 void *Jim_GetAssocData(Jim_Interp *interp, const char *key) 11768 { 11769 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key); 11770 11771 if (entryPtr != NULL) { 11772 AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr); 11773 return assocEntryPtr->data; 11774 } 11775 return NULL; 11776 } 11777 11778 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key) 11779 { 11780 return Jim_DeleteHashEntry(&interp->assocData, key); 11781 } 11782 11783 int Jim_GetExitCode(Jim_Interp *interp) 11784 { 11785 return interp->exitCode; 11786 } 11787 11788 static void UpdateStringOfInt(struct Jim_Obj *objPtr); 11789 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); 11790 11791 static const Jim_ObjType intObjType = { 11792 "int", 11793 NULL, 11794 NULL, 11795 UpdateStringOfInt, 11796 JIM_TYPE_NONE, 11797 }; 11798 11799 static const Jim_ObjType coercedDoubleObjType = { 11800 "coerced-double", 11801 NULL, 11802 NULL, 11803 UpdateStringOfInt, 11804 JIM_TYPE_NONE, 11805 }; 11806 11807 11808 static void UpdateStringOfInt(struct Jim_Obj *objPtr) 11809 { 11810 char buf[JIM_INTEGER_SPACE + 1]; 11811 jim_wide wideValue = JimWideValue(objPtr); 11812 int pos = 0; 11813 11814 if (wideValue == 0) { 11815 buf[pos++] = '0'; 11816 } 11817 else { 11818 char tmp[JIM_INTEGER_SPACE]; 11819 int num = 0; 11820 int i; 11821 11822 if (wideValue < 0) { 11823 buf[pos++] = '-'; 11824 i = wideValue % 10; 11825 tmp[num++] = (i > 0) ? (10 - i) : -i; 11826 wideValue /= -10; 11827 } 11828 11829 while (wideValue) { 11830 tmp[num++] = wideValue % 10; 11831 wideValue /= 10; 11832 } 11833 11834 for (i = 0; i < num; i++) { 11835 buf[pos++] = '0' + tmp[num - i - 1]; 11836 } 11837 } 11838 buf[pos] = 0; 11839 11840 JimSetStringBytes(objPtr, buf); 11841 } 11842 11843 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 11844 { 11845 jim_wide wideValue; 11846 const char *str; 11847 11848 if (objPtr->typePtr == &coercedDoubleObjType) { 11849 11850 objPtr->typePtr = &intObjType; 11851 return JIM_OK; 11852 } 11853 11854 11855 str = Jim_String(objPtr); 11856 11857 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) { 11858 if (flags & JIM_ERRMSG) { 11859 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr); 11860 } 11861 return JIM_ERR; 11862 } 11863 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) { 11864 Jim_SetResultString(interp, "Integer value too big to be represented", -1); 11865 return JIM_ERR; 11866 } 11867 11868 Jim_FreeIntRep(interp, objPtr); 11869 objPtr->typePtr = &intObjType; 11870 objPtr->internalRep.wideValue = wideValue; 11871 return JIM_OK; 11872 } 11873 11874 #ifdef JIM_OPTIMIZATION 11875 static int JimIsWide(Jim_Obj *objPtr) 11876 { 11877 return objPtr->typePtr == &intObjType; 11878 } 11879 #endif 11880 11881 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) 11882 { 11883 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR) 11884 return JIM_ERR; 11885 *widePtr = JimWideValue(objPtr); 11886 return JIM_OK; 11887 } 11888 11889 int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) 11890 { 11891 int ret = JIM_OK; 11892 11893 if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) { 11894 SetIntFromAny(interp, objPtr, 0); 11895 } 11896 if (objPtr->typePtr == &intObjType) { 11897 *widePtr = JimWideValue(objPtr); 11898 } 11899 else { 11900 JimPanic((interp->safeexpr, "interp->safeexpr is set")); 11901 interp->safeexpr++; 11902 ret = Jim_EvalExpression(interp, objPtr); 11903 interp->safeexpr--; 11904 11905 if (ret == JIM_OK) { 11906 ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr); 11907 } 11908 if (ret != JIM_OK) { 11909 Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr); 11910 } 11911 } 11912 return ret; 11913 } 11914 11915 11916 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) 11917 { 11918 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR) 11919 return JIM_ERR; 11920 *widePtr = JimWideValue(objPtr); 11921 return JIM_OK; 11922 } 11923 11924 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr) 11925 { 11926 jim_wide wideValue; 11927 int retval; 11928 11929 retval = Jim_GetWide(interp, objPtr, &wideValue); 11930 if (retval == JIM_OK) { 11931 *longPtr = (long)wideValue; 11932 return JIM_OK; 11933 } 11934 return JIM_ERR; 11935 } 11936 11937 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue) 11938 { 11939 Jim_Obj *objPtr; 11940 11941 objPtr = Jim_NewObj(interp); 11942 objPtr->typePtr = &intObjType; 11943 objPtr->bytes = NULL; 11944 objPtr->internalRep.wideValue = wideValue; 11945 return objPtr; 11946 } 11947 11948 #define JIM_DOUBLE_SPACE 30 11949 11950 static void UpdateStringOfDouble(struct Jim_Obj *objPtr); 11951 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr); 11952 11953 static const Jim_ObjType doubleObjType = { 11954 "double", 11955 NULL, 11956 NULL, 11957 UpdateStringOfDouble, 11958 JIM_TYPE_NONE, 11959 }; 11960 11961 #if !HAVE_DECL_ISNAN 11962 #undef isnan 11963 #define isnan(X) ((X) != (X)) 11964 #endif 11965 #if !HAVE_DECL_ISINF 11966 #undef isinf 11967 #define isinf(X) (1.0 / (X) == 0.0) 11968 #endif 11969 11970 static void UpdateStringOfDouble(struct Jim_Obj *objPtr) 11971 { 11972 double value = objPtr->internalRep.doubleValue; 11973 11974 if (isnan(value)) { 11975 JimSetStringBytes(objPtr, "NaN"); 11976 return; 11977 } 11978 if (isinf(value)) { 11979 if (value < 0) { 11980 JimSetStringBytes(objPtr, "-Inf"); 11981 } 11982 else { 11983 JimSetStringBytes(objPtr, "Inf"); 11984 } 11985 return; 11986 } 11987 { 11988 char buf[JIM_DOUBLE_SPACE + 1]; 11989 int i; 11990 int len = sprintf(buf, "%.12g", value); 11991 11992 11993 for (i = 0; i < len; i++) { 11994 if (buf[i] == '.' || buf[i] == 'e') { 11995 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX) 11996 char *e = strchr(buf, 'e'); 11997 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') { 11998 11999 e += 2; 12000 memmove(e, e + 1, len - (e - buf)); 12001 } 12002 #endif 12003 break; 12004 } 12005 } 12006 if (buf[i] == '\0') { 12007 buf[i++] = '.'; 12008 buf[i++] = '0'; 12009 buf[i] = '\0'; 12010 } 12011 JimSetStringBytes(objPtr, buf); 12012 } 12013 } 12014 12015 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 12016 { 12017 double doubleValue; 12018 jim_wide wideValue; 12019 const char *str; 12020 12021 #ifdef HAVE_LONG_LONG 12022 12023 #define MIN_INT_IN_DOUBLE -(1LL << 53) 12024 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1) 12025 12026 if (objPtr->typePtr == &intObjType 12027 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE 12028 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) { 12029 12030 12031 objPtr->typePtr = &coercedDoubleObjType; 12032 return JIM_OK; 12033 } 12034 #endif 12035 str = Jim_String(objPtr); 12036 12037 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) { 12038 12039 Jim_FreeIntRep(interp, objPtr); 12040 objPtr->typePtr = &coercedDoubleObjType; 12041 objPtr->internalRep.wideValue = wideValue; 12042 return JIM_OK; 12043 } 12044 else { 12045 12046 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) { 12047 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr); 12048 return JIM_ERR; 12049 } 12050 12051 Jim_FreeIntRep(interp, objPtr); 12052 } 12053 objPtr->typePtr = &doubleObjType; 12054 objPtr->internalRep.doubleValue = doubleValue; 12055 return JIM_OK; 12056 } 12057 12058 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr) 12059 { 12060 if (objPtr->typePtr == &coercedDoubleObjType) { 12061 *doublePtr = JimWideValue(objPtr); 12062 return JIM_OK; 12063 } 12064 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR) 12065 return JIM_ERR; 12066 12067 if (objPtr->typePtr == &coercedDoubleObjType) { 12068 *doublePtr = JimWideValue(objPtr); 12069 } 12070 else { 12071 *doublePtr = objPtr->internalRep.doubleValue; 12072 } 12073 return JIM_OK; 12074 } 12075 12076 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue) 12077 { 12078 Jim_Obj *objPtr; 12079 12080 objPtr = Jim_NewObj(interp); 12081 objPtr->typePtr = &doubleObjType; 12082 objPtr->bytes = NULL; 12083 objPtr->internalRep.doubleValue = doubleValue; 12084 return objPtr; 12085 } 12086 12087 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); 12088 12089 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr) 12090 { 12091 if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR) 12092 return JIM_ERR; 12093 *booleanPtr = (int) JimWideValue(objPtr); 12094 return JIM_OK; 12095 } 12096 12097 static const char * const jim_true_false_strings[8] = { 12098 "1", "true", "yes", "on", 12099 "0", "false", "no", "off" 12100 }; 12101 12102 static const int jim_true_false_lens[8] = { 12103 1, 4, 3, 2, 12104 1, 5, 2, 3, 12105 }; 12106 12107 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 12108 { 12109 int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings, 12110 sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings)); 12111 if (index < 0) { 12112 if (flags & JIM_ERRMSG) { 12113 Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr); 12114 } 12115 return JIM_ERR; 12116 } 12117 12118 12119 Jim_FreeIntRep(interp, objPtr); 12120 objPtr->typePtr = &intObjType; 12121 12122 objPtr->internalRep.wideValue = index < 4 ? 1 : 0; 12123 return JIM_OK; 12124 } 12125 12126 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec); 12127 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr); 12128 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 12129 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 12130 static void UpdateStringOfList(struct Jim_Obj *objPtr); 12131 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 12132 12133 static const Jim_ObjType listObjType = { 12134 "list", 12135 FreeListInternalRep, 12136 DupListInternalRep, 12137 UpdateStringOfList, 12138 JIM_TYPE_NONE, 12139 }; 12140 12141 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 12142 { 12143 int i; 12144 12145 for (i = 0; i < objPtr->internalRep.listValue.len; i++) { 12146 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]); 12147 } 12148 Jim_Free(objPtr->internalRep.listValue.ele); 12149 } 12150 12151 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 12152 { 12153 int i; 12154 12155 JIM_NOTUSED(interp); 12156 12157 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len; 12158 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen; 12159 dupPtr->internalRep.listValue.ele = 12160 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen); 12161 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele, 12162 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len); 12163 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) { 12164 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]); 12165 } 12166 dupPtr->typePtr = &listObjType; 12167 } 12168 12169 #define JIM_ELESTR_SIMPLE 0 12170 #define JIM_ELESTR_BRACE 1 12171 #define JIM_ELESTR_QUOTE 2 12172 static unsigned char ListElementQuotingType(const char *s, int len) 12173 { 12174 int i, level, blevel, trySimple = 1; 12175 12176 12177 if (len == 0) 12178 return JIM_ELESTR_BRACE; 12179 if (s[0] == '"' || s[0] == '{') { 12180 trySimple = 0; 12181 goto testbrace; 12182 } 12183 for (i = 0; i < len; i++) { 12184 switch (s[i]) { 12185 case ' ': 12186 case '$': 12187 case '"': 12188 case '[': 12189 case ']': 12190 case ';': 12191 case '\\': 12192 case '\r': 12193 case '\n': 12194 case '\t': 12195 case '\f': 12196 case '\v': 12197 trySimple = 0; 12198 12199 case '{': 12200 case '}': 12201 goto testbrace; 12202 } 12203 } 12204 return JIM_ELESTR_SIMPLE; 12205 12206 testbrace: 12207 12208 if (s[len - 1] == '\\') 12209 return JIM_ELESTR_QUOTE; 12210 level = 0; 12211 blevel = 0; 12212 for (i = 0; i < len; i++) { 12213 switch (s[i]) { 12214 case '{': 12215 level++; 12216 break; 12217 case '}': 12218 level--; 12219 if (level < 0) 12220 return JIM_ELESTR_QUOTE; 12221 break; 12222 case '[': 12223 blevel++; 12224 break; 12225 case ']': 12226 blevel--; 12227 break; 12228 case '\\': 12229 if (s[i + 1] == '\n') 12230 return JIM_ELESTR_QUOTE; 12231 else if (s[i + 1] != '\0') 12232 i++; 12233 break; 12234 } 12235 } 12236 if (blevel < 0) { 12237 return JIM_ELESTR_QUOTE; 12238 } 12239 12240 if (level == 0) { 12241 if (!trySimple) 12242 return JIM_ELESTR_BRACE; 12243 for (i = 0; i < len; i++) { 12244 switch (s[i]) { 12245 case ' ': 12246 case '$': 12247 case '"': 12248 case '[': 12249 case ']': 12250 case ';': 12251 case '\\': 12252 case '\r': 12253 case '\n': 12254 case '\t': 12255 case '\f': 12256 case '\v': 12257 return JIM_ELESTR_BRACE; 12258 break; 12259 } 12260 } 12261 return JIM_ELESTR_SIMPLE; 12262 } 12263 return JIM_ELESTR_QUOTE; 12264 } 12265 12266 static int BackslashQuoteString(const char *s, int len, char *q) 12267 { 12268 char *p = q; 12269 12270 while (len--) { 12271 switch (*s) { 12272 case ' ': 12273 case '$': 12274 case '"': 12275 case '[': 12276 case ']': 12277 case '{': 12278 case '}': 12279 case ';': 12280 case '\\': 12281 *p++ = '\\'; 12282 *p++ = *s++; 12283 break; 12284 case '\n': 12285 *p++ = '\\'; 12286 *p++ = 'n'; 12287 s++; 12288 break; 12289 case '\r': 12290 *p++ = '\\'; 12291 *p++ = 'r'; 12292 s++; 12293 break; 12294 case '\t': 12295 *p++ = '\\'; 12296 *p++ = 't'; 12297 s++; 12298 break; 12299 case '\f': 12300 *p++ = '\\'; 12301 *p++ = 'f'; 12302 s++; 12303 break; 12304 case '\v': 12305 *p++ = '\\'; 12306 *p++ = 'v'; 12307 s++; 12308 break; 12309 default: 12310 *p++ = *s++; 12311 break; 12312 } 12313 } 12314 *p = '\0'; 12315 12316 return p - q; 12317 } 12318 12319 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc) 12320 { 12321 #define STATIC_QUOTING_LEN 32 12322 int i, bufLen, realLength; 12323 const char *strRep; 12324 char *p; 12325 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN]; 12326 12327 12328 if (objc > STATIC_QUOTING_LEN) { 12329 quotingType = Jim_Alloc(objc); 12330 } 12331 else { 12332 quotingType = staticQuoting; 12333 } 12334 bufLen = 0; 12335 for (i = 0; i < objc; i++) { 12336 int len; 12337 12338 strRep = Jim_GetString(objv[i], &len); 12339 quotingType[i] = ListElementQuotingType(strRep, len); 12340 switch (quotingType[i]) { 12341 case JIM_ELESTR_SIMPLE: 12342 if (i != 0 || strRep[0] != '#') { 12343 bufLen += len; 12344 break; 12345 } 12346 12347 quotingType[i] = JIM_ELESTR_BRACE; 12348 12349 case JIM_ELESTR_BRACE: 12350 bufLen += len + 2; 12351 break; 12352 case JIM_ELESTR_QUOTE: 12353 bufLen += len * 2; 12354 break; 12355 } 12356 bufLen++; 12357 } 12358 bufLen++; 12359 12360 12361 p = objPtr->bytes = Jim_Alloc(bufLen + 1); 12362 realLength = 0; 12363 for (i = 0; i < objc; i++) { 12364 int len, qlen; 12365 12366 strRep = Jim_GetString(objv[i], &len); 12367 12368 switch (quotingType[i]) { 12369 case JIM_ELESTR_SIMPLE: 12370 memcpy(p, strRep, len); 12371 p += len; 12372 realLength += len; 12373 break; 12374 case JIM_ELESTR_BRACE: 12375 *p++ = '{'; 12376 memcpy(p, strRep, len); 12377 p += len; 12378 *p++ = '}'; 12379 realLength += len + 2; 12380 break; 12381 case JIM_ELESTR_QUOTE: 12382 if (i == 0 && strRep[0] == '#') { 12383 *p++ = '\\'; 12384 realLength++; 12385 } 12386 qlen = BackslashQuoteString(strRep, len, p); 12387 p += qlen; 12388 realLength += qlen; 12389 break; 12390 } 12391 12392 if (i + 1 != objc) { 12393 *p++ = ' '; 12394 realLength++; 12395 } 12396 } 12397 *p = '\0'; 12398 objPtr->length = realLength; 12399 12400 if (quotingType != staticQuoting) { 12401 Jim_Free(quotingType); 12402 } 12403 } 12404 12405 static void UpdateStringOfList(struct Jim_Obj *objPtr) 12406 { 12407 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len); 12408 } 12409 12410 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 12411 { 12412 struct JimParserCtx parser; 12413 const char *str; 12414 int strLen; 12415 Jim_Obj *fileNameObj; 12416 int linenr; 12417 12418 if (objPtr->typePtr == &listObjType) { 12419 return JIM_OK; 12420 } 12421 12422 12423 if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) { 12424 Jim_Dict *dict = objPtr->internalRep.dictValue; 12425 12426 12427 objPtr->typePtr = &listObjType; 12428 objPtr->internalRep.listValue.len = dict->len; 12429 objPtr->internalRep.listValue.maxLen = dict->maxLen; 12430 objPtr->internalRep.listValue.ele = dict->table; 12431 12432 12433 Jim_Free(dict->ht); 12434 12435 12436 Jim_Free(dict); 12437 return JIM_OK; 12438 } 12439 12440 12441 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr); 12442 Jim_IncrRefCount(fileNameObj); 12443 12444 12445 str = Jim_GetString(objPtr, &strLen); 12446 12447 Jim_FreeIntRep(interp, objPtr); 12448 objPtr->typePtr = &listObjType; 12449 objPtr->internalRep.listValue.len = 0; 12450 objPtr->internalRep.listValue.maxLen = 0; 12451 objPtr->internalRep.listValue.ele = NULL; 12452 12453 12454 if (strLen) { 12455 JimParserInit(&parser, str, strLen, linenr); 12456 while (!parser.eof) { 12457 Jim_Obj *elementPtr; 12458 12459 JimParseList(&parser); 12460 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC) 12461 continue; 12462 elementPtr = JimParserGetTokenObj(interp, &parser); 12463 Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline); 12464 ListAppendElement(objPtr, elementPtr); 12465 } 12466 } 12467 Jim_DecrRefCount(interp, fileNameObj); 12468 return JIM_OK; 12469 } 12470 12471 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) 12472 { 12473 Jim_Obj *objPtr; 12474 12475 objPtr = Jim_NewObj(interp); 12476 objPtr->typePtr = &listObjType; 12477 objPtr->bytes = NULL; 12478 objPtr->internalRep.listValue.ele = NULL; 12479 objPtr->internalRep.listValue.len = 0; 12480 objPtr->internalRep.listValue.maxLen = 0; 12481 12482 if (len) { 12483 ListInsertElements(objPtr, 0, len, elements); 12484 } 12485 12486 return objPtr; 12487 } 12488 12489 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen, 12490 Jim_Obj ***listVec) 12491 { 12492 *listLen = Jim_ListLength(interp, listObj); 12493 *listVec = listObj->internalRep.listValue.ele; 12494 } 12495 12496 12497 static int JimSign(jim_wide w) 12498 { 12499 if (w == 0) { 12500 return 0; 12501 } 12502 else if (w < 0) { 12503 return -1; 12504 } 12505 return 1; 12506 } 12507 12508 12509 struct lsort_info { 12510 jmp_buf jmpbuf; 12511 Jim_Obj *command; 12512 Jim_Interp *interp; 12513 enum { 12514 JIM_LSORT_ASCII, 12515 JIM_LSORT_NOCASE, 12516 JIM_LSORT_INTEGER, 12517 JIM_LSORT_REAL, 12518 JIM_LSORT_COMMAND, 12519 JIM_LSORT_DICT 12520 } type; 12521 int order; 12522 Jim_Obj **indexv; 12523 int indexc; 12524 int unique; 12525 int (*subfn)(Jim_Obj **, Jim_Obj **); 12526 }; 12527 12528 static struct lsort_info *sort_info; 12529 12530 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12531 { 12532 Jim_Obj *lObj, *rObj; 12533 12534 if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK || 12535 Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) { 12536 longjmp(sort_info->jmpbuf, JIM_ERR); 12537 } 12538 return sort_info->subfn(&lObj, &rObj); 12539 } 12540 12541 12542 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12543 { 12544 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; 12545 } 12546 12547 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12548 { 12549 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order; 12550 } 12551 12552 static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12553 { 12554 12555 const char *left = Jim_String(*lhsObj); 12556 const char *right = Jim_String(*rhsObj); 12557 12558 while (1) { 12559 if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) { 12560 12561 jim_wide lint, rint; 12562 char *lend, *rend; 12563 lint = jim_strtoull(left, &lend); 12564 rint = jim_strtoull(right, &rend); 12565 if (lint != rint) { 12566 return JimSign(lint - rint) * sort_info->order; 12567 } 12568 if (lend -left != rend - right) { 12569 return JimSign((lend - left) - (rend - right)) * sort_info->order; 12570 } 12571 left = lend; 12572 right = rend; 12573 } 12574 else { 12575 int cl, cr; 12576 left += utf8_tounicode_case(left, &cl, 1); 12577 right += utf8_tounicode_case(right, &cr, 1); 12578 if (cl != cr) { 12579 return JimSign(cl - cr) * sort_info->order; 12580 } 12581 if (cl == 0) { 12582 12583 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; 12584 } 12585 } 12586 } 12587 } 12588 12589 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12590 { 12591 jim_wide lhs = 0, rhs = 0; 12592 12593 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK || 12594 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { 12595 longjmp(sort_info->jmpbuf, JIM_ERR); 12596 } 12597 12598 return JimSign(lhs - rhs) * sort_info->order; 12599 } 12600 12601 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12602 { 12603 double lhs = 0, rhs = 0; 12604 12605 if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK || 12606 Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { 12607 longjmp(sort_info->jmpbuf, JIM_ERR); 12608 } 12609 if (lhs == rhs) { 12610 return 0; 12611 } 12612 if (lhs > rhs) { 12613 return sort_info->order; 12614 } 12615 return -sort_info->order; 12616 } 12617 12618 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj) 12619 { 12620 Jim_Obj *compare_script; 12621 int rc; 12622 12623 jim_wide ret = 0; 12624 12625 12626 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command); 12627 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj); 12628 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj); 12629 12630 rc = Jim_EvalObj(sort_info->interp, compare_script); 12631 12632 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) { 12633 longjmp(sort_info->jmpbuf, rc); 12634 } 12635 12636 return JimSign(ret) * sort_info->order; 12637 } 12638 12639 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs)) 12640 { 12641 int src; 12642 int dst = 0; 12643 Jim_Obj **ele = listObjPtr->internalRep.listValue.ele; 12644 12645 for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) { 12646 if (comp(&ele[dst], &ele[src]) == 0) { 12647 12648 Jim_DecrRefCount(sort_info->interp, ele[dst]); 12649 } 12650 else { 12651 12652 dst++; 12653 } 12654 ele[dst] = ele[src]; 12655 } 12656 12657 12658 dst++; 12659 if (dst < listObjPtr->internalRep.listValue.len) { 12660 ele[dst] = ele[src]; 12661 } 12662 12663 12664 listObjPtr->internalRep.listValue.len = dst; 12665 } 12666 12667 12668 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info) 12669 { 12670 struct lsort_info *prev_info; 12671 12672 typedef int (qsort_comparator) (const void *, const void *); 12673 int (*fn) (Jim_Obj **, Jim_Obj **); 12674 Jim_Obj **vector; 12675 int len; 12676 int rc; 12677 12678 JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object")); 12679 SetListFromAny(interp, listObjPtr); 12680 12681 12682 prev_info = sort_info; 12683 sort_info = info; 12684 12685 vector = listObjPtr->internalRep.listValue.ele; 12686 len = listObjPtr->internalRep.listValue.len; 12687 switch (info->type) { 12688 case JIM_LSORT_ASCII: 12689 fn = ListSortString; 12690 break; 12691 case JIM_LSORT_NOCASE: 12692 fn = ListSortStringNoCase; 12693 break; 12694 case JIM_LSORT_INTEGER: 12695 fn = ListSortInteger; 12696 break; 12697 case JIM_LSORT_REAL: 12698 fn = ListSortReal; 12699 break; 12700 case JIM_LSORT_COMMAND: 12701 fn = ListSortCommand; 12702 break; 12703 case JIM_LSORT_DICT: 12704 fn = ListSortDict; 12705 break; 12706 default: 12707 fn = NULL; 12708 JimPanic((1, "ListSort called with invalid sort type")); 12709 return -1; 12710 } 12711 12712 if (info->indexc) { 12713 12714 info->subfn = fn; 12715 fn = ListSortIndexHelper; 12716 } 12717 12718 if ((rc = setjmp(info->jmpbuf)) == 0) { 12719 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn); 12720 12721 if (info->unique && len > 1) { 12722 ListRemoveDuplicates(listObjPtr, fn); 12723 } 12724 12725 Jim_InvalidateStringRep(listObjPtr); 12726 } 12727 sort_info = prev_info; 12728 12729 return rc; 12730 } 12731 12732 12733 static void ListEnsureLength(Jim_Obj *listPtr, int idx) 12734 { 12735 assert(idx >= 0); 12736 if (idx >= listPtr->internalRep.listValue.maxLen) { 12737 if (idx < 4) { 12738 12739 idx = 4; 12740 } 12741 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele, 12742 sizeof(Jim_Obj *) * idx); 12743 12744 listPtr->internalRep.listValue.maxLen = idx; 12745 } 12746 } 12747 12748 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec) 12749 { 12750 int currentLen = listPtr->internalRep.listValue.len; 12751 int requiredLen = currentLen + elemc; 12752 int i; 12753 Jim_Obj **point; 12754 12755 if (elemc == 0) { 12756 12757 return; 12758 } 12759 12760 if (requiredLen > listPtr->internalRep.listValue.maxLen) { 12761 if (currentLen) { 12762 12763 requiredLen *= 2; 12764 } 12765 ListEnsureLength(listPtr, requiredLen); 12766 } 12767 if (idx < 0) { 12768 idx = currentLen; 12769 } 12770 point = listPtr->internalRep.listValue.ele + idx; 12771 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *)); 12772 for (i = 0; i < elemc; ++i) { 12773 point[i] = elemVec[i]; 12774 Jim_IncrRefCount(point[i]); 12775 } 12776 listPtr->internalRep.listValue.len += elemc; 12777 } 12778 12779 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr) 12780 { 12781 ListInsertElements(listPtr, -1, 1, &objPtr); 12782 } 12783 12784 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr) 12785 { 12786 ListInsertElements(listPtr, -1, 12787 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele); 12788 } 12789 12790 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr) 12791 { 12792 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object")); 12793 SetListFromAny(interp, listPtr); 12794 Jim_InvalidateStringRep(listPtr); 12795 ListAppendElement(listPtr, objPtr); 12796 } 12797 12798 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr) 12799 { 12800 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object")); 12801 SetListFromAny(interp, listPtr); 12802 SetListFromAny(interp, appendListPtr); 12803 Jim_InvalidateStringRep(listPtr); 12804 ListAppendList(listPtr, appendListPtr); 12805 } 12806 12807 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr) 12808 { 12809 SetListFromAny(interp, objPtr); 12810 return objPtr->internalRep.listValue.len; 12811 } 12812 12813 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx, 12814 int objc, Jim_Obj *const *objVec) 12815 { 12816 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object")); 12817 SetListFromAny(interp, listPtr); 12818 if (idx >= 0 && idx > listPtr->internalRep.listValue.len) 12819 idx = listPtr->internalRep.listValue.len; 12820 else if (idx < 0) 12821 idx = 0; 12822 Jim_InvalidateStringRep(listPtr); 12823 ListInsertElements(listPtr, idx, objc, objVec); 12824 } 12825 12826 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx) 12827 { 12828 SetListFromAny(interp, listPtr); 12829 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) || 12830 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) { 12831 return NULL; 12832 } 12833 if (idx < 0) 12834 idx = listPtr->internalRep.listValue.len + idx; 12835 return listPtr->internalRep.listValue.ele[idx]; 12836 } 12837 12838 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags) 12839 { 12840 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx); 12841 if (*objPtrPtr == NULL) { 12842 if (flags & JIM_ERRMSG) { 12843 Jim_SetResultString(interp, "list index out of range", -1); 12844 } 12845 return JIM_ERR; 12846 } 12847 return JIM_OK; 12848 } 12849 12850 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, 12851 Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags) 12852 { 12853 int i; 12854 int static_idxes[5]; 12855 int *idxes = static_idxes; 12856 int ret = JIM_OK; 12857 12858 if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) { 12859 idxes = Jim_Alloc(indexc * sizeof(*idxes)); 12860 } 12861 12862 for (i = 0; i < indexc; i++) { 12863 ret = Jim_GetIndex(interp, indexv[i], &idxes[i]); 12864 if (ret != JIM_OK) { 12865 goto err; 12866 } 12867 } 12868 12869 for (i = 0; i < indexc; i++) { 12870 Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]); 12871 if (!objPtr) { 12872 if (flags & JIM_ERRMSG) { 12873 if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) { 12874 Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]); 12875 } 12876 else { 12877 Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr); 12878 } 12879 } 12880 return -1; 12881 } 12882 listPtr = objPtr; 12883 } 12884 *resultObj = listPtr; 12885 err: 12886 if (idxes != static_idxes) 12887 Jim_Free(idxes); 12888 return ret; 12889 } 12890 12891 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, 12892 Jim_Obj *newObjPtr, int flags) 12893 { 12894 SetListFromAny(interp, listPtr); 12895 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) || 12896 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) { 12897 if (flags & JIM_ERRMSG) { 12898 Jim_SetResultString(interp, "list index out of range", -1); 12899 } 12900 return JIM_ERR; 12901 } 12902 if (idx < 0) 12903 idx = listPtr->internalRep.listValue.len + idx; 12904 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]); 12905 listPtr->internalRep.listValue.ele[idx] = newObjPtr; 12906 Jim_IncrRefCount(newObjPtr); 12907 return JIM_OK; 12908 } 12909 12910 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr, 12911 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr) 12912 { 12913 Jim_Obj *varObjPtr, *objPtr, *listObjPtr; 12914 int shared, i, idx; 12915 12916 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED); 12917 if (objPtr == NULL) 12918 return JIM_ERR; 12919 if ((shared = Jim_IsShared(objPtr))) 12920 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr); 12921 for (i = 0; i < indexc - 1; i++) { 12922 listObjPtr = objPtr; 12923 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK) 12924 goto err; 12925 12926 objPtr = Jim_ListGetIndex(interp, listObjPtr, idx); 12927 if (objPtr == NULL) { 12928 Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]); 12929 goto err; 12930 } 12931 if (Jim_IsShared(objPtr)) { 12932 objPtr = Jim_DuplicateObj(interp, objPtr); 12933 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE); 12934 } 12935 Jim_InvalidateStringRep(listObjPtr); 12936 } 12937 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK) 12938 goto err; 12939 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR) 12940 goto err; 12941 Jim_InvalidateStringRep(objPtr); 12942 Jim_InvalidateStringRep(varObjPtr); 12943 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) 12944 goto err; 12945 Jim_SetResult(interp, varObjPtr); 12946 return JIM_OK; 12947 err: 12948 if (shared) { 12949 Jim_FreeNewObj(interp, varObjPtr); 12950 } 12951 return JIM_ERR; 12952 } 12953 12954 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen) 12955 { 12956 int i; 12957 int listLen = Jim_ListLength(interp, listObjPtr); 12958 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp); 12959 12960 for (i = 0; i < listLen; ) { 12961 Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i)); 12962 if (++i != listLen) { 12963 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen); 12964 } 12965 } 12966 return resObjPtr; 12967 } 12968 12969 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 12970 { 12971 int i; 12972 12973 for (i = 0; i < objc; i++) { 12974 if (!Jim_IsList(objv[i])) 12975 break; 12976 } 12977 if (i == objc) { 12978 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); 12979 12980 for (i = 0; i < objc; i++) 12981 ListAppendList(objPtr, objv[i]); 12982 return objPtr; 12983 } 12984 else { 12985 12986 int len = 0, objLen; 12987 char *bytes, *p; 12988 12989 12990 for (i = 0; i < objc; i++) { 12991 len += Jim_Length(objv[i]); 12992 } 12993 if (objc) 12994 len += objc - 1; 12995 12996 p = bytes = Jim_Alloc(len + 1); 12997 for (i = 0; i < objc; i++) { 12998 const char *s = Jim_GetString(objv[i], &objLen); 12999 13000 13001 while (objLen && isspace(UCHAR(*s))) { 13002 s++; 13003 objLen--; 13004 len--; 13005 } 13006 13007 while (objLen && isspace(UCHAR(s[objLen - 1]))) { 13008 13009 if (objLen > 1 && s[objLen - 2] == '\\') { 13010 break; 13011 } 13012 objLen--; 13013 len--; 13014 } 13015 memcpy(p, s, objLen); 13016 p += objLen; 13017 if (i + 1 != objc) { 13018 if (objLen) 13019 *p++ = ' '; 13020 else { 13021 len--; 13022 } 13023 } 13024 } 13025 *p = '\0'; 13026 return Jim_NewStringObjNoAlloc(interp, bytes, len); 13027 } 13028 } 13029 13030 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, 13031 Jim_Obj *lastObjPtr) 13032 { 13033 int first, last; 13034 int len, rangeLen; 13035 13036 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK || 13037 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK) 13038 return NULL; 13039 len = Jim_ListLength(interp, listObjPtr); 13040 first = JimRelToAbsIndex(len, first); 13041 last = JimRelToAbsIndex(len, last); 13042 JimRelToAbsRange(len, &first, &last, &rangeLen); 13043 if (first == 0 && last == len) { 13044 return listObjPtr; 13045 } 13046 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen); 13047 } 13048 13049 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 13050 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 13051 static void UpdateStringOfDict(struct Jim_Obj *objPtr); 13052 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 13053 13054 13055 static const Jim_ObjType dictObjType = { 13056 "dict", 13057 FreeDictInternalRep, 13058 DupDictInternalRep, 13059 UpdateStringOfDict, 13060 JIM_TYPE_NONE, 13061 }; 13062 13063 static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict) 13064 { 13065 int i; 13066 for (i = 0; i < dict->len; i++) { 13067 Jim_DecrRefCount(interp, dict->table[i]); 13068 } 13069 Jim_Free(dict->table); 13070 Jim_Free(dict->ht); 13071 Jim_Free(dict); 13072 } 13073 13074 enum { 13075 DICT_HASH_FIND = -1, 13076 DICT_HASH_REMOVE = -2, 13077 DICT_HASH_ADD = -3, 13078 }; 13079 13080 static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset) 13081 { 13082 unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq); 13083 unsigned idx = h & dict->sizemask; 13084 int tvoffset = 0; 13085 unsigned peturb = h; 13086 unsigned first_removed = ~0; 13087 13088 if (dict->len) { 13089 while ((tvoffset = dict->ht[idx].offset)) { 13090 if (tvoffset == -1) { 13091 if (first_removed == ~0) { 13092 first_removed = idx; 13093 } 13094 } 13095 else if (dict->ht[idx].hash == h) { 13096 if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) { 13097 break; 13098 } 13099 } 13100 13101 peturb >>= 5; 13102 idx = (5 * idx + 1 + peturb) & dict->sizemask; 13103 } 13104 } 13105 13106 switch (op_tvoffset) { 13107 case DICT_HASH_FIND: 13108 13109 break; 13110 case DICT_HASH_REMOVE: 13111 if (tvoffset) { 13112 13113 dict->ht[idx].offset = -1; 13114 dict->dummy++; 13115 } 13116 13117 break; 13118 case DICT_HASH_ADD: 13119 if (tvoffset == 0) { 13120 13121 if (first_removed != ~0) { 13122 idx = first_removed; 13123 dict->dummy--; 13124 } 13125 dict->ht[idx].offset = dict->len + 1; 13126 dict->ht[idx].hash = h; 13127 } 13128 13129 break; 13130 default: 13131 assert(tvoffset); 13132 13133 dict->ht[idx].offset = op_tvoffset; 13134 break; 13135 } 13136 13137 return tvoffset; 13138 } 13139 13140 static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size) 13141 { 13142 int i; 13143 struct JimDictHashEntry *prevht = dict->ht; 13144 int prevsize = dict->size; 13145 13146 dict->size = JimHashTableNextPower(size); 13147 dict->sizemask = dict->size - 1; 13148 13149 13150 dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht)); 13151 memset(dict->ht, 0, dict->size * sizeof(*dict->ht)); 13152 13153 13154 for (i = 0; i < prevsize; i++) { 13155 if (prevht[i].offset > 0) { 13156 13157 unsigned h = prevht[i].hash; 13158 unsigned idx = h & dict->sizemask; 13159 unsigned peturb = h; 13160 13161 while (dict->ht[idx].offset) { 13162 peturb >>= 5; 13163 idx = (5 * idx + 1 + peturb) & dict->sizemask; 13164 } 13165 dict->ht[idx].offset = prevht[i].offset; 13166 dict->ht[idx].hash = h; 13167 } 13168 } 13169 Jim_Free(prevht); 13170 } 13171 13172 static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr) 13173 { 13174 if (dict->size <= dict->len + dict->dummy) { 13175 JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8); 13176 } 13177 return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD); 13178 } 13179 13180 static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size) 13181 { 13182 Jim_Dict *dict = Jim_Alloc(sizeof(*dict)); 13183 memset(dict, 0, sizeof(*dict)); 13184 13185 if (ht_size) { 13186 JimDictExpandHashTable(dict, ht_size); 13187 } 13188 if (table_size) { 13189 dict->table = Jim_Alloc(table_size * sizeof(*dict->table)); 13190 dict->maxLen = table_size; 13191 } 13192 #ifdef JIM_RANDOMISE_HASH 13193 dict->uniq = (rand() ^ time(NULL) ^ clock()); 13194 #endif 13195 return dict; 13196 } 13197 13198 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 13199 { 13200 JimFreeDict(interp, objPtr->internalRep.dictValue); 13201 } 13202 13203 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 13204 { 13205 Jim_Dict *oldDict = srcPtr->internalRep.dictValue; 13206 int i; 13207 13208 13209 Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size); 13210 13211 13212 for (i = 0; i < oldDict->len; i++) { 13213 newDict->table[i] = oldDict->table[i]; 13214 Jim_IncrRefCount(newDict->table[i]); 13215 } 13216 newDict->len = oldDict->len; 13217 13218 13219 newDict->uniq = oldDict->uniq; 13220 13221 13222 memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size); 13223 13224 dupPtr->internalRep.dictValue = newDict; 13225 dupPtr->typePtr = &dictObjType; 13226 } 13227 13228 static void UpdateStringOfDict(struct Jim_Obj *objPtr) 13229 { 13230 JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len); 13231 } 13232 13233 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 13234 { 13235 int listlen; 13236 13237 if (objPtr->typePtr == &dictObjType) { 13238 return JIM_OK; 13239 } 13240 13241 if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) { 13242 Jim_String(objPtr); 13243 } 13244 13245 listlen = Jim_ListLength(interp, objPtr); 13246 if (listlen % 2) { 13247 Jim_SetResultString(interp, "missing value to go with key", -1); 13248 return JIM_ERR; 13249 } 13250 else { 13251 13252 Jim_Dict *dict = JimDictNew(interp, 0, listlen); 13253 int i; 13254 13255 13256 dict->table = objPtr->internalRep.listValue.ele; 13257 dict->maxLen = objPtr->internalRep.listValue.maxLen; 13258 13259 13260 for (i = 0; i < listlen; i += 2) { 13261 int tvoffset = JimDictAdd(dict, dict->table[i]); 13262 if (tvoffset) { 13263 13264 13265 Jim_DecrRefCount(interp, dict->table[tvoffset]); 13266 13267 dict->table[tvoffset] = dict->table[i + 1]; 13268 13269 Jim_DecrRefCount(interp, dict->table[i]); 13270 } 13271 else { 13272 if (dict->len != i) { 13273 dict->table[dict->len++] = dict->table[i]; 13274 dict->table[dict->len++] = dict->table[i + 1]; 13275 } 13276 else { 13277 dict->len += 2; 13278 } 13279 } 13280 } 13281 13282 objPtr->typePtr = &dictObjType; 13283 objPtr->internalRep.dictValue = dict; 13284 13285 return JIM_OK; 13286 } 13287 } 13288 13289 13290 13291 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, 13292 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) 13293 { 13294 Jim_Dict *dict = objPtr->internalRep.dictValue; 13295 if (valueObjPtr == NULL) { 13296 13297 int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE); 13298 if (tvoffset) { 13299 13300 Jim_DecrRefCount(interp, dict->table[tvoffset - 1]); 13301 Jim_DecrRefCount(interp, dict->table[tvoffset]); 13302 dict->len -= 2; 13303 if (tvoffset != dict->len + 1) { 13304 13305 dict->table[tvoffset - 1] = dict->table[dict->len]; 13306 dict->table[tvoffset] = dict->table[dict->len + 1]; 13307 13308 13309 JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset); 13310 } 13311 return JIM_OK; 13312 } 13313 return JIM_ERR; 13314 } 13315 else { 13316 13317 int tvoffset = JimDictAdd(dict, keyObjPtr); 13318 if (tvoffset) { 13319 13320 Jim_IncrRefCount(valueObjPtr); 13321 Jim_DecrRefCount(interp, dict->table[tvoffset]); 13322 dict->table[tvoffset] = valueObjPtr; 13323 } 13324 else { 13325 if (dict->maxLen == dict->len) { 13326 13327 if (dict->maxLen < 4) { 13328 dict->maxLen = 4; 13329 } 13330 else { 13331 dict->maxLen *= 2; 13332 } 13333 dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table)); 13334 } 13335 Jim_IncrRefCount(keyObjPtr); 13336 Jim_IncrRefCount(valueObjPtr); 13337 13338 dict->table[dict->len++] = keyObjPtr; 13339 dict->table[dict->len++] = valueObjPtr; 13340 13341 } 13342 return JIM_OK; 13343 } 13344 } 13345 13346 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, 13347 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) 13348 { 13349 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object")); 13350 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 13351 return JIM_ERR; 13352 } 13353 Jim_InvalidateStringRep(objPtr); 13354 return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr); 13355 } 13356 13357 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) 13358 { 13359 Jim_Obj *objPtr; 13360 int i; 13361 13362 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even")); 13363 13364 objPtr = Jim_NewObj(interp); 13365 objPtr->typePtr = &dictObjType; 13366 objPtr->bytes = NULL; 13367 13368 objPtr->internalRep.dictValue = JimDictNew(interp, len, len); 13369 for (i = 0; i < len; i += 2) 13370 DictAddElement(interp, objPtr, elements[i], elements[i + 1]); 13371 return objPtr; 13372 } 13373 13374 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr, 13375 Jim_Obj **objPtrPtr, int flags) 13376 { 13377 int tvoffset; 13378 Jim_Dict *dict; 13379 13380 if (SetDictFromAny(interp, dictPtr) != JIM_OK) { 13381 return -1; 13382 } 13383 dict = dictPtr->internalRep.dictValue; 13384 tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND); 13385 if (tvoffset == 0) { 13386 if (flags & JIM_ERRMSG) { 13387 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr); 13388 } 13389 return JIM_ERR; 13390 } 13391 *objPtrPtr = dict->table[tvoffset]; 13392 return JIM_OK; 13393 } 13394 13395 Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len) 13396 { 13397 13398 if (Jim_IsList(dictPtr)) { 13399 Jim_Obj **table; 13400 JimListGetElements(interp, dictPtr, len, &table); 13401 if (*len % 2 == 0) { 13402 return table; 13403 } 13404 13405 } 13406 if (SetDictFromAny(interp, dictPtr) != JIM_OK) { 13407 13408 *len = 1; 13409 return NULL; 13410 } 13411 *len = dictPtr->internalRep.dictValue->len; 13412 return dictPtr->internalRep.dictValue->table; 13413 } 13414 13415 13416 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr, 13417 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags) 13418 { 13419 int i; 13420 13421 if (keyc == 0) { 13422 *objPtrPtr = dictPtr; 13423 return JIM_OK; 13424 } 13425 13426 for (i = 0; i < keyc; i++) { 13427 Jim_Obj *objPtr; 13428 13429 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags); 13430 if (rc != JIM_OK) { 13431 return rc; 13432 } 13433 dictPtr = objPtr; 13434 } 13435 *objPtrPtr = dictPtr; 13436 return JIM_OK; 13437 } 13438 13439 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr, 13440 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags) 13441 { 13442 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr; 13443 int shared, i; 13444 13445 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags); 13446 if (objPtr == NULL) { 13447 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) { 13448 13449 return JIM_ERR; 13450 } 13451 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0); 13452 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) { 13453 Jim_FreeNewObj(interp, varObjPtr); 13454 return JIM_ERR; 13455 } 13456 } 13457 if ((shared = Jim_IsShared(objPtr))) 13458 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr); 13459 for (i = 0; i < keyc; i++) { 13460 dictObjPtr = objPtr; 13461 13462 13463 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) { 13464 goto err; 13465 } 13466 13467 if (i == keyc - 1) { 13468 13469 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) { 13470 if (newObjPtr || (flags & JIM_MUSTEXIST)) { 13471 goto err; 13472 } 13473 } 13474 break; 13475 } 13476 13477 13478 Jim_InvalidateStringRep(dictObjPtr); 13479 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr, 13480 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) { 13481 if (Jim_IsShared(objPtr)) { 13482 objPtr = Jim_DuplicateObj(interp, objPtr); 13483 DictAddElement(interp, dictObjPtr, keyv[i], objPtr); 13484 } 13485 } 13486 else { 13487 if (newObjPtr == NULL) { 13488 goto err; 13489 } 13490 objPtr = Jim_NewDictObj(interp, NULL, 0); 13491 DictAddElement(interp, dictObjPtr, keyv[i], objPtr); 13492 } 13493 } 13494 13495 Jim_InvalidateStringRep(objPtr); 13496 Jim_InvalidateStringRep(varObjPtr); 13497 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) { 13498 goto err; 13499 } 13500 13501 if (!(flags & JIM_NORESULT)) { 13502 Jim_SetResult(interp, varObjPtr); 13503 } 13504 return JIM_OK; 13505 err: 13506 if (shared) { 13507 Jim_FreeNewObj(interp, varObjPtr); 13508 } 13509 return JIM_ERR; 13510 } 13511 13512 static void UpdateStringOfIndex(struct Jim_Obj *objPtr); 13513 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 13514 13515 static const Jim_ObjType indexObjType = { 13516 "index", 13517 NULL, 13518 NULL, 13519 UpdateStringOfIndex, 13520 JIM_TYPE_NONE, 13521 }; 13522 13523 static void UpdateStringOfIndex(struct Jim_Obj *objPtr) 13524 { 13525 if (objPtr->internalRep.intValue == -1) { 13526 JimSetStringBytes(objPtr, "end"); 13527 } 13528 else { 13529 char buf[JIM_INTEGER_SPACE + 1]; 13530 if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) { 13531 sprintf(buf, "%d", objPtr->internalRep.intValue); 13532 } 13533 else { 13534 13535 sprintf(buf, "end%d", objPtr->internalRep.intValue + 1); 13536 } 13537 JimSetStringBytes(objPtr, buf); 13538 } 13539 } 13540 13541 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 13542 { 13543 jim_wide idx; 13544 int end = 0; 13545 const char *str; 13546 Jim_Obj *exprObj = objPtr; 13547 13548 JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object")); 13549 13550 13551 str = Jim_String(objPtr); 13552 13553 13554 if (strncmp(str, "end", 3) == 0) { 13555 end = 1; 13556 str += 3; 13557 idx = 0; 13558 switch (*str) { 13559 case '\0': 13560 exprObj = NULL; 13561 break; 13562 13563 case '-': 13564 case '+': 13565 exprObj = Jim_NewStringObj(interp, str, -1); 13566 break; 13567 13568 default: 13569 goto badindex; 13570 } 13571 } 13572 if (exprObj) { 13573 int ret; 13574 Jim_IncrRefCount(exprObj); 13575 ret = Jim_GetWideExpr(interp, exprObj, &idx); 13576 Jim_DecrRefCount(interp, exprObj); 13577 if (ret != JIM_OK) { 13578 goto badindex; 13579 } 13580 } 13581 13582 if (end) { 13583 if (idx > 0) { 13584 idx = INT_MAX; 13585 } 13586 else { 13587 13588 idx--; 13589 } 13590 } 13591 else if (idx < 0) { 13592 idx = -INT_MAX; 13593 } 13594 13595 13596 Jim_FreeIntRep(interp, objPtr); 13597 objPtr->typePtr = &indexObjType; 13598 objPtr->internalRep.intValue = idx; 13599 return JIM_OK; 13600 13601 badindex: 13602 Jim_SetResultFormatted(interp, 13603 "bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr); 13604 return JIM_ERR; 13605 } 13606 13607 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr) 13608 { 13609 13610 if (objPtr->typePtr == &intObjType) { 13611 jim_wide val = JimWideValue(objPtr); 13612 13613 if (val < 0) 13614 *indexPtr = -INT_MAX; 13615 else if (val > INT_MAX) 13616 *indexPtr = INT_MAX; 13617 else 13618 *indexPtr = (int)val; 13619 return JIM_OK; 13620 } 13621 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR) 13622 return JIM_ERR; 13623 *indexPtr = objPtr->internalRep.intValue; 13624 return JIM_OK; 13625 } 13626 13627 13628 13629 static const char * const jimReturnCodes[] = { 13630 "ok", 13631 "error", 13632 "return", 13633 "break", 13634 "continue", 13635 "signal", 13636 "exit", 13637 "eval", 13638 NULL 13639 }; 13640 13641 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1) 13642 13643 static const Jim_ObjType returnCodeObjType = { 13644 "return-code", 13645 NULL, 13646 NULL, 13647 NULL, 13648 JIM_TYPE_NONE, 13649 }; 13650 13651 const char *Jim_ReturnCode(int code) 13652 { 13653 if (code < 0 || code >= (int)jimReturnCodesSize) { 13654 return "?"; 13655 } 13656 else { 13657 return jimReturnCodes[code]; 13658 } 13659 } 13660 13661 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 13662 { 13663 int returnCode; 13664 jim_wide wideValue; 13665 13666 13667 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR) 13668 returnCode = (int)wideValue; 13669 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) { 13670 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr); 13671 return JIM_ERR; 13672 } 13673 13674 Jim_FreeIntRep(interp, objPtr); 13675 objPtr->typePtr = &returnCodeObjType; 13676 objPtr->internalRep.intValue = returnCode; 13677 return JIM_OK; 13678 } 13679 13680 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr) 13681 { 13682 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR) 13683 return JIM_ERR; 13684 *intPtr = objPtr->internalRep.intValue; 13685 return JIM_OK; 13686 } 13687 13688 static int JimParseExprOperator(struct JimParserCtx *pc); 13689 static int JimParseExprNumber(struct JimParserCtx *pc); 13690 static int JimParseExprIrrational(struct JimParserCtx *pc); 13691 static int JimParseExprBoolean(struct JimParserCtx *pc); 13692 13693 13694 enum 13695 { 13696 13697 13698 13699 JIM_EXPROP_MUL = JIM_TT_EXPR_OP, 13700 JIM_EXPROP_DIV, 13701 JIM_EXPROP_MOD, 13702 JIM_EXPROP_SUB, 13703 JIM_EXPROP_ADD, 13704 JIM_EXPROP_LSHIFT, 13705 JIM_EXPROP_RSHIFT, 13706 JIM_EXPROP_ROTL, 13707 JIM_EXPROP_ROTR, 13708 JIM_EXPROP_LT, 13709 JIM_EXPROP_GT, 13710 JIM_EXPROP_LTE, 13711 JIM_EXPROP_GTE, 13712 JIM_EXPROP_NUMEQ, 13713 JIM_EXPROP_NUMNE, 13714 JIM_EXPROP_BITAND, 13715 JIM_EXPROP_BITXOR, 13716 JIM_EXPROP_BITOR, 13717 JIM_EXPROP_LOGICAND, 13718 JIM_EXPROP_LOGICOR, 13719 JIM_EXPROP_TERNARY, 13720 JIM_EXPROP_COLON, 13721 JIM_EXPROP_POW, 13722 13723 13724 JIM_EXPROP_STREQ, 13725 JIM_EXPROP_STRNE, 13726 JIM_EXPROP_STRIN, 13727 JIM_EXPROP_STRNI, 13728 JIM_EXPROP_STRLT, 13729 JIM_EXPROP_STRGT, 13730 JIM_EXPROP_STRLE, 13731 JIM_EXPROP_STRGE, 13732 13733 13734 JIM_EXPROP_NOT, 13735 JIM_EXPROP_BITNOT, 13736 JIM_EXPROP_UNARYMINUS, 13737 JIM_EXPROP_UNARYPLUS, 13738 13739 13740 JIM_EXPROP_FUNC_INT, 13741 JIM_EXPROP_FUNC_WIDE, 13742 JIM_EXPROP_FUNC_ABS, 13743 JIM_EXPROP_FUNC_DOUBLE, 13744 JIM_EXPROP_FUNC_ROUND, 13745 JIM_EXPROP_FUNC_RAND, 13746 JIM_EXPROP_FUNC_SRAND, 13747 13748 13749 JIM_EXPROP_FUNC_SIN, 13750 JIM_EXPROP_FUNC_COS, 13751 JIM_EXPROP_FUNC_TAN, 13752 JIM_EXPROP_FUNC_ASIN, 13753 JIM_EXPROP_FUNC_ACOS, 13754 JIM_EXPROP_FUNC_ATAN, 13755 JIM_EXPROP_FUNC_ATAN2, 13756 JIM_EXPROP_FUNC_SINH, 13757 JIM_EXPROP_FUNC_COSH, 13758 JIM_EXPROP_FUNC_TANH, 13759 JIM_EXPROP_FUNC_CEIL, 13760 JIM_EXPROP_FUNC_FLOOR, 13761 JIM_EXPROP_FUNC_EXP, 13762 JIM_EXPROP_FUNC_LOG, 13763 JIM_EXPROP_FUNC_LOG10, 13764 JIM_EXPROP_FUNC_SQRT, 13765 JIM_EXPROP_FUNC_POW, 13766 JIM_EXPROP_FUNC_HYPOT, 13767 JIM_EXPROP_FUNC_FMOD, 13768 }; 13769 13770 struct JimExprNode { 13771 int type; 13772 struct Jim_Obj *objPtr; 13773 13774 struct JimExprNode *left; 13775 struct JimExprNode *right; 13776 struct JimExprNode *ternary; 13777 }; 13778 13779 13780 typedef struct Jim_ExprOperator 13781 { 13782 const char *name; 13783 int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode); 13784 unsigned char precedence; 13785 unsigned char arity; 13786 unsigned char attr; 13787 unsigned char namelen; 13788 } Jim_ExprOperator; 13789 13790 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr); 13791 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node); 13792 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node); 13793 13794 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node) 13795 { 13796 int intresult = 1; 13797 int rc, bA = 0; 13798 double dA, dC = 0; 13799 jim_wide wA, wC = 0; 13800 Jim_Obj *A; 13801 13802 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 13803 return rc; 13804 } 13805 13806 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) { 13807 switch (node->type) { 13808 case JIM_EXPROP_FUNC_INT: 13809 case JIM_EXPROP_FUNC_WIDE: 13810 case JIM_EXPROP_FUNC_ROUND: 13811 case JIM_EXPROP_UNARYPLUS: 13812 wC = wA; 13813 break; 13814 case JIM_EXPROP_FUNC_DOUBLE: 13815 dC = wA; 13816 intresult = 0; 13817 break; 13818 case JIM_EXPROP_FUNC_ABS: 13819 wC = wA >= 0 ? wA : -wA; 13820 break; 13821 case JIM_EXPROP_UNARYMINUS: 13822 wC = -wA; 13823 break; 13824 case JIM_EXPROP_NOT: 13825 wC = !wA; 13826 break; 13827 default: 13828 abort(); 13829 } 13830 } 13831 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) { 13832 switch (node->type) { 13833 case JIM_EXPROP_FUNC_INT: 13834 case JIM_EXPROP_FUNC_WIDE: 13835 wC = dA; 13836 break; 13837 case JIM_EXPROP_FUNC_ROUND: 13838 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5); 13839 break; 13840 case JIM_EXPROP_FUNC_DOUBLE: 13841 case JIM_EXPROP_UNARYPLUS: 13842 dC = dA; 13843 intresult = 0; 13844 break; 13845 case JIM_EXPROP_FUNC_ABS: 13846 #ifdef JIM_MATH_FUNCTIONS 13847 dC = fabs(dA); 13848 #else 13849 dC = dA >= 0 ? dA : -dA; 13850 #endif 13851 intresult = 0; 13852 break; 13853 case JIM_EXPROP_UNARYMINUS: 13854 dC = -dA; 13855 intresult = 0; 13856 break; 13857 case JIM_EXPROP_NOT: 13858 wC = !dA; 13859 break; 13860 default: 13861 abort(); 13862 } 13863 } 13864 else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) { 13865 switch (node->type) { 13866 case JIM_EXPROP_NOT: 13867 wC = !bA; 13868 break; 13869 default: 13870 abort(); 13871 } 13872 } 13873 13874 if (rc == JIM_OK) { 13875 if (intresult) { 13876 Jim_SetResultInt(interp, wC); 13877 } 13878 else { 13879 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); 13880 } 13881 } 13882 13883 Jim_DecrRefCount(interp, A); 13884 13885 return rc; 13886 } 13887 13888 static double JimRandDouble(Jim_Interp *interp) 13889 { 13890 unsigned long x; 13891 JimRandomBytes(interp, &x, sizeof(x)); 13892 13893 return (double)x / (double)~0UL; 13894 } 13895 13896 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node) 13897 { 13898 jim_wide wA; 13899 Jim_Obj *A; 13900 int rc; 13901 13902 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 13903 return rc; 13904 } 13905 13906 rc = Jim_GetWide(interp, A, &wA); 13907 if (rc == JIM_OK) { 13908 switch (node->type) { 13909 case JIM_EXPROP_BITNOT: 13910 Jim_SetResultInt(interp, ~wA); 13911 break; 13912 case JIM_EXPROP_FUNC_SRAND: 13913 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA)); 13914 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp))); 13915 break; 13916 default: 13917 abort(); 13918 } 13919 } 13920 13921 Jim_DecrRefCount(interp, A); 13922 13923 return rc; 13924 } 13925 13926 static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node) 13927 { 13928 JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()")); 13929 13930 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp))); 13931 13932 return JIM_OK; 13933 } 13934 13935 #ifdef JIM_MATH_FUNCTIONS 13936 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node) 13937 { 13938 int rc; 13939 double dA, dC; 13940 Jim_Obj *A; 13941 13942 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 13943 return rc; 13944 } 13945 13946 rc = Jim_GetDouble(interp, A, &dA); 13947 if (rc == JIM_OK) { 13948 switch (node->type) { 13949 case JIM_EXPROP_FUNC_SIN: 13950 dC = sin(dA); 13951 break; 13952 case JIM_EXPROP_FUNC_COS: 13953 dC = cos(dA); 13954 break; 13955 case JIM_EXPROP_FUNC_TAN: 13956 dC = tan(dA); 13957 break; 13958 case JIM_EXPROP_FUNC_ASIN: 13959 dC = asin(dA); 13960 break; 13961 case JIM_EXPROP_FUNC_ACOS: 13962 dC = acos(dA); 13963 break; 13964 case JIM_EXPROP_FUNC_ATAN: 13965 dC = atan(dA); 13966 break; 13967 case JIM_EXPROP_FUNC_SINH: 13968 dC = sinh(dA); 13969 break; 13970 case JIM_EXPROP_FUNC_COSH: 13971 dC = cosh(dA); 13972 break; 13973 case JIM_EXPROP_FUNC_TANH: 13974 dC = tanh(dA); 13975 break; 13976 case JIM_EXPROP_FUNC_CEIL: 13977 dC = ceil(dA); 13978 break; 13979 case JIM_EXPROP_FUNC_FLOOR: 13980 dC = floor(dA); 13981 break; 13982 case JIM_EXPROP_FUNC_EXP: 13983 dC = exp(dA); 13984 break; 13985 case JIM_EXPROP_FUNC_LOG: 13986 dC = log(dA); 13987 break; 13988 case JIM_EXPROP_FUNC_LOG10: 13989 dC = log10(dA); 13990 break; 13991 case JIM_EXPROP_FUNC_SQRT: 13992 dC = sqrt(dA); 13993 break; 13994 default: 13995 abort(); 13996 } 13997 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); 13998 } 13999 14000 Jim_DecrRefCount(interp, A); 14001 14002 return rc; 14003 } 14004 #endif 14005 14006 14007 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node) 14008 { 14009 jim_wide wA, wB; 14010 int rc; 14011 Jim_Obj *A, *B; 14012 14013 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 14014 return rc; 14015 } 14016 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { 14017 Jim_DecrRefCount(interp, A); 14018 return rc; 14019 } 14020 14021 rc = JIM_ERR; 14022 14023 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) { 14024 jim_wide wC; 14025 14026 rc = JIM_OK; 14027 14028 switch (node->type) { 14029 case JIM_EXPROP_LSHIFT: 14030 wC = wA << wB; 14031 break; 14032 case JIM_EXPROP_RSHIFT: 14033 wC = wA >> wB; 14034 break; 14035 case JIM_EXPROP_BITAND: 14036 wC = wA & wB; 14037 break; 14038 case JIM_EXPROP_BITXOR: 14039 wC = wA ^ wB; 14040 break; 14041 case JIM_EXPROP_BITOR: 14042 wC = wA | wB; 14043 break; 14044 case JIM_EXPROP_MOD: 14045 if (wB == 0) { 14046 wC = 0; 14047 Jim_SetResultString(interp, "Division by zero", -1); 14048 rc = JIM_ERR; 14049 } 14050 else { 14051 int negative = 0; 14052 14053 if (wB < 0) { 14054 wB = -wB; 14055 wA = -wA; 14056 negative = 1; 14057 } 14058 wC = wA % wB; 14059 if (wC < 0) { 14060 wC += wB; 14061 } 14062 if (negative) { 14063 wC = -wC; 14064 } 14065 } 14066 break; 14067 case JIM_EXPROP_ROTL: 14068 case JIM_EXPROP_ROTR:{ 14069 14070 unsigned long uA = (unsigned long)wA; 14071 unsigned long uB = (unsigned long)wB; 14072 const unsigned int S = sizeof(unsigned long) * 8; 14073 14074 14075 uB %= S; 14076 14077 if (node->type == JIM_EXPROP_ROTR) { 14078 uB = S - uB; 14079 } 14080 wC = (unsigned long)(uA << uB) | (uA >> (S - uB)); 14081 break; 14082 } 14083 default: 14084 abort(); 14085 } 14086 Jim_SetResultInt(interp, wC); 14087 } 14088 14089 Jim_DecrRefCount(interp, A); 14090 Jim_DecrRefCount(interp, B); 14091 14092 return rc; 14093 } 14094 14095 14096 14097 static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node) 14098 { 14099 int rc = JIM_OK; 14100 double dA, dB, dC = 0; 14101 jim_wide wA, wB, wC = 0; 14102 Jim_Obj *A, *B; 14103 14104 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 14105 return rc; 14106 } 14107 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { 14108 Jim_DecrRefCount(interp, A); 14109 return rc; 14110 } 14111 14112 if ((A->typePtr != &doubleObjType || A->bytes) && 14113 (B->typePtr != &doubleObjType || B->bytes) && 14114 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) { 14115 14116 14117 14118 switch (node->type) { 14119 case JIM_EXPROP_POW: 14120 case JIM_EXPROP_FUNC_POW: 14121 if (wA == 0 && wB < 0) { 14122 Jim_SetResultString(interp, "exponentiation of zero by negative power", -1); 14123 rc = JIM_ERR; 14124 goto done; 14125 } 14126 wC = JimPowWide(wA, wB); 14127 goto intresult; 14128 case JIM_EXPROP_ADD: 14129 wC = wA + wB; 14130 goto intresult; 14131 case JIM_EXPROP_SUB: 14132 wC = wA - wB; 14133 goto intresult; 14134 case JIM_EXPROP_MUL: 14135 wC = wA * wB; 14136 goto intresult; 14137 case JIM_EXPROP_DIV: 14138 if (wB == 0) { 14139 Jim_SetResultString(interp, "Division by zero", -1); 14140 rc = JIM_ERR; 14141 goto done; 14142 } 14143 else { 14144 if (wB < 0) { 14145 wB = -wB; 14146 wA = -wA; 14147 } 14148 wC = wA / wB; 14149 if (wA % wB < 0) { 14150 wC--; 14151 } 14152 goto intresult; 14153 } 14154 case JIM_EXPROP_LT: 14155 wC = wA < wB; 14156 goto intresult; 14157 case JIM_EXPROP_GT: 14158 wC = wA > wB; 14159 goto intresult; 14160 case JIM_EXPROP_LTE: 14161 wC = wA <= wB; 14162 goto intresult; 14163 case JIM_EXPROP_GTE: 14164 wC = wA >= wB; 14165 goto intresult; 14166 case JIM_EXPROP_NUMEQ: 14167 wC = wA == wB; 14168 goto intresult; 14169 case JIM_EXPROP_NUMNE: 14170 wC = wA != wB; 14171 goto intresult; 14172 } 14173 } 14174 if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) { 14175 switch (node->type) { 14176 #ifndef JIM_MATH_FUNCTIONS 14177 case JIM_EXPROP_POW: 14178 case JIM_EXPROP_FUNC_POW: 14179 case JIM_EXPROP_FUNC_ATAN2: 14180 case JIM_EXPROP_FUNC_HYPOT: 14181 case JIM_EXPROP_FUNC_FMOD: 14182 Jim_SetResultString(interp, "unsupported", -1); 14183 rc = JIM_ERR; 14184 goto done; 14185 #else 14186 case JIM_EXPROP_POW: 14187 case JIM_EXPROP_FUNC_POW: 14188 dC = pow(dA, dB); 14189 goto doubleresult; 14190 case JIM_EXPROP_FUNC_ATAN2: 14191 dC = atan2(dA, dB); 14192 goto doubleresult; 14193 case JIM_EXPROP_FUNC_HYPOT: 14194 dC = hypot(dA, dB); 14195 goto doubleresult; 14196 case JIM_EXPROP_FUNC_FMOD: 14197 dC = fmod(dA, dB); 14198 goto doubleresult; 14199 #endif 14200 case JIM_EXPROP_ADD: 14201 dC = dA + dB; 14202 goto doubleresult; 14203 case JIM_EXPROP_SUB: 14204 dC = dA - dB; 14205 goto doubleresult; 14206 case JIM_EXPROP_MUL: 14207 dC = dA * dB; 14208 goto doubleresult; 14209 case JIM_EXPROP_DIV: 14210 if (dB == 0) { 14211 #ifdef INFINITY 14212 dC = dA < 0 ? -INFINITY : INFINITY; 14213 #else 14214 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL); 14215 #endif 14216 } 14217 else { 14218 dC = dA / dB; 14219 } 14220 goto doubleresult; 14221 case JIM_EXPROP_LT: 14222 wC = dA < dB; 14223 goto intresult; 14224 case JIM_EXPROP_GT: 14225 wC = dA > dB; 14226 goto intresult; 14227 case JIM_EXPROP_LTE: 14228 wC = dA <= dB; 14229 goto intresult; 14230 case JIM_EXPROP_GTE: 14231 wC = dA >= dB; 14232 goto intresult; 14233 case JIM_EXPROP_NUMEQ: 14234 wC = dA == dB; 14235 goto intresult; 14236 case JIM_EXPROP_NUMNE: 14237 wC = dA != dB; 14238 goto intresult; 14239 } 14240 } 14241 else { 14242 14243 14244 14245 int i = Jim_StringCompareObj(interp, A, B, 0); 14246 14247 switch (node->type) { 14248 case JIM_EXPROP_LT: 14249 wC = i < 0; 14250 goto intresult; 14251 case JIM_EXPROP_GT: 14252 wC = i > 0; 14253 goto intresult; 14254 case JIM_EXPROP_LTE: 14255 wC = i <= 0; 14256 goto intresult; 14257 case JIM_EXPROP_GTE: 14258 wC = i >= 0; 14259 goto intresult; 14260 case JIM_EXPROP_NUMEQ: 14261 wC = i == 0; 14262 goto intresult; 14263 case JIM_EXPROP_NUMNE: 14264 wC = i != 0; 14265 goto intresult; 14266 } 14267 } 14268 14269 rc = JIM_ERR; 14270 done: 14271 Jim_DecrRefCount(interp, A); 14272 Jim_DecrRefCount(interp, B); 14273 return rc; 14274 intresult: 14275 Jim_SetResultInt(interp, wC); 14276 goto done; 14277 doubleresult: 14278 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); 14279 goto done; 14280 } 14281 14282 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj) 14283 { 14284 int listlen; 14285 int i; 14286 14287 listlen = Jim_ListLength(interp, listObjPtr); 14288 for (i = 0; i < listlen; i++) { 14289 if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) { 14290 return 1; 14291 } 14292 } 14293 return 0; 14294 } 14295 14296 14297 14298 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node) 14299 { 14300 Jim_Obj *A, *B; 14301 jim_wide wC; 14302 int comp, rc; 14303 14304 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { 14305 return rc; 14306 } 14307 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { 14308 Jim_DecrRefCount(interp, A); 14309 return rc; 14310 } 14311 14312 switch (node->type) { 14313 case JIM_EXPROP_STREQ: 14314 case JIM_EXPROP_STRNE: 14315 wC = Jim_StringEqObj(A, B); 14316 if (node->type == JIM_EXPROP_STRNE) { 14317 wC = !wC; 14318 } 14319 break; 14320 case JIM_EXPROP_STRLT: 14321 case JIM_EXPROP_STRGT: 14322 case JIM_EXPROP_STRLE: 14323 case JIM_EXPROP_STRGE: 14324 comp = Jim_StringCompareObj(interp, A, B, 0); 14325 if (node->type == JIM_EXPROP_STRLT) { 14326 wC = comp == -1; 14327 } else if (node->type == JIM_EXPROP_STRGT) { 14328 wC = comp == 1; 14329 } else if (node->type == JIM_EXPROP_STRLE) { 14330 wC = comp == -1 || comp == 0; 14331 } else { 14332 wC = comp == 0 || comp == 1; 14333 } 14334 break; 14335 case JIM_EXPROP_STRIN: 14336 wC = JimSearchList(interp, B, A); 14337 break; 14338 case JIM_EXPROP_STRNI: 14339 wC = !JimSearchList(interp, B, A); 14340 break; 14341 default: 14342 abort(); 14343 } 14344 Jim_SetResultInt(interp, wC); 14345 14346 Jim_DecrRefCount(interp, A); 14347 Jim_DecrRefCount(interp, B); 14348 14349 return rc; 14350 } 14351 14352 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj) 14353 { 14354 long l; 14355 double d; 14356 int b; 14357 int ret = -1; 14358 14359 14360 Jim_IncrRefCount(obj); 14361 14362 if (Jim_GetLong(interp, obj, &l) == JIM_OK) { 14363 ret = (l != 0); 14364 } 14365 else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) { 14366 ret = (d != 0); 14367 } 14368 else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) { 14369 ret = (b != 0); 14370 } 14371 14372 Jim_DecrRefCount(interp, obj); 14373 return ret; 14374 } 14375 14376 static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node) 14377 { 14378 14379 int result = JimExprGetTermBoolean(interp, node->left); 14380 14381 if (result == 1) { 14382 14383 result = JimExprGetTermBoolean(interp, node->right); 14384 } 14385 if (result == -1) { 14386 return JIM_ERR; 14387 } 14388 Jim_SetResultInt(interp, result); 14389 return JIM_OK; 14390 } 14391 14392 static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node) 14393 { 14394 14395 int result = JimExprGetTermBoolean(interp, node->left); 14396 14397 if (result == 0) { 14398 14399 result = JimExprGetTermBoolean(interp, node->right); 14400 } 14401 if (result == -1) { 14402 return JIM_ERR; 14403 } 14404 Jim_SetResultInt(interp, result); 14405 return JIM_OK; 14406 } 14407 14408 static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node) 14409 { 14410 14411 int result = JimExprGetTermBoolean(interp, node->left); 14412 14413 if (result == 1) { 14414 14415 return JimExprEvalTermNode(interp, node->right); 14416 } 14417 else if (result == 0) { 14418 14419 return JimExprEvalTermNode(interp, node->ternary); 14420 } 14421 14422 return JIM_ERR; 14423 } 14424 14425 enum 14426 { 14427 OP_FUNC = 0x0001, 14428 OP_RIGHT_ASSOC = 0x0002, 14429 }; 14430 14431 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1} 14432 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0) 14433 14434 static const struct Jim_ExprOperator Jim_ExprOperators[] = { 14435 OPRINIT("*", 110, 2, JimExprOpBin), 14436 OPRINIT("/", 110, 2, JimExprOpBin), 14437 OPRINIT("%", 110, 2, JimExprOpIntBin), 14438 14439 OPRINIT("-", 100, 2, JimExprOpBin), 14440 OPRINIT("+", 100, 2, JimExprOpBin), 14441 14442 OPRINIT("<<", 90, 2, JimExprOpIntBin), 14443 OPRINIT(">>", 90, 2, JimExprOpIntBin), 14444 14445 OPRINIT("<<<", 90, 2, JimExprOpIntBin), 14446 OPRINIT(">>>", 90, 2, JimExprOpIntBin), 14447 14448 OPRINIT("<", 80, 2, JimExprOpBin), 14449 OPRINIT(">", 80, 2, JimExprOpBin), 14450 OPRINIT("<=", 80, 2, JimExprOpBin), 14451 OPRINIT(">=", 80, 2, JimExprOpBin), 14452 14453 OPRINIT("==", 70, 2, JimExprOpBin), 14454 OPRINIT("!=", 70, 2, JimExprOpBin), 14455 14456 OPRINIT("&", 50, 2, JimExprOpIntBin), 14457 OPRINIT("^", 49, 2, JimExprOpIntBin), 14458 OPRINIT("|", 48, 2, JimExprOpIntBin), 14459 14460 OPRINIT("&&", 10, 2, JimExprOpAnd), 14461 OPRINIT("||", 9, 2, JimExprOpOr), 14462 OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC), 14463 OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC), 14464 14465 14466 OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC), 14467 14468 OPRINIT("eq", 60, 2, JimExprOpStrBin), 14469 OPRINIT("ne", 60, 2, JimExprOpStrBin), 14470 14471 OPRINIT("in", 55, 2, JimExprOpStrBin), 14472 OPRINIT("ni", 55, 2, JimExprOpStrBin), 14473 14474 OPRINIT("lt", 75, 2, JimExprOpStrBin), 14475 OPRINIT("gt", 75, 2, JimExprOpStrBin), 14476 OPRINIT("le", 75, 2, JimExprOpStrBin), 14477 OPRINIT("ge", 75, 2, JimExprOpStrBin), 14478 14479 OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), 14480 OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC), 14481 OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), 14482 OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), 14483 14484 14485 14486 OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC), 14487 OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC), 14488 OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC), 14489 OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC), 14490 OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC), 14491 OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC), 14492 OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC), 14493 14494 #ifdef JIM_MATH_FUNCTIONS 14495 OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14496 OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14497 OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14498 OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14499 OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14500 OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14501 OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC), 14502 OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14503 OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14504 OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14505 OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14506 OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14507 OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14508 OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14509 OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14510 OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC), 14511 OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC), 14512 OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC), 14513 OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC), 14514 #endif 14515 }; 14516 #undef OPRINIT 14517 #undef OPRINIT_ATTR 14518 14519 #define JIM_EXPR_OPERATORS_NUM \ 14520 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) 14521 14522 static int JimParseExpression(struct JimParserCtx *pc) 14523 { 14524 pc->errmsg = NULL; 14525 14526 while (1) { 14527 14528 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) { 14529 if (*pc->p == '\n') { 14530 pc->linenr++; 14531 } 14532 pc->p++; 14533 pc->len--; 14534 } 14535 14536 if (*pc->p == '#') { 14537 JimParseComment(pc); 14538 14539 continue; 14540 } 14541 break; 14542 } 14543 14544 14545 pc->tline = pc->linenr; 14546 pc->tstart = pc->p; 14547 14548 if (pc->len == 0) { 14549 pc->tend = pc->p; 14550 pc->tt = JIM_TT_EOL; 14551 pc->eof = 1; 14552 return JIM_OK; 14553 } 14554 switch (*(pc->p)) { 14555 case '(': 14556 pc->tt = JIM_TT_SUBEXPR_START; 14557 goto singlechar; 14558 case ')': 14559 pc->tt = JIM_TT_SUBEXPR_END; 14560 goto singlechar; 14561 case ',': 14562 pc->tt = JIM_TT_SUBEXPR_COMMA; 14563 singlechar: 14564 pc->tend = pc->p; 14565 pc->p++; 14566 pc->len--; 14567 break; 14568 case '[': 14569 return JimParseCmd(pc); 14570 case '$': 14571 if (JimParseVar(pc) == JIM_ERR) 14572 return JimParseExprOperator(pc); 14573 else { 14574 14575 if (pc->tt == JIM_TT_EXPRSUGAR) { 14576 pc->errmsg = "nesting expr in expr is not allowed"; 14577 return JIM_ERR; 14578 } 14579 return JIM_OK; 14580 } 14581 break; 14582 case '0': 14583 case '1': 14584 case '2': 14585 case '3': 14586 case '4': 14587 case '5': 14588 case '6': 14589 case '7': 14590 case '8': 14591 case '9': 14592 case '.': 14593 return JimParseExprNumber(pc); 14594 case '"': 14595 return JimParseQuote(pc); 14596 case '{': 14597 return JimParseBrace(pc); 14598 14599 case 'N': 14600 case 'I': 14601 case 'n': 14602 case 'i': 14603 if (JimParseExprIrrational(pc) == JIM_ERR) 14604 if (JimParseExprBoolean(pc) == JIM_ERR) 14605 return JimParseExprOperator(pc); 14606 break; 14607 case 't': 14608 case 'f': 14609 case 'o': 14610 case 'y': 14611 if (JimParseExprBoolean(pc) == JIM_ERR) 14612 return JimParseExprOperator(pc); 14613 break; 14614 default: 14615 return JimParseExprOperator(pc); 14616 break; 14617 } 14618 return JIM_OK; 14619 } 14620 14621 static int JimParseExprNumber(struct JimParserCtx *pc) 14622 { 14623 char *end; 14624 14625 14626 pc->tt = JIM_TT_EXPR_INT; 14627 14628 jim_strtoull(pc->p, (char **)&pc->p); 14629 14630 if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) { 14631 if (strtod(pc->tstart, &end)) { } 14632 if (end == pc->tstart) 14633 return JIM_ERR; 14634 if (end > pc->p) { 14635 14636 pc->tt = JIM_TT_EXPR_DOUBLE; 14637 pc->p = end; 14638 } 14639 } 14640 pc->tend = pc->p - 1; 14641 pc->len -= (pc->p - pc->tstart); 14642 return JIM_OK; 14643 } 14644 14645 static int JimParseExprIrrational(struct JimParserCtx *pc) 14646 { 14647 const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL }; 14648 int i; 14649 14650 for (i = 0; irrationals[i]; i++) { 14651 const char *irr = irrationals[i]; 14652 14653 if (strncmp(irr, pc->p, 3) == 0) { 14654 pc->p += 3; 14655 pc->len -= 3; 14656 pc->tend = pc->p - 1; 14657 pc->tt = JIM_TT_EXPR_DOUBLE; 14658 return JIM_OK; 14659 } 14660 } 14661 return JIM_ERR; 14662 } 14663 14664 static int JimParseExprBoolean(struct JimParserCtx *pc) 14665 { 14666 int i; 14667 for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) { 14668 if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) { 14669 pc->p += jim_true_false_lens[i]; 14670 pc->len -= jim_true_false_lens[i]; 14671 pc->tend = pc->p - 1; 14672 pc->tt = JIM_TT_EXPR_BOOLEAN; 14673 return JIM_OK; 14674 } 14675 } 14676 return JIM_ERR; 14677 } 14678 14679 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode) 14680 { 14681 static Jim_ExprOperator dummy_op; 14682 if (opcode < JIM_TT_EXPR_OP) { 14683 return &dummy_op; 14684 } 14685 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP]; 14686 } 14687 14688 static int JimParseExprOperator(struct JimParserCtx *pc) 14689 { 14690 int i; 14691 const struct Jim_ExprOperator *bestOp = NULL; 14692 int bestLen = 0; 14693 14694 14695 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { 14696 const struct Jim_ExprOperator *op = &Jim_ExprOperators[i]; 14697 14698 if (op->name[0] != pc->p[0]) { 14699 continue; 14700 } 14701 14702 if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) { 14703 bestOp = op; 14704 bestLen = op->namelen; 14705 } 14706 } 14707 if (bestOp == NULL) { 14708 return JIM_ERR; 14709 } 14710 14711 14712 if (bestOp->attr & OP_FUNC) { 14713 const char *p = pc->p + bestLen; 14714 int len = pc->len - bestLen; 14715 14716 while (len && isspace(UCHAR(*p))) { 14717 len--; 14718 p++; 14719 } 14720 if (*p != '(') { 14721 pc->errmsg = "function requires parentheses"; 14722 return JIM_ERR; 14723 } 14724 } 14725 pc->tend = pc->p + bestLen - 1; 14726 pc->p += bestLen; 14727 pc->len -= bestLen; 14728 14729 pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP; 14730 return JIM_OK; 14731 } 14732 14733 14734 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 14735 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 14736 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); 14737 14738 static const Jim_ObjType exprObjType = { 14739 "expression", 14740 FreeExprInternalRep, 14741 DupExprInternalRep, 14742 NULL, 14743 JIM_TYPE_NONE, 14744 }; 14745 14746 14747 struct ExprTree 14748 { 14749 struct JimExprNode *expr; 14750 struct JimExprNode *nodes; 14751 int len; 14752 int inUse; 14753 }; 14754 14755 static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num) 14756 { 14757 int i; 14758 for (i = 0; i < num; i++) { 14759 if (nodes[i].objPtr) { 14760 Jim_DecrRefCount(interp, nodes[i].objPtr); 14761 } 14762 } 14763 Jim_Free(nodes); 14764 } 14765 14766 static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr) 14767 { 14768 ExprTreeFreeNodes(interp, expr->nodes, expr->len); 14769 Jim_Free(expr); 14770 } 14771 14772 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 14773 { 14774 struct ExprTree *expr = (void *)objPtr->internalRep.ptr; 14775 14776 if (expr) { 14777 if (--expr->inUse != 0) { 14778 return; 14779 } 14780 14781 ExprTreeFree(interp, expr); 14782 } 14783 } 14784 14785 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 14786 { 14787 JIM_NOTUSED(interp); 14788 JIM_NOTUSED(srcPtr); 14789 14790 14791 dupPtr->typePtr = NULL; 14792 } 14793 14794 struct ExprBuilder { 14795 int parencount; 14796 int level; 14797 ParseToken *token; 14798 ParseToken *first_token; 14799 Jim_Stack stack; 14800 Jim_Obj *exprObjPtr; 14801 Jim_Obj *fileNameObj; 14802 struct JimExprNode *nodes; 14803 struct JimExprNode *next; 14804 }; 14805 14806 #ifdef DEBUG_SHOW_EXPR 14807 static void JimShowExprNode(struct JimExprNode *node, int level) 14808 { 14809 int i; 14810 for (i = 0; i < level; i++) { 14811 printf(" "); 14812 } 14813 if (TOKEN_IS_EXPR_OP(node->type)) { 14814 printf("%s\n", jim_tt_name(node->type)); 14815 if (node->left) { 14816 JimShowExprNode(node->left, level + 1); 14817 } 14818 if (node->right) { 14819 JimShowExprNode(node->right, level + 1); 14820 } 14821 if (node->ternary) { 14822 JimShowExprNode(node->ternary, level + 1); 14823 } 14824 } 14825 else { 14826 printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr)); 14827 } 14828 } 14829 #endif 14830 14831 #define EXPR_UNTIL_CLOSE 0x0001 14832 #define EXPR_FUNC_ARGS 0x0002 14833 #define EXPR_TERNARY 0x0004 14834 14835 static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) { 14836 int rc; 14837 struct JimExprNode *node; 14838 14839 int exp_stacklen = builder->stack.len + exp_numterms; 14840 14841 if (builder->level++ > 200) { 14842 Jim_SetResultString(interp, "Expression too complex", -1); 14843 return JIM_ERR; 14844 } 14845 14846 while (builder->token->type != JIM_TT_EOL) { 14847 ParseToken *t = builder->token++; 14848 int prevtt; 14849 14850 if (t == builder->first_token) { 14851 prevtt = JIM_TT_NONE; 14852 } 14853 else { 14854 prevtt = t[-1].type; 14855 } 14856 14857 if (t->type == JIM_TT_SUBEXPR_START) { 14858 if (builder->stack.len == exp_stacklen) { 14859 Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr); 14860 return JIM_ERR; 14861 } 14862 builder->parencount++; 14863 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1); 14864 if (rc != JIM_OK) { 14865 return rc; 14866 } 14867 14868 } 14869 else if (t->type == JIM_TT_SUBEXPR_END) { 14870 if (!(flags & EXPR_UNTIL_CLOSE)) { 14871 if (builder->stack.len == exp_stacklen && builder->level > 1) { 14872 builder->token--; 14873 builder->level--; 14874 return JIM_OK; 14875 } 14876 Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr); 14877 return JIM_ERR; 14878 } 14879 builder->parencount--; 14880 if (builder->stack.len == exp_stacklen) { 14881 14882 break; 14883 } 14884 } 14885 else if (t->type == JIM_TT_SUBEXPR_COMMA) { 14886 if (!(flags & EXPR_FUNC_ARGS)) { 14887 if (builder->stack.len == exp_stacklen) { 14888 14889 builder->token--; 14890 builder->level--; 14891 return JIM_OK; 14892 } 14893 Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr); 14894 return JIM_ERR; 14895 } 14896 else { 14897 14898 if (builder->stack.len > exp_stacklen) { 14899 Jim_SetResultFormatted(interp, "too many arguments to math function"); 14900 return JIM_ERR; 14901 } 14902 } 14903 14904 } 14905 else if (t->type == JIM_EXPROP_COLON) { 14906 if (!(flags & EXPR_TERNARY)) { 14907 if (builder->level != 1) { 14908 14909 builder->token--; 14910 builder->level--; 14911 return JIM_OK; 14912 } 14913 Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr); 14914 return JIM_ERR; 14915 } 14916 if (builder->stack.len == exp_stacklen) { 14917 14918 builder->token--; 14919 builder->level--; 14920 return JIM_OK; 14921 } 14922 14923 } 14924 else if (TOKEN_IS_EXPR_OP(t->type)) { 14925 const struct Jim_ExprOperator *op; 14926 14927 14928 if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) { 14929 if (t->type == JIM_EXPROP_SUB) { 14930 t->type = JIM_EXPROP_UNARYMINUS; 14931 } 14932 else if (t->type == JIM_EXPROP_ADD) { 14933 t->type = JIM_EXPROP_UNARYPLUS; 14934 } 14935 } 14936 14937 op = JimExprOperatorInfoByOpcode(t->type); 14938 14939 if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) { 14940 14941 builder->token--; 14942 break; 14943 } 14944 14945 if (op->attr & OP_FUNC) { 14946 if (builder->token->type != JIM_TT_SUBEXPR_START) { 14947 Jim_SetResultString(interp, "missing arguments for math function", -1); 14948 return JIM_ERR; 14949 } 14950 builder->token++; 14951 if (op->arity == 0) { 14952 if (builder->token->type != JIM_TT_SUBEXPR_END) { 14953 Jim_SetResultString(interp, "too many arguments for math function", -1); 14954 return JIM_ERR; 14955 } 14956 builder->token++; 14957 goto noargs; 14958 } 14959 builder->parencount++; 14960 14961 14962 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity); 14963 } 14964 else if (t->type == JIM_EXPROP_TERNARY) { 14965 14966 rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2); 14967 } 14968 else { 14969 rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1); 14970 } 14971 14972 if (rc != JIM_OK) { 14973 return rc; 14974 } 14975 14976 noargs: 14977 node = builder->next++; 14978 node->type = t->type; 14979 14980 if (op->arity >= 3) { 14981 node->ternary = Jim_StackPop(&builder->stack); 14982 if (node->ternary == NULL) { 14983 goto missingoperand; 14984 } 14985 } 14986 if (op->arity >= 2) { 14987 node->right = Jim_StackPop(&builder->stack); 14988 if (node->right == NULL) { 14989 goto missingoperand; 14990 } 14991 } 14992 if (op->arity >= 1) { 14993 node->left = Jim_StackPop(&builder->stack); 14994 if (node->left == NULL) { 14995 missingoperand: 14996 Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr); 14997 builder->next--; 14998 return JIM_ERR; 14999 15000 } 15001 } 15002 15003 15004 Jim_StackPush(&builder->stack, node); 15005 } 15006 else { 15007 Jim_Obj *objPtr = NULL; 15008 15009 15010 15011 15012 if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) { 15013 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr); 15014 return JIM_ERR; 15015 } 15016 15017 15018 if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) { 15019 char *endptr; 15020 if (t->type == JIM_TT_EXPR_INT) { 15021 objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr)); 15022 } 15023 else { 15024 objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr)); 15025 } 15026 if (endptr != t->token + t->len) { 15027 15028 Jim_FreeNewObj(interp, objPtr); 15029 objPtr = NULL; 15030 } 15031 } 15032 15033 if (!objPtr) { 15034 15035 objPtr = Jim_NewStringObj(interp, t->token, t->len); 15036 if (t->type == JIM_TT_CMD) { 15037 15038 Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line); 15039 } 15040 } 15041 15042 15043 node = builder->next++; 15044 node->objPtr = objPtr; 15045 Jim_IncrRefCount(node->objPtr); 15046 node->type = t->type; 15047 Jim_StackPush(&builder->stack, node); 15048 } 15049 } 15050 15051 if (builder->stack.len == exp_stacklen) { 15052 builder->level--; 15053 return JIM_OK; 15054 } 15055 15056 if ((flags & EXPR_FUNC_ARGS)) { 15057 Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many"); 15058 } 15059 else { 15060 if (builder->stack.len < exp_stacklen) { 15061 if (builder->level == 0) { 15062 Jim_SetResultFormatted(interp, "empty expression"); 15063 } 15064 else { 15065 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr); 15066 } 15067 } 15068 else { 15069 Jim_SetResultFormatted(interp, "extra terms after expression"); 15070 } 15071 } 15072 15073 return JIM_ERR; 15074 } 15075 15076 static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj) 15077 { 15078 struct ExprTree *expr; 15079 struct ExprBuilder builder; 15080 int rc; 15081 struct JimExprNode *top = NULL; 15082 15083 builder.parencount = 0; 15084 builder.level = 0; 15085 builder.token = builder.first_token = tokenlist->list; 15086 builder.exprObjPtr = exprObjPtr; 15087 builder.fileNameObj = fileNameObj; 15088 15089 builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1)); 15090 memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1)); 15091 builder.next = builder.nodes; 15092 Jim_InitStack(&builder.stack); 15093 15094 rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1); 15095 15096 if (rc == JIM_OK) { 15097 top = Jim_StackPop(&builder.stack); 15098 15099 if (builder.parencount) { 15100 Jim_SetResultString(interp, "missing close parenthesis", -1); 15101 rc = JIM_ERR; 15102 } 15103 } 15104 15105 15106 Jim_FreeStack(&builder.stack); 15107 15108 if (rc != JIM_OK) { 15109 ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes); 15110 return NULL; 15111 } 15112 15113 expr = Jim_Alloc(sizeof(*expr)); 15114 expr->inUse = 1; 15115 expr->expr = top; 15116 expr->nodes = builder.nodes; 15117 expr->len = builder.next - builder.nodes; 15118 15119 assert(expr->len <= tokenlist->count - 1); 15120 15121 return expr; 15122 } 15123 15124 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) 15125 { 15126 int exprTextLen; 15127 const char *exprText; 15128 struct JimParserCtx parser; 15129 struct ExprTree *expr; 15130 ParseTokenList tokenlist; 15131 int line; 15132 Jim_Obj *fileNameObj; 15133 int rc = JIM_ERR; 15134 15135 15136 fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line); 15137 Jim_IncrRefCount(fileNameObj); 15138 15139 exprText = Jim_GetString(objPtr, &exprTextLen); 15140 15141 15142 ScriptTokenListInit(&tokenlist); 15143 15144 JimParserInit(&parser, exprText, exprTextLen, line); 15145 while (!parser.eof) { 15146 if (JimParseExpression(&parser) != JIM_OK) { 15147 ScriptTokenListFree(&tokenlist); 15148 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr); 15149 if (parser.errmsg) { 15150 Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL); 15151 } 15152 expr = NULL; 15153 goto err; 15154 } 15155 15156 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, 15157 parser.tline); 15158 } 15159 15160 #ifdef DEBUG_SHOW_EXPR_TOKENS 15161 { 15162 int i; 15163 printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj)); 15164 for (i = 0; i < tokenlist.count; i++) { 15165 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type), 15166 tokenlist.list[i].len, tokenlist.list[i].token); 15167 } 15168 } 15169 #endif 15170 15171 if (tokenlist.count <= 1) { 15172 Jim_SetResultString(interp, "empty expression", -1); 15173 rc = JIM_ERR; 15174 } 15175 else { 15176 rc = JimParseCheckMissing(interp, parser.missing.ch); 15177 } 15178 if (rc != JIM_OK) { 15179 ScriptTokenListFree(&tokenlist); 15180 Jim_DecrRefCount(interp, fileNameObj); 15181 return rc; 15182 } 15183 15184 15185 expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj); 15186 15187 15188 ScriptTokenListFree(&tokenlist); 15189 15190 if (!expr) { 15191 goto err; 15192 } 15193 15194 #ifdef DEBUG_SHOW_EXPR 15195 printf("==== Expr ====\n"); 15196 JimShowExprNode(expr->expr, 0); 15197 #endif 15198 15199 rc = JIM_OK; 15200 15201 err: 15202 15203 Jim_DecrRefCount(interp, fileNameObj); 15204 Jim_FreeIntRep(interp, objPtr); 15205 Jim_SetIntRepPtr(objPtr, expr); 15206 objPtr->typePtr = &exprObjType; 15207 return rc; 15208 } 15209 15210 static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr) 15211 { 15212 if (objPtr->typePtr != &exprObjType) { 15213 if (SetExprFromAny(interp, objPtr) != JIM_OK) { 15214 return NULL; 15215 } 15216 } 15217 return (struct ExprTree *) Jim_GetIntRepPtr(objPtr); 15218 } 15219 15220 #ifdef JIM_OPTIMIZATION 15221 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node) 15222 { 15223 if (node->type == JIM_TT_EXPR_INT) 15224 return node->objPtr; 15225 else if (node->type == JIM_TT_VAR) 15226 return Jim_GetVariable(interp, node->objPtr, JIM_NONE); 15227 else if (node->type == JIM_TT_DICTSUGAR) 15228 return JimExpandDictSugar(interp, node->objPtr); 15229 else 15230 return NULL; 15231 } 15232 #endif 15233 15234 15235 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node) 15236 { 15237 if (TOKEN_IS_EXPR_OP(node->type)) { 15238 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type); 15239 return op->funcop(interp, node); 15240 } 15241 else { 15242 Jim_Obj *objPtr; 15243 15244 15245 switch (node->type) { 15246 case JIM_TT_EXPR_INT: 15247 case JIM_TT_EXPR_DOUBLE: 15248 case JIM_TT_EXPR_BOOLEAN: 15249 case JIM_TT_STR: 15250 Jim_SetResult(interp, node->objPtr); 15251 return JIM_OK; 15252 15253 case JIM_TT_VAR: 15254 objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG); 15255 if (objPtr) { 15256 Jim_SetResult(interp, objPtr); 15257 return JIM_OK; 15258 } 15259 return JIM_ERR; 15260 15261 case JIM_TT_DICTSUGAR: 15262 objPtr = JimExpandDictSugar(interp, node->objPtr); 15263 if (objPtr) { 15264 Jim_SetResult(interp, objPtr); 15265 return JIM_OK; 15266 } 15267 return JIM_ERR; 15268 15269 case JIM_TT_ESC: 15270 if (interp->safeexpr) { 15271 return JIM_ERR; 15272 } 15273 if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) { 15274 Jim_SetResult(interp, objPtr); 15275 return JIM_OK; 15276 } 15277 return JIM_ERR; 15278 15279 case JIM_TT_CMD: 15280 if (interp->safeexpr) { 15281 return JIM_ERR; 15282 } 15283 return Jim_EvalObj(interp, node->objPtr); 15284 15285 default: 15286 15287 return JIM_ERR; 15288 } 15289 } 15290 } 15291 15292 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr) 15293 { 15294 int rc = JimExprEvalTermNode(interp, node); 15295 if (rc == JIM_OK) { 15296 *objPtrPtr = Jim_GetResult(interp); 15297 Jim_IncrRefCount(*objPtrPtr); 15298 } 15299 return rc; 15300 } 15301 15302 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node) 15303 { 15304 if (JimExprEvalTermNode(interp, node) == JIM_OK) { 15305 return ExprBool(interp, Jim_GetResult(interp)); 15306 } 15307 return -1; 15308 } 15309 15310 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr) 15311 { 15312 struct ExprTree *expr; 15313 int retcode = JIM_OK; 15314 15315 Jim_IncrRefCount(exprObjPtr); 15316 expr = JimGetExpression(interp, exprObjPtr); 15317 if (!expr) { 15318 retcode = JIM_ERR; 15319 goto done; 15320 } 15321 15322 #ifdef JIM_OPTIMIZATION 15323 if (!interp->safeexpr) { 15324 Jim_Obj *objPtr; 15325 15326 15327 switch (expr->len) { 15328 case 1: 15329 objPtr = JimExprIntValOrVar(interp, expr->expr); 15330 if (objPtr) { 15331 Jim_SetResult(interp, objPtr); 15332 goto done; 15333 } 15334 break; 15335 15336 case 2: 15337 if (expr->expr->type == JIM_EXPROP_NOT) { 15338 objPtr = JimExprIntValOrVar(interp, expr->expr->left); 15339 15340 if (objPtr && JimIsWide(objPtr)) { 15341 Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj); 15342 goto done; 15343 } 15344 } 15345 break; 15346 15347 case 3: 15348 objPtr = JimExprIntValOrVar(interp, expr->expr->left); 15349 if (objPtr && JimIsWide(objPtr)) { 15350 Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right); 15351 if (objPtr2 && JimIsWide(objPtr2)) { 15352 jim_wide wideValueA = JimWideValue(objPtr); 15353 jim_wide wideValueB = JimWideValue(objPtr2); 15354 int cmpRes; 15355 switch (expr->expr->type) { 15356 case JIM_EXPROP_LT: 15357 cmpRes = wideValueA < wideValueB; 15358 break; 15359 case JIM_EXPROP_LTE: 15360 cmpRes = wideValueA <= wideValueB; 15361 break; 15362 case JIM_EXPROP_GT: 15363 cmpRes = wideValueA > wideValueB; 15364 break; 15365 case JIM_EXPROP_GTE: 15366 cmpRes = wideValueA >= wideValueB; 15367 break; 15368 case JIM_EXPROP_NUMEQ: 15369 cmpRes = wideValueA == wideValueB; 15370 break; 15371 case JIM_EXPROP_NUMNE: 15372 cmpRes = wideValueA != wideValueB; 15373 break; 15374 default: 15375 goto noopt; 15376 } 15377 Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj); 15378 goto done; 15379 } 15380 } 15381 break; 15382 } 15383 } 15384 noopt: 15385 #endif 15386 15387 expr->inUse++; 15388 15389 15390 retcode = JimExprEvalTermNode(interp, expr->expr); 15391 15392 15393 Jim_FreeIntRep(interp, exprObjPtr); 15394 exprObjPtr->typePtr = &exprObjType; 15395 Jim_SetIntRepPtr(exprObjPtr, expr); 15396 15397 done: 15398 Jim_DecrRefCount(interp, exprObjPtr); 15399 15400 return retcode; 15401 } 15402 15403 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr) 15404 { 15405 int retcode = Jim_EvalExpression(interp, exprObjPtr); 15406 15407 if (retcode == JIM_OK) { 15408 switch (ExprBool(interp, Jim_GetResult(interp))) { 15409 case 0: 15410 *boolPtr = 0; 15411 break; 15412 15413 case 1: 15414 *boolPtr = 1; 15415 break; 15416 15417 case -1: 15418 retcode = JIM_ERR; 15419 break; 15420 } 15421 } 15422 return retcode; 15423 } 15424 15425 15426 15427 15428 typedef struct ScanFmtPartDescr 15429 { 15430 const char *arg; 15431 const char *prefix; 15432 size_t width; 15433 int pos; 15434 char type; 15435 char modifier; 15436 } ScanFmtPartDescr; 15437 15438 15439 typedef struct ScanFmtStringObj 15440 { 15441 jim_wide size; 15442 char *stringRep; 15443 size_t count; 15444 size_t convCount; 15445 size_t maxPos; 15446 const char *error; 15447 char *scratch; 15448 ScanFmtPartDescr descr[1]; 15449 } ScanFmtStringObj; 15450 15451 15452 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); 15453 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); 15454 static void UpdateStringOfScanFmt(Jim_Obj *objPtr); 15455 15456 static const Jim_ObjType scanFmtStringObjType = { 15457 "scanformatstring", 15458 FreeScanFmtInternalRep, 15459 DupScanFmtInternalRep, 15460 UpdateStringOfScanFmt, 15461 JIM_TYPE_NONE, 15462 }; 15463 15464 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) 15465 { 15466 JIM_NOTUSED(interp); 15467 Jim_Free((char *)objPtr->internalRep.ptr); 15468 objPtr->internalRep.ptr = 0; 15469 } 15470 15471 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) 15472 { 15473 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size; 15474 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size); 15475 15476 JIM_NOTUSED(interp); 15477 memcpy(newVec, srcPtr->internalRep.ptr, size); 15478 dupPtr->internalRep.ptr = newVec; 15479 dupPtr->typePtr = &scanFmtStringObjType; 15480 } 15481 15482 static void UpdateStringOfScanFmt(Jim_Obj *objPtr) 15483 { 15484 JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep); 15485 } 15486 15487 15488 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) 15489 { 15490 ScanFmtStringObj *fmtObj; 15491 char *buffer; 15492 int maxCount, i, approxSize, lastPos = -1; 15493 const char *fmt = Jim_String(objPtr); 15494 int maxFmtLen = Jim_Length(objPtr); 15495 const char *fmtEnd = fmt + maxFmtLen; 15496 int curr; 15497 15498 Jim_FreeIntRep(interp, objPtr); 15499 15500 for (i = 0, maxCount = 0; i < maxFmtLen; ++i) 15501 if (fmt[i] == '%') 15502 ++maxCount; 15503 15504 approxSize = sizeof(ScanFmtStringObj) 15505 +(maxCount + 1) * sizeof(ScanFmtPartDescr) 15506 +maxFmtLen * sizeof(char) + 3 + 1 15507 + maxFmtLen * sizeof(char) + 1 15508 + maxFmtLen * sizeof(char) 15509 +(maxCount + 1) * sizeof(char) 15510 +1; 15511 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize); 15512 memset(fmtObj, 0, approxSize); 15513 fmtObj->size = approxSize; 15514 fmtObj->maxPos = 0; 15515 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1]; 15516 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1; 15517 memcpy(fmtObj->stringRep, fmt, maxFmtLen); 15518 buffer = fmtObj->stringRep + maxFmtLen + 1; 15519 objPtr->internalRep.ptr = fmtObj; 15520 objPtr->typePtr = &scanFmtStringObjType; 15521 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) { 15522 int width = 0, skip; 15523 ScanFmtPartDescr *descr = &fmtObj->descr[curr]; 15524 15525 fmtObj->count++; 15526 descr->width = 0; 15527 15528 if (*fmt != '%' || fmt[1] == '%') { 15529 descr->type = 0; 15530 descr->prefix = &buffer[i]; 15531 for (; fmt < fmtEnd; ++fmt) { 15532 if (*fmt == '%') { 15533 if (fmt[1] != '%') 15534 break; 15535 ++fmt; 15536 } 15537 buffer[i++] = *fmt; 15538 } 15539 buffer[i++] = 0; 15540 } 15541 15542 ++fmt; 15543 15544 if (fmt >= fmtEnd) 15545 goto done; 15546 descr->pos = 0; 15547 if (*fmt == '*') { 15548 descr->pos = -1; 15549 ++fmt; 15550 } 15551 else 15552 fmtObj->convCount++; 15553 15554 if (sscanf(fmt, "%d%n", &width, &skip) == 1) { 15555 fmt += skip; 15556 15557 if (descr->pos != -1 && *fmt == '$') { 15558 int prev; 15559 15560 ++fmt; 15561 descr->pos = width; 15562 width = 0; 15563 15564 if ((lastPos == 0 && descr->pos > 0) 15565 || (lastPos > 0 && descr->pos == 0)) { 15566 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers"; 15567 return JIM_ERR; 15568 } 15569 15570 for (prev = 0; prev < curr; ++prev) { 15571 if (fmtObj->descr[prev].pos == -1) 15572 continue; 15573 if (fmtObj->descr[prev].pos == descr->pos) { 15574 fmtObj->error = 15575 "variable is assigned by multiple \"%n$\" conversion specifiers"; 15576 return JIM_ERR; 15577 } 15578 } 15579 if (descr->pos < 0) { 15580 fmtObj->error = 15581 "\"%n$\" conversion specifier is negative"; 15582 return JIM_ERR; 15583 } 15584 15585 if (sscanf(fmt, "%d%n", &width, &skip) == 1) { 15586 descr->width = width; 15587 fmt += skip; 15588 } 15589 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos) 15590 fmtObj->maxPos = descr->pos; 15591 } 15592 else { 15593 15594 descr->width = width; 15595 } 15596 } 15597 15598 if (lastPos == -1) 15599 lastPos = descr->pos; 15600 15601 if (*fmt == '[') { 15602 int swapped = 1, beg = i, end, j; 15603 15604 descr->type = '['; 15605 descr->arg = &buffer[i]; 15606 ++fmt; 15607 if (*fmt == '^') 15608 buffer[i++] = *fmt++; 15609 if (*fmt == ']') 15610 buffer[i++] = *fmt++; 15611 while (*fmt && *fmt != ']') 15612 buffer[i++] = *fmt++; 15613 if (*fmt != ']') { 15614 fmtObj->error = "unmatched [ in format string"; 15615 return JIM_ERR; 15616 } 15617 end = i; 15618 buffer[i++] = 0; 15619 15620 while (swapped) { 15621 swapped = 0; 15622 for (j = beg + 1; j < end - 1; ++j) { 15623 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) { 15624 char tmp = buffer[j - 1]; 15625 15626 buffer[j - 1] = buffer[j + 1]; 15627 buffer[j + 1] = tmp; 15628 swapped = 1; 15629 } 15630 } 15631 } 15632 } 15633 else { 15634 15635 if (fmt < fmtEnd && strchr("hlL", *fmt)) 15636 descr->modifier = tolower((int)*fmt++); 15637 15638 if (fmt >= fmtEnd) { 15639 fmtObj->error = "missing scan conversion character"; 15640 return JIM_ERR; 15641 } 15642 15643 descr->type = *fmt; 15644 if (strchr("efgcsndoxui", *fmt) == 0) { 15645 fmtObj->error = "bad scan conversion character"; 15646 return JIM_ERR; 15647 } 15648 else if (*fmt == 'c' && descr->width != 0) { 15649 fmtObj->error = "field width may not be specified in %c " "conversion"; 15650 return JIM_ERR; 15651 } 15652 else if (*fmt == 'u' && descr->modifier == 'l') { 15653 fmtObj->error = "unsigned wide not supported"; 15654 return JIM_ERR; 15655 } 15656 } 15657 curr++; 15658 } 15659 done: 15660 return JIM_OK; 15661 } 15662 15663 15664 15665 #define FormatGetCnvCount(_fo_) \ 15666 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount 15667 #define FormatGetMaxPos(_fo_) \ 15668 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos 15669 #define FormatGetError(_fo_) \ 15670 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error 15671 15672 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str) 15673 { 15674 char *buffer = Jim_StrDup(str); 15675 char *p = buffer; 15676 15677 while (*str) { 15678 int c; 15679 int n; 15680 15681 if (!sdescr && isspace(UCHAR(*str))) 15682 break; 15683 15684 n = utf8_tounicode(str, &c); 15685 if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN)) 15686 break; 15687 while (n--) 15688 *p++ = *str++; 15689 } 15690 *p = 0; 15691 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer); 15692 } 15693 15694 15695 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen, 15696 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr) 15697 { 15698 const char *tok; 15699 const ScanFmtPartDescr *descr = &fmtObj->descr[idx]; 15700 size_t scanned = 0; 15701 size_t anchor = pos; 15702 int i; 15703 Jim_Obj *tmpObj = NULL; 15704 15705 15706 *valObjPtr = 0; 15707 if (descr->prefix) { 15708 for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) { 15709 15710 if (isspace(UCHAR(descr->prefix[i]))) 15711 while (pos < str_bytelen && isspace(UCHAR(str[pos]))) 15712 ++pos; 15713 else if (descr->prefix[i] != str[pos]) 15714 break; 15715 else 15716 ++pos; 15717 } 15718 if (pos >= str_bytelen) { 15719 return -1; 15720 } 15721 else if (descr->prefix[i] != 0) 15722 return 0; 15723 } 15724 15725 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n') 15726 while (isspace(UCHAR(str[pos]))) 15727 ++pos; 15728 15729 15730 scanned = pos - anchor; 15731 15732 15733 if (descr->type == 'n') { 15734 15735 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned); 15736 } 15737 else if (pos >= str_bytelen) { 15738 15739 return -1; 15740 } 15741 else if (descr->type == 'c') { 15742 int c; 15743 scanned += utf8_tounicode(&str[pos], &c); 15744 *valObjPtr = Jim_NewIntObj(interp, c); 15745 return scanned; 15746 } 15747 else { 15748 15749 if (descr->width > 0) { 15750 size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos); 15751 size_t tLen = descr->width > sLen ? sLen : descr->width; 15752 15753 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen); 15754 tok = tmpObj->bytes; 15755 } 15756 else { 15757 15758 tok = &str[pos]; 15759 } 15760 switch (descr->type) { 15761 case 'd': 15762 case 'o': 15763 case 'x': 15764 case 'u': 15765 case 'i':{ 15766 char *endp; 15767 jim_wide w; 15768 15769 int base = descr->type == 'o' ? 8 15770 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10; 15771 15772 15773 if (base == 0) { 15774 w = jim_strtoull(tok, &endp); 15775 } 15776 else { 15777 w = strtoull(tok, &endp, base); 15778 } 15779 15780 if (endp != tok) { 15781 15782 *valObjPtr = Jim_NewIntObj(interp, w); 15783 15784 15785 scanned += endp - tok; 15786 } 15787 else { 15788 scanned = *tok ? 0 : -1; 15789 } 15790 break; 15791 } 15792 case 's': 15793 case '[':{ 15794 *valObjPtr = JimScanAString(interp, descr->arg, tok); 15795 scanned += Jim_Length(*valObjPtr); 15796 break; 15797 } 15798 case 'e': 15799 case 'f': 15800 case 'g':{ 15801 char *endp; 15802 double value = strtod(tok, &endp); 15803 15804 if (endp != tok) { 15805 15806 *valObjPtr = Jim_NewDoubleObj(interp, value); 15807 15808 scanned += endp - tok; 15809 } 15810 else { 15811 scanned = *tok ? 0 : -1; 15812 } 15813 break; 15814 } 15815 } 15816 if (tmpObj) { 15817 Jim_FreeNewObj(interp, tmpObj); 15818 } 15819 } 15820 return scanned; 15821 } 15822 15823 15824 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags) 15825 { 15826 size_t i, pos; 15827 int scanned = 1; 15828 const char *str = Jim_String(strObjPtr); 15829 int str_bytelen = Jim_Length(strObjPtr); 15830 Jim_Obj *resultList = 0; 15831 Jim_Obj **resultVec = 0; 15832 int resultc; 15833 Jim_Obj *emptyStr = 0; 15834 ScanFmtStringObj *fmtObj; 15835 15836 15837 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format")); 15838 15839 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr; 15840 15841 if (fmtObj->error != 0) { 15842 if (flags & JIM_ERRMSG) 15843 Jim_SetResultString(interp, fmtObj->error, -1); 15844 return 0; 15845 } 15846 15847 emptyStr = Jim_NewEmptyStringObj(interp); 15848 Jim_IncrRefCount(emptyStr); 15849 15850 resultList = Jim_NewListObj(interp, NULL, 0); 15851 if (fmtObj->maxPos > 0) { 15852 for (i = 0; i < fmtObj->maxPos; ++i) 15853 Jim_ListAppendElement(interp, resultList, emptyStr); 15854 JimListGetElements(interp, resultList, &resultc, &resultVec); 15855 } 15856 15857 for (i = 0, pos = 0; i < fmtObj->count; ++i) { 15858 ScanFmtPartDescr *descr = &(fmtObj->descr[i]); 15859 Jim_Obj *value = 0; 15860 15861 15862 if (descr->type == 0) 15863 continue; 15864 15865 if (scanned > 0) 15866 scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value); 15867 15868 if (scanned == -1 && i == 0) 15869 goto eof; 15870 15871 pos += scanned; 15872 15873 15874 if (value == 0) 15875 value = Jim_NewEmptyStringObj(interp); 15876 15877 if (descr->pos == -1) { 15878 Jim_FreeNewObj(interp, value); 15879 } 15880 else if (descr->pos == 0) 15881 15882 Jim_ListAppendElement(interp, resultList, value); 15883 else if (resultVec[descr->pos - 1] == emptyStr) { 15884 15885 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]); 15886 Jim_IncrRefCount(value); 15887 resultVec[descr->pos - 1] = value; 15888 } 15889 else { 15890 15891 Jim_FreeNewObj(interp, value); 15892 goto err; 15893 } 15894 } 15895 Jim_DecrRefCount(interp, emptyStr); 15896 return resultList; 15897 eof: 15898 Jim_DecrRefCount(interp, emptyStr); 15899 Jim_FreeNewObj(interp, resultList); 15900 return (Jim_Obj *)EOF; 15901 err: 15902 Jim_DecrRefCount(interp, emptyStr); 15903 Jim_FreeNewObj(interp, resultList); 15904 return 0; 15905 } 15906 15907 15908 static void JimPrngInit(Jim_Interp *interp) 15909 { 15910 #define PRNG_SEED_SIZE 256 15911 int i; 15912 unsigned int *seed; 15913 time_t t = time(NULL); 15914 15915 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState)); 15916 15917 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed)); 15918 for (i = 0; i < PRNG_SEED_SIZE; i++) { 15919 seed[i] = (rand() ^ t ^ clock()); 15920 } 15921 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed)); 15922 Jim_Free(seed); 15923 } 15924 15925 15926 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len) 15927 { 15928 Jim_PrngState *prng; 15929 unsigned char *destByte = (unsigned char *)dest; 15930 unsigned int si, sj, x; 15931 15932 15933 if (interp->prngState == NULL) 15934 JimPrngInit(interp); 15935 prng = interp->prngState; 15936 15937 for (x = 0; x < len; x++) { 15938 prng->i = (prng->i + 1) & 0xff; 15939 si = prng->sbox[prng->i]; 15940 prng->j = (prng->j + si) & 0xff; 15941 sj = prng->sbox[prng->j]; 15942 prng->sbox[prng->i] = sj; 15943 prng->sbox[prng->j] = si; 15944 *destByte++ = prng->sbox[(si + sj) & 0xff]; 15945 } 15946 } 15947 15948 15949 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen) 15950 { 15951 int i; 15952 Jim_PrngState *prng; 15953 15954 15955 if (interp->prngState == NULL) 15956 JimPrngInit(interp); 15957 prng = interp->prngState; 15958 15959 15960 for (i = 0; i < 256; i++) 15961 prng->sbox[i] = i; 15962 15963 for (i = 0; i < seedLen; i++) { 15964 unsigned char t; 15965 15966 t = prng->sbox[i & 0xFF]; 15967 prng->sbox[i & 0xFF] = prng->sbox[seed[i]]; 15968 prng->sbox[seed[i]] = t; 15969 } 15970 prng->i = prng->j = 0; 15971 15972 for (i = 0; i < 256; i += seedLen) { 15973 JimRandomBytes(interp, seed, seedLen); 15974 } 15975 } 15976 15977 15978 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 15979 { 15980 jim_wide wideValue, increment = 1; 15981 Jim_Obj *intObjPtr; 15982 15983 if (argc != 2 && argc != 3) { 15984 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?"); 15985 return JIM_ERR; 15986 } 15987 if (argc == 3) { 15988 if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK) 15989 return JIM_ERR; 15990 } 15991 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); 15992 if (!intObjPtr) { 15993 15994 wideValue = 0; 15995 } 15996 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) { 15997 return JIM_ERR; 15998 } 15999 if (!intObjPtr || Jim_IsShared(intObjPtr)) { 16000 intObjPtr = Jim_NewIntObj(interp, wideValue + increment); 16001 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) { 16002 Jim_FreeNewObj(interp, intObjPtr); 16003 return JIM_ERR; 16004 } 16005 } 16006 else { 16007 16008 Jim_InvalidateStringRep(intObjPtr); 16009 JimWideValue(intObjPtr) = wideValue + increment; 16010 16011 if (argv[1]->typePtr != &variableObjType) { 16012 16013 Jim_SetVariable(interp, argv[1], intObjPtr); 16014 } 16015 } 16016 Jim_SetResult(interp, intObjPtr); 16017 return JIM_OK; 16018 } 16019 16020 16021 #define JIM_EVAL_SARGV_LEN 8 16022 #define JIM_EVAL_SINTV_LEN 8 16023 16024 static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv) 16025 { 16026 JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object")); 16027 16028 int ret; 16029 Jim_Obj *nargv[7]; 16030 Jim_Obj *traceCmdObj = interp->traceCmdObj; 16031 Jim_Obj *resultObj = Jim_GetResult(interp); 16032 ScriptObj *script = NULL; 16033 16034 16035 16036 if (interp->evalFrame->scriptObj) { 16037 script = JimGetScript(interp, interp->evalFrame->scriptObj); 16038 } 16039 16040 nargv[0] = traceCmdObj; 16041 nargv[1] = Jim_NewStringObj(interp, type, -1); 16042 nargv[2] = script ? script->fileNameObj : interp->emptyObj; 16043 nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1); 16044 nargv[4] = resultObj; 16045 nargv[5] = argv[0]; 16046 nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1); 16047 16048 16049 interp->traceCmdObj = NULL; 16050 16051 Jim_IncrRefCount(resultObj); 16052 ret = Jim_EvalObjVector(interp, 7, nargv); 16053 Jim_DecrRefCount(interp, resultObj); 16054 16055 if (ret == JIM_OK || ret == JIM_RETURN) { 16056 16057 interp->traceCmdObj = traceCmdObj; 16058 Jim_SetEmptyResult(interp); 16059 ret = JIM_OK; 16060 } 16061 else { 16062 16063 Jim_DecrRefCount(interp, traceCmdObj); 16064 } 16065 return ret; 16066 } 16067 16068 16069 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 16070 { 16071 int retcode; 16072 16073 if (interp->unknown_called > 50) { 16074 return JIM_ERR; 16075 } 16076 16077 16078 16079 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL) 16080 return JIM_ERR; 16081 16082 interp->unknown_called++; 16083 16084 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv); 16085 interp->unknown_called--; 16086 16087 return retcode; 16088 } 16089 16090 static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj) 16091 { 16092 memset(frame, 0, sizeof(*frame)); 16093 frame->parent = interp->evalFrame; 16094 frame->level = frame->parent->level + 1; 16095 frame->procLevel = interp->procLevel; 16096 frame->framePtr = interp->framePtr; 16097 if (scriptObj) { 16098 frame->scriptObj = scriptObj; 16099 } 16100 else { 16101 frame->scriptObj = frame->parent->scriptObj; 16102 } 16103 interp->evalFrame = frame; 16104 #if 0 16105 if (frame->scriptObj) { 16106 printf("script: %.*s\n", 20, Jim_String(frame->scriptObj)); 16107 } 16108 #endif 16109 } 16110 16111 static void JimPopEvalFrame(Jim_Interp *interp) 16112 { 16113 interp->evalFrame = interp->evalFrame->parent; 16114 } 16115 16116 16117 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 16118 { 16119 int retcode; 16120 Jim_Cmd *cmdPtr; 16121 void *prevPrivData; 16122 Jim_Obj *tailcallObj = NULL; 16123 16124 #if 0 16125 printf("invoke"); 16126 int j; 16127 for (j = 0; j < objc; j++) { 16128 printf(" '%s'", Jim_String(objv[j])); 16129 } 16130 printf("\n"); 16131 #endif 16132 16133 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG); 16134 if (cmdPtr == NULL) { 16135 return JimUnknown(interp, objc, objv); 16136 } 16137 JimIncrCmdRefCount(cmdPtr); 16138 16139 if (interp->evalDepth == interp->maxEvalDepth) { 16140 Jim_SetResultString(interp, "Infinite eval recursion", -1); 16141 retcode = JIM_ERR; 16142 goto out; 16143 } 16144 interp->evalDepth++; 16145 prevPrivData = interp->cmdPrivData; 16146 16147 tailcall: 16148 16149 interp->evalFrame->argc = objc; 16150 interp->evalFrame->argv = objv; 16151 interp->evalFrame->cmd = cmdPtr; 16152 16153 if (!interp->traceCmdObj || 16154 (retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) { 16155 16156 Jim_SetEmptyResult(interp); 16157 if (cmdPtr->isproc) { 16158 retcode = JimCallProcedure(interp, cmdPtr, objc, objv); 16159 } 16160 else { 16161 interp->cmdPrivData = cmdPtr->u.native.privData; 16162 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); 16163 } 16164 if (retcode == JIM_ERR) { 16165 JimSetErrorStack(interp, NULL); 16166 } 16167 } 16168 16169 if (tailcallObj) { 16170 16171 Jim_DecrRefCount(interp, tailcallObj); 16172 tailcallObj = NULL; 16173 } 16174 16175 16176 interp->evalFrame->argc = 0; 16177 interp->evalFrame->argv = NULL; 16178 16179 16180 if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) { 16181 JimDecrCmdRefCount(interp, cmdPtr); 16182 16183 16184 cmdPtr = interp->framePtr->tailcallCmd; 16185 interp->framePtr->tailcallCmd = NULL; 16186 tailcallObj = interp->framePtr->tailcallObj; 16187 interp->framePtr->tailcallObj = NULL; 16188 objc = tailcallObj->internalRep.listValue.len; 16189 objv = tailcallObj->internalRep.listValue.ele; 16190 goto tailcall; 16191 } 16192 16193 interp->cmdPrivData = prevPrivData; 16194 interp->evalDepth--; 16195 16196 out: 16197 JimDecrCmdRefCount(interp, cmdPtr); 16198 16199 if (retcode == JIM_ERR) { 16200 JimSetErrorStack(interp, NULL); 16201 } 16202 16203 if (interp->framePtr->tailcallObj) { 16204 JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd); 16205 Jim_DecrRefCount(interp, interp->framePtr->tailcallObj); 16206 interp->framePtr->tailcallCmd = NULL; 16207 interp->framePtr->tailcallObj = NULL; 16208 } 16209 16210 return retcode; 16211 } 16212 16213 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 16214 { 16215 int i, retcode; 16216 Jim_EvalFrame frame; 16217 16218 16219 for (i = 0; i < objc; i++) 16220 Jim_IncrRefCount(objv[i]); 16221 16222 16223 JimPushEvalFrame(interp, &frame, NULL); 16224 16225 retcode = JimInvokeCommand(interp, objc, objv); 16226 16227 JimPopEvalFrame(interp); 16228 16229 16230 for (i = 0; i < objc; i++) 16231 Jim_DecrRefCount(interp, objv[i]); 16232 16233 return retcode; 16234 } 16235 16236 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv) 16237 { 16238 int ret; 16239 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv)); 16240 16241 nargv[0] = prefix; 16242 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc); 16243 ret = Jim_EvalObjVector(interp, objc + 1, nargv); 16244 Jim_Free(nargv); 16245 return ret; 16246 } 16247 16248 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr) 16249 { 16250 Jim_Obj *objPtr; 16251 int ret = JIM_ERR; 16252 16253 switch (token->type) { 16254 case JIM_TT_STR: 16255 case JIM_TT_ESC: 16256 objPtr = token->objPtr; 16257 break; 16258 case JIM_TT_VAR: 16259 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG); 16260 break; 16261 case JIM_TT_DICTSUGAR: 16262 objPtr = JimExpandDictSugar(interp, token->objPtr); 16263 break; 16264 case JIM_TT_EXPRSUGAR: 16265 ret = Jim_EvalExpression(interp, token->objPtr); 16266 if (ret == JIM_OK) { 16267 objPtr = Jim_GetResult(interp); 16268 } 16269 else { 16270 objPtr = NULL; 16271 } 16272 break; 16273 case JIM_TT_CMD: 16274 ret = Jim_EvalObj(interp, token->objPtr); 16275 if (ret == JIM_OK || ret == JIM_RETURN) { 16276 objPtr = interp->result; 16277 } else { 16278 16279 objPtr = NULL; 16280 } 16281 break; 16282 default: 16283 JimPanic((1, 16284 "default token type (%d) reached " "in Jim_SubstObj().", token->type)); 16285 objPtr = NULL; 16286 break; 16287 } 16288 if (objPtr) { 16289 *objPtrPtr = objPtr; 16290 return JIM_OK; 16291 } 16292 return ret; 16293 } 16294 16295 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags) 16296 { 16297 int totlen = 0, i; 16298 Jim_Obj **intv; 16299 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN]; 16300 Jim_Obj *objPtr; 16301 char *s; 16302 16303 if (tokens <= JIM_EVAL_SINTV_LEN) 16304 intv = sintv; 16305 else 16306 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens); 16307 16308 for (i = 0; i < tokens; i++) { 16309 switch (JimSubstOneToken(interp, &token[i], &intv[i])) { 16310 case JIM_OK: 16311 case JIM_RETURN: 16312 break; 16313 case JIM_BREAK: 16314 if (flags & JIM_SUBST_FLAG) { 16315 16316 tokens = i; 16317 continue; 16318 } 16319 16320 16321 case JIM_CONTINUE: 16322 if (flags & JIM_SUBST_FLAG) { 16323 intv[i] = NULL; 16324 continue; 16325 } 16326 16327 16328 default: 16329 while (i--) { 16330 Jim_DecrRefCount(interp, intv[i]); 16331 } 16332 if (intv != sintv) { 16333 Jim_Free(intv); 16334 } 16335 return NULL; 16336 } 16337 Jim_IncrRefCount(intv[i]); 16338 Jim_String(intv[i]); 16339 totlen += intv[i]->length; 16340 } 16341 16342 16343 if (tokens == 1 && intv[0] && intv == sintv) { 16344 16345 intv[0]->refCount--; 16346 return intv[0]; 16347 } 16348 16349 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0); 16350 16351 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC 16352 && token[2].type == JIM_TT_VAR) { 16353 16354 objPtr->typePtr = &interpolatedObjType; 16355 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr; 16356 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2]; 16357 Jim_IncrRefCount(intv[2]); 16358 } 16359 else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) { 16360 16361 int line; 16362 Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line); 16363 Jim_SetSourceInfo(interp, objPtr, fileNameObj, line); 16364 } 16365 16366 16367 s = objPtr->bytes = Jim_Alloc(totlen + 1); 16368 objPtr->length = totlen; 16369 for (i = 0; i < tokens; i++) { 16370 if (intv[i]) { 16371 memcpy(s, intv[i]->bytes, intv[i]->length); 16372 s += intv[i]->length; 16373 Jim_DecrRefCount(interp, intv[i]); 16374 } 16375 } 16376 objPtr->bytes[totlen] = '\0'; 16377 16378 if (intv != sintv) { 16379 Jim_Free(intv); 16380 } 16381 16382 return objPtr; 16383 } 16384 16385 16386 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr) 16387 { 16388 int retcode = JIM_OK; 16389 Jim_EvalFrame frame; 16390 16391 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list.")); 16392 16393 JimPushEvalFrame(interp, &frame, NULL); 16394 16395 if (listPtr->internalRep.listValue.len) { 16396 Jim_IncrRefCount(listPtr); 16397 retcode = JimInvokeCommand(interp, 16398 listPtr->internalRep.listValue.len, 16399 listPtr->internalRep.listValue.ele); 16400 Jim_DecrRefCount(interp, listPtr); 16401 } 16402 16403 JimPopEvalFrame(interp); 16404 16405 return retcode; 16406 } 16407 16408 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr) 16409 { 16410 SetListFromAny(interp, listPtr); 16411 return JimEvalObjList(interp, listPtr); 16412 } 16413 16414 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr) 16415 { 16416 int i; 16417 ScriptObj *script; 16418 ScriptToken *token; 16419 int retcode = JIM_OK; 16420 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL; 16421 Jim_EvalFrame frame; 16422 16423 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { 16424 return JimEvalObjList(interp, scriptObjPtr); 16425 } 16426 16427 Jim_IncrRefCount(scriptObjPtr); 16428 script = JimGetScript(interp, scriptObjPtr); 16429 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) { 16430 JimSetErrorStack(interp, script); 16431 Jim_DecrRefCount(interp, scriptObjPtr); 16432 return JIM_ERR; 16433 } 16434 16435 Jim_SetEmptyResult(interp); 16436 16437 token = script->token; 16438 16439 #ifdef JIM_OPTIMIZATION 16440 if (script->len == 0) { 16441 Jim_DecrRefCount(interp, scriptObjPtr); 16442 return JIM_OK; 16443 } 16444 if (script->len == 3 16445 && token[1].objPtr->typePtr == &commandObjType 16446 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0 16447 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand 16448 && token[2].objPtr->typePtr == &variableObjType) { 16449 16450 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE); 16451 16452 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { 16453 JimWideValue(objPtr)++; 16454 Jim_InvalidateStringRep(objPtr); 16455 Jim_DecrRefCount(interp, scriptObjPtr); 16456 Jim_SetResult(interp, objPtr); 16457 return JIM_OK; 16458 } 16459 } 16460 #endif 16461 16462 script->inUse++; 16463 16464 JimPushEvalFrame(interp, &frame, scriptObjPtr); 16465 16466 16467 interp->errorFlag = 0; 16468 argv = sargv; 16469 16470 for (i = 0; i < script->len && retcode == JIM_OK; ) { 16471 int argc; 16472 int j; 16473 16474 16475 argc = token[i].objPtr->internalRep.scriptLineValue.argc; 16476 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line; 16477 16478 16479 if (argc > JIM_EVAL_SARGV_LEN) 16480 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc); 16481 16482 16483 i++; 16484 16485 for (j = 0; j < argc; j++) { 16486 long wordtokens = 1; 16487 int expand = 0; 16488 Jim_Obj *wordObjPtr = NULL; 16489 16490 if (token[i].type == JIM_TT_WORD) { 16491 wordtokens = JimWideValue(token[i++].objPtr); 16492 if (wordtokens < 0) { 16493 expand = 1; 16494 wordtokens = -wordtokens; 16495 } 16496 } 16497 16498 if (wordtokens == 1) { 16499 16500 switch (token[i].type) { 16501 case JIM_TT_ESC: 16502 case JIM_TT_STR: 16503 wordObjPtr = token[i].objPtr; 16504 break; 16505 case JIM_TT_VAR: 16506 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG); 16507 break; 16508 case JIM_TT_EXPRSUGAR: 16509 retcode = Jim_EvalExpression(interp, token[i].objPtr); 16510 if (retcode == JIM_OK) { 16511 wordObjPtr = Jim_GetResult(interp); 16512 } 16513 else { 16514 wordObjPtr = NULL; 16515 } 16516 break; 16517 case JIM_TT_DICTSUGAR: 16518 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr); 16519 break; 16520 case JIM_TT_CMD: 16521 retcode = Jim_EvalObj(interp, token[i].objPtr); 16522 if (retcode == JIM_OK) { 16523 wordObjPtr = Jim_GetResult(interp); 16524 } 16525 break; 16526 default: 16527 JimPanic((1, "default token type reached " "in Jim_EvalObj().")); 16528 } 16529 } 16530 else { 16531 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE); 16532 } 16533 16534 if (!wordObjPtr) { 16535 if (retcode == JIM_OK) { 16536 retcode = JIM_ERR; 16537 } 16538 break; 16539 } 16540 16541 Jim_IncrRefCount(wordObjPtr); 16542 i += wordtokens; 16543 16544 if (!expand) { 16545 argv[j] = wordObjPtr; 16546 } 16547 else { 16548 16549 int len = Jim_ListLength(interp, wordObjPtr); 16550 int newargc = argc + len - 1; 16551 int k; 16552 16553 if (len > 1) { 16554 if (argv == sargv) { 16555 if (newargc > JIM_EVAL_SARGV_LEN) { 16556 argv = Jim_Alloc(sizeof(*argv) * newargc); 16557 memcpy(argv, sargv, sizeof(*argv) * j); 16558 } 16559 } 16560 else { 16561 16562 argv = Jim_Realloc(argv, sizeof(*argv) * newargc); 16563 } 16564 } 16565 16566 16567 for (k = 0; k < len; k++) { 16568 argv[j++] = wordObjPtr->internalRep.listValue.ele[k]; 16569 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]); 16570 } 16571 16572 Jim_DecrRefCount(interp, wordObjPtr); 16573 16574 16575 j--; 16576 argc += len - 1; 16577 } 16578 } 16579 16580 if (retcode == JIM_OK && argc) { 16581 16582 retcode = JimInvokeCommand(interp, argc, argv); 16583 16584 if (Jim_CheckSignal(interp)) { 16585 retcode = JIM_SIGNAL; 16586 } 16587 } 16588 16589 16590 while (j-- > 0) { 16591 Jim_DecrRefCount(interp, argv[j]); 16592 } 16593 16594 if (argv != sargv) { 16595 Jim_Free(argv); 16596 argv = sargv; 16597 } 16598 } 16599 16600 16601 if (retcode == JIM_ERR) { 16602 JimSetErrorStack(interp, NULL); 16603 } 16604 16605 JimPopEvalFrame(interp); 16606 16607 Jim_FreeIntRep(interp, scriptObjPtr); 16608 scriptObjPtr->typePtr = &scriptObjType; 16609 Jim_SetIntRepPtr(scriptObjPtr, script); 16610 Jim_DecrRefCount(interp, scriptObjPtr); 16611 16612 return retcode; 16613 } 16614 16615 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj) 16616 { 16617 int retcode; 16618 16619 const char *varname = Jim_String(argNameObj); 16620 if (*varname == '&') { 16621 16622 Jim_Obj *objPtr; 16623 Jim_CallFrame *savedCallFrame = interp->framePtr; 16624 16625 interp->framePtr = interp->framePtr->parent; 16626 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG); 16627 interp->framePtr = savedCallFrame; 16628 if (!objPtr) { 16629 return JIM_ERR; 16630 } 16631 16632 16633 objPtr = Jim_NewStringObj(interp, varname + 1, -1); 16634 Jim_IncrRefCount(objPtr); 16635 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent); 16636 Jim_DecrRefCount(interp, objPtr); 16637 } 16638 else { 16639 retcode = Jim_SetVariable(interp, argNameObj, argValObj); 16640 } 16641 return retcode; 16642 } 16643 16644 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd) 16645 { 16646 16647 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0); 16648 int i; 16649 16650 for (i = 0; i < cmd->u.proc.argListLen; i++) { 16651 Jim_AppendString(interp, argmsg, " ", 1); 16652 16653 if (i == cmd->u.proc.argsPos) { 16654 if (cmd->u.proc.arglist[i].defaultObjPtr) { 16655 16656 Jim_AppendString(interp, argmsg, "?", 1); 16657 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr); 16658 Jim_AppendString(interp, argmsg, " ...?", -1); 16659 } 16660 else { 16661 16662 Jim_AppendString(interp, argmsg, "?arg ...?", -1); 16663 } 16664 } 16665 else { 16666 if (cmd->u.proc.arglist[i].defaultObjPtr) { 16667 Jim_AppendString(interp, argmsg, "?", 1); 16668 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr); 16669 Jim_AppendString(interp, argmsg, "?", 1); 16670 } 16671 else { 16672 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr); 16673 if (*arg == '&') { 16674 arg++; 16675 } 16676 Jim_AppendString(interp, argmsg, arg, -1); 16677 } 16678 } 16679 } 16680 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg); 16681 } 16682 16683 #ifdef jim_ext_namespace 16684 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj) 16685 { 16686 Jim_CallFrame *callFramePtr; 16687 int retcode; 16688 16689 16690 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj); 16691 callFramePtr->argv = interp->evalFrame->argv; 16692 callFramePtr->argc = interp->evalFrame->argc; 16693 callFramePtr->procArgsObjPtr = NULL; 16694 callFramePtr->procBodyObjPtr = scriptObj; 16695 callFramePtr->staticVars = NULL; 16696 Jim_IncrRefCount(scriptObj); 16697 interp->framePtr = callFramePtr; 16698 16699 16700 if (interp->framePtr->level == interp->maxCallFrameDepth) { 16701 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1); 16702 retcode = JIM_ERR; 16703 } 16704 else { 16705 16706 retcode = Jim_EvalObj(interp, scriptObj); 16707 } 16708 16709 16710 interp->framePtr = interp->framePtr->parent; 16711 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE); 16712 16713 return retcode; 16714 } 16715 #endif 16716 16717 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv) 16718 { 16719 Jim_CallFrame *callFramePtr; 16720 int i, d, retcode, optargs; 16721 16722 16723 if (argc - 1 < cmd->u.proc.reqArity || 16724 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) { 16725 JimSetProcWrongArgs(interp, argv[0], cmd); 16726 return JIM_ERR; 16727 } 16728 16729 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) { 16730 16731 return JIM_OK; 16732 } 16733 16734 16735 if (interp->framePtr->level == interp->maxCallFrameDepth) { 16736 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1); 16737 return JIM_ERR; 16738 } 16739 16740 16741 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj); 16742 callFramePtr->argv = argv; 16743 callFramePtr->argc = argc; 16744 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr; 16745 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr; 16746 callFramePtr->staticVars = cmd->u.proc.staticVars; 16747 16748 interp->procLevel++; 16749 16750 Jim_IncrRefCount(cmd->u.proc.argListObjPtr); 16751 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr); 16752 interp->framePtr = callFramePtr; 16753 16754 16755 optargs = (argc - 1 - cmd->u.proc.reqArity); 16756 16757 16758 i = 1; 16759 for (d = 0; d < cmd->u.proc.argListLen; d++) { 16760 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr; 16761 if (d == cmd->u.proc.argsPos) { 16762 16763 Jim_Obj *listObjPtr; 16764 int argsLen = 0; 16765 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) { 16766 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity); 16767 } 16768 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen); 16769 16770 16771 if (cmd->u.proc.arglist[d].defaultObjPtr) { 16772 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr; 16773 } 16774 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr); 16775 if (retcode != JIM_OK) { 16776 goto badargset; 16777 } 16778 16779 i += argsLen; 16780 continue; 16781 } 16782 16783 16784 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) { 16785 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]); 16786 } 16787 else { 16788 16789 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr); 16790 } 16791 if (retcode != JIM_OK) { 16792 goto badargset; 16793 } 16794 } 16795 16796 if (interp->traceCmdObj == NULL || 16797 (retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) { 16798 16799 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr); 16800 } 16801 16802 badargset: 16803 16804 16805 retcode = JimInvokeDefer(interp, retcode); 16806 interp->framePtr = interp->framePtr->parent; 16807 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE); 16808 16809 16810 if (retcode == JIM_RETURN) { 16811 if (--interp->returnLevel <= 0) { 16812 retcode = interp->returnCode; 16813 interp->returnCode = JIM_OK; 16814 interp->returnLevel = 0; 16815 } 16816 } 16817 interp->procLevel--; 16818 16819 return retcode; 16820 } 16821 16822 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script) 16823 { 16824 int retval; 16825 Jim_Obj *scriptObjPtr; 16826 16827 scriptObjPtr = Jim_NewStringObj(interp, script, -1); 16828 Jim_IncrRefCount(scriptObjPtr); 16829 if (filename) { 16830 Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno); 16831 } 16832 retval = Jim_EvalObj(interp, scriptObjPtr); 16833 Jim_DecrRefCount(interp, scriptObjPtr); 16834 return retval; 16835 } 16836 16837 int Jim_Eval(Jim_Interp *interp, const char *script) 16838 { 16839 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1)); 16840 } 16841 16842 16843 int Jim_EvalGlobal(Jim_Interp *interp, const char *script) 16844 { 16845 int retval; 16846 Jim_CallFrame *savedFramePtr = interp->framePtr; 16847 16848 interp->framePtr = interp->topFramePtr; 16849 retval = Jim_Eval(interp, script); 16850 interp->framePtr = savedFramePtr; 16851 16852 return retval; 16853 } 16854 16855 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename) 16856 { 16857 int retval; 16858 Jim_CallFrame *savedFramePtr = interp->framePtr; 16859 16860 interp->framePtr = interp->topFramePtr; 16861 retval = Jim_EvalFile(interp, filename); 16862 interp->framePtr = savedFramePtr; 16863 16864 return retval; 16865 } 16866 16867 #include <sys/stat.h> 16868 16869 static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename) 16870 { 16871 jim_stat_t sb; 16872 int fd; 16873 char *buf; 16874 int readlen; 16875 16876 if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) { 16877 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno)); 16878 return NULL; 16879 } 16880 buf = Jim_Alloc(sb.st_size + 1); 16881 readlen = read(fd, buf, sb.st_size); 16882 close(fd); 16883 if (readlen < 0) { 16884 Jim_Free(buf); 16885 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno)); 16886 return NULL; 16887 } 16888 else { 16889 Jim_Obj *objPtr; 16890 buf[readlen] = 0; 16891 16892 objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen); 16893 16894 return objPtr; 16895 } 16896 } 16897 16898 16899 int Jim_EvalFile(Jim_Interp *interp, const char *filename) 16900 { 16901 Jim_Obj *filenameObj; 16902 Jim_Obj *oldFilenameObj; 16903 Jim_Obj *scriptObjPtr; 16904 int retcode; 16905 16906 scriptObjPtr = JimReadTextFile(interp, filename); 16907 if (!scriptObjPtr) { 16908 return JIM_ERR; 16909 } 16910 16911 filenameObj = Jim_NewStringObj(interp, filename, -1); 16912 Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1); 16913 16914 oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj); 16915 16916 retcode = Jim_EvalObj(interp, scriptObjPtr); 16917 16918 JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj); 16919 16920 16921 if (retcode == JIM_RETURN) { 16922 if (--interp->returnLevel <= 0) { 16923 retcode = interp->returnCode; 16924 interp->returnCode = JIM_OK; 16925 interp->returnLevel = 0; 16926 } 16927 } 16928 16929 return retcode; 16930 } 16931 16932 static void JimParseSubst(struct JimParserCtx *pc, int flags) 16933 { 16934 pc->tstart = pc->p; 16935 pc->tline = pc->linenr; 16936 16937 if (pc->len == 0) { 16938 pc->tend = pc->p; 16939 pc->tt = JIM_TT_EOL; 16940 pc->eof = 1; 16941 return; 16942 } 16943 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) { 16944 JimParseCmd(pc); 16945 return; 16946 } 16947 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) { 16948 if (JimParseVar(pc) == JIM_OK) { 16949 return; 16950 } 16951 16952 pc->tstart = pc->p; 16953 16954 pc->p++; 16955 pc->len--; 16956 } 16957 while (pc->len) { 16958 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) { 16959 break; 16960 } 16961 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) { 16962 break; 16963 } 16964 if (*pc->p == '\\' && pc->len > 1) { 16965 pc->p++; 16966 pc->len--; 16967 } 16968 pc->p++; 16969 pc->len--; 16970 } 16971 pc->tend = pc->p - 1; 16972 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC; 16973 } 16974 16975 16976 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags) 16977 { 16978 int scriptTextLen; 16979 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); 16980 struct JimParserCtx parser; 16981 struct ScriptObj *script = Jim_Alloc(sizeof(*script)); 16982 ParseTokenList tokenlist; 16983 16984 16985 ScriptTokenListInit(&tokenlist); 16986 16987 JimParserInit(&parser, scriptText, scriptTextLen, 1); 16988 while (1) { 16989 JimParseSubst(&parser, flags); 16990 if (parser.eof) { 16991 16992 break; 16993 } 16994 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, 16995 parser.tline); 16996 } 16997 16998 16999 script->inUse = 1; 17000 script->substFlags = flags; 17001 script->fileNameObj = interp->emptyObj; 17002 Jim_IncrRefCount(script->fileNameObj); 17003 SubstObjAddTokens(interp, script, &tokenlist); 17004 17005 17006 ScriptTokenListFree(&tokenlist); 17007 17008 #ifdef DEBUG_SHOW_SUBST 17009 { 17010 int i; 17011 17012 printf("==== Subst ====\n"); 17013 for (i = 0; i < script->len; i++) { 17014 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type), 17015 Jim_String(script->token[i].objPtr)); 17016 } 17017 } 17018 #endif 17019 17020 17021 Jim_FreeIntRep(interp, objPtr); 17022 Jim_SetIntRepPtr(objPtr, script); 17023 objPtr->typePtr = &scriptObjType; 17024 return JIM_OK; 17025 } 17026 17027 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags) 17028 { 17029 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags) 17030 SetSubstFromAny(interp, objPtr, flags); 17031 return (ScriptObj *) Jim_GetIntRepPtr(objPtr); 17032 } 17033 17034 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags) 17035 { 17036 ScriptObj *script; 17037 17038 JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object")); 17039 17040 script = Jim_GetSubst(interp, substObjPtr, flags); 17041 17042 Jim_IncrRefCount(substObjPtr); 17043 script->inUse++; 17044 17045 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags); 17046 17047 script->inUse--; 17048 Jim_DecrRefCount(interp, substObjPtr); 17049 if (*resObjPtrPtr == NULL) { 17050 return JIM_ERR; 17051 } 17052 return JIM_OK; 17053 } 17054 17055 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg) 17056 { 17057 Jim_Obj *objPtr; 17058 Jim_Obj *listObjPtr; 17059 17060 JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0")); 17061 17062 listObjPtr = Jim_NewListObj(interp, argv, argc); 17063 17064 if (msg && *msg) { 17065 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1)); 17066 } 17067 Jim_IncrRefCount(listObjPtr); 17068 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1); 17069 Jim_DecrRefCount(interp, listObjPtr); 17070 17071 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr); 17072 } 17073 17074 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, 17075 Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type); 17076 17077 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL) 17078 17079 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr, 17080 JimHashtableIteratorCallbackType *callback, int type) 17081 { 17082 Jim_HashEntry *he; 17083 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); 17084 17085 17086 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) { 17087 he = Jim_FindHashEntry(ht, patternObjPtr); 17088 if (he) { 17089 callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he), 17090 patternObjPtr, type); 17091 } 17092 } 17093 else { 17094 Jim_HashTableIterator htiter; 17095 JimInitHashTableIterator(ht, &htiter); 17096 while ((he = Jim_NextHashEntry(&htiter)) != NULL) { 17097 callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he), 17098 patternObjPtr, type); 17099 } 17100 } 17101 return listObjPtr; 17102 } 17103 17104 17105 #define JIM_CMDLIST_COMMANDS 0 17106 #define JIM_CMDLIST_PROCS 1 17107 #define JIM_CMDLIST_CHANNELS 2 17108 17109 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, 17110 Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type) 17111 { 17112 Jim_Cmd *cmdPtr = (Jim_Cmd *)value; 17113 17114 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) { 17115 17116 return; 17117 } 17118 17119 Jim_IncrRefCount(keyObj); 17120 17121 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) { 17122 int match = 1; 17123 if (patternObj) { 17124 int plen, slen; 17125 const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen); 17126 const char *str = Jim_GetStringNoQualifier(keyObj, &slen); 17127 #ifdef JIM_NO_INTROSPECTION 17128 17129 match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0); 17130 #else 17131 match = JimGlobMatch(pattern, plen, str, slen, 0); 17132 #endif 17133 } 17134 if (match) { 17135 Jim_ListAppendElement(interp, listObjPtr, keyObj); 17136 } 17137 } 17138 Jim_DecrRefCount(interp, keyObj); 17139 } 17140 17141 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type) 17142 { 17143 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type); 17144 } 17145 17146 17147 #define JIM_VARLIST_GLOBALS 0 17148 #define JIM_VARLIST_LOCALS 1 17149 #define JIM_VARLIST_VARS 2 17150 #define JIM_VARLIST_MASK 0x000f 17151 17152 #define JIM_VARLIST_VALUES 0x1000 17153 17154 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, 17155 Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type) 17156 { 17157 Jim_VarVal *vv = (Jim_VarVal *)value; 17158 17159 if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) { 17160 if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) { 17161 Jim_ListAppendElement(interp, listObjPtr, keyObj); 17162 if (type & JIM_VARLIST_VALUES) { 17163 Jim_ListAppendElement(interp, listObjPtr, vv->objPtr); 17164 } 17165 } 17166 } 17167 } 17168 17169 17170 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode) 17171 { 17172 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) { 17173 return interp->emptyObj; 17174 } 17175 else { 17176 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr; 17177 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, 17178 mode); 17179 } 17180 } 17181 17182 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr) 17183 { 17184 long level; 17185 17186 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) { 17187 Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level); 17188 if (targetCallFrame && targetCallFrame != interp->topFramePtr) { 17189 #ifdef JIM_NO_INTROSPECTION 17190 17191 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1); 17192 #else 17193 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc); 17194 #endif 17195 return JIM_OK; 17196 } 17197 } 17198 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); 17199 return JIM_ERR; 17200 } 17201 17202 static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr) 17203 { 17204 long level; 17205 17206 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) { 17207 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level); 17208 if (frame) { 17209 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 17210 17211 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1)); 17212 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1)); 17213 if (frame->scriptObj) { 17214 ScriptObj *script = JimGetScript(interp, frame->scriptObj); 17215 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1)); 17216 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr)); 17217 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1)); 17218 Jim_ListAppendElement(interp, listObj, script->fileNameObj); 17219 } 17220 #ifndef JIM_NO_INTROSPECTION 17221 { 17222 Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc); 17223 17224 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1)); 17225 Jim_ListAppendElement(interp, listObj, cmdObj); 17226 } 17227 #endif 17228 { 17229 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame); 17230 if (procNameObj) { 17231 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1)); 17232 Jim_ListAppendElement(interp, listObj, procNameObj); 17233 } 17234 } 17235 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1)); 17236 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level)); 17237 17238 *objPtrPtr = listObj; 17239 return JIM_OK; 17240 } 17241 } 17242 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); 17243 return JIM_ERR; 17244 } 17245 17246 17247 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17248 { 17249 if (argc != 2 && argc != 3) { 17250 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string"); 17251 return JIM_ERR; 17252 } 17253 if (argc == 3) { 17254 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) { 17255 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1); 17256 return JIM_ERR; 17257 } 17258 else { 17259 fputs(Jim_String(argv[2]), stdout); 17260 } 17261 } 17262 else { 17263 puts(Jim_String(argv[1])); 17264 } 17265 return JIM_OK; 17266 } 17267 17268 17269 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) 17270 { 17271 jim_wide wideValue, res; 17272 double doubleValue, doubleRes; 17273 int i; 17274 17275 res = (op == JIM_EXPROP_ADD) ? 0 : 1; 17276 17277 for (i = 1; i < argc; i++) { 17278 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) 17279 goto trydouble; 17280 if (op == JIM_EXPROP_ADD) 17281 res += wideValue; 17282 else 17283 res *= wideValue; 17284 } 17285 Jim_SetResultInt(interp, res); 17286 return JIM_OK; 17287 trydouble: 17288 doubleRes = (double)res; 17289 for (; i < argc; i++) { 17290 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK) 17291 return JIM_ERR; 17292 if (op == JIM_EXPROP_ADD) 17293 doubleRes += doubleValue; 17294 else 17295 doubleRes *= doubleValue; 17296 } 17297 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17298 return JIM_OK; 17299 } 17300 17301 17302 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) 17303 { 17304 jim_wide wideValue, res = 0; 17305 double doubleValue, doubleRes = 0; 17306 int i = 2; 17307 17308 if (argc < 2) { 17309 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?"); 17310 return JIM_ERR; 17311 } 17312 else if (argc == 2) { 17313 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) { 17314 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) { 17315 return JIM_ERR; 17316 } 17317 else { 17318 if (op == JIM_EXPROP_SUB) 17319 doubleRes = -doubleValue; 17320 else 17321 doubleRes = 1.0 / doubleValue; 17322 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17323 return JIM_OK; 17324 } 17325 } 17326 if (op == JIM_EXPROP_SUB) { 17327 res = -wideValue; 17328 Jim_SetResultInt(interp, res); 17329 } 17330 else { 17331 doubleRes = 1.0 / wideValue; 17332 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17333 } 17334 return JIM_OK; 17335 } 17336 else { 17337 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) { 17338 if (Jim_GetDouble(interp, argv[1], &doubleRes) 17339 != JIM_OK) { 17340 return JIM_ERR; 17341 } 17342 else { 17343 goto trydouble; 17344 } 17345 } 17346 } 17347 for (i = 2; i < argc; i++) { 17348 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) { 17349 doubleRes = (double)res; 17350 goto trydouble; 17351 } 17352 if (op == JIM_EXPROP_SUB) 17353 res -= wideValue; 17354 else { 17355 if (wideValue == 0) { 17356 Jim_SetResultString(interp, "Division by zero", -1); 17357 return JIM_ERR; 17358 } 17359 res /= wideValue; 17360 } 17361 } 17362 Jim_SetResultInt(interp, res); 17363 return JIM_OK; 17364 trydouble: 17365 for (; i < argc; i++) { 17366 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK) 17367 return JIM_ERR; 17368 if (op == JIM_EXPROP_SUB) 17369 doubleRes -= doubleValue; 17370 else 17371 doubleRes /= doubleValue; 17372 } 17373 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); 17374 return JIM_OK; 17375 } 17376 17377 17378 17379 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17380 { 17381 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD); 17382 } 17383 17384 17385 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17386 { 17387 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL); 17388 } 17389 17390 17391 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17392 { 17393 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB); 17394 } 17395 17396 17397 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17398 { 17399 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV); 17400 } 17401 17402 17403 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17404 { 17405 if (argc != 2 && argc != 3) { 17406 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?"); 17407 return JIM_ERR; 17408 } 17409 if (argc == 2) { 17410 Jim_Obj *objPtr; 17411 17412 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); 17413 if (!objPtr) 17414 return JIM_ERR; 17415 Jim_SetResult(interp, objPtr); 17416 return JIM_OK; 17417 } 17418 17419 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) 17420 return JIM_ERR; 17421 Jim_SetResult(interp, argv[2]); 17422 return JIM_OK; 17423 } 17424 17425 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17426 { 17427 int i = 1; 17428 int complain = 1; 17429 17430 while (i < argc) { 17431 if (Jim_CompareStringImmediate(interp, argv[i], "--")) { 17432 i++; 17433 break; 17434 } 17435 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) { 17436 complain = 0; 17437 i++; 17438 continue; 17439 } 17440 break; 17441 } 17442 17443 while (i < argc) { 17444 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK 17445 && complain) { 17446 return JIM_ERR; 17447 } 17448 i++; 17449 } 17450 17451 Jim_SetEmptyResult(interp); 17452 return JIM_OK; 17453 } 17454 17455 static int JimCheckLoopRetcode(Jim_Interp *interp, int retval) 17456 { 17457 if (retval == JIM_BREAK || retval == JIM_CONTINUE) { 17458 if (--interp->break_level > 0) { 17459 return 1; 17460 } 17461 } 17462 return 0; 17463 } 17464 17465 17466 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17467 { 17468 if (argc != 3) { 17469 Jim_WrongNumArgs(interp, 1, argv, "condition body"); 17470 return JIM_ERR; 17471 } 17472 17473 17474 while (1) { 17475 int boolean = 0, retval; 17476 17477 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK) 17478 return retval; 17479 if (!boolean) 17480 break; 17481 17482 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) { 17483 if (JimCheckLoopRetcode(interp, retval)) { 17484 return retval; 17485 } 17486 switch (retval) { 17487 case JIM_BREAK: 17488 goto out; 17489 case JIM_CONTINUE: 17490 continue; 17491 default: 17492 return retval; 17493 } 17494 } 17495 } 17496 out: 17497 Jim_SetEmptyResult(interp); 17498 return JIM_OK; 17499 } 17500 17501 17502 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17503 { 17504 int retval; 17505 int boolean = 1; 17506 int immediate = 0; 17507 Jim_Obj *varNamePtr = NULL; 17508 Jim_Obj *stopVarNamePtr = NULL; 17509 17510 if (argc != 5) { 17511 Jim_WrongNumArgs(interp, 1, argv, "start test next body"); 17512 return JIM_ERR; 17513 } 17514 17515 17516 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) { 17517 return retval; 17518 } 17519 17520 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); 17521 17522 17523 #ifdef JIM_OPTIMIZATION 17524 if (retval == JIM_OK && boolean) { 17525 ScriptObj *incrScript; 17526 struct ExprTree *expr; 17527 jim_wide stop, currentVal; 17528 Jim_Obj *objPtr; 17529 int cmpOffset; 17530 17531 17532 expr = JimGetExpression(interp, argv[2]); 17533 incrScript = JimGetScript(interp, argv[3]); 17534 17535 17536 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) { 17537 goto evalstart; 17538 } 17539 17540 if (incrScript->token[1].type != JIM_TT_ESC) { 17541 goto evalstart; 17542 } 17543 17544 if (expr->expr->type == JIM_EXPROP_LT) { 17545 cmpOffset = 0; 17546 } 17547 else if (expr->expr->type == JIM_EXPROP_LTE) { 17548 cmpOffset = 1; 17549 } 17550 else { 17551 goto evalstart; 17552 } 17553 17554 if (expr->expr->left->type != JIM_TT_VAR) { 17555 goto evalstart; 17556 } 17557 17558 if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) { 17559 goto evalstart; 17560 } 17561 17562 17563 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) { 17564 goto evalstart; 17565 } 17566 17567 17568 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) { 17569 goto evalstart; 17570 } 17571 17572 17573 if (expr->expr->right->type == JIM_TT_EXPR_INT) { 17574 if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) { 17575 goto evalstart; 17576 } 17577 } 17578 else { 17579 stopVarNamePtr = expr->expr->right->objPtr; 17580 Jim_IncrRefCount(stopVarNamePtr); 17581 17582 stop = 0; 17583 } 17584 17585 17586 varNamePtr = expr->expr->left->objPtr; 17587 Jim_IncrRefCount(varNamePtr); 17588 17589 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE); 17590 if (objPtr == NULL || Jim_GetWide(interp, objPtr, ¤tVal) != JIM_OK) { 17591 goto testcond; 17592 } 17593 17594 17595 while (retval == JIM_OK) { 17596 17597 17598 17599 17600 if (stopVarNamePtr) { 17601 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE); 17602 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) { 17603 goto testcond; 17604 } 17605 } 17606 17607 if (currentVal >= stop + cmpOffset) { 17608 break; 17609 } 17610 17611 17612 retval = Jim_EvalObj(interp, argv[4]); 17613 if (JimCheckLoopRetcode(interp, retval)) { 17614 immediate++; 17615 goto out; 17616 } 17617 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17618 retval = JIM_OK; 17619 17620 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG); 17621 17622 17623 if (objPtr == NULL) { 17624 retval = JIM_ERR; 17625 goto out; 17626 } 17627 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { 17628 currentVal = ++JimWideValue(objPtr); 17629 Jim_InvalidateStringRep(objPtr); 17630 } 17631 else { 17632 if (Jim_GetWide(interp, objPtr, ¤tVal) != JIM_OK || 17633 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp, 17634 ++currentVal)) != JIM_OK) { 17635 goto evalnext; 17636 } 17637 } 17638 } 17639 } 17640 goto out; 17641 } 17642 evalstart: 17643 #endif 17644 17645 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) { 17646 17647 retval = Jim_EvalObj(interp, argv[4]); 17648 if (JimCheckLoopRetcode(interp, retval)) { 17649 immediate++; 17650 break; 17651 } 17652 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17653 17654 JIM_IF_OPTIM(evalnext:) 17655 retval = Jim_EvalObj(interp, argv[3]); 17656 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17657 17658 JIM_IF_OPTIM(testcond:) 17659 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); 17660 } 17661 } 17662 } 17663 JIM_IF_OPTIM(out:) 17664 if (stopVarNamePtr) { 17665 Jim_DecrRefCount(interp, stopVarNamePtr); 17666 } 17667 if (varNamePtr) { 17668 Jim_DecrRefCount(interp, varNamePtr); 17669 } 17670 17671 if (!immediate) { 17672 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) { 17673 Jim_SetEmptyResult(interp); 17674 return JIM_OK; 17675 } 17676 } 17677 17678 return retval; 17679 } 17680 17681 17682 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17683 { 17684 int retval; 17685 jim_wide i; 17686 jim_wide limit = 0; 17687 jim_wide incr = 1; 17688 Jim_Obj *bodyObjPtr; 17689 17690 if (argc < 4 || argc > 6) { 17691 Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body"); 17692 return JIM_ERR; 17693 } 17694 17695 retval = Jim_GetWideExpr(interp, argv[2], &i); 17696 if (argc > 4 && retval == JIM_OK) { 17697 retval = Jim_GetWideExpr(interp, argv[3], &limit); 17698 } 17699 if (argc > 5 && retval == JIM_OK) { 17700 Jim_GetWideExpr(interp, argv[4], &incr); 17701 } 17702 if (retval != JIM_OK) { 17703 return retval; 17704 } 17705 if (argc == 4) { 17706 limit = i; 17707 i = 0; 17708 } 17709 bodyObjPtr = argv[argc - 1]; 17710 17711 retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i)); 17712 17713 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) { 17714 retval = Jim_EvalObj(interp, bodyObjPtr); 17715 if (JimCheckLoopRetcode(interp, retval)) { 17716 return retval; 17717 } 17718 if (retval == JIM_OK || retval == JIM_CONTINUE) { 17719 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); 17720 17721 retval = JIM_OK; 17722 17723 17724 i += incr; 17725 17726 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { 17727 if (argv[1]->typePtr != &variableObjType) { 17728 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { 17729 return JIM_ERR; 17730 } 17731 } 17732 JimWideValue(objPtr) = i; 17733 Jim_InvalidateStringRep(objPtr); 17734 17735 if (argv[1]->typePtr != &variableObjType) { 17736 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { 17737 retval = JIM_ERR; 17738 break; 17739 } 17740 } 17741 } 17742 else { 17743 objPtr = Jim_NewIntObj(interp, i); 17744 retval = Jim_SetVariable(interp, argv[1], objPtr); 17745 if (retval != JIM_OK) { 17746 Jim_FreeNewObj(interp, objPtr); 17747 } 17748 } 17749 } 17750 } 17751 17752 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) { 17753 Jim_SetEmptyResult(interp); 17754 return JIM_OK; 17755 } 17756 return retval; 17757 } 17758 17759 typedef struct { 17760 Jim_Obj *objPtr; 17761 int idx; 17762 } Jim_ListIter; 17763 17764 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr) 17765 { 17766 iter->objPtr = objPtr; 17767 iter->idx = 0; 17768 } 17769 17770 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter) 17771 { 17772 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) { 17773 return NULL; 17774 } 17775 return iter->objPtr->internalRep.listValue.ele[iter->idx++]; 17776 } 17777 17778 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter) 17779 { 17780 return iter->idx >= Jim_ListLength(interp, iter->objPtr); 17781 } 17782 17783 17784 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap) 17785 { 17786 int result = JIM_OK; 17787 int i, numargs; 17788 Jim_ListIter twoiters[2]; 17789 Jim_ListIter *iters; 17790 Jim_Obj *script; 17791 Jim_Obj *resultObj; 17792 17793 if (argc < 4 || argc % 2 != 0) { 17794 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script"); 17795 return JIM_ERR; 17796 } 17797 script = argv[argc - 1]; 17798 numargs = (argc - 1 - 1); 17799 17800 if (numargs == 2) { 17801 iters = twoiters; 17802 } 17803 else { 17804 iters = Jim_Alloc(numargs * sizeof(*iters)); 17805 } 17806 for (i = 0; i < numargs; i++) { 17807 JimListIterInit(&iters[i], argv[i + 1]); 17808 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) { 17809 result = JIM_ERR; 17810 } 17811 } 17812 if (result != JIM_OK) { 17813 Jim_SetResultString(interp, "foreach varlist is empty", -1); 17814 goto empty_varlist; 17815 } 17816 17817 if (doMap) { 17818 resultObj = Jim_NewListObj(interp, NULL, 0); 17819 } 17820 else { 17821 resultObj = interp->emptyObj; 17822 } 17823 Jim_IncrRefCount(resultObj); 17824 17825 while (1) { 17826 17827 for (i = 0; i < numargs; i += 2) { 17828 if (!JimListIterDone(interp, &iters[i + 1])) { 17829 break; 17830 } 17831 } 17832 if (i == numargs) { 17833 17834 break; 17835 } 17836 17837 17838 for (i = 0; i < numargs; i += 2) { 17839 Jim_Obj *varName; 17840 17841 17842 JimListIterInit(&iters[i], argv[i + 1]); 17843 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) { 17844 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]); 17845 if (!valObj) { 17846 17847 valObj = interp->emptyObj; 17848 } 17849 17850 Jim_IncrRefCount(valObj); 17851 result = Jim_SetVariable(interp, varName, valObj); 17852 Jim_DecrRefCount(interp, valObj); 17853 if (result != JIM_OK) { 17854 goto err; 17855 } 17856 } 17857 } 17858 result = Jim_EvalObj(interp, script); 17859 if (JimCheckLoopRetcode(interp, result)) { 17860 goto err; 17861 } 17862 switch (result) { 17863 case JIM_OK: 17864 if (doMap) { 17865 Jim_ListAppendElement(interp, resultObj, interp->result); 17866 } 17867 break; 17868 case JIM_CONTINUE: 17869 break; 17870 case JIM_BREAK: 17871 goto out; 17872 default: 17873 goto err; 17874 } 17875 } 17876 out: 17877 result = JIM_OK; 17878 Jim_SetResult(interp, resultObj); 17879 err: 17880 Jim_DecrRefCount(interp, resultObj); 17881 empty_varlist: 17882 if (numargs > 2) { 17883 Jim_Free(iters); 17884 } 17885 return result; 17886 } 17887 17888 17889 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17890 { 17891 return JimForeachMapHelper(interp, argc, argv, 0); 17892 } 17893 17894 17895 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17896 { 17897 return JimForeachMapHelper(interp, argc, argv, 1); 17898 } 17899 17900 17901 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17902 { 17903 int result = JIM_ERR; 17904 int i; 17905 Jim_ListIter iter; 17906 Jim_Obj *resultObj; 17907 17908 if (argc < 2) { 17909 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?"); 17910 return JIM_ERR; 17911 } 17912 17913 JimListIterInit(&iter, argv[1]); 17914 17915 for (i = 2; i < argc; i++) { 17916 Jim_Obj *valObj = JimListIterNext(interp, &iter); 17917 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj); 17918 if (result != JIM_OK) { 17919 return result; 17920 } 17921 } 17922 17923 resultObj = Jim_NewListObj(interp, NULL, 0); 17924 while (!JimListIterDone(interp, &iter)) { 17925 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter)); 17926 } 17927 17928 Jim_SetResult(interp, resultObj); 17929 17930 return JIM_OK; 17931 } 17932 17933 17934 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 17935 { 17936 int boolean, retval, current = 1, falsebody = 0; 17937 17938 if (argc >= 3) { 17939 while (1) { 17940 17941 if (current >= argc) 17942 goto err; 17943 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean)) 17944 != JIM_OK) 17945 return retval; 17946 17947 if (current >= argc) 17948 goto err; 17949 if (Jim_CompareStringImmediate(interp, argv[current], "then")) 17950 current++; 17951 17952 if (current >= argc) 17953 goto err; 17954 if (boolean) 17955 return Jim_EvalObj(interp, argv[current]); 17956 17957 if (++current >= argc) { 17958 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); 17959 return JIM_OK; 17960 } 17961 falsebody = current++; 17962 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) { 17963 17964 if (current != argc - 1) 17965 goto err; 17966 return Jim_EvalObj(interp, argv[current]); 17967 } 17968 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif")) 17969 continue; 17970 17971 else if (falsebody != argc - 1) 17972 goto err; 17973 return Jim_EvalObj(interp, argv[falsebody]); 17974 } 17975 return JIM_OK; 17976 } 17977 err: 17978 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody"); 17979 return JIM_ERR; 17980 } 17981 17982 17983 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj, 17984 Jim_Obj *stringObj, int flags) 17985 { 17986 Jim_Obj *parms[5]; 17987 int argc = 0; 17988 long eq; 17989 int rc; 17990 17991 parms[argc++] = commandObj; 17992 if (flags & JIM_NOCASE) { 17993 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1); 17994 } 17995 if (flags & JIM_OPT_END) { 17996 parms[argc++] = Jim_NewStringObj(interp, "--", -1); 17997 } 17998 parms[argc++] = patternObj; 17999 parms[argc++] = stringObj; 18000 18001 rc = Jim_EvalObjVector(interp, argc, parms); 18002 18003 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) { 18004 eq = -rc; 18005 } 18006 18007 return eq; 18008 } 18009 18010 18011 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18012 { 18013 enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD }; 18014 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i; 18015 int match_flags = 0; 18016 Jim_Obj *command = NULL, *scriptObj = NULL, *strObj; 18017 Jim_Obj **caseList; 18018 18019 if (argc < 3) { 18020 wrongnumargs: 18021 Jim_WrongNumArgs(interp, 1, argv, "?options? string " 18022 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}"); 18023 return JIM_ERR; 18024 } 18025 for (opt = 1; opt < argc; ++opt) { 18026 const char *option = Jim_String(argv[opt]); 18027 18028 if (*option != '-') 18029 break; 18030 else if (strncmp(option, "--", 2) == 0) { 18031 ++opt; 18032 break; 18033 } 18034 else if (strncmp(option, "-exact", 2) == 0) 18035 matchOpt = SWITCH_EXACT; 18036 else if (strncmp(option, "-glob", 2) == 0) 18037 matchOpt = SWITCH_GLOB; 18038 else if (strncmp(option, "-regexp", 2) == 0) { 18039 matchOpt = SWITCH_RE; 18040 match_flags |= JIM_OPT_END; 18041 } 18042 else if (strncmp(option, "-command", 2) == 0) { 18043 matchOpt = SWITCH_CMD; 18044 if ((argc - opt) < 2) 18045 goto wrongnumargs; 18046 command = argv[++opt]; 18047 } 18048 else { 18049 Jim_SetResultFormatted(interp, 18050 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --", 18051 argv[opt]); 18052 return JIM_ERR; 18053 } 18054 if ((argc - opt) < 2) 18055 goto wrongnumargs; 18056 } 18057 strObj = argv[opt++]; 18058 patCount = argc - opt; 18059 if (patCount == 1) { 18060 JimListGetElements(interp, argv[opt], &patCount, &caseList); 18061 } 18062 else 18063 caseList = (Jim_Obj **)&argv[opt]; 18064 if (patCount == 0 || patCount % 2 != 0) 18065 goto wrongnumargs; 18066 for (i = 0; scriptObj == NULL && i < patCount; i += 2) { 18067 Jim_Obj *patObj = caseList[i]; 18068 18069 if (!Jim_CompareStringImmediate(interp, patObj, "default") 18070 || i < (patCount - 2)) { 18071 switch (matchOpt) { 18072 case SWITCH_EXACT: 18073 if (Jim_StringEqObj(strObj, patObj)) 18074 scriptObj = caseList[i + 1]; 18075 break; 18076 case SWITCH_GLOB: 18077 if (Jim_StringMatchObj(interp, patObj, strObj, 0)) 18078 scriptObj = caseList[i + 1]; 18079 break; 18080 case SWITCH_RE: 18081 command = Jim_NewStringObj(interp, "regexp", -1); 18082 18083 case SWITCH_CMD:{ 18084 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags); 18085 18086 if (argc - opt == 1) { 18087 JimListGetElements(interp, argv[opt], &patCount, &caseList); 18088 } 18089 18090 if (rc < 0) { 18091 return -rc; 18092 } 18093 if (rc) 18094 scriptObj = caseList[i + 1]; 18095 break; 18096 } 18097 } 18098 } 18099 else { 18100 scriptObj = caseList[i + 1]; 18101 } 18102 } 18103 for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2) 18104 scriptObj = caseList[i + 1]; 18105 if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) { 18106 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]); 18107 return JIM_ERR; 18108 } 18109 Jim_SetEmptyResult(interp); 18110 if (scriptObj) { 18111 return Jim_EvalObj(interp, scriptObj); 18112 } 18113 return JIM_OK; 18114 } 18115 18116 18117 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18118 { 18119 Jim_Obj *listObjPtr; 18120 18121 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1); 18122 Jim_SetResult(interp, listObjPtr); 18123 return JIM_OK; 18124 } 18125 18126 18127 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18128 { 18129 Jim_Obj *objPtr; 18130 int ret; 18131 18132 if (argc < 2) { 18133 Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?"); 18134 return JIM_ERR; 18135 } 18136 ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE); 18137 if (ret < 0) { 18138 ret = JIM_OK; 18139 Jim_SetEmptyResult(interp); 18140 } 18141 else if (ret == JIM_OK) { 18142 Jim_SetResult(interp, objPtr); 18143 } 18144 return ret; 18145 } 18146 18147 18148 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18149 { 18150 if (argc != 2) { 18151 Jim_WrongNumArgs(interp, 1, argv, "list"); 18152 return JIM_ERR; 18153 } 18154 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1])); 18155 return JIM_OK; 18156 } 18157 18158 18159 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18160 { 18161 static const char * const options[] = { 18162 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command", 18163 "-stride", "-index", NULL 18164 }; 18165 enum 18166 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE, 18167 OPT_COMMAND, OPT_STRIDE, OPT_INDEX }; 18168 int i; 18169 int opt_bool = 0; 18170 int opt_not = 0; 18171 int opt_all = 0; 18172 int opt_inline = 0; 18173 int opt_match = OPT_EXACT; 18174 int listlen; 18175 int rc = JIM_OK; 18176 Jim_Obj *listObjPtr = NULL; 18177 Jim_Obj *commandObj = NULL; 18178 Jim_Obj *indexObj = NULL; 18179 int match_flags = 0; 18180 long stride = 1; 18181 18182 if (argc < 3) { 18183 wrongargs: 18184 Jim_WrongNumArgs(interp, 1, argv, 18185 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value"); 18186 return JIM_ERR; 18187 } 18188 18189 for (i = 1; i < argc - 2; i++) { 18190 int option; 18191 18192 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { 18193 return JIM_ERR; 18194 } 18195 switch (option) { 18196 case OPT_BOOL: 18197 opt_bool = 1; 18198 opt_inline = 0; 18199 break; 18200 case OPT_NOT: 18201 opt_not = 1; 18202 break; 18203 case OPT_NOCASE: 18204 match_flags |= JIM_NOCASE; 18205 break; 18206 case OPT_INLINE: 18207 opt_inline = 1; 18208 opt_bool = 0; 18209 break; 18210 case OPT_ALL: 18211 opt_all = 1; 18212 break; 18213 case OPT_REGEXP: 18214 opt_match = option; 18215 match_flags |= JIM_OPT_END; 18216 break; 18217 case OPT_COMMAND: 18218 if (i >= argc - 2) { 18219 goto wrongargs; 18220 } 18221 commandObj = argv[++i]; 18222 18223 case OPT_EXACT: 18224 case OPT_GLOB: 18225 opt_match = option; 18226 break; 18227 case OPT_INDEX: 18228 if (i >= argc - 2) { 18229 goto wrongargs; 18230 } 18231 indexObj = argv[++i]; 18232 break; 18233 case OPT_STRIDE: 18234 if (i >= argc - 2) { 18235 goto wrongargs; 18236 } 18237 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) { 18238 return JIM_ERR; 18239 } 18240 if (stride < 1) { 18241 Jim_SetResultString(interp, "stride length must be at least 1", -1); 18242 return JIM_ERR; 18243 } 18244 break; 18245 } 18246 } 18247 18248 argc -= i; 18249 if (argc < 2) { 18250 goto wrongargs; 18251 } 18252 argv += i; 18253 18254 listlen = Jim_ListLength(interp, argv[0]); 18255 if (listlen % stride) { 18256 Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1); 18257 return JIM_ERR; 18258 } 18259 18260 if (opt_all) { 18261 listObjPtr = Jim_NewListObj(interp, NULL, 0); 18262 } 18263 if (opt_match == OPT_REGEXP) { 18264 commandObj = Jim_NewStringObj(interp, "regexp", -1); 18265 } 18266 if (commandObj) { 18267 Jim_IncrRefCount(commandObj); 18268 } 18269 18270 for (i = 0; i < listlen; i += stride) { 18271 int eq = 0; 18272 Jim_Obj *searchListObj; 18273 Jim_Obj *objPtr; 18274 int offset; 18275 18276 if (indexObj) { 18277 int indexlen = Jim_ListLength(interp, indexObj); 18278 if (stride == 1) { 18279 searchListObj = Jim_ListGetIndex(interp, argv[0], i); 18280 } 18281 else { 18282 searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride); 18283 } 18284 Jim_IncrRefCount(searchListObj); 18285 rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG); 18286 if (rc != JIM_OK) { 18287 Jim_DecrRefCount(interp, searchListObj); 18288 rc = JIM_ERR; 18289 goto done; 18290 } 18291 18292 offset = 0; 18293 } 18294 else { 18295 18296 searchListObj = argv[0]; 18297 offset = i; 18298 objPtr = Jim_ListGetIndex(interp, searchListObj, i); 18299 Jim_IncrRefCount(searchListObj); 18300 } 18301 18302 switch (opt_match) { 18303 case OPT_EXACT: 18304 eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0; 18305 break; 18306 18307 case OPT_GLOB: 18308 eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags); 18309 break; 18310 18311 case OPT_REGEXP: 18312 case OPT_COMMAND: 18313 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags); 18314 if (eq < 0) { 18315 Jim_DecrRefCount(interp, searchListObj); 18316 rc = JIM_ERR; 18317 goto done; 18318 } 18319 break; 18320 } 18321 18322 18323 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) { 18324 Jim_Obj *resultObj; 18325 18326 if (opt_bool) { 18327 resultObj = Jim_NewIntObj(interp, eq ^ opt_not); 18328 } 18329 else if (!opt_inline) { 18330 resultObj = Jim_NewIntObj(interp, i); 18331 } 18332 else if (stride == 1) { 18333 resultObj = objPtr; 18334 } 18335 else if (opt_all) { 18336 18337 ListInsertElements(listObjPtr, -1, stride, 18338 searchListObj->internalRep.listValue.ele + offset); 18339 18340 resultObj = NULL; 18341 } 18342 else { 18343 resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride); 18344 } 18345 18346 if (opt_all) { 18347 18348 if (stride == 1) { 18349 Jim_ListAppendElement(interp, listObjPtr, resultObj); 18350 } 18351 } 18352 else { 18353 Jim_SetResult(interp, resultObj); 18354 Jim_DecrRefCount(interp, searchListObj); 18355 goto done; 18356 } 18357 } 18358 Jim_DecrRefCount(interp, searchListObj); 18359 } 18360 18361 if (opt_all) { 18362 Jim_SetResult(interp, listObjPtr); 18363 listObjPtr = NULL; 18364 } 18365 else { 18366 18367 if (opt_bool) { 18368 Jim_SetResultBool(interp, opt_not); 18369 } 18370 else if (!opt_inline) { 18371 Jim_SetResultInt(interp, -1); 18372 } 18373 } 18374 18375 done: 18376 if (listObjPtr) { 18377 Jim_FreeNewObj(interp, listObjPtr); 18378 } 18379 if (commandObj) { 18380 Jim_DecrRefCount(interp, commandObj); 18381 } 18382 return rc; 18383 } 18384 18385 18386 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18387 { 18388 Jim_Obj *listObjPtr; 18389 int new_obj = 0; 18390 int i; 18391 18392 if (argc < 2) { 18393 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?"); 18394 return JIM_ERR; 18395 } 18396 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); 18397 if (!listObjPtr) { 18398 18399 listObjPtr = Jim_NewListObj(interp, NULL, 0); 18400 new_obj = 1; 18401 } 18402 else if (Jim_IsShared(listObjPtr)) { 18403 listObjPtr = Jim_DuplicateObj(interp, listObjPtr); 18404 new_obj = 1; 18405 } 18406 for (i = 2; i < argc; i++) 18407 Jim_ListAppendElement(interp, listObjPtr, argv[i]); 18408 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) { 18409 if (new_obj) 18410 Jim_FreeNewObj(interp, listObjPtr); 18411 return JIM_ERR; 18412 } 18413 Jim_SetResult(interp, listObjPtr); 18414 return JIM_OK; 18415 } 18416 18417 18418 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18419 { 18420 int idx, len; 18421 Jim_Obj *listPtr; 18422 18423 if (argc < 3) { 18424 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?"); 18425 return JIM_ERR; 18426 } 18427 listPtr = argv[1]; 18428 if (Jim_IsShared(listPtr)) 18429 listPtr = Jim_DuplicateObj(interp, listPtr); 18430 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK) 18431 goto err; 18432 len = Jim_ListLength(interp, listPtr); 18433 if (idx >= len) 18434 idx = len; 18435 else if (idx < 0) 18436 idx = len + idx + 1; 18437 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]); 18438 Jim_SetResult(interp, listPtr); 18439 return JIM_OK; 18440 err: 18441 if (listPtr != argv[1]) { 18442 Jim_FreeNewObj(interp, listPtr); 18443 } 18444 return JIM_ERR; 18445 } 18446 18447 18448 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18449 { 18450 int first, last, len, rangeLen; 18451 Jim_Obj *listObj; 18452 Jim_Obj *newListObj; 18453 18454 if (argc < 4) { 18455 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?"); 18456 return JIM_ERR; 18457 } 18458 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK || 18459 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) { 18460 return JIM_ERR; 18461 } 18462 18463 listObj = argv[1]; 18464 len = Jim_ListLength(interp, listObj); 18465 18466 first = JimRelToAbsIndex(len, first); 18467 last = JimRelToAbsIndex(len, last); 18468 JimRelToAbsRange(len, &first, &last, &rangeLen); 18469 18470 18471 if (first > len) { 18472 first = len; 18473 } 18474 18475 18476 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first); 18477 18478 18479 ListInsertElements(newListObj, -1, argc - 4, argv + 4); 18480 18481 18482 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen); 18483 18484 Jim_SetResult(interp, newListObj); 18485 return JIM_OK; 18486 } 18487 18488 18489 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18490 { 18491 if (argc < 3) { 18492 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value"); 18493 return JIM_ERR; 18494 } 18495 else if (argc == 3) { 18496 18497 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) 18498 return JIM_ERR; 18499 Jim_SetResult(interp, argv[2]); 18500 return JIM_OK; 18501 } 18502 return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]); 18503 } 18504 18505 18506 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[]) 18507 { 18508 static const char * const options[] = { 18509 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", 18510 "-stride", "-dictionary", NULL 18511 }; 18512 enum { 18513 OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE, 18514 OPT_STRIDE, OPT_DICT 18515 }; 18516 Jim_Obj *resObj; 18517 int i; 18518 int retCode; 18519 int shared; 18520 long stride = 1; 18521 Jim_Obj **elements; 18522 int listlen; 18523 18524 struct lsort_info info; 18525 18526 if (argc < 2) { 18527 wrongargs: 18528 Jim_WrongNumArgs(interp, 1, argv, "?options? list"); 18529 return JIM_ERR; 18530 } 18531 18532 info.type = JIM_LSORT_ASCII; 18533 info.order = 1; 18534 info.indexc = 0; 18535 info.unique = 0; 18536 info.command = NULL; 18537 info.interp = interp; 18538 18539 for (i = 1; i < (argc - 1); i++) { 18540 int option; 18541 18542 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) 18543 != JIM_OK) 18544 return JIM_ERR; 18545 switch (option) { 18546 case OPT_ASCII: 18547 info.type = JIM_LSORT_ASCII; 18548 break; 18549 case OPT_DICT: 18550 info.type = JIM_LSORT_DICT; 18551 break; 18552 case OPT_NOCASE: 18553 info.type = JIM_LSORT_NOCASE; 18554 break; 18555 case OPT_INTEGER: 18556 info.type = JIM_LSORT_INTEGER; 18557 break; 18558 case OPT_REAL: 18559 info.type = JIM_LSORT_REAL; 18560 break; 18561 case OPT_INCREASING: 18562 info.order = 1; 18563 break; 18564 case OPT_DECREASING: 18565 info.order = -1; 18566 break; 18567 case OPT_UNIQUE: 18568 info.unique = 1; 18569 break; 18570 case OPT_COMMAND: 18571 if (i >= (argc - 2)) { 18572 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1); 18573 return JIM_ERR; 18574 } 18575 info.type = JIM_LSORT_COMMAND; 18576 info.command = argv[i + 1]; 18577 i++; 18578 break; 18579 case OPT_STRIDE: 18580 if (i >= argc - 2) { 18581 goto wrongargs; 18582 } 18583 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) { 18584 return JIM_ERR; 18585 } 18586 if (stride < 2) { 18587 Jim_SetResultString(interp, "stride length must be at least 2", -1); 18588 return JIM_ERR; 18589 } 18590 break; 18591 case OPT_INDEX: 18592 if (i >= (argc - 2)) { 18593 badindex: 18594 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1); 18595 return JIM_ERR; 18596 } 18597 JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv); 18598 if (info.indexc == 0) { 18599 goto badindex; 18600 } 18601 i++; 18602 break; 18603 } 18604 } 18605 resObj = argv[argc - 1]; 18606 JimListGetElements(interp, resObj, &listlen, &elements); 18607 if (listlen <= 1) { 18608 18609 Jim_SetResult(interp, resObj); 18610 return JIM_OK; 18611 } 18612 18613 if (stride > 1) { 18614 Jim_Obj *tmpListObj; 18615 int i; 18616 18617 if (listlen % stride) { 18618 Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1); 18619 return JIM_ERR; 18620 } 18621 18622 tmpListObj = Jim_NewListObj(interp, NULL, 0); 18623 Jim_IncrRefCount(tmpListObj); 18624 for (i = 0; i < listlen; i += stride) { 18625 Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride)); 18626 } 18627 retCode = ListSortElements(interp, tmpListObj, &info); 18628 if (retCode == JIM_OK) { 18629 resObj = Jim_NewListObj(interp, NULL, 0); 18630 18631 for (i = 0; i < listlen; i += stride) { 18632 Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride)); 18633 } 18634 Jim_SetResult(interp, resObj); 18635 } 18636 Jim_DecrRefCount(interp, tmpListObj); 18637 } 18638 else { 18639 if ((shared = Jim_IsShared(resObj))) { 18640 resObj = Jim_DuplicateObj(interp, resObj); 18641 } 18642 retCode = ListSortElements(interp, resObj, &info); 18643 if (retCode == JIM_OK) { 18644 Jim_SetResult(interp, resObj); 18645 } 18646 else if (shared) { 18647 Jim_FreeNewObj(interp, resObj); 18648 } 18649 } 18650 return retCode; 18651 } 18652 18653 18654 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18655 { 18656 Jim_Obj *stringObjPtr; 18657 int i; 18658 18659 if (argc < 2) { 18660 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?"); 18661 return JIM_ERR; 18662 } 18663 if (argc == 2) { 18664 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); 18665 if (!stringObjPtr) 18666 return JIM_ERR; 18667 } 18668 else { 18669 int new_obj = 0; 18670 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); 18671 if (!stringObjPtr) { 18672 18673 stringObjPtr = Jim_NewEmptyStringObj(interp); 18674 new_obj = 1; 18675 } 18676 else if (Jim_IsShared(stringObjPtr)) { 18677 new_obj = 1; 18678 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr); 18679 } 18680 for (i = 2; i < argc; i++) { 18681 Jim_AppendObj(interp, stringObjPtr, argv[i]); 18682 } 18683 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) { 18684 if (new_obj) { 18685 Jim_FreeNewObj(interp, stringObjPtr); 18686 } 18687 return JIM_ERR; 18688 } 18689 } 18690 Jim_SetResult(interp, stringObjPtr); 18691 return JIM_OK; 18692 } 18693 18694 18695 18696 18697 18698 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18699 { 18700 int rc; 18701 18702 if (argc < 2) { 18703 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?"); 18704 return JIM_ERR; 18705 } 18706 18707 if (argc == 2) { 18708 rc = Jim_EvalObj(interp, argv[1]); 18709 } 18710 else { 18711 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); 18712 } 18713 18714 return rc; 18715 } 18716 18717 18718 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18719 { 18720 if (argc >= 2) { 18721 int retcode; 18722 Jim_CallFrame *savedCallFrame, *targetCallFrame; 18723 const char *str; 18724 18725 18726 savedCallFrame = interp->framePtr; 18727 18728 18729 str = Jim_String(argv[1]); 18730 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') { 18731 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]); 18732 argc--; 18733 argv++; 18734 } 18735 else { 18736 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL); 18737 } 18738 if (targetCallFrame == NULL) { 18739 return JIM_ERR; 18740 } 18741 if (argc < 2) { 18742 Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?"); 18743 return JIM_ERR; 18744 } 18745 18746 interp->framePtr = targetCallFrame; 18747 if (argc == 2) { 18748 retcode = Jim_EvalObj(interp, argv[1]); 18749 } 18750 else { 18751 retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); 18752 } 18753 interp->framePtr = savedCallFrame; 18754 return retcode; 18755 } 18756 else { 18757 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?"); 18758 return JIM_ERR; 18759 } 18760 } 18761 18762 18763 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18764 { 18765 int retcode; 18766 18767 if (argc == 2) { 18768 retcode = Jim_EvalExpression(interp, argv[1]); 18769 } 18770 #ifndef JIM_COMPAT 18771 else { 18772 Jim_WrongNumArgs(interp, 1, argv, "expression"); 18773 retcode = JIM_ERR; 18774 } 18775 #else 18776 else if (argc > 2) { 18777 Jim_Obj *objPtr; 18778 18779 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1); 18780 Jim_IncrRefCount(objPtr); 18781 retcode = Jim_EvalExpression(interp, objPtr); 18782 Jim_DecrRefCount(interp, objPtr); 18783 } 18784 else { 18785 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?"); 18786 return JIM_ERR; 18787 } 18788 #endif 18789 return retcode; 18790 } 18791 18792 static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode) 18793 { 18794 if (argc != 1 && argc != 2) { 18795 Jim_WrongNumArgs(interp, 1, argv, "?level?"); 18796 return JIM_ERR; 18797 } 18798 if (argc == 2) { 18799 long level; 18800 int ret = Jim_GetLong(interp, argv[1], &level); 18801 if (ret != JIM_OK) { 18802 return ret; 18803 } 18804 interp->break_level = level; 18805 } 18806 return retcode; 18807 } 18808 18809 18810 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18811 { 18812 return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK); 18813 } 18814 18815 18816 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18817 { 18818 return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE); 18819 } 18820 18821 18822 static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18823 { 18824 Jim_Obj *listObj; 18825 int i; 18826 jim_wide skip = 0; 18827 jim_wide last = 0; 18828 18829 if (argc > 1) { 18830 if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) { 18831 return JIM_ERR; 18832 } 18833 } 18834 if (argc > 2) { 18835 if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) { 18836 return JIM_ERR; 18837 } 18838 } 18839 18840 listObj = Jim_NewListObj(interp, NULL, 0); 18841 for (i = skip; i <= interp->procLevel; i++) { 18842 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i); 18843 if (frame->procLevel < last) { 18844 break; 18845 } 18846 JimAddStackFrame(interp, frame, listObj); 18847 } 18848 Jim_SetResult(interp, listObj); 18849 return JIM_OK; 18850 } 18851 18852 18853 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18854 { 18855 int i; 18856 Jim_Obj *stackTraceObj = NULL; 18857 Jim_Obj *errorCodeObj = NULL; 18858 int returnCode = JIM_OK; 18859 long level = 1; 18860 18861 for (i = 1; i < argc - 1; i += 2) { 18862 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) { 18863 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) { 18864 return JIM_ERR; 18865 } 18866 } 18867 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) { 18868 stackTraceObj = argv[i + 1]; 18869 } 18870 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) { 18871 errorCodeObj = argv[i + 1]; 18872 } 18873 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) { 18874 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) { 18875 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]); 18876 return JIM_ERR; 18877 } 18878 } 18879 else { 18880 break; 18881 } 18882 } 18883 18884 if (i != argc - 1 && i != argc) { 18885 Jim_WrongNumArgs(interp, 1, argv, 18886 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?"); 18887 } 18888 18889 18890 if (stackTraceObj && returnCode == JIM_ERR) { 18891 JimSetStackTrace(interp, stackTraceObj); 18892 } 18893 18894 if (errorCodeObj && returnCode == JIM_ERR) { 18895 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj); 18896 } 18897 interp->returnCode = returnCode; 18898 interp->returnLevel = level; 18899 18900 if (i == argc - 1) { 18901 Jim_SetResult(interp, argv[i]); 18902 } 18903 return level == 0 ? returnCode : JIM_RETURN; 18904 } 18905 18906 18907 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18908 { 18909 if (interp->framePtr->level == 0) { 18910 Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1); 18911 return JIM_ERR; 18912 } 18913 else if (argc >= 2) { 18914 18915 Jim_CallFrame *cf = interp->framePtr->parent; 18916 18917 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); 18918 if (cmdPtr == NULL) { 18919 return JIM_ERR; 18920 } 18921 18922 JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd")); 18923 18924 18925 JimIncrCmdRefCount(cmdPtr); 18926 cf->tailcallCmd = cmdPtr; 18927 18928 18929 JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj")); 18930 18931 cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1); 18932 Jim_IncrRefCount(cf->tailcallObj); 18933 18934 18935 return JIM_EVAL; 18936 } 18937 return JIM_OK; 18938 } 18939 18940 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18941 { 18942 Jim_Obj *cmdList; 18943 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp); 18944 18945 18946 cmdList = Jim_DuplicateObj(interp, prefixListObj); 18947 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1); 18948 18949 return JimEvalObjList(interp, cmdList); 18950 } 18951 18952 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData) 18953 { 18954 Jim_Obj *prefixListObj = privData; 18955 Jim_DecrRefCount(interp, prefixListObj); 18956 } 18957 18958 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18959 { 18960 Jim_Obj *prefixListObj; 18961 18962 if (argc < 3) { 18963 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?"); 18964 return JIM_ERR; 18965 } 18966 18967 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2); 18968 Jim_IncrRefCount(prefixListObj); 18969 Jim_SetResult(interp, argv[1]); 18970 18971 return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete); 18972 } 18973 18974 18975 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 18976 { 18977 Jim_Cmd *cmd; 18978 18979 if (argc != 4 && argc != 5) { 18980 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body"); 18981 return JIM_ERR; 18982 } 18983 18984 if (argc == 4) { 18985 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL); 18986 } 18987 else { 18988 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL); 18989 } 18990 18991 if (cmd) { 18992 18993 Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]); 18994 JimCreateCommand(interp, nameObjPtr, cmd); 18995 18996 18997 JimUpdateProcNamespace(interp, cmd, nameObjPtr); 18998 Jim_DecrRefCount(interp, nameObjPtr); 18999 19000 19001 Jim_SetResult(interp, argv[1]); 19002 return JIM_OK; 19003 } 19004 return JIM_ERR; 19005 } 19006 19007 19008 static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19009 { 19010 if (argc != 2) { 19011 Jim_WrongNumArgs(interp, 1, argv, "callback"); 19012 return JIM_ERR; 19013 } 19014 19015 if (interp->traceCmdObj) { 19016 Jim_DecrRefCount(interp, interp->traceCmdObj); 19017 interp->traceCmdObj = NULL; 19018 } 19019 19020 if (Jim_Length(argv[1])) { 19021 19022 interp->traceCmdObj = argv[1]; 19023 Jim_IncrRefCount(interp->traceCmdObj); 19024 } 19025 return JIM_OK; 19026 } 19027 19028 19029 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19030 { 19031 int retcode; 19032 19033 if (argc < 2) { 19034 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?"); 19035 return JIM_ERR; 19036 } 19037 19038 19039 interp->local++; 19040 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); 19041 interp->local--; 19042 19043 19044 19045 if (retcode == 0) { 19046 Jim_Obj *cmdNameObj = Jim_GetResult(interp); 19047 19048 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) { 19049 return JIM_ERR; 19050 } 19051 if (interp->framePtr->localCommands == NULL) { 19052 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands)); 19053 Jim_InitStack(interp->framePtr->localCommands); 19054 } 19055 Jim_IncrRefCount(cmdNameObj); 19056 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj); 19057 } 19058 19059 return retcode; 19060 } 19061 19062 19063 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19064 { 19065 if (argc < 2) { 19066 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?"); 19067 return JIM_ERR; 19068 } 19069 else { 19070 int retcode; 19071 19072 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); 19073 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) { 19074 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]); 19075 return JIM_ERR; 19076 } 19077 19078 cmdPtr->u.proc.upcall++; 19079 JimIncrCmdRefCount(cmdPtr); 19080 19081 19082 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); 19083 19084 19085 cmdPtr->u.proc.upcall--; 19086 JimDecrCmdRefCount(interp, cmdPtr); 19087 19088 return retcode; 19089 } 19090 } 19091 19092 19093 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19094 { 19095 if (argc < 2) { 19096 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?"); 19097 return JIM_ERR; 19098 } 19099 else { 19100 int ret; 19101 Jim_Cmd *cmd; 19102 Jim_Obj *argListObjPtr; 19103 Jim_Obj *bodyObjPtr; 19104 Jim_Obj *nsObj = NULL; 19105 Jim_Obj **nargv; 19106 19107 int len = Jim_ListLength(interp, argv[1]); 19108 if (len != 2 && len != 3) { 19109 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]); 19110 return JIM_ERR; 19111 } 19112 19113 if (len == 3) { 19114 #ifdef jim_ext_namespace 19115 19116 nsObj = Jim_ListGetIndex(interp, argv[1], 2); 19117 #else 19118 Jim_SetResultString(interp, "namespaces not enabled", -1); 19119 return JIM_ERR; 19120 #endif 19121 } 19122 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0); 19123 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1); 19124 19125 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj); 19126 19127 if (cmd) { 19128 19129 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv)); 19130 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1); 19131 Jim_IncrRefCount(nargv[0]); 19132 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv)); 19133 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv); 19134 Jim_DecrRefCount(interp, nargv[0]); 19135 Jim_Free(nargv); 19136 19137 JimDecrCmdRefCount(interp, cmd); 19138 return ret; 19139 } 19140 return JIM_ERR; 19141 } 19142 } 19143 19144 19145 19146 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19147 { 19148 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); 19149 return JIM_OK; 19150 } 19151 19152 19153 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19154 { 19155 int i; 19156 Jim_CallFrame *targetCallFrame; 19157 19158 19159 if (argc > 3 && (argc % 2 == 0)) { 19160 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]); 19161 argc--; 19162 argv++; 19163 } 19164 else { 19165 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL); 19166 } 19167 if (targetCallFrame == NULL) { 19168 return JIM_ERR; 19169 } 19170 19171 19172 if (argc < 3) { 19173 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?"); 19174 return JIM_ERR; 19175 } 19176 19177 19178 for (i = 1; i < argc; i += 2) { 19179 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK) 19180 return JIM_ERR; 19181 } 19182 return JIM_OK; 19183 } 19184 19185 19186 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19187 { 19188 int i; 19189 19190 if (argc < 2) { 19191 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?"); 19192 return JIM_ERR; 19193 } 19194 19195 if (interp->framePtr->level == 0) 19196 return JIM_OK; 19197 for (i = 1; i < argc; i++) { 19198 19199 const char *name = Jim_String(argv[i]); 19200 if (name[0] != ':' || name[1] != ':') { 19201 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK) 19202 return JIM_ERR; 19203 } 19204 } 19205 return JIM_OK; 19206 } 19207 19208 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr, 19209 Jim_Obj *objPtr, int nocase) 19210 { 19211 int numMaps; 19212 const char *str, *noMatchStart = NULL; 19213 int strLen, i; 19214 Jim_Obj *resultObjPtr; 19215 19216 numMaps = Jim_ListLength(interp, mapListObjPtr); 19217 if (numMaps % 2) { 19218 Jim_SetResultString(interp, "list must contain an even number of elements", -1); 19219 return NULL; 19220 } 19221 19222 str = Jim_String(objPtr); 19223 strLen = Jim_Utf8Length(interp, objPtr); 19224 19225 19226 resultObjPtr = Jim_NewStringObj(interp, "", 0); 19227 while (strLen) { 19228 for (i = 0; i < numMaps; i += 2) { 19229 Jim_Obj *eachObjPtr; 19230 const char *k; 19231 int kl; 19232 19233 eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i); 19234 k = Jim_String(eachObjPtr); 19235 kl = Jim_Utf8Length(interp, eachObjPtr); 19236 19237 if (strLen >= kl && kl) { 19238 int rc; 19239 rc = JimStringCompareUtf8(str, kl, k, kl, nocase); 19240 if (rc == 0) { 19241 if (noMatchStart) { 19242 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); 19243 noMatchStart = NULL; 19244 } 19245 Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1)); 19246 str += utf8_index(str, kl); 19247 strLen -= kl; 19248 break; 19249 } 19250 } 19251 } 19252 if (i == numMaps) { 19253 int c; 19254 if (noMatchStart == NULL) 19255 noMatchStart = str; 19256 str += utf8_tounicode(str, &c); 19257 strLen--; 19258 } 19259 } 19260 if (noMatchStart) { 19261 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); 19262 } 19263 return resultObjPtr; 19264 } 19265 19266 19267 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19268 { 19269 int len; 19270 int opt_case = 1; 19271 int option; 19272 static const char * const nocase_options[] = { 19273 "-nocase", NULL 19274 }; 19275 static const char * const nocase_length_options[] = { 19276 "-nocase", "-length", NULL 19277 }; 19278 19279 enum { 19280 OPT_BYTELENGTH, 19281 OPT_BYTERANGE, 19282 OPT_CAT, 19283 OPT_COMPARE, 19284 OPT_EQUAL, 19285 OPT_FIRST, 19286 OPT_INDEX, 19287 OPT_IS, 19288 OPT_LAST, 19289 OPT_LENGTH, 19290 OPT_MAP, 19291 OPT_MATCH, 19292 OPT_RANGE, 19293 OPT_REPEAT, 19294 OPT_REPLACE, 19295 OPT_REVERSE, 19296 OPT_TOLOWER, 19297 OPT_TOTITLE, 19298 OPT_TOUPPER, 19299 OPT_TRIM, 19300 OPT_TRIMLEFT, 19301 OPT_TRIMRIGHT, 19302 OPT_COUNT 19303 }; 19304 static const jim_subcmd_type cmds[OPT_COUNT + 1] = { 19305 JIM_DEF_SUBCMD("bytelength", "string", 1, 1), 19306 JIM_DEF_SUBCMD("byterange", "string first last", 3, 3), 19307 JIM_DEF_SUBCMD("cat", "?...?", 0, -1), 19308 JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5), 19309 JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5), 19310 JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3), 19311 JIM_DEF_SUBCMD("index", "string index", 2, 2), 19312 JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3), 19313 JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3), 19314 JIM_DEF_SUBCMD("length","string", 1, 1), 19315 JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3), 19316 JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3), 19317 JIM_DEF_SUBCMD("range", "string first last", 3, 3), 19318 JIM_DEF_SUBCMD("repeat", "string count", 2, 2), 19319 JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4), 19320 JIM_DEF_SUBCMD("reverse", "string", 1, 1), 19321 JIM_DEF_SUBCMD("tolower", "string", 1, 1), 19322 JIM_DEF_SUBCMD("totitle", "string", 1, 1), 19323 JIM_DEF_SUBCMD("toupper", "string", 1, 1), 19324 JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2), 19325 JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2), 19326 JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2), 19327 { NULL } 19328 }; 19329 const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv); 19330 if (!ct) { 19331 return JIM_ERR; 19332 } 19333 if (ct->function) { 19334 19335 return ct->function(interp, argc, argv); 19336 } 19337 19338 option = ct - cmds; 19339 19340 switch (option) { 19341 case OPT_LENGTH: 19342 Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2])); 19343 return JIM_OK; 19344 19345 case OPT_BYTELENGTH: 19346 Jim_SetResultInt(interp, Jim_Length(argv[2])); 19347 return JIM_OK; 19348 19349 case OPT_CAT:{ 19350 Jim_Obj *objPtr; 19351 if (argc == 3) { 19352 19353 objPtr = argv[2]; 19354 } 19355 else { 19356 int i; 19357 19358 objPtr = Jim_NewStringObj(interp, "", 0); 19359 19360 for (i = 2; i < argc; i++) { 19361 Jim_AppendObj(interp, objPtr, argv[i]); 19362 } 19363 } 19364 Jim_SetResult(interp, objPtr); 19365 return JIM_OK; 19366 } 19367 19368 case OPT_COMPARE: 19369 case OPT_EQUAL: 19370 { 19371 19372 long opt_length = -1; 19373 int n = argc - 4; 19374 int i = 2; 19375 while (n > 0) { 19376 int subopt; 19377 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL, 19378 JIM_ENUM_ABBREV) != JIM_OK) { 19379 badcompareargs: 19380 Jim_SubCmdArgError(interp, ct, argv[0]); 19381 return JIM_ERR; 19382 } 19383 if (subopt == 0) { 19384 19385 opt_case = 0; 19386 n--; 19387 } 19388 else { 19389 19390 if (n < 2) { 19391 goto badcompareargs; 19392 } 19393 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) { 19394 return JIM_ERR; 19395 } 19396 n -= 2; 19397 } 19398 } 19399 if (n) { 19400 goto badcompareargs; 19401 } 19402 argv += argc - 2; 19403 if (opt_length < 0 && option != OPT_COMPARE && opt_case) { 19404 19405 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1])); 19406 } 19407 else { 19408 const char *s1 = Jim_String(argv[0]); 19409 int l1 = Jim_Utf8Length(interp, argv[0]); 19410 const char *s2 = Jim_String(argv[1]); 19411 int l2 = Jim_Utf8Length(interp, argv[1]); 19412 if (opt_length >= 0) { 19413 if (l1 > opt_length) { 19414 l1 = opt_length; 19415 } 19416 if (l2 > opt_length) { 19417 l2 = opt_length; 19418 } 19419 } 19420 n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case); 19421 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0); 19422 } 19423 return JIM_OK; 19424 } 19425 19426 case OPT_MATCH: 19427 if (argc != 4 && 19428 (argc != 5 || 19429 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL, 19430 JIM_ENUM_ABBREV) != JIM_OK)) { 19431 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string"); 19432 return JIM_ERR; 19433 } 19434 if (opt_case == 0) { 19435 argv++; 19436 } 19437 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case)); 19438 return JIM_OK; 19439 19440 case OPT_MAP:{ 19441 Jim_Obj *objPtr; 19442 19443 if (argc != 4 && 19444 (argc != 5 || 19445 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL, 19446 JIM_ENUM_ABBREV) != JIM_OK)) { 19447 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string"); 19448 return JIM_ERR; 19449 } 19450 19451 if (opt_case == 0) { 19452 argv++; 19453 } 19454 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case); 19455 if (objPtr == NULL) { 19456 return JIM_ERR; 19457 } 19458 Jim_SetResult(interp, objPtr); 19459 return JIM_OK; 19460 } 19461 19462 case OPT_RANGE:{ 19463 Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]); 19464 if (objPtr == NULL) { 19465 return JIM_ERR; 19466 } 19467 Jim_SetResult(interp, objPtr); 19468 return JIM_OK; 19469 } 19470 19471 case OPT_BYTERANGE:{ 19472 Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]); 19473 if (objPtr == NULL) { 19474 return JIM_ERR; 19475 } 19476 Jim_SetResult(interp, objPtr); 19477 return JIM_OK; 19478 } 19479 19480 case OPT_REPLACE:{ 19481 Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL); 19482 if (objPtr == NULL) { 19483 return JIM_ERR; 19484 } 19485 Jim_SetResult(interp, objPtr); 19486 return JIM_OK; 19487 } 19488 19489 19490 case OPT_REPEAT:{ 19491 Jim_Obj *objPtr; 19492 jim_wide count; 19493 19494 if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) { 19495 return JIM_ERR; 19496 } 19497 objPtr = Jim_NewStringObj(interp, "", 0); 19498 if (count > 0) { 19499 while (count--) { 19500 Jim_AppendObj(interp, objPtr, argv[2]); 19501 } 19502 } 19503 Jim_SetResult(interp, objPtr); 19504 return JIM_OK; 19505 } 19506 19507 case OPT_REVERSE:{ 19508 char *buf, *p; 19509 const char *str; 19510 int i; 19511 19512 str = Jim_GetString(argv[2], &len); 19513 buf = Jim_Alloc(len + 1); 19514 assert(buf); 19515 p = buf + len; 19516 *p = 0; 19517 for (i = 0; i < len; ) { 19518 int c; 19519 int l = utf8_tounicode(str, &c); 19520 memcpy(p - l, str, l); 19521 p -= l; 19522 i += l; 19523 str += l; 19524 } 19525 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); 19526 return JIM_OK; 19527 } 19528 19529 case OPT_INDEX:{ 19530 int idx; 19531 const char *str; 19532 19533 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) { 19534 return JIM_ERR; 19535 } 19536 str = Jim_String(argv[2]); 19537 len = Jim_Utf8Length(interp, argv[2]); 19538 idx = JimRelToAbsIndex(len, idx); 19539 if (idx < 0 || idx >= len || str == NULL) { 19540 Jim_SetResultString(interp, "", 0); 19541 } 19542 else if (len == Jim_Length(argv[2])) { 19543 19544 Jim_SetResultString(interp, str + idx, 1); 19545 } 19546 else { 19547 int c; 19548 int i = utf8_index(str, idx); 19549 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c)); 19550 } 19551 return JIM_OK; 19552 } 19553 19554 case OPT_FIRST: 19555 case OPT_LAST:{ 19556 int idx = 0, l1, l2; 19557 const char *s1, *s2; 19558 19559 s1 = Jim_String(argv[2]); 19560 s2 = Jim_String(argv[3]); 19561 l1 = Jim_Utf8Length(interp, argv[2]); 19562 l2 = Jim_Utf8Length(interp, argv[3]); 19563 if (argc == 5) { 19564 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) { 19565 return JIM_ERR; 19566 } 19567 idx = JimRelToAbsIndex(l2, idx); 19568 if (idx < 0) { 19569 idx = 0; 19570 } 19571 } 19572 else if (option == OPT_LAST) { 19573 idx = l2; 19574 } 19575 if (option == OPT_FIRST) { 19576 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx)); 19577 } 19578 else { 19579 #ifdef JIM_UTF8 19580 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx)); 19581 #else 19582 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx)); 19583 #endif 19584 } 19585 return JIM_OK; 19586 } 19587 19588 case OPT_TRIM: 19589 Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL)); 19590 return JIM_OK; 19591 case OPT_TRIMLEFT: 19592 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL)); 19593 return JIM_OK; 19594 case OPT_TRIMRIGHT:{ 19595 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL)); 19596 return JIM_OK; 19597 } 19598 19599 case OPT_TOLOWER: 19600 Jim_SetResult(interp, JimStringToLower(interp, argv[2])); 19601 return JIM_OK; 19602 case OPT_TOUPPER: 19603 Jim_SetResult(interp, JimStringToUpper(interp, argv[2])); 19604 return JIM_OK; 19605 case OPT_TOTITLE: 19606 Jim_SetResult(interp, JimStringToTitle(interp, argv[2])); 19607 return JIM_OK; 19608 19609 case OPT_IS: 19610 if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) { 19611 Jim_SubCmdArgError(interp, ct, argv[0]); 19612 return JIM_ERR; 19613 } 19614 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5); 19615 } 19616 return JIM_OK; 19617 } 19618 19619 19620 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19621 { 19622 long i, count = 1; 19623 jim_wide start, elapsed; 19624 19625 if (argc < 2) { 19626 Jim_WrongNumArgs(interp, 1, argv, "script ?count?"); 19627 return JIM_ERR; 19628 } 19629 if (argc == 3) { 19630 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK) 19631 return JIM_ERR; 19632 } 19633 if (count < 0) 19634 return JIM_OK; 19635 i = count; 19636 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 19637 while (i-- > 0) { 19638 int retval; 19639 19640 retval = Jim_EvalObj(interp, argv[1]); 19641 if (retval != JIM_OK) { 19642 return retval; 19643 } 19644 } 19645 elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start; 19646 if (elapsed < count * 10) { 19647 Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count)); 19648 } 19649 else { 19650 Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count); 19651 } 19652 Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1); 19653 return JIM_OK; 19654 } 19655 19656 19657 static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19658 { 19659 long us = 0; 19660 jim_wide start, delta, overhead; 19661 Jim_Obj *objPtr; 19662 double us_per_iter; 19663 int count; 19664 int n; 19665 19666 if (argc < 2) { 19667 Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?"); 19668 return JIM_ERR; 19669 } 19670 if (argc == 3) { 19671 if (Jim_GetLong(interp, argv[2], &us) != JIM_OK) 19672 return JIM_ERR; 19673 us *= 1000; 19674 } 19675 if (us < 1) { 19676 19677 us = 1000 * 1000; 19678 } 19679 19680 19681 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 19682 count = 0; 19683 do { 19684 int retval = Jim_EvalObj(interp, argv[1]); 19685 delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start; 19686 if (retval != JIM_OK) { 19687 return retval; 19688 } 19689 count++; 19690 } while (delta < us); 19691 19692 19693 start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW); 19694 n = 0; 19695 do { 19696 int retval = Jim_EvalObj(interp, interp->nullScriptObj); 19697 overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start; 19698 if (retval != JIM_OK) { 19699 return retval; 19700 } 19701 n++; 19702 } while (n < count); 19703 19704 delta -= overhead; 19705 19706 us_per_iter = (double)delta / count; 19707 objPtr = Jim_NewListObj(interp, NULL, 0); 19708 19709 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1)); 19710 Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter)); 19711 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1)); 19712 Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter)); 19713 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1)); 19714 Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count)); 19715 Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1)); 19716 Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta)); 19717 Jim_SetResult(interp, objPtr); 19718 return JIM_OK; 19719 } 19720 19721 19722 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19723 { 19724 long exitCode = 0; 19725 19726 if (argc > 2) { 19727 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?"); 19728 return JIM_ERR; 19729 } 19730 if (argc == 2) { 19731 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK) 19732 return JIM_ERR; 19733 Jim_SetResult(interp, argv[1]); 19734 } 19735 interp->exitCode = exitCode; 19736 return JIM_EXIT; 19737 } 19738 19739 static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc) 19740 { 19741 int len = Jim_ListLength(interp, retcodeListObj); 19742 int i; 19743 for (i = 0; i < len; i++) { 19744 int returncode; 19745 if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) { 19746 return JIM_ERR; 19747 } 19748 if (rc == returncode) { 19749 return JIM_OK; 19750 } 19751 } 19752 return -1; 19753 } 19754 19755 19756 static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv) 19757 { 19758 static const char * const wrongargs_catchtry[2] = { 19759 "?-?no?code ... --? script ?resultVarName? ?optionVarName?", 19760 "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?" 19761 }; 19762 int exitCode = 0; 19763 int i; 19764 int sig = 0; 19765 int ok; 19766 Jim_Obj *finallyScriptObj = NULL; 19767 Jim_Obj *msgVarObj = NULL; 19768 Jim_Obj *optsVarObj = NULL; 19769 Jim_Obj *handlerScriptObj = NULL; 19770 Jim_Obj *errorCodeObj; 19771 int idx; 19772 19773 19774 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL); 19775 static const int max_ignore_code = sizeof(ignore_mask) * 8; 19776 19777 JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper")); 19778 19779 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1)); 19780 19781 for (i = 1; i < argc - 1; i++) { 19782 const char *arg = Jim_String(argv[i]); 19783 jim_wide option; 19784 int ignore; 19785 19786 19787 if (strcmp(arg, "--") == 0) { 19788 i++; 19789 break; 19790 } 19791 if (*arg != '-') { 19792 break; 19793 } 19794 19795 if (strncmp(arg, "-no", 3) == 0) { 19796 arg += 3; 19797 ignore = 1; 19798 } 19799 else { 19800 arg++; 19801 ignore = 0; 19802 } 19803 19804 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) { 19805 option = -1; 19806 } 19807 if (option < 0) { 19808 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize); 19809 } 19810 if (option < 0) { 19811 goto wrongargs; 19812 } 19813 19814 if (ignore) { 19815 ignore_mask |= ((jim_wide)1 << option); 19816 } 19817 else { 19818 ignore_mask &= (~((jim_wide)1 << option)); 19819 } 19820 } 19821 19822 idx = i; 19823 19824 if (argc - idx < 1) { 19825 wrongargs: 19826 Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]); 19827 return JIM_ERR; 19828 } 19829 19830 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { 19831 sig++; 19832 } 19833 19834 interp->signal_level += sig; 19835 if (Jim_CheckSignal(interp)) { 19836 19837 exitCode = JIM_SIGNAL; 19838 } 19839 else { 19840 exitCode = Jim_EvalObj(interp, argv[idx]); 19841 19842 interp->errorFlag = 0; 19843 } 19844 interp->signal_level -= sig; 19845 19846 errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE); 19847 19848 idx++; 19849 if (istry) { 19850 while (idx < argc) { 19851 int option; 19852 int ret; 19853 static const char * const try_options[] = { "on", "trap", "finally", NULL }; 19854 enum { TRY_ON, TRY_TRAP, TRY_FINALLY, }; 19855 19856 if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) { 19857 return JIM_ERR; 19858 } 19859 switch (option) { 19860 case TRY_ON: 19861 case TRY_TRAP: 19862 if (idx + 4 > argc) { 19863 goto wrongargs; 19864 } 19865 if (option == TRY_ON) { 19866 ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode); 19867 if (ret > JIM_OK) { 19868 goto wrongargs; 19869 } 19870 } 19871 else if (errorCodeObj) { 19872 int len = Jim_ListLength(interp, argv[idx + 1]); 19873 int i; 19874 19875 ret = JIM_OK; 19876 19877 for (i = 0; i < len; i++) { 19878 Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i); 19879 Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i); 19880 if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) { 19881 ret = -1; 19882 break; 19883 } 19884 } 19885 } 19886 else { 19887 19888 ret = -1; 19889 } 19890 19891 if (ret == JIM_OK && handlerScriptObj == NULL) { 19892 msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0); 19893 optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1); 19894 handlerScriptObj = argv[idx + 3]; 19895 } 19896 idx += 4; 19897 break; 19898 case TRY_FINALLY: 19899 if (idx + 2 != argc) { 19900 goto wrongargs; 19901 } 19902 finallyScriptObj = argv[idx + 1]; 19903 idx += 2; 19904 break; 19905 } 19906 } 19907 } 19908 else { 19909 if (argc - idx >= 1) { 19910 msgVarObj = argv[idx]; 19911 idx++; 19912 if (argc - idx >= 1) { 19913 optsVarObj = argv[idx]; 19914 idx++; 19915 } 19916 } 19917 } 19918 19919 19920 if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) { 19921 19922 if (finallyScriptObj) { 19923 Jim_EvalObj(interp, finallyScriptObj); 19924 } 19925 return exitCode; 19926 } 19927 19928 if (sig && exitCode == JIM_SIGNAL) { 19929 19930 if (interp->signal_set_result) { 19931 interp->signal_set_result(interp, interp->sigmask); 19932 } 19933 else if (!istry) { 19934 Jim_SetResultInt(interp, interp->sigmask); 19935 } 19936 interp->sigmask = 0; 19937 } 19938 19939 ok = 1; 19940 if (msgVarObj && Jim_Length(msgVarObj)) { 19941 if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) { 19942 ok = 0; 19943 } 19944 } 19945 if (ok && optsVarObj && Jim_Length(optsVarObj)) { 19946 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0); 19947 19948 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1)); 19949 Jim_ListAppendElement(interp, optListObj, 19950 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode)); 19951 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1)); 19952 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel)); 19953 if (exitCode == JIM_ERR) { 19954 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo", 19955 -1)); 19956 Jim_ListAppendElement(interp, optListObj, interp->stackTrace); 19957 19958 if (errorCodeObj) { 19959 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1)); 19960 Jim_ListAppendElement(interp, optListObj, errorCodeObj); 19961 } 19962 } 19963 if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) { 19964 ok = 0; 19965 } 19966 } 19967 if (ok && handlerScriptObj) { 19968 19969 exitCode = Jim_EvalObj(interp, handlerScriptObj); 19970 } 19971 19972 if (finallyScriptObj) { 19973 19974 Jim_Obj *prevResultObj = Jim_GetResult(interp); 19975 Jim_IncrRefCount(prevResultObj); 19976 int ret = Jim_EvalObj(interp, finallyScriptObj); 19977 if (ret == JIM_OK) { 19978 Jim_SetResult(interp, prevResultObj); 19979 } 19980 else { 19981 exitCode = ret; 19982 } 19983 Jim_DecrRefCount(interp, prevResultObj); 19984 } 19985 if (!istry) { 19986 Jim_SetResultInt(interp, exitCode); 19987 exitCode = JIM_OK; 19988 } 19989 return exitCode; 19990 } 19991 19992 19993 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 19994 { 19995 return JimCatchTryHelper(interp, 0, argc, argv); 19996 } 19997 19998 19999 static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20000 { 20001 return JimCatchTryHelper(interp, 1, argc, argv); 20002 } 20003 20004 20005 20006 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20007 { 20008 if (argc != 3) { 20009 Jim_WrongNumArgs(interp, 1, argv, "oldName newName"); 20010 return JIM_ERR; 20011 } 20012 20013 return Jim_RenameCommand(interp, argv[1], argv[2]); 20014 } 20015 20016 #define JIM_DICTMATCH_KEYS 0x0001 20017 #define JIM_DICTMATCH_VALUES 0x002 20018 20019 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types) 20020 { 20021 Jim_Obj *listObjPtr; 20022 Jim_Dict *dict; 20023 int i; 20024 20025 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 20026 return JIM_ERR; 20027 } 20028 dict = objPtr->internalRep.dictValue; 20029 20030 listObjPtr = Jim_NewListObj(interp, NULL, 0); 20031 20032 for (i = 0; i < dict->len; i += 2 ) { 20033 Jim_Obj *keyObj = dict->table[i]; 20034 Jim_Obj *valObj = dict->table[i + 1]; 20035 if (patternObj) { 20036 Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj; 20037 if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) { 20038 20039 continue; 20040 } 20041 } 20042 if (return_types & JIM_DICTMATCH_KEYS) { 20043 Jim_ListAppendElement(interp, listObjPtr, keyObj); 20044 } 20045 if (return_types & JIM_DICTMATCH_VALUES) { 20046 Jim_ListAppendElement(interp, listObjPtr, valObj); 20047 } 20048 } 20049 20050 Jim_SetResult(interp, listObjPtr); 20051 return JIM_OK; 20052 } 20053 20054 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr) 20055 { 20056 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 20057 return -1; 20058 } 20059 return objPtr->internalRep.dictValue->len / 2; 20060 } 20061 20062 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv) 20063 { 20064 Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0); 20065 int i; 20066 20067 JimPanic((objc == 0, "Jim_DictMerge called with objc=0")); 20068 20069 20070 20071 for (i = 0; i < objc; i++) { 20072 Jim_Obj **table; 20073 int tablelen; 20074 int j; 20075 20076 table = Jim_DictPairs(interp, objv[i], &tablelen); 20077 if (tablelen && !table) { 20078 Jim_FreeNewObj(interp, objPtr); 20079 return NULL; 20080 } 20081 for (j = 0; j < tablelen; j += 2) { 20082 DictAddElement(interp, objPtr, table[j], table[j + 1]); 20083 } 20084 } 20085 return objPtr; 20086 } 20087 20088 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr) 20089 { 20090 char buffer[100]; 20091 Jim_Obj *output; 20092 Jim_Dict *dict; 20093 20094 if (SetDictFromAny(interp, objPtr) != JIM_OK) { 20095 return JIM_ERR; 20096 } 20097 20098 dict = objPtr->internalRep.dictValue; 20099 20100 20101 snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size); 20102 output = Jim_NewStringObj(interp, buffer, -1); 20103 Jim_SetResult(interp, output); 20104 return JIM_OK; 20105 } 20106 20107 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv) 20108 { 20109 Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1); 20110 20111 Jim_AppendString(interp, prefixObj, " ", 1); 20112 Jim_AppendString(interp, prefixObj, subcmd, -1); 20113 20114 return Jim_EvalObjPrefix(interp, prefixObj, argc, argv); 20115 } 20116 20117 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj) 20118 { 20119 int i; 20120 Jim_Obj *objPtr; 20121 Jim_Obj *dictObj; 20122 Jim_Obj **dictValues; 20123 int len; 20124 int ret = JIM_OK; 20125 20126 20127 dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG); 20128 if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) { 20129 return JIM_ERR; 20130 } 20131 20132 dictValues = Jim_DictPairs(interp, objPtr, &len); 20133 if (len && dictValues == NULL) { 20134 return JIM_ERR; 20135 } 20136 for (i = 0; i < len; i += 2) { 20137 if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) { 20138 return JIM_ERR; 20139 } 20140 } 20141 20142 20143 if (Jim_Length(scriptObj)) { 20144 ret = Jim_EvalObj(interp, scriptObj); 20145 20146 20147 if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) { 20148 20149 Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1)); 20150 for (i = 0; i < keyc; i++) { 20151 newkeyv[i] = keyv[i]; 20152 } 20153 20154 for (i = 0; i < len; i += 2) { 20155 20156 if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) { 20157 20158 objPtr = Jim_GetVariable(interp, dictValues[i], 0); 20159 newkeyv[keyc] = dictValues[i]; 20160 Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT); 20161 } 20162 } 20163 Jim_Free(newkeyv); 20164 } 20165 } 20166 20167 return ret; 20168 } 20169 20170 20171 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20172 { 20173 Jim_Obj *objPtr; 20174 int types = JIM_DICTMATCH_KEYS; 20175 20176 enum { 20177 OPT_CREATE, 20178 OPT_GET, 20179 OPT_GETDEF, 20180 OPT_GETWITHDEFAULT, 20181 OPT_SET, 20182 OPT_UNSET, 20183 OPT_EXISTS, 20184 OPT_KEYS, 20185 OPT_SIZE, 20186 OPT_INFO, 20187 OPT_MERGE, 20188 OPT_WITH, 20189 OPT_APPEND, 20190 OPT_LAPPEND, 20191 OPT_INCR, 20192 OPT_REMOVE, 20193 OPT_VALUES, 20194 OPT_FOR, 20195 OPT_REPLACE, 20196 OPT_UPDATE, 20197 OPT_COUNT 20198 }; 20199 static const jim_subcmd_type cmds[OPT_COUNT + 1] = { 20200 JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2), 20201 JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1), 20202 JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1), 20203 JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1), 20204 JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1), 20205 JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1), 20206 JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1), 20207 JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2), 20208 JIM_DEF_SUBCMD("size", "dictionary", 1, 1), 20209 JIM_DEF_SUBCMD("info", "dictionary", 1, 1), 20210 JIM_DEF_SUBCMD("merge", "?...?", 0, -1), 20211 JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1), 20212 JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1), 20213 JIM_DEF_SUBCMD("lappend", "varName key ?value ...?", 2, -1), 20214 JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3), 20215 JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1), 20216 JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2), 20217 JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3), 20218 JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1), 20219 JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1), 20220 { NULL } 20221 }; 20222 const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv); 20223 if (!ct) { 20224 return JIM_ERR; 20225 } 20226 if (ct->function) { 20227 20228 return ct->function(interp, argc, argv); 20229 } 20230 20231 20232 switch (ct - cmds) { 20233 case OPT_GET: 20234 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, 20235 JIM_ERRMSG) != JIM_OK) { 20236 return JIM_ERR; 20237 } 20238 Jim_SetResult(interp, objPtr); 20239 return JIM_OK; 20240 20241 case OPT_GETDEF: 20242 case OPT_GETWITHDEFAULT:{ 20243 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG); 20244 if (rc == -1) { 20245 20246 return JIM_ERR; 20247 } 20248 if (rc == JIM_ERR) { 20249 Jim_SetResult(interp, argv[argc - 1]); 20250 } 20251 else { 20252 Jim_SetResult(interp, objPtr); 20253 } 20254 return JIM_OK; 20255 } 20256 20257 case OPT_SET: 20258 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG); 20259 20260 case OPT_EXISTS:{ 20261 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE); 20262 if (rc < 0) { 20263 return JIM_ERR; 20264 } 20265 Jim_SetResultBool(interp, rc == JIM_OK); 20266 return JIM_OK; 20267 } 20268 20269 case OPT_UNSET: 20270 if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) { 20271 return JIM_ERR; 20272 } 20273 return JIM_OK; 20274 20275 case OPT_VALUES: 20276 types = JIM_DICTMATCH_VALUES; 20277 20278 case OPT_KEYS: 20279 return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types); 20280 20281 case OPT_SIZE: 20282 if (Jim_DictSize(interp, argv[2]) < 0) { 20283 return JIM_ERR; 20284 } 20285 Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2])); 20286 return JIM_OK; 20287 20288 case OPT_MERGE: 20289 if (argc == 2) { 20290 return JIM_OK; 20291 } 20292 objPtr = Jim_DictMerge(interp, argc - 2, argv + 2); 20293 if (objPtr == NULL) { 20294 return JIM_ERR; 20295 } 20296 Jim_SetResult(interp, objPtr); 20297 return JIM_OK; 20298 20299 case OPT_CREATE: 20300 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2); 20301 Jim_SetResult(interp, objPtr); 20302 return JIM_OK; 20303 20304 case OPT_INFO: 20305 return Jim_DictInfo(interp, argv[2]); 20306 20307 case OPT_WITH: 20308 return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]); 20309 20310 case OPT_UPDATE: 20311 if (argc < 6 || argc % 2) { 20312 20313 argc = 2; 20314 } 20315 20316 default: 20317 return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2); 20318 } 20319 } 20320 20321 20322 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20323 { 20324 static const char * const options[] = { 20325 "-nobackslashes", "-nocommands", "-novariables", NULL 20326 }; 20327 enum 20328 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES }; 20329 int i; 20330 int flags = JIM_SUBST_FLAG; 20331 Jim_Obj *objPtr; 20332 20333 if (argc < 2) { 20334 Jim_WrongNumArgs(interp, 1, argv, "?options? string"); 20335 return JIM_ERR; 20336 } 20337 for (i = 1; i < (argc - 1); i++) { 20338 int option; 20339 20340 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, 20341 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 20342 return JIM_ERR; 20343 } 20344 switch (option) { 20345 case OPT_NOBACKSLASHES: 20346 flags |= JIM_SUBST_NOESC; 20347 break; 20348 case OPT_NOCOMMANDS: 20349 flags |= JIM_SUBST_NOCMD; 20350 break; 20351 case OPT_NOVARIABLES: 20352 flags |= JIM_SUBST_NOVAR; 20353 break; 20354 } 20355 } 20356 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) { 20357 return JIM_ERR; 20358 } 20359 Jim_SetResult(interp, objPtr); 20360 return JIM_OK; 20361 } 20362 20363 #ifdef jim_ext_namespace 20364 static int JimIsGlobalNamespace(Jim_Obj *objPtr) 20365 { 20366 int len; 20367 const char *str = Jim_GetString(objPtr, &len); 20368 return len >= 2 && str[0] == ':' && str[1] == ':'; 20369 } 20370 #endif 20371 20372 20373 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20374 { 20375 Jim_Obj *objPtr; 20376 int mode = 0; 20377 20378 20379 enum { 20380 INFO_ALIAS, 20381 INFO_ARGS, 20382 INFO_BODY, 20383 INFO_CHANNELS, 20384 INFO_COMMANDS, 20385 INFO_COMPLETE, 20386 INFO_EXISTS, 20387 INFO_FRAME, 20388 INFO_GLOBALS, 20389 INFO_HOSTNAME, 20390 INFO_LEVEL, 20391 INFO_LOCALS, 20392 INFO_NAMEOFEXECUTABLE, 20393 INFO_PATCHLEVEL, 20394 INFO_PROCS, 20395 INFO_REFERENCES, 20396 INFO_RETURNCODES, 20397 INFO_SCRIPT, 20398 INFO_SOURCE, 20399 INFO_STACKTRACE, 20400 INFO_STATICS, 20401 INFO_VARS, 20402 INFO_VERSION, 20403 INFO_COUNT 20404 }; 20405 static const jim_subcmd_type cmds[INFO_COUNT + 1] = { 20406 JIM_DEF_SUBCMD("alias", "command", 1, 1), 20407 JIM_DEF_SUBCMD("args", "procname", 1, 1), 20408 JIM_DEF_SUBCMD("body", "procname", 1, 1), 20409 JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1), 20410 JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1), 20411 JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2), 20412 JIM_DEF_SUBCMD("exists", "varName", 1, 1), 20413 JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1), 20414 JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1), 20415 JIM_DEF_SUBCMD("hostname", NULL, 0, 0), 20416 JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1), 20417 JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1), 20418 JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0), 20419 JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0), 20420 JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1), 20421 JIM_DEF_SUBCMD("references", NULL, 0, 0), 20422 JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1), 20423 JIM_DEF_SUBCMD("script", "?filename?", 0, 1), 20424 JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3), 20425 JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0), 20426 JIM_DEF_SUBCMD("statics", "procname", 1, 1), 20427 JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1), 20428 JIM_DEF_SUBCMD("version", NULL, 0, 0), 20429 { NULL } 20430 }; 20431 const jim_subcmd_type *ct; 20432 #ifdef jim_ext_namespace 20433 int nons = 0; 20434 20435 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) { 20436 20437 argc--; 20438 argv++; 20439 nons = 1; 20440 } 20441 #endif 20442 ct = Jim_ParseSubCmd(interp, cmds, argc, argv); 20443 if (!ct) { 20444 return JIM_ERR; 20445 } 20446 if (ct->function) { 20447 20448 return ct->function(interp, argc, argv); 20449 } 20450 20451 int option = ct - cmds; 20452 20453 switch (option) { 20454 case INFO_EXISTS: 20455 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL); 20456 return JIM_OK; 20457 20458 case INFO_ALIAS:{ 20459 Jim_Cmd *cmdPtr; 20460 20461 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) { 20462 return JIM_ERR; 20463 } 20464 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) { 20465 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]); 20466 return JIM_ERR; 20467 } 20468 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData); 20469 return JIM_OK; 20470 } 20471 20472 case INFO_CHANNELS: 20473 mode++; 20474 #ifndef jim_ext_aio 20475 Jim_SetResultString(interp, "aio not enabled", -1); 20476 return JIM_ERR; 20477 #endif 20478 20479 case INFO_PROCS: 20480 mode++; 20481 20482 case INFO_COMMANDS: 20483 20484 #ifdef jim_ext_namespace 20485 if (!nons) { 20486 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) { 20487 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1); 20488 } 20489 } 20490 #endif 20491 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode)); 20492 return JIM_OK; 20493 20494 case INFO_VARS: 20495 mode++; 20496 20497 case INFO_LOCALS: 20498 mode++; 20499 20500 case INFO_GLOBALS: 20501 20502 #ifdef jim_ext_namespace 20503 if (!nons) { 20504 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) { 20505 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1); 20506 } 20507 } 20508 #endif 20509 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode)); 20510 return JIM_OK; 20511 20512 case INFO_SCRIPT: 20513 if (argc == 3) { 20514 Jim_IncrRefCount(argv[2]); 20515 Jim_DecrRefCount(interp, interp->currentFilenameObj); 20516 interp->currentFilenameObj = argv[2]; 20517 } 20518 Jim_SetResult(interp, interp->currentFilenameObj); 20519 return JIM_OK; 20520 20521 case INFO_SOURCE:{ 20522 Jim_Obj *resObjPtr; 20523 Jim_Obj *fileNameObj; 20524 20525 if (argc == 4) { 20526 Jim_SubCmdArgError(interp, ct, argv[0]); 20527 return JIM_ERR; 20528 } 20529 if (argc == 5) { 20530 jim_wide line; 20531 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) { 20532 return JIM_ERR; 20533 } 20534 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2])); 20535 Jim_SetSourceInfo(interp, resObjPtr, argv[3], line); 20536 } 20537 else { 20538 int line; 20539 fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line); 20540 resObjPtr = Jim_NewListObj(interp, NULL, 0); 20541 Jim_ListAppendElement(interp, resObjPtr, fileNameObj); 20542 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line)); 20543 } 20544 Jim_SetResult(interp, resObjPtr); 20545 return JIM_OK; 20546 } 20547 20548 case INFO_STACKTRACE: 20549 Jim_SetResult(interp, interp->stackTrace); 20550 return JIM_OK; 20551 20552 case INFO_LEVEL: 20553 if (argc == 2) { 20554 Jim_SetResultInt(interp, interp->framePtr->level); 20555 } 20556 else { 20557 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) { 20558 return JIM_ERR; 20559 } 20560 Jim_SetResult(interp, objPtr); 20561 } 20562 return JIM_OK; 20563 20564 case INFO_FRAME: 20565 if (argc == 2) { 20566 Jim_SetResultInt(interp, interp->procLevel + 1); 20567 } 20568 else { 20569 if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) { 20570 return JIM_ERR; 20571 } 20572 Jim_SetResult(interp, objPtr); 20573 } 20574 return JIM_OK; 20575 20576 case INFO_BODY: 20577 case INFO_STATICS: 20578 case INFO_ARGS:{ 20579 Jim_Cmd *cmdPtr; 20580 20581 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) { 20582 return JIM_ERR; 20583 } 20584 if (!cmdPtr->isproc) { 20585 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]); 20586 return JIM_ERR; 20587 } 20588 switch (option) { 20589 #ifdef JIM_NO_INTROSPECTION 20590 default: 20591 Jim_SetResultString(interp, "unsupported", -1); 20592 return JIM_ERR; 20593 #else 20594 case INFO_BODY: 20595 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr); 20596 break; 20597 case INFO_ARGS: 20598 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr); 20599 break; 20600 #endif 20601 case INFO_STATICS: 20602 if (cmdPtr->u.proc.staticVars) { 20603 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars, 20604 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES)); 20605 } 20606 break; 20607 } 20608 return JIM_OK; 20609 } 20610 20611 case INFO_VERSION: 20612 case INFO_PATCHLEVEL:{ 20613 char buf[(JIM_INTEGER_SPACE * 2) + 1]; 20614 20615 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100); 20616 Jim_SetResultString(interp, buf, -1); 20617 return JIM_OK; 20618 } 20619 20620 case INFO_COMPLETE: { 20621 char missing; 20622 20623 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing)); 20624 if (missing != ' ' && argc == 4) { 20625 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1)); 20626 } 20627 return JIM_OK; 20628 } 20629 20630 case INFO_HOSTNAME: 20631 20632 return Jim_Eval(interp, "os.gethostname"); 20633 20634 case INFO_NAMEOFEXECUTABLE: 20635 20636 return Jim_Eval(interp, "{info nameofexecutable}"); 20637 20638 case INFO_RETURNCODES: 20639 if (argc == 2) { 20640 int i; 20641 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); 20642 20643 for (i = 0; jimReturnCodes[i]; i++) { 20644 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i)); 20645 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, 20646 jimReturnCodes[i], -1)); 20647 } 20648 20649 Jim_SetResult(interp, listObjPtr); 20650 } 20651 else if (argc == 3) { 20652 long code; 20653 const char *name; 20654 20655 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) { 20656 return JIM_ERR; 20657 } 20658 name = Jim_ReturnCode(code); 20659 if (*name == '?') { 20660 Jim_SetResultInt(interp, code); 20661 } 20662 else { 20663 Jim_SetResultString(interp, name, -1); 20664 } 20665 } 20666 return JIM_OK; 20667 case INFO_REFERENCES: 20668 #ifdef JIM_REFERENCES 20669 return JimInfoReferences(interp, argc, argv); 20670 #else 20671 Jim_SetResultString(interp, "not supported", -1); 20672 return JIM_ERR; 20673 #endif 20674 default: 20675 abort(); 20676 } 20677 } 20678 20679 20680 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20681 { 20682 Jim_Obj *objPtr; 20683 int result = 0; 20684 20685 static const char * const options[] = { 20686 "-command", "-proc", "-alias", "-var", NULL 20687 }; 20688 enum 20689 { 20690 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR 20691 }; 20692 int option; 20693 20694 if (argc == 2) { 20695 option = OPT_VAR; 20696 objPtr = argv[1]; 20697 } 20698 else if (argc == 3) { 20699 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 20700 return JIM_ERR; 20701 } 20702 objPtr = argv[2]; 20703 } 20704 else { 20705 Jim_WrongNumArgs(interp, 1, argv, "?option? name"); 20706 return JIM_ERR; 20707 } 20708 20709 if (option == OPT_VAR) { 20710 result = Jim_GetVariable(interp, objPtr, 0) != NULL; 20711 } 20712 else { 20713 20714 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE); 20715 20716 if (cmd) { 20717 switch (option) { 20718 case OPT_COMMAND: 20719 result = 1; 20720 break; 20721 20722 case OPT_ALIAS: 20723 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd; 20724 break; 20725 20726 case OPT_PROC: 20727 result = cmd->isproc; 20728 break; 20729 } 20730 } 20731 } 20732 Jim_SetResultBool(interp, result); 20733 return JIM_OK; 20734 } 20735 20736 20737 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20738 { 20739 const char *str, *splitChars, *noMatchStart; 20740 int splitLen, strLen; 20741 Jim_Obj *resObjPtr; 20742 int c; 20743 int len; 20744 20745 if (argc != 2 && argc != 3) { 20746 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?"); 20747 return JIM_ERR; 20748 } 20749 20750 str = Jim_GetString(argv[1], &len); 20751 if (len == 0) { 20752 return JIM_OK; 20753 } 20754 strLen = Jim_Utf8Length(interp, argv[1]); 20755 20756 20757 if (argc == 2) { 20758 splitChars = " \n\t\r"; 20759 splitLen = 4; 20760 } 20761 else { 20762 splitChars = Jim_String(argv[2]); 20763 splitLen = Jim_Utf8Length(interp, argv[2]); 20764 } 20765 20766 noMatchStart = str; 20767 resObjPtr = Jim_NewListObj(interp, NULL, 0); 20768 20769 20770 if (splitLen) { 20771 Jim_Obj *objPtr; 20772 while (strLen--) { 20773 const char *sc = splitChars; 20774 int scLen = splitLen; 20775 int sl = utf8_tounicode(str, &c); 20776 while (scLen--) { 20777 int pc; 20778 sc += utf8_tounicode(sc, &pc); 20779 if (c == pc) { 20780 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart)); 20781 Jim_ListAppendElement(interp, resObjPtr, objPtr); 20782 noMatchStart = str + sl; 20783 break; 20784 } 20785 } 20786 str += sl; 20787 } 20788 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart)); 20789 Jim_ListAppendElement(interp, resObjPtr, objPtr); 20790 } 20791 else { 20792 Jim_Obj **commonObj = NULL; 20793 #define NUM_COMMON (128 - 9) 20794 while (strLen--) { 20795 int n = utf8_tounicode(str, &c); 20796 #ifdef JIM_OPTIMIZATION 20797 if (c >= 9 && c < 128) { 20798 20799 c -= 9; 20800 if (!commonObj) { 20801 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON); 20802 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON); 20803 } 20804 if (!commonObj[c]) { 20805 commonObj[c] = Jim_NewStringObj(interp, str, 1); 20806 } 20807 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]); 20808 str++; 20809 continue; 20810 } 20811 #endif 20812 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1)); 20813 str += n; 20814 } 20815 Jim_Free(commonObj); 20816 } 20817 20818 Jim_SetResult(interp, resObjPtr); 20819 return JIM_OK; 20820 } 20821 20822 20823 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20824 { 20825 const char *joinStr; 20826 int joinStrLen; 20827 20828 if (argc != 2 && argc != 3) { 20829 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?"); 20830 return JIM_ERR; 20831 } 20832 20833 if (argc == 2) { 20834 joinStr = " "; 20835 joinStrLen = 1; 20836 } 20837 else { 20838 joinStr = Jim_GetString(argv[2], &joinStrLen); 20839 } 20840 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen)); 20841 return JIM_OK; 20842 } 20843 20844 20845 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20846 { 20847 Jim_Obj *objPtr; 20848 20849 if (argc < 2) { 20850 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?"); 20851 return JIM_ERR; 20852 } 20853 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2); 20854 if (objPtr == NULL) 20855 return JIM_ERR; 20856 Jim_SetResult(interp, objPtr); 20857 return JIM_OK; 20858 } 20859 20860 20861 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20862 { 20863 Jim_Obj *listPtr, **outVec; 20864 int outc, i; 20865 20866 if (argc < 3) { 20867 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?"); 20868 return JIM_ERR; 20869 } 20870 if (argv[2]->typePtr != &scanFmtStringObjType) 20871 SetScanFmtFromAny(interp, argv[2]); 20872 if (FormatGetError(argv[2]) != 0) { 20873 Jim_SetResultString(interp, FormatGetError(argv[2]), -1); 20874 return JIM_ERR; 20875 } 20876 if (argc > 3) { 20877 int maxPos = FormatGetMaxPos(argv[2]); 20878 int count = FormatGetCnvCount(argv[2]); 20879 20880 if (maxPos > argc - 3) { 20881 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1); 20882 return JIM_ERR; 20883 } 20884 else if (count > argc - 3) { 20885 Jim_SetResultString(interp, "different numbers of variable names and " 20886 "field specifiers", -1); 20887 return JIM_ERR; 20888 } 20889 else if (count < argc - 3) { 20890 Jim_SetResultString(interp, "variable is not assigned by any " 20891 "conversion specifiers", -1); 20892 return JIM_ERR; 20893 } 20894 } 20895 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG); 20896 if (listPtr == 0) 20897 return JIM_ERR; 20898 if (argc > 3) { 20899 int rc = JIM_OK; 20900 int count = 0; 20901 20902 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) { 20903 int len = Jim_ListLength(interp, listPtr); 20904 20905 if (len != 0) { 20906 JimListGetElements(interp, listPtr, &outc, &outVec); 20907 for (i = 0; i < outc; ++i) { 20908 if (Jim_Length(outVec[i]) > 0) { 20909 ++count; 20910 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) { 20911 rc = JIM_ERR; 20912 } 20913 } 20914 } 20915 } 20916 Jim_FreeNewObj(interp, listPtr); 20917 } 20918 else { 20919 count = -1; 20920 } 20921 if (rc == JIM_OK) { 20922 Jim_SetResultInt(interp, count); 20923 } 20924 return rc; 20925 } 20926 else { 20927 if (listPtr == (Jim_Obj *)EOF) { 20928 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0)); 20929 return JIM_OK; 20930 } 20931 Jim_SetResult(interp, listPtr); 20932 } 20933 return JIM_OK; 20934 } 20935 20936 20937 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20938 { 20939 if (argc != 2 && argc != 3) { 20940 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?"); 20941 return JIM_ERR; 20942 } 20943 Jim_SetResult(interp, argv[1]); 20944 if (argc == 3) { 20945 JimSetStackTrace(interp, argv[2]); 20946 return JIM_ERR; 20947 } 20948 return JIM_ERR; 20949 } 20950 20951 20952 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20953 { 20954 Jim_Obj *objPtr; 20955 20956 if (argc != 4) { 20957 Jim_WrongNumArgs(interp, 1, argv, "list first last"); 20958 return JIM_ERR; 20959 } 20960 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL) 20961 return JIM_ERR; 20962 Jim_SetResult(interp, objPtr); 20963 return JIM_OK; 20964 } 20965 20966 20967 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 20968 { 20969 Jim_Obj *objPtr; 20970 jim_wide count; 20971 20972 if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) { 20973 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?"); 20974 return JIM_ERR; 20975 } 20976 if (count == 0 || argc == 2) { 20977 Jim_SetEmptyResult(interp); 20978 return JIM_OK; 20979 } 20980 20981 argc -= 2; 20982 argv += 2; 20983 20984 objPtr = Jim_NewListObj(interp, NULL, 0); 20985 ListEnsureLength(objPtr, argc * count); 20986 while (count--) { 20987 ListInsertElements(objPtr, -1, argc, argv); 20988 } 20989 20990 Jim_SetResult(interp, objPtr); 20991 return JIM_OK; 20992 } 20993 20994 char **Jim_GetEnviron(void) 20995 { 20996 #if defined(HAVE__NSGETENVIRON) 20997 return *_NSGetEnviron(); 20998 #elif defined(_environ) 20999 return _environ; 21000 #else 21001 #if !defined(NO_ENVIRON_EXTERN) 21002 extern char **environ; 21003 #endif 21004 return environ; 21005 #endif 21006 } 21007 21008 void Jim_SetEnviron(char **env) 21009 { 21010 #if defined(HAVE__NSGETENVIRON) 21011 *_NSGetEnviron() = env; 21012 #elif defined(_environ) 21013 _environ = env; 21014 #else 21015 #if !defined(NO_ENVIRON_EXTERN) 21016 extern char **environ; 21017 #endif 21018 21019 environ = env; 21020 #endif 21021 } 21022 21023 21024 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21025 { 21026 const char *key; 21027 const char *val; 21028 21029 if (argc == 1) { 21030 char **e = Jim_GetEnviron(); 21031 21032 int i; 21033 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); 21034 21035 for (i = 0; e[i]; i++) { 21036 const char *equals = strchr(e[i], '='); 21037 21038 if (equals) { 21039 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i], 21040 equals - e[i])); 21041 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1)); 21042 } 21043 } 21044 21045 Jim_SetResult(interp, listObjPtr); 21046 return JIM_OK; 21047 } 21048 21049 if (argc > 3) { 21050 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?"); 21051 return JIM_ERR; 21052 } 21053 key = Jim_String(argv[1]); 21054 val = getenv(key); 21055 if (val == NULL) { 21056 if (argc < 3) { 21057 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]); 21058 return JIM_ERR; 21059 } 21060 val = Jim_String(argv[2]); 21061 } 21062 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1)); 21063 return JIM_OK; 21064 } 21065 21066 21067 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21068 { 21069 int retval; 21070 21071 if (argc != 2) { 21072 Jim_WrongNumArgs(interp, 1, argv, "fileName"); 21073 return JIM_ERR; 21074 } 21075 retval = Jim_EvalFile(interp, Jim_String(argv[1])); 21076 if (retval == JIM_RETURN) 21077 return JIM_OK; 21078 return retval; 21079 } 21080 21081 21082 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21083 { 21084 Jim_Obj *revObjPtr, **ele; 21085 int len; 21086 21087 if (argc != 2) { 21088 Jim_WrongNumArgs(interp, 1, argv, "list"); 21089 return JIM_ERR; 21090 } 21091 JimListGetElements(interp, argv[1], &len, &ele); 21092 revObjPtr = Jim_NewListObj(interp, NULL, 0); 21093 ListEnsureLength(revObjPtr, len); 21094 len--; 21095 while (len >= 0) 21096 ListAppendElement(revObjPtr, ele[len--]); 21097 Jim_SetResult(interp, revObjPtr); 21098 return JIM_OK; 21099 } 21100 21101 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step) 21102 { 21103 jim_wide len; 21104 21105 if (step == 0) 21106 return -1; 21107 if (start == end) 21108 return 0; 21109 else if (step > 0 && start > end) 21110 return -1; 21111 else if (step < 0 && end > start) 21112 return -1; 21113 len = end - start; 21114 if (len < 0) 21115 len = -len; 21116 if (step < 0) 21117 step = -step; 21118 len = 1 + ((len - 1) / step); 21119 if (len > INT_MAX) 21120 len = INT_MAX; 21121 return (int)((len < 0) ? -1 : len); 21122 } 21123 21124 21125 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21126 { 21127 jim_wide start = 0, end, step = 1; 21128 int len, i; 21129 Jim_Obj *objPtr; 21130 21131 if (argc < 2 || argc > 4) { 21132 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?"); 21133 return JIM_ERR; 21134 } 21135 if (argc == 2) { 21136 if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK) 21137 return JIM_ERR; 21138 } 21139 else { 21140 if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK || 21141 Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK) 21142 return JIM_ERR; 21143 if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK) 21144 return JIM_ERR; 21145 } 21146 if ((len = JimRangeLen(start, end, step)) == -1) { 21147 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1); 21148 return JIM_ERR; 21149 } 21150 objPtr = Jim_NewListObj(interp, NULL, 0); 21151 ListEnsureLength(objPtr, len); 21152 for (i = 0; i < len; i++) 21153 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step)); 21154 Jim_SetResult(interp, objPtr); 21155 return JIM_OK; 21156 } 21157 21158 21159 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21160 { 21161 jim_wide min = 0, max = 0, len, maxMul; 21162 21163 if (argc < 1 || argc > 3) { 21164 Jim_WrongNumArgs(interp, 1, argv, "?min? max"); 21165 return JIM_ERR; 21166 } 21167 if (argc == 1) { 21168 max = JIM_WIDE_MAX; 21169 } else if (argc == 2) { 21170 if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK) 21171 return JIM_ERR; 21172 } else if (argc == 3) { 21173 if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK || 21174 Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK) 21175 return JIM_ERR; 21176 } 21177 len = max-min; 21178 if (len < 0) { 21179 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1); 21180 return JIM_ERR; 21181 } 21182 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0); 21183 while (1) { 21184 jim_wide r; 21185 21186 JimRandomBytes(interp, &r, sizeof(jim_wide)); 21187 if (r < 0 || r >= maxMul) continue; 21188 r = (len == 0) ? 0 : r%len; 21189 Jim_SetResultInt(interp, min+r); 21190 return JIM_OK; 21191 } 21192 } 21193 21194 static const struct { 21195 const char *name; 21196 Jim_CmdProc *cmdProc; 21197 } Jim_CoreCommandsTable[] = { 21198 {"alias", Jim_AliasCoreCommand}, 21199 {"set", Jim_SetCoreCommand}, 21200 {"unset", Jim_UnsetCoreCommand}, 21201 {"puts", Jim_PutsCoreCommand}, 21202 {"+", Jim_AddCoreCommand}, 21203 {"*", Jim_MulCoreCommand}, 21204 {"-", Jim_SubCoreCommand}, 21205 {"/", Jim_DivCoreCommand}, 21206 {"incr", Jim_IncrCoreCommand}, 21207 {"while", Jim_WhileCoreCommand}, 21208 {"loop", Jim_LoopCoreCommand}, 21209 {"for", Jim_ForCoreCommand}, 21210 {"foreach", Jim_ForeachCoreCommand}, 21211 {"lmap", Jim_LmapCoreCommand}, 21212 {"lassign", Jim_LassignCoreCommand}, 21213 {"if", Jim_IfCoreCommand}, 21214 {"switch", Jim_SwitchCoreCommand}, 21215 {"list", Jim_ListCoreCommand}, 21216 {"lindex", Jim_LindexCoreCommand}, 21217 {"lset", Jim_LsetCoreCommand}, 21218 {"lsearch", Jim_LsearchCoreCommand}, 21219 {"llength", Jim_LlengthCoreCommand}, 21220 {"lappend", Jim_LappendCoreCommand}, 21221 {"linsert", Jim_LinsertCoreCommand}, 21222 {"lreplace", Jim_LreplaceCoreCommand}, 21223 {"lsort", Jim_LsortCoreCommand}, 21224 {"append", Jim_AppendCoreCommand}, 21225 {"eval", Jim_EvalCoreCommand}, 21226 {"uplevel", Jim_UplevelCoreCommand}, 21227 {"expr", Jim_ExprCoreCommand}, 21228 {"break", Jim_BreakCoreCommand}, 21229 {"continue", Jim_ContinueCoreCommand}, 21230 {"proc", Jim_ProcCoreCommand}, 21231 {"xtrace", Jim_XtraceCoreCommand}, 21232 {"concat", Jim_ConcatCoreCommand}, 21233 {"return", Jim_ReturnCoreCommand}, 21234 {"upvar", Jim_UpvarCoreCommand}, 21235 {"global", Jim_GlobalCoreCommand}, 21236 {"string", Jim_StringCoreCommand}, 21237 {"time", Jim_TimeCoreCommand}, 21238 {"timerate", Jim_TimeRateCoreCommand}, 21239 {"exit", Jim_ExitCoreCommand}, 21240 {"catch", Jim_CatchCoreCommand}, 21241 {"try", Jim_TryCoreCommand}, 21242 #ifdef JIM_REFERENCES 21243 {"ref", Jim_RefCoreCommand}, 21244 {"getref", Jim_GetrefCoreCommand}, 21245 {"setref", Jim_SetrefCoreCommand}, 21246 {"finalize", Jim_FinalizeCoreCommand}, 21247 {"collect", Jim_CollectCoreCommand}, 21248 #endif 21249 {"rename", Jim_RenameCoreCommand}, 21250 {"dict", Jim_DictCoreCommand}, 21251 {"subst", Jim_SubstCoreCommand}, 21252 {"info", Jim_InfoCoreCommand}, 21253 {"exists", Jim_ExistsCoreCommand}, 21254 {"split", Jim_SplitCoreCommand}, 21255 {"join", Jim_JoinCoreCommand}, 21256 {"format", Jim_FormatCoreCommand}, 21257 {"scan", Jim_ScanCoreCommand}, 21258 {"error", Jim_ErrorCoreCommand}, 21259 {"lrange", Jim_LrangeCoreCommand}, 21260 {"lrepeat", Jim_LrepeatCoreCommand}, 21261 {"env", Jim_EnvCoreCommand}, 21262 {"source", Jim_SourceCoreCommand}, 21263 {"lreverse", Jim_LreverseCoreCommand}, 21264 {"range", Jim_RangeCoreCommand}, 21265 {"rand", Jim_RandCoreCommand}, 21266 {"tailcall", Jim_TailcallCoreCommand}, 21267 {"local", Jim_LocalCoreCommand}, 21268 {"upcall", Jim_UpcallCoreCommand}, 21269 {"apply", Jim_ApplyCoreCommand}, 21270 {"stacktrace", Jim_StacktraceCoreCommand}, 21271 {NULL, NULL}, 21272 }; 21273 21274 void Jim_RegisterCoreCommands(Jim_Interp *interp) 21275 { 21276 int i = 0; 21277 21278 while (Jim_CoreCommandsTable[i].name != NULL) { 21279 Jim_CreateCommand(interp, 21280 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL); 21281 i++; 21282 } 21283 } 21284 21285 void Jim_MakeErrorMessage(Jim_Interp *interp) 21286 { 21287 Jim_Obj *argv[2]; 21288 21289 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1); 21290 argv[1] = interp->result; 21291 21292 Jim_EvalObjVector(interp, 2, argv); 21293 } 21294 21295 static char **JimSortStringTable(const char *const *tablePtr) 21296 { 21297 int count; 21298 char **tablePtrSorted; 21299 21300 21301 for (count = 0; tablePtr[count]; count++) { 21302 } 21303 21304 21305 tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1)); 21306 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count); 21307 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers); 21308 tablePtrSorted[count] = NULL; 21309 21310 return tablePtrSorted; 21311 } 21312 21313 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, 21314 const char *prefix, const char *const *tablePtr, const char *name) 21315 { 21316 char **tablePtrSorted; 21317 int i; 21318 21319 if (name == NULL) { 21320 name = "option"; 21321 } 21322 21323 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg); 21324 tablePtrSorted = JimSortStringTable(tablePtr); 21325 for (i = 0; tablePtrSorted[i]; i++) { 21326 if (tablePtrSorted[i + 1] == NULL && i > 0) { 21327 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1); 21328 } 21329 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL); 21330 if (tablePtrSorted[i + 1]) { 21331 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1); 21332 } 21333 } 21334 Jim_Free(tablePtrSorted); 21335 } 21336 21337 21338 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr) 21339 { 21340 if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) { 21341 int i; 21342 char **tablePtrSorted = JimSortStringTable(tablePtr); 21343 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); 21344 for (i = 0; tablePtrSorted[i]; i++) { 21345 Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1)); 21346 } 21347 Jim_Free(tablePtrSorted); 21348 return JIM_OK; 21349 } 21350 return JIM_ERR; 21351 } 21352 21353 static const Jim_ObjType getEnumObjType = { 21354 "get-enum", 21355 NULL, 21356 NULL, 21357 NULL, 21358 JIM_TYPE_REFERENCES 21359 }; 21360 21361 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr, 21362 const char *const *tablePtr, int *indexPtr, const char *name, int flags) 21363 { 21364 const char *bad = "bad "; 21365 const char *const *entryPtr = NULL; 21366 int i; 21367 int match = -1; 21368 int arglen; 21369 const char *arg; 21370 21371 if (objPtr->typePtr == &getEnumObjType) { 21372 if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) { 21373 *indexPtr = objPtr->internalRep.ptrIntValue.int2; 21374 return JIM_OK; 21375 } 21376 } 21377 21378 arg = Jim_GetString(objPtr, &arglen); 21379 21380 *indexPtr = -1; 21381 21382 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) { 21383 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) { 21384 21385 match = i; 21386 goto found; 21387 } 21388 if (flags & JIM_ENUM_ABBREV) { 21389 if (strncmp(arg, *entryPtr, arglen) == 0) { 21390 if (*arg == '-' && arglen == 1) { 21391 break; 21392 } 21393 if (match >= 0) { 21394 bad = "ambiguous "; 21395 goto ambiguous; 21396 } 21397 match = i; 21398 } 21399 } 21400 } 21401 21402 21403 if (match >= 0) { 21404 found: 21405 21406 Jim_FreeIntRep(interp, objPtr); 21407 objPtr->typePtr = &getEnumObjType; 21408 objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr; 21409 objPtr->internalRep.ptrIntValue.int1 = flags; 21410 objPtr->internalRep.ptrIntValue.int2 = match; 21411 21412 *indexPtr = match; 21413 return JIM_OK; 21414 } 21415 21416 ambiguous: 21417 if (flags & JIM_ERRMSG) { 21418 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name); 21419 } 21420 return JIM_ERR; 21421 } 21422 21423 int Jim_FindByName(const char *name, const char * const array[], size_t len) 21424 { 21425 int i; 21426 21427 for (i = 0; i < (int)len; i++) { 21428 if (array[i] && strcmp(array[i], name) == 0) { 21429 return i; 21430 } 21431 } 21432 return -1; 21433 } 21434 21435 int Jim_IsDict(Jim_Obj *objPtr) 21436 { 21437 return objPtr->typePtr == &dictObjType; 21438 } 21439 21440 int Jim_IsList(Jim_Obj *objPtr) 21441 { 21442 return objPtr->typePtr == &listObjType; 21443 } 21444 21445 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...) 21446 { 21447 21448 int len = strlen(format); 21449 int extra = 0; 21450 int n = 0; 21451 const char *params[5]; 21452 int nobjparam = 0; 21453 Jim_Obj *objparam[5]; 21454 char *buf; 21455 va_list args; 21456 int i; 21457 21458 va_start(args, format); 21459 21460 for (i = 0; i < len && n < 5; i++) { 21461 int l; 21462 21463 if (strncmp(format + i, "%s", 2) == 0) { 21464 params[n] = va_arg(args, char *); 21465 21466 l = strlen(params[n]); 21467 } 21468 else if (strncmp(format + i, "%#s", 3) == 0) { 21469 Jim_Obj *objPtr = va_arg(args, Jim_Obj *); 21470 21471 params[n] = Jim_GetString(objPtr, &l); 21472 objparam[nobjparam++] = objPtr; 21473 Jim_IncrRefCount(objPtr); 21474 } 21475 else { 21476 if (format[i] == '%') { 21477 i++; 21478 } 21479 continue; 21480 } 21481 n++; 21482 extra += l; 21483 } 21484 21485 len += extra; 21486 buf = Jim_Alloc(len + 1); 21487 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]); 21488 21489 va_end(args); 21490 21491 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); 21492 21493 for (i = 0; i < nobjparam; i++) { 21494 Jim_DecrRefCount(interp, objparam[i]); 21495 } 21496 } 21497 21498 int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version) 21499 { 21500 if (abi_version != JIM_ABI_VERSION) { 21501 Jim_SetResultString(interp, "ABI version mismatch", -1); 21502 return JIM_ERR; 21503 } 21504 return JIM_OK; 21505 } 21506 21507 21508 #ifndef jim_ext_package 21509 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags) 21510 { 21511 return JIM_OK; 21512 } 21513 #endif 21514 #ifndef jim_ext_aio 21515 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj) 21516 { 21517 return -1; 21518 } 21519 #endif 21520 21521 21522 #include <stdio.h> 21523 #include <string.h> 21524 21525 21526 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21527 { 21528 21529 return JIM_OK; 21530 } 21531 21532 static const jim_subcmd_type dummy_subcmd = { 21533 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN 21534 }; 21535 21536 static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep) 21537 { 21538 21539 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 21540 Jim_Obj *sortCmd[2]; 21541 21542 for (; ct->cmd; ct++) { 21543 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { 21544 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1)); 21545 } 21546 } 21547 21548 21549 sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1); 21550 sortCmd[1] = listObj; 21551 21552 if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) { 21553 return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep)); 21554 } 21555 21556 return Jim_GetResult(interp); 21557 } 21558 21559 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type, 21560 Jim_Obj *cmd, Jim_Obj *subcmd) 21561 { 21562 Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type, 21563 subcmd, subcmd_cmd_list(interp, command_table, ", ")); 21564 } 21565 21566 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc, 21567 Jim_Obj *const *argv) 21568 { 21569 Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s", 21570 argv[0], subcmd_cmd_list(interp, command_table, ", ")); 21571 } 21572 21573 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd) 21574 { 21575 if (cmd) { 21576 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL); 21577 } 21578 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL); 21579 if (ct->args && *ct->args) { 21580 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL); 21581 } 21582 } 21583 21584 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd) 21585 { 21586 Jim_SetResultString(interp, "wrong # args: should be \"", -1); 21587 add_cmd_usage(interp, ct, subcmd); 21588 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); 21589 } 21590 21591 static const Jim_ObjType subcmdLookupObjType = { 21592 "subcmd-lookup", 21593 NULL, 21594 NULL, 21595 NULL, 21596 JIM_TYPE_REFERENCES 21597 }; 21598 21599 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table, 21600 int argc, Jim_Obj *const *argv) 21601 { 21602 const jim_subcmd_type *ct; 21603 const jim_subcmd_type *partial = 0; 21604 int cmdlen; 21605 Jim_Obj *cmd; 21606 const char *cmdstr; 21607 int help = 0; 21608 int argsok = 1; 21609 21610 if (argc < 2) { 21611 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n" 21612 "Use \"%#s -help ?command?\" for help", argv[0], argv[0]); 21613 return 0; 21614 } 21615 21616 cmd = argv[1]; 21617 21618 21619 if (cmd->typePtr == &subcmdLookupObjType) { 21620 if (cmd->internalRep.ptrIntValue.ptr == command_table) { 21621 ct = command_table + cmd->internalRep.ptrIntValue.int1; 21622 goto found; 21623 } 21624 } 21625 21626 21627 if (Jim_CompareStringImmediate(interp, cmd, "-help")) { 21628 if (argc == 2) { 21629 21630 show_cmd_usage(interp, command_table, argc, argv); 21631 return &dummy_subcmd; 21632 } 21633 help = 1; 21634 21635 21636 cmd = argv[2]; 21637 } 21638 21639 21640 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) { 21641 Jim_SetResult(interp, subcmd_cmd_list(interp, command_table, " ")); 21642 return &dummy_subcmd; 21643 } 21644 21645 cmdstr = Jim_GetString(cmd, &cmdlen); 21646 21647 for (ct = command_table; ct->cmd; ct++) { 21648 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) { 21649 21650 break; 21651 } 21652 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) { 21653 if (partial) { 21654 21655 if (help) { 21656 21657 show_cmd_usage(interp, command_table, argc, argv); 21658 return &dummy_subcmd; 21659 } 21660 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]); 21661 return 0; 21662 } 21663 partial = ct; 21664 } 21665 continue; 21666 } 21667 21668 21669 if (partial && !ct->cmd) { 21670 ct = partial; 21671 } 21672 21673 if (!ct->cmd) { 21674 21675 if (help) { 21676 21677 show_cmd_usage(interp, command_table, argc, argv); 21678 return &dummy_subcmd; 21679 } 21680 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]); 21681 return 0; 21682 } 21683 21684 if (help) { 21685 Jim_SetResultString(interp, "Usage: ", -1); 21686 21687 add_cmd_usage(interp, ct, argv[0]); 21688 return &dummy_subcmd; 21689 } 21690 21691 21692 Jim_FreeIntRep(interp, cmd); 21693 cmd->typePtr = &subcmdLookupObjType; 21694 cmd->internalRep.ptrIntValue.ptr = (void *)command_table; 21695 cmd->internalRep.ptrIntValue.int1 = ct - command_table; 21696 21697 found: 21698 21699 21700 if (argc - 2 < ct->minargs) { 21701 argsok = 0; 21702 } 21703 else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) { 21704 argsok = 0; 21705 } 21706 else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) { 21707 21708 argsok = 0; 21709 } 21710 if (!argsok) { 21711 Jim_SetResultString(interp, "wrong # args: should be \"", -1); 21712 21713 add_cmd_usage(interp, ct, argv[0]); 21714 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); 21715 21716 return 0; 21717 } 21718 21719 21720 return ct; 21721 } 21722 21723 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv) 21724 { 21725 int ret = JIM_ERR; 21726 21727 if (ct) { 21728 if (ct->flags & JIM_MODFLAG_FULLARGV) { 21729 ret = ct->function(interp, argc, argv); 21730 } 21731 else { 21732 ret = ct->function(interp, argc - 2, argv + 2); 21733 } 21734 if (ret < 0) { 21735 Jim_SubCmdArgError(interp, ct, argv[0]); 21736 ret = JIM_ERR; 21737 } 21738 } 21739 return ret; 21740 } 21741 21742 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 21743 { 21744 const jim_subcmd_type *ct = 21745 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv); 21746 21747 return Jim_CallSubCmd(interp, ct, argc, argv); 21748 } 21749 21750 #include <ctype.h> 21751 #include <stdlib.h> 21752 #include <string.h> 21753 #include <stdio.h> 21754 #include <assert.h> 21755 21756 21757 int utf8_fromunicode(char *p, unsigned uc) 21758 { 21759 if (uc <= 0x7f) { 21760 *p = uc; 21761 return 1; 21762 } 21763 else if (uc <= 0x7ff) { 21764 *p++ = 0xc0 | ((uc & 0x7c0) >> 6); 21765 *p = 0x80 | (uc & 0x3f); 21766 return 2; 21767 } 21768 else if (uc <= 0xffff) { 21769 *p++ = 0xe0 | ((uc & 0xf000) >> 12); 21770 *p++ = 0x80 | ((uc & 0xfc0) >> 6); 21771 *p = 0x80 | (uc & 0x3f); 21772 return 3; 21773 } 21774 21775 else { 21776 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18); 21777 *p++ = 0x80 | ((uc & 0x3f000) >> 12); 21778 *p++ = 0x80 | ((uc & 0xfc0) >> 6); 21779 *p = 0x80 | (uc & 0x3f); 21780 return 4; 21781 } 21782 } 21783 21784 #include <ctype.h> 21785 #include <string.h> 21786 #include <stdio.h> 21787 21788 21789 #define JIM_INTEGER_SPACE 24 21790 #define MAX_FLOAT_WIDTH 320 21791 21792 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv) 21793 { 21794 const char *span, *format, *formatEnd, *msg; 21795 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0; 21796 static const char * const mixedXPG = 21797 "cannot mix \"%\" and \"%n$\" conversion specifiers"; 21798 static const char * const badIndex[2] = { 21799 "not enough arguments for all format specifiers", 21800 "\"%n$\" argument index out of range" 21801 }; 21802 int formatLen; 21803 Jim_Obj *resultPtr; 21804 21805 char *num_buffer = NULL; 21806 int num_buffer_size = 0; 21807 21808 span = format = Jim_GetString(fmtObjPtr, &formatLen); 21809 formatEnd = format + formatLen; 21810 resultPtr = Jim_NewEmptyStringObj(interp); 21811 21812 while (format != formatEnd) { 21813 char *end; 21814 int gotMinus, sawFlag; 21815 int gotPrecision, useShort; 21816 long width, precision; 21817 int newXpg; 21818 int ch; 21819 int step; 21820 int doubleType; 21821 char pad = ' '; 21822 char spec[2*JIM_INTEGER_SPACE + 12]; 21823 char *p; 21824 21825 int formatted_chars; 21826 int formatted_bytes; 21827 const char *formatted_buf; 21828 21829 step = utf8_tounicode(format, &ch); 21830 format += step; 21831 if (ch != '%') { 21832 numBytes += step; 21833 continue; 21834 } 21835 if (numBytes) { 21836 Jim_AppendString(interp, resultPtr, span, numBytes); 21837 numBytes = 0; 21838 } 21839 21840 21841 step = utf8_tounicode(format, &ch); 21842 if (ch == '%') { 21843 span = format; 21844 numBytes = step; 21845 format += step; 21846 continue; 21847 } 21848 21849 21850 newXpg = 0; 21851 if (isdigit(ch)) { 21852 int position = strtoul(format, &end, 10); 21853 if (*end == '$') { 21854 newXpg = 1; 21855 objIndex = position - 1; 21856 format = end + 1; 21857 step = utf8_tounicode(format, &ch); 21858 } 21859 } 21860 if (newXpg) { 21861 if (gotSequential) { 21862 msg = mixedXPG; 21863 goto errorMsg; 21864 } 21865 gotXpg = 1; 21866 } else { 21867 if (gotXpg) { 21868 msg = mixedXPG; 21869 goto errorMsg; 21870 } 21871 gotSequential = 1; 21872 } 21873 if ((objIndex < 0) || (objIndex >= objc)) { 21874 msg = badIndex[gotXpg]; 21875 goto errorMsg; 21876 } 21877 21878 p = spec; 21879 *p++ = '%'; 21880 21881 gotMinus = 0; 21882 sawFlag = 1; 21883 do { 21884 switch (ch) { 21885 case '-': 21886 gotMinus = 1; 21887 break; 21888 case '0': 21889 pad = ch; 21890 break; 21891 case ' ': 21892 case '+': 21893 case '#': 21894 break; 21895 default: 21896 sawFlag = 0; 21897 continue; 21898 } 21899 *p++ = ch; 21900 format += step; 21901 step = utf8_tounicode(format, &ch); 21902 21903 } while (sawFlag && (p - spec <= 5)); 21904 21905 21906 width = 0; 21907 if (isdigit(ch)) { 21908 width = strtoul(format, &end, 10); 21909 format = end; 21910 step = utf8_tounicode(format, &ch); 21911 } else if (ch == '*') { 21912 if (objIndex >= objc - 1) { 21913 msg = badIndex[gotXpg]; 21914 goto errorMsg; 21915 } 21916 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) { 21917 goto error; 21918 } 21919 if (width < 0) { 21920 width = -width; 21921 if (!gotMinus) { 21922 *p++ = '-'; 21923 gotMinus = 1; 21924 } 21925 } 21926 objIndex++; 21927 format += step; 21928 step = utf8_tounicode(format, &ch); 21929 } 21930 21931 21932 gotPrecision = precision = 0; 21933 if (ch == '.') { 21934 gotPrecision = 1; 21935 format += step; 21936 step = utf8_tounicode(format, &ch); 21937 } 21938 if (isdigit(ch)) { 21939 precision = strtoul(format, &end, 10); 21940 format = end; 21941 step = utf8_tounicode(format, &ch); 21942 } else if (ch == '*') { 21943 if (objIndex >= objc - 1) { 21944 msg = badIndex[gotXpg]; 21945 goto errorMsg; 21946 } 21947 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) { 21948 goto error; 21949 } 21950 21951 21952 if (precision < 0) { 21953 precision = 0; 21954 } 21955 objIndex++; 21956 format += step; 21957 step = utf8_tounicode(format, &ch); 21958 } 21959 21960 21961 useShort = 0; 21962 if (ch == 'h') { 21963 useShort = 1; 21964 format += step; 21965 step = utf8_tounicode(format, &ch); 21966 } else if (ch == 'l') { 21967 21968 format += step; 21969 step = utf8_tounicode(format, &ch); 21970 if (ch == 'l') { 21971 format += step; 21972 step = utf8_tounicode(format, &ch); 21973 } 21974 } 21975 21976 format += step; 21977 span = format; 21978 21979 21980 if (ch == 'i') { 21981 ch = 'd'; 21982 } 21983 21984 doubleType = 0; 21985 21986 switch (ch) { 21987 case '\0': 21988 msg = "format string ended in middle of field specifier"; 21989 goto errorMsg; 21990 case 's': { 21991 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes); 21992 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]); 21993 if (gotPrecision && (precision < formatted_chars)) { 21994 21995 formatted_chars = precision; 21996 formatted_bytes = utf8_index(formatted_buf, precision); 21997 } 21998 break; 21999 } 22000 case 'c': { 22001 jim_wide code; 22002 22003 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) { 22004 goto error; 22005 } 22006 22007 formatted_bytes = utf8_getchars(spec, code); 22008 formatted_buf = spec; 22009 formatted_chars = 1; 22010 break; 22011 } 22012 case 'b': { 22013 unsigned jim_wide w; 22014 int length; 22015 int i; 22016 int j; 22017 22018 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) { 22019 goto error; 22020 } 22021 length = sizeof(w) * 8; 22022 22023 22024 22025 if (num_buffer_size < length + 1) { 22026 num_buffer_size = length + 1; 22027 num_buffer = Jim_Realloc(num_buffer, num_buffer_size); 22028 } 22029 22030 j = 0; 22031 for (i = length; i > 0; ) { 22032 i--; 22033 if (w & ((unsigned jim_wide)1 << i)) { 22034 num_buffer[j++] = '1'; 22035 } 22036 else if (j || i == 0) { 22037 num_buffer[j++] = '0'; 22038 } 22039 } 22040 num_buffer[j] = 0; 22041 formatted_chars = formatted_bytes = j; 22042 formatted_buf = num_buffer; 22043 break; 22044 } 22045 22046 case 'e': 22047 case 'E': 22048 case 'f': 22049 case 'g': 22050 case 'G': 22051 doubleType = 1; 22052 22053 case 'd': 22054 case 'u': 22055 case 'o': 22056 case 'x': 22057 case 'X': { 22058 jim_wide w; 22059 double d; 22060 int length; 22061 22062 22063 if (width) { 22064 p += sprintf(p, "%ld", width); 22065 } 22066 if (gotPrecision) { 22067 p += sprintf(p, ".%ld", precision); 22068 } 22069 22070 22071 if (doubleType) { 22072 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) { 22073 goto error; 22074 } 22075 length = MAX_FLOAT_WIDTH; 22076 } 22077 else { 22078 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) { 22079 goto error; 22080 } 22081 length = JIM_INTEGER_SPACE; 22082 if (useShort) { 22083 if (ch == 'd') { 22084 w = (short)w; 22085 } 22086 else { 22087 w = (unsigned short)w; 22088 } 22089 } 22090 *p++ = 'l'; 22091 #ifdef HAVE_LONG_LONG 22092 if (sizeof(long long) == sizeof(jim_wide)) { 22093 *p++ = 'l'; 22094 } 22095 #endif 22096 } 22097 22098 *p++ = (char) ch; 22099 *p = '\0'; 22100 22101 22102 if (width > 10000 || length > 10000 || precision > 10000) { 22103 Jim_SetResultString(interp, "format too long", -1); 22104 goto error; 22105 } 22106 22107 22108 22109 if (width > length) { 22110 length = width; 22111 } 22112 if (gotPrecision) { 22113 length += precision; 22114 } 22115 22116 22117 if (num_buffer_size < length + 1) { 22118 num_buffer_size = length + 1; 22119 num_buffer = Jim_Realloc(num_buffer, num_buffer_size); 22120 } 22121 22122 if (doubleType) { 22123 snprintf(num_buffer, length + 1, spec, d); 22124 } 22125 else { 22126 formatted_bytes = snprintf(num_buffer, length + 1, spec, w); 22127 } 22128 formatted_chars = formatted_bytes = strlen(num_buffer); 22129 formatted_buf = num_buffer; 22130 break; 22131 } 22132 22133 default: { 22134 22135 spec[0] = ch; 22136 spec[1] = '\0'; 22137 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec); 22138 goto error; 22139 } 22140 } 22141 22142 if (!gotMinus) { 22143 while (formatted_chars < width) { 22144 Jim_AppendString(interp, resultPtr, &pad, 1); 22145 formatted_chars++; 22146 } 22147 } 22148 22149 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes); 22150 22151 while (formatted_chars < width) { 22152 Jim_AppendString(interp, resultPtr, &pad, 1); 22153 formatted_chars++; 22154 } 22155 22156 objIndex += gotSequential; 22157 } 22158 if (numBytes) { 22159 Jim_AppendString(interp, resultPtr, span, numBytes); 22160 } 22161 22162 Jim_Free(num_buffer); 22163 return resultPtr; 22164 22165 errorMsg: 22166 Jim_SetResultString(interp, msg, -1); 22167 error: 22168 Jim_FreeNewObj(interp, resultPtr); 22169 Jim_Free(num_buffer); 22170 return NULL; 22171 } 22172 22173 22174 #if defined(JIM_REGEXP) 22175 #include <stdio.h> 22176 #include <ctype.h> 22177 #include <stdlib.h> 22178 #include <string.h> 22179 22180 22181 22182 #define REG_MAX_PAREN 100 22183 22184 22185 22186 #define END 0 22187 #define BOL 1 22188 #define EOL 2 22189 #define ANY 3 22190 #define ANYOF 4 22191 #define ANYBUT 5 22192 #define BRANCH 6 22193 #define BACK 7 22194 #define EXACTLY 8 22195 #define NOTHING 9 22196 #define REP 10 22197 #define REPMIN 11 22198 #define REPX 12 22199 #define REPXMIN 13 22200 #define BOLX 14 22201 #define EOLX 15 22202 #define WORDA 16 22203 #define WORDZ 17 22204 22205 #define OPENNC 1000 22206 #define OPEN 1001 22207 22208 22209 22210 22211 #define CLOSENC 2000 22212 #define CLOSE 2001 22213 #define CLOSE_END (CLOSE+REG_MAX_PAREN) 22214 22215 #define REG_MAGIC 0xFADED00D 22216 22217 22218 #define OP(preg, p) (preg->program[p]) 22219 #define NEXT(preg, p) (preg->program[p + 1]) 22220 #define OPERAND(p) ((p) + 2) 22221 22222 22223 22224 22225 #define FAIL(R,M) { (R)->err = (M); return (M); } 22226 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{') 22227 #define META "^$.[()|?{+*" 22228 22229 #define HASWIDTH 1 22230 #define SIMPLE 2 22231 #define SPSTART 4 22232 #define WORST 0 22233 22234 #define MAX_REP_COUNT 1000000 22235 22236 static int reg(regex_t *preg, int paren, int *flagp ); 22237 static int regpiece(regex_t *preg, int *flagp ); 22238 static int regbranch(regex_t *preg, int *flagp ); 22239 static int regatom(regex_t *preg, int *flagp ); 22240 static int regnode(regex_t *preg, int op ); 22241 static int regnext(regex_t *preg, int p ); 22242 static void regc(regex_t *preg, int b ); 22243 static int reginsert(regex_t *preg, int op, int size, int opnd ); 22244 static void regtail(regex_t *preg, int p, int val); 22245 static void regoptail(regex_t *preg, int p, int val ); 22246 static int regopsize(regex_t *preg, int p ); 22247 22248 static int reg_range_find(const int *string, int c); 22249 static const char *str_find(const char *string, int c, int nocase); 22250 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase); 22251 22252 22253 #ifdef DEBUG 22254 static int regnarrate = 0; 22255 static void regdump(regex_t *preg); 22256 static const char *regprop( int op ); 22257 #endif 22258 22259 22260 static int str_int_len(const int *seq) 22261 { 22262 int n = 0; 22263 while (*seq++) { 22264 n++; 22265 } 22266 return n; 22267 } 22268 22269 int jim_regcomp(regex_t *preg, const char *exp, int cflags) 22270 { 22271 int scan; 22272 int longest; 22273 unsigned len; 22274 int flags; 22275 22276 #ifdef DEBUG 22277 fprintf(stderr, "Compiling: '%s'\n", exp); 22278 #endif 22279 memset(preg, 0, sizeof(*preg)); 22280 22281 if (exp == NULL) 22282 FAIL(preg, REG_ERR_NULL_ARGUMENT); 22283 22284 22285 preg->cflags = cflags; 22286 preg->regparse = exp; 22287 22288 22289 preg->proglen = (strlen(exp) + 1) * 5; 22290 preg->program = malloc(preg->proglen * sizeof(int)); 22291 if (preg->program == NULL) 22292 FAIL(preg, REG_ERR_NOMEM); 22293 22294 regc(preg, REG_MAGIC); 22295 if (reg(preg, 0, &flags) == 0) { 22296 return preg->err; 22297 } 22298 22299 22300 if (preg->re_nsub >= REG_MAX_PAREN) 22301 FAIL(preg,REG_ERR_TOO_BIG); 22302 22303 22304 preg->regstart = 0; 22305 preg->reganch = 0; 22306 preg->regmust = 0; 22307 preg->regmlen = 0; 22308 scan = 1; 22309 if (OP(preg, regnext(preg, scan)) == END) { 22310 scan = OPERAND(scan); 22311 22312 22313 if (OP(preg, scan) == EXACTLY) { 22314 preg->regstart = preg->program[OPERAND(scan)]; 22315 } 22316 else if (OP(preg, scan) == BOL) 22317 preg->reganch++; 22318 22319 if (flags&SPSTART) { 22320 longest = 0; 22321 len = 0; 22322 for (; scan != 0; scan = regnext(preg, scan)) { 22323 if (OP(preg, scan) == EXACTLY) { 22324 int plen = str_int_len(preg->program + OPERAND(scan)); 22325 if (plen >= len) { 22326 longest = OPERAND(scan); 22327 len = plen; 22328 } 22329 } 22330 } 22331 preg->regmust = longest; 22332 preg->regmlen = len; 22333 } 22334 } 22335 22336 #ifdef DEBUG 22337 regdump(preg); 22338 #endif 22339 22340 return 0; 22341 } 22342 22343 static int reg(regex_t *preg, int paren, int *flagp ) 22344 { 22345 int ret; 22346 int br; 22347 int ender; 22348 int parno = 0; 22349 int flags; 22350 22351 *flagp = HASWIDTH; 22352 22353 22354 if (paren) { 22355 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') { 22356 22357 preg->regparse += 2; 22358 parno = -1; 22359 } 22360 else { 22361 parno = ++preg->re_nsub; 22362 } 22363 ret = regnode(preg, OPEN+parno); 22364 } else 22365 ret = 0; 22366 22367 22368 br = regbranch(preg, &flags); 22369 if (br == 0) 22370 return 0; 22371 if (ret != 0) 22372 regtail(preg, ret, br); 22373 else 22374 ret = br; 22375 if (!(flags&HASWIDTH)) 22376 *flagp &= ~HASWIDTH; 22377 *flagp |= flags&SPSTART; 22378 while (*preg->regparse == '|') { 22379 preg->regparse++; 22380 br = regbranch(preg, &flags); 22381 if (br == 0) 22382 return 0; 22383 regtail(preg, ret, br); 22384 if (!(flags&HASWIDTH)) 22385 *flagp &= ~HASWIDTH; 22386 *flagp |= flags&SPSTART; 22387 } 22388 22389 22390 ender = regnode(preg, (paren) ? CLOSE+parno : END); 22391 regtail(preg, ret, ender); 22392 22393 22394 for (br = ret; br != 0; br = regnext(preg, br)) 22395 regoptail(preg, br, ender); 22396 22397 22398 if (paren && *preg->regparse++ != ')') { 22399 preg->err = REG_ERR_UNMATCHED_PAREN; 22400 return 0; 22401 } else if (!paren && *preg->regparse != '\0') { 22402 if (*preg->regparse == ')') { 22403 preg->err = REG_ERR_UNMATCHED_PAREN; 22404 return 0; 22405 } else { 22406 preg->err = REG_ERR_JUNK_ON_END; 22407 return 0; 22408 } 22409 } 22410 22411 return(ret); 22412 } 22413 22414 static int regbranch(regex_t *preg, int *flagp ) 22415 { 22416 int ret; 22417 int chain; 22418 int latest; 22419 int flags; 22420 22421 *flagp = WORST; 22422 22423 ret = regnode(preg, BRANCH); 22424 chain = 0; 22425 while (*preg->regparse != '\0' && *preg->regparse != ')' && 22426 *preg->regparse != '|') { 22427 latest = regpiece(preg, &flags); 22428 if (latest == 0) 22429 return 0; 22430 *flagp |= flags&HASWIDTH; 22431 if (chain == 0) { 22432 *flagp |= flags&SPSTART; 22433 } 22434 else { 22435 regtail(preg, chain, latest); 22436 } 22437 chain = latest; 22438 } 22439 if (chain == 0) 22440 (void) regnode(preg, NOTHING); 22441 22442 return(ret); 22443 } 22444 22445 static int regpiece(regex_t *preg, int *flagp) 22446 { 22447 int ret; 22448 char op; 22449 int next; 22450 int flags; 22451 int min; 22452 int max; 22453 22454 ret = regatom(preg, &flags); 22455 if (ret == 0) 22456 return 0; 22457 22458 op = *preg->regparse; 22459 if (!ISMULT(op)) { 22460 *flagp = flags; 22461 return(ret); 22462 } 22463 22464 if (!(flags&HASWIDTH) && op != '?') { 22465 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY; 22466 return 0; 22467 } 22468 22469 22470 if (op == '{') { 22471 char *end; 22472 22473 min = strtoul(preg->regparse + 1, &end, 10); 22474 if (end == preg->regparse + 1) { 22475 preg->err = REG_ERR_BAD_COUNT; 22476 return 0; 22477 } 22478 if (*end == '}') { 22479 max = min; 22480 } 22481 else if (*end == '\0') { 22482 preg->err = REG_ERR_UNMATCHED_BRACES; 22483 return 0; 22484 } 22485 else { 22486 preg->regparse = end; 22487 max = strtoul(preg->regparse + 1, &end, 10); 22488 if (*end != '}') { 22489 preg->err = REG_ERR_UNMATCHED_BRACES; 22490 return 0; 22491 } 22492 } 22493 if (end == preg->regparse + 1) { 22494 max = MAX_REP_COUNT; 22495 } 22496 else if (max < min || max >= 100) { 22497 preg->err = REG_ERR_BAD_COUNT; 22498 return 0; 22499 } 22500 if (min >= 100) { 22501 preg->err = REG_ERR_BAD_COUNT; 22502 return 0; 22503 } 22504 22505 preg->regparse = strchr(preg->regparse, '}'); 22506 } 22507 else { 22508 min = (op == '+'); 22509 max = (op == '?' ? 1 : MAX_REP_COUNT); 22510 } 22511 22512 if (preg->regparse[1] == '?') { 22513 preg->regparse++; 22514 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret); 22515 } 22516 else { 22517 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret); 22518 } 22519 preg->program[ret + 2] = max; 22520 preg->program[ret + 3] = min; 22521 preg->program[ret + 4] = 0; 22522 22523 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART); 22524 22525 if (!(flags & SIMPLE)) { 22526 int back = regnode(preg, BACK); 22527 regtail(preg, back, ret); 22528 regtail(preg, next, back); 22529 } 22530 22531 preg->regparse++; 22532 if (ISMULT(*preg->regparse)) { 22533 preg->err = REG_ERR_NESTED_COUNT; 22534 return 0; 22535 } 22536 22537 return ret; 22538 } 22539 22540 static void reg_addrange(regex_t *preg, int lower, int upper) 22541 { 22542 if (lower > upper) { 22543 reg_addrange(preg, upper, lower); 22544 } 22545 22546 regc(preg, upper - lower + 1); 22547 regc(preg, lower); 22548 } 22549 22550 static void reg_addrange_str(regex_t *preg, const char *str) 22551 { 22552 while (*str) { 22553 reg_addrange(preg, *str, *str); 22554 str++; 22555 } 22556 } 22557 22558 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper) 22559 { 22560 int l = utf8_tounicode(s, uc); 22561 if (upper) { 22562 *uc = utf8_upper(*uc); 22563 } 22564 return l; 22565 } 22566 22567 static int hexdigitval(int c) 22568 { 22569 if (c >= '0' && c <= '9') 22570 return c - '0'; 22571 if (c >= 'a' && c <= 'f') 22572 return c - 'a' + 10; 22573 if (c >= 'A' && c <= 'F') 22574 return c - 'A' + 10; 22575 return -1; 22576 } 22577 22578 static int parse_hex(const char *s, int n, int *uc) 22579 { 22580 int val = 0; 22581 int k; 22582 22583 for (k = 0; k < n; k++) { 22584 int c = hexdigitval(*s++); 22585 if (c == -1) { 22586 break; 22587 } 22588 val = (val << 4) | c; 22589 } 22590 if (k) { 22591 *uc = val; 22592 } 22593 return k; 22594 } 22595 22596 static int reg_decode_escape(const char *s, int *ch) 22597 { 22598 int n; 22599 const char *s0 = s; 22600 22601 *ch = *s++; 22602 22603 switch (*ch) { 22604 case 'b': *ch = '\b'; break; 22605 case 'e': *ch = 27; break; 22606 case 'f': *ch = '\f'; break; 22607 case 'n': *ch = '\n'; break; 22608 case 'r': *ch = '\r'; break; 22609 case 't': *ch = '\t'; break; 22610 case 'v': *ch = '\v'; break; 22611 case 'u': 22612 if (*s == '{') { 22613 22614 n = parse_hex(s + 1, 6, ch); 22615 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) { 22616 s += n + 2; 22617 } 22618 else { 22619 22620 *ch = 'u'; 22621 } 22622 } 22623 else if ((n = parse_hex(s, 4, ch)) > 0) { 22624 s += n; 22625 } 22626 break; 22627 case 'U': 22628 if ((n = parse_hex(s, 8, ch)) > 0) { 22629 s += n; 22630 } 22631 break; 22632 case 'x': 22633 if ((n = parse_hex(s, 2, ch)) > 0) { 22634 s += n; 22635 } 22636 break; 22637 case '\0': 22638 s--; 22639 *ch = '\\'; 22640 break; 22641 } 22642 return s - s0; 22643 } 22644 22645 static int regatom(regex_t *preg, int *flagp) 22646 { 22647 int ret; 22648 int flags; 22649 int nocase = (preg->cflags & REG_ICASE); 22650 22651 int ch; 22652 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase); 22653 22654 *flagp = WORST; 22655 22656 preg->regparse += n; 22657 switch (ch) { 22658 22659 case '^': 22660 ret = regnode(preg, BOL); 22661 break; 22662 case '$': 22663 ret = regnode(preg, EOL); 22664 break; 22665 case '.': 22666 ret = regnode(preg, ANY); 22667 *flagp |= HASWIDTH|SIMPLE; 22668 break; 22669 case '[': { 22670 const char *pattern = preg->regparse; 22671 22672 if (*pattern == '^') { 22673 ret = regnode(preg, ANYBUT); 22674 pattern++; 22675 } else 22676 ret = regnode(preg, ANYOF); 22677 22678 22679 if (*pattern == ']' || *pattern == '-') { 22680 reg_addrange(preg, *pattern, *pattern); 22681 pattern++; 22682 } 22683 22684 while (*pattern != ']') { 22685 22686 int start; 22687 int end; 22688 22689 enum { 22690 CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER, 22691 CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT, 22692 CC_NUM 22693 }; 22694 int cc; 22695 22696 if (!*pattern) { 22697 preg->err = REG_ERR_UNMATCHED_BRACKET; 22698 return 0; 22699 } 22700 22701 pattern += reg_utf8_tounicode_case(pattern, &start, nocase); 22702 if (start == '\\') { 22703 22704 switch (*pattern) { 22705 case 's': 22706 pattern++; 22707 cc = CC_SPACE; 22708 goto cc_switch; 22709 case 'd': 22710 pattern++; 22711 cc = CC_DIGIT; 22712 goto cc_switch; 22713 case 'w': 22714 pattern++; 22715 reg_addrange(preg, '_', '_'); 22716 cc = CC_ALNUM; 22717 goto cc_switch; 22718 } 22719 pattern += reg_decode_escape(pattern, &start); 22720 if (start == 0) { 22721 preg->err = REG_ERR_NULL_CHAR; 22722 return 0; 22723 } 22724 if (start == '\\' && *pattern == 0) { 22725 preg->err = REG_ERR_INVALID_ESCAPE; 22726 return 0; 22727 } 22728 } 22729 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') { 22730 22731 pattern += utf8_tounicode(pattern, &end); 22732 pattern += reg_utf8_tounicode_case(pattern, &end, nocase); 22733 if (end == '\\') { 22734 pattern += reg_decode_escape(pattern, &end); 22735 if (end == 0) { 22736 preg->err = REG_ERR_NULL_CHAR; 22737 return 0; 22738 } 22739 if (end == '\\' && *pattern == 0) { 22740 preg->err = REG_ERR_INVALID_ESCAPE; 22741 return 0; 22742 } 22743 } 22744 22745 reg_addrange(preg, start, end); 22746 continue; 22747 } 22748 if (start == '[' && pattern[0] == ':') { 22749 static const char *character_class[] = { 22750 ":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:", 22751 ":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:", 22752 }; 22753 22754 for (cc = 0; cc < CC_NUM; cc++) { 22755 n = strlen(character_class[cc]); 22756 if (strncmp(pattern, character_class[cc], n) == 0) { 22757 if (pattern[n] != ']') { 22758 preg->err = REG_ERR_UNMATCHED_BRACKET; 22759 return 0; 22760 } 22761 22762 pattern += n + 1; 22763 break; 22764 } 22765 } 22766 if (cc != CC_NUM) { 22767 cc_switch: 22768 switch (cc) { 22769 case CC_ALNUM: 22770 reg_addrange(preg, '0', '9'); 22771 22772 case CC_ALPHA: 22773 if ((preg->cflags & REG_ICASE) == 0) { 22774 reg_addrange(preg, 'a', 'z'); 22775 } 22776 reg_addrange(preg, 'A', 'Z'); 22777 break; 22778 case CC_SPACE: 22779 reg_addrange_str(preg, " \t\r\n\f\v"); 22780 break; 22781 case CC_BLANK: 22782 reg_addrange_str(preg, " \t"); 22783 break; 22784 case CC_UPPER: 22785 reg_addrange(preg, 'A', 'Z'); 22786 break; 22787 case CC_LOWER: 22788 reg_addrange(preg, 'a', 'z'); 22789 break; 22790 case CC_XDIGIT: 22791 reg_addrange(preg, 'a', 'f'); 22792 reg_addrange(preg, 'A', 'F'); 22793 22794 case CC_DIGIT: 22795 reg_addrange(preg, '0', '9'); 22796 break; 22797 case CC_CNTRL: 22798 reg_addrange(preg, 0, 31); 22799 reg_addrange(preg, 127, 127); 22800 break; 22801 case CC_PRINT: 22802 reg_addrange(preg, ' ', '~'); 22803 break; 22804 case CC_GRAPH: 22805 reg_addrange(preg, '!', '~'); 22806 break; 22807 case CC_PUNCT: 22808 reg_addrange(preg, '!', '/'); 22809 reg_addrange(preg, ':', '@'); 22810 reg_addrange(preg, '[', '`'); 22811 reg_addrange(preg, '{', '~'); 22812 break; 22813 } 22814 continue; 22815 } 22816 } 22817 22818 reg_addrange(preg, start, start); 22819 } 22820 regc(preg, '\0'); 22821 22822 if (*pattern) { 22823 pattern++; 22824 } 22825 preg->regparse = pattern; 22826 22827 *flagp |= HASWIDTH|SIMPLE; 22828 } 22829 break; 22830 case '(': 22831 ret = reg(preg, 1, &flags); 22832 if (ret == 0) 22833 return 0; 22834 *flagp |= flags&(HASWIDTH|SPSTART); 22835 break; 22836 case '\0': 22837 case '|': 22838 case ')': 22839 preg->err = REG_ERR_INTERNAL; 22840 return 0; 22841 case '?': 22842 case '+': 22843 case '*': 22844 case '{': 22845 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING; 22846 return 0; 22847 case '\\': 22848 ch = *preg->regparse++; 22849 switch (ch) { 22850 case '\0': 22851 preg->err = REG_ERR_INVALID_ESCAPE; 22852 return 0; 22853 case 'A': 22854 ret = regnode(preg, BOLX); 22855 break; 22856 case 'Z': 22857 ret = regnode(preg, EOLX); 22858 break; 22859 case '<': 22860 case 'm': 22861 ret = regnode(preg, WORDA); 22862 break; 22863 case '>': 22864 case 'M': 22865 ret = regnode(preg, WORDZ); 22866 break; 22867 case 'd': 22868 case 'D': 22869 ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT); 22870 reg_addrange(preg, '0', '9'); 22871 regc(preg, '\0'); 22872 *flagp |= HASWIDTH|SIMPLE; 22873 break; 22874 case 'w': 22875 case 'W': 22876 ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT); 22877 if ((preg->cflags & REG_ICASE) == 0) { 22878 reg_addrange(preg, 'a', 'z'); 22879 } 22880 reg_addrange(preg, 'A', 'Z'); 22881 reg_addrange(preg, '0', '9'); 22882 reg_addrange(preg, '_', '_'); 22883 regc(preg, '\0'); 22884 *flagp |= HASWIDTH|SIMPLE; 22885 break; 22886 case 's': 22887 case 'S': 22888 ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT); 22889 reg_addrange_str(preg," \t\r\n\f\v"); 22890 regc(preg, '\0'); 22891 *flagp |= HASWIDTH|SIMPLE; 22892 break; 22893 22894 default: 22895 22896 22897 preg->regparse--; 22898 goto de_fault; 22899 } 22900 break; 22901 de_fault: 22902 default: { 22903 int added = 0; 22904 22905 22906 preg->regparse -= n; 22907 22908 ret = regnode(preg, EXACTLY); 22909 22910 22911 22912 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) { 22913 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE)); 22914 if (ch == '\\' && preg->regparse[n]) { 22915 if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) { 22916 22917 break; 22918 } 22919 n += reg_decode_escape(preg->regparse + n, &ch); 22920 if (ch == 0) { 22921 preg->err = REG_ERR_NULL_CHAR; 22922 return 0; 22923 } 22924 } 22925 22926 22927 if (ISMULT(preg->regparse[n])) { 22928 22929 if (added) { 22930 22931 break; 22932 } 22933 22934 regc(preg, ch); 22935 added++; 22936 preg->regparse += n; 22937 break; 22938 } 22939 22940 22941 regc(preg, ch); 22942 added++; 22943 preg->regparse += n; 22944 } 22945 regc(preg, '\0'); 22946 22947 *flagp |= HASWIDTH; 22948 if (added == 1) 22949 *flagp |= SIMPLE; 22950 break; 22951 } 22952 break; 22953 } 22954 22955 return(ret); 22956 } 22957 22958 static void reg_grow(regex_t *preg, int n) 22959 { 22960 if (preg->p + n >= preg->proglen) { 22961 preg->proglen = (preg->p + n) * 2; 22962 preg->program = realloc(preg->program, preg->proglen * sizeof(int)); 22963 } 22964 } 22965 22966 22967 static int regnode(regex_t *preg, int op) 22968 { 22969 reg_grow(preg, 2); 22970 22971 22972 preg->program[preg->p++] = op; 22973 preg->program[preg->p++] = 0; 22974 22975 22976 return preg->p - 2; 22977 } 22978 22979 static void regc(regex_t *preg, int b ) 22980 { 22981 reg_grow(preg, 1); 22982 preg->program[preg->p++] = b; 22983 } 22984 22985 static int reginsert(regex_t *preg, int op, int size, int opnd ) 22986 { 22987 reg_grow(preg, size); 22988 22989 22990 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd)); 22991 22992 memset(preg->program + opnd, 0, sizeof(int) * size); 22993 22994 preg->program[opnd] = op; 22995 22996 preg->p += size; 22997 22998 return opnd + size; 22999 } 23000 23001 static void regtail(regex_t *preg, int p, int val) 23002 { 23003 int scan; 23004 int temp; 23005 int offset; 23006 23007 23008 scan = p; 23009 for (;;) { 23010 temp = regnext(preg, scan); 23011 if (temp == 0) 23012 break; 23013 scan = temp; 23014 } 23015 23016 if (OP(preg, scan) == BACK) 23017 offset = scan - val; 23018 else 23019 offset = val - scan; 23020 23021 preg->program[scan + 1] = offset; 23022 } 23023 23024 23025 static void regoptail(regex_t *preg, int p, int val ) 23026 { 23027 23028 if (p != 0 && OP(preg, p) == BRANCH) { 23029 regtail(preg, OPERAND(p), val); 23030 } 23031 } 23032 23033 23034 static int regtry(regex_t *preg, const char *string ); 23035 static int regmatch(regex_t *preg, int prog); 23036 static int regrepeat(regex_t *preg, int p, int max); 23037 23038 int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) 23039 { 23040 const char *s; 23041 int scan; 23042 23043 23044 if (preg == NULL || preg->program == NULL || string == NULL) { 23045 return REG_ERR_NULL_ARGUMENT; 23046 } 23047 23048 23049 if (*preg->program != REG_MAGIC) { 23050 return REG_ERR_CORRUPTED; 23051 } 23052 23053 #ifdef DEBUG 23054 fprintf(stderr, "regexec: %s\n", string); 23055 regdump(preg); 23056 #endif 23057 23058 preg->eflags = eflags; 23059 preg->pmatch = pmatch; 23060 preg->nmatch = nmatch; 23061 preg->start = string; 23062 23063 23064 for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) { 23065 int op = OP(preg, scan); 23066 if (op == END) 23067 break; 23068 if (op == REPX || op == REPXMIN) 23069 preg->program[scan + 4] = 0; 23070 } 23071 23072 23073 if (preg->regmust != 0) { 23074 s = string; 23075 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) { 23076 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) { 23077 break; 23078 } 23079 s++; 23080 } 23081 if (s == NULL) 23082 return REG_NOMATCH; 23083 } 23084 23085 23086 preg->regbol = string; 23087 23088 23089 if (preg->reganch) { 23090 if (eflags & REG_NOTBOL) { 23091 23092 goto nextline; 23093 } 23094 while (1) { 23095 if (regtry(preg, string)) { 23096 return REG_NOERROR; 23097 } 23098 if (*string) { 23099 nextline: 23100 if (preg->cflags & REG_NEWLINE) { 23101 23102 string = strchr(string, '\n'); 23103 if (string) { 23104 preg->regbol = ++string; 23105 continue; 23106 } 23107 } 23108 } 23109 return REG_NOMATCH; 23110 } 23111 } 23112 23113 23114 s = string; 23115 if (preg->regstart != '\0') { 23116 23117 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) { 23118 if (regtry(preg, s)) 23119 return REG_NOERROR; 23120 s++; 23121 } 23122 } 23123 else 23124 23125 while (1) { 23126 if (regtry(preg, s)) 23127 return REG_NOERROR; 23128 if (*s == '\0') { 23129 break; 23130 } 23131 else { 23132 int c; 23133 s += utf8_tounicode(s, &c); 23134 } 23135 } 23136 23137 23138 return REG_NOMATCH; 23139 } 23140 23141 23142 static int regtry( regex_t *preg, const char *string ) 23143 { 23144 int i; 23145 23146 preg->reginput = string; 23147 23148 for (i = 0; i < preg->nmatch; i++) { 23149 preg->pmatch[i].rm_so = -1; 23150 preg->pmatch[i].rm_eo = -1; 23151 } 23152 if (regmatch(preg, 1)) { 23153 preg->pmatch[0].rm_so = string - preg->start; 23154 preg->pmatch[0].rm_eo = preg->reginput - preg->start; 23155 return(1); 23156 } else 23157 return(0); 23158 } 23159 23160 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase) 23161 { 23162 const char *s = string; 23163 while (proglen && *s) { 23164 int ch; 23165 int n = reg_utf8_tounicode_case(s, &ch, nocase); 23166 if (ch != *prog) { 23167 return -1; 23168 } 23169 prog++; 23170 s += n; 23171 proglen--; 23172 } 23173 if (proglen == 0) { 23174 return s - string; 23175 } 23176 return -1; 23177 } 23178 23179 static int reg_range_find(const int *range, int c) 23180 { 23181 while (*range) { 23182 23183 if (c >= range[1] && c <= (range[0] + range[1] - 1)) { 23184 return 1; 23185 } 23186 range += 2; 23187 } 23188 return 0; 23189 } 23190 23191 static const char *str_find(const char *string, int c, int nocase) 23192 { 23193 if (nocase) { 23194 23195 c = utf8_upper(c); 23196 } 23197 while (*string) { 23198 int ch; 23199 int n = reg_utf8_tounicode_case(string, &ch, nocase); 23200 if (c == ch) { 23201 return string; 23202 } 23203 string += n; 23204 } 23205 return NULL; 23206 } 23207 23208 static int reg_iseol(regex_t *preg, int ch) 23209 { 23210 if (preg->cflags & REG_NEWLINE) { 23211 return ch == '\0' || ch == '\n'; 23212 } 23213 else { 23214 return ch == '\0'; 23215 } 23216 } 23217 23218 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin) 23219 { 23220 int nextch = '\0'; 23221 const char *save; 23222 int no; 23223 int c; 23224 23225 int max = preg->program[scan + 2]; 23226 int min = preg->program[scan + 3]; 23227 int next = regnext(preg, scan); 23228 23229 if (OP(preg, next) == EXACTLY) { 23230 nextch = preg->program[OPERAND(next)]; 23231 } 23232 save = preg->reginput; 23233 no = regrepeat(preg, scan + 5, max); 23234 if (no < min) { 23235 return 0; 23236 } 23237 if (matchmin) { 23238 23239 max = no; 23240 no = min; 23241 } 23242 23243 while (1) { 23244 if (matchmin) { 23245 if (no > max) { 23246 break; 23247 } 23248 } 23249 else { 23250 if (no < min) { 23251 break; 23252 } 23253 } 23254 preg->reginput = save + utf8_index(save, no); 23255 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); 23256 23257 if (reg_iseol(preg, nextch) || c == nextch) { 23258 if (regmatch(preg, next)) { 23259 return(1); 23260 } 23261 } 23262 if (matchmin) { 23263 23264 no++; 23265 } 23266 else { 23267 23268 no--; 23269 } 23270 } 23271 return(0); 23272 } 23273 23274 static int regmatchrepeat(regex_t *preg, int scan, int matchmin) 23275 { 23276 int *scanpt = preg->program + scan; 23277 23278 int max = scanpt[2]; 23279 int min = scanpt[3]; 23280 23281 23282 if (scanpt[4] < min) { 23283 23284 scanpt[4]++; 23285 if (regmatch(preg, scan + 5)) { 23286 return 1; 23287 } 23288 scanpt[4]--; 23289 return 0; 23290 } 23291 if (scanpt[4] > max) { 23292 return 0; 23293 } 23294 23295 if (matchmin) { 23296 23297 if (regmatch(preg, regnext(preg, scan))) { 23298 return 1; 23299 } 23300 23301 scanpt[4]++; 23302 if (regmatch(preg, scan + 5)) { 23303 return 1; 23304 } 23305 scanpt[4]--; 23306 return 0; 23307 } 23308 23309 if (scanpt[4] < max) { 23310 scanpt[4]++; 23311 if (regmatch(preg, scan + 5)) { 23312 return 1; 23313 } 23314 scanpt[4]--; 23315 } 23316 23317 return regmatch(preg, regnext(preg, scan)); 23318 } 23319 23320 23321 static int regmatch(regex_t *preg, int prog) 23322 { 23323 int scan; 23324 int next; 23325 const char *save; 23326 23327 scan = prog; 23328 23329 #ifdef DEBUG 23330 if (scan != 0 && regnarrate) 23331 fprintf(stderr, "%s(\n", regprop(scan)); 23332 #endif 23333 while (scan != 0) { 23334 int n; 23335 int c; 23336 #ifdef DEBUG 23337 if (regnarrate) { 23338 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan))); 23339 } 23340 #endif 23341 next = regnext(preg, scan); 23342 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); 23343 23344 switch (OP(preg, scan)) { 23345 case BOLX: 23346 if ((preg->eflags & REG_NOTBOL)) { 23347 return(0); 23348 } 23349 23350 case BOL: 23351 if (preg->reginput != preg->regbol) { 23352 return(0); 23353 } 23354 break; 23355 case EOLX: 23356 if (c != 0) { 23357 23358 return 0; 23359 } 23360 break; 23361 case EOL: 23362 if (!reg_iseol(preg, c)) { 23363 return(0); 23364 } 23365 break; 23366 case WORDA: 23367 23368 if ((!isalnum(UCHAR(c))) && c != '_') 23369 return(0); 23370 23371 if (preg->reginput > preg->regbol && 23372 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_')) 23373 return(0); 23374 break; 23375 case WORDZ: 23376 23377 if (preg->reginput > preg->regbol) { 23378 23379 if (reg_iseol(preg, c) || !(isalnum(UCHAR(c)) || c == '_')) { 23380 c = preg->reginput[-1]; 23381 23382 if (isalnum(UCHAR(c)) || c == '_') { 23383 break; 23384 } 23385 } 23386 } 23387 23388 return(0); 23389 23390 case ANY: 23391 if (reg_iseol(preg, c)) 23392 return 0; 23393 preg->reginput += n; 23394 break; 23395 case EXACTLY: { 23396 int opnd; 23397 int len; 23398 int slen; 23399 23400 opnd = OPERAND(scan); 23401 len = str_int_len(preg->program + opnd); 23402 23403 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE); 23404 if (slen < 0) { 23405 return(0); 23406 } 23407 preg->reginput += slen; 23408 } 23409 break; 23410 case ANYOF: 23411 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) { 23412 return(0); 23413 } 23414 preg->reginput += n; 23415 break; 23416 case ANYBUT: 23417 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) { 23418 return(0); 23419 } 23420 preg->reginput += n; 23421 break; 23422 case NOTHING: 23423 break; 23424 case BACK: 23425 break; 23426 case BRANCH: 23427 if (OP(preg, next) != BRANCH) 23428 next = OPERAND(scan); 23429 else { 23430 do { 23431 save = preg->reginput; 23432 if (regmatch(preg, OPERAND(scan))) { 23433 return(1); 23434 } 23435 preg->reginput = save; 23436 scan = regnext(preg, scan); 23437 } while (scan != 0 && OP(preg, scan) == BRANCH); 23438 return(0); 23439 23440 } 23441 break; 23442 case REP: 23443 case REPMIN: 23444 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN); 23445 23446 case REPX: 23447 case REPXMIN: 23448 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN); 23449 23450 case END: 23451 return 1; 23452 23453 case OPENNC: 23454 case CLOSENC: 23455 return regmatch(preg, next); 23456 23457 default: 23458 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) { 23459 save = preg->reginput; 23460 if (regmatch(preg, next)) { 23461 if (OP(preg, scan) < CLOSE) { 23462 int no = OP(preg, scan) - OPEN; 23463 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) { 23464 preg->pmatch[no].rm_so = save - preg->start; 23465 } 23466 } 23467 else { 23468 int no = OP(preg, scan) - CLOSE; 23469 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) { 23470 preg->pmatch[no].rm_eo = save - preg->start; 23471 } 23472 } 23473 return(1); 23474 } 23475 23476 preg->reginput = save; 23477 return(0); 23478 } 23479 return REG_ERR_INTERNAL; 23480 } 23481 23482 scan = next; 23483 } 23484 23485 return REG_ERR_INTERNAL; 23486 } 23487 23488 static int regrepeat(regex_t *preg, int p, int max) 23489 { 23490 int count = 0; 23491 const char *scan; 23492 int opnd; 23493 int ch; 23494 int n; 23495 23496 scan = preg->reginput; 23497 opnd = OPERAND(p); 23498 switch (OP(preg, p)) { 23499 case ANY: 23500 while (!reg_iseol(preg, *scan) && count < max) { 23501 count++; 23502 scan += utf8_charlen(*scan); 23503 } 23504 break; 23505 case EXACTLY: 23506 while (count < max) { 23507 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); 23508 if (preg->program[opnd] != ch) { 23509 break; 23510 } 23511 count++; 23512 scan += n; 23513 } 23514 break; 23515 case ANYOF: 23516 while (count < max) { 23517 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); 23518 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) { 23519 break; 23520 } 23521 count++; 23522 scan += n; 23523 } 23524 break; 23525 case ANYBUT: 23526 while (count < max) { 23527 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); 23528 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) { 23529 break; 23530 } 23531 count++; 23532 scan += n; 23533 } 23534 break; 23535 default: 23536 preg->err = REG_ERR_INTERNAL; 23537 count = 0; 23538 break; 23539 } 23540 preg->reginput = scan; 23541 23542 return(count); 23543 } 23544 23545 static int regnext(regex_t *preg, int p ) 23546 { 23547 int offset; 23548 23549 offset = NEXT(preg, p); 23550 23551 if (offset == 0) 23552 return 0; 23553 23554 if (OP(preg, p) == BACK) 23555 return(p-offset); 23556 else 23557 return(p+offset); 23558 } 23559 23560 static int regopsize(regex_t *preg, int p ) 23561 { 23562 23563 switch (OP(preg, p)) { 23564 case REP: 23565 case REPMIN: 23566 case REPX: 23567 case REPXMIN: 23568 return 5; 23569 23570 case ANYOF: 23571 case ANYBUT: 23572 case EXACTLY: { 23573 int s = p + 2; 23574 while (preg->program[s++]) { 23575 } 23576 return s - p; 23577 } 23578 } 23579 return 2; 23580 } 23581 23582 23583 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) 23584 { 23585 static const char *error_strings[] = { 23586 "success", 23587 "no match", 23588 "bad pattern", 23589 "null argument", 23590 "unknown error", 23591 "too big", 23592 "out of memory", 23593 "too many ()", 23594 "parentheses () not balanced", 23595 "braces {} not balanced", 23596 "invalid repetition count(s)", 23597 "extra characters", 23598 "*+ of empty atom", 23599 "nested count", 23600 "internal error", 23601 "count follows nothing", 23602 "invalid escape \\ sequence", 23603 "corrupted program", 23604 "contains null char", 23605 "brackets [] not balanced", 23606 }; 23607 const char *err; 23608 23609 if (errcode < 0 || errcode >= REG_ERR_NUM) { 23610 err = "Bad error code"; 23611 } 23612 else { 23613 err = error_strings[errcode]; 23614 } 23615 23616 return snprintf(errbuf, errbuf_size, "%s", err); 23617 } 23618 23619 void jim_regfree(regex_t *preg) 23620 { 23621 free(preg->program); 23622 } 23623 23624 #endif 23625 #include <string.h> 23626 23627 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg) 23628 { 23629 Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno())); 23630 } 23631 23632 #if defined(_WIN32) || defined(WIN32) 23633 #include <sys/stat.h> 23634 23635 int Jim_Errno(void) 23636 { 23637 switch (GetLastError()) { 23638 case ERROR_FILE_NOT_FOUND: return ENOENT; 23639 case ERROR_PATH_NOT_FOUND: return ENOENT; 23640 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; 23641 case ERROR_ACCESS_DENIED: return EACCES; 23642 case ERROR_INVALID_HANDLE: return EBADF; 23643 case ERROR_BAD_ENVIRONMENT: return E2BIG; 23644 case ERROR_BAD_FORMAT: return ENOEXEC; 23645 case ERROR_INVALID_ACCESS: return EACCES; 23646 case ERROR_INVALID_DRIVE: return ENOENT; 23647 case ERROR_CURRENT_DIRECTORY: return EACCES; 23648 case ERROR_NOT_SAME_DEVICE: return EXDEV; 23649 case ERROR_NO_MORE_FILES: return ENOENT; 23650 case ERROR_WRITE_PROTECT: return EROFS; 23651 case ERROR_BAD_UNIT: return ENXIO; 23652 case ERROR_NOT_READY: return EBUSY; 23653 case ERROR_BAD_COMMAND: return EIO; 23654 case ERROR_CRC: return EIO; 23655 case ERROR_BAD_LENGTH: return EIO; 23656 case ERROR_SEEK: return EIO; 23657 case ERROR_WRITE_FAULT: return EIO; 23658 case ERROR_READ_FAULT: return EIO; 23659 case ERROR_GEN_FAILURE: return EIO; 23660 case ERROR_SHARING_VIOLATION: return EACCES; 23661 case ERROR_LOCK_VIOLATION: return EACCES; 23662 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE; 23663 case ERROR_HANDLE_DISK_FULL: return ENOSPC; 23664 case ERROR_NOT_SUPPORTED: return ENODEV; 23665 case ERROR_REM_NOT_LIST: return EBUSY; 23666 case ERROR_DUP_NAME: return EEXIST; 23667 case ERROR_BAD_NETPATH: return ENOENT; 23668 case ERROR_NETWORK_BUSY: return EBUSY; 23669 case ERROR_DEV_NOT_EXIST: return ENODEV; 23670 case ERROR_TOO_MANY_CMDS: return EAGAIN; 23671 case ERROR_ADAP_HDW_ERR: return EIO; 23672 case ERROR_BAD_NET_RESP: return EIO; 23673 case ERROR_UNEXP_NET_ERR: return EIO; 23674 case ERROR_NETNAME_DELETED: return ENOENT; 23675 case ERROR_NETWORK_ACCESS_DENIED: return EACCES; 23676 case ERROR_BAD_DEV_TYPE: return ENODEV; 23677 case ERROR_BAD_NET_NAME: return ENOENT; 23678 case ERROR_TOO_MANY_NAMES: return ENFILE; 23679 case ERROR_TOO_MANY_SESS: return EIO; 23680 case ERROR_SHARING_PAUSED: return EAGAIN; 23681 case ERROR_REDIR_PAUSED: return EAGAIN; 23682 case ERROR_FILE_EXISTS: return EEXIST; 23683 case ERROR_CANNOT_MAKE: return ENOSPC; 23684 case ERROR_OUT_OF_STRUCTURES: return ENFILE; 23685 case ERROR_ALREADY_ASSIGNED: return EEXIST; 23686 case ERROR_INVALID_PASSWORD: return EPERM; 23687 case ERROR_NET_WRITE_FAULT: return EIO; 23688 case ERROR_NO_PROC_SLOTS: return EAGAIN; 23689 case ERROR_DISK_CHANGE: return EXDEV; 23690 case ERROR_BROKEN_PIPE: return EPIPE; 23691 case ERROR_OPEN_FAILED: return ENOENT; 23692 case ERROR_DISK_FULL: return ENOSPC; 23693 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE; 23694 case ERROR_INVALID_TARGET_HANDLE: return EBADF; 23695 case ERROR_INVALID_NAME: return ENOENT; 23696 case ERROR_PROC_NOT_FOUND: return ESRCH; 23697 case ERROR_WAIT_NO_CHILDREN: return ECHILD; 23698 case ERROR_CHILD_NOT_COMPLETE: return ECHILD; 23699 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; 23700 case ERROR_SEEK_ON_DEVICE: return ESPIPE; 23701 case ERROR_BUSY_DRIVE: return EAGAIN; 23702 case ERROR_DIR_NOT_EMPTY: return EEXIST; 23703 case ERROR_NOT_LOCKED: return EACCES; 23704 case ERROR_BAD_PATHNAME: return ENOENT; 23705 case ERROR_LOCK_FAILED: return EACCES; 23706 case ERROR_ALREADY_EXISTS: return EEXIST; 23707 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; 23708 case ERROR_BAD_PIPE: return EPIPE; 23709 case ERROR_PIPE_BUSY: return EAGAIN; 23710 case ERROR_PIPE_NOT_CONNECTED: return EPIPE; 23711 case ERROR_DIRECTORY: return ENOTDIR; 23712 } 23713 return EINVAL; 23714 } 23715 23716 long JimProcessPid(phandle_t pid) 23717 { 23718 if (pid == INVALID_HANDLE_VALUE) { 23719 return -1; 23720 } 23721 return GetProcessId(pid); 23722 } 23723 23724 phandle_t JimWaitPid(long pid, int *status, int nohang) 23725 { 23726 if (pid > 0) { 23727 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid); 23728 if (h) { 23729 long pid = waitpid(h, status, nohang); 23730 CloseHandle(h); 23731 if (pid > 0) { 23732 return h; 23733 } 23734 } 23735 } 23736 return JIM_BAD_PHANDLE; 23737 } 23738 23739 long waitpid(phandle_t phandle, int *status, int nohang) 23740 { 23741 long pid; 23742 DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE); 23743 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) { 23744 23745 return -1; 23746 } 23747 GetExitCodeProcess(phandle, &ret); 23748 *status = ret; 23749 23750 pid = GetProcessId(phandle); 23751 CloseHandle(phandle); 23752 return pid; 23753 } 23754 23755 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file) 23756 { 23757 char name[MAX_PATH]; 23758 HANDLE handle; 23759 23760 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) { 23761 return -1; 23762 } 23763 23764 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, 23765 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0), 23766 NULL); 23767 23768 if (handle == INVALID_HANDLE_VALUE) { 23769 goto error; 23770 } 23771 23772 Jim_SetResultString(interp, name, -1); 23773 return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT); 23774 23775 error: 23776 Jim_SetResultErrno(interp, name); 23777 DeleteFile(name); 23778 return -1; 23779 } 23780 23781 int Jim_OpenForWrite(const char *filename, int append) 23782 { 23783 if (strcmp(filename, "/dev/null") == 0) { 23784 filename = "nul:"; 23785 } 23786 int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE); 23787 if (fd >= 0 && append) { 23788 23789 _lseek(fd, 0L, SEEK_END); 23790 } 23791 return fd; 23792 } 23793 23794 int Jim_OpenForRead(const char *filename) 23795 { 23796 if (strcmp(filename, "/dev/null") == 0) { 23797 filename = "nul:"; 23798 } 23799 return _open(filename, _O_RDONLY | _O_TEXT, 0); 23800 } 23801 23802 #elif defined(HAVE_UNISTD_H) 23803 23804 23805 23806 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file) 23807 { 23808 int fd; 23809 mode_t mask; 23810 Jim_Obj *filenameObj; 23811 23812 if (filename_template == NULL) { 23813 const char *tmpdir = getenv("TMPDIR"); 23814 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) { 23815 tmpdir = "/tmp/"; 23816 } 23817 filenameObj = Jim_NewStringObj(interp, tmpdir, -1); 23818 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') { 23819 Jim_AppendString(interp, filenameObj, "/", 1); 23820 } 23821 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1); 23822 } 23823 else { 23824 filenameObj = Jim_NewStringObj(interp, filename_template, -1); 23825 } 23826 23827 23828 #ifdef HAVE_UMASK 23829 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); 23830 #endif 23831 #ifdef HAVE_MKSTEMP 23832 fd = mkstemp(filenameObj->bytes); 23833 #else 23834 if (mktemp(filenameObj->bytes) == NULL) { 23835 fd = -1; 23836 } 23837 else { 23838 fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC); 23839 } 23840 #endif 23841 #ifdef HAVE_UMASK 23842 umask(mask); 23843 #endif 23844 if (fd < 0) { 23845 Jim_SetResultErrno(interp, Jim_String(filenameObj)); 23846 Jim_FreeNewObj(interp, filenameObj); 23847 return -1; 23848 } 23849 if (unlink_file) { 23850 remove(Jim_String(filenameObj)); 23851 } 23852 23853 Jim_SetResult(interp, filenameObj); 23854 return fd; 23855 } 23856 23857 int Jim_OpenForWrite(const char *filename, int append) 23858 { 23859 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666); 23860 } 23861 23862 int Jim_OpenForRead(const char *filename) 23863 { 23864 return open(filename, O_RDONLY, 0); 23865 } 23866 23867 #endif 23868 23869 #if defined(_WIN32) || defined(WIN32) 23870 #ifndef STRICT 23871 #define STRICT 23872 #endif 23873 #define WIN32_LEAN_AND_MEAN 23874 #include <windows.h> 23875 23876 #if defined(HAVE_DLOPEN_COMPAT) 23877 void *dlopen(const char *path, int mode) 23878 { 23879 JIM_NOTUSED(mode); 23880 23881 return (void *)LoadLibraryA(path); 23882 } 23883 23884 int dlclose(void *handle) 23885 { 23886 FreeLibrary((HANDLE)handle); 23887 return 0; 23888 } 23889 23890 void *dlsym(void *handle, const char *symbol) 23891 { 23892 return GetProcAddress((HMODULE)handle, symbol); 23893 } 23894 23895 char *dlerror(void) 23896 { 23897 static char msg[121]; 23898 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 23899 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL); 23900 return msg; 23901 } 23902 #endif 23903 23904 #ifdef _MSC_VER 23905 23906 #include <sys/timeb.h> 23907 23908 23909 int gettimeofday(struct timeval *tv, void *unused) 23910 { 23911 struct _timeb tb; 23912 23913 _ftime(&tb); 23914 tv->tv_sec = tb.time; 23915 tv->tv_usec = tb.millitm * 1000; 23916 23917 return 0; 23918 } 23919 23920 23921 DIR *opendir(const char *name) 23922 { 23923 DIR *dir = 0; 23924 23925 if (name && name[0]) { 23926 size_t base_length = strlen(name); 23927 const char *all = 23928 strchr("/\\", name[base_length - 1]) ? "*" : "/*"; 23929 23930 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 && 23931 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) { 23932 strcat(strcpy(dir->name, name), all); 23933 23934 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1) 23935 dir->result.d_name = 0; 23936 else { 23937 Jim_Free(dir->name); 23938 Jim_Free(dir); 23939 dir = 0; 23940 } 23941 } 23942 else { 23943 Jim_Free(dir); 23944 dir = 0; 23945 errno = ENOMEM; 23946 } 23947 } 23948 else { 23949 errno = EINVAL; 23950 } 23951 return dir; 23952 } 23953 23954 int closedir(DIR * dir) 23955 { 23956 int result = -1; 23957 23958 if (dir) { 23959 if (dir->handle != -1) 23960 result = _findclose(dir->handle); 23961 Jim_Free(dir->name); 23962 Jim_Free(dir); 23963 } 23964 if (result == -1) 23965 errno = EBADF; 23966 return result; 23967 } 23968 23969 struct dirent *readdir(DIR * dir) 23970 { 23971 struct dirent *result = 0; 23972 23973 if (dir && dir->handle != -1) { 23974 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { 23975 result = &dir->result; 23976 result->d_name = dir->info.name; 23977 } 23978 } 23979 else { 23980 errno = EBADF; 23981 } 23982 return result; 23983 } 23984 #endif 23985 #endif 23986 #include <stdio.h> 23987 #include <signal.h> 23988 23989 23990 23991 23992 23993 23994 #ifndef SIGPIPE 23995 #define SIGPIPE 13 23996 #endif 23997 #ifndef SIGINT 23998 #define SIGINT 2 23999 #endif 24000 24001 const char *Jim_SignalId(int sig) 24002 { 24003 static char buf[10]; 24004 switch (sig) { 24005 case SIGINT: return "SIGINT"; 24006 case SIGPIPE: return "SIGPIPE"; 24007 24008 } 24009 snprintf(buf, sizeof(buf), "%d", sig); 24010 return buf; 24011 } 24012 #ifndef JIM_BOOTSTRAP_LIB_ONLY 24013 #include <errno.h> 24014 #include <string.h> 24015 #include <stdio.h> 24016 24017 24018 #ifdef USE_LINENOISE 24019 #ifdef HAVE_UNISTD_H 24020 #include <unistd.h> 24021 #endif 24022 #ifdef HAVE_SYS_STAT_H 24023 #include <sys/stat.h> 24024 #endif 24025 #include "linenoise.h" 24026 #else 24027 #define MAX_LINE_LEN 512 24028 #endif 24029 24030 #ifdef USE_LINENOISE 24031 struct JimCompletionInfo { 24032 Jim_Interp *interp; 24033 Jim_Obj *completion_command; 24034 Jim_Obj *hints_command; 24035 24036 }; 24037 24038 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp); 24039 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata); 24040 static const char completion_callback_assoc_key[] = "interactive-completion"; 24041 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata); 24042 static void JimFreeHintsCallback(void *hint, void *userdata); 24043 #endif 24044 24045 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt) 24046 { 24047 #ifdef USE_LINENOISE 24048 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp); 24049 char *result; 24050 Jim_Obj *objPtr; 24051 long mlmode = 0; 24052 if (compinfo->completion_command) { 24053 linenoiseSetCompletionCallback(JimCompletionCallback, compinfo); 24054 } 24055 if (compinfo->hints_command) { 24056 linenoiseSetHintsCallback(JimHintsCallback, compinfo); 24057 linenoiseSetFreeHintsCallback(JimFreeHintsCallback); 24058 } 24059 objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE); 24060 if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) { 24061 linenoiseSetMultiLine(mlmode); 24062 } 24063 24064 result = linenoise(prompt); 24065 24066 linenoiseSetCompletionCallback(NULL, NULL); 24067 linenoiseSetHintsCallback(NULL, NULL); 24068 linenoiseSetFreeHintsCallback(NULL); 24069 return result; 24070 #else 24071 int len; 24072 char *line = Jim_Alloc(MAX_LINE_LEN); 24073 24074 fputs(prompt, stdout); 24075 fflush(stdout); 24076 24077 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) { 24078 Jim_Free(line); 24079 return NULL; 24080 } 24081 len = strlen(line); 24082 if (len && line[len - 1] == '\n') { 24083 line[len - 1] = '\0'; 24084 } 24085 return line; 24086 #endif 24087 } 24088 24089 void Jim_HistoryLoad(const char *filename) 24090 { 24091 #ifdef USE_LINENOISE 24092 linenoiseHistoryLoad(filename); 24093 #endif 24094 } 24095 24096 void Jim_HistoryAdd(const char *line) 24097 { 24098 #ifdef USE_LINENOISE 24099 linenoiseHistoryAdd(line); 24100 #endif 24101 } 24102 24103 void Jim_HistorySave(const char *filename) 24104 { 24105 #ifdef USE_LINENOISE 24106 #ifdef HAVE_UMASK 24107 mode_t mask; 24108 24109 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); 24110 #endif 24111 linenoiseHistorySave(filename); 24112 #ifdef HAVE_UMASK 24113 umask(mask); 24114 #endif 24115 #endif 24116 } 24117 24118 void Jim_HistoryShow(void) 24119 { 24120 #ifdef USE_LINENOISE 24121 24122 int i; 24123 int len; 24124 char **history = linenoiseHistory(&len); 24125 for (i = 0; i < len; i++) { 24126 printf("%4d %s\n", i + 1, history[i]); 24127 } 24128 #endif 24129 } 24130 24131 void Jim_HistorySetMaxLen(int length) 24132 { 24133 #ifdef USE_LINENOISE 24134 linenoiseHistorySetMaxLen(length); 24135 #endif 24136 } 24137 24138 int Jim_HistoryGetMaxLen(void) 24139 { 24140 #ifdef USE_LINENOISE 24141 return linenoiseHistoryGetMaxLen(); 24142 #endif 24143 return 0; 24144 } 24145 24146 #ifdef USE_LINENOISE 24147 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata) 24148 { 24149 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata; 24150 Jim_Obj *objv[2]; 24151 int ret; 24152 24153 objv[0] = info->completion_command; 24154 objv[1] = Jim_NewStringObj(info->interp, prefix, -1); 24155 24156 ret = Jim_EvalObjVector(info->interp, 2, objv); 24157 24158 24159 if (ret == JIM_OK) { 24160 int i; 24161 Jim_Obj *listObj = Jim_GetResult(info->interp); 24162 int len = Jim_ListLength(info->interp, listObj); 24163 for (i = 0; i < len; i++) { 24164 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i))); 24165 } 24166 } 24167 } 24168 24169 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata) 24170 { 24171 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata; 24172 Jim_Obj *objv[2]; 24173 int ret; 24174 char *result = NULL; 24175 24176 objv[0] = info->hints_command; 24177 objv[1] = Jim_NewStringObj(info->interp, prefix, -1); 24178 24179 ret = Jim_EvalObjVector(info->interp, 2, objv); 24180 24181 24182 if (ret == JIM_OK) { 24183 Jim_Obj *listObj = Jim_GetResult(info->interp); 24184 Jim_IncrRefCount(listObj); 24185 24186 int len = Jim_ListLength(info->interp, listObj); 24187 if (len >= 1) { 24188 long x; 24189 result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0))); 24190 if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) { 24191 *color = x; 24192 } 24193 if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) { 24194 *bold = x; 24195 } 24196 } 24197 Jim_DecrRefCount(info->interp, listObj); 24198 } 24199 return result; 24200 } 24201 24202 static void JimFreeHintsCallback(void *hint, void *userdata) 24203 { 24204 Jim_Free(hint); 24205 } 24206 24207 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data) 24208 { 24209 struct JimCompletionInfo *compinfo = data; 24210 24211 if (compinfo->completion_command) { 24212 Jim_DecrRefCount(interp, compinfo->completion_command); 24213 } 24214 if (compinfo->hints_command) { 24215 Jim_DecrRefCount(interp, compinfo->hints_command); 24216 } 24217 24218 Jim_Free(compinfo); 24219 } 24220 24221 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp) 24222 { 24223 struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key); 24224 if (compinfo == NULL) { 24225 compinfo = Jim_Alloc(sizeof(*compinfo)); 24226 compinfo->interp = interp; 24227 compinfo->completion_command = NULL; 24228 compinfo->hints_command = NULL; 24229 Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo); 24230 } 24231 return compinfo; 24232 } 24233 #endif 24234 24235 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj) 24236 { 24237 #ifdef USE_LINENOISE 24238 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp); 24239 24240 if (completionCommandObj) { 24241 24242 Jim_IncrRefCount(completionCommandObj); 24243 } 24244 if (compinfo->completion_command) { 24245 Jim_DecrRefCount(interp, compinfo->completion_command); 24246 } 24247 compinfo->completion_command = completionCommandObj; 24248 #endif 24249 } 24250 24251 void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj) 24252 { 24253 #ifdef USE_LINENOISE 24254 struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp); 24255 24256 if (hintsCommandObj) { 24257 24258 Jim_IncrRefCount(hintsCommandObj); 24259 } 24260 if (compinfo->hints_command) { 24261 Jim_DecrRefCount(interp, compinfo->hints_command); 24262 } 24263 compinfo->hints_command = hintsCommandObj; 24264 #endif 24265 } 24266 24267 int Jim_InteractivePrompt(Jim_Interp *interp) 24268 { 24269 int retcode = JIM_OK; 24270 char *history_file = NULL; 24271 #ifdef USE_LINENOISE 24272 const char *home; 24273 24274 home = getenv("HOME"); 24275 if (home && isatty(STDIN_FILENO)) { 24276 int history_len = strlen(home) + sizeof("/.jim_history"); 24277 history_file = Jim_Alloc(history_len); 24278 snprintf(history_file, history_len, "%s/.jim_history", home); 24279 Jim_HistoryLoad(history_file); 24280 } 24281 24282 Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1)); 24283 Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1)); 24284 #endif 24285 24286 printf("Welcome to Jim version %d.%d\n", 24287 JIM_VERSION / 100, JIM_VERSION % 100); 24288 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1"); 24289 24290 while (1) { 24291 Jim_Obj *scriptObjPtr; 24292 const char *result; 24293 int reslen; 24294 char prompt[20]; 24295 24296 if (retcode != JIM_OK) { 24297 const char *retcodestr = Jim_ReturnCode(retcode); 24298 24299 if (*retcodestr == '?') { 24300 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode); 24301 } 24302 else { 24303 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr); 24304 } 24305 } 24306 else { 24307 strcpy(prompt, ". "); 24308 } 24309 24310 scriptObjPtr = Jim_NewStringObj(interp, "", 0); 24311 Jim_IncrRefCount(scriptObjPtr); 24312 while (1) { 24313 char state; 24314 char *line; 24315 24316 line = Jim_HistoryGetline(interp, prompt); 24317 if (line == NULL) { 24318 if (errno == EINTR) { 24319 continue; 24320 } 24321 Jim_DecrRefCount(interp, scriptObjPtr); 24322 retcode = JIM_OK; 24323 goto out; 24324 } 24325 if (Jim_Length(scriptObjPtr) != 0) { 24326 24327 Jim_AppendString(interp, scriptObjPtr, "\n", 1); 24328 } 24329 Jim_AppendString(interp, scriptObjPtr, line, -1); 24330 Jim_Free(line); 24331 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state)) 24332 break; 24333 24334 snprintf(prompt, sizeof(prompt), "%c> ", state); 24335 } 24336 #ifdef USE_LINENOISE 24337 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) { 24338 24339 Jim_HistoryShow(); 24340 Jim_DecrRefCount(interp, scriptObjPtr); 24341 continue; 24342 } 24343 24344 Jim_HistoryAdd(Jim_String(scriptObjPtr)); 24345 if (history_file) { 24346 Jim_HistorySave(history_file); 24347 } 24348 #endif 24349 retcode = Jim_EvalObj(interp, scriptObjPtr); 24350 Jim_DecrRefCount(interp, scriptObjPtr); 24351 24352 if (retcode == JIM_EXIT) { 24353 break; 24354 } 24355 if (retcode == JIM_ERR) { 24356 Jim_MakeErrorMessage(interp); 24357 } 24358 result = Jim_GetString(Jim_GetResult(interp), &reslen); 24359 if (reslen) { 24360 if (fwrite(result, reslen, 1, stdout) == 0) { 24361 24362 } 24363 putchar('\n'); 24364 } 24365 } 24366 out: 24367 Jim_Free(history_file); 24368 24369 return retcode; 24370 } 24371 24372 #include <stdio.h> 24373 #include <stdlib.h> 24374 #include <string.h> 24375 24376 24377 24378 extern int Jim_initjimshInit(Jim_Interp *interp); 24379 24380 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[]) 24381 { 24382 int n; 24383 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 24384 24385 24386 for (n = 0; n < argc; n++) { 24387 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1); 24388 24389 Jim_ListAppendElement(interp, listObj, obj); 24390 } 24391 24392 Jim_SetVariableStr(interp, "argv", listObj); 24393 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc)); 24394 } 24395 24396 static void JimPrintErrorMessage(Jim_Interp *interp) 24397 { 24398 Jim_MakeErrorMessage(interp); 24399 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); 24400 } 24401 24402 void usage(const char* executable_name) 24403 { 24404 printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); 24405 printf("Usage: %s\n", executable_name); 24406 printf("or : %s [options] [filename]\n", executable_name); 24407 printf("\n"); 24408 printf("Without options: Interactive mode\n"); 24409 printf("\n"); 24410 printf("Options:\n"); 24411 printf(" --version : prints the version string\n"); 24412 printf(" --help : prints this text\n"); 24413 printf(" -e CMD : executes command CMD\n"); 24414 printf(" NOTE: all subsequent options will be passed as arguments to the command\n"); 24415 printf(" [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n"); 24416 printf(" NOTE: all subsequent options will be passed to the script\n\n"); 24417 } 24418 24419 int main(int argc, char *const argv[]) 24420 { 24421 int retcode; 24422 Jim_Interp *interp; 24423 char *const orig_argv0 = argv[0]; 24424 24425 24426 if (argc > 1 && strcmp(argv[1], "--version") == 0) { 24427 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); 24428 return 0; 24429 } 24430 else if (argc > 1 && strcmp(argv[1], "--help") == 0) { 24431 usage(argv[0]); 24432 return 0; 24433 } 24434 24435 24436 interp = Jim_CreateInterp(); 24437 Jim_RegisterCoreCommands(interp); 24438 24439 24440 if (Jim_InitStaticExtensions(interp) != JIM_OK) { 24441 JimPrintErrorMessage(interp); 24442 } 24443 24444 Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0); 24445 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0"); 24446 #ifdef USE_LINENOISE 24447 Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1"); 24448 #else 24449 Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0"); 24450 #endif 24451 retcode = Jim_initjimshInit(interp); 24452 24453 if (argc == 1) { 24454 24455 if (retcode == JIM_ERR) { 24456 JimPrintErrorMessage(interp); 24457 } 24458 if (retcode != JIM_EXIT) { 24459 JimSetArgv(interp, 0, NULL); 24460 if (!isatty(STDIN_FILENO)) { 24461 24462 goto eval_stdin; 24463 } 24464 retcode = Jim_InteractivePrompt(interp); 24465 } 24466 } 24467 else { 24468 24469 if (argc > 2 && strcmp(argv[1], "-e") == 0) { 24470 24471 JimSetArgv(interp, argc - 3, argv + 3); 24472 retcode = Jim_Eval(interp, argv[2]); 24473 if (retcode != JIM_ERR) { 24474 int len; 24475 const char *msg = Jim_GetString(Jim_GetResult(interp), &len); 24476 if (fwrite(msg, len, 1, stdout) == 0) { 24477 24478 } 24479 putchar('\n'); 24480 } 24481 } 24482 else { 24483 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1)); 24484 JimSetArgv(interp, argc - 2, argv + 2); 24485 if (strcmp(argv[1], "-") == 0) { 24486 eval_stdin: 24487 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]"); 24488 } else { 24489 retcode = Jim_EvalFile(interp, argv[1]); 24490 } 24491 } 24492 if (retcode == JIM_ERR) { 24493 JimPrintErrorMessage(interp); 24494 } 24495 } 24496 if (retcode == JIM_EXIT) { 24497 retcode = Jim_GetExitCode(interp); 24498 } 24499 else if (retcode == JIM_ERR) { 24500 retcode = 1; 24501 } 24502 else { 24503 retcode = 0; 24504 } 24505 Jim_FreeInterp(interp); 24506 return retcode; 24507 } 24508 #endif 24509