Fossil SCM
Merge in latest from trunk to keep up-to-date.
Commit
c6500ac9857ae5353a8ac4c74e0d253477c96a20
Parent
6b90659f83b7cda…
20 files changed
+186
-137
+129
-58
+41
-8
+26
-5
+26
-5
+6
-6
+1
+8
-1
+4
+6
+177
-87
+2
-2
+4
-6
+124
-7
+2
-1
+1
-1
+6
-6
+6
-6
+5
+16
-1
~
autosetup/jimsh0.c
~
src/checkin.c
~
src/file.c
~
src/main.c
~
src/main.c
~
src/makemake.tcl
~
src/setup.c
~
src/style.c
~
src/th.h
~
src/th_main.c
~
src/th_tcl.c
~
src/timeline.c
~
src/util.c
~
src/vfile.c
~
test/th1-tcl.test
~
test/th1-tcl8.txt
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/fossil.rc
~
www/server.wiki
+186
-137
| --- autosetup/jimsh0.c | ||
| +++ autosetup/jimsh0.c | ||
| @@ -185,11 +185,11 @@ | ||
| 185 | 185 | #endif |
| 186 | 186 | |
| 187 | 187 | #define UCHAR(c) ((unsigned char)(c)) |
| 188 | 188 | |
| 189 | 189 | |
| 190 | -#define JIM_VERSION 73 | |
| 190 | +#define JIM_VERSION 74 | |
| 191 | 191 | |
| 192 | 192 | #define JIM_OK 0 |
| 193 | 193 | #define JIM_ERR 1 |
| 194 | 194 | #define JIM_RETURN 2 |
| 195 | 195 | #define JIM_BREAK 3 |
| @@ -321,14 +321,14 @@ | ||
| 321 | 321 | #define Jim_GetHashTableSize(ht) ((ht)->size) |
| 322 | 322 | #define Jim_GetHashTableUsed(ht) ((ht)->used) |
| 323 | 323 | |
| 324 | 324 | |
| 325 | 325 | typedef struct Jim_Obj { |
| 326 | - int refCount; | |
| 327 | 326 | char *bytes; |
| 328 | - int length; | |
| 329 | 327 | const struct Jim_ObjType *typePtr; |
| 328 | + int refCount; | |
| 329 | + int length; | |
| 330 | 330 | |
| 331 | 331 | union { |
| 332 | 332 | |
| 333 | 333 | jim_wide wideValue; |
| 334 | 334 | |
| @@ -665,12 +665,10 @@ | ||
| 665 | 665 | |
| 666 | 666 | |
| 667 | 667 | JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); |
| 668 | 668 | JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); |
| 669 | 669 | JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); |
| 670 | -JIM_EXPORT void Jim_InitStringRep (Jim_Obj *objPtr, const char *bytes, | |
| 671 | - int length); | |
| 672 | 670 | JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp, |
| 673 | 671 | Jim_Obj *objPtr); |
| 674 | 672 | JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, |
| 675 | 673 | int *lenPtr); |
| 676 | 674 | JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr); |
| @@ -877,10 +875,12 @@ | ||
| 877 | 875 | JIM_EXPORT void Jim_HistoryShow(void); |
| 878 | 876 | |
| 879 | 877 | |
| 880 | 878 | JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); |
| 881 | 879 | JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); |
| 880 | +JIM_EXPORT int Jim_CheckSignal(Jim_Interp *interp); | |
| 881 | +#define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask) | |
| 882 | 882 | |
| 883 | 883 | |
| 884 | 884 | JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); |
| 885 | 885 | JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); |
| 886 | 886 | |
| @@ -5671,10 +5671,13 @@ | ||
| 5671 | 5671 | #ifdef JIM_MAINTAINER |
| 5672 | 5672 | #define JIM_DEBUG_COMMAND |
| 5673 | 5673 | #define JIM_DEBUG_PANIC |
| 5674 | 5674 | #endif |
| 5675 | 5675 | |
| 5676 | + | |
| 5677 | +#define JIM_INTEGER_SPACE 24 | |
| 5678 | + | |
| 5676 | 5679 | const char *jim_tt_name(int type); |
| 5677 | 5680 | |
| 5678 | 5681 | #ifdef JIM_DEBUG_PANIC |
| 5679 | 5682 | static void JimPanicDump(int panic_condition, const char *fmt, ...); |
| 5680 | 5683 | #define JimPanic(X) JimPanicDump X |
| @@ -5690,10 +5693,11 @@ | ||
| 5690 | 5693 | static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, |
| 5691 | 5694 | int flags); |
| 5692 | 5695 | static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands); |
| 5693 | 5696 | static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); |
| 5694 | 5697 | static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); |
| 5698 | +static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len); | |
| 5695 | 5699 | static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, |
| 5696 | 5700 | const char *prefix, const char *const *tablePtr, const char *name); |
| 5697 | 5701 | static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv); |
| 5698 | 5702 | static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); |
| 5699 | 5703 | static int JimSign(jim_wide w); |
| @@ -5934,15 +5938,42 @@ | ||
| 5934 | 5938 | } |
| 5935 | 5939 | return n; |
| 5936 | 5940 | } |
| 5937 | 5941 | #endif |
| 5938 | 5942 | |
| 5939 | -int Jim_WideToString(char *buf, jim_wide wideValue) | |
| 5943 | +static int JimWideToString(char *buf, jim_wide wideValue) | |
| 5940 | 5944 | { |
| 5941 | - const char *fmt = "%" JIM_WIDE_MODIFIER; | |
| 5945 | + int pos = 0; | |
| 5942 | 5946 | |
| 5943 | - return sprintf(buf, fmt, wideValue); | |
| 5947 | + if (wideValue == 0) { | |
| 5948 | + buf[pos++] = '0'; | |
| 5949 | + } | |
| 5950 | + else { | |
| 5951 | + char tmp[JIM_INTEGER_SPACE]; | |
| 5952 | + int num = 0; | |
| 5953 | + int i; | |
| 5954 | + | |
| 5955 | + if (wideValue < 0) { | |
| 5956 | + buf[pos++] = '-'; | |
| 5957 | + | |
| 5958 | + i = wideValue % 10; | |
| 5959 | + tmp[num++] = (i > 0) ? (10 - i) : -i; | |
| 5960 | + wideValue /= -10; | |
| 5961 | + } | |
| 5962 | + | |
| 5963 | + while (wideValue) { | |
| 5964 | + tmp[num++] = wideValue % 10; | |
| 5965 | + wideValue /= 10; | |
| 5966 | + } | |
| 5967 | + | |
| 5968 | + for (i = 0; i < num; i++) { | |
| 5969 | + buf[pos++] = '0' + tmp[num - i - 1]; | |
| 5970 | + } | |
| 5971 | + } | |
| 5972 | + buf[pos] = 0; | |
| 5973 | + | |
| 5974 | + return pos; | |
| 5944 | 5975 | } |
| 5945 | 5976 | |
| 5946 | 5977 | static int JimCheckConversion(const char *str, const char *endptr) |
| 5947 | 5978 | { |
| 5948 | 5979 | if (str[0] == '\0' || str == endptr) { |
| @@ -6229,10 +6260,18 @@ | ||
| 6229 | 6260 | ht->sizemask = 0; |
| 6230 | 6261 | ht->used = 0; |
| 6231 | 6262 | ht->collisions = 0; |
| 6232 | 6263 | } |
| 6233 | 6264 | |
| 6265 | +static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter) | |
| 6266 | +{ | |
| 6267 | + iter->ht = ht; | |
| 6268 | + iter->index = -1; | |
| 6269 | + iter->entry = NULL; | |
| 6270 | + iter->nextEntry = NULL; | |
| 6271 | +} | |
| 6272 | + | |
| 6234 | 6273 | |
| 6235 | 6274 | int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr) |
| 6236 | 6275 | { |
| 6237 | 6276 | JimResetHashTable(ht); |
| 6238 | 6277 | ht->type = type; |
| @@ -6408,15 +6447,11 @@ | ||
| 6408 | 6447 | } |
| 6409 | 6448 | |
| 6410 | 6449 | Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht) |
| 6411 | 6450 | { |
| 6412 | 6451 | Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter)); |
| 6413 | - | |
| 6414 | - iter->ht = ht; | |
| 6415 | - iter->index = -1; | |
| 6416 | - iter->entry = NULL; | |
| 6417 | - iter->nextEntry = NULL; | |
| 6452 | + JimInitHashTableIterator(ht, iter); | |
| 6418 | 6453 | return iter; |
| 6419 | 6454 | } |
| 6420 | 6455 | |
| 6421 | 6456 | Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter) |
| 6422 | 6457 | { |
| @@ -10500,12 +10535,10 @@ | ||
| 10500 | 10535 | int Jim_GetExitCode(Jim_Interp *interp) |
| 10501 | 10536 | { |
| 10502 | 10537 | return interp->exitCode; |
| 10503 | 10538 | } |
| 10504 | 10539 | |
| 10505 | -#define JIM_INTEGER_SPACE 24 | |
| 10506 | - | |
| 10507 | 10540 | static void UpdateStringOfInt(struct Jim_Obj *objPtr); |
| 10508 | 10541 | static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); |
| 10509 | 10542 | |
| 10510 | 10543 | static const Jim_ObjType intObjType = { |
| 10511 | 10544 | "int", |
| @@ -10522,16 +10555,16 @@ | ||
| 10522 | 10555 | UpdateStringOfInt, |
| 10523 | 10556 | JIM_TYPE_NONE, |
| 10524 | 10557 | }; |
| 10525 | 10558 | |
| 10526 | 10559 | |
| 10527 | -void UpdateStringOfInt(struct Jim_Obj *objPtr) | |
| 10560 | +static void UpdateStringOfInt(struct Jim_Obj *objPtr) | |
| 10528 | 10561 | { |
| 10529 | 10562 | int len; |
| 10530 | 10563 | char buf[JIM_INTEGER_SPACE + 1]; |
| 10531 | 10564 | |
| 10532 | - len = Jim_WideToString(buf, JimWideValue(objPtr)); | |
| 10565 | + len = JimWideToString(buf, JimWideValue(objPtr)); | |
| 10533 | 10566 | objPtr->bytes = Jim_Alloc(len + 1); |
| 10534 | 10567 | memcpy(objPtr->bytes, buf, len + 1); |
| 10535 | 10568 | objPtr->length = len; |
| 10536 | 10569 | } |
| 10537 | 10570 | |
| @@ -10755,11 +10788,11 @@ | ||
| 10755 | 10788 | } |
| 10756 | 10789 | |
| 10757 | 10790 | #define JIM_ELESTR_SIMPLE 0 |
| 10758 | 10791 | #define JIM_ELESTR_BRACE 1 |
| 10759 | 10792 | #define JIM_ELESTR_QUOTE 2 |
| 10760 | -static int ListElementQuotingType(const char *s, int len) | |
| 10793 | +static unsigned char ListElementQuotingType(const char *s, int len) | |
| 10761 | 10794 | { |
| 10762 | 10795 | int i, level, blevel, trySimple = 1; |
| 10763 | 10796 | |
| 10764 | 10797 | |
| 10765 | 10798 | if (len == 0) |
| @@ -10903,17 +10936,23 @@ | ||
| 10903 | 10936 | return p - q; |
| 10904 | 10937 | } |
| 10905 | 10938 | |
| 10906 | 10939 | static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc) |
| 10907 | 10940 | { |
| 10941 | + #define STATIC_QUOTING_LEN 32 | |
| 10908 | 10942 | int i, bufLen, realLength; |
| 10909 | 10943 | const char *strRep; |
| 10910 | 10944 | char *p; |
| 10911 | - int *quotingType; | |
| 10945 | + unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN]; | |
| 10912 | 10946 | |
| 10913 | 10947 | |
| 10914 | - quotingType = Jim_Alloc(sizeof(int) * objc + 1); | |
| 10948 | + if (objc > STATIC_QUOTING_LEN) { | |
| 10949 | + quotingType = Jim_Alloc(objc); | |
| 10950 | + } | |
| 10951 | + else { | |
| 10952 | + quotingType = staticQuoting; | |
| 10953 | + } | |
| 10915 | 10954 | bufLen = 0; |
| 10916 | 10955 | for (i = 0; i < objc; i++) { |
| 10917 | 10956 | int len; |
| 10918 | 10957 | |
| 10919 | 10958 | strRep = Jim_GetString(objv[i], &len); |
| @@ -10975,11 +11014,14 @@ | ||
| 10975 | 11014 | realLength++; |
| 10976 | 11015 | } |
| 10977 | 11016 | } |
| 10978 | 11017 | *p = '\0'; |
| 10979 | 11018 | objPtr->length = realLength; |
| 10980 | - Jim_Free(quotingType); | |
| 11019 | + | |
| 11020 | + if (quotingType != staticQuoting) { | |
| 11021 | + Jim_Free(quotingType); | |
| 11022 | + } | |
| 10981 | 11023 | } |
| 10982 | 11024 | |
| 10983 | 11025 | static void UpdateStringOfList(struct Jim_Obj *objPtr) |
| 10984 | 11026 | { |
| 10985 | 11027 | JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len); |
| @@ -11000,11 +11042,11 @@ | ||
| 11000 | 11042 | if (Jim_IsDict(objPtr) && !Jim_IsShared(objPtr)) { |
| 11001 | 11043 | Jim_Obj **listObjPtrPtr; |
| 11002 | 11044 | int len; |
| 11003 | 11045 | int i; |
| 11004 | 11046 | |
| 11005 | - Jim_DictPairs(interp, objPtr, &listObjPtrPtr, &len); | |
| 11047 | + listObjPtrPtr = JimDictPairs(objPtr, &len); | |
| 11006 | 11048 | for (i = 0; i < len; i++) { |
| 11007 | 11049 | Jim_IncrRefCount(listObjPtrPtr[i]); |
| 11008 | 11050 | } |
| 11009 | 11051 | |
| 11010 | 11052 | |
| @@ -11225,14 +11267,22 @@ | ||
| 11225 | 11267 | int requiredLen = currentLen + elemc; |
| 11226 | 11268 | int i; |
| 11227 | 11269 | Jim_Obj **point; |
| 11228 | 11270 | |
| 11229 | 11271 | if (requiredLen > listPtr->internalRep.listValue.maxLen) { |
| 11230 | - listPtr->internalRep.listValue.maxLen = requiredLen * 2; | |
| 11272 | + if (requiredLen < 2) { | |
| 11273 | + | |
| 11274 | + requiredLen = 4; | |
| 11275 | + } | |
| 11276 | + else { | |
| 11277 | + requiredLen *= 2; | |
| 11278 | + } | |
| 11231 | 11279 | |
| 11232 | 11280 | listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele, |
| 11233 | - sizeof(Jim_Obj *) * listPtr->internalRep.listValue.maxLen); | |
| 11281 | + sizeof(Jim_Obj *) * requiredLen); | |
| 11282 | + | |
| 11283 | + listPtr->internalRep.listValue.maxLen = requiredLen; | |
| 11234 | 11284 | } |
| 11235 | 11285 | if (idx < 0) { |
| 11236 | 11286 | idx = currentLen; |
| 11237 | 11287 | } |
| 11238 | 11288 | point = listPtr->internalRep.listValue.ele + idx; |
| @@ -11523,55 +11573,53 @@ | ||
| 11523 | 11573 | } |
| 11524 | 11574 | |
| 11525 | 11575 | void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) |
| 11526 | 11576 | { |
| 11527 | 11577 | Jim_HashTable *ht, *dupHt; |
| 11528 | - Jim_HashTableIterator *htiter; | |
| 11578 | + Jim_HashTableIterator htiter; | |
| 11529 | 11579 | Jim_HashEntry *he; |
| 11530 | 11580 | |
| 11531 | 11581 | |
| 11532 | 11582 | ht = srcPtr->internalRep.ptr; |
| 11533 | 11583 | dupHt = Jim_Alloc(sizeof(*dupHt)); |
| 11534 | 11584 | Jim_InitHashTable(dupHt, &JimDictHashTableType, interp); |
| 11535 | 11585 | if (ht->size != 0) |
| 11536 | 11586 | Jim_ExpandHashTable(dupHt, ht->size); |
| 11537 | 11587 | |
| 11538 | - htiter = Jim_GetHashTableIterator(ht); | |
| 11539 | - while ((he = Jim_NextHashEntry(htiter)) != NULL) { | |
| 11588 | + JimInitHashTableIterator(ht, &htiter); | |
| 11589 | + while ((he = Jim_NextHashEntry(&htiter)) != NULL) { | |
| 11540 | 11590 | const Jim_Obj *keyObjPtr = he->key; |
| 11541 | 11591 | Jim_Obj *valObjPtr = he->u.val; |
| 11542 | 11592 | |
| 11543 | 11593 | Jim_IncrRefCount((Jim_Obj *)keyObjPtr); |
| 11544 | 11594 | Jim_IncrRefCount(valObjPtr); |
| 11545 | 11595 | Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr); |
| 11546 | 11596 | } |
| 11547 | - Jim_FreeHashTableIterator(htiter); | |
| 11548 | 11597 | |
| 11549 | 11598 | dupPtr->internalRep.ptr = dupHt; |
| 11550 | 11599 | dupPtr->typePtr = &dictObjType; |
| 11551 | 11600 | } |
| 11552 | 11601 | |
| 11553 | 11602 | static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len) |
| 11554 | 11603 | { |
| 11555 | 11604 | Jim_HashTable *ht; |
| 11556 | - Jim_HashTableIterator *htiter; | |
| 11605 | + Jim_HashTableIterator htiter; | |
| 11557 | 11606 | Jim_HashEntry *he; |
| 11558 | 11607 | Jim_Obj **objv; |
| 11559 | 11608 | int i; |
| 11560 | 11609 | |
| 11561 | 11610 | ht = dictPtr->internalRep.ptr; |
| 11562 | 11611 | |
| 11563 | 11612 | |
| 11564 | 11613 | objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *)); |
| 11565 | - htiter = Jim_GetHashTableIterator(ht); | |
| 11614 | + JimInitHashTableIterator(ht, &htiter); | |
| 11566 | 11615 | i = 0; |
| 11567 | - while ((he = Jim_NextHashEntry(htiter)) != NULL) { | |
| 11616 | + while ((he = Jim_NextHashEntry(&htiter)) != NULL) { | |
| 11568 | 11617 | objv[i++] = (Jim_Obj *)he->key; |
| 11569 | 11618 | objv[i++] = he->u.val; |
| 11570 | 11619 | } |
| 11571 | 11620 | *len = i; |
| 11572 | - Jim_FreeHashTableIterator(htiter); | |
| 11573 | 11621 | return objv; |
| 11574 | 11622 | } |
| 11575 | 11623 | |
| 11576 | 11624 | static void UpdateStringOfDict(struct Jim_Obj *objPtr) |
| 11577 | 11625 | { |
| @@ -12089,14 +12137,15 @@ | ||
| 12089 | 12137 | |
| 12090 | 12138 | |
| 12091 | 12139 | typedef struct Jim_ExprOperator |
| 12092 | 12140 | { |
| 12093 | 12141 | const char *name; |
| 12094 | - int precedence; | |
| 12095 | - int arity; | |
| 12096 | 12142 | int (*funcop) (Jim_Interp *interp, struct JimExprState * e); |
| 12097 | - int lazy; | |
| 12143 | + unsigned char precedence; | |
| 12144 | + unsigned char arity; | |
| 12145 | + unsigned char lazy; | |
| 12146 | + unsigned char namelen; | |
| 12098 | 12147 | } Jim_ExprOperator; |
| 12099 | 12148 | |
| 12100 | 12149 | static void ExprPush(struct JimExprState *e, Jim_Obj *obj) |
| 12101 | 12150 | { |
| 12102 | 12151 | Jim_IncrRefCount(obj); |
| @@ -12774,93 +12823,96 @@ | ||
| 12774 | 12823 | LAZY_OP, |
| 12775 | 12824 | LAZY_LEFT, |
| 12776 | 12825 | LAZY_RIGHT |
| 12777 | 12826 | }; |
| 12778 | 12827 | |
| 12828 | +#define OPRINIT(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1} | |
| 12829 | + | |
| 12779 | 12830 | static const struct Jim_ExprOperator Jim_ExprOperators[] = { |
| 12780 | - {"*", 200, 2, JimExprOpBin, LAZY_NONE}, | |
| 12781 | - {"/", 200, 2, JimExprOpBin, LAZY_NONE}, | |
| 12782 | - {"%", 200, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12783 | - | |
| 12784 | - {"-", 100, 2, JimExprOpBin, LAZY_NONE}, | |
| 12785 | - {"+", 100, 2, JimExprOpBin, LAZY_NONE}, | |
| 12786 | - | |
| 12787 | - {"<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12788 | - {">>", 90, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12789 | - | |
| 12790 | - {"<<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12791 | - {">>>", 90, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12792 | - | |
| 12793 | - {"<", 80, 2, JimExprOpBin, LAZY_NONE}, | |
| 12794 | - {">", 80, 2, JimExprOpBin, LAZY_NONE}, | |
| 12795 | - {"<=", 80, 2, JimExprOpBin, LAZY_NONE}, | |
| 12796 | - {">=", 80, 2, JimExprOpBin, LAZY_NONE}, | |
| 12797 | - | |
| 12798 | - {"==", 70, 2, JimExprOpBin, LAZY_NONE}, | |
| 12799 | - {"!=", 70, 2, JimExprOpBin, LAZY_NONE}, | |
| 12800 | - | |
| 12801 | - {"&", 50, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12802 | - {"^", 49, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12803 | - {"|", 48, 2, JimExprOpIntBin, LAZY_NONE}, | |
| 12804 | - | |
| 12805 | - {"&&", 10, 2, NULL, LAZY_OP}, | |
| 12806 | - {NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT}, | |
| 12807 | - {NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT}, | |
| 12808 | - | |
| 12809 | - {"||", 9, 2, NULL, LAZY_OP}, | |
| 12810 | - {NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT}, | |
| 12811 | - {NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT}, | |
| 12812 | - | |
| 12813 | - {"?", 5, 2, JimExprOpNull, LAZY_OP}, | |
| 12814 | - {NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT}, | |
| 12815 | - {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, | |
| 12816 | - | |
| 12817 | - {":", 5, 2, JimExprOpNull, LAZY_OP}, | |
| 12818 | - {NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT}, | |
| 12819 | - {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, | |
| 12820 | - | |
| 12821 | - {"**", 250, 2, JimExprOpBin, LAZY_NONE}, | |
| 12822 | - | |
| 12823 | - {"eq", 60, 2, JimExprOpStrBin, LAZY_NONE}, | |
| 12824 | - {"ne", 60, 2, JimExprOpStrBin, LAZY_NONE}, | |
| 12825 | - | |
| 12826 | - {"in", 55, 2, JimExprOpStrBin, LAZY_NONE}, | |
| 12827 | - {"ni", 55, 2, JimExprOpStrBin, LAZY_NONE}, | |
| 12828 | - | |
| 12829 | - {"!", 300, 1, JimExprOpNumUnary, LAZY_NONE}, | |
| 12830 | - {"~", 300, 1, JimExprOpIntUnary, LAZY_NONE}, | |
| 12831 | - {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, | |
| 12832 | - {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, | |
| 12833 | - | |
| 12834 | - | |
| 12835 | - | |
| 12836 | - {"int", 400, 1, JimExprOpNumUnary, LAZY_NONE}, | |
| 12837 | - {"abs", 400, 1, JimExprOpNumUnary, LAZY_NONE}, | |
| 12838 | - {"double", 400, 1, JimExprOpNumUnary, LAZY_NONE}, | |
| 12839 | - {"round", 400, 1, JimExprOpNumUnary, LAZY_NONE}, | |
| 12840 | - {"rand", 400, 0, JimExprOpNone, LAZY_NONE}, | |
| 12841 | - {"srand", 400, 1, JimExprOpIntUnary, LAZY_NONE}, | |
| 12831 | + OPRINIT("*", 110, 2, JimExprOpBin, LAZY_NONE), | |
| 12832 | + OPRINIT("/", 110, 2, JimExprOpBin, LAZY_NONE), | |
| 12833 | + OPRINIT("%", 110, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12834 | + | |
| 12835 | + OPRINIT("-", 100, 2, JimExprOpBin, LAZY_NONE), | |
| 12836 | + OPRINIT("+", 100, 2, JimExprOpBin, LAZY_NONE), | |
| 12837 | + | |
| 12838 | + OPRINIT("<<", 90, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12839 | + OPRINIT(">>", 90, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12840 | + | |
| 12841 | + OPRINIT("<<<", 90, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12842 | + OPRINIT(">>>", 90, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12843 | + | |
| 12844 | + OPRINIT("<", 80, 2, JimExprOpBin, LAZY_NONE), | |
| 12845 | + OPRINIT(">", 80, 2, JimExprOpBin, LAZY_NONE), | |
| 12846 | + OPRINIT("<=", 80, 2, JimExprOpBin, LAZY_NONE), | |
| 12847 | + OPRINIT(">=", 80, 2, JimExprOpBin, LAZY_NONE), | |
| 12848 | + | |
| 12849 | + OPRINIT("==", 70, 2, JimExprOpBin, LAZY_NONE), | |
| 12850 | + OPRINIT("!=", 70, 2, JimExprOpBin, LAZY_NONE), | |
| 12851 | + | |
| 12852 | + OPRINIT("&", 50, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12853 | + OPRINIT("^", 49, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12854 | + OPRINIT("|", 48, 2, JimExprOpIntBin, LAZY_NONE), | |
| 12855 | + | |
| 12856 | + OPRINIT("&&", 10, 2, NULL, LAZY_OP), | |
| 12857 | + OPRINIT(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT), | |
| 12858 | + OPRINIT(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT), | |
| 12859 | + | |
| 12860 | + OPRINIT("||", 9, 2, NULL, LAZY_OP), | |
| 12861 | + OPRINIT(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT), | |
| 12862 | + OPRINIT(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT), | |
| 12863 | + | |
| 12864 | + OPRINIT("?", 5, 2, JimExprOpNull, LAZY_OP), | |
| 12865 | + OPRINIT(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT), | |
| 12866 | + OPRINIT(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), | |
| 12867 | + | |
| 12868 | + OPRINIT(":", 5, 2, JimExprOpNull, LAZY_OP), | |
| 12869 | + OPRINIT(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT), | |
| 12870 | + OPRINIT(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), | |
| 12871 | + | |
| 12872 | + OPRINIT("**", 250, 2, JimExprOpBin, LAZY_NONE), | |
| 12873 | + | |
| 12874 | + OPRINIT("eq", 60, 2, JimExprOpStrBin, LAZY_NONE), | |
| 12875 | + OPRINIT("ne", 60, 2, JimExprOpStrBin, LAZY_NONE), | |
| 12876 | + | |
| 12877 | + OPRINIT("in", 55, 2, JimExprOpStrBin, LAZY_NONE), | |
| 12878 | + OPRINIT("ni", 55, 2, JimExprOpStrBin, LAZY_NONE), | |
| 12879 | + | |
| 12880 | + OPRINIT("!", 150, 1, JimExprOpNumUnary, LAZY_NONE), | |
| 12881 | + OPRINIT("~", 150, 1, JimExprOpIntUnary, LAZY_NONE), | |
| 12882 | + OPRINIT(NULL, 150, 1, JimExprOpNumUnary, LAZY_NONE), | |
| 12883 | + OPRINIT(NULL, 150, 1, JimExprOpNumUnary, LAZY_NONE), | |
| 12884 | + | |
| 12885 | + | |
| 12886 | + | |
| 12887 | + OPRINIT("int", 200, 1, JimExprOpNumUnary, LAZY_NONE), | |
| 12888 | + OPRINIT("abs", 200, 1, JimExprOpNumUnary, LAZY_NONE), | |
| 12889 | + OPRINIT("double", 200, 1, JimExprOpNumUnary, LAZY_NONE), | |
| 12890 | + OPRINIT("round", 200, 1, JimExprOpNumUnary, LAZY_NONE), | |
| 12891 | + OPRINIT("rand", 200, 0, JimExprOpNone, LAZY_NONE), | |
| 12892 | + OPRINIT("srand", 200, 1, JimExprOpIntUnary, LAZY_NONE), | |
| 12842 | 12893 | |
| 12843 | 12894 | #ifdef JIM_MATH_FUNCTIONS |
| 12844 | - {"sin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12845 | - {"cos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12846 | - {"tan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12847 | - {"asin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12848 | - {"acos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12849 | - {"atan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12850 | - {"sinh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12851 | - {"cosh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12852 | - {"tanh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12853 | - {"ceil", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12854 | - {"floor", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12855 | - {"exp", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12856 | - {"log", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12857 | - {"log10", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12858 | - {"sqrt", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, | |
| 12859 | - {"pow", 400, 2, JimExprOpBin, LAZY_NONE}, | |
| 12895 | + OPRINIT("sin", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12896 | + OPRINIT("cos", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12897 | + OPRINIT("tan", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12898 | + OPRINIT("asin", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12899 | + OPRINIT("acos", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12900 | + OPRINIT("atan", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12901 | + OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12902 | + OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12903 | + OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12904 | + OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12905 | + OPRINIT("floor", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12906 | + OPRINIT("exp", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12907 | + OPRINIT("log", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12908 | + OPRINIT("log10", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12909 | + OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), | |
| 12910 | + OPRINIT("pow", 200, 2, JimExprOpBin, LAZY_NONE), | |
| 12860 | 12911 | #endif |
| 12861 | 12912 | }; |
| 12913 | +#undef OPRINIT | |
| 12862 | 12914 | |
| 12863 | 12915 | #define JIM_EXPR_OPERATORS_NUM \ |
| 12864 | 12916 | (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) |
| 12865 | 12917 | |
| 12866 | 12918 | static int JimParseExpression(struct JimParserCtx *pc) |
| @@ -13026,20 +13078,18 @@ | ||
| 13026 | 13078 | int i; |
| 13027 | 13079 | int bestIdx = -1, bestLen = 0; |
| 13028 | 13080 | |
| 13029 | 13081 | |
| 13030 | 13082 | for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { |
| 13031 | - const char *opname; | |
| 13032 | - int oplen; | |
| 13083 | + const char * const opname = Jim_ExprOperators[i].name; | |
| 13084 | + const int oplen = Jim_ExprOperators[i].namelen; | |
| 13033 | 13085 | |
| 13034 | - opname = Jim_ExprOperators[i].name; | |
| 13035 | - if (opname == NULL) { | |
| 13086 | + if (opname == NULL || opname[0] != pc->p[0]) { | |
| 13036 | 13087 | continue; |
| 13037 | 13088 | } |
| 13038 | - oplen = strlen(opname); | |
| 13039 | 13089 | |
| 13040 | - if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) { | |
| 13090 | + if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) { | |
| 13041 | 13091 | bestIdx = i + JIM_TT_EXPR_OP; |
| 13042 | 13092 | bestLen = oplen; |
| 13043 | 13093 | } |
| 13044 | 13094 | } |
| 13045 | 13095 | if (bestIdx == -1) { |
| @@ -13111,12 +13161,12 @@ | ||
| 13111 | 13161 | }; |
| 13112 | 13162 | |
| 13113 | 13163 | |
| 13114 | 13164 | typedef struct ExprByteCode |
| 13115 | 13165 | { |
| 13116 | - int len; | |
| 13117 | 13166 | ScriptToken *token; |
| 13167 | + int len; | |
| 13118 | 13168 | int inUse; |
| 13119 | 13169 | } ExprByteCode; |
| 13120 | 13170 | |
| 13121 | 13171 | static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr) |
| 13122 | 13172 | { |
| @@ -13855,16 +13905,16 @@ | ||
| 13855 | 13905 | |
| 13856 | 13906 | |
| 13857 | 13907 | |
| 13858 | 13908 | typedef struct ScanFmtPartDescr |
| 13859 | 13909 | { |
| 13860 | - char type; | |
| 13861 | - char modifier; | |
| 13910 | + char *arg; | |
| 13911 | + char *prefix; | |
| 13862 | 13912 | size_t width; |
| 13863 | 13913 | int pos; |
| 13864 | - char *arg; | |
| 13865 | - char *prefix; | |
| 13914 | + char type; | |
| 13915 | + char modifier; | |
| 13866 | 13916 | } ScanFmtPartDescr; |
| 13867 | 13917 | |
| 13868 | 13918 | |
| 13869 | 13919 | typedef struct ScanFmtStringObj |
| 13870 | 13920 | { |
| @@ -14871,12 +14921,12 @@ | ||
| 14871 | 14921 | } |
| 14872 | 14922 | |
| 14873 | 14923 | if (retcode == JIM_OK && argc) { |
| 14874 | 14924 | |
| 14875 | 14925 | retcode = JimInvokeCommand(interp, argc, argv); |
| 14876 | - if (interp->signal_level && interp->sigmask) { | |
| 14877 | - | |
| 14926 | + | |
| 14927 | + if (Jim_CheckSignal(interp)) { | |
| 14878 | 14928 | retcode = JIM_SIGNAL; |
| 14879 | 14929 | } |
| 14880 | 14930 | } |
| 14881 | 14931 | |
| 14882 | 14932 | |
| @@ -15446,17 +15496,17 @@ | ||
| 15446 | 15496 | if (he) { |
| 15447 | 15497 | callback(interp, listObjPtr, he, type); |
| 15448 | 15498 | } |
| 15449 | 15499 | } |
| 15450 | 15500 | else { |
| 15451 | - Jim_HashTableIterator *htiter = Jim_GetHashTableIterator(ht); | |
| 15452 | - while ((he = Jim_NextHashEntry(htiter)) != NULL) { | |
| 15501 | + Jim_HashTableIterator htiter; | |
| 15502 | + JimInitHashTableIterator(ht, &htiter); | |
| 15503 | + while ((he = Jim_NextHashEntry(&htiter)) != NULL) { | |
| 15453 | 15504 | if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) { |
| 15454 | 15505 | callback(interp, listObjPtr, he, type); |
| 15455 | 15506 | } |
| 15456 | 15507 | } |
| 15457 | - Jim_FreeHashTableIterator(htiter); | |
| 15458 | 15508 | } |
| 15459 | 15509 | return listObjPtr; |
| 15460 | 15510 | } |
| 15461 | 15511 | |
| 15462 | 15512 | |
| @@ -17795,11 +17845,11 @@ | ||
| 17795 | 17845 | if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { |
| 17796 | 17846 | sig++; |
| 17797 | 17847 | } |
| 17798 | 17848 | |
| 17799 | 17849 | interp->signal_level += sig; |
| 17800 | - if (interp->signal_level && interp->sigmask) { | |
| 17850 | + if (Jim_CheckSignal(interp)) { | |
| 17801 | 17851 | |
| 17802 | 17852 | exitCode = JIM_SIGNAL; |
| 17803 | 17853 | } |
| 17804 | 17854 | else { |
| 17805 | 17855 | exitCode = Jim_EvalObj(interp, argv[0]); |
| @@ -17951,25 +18001,24 @@ | ||
| 17951 | 18001 | |
| 17952 | 18002 | |
| 17953 | 18003 | static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv) |
| 17954 | 18004 | { |
| 17955 | 18005 | Jim_Obj *listObjPtr; |
| 17956 | - Jim_HashTableIterator *htiter; | |
| 18006 | + Jim_HashTableIterator htiter; | |
| 17957 | 18007 | Jim_HashEntry *he; |
| 17958 | 18008 | |
| 17959 | 18009 | listObjPtr = Jim_NewListObj(interp, NULL, 0); |
| 17960 | 18010 | |
| 17961 | - htiter = Jim_GetHashTableIterator(&interp->references); | |
| 17962 | - while ((he = Jim_NextHashEntry(htiter)) != NULL) { | |
| 18011 | + JimInitHashTableIterator(&interp->references, &htiter); | |
| 18012 | + while ((he = Jim_NextHashEntry(&htiter)) != NULL) { | |
| 17963 | 18013 | char buf[JIM_REFERENCE_SPACE + 1]; |
| 17964 | 18014 | Jim_Reference *refPtr = he->u.val; |
| 17965 | 18015 | const unsigned long *refId = he->key; |
| 17966 | 18016 | |
| 17967 | 18017 | JimFormatReference(buf, refPtr, *refId); |
| 17968 | 18018 | Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1)); |
| 17969 | 18019 | } |
| 17970 | - Jim_FreeHashTableIterator(htiter); | |
| 17971 | 18020 | Jim_SetResult(interp, listObjPtr); |
| 17972 | 18021 | return JIM_OK; |
| 17973 | 18022 | } |
| 17974 | 18023 | #endif |
| 17975 | 18024 | |
| @@ -18005,17 +18054,17 @@ | ||
| 18005 | 18054 | { |
| 18006 | 18055 | Jim_HashEntry *he; |
| 18007 | 18056 | Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); |
| 18008 | 18057 | |
| 18009 | 18058 | |
| 18010 | - Jim_HashTableIterator *htiter = Jim_GetHashTableIterator(ht); | |
| 18011 | - while ((he = Jim_NextHashEntry(htiter)) != NULL) { | |
| 18059 | + Jim_HashTableIterator htiter; | |
| 18060 | + JimInitHashTableIterator(ht, &htiter); | |
| 18061 | + while ((he = Jim_NextHashEntry(&htiter)) != NULL) { | |
| 18012 | 18062 | if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) { |
| 18013 | 18063 | callback(interp, listObjPtr, he, type); |
| 18014 | 18064 | } |
| 18015 | 18065 | } |
| 18016 | - Jim_FreeHashTableIterator(htiter); | |
| 18017 | 18066 | |
| 18018 | 18067 | return listObjPtr; |
| 18019 | 18068 | } |
| 18020 | 18069 | |
| 18021 | 18070 | |
| 18022 | 18071 |
| --- autosetup/jimsh0.c | |
| +++ autosetup/jimsh0.c | |
| @@ -185,11 +185,11 @@ | |
| 185 | #endif |
| 186 | |
| 187 | #define UCHAR(c) ((unsigned char)(c)) |
| 188 | |
| 189 | |
| 190 | #define JIM_VERSION 73 |
| 191 | |
| 192 | #define JIM_OK 0 |
| 193 | #define JIM_ERR 1 |
| 194 | #define JIM_RETURN 2 |
| 195 | #define JIM_BREAK 3 |
| @@ -321,14 +321,14 @@ | |
| 321 | #define Jim_GetHashTableSize(ht) ((ht)->size) |
| 322 | #define Jim_GetHashTableUsed(ht) ((ht)->used) |
| 323 | |
| 324 | |
| 325 | typedef struct Jim_Obj { |
| 326 | int refCount; |
| 327 | char *bytes; |
| 328 | int length; |
| 329 | const struct Jim_ObjType *typePtr; |
| 330 | |
| 331 | union { |
| 332 | |
| 333 | jim_wide wideValue; |
| 334 | |
| @@ -665,12 +665,10 @@ | |
| 665 | |
| 666 | |
| 667 | JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); |
| 668 | JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); |
| 669 | JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); |
| 670 | JIM_EXPORT void Jim_InitStringRep (Jim_Obj *objPtr, const char *bytes, |
| 671 | int length); |
| 672 | JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp, |
| 673 | Jim_Obj *objPtr); |
| 674 | JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, |
| 675 | int *lenPtr); |
| 676 | JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr); |
| @@ -877,10 +875,12 @@ | |
| 877 | JIM_EXPORT void Jim_HistoryShow(void); |
| 878 | |
| 879 | |
| 880 | JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); |
| 881 | JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); |
| 882 | |
| 883 | |
| 884 | JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); |
| 885 | JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); |
| 886 | |
| @@ -5671,10 +5671,13 @@ | |
| 5671 | #ifdef JIM_MAINTAINER |
| 5672 | #define JIM_DEBUG_COMMAND |
| 5673 | #define JIM_DEBUG_PANIC |
| 5674 | #endif |
| 5675 | |
| 5676 | const char *jim_tt_name(int type); |
| 5677 | |
| 5678 | #ifdef JIM_DEBUG_PANIC |
| 5679 | static void JimPanicDump(int panic_condition, const char *fmt, ...); |
| 5680 | #define JimPanic(X) JimPanicDump X |
| @@ -5690,10 +5693,11 @@ | |
| 5690 | static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, |
| 5691 | int flags); |
| 5692 | static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands); |
| 5693 | static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); |
| 5694 | static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); |
| 5695 | static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, |
| 5696 | const char *prefix, const char *const *tablePtr, const char *name); |
| 5697 | static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv); |
| 5698 | static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); |
| 5699 | static int JimSign(jim_wide w); |
| @@ -5934,15 +5938,42 @@ | |
| 5934 | } |
| 5935 | return n; |
| 5936 | } |
| 5937 | #endif |
| 5938 | |
| 5939 | int Jim_WideToString(char *buf, jim_wide wideValue) |
| 5940 | { |
| 5941 | const char *fmt = "%" JIM_WIDE_MODIFIER; |
| 5942 | |
| 5943 | return sprintf(buf, fmt, wideValue); |
| 5944 | } |
| 5945 | |
| 5946 | static int JimCheckConversion(const char *str, const char *endptr) |
| 5947 | { |
| 5948 | if (str[0] == '\0' || str == endptr) { |
| @@ -6229,10 +6260,18 @@ | |
| 6229 | ht->sizemask = 0; |
| 6230 | ht->used = 0; |
| 6231 | ht->collisions = 0; |
| 6232 | } |
| 6233 | |
| 6234 | |
| 6235 | int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr) |
| 6236 | { |
| 6237 | JimResetHashTable(ht); |
| 6238 | ht->type = type; |
| @@ -6408,15 +6447,11 @@ | |
| 6408 | } |
| 6409 | |
| 6410 | Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht) |
| 6411 | { |
| 6412 | Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter)); |
| 6413 | |
| 6414 | iter->ht = ht; |
| 6415 | iter->index = -1; |
| 6416 | iter->entry = NULL; |
| 6417 | iter->nextEntry = NULL; |
| 6418 | return iter; |
| 6419 | } |
| 6420 | |
| 6421 | Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter) |
| 6422 | { |
| @@ -10500,12 +10535,10 @@ | |
| 10500 | int Jim_GetExitCode(Jim_Interp *interp) |
| 10501 | { |
| 10502 | return interp->exitCode; |
| 10503 | } |
| 10504 | |
| 10505 | #define JIM_INTEGER_SPACE 24 |
| 10506 | |
| 10507 | static void UpdateStringOfInt(struct Jim_Obj *objPtr); |
| 10508 | static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); |
| 10509 | |
| 10510 | static const Jim_ObjType intObjType = { |
| 10511 | "int", |
| @@ -10522,16 +10555,16 @@ | |
| 10522 | UpdateStringOfInt, |
| 10523 | JIM_TYPE_NONE, |
| 10524 | }; |
| 10525 | |
| 10526 | |
| 10527 | void UpdateStringOfInt(struct Jim_Obj *objPtr) |
| 10528 | { |
| 10529 | int len; |
| 10530 | char buf[JIM_INTEGER_SPACE + 1]; |
| 10531 | |
| 10532 | len = Jim_WideToString(buf, JimWideValue(objPtr)); |
| 10533 | objPtr->bytes = Jim_Alloc(len + 1); |
| 10534 | memcpy(objPtr->bytes, buf, len + 1); |
| 10535 | objPtr->length = len; |
| 10536 | } |
| 10537 | |
| @@ -10755,11 +10788,11 @@ | |
| 10755 | } |
| 10756 | |
| 10757 | #define JIM_ELESTR_SIMPLE 0 |
| 10758 | #define JIM_ELESTR_BRACE 1 |
| 10759 | #define JIM_ELESTR_QUOTE 2 |
| 10760 | static int ListElementQuotingType(const char *s, int len) |
| 10761 | { |
| 10762 | int i, level, blevel, trySimple = 1; |
| 10763 | |
| 10764 | |
| 10765 | if (len == 0) |
| @@ -10903,17 +10936,23 @@ | |
| 10903 | return p - q; |
| 10904 | } |
| 10905 | |
| 10906 | static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc) |
| 10907 | { |
| 10908 | int i, bufLen, realLength; |
| 10909 | const char *strRep; |
| 10910 | char *p; |
| 10911 | int *quotingType; |
| 10912 | |
| 10913 | |
| 10914 | quotingType = Jim_Alloc(sizeof(int) * objc + 1); |
| 10915 | bufLen = 0; |
| 10916 | for (i = 0; i < objc; i++) { |
| 10917 | int len; |
| 10918 | |
| 10919 | strRep = Jim_GetString(objv[i], &len); |
| @@ -10975,11 +11014,14 @@ | |
| 10975 | realLength++; |
| 10976 | } |
| 10977 | } |
| 10978 | *p = '\0'; |
| 10979 | objPtr->length = realLength; |
| 10980 | Jim_Free(quotingType); |
| 10981 | } |
| 10982 | |
| 10983 | static void UpdateStringOfList(struct Jim_Obj *objPtr) |
| 10984 | { |
| 10985 | JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len); |
| @@ -11000,11 +11042,11 @@ | |
| 11000 | if (Jim_IsDict(objPtr) && !Jim_IsShared(objPtr)) { |
| 11001 | Jim_Obj **listObjPtrPtr; |
| 11002 | int len; |
| 11003 | int i; |
| 11004 | |
| 11005 | Jim_DictPairs(interp, objPtr, &listObjPtrPtr, &len); |
| 11006 | for (i = 0; i < len; i++) { |
| 11007 | Jim_IncrRefCount(listObjPtrPtr[i]); |
| 11008 | } |
| 11009 | |
| 11010 | |
| @@ -11225,14 +11267,22 @@ | |
| 11225 | int requiredLen = currentLen + elemc; |
| 11226 | int i; |
| 11227 | Jim_Obj **point; |
| 11228 | |
| 11229 | if (requiredLen > listPtr->internalRep.listValue.maxLen) { |
| 11230 | listPtr->internalRep.listValue.maxLen = requiredLen * 2; |
| 11231 | |
| 11232 | listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele, |
| 11233 | sizeof(Jim_Obj *) * listPtr->internalRep.listValue.maxLen); |
| 11234 | } |
| 11235 | if (idx < 0) { |
| 11236 | idx = currentLen; |
| 11237 | } |
| 11238 | point = listPtr->internalRep.listValue.ele + idx; |
| @@ -11523,55 +11573,53 @@ | |
| 11523 | } |
| 11524 | |
| 11525 | void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) |
| 11526 | { |
| 11527 | Jim_HashTable *ht, *dupHt; |
| 11528 | Jim_HashTableIterator *htiter; |
| 11529 | Jim_HashEntry *he; |
| 11530 | |
| 11531 | |
| 11532 | ht = srcPtr->internalRep.ptr; |
| 11533 | dupHt = Jim_Alloc(sizeof(*dupHt)); |
| 11534 | Jim_InitHashTable(dupHt, &JimDictHashTableType, interp); |
| 11535 | if (ht->size != 0) |
| 11536 | Jim_ExpandHashTable(dupHt, ht->size); |
| 11537 | |
| 11538 | htiter = Jim_GetHashTableIterator(ht); |
| 11539 | while ((he = Jim_NextHashEntry(htiter)) != NULL) { |
| 11540 | const Jim_Obj *keyObjPtr = he->key; |
| 11541 | Jim_Obj *valObjPtr = he->u.val; |
| 11542 | |
| 11543 | Jim_IncrRefCount((Jim_Obj *)keyObjPtr); |
| 11544 | Jim_IncrRefCount(valObjPtr); |
| 11545 | Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr); |
| 11546 | } |
| 11547 | Jim_FreeHashTableIterator(htiter); |
| 11548 | |
| 11549 | dupPtr->internalRep.ptr = dupHt; |
| 11550 | dupPtr->typePtr = &dictObjType; |
| 11551 | } |
| 11552 | |
| 11553 | static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len) |
| 11554 | { |
| 11555 | Jim_HashTable *ht; |
| 11556 | Jim_HashTableIterator *htiter; |
| 11557 | Jim_HashEntry *he; |
| 11558 | Jim_Obj **objv; |
| 11559 | int i; |
| 11560 | |
| 11561 | ht = dictPtr->internalRep.ptr; |
| 11562 | |
| 11563 | |
| 11564 | objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *)); |
| 11565 | htiter = Jim_GetHashTableIterator(ht); |
| 11566 | i = 0; |
| 11567 | while ((he = Jim_NextHashEntry(htiter)) != NULL) { |
| 11568 | objv[i++] = (Jim_Obj *)he->key; |
| 11569 | objv[i++] = he->u.val; |
| 11570 | } |
| 11571 | *len = i; |
| 11572 | Jim_FreeHashTableIterator(htiter); |
| 11573 | return objv; |
| 11574 | } |
| 11575 | |
| 11576 | static void UpdateStringOfDict(struct Jim_Obj *objPtr) |
| 11577 | { |
| @@ -12089,14 +12137,15 @@ | |
| 12089 | |
| 12090 | |
| 12091 | typedef struct Jim_ExprOperator |
| 12092 | { |
| 12093 | const char *name; |
| 12094 | int precedence; |
| 12095 | int arity; |
| 12096 | int (*funcop) (Jim_Interp *interp, struct JimExprState * e); |
| 12097 | int lazy; |
| 12098 | } Jim_ExprOperator; |
| 12099 | |
| 12100 | static void ExprPush(struct JimExprState *e, Jim_Obj *obj) |
| 12101 | { |
| 12102 | Jim_IncrRefCount(obj); |
| @@ -12774,93 +12823,96 @@ | |
| 12774 | LAZY_OP, |
| 12775 | LAZY_LEFT, |
| 12776 | LAZY_RIGHT |
| 12777 | }; |
| 12778 | |
| 12779 | static const struct Jim_ExprOperator Jim_ExprOperators[] = { |
| 12780 | {"*", 200, 2, JimExprOpBin, LAZY_NONE}, |
| 12781 | {"/", 200, 2, JimExprOpBin, LAZY_NONE}, |
| 12782 | {"%", 200, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12783 | |
| 12784 | {"-", 100, 2, JimExprOpBin, LAZY_NONE}, |
| 12785 | {"+", 100, 2, JimExprOpBin, LAZY_NONE}, |
| 12786 | |
| 12787 | {"<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12788 | {">>", 90, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12789 | |
| 12790 | {"<<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12791 | {">>>", 90, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12792 | |
| 12793 | {"<", 80, 2, JimExprOpBin, LAZY_NONE}, |
| 12794 | {">", 80, 2, JimExprOpBin, LAZY_NONE}, |
| 12795 | {"<=", 80, 2, JimExprOpBin, LAZY_NONE}, |
| 12796 | {">=", 80, 2, JimExprOpBin, LAZY_NONE}, |
| 12797 | |
| 12798 | {"==", 70, 2, JimExprOpBin, LAZY_NONE}, |
| 12799 | {"!=", 70, 2, JimExprOpBin, LAZY_NONE}, |
| 12800 | |
| 12801 | {"&", 50, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12802 | {"^", 49, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12803 | {"|", 48, 2, JimExprOpIntBin, LAZY_NONE}, |
| 12804 | |
| 12805 | {"&&", 10, 2, NULL, LAZY_OP}, |
| 12806 | {NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT}, |
| 12807 | {NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT}, |
| 12808 | |
| 12809 | {"||", 9, 2, NULL, LAZY_OP}, |
| 12810 | {NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT}, |
| 12811 | {NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT}, |
| 12812 | |
| 12813 | {"?", 5, 2, JimExprOpNull, LAZY_OP}, |
| 12814 | {NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT}, |
| 12815 | {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, |
| 12816 | |
| 12817 | {":", 5, 2, JimExprOpNull, LAZY_OP}, |
| 12818 | {NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT}, |
| 12819 | {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, |
| 12820 | |
| 12821 | {"**", 250, 2, JimExprOpBin, LAZY_NONE}, |
| 12822 | |
| 12823 | {"eq", 60, 2, JimExprOpStrBin, LAZY_NONE}, |
| 12824 | {"ne", 60, 2, JimExprOpStrBin, LAZY_NONE}, |
| 12825 | |
| 12826 | {"in", 55, 2, JimExprOpStrBin, LAZY_NONE}, |
| 12827 | {"ni", 55, 2, JimExprOpStrBin, LAZY_NONE}, |
| 12828 | |
| 12829 | {"!", 300, 1, JimExprOpNumUnary, LAZY_NONE}, |
| 12830 | {"~", 300, 1, JimExprOpIntUnary, LAZY_NONE}, |
| 12831 | {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, |
| 12832 | {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, |
| 12833 | |
| 12834 | |
| 12835 | |
| 12836 | {"int", 400, 1, JimExprOpNumUnary, LAZY_NONE}, |
| 12837 | {"abs", 400, 1, JimExprOpNumUnary, LAZY_NONE}, |
| 12838 | {"double", 400, 1, JimExprOpNumUnary, LAZY_NONE}, |
| 12839 | {"round", 400, 1, JimExprOpNumUnary, LAZY_NONE}, |
| 12840 | {"rand", 400, 0, JimExprOpNone, LAZY_NONE}, |
| 12841 | {"srand", 400, 1, JimExprOpIntUnary, LAZY_NONE}, |
| 12842 | |
| 12843 | #ifdef JIM_MATH_FUNCTIONS |
| 12844 | {"sin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12845 | {"cos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12846 | {"tan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12847 | {"asin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12848 | {"acos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12849 | {"atan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12850 | {"sinh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12851 | {"cosh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12852 | {"tanh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12853 | {"ceil", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12854 | {"floor", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12855 | {"exp", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12856 | {"log", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12857 | {"log10", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12858 | {"sqrt", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, |
| 12859 | {"pow", 400, 2, JimExprOpBin, LAZY_NONE}, |
| 12860 | #endif |
| 12861 | }; |
| 12862 | |
| 12863 | #define JIM_EXPR_OPERATORS_NUM \ |
| 12864 | (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) |
| 12865 | |
| 12866 | static int JimParseExpression(struct JimParserCtx *pc) |
| @@ -13026,20 +13078,18 @@ | |
| 13026 | int i; |
| 13027 | int bestIdx = -1, bestLen = 0; |
| 13028 | |
| 13029 | |
| 13030 | for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { |
| 13031 | const char *opname; |
| 13032 | int oplen; |
| 13033 | |
| 13034 | opname = Jim_ExprOperators[i].name; |
| 13035 | if (opname == NULL) { |
| 13036 | continue; |
| 13037 | } |
| 13038 | oplen = strlen(opname); |
| 13039 | |
| 13040 | if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) { |
| 13041 | bestIdx = i + JIM_TT_EXPR_OP; |
| 13042 | bestLen = oplen; |
| 13043 | } |
| 13044 | } |
| 13045 | if (bestIdx == -1) { |
| @@ -13111,12 +13161,12 @@ | |
| 13111 | }; |
| 13112 | |
| 13113 | |
| 13114 | typedef struct ExprByteCode |
| 13115 | { |
| 13116 | int len; |
| 13117 | ScriptToken *token; |
| 13118 | int inUse; |
| 13119 | } ExprByteCode; |
| 13120 | |
| 13121 | static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr) |
| 13122 | { |
| @@ -13855,16 +13905,16 @@ | |
| 13855 | |
| 13856 | |
| 13857 | |
| 13858 | typedef struct ScanFmtPartDescr |
| 13859 | { |
| 13860 | char type; |
| 13861 | char modifier; |
| 13862 | size_t width; |
| 13863 | int pos; |
| 13864 | char *arg; |
| 13865 | char *prefix; |
| 13866 | } ScanFmtPartDescr; |
| 13867 | |
| 13868 | |
| 13869 | typedef struct ScanFmtStringObj |
| 13870 | { |
| @@ -14871,12 +14921,12 @@ | |
| 14871 | } |
| 14872 | |
| 14873 | if (retcode == JIM_OK && argc) { |
| 14874 | |
| 14875 | retcode = JimInvokeCommand(interp, argc, argv); |
| 14876 | if (interp->signal_level && interp->sigmask) { |
| 14877 | |
| 14878 | retcode = JIM_SIGNAL; |
| 14879 | } |
| 14880 | } |
| 14881 | |
| 14882 | |
| @@ -15446,17 +15496,17 @@ | |
| 15446 | if (he) { |
| 15447 | callback(interp, listObjPtr, he, type); |
| 15448 | } |
| 15449 | } |
| 15450 | else { |
| 15451 | Jim_HashTableIterator *htiter = Jim_GetHashTableIterator(ht); |
| 15452 | while ((he = Jim_NextHashEntry(htiter)) != NULL) { |
| 15453 | if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) { |
| 15454 | callback(interp, listObjPtr, he, type); |
| 15455 | } |
| 15456 | } |
| 15457 | Jim_FreeHashTableIterator(htiter); |
| 15458 | } |
| 15459 | return listObjPtr; |
| 15460 | } |
| 15461 | |
| 15462 | |
| @@ -17795,11 +17845,11 @@ | |
| 17795 | if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { |
| 17796 | sig++; |
| 17797 | } |
| 17798 | |
| 17799 | interp->signal_level += sig; |
| 17800 | if (interp->signal_level && interp->sigmask) { |
| 17801 | |
| 17802 | exitCode = JIM_SIGNAL; |
| 17803 | } |
| 17804 | else { |
| 17805 | exitCode = Jim_EvalObj(interp, argv[0]); |
| @@ -17951,25 +18001,24 @@ | |
| 17951 | |
| 17952 | |
| 17953 | static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv) |
| 17954 | { |
| 17955 | Jim_Obj *listObjPtr; |
| 17956 | Jim_HashTableIterator *htiter; |
| 17957 | Jim_HashEntry *he; |
| 17958 | |
| 17959 | listObjPtr = Jim_NewListObj(interp, NULL, 0); |
| 17960 | |
| 17961 | htiter = Jim_GetHashTableIterator(&interp->references); |
| 17962 | while ((he = Jim_NextHashEntry(htiter)) != NULL) { |
| 17963 | char buf[JIM_REFERENCE_SPACE + 1]; |
| 17964 | Jim_Reference *refPtr = he->u.val; |
| 17965 | const unsigned long *refId = he->key; |
| 17966 | |
| 17967 | JimFormatReference(buf, refPtr, *refId); |
| 17968 | Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1)); |
| 17969 | } |
| 17970 | Jim_FreeHashTableIterator(htiter); |
| 17971 | Jim_SetResult(interp, listObjPtr); |
| 17972 | return JIM_OK; |
| 17973 | } |
| 17974 | #endif |
| 17975 | |
| @@ -18005,17 +18054,17 @@ | |
| 18005 | { |
| 18006 | Jim_HashEntry *he; |
| 18007 | Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); |
| 18008 | |
| 18009 | |
| 18010 | Jim_HashTableIterator *htiter = Jim_GetHashTableIterator(ht); |
| 18011 | while ((he = Jim_NextHashEntry(htiter)) != NULL) { |
| 18012 | if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) { |
| 18013 | callback(interp, listObjPtr, he, type); |
| 18014 | } |
| 18015 | } |
| 18016 | Jim_FreeHashTableIterator(htiter); |
| 18017 | |
| 18018 | return listObjPtr; |
| 18019 | } |
| 18020 | |
| 18021 | |
| 18022 |
| --- autosetup/jimsh0.c | |
| +++ autosetup/jimsh0.c | |
| @@ -185,11 +185,11 @@ | |
| 185 | #endif |
| 186 | |
| 187 | #define UCHAR(c) ((unsigned char)(c)) |
| 188 | |
| 189 | |
| 190 | #define JIM_VERSION 74 |
| 191 | |
| 192 | #define JIM_OK 0 |
| 193 | #define JIM_ERR 1 |
| 194 | #define JIM_RETURN 2 |
| 195 | #define JIM_BREAK 3 |
| @@ -321,14 +321,14 @@ | |
| 321 | #define Jim_GetHashTableSize(ht) ((ht)->size) |
| 322 | #define Jim_GetHashTableUsed(ht) ((ht)->used) |
| 323 | |
| 324 | |
| 325 | typedef struct Jim_Obj { |
| 326 | char *bytes; |
| 327 | const struct Jim_ObjType *typePtr; |
| 328 | int refCount; |
| 329 | int length; |
| 330 | |
| 331 | union { |
| 332 | |
| 333 | jim_wide wideValue; |
| 334 | |
| @@ -665,12 +665,10 @@ | |
| 665 | |
| 666 | |
| 667 | JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); |
| 668 | JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); |
| 669 | JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); |
| 670 | JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp, |
| 671 | Jim_Obj *objPtr); |
| 672 | JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, |
| 673 | int *lenPtr); |
| 674 | JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr); |
| @@ -877,10 +875,12 @@ | |
| 875 | JIM_EXPORT void Jim_HistoryShow(void); |
| 876 | |
| 877 | |
| 878 | JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); |
| 879 | JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); |
| 880 | JIM_EXPORT int Jim_CheckSignal(Jim_Interp *interp); |
| 881 | #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask) |
| 882 | |
| 883 | |
| 884 | JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); |
| 885 | JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); |
| 886 | |
| @@ -5671,10 +5671,13 @@ | |
| 5671 | #ifdef JIM_MAINTAINER |
| 5672 | #define JIM_DEBUG_COMMAND |
| 5673 | #define JIM_DEBUG_PANIC |
| 5674 | #endif |
| 5675 | |
| 5676 | |
| 5677 | #define JIM_INTEGER_SPACE 24 |
| 5678 | |
| 5679 | const char *jim_tt_name(int type); |
| 5680 | |
| 5681 | #ifdef JIM_DEBUG_PANIC |
| 5682 | static void JimPanicDump(int panic_condition, const char *fmt, ...); |
| 5683 | #define JimPanic(X) JimPanicDump X |
| @@ -5690,10 +5693,11 @@ | |
| 5693 | static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, |
| 5694 | int flags); |
| 5695 | static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands); |
| 5696 | static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); |
| 5697 | static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); |
| 5698 | static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len); |
| 5699 | static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, |
| 5700 | const char *prefix, const char *const *tablePtr, const char *name); |
| 5701 | static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv); |
| 5702 | static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); |
| 5703 | static int JimSign(jim_wide w); |
| @@ -5934,15 +5938,42 @@ | |
| 5938 | } |
| 5939 | return n; |
| 5940 | } |
| 5941 | #endif |
| 5942 | |
| 5943 | static int JimWideToString(char *buf, jim_wide wideValue) |
| 5944 | { |
| 5945 | int pos = 0; |
| 5946 | |
| 5947 | if (wideValue == 0) { |
| 5948 | buf[pos++] = '0'; |
| 5949 | } |
| 5950 | else { |
| 5951 | char tmp[JIM_INTEGER_SPACE]; |
| 5952 | int num = 0; |
| 5953 | int i; |
| 5954 | |
| 5955 | if (wideValue < 0) { |
| 5956 | buf[pos++] = '-'; |
| 5957 | |
| 5958 | i = wideValue % 10; |
| 5959 | tmp[num++] = (i > 0) ? (10 - i) : -i; |
| 5960 | wideValue /= -10; |
| 5961 | } |
| 5962 | |
| 5963 | while (wideValue) { |
| 5964 | tmp[num++] = wideValue % 10; |
| 5965 | wideValue /= 10; |
| 5966 | } |
| 5967 | |
| 5968 | for (i = 0; i < num; i++) { |
| 5969 | buf[pos++] = '0' + tmp[num - i - 1]; |
| 5970 | } |
| 5971 | } |
| 5972 | buf[pos] = 0; |
| 5973 | |
| 5974 | return pos; |
| 5975 | } |
| 5976 | |
| 5977 | static int JimCheckConversion(const char *str, const char *endptr) |
| 5978 | { |
| 5979 | if (str[0] == '\0' || str == endptr) { |
| @@ -6229,10 +6260,18 @@ | |
| 6260 | ht->sizemask = 0; |
| 6261 | ht->used = 0; |
| 6262 | ht->collisions = 0; |
| 6263 | } |
| 6264 | |
| 6265 | static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter) |
| 6266 | { |
| 6267 | iter->ht = ht; |
| 6268 | iter->index = -1; |
| 6269 | iter->entry = NULL; |
| 6270 | iter->nextEntry = NULL; |
| 6271 | } |
| 6272 | |
| 6273 | |
| 6274 | int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr) |
| 6275 | { |
| 6276 | JimResetHashTable(ht); |
| 6277 | ht->type = type; |
| @@ -6408,15 +6447,11 @@ | |
| 6447 | } |
| 6448 | |
| 6449 | Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht) |
| 6450 | { |
| 6451 | Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter)); |
| 6452 | JimInitHashTableIterator(ht, iter); |
| 6453 | return iter; |
| 6454 | } |
| 6455 | |
| 6456 | Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter) |
| 6457 | { |
| @@ -10500,12 +10535,10 @@ | |
| 10535 | int Jim_GetExitCode(Jim_Interp *interp) |
| 10536 | { |
| 10537 | return interp->exitCode; |
| 10538 | } |
| 10539 | |
| 10540 | static void UpdateStringOfInt(struct Jim_Obj *objPtr); |
| 10541 | static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); |
| 10542 | |
| 10543 | static const Jim_ObjType intObjType = { |
| 10544 | "int", |
| @@ -10522,16 +10555,16 @@ | |
| 10555 | UpdateStringOfInt, |
| 10556 | JIM_TYPE_NONE, |
| 10557 | }; |
| 10558 | |
| 10559 | |
| 10560 | static void UpdateStringOfInt(struct Jim_Obj *objPtr) |
| 10561 | { |
| 10562 | int len; |
| 10563 | char buf[JIM_INTEGER_SPACE + 1]; |
| 10564 | |
| 10565 | len = JimWideToString(buf, JimWideValue(objPtr)); |
| 10566 | objPtr->bytes = Jim_Alloc(len + 1); |
| 10567 | memcpy(objPtr->bytes, buf, len + 1); |
| 10568 | objPtr->length = len; |
| 10569 | } |
| 10570 | |
| @@ -10755,11 +10788,11 @@ | |
| 10788 | } |
| 10789 | |
| 10790 | #define JIM_ELESTR_SIMPLE 0 |
| 10791 | #define JIM_ELESTR_BRACE 1 |
| 10792 | #define JIM_ELESTR_QUOTE 2 |
| 10793 | static unsigned char ListElementQuotingType(const char *s, int len) |
| 10794 | { |
| 10795 | int i, level, blevel, trySimple = 1; |
| 10796 | |
| 10797 | |
| 10798 | if (len == 0) |
| @@ -10903,17 +10936,23 @@ | |
| 10936 | return p - q; |
| 10937 | } |
| 10938 | |
| 10939 | static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc) |
| 10940 | { |
| 10941 | #define STATIC_QUOTING_LEN 32 |
| 10942 | int i, bufLen, realLength; |
| 10943 | const char *strRep; |
| 10944 | char *p; |
| 10945 | unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN]; |
| 10946 | |
| 10947 | |
| 10948 | if (objc > STATIC_QUOTING_LEN) { |
| 10949 | quotingType = Jim_Alloc(objc); |
| 10950 | } |
| 10951 | else { |
| 10952 | quotingType = staticQuoting; |
| 10953 | } |
| 10954 | bufLen = 0; |
| 10955 | for (i = 0; i < objc; i++) { |
| 10956 | int len; |
| 10957 | |
| 10958 | strRep = Jim_GetString(objv[i], &len); |
| @@ -10975,11 +11014,14 @@ | |
| 11014 | realLength++; |
| 11015 | } |
| 11016 | } |
| 11017 | *p = '\0'; |
| 11018 | objPtr->length = realLength; |
| 11019 | |
| 11020 | if (quotingType != staticQuoting) { |
| 11021 | Jim_Free(quotingType); |
| 11022 | } |
| 11023 | } |
| 11024 | |
| 11025 | static void UpdateStringOfList(struct Jim_Obj *objPtr) |
| 11026 | { |
| 11027 | JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len); |
| @@ -11000,11 +11042,11 @@ | |
| 11042 | if (Jim_IsDict(objPtr) && !Jim_IsShared(objPtr)) { |
| 11043 | Jim_Obj **listObjPtrPtr; |
| 11044 | int len; |
| 11045 | int i; |
| 11046 | |
| 11047 | listObjPtrPtr = JimDictPairs(objPtr, &len); |
| 11048 | for (i = 0; i < len; i++) { |
| 11049 | Jim_IncrRefCount(listObjPtrPtr[i]); |
| 11050 | } |
| 11051 | |
| 11052 | |
| @@ -11225,14 +11267,22 @@ | |
| 11267 | int requiredLen = currentLen + elemc; |
| 11268 | int i; |
| 11269 | Jim_Obj **point; |
| 11270 | |
| 11271 | if (requiredLen > listPtr->internalRep.listValue.maxLen) { |
| 11272 | if (requiredLen < 2) { |
| 11273 | |
| 11274 | requiredLen = 4; |
| 11275 | } |
| 11276 | else { |
| 11277 | requiredLen *= 2; |
| 11278 | } |
| 11279 | |
| 11280 | listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele, |
| 11281 | sizeof(Jim_Obj *) * requiredLen); |
| 11282 | |
| 11283 | listPtr->internalRep.listValue.maxLen = requiredLen; |
| 11284 | } |
| 11285 | if (idx < 0) { |
| 11286 | idx = currentLen; |
| 11287 | } |
| 11288 | point = listPtr->internalRep.listValue.ele + idx; |
| @@ -11523,55 +11573,53 @@ | |
| 11573 | } |
| 11574 | |
| 11575 | void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) |
| 11576 | { |
| 11577 | Jim_HashTable *ht, *dupHt; |
| 11578 | Jim_HashTableIterator htiter; |
| 11579 | Jim_HashEntry *he; |
| 11580 | |
| 11581 | |
| 11582 | ht = srcPtr->internalRep.ptr; |
| 11583 | dupHt = Jim_Alloc(sizeof(*dupHt)); |
| 11584 | Jim_InitHashTable(dupHt, &JimDictHashTableType, interp); |
| 11585 | if (ht->size != 0) |
| 11586 | Jim_ExpandHashTable(dupHt, ht->size); |
| 11587 | |
| 11588 | JimInitHashTableIterator(ht, &htiter); |
| 11589 | while ((he = Jim_NextHashEntry(&htiter)) != NULL) { |
| 11590 | const Jim_Obj *keyObjPtr = he->key; |
| 11591 | Jim_Obj *valObjPtr = he->u.val; |
| 11592 | |
| 11593 | Jim_IncrRefCount((Jim_Obj *)keyObjPtr); |
| 11594 | Jim_IncrRefCount(valObjPtr); |
| 11595 | Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr); |
| 11596 | } |
| 11597 | |
| 11598 | dupPtr->internalRep.ptr = dupHt; |
| 11599 | dupPtr->typePtr = &dictObjType; |
| 11600 | } |
| 11601 | |
| 11602 | static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len) |
| 11603 | { |
| 11604 | Jim_HashTable *ht; |
| 11605 | Jim_HashTableIterator htiter; |
| 11606 | Jim_HashEntry *he; |
| 11607 | Jim_Obj **objv; |
| 11608 | int i; |
| 11609 | |
| 11610 | ht = dictPtr->internalRep.ptr; |
| 11611 | |
| 11612 | |
| 11613 | objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *)); |
| 11614 | JimInitHashTableIterator(ht, &htiter); |
| 11615 | i = 0; |
| 11616 | while ((he = Jim_NextHashEntry(&htiter)) != NULL) { |
| 11617 | objv[i++] = (Jim_Obj *)he->key; |
| 11618 | objv[i++] = he->u.val; |
| 11619 | } |
| 11620 | *len = i; |
| 11621 | return objv; |
| 11622 | } |
| 11623 | |
| 11624 | static void UpdateStringOfDict(struct Jim_Obj *objPtr) |
| 11625 | { |
| @@ -12089,14 +12137,15 @@ | |
| 12137 | |
| 12138 | |
| 12139 | typedef struct Jim_ExprOperator |
| 12140 | { |
| 12141 | const char *name; |
| 12142 | int (*funcop) (Jim_Interp *interp, struct JimExprState * e); |
| 12143 | unsigned char precedence; |
| 12144 | unsigned char arity; |
| 12145 | unsigned char lazy; |
| 12146 | unsigned char namelen; |
| 12147 | } Jim_ExprOperator; |
| 12148 | |
| 12149 | static void ExprPush(struct JimExprState *e, Jim_Obj *obj) |
| 12150 | { |
| 12151 | Jim_IncrRefCount(obj); |
| @@ -12774,93 +12823,96 @@ | |
| 12823 | LAZY_OP, |
| 12824 | LAZY_LEFT, |
| 12825 | LAZY_RIGHT |
| 12826 | }; |
| 12827 | |
| 12828 | #define OPRINIT(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1} |
| 12829 | |
| 12830 | static const struct Jim_ExprOperator Jim_ExprOperators[] = { |
| 12831 | OPRINIT("*", 110, 2, JimExprOpBin, LAZY_NONE), |
| 12832 | OPRINIT("/", 110, 2, JimExprOpBin, LAZY_NONE), |
| 12833 | OPRINIT("%", 110, 2, JimExprOpIntBin, LAZY_NONE), |
| 12834 | |
| 12835 | OPRINIT("-", 100, 2, JimExprOpBin, LAZY_NONE), |
| 12836 | OPRINIT("+", 100, 2, JimExprOpBin, LAZY_NONE), |
| 12837 | |
| 12838 | OPRINIT("<<", 90, 2, JimExprOpIntBin, LAZY_NONE), |
| 12839 | OPRINIT(">>", 90, 2, JimExprOpIntBin, LAZY_NONE), |
| 12840 | |
| 12841 | OPRINIT("<<<", 90, 2, JimExprOpIntBin, LAZY_NONE), |
| 12842 | OPRINIT(">>>", 90, 2, JimExprOpIntBin, LAZY_NONE), |
| 12843 | |
| 12844 | OPRINIT("<", 80, 2, JimExprOpBin, LAZY_NONE), |
| 12845 | OPRINIT(">", 80, 2, JimExprOpBin, LAZY_NONE), |
| 12846 | OPRINIT("<=", 80, 2, JimExprOpBin, LAZY_NONE), |
| 12847 | OPRINIT(">=", 80, 2, JimExprOpBin, LAZY_NONE), |
| 12848 | |
| 12849 | OPRINIT("==", 70, 2, JimExprOpBin, LAZY_NONE), |
| 12850 | OPRINIT("!=", 70, 2, JimExprOpBin, LAZY_NONE), |
| 12851 | |
| 12852 | OPRINIT("&", 50, 2, JimExprOpIntBin, LAZY_NONE), |
| 12853 | OPRINIT("^", 49, 2, JimExprOpIntBin, LAZY_NONE), |
| 12854 | OPRINIT("|", 48, 2, JimExprOpIntBin, LAZY_NONE), |
| 12855 | |
| 12856 | OPRINIT("&&", 10, 2, NULL, LAZY_OP), |
| 12857 | OPRINIT(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT), |
| 12858 | OPRINIT(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT), |
| 12859 | |
| 12860 | OPRINIT("||", 9, 2, NULL, LAZY_OP), |
| 12861 | OPRINIT(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT), |
| 12862 | OPRINIT(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT), |
| 12863 | |
| 12864 | OPRINIT("?", 5, 2, JimExprOpNull, LAZY_OP), |
| 12865 | OPRINIT(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT), |
| 12866 | OPRINIT(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), |
| 12867 | |
| 12868 | OPRINIT(":", 5, 2, JimExprOpNull, LAZY_OP), |
| 12869 | OPRINIT(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT), |
| 12870 | OPRINIT(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), |
| 12871 | |
| 12872 | OPRINIT("**", 250, 2, JimExprOpBin, LAZY_NONE), |
| 12873 | |
| 12874 | OPRINIT("eq", 60, 2, JimExprOpStrBin, LAZY_NONE), |
| 12875 | OPRINIT("ne", 60, 2, JimExprOpStrBin, LAZY_NONE), |
| 12876 | |
| 12877 | OPRINIT("in", 55, 2, JimExprOpStrBin, LAZY_NONE), |
| 12878 | OPRINIT("ni", 55, 2, JimExprOpStrBin, LAZY_NONE), |
| 12879 | |
| 12880 | OPRINIT("!", 150, 1, JimExprOpNumUnary, LAZY_NONE), |
| 12881 | OPRINIT("~", 150, 1, JimExprOpIntUnary, LAZY_NONE), |
| 12882 | OPRINIT(NULL, 150, 1, JimExprOpNumUnary, LAZY_NONE), |
| 12883 | OPRINIT(NULL, 150, 1, JimExprOpNumUnary, LAZY_NONE), |
| 12884 | |
| 12885 | |
| 12886 | |
| 12887 | OPRINIT("int", 200, 1, JimExprOpNumUnary, LAZY_NONE), |
| 12888 | OPRINIT("abs", 200, 1, JimExprOpNumUnary, LAZY_NONE), |
| 12889 | OPRINIT("double", 200, 1, JimExprOpNumUnary, LAZY_NONE), |
| 12890 | OPRINIT("round", 200, 1, JimExprOpNumUnary, LAZY_NONE), |
| 12891 | OPRINIT("rand", 200, 0, JimExprOpNone, LAZY_NONE), |
| 12892 | OPRINIT("srand", 200, 1, JimExprOpIntUnary, LAZY_NONE), |
| 12893 | |
| 12894 | #ifdef JIM_MATH_FUNCTIONS |
| 12895 | OPRINIT("sin", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12896 | OPRINIT("cos", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12897 | OPRINIT("tan", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12898 | OPRINIT("asin", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12899 | OPRINIT("acos", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12900 | OPRINIT("atan", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12901 | OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12902 | OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12903 | OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12904 | OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12905 | OPRINIT("floor", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12906 | OPRINIT("exp", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12907 | OPRINIT("log", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12908 | OPRINIT("log10", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12909 | OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary, LAZY_NONE), |
| 12910 | OPRINIT("pow", 200, 2, JimExprOpBin, LAZY_NONE), |
| 12911 | #endif |
| 12912 | }; |
| 12913 | #undef OPRINIT |
| 12914 | |
| 12915 | #define JIM_EXPR_OPERATORS_NUM \ |
| 12916 | (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) |
| 12917 | |
| 12918 | static int JimParseExpression(struct JimParserCtx *pc) |
| @@ -13026,20 +13078,18 @@ | |
| 13078 | int i; |
| 13079 | int bestIdx = -1, bestLen = 0; |
| 13080 | |
| 13081 | |
| 13082 | for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { |
| 13083 | const char * const opname = Jim_ExprOperators[i].name; |
| 13084 | const int oplen = Jim_ExprOperators[i].namelen; |
| 13085 | |
| 13086 | if (opname == NULL || opname[0] != pc->p[0]) { |
| 13087 | continue; |
| 13088 | } |
| 13089 | |
| 13090 | if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) { |
| 13091 | bestIdx = i + JIM_TT_EXPR_OP; |
| 13092 | bestLen = oplen; |
| 13093 | } |
| 13094 | } |
| 13095 | if (bestIdx == -1) { |
| @@ -13111,12 +13161,12 @@ | |
| 13161 | }; |
| 13162 | |
| 13163 | |
| 13164 | typedef struct ExprByteCode |
| 13165 | { |
| 13166 | ScriptToken *token; |
| 13167 | int len; |
| 13168 | int inUse; |
| 13169 | } ExprByteCode; |
| 13170 | |
| 13171 | static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr) |
| 13172 | { |
| @@ -13855,16 +13905,16 @@ | |
| 13905 | |
| 13906 | |
| 13907 | |
| 13908 | typedef struct ScanFmtPartDescr |
| 13909 | { |
| 13910 | char *arg; |
| 13911 | char *prefix; |
| 13912 | size_t width; |
| 13913 | int pos; |
| 13914 | char type; |
| 13915 | char modifier; |
| 13916 | } ScanFmtPartDescr; |
| 13917 | |
| 13918 | |
| 13919 | typedef struct ScanFmtStringObj |
| 13920 | { |
| @@ -14871,12 +14921,12 @@ | |
| 14921 | } |
| 14922 | |
| 14923 | if (retcode == JIM_OK && argc) { |
| 14924 | |
| 14925 | retcode = JimInvokeCommand(interp, argc, argv); |
| 14926 | |
| 14927 | if (Jim_CheckSignal(interp)) { |
| 14928 | retcode = JIM_SIGNAL; |
| 14929 | } |
| 14930 | } |
| 14931 | |
| 14932 | |
| @@ -15446,17 +15496,17 @@ | |
| 15496 | if (he) { |
| 15497 | callback(interp, listObjPtr, he, type); |
| 15498 | } |
| 15499 | } |
| 15500 | else { |
| 15501 | Jim_HashTableIterator htiter; |
| 15502 | JimInitHashTableIterator(ht, &htiter); |
| 15503 | while ((he = Jim_NextHashEntry(&htiter)) != NULL) { |
| 15504 | if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) { |
| 15505 | callback(interp, listObjPtr, he, type); |
| 15506 | } |
| 15507 | } |
| 15508 | } |
| 15509 | return listObjPtr; |
| 15510 | } |
| 15511 | |
| 15512 | |
| @@ -17795,11 +17845,11 @@ | |
| 17845 | if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { |
| 17846 | sig++; |
| 17847 | } |
| 17848 | |
| 17849 | interp->signal_level += sig; |
| 17850 | if (Jim_CheckSignal(interp)) { |
| 17851 | |
| 17852 | exitCode = JIM_SIGNAL; |
| 17853 | } |
| 17854 | else { |
| 17855 | exitCode = Jim_EvalObj(interp, argv[0]); |
| @@ -17951,25 +18001,24 @@ | |
| 18001 | |
| 18002 | |
| 18003 | static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv) |
| 18004 | { |
| 18005 | Jim_Obj *listObjPtr; |
| 18006 | Jim_HashTableIterator htiter; |
| 18007 | Jim_HashEntry *he; |
| 18008 | |
| 18009 | listObjPtr = Jim_NewListObj(interp, NULL, 0); |
| 18010 | |
| 18011 | JimInitHashTableIterator(&interp->references, &htiter); |
| 18012 | while ((he = Jim_NextHashEntry(&htiter)) != NULL) { |
| 18013 | char buf[JIM_REFERENCE_SPACE + 1]; |
| 18014 | Jim_Reference *refPtr = he->u.val; |
| 18015 | const unsigned long *refId = he->key; |
| 18016 | |
| 18017 | JimFormatReference(buf, refPtr, *refId); |
| 18018 | Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1)); |
| 18019 | } |
| 18020 | Jim_SetResult(interp, listObjPtr); |
| 18021 | return JIM_OK; |
| 18022 | } |
| 18023 | #endif |
| 18024 | |
| @@ -18005,17 +18054,17 @@ | |
| 18054 | { |
| 18055 | Jim_HashEntry *he; |
| 18056 | Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); |
| 18057 | |
| 18058 | |
| 18059 | Jim_HashTableIterator htiter; |
| 18060 | JimInitHashTableIterator(ht, &htiter); |
| 18061 | while ((he = Jim_NextHashEntry(&htiter)) != NULL) { |
| 18062 | if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) { |
| 18063 | callback(interp, listObjPtr, he, type); |
| 18064 | } |
| 18065 | } |
| 18066 | |
| 18067 | return listObjPtr; |
| 18068 | } |
| 18069 | |
| 18070 | |
| 18071 |
+129
-58
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -248,11 +248,11 @@ | ||
| 248 | 248 | ** COMMAND: ls |
| 249 | 249 | ** |
| 250 | 250 | ** Usage: %fossil ls ?OPTIONS? ?VERSION? ?FILENAMES? |
| 251 | 251 | ** |
| 252 | 252 | ** Show the names of all files in the current checkout. The -v provides |
| 253 | -** extra information about each file. If FILENAMES are included, the only | |
| 253 | +** extra information about each file. If FILENAMES are included, only | |
| 254 | 254 | ** the files listed (or their children if they are directories) are shown. |
| 255 | 255 | ** |
| 256 | 256 | ** Options: |
| 257 | 257 | ** --age Show when each file was committed |
| 258 | 258 | ** -v|--verbose Provide extra information about each file. |
| @@ -363,27 +363,28 @@ | ||
| 363 | 363 | } |
| 364 | 364 | db_finalize(&q); |
| 365 | 365 | } |
| 366 | 366 | |
| 367 | 367 | /* |
| 368 | -** Create a TEMP table named SFILE and add all unmanaged files named on the command-line | |
| 369 | -** to that table. If directories are named, then add all unmanaged files contained | |
| 370 | -** underneath those directories. If there are no files or directories named on the | |
| 371 | -** command-line, then add all unmanaged files anywhere in the checkout. | |
| 368 | +** Create a TEMP table named SFILE and add all unmanaged files named on | |
| 369 | +** the command-line to that table. If directories are named, then add | |
| 370 | +** all unmanaged files contained underneath those directories. If there | |
| 371 | +** are no files or directories named on the command-line, then add all | |
| 372 | +** unmanaged files anywhere in the checkout. | |
| 372 | 373 | */ |
| 373 | 374 | static void locate_unmanaged_files( |
| 374 | - int argc, /* Number of command-line arguments to examine */ | |
| 375 | - char **argv, /* values of command-line arguments */ | |
| 376 | - unsigned scanFlags, /* Zero or more SCAN_xxx flags */ | |
| 377 | - Glob *pIgnore1, /* Do not add files that match this GLOB */ | |
| 378 | - Glob *pIgnore2 /* Omit files matching this GLOB too */ | |
| 375 | + int argc, /* Number of command-line arguments to examine */ | |
| 376 | + char **argv, /* values of command-line arguments */ | |
| 377 | + unsigned scanFlags, /* Zero or more SCAN_xxx flags */ | |
| 378 | + Glob *pIgnore1, /* Do not add files that match this GLOB */ | |
| 379 | + Glob *pIgnore2 /* Omit files matching this GLOB too */ | |
| 379 | 380 | ){ |
| 380 | - Blob name; /* Name of a candidate file or directory */ | |
| 381 | - char *zName; /* Name of a candidate file or directory */ | |
| 382 | - int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ | |
| 383 | - int i; /* Loop counter */ | |
| 384 | - int nRoot; /* length of g.zLocalRoot */ | |
| 381 | + Blob name; /* Name of a candidate file or directory */ | |
| 382 | + char *zName; /* Name of a candidate file or directory */ | |
| 383 | + int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ | |
| 384 | + int i; /* Loop counter */ | |
| 385 | + int nRoot; /* length of g.zLocalRoot */ | |
| 385 | 386 | |
| 386 | 387 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 387 | 388 | filename_collation()); |
| 388 | 389 | nRoot = (int)strlen(g.zLocalRoot); |
| 389 | 390 | if( argc==0 ){ |
| @@ -506,12 +507,28 @@ | ||
| 506 | 507 | ** Files and subdirectories whose names begin with "." are |
| 507 | 508 | ** normally kept. They are handled if the "--dotfiles" option |
| 508 | 509 | ** is used. |
| 509 | 510 | ** |
| 510 | 511 | ** Options: |
| 512 | +** --allckouts Check for empty directories within any checkouts | |
| 513 | +** that may be nested within the current one. This | |
| 514 | +** option should be used with great care because the | |
| 515 | +** empty-dirs setting (and other applicable settings) | |
| 516 | +** belonging to the other repositories, if any, will | |
| 517 | +** not be checked. | |
| 511 | 518 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 519 | +** --dirsonly Only remove empty directories. No files will | |
| 520 | +** be removed. Using this option will automatically | |
| 521 | +** enable the --emptydirs option as well. | |
| 512 | 522 | ** --dotfiles Include files beginning with a dot ("."). |
| 523 | +** --emptydirs Remove any empty directories that are not | |
| 524 | +** explicitly exempted via the empty-dirs setting | |
| 525 | +** or another applicable setting or command line | |
| 526 | +** argument. Matching files, if any, are removed | |
| 527 | +** prior to checking for any empty directories; | |
| 528 | +** therefore, directories that contain only files | |
| 529 | +** that were removed will be removed as well. | |
| 513 | 530 | ** -f|--force Remove files without prompting. |
| 514 | 531 | ** --clean <CSG> Never prompt for files matching this |
| 515 | 532 | ** comma separated list of glob patterns. |
| 516 | 533 | ** --ignore <CSG> Ignore files matching patterns from the |
| 517 | 534 | ** comma separated list of glob patterns. |
| @@ -522,25 +539,27 @@ | ||
| 522 | 539 | ** -v|--verbose Show all files as they are removed. |
| 523 | 540 | ** |
| 524 | 541 | ** See also: addremove, extra, status |
| 525 | 542 | */ |
| 526 | 543 | void clean_cmd(void){ |
| 527 | - int allFlag, dryRunFlag, verboseFlag; | |
| 544 | + int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; | |
| 545 | + int emptyDirsFlag, dirsOnlyFlag; | |
| 528 | 546 | unsigned scanFlags = 0; |
| 529 | 547 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 530 | - Blob repo; | |
| 531 | - Stmt q; | |
| 532 | 548 | Glob *pIgnore, *pKeep, *pClean; |
| 533 | 549 | int nRoot; |
| 534 | 550 | |
| 535 | 551 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 536 | 552 | if( !dryRunFlag ){ |
| 537 | 553 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 538 | 554 | } |
| 539 | - allFlag = find_option("force","f",0)!=0; | |
| 555 | + allFileFlag = allDirFlag = find_option("force","f",0)!=0; | |
| 556 | + dirsOnlyFlag = find_option("dirsonly",0,0)!=0; | |
| 557 | + emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag; | |
| 540 | 558 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 541 | 559 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 560 | + if( find_option("allckouts",0,0)!=0 ) scanFlags |= SCAN_NESTED; | |
| 542 | 561 | zIgnoreFlag = find_option("ignore",0,1); |
| 543 | 562 | verboseFlag = find_option("verbose","v",0)!=0; |
| 544 | 563 | zKeepFlag = find_option("keep",0,1); |
| 545 | 564 | zCleanFlag = find_option("clean",0,1); |
| 546 | 565 | capture_case_sensitive_option(); |
| @@ -556,51 +575,99 @@ | ||
| 556 | 575 | } |
| 557 | 576 | verify_all_options(); |
| 558 | 577 | pIgnore = glob_create(zIgnoreFlag); |
| 559 | 578 | pKeep = glob_create(zKeepFlag); |
| 560 | 579 | pClean = glob_create(zCleanFlag); |
| 561 | - locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, pKeep); | |
| 562 | - glob_free(pKeep); | |
| 563 | - glob_free(pIgnore); | |
| 564 | - db_prepare(&q, | |
| 565 | - "SELECT %Q || x FROM sfile" | |
| 566 | - " WHERE x NOT IN (%s)" | |
| 567 | - " ORDER BY 1", | |
| 568 | - g.zLocalRoot, fossil_all_reserved_names(0) | |
| 569 | - ); | |
| 570 | - if( file_tree_name(g.zRepositoryName, &repo, 0) ){ | |
| 571 | - db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); | |
| 572 | - } | |
| 573 | - db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); | |
| 574 | 580 | nRoot = (int)strlen(g.zLocalRoot); |
| 575 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 576 | - const char *zName = db_column_text(&q, 0); | |
| 577 | - if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ | |
| 578 | - Blob ans; | |
| 579 | - char cReply; | |
| 580 | - char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", | |
| 581 | - zName+nRoot); | |
| 582 | - blob_zero(&ans); | |
| 583 | - prompt_user(prompt, &ans); | |
| 584 | - cReply = blob_str(&ans)[0]; | |
| 585 | - if( cReply=='a' || cReply=='A' ){ | |
| 586 | - allFlag = 1; | |
| 587 | - }else if( cReply!='y' && cReply!='Y' ){ | |
| 588 | - blob_reset(&ans); | |
| 589 | - continue; | |
| 590 | - } | |
| 591 | - blob_reset(&ans); | |
| 592 | - } | |
| 593 | - if( verboseFlag || dryRunFlag ){ | |
| 594 | - fossil_print("Removed unmanaged file: %s\n", zName+nRoot); | |
| 595 | - } | |
| 596 | - if( !dryRunFlag ){ | |
| 597 | - file_delete(zName); | |
| 598 | - } | |
| 581 | + if( !dirsOnlyFlag ){ | |
| 582 | + Stmt q; | |
| 583 | + Blob repo; | |
| 584 | + locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, pKeep); | |
| 585 | + db_prepare(&q, | |
| 586 | + "SELECT %Q || x FROM sfile" | |
| 587 | + " WHERE x NOT IN (%s)" | |
| 588 | + " ORDER BY 1", | |
| 589 | + g.zLocalRoot, fossil_all_reserved_names(0) | |
| 590 | + ); | |
| 591 | + if( file_tree_name(g.zRepositoryName, &repo, 0) ){ | |
| 592 | + db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); | |
| 593 | + } | |
| 594 | + db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); | |
| 595 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 596 | + const char *zName = db_column_text(&q, 0); | |
| 597 | + if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ | |
| 598 | + Blob ans; | |
| 599 | + char cReply; | |
| 600 | + char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", | |
| 601 | + zName+nRoot); | |
| 602 | + blob_zero(&ans); | |
| 603 | + prompt_user(prompt, &ans); | |
| 604 | + cReply = blob_str(&ans)[0]; | |
| 605 | + if( cReply=='a' || cReply=='A' ){ | |
| 606 | + allFileFlag = 1; | |
| 607 | + }else if( cReply!='y' && cReply!='Y' ){ | |
| 608 | + blob_reset(&ans); | |
| 609 | + continue; | |
| 610 | + } | |
| 611 | + blob_reset(&ans); | |
| 612 | + } | |
| 613 | + if ( dryRunFlag || file_delete(zName)==0 ){ | |
| 614 | + if( verboseFlag || dryRunFlag ){ | |
| 615 | + fossil_print("Removed unmanaged file: %s\n", zName+nRoot); | |
| 616 | + } | |
| 617 | + }else if( verboseFlag ){ | |
| 618 | + fossil_print("Could not remove file: %s\n", zName+nRoot); | |
| 619 | + } | |
| 620 | + } | |
| 621 | + db_finalize(&q); | |
| 622 | + } | |
| 623 | + if( emptyDirsFlag ){ | |
| 624 | + Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); | |
| 625 | + Stmt q; | |
| 626 | + Blob root; | |
| 627 | + blob_init(&root, g.zLocalRoot, nRoot - 1); | |
| 628 | + vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, pKeep, | |
| 629 | + pEmptyDirs); | |
| 630 | + blob_reset(&root); | |
| 631 | + db_prepare(&q, | |
| 632 | + "SELECT %Q || x FROM dscan_temp" | |
| 633 | + " WHERE x NOT IN (%s) AND y = 0" | |
| 634 | + " ORDER BY 1 DESC", | |
| 635 | + g.zLocalRoot, fossil_all_reserved_names(0) | |
| 636 | + ); | |
| 637 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 638 | + const char *zName = db_column_text(&q, 0); | |
| 639 | + if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ | |
| 640 | + Blob ans; | |
| 641 | + char cReply; | |
| 642 | + char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", | |
| 643 | + zName+nRoot); | |
| 644 | + blob_zero(&ans); | |
| 645 | + prompt_user(prompt, &ans); | |
| 646 | + cReply = blob_str(&ans)[0]; | |
| 647 | + if( cReply=='a' || cReply=='A' ){ | |
| 648 | + allDirFlag = 1; | |
| 649 | + }else if( cReply!='y' && cReply!='Y' ){ | |
| 650 | + blob_reset(&ans); | |
| 651 | + continue; | |
| 652 | + } | |
| 653 | + blob_reset(&ans); | |
| 654 | + } | |
| 655 | + if ( dryRunFlag || file_rmdir(zName)==0 ){ | |
| 656 | + if( verboseFlag || dryRunFlag ){ | |
| 657 | + fossil_print("Removed unmanaged directory: %s\n", zName+nRoot); | |
| 658 | + } | |
| 659 | + }else if( verboseFlag ){ | |
| 660 | + fossil_print("Could not remove directory: %s\n", zName+nRoot); | |
| 661 | + } | |
| 662 | + } | |
| 663 | + db_finalize(&q); | |
| 664 | + glob_free(pEmptyDirs); | |
| 599 | 665 | } |
| 600 | 666 | glob_free(pClean); |
| 601 | - db_finalize(&q); | |
| 667 | + glob_free(pKeep); | |
| 668 | + glob_free(pIgnore); | |
| 602 | 669 | } |
| 603 | 670 | |
| 604 | 671 | /* |
| 605 | 672 | ** Prompt the user for a check-in or stash comment (given in pPrompt), |
| 606 | 673 | ** gather the response, then return the response in pComment. |
| @@ -642,12 +709,16 @@ | ||
| 642 | 709 | "# and because no comment was specified using the \"-m\" or \"-M\"\n" |
| 643 | 710 | "# command-line options, you will need to enter the comment below.\n" |
| 644 | 711 | "# Type \".\" on a line by itself when you are done:\n", -1); |
| 645 | 712 | zFile = mprintf("-"); |
| 646 | 713 | }else{ |
| 714 | + Blob fname; | |
| 715 | + blob_zero(&fname); | |
| 716 | + file_relative_name(g.zLocalRoot, &fname, 1); | |
| 647 | 717 | zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'", |
| 648 | - g.zLocalRoot); | |
| 718 | + blob_str(&fname)); | |
| 719 | + blob_reset(&fname); | |
| 649 | 720 | } |
| 650 | 721 | #if defined(_WIN32) |
| 651 | 722 | blob_add_cr(pPrompt); |
| 652 | 723 | #endif |
| 653 | 724 | blob_write_to_file(pPrompt, zFile); |
| 654 | 725 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -248,11 +248,11 @@ | |
| 248 | ** COMMAND: ls |
| 249 | ** |
| 250 | ** Usage: %fossil ls ?OPTIONS? ?VERSION? ?FILENAMES? |
| 251 | ** |
| 252 | ** Show the names of all files in the current checkout. The -v provides |
| 253 | ** extra information about each file. If FILENAMES are included, the only |
| 254 | ** the files listed (or their children if they are directories) are shown. |
| 255 | ** |
| 256 | ** Options: |
| 257 | ** --age Show when each file was committed |
| 258 | ** -v|--verbose Provide extra information about each file. |
| @@ -363,27 +363,28 @@ | |
| 363 | } |
| 364 | db_finalize(&q); |
| 365 | } |
| 366 | |
| 367 | /* |
| 368 | ** Create a TEMP table named SFILE and add all unmanaged files named on the command-line |
| 369 | ** to that table. If directories are named, then add all unmanaged files contained |
| 370 | ** underneath those directories. If there are no files or directories named on the |
| 371 | ** command-line, then add all unmanaged files anywhere in the checkout. |
| 372 | */ |
| 373 | static void locate_unmanaged_files( |
| 374 | int argc, /* Number of command-line arguments to examine */ |
| 375 | char **argv, /* values of command-line arguments */ |
| 376 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 377 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 378 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 379 | ){ |
| 380 | Blob name; /* Name of a candidate file or directory */ |
| 381 | char *zName; /* Name of a candidate file or directory */ |
| 382 | int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ |
| 383 | int i; /* Loop counter */ |
| 384 | int nRoot; /* length of g.zLocalRoot */ |
| 385 | |
| 386 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 387 | filename_collation()); |
| 388 | nRoot = (int)strlen(g.zLocalRoot); |
| 389 | if( argc==0 ){ |
| @@ -506,12 +507,28 @@ | |
| 506 | ** Files and subdirectories whose names begin with "." are |
| 507 | ** normally kept. They are handled if the "--dotfiles" option |
| 508 | ** is used. |
| 509 | ** |
| 510 | ** Options: |
| 511 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 512 | ** --dotfiles Include files beginning with a dot ("."). |
| 513 | ** -f|--force Remove files without prompting. |
| 514 | ** --clean <CSG> Never prompt for files matching this |
| 515 | ** comma separated list of glob patterns. |
| 516 | ** --ignore <CSG> Ignore files matching patterns from the |
| 517 | ** comma separated list of glob patterns. |
| @@ -522,25 +539,27 @@ | |
| 522 | ** -v|--verbose Show all files as they are removed. |
| 523 | ** |
| 524 | ** See also: addremove, extra, status |
| 525 | */ |
| 526 | void clean_cmd(void){ |
| 527 | int allFlag, dryRunFlag, verboseFlag; |
| 528 | unsigned scanFlags = 0; |
| 529 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 530 | Blob repo; |
| 531 | Stmt q; |
| 532 | Glob *pIgnore, *pKeep, *pClean; |
| 533 | int nRoot; |
| 534 | |
| 535 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 536 | if( !dryRunFlag ){ |
| 537 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 538 | } |
| 539 | allFlag = find_option("force","f",0)!=0; |
| 540 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 541 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 542 | zIgnoreFlag = find_option("ignore",0,1); |
| 543 | verboseFlag = find_option("verbose","v",0)!=0; |
| 544 | zKeepFlag = find_option("keep",0,1); |
| 545 | zCleanFlag = find_option("clean",0,1); |
| 546 | capture_case_sensitive_option(); |
| @@ -556,51 +575,99 @@ | |
| 556 | } |
| 557 | verify_all_options(); |
| 558 | pIgnore = glob_create(zIgnoreFlag); |
| 559 | pKeep = glob_create(zKeepFlag); |
| 560 | pClean = glob_create(zCleanFlag); |
| 561 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, pKeep); |
| 562 | glob_free(pKeep); |
| 563 | glob_free(pIgnore); |
| 564 | db_prepare(&q, |
| 565 | "SELECT %Q || x FROM sfile" |
| 566 | " WHERE x NOT IN (%s)" |
| 567 | " ORDER BY 1", |
| 568 | g.zLocalRoot, fossil_all_reserved_names(0) |
| 569 | ); |
| 570 | if( file_tree_name(g.zRepositoryName, &repo, 0) ){ |
| 571 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 572 | } |
| 573 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 574 | nRoot = (int)strlen(g.zLocalRoot); |
| 575 | while( db_step(&q)==SQLITE_ROW ){ |
| 576 | const char *zName = db_column_text(&q, 0); |
| 577 | if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 578 | Blob ans; |
| 579 | char cReply; |
| 580 | char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", |
| 581 | zName+nRoot); |
| 582 | blob_zero(&ans); |
| 583 | prompt_user(prompt, &ans); |
| 584 | cReply = blob_str(&ans)[0]; |
| 585 | if( cReply=='a' || cReply=='A' ){ |
| 586 | allFlag = 1; |
| 587 | }else if( cReply!='y' && cReply!='Y' ){ |
| 588 | blob_reset(&ans); |
| 589 | continue; |
| 590 | } |
| 591 | blob_reset(&ans); |
| 592 | } |
| 593 | if( verboseFlag || dryRunFlag ){ |
| 594 | fossil_print("Removed unmanaged file: %s\n", zName+nRoot); |
| 595 | } |
| 596 | if( !dryRunFlag ){ |
| 597 | file_delete(zName); |
| 598 | } |
| 599 | } |
| 600 | glob_free(pClean); |
| 601 | db_finalize(&q); |
| 602 | } |
| 603 | |
| 604 | /* |
| 605 | ** Prompt the user for a check-in or stash comment (given in pPrompt), |
| 606 | ** gather the response, then return the response in pComment. |
| @@ -642,12 +709,16 @@ | |
| 642 | "# and because no comment was specified using the \"-m\" or \"-M\"\n" |
| 643 | "# command-line options, you will need to enter the comment below.\n" |
| 644 | "# Type \".\" on a line by itself when you are done:\n", -1); |
| 645 | zFile = mprintf("-"); |
| 646 | }else{ |
| 647 | zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'", |
| 648 | g.zLocalRoot); |
| 649 | } |
| 650 | #if defined(_WIN32) |
| 651 | blob_add_cr(pPrompt); |
| 652 | #endif |
| 653 | blob_write_to_file(pPrompt, zFile); |
| 654 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -248,11 +248,11 @@ | |
| 248 | ** COMMAND: ls |
| 249 | ** |
| 250 | ** Usage: %fossil ls ?OPTIONS? ?VERSION? ?FILENAMES? |
| 251 | ** |
| 252 | ** Show the names of all files in the current checkout. The -v provides |
| 253 | ** extra information about each file. If FILENAMES are included, only |
| 254 | ** the files listed (or their children if they are directories) are shown. |
| 255 | ** |
| 256 | ** Options: |
| 257 | ** --age Show when each file was committed |
| 258 | ** -v|--verbose Provide extra information about each file. |
| @@ -363,27 +363,28 @@ | |
| 363 | } |
| 364 | db_finalize(&q); |
| 365 | } |
| 366 | |
| 367 | /* |
| 368 | ** Create a TEMP table named SFILE and add all unmanaged files named on |
| 369 | ** the command-line to that table. If directories are named, then add |
| 370 | ** all unmanaged files contained underneath those directories. If there |
| 371 | ** are no files or directories named on the command-line, then add all |
| 372 | ** unmanaged files anywhere in the checkout. |
| 373 | */ |
| 374 | static void locate_unmanaged_files( |
| 375 | int argc, /* Number of command-line arguments to examine */ |
| 376 | char **argv, /* values of command-line arguments */ |
| 377 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 378 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 379 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 380 | ){ |
| 381 | Blob name; /* Name of a candidate file or directory */ |
| 382 | char *zName; /* Name of a candidate file or directory */ |
| 383 | int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ |
| 384 | int i; /* Loop counter */ |
| 385 | int nRoot; /* length of g.zLocalRoot */ |
| 386 | |
| 387 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 388 | filename_collation()); |
| 389 | nRoot = (int)strlen(g.zLocalRoot); |
| 390 | if( argc==0 ){ |
| @@ -506,12 +507,28 @@ | |
| 507 | ** Files and subdirectories whose names begin with "." are |
| 508 | ** normally kept. They are handled if the "--dotfiles" option |
| 509 | ** is used. |
| 510 | ** |
| 511 | ** Options: |
| 512 | ** --allckouts Check for empty directories within any checkouts |
| 513 | ** that may be nested within the current one. This |
| 514 | ** option should be used with great care because the |
| 515 | ** empty-dirs setting (and other applicable settings) |
| 516 | ** belonging to the other repositories, if any, will |
| 517 | ** not be checked. |
| 518 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 519 | ** --dirsonly Only remove empty directories. No files will |
| 520 | ** be removed. Using this option will automatically |
| 521 | ** enable the --emptydirs option as well. |
| 522 | ** --dotfiles Include files beginning with a dot ("."). |
| 523 | ** --emptydirs Remove any empty directories that are not |
| 524 | ** explicitly exempted via the empty-dirs setting |
| 525 | ** or another applicable setting or command line |
| 526 | ** argument. Matching files, if any, are removed |
| 527 | ** prior to checking for any empty directories; |
| 528 | ** therefore, directories that contain only files |
| 529 | ** that were removed will be removed as well. |
| 530 | ** -f|--force Remove files without prompting. |
| 531 | ** --clean <CSG> Never prompt for files matching this |
| 532 | ** comma separated list of glob patterns. |
| 533 | ** --ignore <CSG> Ignore files matching patterns from the |
| 534 | ** comma separated list of glob patterns. |
| @@ -522,25 +539,27 @@ | |
| 539 | ** -v|--verbose Show all files as they are removed. |
| 540 | ** |
| 541 | ** See also: addremove, extra, status |
| 542 | */ |
| 543 | void clean_cmd(void){ |
| 544 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| 545 | int emptyDirsFlag, dirsOnlyFlag; |
| 546 | unsigned scanFlags = 0; |
| 547 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 548 | Glob *pIgnore, *pKeep, *pClean; |
| 549 | int nRoot; |
| 550 | |
| 551 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 552 | if( !dryRunFlag ){ |
| 553 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 554 | } |
| 555 | allFileFlag = allDirFlag = find_option("force","f",0)!=0; |
| 556 | dirsOnlyFlag = find_option("dirsonly",0,0)!=0; |
| 557 | emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag; |
| 558 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 559 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 560 | if( find_option("allckouts",0,0)!=0 ) scanFlags |= SCAN_NESTED; |
| 561 | zIgnoreFlag = find_option("ignore",0,1); |
| 562 | verboseFlag = find_option("verbose","v",0)!=0; |
| 563 | zKeepFlag = find_option("keep",0,1); |
| 564 | zCleanFlag = find_option("clean",0,1); |
| 565 | capture_case_sensitive_option(); |
| @@ -556,51 +575,99 @@ | |
| 575 | } |
| 576 | verify_all_options(); |
| 577 | pIgnore = glob_create(zIgnoreFlag); |
| 578 | pKeep = glob_create(zKeepFlag); |
| 579 | pClean = glob_create(zCleanFlag); |
| 580 | nRoot = (int)strlen(g.zLocalRoot); |
| 581 | if( !dirsOnlyFlag ){ |
| 582 | Stmt q; |
| 583 | Blob repo; |
| 584 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, pKeep); |
| 585 | db_prepare(&q, |
| 586 | "SELECT %Q || x FROM sfile" |
| 587 | " WHERE x NOT IN (%s)" |
| 588 | " ORDER BY 1", |
| 589 | g.zLocalRoot, fossil_all_reserved_names(0) |
| 590 | ); |
| 591 | if( file_tree_name(g.zRepositoryName, &repo, 0) ){ |
| 592 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 593 | } |
| 594 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 595 | while( db_step(&q)==SQLITE_ROW ){ |
| 596 | const char *zName = db_column_text(&q, 0); |
| 597 | if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 598 | Blob ans; |
| 599 | char cReply; |
| 600 | char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", |
| 601 | zName+nRoot); |
| 602 | blob_zero(&ans); |
| 603 | prompt_user(prompt, &ans); |
| 604 | cReply = blob_str(&ans)[0]; |
| 605 | if( cReply=='a' || cReply=='A' ){ |
| 606 | allFileFlag = 1; |
| 607 | }else if( cReply!='y' && cReply!='Y' ){ |
| 608 | blob_reset(&ans); |
| 609 | continue; |
| 610 | } |
| 611 | blob_reset(&ans); |
| 612 | } |
| 613 | if ( dryRunFlag || file_delete(zName)==0 ){ |
| 614 | if( verboseFlag || dryRunFlag ){ |
| 615 | fossil_print("Removed unmanaged file: %s\n", zName+nRoot); |
| 616 | } |
| 617 | }else if( verboseFlag ){ |
| 618 | fossil_print("Could not remove file: %s\n", zName+nRoot); |
| 619 | } |
| 620 | } |
| 621 | db_finalize(&q); |
| 622 | } |
| 623 | if( emptyDirsFlag ){ |
| 624 | Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); |
| 625 | Stmt q; |
| 626 | Blob root; |
| 627 | blob_init(&root, g.zLocalRoot, nRoot - 1); |
| 628 | vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, pKeep, |
| 629 | pEmptyDirs); |
| 630 | blob_reset(&root); |
| 631 | db_prepare(&q, |
| 632 | "SELECT %Q || x FROM dscan_temp" |
| 633 | " WHERE x NOT IN (%s) AND y = 0" |
| 634 | " ORDER BY 1 DESC", |
| 635 | g.zLocalRoot, fossil_all_reserved_names(0) |
| 636 | ); |
| 637 | while( db_step(&q)==SQLITE_ROW ){ |
| 638 | const char *zName = db_column_text(&q, 0); |
| 639 | if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 640 | Blob ans; |
| 641 | char cReply; |
| 642 | char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", |
| 643 | zName+nRoot); |
| 644 | blob_zero(&ans); |
| 645 | prompt_user(prompt, &ans); |
| 646 | cReply = blob_str(&ans)[0]; |
| 647 | if( cReply=='a' || cReply=='A' ){ |
| 648 | allDirFlag = 1; |
| 649 | }else if( cReply!='y' && cReply!='Y' ){ |
| 650 | blob_reset(&ans); |
| 651 | continue; |
| 652 | } |
| 653 | blob_reset(&ans); |
| 654 | } |
| 655 | if ( dryRunFlag || file_rmdir(zName)==0 ){ |
| 656 | if( verboseFlag || dryRunFlag ){ |
| 657 | fossil_print("Removed unmanaged directory: %s\n", zName+nRoot); |
| 658 | } |
| 659 | }else if( verboseFlag ){ |
| 660 | fossil_print("Could not remove directory: %s\n", zName+nRoot); |
| 661 | } |
| 662 | } |
| 663 | db_finalize(&q); |
| 664 | glob_free(pEmptyDirs); |
| 665 | } |
| 666 | glob_free(pClean); |
| 667 | glob_free(pKeep); |
| 668 | glob_free(pIgnore); |
| 669 | } |
| 670 | |
| 671 | /* |
| 672 | ** Prompt the user for a check-in or stash comment (given in pPrompt), |
| 673 | ** gather the response, then return the response in pComment. |
| @@ -642,12 +709,16 @@ | |
| 709 | "# and because no comment was specified using the \"-m\" or \"-M\"\n" |
| 710 | "# command-line options, you will need to enter the comment below.\n" |
| 711 | "# Type \".\" on a line by itself when you are done:\n", -1); |
| 712 | zFile = mprintf("-"); |
| 713 | }else{ |
| 714 | Blob fname; |
| 715 | blob_zero(&fname); |
| 716 | file_relative_name(g.zLocalRoot, &fname, 1); |
| 717 | zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'", |
| 718 | blob_str(&fname)); |
| 719 | blob_reset(&fname); |
| 720 | } |
| 721 | #if defined(_WIN32) |
| 722 | blob_add_cr(pPrompt); |
| 723 | #endif |
| 724 | blob_write_to_file(pPrompt, zFile); |
| 725 |
+41
-8
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -461,20 +461,24 @@ | ||
| 461 | 461 | fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime); |
| 462 | 462 | } |
| 463 | 463 | |
| 464 | 464 | /* |
| 465 | 465 | ** Delete a file. |
| 466 | +** | |
| 467 | +** Returns zero upon success. | |
| 466 | 468 | */ |
| 467 | -void file_delete(const char *zFilename){ | |
| 469 | +int file_delete(const char *zFilename){ | |
| 470 | + int rc; | |
| 468 | 471 | #ifdef _WIN32 |
| 469 | 472 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 470 | - _wunlink(z); | |
| 473 | + rc = _wunlink(z); | |
| 471 | 474 | #else |
| 472 | 475 | char *z = fossil_utf8_to_filename(zFilename); |
| 473 | - unlink(zFilename); | |
| 476 | + rc = unlink(zFilename); | |
| 474 | 477 | #endif |
| 475 | 478 | fossil_filename_free(z); |
| 479 | + return rc; | |
| 476 | 480 | } |
| 477 | 481 | |
| 478 | 482 | /* |
| 479 | 483 | ** Create the directory named in the argument, if it does not already |
| 480 | 484 | ** exist. If forceFlag is 1, delete any prior non-directory object |
| @@ -493,10 +497,33 @@ | ||
| 493 | 497 | wchar_t *zMbcs = fossil_utf8_to_filename(zName); |
| 494 | 498 | rc = _wmkdir(zMbcs); |
| 495 | 499 | #else |
| 496 | 500 | char *zMbcs = fossil_utf8_to_filename(zName); |
| 497 | 501 | rc = mkdir(zName, 0755); |
| 502 | +#endif | |
| 503 | + fossil_filename_free(zMbcs); | |
| 504 | + return rc; | |
| 505 | + } | |
| 506 | + return 0; | |
| 507 | +} | |
| 508 | + | |
| 509 | +/* | |
| 510 | +** Removes the directory named in the argument, if it exists. The directory | |
| 511 | +** must be empty and cannot be the current directory or the root directory. | |
| 512 | +** | |
| 513 | +** Returns zero upon success. | |
| 514 | +*/ | |
| 515 | +int file_rmdir(const char *zName){ | |
| 516 | + int rc = file_wd_isdir(zName); | |
| 517 | + if( rc==2 ) return 1; /* cannot remove normal file */ | |
| 518 | + if( rc==1 ){ | |
| 519 | +#if defined(_WIN32) | |
| 520 | + wchar_t *zMbcs = fossil_utf8_to_filename(zName); | |
| 521 | + rc = _wrmdir(zMbcs); | |
| 522 | +#else | |
| 523 | + char *zMbcs = fossil_utf8_to_filename(zName); | |
| 524 | + rc = rmdir(zName); | |
| 498 | 525 | #endif |
| 499 | 526 | fossil_filename_free(zMbcs); |
| 500 | 527 | return rc; |
| 501 | 528 | } |
| 502 | 529 | return 0; |
| @@ -794,15 +821,16 @@ | ||
| 794 | 821 | ** Also test Fossil's ability to measure attributes of a file. |
| 795 | 822 | */ |
| 796 | 823 | void cmd_test_canonical_name(void){ |
| 797 | 824 | int i; |
| 798 | 825 | Blob x; |
| 826 | + int slashFlag = find_option("slash",0,0)!=0; | |
| 799 | 827 | blob_zero(&x); |
| 800 | 828 | for(i=2; i<g.argc; i++){ |
| 801 | 829 | char zBuf[100]; |
| 802 | 830 | const char *zName = g.argv[i]; |
| 803 | - file_canonical_name(zName, &x, 0); | |
| 831 | + file_canonical_name(zName, &x, slashFlag); | |
| 804 | 832 | fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); |
| 805 | 833 | blob_reset(&x); |
| 806 | 834 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); |
| 807 | 835 | fossil_print(" file_size = %s\n", zBuf); |
| 808 | 836 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); |
| @@ -876,21 +904,25 @@ | ||
| 876 | 904 | while( zPath[i] && fossil_tolower(zPwd[i])==fossil_tolower(zPath[i]) ) i++; |
| 877 | 905 | #else |
| 878 | 906 | while( zPath[i] && zPwd[i]==zPath[i] ) i++; |
| 879 | 907 | #endif |
| 880 | 908 | if( zPath[i]==0 ){ |
| 881 | - blob_reset(pOut); | |
| 909 | + memcpy(&tmp, pOut, sizeof(tmp)); | |
| 882 | 910 | if( zPwd[i]==0 ){ |
| 883 | - blob_append(pOut, ".", 1); | |
| 911 | + blob_set(pOut, "."); | |
| 884 | 912 | }else{ |
| 885 | - blob_append(pOut, "..", 2); | |
| 913 | + blob_set(pOut, ".."); | |
| 886 | 914 | for(j=i+1; zPwd[j]; j++){ |
| 887 | 915 | if( zPwd[j]=='/' ){ |
| 888 | 916 | blob_append(pOut, "/..", 3); |
| 889 | 917 | } |
| 890 | 918 | } |
| 891 | 919 | } |
| 920 | + if( slash && i>0 && zPath[strlen(zPath)-1]=='/'){ | |
| 921 | + blob_append(pOut, "/", 1); | |
| 922 | + } | |
| 923 | + blob_reset(&tmp); | |
| 892 | 924 | return; |
| 893 | 925 | } |
| 894 | 926 | if( zPwd[i]==0 && zPath[i]=='/' ){ |
| 895 | 927 | memcpy(&tmp, pOut, sizeof(tmp)); |
| 896 | 928 | blob_set(pOut, "./"); |
| @@ -917,13 +949,14 @@ | ||
| 917 | 949 | ** Test the operation of the relative name generator. |
| 918 | 950 | */ |
| 919 | 951 | void cmd_test_relative_name(void){ |
| 920 | 952 | int i; |
| 921 | 953 | Blob x; |
| 954 | + int slashFlag = find_option("slash",0,0)!=0; | |
| 922 | 955 | blob_zero(&x); |
| 923 | 956 | for(i=2; i<g.argc; i++){ |
| 924 | - file_relative_name(g.argv[i], &x, 0); | |
| 957 | + file_relative_name(g.argv[i], &x, slashFlag); | |
| 925 | 958 | fossil_print("%s\n", blob_buffer(&x)); |
| 926 | 959 | blob_reset(&x); |
| 927 | 960 | } |
| 928 | 961 | } |
| 929 | 962 | |
| 930 | 963 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -461,20 +461,24 @@ | |
| 461 | fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime); |
| 462 | } |
| 463 | |
| 464 | /* |
| 465 | ** Delete a file. |
| 466 | */ |
| 467 | void file_delete(const char *zFilename){ |
| 468 | #ifdef _WIN32 |
| 469 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 470 | _wunlink(z); |
| 471 | #else |
| 472 | char *z = fossil_utf8_to_filename(zFilename); |
| 473 | unlink(zFilename); |
| 474 | #endif |
| 475 | fossil_filename_free(z); |
| 476 | } |
| 477 | |
| 478 | /* |
| 479 | ** Create the directory named in the argument, if it does not already |
| 480 | ** exist. If forceFlag is 1, delete any prior non-directory object |
| @@ -493,10 +497,33 @@ | |
| 493 | wchar_t *zMbcs = fossil_utf8_to_filename(zName); |
| 494 | rc = _wmkdir(zMbcs); |
| 495 | #else |
| 496 | char *zMbcs = fossil_utf8_to_filename(zName); |
| 497 | rc = mkdir(zName, 0755); |
| 498 | #endif |
| 499 | fossil_filename_free(zMbcs); |
| 500 | return rc; |
| 501 | } |
| 502 | return 0; |
| @@ -794,15 +821,16 @@ | |
| 794 | ** Also test Fossil's ability to measure attributes of a file. |
| 795 | */ |
| 796 | void cmd_test_canonical_name(void){ |
| 797 | int i; |
| 798 | Blob x; |
| 799 | blob_zero(&x); |
| 800 | for(i=2; i<g.argc; i++){ |
| 801 | char zBuf[100]; |
| 802 | const char *zName = g.argv[i]; |
| 803 | file_canonical_name(zName, &x, 0); |
| 804 | fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); |
| 805 | blob_reset(&x); |
| 806 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); |
| 807 | fossil_print(" file_size = %s\n", zBuf); |
| 808 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); |
| @@ -876,21 +904,25 @@ | |
| 876 | while( zPath[i] && fossil_tolower(zPwd[i])==fossil_tolower(zPath[i]) ) i++; |
| 877 | #else |
| 878 | while( zPath[i] && zPwd[i]==zPath[i] ) i++; |
| 879 | #endif |
| 880 | if( zPath[i]==0 ){ |
| 881 | blob_reset(pOut); |
| 882 | if( zPwd[i]==0 ){ |
| 883 | blob_append(pOut, ".", 1); |
| 884 | }else{ |
| 885 | blob_append(pOut, "..", 2); |
| 886 | for(j=i+1; zPwd[j]; j++){ |
| 887 | if( zPwd[j]=='/' ){ |
| 888 | blob_append(pOut, "/..", 3); |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | return; |
| 893 | } |
| 894 | if( zPwd[i]==0 && zPath[i]=='/' ){ |
| 895 | memcpy(&tmp, pOut, sizeof(tmp)); |
| 896 | blob_set(pOut, "./"); |
| @@ -917,13 +949,14 @@ | |
| 917 | ** Test the operation of the relative name generator. |
| 918 | */ |
| 919 | void cmd_test_relative_name(void){ |
| 920 | int i; |
| 921 | Blob x; |
| 922 | blob_zero(&x); |
| 923 | for(i=2; i<g.argc; i++){ |
| 924 | file_relative_name(g.argv[i], &x, 0); |
| 925 | fossil_print("%s\n", blob_buffer(&x)); |
| 926 | blob_reset(&x); |
| 927 | } |
| 928 | } |
| 929 | |
| 930 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -461,20 +461,24 @@ | |
| 461 | fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime); |
| 462 | } |
| 463 | |
| 464 | /* |
| 465 | ** Delete a file. |
| 466 | ** |
| 467 | ** Returns zero upon success. |
| 468 | */ |
| 469 | int file_delete(const char *zFilename){ |
| 470 | int rc; |
| 471 | #ifdef _WIN32 |
| 472 | wchar_t *z = fossil_utf8_to_filename(zFilename); |
| 473 | rc = _wunlink(z); |
| 474 | #else |
| 475 | char *z = fossil_utf8_to_filename(zFilename); |
| 476 | rc = unlink(zFilename); |
| 477 | #endif |
| 478 | fossil_filename_free(z); |
| 479 | return rc; |
| 480 | } |
| 481 | |
| 482 | /* |
| 483 | ** Create the directory named in the argument, if it does not already |
| 484 | ** exist. If forceFlag is 1, delete any prior non-directory object |
| @@ -493,10 +497,33 @@ | |
| 497 | wchar_t *zMbcs = fossil_utf8_to_filename(zName); |
| 498 | rc = _wmkdir(zMbcs); |
| 499 | #else |
| 500 | char *zMbcs = fossil_utf8_to_filename(zName); |
| 501 | rc = mkdir(zName, 0755); |
| 502 | #endif |
| 503 | fossil_filename_free(zMbcs); |
| 504 | return rc; |
| 505 | } |
| 506 | return 0; |
| 507 | } |
| 508 | |
| 509 | /* |
| 510 | ** Removes the directory named in the argument, if it exists. The directory |
| 511 | ** must be empty and cannot be the current directory or the root directory. |
| 512 | ** |
| 513 | ** Returns zero upon success. |
| 514 | */ |
| 515 | int file_rmdir(const char *zName){ |
| 516 | int rc = file_wd_isdir(zName); |
| 517 | if( rc==2 ) return 1; /* cannot remove normal file */ |
| 518 | if( rc==1 ){ |
| 519 | #if defined(_WIN32) |
| 520 | wchar_t *zMbcs = fossil_utf8_to_filename(zName); |
| 521 | rc = _wrmdir(zMbcs); |
| 522 | #else |
| 523 | char *zMbcs = fossil_utf8_to_filename(zName); |
| 524 | rc = rmdir(zName); |
| 525 | #endif |
| 526 | fossil_filename_free(zMbcs); |
| 527 | return rc; |
| 528 | } |
| 529 | return 0; |
| @@ -794,15 +821,16 @@ | |
| 821 | ** Also test Fossil's ability to measure attributes of a file. |
| 822 | */ |
| 823 | void cmd_test_canonical_name(void){ |
| 824 | int i; |
| 825 | Blob x; |
| 826 | int slashFlag = find_option("slash",0,0)!=0; |
| 827 | blob_zero(&x); |
| 828 | for(i=2; i<g.argc; i++){ |
| 829 | char zBuf[100]; |
| 830 | const char *zName = g.argv[i]; |
| 831 | file_canonical_name(zName, &x, slashFlag); |
| 832 | fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); |
| 833 | blob_reset(&x); |
| 834 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); |
| 835 | fossil_print(" file_size = %s\n", zBuf); |
| 836 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); |
| @@ -876,21 +904,25 @@ | |
| 904 | while( zPath[i] && fossil_tolower(zPwd[i])==fossil_tolower(zPath[i]) ) i++; |
| 905 | #else |
| 906 | while( zPath[i] && zPwd[i]==zPath[i] ) i++; |
| 907 | #endif |
| 908 | if( zPath[i]==0 ){ |
| 909 | memcpy(&tmp, pOut, sizeof(tmp)); |
| 910 | if( zPwd[i]==0 ){ |
| 911 | blob_set(pOut, "."); |
| 912 | }else{ |
| 913 | blob_set(pOut, ".."); |
| 914 | for(j=i+1; zPwd[j]; j++){ |
| 915 | if( zPwd[j]=='/' ){ |
| 916 | blob_append(pOut, "/..", 3); |
| 917 | } |
| 918 | } |
| 919 | } |
| 920 | if( slash && i>0 && zPath[strlen(zPath)-1]=='/'){ |
| 921 | blob_append(pOut, "/", 1); |
| 922 | } |
| 923 | blob_reset(&tmp); |
| 924 | return; |
| 925 | } |
| 926 | if( zPwd[i]==0 && zPath[i]=='/' ){ |
| 927 | memcpy(&tmp, pOut, sizeof(tmp)); |
| 928 | blob_set(pOut, "./"); |
| @@ -917,13 +949,14 @@ | |
| 949 | ** Test the operation of the relative name generator. |
| 950 | */ |
| 951 | void cmd_test_relative_name(void){ |
| 952 | int i; |
| 953 | Blob x; |
| 954 | int slashFlag = find_option("slash",0,0)!=0; |
| 955 | blob_zero(&x); |
| 956 | for(i=2; i<g.argc; i++){ |
| 957 | file_relative_name(g.argv[i], &x, slashFlag); |
| 958 | fossil_print("%s\n", blob_buffer(&x)); |
| 959 | blob_reset(&x); |
| 960 | } |
| 961 | } |
| 962 | |
| 963 |
+26
-5
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -100,11 +100,13 @@ | ||
| 100 | 100 | char **argv; /* Full copy of the original (expanded) arguments. */ |
| 101 | 101 | void *library; /* The Tcl library module handle. */ |
| 102 | 102 | void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */ |
| 103 | 103 | void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */ |
| 104 | 104 | void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */ |
| 105 | + void *xFinalize; /* See tcl_FinalizeProc in th_tcl.c. */ | |
| 105 | 106 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 107 | + int useObjProc; /* Non-zero if an objProc can be called directly. */ | |
| 106 | 108 | char *setup; /* The optional Tcl setup script. */ |
| 107 | 109 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 108 | 110 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 109 | 111 | void *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 110 | 112 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -345,10 +347,24 @@ | ||
| 345 | 347 | /* |
| 346 | 348 | ** atexit() handler which frees up "some" of the resources |
| 347 | 349 | ** used by fossil. |
| 348 | 350 | */ |
| 349 | 351 | static void fossil_atexit(void) { |
| 352 | +#if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \ | |
| 353 | + defined(USE_TCL_STUBS) | |
| 354 | + /* | |
| 355 | + ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash | |
| 356 | + ** when exiting while a stubs-enabled Tcl is still loaded. This is due to | |
| 357 | + ** a bug in MinGW, see: | |
| 358 | + ** | |
| 359 | + ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 | |
| 360 | + ** | |
| 361 | + ** The workaround is to manually unload the loaded Tcl library prior to | |
| 362 | + ** exiting the process. This issue does not impact 64-bit Windows. | |
| 363 | + */ | |
| 364 | + unloadTcl(g.interp, &g.tcl); | |
| 365 | +#endif | |
| 350 | 366 | #ifdef FOSSIL_ENABLE_JSON |
| 351 | 367 | cson_value_free(g.json.gc.v); |
| 352 | 368 | memset(&g.json, 0, sizeof(g.json)); |
| 353 | 369 | #endif |
| 354 | 370 | free(g.zErrMsg); |
| @@ -526,10 +542,13 @@ | ||
| 526 | 542 | */ |
| 527 | 543 | #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) |
| 528 | 544 | int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ |
| 529 | 545 | int wmain(int argc, wchar_t **argv) |
| 530 | 546 | #else |
| 547 | +#if defined(_WIN32) | |
| 548 | +int _CRT_glob = 0x0001; /* See MinGW bug #2062 */ | |
| 549 | +#endif | |
| 531 | 550 | int main(int argc, char **argv) |
| 532 | 551 | #endif |
| 533 | 552 | { |
| 534 | 553 | const char *zCmdName = "unknown"; |
| 535 | 554 | int idx; |
| @@ -816,16 +835,19 @@ | ||
| 816 | 835 | #if defined(FOSSIL_ENABLE_SSL) |
| 817 | 836 | fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT); |
| 818 | 837 | #endif |
| 819 | 838 | #if defined(FOSSIL_ENABLE_TCL) |
| 820 | 839 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL); |
| 821 | - rc = Th_Eval(g.interp, 0, "tclEval {info patchlevel}", -1); | |
| 840 | + rc = Th_Eval(g.interp, 0, "tclInvoke info patchlevel", -1); | |
| 822 | 841 | zRc = Th_ReturnCodeName(rc, 0); |
| 823 | 842 | fossil_print("TCL (Tcl %s, loaded %s: %s)\n", |
| 824 | 843 | TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) |
| 825 | 844 | ); |
| 826 | 845 | #endif |
| 846 | +#if defined(USE_TCL_STUBS) | |
| 847 | + fossil_print("USE_TCL_STUBS\n"); | |
| 848 | +#endif | |
| 827 | 849 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 828 | 850 | fossil_print("TCL_STUBS\n"); |
| 829 | 851 | #endif |
| 830 | 852 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 831 | 853 | fossil_print("TCL_PRIVATE_STUBS\n"); |
| @@ -1130,10 +1152,13 @@ | ||
| 1130 | 1152 | if( getuid()==0 ){ |
| 1131 | 1153 | int i; |
| 1132 | 1154 | struct stat sStat; |
| 1133 | 1155 | Blob dir; |
| 1134 | 1156 | char *zDir; |
| 1157 | + if( g.db!=0 ){ | |
| 1158 | + db_close(1); | |
| 1159 | + } | |
| 1135 | 1160 | |
| 1136 | 1161 | file_canonical_name(zRepo, &dir, 0); |
| 1137 | 1162 | zDir = blob_str(&dir); |
| 1138 | 1163 | if( file_isdir(zDir)==1 ){ |
| 1139 | 1164 | if( file_chdir(zDir, 1) ){ |
| @@ -1158,14 +1183,10 @@ | ||
| 1158 | 1183 | i = setgid(sStat.st_gid); |
| 1159 | 1184 | i = i || setuid(sStat.st_uid); |
| 1160 | 1185 | if(i){ |
| 1161 | 1186 | fossil_fatal("setgid/uid() failed with errno %d", errno); |
| 1162 | 1187 | } |
| 1163 | - if( g.db!=0 ){ | |
| 1164 | - db_close(1); | |
| 1165 | - db_open_repository(zRepo); | |
| 1166 | - } | |
| 1167 | 1188 | } |
| 1168 | 1189 | #endif |
| 1169 | 1190 | return zRepo; |
| 1170 | 1191 | } |
| 1171 | 1192 | |
| 1172 | 1193 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -100,11 +100,13 @@ | |
| 100 | char **argv; /* Full copy of the original (expanded) arguments. */ |
| 101 | void *library; /* The Tcl library module handle. */ |
| 102 | void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */ |
| 103 | void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */ |
| 104 | void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */ |
| 105 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 106 | char *setup; /* The optional Tcl setup script. */ |
| 107 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 108 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 109 | void *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 110 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -345,10 +347,24 @@ | |
| 345 | /* |
| 346 | ** atexit() handler which frees up "some" of the resources |
| 347 | ** used by fossil. |
| 348 | */ |
| 349 | static void fossil_atexit(void) { |
| 350 | #ifdef FOSSIL_ENABLE_JSON |
| 351 | cson_value_free(g.json.gc.v); |
| 352 | memset(&g.json, 0, sizeof(g.json)); |
| 353 | #endif |
| 354 | free(g.zErrMsg); |
| @@ -526,10 +542,13 @@ | |
| 526 | */ |
| 527 | #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) |
| 528 | int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ |
| 529 | int wmain(int argc, wchar_t **argv) |
| 530 | #else |
| 531 | int main(int argc, char **argv) |
| 532 | #endif |
| 533 | { |
| 534 | const char *zCmdName = "unknown"; |
| 535 | int idx; |
| @@ -816,16 +835,19 @@ | |
| 816 | #if defined(FOSSIL_ENABLE_SSL) |
| 817 | fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT); |
| 818 | #endif |
| 819 | #if defined(FOSSIL_ENABLE_TCL) |
| 820 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL); |
| 821 | rc = Th_Eval(g.interp, 0, "tclEval {info patchlevel}", -1); |
| 822 | zRc = Th_ReturnCodeName(rc, 0); |
| 823 | fossil_print("TCL (Tcl %s, loaded %s: %s)\n", |
| 824 | TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) |
| 825 | ); |
| 826 | #endif |
| 827 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 828 | fossil_print("TCL_STUBS\n"); |
| 829 | #endif |
| 830 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 831 | fossil_print("TCL_PRIVATE_STUBS\n"); |
| @@ -1130,10 +1152,13 @@ | |
| 1130 | if( getuid()==0 ){ |
| 1131 | int i; |
| 1132 | struct stat sStat; |
| 1133 | Blob dir; |
| 1134 | char *zDir; |
| 1135 | |
| 1136 | file_canonical_name(zRepo, &dir, 0); |
| 1137 | zDir = blob_str(&dir); |
| 1138 | if( file_isdir(zDir)==1 ){ |
| 1139 | if( file_chdir(zDir, 1) ){ |
| @@ -1158,14 +1183,10 @@ | |
| 1158 | i = setgid(sStat.st_gid); |
| 1159 | i = i || setuid(sStat.st_uid); |
| 1160 | if(i){ |
| 1161 | fossil_fatal("setgid/uid() failed with errno %d", errno); |
| 1162 | } |
| 1163 | if( g.db!=0 ){ |
| 1164 | db_close(1); |
| 1165 | db_open_repository(zRepo); |
| 1166 | } |
| 1167 | } |
| 1168 | #endif |
| 1169 | return zRepo; |
| 1170 | } |
| 1171 | |
| 1172 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -100,11 +100,13 @@ | |
| 100 | char **argv; /* Full copy of the original (expanded) arguments. */ |
| 101 | void *library; /* The Tcl library module handle. */ |
| 102 | void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */ |
| 103 | void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */ |
| 104 | void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */ |
| 105 | void *xFinalize; /* See tcl_FinalizeProc in th_tcl.c. */ |
| 106 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 107 | int useObjProc; /* Non-zero if an objProc can be called directly. */ |
| 108 | char *setup; /* The optional Tcl setup script. */ |
| 109 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 110 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 111 | void *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 112 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -345,10 +347,24 @@ | |
| 347 | /* |
| 348 | ** atexit() handler which frees up "some" of the resources |
| 349 | ** used by fossil. |
| 350 | */ |
| 351 | static void fossil_atexit(void) { |
| 352 | #if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \ |
| 353 | defined(USE_TCL_STUBS) |
| 354 | /* |
| 355 | ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash |
| 356 | ** when exiting while a stubs-enabled Tcl is still loaded. This is due to |
| 357 | ** a bug in MinGW, see: |
| 358 | ** |
| 359 | ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 |
| 360 | ** |
| 361 | ** The workaround is to manually unload the loaded Tcl library prior to |
| 362 | ** exiting the process. This issue does not impact 64-bit Windows. |
| 363 | */ |
| 364 | unloadTcl(g.interp, &g.tcl); |
| 365 | #endif |
| 366 | #ifdef FOSSIL_ENABLE_JSON |
| 367 | cson_value_free(g.json.gc.v); |
| 368 | memset(&g.json, 0, sizeof(g.json)); |
| 369 | #endif |
| 370 | free(g.zErrMsg); |
| @@ -526,10 +542,13 @@ | |
| 542 | */ |
| 543 | #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) |
| 544 | int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ |
| 545 | int wmain(int argc, wchar_t **argv) |
| 546 | #else |
| 547 | #if defined(_WIN32) |
| 548 | int _CRT_glob = 0x0001; /* See MinGW bug #2062 */ |
| 549 | #endif |
| 550 | int main(int argc, char **argv) |
| 551 | #endif |
| 552 | { |
| 553 | const char *zCmdName = "unknown"; |
| 554 | int idx; |
| @@ -816,16 +835,19 @@ | |
| 835 | #if defined(FOSSIL_ENABLE_SSL) |
| 836 | fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT); |
| 837 | #endif |
| 838 | #if defined(FOSSIL_ENABLE_TCL) |
| 839 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL); |
| 840 | rc = Th_Eval(g.interp, 0, "tclInvoke info patchlevel", -1); |
| 841 | zRc = Th_ReturnCodeName(rc, 0); |
| 842 | fossil_print("TCL (Tcl %s, loaded %s: %s)\n", |
| 843 | TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) |
| 844 | ); |
| 845 | #endif |
| 846 | #if defined(USE_TCL_STUBS) |
| 847 | fossil_print("USE_TCL_STUBS\n"); |
| 848 | #endif |
| 849 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 850 | fossil_print("TCL_STUBS\n"); |
| 851 | #endif |
| 852 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 853 | fossil_print("TCL_PRIVATE_STUBS\n"); |
| @@ -1130,10 +1152,13 @@ | |
| 1152 | if( getuid()==0 ){ |
| 1153 | int i; |
| 1154 | struct stat sStat; |
| 1155 | Blob dir; |
| 1156 | char *zDir; |
| 1157 | if( g.db!=0 ){ |
| 1158 | db_close(1); |
| 1159 | } |
| 1160 | |
| 1161 | file_canonical_name(zRepo, &dir, 0); |
| 1162 | zDir = blob_str(&dir); |
| 1163 | if( file_isdir(zDir)==1 ){ |
| 1164 | if( file_chdir(zDir, 1) ){ |
| @@ -1158,14 +1183,10 @@ | |
| 1183 | i = setgid(sStat.st_gid); |
| 1184 | i = i || setuid(sStat.st_uid); |
| 1185 | if(i){ |
| 1186 | fossil_fatal("setgid/uid() failed with errno %d", errno); |
| 1187 | } |
| 1188 | } |
| 1189 | #endif |
| 1190 | return zRepo; |
| 1191 | } |
| 1192 | |
| 1193 |
+26
-5
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -100,11 +100,13 @@ | ||
| 100 | 100 | char **argv; /* Full copy of the original (expanded) arguments. */ |
| 101 | 101 | void *library; /* The Tcl library module handle. */ |
| 102 | 102 | void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */ |
| 103 | 103 | void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */ |
| 104 | 104 | void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */ |
| 105 | + void *xFinalize; /* See tcl_FinalizeProc in th_tcl.c. */ | |
| 105 | 106 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 107 | + int useObjProc; /* Non-zero if an objProc can be called directly. */ | |
| 106 | 108 | char *setup; /* The optional Tcl setup script. */ |
| 107 | 109 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 108 | 110 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 109 | 111 | void *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 110 | 112 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -345,10 +347,24 @@ | ||
| 345 | 347 | /* |
| 346 | 348 | ** atexit() handler which frees up "some" of the resources |
| 347 | 349 | ** used by fossil. |
| 348 | 350 | */ |
| 349 | 351 | static void fossil_atexit(void) { |
| 352 | +#if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \ | |
| 353 | + defined(USE_TCL_STUBS) | |
| 354 | + /* | |
| 355 | + ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash | |
| 356 | + ** when exiting while a stubs-enabled Tcl is still loaded. This is due to | |
| 357 | + ** a bug in MinGW, see: | |
| 358 | + ** | |
| 359 | + ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 | |
| 360 | + ** | |
| 361 | + ** The workaround is to manually unload the loaded Tcl library prior to | |
| 362 | + ** exiting the process. This issue does not impact 64-bit Windows. | |
| 363 | + */ | |
| 364 | + unloadTcl(g.interp, &g.tcl); | |
| 365 | +#endif | |
| 350 | 366 | #ifdef FOSSIL_ENABLE_JSON |
| 351 | 367 | cson_value_free(g.json.gc.v); |
| 352 | 368 | memset(&g.json, 0, sizeof(g.json)); |
| 353 | 369 | #endif |
| 354 | 370 | free(g.zErrMsg); |
| @@ -526,10 +542,13 @@ | ||
| 526 | 542 | */ |
| 527 | 543 | #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) |
| 528 | 544 | int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ |
| 529 | 545 | int wmain(int argc, wchar_t **argv) |
| 530 | 546 | #else |
| 547 | +#if defined(_WIN32) | |
| 548 | +int _CRT_glob = 0x0001; /* See MinGW bug #2062 */ | |
| 549 | +#endif | |
| 531 | 550 | int main(int argc, char **argv) |
| 532 | 551 | #endif |
| 533 | 552 | { |
| 534 | 553 | const char *zCmdName = "unknown"; |
| 535 | 554 | int idx; |
| @@ -816,16 +835,19 @@ | ||
| 816 | 835 | #if defined(FOSSIL_ENABLE_SSL) |
| 817 | 836 | fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT); |
| 818 | 837 | #endif |
| 819 | 838 | #if defined(FOSSIL_ENABLE_TCL) |
| 820 | 839 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL); |
| 821 | - rc = Th_Eval(g.interp, 0, "tclEval {info patchlevel}", -1); | |
| 840 | + rc = Th_Eval(g.interp, 0, "tclInvoke info patchlevel", -1); | |
| 822 | 841 | zRc = Th_ReturnCodeName(rc, 0); |
| 823 | 842 | fossil_print("TCL (Tcl %s, loaded %s: %s)\n", |
| 824 | 843 | TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) |
| 825 | 844 | ); |
| 826 | 845 | #endif |
| 846 | +#if defined(USE_TCL_STUBS) | |
| 847 | + fossil_print("USE_TCL_STUBS\n"); | |
| 848 | +#endif | |
| 827 | 849 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 828 | 850 | fossil_print("TCL_STUBS\n"); |
| 829 | 851 | #endif |
| 830 | 852 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 831 | 853 | fossil_print("TCL_PRIVATE_STUBS\n"); |
| @@ -1130,10 +1152,13 @@ | ||
| 1130 | 1152 | if( getuid()==0 ){ |
| 1131 | 1153 | int i; |
| 1132 | 1154 | struct stat sStat; |
| 1133 | 1155 | Blob dir; |
| 1134 | 1156 | char *zDir; |
| 1157 | + if( g.db!=0 ){ | |
| 1158 | + db_close(1); | |
| 1159 | + } | |
| 1135 | 1160 | |
| 1136 | 1161 | file_canonical_name(zRepo, &dir, 0); |
| 1137 | 1162 | zDir = blob_str(&dir); |
| 1138 | 1163 | if( file_isdir(zDir)==1 ){ |
| 1139 | 1164 | if( file_chdir(zDir, 1) ){ |
| @@ -1158,14 +1183,10 @@ | ||
| 1158 | 1183 | i = setgid(sStat.st_gid); |
| 1159 | 1184 | i = i || setuid(sStat.st_uid); |
| 1160 | 1185 | if(i){ |
| 1161 | 1186 | fossil_fatal("setgid/uid() failed with errno %d", errno); |
| 1162 | 1187 | } |
| 1163 | - if( g.db!=0 ){ | |
| 1164 | - db_close(1); | |
| 1165 | - db_open_repository(zRepo); | |
| 1166 | - } | |
| 1167 | 1188 | } |
| 1168 | 1189 | #endif |
| 1169 | 1190 | return zRepo; |
| 1170 | 1191 | } |
| 1171 | 1192 | |
| 1172 | 1193 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -100,11 +100,13 @@ | |
| 100 | char **argv; /* Full copy of the original (expanded) arguments. */ |
| 101 | void *library; /* The Tcl library module handle. */ |
| 102 | void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */ |
| 103 | void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */ |
| 104 | void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */ |
| 105 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 106 | char *setup; /* The optional Tcl setup script. */ |
| 107 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 108 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 109 | void *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 110 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -345,10 +347,24 @@ | |
| 345 | /* |
| 346 | ** atexit() handler which frees up "some" of the resources |
| 347 | ** used by fossil. |
| 348 | */ |
| 349 | static void fossil_atexit(void) { |
| 350 | #ifdef FOSSIL_ENABLE_JSON |
| 351 | cson_value_free(g.json.gc.v); |
| 352 | memset(&g.json, 0, sizeof(g.json)); |
| 353 | #endif |
| 354 | free(g.zErrMsg); |
| @@ -526,10 +542,13 @@ | |
| 526 | */ |
| 527 | #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) |
| 528 | int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ |
| 529 | int wmain(int argc, wchar_t **argv) |
| 530 | #else |
| 531 | int main(int argc, char **argv) |
| 532 | #endif |
| 533 | { |
| 534 | const char *zCmdName = "unknown"; |
| 535 | int idx; |
| @@ -816,16 +835,19 @@ | |
| 816 | #if defined(FOSSIL_ENABLE_SSL) |
| 817 | fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT); |
| 818 | #endif |
| 819 | #if defined(FOSSIL_ENABLE_TCL) |
| 820 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL); |
| 821 | rc = Th_Eval(g.interp, 0, "tclEval {info patchlevel}", -1); |
| 822 | zRc = Th_ReturnCodeName(rc, 0); |
| 823 | fossil_print("TCL (Tcl %s, loaded %s: %s)\n", |
| 824 | TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) |
| 825 | ); |
| 826 | #endif |
| 827 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 828 | fossil_print("TCL_STUBS\n"); |
| 829 | #endif |
| 830 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 831 | fossil_print("TCL_PRIVATE_STUBS\n"); |
| @@ -1130,10 +1152,13 @@ | |
| 1130 | if( getuid()==0 ){ |
| 1131 | int i; |
| 1132 | struct stat sStat; |
| 1133 | Blob dir; |
| 1134 | char *zDir; |
| 1135 | |
| 1136 | file_canonical_name(zRepo, &dir, 0); |
| 1137 | zDir = blob_str(&dir); |
| 1138 | if( file_isdir(zDir)==1 ){ |
| 1139 | if( file_chdir(zDir, 1) ){ |
| @@ -1158,14 +1183,10 @@ | |
| 1158 | i = setgid(sStat.st_gid); |
| 1159 | i = i || setuid(sStat.st_uid); |
| 1160 | if(i){ |
| 1161 | fossil_fatal("setgid/uid() failed with errno %d", errno); |
| 1162 | } |
| 1163 | if( g.db!=0 ){ |
| 1164 | db_close(1); |
| 1165 | db_open_repository(zRepo); |
| 1166 | } |
| 1167 | } |
| 1168 | #endif |
| 1169 | return zRepo; |
| 1170 | } |
| 1171 | |
| 1172 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -100,11 +100,13 @@ | |
| 100 | char **argv; /* Full copy of the original (expanded) arguments. */ |
| 101 | void *library; /* The Tcl library module handle. */ |
| 102 | void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */ |
| 103 | void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */ |
| 104 | void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */ |
| 105 | void *xFinalize; /* See tcl_FinalizeProc in th_tcl.c. */ |
| 106 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 107 | int useObjProc; /* Non-zero if an objProc can be called directly. */ |
| 108 | char *setup; /* The optional Tcl setup script. */ |
| 109 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 110 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 111 | void *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 112 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -345,10 +347,24 @@ | |
| 347 | /* |
| 348 | ** atexit() handler which frees up "some" of the resources |
| 349 | ** used by fossil. |
| 350 | */ |
| 351 | static void fossil_atexit(void) { |
| 352 | #if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \ |
| 353 | defined(USE_TCL_STUBS) |
| 354 | /* |
| 355 | ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash |
| 356 | ** when exiting while a stubs-enabled Tcl is still loaded. This is due to |
| 357 | ** a bug in MinGW, see: |
| 358 | ** |
| 359 | ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 |
| 360 | ** |
| 361 | ** The workaround is to manually unload the loaded Tcl library prior to |
| 362 | ** exiting the process. This issue does not impact 64-bit Windows. |
| 363 | */ |
| 364 | unloadTcl(g.interp, &g.tcl); |
| 365 | #endif |
| 366 | #ifdef FOSSIL_ENABLE_JSON |
| 367 | cson_value_free(g.json.gc.v); |
| 368 | memset(&g.json, 0, sizeof(g.json)); |
| 369 | #endif |
| 370 | free(g.zErrMsg); |
| @@ -526,10 +542,13 @@ | |
| 542 | */ |
| 543 | #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) |
| 544 | int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ |
| 545 | int wmain(int argc, wchar_t **argv) |
| 546 | #else |
| 547 | #if defined(_WIN32) |
| 548 | int _CRT_glob = 0x0001; /* See MinGW bug #2062 */ |
| 549 | #endif |
| 550 | int main(int argc, char **argv) |
| 551 | #endif |
| 552 | { |
| 553 | const char *zCmdName = "unknown"; |
| 554 | int idx; |
| @@ -816,16 +835,19 @@ | |
| 835 | #if defined(FOSSIL_ENABLE_SSL) |
| 836 | fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT); |
| 837 | #endif |
| 838 | #if defined(FOSSIL_ENABLE_TCL) |
| 839 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL); |
| 840 | rc = Th_Eval(g.interp, 0, "tclInvoke info patchlevel", -1); |
| 841 | zRc = Th_ReturnCodeName(rc, 0); |
| 842 | fossil_print("TCL (Tcl %s, loaded %s: %s)\n", |
| 843 | TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) |
| 844 | ); |
| 845 | #endif |
| 846 | #if defined(USE_TCL_STUBS) |
| 847 | fossil_print("USE_TCL_STUBS\n"); |
| 848 | #endif |
| 849 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 850 | fossil_print("TCL_STUBS\n"); |
| 851 | #endif |
| 852 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 853 | fossil_print("TCL_PRIVATE_STUBS\n"); |
| @@ -1130,10 +1152,13 @@ | |
| 1152 | if( getuid()==0 ){ |
| 1153 | int i; |
| 1154 | struct stat sStat; |
| 1155 | Blob dir; |
| 1156 | char *zDir; |
| 1157 | if( g.db!=0 ){ |
| 1158 | db_close(1); |
| 1159 | } |
| 1160 | |
| 1161 | file_canonical_name(zRepo, &dir, 0); |
| 1162 | zDir = blob_str(&dir); |
| 1163 | if( file_isdir(zDir)==1 ){ |
| 1164 | if( file_chdir(zDir, 1) ){ |
| @@ -1158,14 +1183,10 @@ | |
| 1183 | i = setgid(sStat.st_gid); |
| 1184 | i = i || setuid(sStat.st_uid); |
| 1185 | if(i){ |
| 1186 | fossil_fatal("setgid/uid() failed with errno %d", errno); |
| 1187 | } |
| 1188 | } |
| 1189 | #endif |
| 1190 | return zRepo; |
| 1191 | } |
| 1192 | |
| 1193 |
+6
-6
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -407,13 +407,13 @@ | ||
| 407 | 407 | FOSSIL_TCL_SOURCE = 1 |
| 408 | 408 | |
| 409 | 409 | #### Check if the workaround for the MinGW command line handling needs to |
| 410 | 410 | # be enabled by default. |
| 411 | 411 | # |
| 412 | -ifndef BROKEN_MINGW_CMDLINE | |
| 412 | +ifndef MINGW_IS_32BIT_ONLY | |
| 413 | 413 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 414 | -BROKEN_MINGW_CMDLINE = 1 | |
| 414 | +MINGW_IS_32BIT_ONLY = 1 | |
| 415 | 415 | endif |
| 416 | 416 | endif |
| 417 | 417 | |
| 418 | 418 | #### The directories where the zlib include and library files are located. |
| 419 | 419 | # |
| @@ -501,13 +501,13 @@ | ||
| 501 | 501 | RCC += -I$(TCLINCDIR) |
| 502 | 502 | endif |
| 503 | 503 | endif |
| 504 | 504 | |
| 505 | 505 | # With MinGW command line handling workaround |
| 506 | -ifdef BROKEN_MINGW_CMDLINE | |
| 507 | -TCC += -DBROKEN_MINGW_CMDLINE=1 | |
| 508 | -RCC += -DBROKEN_MINGW_CMDLINE=1 | |
| 506 | +ifdef MINGW_IS_32BIT_ONLY | |
| 507 | +TCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T | |
| 508 | +RCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T | |
| 509 | 509 | endif |
| 510 | 510 | |
| 511 | 511 | # With HTTPS support |
| 512 | 512 | ifdef FOSSIL_ENABLE_SSL |
| 513 | 513 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -542,11 +542,11 @@ | ||
| 542 | 542 | # executable that will run in a chroot jail. |
| 543 | 543 | # |
| 544 | 544 | LIB = -static |
| 545 | 545 | |
| 546 | 546 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 547 | -ifndef BROKEN_MINGW_CMDLINE | |
| 547 | +ifndef MINGW_IS_32BIT_ONLY | |
| 548 | 548 | LIB += -municode |
| 549 | 549 | endif |
| 550 | 550 | |
| 551 | 551 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 552 | 552 | ifdef FOSSIL_ENABLE_SSL |
| 553 | 553 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -407,13 +407,13 @@ | |
| 407 | FOSSIL_TCL_SOURCE = 1 |
| 408 | |
| 409 | #### Check if the workaround for the MinGW command line handling needs to |
| 410 | # be enabled by default. |
| 411 | # |
| 412 | ifndef BROKEN_MINGW_CMDLINE |
| 413 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 414 | BROKEN_MINGW_CMDLINE = 1 |
| 415 | endif |
| 416 | endif |
| 417 | |
| 418 | #### The directories where the zlib include and library files are located. |
| 419 | # |
| @@ -501,13 +501,13 @@ | |
| 501 | RCC += -I$(TCLINCDIR) |
| 502 | endif |
| 503 | endif |
| 504 | |
| 505 | # With MinGW command line handling workaround |
| 506 | ifdef BROKEN_MINGW_CMDLINE |
| 507 | TCC += -DBROKEN_MINGW_CMDLINE=1 |
| 508 | RCC += -DBROKEN_MINGW_CMDLINE=1 |
| 509 | endif |
| 510 | |
| 511 | # With HTTPS support |
| 512 | ifdef FOSSIL_ENABLE_SSL |
| 513 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -542,11 +542,11 @@ | |
| 542 | # executable that will run in a chroot jail. |
| 543 | # |
| 544 | LIB = -static |
| 545 | |
| 546 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 547 | ifndef BROKEN_MINGW_CMDLINE |
| 548 | LIB += -municode |
| 549 | endif |
| 550 | |
| 551 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 552 | ifdef FOSSIL_ENABLE_SSL |
| 553 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -407,13 +407,13 @@ | |
| 407 | FOSSIL_TCL_SOURCE = 1 |
| 408 | |
| 409 | #### Check if the workaround for the MinGW command line handling needs to |
| 410 | # be enabled by default. |
| 411 | # |
| 412 | ifndef MINGW_IS_32BIT_ONLY |
| 413 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 414 | MINGW_IS_32BIT_ONLY = 1 |
| 415 | endif |
| 416 | endif |
| 417 | |
| 418 | #### The directories where the zlib include and library files are located. |
| 419 | # |
| @@ -501,13 +501,13 @@ | |
| 501 | RCC += -I$(TCLINCDIR) |
| 502 | endif |
| 503 | endif |
| 504 | |
| 505 | # With MinGW command line handling workaround |
| 506 | ifdef MINGW_IS_32BIT_ONLY |
| 507 | TCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T |
| 508 | RCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T |
| 509 | endif |
| 510 | |
| 511 | # With HTTPS support |
| 512 | ifdef FOSSIL_ENABLE_SSL |
| 513 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -542,11 +542,11 @@ | |
| 542 | # executable that will run in a chroot jail. |
| 543 | # |
| 544 | LIB = -static |
| 545 | |
| 546 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 547 | ifndef MINGW_IS_32BIT_ONLY |
| 548 | LIB += -municode |
| 549 | endif |
| 550 | |
| 551 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 552 | ifdef FOSSIL_ENABLE_SSL |
| 553 |
+1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -1199,10 +1199,11 @@ | ||
| 1199 | 1199 | login_check_credentials(); |
| 1200 | 1200 | if( !g.perm.Setup ){ |
| 1201 | 1201 | login_needed(); |
| 1202 | 1202 | } |
| 1203 | 1203 | |
| 1204 | + (void) aCmdHelp; /* NOTE: Silence compiler warning. */ | |
| 1204 | 1205 | style_header("Settings"); |
| 1205 | 1206 | db_open_local(0); |
| 1206 | 1207 | db_begin_transaction(); |
| 1207 | 1208 | @ <p>This page provides a simple interface to the "fossil setting" command. |
| 1208 | 1209 | @ See the "fossil help setting" output below for further information on |
| 1209 | 1210 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -1199,10 +1199,11 @@ | |
| 1199 | login_check_credentials(); |
| 1200 | if( !g.perm.Setup ){ |
| 1201 | login_needed(); |
| 1202 | } |
| 1203 | |
| 1204 | style_header("Settings"); |
| 1205 | db_open_local(0); |
| 1206 | db_begin_transaction(); |
| 1207 | @ <p>This page provides a simple interface to the "fossil setting" command. |
| 1208 | @ See the "fossil help setting" output below for further information on |
| 1209 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -1199,10 +1199,11 @@ | |
| 1199 | login_check_credentials(); |
| 1200 | if( !g.perm.Setup ){ |
| 1201 | login_needed(); |
| 1202 | } |
| 1203 | |
| 1204 | (void) aCmdHelp; /* NOTE: Silence compiler warning. */ |
| 1205 | style_header("Settings"); |
| 1206 | db_open_local(0); |
| 1207 | db_begin_transaction(); |
| 1208 | @ <p>This page provides a simple interface to the "fossil setting" command. |
| 1209 | @ See the "fossil help setting" output below for further information on |
| 1210 |
+8
-1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -175,11 +175,18 @@ | ||
| 175 | 175 | } |
| 176 | 176 | for(i=0; i<nFormAction; i++){ |
| 177 | 177 | @ gebi("form%d(i+1)").action="%s(aFormAction[i])"; |
| 178 | 178 | } |
| 179 | 179 | @ } |
| 180 | - if( db_get_boolean("auto-hyperlink-mouseover",0) ){ | |
| 180 | + if( strglob("*Opera Mini/[1-9]*", P("HTTP_USER_AGENT")) ){ | |
| 181 | + /* Special case for Opera Mini, which executes JS server-side */ | |
| 182 | + @ var isOperaMini = Object.prototype.toString.call(window.operamini) | |
| 183 | + @ === "[object OperaMini]"; | |
| 184 | + @ if( isOperaMini ){ | |
| 185 | + @ setTimeout("setAllHrefs();",%d(nDelay)); | |
| 186 | + @ } | |
| 187 | + }else if( db_get_boolean("auto-hyperlink-mouseover",0) ){ | |
| 181 | 188 | /* Require mouse movement prior to activating hyperlinks */ |
| 182 | 189 | @ document.getElementsByTagName("body")[0].onmousemove=function(){ |
| 183 | 190 | @ setTimeout("setAllHrefs();",%d(nDelay)); |
| 184 | 191 | @ this.onmousemove = null; |
| 185 | 192 | @ } |
| 186 | 193 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -175,11 +175,18 @@ | |
| 175 | } |
| 176 | for(i=0; i<nFormAction; i++){ |
| 177 | @ gebi("form%d(i+1)").action="%s(aFormAction[i])"; |
| 178 | } |
| 179 | @ } |
| 180 | if( db_get_boolean("auto-hyperlink-mouseover",0) ){ |
| 181 | /* Require mouse movement prior to activating hyperlinks */ |
| 182 | @ document.getElementsByTagName("body")[0].onmousemove=function(){ |
| 183 | @ setTimeout("setAllHrefs();",%d(nDelay)); |
| 184 | @ this.onmousemove = null; |
| 185 | @ } |
| 186 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -175,11 +175,18 @@ | |
| 175 | } |
| 176 | for(i=0; i<nFormAction; i++){ |
| 177 | @ gebi("form%d(i+1)").action="%s(aFormAction[i])"; |
| 178 | } |
| 179 | @ } |
| 180 | if( strglob("*Opera Mini/[1-9]*", P("HTTP_USER_AGENT")) ){ |
| 181 | /* Special case for Opera Mini, which executes JS server-side */ |
| 182 | @ var isOperaMini = Object.prototype.toString.call(window.operamini) |
| 183 | @ === "[object OperaMini]"; |
| 184 | @ if( isOperaMini ){ |
| 185 | @ setTimeout("setAllHrefs();",%d(nDelay)); |
| 186 | @ } |
| 187 | }else if( db_get_boolean("auto-hyperlink-mouseover",0) ){ |
| 188 | /* Require mouse movement prior to activating hyperlinks */ |
| 189 | @ document.getElementsByTagName("body")[0].onmousemove=function(){ |
| 190 | @ setTimeout("setAllHrefs();",%d(nDelay)); |
| 191 | @ this.onmousemove = null; |
| 192 | @ } |
| 193 |
M
src/th.h
+4
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -155,11 +155,15 @@ | ||
| 155 | 155 | */ |
| 156 | 156 | int th_register_language(Th_Interp *interp); /* th_lang.c */ |
| 157 | 157 | int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */ |
| 158 | 158 | int th_register_vfs(Th_Interp *interp); /* th_vfs.c */ |
| 159 | 159 | int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */ |
| 160 | + | |
| 161 | +#ifdef FOSSIL_ENABLE_TCL | |
| 160 | 162 | int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */ |
| 163 | +int unloadTcl(Th_Interp *interp, void *pContext); /* th_tcl.c */ | |
| 164 | +#endif | |
| 161 | 165 | |
| 162 | 166 | /* |
| 163 | 167 | ** General purpose hash table from th_lang.c. |
| 164 | 168 | */ |
| 165 | 169 | typedef struct Th_Hash Th_Hash; |
| 166 | 170 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -155,11 +155,15 @@ | |
| 155 | */ |
| 156 | int th_register_language(Th_Interp *interp); /* th_lang.c */ |
| 157 | int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */ |
| 158 | int th_register_vfs(Th_Interp *interp); /* th_vfs.c */ |
| 159 | int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */ |
| 160 | int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */ |
| 161 | |
| 162 | /* |
| 163 | ** General purpose hash table from th_lang.c. |
| 164 | */ |
| 165 | typedef struct Th_Hash Th_Hash; |
| 166 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -155,11 +155,15 @@ | |
| 155 | */ |
| 156 | int th_register_language(Th_Interp *interp); /* th_lang.c */ |
| 157 | int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */ |
| 158 | int th_register_vfs(Th_Interp *interp); /* th_vfs.c */ |
| 159 | int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */ |
| 160 | |
| 161 | #ifdef FOSSIL_ENABLE_TCL |
| 162 | int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */ |
| 163 | int unloadTcl(Th_Interp *interp, void *pContext); /* th_tcl.c */ |
| 164 | #endif |
| 165 | |
| 166 | /* |
| 167 | ** General purpose hash table from th_lang.c. |
| 168 | */ |
| 169 | typedef struct Th_Hash Th_Hash; |
| 170 |
+6
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -298,10 +298,11 @@ | ||
| 298 | 298 | ** Return true if the fossil binary has the given compile-time feature |
| 299 | 299 | ** enabled. The set of features includes: |
| 300 | 300 | ** |
| 301 | 301 | ** "ssl" = FOSSIL_ENABLE_SSL |
| 302 | 302 | ** "tcl" = FOSSIL_ENABLE_TCL |
| 303 | +** "useTclStubs" = USE_TCL_STUBS | |
| 303 | 304 | ** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS |
| 304 | 305 | ** "tclPrivateStubs" = FOSSIL_ENABLE_TCL_PRIVATE_STUBS |
| 305 | 306 | ** "json" = FOSSIL_ENABLE_JSON |
| 306 | 307 | ** "markdown" = FOSSIL_ENABLE_MARKDOWN |
| 307 | 308 | ** |
| @@ -329,10 +330,15 @@ | ||
| 329 | 330 | #endif |
| 330 | 331 | #if defined(FOSSIL_ENABLE_TCL) |
| 331 | 332 | else if( 0 == fossil_strnicmp( zArg, "tcl\0", 4 ) ){ |
| 332 | 333 | rc = 1; |
| 333 | 334 | } |
| 335 | +#endif | |
| 336 | +#if defined(USE_TCL_STUBS) | |
| 337 | + else if( 0 == fossil_strnicmp( zArg, "useTclStubs\0", 12 ) ){ | |
| 338 | + rc = 1; | |
| 339 | + } | |
| 334 | 340 | #endif |
| 335 | 341 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 336 | 342 | else if( 0 == fossil_strnicmp( zArg, "tclStubs\0", 9 ) ){ |
| 337 | 343 | rc = 1; |
| 338 | 344 | } |
| 339 | 345 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -298,10 +298,11 @@ | |
| 298 | ** Return true if the fossil binary has the given compile-time feature |
| 299 | ** enabled. The set of features includes: |
| 300 | ** |
| 301 | ** "ssl" = FOSSIL_ENABLE_SSL |
| 302 | ** "tcl" = FOSSIL_ENABLE_TCL |
| 303 | ** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS |
| 304 | ** "tclPrivateStubs" = FOSSIL_ENABLE_TCL_PRIVATE_STUBS |
| 305 | ** "json" = FOSSIL_ENABLE_JSON |
| 306 | ** "markdown" = FOSSIL_ENABLE_MARKDOWN |
| 307 | ** |
| @@ -329,10 +330,15 @@ | |
| 329 | #endif |
| 330 | #if defined(FOSSIL_ENABLE_TCL) |
| 331 | else if( 0 == fossil_strnicmp( zArg, "tcl\0", 4 ) ){ |
| 332 | rc = 1; |
| 333 | } |
| 334 | #endif |
| 335 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 336 | else if( 0 == fossil_strnicmp( zArg, "tclStubs\0", 9 ) ){ |
| 337 | rc = 1; |
| 338 | } |
| 339 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -298,10 +298,11 @@ | |
| 298 | ** Return true if the fossil binary has the given compile-time feature |
| 299 | ** enabled. The set of features includes: |
| 300 | ** |
| 301 | ** "ssl" = FOSSIL_ENABLE_SSL |
| 302 | ** "tcl" = FOSSIL_ENABLE_TCL |
| 303 | ** "useTclStubs" = USE_TCL_STUBS |
| 304 | ** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS |
| 305 | ** "tclPrivateStubs" = FOSSIL_ENABLE_TCL_PRIVATE_STUBS |
| 306 | ** "json" = FOSSIL_ENABLE_JSON |
| 307 | ** "markdown" = FOSSIL_ENABLE_MARKDOWN |
| 308 | ** |
| @@ -329,10 +330,15 @@ | |
| 330 | #endif |
| 331 | #if defined(FOSSIL_ENABLE_TCL) |
| 332 | else if( 0 == fossil_strnicmp( zArg, "tcl\0", 4 ) ){ |
| 333 | rc = 1; |
| 334 | } |
| 335 | #endif |
| 336 | #if defined(USE_TCL_STUBS) |
| 337 | else if( 0 == fossil_strnicmp( zArg, "useTclStubs\0", 12 ) ){ |
| 338 | rc = 1; |
| 339 | } |
| 340 | #endif |
| 341 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 342 | else if( 0 == fossil_strnicmp( zArg, "tclStubs\0", 9 ) ){ |
| 343 | rc = 1; |
| 344 | } |
| 345 |
+177
-87
| --- src/th_tcl.c | ||
| +++ src/th_tcl.c | ||
| @@ -23,48 +23,14 @@ | ||
| 23 | 23 | #ifdef FOSSIL_ENABLE_TCL |
| 24 | 24 | |
| 25 | 25 | #include "th.h" |
| 26 | 26 | #include "tcl.h" |
| 27 | 27 | |
| 28 | -/* | |
| 29 | -** Has the decision about whether or not to use Tcl_EvalObjv already been made | |
| 30 | -** via the Makefile? | |
| 31 | - */ | |
| 32 | -#if !defined(USE_TCL_EVALOBJV) | |
| 33 | -/* | |
| 34 | -** Are we being compiled against Tcl 8.6b1 or b2? This check is [mostly] | |
| 35 | -** wrong for at the following reason: | |
| 36 | -** | |
| 37 | -** 1. Technically, this check is completely useless when the stubs mechanism | |
| 38 | -** is in use. In that case, a runtime version check would be required and | |
| 39 | -** that has not been implemented. | |
| 40 | -** | |
| 41 | -** However, if a particular user compiles and runs against Tcl 8.6b1 or b2, | |
| 42 | -** this will cause a fallback to using the "conservative" method of directly | |
| 43 | -** invoking a Tcl command. In that case, potential crashes will be avoided if | |
| 44 | -** the user just so happened to compile or run against Tcl 8.6b1 or b2. | |
| 45 | - */ | |
| 46 | -#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 6) && \ | |
| 47 | - (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE) && (TCL_RELEASE_SERIAL < 3) | |
| 48 | -/* | |
| 49 | -** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using | |
| 50 | -** Tcl_EvalObjv instead of invoking the objProc directly. | |
| 51 | - */ | |
| 52 | -# define USE_TCL_EVALOBJV (1) | |
| 53 | -#else | |
| 54 | -/* | |
| 55 | -** We should be able to safely use Tcl_GetCommandInfoFromToken, when the need | |
| 56 | -** arises, to invoke a specific Tcl command "directly" with some arguments. | |
| 57 | - */ | |
| 58 | -# define USE_TCL_EVALOBJV (0) | |
| 59 | -#endif /* (TCL_MAJOR_VERSION > 8) ... */ | |
| 60 | -#endif /* !defined(USE_TCL_EVALOBJV) */ | |
| 61 | - | |
| 62 | 28 | /* |
| 63 | 29 | ** These macros are designed to reduce the redundant code required to marshal |
| 64 | 30 | ** arguments from TH1 to Tcl. |
| 65 | - */ | |
| 31 | +*/ | |
| 66 | 32 | #define USE_ARGV_TO_OBJV() \ |
| 67 | 33 | int objc; \ |
| 68 | 34 | Tcl_Obj **objv; \ |
| 69 | 35 | int i; |
| 70 | 36 | |
| @@ -83,18 +49,25 @@ | ||
| 83 | 49 | ckfree((char *)objv); |
| 84 | 50 | |
| 85 | 51 | /* |
| 86 | 52 | ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl |
| 87 | 53 | ** context. |
| 88 | - */ | |
| 54 | +*/ | |
| 89 | 55 | #define GET_CTX_TCL_INTERP(ctx) \ |
| 90 | 56 | ((struct TclContext *)(ctx))->interp |
| 91 | 57 | |
| 58 | +/* | |
| 59 | +** Fetch the (logically boolean) value from the specified void pointer that | |
| 60 | +** indicates whether or not we can/should use direct objProc calls. | |
| 61 | +*/ | |
| 62 | +#define GET_CTX_TCL_USEOBJPROC(ctx) \ | |
| 63 | + ((struct TclContext *)(ctx))->useObjProc | |
| 64 | + | |
| 92 | 65 | /* |
| 93 | 66 | ** Define the Tcl shared library name, some exported function names, and some |
| 94 | 67 | ** cross-platform macros for use with the Tcl stubs mechanism, when enabled. |
| 95 | - */ | |
| 68 | +*/ | |
| 96 | 69 | #if defined(USE_TCL_STUBS) |
| 97 | 70 | # if defined(_WIN32) |
| 98 | 71 | # define WIN32_LEAN_AND_MEAN |
| 99 | 72 | # include <windows.h> |
| 100 | 73 | # ifndef TCL_LIBRARY_NAME |
| @@ -144,33 +117,37 @@ | ||
| 144 | 117 | # define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp" |
| 145 | 118 | # endif |
| 146 | 119 | # ifndef TCL_DELETEINTERP_NAME |
| 147 | 120 | # define TCL_DELETEINTERP_NAME "_Tcl_DeleteInterp" |
| 148 | 121 | # endif |
| 122 | +# ifndef TCL_FINALIZE_NAME | |
| 123 | +# define TCL_FINALIZE_NAME "_Tcl_Finalize" | |
| 124 | +# endif | |
| 149 | 125 | #endif /* defined(USE_TCL_STUBS) */ |
| 150 | 126 | |
| 151 | 127 | /* |
| 152 | 128 | ** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed |
| 153 | 129 | ** when the Tcl library is being loaded dynamically by a stubs-enabled |
| 154 | 130 | ** application (i.e. the inverse of using a stubs-enabled package). These are |
| 155 | 131 | ** the only Tcl API functions that MUST be called prior to being able to call |
| 156 | 132 | ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete |
| 157 | 133 | ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp |
| 158 | -** function type is also required. | |
| 159 | - */ | |
| 134 | +** and Tcl_Finalize function types are also required. | |
| 135 | +*/ | |
| 160 | 136 | typedef void (tcl_FindExecutableProc) (const char * argv0); |
| 161 | 137 | typedef Tcl_Interp *(tcl_CreateInterpProc) (void); |
| 162 | 138 | typedef void (tcl_DeleteInterpProc) (Tcl_Interp *interp); |
| 139 | +typedef void (tcl_FinalizeProc) (void); | |
| 163 | 140 | |
| 164 | 141 | /* |
| 165 | 142 | ** The function types for the "hook" functions to be called before and after a |
| 166 | 143 | ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function |
| 167 | 144 | ** returns anything but TH_OK, then evaluation of the Tcl script is skipped and |
| 168 | 145 | ** that value is used as the return code. If the "post" function returns |
| 169 | 146 | ** anything other than its rc argument, that will become the new return code |
| 170 | 147 | ** for the command. |
| 171 | - */ | |
| 148 | +*/ | |
| 172 | 149 | typedef int (tcl_NotifyProc) ( |
| 173 | 150 | void *pContext, /* The context for this notification. */ |
| 174 | 151 | Th_Interp *interp, /* The TH1 interpreter being used. */ |
| 175 | 152 | void *ctx, /* The original TH1 command context. */ |
| 176 | 153 | int argc, /* Number of arguments for the TH1 command. */ |
| @@ -181,27 +158,27 @@ | ||
| 181 | 158 | |
| 182 | 159 | /* |
| 183 | 160 | ** Are we using our own private implementation of the Tcl stubs mechanism? If |
| 184 | 161 | ** this is enabled, it prevents the user from having to link against the Tcl |
| 185 | 162 | ** stubs library for the target platform, which may not be readily available. |
| 186 | - */ | |
| 163 | +*/ | |
| 187 | 164 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 188 | 165 | /* |
| 189 | 166 | ** HACK: Using some preprocessor magic and a private static variable, redirect |
| 190 | 167 | ** the Tcl API calls [found within this file] to the function pointers |
| 191 | 168 | ** that will be contained in our private Tcl stubs table. This takes |
| 192 | 169 | ** advantage of the fact that the Tcl headers always define the Tcl API |
| 193 | 170 | ** functions in terms of the "tclStubsPtr" variable. |
| 194 | - */ | |
| 171 | +*/ | |
| 195 | 172 | #define tclStubsPtr privateTclStubsPtr |
| 196 | 173 | static const TclStubs *tclStubsPtr = NULL; |
| 197 | 174 | |
| 198 | 175 | /* |
| 199 | 176 | ** Create a Tcl interpreter structure that mirrors just enough fields to get |
| 200 | 177 | ** it up and running successfully with our private implementation of the Tcl |
| 201 | 178 | ** stubs mechanism. |
| 202 | - */ | |
| 179 | +*/ | |
| 203 | 180 | struct PrivateTclInterp { |
| 204 | 181 | char *result; |
| 205 | 182 | Tcl_FreeProc *freeProc; |
| 206 | 183 | int errorLine; |
| 207 | 184 | const struct TclStubs *stubTable; |
| @@ -209,11 +186,11 @@ | ||
| 209 | 186 | |
| 210 | 187 | /* |
| 211 | 188 | ** Fossil can now be compiled without linking to the actual Tcl stubs library. |
| 212 | 189 | ** In that case, this function will be used to perform those steps that would |
| 213 | 190 | ** normally be performed within the Tcl stubs library. |
| 214 | - */ | |
| 191 | +*/ | |
| 215 | 192 | static int initTclStubs( |
| 216 | 193 | Th_Interp *interp, |
| 217 | 194 | Tcl_Interp *tclInterp |
| 218 | 195 | ){ |
| 219 | 196 | tclStubsPtr = ((struct PrivateTclInterp *)tclInterp)->stubTable; |
| @@ -231,24 +208,56 @@ | ||
| 231 | 208 | return TH_ERROR; |
| 232 | 209 | } |
| 233 | 210 | return TH_OK; |
| 234 | 211 | } |
| 235 | 212 | #endif /* defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) */ |
| 213 | + | |
| 214 | +/* | |
| 215 | +** Is the loaded version of Tcl one where querying and/or calling the objProc | |
| 216 | +** for a command does not work for some reason? The following special cases | |
| 217 | +** are currently handled by this function: | |
| 218 | +** | |
| 219 | +** 1. All versions of Tcl 8.4 have a bug that causes a crash when calling into | |
| 220 | +** the Tcl_GetCommandFromObj function via stubs (i.e. the stubs table entry | |
| 221 | +** is NULL). | |
| 222 | +** | |
| 223 | +** 2. Various beta builds of Tcl 8.6, namely 1 and 2, have an NRE-specific bug | |
| 224 | +** in Tcl_EvalObjCmd (SF bug #3399564) that cause a panic when calling into | |
| 225 | +** the objProc directly. | |
| 226 | +** | |
| 227 | +** For both of the above cases, the Tcl_EvalObjv function must be used instead | |
| 228 | +** of the more direct route of querying and calling the objProc directly. | |
| 229 | +*/ | |
| 230 | +static int canUseObjProc(){ | |
| 231 | + int major = -1, minor = -1, patchLevel = -1, type = -1; | |
| 232 | + | |
| 233 | + Tcl_GetVersion(&major, &minor, &patchLevel, &type); | |
| 234 | + if( major<0 || minor<0 || patchLevel<0 || type<0 ){ | |
| 235 | + return 0; /* NOTE: Invalid version info, assume bad. */ | |
| 236 | + } | |
| 237 | + if( major==8 && minor==4 ){ | |
| 238 | + return 0; /* NOTE: Disabled on Tcl 8.4, missing public API. */ | |
| 239 | + } | |
| 240 | + if( major==8 && minor==6 && type==TCL_BETA_RELEASE && patchLevel<3 ){ | |
| 241 | + return 0; /* NOTE: Disabled on Tcl 8.6b1/b2, SF bug #3399564. */ | |
| 242 | + } | |
| 243 | + return 1; /* NOTE: For all other cases, assume good. */ | |
| 244 | +} | |
| 236 | 245 | |
| 237 | 246 | /* |
| 238 | 247 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 239 | 248 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 240 | 249 | ** by the caller. This must be declared here because quite a few functions in |
| 241 | 250 | ** this file need to use it before it can be defined. |
| 242 | - */ | |
| 251 | +*/ | |
| 243 | 252 | static int createTclInterp(Th_Interp *interp, void *pContext); |
| 244 | 253 | |
| 245 | 254 | /* |
| 246 | 255 | ** Returns the Tcl interpreter result as a string with the associated length. |
| 247 | 256 | ** If the Tcl interpreter or the Tcl result are NULL, the length will be 0. |
| 248 | 257 | ** If the length pointer is NULL, the length will not be stored. |
| 249 | - */ | |
| 258 | +*/ | |
| 250 | 259 | static char *getTclResult( |
| 251 | 260 | Tcl_Interp *pInterp, |
| 252 | 261 | int *pN |
| 253 | 262 | ){ |
| 254 | 263 | Tcl_Obj *resultPtr; |
| @@ -274,11 +283,13 @@ | ||
| 274 | 283 | char **argv; /* Full copy of the original arguments. */ |
| 275 | 284 | void *library; /* The Tcl library module handle. */ |
| 276 | 285 | tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */ |
| 277 | 286 | tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */ |
| 278 | 287 | tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 288 | + tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */ | |
| 279 | 289 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 290 | + int useObjProc; /* Non-zero if an objProc can be called directly. */ | |
| 280 | 291 | char *setup; /* The optional Tcl setup script. */ |
| 281 | 292 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 282 | 293 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 283 | 294 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 284 | 295 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -443,20 +454,13 @@ | ||
| 443 | 454 | int argc, |
| 444 | 455 | const char **argv, |
| 445 | 456 | int *argl |
| 446 | 457 | ){ |
| 447 | 458 | Tcl_Interp *tclInterp; |
| 448 | -#if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV | |
| 449 | - Tcl_Command command; | |
| 450 | - Tcl_CmdInfo cmdInfo; | |
| 451 | -#endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ | |
| 452 | 459 | int rc = TH_OK; |
| 453 | 460 | int nResult; |
| 454 | 461 | const char *zResult; |
| 455 | -#if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV | |
| 456 | - Tcl_Obj *objPtr; | |
| 457 | -#endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ | |
| 458 | 462 | USE_ARGV_TO_OBJV(); |
| 459 | 463 | |
| 460 | 464 | if( createTclInterp(interp, ctx)!=TH_OK ){ |
| 461 | 465 | return TH_ERROR; |
| 462 | 466 | } |
| @@ -472,35 +476,40 @@ | ||
| 472 | 476 | if( rc!=TH_OK ){ |
| 473 | 477 | return rc; |
| 474 | 478 | } |
| 475 | 479 | Tcl_Preserve((ClientData)tclInterp); |
| 476 | 480 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 477 | - objPtr = Tcl_NewStringObj(argv[1], argl[1]); | |
| 478 | - Tcl_IncrRefCount(objPtr); | |
| 479 | - command = Tcl_GetCommandFromObj(tclInterp, objPtr); | |
| 480 | - if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ | |
| 481 | - Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); | |
| 482 | - Tcl_DecrRefCount(objPtr); | |
| 483 | - Tcl_Release((ClientData)tclInterp); | |
| 484 | - return TH_ERROR; | |
| 485 | - } | |
| 486 | - if( !cmdInfo.objProc ){ | |
| 487 | - Th_ErrorMessage(interp, "cannot invoke Tcl command:", argv[1], argl[1]); | |
| 488 | - Tcl_DecrRefCount(objPtr); | |
| 489 | - Tcl_Release((ClientData)tclInterp); | |
| 490 | - return TH_ERROR; | |
| 491 | - } | |
| 492 | - Tcl_DecrRefCount(objPtr); | |
| 493 | -#endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ | |
| 494 | - COPY_ARGV_TO_OBJV(); | |
| 495 | -#if defined(USE_TCL_EVALOBJV) && USE_TCL_EVALOBJV | |
| 496 | - rc = Tcl_EvalObjv(tclInterp, objc, objv, 0); | |
| 497 | -#else | |
| 498 | - Tcl_ResetResult(tclInterp); | |
| 499 | - rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv); | |
| 500 | -#endif /* defined(USE_TCL_EVALOBJV) && USE_TCL_EVALOBJV */ | |
| 501 | - FREE_ARGV_TO_OBJV(); | |
| 481 | + if( GET_CTX_TCL_USEOBJPROC(ctx) ){ | |
| 482 | + Tcl_Command command; | |
| 483 | + Tcl_CmdInfo cmdInfo; | |
| 484 | + Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], argl[1]); | |
| 485 | + Tcl_IncrRefCount(objPtr); | |
| 486 | + command = Tcl_GetCommandFromObj(tclInterp, objPtr); | |
| 487 | + if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ | |
| 488 | + Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); | |
| 489 | + Tcl_DecrRefCount(objPtr); | |
| 490 | + Tcl_Release((ClientData)tclInterp); | |
| 491 | + return TH_ERROR; | |
| 492 | + } | |
| 493 | + if( !cmdInfo.objProc ){ | |
| 494 | + Th_ErrorMessage(interp, "cannot invoke Tcl command:", argv[1], argl[1]); | |
| 495 | + Tcl_DecrRefCount(objPtr); | |
| 496 | + Tcl_Release((ClientData)tclInterp); | |
| 497 | + return TH_ERROR; | |
| 498 | + } | |
| 499 | + Tcl_DecrRefCount(objPtr); | |
| 500 | + COPY_ARGV_TO_OBJV(); | |
| 501 | + Tcl_ResetResult(tclInterp); | |
| 502 | + rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv); | |
| 503 | + FREE_ARGV_TO_OBJV(); | |
| 504 | + }else | |
| 505 | +#endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ | |
| 506 | + { | |
| 507 | + COPY_ARGV_TO_OBJV(); | |
| 508 | + rc = Tcl_EvalObjv(tclInterp, objc, objv, 0); | |
| 509 | + FREE_ARGV_TO_OBJV(); | |
| 510 | + } | |
| 502 | 511 | zResult = getTclResult(tclInterp, &nResult); |
| 503 | 512 | Th_SetResult(interp, zResult, nResult); |
| 504 | 513 | Tcl_Release((ClientData)tclInterp); |
| 505 | 514 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc); |
| 506 | 515 | return rc; |
| @@ -586,11 +595,11 @@ | ||
| 586 | 595 | }; |
| 587 | 596 | |
| 588 | 597 | /* |
| 589 | 598 | ** Called if the Tcl interpreter is deleted. Removes the Tcl integration |
| 590 | 599 | ** commands from the TH1 interpreter. |
| 591 | - */ | |
| 600 | +*/ | |
| 592 | 601 | static void Th1DeleteProc( |
| 593 | 602 | ClientData clientData, |
| 594 | 603 | Tcl_Interp *interp |
| 595 | 604 | ){ |
| 596 | 605 | int i; |
| @@ -607,23 +616,25 @@ | ||
| 607 | 616 | ** When Tcl stubs support is enabled, attempts to dynamically load the Tcl |
| 608 | 617 | ** shared library and fetch the function pointers necessary to create an |
| 609 | 618 | ** interpreter and initialize the stubs mechanism; otherwise, simply setup |
| 610 | 619 | ** the function pointers provided by the caller with the statically linked |
| 611 | 620 | ** functions. |
| 612 | - */ | |
| 621 | +*/ | |
| 613 | 622 | static int loadTcl( |
| 614 | 623 | Th_Interp *interp, |
| 615 | 624 | void **pLibrary, |
| 616 | 625 | tcl_FindExecutableProc **pxFindExecutable, |
| 617 | 626 | tcl_CreateInterpProc **pxCreateInterp, |
| 618 | - tcl_DeleteInterpProc **pxDeleteInterp | |
| 627 | + tcl_DeleteInterpProc **pxDeleteInterp, | |
| 628 | + tcl_FinalizeProc **pxFinalize | |
| 619 | 629 | ){ |
| 620 | 630 | #if defined(USE_TCL_STUBS) |
| 621 | 631 | char fileName[] = TCL_LIBRARY_NAME; |
| 622 | 632 | #endif /* defined(USE_TCL_STUBS) */ |
| 623 | 633 | |
| 624 | - if( !pLibrary || !pxFindExecutable || !pxCreateInterp || !pxDeleteInterp ){ | |
| 634 | + if( !pLibrary || !pxFindExecutable || !pxCreateInterp || | |
| 635 | + !pxDeleteInterp || !pxFinalize ){ | |
| 625 | 636 | Th_ErrorMessage(interp, |
| 626 | 637 | "invalid Tcl loader argument(s)", (const char *)"", 0); |
| 627 | 638 | return TH_ERROR; |
| 628 | 639 | } |
| 629 | 640 | #if defined(USE_TCL_STUBS) |
| @@ -631,10 +642,11 @@ | ||
| 631 | 642 | void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL); |
| 632 | 643 | if( library ){ |
| 633 | 644 | tcl_FindExecutableProc *xFindExecutable; |
| 634 | 645 | tcl_CreateInterpProc *xCreateInterp; |
| 635 | 646 | tcl_DeleteInterpProc *xDeleteInterp; |
| 647 | + tcl_FinalizeProc *xFinalize; | |
| 636 | 648 | const char *procName = TCL_FINDEXECUTABLE_NAME; |
| 637 | 649 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1); |
| 638 | 650 | if( !xFindExecutable ){ |
| 639 | 651 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName); |
| 640 | 652 | } |
| @@ -663,35 +675,49 @@ | ||
| 663 | 675 | if( !xDeleteInterp ){ |
| 664 | 676 | Th_ErrorMessage(interp, |
| 665 | 677 | "could not locate Tcl_DeleteInterp", (const char *)"", 0); |
| 666 | 678 | dlclose(library); |
| 667 | 679 | return TH_ERROR; |
| 680 | + } | |
| 681 | + procName = TCL_FINALIZE_NAME; | |
| 682 | + xFinalize = (tcl_FinalizeProc *)dlsym(library, procName + 1); | |
| 683 | + if( !xFinalize ){ | |
| 684 | + xFinalize = (tcl_FinalizeProc *)dlsym(library, procName); | |
| 685 | + } | |
| 686 | + if( !xFinalize ){ | |
| 687 | + Th_ErrorMessage(interp, | |
| 688 | + "could not locate Tcl_Finalize", (const char *)"", 0); | |
| 689 | + dlclose(library); | |
| 690 | + return TH_ERROR; | |
| 668 | 691 | } |
| 669 | 692 | *pLibrary = library; |
| 670 | 693 | *pxFindExecutable = xFindExecutable; |
| 671 | 694 | *pxCreateInterp = xCreateInterp; |
| 672 | 695 | *pxDeleteInterp = xDeleteInterp; |
| 696 | + *pxFinalize = xFinalize; | |
| 673 | 697 | return TH_OK; |
| 674 | 698 | } |
| 675 | 699 | } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */ |
| 700 | + fileName[TCL_MINOR_OFFSET] = 'x'; | |
| 676 | 701 | Th_ErrorMessage(interp, |
| 677 | - "could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"", | |
| 678 | - (const char *)"", 0); | |
| 702 | + "could not load any supported Tcl 8.6, 8.5, or 8.4 shared library \"", | |
| 703 | + fileName, -1); | |
| 679 | 704 | return TH_ERROR; |
| 680 | 705 | #else |
| 681 | 706 | *pLibrary = 0; |
| 682 | 707 | *pxFindExecutable = Tcl_FindExecutable; |
| 683 | 708 | *pxCreateInterp = Tcl_CreateInterp; |
| 684 | 709 | *pxDeleteInterp = Tcl_DeleteInterp; |
| 710 | + *pxFinalize = Tcl_Finalize; | |
| 685 | 711 | return TH_OK; |
| 686 | 712 | #endif /* defined(USE_TCL_STUBS) */ |
| 687 | 713 | } |
| 688 | 714 | |
| 689 | 715 | /* |
| 690 | 716 | ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter |
| 691 | 717 | ** based on the supplied command line arguments. |
| 692 | - */ | |
| 718 | +*/ | |
| 693 | 719 | static int setTclArguments( |
| 694 | 720 | Tcl_Interp *pInterp, |
| 695 | 721 | int argc, |
| 696 | 722 | char **argv |
| 697 | 723 | ){ |
| @@ -745,11 +771,11 @@ | ||
| 745 | 771 | |
| 746 | 772 | /* |
| 747 | 773 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 748 | 774 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 749 | 775 | ** by the caller. |
| 750 | - */ | |
| 776 | +*/ | |
| 751 | 777 | static int createTclInterp( |
| 752 | 778 | Th_Interp *interp, |
| 753 | 779 | void *pContext |
| 754 | 780 | ){ |
| 755 | 781 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| @@ -766,11 +792,12 @@ | ||
| 766 | 792 | } |
| 767 | 793 | if ( tclContext->interp ){ |
| 768 | 794 | return TH_OK; |
| 769 | 795 | } |
| 770 | 796 | if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable, |
| 771 | - &tclContext->xCreateInterp, &tclContext->xDeleteInterp)!=TH_OK ){ | |
| 797 | + &tclContext->xCreateInterp, &tclContext->xDeleteInterp, | |
| 798 | + &tclContext->xFinalize)!=TH_OK ){ | |
| 772 | 799 | return TH_ERROR; |
| 773 | 800 | } |
| 774 | 801 | argc = tclContext->argc; |
| 775 | 802 | argv = tclContext->argv; |
| 776 | 803 | if( argc>0 && argv ){ |
| @@ -820,10 +847,15 @@ | ||
| 820 | 847 | "Tcl error setting arguments:", Tcl_GetStringResult(tclInterp), -1); |
| 821 | 848 | Tcl_DeleteInterp(tclInterp); |
| 822 | 849 | tclContext->interp = tclInterp = 0; |
| 823 | 850 | return TH_ERROR; |
| 824 | 851 | } |
| 852 | + /* | |
| 853 | + ** Determine if an objProc can be called directly for a Tcl command invoked | |
| 854 | + ** via the tclInvoke TH1 command. | |
| 855 | + */ | |
| 856 | + tclContext->useObjProc = canUseObjProc(); | |
| 825 | 857 | /* Add the TH1 integration commands to Tcl. */ |
| 826 | 858 | Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp); |
| 827 | 859 | Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL); |
| 828 | 860 | Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL); |
| 829 | 861 | /* If necessary, evaluate the custom Tcl setup script. */ |
| @@ -835,10 +867,68 @@ | ||
| 835 | 867 | tclContext->interp = tclInterp = 0; |
| 836 | 868 | return TH_ERROR; |
| 837 | 869 | } |
| 838 | 870 | return TH_OK; |
| 839 | 871 | } |
| 872 | + | |
| 873 | +/* | |
| 874 | +** Finalizes and unloads the previously loaded Tcl library, if applicable. | |
| 875 | +*/ | |
| 876 | +int unloadTcl( | |
| 877 | + Th_Interp *interp, | |
| 878 | + void *pContext | |
| 879 | +){ | |
| 880 | + struct TclContext *tclContext = (struct TclContext *)pContext; | |
| 881 | + Tcl_Interp *tclInterp; | |
| 882 | + tcl_FinalizeProc *xFinalize; | |
| 883 | +#if defined(USE_TCL_STUBS) | |
| 884 | + void *library; | |
| 885 | +#endif /* defined(USE_TCL_STUBS) */ | |
| 886 | + | |
| 887 | + if ( !tclContext ){ | |
| 888 | + Th_ErrorMessage(interp, | |
| 889 | + "invalid Tcl context", (const char *)"", 0); | |
| 890 | + return TH_ERROR; | |
| 891 | + } | |
| 892 | + /* | |
| 893 | + ** Grab the Tcl_Finalize function pointer prior to deleting the Tcl | |
| 894 | + ** interpreter because the memory backing the Tcl stubs table will | |
| 895 | + ** be going away. | |
| 896 | + */ | |
| 897 | + xFinalize = tclContext->xFinalize; | |
| 898 | + /* | |
| 899 | + ** If the Tcl interpreter has been created, formally delete it now. | |
| 900 | + */ | |
| 901 | + tclInterp = tclContext->interp; | |
| 902 | + if ( tclInterp ){ | |
| 903 | + Tcl_DeleteInterp(tclInterp); | |
| 904 | + tclContext->interp = tclInterp = 0; | |
| 905 | + } | |
| 906 | + /* | |
| 907 | + ** If the Tcl library is not finalized prior to unloading it, a deadlock | |
| 908 | + ** can occur in some circumstances (i.e. the [clock] thread is running). | |
| 909 | + */ | |
| 910 | + if( xFinalize ) xFinalize(); | |
| 911 | +#if defined(USE_TCL_STUBS) | |
| 912 | + /* | |
| 913 | + ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash | |
| 914 | + ** when exiting while a stubs-enabled Tcl is still loaded. This is due to | |
| 915 | + ** a bug in MinGW, see: | |
| 916 | + ** | |
| 917 | + ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 | |
| 918 | + ** | |
| 919 | + ** The workaround is to manually unload the loaded Tcl library prior to | |
| 920 | + ** exiting the process. | |
| 921 | + */ | |
| 922 | + library = tclContext->library; | |
| 923 | + if( library ){ | |
| 924 | + dlclose(library); | |
| 925 | + tclContext->library = library = 0; | |
| 926 | + } | |
| 927 | +#endif /* defined(USE_TCL_STUBS) */ | |
| 928 | + return TH_OK; | |
| 929 | +} | |
| 840 | 930 | |
| 841 | 931 | /* |
| 842 | 932 | ** Register the Tcl language commands with interpreter interp. |
| 843 | 933 | ** Usually this is called soon after interpreter creation. |
| 844 | 934 | */ |
| 845 | 935 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -23,48 +23,14 @@ | |
| 23 | #ifdef FOSSIL_ENABLE_TCL |
| 24 | |
| 25 | #include "th.h" |
| 26 | #include "tcl.h" |
| 27 | |
| 28 | /* |
| 29 | ** Has the decision about whether or not to use Tcl_EvalObjv already been made |
| 30 | ** via the Makefile? |
| 31 | */ |
| 32 | #if !defined(USE_TCL_EVALOBJV) |
| 33 | /* |
| 34 | ** Are we being compiled against Tcl 8.6b1 or b2? This check is [mostly] |
| 35 | ** wrong for at the following reason: |
| 36 | ** |
| 37 | ** 1. Technically, this check is completely useless when the stubs mechanism |
| 38 | ** is in use. In that case, a runtime version check would be required and |
| 39 | ** that has not been implemented. |
| 40 | ** |
| 41 | ** However, if a particular user compiles and runs against Tcl 8.6b1 or b2, |
| 42 | ** this will cause a fallback to using the "conservative" method of directly |
| 43 | ** invoking a Tcl command. In that case, potential crashes will be avoided if |
| 44 | ** the user just so happened to compile or run against Tcl 8.6b1 or b2. |
| 45 | */ |
| 46 | #if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 6) && \ |
| 47 | (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE) && (TCL_RELEASE_SERIAL < 3) |
| 48 | /* |
| 49 | ** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using |
| 50 | ** Tcl_EvalObjv instead of invoking the objProc directly. |
| 51 | */ |
| 52 | # define USE_TCL_EVALOBJV (1) |
| 53 | #else |
| 54 | /* |
| 55 | ** We should be able to safely use Tcl_GetCommandInfoFromToken, when the need |
| 56 | ** arises, to invoke a specific Tcl command "directly" with some arguments. |
| 57 | */ |
| 58 | # define USE_TCL_EVALOBJV (0) |
| 59 | #endif /* (TCL_MAJOR_VERSION > 8) ... */ |
| 60 | #endif /* !defined(USE_TCL_EVALOBJV) */ |
| 61 | |
| 62 | /* |
| 63 | ** These macros are designed to reduce the redundant code required to marshal |
| 64 | ** arguments from TH1 to Tcl. |
| 65 | */ |
| 66 | #define USE_ARGV_TO_OBJV() \ |
| 67 | int objc; \ |
| 68 | Tcl_Obj **objv; \ |
| 69 | int i; |
| 70 | |
| @@ -83,18 +49,25 @@ | |
| 83 | ckfree((char *)objv); |
| 84 | |
| 85 | /* |
| 86 | ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl |
| 87 | ** context. |
| 88 | */ |
| 89 | #define GET_CTX_TCL_INTERP(ctx) \ |
| 90 | ((struct TclContext *)(ctx))->interp |
| 91 | |
| 92 | /* |
| 93 | ** Define the Tcl shared library name, some exported function names, and some |
| 94 | ** cross-platform macros for use with the Tcl stubs mechanism, when enabled. |
| 95 | */ |
| 96 | #if defined(USE_TCL_STUBS) |
| 97 | # if defined(_WIN32) |
| 98 | # define WIN32_LEAN_AND_MEAN |
| 99 | # include <windows.h> |
| 100 | # ifndef TCL_LIBRARY_NAME |
| @@ -144,33 +117,37 @@ | |
| 144 | # define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp" |
| 145 | # endif |
| 146 | # ifndef TCL_DELETEINTERP_NAME |
| 147 | # define TCL_DELETEINTERP_NAME "_Tcl_DeleteInterp" |
| 148 | # endif |
| 149 | #endif /* defined(USE_TCL_STUBS) */ |
| 150 | |
| 151 | /* |
| 152 | ** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed |
| 153 | ** when the Tcl library is being loaded dynamically by a stubs-enabled |
| 154 | ** application (i.e. the inverse of using a stubs-enabled package). These are |
| 155 | ** the only Tcl API functions that MUST be called prior to being able to call |
| 156 | ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete |
| 157 | ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp |
| 158 | ** function type is also required. |
| 159 | */ |
| 160 | typedef void (tcl_FindExecutableProc) (const char * argv0); |
| 161 | typedef Tcl_Interp *(tcl_CreateInterpProc) (void); |
| 162 | typedef void (tcl_DeleteInterpProc) (Tcl_Interp *interp); |
| 163 | |
| 164 | /* |
| 165 | ** The function types for the "hook" functions to be called before and after a |
| 166 | ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function |
| 167 | ** returns anything but TH_OK, then evaluation of the Tcl script is skipped and |
| 168 | ** that value is used as the return code. If the "post" function returns |
| 169 | ** anything other than its rc argument, that will become the new return code |
| 170 | ** for the command. |
| 171 | */ |
| 172 | typedef int (tcl_NotifyProc) ( |
| 173 | void *pContext, /* The context for this notification. */ |
| 174 | Th_Interp *interp, /* The TH1 interpreter being used. */ |
| 175 | void *ctx, /* The original TH1 command context. */ |
| 176 | int argc, /* Number of arguments for the TH1 command. */ |
| @@ -181,27 +158,27 @@ | |
| 181 | |
| 182 | /* |
| 183 | ** Are we using our own private implementation of the Tcl stubs mechanism? If |
| 184 | ** this is enabled, it prevents the user from having to link against the Tcl |
| 185 | ** stubs library for the target platform, which may not be readily available. |
| 186 | */ |
| 187 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 188 | /* |
| 189 | ** HACK: Using some preprocessor magic and a private static variable, redirect |
| 190 | ** the Tcl API calls [found within this file] to the function pointers |
| 191 | ** that will be contained in our private Tcl stubs table. This takes |
| 192 | ** advantage of the fact that the Tcl headers always define the Tcl API |
| 193 | ** functions in terms of the "tclStubsPtr" variable. |
| 194 | */ |
| 195 | #define tclStubsPtr privateTclStubsPtr |
| 196 | static const TclStubs *tclStubsPtr = NULL; |
| 197 | |
| 198 | /* |
| 199 | ** Create a Tcl interpreter structure that mirrors just enough fields to get |
| 200 | ** it up and running successfully with our private implementation of the Tcl |
| 201 | ** stubs mechanism. |
| 202 | */ |
| 203 | struct PrivateTclInterp { |
| 204 | char *result; |
| 205 | Tcl_FreeProc *freeProc; |
| 206 | int errorLine; |
| 207 | const struct TclStubs *stubTable; |
| @@ -209,11 +186,11 @@ | |
| 209 | |
| 210 | /* |
| 211 | ** Fossil can now be compiled without linking to the actual Tcl stubs library. |
| 212 | ** In that case, this function will be used to perform those steps that would |
| 213 | ** normally be performed within the Tcl stubs library. |
| 214 | */ |
| 215 | static int initTclStubs( |
| 216 | Th_Interp *interp, |
| 217 | Tcl_Interp *tclInterp |
| 218 | ){ |
| 219 | tclStubsPtr = ((struct PrivateTclInterp *)tclInterp)->stubTable; |
| @@ -231,24 +208,56 @@ | |
| 231 | return TH_ERROR; |
| 232 | } |
| 233 | return TH_OK; |
| 234 | } |
| 235 | #endif /* defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) */ |
| 236 | |
| 237 | /* |
| 238 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 239 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 240 | ** by the caller. This must be declared here because quite a few functions in |
| 241 | ** this file need to use it before it can be defined. |
| 242 | */ |
| 243 | static int createTclInterp(Th_Interp *interp, void *pContext); |
| 244 | |
| 245 | /* |
| 246 | ** Returns the Tcl interpreter result as a string with the associated length. |
| 247 | ** If the Tcl interpreter or the Tcl result are NULL, the length will be 0. |
| 248 | ** If the length pointer is NULL, the length will not be stored. |
| 249 | */ |
| 250 | static char *getTclResult( |
| 251 | Tcl_Interp *pInterp, |
| 252 | int *pN |
| 253 | ){ |
| 254 | Tcl_Obj *resultPtr; |
| @@ -274,11 +283,13 @@ | |
| 274 | char **argv; /* Full copy of the original arguments. */ |
| 275 | void *library; /* The Tcl library module handle. */ |
| 276 | tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */ |
| 277 | tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */ |
| 278 | tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 279 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 280 | char *setup; /* The optional Tcl setup script. */ |
| 281 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 282 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 283 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 284 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -443,20 +454,13 @@ | |
| 443 | int argc, |
| 444 | const char **argv, |
| 445 | int *argl |
| 446 | ){ |
| 447 | Tcl_Interp *tclInterp; |
| 448 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 449 | Tcl_Command command; |
| 450 | Tcl_CmdInfo cmdInfo; |
| 451 | #endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ |
| 452 | int rc = TH_OK; |
| 453 | int nResult; |
| 454 | const char *zResult; |
| 455 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 456 | Tcl_Obj *objPtr; |
| 457 | #endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ |
| 458 | USE_ARGV_TO_OBJV(); |
| 459 | |
| 460 | if( createTclInterp(interp, ctx)!=TH_OK ){ |
| 461 | return TH_ERROR; |
| 462 | } |
| @@ -472,35 +476,40 @@ | |
| 472 | if( rc!=TH_OK ){ |
| 473 | return rc; |
| 474 | } |
| 475 | Tcl_Preserve((ClientData)tclInterp); |
| 476 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 477 | objPtr = Tcl_NewStringObj(argv[1], argl[1]); |
| 478 | Tcl_IncrRefCount(objPtr); |
| 479 | command = Tcl_GetCommandFromObj(tclInterp, objPtr); |
| 480 | if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ |
| 481 | Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); |
| 482 | Tcl_DecrRefCount(objPtr); |
| 483 | Tcl_Release((ClientData)tclInterp); |
| 484 | return TH_ERROR; |
| 485 | } |
| 486 | if( !cmdInfo.objProc ){ |
| 487 | Th_ErrorMessage(interp, "cannot invoke Tcl command:", argv[1], argl[1]); |
| 488 | Tcl_DecrRefCount(objPtr); |
| 489 | Tcl_Release((ClientData)tclInterp); |
| 490 | return TH_ERROR; |
| 491 | } |
| 492 | Tcl_DecrRefCount(objPtr); |
| 493 | #endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ |
| 494 | COPY_ARGV_TO_OBJV(); |
| 495 | #if defined(USE_TCL_EVALOBJV) && USE_TCL_EVALOBJV |
| 496 | rc = Tcl_EvalObjv(tclInterp, objc, objv, 0); |
| 497 | #else |
| 498 | Tcl_ResetResult(tclInterp); |
| 499 | rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv); |
| 500 | #endif /* defined(USE_TCL_EVALOBJV) && USE_TCL_EVALOBJV */ |
| 501 | FREE_ARGV_TO_OBJV(); |
| 502 | zResult = getTclResult(tclInterp, &nResult); |
| 503 | Th_SetResult(interp, zResult, nResult); |
| 504 | Tcl_Release((ClientData)tclInterp); |
| 505 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc); |
| 506 | return rc; |
| @@ -586,11 +595,11 @@ | |
| 586 | }; |
| 587 | |
| 588 | /* |
| 589 | ** Called if the Tcl interpreter is deleted. Removes the Tcl integration |
| 590 | ** commands from the TH1 interpreter. |
| 591 | */ |
| 592 | static void Th1DeleteProc( |
| 593 | ClientData clientData, |
| 594 | Tcl_Interp *interp |
| 595 | ){ |
| 596 | int i; |
| @@ -607,23 +616,25 @@ | |
| 607 | ** When Tcl stubs support is enabled, attempts to dynamically load the Tcl |
| 608 | ** shared library and fetch the function pointers necessary to create an |
| 609 | ** interpreter and initialize the stubs mechanism; otherwise, simply setup |
| 610 | ** the function pointers provided by the caller with the statically linked |
| 611 | ** functions. |
| 612 | */ |
| 613 | static int loadTcl( |
| 614 | Th_Interp *interp, |
| 615 | void **pLibrary, |
| 616 | tcl_FindExecutableProc **pxFindExecutable, |
| 617 | tcl_CreateInterpProc **pxCreateInterp, |
| 618 | tcl_DeleteInterpProc **pxDeleteInterp |
| 619 | ){ |
| 620 | #if defined(USE_TCL_STUBS) |
| 621 | char fileName[] = TCL_LIBRARY_NAME; |
| 622 | #endif /* defined(USE_TCL_STUBS) */ |
| 623 | |
| 624 | if( !pLibrary || !pxFindExecutable || !pxCreateInterp || !pxDeleteInterp ){ |
| 625 | Th_ErrorMessage(interp, |
| 626 | "invalid Tcl loader argument(s)", (const char *)"", 0); |
| 627 | return TH_ERROR; |
| 628 | } |
| 629 | #if defined(USE_TCL_STUBS) |
| @@ -631,10 +642,11 @@ | |
| 631 | void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL); |
| 632 | if( library ){ |
| 633 | tcl_FindExecutableProc *xFindExecutable; |
| 634 | tcl_CreateInterpProc *xCreateInterp; |
| 635 | tcl_DeleteInterpProc *xDeleteInterp; |
| 636 | const char *procName = TCL_FINDEXECUTABLE_NAME; |
| 637 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1); |
| 638 | if( !xFindExecutable ){ |
| 639 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName); |
| 640 | } |
| @@ -663,35 +675,49 @@ | |
| 663 | if( !xDeleteInterp ){ |
| 664 | Th_ErrorMessage(interp, |
| 665 | "could not locate Tcl_DeleteInterp", (const char *)"", 0); |
| 666 | dlclose(library); |
| 667 | return TH_ERROR; |
| 668 | } |
| 669 | *pLibrary = library; |
| 670 | *pxFindExecutable = xFindExecutable; |
| 671 | *pxCreateInterp = xCreateInterp; |
| 672 | *pxDeleteInterp = xDeleteInterp; |
| 673 | return TH_OK; |
| 674 | } |
| 675 | } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */ |
| 676 | Th_ErrorMessage(interp, |
| 677 | "could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"", |
| 678 | (const char *)"", 0); |
| 679 | return TH_ERROR; |
| 680 | #else |
| 681 | *pLibrary = 0; |
| 682 | *pxFindExecutable = Tcl_FindExecutable; |
| 683 | *pxCreateInterp = Tcl_CreateInterp; |
| 684 | *pxDeleteInterp = Tcl_DeleteInterp; |
| 685 | return TH_OK; |
| 686 | #endif /* defined(USE_TCL_STUBS) */ |
| 687 | } |
| 688 | |
| 689 | /* |
| 690 | ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter |
| 691 | ** based on the supplied command line arguments. |
| 692 | */ |
| 693 | static int setTclArguments( |
| 694 | Tcl_Interp *pInterp, |
| 695 | int argc, |
| 696 | char **argv |
| 697 | ){ |
| @@ -745,11 +771,11 @@ | |
| 745 | |
| 746 | /* |
| 747 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 748 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 749 | ** by the caller. |
| 750 | */ |
| 751 | static int createTclInterp( |
| 752 | Th_Interp *interp, |
| 753 | void *pContext |
| 754 | ){ |
| 755 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| @@ -766,11 +792,12 @@ | |
| 766 | } |
| 767 | if ( tclContext->interp ){ |
| 768 | return TH_OK; |
| 769 | } |
| 770 | if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable, |
| 771 | &tclContext->xCreateInterp, &tclContext->xDeleteInterp)!=TH_OK ){ |
| 772 | return TH_ERROR; |
| 773 | } |
| 774 | argc = tclContext->argc; |
| 775 | argv = tclContext->argv; |
| 776 | if( argc>0 && argv ){ |
| @@ -820,10 +847,15 @@ | |
| 820 | "Tcl error setting arguments:", Tcl_GetStringResult(tclInterp), -1); |
| 821 | Tcl_DeleteInterp(tclInterp); |
| 822 | tclContext->interp = tclInterp = 0; |
| 823 | return TH_ERROR; |
| 824 | } |
| 825 | /* Add the TH1 integration commands to Tcl. */ |
| 826 | Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp); |
| 827 | Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL); |
| 828 | Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL); |
| 829 | /* If necessary, evaluate the custom Tcl setup script. */ |
| @@ -835,10 +867,68 @@ | |
| 835 | tclContext->interp = tclInterp = 0; |
| 836 | return TH_ERROR; |
| 837 | } |
| 838 | return TH_OK; |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | ** Register the Tcl language commands with interpreter interp. |
| 843 | ** Usually this is called soon after interpreter creation. |
| 844 | */ |
| 845 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -23,48 +23,14 @@ | |
| 23 | #ifdef FOSSIL_ENABLE_TCL |
| 24 | |
| 25 | #include "th.h" |
| 26 | #include "tcl.h" |
| 27 | |
| 28 | /* |
| 29 | ** These macros are designed to reduce the redundant code required to marshal |
| 30 | ** arguments from TH1 to Tcl. |
| 31 | */ |
| 32 | #define USE_ARGV_TO_OBJV() \ |
| 33 | int objc; \ |
| 34 | Tcl_Obj **objv; \ |
| 35 | int i; |
| 36 | |
| @@ -83,18 +49,25 @@ | |
| 49 | ckfree((char *)objv); |
| 50 | |
| 51 | /* |
| 52 | ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl |
| 53 | ** context. |
| 54 | */ |
| 55 | #define GET_CTX_TCL_INTERP(ctx) \ |
| 56 | ((struct TclContext *)(ctx))->interp |
| 57 | |
| 58 | /* |
| 59 | ** Fetch the (logically boolean) value from the specified void pointer that |
| 60 | ** indicates whether or not we can/should use direct objProc calls. |
| 61 | */ |
| 62 | #define GET_CTX_TCL_USEOBJPROC(ctx) \ |
| 63 | ((struct TclContext *)(ctx))->useObjProc |
| 64 | |
| 65 | /* |
| 66 | ** Define the Tcl shared library name, some exported function names, and some |
| 67 | ** cross-platform macros for use with the Tcl stubs mechanism, when enabled. |
| 68 | */ |
| 69 | #if defined(USE_TCL_STUBS) |
| 70 | # if defined(_WIN32) |
| 71 | # define WIN32_LEAN_AND_MEAN |
| 72 | # include <windows.h> |
| 73 | # ifndef TCL_LIBRARY_NAME |
| @@ -144,33 +117,37 @@ | |
| 117 | # define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp" |
| 118 | # endif |
| 119 | # ifndef TCL_DELETEINTERP_NAME |
| 120 | # define TCL_DELETEINTERP_NAME "_Tcl_DeleteInterp" |
| 121 | # endif |
| 122 | # ifndef TCL_FINALIZE_NAME |
| 123 | # define TCL_FINALIZE_NAME "_Tcl_Finalize" |
| 124 | # endif |
| 125 | #endif /* defined(USE_TCL_STUBS) */ |
| 126 | |
| 127 | /* |
| 128 | ** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed |
| 129 | ** when the Tcl library is being loaded dynamically by a stubs-enabled |
| 130 | ** application (i.e. the inverse of using a stubs-enabled package). These are |
| 131 | ** the only Tcl API functions that MUST be called prior to being able to call |
| 132 | ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete |
| 133 | ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp |
| 134 | ** and Tcl_Finalize function types are also required. |
| 135 | */ |
| 136 | typedef void (tcl_FindExecutableProc) (const char * argv0); |
| 137 | typedef Tcl_Interp *(tcl_CreateInterpProc) (void); |
| 138 | typedef void (tcl_DeleteInterpProc) (Tcl_Interp *interp); |
| 139 | typedef void (tcl_FinalizeProc) (void); |
| 140 | |
| 141 | /* |
| 142 | ** The function types for the "hook" functions to be called before and after a |
| 143 | ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function |
| 144 | ** returns anything but TH_OK, then evaluation of the Tcl script is skipped and |
| 145 | ** that value is used as the return code. If the "post" function returns |
| 146 | ** anything other than its rc argument, that will become the new return code |
| 147 | ** for the command. |
| 148 | */ |
| 149 | typedef int (tcl_NotifyProc) ( |
| 150 | void *pContext, /* The context for this notification. */ |
| 151 | Th_Interp *interp, /* The TH1 interpreter being used. */ |
| 152 | void *ctx, /* The original TH1 command context. */ |
| 153 | int argc, /* Number of arguments for the TH1 command. */ |
| @@ -181,27 +158,27 @@ | |
| 158 | |
| 159 | /* |
| 160 | ** Are we using our own private implementation of the Tcl stubs mechanism? If |
| 161 | ** this is enabled, it prevents the user from having to link against the Tcl |
| 162 | ** stubs library for the target platform, which may not be readily available. |
| 163 | */ |
| 164 | #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) |
| 165 | /* |
| 166 | ** HACK: Using some preprocessor magic and a private static variable, redirect |
| 167 | ** the Tcl API calls [found within this file] to the function pointers |
| 168 | ** that will be contained in our private Tcl stubs table. This takes |
| 169 | ** advantage of the fact that the Tcl headers always define the Tcl API |
| 170 | ** functions in terms of the "tclStubsPtr" variable. |
| 171 | */ |
| 172 | #define tclStubsPtr privateTclStubsPtr |
| 173 | static const TclStubs *tclStubsPtr = NULL; |
| 174 | |
| 175 | /* |
| 176 | ** Create a Tcl interpreter structure that mirrors just enough fields to get |
| 177 | ** it up and running successfully with our private implementation of the Tcl |
| 178 | ** stubs mechanism. |
| 179 | */ |
| 180 | struct PrivateTclInterp { |
| 181 | char *result; |
| 182 | Tcl_FreeProc *freeProc; |
| 183 | int errorLine; |
| 184 | const struct TclStubs *stubTable; |
| @@ -209,11 +186,11 @@ | |
| 186 | |
| 187 | /* |
| 188 | ** Fossil can now be compiled without linking to the actual Tcl stubs library. |
| 189 | ** In that case, this function will be used to perform those steps that would |
| 190 | ** normally be performed within the Tcl stubs library. |
| 191 | */ |
| 192 | static int initTclStubs( |
| 193 | Th_Interp *interp, |
| 194 | Tcl_Interp *tclInterp |
| 195 | ){ |
| 196 | tclStubsPtr = ((struct PrivateTclInterp *)tclInterp)->stubTable; |
| @@ -231,24 +208,56 @@ | |
| 208 | return TH_ERROR; |
| 209 | } |
| 210 | return TH_OK; |
| 211 | } |
| 212 | #endif /* defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) */ |
| 213 | |
| 214 | /* |
| 215 | ** Is the loaded version of Tcl one where querying and/or calling the objProc |
| 216 | ** for a command does not work for some reason? The following special cases |
| 217 | ** are currently handled by this function: |
| 218 | ** |
| 219 | ** 1. All versions of Tcl 8.4 have a bug that causes a crash when calling into |
| 220 | ** the Tcl_GetCommandFromObj function via stubs (i.e. the stubs table entry |
| 221 | ** is NULL). |
| 222 | ** |
| 223 | ** 2. Various beta builds of Tcl 8.6, namely 1 and 2, have an NRE-specific bug |
| 224 | ** in Tcl_EvalObjCmd (SF bug #3399564) that cause a panic when calling into |
| 225 | ** the objProc directly. |
| 226 | ** |
| 227 | ** For both of the above cases, the Tcl_EvalObjv function must be used instead |
| 228 | ** of the more direct route of querying and calling the objProc directly. |
| 229 | */ |
| 230 | static int canUseObjProc(){ |
| 231 | int major = -1, minor = -1, patchLevel = -1, type = -1; |
| 232 | |
| 233 | Tcl_GetVersion(&major, &minor, &patchLevel, &type); |
| 234 | if( major<0 || minor<0 || patchLevel<0 || type<0 ){ |
| 235 | return 0; /* NOTE: Invalid version info, assume bad. */ |
| 236 | } |
| 237 | if( major==8 && minor==4 ){ |
| 238 | return 0; /* NOTE: Disabled on Tcl 8.4, missing public API. */ |
| 239 | } |
| 240 | if( major==8 && minor==6 && type==TCL_BETA_RELEASE && patchLevel<3 ){ |
| 241 | return 0; /* NOTE: Disabled on Tcl 8.6b1/b2, SF bug #3399564. */ |
| 242 | } |
| 243 | return 1; /* NOTE: For all other cases, assume good. */ |
| 244 | } |
| 245 | |
| 246 | /* |
| 247 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 248 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 249 | ** by the caller. This must be declared here because quite a few functions in |
| 250 | ** this file need to use it before it can be defined. |
| 251 | */ |
| 252 | static int createTclInterp(Th_Interp *interp, void *pContext); |
| 253 | |
| 254 | /* |
| 255 | ** Returns the Tcl interpreter result as a string with the associated length. |
| 256 | ** If the Tcl interpreter or the Tcl result are NULL, the length will be 0. |
| 257 | ** If the length pointer is NULL, the length will not be stored. |
| 258 | */ |
| 259 | static char *getTclResult( |
| 260 | Tcl_Interp *pInterp, |
| 261 | int *pN |
| 262 | ){ |
| 263 | Tcl_Obj *resultPtr; |
| @@ -274,11 +283,13 @@ | |
| 283 | char **argv; /* Full copy of the original arguments. */ |
| 284 | void *library; /* The Tcl library module handle. */ |
| 285 | tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */ |
| 286 | tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */ |
| 287 | tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 288 | tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */ |
| 289 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 290 | int useObjProc; /* Non-zero if an objProc can be called directly. */ |
| 291 | char *setup; /* The optional Tcl setup script. */ |
| 292 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 293 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 294 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 295 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| @@ -443,20 +454,13 @@ | |
| 454 | int argc, |
| 455 | const char **argv, |
| 456 | int *argl |
| 457 | ){ |
| 458 | Tcl_Interp *tclInterp; |
| 459 | int rc = TH_OK; |
| 460 | int nResult; |
| 461 | const char *zResult; |
| 462 | USE_ARGV_TO_OBJV(); |
| 463 | |
| 464 | if( createTclInterp(interp, ctx)!=TH_OK ){ |
| 465 | return TH_ERROR; |
| 466 | } |
| @@ -472,35 +476,40 @@ | |
| 476 | if( rc!=TH_OK ){ |
| 477 | return rc; |
| 478 | } |
| 479 | Tcl_Preserve((ClientData)tclInterp); |
| 480 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 481 | if( GET_CTX_TCL_USEOBJPROC(ctx) ){ |
| 482 | Tcl_Command command; |
| 483 | Tcl_CmdInfo cmdInfo; |
| 484 | Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], argl[1]); |
| 485 | Tcl_IncrRefCount(objPtr); |
| 486 | command = Tcl_GetCommandFromObj(tclInterp, objPtr); |
| 487 | if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ |
| 488 | Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); |
| 489 | Tcl_DecrRefCount(objPtr); |
| 490 | Tcl_Release((ClientData)tclInterp); |
| 491 | return TH_ERROR; |
| 492 | } |
| 493 | if( !cmdInfo.objProc ){ |
| 494 | Th_ErrorMessage(interp, "cannot invoke Tcl command:", argv[1], argl[1]); |
| 495 | Tcl_DecrRefCount(objPtr); |
| 496 | Tcl_Release((ClientData)tclInterp); |
| 497 | return TH_ERROR; |
| 498 | } |
| 499 | Tcl_DecrRefCount(objPtr); |
| 500 | COPY_ARGV_TO_OBJV(); |
| 501 | Tcl_ResetResult(tclInterp); |
| 502 | rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv); |
| 503 | FREE_ARGV_TO_OBJV(); |
| 504 | }else |
| 505 | #endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ |
| 506 | { |
| 507 | COPY_ARGV_TO_OBJV(); |
| 508 | rc = Tcl_EvalObjv(tclInterp, objc, objv, 0); |
| 509 | FREE_ARGV_TO_OBJV(); |
| 510 | } |
| 511 | zResult = getTclResult(tclInterp, &nResult); |
| 512 | Th_SetResult(interp, zResult, nResult); |
| 513 | Tcl_Release((ClientData)tclInterp); |
| 514 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc); |
| 515 | return rc; |
| @@ -586,11 +595,11 @@ | |
| 595 | }; |
| 596 | |
| 597 | /* |
| 598 | ** Called if the Tcl interpreter is deleted. Removes the Tcl integration |
| 599 | ** commands from the TH1 interpreter. |
| 600 | */ |
| 601 | static void Th1DeleteProc( |
| 602 | ClientData clientData, |
| 603 | Tcl_Interp *interp |
| 604 | ){ |
| 605 | int i; |
| @@ -607,23 +616,25 @@ | |
| 616 | ** When Tcl stubs support is enabled, attempts to dynamically load the Tcl |
| 617 | ** shared library and fetch the function pointers necessary to create an |
| 618 | ** interpreter and initialize the stubs mechanism; otherwise, simply setup |
| 619 | ** the function pointers provided by the caller with the statically linked |
| 620 | ** functions. |
| 621 | */ |
| 622 | static int loadTcl( |
| 623 | Th_Interp *interp, |
| 624 | void **pLibrary, |
| 625 | tcl_FindExecutableProc **pxFindExecutable, |
| 626 | tcl_CreateInterpProc **pxCreateInterp, |
| 627 | tcl_DeleteInterpProc **pxDeleteInterp, |
| 628 | tcl_FinalizeProc **pxFinalize |
| 629 | ){ |
| 630 | #if defined(USE_TCL_STUBS) |
| 631 | char fileName[] = TCL_LIBRARY_NAME; |
| 632 | #endif /* defined(USE_TCL_STUBS) */ |
| 633 | |
| 634 | if( !pLibrary || !pxFindExecutable || !pxCreateInterp || |
| 635 | !pxDeleteInterp || !pxFinalize ){ |
| 636 | Th_ErrorMessage(interp, |
| 637 | "invalid Tcl loader argument(s)", (const char *)"", 0); |
| 638 | return TH_ERROR; |
| 639 | } |
| 640 | #if defined(USE_TCL_STUBS) |
| @@ -631,10 +642,11 @@ | |
| 642 | void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL); |
| 643 | if( library ){ |
| 644 | tcl_FindExecutableProc *xFindExecutable; |
| 645 | tcl_CreateInterpProc *xCreateInterp; |
| 646 | tcl_DeleteInterpProc *xDeleteInterp; |
| 647 | tcl_FinalizeProc *xFinalize; |
| 648 | const char *procName = TCL_FINDEXECUTABLE_NAME; |
| 649 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1); |
| 650 | if( !xFindExecutable ){ |
| 651 | xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName); |
| 652 | } |
| @@ -663,35 +675,49 @@ | |
| 675 | if( !xDeleteInterp ){ |
| 676 | Th_ErrorMessage(interp, |
| 677 | "could not locate Tcl_DeleteInterp", (const char *)"", 0); |
| 678 | dlclose(library); |
| 679 | return TH_ERROR; |
| 680 | } |
| 681 | procName = TCL_FINALIZE_NAME; |
| 682 | xFinalize = (tcl_FinalizeProc *)dlsym(library, procName + 1); |
| 683 | if( !xFinalize ){ |
| 684 | xFinalize = (tcl_FinalizeProc *)dlsym(library, procName); |
| 685 | } |
| 686 | if( !xFinalize ){ |
| 687 | Th_ErrorMessage(interp, |
| 688 | "could not locate Tcl_Finalize", (const char *)"", 0); |
| 689 | dlclose(library); |
| 690 | return TH_ERROR; |
| 691 | } |
| 692 | *pLibrary = library; |
| 693 | *pxFindExecutable = xFindExecutable; |
| 694 | *pxCreateInterp = xCreateInterp; |
| 695 | *pxDeleteInterp = xDeleteInterp; |
| 696 | *pxFinalize = xFinalize; |
| 697 | return TH_OK; |
| 698 | } |
| 699 | } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */ |
| 700 | fileName[TCL_MINOR_OFFSET] = 'x'; |
| 701 | Th_ErrorMessage(interp, |
| 702 | "could not load any supported Tcl 8.6, 8.5, or 8.4 shared library \"", |
| 703 | fileName, -1); |
| 704 | return TH_ERROR; |
| 705 | #else |
| 706 | *pLibrary = 0; |
| 707 | *pxFindExecutable = Tcl_FindExecutable; |
| 708 | *pxCreateInterp = Tcl_CreateInterp; |
| 709 | *pxDeleteInterp = Tcl_DeleteInterp; |
| 710 | *pxFinalize = Tcl_Finalize; |
| 711 | return TH_OK; |
| 712 | #endif /* defined(USE_TCL_STUBS) */ |
| 713 | } |
| 714 | |
| 715 | /* |
| 716 | ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter |
| 717 | ** based on the supplied command line arguments. |
| 718 | */ |
| 719 | static int setTclArguments( |
| 720 | Tcl_Interp *pInterp, |
| 721 | int argc, |
| 722 | char **argv |
| 723 | ){ |
| @@ -745,11 +771,11 @@ | |
| 771 | |
| 772 | /* |
| 773 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 774 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 775 | ** by the caller. |
| 776 | */ |
| 777 | static int createTclInterp( |
| 778 | Th_Interp *interp, |
| 779 | void *pContext |
| 780 | ){ |
| 781 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| @@ -766,11 +792,12 @@ | |
| 792 | } |
| 793 | if ( tclContext->interp ){ |
| 794 | return TH_OK; |
| 795 | } |
| 796 | if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable, |
| 797 | &tclContext->xCreateInterp, &tclContext->xDeleteInterp, |
| 798 | &tclContext->xFinalize)!=TH_OK ){ |
| 799 | return TH_ERROR; |
| 800 | } |
| 801 | argc = tclContext->argc; |
| 802 | argv = tclContext->argv; |
| 803 | if( argc>0 && argv ){ |
| @@ -820,10 +847,15 @@ | |
| 847 | "Tcl error setting arguments:", Tcl_GetStringResult(tclInterp), -1); |
| 848 | Tcl_DeleteInterp(tclInterp); |
| 849 | tclContext->interp = tclInterp = 0; |
| 850 | return TH_ERROR; |
| 851 | } |
| 852 | /* |
| 853 | ** Determine if an objProc can be called directly for a Tcl command invoked |
| 854 | ** via the tclInvoke TH1 command. |
| 855 | */ |
| 856 | tclContext->useObjProc = canUseObjProc(); |
| 857 | /* Add the TH1 integration commands to Tcl. */ |
| 858 | Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp); |
| 859 | Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL); |
| 860 | Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL); |
| 861 | /* If necessary, evaluate the custom Tcl setup script. */ |
| @@ -835,10 +867,68 @@ | |
| 867 | tclContext->interp = tclInterp = 0; |
| 868 | return TH_ERROR; |
| 869 | } |
| 870 | return TH_OK; |
| 871 | } |
| 872 | |
| 873 | /* |
| 874 | ** Finalizes and unloads the previously loaded Tcl library, if applicable. |
| 875 | */ |
| 876 | int unloadTcl( |
| 877 | Th_Interp *interp, |
| 878 | void *pContext |
| 879 | ){ |
| 880 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| 881 | Tcl_Interp *tclInterp; |
| 882 | tcl_FinalizeProc *xFinalize; |
| 883 | #if defined(USE_TCL_STUBS) |
| 884 | void *library; |
| 885 | #endif /* defined(USE_TCL_STUBS) */ |
| 886 | |
| 887 | if ( !tclContext ){ |
| 888 | Th_ErrorMessage(interp, |
| 889 | "invalid Tcl context", (const char *)"", 0); |
| 890 | return TH_ERROR; |
| 891 | } |
| 892 | /* |
| 893 | ** Grab the Tcl_Finalize function pointer prior to deleting the Tcl |
| 894 | ** interpreter because the memory backing the Tcl stubs table will |
| 895 | ** be going away. |
| 896 | */ |
| 897 | xFinalize = tclContext->xFinalize; |
| 898 | /* |
| 899 | ** If the Tcl interpreter has been created, formally delete it now. |
| 900 | */ |
| 901 | tclInterp = tclContext->interp; |
| 902 | if ( tclInterp ){ |
| 903 | Tcl_DeleteInterp(tclInterp); |
| 904 | tclContext->interp = tclInterp = 0; |
| 905 | } |
| 906 | /* |
| 907 | ** If the Tcl library is not finalized prior to unloading it, a deadlock |
| 908 | ** can occur in some circumstances (i.e. the [clock] thread is running). |
| 909 | */ |
| 910 | if( xFinalize ) xFinalize(); |
| 911 | #if defined(USE_TCL_STUBS) |
| 912 | /* |
| 913 | ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash |
| 914 | ** when exiting while a stubs-enabled Tcl is still loaded. This is due to |
| 915 | ** a bug in MinGW, see: |
| 916 | ** |
| 917 | ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 |
| 918 | ** |
| 919 | ** The workaround is to manually unload the loaded Tcl library prior to |
| 920 | ** exiting the process. |
| 921 | */ |
| 922 | library = tclContext->library; |
| 923 | if( library ){ |
| 924 | dlclose(library); |
| 925 | tclContext->library = library = 0; |
| 926 | } |
| 927 | #endif /* defined(USE_TCL_STUBS) */ |
| 928 | return TH_OK; |
| 929 | } |
| 930 | |
| 931 | /* |
| 932 | ** Register the Tcl language commands with interpreter interp. |
| 933 | ** Usually this is called soon after interpreter creation. |
| 934 | */ |
| 935 |
+2
-2
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1159,12 +1159,12 @@ | ||
| 1159 | 1159 | p = p->u.pTo; |
| 1160 | 1160 | } |
| 1161 | 1161 | blob_append(&sql, ")", -1); |
| 1162 | 1162 | path_reset(); |
| 1163 | 1163 | blob_append(&desc, "All nodes on the path from ", -1); |
| 1164 | - blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom); | |
| 1165 | - blob_append(&desc, " and ", -1); | |
| 1164 | + blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h", zFrom), zFrom); | |
| 1165 | + blob_append(&desc, " to ", -1); | |
| 1166 | 1166 | blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo); |
| 1167 | 1167 | tmFlags |= TIMELINE_DISJOINT; |
| 1168 | 1168 | db_multi_exec("%s", blob_str(&sql)); |
| 1169 | 1169 | }else if( (p_rid || d_rid) && g.perm.Read ){ |
| 1170 | 1170 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 1171 | 1171 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1159,12 +1159,12 @@ | |
| 1159 | p = p->u.pTo; |
| 1160 | } |
| 1161 | blob_append(&sql, ")", -1); |
| 1162 | path_reset(); |
| 1163 | blob_append(&desc, "All nodes on the path from ", -1); |
| 1164 | blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom); |
| 1165 | blob_append(&desc, " and ", -1); |
| 1166 | blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo); |
| 1167 | tmFlags |= TIMELINE_DISJOINT; |
| 1168 | db_multi_exec("%s", blob_str(&sql)); |
| 1169 | }else if( (p_rid || d_rid) && g.perm.Read ){ |
| 1170 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 1171 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1159,12 +1159,12 @@ | |
| 1159 | p = p->u.pTo; |
| 1160 | } |
| 1161 | blob_append(&sql, ")", -1); |
| 1162 | path_reset(); |
| 1163 | blob_append(&desc, "All nodes on the path from ", -1); |
| 1164 | blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h", zFrom), zFrom); |
| 1165 | blob_append(&desc, " to ", -1); |
| 1166 | blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo); |
| 1167 | tmFlags |= TIMELINE_DISJOINT; |
| 1168 | db_multi_exec("%s", blob_str(&sql)); |
| 1169 | }else if( (p_rid || d_rid) && g.perm.Read ){ |
| 1170 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 1171 |
+4
-6
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -230,13 +230,11 @@ | ||
| 230 | 230 | ** Returns the difference in CPU times in microseconds since |
| 231 | 231 | ** fossil_timer_start() was called and returned the given timer ID (or |
| 232 | 232 | ** since it was last reset). Returns 0 if timerId is out of range. |
| 233 | 233 | */ |
| 234 | 234 | sqlite3_uint64 fossil_timer_fetch(int timerId){ |
| 235 | - if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){ | |
| 236 | - return 0; | |
| 237 | - }else{ | |
| 235 | + if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ | |
| 238 | 236 | struct FossilTimer * start = &fossilTimerList[timerId-1]; |
| 239 | 237 | if( !start->id ){ |
| 240 | 238 | fossil_fatal("Invalid call to fetch a non-allocated " |
| 241 | 239 | "timer (#%d)", timerId); |
| 242 | 240 | /*NOTREACHED*/ |
| @@ -244,20 +242,19 @@ | ||
| 244 | 242 | sqlite3_uint64 eu = 0, es = 0; |
| 245 | 243 | fossil_cpu_times( &eu, &es ); |
| 246 | 244 | return (eu - start->u) + (es - start->s); |
| 247 | 245 | } |
| 248 | 246 | } |
| 247 | + return 0; | |
| 249 | 248 | } |
| 250 | 249 | |
| 251 | 250 | /* |
| 252 | 251 | ** Resets the timer associated with the given ID, as obtained via |
| 253 | 252 | ** fossil_timer_start(), to the current CPU time values. |
| 254 | 253 | */ |
| 255 | 254 | sqlite3_uint64 fossil_timer_reset(int timerId){ |
| 256 | - if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){ | |
| 257 | - return 0; | |
| 258 | - }else{ | |
| 255 | + if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ | |
| 259 | 256 | struct FossilTimer * start = &fossilTimerList[timerId-1]; |
| 260 | 257 | if( !start->id ){ |
| 261 | 258 | fossil_fatal("Invalid call to reset a non-allocated " |
| 262 | 259 | "timer (#%d)", timerId); |
| 263 | 260 | /*NOTREACHED*/ |
| @@ -265,10 +262,11 @@ | ||
| 265 | 262 | sqlite3_uint64 const rc = fossil_timer_fetch(timerId); |
| 266 | 263 | fossil_cpu_times( &start->u, &start->s ); |
| 267 | 264 | return rc; |
| 268 | 265 | } |
| 269 | 266 | } |
| 267 | + return 0; | |
| 270 | 268 | } |
| 271 | 269 | |
| 272 | 270 | /** |
| 273 | 271 | "Deallocates" the fossil timer identified by the given timer ID. |
| 274 | 272 | returns the difference (in uSec) between the last time that timer |
| 275 | 273 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -230,13 +230,11 @@ | |
| 230 | ** Returns the difference in CPU times in microseconds since |
| 231 | ** fossil_timer_start() was called and returned the given timer ID (or |
| 232 | ** since it was last reset). Returns 0 if timerId is out of range. |
| 233 | */ |
| 234 | sqlite3_uint64 fossil_timer_fetch(int timerId){ |
| 235 | if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){ |
| 236 | return 0; |
| 237 | }else{ |
| 238 | struct FossilTimer * start = &fossilTimerList[timerId-1]; |
| 239 | if( !start->id ){ |
| 240 | fossil_fatal("Invalid call to fetch a non-allocated " |
| 241 | "timer (#%d)", timerId); |
| 242 | /*NOTREACHED*/ |
| @@ -244,20 +242,19 @@ | |
| 244 | sqlite3_uint64 eu = 0, es = 0; |
| 245 | fossil_cpu_times( &eu, &es ); |
| 246 | return (eu - start->u) + (es - start->s); |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | /* |
| 252 | ** Resets the timer associated with the given ID, as obtained via |
| 253 | ** fossil_timer_start(), to the current CPU time values. |
| 254 | */ |
| 255 | sqlite3_uint64 fossil_timer_reset(int timerId){ |
| 256 | if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){ |
| 257 | return 0; |
| 258 | }else{ |
| 259 | struct FossilTimer * start = &fossilTimerList[timerId-1]; |
| 260 | if( !start->id ){ |
| 261 | fossil_fatal("Invalid call to reset a non-allocated " |
| 262 | "timer (#%d)", timerId); |
| 263 | /*NOTREACHED*/ |
| @@ -265,10 +262,11 @@ | |
| 265 | sqlite3_uint64 const rc = fossil_timer_fetch(timerId); |
| 266 | fossil_cpu_times( &start->u, &start->s ); |
| 267 | return rc; |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | "Deallocates" the fossil timer identified by the given timer ID. |
| 274 | returns the difference (in uSec) between the last time that timer |
| 275 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -230,13 +230,11 @@ | |
| 230 | ** Returns the difference in CPU times in microseconds since |
| 231 | ** fossil_timer_start() was called and returned the given timer ID (or |
| 232 | ** since it was last reset). Returns 0 if timerId is out of range. |
| 233 | */ |
| 234 | sqlite3_uint64 fossil_timer_fetch(int timerId){ |
| 235 | if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ |
| 236 | struct FossilTimer * start = &fossilTimerList[timerId-1]; |
| 237 | if( !start->id ){ |
| 238 | fossil_fatal("Invalid call to fetch a non-allocated " |
| 239 | "timer (#%d)", timerId); |
| 240 | /*NOTREACHED*/ |
| @@ -244,20 +242,19 @@ | |
| 242 | sqlite3_uint64 eu = 0, es = 0; |
| 243 | fossil_cpu_times( &eu, &es ); |
| 244 | return (eu - start->u) + (es - start->s); |
| 245 | } |
| 246 | } |
| 247 | return 0; |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | ** Resets the timer associated with the given ID, as obtained via |
| 252 | ** fossil_timer_start(), to the current CPU time values. |
| 253 | */ |
| 254 | sqlite3_uint64 fossil_timer_reset(int timerId){ |
| 255 | if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ |
| 256 | struct FossilTimer * start = &fossilTimerList[timerId-1]; |
| 257 | if( !start->id ){ |
| 258 | fossil_fatal("Invalid call to reset a non-allocated " |
| 259 | "timer (#%d)", timerId); |
| 260 | /*NOTREACHED*/ |
| @@ -265,10 +262,11 @@ | |
| 262 | sqlite3_uint64 const rc = fossil_timer_fetch(timerId); |
| 263 | fossil_cpu_times( &start->u, &start->s ); |
| 264 | return rc; |
| 265 | } |
| 266 | } |
| 267 | return 0; |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | "Deallocates" the fossil timer identified by the given timer ID. |
| 272 | returns the difference (in uSec) between the last time that timer |
| 273 |
+124
-7
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -418,10 +418,11 @@ | ||
| 418 | 418 | /* |
| 419 | 419 | ** Values for the scanFlags parameter to vfile_scan(). |
| 420 | 420 | */ |
| 421 | 421 | #define SCAN_ALL 0x001 /* Includes files that begin with "." */ |
| 422 | 422 | #define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */ |
| 423 | +#define SCAN_NESTED 0x004 /* Scan for empty dirs in nested checkouts */ | |
| 423 | 424 | #endif /* INTERFACE */ |
| 424 | 425 | |
| 425 | 426 | /* |
| 426 | 427 | ** Load into table SFILE the name of every ordinary file in |
| 427 | 428 | ** the directory pPath. Omit the first nPrefix characters of |
| @@ -428,15 +429,16 @@ | ||
| 428 | 429 | ** of pPath when inserting into the SFILE table. |
| 429 | 430 | ** |
| 430 | 431 | ** Subdirectories are scanned recursively. |
| 431 | 432 | ** Omit files named in VFILE. |
| 432 | 433 | ** |
| 433 | -** Files whose names begin with "." are omitted unless allFlag is true. | |
| 434 | +** Files whose names begin with "." are omitted unless the SCAN_ALL | |
| 435 | +** flag is set. | |
| 434 | 436 | ** |
| 435 | -** Any files or directories that match the glob pattern pIgnore are | |
| 436 | -** excluded from the scan. Name matching occurs after the first | |
| 437 | -** nPrefix characters are elided from the filename. | |
| 437 | +** Any files or directories that match the glob patterns pIgnore* | |
| 438 | +** are excluded from the scan. Name matching occurs after the | |
| 439 | +** first nPrefix characters are elided from the filename. | |
| 438 | 440 | */ |
| 439 | 441 | void vfile_scan( |
| 440 | 442 | Blob *pPath, /* Directory to be scanned */ |
| 441 | 443 | int nPrefix, /* Number of bytes in directory name */ |
| 442 | 444 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| @@ -443,11 +445,10 @@ | ||
| 443 | 445 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 444 | 446 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 445 | 447 | ){ |
| 446 | 448 | DIR *d; |
| 447 | 449 | int origSize; |
| 448 | - const char *zDir; | |
| 449 | 450 | struct dirent *pEntry; |
| 450 | 451 | int skipAll = 0; |
| 451 | 452 | static Stmt ins; |
| 452 | 453 | static int depth = 0; |
| 453 | 454 | void *zNative; |
| @@ -468,12 +469,11 @@ | ||
| 468 | 469 | " pathname=:file %s)", filename_collation() |
| 469 | 470 | ); |
| 470 | 471 | } |
| 471 | 472 | depth++; |
| 472 | 473 | |
| 473 | - zDir = blob_str(pPath); | |
| 474 | - zNative = fossil_utf8_to_filename(zDir); | |
| 474 | + zNative = fossil_utf8_to_filename(blob_str(pPath)); | |
| 475 | 475 | d = opendir(zNative); |
| 476 | 476 | if( d ){ |
| 477 | 477 | while( (pEntry=readdir(d))!=0 ){ |
| 478 | 478 | char *zPath; |
| 479 | 479 | char *zUtf8; |
| @@ -509,10 +509,127 @@ | ||
| 509 | 509 | depth--; |
| 510 | 510 | if( depth==0 ){ |
| 511 | 511 | db_finalize(&ins); |
| 512 | 512 | } |
| 513 | 513 | } |
| 514 | + | |
| 515 | +/* | |
| 516 | +** Scans the specified base directory for any directories within it, while | |
| 517 | +** keeping a count of how many files they each contains, either directly or | |
| 518 | +** indirectly. | |
| 519 | +** | |
| 520 | +** Subdirectories are scanned recursively. | |
| 521 | +** Omit files named in VFILE. | |
| 522 | +** | |
| 523 | +** Directories whose names begin with "." are omitted unless the SCAN_ALL | |
| 524 | +** flag is set. | |
| 525 | +** | |
| 526 | +** Any directories that match the glob patterns pIgnore* are excluded from | |
| 527 | +** the scan. Name matching occurs after the first nPrefix characters are | |
| 528 | +** elided from the filename. | |
| 529 | +** | |
| 530 | +** Returns the total number of files found. | |
| 531 | +*/ | |
| 532 | +int vfile_dir_scan( | |
| 533 | + Blob *pPath, /* Base directory to be scanned */ | |
| 534 | + int nPrefix, /* Number of bytes in base directory name */ | |
| 535 | + unsigned scanFlags, /* Zero or more SCAN_xxx flags */ | |
| 536 | + Glob *pIgnore1, /* Do not add directories that match this GLOB */ | |
| 537 | + Glob *pIgnore2, /* Omit directories matching this GLOB too */ | |
| 538 | + Glob *pIgnore3 /* Omit directories matching this GLOB too */ | |
| 539 | +){ | |
| 540 | + int result = 0; | |
| 541 | + DIR *d; | |
| 542 | + int origSize; | |
| 543 | + struct dirent *pEntry; | |
| 544 | + int skipAll = 0; | |
| 545 | + static Stmt ins; | |
| 546 | + static Stmt upd; | |
| 547 | + static int depth = 0; | |
| 548 | + void *zNative; | |
| 549 | + | |
| 550 | + origSize = blob_size(pPath); | |
| 551 | + if( pIgnore1 || pIgnore2 || pIgnore3 ){ | |
| 552 | + blob_appendf(pPath, "/"); | |
| 553 | + if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; | |
| 554 | + if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; | |
| 555 | + if( glob_match(pIgnore3, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; | |
| 556 | + blob_resize(pPath, origSize); | |
| 557 | + } | |
| 558 | + if( skipAll ) return result; | |
| 559 | + | |
| 560 | + if( depth==0 ){ | |
| 561 | + db_multi_exec("DROP TABLE IF EXISTS dscan_temp;" | |
| 562 | + "CREATE TEMP TABLE dscan_temp(" | |
| 563 | + " x TEXT PRIMARY KEY %s, y INTEGER)", | |
| 564 | + filename_collation()); | |
| 565 | + db_prepare(&ins, | |
| 566 | + "INSERT OR IGNORE INTO dscan_temp(x, y) SELECT :file, :count" | |
| 567 | + " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" | |
| 568 | + " pathname GLOB :file || '/*' %s)", filename_collation() | |
| 569 | + ); | |
| 570 | + db_prepare(&upd, | |
| 571 | + "UPDATE OR IGNORE dscan_temp SET y = coalesce(y, 0) + 1" | |
| 572 | + " WHERE x=:file %s", | |
| 573 | + filename_collation() | |
| 574 | + ); | |
| 575 | + } | |
| 576 | + depth++; | |
| 577 | + | |
| 578 | + zNative = fossil_utf8_to_filename(blob_str(pPath)); | |
| 579 | + d = opendir(zNative); | |
| 580 | + if( d ){ | |
| 581 | + while( (pEntry=readdir(d))!=0 ){ | |
| 582 | + char *zOrigPath; | |
| 583 | + char *zPath; | |
| 584 | + char *zUtf8; | |
| 585 | + if( pEntry->d_name[0]=='.' ){ | |
| 586 | + if( (scanFlags & SCAN_ALL)==0 ) continue; | |
| 587 | + if( pEntry->d_name[1]==0 ) continue; | |
| 588 | + if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; | |
| 589 | + } | |
| 590 | + zOrigPath = mprintf("%s", blob_str(pPath)); | |
| 591 | + zUtf8 = fossil_filename_to_utf8(pEntry->d_name); | |
| 592 | + blob_appendf(pPath, "/%s", zUtf8); | |
| 593 | + zPath = blob_str(pPath); | |
| 594 | + if( glob_match(pIgnore1, &zPath[nPrefix+1]) || | |
| 595 | + glob_match(pIgnore2, &zPath[nPrefix+1]) || | |
| 596 | + glob_match(pIgnore3, &zPath[nPrefix+1]) ){ | |
| 597 | + /* do nothing */ | |
| 598 | + }else if( file_wd_isdir(zPath)==1 ){ | |
| 599 | + if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ | |
| 600 | + char *zSavePath = mprintf("%s", zPath); | |
| 601 | + int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, | |
| 602 | + pIgnore2, pIgnore3); | |
| 603 | + db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); | |
| 604 | + db_bind_int(&ins, ":count", count); | |
| 605 | + db_step(&ins); | |
| 606 | + db_reset(&ins); | |
| 607 | + fossil_free(zSavePath); | |
| 608 | + result += count; /* found X normal files? */ | |
| 609 | + } | |
| 610 | + }else if( file_wd_isfile_or_link(zPath) ){ | |
| 611 | + db_bind_text(&upd, ":file", zOrigPath); | |
| 612 | + db_step(&upd); | |
| 613 | + db_reset(&upd); | |
| 614 | + result++; /* found 1 normal file */ | |
| 615 | + } | |
| 616 | + fossil_filename_free(zUtf8); | |
| 617 | + blob_resize(pPath, origSize); | |
| 618 | + fossil_free(zOrigPath); | |
| 619 | + } | |
| 620 | + closedir(d); | |
| 621 | + } | |
| 622 | + fossil_filename_free(zNative); | |
| 623 | + | |
| 624 | + depth--; | |
| 625 | + if( depth==0 ){ | |
| 626 | + db_finalize(&upd); | |
| 627 | + db_finalize(&ins); | |
| 628 | + } | |
| 629 | + return result; | |
| 630 | +} | |
| 514 | 631 | |
| 515 | 632 | /* |
| 516 | 633 | ** Compute an aggregate MD5 checksum over the disk image of every |
| 517 | 634 | ** file in vid. The file names are part of the checksum. The resulting |
| 518 | 635 | ** checksum is the same as is expected on the R-card of a manifest. |
| 519 | 636 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -418,10 +418,11 @@ | |
| 418 | /* |
| 419 | ** Values for the scanFlags parameter to vfile_scan(). |
| 420 | */ |
| 421 | #define SCAN_ALL 0x001 /* Includes files that begin with "." */ |
| 422 | #define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */ |
| 423 | #endif /* INTERFACE */ |
| 424 | |
| 425 | /* |
| 426 | ** Load into table SFILE the name of every ordinary file in |
| 427 | ** the directory pPath. Omit the first nPrefix characters of |
| @@ -428,15 +429,16 @@ | |
| 428 | ** of pPath when inserting into the SFILE table. |
| 429 | ** |
| 430 | ** Subdirectories are scanned recursively. |
| 431 | ** Omit files named in VFILE. |
| 432 | ** |
| 433 | ** Files whose names begin with "." are omitted unless allFlag is true. |
| 434 | ** |
| 435 | ** Any files or directories that match the glob pattern pIgnore are |
| 436 | ** excluded from the scan. Name matching occurs after the first |
| 437 | ** nPrefix characters are elided from the filename. |
| 438 | */ |
| 439 | void vfile_scan( |
| 440 | Blob *pPath, /* Directory to be scanned */ |
| 441 | int nPrefix, /* Number of bytes in directory name */ |
| 442 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| @@ -443,11 +445,10 @@ | |
| 443 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 444 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 445 | ){ |
| 446 | DIR *d; |
| 447 | int origSize; |
| 448 | const char *zDir; |
| 449 | struct dirent *pEntry; |
| 450 | int skipAll = 0; |
| 451 | static Stmt ins; |
| 452 | static int depth = 0; |
| 453 | void *zNative; |
| @@ -468,12 +469,11 @@ | |
| 468 | " pathname=:file %s)", filename_collation() |
| 469 | ); |
| 470 | } |
| 471 | depth++; |
| 472 | |
| 473 | zDir = blob_str(pPath); |
| 474 | zNative = fossil_utf8_to_filename(zDir); |
| 475 | d = opendir(zNative); |
| 476 | if( d ){ |
| 477 | while( (pEntry=readdir(d))!=0 ){ |
| 478 | char *zPath; |
| 479 | char *zUtf8; |
| @@ -509,10 +509,127 @@ | |
| 509 | depth--; |
| 510 | if( depth==0 ){ |
| 511 | db_finalize(&ins); |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | ** Compute an aggregate MD5 checksum over the disk image of every |
| 517 | ** file in vid. The file names are part of the checksum. The resulting |
| 518 | ** checksum is the same as is expected on the R-card of a manifest. |
| 519 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -418,10 +418,11 @@ | |
| 418 | /* |
| 419 | ** Values for the scanFlags parameter to vfile_scan(). |
| 420 | */ |
| 421 | #define SCAN_ALL 0x001 /* Includes files that begin with "." */ |
| 422 | #define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */ |
| 423 | #define SCAN_NESTED 0x004 /* Scan for empty dirs in nested checkouts */ |
| 424 | #endif /* INTERFACE */ |
| 425 | |
| 426 | /* |
| 427 | ** Load into table SFILE the name of every ordinary file in |
| 428 | ** the directory pPath. Omit the first nPrefix characters of |
| @@ -428,15 +429,16 @@ | |
| 429 | ** of pPath when inserting into the SFILE table. |
| 430 | ** |
| 431 | ** Subdirectories are scanned recursively. |
| 432 | ** Omit files named in VFILE. |
| 433 | ** |
| 434 | ** Files whose names begin with "." are omitted unless the SCAN_ALL |
| 435 | ** flag is set. |
| 436 | ** |
| 437 | ** Any files or directories that match the glob patterns pIgnore* |
| 438 | ** are excluded from the scan. Name matching occurs after the |
| 439 | ** first nPrefix characters are elided from the filename. |
| 440 | */ |
| 441 | void vfile_scan( |
| 442 | Blob *pPath, /* Directory to be scanned */ |
| 443 | int nPrefix, /* Number of bytes in directory name */ |
| 444 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| @@ -443,11 +445,10 @@ | |
| 445 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 446 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 447 | ){ |
| 448 | DIR *d; |
| 449 | int origSize; |
| 450 | struct dirent *pEntry; |
| 451 | int skipAll = 0; |
| 452 | static Stmt ins; |
| 453 | static int depth = 0; |
| 454 | void *zNative; |
| @@ -468,12 +469,11 @@ | |
| 469 | " pathname=:file %s)", filename_collation() |
| 470 | ); |
| 471 | } |
| 472 | depth++; |
| 473 | |
| 474 | zNative = fossil_utf8_to_filename(blob_str(pPath)); |
| 475 | d = opendir(zNative); |
| 476 | if( d ){ |
| 477 | while( (pEntry=readdir(d))!=0 ){ |
| 478 | char *zPath; |
| 479 | char *zUtf8; |
| @@ -509,10 +509,127 @@ | |
| 509 | depth--; |
| 510 | if( depth==0 ){ |
| 511 | db_finalize(&ins); |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | ** Scans the specified base directory for any directories within it, while |
| 517 | ** keeping a count of how many files they each contains, either directly or |
| 518 | ** indirectly. |
| 519 | ** |
| 520 | ** Subdirectories are scanned recursively. |
| 521 | ** Omit files named in VFILE. |
| 522 | ** |
| 523 | ** Directories whose names begin with "." are omitted unless the SCAN_ALL |
| 524 | ** flag is set. |
| 525 | ** |
| 526 | ** Any directories that match the glob patterns pIgnore* are excluded from |
| 527 | ** the scan. Name matching occurs after the first nPrefix characters are |
| 528 | ** elided from the filename. |
| 529 | ** |
| 530 | ** Returns the total number of files found. |
| 531 | */ |
| 532 | int vfile_dir_scan( |
| 533 | Blob *pPath, /* Base directory to be scanned */ |
| 534 | int nPrefix, /* Number of bytes in base directory name */ |
| 535 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 536 | Glob *pIgnore1, /* Do not add directories that match this GLOB */ |
| 537 | Glob *pIgnore2, /* Omit directories matching this GLOB too */ |
| 538 | Glob *pIgnore3 /* Omit directories matching this GLOB too */ |
| 539 | ){ |
| 540 | int result = 0; |
| 541 | DIR *d; |
| 542 | int origSize; |
| 543 | struct dirent *pEntry; |
| 544 | int skipAll = 0; |
| 545 | static Stmt ins; |
| 546 | static Stmt upd; |
| 547 | static int depth = 0; |
| 548 | void *zNative; |
| 549 | |
| 550 | origSize = blob_size(pPath); |
| 551 | if( pIgnore1 || pIgnore2 || pIgnore3 ){ |
| 552 | blob_appendf(pPath, "/"); |
| 553 | if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; |
| 554 | if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; |
| 555 | if( glob_match(pIgnore3, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; |
| 556 | blob_resize(pPath, origSize); |
| 557 | } |
| 558 | if( skipAll ) return result; |
| 559 | |
| 560 | if( depth==0 ){ |
| 561 | db_multi_exec("DROP TABLE IF EXISTS dscan_temp;" |
| 562 | "CREATE TEMP TABLE dscan_temp(" |
| 563 | " x TEXT PRIMARY KEY %s, y INTEGER)", |
| 564 | filename_collation()); |
| 565 | db_prepare(&ins, |
| 566 | "INSERT OR IGNORE INTO dscan_temp(x, y) SELECT :file, :count" |
| 567 | " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" |
| 568 | " pathname GLOB :file || '/*' %s)", filename_collation() |
| 569 | ); |
| 570 | db_prepare(&upd, |
| 571 | "UPDATE OR IGNORE dscan_temp SET y = coalesce(y, 0) + 1" |
| 572 | " WHERE x=:file %s", |
| 573 | filename_collation() |
| 574 | ); |
| 575 | } |
| 576 | depth++; |
| 577 | |
| 578 | zNative = fossil_utf8_to_filename(blob_str(pPath)); |
| 579 | d = opendir(zNative); |
| 580 | if( d ){ |
| 581 | while( (pEntry=readdir(d))!=0 ){ |
| 582 | char *zOrigPath; |
| 583 | char *zPath; |
| 584 | char *zUtf8; |
| 585 | if( pEntry->d_name[0]=='.' ){ |
| 586 | if( (scanFlags & SCAN_ALL)==0 ) continue; |
| 587 | if( pEntry->d_name[1]==0 ) continue; |
| 588 | if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; |
| 589 | } |
| 590 | zOrigPath = mprintf("%s", blob_str(pPath)); |
| 591 | zUtf8 = fossil_filename_to_utf8(pEntry->d_name); |
| 592 | blob_appendf(pPath, "/%s", zUtf8); |
| 593 | zPath = blob_str(pPath); |
| 594 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 595 | glob_match(pIgnore2, &zPath[nPrefix+1]) || |
| 596 | glob_match(pIgnore3, &zPath[nPrefix+1]) ){ |
| 597 | /* do nothing */ |
| 598 | }else if( file_wd_isdir(zPath)==1 ){ |
| 599 | if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ |
| 600 | char *zSavePath = mprintf("%s", zPath); |
| 601 | int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, |
| 602 | pIgnore2, pIgnore3); |
| 603 | db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); |
| 604 | db_bind_int(&ins, ":count", count); |
| 605 | db_step(&ins); |
| 606 | db_reset(&ins); |
| 607 | fossil_free(zSavePath); |
| 608 | result += count; /* found X normal files? */ |
| 609 | } |
| 610 | }else if( file_wd_isfile_or_link(zPath) ){ |
| 611 | db_bind_text(&upd, ":file", zOrigPath); |
| 612 | db_step(&upd); |
| 613 | db_reset(&upd); |
| 614 | result++; /* found 1 normal file */ |
| 615 | } |
| 616 | fossil_filename_free(zUtf8); |
| 617 | blob_resize(pPath, origSize); |
| 618 | fossil_free(zOrigPath); |
| 619 | } |
| 620 | closedir(d); |
| 621 | } |
| 622 | fossil_filename_free(zNative); |
| 623 | |
| 624 | depth--; |
| 625 | if( depth==0 ){ |
| 626 | db_finalize(&upd); |
| 627 | db_finalize(&ins); |
| 628 | } |
| 629 | return result; |
| 630 | } |
| 631 | |
| 632 | /* |
| 633 | ** Compute an aggregate MD5 checksum over the disk image of every |
| 634 | ** file in vid. The file names are part of the checksum. The resulting |
| 635 | ** checksum is the same as is expected on the R-card of a manifest. |
| 636 |
+2
-1
| --- test/th1-tcl.test | ||
| +++ test/th1-tcl.test | ||
| @@ -114,11 +114,12 @@ | ||
| 114 | 114 | [file nativename [file join $dir th1-tcl8.txt]] |
| 115 | 115 | |
| 116 | 116 | test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\ |
| 117 | 117 | cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\ |
| 118 | 118 | class="thmainError">ERROR: tailcall can only be called from a proc or\ |
| 119 | -lambda</p>}} | |
| 119 | +lambda</p>} || $RESULT eq {<hr><p class="thmainError">ERROR: This test\ | |
| 120 | +requires Tcl 8.6 or higher.</p>}} | |
| 120 | 121 | |
| 121 | 122 | ############################################################################### |
| 122 | 123 | |
| 123 | 124 | fossil test-th-render --th-open-config \ |
| 124 | 125 | [file nativename [file join $dir th1-tcl9.txt]] |
| 125 | 126 |
| --- test/th1-tcl.test | |
| +++ test/th1-tcl.test | |
| @@ -114,11 +114,12 @@ | |
| 114 | [file nativename [file join $dir th1-tcl8.txt]] |
| 115 | |
| 116 | test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\ |
| 117 | cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\ |
| 118 | class="thmainError">ERROR: tailcall can only be called from a proc or\ |
| 119 | lambda</p>}} |
| 120 | |
| 121 | ############################################################################### |
| 122 | |
| 123 | fossil test-th-render --th-open-config \ |
| 124 | [file nativename [file join $dir th1-tcl9.txt]] |
| 125 |
| --- test/th1-tcl.test | |
| +++ test/th1-tcl.test | |
| @@ -114,11 +114,12 @@ | |
| 114 | [file nativename [file join $dir th1-tcl8.txt]] |
| 115 | |
| 116 | test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\ |
| 117 | cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\ |
| 118 | class="thmainError">ERROR: tailcall can only be called from a proc or\ |
| 119 | lambda</p>} || $RESULT eq {<hr><p class="thmainError">ERROR: This test\ |
| 120 | requires Tcl 8.6 or higher.</p>}} |
| 121 | |
| 122 | ############################################################################### |
| 123 | |
| 124 | fossil test-th-render --th-open-config \ |
| 125 | [file nativename [file join $dir th1-tcl9.txt]] |
| 126 |
+1
-1
| --- test/th1-tcl8.txt | ||
| +++ test/th1-tcl8.txt | ||
| @@ -7,8 +7,8 @@ | ||
| 7 | 7 | proc doOut {msg} {puts $msg; puts \n} |
| 8 | 8 | |
| 9 | 9 | if {[tclInvoke set tcl_version] >= 8.6} { |
| 10 | 10 | doOut [tclInvoke tailcall set x 1] |
| 11 | 11 | } else { |
| 12 | - doOut "This test requires Tcl 8.6 or higher." | |
| 12 | + error "This test requires Tcl 8.6 or higher." | |
| 13 | 13 | } |
| 14 | 14 | </th1> |
| 15 | 15 |
| --- test/th1-tcl8.txt | |
| +++ test/th1-tcl8.txt | |
| @@ -7,8 +7,8 @@ | |
| 7 | proc doOut {msg} {puts $msg; puts \n} |
| 8 | |
| 9 | if {[tclInvoke set tcl_version] >= 8.6} { |
| 10 | doOut [tclInvoke tailcall set x 1] |
| 11 | } else { |
| 12 | doOut "This test requires Tcl 8.6 or higher." |
| 13 | } |
| 14 | </th1> |
| 15 |
| --- test/th1-tcl8.txt | |
| +++ test/th1-tcl8.txt | |
| @@ -7,8 +7,8 @@ | |
| 7 | proc doOut {msg} {puts $msg; puts \n} |
| 8 | |
| 9 | if {[tclInvoke set tcl_version] >= 8.6} { |
| 10 | doOut [tclInvoke tailcall set x 1] |
| 11 | } else { |
| 12 | error "This test requires Tcl 8.6 or higher." |
| 13 | } |
| 14 | </th1> |
| 15 |
+6
-6
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -68,13 +68,13 @@ | ||
| 68 | 68 | FOSSIL_TCL_SOURCE = 1 |
| 69 | 69 | |
| 70 | 70 | #### Check if the workaround for the MinGW command line handling needs to |
| 71 | 71 | # be enabled by default. |
| 72 | 72 | # |
| 73 | -ifndef BROKEN_MINGW_CMDLINE | |
| 73 | +ifndef MINGW_IS_32BIT_ONLY | |
| 74 | 74 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 75 | -BROKEN_MINGW_CMDLINE = 1 | |
| 75 | +MINGW_IS_32BIT_ONLY = 1 | |
| 76 | 76 | endif |
| 77 | 77 | endif |
| 78 | 78 | |
| 79 | 79 | #### The directories where the zlib include and library files are located. |
| 80 | 80 | # |
| @@ -162,13 +162,13 @@ | ||
| 162 | 162 | RCC += -I$(TCLINCDIR) |
| 163 | 163 | endif |
| 164 | 164 | endif |
| 165 | 165 | |
| 166 | 166 | # With MinGW command line handling workaround |
| 167 | -ifdef BROKEN_MINGW_CMDLINE | |
| 168 | -TCC += -DBROKEN_MINGW_CMDLINE=1 | |
| 169 | -RCC += -DBROKEN_MINGW_CMDLINE=1 | |
| 167 | +ifdef MINGW_IS_32BIT_ONLY | |
| 168 | +TCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T | |
| 169 | +RCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T | |
| 170 | 170 | endif |
| 171 | 171 | |
| 172 | 172 | # With HTTPS support |
| 173 | 173 | ifdef FOSSIL_ENABLE_SSL |
| 174 | 174 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -203,11 +203,11 @@ | ||
| 203 | 203 | # executable that will run in a chroot jail. |
| 204 | 204 | # |
| 205 | 205 | LIB = -static |
| 206 | 206 | |
| 207 | 207 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 208 | -ifndef BROKEN_MINGW_CMDLINE | |
| 208 | +ifndef MINGW_IS_32BIT_ONLY | |
| 209 | 209 | LIB += -municode |
| 210 | 210 | endif |
| 211 | 211 | |
| 212 | 212 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 213 | 213 | ifdef FOSSIL_ENABLE_SSL |
| 214 | 214 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -68,13 +68,13 @@ | |
| 68 | FOSSIL_TCL_SOURCE = 1 |
| 69 | |
| 70 | #### Check if the workaround for the MinGW command line handling needs to |
| 71 | # be enabled by default. |
| 72 | # |
| 73 | ifndef BROKEN_MINGW_CMDLINE |
| 74 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 75 | BROKEN_MINGW_CMDLINE = 1 |
| 76 | endif |
| 77 | endif |
| 78 | |
| 79 | #### The directories where the zlib include and library files are located. |
| 80 | # |
| @@ -162,13 +162,13 @@ | |
| 162 | RCC += -I$(TCLINCDIR) |
| 163 | endif |
| 164 | endif |
| 165 | |
| 166 | # With MinGW command line handling workaround |
| 167 | ifdef BROKEN_MINGW_CMDLINE |
| 168 | TCC += -DBROKEN_MINGW_CMDLINE=1 |
| 169 | RCC += -DBROKEN_MINGW_CMDLINE=1 |
| 170 | endif |
| 171 | |
| 172 | # With HTTPS support |
| 173 | ifdef FOSSIL_ENABLE_SSL |
| 174 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -203,11 +203,11 @@ | |
| 203 | # executable that will run in a chroot jail. |
| 204 | # |
| 205 | LIB = -static |
| 206 | |
| 207 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 208 | ifndef BROKEN_MINGW_CMDLINE |
| 209 | LIB += -municode |
| 210 | endif |
| 211 | |
| 212 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 213 | ifdef FOSSIL_ENABLE_SSL |
| 214 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -68,13 +68,13 @@ | |
| 68 | FOSSIL_TCL_SOURCE = 1 |
| 69 | |
| 70 | #### Check if the workaround for the MinGW command line handling needs to |
| 71 | # be enabled by default. |
| 72 | # |
| 73 | ifndef MINGW_IS_32BIT_ONLY |
| 74 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 75 | MINGW_IS_32BIT_ONLY = 1 |
| 76 | endif |
| 77 | endif |
| 78 | |
| 79 | #### The directories where the zlib include and library files are located. |
| 80 | # |
| @@ -162,13 +162,13 @@ | |
| 162 | RCC += -I$(TCLINCDIR) |
| 163 | endif |
| 164 | endif |
| 165 | |
| 166 | # With MinGW command line handling workaround |
| 167 | ifdef MINGW_IS_32BIT_ONLY |
| 168 | TCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T |
| 169 | RCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T |
| 170 | endif |
| 171 | |
| 172 | # With HTTPS support |
| 173 | ifdef FOSSIL_ENABLE_SSL |
| 174 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -203,11 +203,11 @@ | |
| 203 | # executable that will run in a chroot jail. |
| 204 | # |
| 205 | LIB = -static |
| 206 | |
| 207 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 208 | ifndef MINGW_IS_32BIT_ONLY |
| 209 | LIB += -municode |
| 210 | endif |
| 211 | |
| 212 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 213 | ifdef FOSSIL_ENABLE_SSL |
| 214 |
+6
-6
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -68,13 +68,13 @@ | ||
| 68 | 68 | FOSSIL_TCL_SOURCE = 1 |
| 69 | 69 | |
| 70 | 70 | #### Check if the workaround for the MinGW command line handling needs to |
| 71 | 71 | # be enabled by default. |
| 72 | 72 | # |
| 73 | -ifndef BROKEN_MINGW_CMDLINE | |
| 73 | +ifndef MINGW_IS_32BIT_ONLY | |
| 74 | 74 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 75 | -BROKEN_MINGW_CMDLINE = 1 | |
| 75 | +MINGW_IS_32BIT_ONLY = 1 | |
| 76 | 76 | endif |
| 77 | 77 | endif |
| 78 | 78 | |
| 79 | 79 | #### The directories where the zlib include and library files are located. |
| 80 | 80 | # |
| @@ -162,13 +162,13 @@ | ||
| 162 | 162 | RCC += -I$(TCLINCDIR) |
| 163 | 163 | endif |
| 164 | 164 | endif |
| 165 | 165 | |
| 166 | 166 | # With MinGW command line handling workaround |
| 167 | -ifdef BROKEN_MINGW_CMDLINE | |
| 168 | -TCC += -DBROKEN_MINGW_CMDLINE=1 | |
| 169 | -RCC += -DBROKEN_MINGW_CMDLINE=1 | |
| 167 | +ifdef MINGW_IS_32BIT_ONLY | |
| 168 | +TCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T | |
| 169 | +RCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T | |
| 170 | 170 | endif |
| 171 | 171 | |
| 172 | 172 | # With HTTPS support |
| 173 | 173 | ifdef FOSSIL_ENABLE_SSL |
| 174 | 174 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -203,11 +203,11 @@ | ||
| 203 | 203 | # executable that will run in a chroot jail. |
| 204 | 204 | # |
| 205 | 205 | LIB = -static |
| 206 | 206 | |
| 207 | 207 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 208 | -ifndef BROKEN_MINGW_CMDLINE | |
| 208 | +ifndef MINGW_IS_32BIT_ONLY | |
| 209 | 209 | LIB += -municode |
| 210 | 210 | endif |
| 211 | 211 | |
| 212 | 212 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 213 | 213 | ifdef FOSSIL_ENABLE_SSL |
| 214 | 214 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -68,13 +68,13 @@ | |
| 68 | FOSSIL_TCL_SOURCE = 1 |
| 69 | |
| 70 | #### Check if the workaround for the MinGW command line handling needs to |
| 71 | # be enabled by default. |
| 72 | # |
| 73 | ifndef BROKEN_MINGW_CMDLINE |
| 74 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 75 | BROKEN_MINGW_CMDLINE = 1 |
| 76 | endif |
| 77 | endif |
| 78 | |
| 79 | #### The directories where the zlib include and library files are located. |
| 80 | # |
| @@ -162,13 +162,13 @@ | |
| 162 | RCC += -I$(TCLINCDIR) |
| 163 | endif |
| 164 | endif |
| 165 | |
| 166 | # With MinGW command line handling workaround |
| 167 | ifdef BROKEN_MINGW_CMDLINE |
| 168 | TCC += -DBROKEN_MINGW_CMDLINE=1 |
| 169 | RCC += -DBROKEN_MINGW_CMDLINE=1 |
| 170 | endif |
| 171 | |
| 172 | # With HTTPS support |
| 173 | ifdef FOSSIL_ENABLE_SSL |
| 174 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -203,11 +203,11 @@ | |
| 203 | # executable that will run in a chroot jail. |
| 204 | # |
| 205 | LIB = -static |
| 206 | |
| 207 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 208 | ifndef BROKEN_MINGW_CMDLINE |
| 209 | LIB += -municode |
| 210 | endif |
| 211 | |
| 212 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 213 | ifdef FOSSIL_ENABLE_SSL |
| 214 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -68,13 +68,13 @@ | |
| 68 | FOSSIL_TCL_SOURCE = 1 |
| 69 | |
| 70 | #### Check if the workaround for the MinGW command line handling needs to |
| 71 | # be enabled by default. |
| 72 | # |
| 73 | ifndef MINGW_IS_32BIT_ONLY |
| 74 | ifeq (,$(findstring w64-mingw32,$(PREFIX))) |
| 75 | MINGW_IS_32BIT_ONLY = 1 |
| 76 | endif |
| 77 | endif |
| 78 | |
| 79 | #### The directories where the zlib include and library files are located. |
| 80 | # |
| @@ -162,13 +162,13 @@ | |
| 162 | RCC += -I$(TCLINCDIR) |
| 163 | endif |
| 164 | endif |
| 165 | |
| 166 | # With MinGW command line handling workaround |
| 167 | ifdef MINGW_IS_32BIT_ONLY |
| 168 | TCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T |
| 169 | RCC += -DBROKEN_MINGW_CMDLINE=1 -D_USE_32BIT_TIME_T |
| 170 | endif |
| 171 | |
| 172 | # With HTTPS support |
| 173 | ifdef FOSSIL_ENABLE_SSL |
| 174 | TCC += -DFOSSIL_ENABLE_SSL=1 |
| @@ -203,11 +203,11 @@ | |
| 203 | # executable that will run in a chroot jail. |
| 204 | # |
| 205 | LIB = -static |
| 206 | |
| 207 | # MinGW: If available, use the Unicode capable runtime startup code. |
| 208 | ifndef MINGW_IS_32BIT_ONLY |
| 209 | LIB += -municode |
| 210 | endif |
| 211 | |
| 212 | # OpenSSL: Add the necessary libraries required, if enabled. |
| 213 | ifdef FOSSIL_ENABLE_SSL |
| 214 |
+5
| --- win/fossil.rc | ||
| +++ win/fossil.rc | ||
| @@ -103,10 +103,15 @@ | ||
| 103 | 103 | #if defined(FOSSIL_ENABLE_SSL) |
| 104 | 104 | VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0" |
| 105 | 105 | #endif /* defined(FOSSIL_ENABLE_SSL) */ |
| 106 | 106 | #if defined(FOSSIL_ENABLE_TCL) |
| 107 | 107 | VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0" |
| 108 | +#if defined(USE_TCL_STUBS) | |
| 109 | + VALUE "UseTclStubsEnabled", "Yes\0" | |
| 110 | +#else | |
| 111 | + VALUE "UseTclStubsEnabled", "No\0" | |
| 112 | +#endif /* defined(USE_TCL_STUBS) */ | |
| 108 | 113 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 109 | 114 | VALUE "TclStubsEnabled", "Yes\0" |
| 110 | 115 | #else |
| 111 | 116 | VALUE "TclStubsEnabled", "No\0" |
| 112 | 117 | #endif /* defined(FOSSIL_ENABLE_TCL_STUBS) */ |
| 113 | 118 |
| --- win/fossil.rc | |
| +++ win/fossil.rc | |
| @@ -103,10 +103,15 @@ | |
| 103 | #if defined(FOSSIL_ENABLE_SSL) |
| 104 | VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0" |
| 105 | #endif /* defined(FOSSIL_ENABLE_SSL) */ |
| 106 | #if defined(FOSSIL_ENABLE_TCL) |
| 107 | VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0" |
| 108 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 109 | VALUE "TclStubsEnabled", "Yes\0" |
| 110 | #else |
| 111 | VALUE "TclStubsEnabled", "No\0" |
| 112 | #endif /* defined(FOSSIL_ENABLE_TCL_STUBS) */ |
| 113 |
| --- win/fossil.rc | |
| +++ win/fossil.rc | |
| @@ -103,10 +103,15 @@ | |
| 103 | #if defined(FOSSIL_ENABLE_SSL) |
| 104 | VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0" |
| 105 | #endif /* defined(FOSSIL_ENABLE_SSL) */ |
| 106 | #if defined(FOSSIL_ENABLE_TCL) |
| 107 | VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0" |
| 108 | #if defined(USE_TCL_STUBS) |
| 109 | VALUE "UseTclStubsEnabled", "Yes\0" |
| 110 | #else |
| 111 | VALUE "UseTclStubsEnabled", "No\0" |
| 112 | #endif /* defined(USE_TCL_STUBS) */ |
| 113 | #if defined(FOSSIL_ENABLE_TCL_STUBS) |
| 114 | VALUE "TclStubsEnabled", "Yes\0" |
| 115 | #else |
| 116 | VALUE "TclStubsEnabled", "No\0" |
| 117 | #endif /* defined(FOSSIL_ENABLE_TCL_STUBS) */ |
| 118 |
+16
-1
| --- www/server.wiki | ||
| +++ www/server.wiki | ||
| @@ -129,16 +129,31 @@ | ||
| 129 | 129 | <blockquote><pre> |
| 130 | 130 | #!/usr/bin/fossil |
| 131 | 131 | repository: /home/fossil/repo.fossil |
| 132 | 132 | </pre></blockquote> |
| 133 | 133 | </p> |
| 134 | + | |
| 134 | 135 | <p> |
| 135 | 136 | As always, adjust your paths appropriately. |
| 136 | 137 | It may be necessary to set permissions properly, or to modify an ".htaccess" |
| 137 | 138 | file or make other server-specific changes. Consult the documentation |
| 138 | -for your particular web server. | |
| 139 | +for your particular web server. In particular, the following permissions are | |
| 140 | +<em>normally</em> required (but, again, may be different for a particular | |
| 141 | +configuration): | |
| 142 | + | |
| 143 | +<ul> | |
| 144 | +<li>The Fossil binary must be readable/executable, and ALL directories leading up to it | |
| 145 | +must be readable by the process which executes the CGI.</li> | |
| 146 | +<li>ALL directories leading to the CGI script must also be readable and the CGI | |
| 147 | +script itself must be executable for the user under which it will run (which often differs | |
| 148 | +from the one running the web server - consult your site's documentation or administrator).</li> | |
| 149 | +<li>The repository file AND the directory containing it must be writable by the same account | |
| 150 | +which executes the Fossil binary (again, this might differ from the WWW user). The directory | |
| 151 | +needs to be writable so that sqlite can write its journal files.</li> | |
| 152 | +</ul> | |
| 139 | 153 | </p> |
| 154 | + | |
| 140 | 155 | <p> |
| 141 | 156 | Once the script is set up correctly, and assuming your server is also set |
| 142 | 157 | correctly, you should be able to access your repository with a URL like: |
| 143 | 158 | <b>http://mydomain.org/cgi-bin/repo</b> (assuming the "repo" script is |
| 144 | 159 | accessible under "cgi-bin", which would be a typical deployment on Apache |
| 145 | 160 |
| --- www/server.wiki | |
| +++ www/server.wiki | |
| @@ -129,16 +129,31 @@ | |
| 129 | <blockquote><pre> |
| 130 | #!/usr/bin/fossil |
| 131 | repository: /home/fossil/repo.fossil |
| 132 | </pre></blockquote> |
| 133 | </p> |
| 134 | <p> |
| 135 | As always, adjust your paths appropriately. |
| 136 | It may be necessary to set permissions properly, or to modify an ".htaccess" |
| 137 | file or make other server-specific changes. Consult the documentation |
| 138 | for your particular web server. |
| 139 | </p> |
| 140 | <p> |
| 141 | Once the script is set up correctly, and assuming your server is also set |
| 142 | correctly, you should be able to access your repository with a URL like: |
| 143 | <b>http://mydomain.org/cgi-bin/repo</b> (assuming the "repo" script is |
| 144 | accessible under "cgi-bin", which would be a typical deployment on Apache |
| 145 |
| --- www/server.wiki | |
| +++ www/server.wiki | |
| @@ -129,16 +129,31 @@ | |
| 129 | <blockquote><pre> |
| 130 | #!/usr/bin/fossil |
| 131 | repository: /home/fossil/repo.fossil |
| 132 | </pre></blockquote> |
| 133 | </p> |
| 134 | |
| 135 | <p> |
| 136 | As always, adjust your paths appropriately. |
| 137 | It may be necessary to set permissions properly, or to modify an ".htaccess" |
| 138 | file or make other server-specific changes. Consult the documentation |
| 139 | for your particular web server. In particular, the following permissions are |
| 140 | <em>normally</em> required (but, again, may be different for a particular |
| 141 | configuration): |
| 142 | |
| 143 | <ul> |
| 144 | <li>The Fossil binary must be readable/executable, and ALL directories leading up to it |
| 145 | must be readable by the process which executes the CGI.</li> |
| 146 | <li>ALL directories leading to the CGI script must also be readable and the CGI |
| 147 | script itself must be executable for the user under which it will run (which often differs |
| 148 | from the one running the web server - consult your site's documentation or administrator).</li> |
| 149 | <li>The repository file AND the directory containing it must be writable by the same account |
| 150 | which executes the Fossil binary (again, this might differ from the WWW user). The directory |
| 151 | needs to be writable so that sqlite can write its journal files.</li> |
| 152 | </ul> |
| 153 | </p> |
| 154 | |
| 155 | <p> |
| 156 | Once the script is set up correctly, and assuming your server is also set |
| 157 | correctly, you should be able to access your repository with a URL like: |
| 158 | <b>http://mydomain.org/cgi-bin/repo</b> (assuming the "repo" script is |
| 159 | accessible under "cgi-bin", which would be a typical deployment on Apache |
| 160 |