Fossil SCM

Merge in latest from trunk to keep up-to-date.

andybradford 2013-10-02 04:22 ssh-transport-changes merge
Commit c6500ac9857ae5353a8ac4c74e0d253477c96a20
+186 -137
--- autosetup/jimsh0.c
+++ autosetup/jimsh0.c
@@ -185,11 +185,11 @@
185185
#endif
186186
187187
#define UCHAR(c) ((unsigned char)(c))
188188
189189
190
-#define JIM_VERSION 73
190
+#define JIM_VERSION 74
191191
192192
#define JIM_OK 0
193193
#define JIM_ERR 1
194194
#define JIM_RETURN 2
195195
#define JIM_BREAK 3
@@ -321,14 +321,14 @@
321321
#define Jim_GetHashTableSize(ht) ((ht)->size)
322322
#define Jim_GetHashTableUsed(ht) ((ht)->used)
323323
324324
325325
typedef struct Jim_Obj {
326
- int refCount;
327326
char *bytes;
328
- int length;
329327
const struct Jim_ObjType *typePtr;
328
+ int refCount;
329
+ int length;
330330
331331
union {
332332
333333
jim_wide wideValue;
334334
@@ -665,12 +665,10 @@
665665
666666
667667
JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
668668
JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
669669
JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
670
-JIM_EXPORT void Jim_InitStringRep (Jim_Obj *objPtr, const char *bytes,
671
- int length);
672670
JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
673671
Jim_Obj *objPtr);
674672
JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
675673
int *lenPtr);
676674
JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
@@ -877,10 +875,12 @@
877875
JIM_EXPORT void Jim_HistoryShow(void);
878876
879877
880878
JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
881879
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)
882882
883883
884884
JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
885885
JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
886886
@@ -5671,10 +5671,13 @@
56715671
#ifdef JIM_MAINTAINER
56725672
#define JIM_DEBUG_COMMAND
56735673
#define JIM_DEBUG_PANIC
56745674
#endif
56755675
5676
+
5677
+#define JIM_INTEGER_SPACE 24
5678
+
56765679
const char *jim_tt_name(int type);
56775680
56785681
#ifdef JIM_DEBUG_PANIC
56795682
static void JimPanicDump(int panic_condition, const char *fmt, ...);
56805683
#define JimPanic(X) JimPanicDump X
@@ -5690,10 +5693,11 @@
56905693
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
56915694
int flags);
56925695
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
56935696
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
56945697
static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5698
+static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
56955699
static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
56965700
const char *prefix, const char *const *tablePtr, const char *name);
56975701
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
56985702
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
56995703
static int JimSign(jim_wide w);
@@ -5934,15 +5938,42 @@
59345938
}
59355939
return n;
59365940
}
59375941
#endif
59385942
5939
-int Jim_WideToString(char *buf, jim_wide wideValue)
5943
+static int JimWideToString(char *buf, jim_wide wideValue)
59405944
{
5941
- const char *fmt = "%" JIM_WIDE_MODIFIER;
5945
+ int pos = 0;
59425946
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;
59445975
}
59455976
59465977
static int JimCheckConversion(const char *str, const char *endptr)
59475978
{
59485979
if (str[0] == '\0' || str == endptr) {
@@ -6229,10 +6260,18 @@
62296260
ht->sizemask = 0;
62306261
ht->used = 0;
62316262
ht->collisions = 0;
62326263
}
62336264
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
+
62346273
62356274
int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
62366275
{
62376276
JimResetHashTable(ht);
62386277
ht->type = type;
@@ -6408,15 +6447,11 @@
64086447
}
64096448
64106449
Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
64116450
{
64126451
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);
64186453
return iter;
64196454
}
64206455
64216456
Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
64226457
{
@@ -10500,12 +10535,10 @@
1050010535
int Jim_GetExitCode(Jim_Interp *interp)
1050110536
{
1050210537
return interp->exitCode;
1050310538
}
1050410539
10505
-#define JIM_INTEGER_SPACE 24
10506
-
1050710540
static void UpdateStringOfInt(struct Jim_Obj *objPtr);
1050810541
static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
1050910542
1051010543
static const Jim_ObjType intObjType = {
1051110544
"int",
@@ -10522,16 +10555,16 @@
1052210555
UpdateStringOfInt,
1052310556
JIM_TYPE_NONE,
1052410557
};
1052510558
1052610559
10527
-void UpdateStringOfInt(struct Jim_Obj *objPtr)
10560
+static void UpdateStringOfInt(struct Jim_Obj *objPtr)
1052810561
{
1052910562
int len;
1053010563
char buf[JIM_INTEGER_SPACE + 1];
1053110564
10532
- len = Jim_WideToString(buf, JimWideValue(objPtr));
10565
+ len = JimWideToString(buf, JimWideValue(objPtr));
1053310566
objPtr->bytes = Jim_Alloc(len + 1);
1053410567
memcpy(objPtr->bytes, buf, len + 1);
1053510568
objPtr->length = len;
1053610569
}
1053710570
@@ -10755,11 +10788,11 @@
1075510788
}
1075610789
1075710790
#define JIM_ELESTR_SIMPLE 0
1075810791
#define JIM_ELESTR_BRACE 1
1075910792
#define JIM_ELESTR_QUOTE 2
10760
-static int ListElementQuotingType(const char *s, int len)
10793
+static unsigned char ListElementQuotingType(const char *s, int len)
1076110794
{
1076210795
int i, level, blevel, trySimple = 1;
1076310796
1076410797
1076510798
if (len == 0)
@@ -10903,17 +10936,23 @@
1090310936
return p - q;
1090410937
}
1090510938
1090610939
static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
1090710940
{
10941
+ #define STATIC_QUOTING_LEN 32
1090810942
int i, bufLen, realLength;
1090910943
const char *strRep;
1091010944
char *p;
10911
- int *quotingType;
10945
+ unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
1091210946
1091310947
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
+ }
1091510954
bufLen = 0;
1091610955
for (i = 0; i < objc; i++) {
1091710956
int len;
1091810957
1091910958
strRep = Jim_GetString(objv[i], &len);
@@ -10975,11 +11014,14 @@
1097511014
realLength++;
1097611015
}
1097711016
}
1097811017
*p = '\0';
1097911018
objPtr->length = realLength;
10980
- Jim_Free(quotingType);
11019
+
11020
+ if (quotingType != staticQuoting) {
11021
+ Jim_Free(quotingType);
11022
+ }
1098111023
}
1098211024
1098311025
static void UpdateStringOfList(struct Jim_Obj *objPtr)
1098411026
{
1098511027
JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
@@ -11000,11 +11042,11 @@
1100011042
if (Jim_IsDict(objPtr) && !Jim_IsShared(objPtr)) {
1100111043
Jim_Obj **listObjPtrPtr;
1100211044
int len;
1100311045
int i;
1100411046
11005
- Jim_DictPairs(interp, objPtr, &listObjPtrPtr, &len);
11047
+ listObjPtrPtr = JimDictPairs(objPtr, &len);
1100611048
for (i = 0; i < len; i++) {
1100711049
Jim_IncrRefCount(listObjPtrPtr[i]);
1100811050
}
1100911051
1101011052
@@ -11225,14 +11267,22 @@
1122511267
int requiredLen = currentLen + elemc;
1122611268
int i;
1122711269
Jim_Obj **point;
1122811270
1122911271
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
+ }
1123111279
1123211280
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;
1123411284
}
1123511285
if (idx < 0) {
1123611286
idx = currentLen;
1123711287
}
1123811288
point = listPtr->internalRep.listValue.ele + idx;
@@ -11523,55 +11573,53 @@
1152311573
}
1152411574
1152511575
void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
1152611576
{
1152711577
Jim_HashTable *ht, *dupHt;
11528
- Jim_HashTableIterator *htiter;
11578
+ Jim_HashTableIterator htiter;
1152911579
Jim_HashEntry *he;
1153011580
1153111581
1153211582
ht = srcPtr->internalRep.ptr;
1153311583
dupHt = Jim_Alloc(sizeof(*dupHt));
1153411584
Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
1153511585
if (ht->size != 0)
1153611586
Jim_ExpandHashTable(dupHt, ht->size);
1153711587
11538
- htiter = Jim_GetHashTableIterator(ht);
11539
- while ((he = Jim_NextHashEntry(htiter)) != NULL) {
11588
+ JimInitHashTableIterator(ht, &htiter);
11589
+ while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
1154011590
const Jim_Obj *keyObjPtr = he->key;
1154111591
Jim_Obj *valObjPtr = he->u.val;
1154211592
1154311593
Jim_IncrRefCount((Jim_Obj *)keyObjPtr);
1154411594
Jim_IncrRefCount(valObjPtr);
1154511595
Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr);
1154611596
}
11547
- Jim_FreeHashTableIterator(htiter);
1154811597
1154911598
dupPtr->internalRep.ptr = dupHt;
1155011599
dupPtr->typePtr = &dictObjType;
1155111600
}
1155211601
1155311602
static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
1155411603
{
1155511604
Jim_HashTable *ht;
11556
- Jim_HashTableIterator *htiter;
11605
+ Jim_HashTableIterator htiter;
1155711606
Jim_HashEntry *he;
1155811607
Jim_Obj **objv;
1155911608
int i;
1156011609
1156111610
ht = dictPtr->internalRep.ptr;
1156211611
1156311612
1156411613
objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
11565
- htiter = Jim_GetHashTableIterator(ht);
11614
+ JimInitHashTableIterator(ht, &htiter);
1156611615
i = 0;
11567
- while ((he = Jim_NextHashEntry(htiter)) != NULL) {
11616
+ while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
1156811617
objv[i++] = (Jim_Obj *)he->key;
1156911618
objv[i++] = he->u.val;
1157011619
}
1157111620
*len = i;
11572
- Jim_FreeHashTableIterator(htiter);
1157311621
return objv;
1157411622
}
1157511623
1157611624
static void UpdateStringOfDict(struct Jim_Obj *objPtr)
1157711625
{
@@ -12089,14 +12137,15 @@
1208912137
1209012138
1209112139
typedef struct Jim_ExprOperator
1209212140
{
1209312141
const char *name;
12094
- int precedence;
12095
- int arity;
1209612142
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;
1209812147
} Jim_ExprOperator;
1209912148
1210012149
static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
1210112150
{
1210212151
Jim_IncrRefCount(obj);
@@ -12774,93 +12823,96 @@
1277412823
LAZY_OP,
1277512824
LAZY_LEFT,
1277612825
LAZY_RIGHT
1277712826
};
1277812827
12828
+#define OPRINIT(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1}
12829
+
1277912830
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),
1284212893
1284312894
#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),
1286012911
#endif
1286112912
};
12913
+#undef OPRINIT
1286212914
1286312915
#define JIM_EXPR_OPERATORS_NUM \
1286412916
(sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
1286512917
1286612918
static int JimParseExpression(struct JimParserCtx *pc)
@@ -13026,20 +13078,18 @@
1302613078
int i;
1302713079
int bestIdx = -1, bestLen = 0;
1302813080
1302913081
1303013082
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;
1303313085
13034
- opname = Jim_ExprOperators[i].name;
13035
- if (opname == NULL) {
13086
+ if (opname == NULL || opname[0] != pc->p[0]) {
1303613087
continue;
1303713088
}
13038
- oplen = strlen(opname);
1303913089
13040
- if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) {
13090
+ if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
1304113091
bestIdx = i + JIM_TT_EXPR_OP;
1304213092
bestLen = oplen;
1304313093
}
1304413094
}
1304513095
if (bestIdx == -1) {
@@ -13111,12 +13161,12 @@
1311113161
};
1311213162
1311313163
1311413164
typedef struct ExprByteCode
1311513165
{
13116
- int len;
1311713166
ScriptToken *token;
13167
+ int len;
1311813168
int inUse;
1311913169
} ExprByteCode;
1312013170
1312113171
static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
1312213172
{
@@ -13855,16 +13905,16 @@
1385513905
1385613906
1385713907
1385813908
typedef struct ScanFmtPartDescr
1385913909
{
13860
- char type;
13861
- char modifier;
13910
+ char *arg;
13911
+ char *prefix;
1386213912
size_t width;
1386313913
int pos;
13864
- char *arg;
13865
- char *prefix;
13914
+ char type;
13915
+ char modifier;
1386613916
} ScanFmtPartDescr;
1386713917
1386813918
1386913919
typedef struct ScanFmtStringObj
1387013920
{
@@ -14871,12 +14921,12 @@
1487114921
}
1487214922
1487314923
if (retcode == JIM_OK && argc) {
1487414924
1487514925
retcode = JimInvokeCommand(interp, argc, argv);
14876
- if (interp->signal_level && interp->sigmask) {
14877
-
14926
+
14927
+ if (Jim_CheckSignal(interp)) {
1487814928
retcode = JIM_SIGNAL;
1487914929
}
1488014930
}
1488114931
1488214932
@@ -15446,17 +15496,17 @@
1544615496
if (he) {
1544715497
callback(interp, listObjPtr, he, type);
1544815498
}
1544915499
}
1545015500
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) {
1545315504
if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
1545415505
callback(interp, listObjPtr, he, type);
1545515506
}
1545615507
}
15457
- Jim_FreeHashTableIterator(htiter);
1545815508
}
1545915509
return listObjPtr;
1546015510
}
1546115511
1546215512
@@ -17795,11 +17845,11 @@
1779517845
if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
1779617846
sig++;
1779717847
}
1779817848
1779917849
interp->signal_level += sig;
17800
- if (interp->signal_level && interp->sigmask) {
17850
+ if (Jim_CheckSignal(interp)) {
1780117851
1780217852
exitCode = JIM_SIGNAL;
1780317853
}
1780417854
else {
1780517855
exitCode = Jim_EvalObj(interp, argv[0]);
@@ -17951,25 +18001,24 @@
1795118001
1795218002
1795318003
static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1795418004
{
1795518005
Jim_Obj *listObjPtr;
17956
- Jim_HashTableIterator *htiter;
18006
+ Jim_HashTableIterator htiter;
1795718007
Jim_HashEntry *he;
1795818008
1795918009
listObjPtr = Jim_NewListObj(interp, NULL, 0);
1796018010
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) {
1796318013
char buf[JIM_REFERENCE_SPACE + 1];
1796418014
Jim_Reference *refPtr = he->u.val;
1796518015
const unsigned long *refId = he->key;
1796618016
1796718017
JimFormatReference(buf, refPtr, *refId);
1796818018
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1));
1796918019
}
17970
- Jim_FreeHashTableIterator(htiter);
1797118020
Jim_SetResult(interp, listObjPtr);
1797218021
return JIM_OK;
1797318022
}
1797418023
#endif
1797518024
@@ -18005,17 +18054,17 @@
1800518054
{
1800618055
Jim_HashEntry *he;
1800718056
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
1800818057
1800918058
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) {
1801218062
if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) {
1801318063
callback(interp, listObjPtr, he, type);
1801418064
}
1801518065
}
18016
- Jim_FreeHashTableIterator(htiter);
1801718066
1801818067
return listObjPtr;
1801918068
}
1802018069
1802118070
1802218071
--- 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 @@
248248
** COMMAND: ls
249249
**
250250
** Usage: %fossil ls ?OPTIONS? ?VERSION? ?FILENAMES?
251251
**
252252
** 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
254254
** the files listed (or their children if they are directories) are shown.
255255
**
256256
** Options:
257257
** --age Show when each file was committed
258258
** -v|--verbose Provide extra information about each file.
@@ -363,27 +363,28 @@
363363
}
364364
db_finalize(&q);
365365
}
366366
367367
/*
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.
372373
*/
373374
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 */
379380
){
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 */
385386
386387
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)",
387388
filename_collation());
388389
nRoot = (int)strlen(g.zLocalRoot);
389390
if( argc==0 ){
@@ -506,12 +507,28 @@
506507
** Files and subdirectories whose names begin with "." are
507508
** normally kept. They are handled if the "--dotfiles" option
508509
** is used.
509510
**
510511
** 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.
511518
** --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.
512522
** --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.
513530
** -f|--force Remove files without prompting.
514531
** --clean <CSG> Never prompt for files matching this
515532
** comma separated list of glob patterns.
516533
** --ignore <CSG> Ignore files matching patterns from the
517534
** comma separated list of glob patterns.
@@ -522,25 +539,27 @@
522539
** -v|--verbose Show all files as they are removed.
523540
**
524541
** See also: addremove, extra, status
525542
*/
526543
void clean_cmd(void){
527
- int allFlag, dryRunFlag, verboseFlag;
544
+ int allFileFlag, allDirFlag, dryRunFlag, verboseFlag;
545
+ int emptyDirsFlag, dirsOnlyFlag;
528546
unsigned scanFlags = 0;
529547
const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag;
530
- Blob repo;
531
- Stmt q;
532548
Glob *pIgnore, *pKeep, *pClean;
533549
int nRoot;
534550
535551
dryRunFlag = find_option("dry-run","n",0)!=0;
536552
if( !dryRunFlag ){
537553
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
538554
}
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;
540558
if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
541559
if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
560
+ if( find_option("allckouts",0,0)!=0 ) scanFlags |= SCAN_NESTED;
542561
zIgnoreFlag = find_option("ignore",0,1);
543562
verboseFlag = find_option("verbose","v",0)!=0;
544563
zKeepFlag = find_option("keep",0,1);
545564
zCleanFlag = find_option("clean",0,1);
546565
capture_case_sensitive_option();
@@ -556,51 +575,99 @@
556575
}
557576
verify_all_options();
558577
pIgnore = glob_create(zIgnoreFlag);
559578
pKeep = glob_create(zKeepFlag);
560579
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)");
574580
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);
599665
}
600666
glob_free(pClean);
601
- db_finalize(&q);
667
+ glob_free(pKeep);
668
+ glob_free(pIgnore);
602669
}
603670
604671
/*
605672
** Prompt the user for a check-in or stash comment (given in pPrompt),
606673
** gather the response, then return the response in pComment.
@@ -642,12 +709,16 @@
642709
"# and because no comment was specified using the \"-m\" or \"-M\"\n"
643710
"# command-line options, you will need to enter the comment below.\n"
644711
"# Type \".\" on a line by itself when you are done:\n", -1);
645712
zFile = mprintf("-");
646713
}else{
714
+ Blob fname;
715
+ blob_zero(&fname);
716
+ file_relative_name(g.zLocalRoot, &fname, 1);
647717
zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
648
- g.zLocalRoot);
718
+ blob_str(&fname));
719
+ blob_reset(&fname);
649720
}
650721
#if defined(_WIN32)
651722
blob_add_cr(pPrompt);
652723
#endif
653724
blob_write_to_file(pPrompt, zFile);
654725
--- 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 @@
461461
fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
462462
}
463463
464464
/*
465465
** Delete a file.
466
+**
467
+** Returns zero upon success.
466468
*/
467
-void file_delete(const char *zFilename){
469
+int file_delete(const char *zFilename){
470
+ int rc;
468471
#ifdef _WIN32
469472
wchar_t *z = fossil_utf8_to_filename(zFilename);
470
- _wunlink(z);
473
+ rc = _wunlink(z);
471474
#else
472475
char *z = fossil_utf8_to_filename(zFilename);
473
- unlink(zFilename);
476
+ rc = unlink(zFilename);
474477
#endif
475478
fossil_filename_free(z);
479
+ return rc;
476480
}
477481
478482
/*
479483
** Create the directory named in the argument, if it does not already
480484
** exist. If forceFlag is 1, delete any prior non-directory object
@@ -493,10 +497,33 @@
493497
wchar_t *zMbcs = fossil_utf8_to_filename(zName);
494498
rc = _wmkdir(zMbcs);
495499
#else
496500
char *zMbcs = fossil_utf8_to_filename(zName);
497501
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);
498525
#endif
499526
fossil_filename_free(zMbcs);
500527
return rc;
501528
}
502529
return 0;
@@ -794,15 +821,16 @@
794821
** Also test Fossil's ability to measure attributes of a file.
795822
*/
796823
void cmd_test_canonical_name(void){
797824
int i;
798825
Blob x;
826
+ int slashFlag = find_option("slash",0,0)!=0;
799827
blob_zero(&x);
800828
for(i=2; i<g.argc; i++){
801829
char zBuf[100];
802830
const char *zName = g.argv[i];
803
- file_canonical_name(zName, &x, 0);
831
+ file_canonical_name(zName, &x, slashFlag);
804832
fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x));
805833
blob_reset(&x);
806834
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName));
807835
fossil_print(" file_size = %s\n", zBuf);
808836
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName));
@@ -876,21 +904,25 @@
876904
while( zPath[i] && fossil_tolower(zPwd[i])==fossil_tolower(zPath[i]) ) i++;
877905
#else
878906
while( zPath[i] && zPwd[i]==zPath[i] ) i++;
879907
#endif
880908
if( zPath[i]==0 ){
881
- blob_reset(pOut);
909
+ memcpy(&tmp, pOut, sizeof(tmp));
882910
if( zPwd[i]==0 ){
883
- blob_append(pOut, ".", 1);
911
+ blob_set(pOut, ".");
884912
}else{
885
- blob_append(pOut, "..", 2);
913
+ blob_set(pOut, "..");
886914
for(j=i+1; zPwd[j]; j++){
887915
if( zPwd[j]=='/' ){
888916
blob_append(pOut, "/..", 3);
889917
}
890918
}
891919
}
920
+ if( slash && i>0 && zPath[strlen(zPath)-1]=='/'){
921
+ blob_append(pOut, "/", 1);
922
+ }
923
+ blob_reset(&tmp);
892924
return;
893925
}
894926
if( zPwd[i]==0 && zPath[i]=='/' ){
895927
memcpy(&tmp, pOut, sizeof(tmp));
896928
blob_set(pOut, "./");
@@ -917,13 +949,14 @@
917949
** Test the operation of the relative name generator.
918950
*/
919951
void cmd_test_relative_name(void){
920952
int i;
921953
Blob x;
954
+ int slashFlag = find_option("slash",0,0)!=0;
922955
blob_zero(&x);
923956
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);
925958
fossil_print("%s\n", blob_buffer(&x));
926959
blob_reset(&x);
927960
}
928961
}
929962
930963
--- 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 @@
100100
char **argv; /* Full copy of the original (expanded) arguments. */
101101
void *library; /* The Tcl library module handle. */
102102
void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */
103103
void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */
104104
void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */
105
+ void *xFinalize; /* See tcl_FinalizeProc in th_tcl.c. */
105106
Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
107
+ int useObjProc; /* Non-zero if an objProc can be called directly. */
106108
char *setup; /* The optional Tcl setup script. */
107109
void *xPreEval; /* Optional, called before Tcl_Eval*(). */
108110
void *pPreContext; /* Optional, provided to xPreEval(). */
109111
void *xPostEval; /* Optional, called after Tcl_Eval*(). */
110112
void *pPostContext; /* Optional, provided to xPostEval(). */
@@ -345,10 +347,24 @@
345347
/*
346348
** atexit() handler which frees up "some" of the resources
347349
** used by fossil.
348350
*/
349351
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
350366
#ifdef FOSSIL_ENABLE_JSON
351367
cson_value_free(g.json.gc.v);
352368
memset(&g.json, 0, sizeof(g.json));
353369
#endif
354370
free(g.zErrMsg);
@@ -526,10 +542,13 @@
526542
*/
527543
#if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE)
528544
int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */
529545
int wmain(int argc, wchar_t **argv)
530546
#else
547
+#if defined(_WIN32)
548
+int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
549
+#endif
531550
int main(int argc, char **argv)
532551
#endif
533552
{
534553
const char *zCmdName = "unknown";
535554
int idx;
@@ -816,16 +835,19 @@
816835
#if defined(FOSSIL_ENABLE_SSL)
817836
fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT);
818837
#endif
819838
#if defined(FOSSIL_ENABLE_TCL)
820839
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);
822841
zRc = Th_ReturnCodeName(rc, 0);
823842
fossil_print("TCL (Tcl %s, loaded %s: %s)\n",
824843
TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0)
825844
);
826845
#endif
846
+#if defined(USE_TCL_STUBS)
847
+ fossil_print("USE_TCL_STUBS\n");
848
+#endif
827849
#if defined(FOSSIL_ENABLE_TCL_STUBS)
828850
fossil_print("TCL_STUBS\n");
829851
#endif
830852
#if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS)
831853
fossil_print("TCL_PRIVATE_STUBS\n");
@@ -1130,10 +1152,13 @@
11301152
if( getuid()==0 ){
11311153
int i;
11321154
struct stat sStat;
11331155
Blob dir;
11341156
char *zDir;
1157
+ if( g.db!=0 ){
1158
+ db_close(1);
1159
+ }
11351160
11361161
file_canonical_name(zRepo, &dir, 0);
11371162
zDir = blob_str(&dir);
11381163
if( file_isdir(zDir)==1 ){
11391164
if( file_chdir(zDir, 1) ){
@@ -1158,14 +1183,10 @@
11581183
i = setgid(sStat.st_gid);
11591184
i = i || setuid(sStat.st_uid);
11601185
if(i){
11611186
fossil_fatal("setgid/uid() failed with errno %d", errno);
11621187
}
1163
- if( g.db!=0 ){
1164
- db_close(1);
1165
- db_open_repository(zRepo);
1166
- }
11671188
}
11681189
#endif
11691190
return zRepo;
11701191
}
11711192
11721193
--- 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 @@
100100
char **argv; /* Full copy of the original (expanded) arguments. */
101101
void *library; /* The Tcl library module handle. */
102102
void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */
103103
void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */
104104
void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */
105
+ void *xFinalize; /* See tcl_FinalizeProc in th_tcl.c. */
105106
Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
107
+ int useObjProc; /* Non-zero if an objProc can be called directly. */
106108
char *setup; /* The optional Tcl setup script. */
107109
void *xPreEval; /* Optional, called before Tcl_Eval*(). */
108110
void *pPreContext; /* Optional, provided to xPreEval(). */
109111
void *xPostEval; /* Optional, called after Tcl_Eval*(). */
110112
void *pPostContext; /* Optional, provided to xPostEval(). */
@@ -345,10 +347,24 @@
345347
/*
346348
** atexit() handler which frees up "some" of the resources
347349
** used by fossil.
348350
*/
349351
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
350366
#ifdef FOSSIL_ENABLE_JSON
351367
cson_value_free(g.json.gc.v);
352368
memset(&g.json, 0, sizeof(g.json));
353369
#endif
354370
free(g.zErrMsg);
@@ -526,10 +542,13 @@
526542
*/
527543
#if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE)
528544
int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */
529545
int wmain(int argc, wchar_t **argv)
530546
#else
547
+#if defined(_WIN32)
548
+int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
549
+#endif
531550
int main(int argc, char **argv)
532551
#endif
533552
{
534553
const char *zCmdName = "unknown";
535554
int idx;
@@ -816,16 +835,19 @@
816835
#if defined(FOSSIL_ENABLE_SSL)
817836
fossil_print("SSL (%s)\n", OPENSSL_VERSION_TEXT);
818837
#endif
819838
#if defined(FOSSIL_ENABLE_TCL)
820839
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);
822841
zRc = Th_ReturnCodeName(rc, 0);
823842
fossil_print("TCL (Tcl %s, loaded %s: %s)\n",
824843
TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0)
825844
);
826845
#endif
846
+#if defined(USE_TCL_STUBS)
847
+ fossil_print("USE_TCL_STUBS\n");
848
+#endif
827849
#if defined(FOSSIL_ENABLE_TCL_STUBS)
828850
fossil_print("TCL_STUBS\n");
829851
#endif
830852
#if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS)
831853
fossil_print("TCL_PRIVATE_STUBS\n");
@@ -1130,10 +1152,13 @@
11301152
if( getuid()==0 ){
11311153
int i;
11321154
struct stat sStat;
11331155
Blob dir;
11341156
char *zDir;
1157
+ if( g.db!=0 ){
1158
+ db_close(1);
1159
+ }
11351160
11361161
file_canonical_name(zRepo, &dir, 0);
11371162
zDir = blob_str(&dir);
11381163
if( file_isdir(zDir)==1 ){
11391164
if( file_chdir(zDir, 1) ){
@@ -1158,14 +1183,10 @@
11581183
i = setgid(sStat.st_gid);
11591184
i = i || setuid(sStat.st_uid);
11601185
if(i){
11611186
fossil_fatal("setgid/uid() failed with errno %d", errno);
11621187
}
1163
- if( g.db!=0 ){
1164
- db_close(1);
1165
- db_open_repository(zRepo);
1166
- }
11671188
}
11681189
#endif
11691190
return zRepo;
11701191
}
11711192
11721193
--- 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
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -407,13 +407,13 @@
407407
FOSSIL_TCL_SOURCE = 1
408408
409409
#### Check if the workaround for the MinGW command line handling needs to
410410
# be enabled by default.
411411
#
412
-ifndef BROKEN_MINGW_CMDLINE
412
+ifndef MINGW_IS_32BIT_ONLY
413413
ifeq (,$(findstring w64-mingw32,$(PREFIX)))
414
-BROKEN_MINGW_CMDLINE = 1
414
+MINGW_IS_32BIT_ONLY = 1
415415
endif
416416
endif
417417
418418
#### The directories where the zlib include and library files are located.
419419
#
@@ -501,13 +501,13 @@
501501
RCC += -I$(TCLINCDIR)
502502
endif
503503
endif
504504
505505
# 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
509509
endif
510510
511511
# With HTTPS support
512512
ifdef FOSSIL_ENABLE_SSL
513513
TCC += -DFOSSIL_ENABLE_SSL=1
@@ -542,11 +542,11 @@
542542
# executable that will run in a chroot jail.
543543
#
544544
LIB = -static
545545
546546
# MinGW: If available, use the Unicode capable runtime startup code.
547
-ifndef BROKEN_MINGW_CMDLINE
547
+ifndef MINGW_IS_32BIT_ONLY
548548
LIB += -municode
549549
endif
550550
551551
# OpenSSL: Add the necessary libraries required, if enabled.
552552
ifdef FOSSIL_ENABLE_SSL
553553
--- 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
--- src/setup.c
+++ src/setup.c
@@ -1199,10 +1199,11 @@
11991199
login_check_credentials();
12001200
if( !g.perm.Setup ){
12011201
login_needed();
12021202
}
12031203
1204
+ (void) aCmdHelp; /* NOTE: Silence compiler warning. */
12041205
style_header("Settings");
12051206
db_open_local(0);
12061207
db_begin_transaction();
12071208
@ <p>This page provides a simple interface to the "fossil setting" command.
12081209
@ See the "fossil help setting" output below for further information on
12091210
--- 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 @@
175175
}
176176
for(i=0; i<nFormAction; i++){
177177
@ gebi("form%d(i+1)").action="%s(aFormAction[i])";
178178
}
179179
@ }
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) ){
181188
/* Require mouse movement prior to activating hyperlinks */
182189
@ document.getElementsByTagName("body")[0].onmousemove=function(){
183190
@ setTimeout("setAllHrefs();",%d(nDelay));
184191
@ this.onmousemove = null;
185192
@ }
186193
--- 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
+4
--- src/th.h
+++ src/th.h
@@ -155,11 +155,15 @@
155155
*/
156156
int th_register_language(Th_Interp *interp); /* th_lang.c */
157157
int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */
158158
int th_register_vfs(Th_Interp *interp); /* th_vfs.c */
159159
int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */
160
+
161
+#ifdef FOSSIL_ENABLE_TCL
160162
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
161165
162166
/*
163167
** General purpose hash table from th_lang.c.
164168
*/
165169
typedef struct Th_Hash Th_Hash;
166170
--- 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
--- src/th_main.c
+++ src/th_main.c
@@ -298,10 +298,11 @@
298298
** Return true if the fossil binary has the given compile-time feature
299299
** enabled. The set of features includes:
300300
**
301301
** "ssl" = FOSSIL_ENABLE_SSL
302302
** "tcl" = FOSSIL_ENABLE_TCL
303
+** "useTclStubs" = USE_TCL_STUBS
303304
** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS
304305
** "tclPrivateStubs" = FOSSIL_ENABLE_TCL_PRIVATE_STUBS
305306
** "json" = FOSSIL_ENABLE_JSON
306307
** "markdown" = FOSSIL_ENABLE_MARKDOWN
307308
**
@@ -329,10 +330,15 @@
329330
#endif
330331
#if defined(FOSSIL_ENABLE_TCL)
331332
else if( 0 == fossil_strnicmp( zArg, "tcl\0", 4 ) ){
332333
rc = 1;
333334
}
335
+#endif
336
+#if defined(USE_TCL_STUBS)
337
+ else if( 0 == fossil_strnicmp( zArg, "useTclStubs\0", 12 ) ){
338
+ rc = 1;
339
+ }
334340
#endif
335341
#if defined(FOSSIL_ENABLE_TCL_STUBS)
336342
else if( 0 == fossil_strnicmp( zArg, "tclStubs\0", 9 ) ){
337343
rc = 1;
338344
}
339345
--- 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 @@
2323
#ifdef FOSSIL_ENABLE_TCL
2424
2525
#include "th.h"
2626
#include "tcl.h"
2727
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
-
6228
/*
6329
** These macros are designed to reduce the redundant code required to marshal
6430
** arguments from TH1 to Tcl.
65
- */
31
+*/
6632
#define USE_ARGV_TO_OBJV() \
6733
int objc; \
6834
Tcl_Obj **objv; \
6935
int i;
7036
@@ -83,18 +49,25 @@
8349
ckfree((char *)objv);
8450
8551
/*
8652
** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl
8753
** context.
88
- */
54
+*/
8955
#define GET_CTX_TCL_INTERP(ctx) \
9056
((struct TclContext *)(ctx))->interp
9157
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
+
9265
/*
9366
** Define the Tcl shared library name, some exported function names, and some
9467
** cross-platform macros for use with the Tcl stubs mechanism, when enabled.
95
- */
68
+*/
9669
#if defined(USE_TCL_STUBS)
9770
# if defined(_WIN32)
9871
# define WIN32_LEAN_AND_MEAN
9972
# include <windows.h>
10073
# ifndef TCL_LIBRARY_NAME
@@ -144,33 +117,37 @@
144117
# define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp"
145118
# endif
146119
# ifndef TCL_DELETEINTERP_NAME
147120
# define TCL_DELETEINTERP_NAME "_Tcl_DeleteInterp"
148121
# endif
122
+# ifndef TCL_FINALIZE_NAME
123
+# define TCL_FINALIZE_NAME "_Tcl_Finalize"
124
+# endif
149125
#endif /* defined(USE_TCL_STUBS) */
150126
151127
/*
152128
** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed
153129
** when the Tcl library is being loaded dynamically by a stubs-enabled
154130
** application (i.e. the inverse of using a stubs-enabled package). These are
155131
** the only Tcl API functions that MUST be called prior to being able to call
156132
** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete
157133
** 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
+*/
160136
typedef void (tcl_FindExecutableProc) (const char * argv0);
161137
typedef Tcl_Interp *(tcl_CreateInterpProc) (void);
162138
typedef void (tcl_DeleteInterpProc) (Tcl_Interp *interp);
139
+typedef void (tcl_FinalizeProc) (void);
163140
164141
/*
165142
** The function types for the "hook" functions to be called before and after a
166143
** TH1 command makes a call to evaluate a Tcl script. If the "pre" function
167144
** returns anything but TH_OK, then evaluation of the Tcl script is skipped and
168145
** that value is used as the return code. If the "post" function returns
169146
** anything other than its rc argument, that will become the new return code
170147
** for the command.
171
- */
148
+*/
172149
typedef int (tcl_NotifyProc) (
173150
void *pContext, /* The context for this notification. */
174151
Th_Interp *interp, /* The TH1 interpreter being used. */
175152
void *ctx, /* The original TH1 command context. */
176153
int argc, /* Number of arguments for the TH1 command. */
@@ -181,27 +158,27 @@
181158
182159
/*
183160
** Are we using our own private implementation of the Tcl stubs mechanism? If
184161
** this is enabled, it prevents the user from having to link against the Tcl
185162
** stubs library for the target platform, which may not be readily available.
186
- */
163
+*/
187164
#if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS)
188165
/*
189166
** HACK: Using some preprocessor magic and a private static variable, redirect
190167
** the Tcl API calls [found within this file] to the function pointers
191168
** that will be contained in our private Tcl stubs table. This takes
192169
** advantage of the fact that the Tcl headers always define the Tcl API
193170
** functions in terms of the "tclStubsPtr" variable.
194
- */
171
+*/
195172
#define tclStubsPtr privateTclStubsPtr
196173
static const TclStubs *tclStubsPtr = NULL;
197174
198175
/*
199176
** Create a Tcl interpreter structure that mirrors just enough fields to get
200177
** it up and running successfully with our private implementation of the Tcl
201178
** stubs mechanism.
202
- */
179
+*/
203180
struct PrivateTclInterp {
204181
char *result;
205182
Tcl_FreeProc *freeProc;
206183
int errorLine;
207184
const struct TclStubs *stubTable;
@@ -209,11 +186,11 @@
209186
210187
/*
211188
** Fossil can now be compiled without linking to the actual Tcl stubs library.
212189
** In that case, this function will be used to perform those steps that would
213190
** normally be performed within the Tcl stubs library.
214
- */
191
+*/
215192
static int initTclStubs(
216193
Th_Interp *interp,
217194
Tcl_Interp *tclInterp
218195
){
219196
tclStubsPtr = ((struct PrivateTclInterp *)tclInterp)->stubTable;
@@ -231,24 +208,56 @@
231208
return TH_ERROR;
232209
}
233210
return TH_OK;
234211
}
235212
#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
+}
236245
237246
/*
238247
** Creates and initializes a Tcl interpreter for use with the specified TH1
239248
** interpreter. Stores the created Tcl interpreter in the Tcl context supplied
240249
** by the caller. This must be declared here because quite a few functions in
241250
** this file need to use it before it can be defined.
242
- */
251
+*/
243252
static int createTclInterp(Th_Interp *interp, void *pContext);
244253
245254
/*
246255
** Returns the Tcl interpreter result as a string with the associated length.
247256
** If the Tcl interpreter or the Tcl result are NULL, the length will be 0.
248257
** If the length pointer is NULL, the length will not be stored.
249
- */
258
+*/
250259
static char *getTclResult(
251260
Tcl_Interp *pInterp,
252261
int *pN
253262
){
254263
Tcl_Obj *resultPtr;
@@ -274,11 +283,13 @@
274283
char **argv; /* Full copy of the original arguments. */
275284
void *library; /* The Tcl library module handle. */
276285
tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */
277286
tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */
278287
tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */
288
+ tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */
279289
Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
290
+ int useObjProc; /* Non-zero if an objProc can be called directly. */
280291
char *setup; /* The optional Tcl setup script. */
281292
tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */
282293
void *pPreContext; /* Optional, provided to xPreEval(). */
283294
tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */
284295
void *pPostContext; /* Optional, provided to xPostEval(). */
@@ -443,20 +454,13 @@
443454
int argc,
444455
const char **argv,
445456
int *argl
446457
){
447458
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 */
452459
int rc = TH_OK;
453460
int nResult;
454461
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 */
458462
USE_ARGV_TO_OBJV();
459463
460464
if( createTclInterp(interp, ctx)!=TH_OK ){
461465
return TH_ERROR;
462466
}
@@ -472,35 +476,40 @@
472476
if( rc!=TH_OK ){
473477
return rc;
474478
}
475479
Tcl_Preserve((ClientData)tclInterp);
476480
#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
+ }
502511
zResult = getTclResult(tclInterp, &nResult);
503512
Th_SetResult(interp, zResult, nResult);
504513
Tcl_Release((ClientData)tclInterp);
505514
rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
506515
return rc;
@@ -586,11 +595,11 @@
586595
};
587596
588597
/*
589598
** Called if the Tcl interpreter is deleted. Removes the Tcl integration
590599
** commands from the TH1 interpreter.
591
- */
600
+*/
592601
static void Th1DeleteProc(
593602
ClientData clientData,
594603
Tcl_Interp *interp
595604
){
596605
int i;
@@ -607,23 +616,25 @@
607616
** When Tcl stubs support is enabled, attempts to dynamically load the Tcl
608617
** shared library and fetch the function pointers necessary to create an
609618
** interpreter and initialize the stubs mechanism; otherwise, simply setup
610619
** the function pointers provided by the caller with the statically linked
611620
** functions.
612
- */
621
+*/
613622
static int loadTcl(
614623
Th_Interp *interp,
615624
void **pLibrary,
616625
tcl_FindExecutableProc **pxFindExecutable,
617626
tcl_CreateInterpProc **pxCreateInterp,
618
- tcl_DeleteInterpProc **pxDeleteInterp
627
+ tcl_DeleteInterpProc **pxDeleteInterp,
628
+ tcl_FinalizeProc **pxFinalize
619629
){
620630
#if defined(USE_TCL_STUBS)
621631
char fileName[] = TCL_LIBRARY_NAME;
622632
#endif /* defined(USE_TCL_STUBS) */
623633
624
- if( !pLibrary || !pxFindExecutable || !pxCreateInterp || !pxDeleteInterp ){
634
+ if( !pLibrary || !pxFindExecutable || !pxCreateInterp ||
635
+ !pxDeleteInterp || !pxFinalize ){
625636
Th_ErrorMessage(interp,
626637
"invalid Tcl loader argument(s)", (const char *)"", 0);
627638
return TH_ERROR;
628639
}
629640
#if defined(USE_TCL_STUBS)
@@ -631,10 +642,11 @@
631642
void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL);
632643
if( library ){
633644
tcl_FindExecutableProc *xFindExecutable;
634645
tcl_CreateInterpProc *xCreateInterp;
635646
tcl_DeleteInterpProc *xDeleteInterp;
647
+ tcl_FinalizeProc *xFinalize;
636648
const char *procName = TCL_FINDEXECUTABLE_NAME;
637649
xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1);
638650
if( !xFindExecutable ){
639651
xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName);
640652
}
@@ -663,35 +675,49 @@
663675
if( !xDeleteInterp ){
664676
Th_ErrorMessage(interp,
665677
"could not locate Tcl_DeleteInterp", (const char *)"", 0);
666678
dlclose(library);
667679
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;
668691
}
669692
*pLibrary = library;
670693
*pxFindExecutable = xFindExecutable;
671694
*pxCreateInterp = xCreateInterp;
672695
*pxDeleteInterp = xDeleteInterp;
696
+ *pxFinalize = xFinalize;
673697
return TH_OK;
674698
}
675699
} while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */
700
+ fileName[TCL_MINOR_OFFSET] = 'x';
676701
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);
679704
return TH_ERROR;
680705
#else
681706
*pLibrary = 0;
682707
*pxFindExecutable = Tcl_FindExecutable;
683708
*pxCreateInterp = Tcl_CreateInterp;
684709
*pxDeleteInterp = Tcl_DeleteInterp;
710
+ *pxFinalize = Tcl_Finalize;
685711
return TH_OK;
686712
#endif /* defined(USE_TCL_STUBS) */
687713
}
688714
689715
/*
690716
** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter
691717
** based on the supplied command line arguments.
692
- */
718
+*/
693719
static int setTclArguments(
694720
Tcl_Interp *pInterp,
695721
int argc,
696722
char **argv
697723
){
@@ -745,11 +771,11 @@
745771
746772
/*
747773
** Creates and initializes a Tcl interpreter for use with the specified TH1
748774
** interpreter. Stores the created Tcl interpreter in the Tcl context supplied
749775
** by the caller.
750
- */
776
+*/
751777
static int createTclInterp(
752778
Th_Interp *interp,
753779
void *pContext
754780
){
755781
struct TclContext *tclContext = (struct TclContext *)pContext;
@@ -766,11 +792,12 @@
766792
}
767793
if ( tclContext->interp ){
768794
return TH_OK;
769795
}
770796
if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable,
771
- &tclContext->xCreateInterp, &tclContext->xDeleteInterp)!=TH_OK ){
797
+ &tclContext->xCreateInterp, &tclContext->xDeleteInterp,
798
+ &tclContext->xFinalize)!=TH_OK ){
772799
return TH_ERROR;
773800
}
774801
argc = tclContext->argc;
775802
argv = tclContext->argv;
776803
if( argc>0 && argv ){
@@ -820,10 +847,15 @@
820847
"Tcl error setting arguments:", Tcl_GetStringResult(tclInterp), -1);
821848
Tcl_DeleteInterp(tclInterp);
822849
tclContext->interp = tclInterp = 0;
823850
return TH_ERROR;
824851
}
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();
825857
/* Add the TH1 integration commands to Tcl. */
826858
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
827859
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
828860
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
829861
/* If necessary, evaluate the custom Tcl setup script. */
@@ -835,10 +867,68 @@
835867
tclContext->interp = tclInterp = 0;
836868
return TH_ERROR;
837869
}
838870
return TH_OK;
839871
}
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
+}
840930
841931
/*
842932
** Register the Tcl language commands with interpreter interp.
843933
** Usually this is called soon after interpreter creation.
844934
*/
845935
--- 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 @@
11591159
p = p->u.pTo;
11601160
}
11611161
blob_append(&sql, ")", -1);
11621162
path_reset();
11631163
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);
11661166
blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
11671167
tmFlags |= TIMELINE_DISJOINT;
11681168
db_multi_exec("%s", blob_str(&sql));
11691169
}else if( (p_rid || d_rid) && g.perm.Read ){
11701170
/* If p= or d= is present, ignore all other parameters other than n= */
11711171
--- 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 @@
230230
** Returns the difference in CPU times in microseconds since
231231
** fossil_timer_start() was called and returned the given timer ID (or
232232
** since it was last reset). Returns 0 if timerId is out of range.
233233
*/
234234
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 ){
238236
struct FossilTimer * start = &fossilTimerList[timerId-1];
239237
if( !start->id ){
240238
fossil_fatal("Invalid call to fetch a non-allocated "
241239
"timer (#%d)", timerId);
242240
/*NOTREACHED*/
@@ -244,20 +242,19 @@
244242
sqlite3_uint64 eu = 0, es = 0;
245243
fossil_cpu_times( &eu, &es );
246244
return (eu - start->u) + (es - start->s);
247245
}
248246
}
247
+ return 0;
249248
}
250249
251250
/*
252251
** Resets the timer associated with the given ID, as obtained via
253252
** fossil_timer_start(), to the current CPU time values.
254253
*/
255254
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 ){
259256
struct FossilTimer * start = &fossilTimerList[timerId-1];
260257
if( !start->id ){
261258
fossil_fatal("Invalid call to reset a non-allocated "
262259
"timer (#%d)", timerId);
263260
/*NOTREACHED*/
@@ -265,10 +262,11 @@
265262
sqlite3_uint64 const rc = fossil_timer_fetch(timerId);
266263
fossil_cpu_times( &start->u, &start->s );
267264
return rc;
268265
}
269266
}
267
+ return 0;
270268
}
271269
272270
/**
273271
"Deallocates" the fossil timer identified by the given timer ID.
274272
returns the difference (in uSec) between the last time that timer
275273
--- 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 @@
418418
/*
419419
** Values for the scanFlags parameter to vfile_scan().
420420
*/
421421
#define SCAN_ALL 0x001 /* Includes files that begin with "." */
422422
#define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */
423
+#define SCAN_NESTED 0x004 /* Scan for empty dirs in nested checkouts */
423424
#endif /* INTERFACE */
424425
425426
/*
426427
** Load into table SFILE the name of every ordinary file in
427428
** the directory pPath. Omit the first nPrefix characters of
@@ -428,15 +429,16 @@
428429
** of pPath when inserting into the SFILE table.
429430
**
430431
** Subdirectories are scanned recursively.
431432
** Omit files named in VFILE.
432433
**
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.
434436
**
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.
438440
*/
439441
void vfile_scan(
440442
Blob *pPath, /* Directory to be scanned */
441443
int nPrefix, /* Number of bytes in directory name */
442444
unsigned scanFlags, /* Zero or more SCAN_xxx flags */
@@ -443,11 +445,10 @@
443445
Glob *pIgnore1, /* Do not add files that match this GLOB */
444446
Glob *pIgnore2 /* Omit files matching this GLOB too */
445447
){
446448
DIR *d;
447449
int origSize;
448
- const char *zDir;
449450
struct dirent *pEntry;
450451
int skipAll = 0;
451452
static Stmt ins;
452453
static int depth = 0;
453454
void *zNative;
@@ -468,12 +469,11 @@
468469
" pathname=:file %s)", filename_collation()
469470
);
470471
}
471472
depth++;
472473
473
- zDir = blob_str(pPath);
474
- zNative = fossil_utf8_to_filename(zDir);
474
+ zNative = fossil_utf8_to_filename(blob_str(pPath));
475475
d = opendir(zNative);
476476
if( d ){
477477
while( (pEntry=readdir(d))!=0 ){
478478
char *zPath;
479479
char *zUtf8;
@@ -509,10 +509,127 @@
509509
depth--;
510510
if( depth==0 ){
511511
db_finalize(&ins);
512512
}
513513
}
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
+}
514631
515632
/*
516633
** Compute an aggregate MD5 checksum over the disk image of every
517634
** file in vid. The file names are part of the checksum. The resulting
518635
** checksum is the same as is expected on the R-card of a manifest.
519636
--- 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
--- test/th1-tcl.test
+++ test/th1-tcl.test
@@ -114,11 +114,12 @@
114114
[file nativename [file join $dir th1-tcl8.txt]]
115115
116116
test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\
117117
cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\
118118
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>}}
120121
121122
###############################################################################
122123
123124
fossil test-th-render --th-open-config \
124125
[file nativename [file join $dir th1-tcl9.txt]]
125126
--- 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
--- test/th1-tcl8.txt
+++ test/th1-tcl8.txt
@@ -7,8 +7,8 @@
77
proc doOut {msg} {puts $msg; puts \n}
88
99
if {[tclInvoke set tcl_version] >= 8.6} {
1010
doOut [tclInvoke tailcall set x 1]
1111
} else {
12
- doOut "This test requires Tcl 8.6 or higher."
12
+ error "This test requires Tcl 8.6 or higher."
1313
}
1414
</th1>
1515
--- 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
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -68,13 +68,13 @@
6868
FOSSIL_TCL_SOURCE = 1
6969
7070
#### Check if the workaround for the MinGW command line handling needs to
7171
# be enabled by default.
7272
#
73
-ifndef BROKEN_MINGW_CMDLINE
73
+ifndef MINGW_IS_32BIT_ONLY
7474
ifeq (,$(findstring w64-mingw32,$(PREFIX)))
75
-BROKEN_MINGW_CMDLINE = 1
75
+MINGW_IS_32BIT_ONLY = 1
7676
endif
7777
endif
7878
7979
#### The directories where the zlib include and library files are located.
8080
#
@@ -162,13 +162,13 @@
162162
RCC += -I$(TCLINCDIR)
163163
endif
164164
endif
165165
166166
# 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
170170
endif
171171
172172
# With HTTPS support
173173
ifdef FOSSIL_ENABLE_SSL
174174
TCC += -DFOSSIL_ENABLE_SSL=1
@@ -203,11 +203,11 @@
203203
# executable that will run in a chroot jail.
204204
#
205205
LIB = -static
206206
207207
# MinGW: If available, use the Unicode capable runtime startup code.
208
-ifndef BROKEN_MINGW_CMDLINE
208
+ifndef MINGW_IS_32BIT_ONLY
209209
LIB += -municode
210210
endif
211211
212212
# OpenSSL: Add the necessary libraries required, if enabled.
213213
ifdef FOSSIL_ENABLE_SSL
214214
--- 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
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -68,13 +68,13 @@
6868
FOSSIL_TCL_SOURCE = 1
6969
7070
#### Check if the workaround for the MinGW command line handling needs to
7171
# be enabled by default.
7272
#
73
-ifndef BROKEN_MINGW_CMDLINE
73
+ifndef MINGW_IS_32BIT_ONLY
7474
ifeq (,$(findstring w64-mingw32,$(PREFIX)))
75
-BROKEN_MINGW_CMDLINE = 1
75
+MINGW_IS_32BIT_ONLY = 1
7676
endif
7777
endif
7878
7979
#### The directories where the zlib include and library files are located.
8080
#
@@ -162,13 +162,13 @@
162162
RCC += -I$(TCLINCDIR)
163163
endif
164164
endif
165165
166166
# 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
170170
endif
171171
172172
# With HTTPS support
173173
ifdef FOSSIL_ENABLE_SSL
174174
TCC += -DFOSSIL_ENABLE_SSL=1
@@ -203,11 +203,11 @@
203203
# executable that will run in a chroot jail.
204204
#
205205
LIB = -static
206206
207207
# MinGW: If available, use the Unicode capable runtime startup code.
208
-ifndef BROKEN_MINGW_CMDLINE
208
+ifndef MINGW_IS_32BIT_ONLY
209209
LIB += -municode
210210
endif
211211
212212
# OpenSSL: Add the necessary libraries required, if enabled.
213213
ifdef FOSSIL_ENABLE_SSL
214214
--- 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
--- win/fossil.rc
+++ win/fossil.rc
@@ -103,10 +103,15 @@
103103
#if defined(FOSSIL_ENABLE_SSL)
104104
VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
105105
#endif /* defined(FOSSIL_ENABLE_SSL) */
106106
#if defined(FOSSIL_ENABLE_TCL)
107107
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) */
108113
#if defined(FOSSIL_ENABLE_TCL_STUBS)
109114
VALUE "TclStubsEnabled", "Yes\0"
110115
#else
111116
VALUE "TclStubsEnabled", "No\0"
112117
#endif /* defined(FOSSIL_ENABLE_TCL_STUBS) */
113118
--- 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 @@
129129
<blockquote><pre>
130130
#!/usr/bin/fossil
131131
repository: /home/fossil/repo.fossil
132132
</pre></blockquote>
133133
</p>
134
+
134135
<p>
135136
As always, adjust your paths appropriately.
136137
It may be necessary to set permissions properly, or to modify an ".htaccess"
137138
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>
139153
</p>
154
+
140155
<p>
141156
Once the script is set up correctly, and assuming your server is also set
142157
correctly, you should be able to access your repository with a URL like:
143158
<b>http://mydomain.org/cgi-bin/repo</b> (assuming the "repo" script is
144159
accessible under "cgi-bin", which would be a typical deployment on Apache
145160
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button