Fossil SCM

Merge from trunk.

brickviking 2025-03-20 21:13 bv-infotool merge
Commit 3eac814daa37e7dc3c0521b00cc6bf8167d7a4cf1644b67b35b62dfed19928b8
--- .fossil-settings/crlf-glob
+++ .fossil-settings/crlf-glob
@@ -1,5 +1,7 @@
11
compat/zlib/*
22
setup/fossil.iss
33
test/th1-docs-input.txt
44
test/th1-hooks-input.txt
5
+win/build32.bat
6
+win/build64.bat
57
win/buildmsvc.bat
68
--- .fossil-settings/crlf-glob
+++ .fossil-settings/crlf-glob
@@ -1,5 +1,7 @@
1 compat/zlib/*
2 setup/fossil.iss
3 test/th1-docs-input.txt
4 test/th1-hooks-input.txt
 
 
5 win/buildmsvc.bat
6
--- .fossil-settings/crlf-glob
+++ .fossil-settings/crlf-glob
@@ -1,5 +1,7 @@
1 compat/zlib/*
2 setup/fossil.iss
3 test/th1-docs-input.txt
4 test/th1-hooks-input.txt
5 win/build32.bat
6 win/build64.bat
7 win/buildmsvc.bat
8
--- extsrc/pikchr-worker.js
+++ extsrc/pikchr-worker.js
@@ -206,11 +206,11 @@
206206
data:{step: ++f.last.step, text: text||null}
207207
});
208208
}
209209
};
210210
211
- importScripts('pikchr-v7583078860.js');
211
+ importScripts('pikchr-v2813665466.js');
212212
/**
213213
initPikchrModule() is installed via pikchr.js due to
214214
building with:
215215
216216
emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule
217217
--- extsrc/pikchr-worker.js
+++ extsrc/pikchr-worker.js
@@ -206,11 +206,11 @@
206 data:{step: ++f.last.step, text: text||null}
207 });
208 }
209 };
210
211 importScripts('pikchr-v7583078860.js');
212 /**
213 initPikchrModule() is installed via pikchr.js due to
214 building with:
215
216 emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule
217
--- extsrc/pikchr-worker.js
+++ extsrc/pikchr-worker.js
@@ -206,11 +206,11 @@
206 data:{step: ++f.last.step, text: text||null}
207 });
208 }
209 };
210
211 importScripts('pikchr-v2813665466.js');
212 /**
213 initPikchrModule() is installed via pikchr.js due to
214 building with:
215
216 emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule
217
+31 -16
--- extsrc/pikchr.c
+++ extsrc/pikchr.c
@@ -25,17 +25,17 @@
2525
** The following is the concatenation of all %include directives from the
2626
** input grammar file:
2727
*/
2828
/************ Begin %include sections from the grammar ************************/
2929
#line 1 "VERSION.h"
30
-#define MANIFEST_UUID "052f07296e76ab2312caf2a4bf6237e574b3e533c7a36ee8f34db833baa3efb4"
31
-#define MANIFEST_VERSION "[052f07296e]"
32
-#define MANIFEST_DATE "2025-03-05 10:54:16"
30
+#define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845"
31
+#define MANIFEST_VERSION "[8a43b02014]"
32
+#define MANIFEST_DATE "2025-03-19 16:19:43"
3333
#define MANIFEST_YEAR "2025"
34
-#define MANIFEST_ISODATE "20250305105416"
35
-#define MANIFEST_NUMERIC_DATE 20250305
36
-#define MANIFEST_NUMERIC_TIME 105416
34
+#define MANIFEST_ISODATE "20250319161943"
35
+#define MANIFEST_NUMERIC_DATE 20250319
36
+#define MANIFEST_NUMERIC_TIME 161943
3737
#define RELEASE_VERSION "1.0"
3838
#define RELEASE_VERSION_NUMBER 10000
3939
#define RELEASE_RESOURCE_VERSION 1,0,0,0
4040
#define COMPILER "gcc-13.3.0"
4141
#line 2 "pikchr.y"
@@ -3687,42 +3687,57 @@
36873687
** than true arcs. Multiple reasons: (1) the legacy-PIC parameters
36883688
** that control arcs are obscure and I could not figure out what they
36893689
** mean based on available documentation. (2) Arcs are rarely used,
36903690
** and so do not seem that important.
36913691
*/
3692
-static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){
3692
+static PPoint arcControlPoint(int cw, PPoint f, PPoint t){
36933693
PPoint m;
36943694
PNum dx, dy;
36953695
m.x = 0.5*(f.x+t.x);
36963696
m.y = 0.5*(f.y+t.y);
36973697
dx = t.x - f.x;
36983698
dy = t.y - f.y;
36993699
if( cw ){
3700
- m.x -= 0.5*rScale*dy;
3701
- m.y += 0.5*rScale*dx;
3700
+ m.x -= 0.5*dy;
3701
+ m.y += 0.5*dx;
37023702
}else{
3703
- m.x += 0.5*rScale*dy;
3704
- m.y -= 0.5*rScale*dx;
3703
+ m.x += 0.5*dy;
3704
+ m.y -= 0.5*dx;
37053705
}
37063706
return m;
37073707
}
37083708
static void arcCheck(Pik *p, PObj *pObj){
3709
- PPoint m;
3709
+ PPoint f, m, t;
3710
+ PNum sw;
3711
+ int i;
37103712
if( p->nTPath>2 ){
37113713
pik_error(p, &pObj->errTok, "arc geometry error");
37123714
return;
37133715
}
3714
- m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5);
3715
- pik_bbox_add_xy(&pObj->bbox, m.x, m.y);
3716
+ f = p->aTPath[0];
3717
+ t = p->aTPath[1];
3718
+ m = arcControlPoint(pObj->cw, f, t);
3719
+ sw = pObj->sw;
3720
+ for(i=1; i<16; i++){
3721
+ PNum t1, t2, a, b, c, x, y;
3722
+ t1 = 0.0625*i;
3723
+ t2 = 1.0 - t1;
3724
+ a = t2*t2;
3725
+ b = 2*t1*t2;
3726
+ c = t1*t1;
3727
+ x = a*f.x + b*m.x + c*t.x;
3728
+ y = a*f.y + b*m.y + c*t.y;
3729
+ pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw);
3730
+ }
37163731
}
37173732
static void arcRender(Pik *p, PObj *pObj){
37183733
PPoint f, m, t;
37193734
if( pObj->nPath<2 ) return;
37203735
if( pObj->sw<0.0 ) return;
37213736
f = pObj->aPath[0];
37223737
t = pObj->aPath[1];
3723
- m = arcControlPoint(pObj->cw,f,t,1.0);
3738
+ m = arcControlPoint(pObj->cw,f,t);
37243739
if( pObj->larrow ){
37253740
pik_draw_arrowhead(p,&m,&f,pObj);
37263741
}
37273742
if( pObj->rarrow ){
37283743
pik_draw_arrowhead(p,&m,&t,pObj);
@@ -8315,6 +8330,6 @@
83158330
83168331
83178332
#endif /* PIKCHR_TCL */
83188333
83198334
8320
-#line 8320 "pikchr.c"
8335
+#line 8335 "pikchr.c"
83218336
--- extsrc/pikchr.c
+++ extsrc/pikchr.c
@@ -25,17 +25,17 @@
25 ** The following is the concatenation of all %include directives from the
26 ** input grammar file:
27 */
28 /************ Begin %include sections from the grammar ************************/
29 #line 1 "VERSION.h"
30 #define MANIFEST_UUID "052f07296e76ab2312caf2a4bf6237e574b3e533c7a36ee8f34db833baa3efb4"
31 #define MANIFEST_VERSION "[052f07296e]"
32 #define MANIFEST_DATE "2025-03-05 10:54:16"
33 #define MANIFEST_YEAR "2025"
34 #define MANIFEST_ISODATE "20250305105416"
35 #define MANIFEST_NUMERIC_DATE 20250305
36 #define MANIFEST_NUMERIC_TIME 105416
37 #define RELEASE_VERSION "1.0"
38 #define RELEASE_VERSION_NUMBER 10000
39 #define RELEASE_RESOURCE_VERSION 1,0,0,0
40 #define COMPILER "gcc-13.3.0"
41 #line 2 "pikchr.y"
@@ -3687,42 +3687,57 @@
3687 ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters
3688 ** that control arcs are obscure and I could not figure out what they
3689 ** mean based on available documentation. (2) Arcs are rarely used,
3690 ** and so do not seem that important.
3691 */
3692 static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){
3693 PPoint m;
3694 PNum dx, dy;
3695 m.x = 0.5*(f.x+t.x);
3696 m.y = 0.5*(f.y+t.y);
3697 dx = t.x - f.x;
3698 dy = t.y - f.y;
3699 if( cw ){
3700 m.x -= 0.5*rScale*dy;
3701 m.y += 0.5*rScale*dx;
3702 }else{
3703 m.x += 0.5*rScale*dy;
3704 m.y -= 0.5*rScale*dx;
3705 }
3706 return m;
3707 }
3708 static void arcCheck(Pik *p, PObj *pObj){
3709 PPoint m;
 
 
3710 if( p->nTPath>2 ){
3711 pik_error(p, &pObj->errTok, "arc geometry error");
3712 return;
3713 }
3714 m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5);
3715 pik_bbox_add_xy(&pObj->bbox, m.x, m.y);
 
 
 
 
 
 
 
 
 
 
 
 
 
3716 }
3717 static void arcRender(Pik *p, PObj *pObj){
3718 PPoint f, m, t;
3719 if( pObj->nPath<2 ) return;
3720 if( pObj->sw<0.0 ) return;
3721 f = pObj->aPath[0];
3722 t = pObj->aPath[1];
3723 m = arcControlPoint(pObj->cw,f,t,1.0);
3724 if( pObj->larrow ){
3725 pik_draw_arrowhead(p,&m,&f,pObj);
3726 }
3727 if( pObj->rarrow ){
3728 pik_draw_arrowhead(p,&m,&t,pObj);
@@ -8315,6 +8330,6 @@
8315
8316
8317 #endif /* PIKCHR_TCL */
8318
8319
8320 #line 8320 "pikchr.c"
8321
--- extsrc/pikchr.c
+++ extsrc/pikchr.c
@@ -25,17 +25,17 @@
25 ** The following is the concatenation of all %include directives from the
26 ** input grammar file:
27 */
28 /************ Begin %include sections from the grammar ************************/
29 #line 1 "VERSION.h"
30 #define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845"
31 #define MANIFEST_VERSION "[8a43b02014]"
32 #define MANIFEST_DATE "2025-03-19 16:19:43"
33 #define MANIFEST_YEAR "2025"
34 #define MANIFEST_ISODATE "20250319161943"
35 #define MANIFEST_NUMERIC_DATE 20250319
36 #define MANIFEST_NUMERIC_TIME 161943
37 #define RELEASE_VERSION "1.0"
38 #define RELEASE_VERSION_NUMBER 10000
39 #define RELEASE_RESOURCE_VERSION 1,0,0,0
40 #define COMPILER "gcc-13.3.0"
41 #line 2 "pikchr.y"
@@ -3687,42 +3687,57 @@
3687 ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters
3688 ** that control arcs are obscure and I could not figure out what they
3689 ** mean based on available documentation. (2) Arcs are rarely used,
3690 ** and so do not seem that important.
3691 */
3692 static PPoint arcControlPoint(int cw, PPoint f, PPoint t){
3693 PPoint m;
3694 PNum dx, dy;
3695 m.x = 0.5*(f.x+t.x);
3696 m.y = 0.5*(f.y+t.y);
3697 dx = t.x - f.x;
3698 dy = t.y - f.y;
3699 if( cw ){
3700 m.x -= 0.5*dy;
3701 m.y += 0.5*dx;
3702 }else{
3703 m.x += 0.5*dy;
3704 m.y -= 0.5*dx;
3705 }
3706 return m;
3707 }
3708 static void arcCheck(Pik *p, PObj *pObj){
3709 PPoint f, m, t;
3710 PNum sw;
3711 int i;
3712 if( p->nTPath>2 ){
3713 pik_error(p, &pObj->errTok, "arc geometry error");
3714 return;
3715 }
3716 f = p->aTPath[0];
3717 t = p->aTPath[1];
3718 m = arcControlPoint(pObj->cw, f, t);
3719 sw = pObj->sw;
3720 for(i=1; i<16; i++){
3721 PNum t1, t2, a, b, c, x, y;
3722 t1 = 0.0625*i;
3723 t2 = 1.0 - t1;
3724 a = t2*t2;
3725 b = 2*t1*t2;
3726 c = t1*t1;
3727 x = a*f.x + b*m.x + c*t.x;
3728 y = a*f.y + b*m.y + c*t.y;
3729 pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw);
3730 }
3731 }
3732 static void arcRender(Pik *p, PObj *pObj){
3733 PPoint f, m, t;
3734 if( pObj->nPath<2 ) return;
3735 if( pObj->sw<0.0 ) return;
3736 f = pObj->aPath[0];
3737 t = pObj->aPath[1];
3738 m = arcControlPoint(pObj->cw,f,t);
3739 if( pObj->larrow ){
3740 pik_draw_arrowhead(p,&m,&f,pObj);
3741 }
3742 if( pObj->rarrow ){
3743 pik_draw_arrowhead(p,&m,&t,pObj);
@@ -8315,6 +8330,6 @@
8330
8331
8332 #endif /* PIKCHR_TCL */
8333
8334
8335 #line 8335 "pikchr.c"
8336
--- extsrc/pikchr.js
+++ extsrc/pikchr.js
@@ -300,11 +300,11 @@
300300
301301
// end include: URIUtils.js
302302
// include: runtime_exceptions.js
303303
// end include: runtime_exceptions.js
304304
function findWasmBinary() {
305
- var f = "pikchr-v7583078860.wasm";
305
+ var f = "pikchr-v2813665466.wasm";
306306
if (!isDataURI(f)) {
307307
return locateFile(f);
308308
}
309309
return f;
310310
}
311311
--- extsrc/pikchr.js
+++ extsrc/pikchr.js
@@ -300,11 +300,11 @@
300
301 // end include: URIUtils.js
302 // include: runtime_exceptions.js
303 // end include: runtime_exceptions.js
304 function findWasmBinary() {
305 var f = "pikchr-v7583078860.wasm";
306 if (!isDataURI(f)) {
307 return locateFile(f);
308 }
309 return f;
310 }
311
--- extsrc/pikchr.js
+++ extsrc/pikchr.js
@@ -300,11 +300,11 @@
300
301 // end include: URIUtils.js
302 // include: runtime_exceptions.js
303 // end include: runtime_exceptions.js
304 function findWasmBinary() {
305 var f = "pikchr-v2813665466.wasm";
306 if (!isDataURI(f)) {
307 return locateFile(f);
308 }
309 return f;
310 }
311
--- extsrc/pikchr.wasm
+++ extsrc/pikchr.wasm
cannot compute difference between binary files
11
--- extsrc/pikchr.wasm
+++ extsrc/pikchr.wasm
0 annot compute difference between binary files
1
--- extsrc/pikchr.wasm
+++ extsrc/pikchr.wasm
0 annot compute difference between binary files
1
+19 -8
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -6729,23 +6729,21 @@
67296729
if( pCur->ss.iBase<iMin ){
67306730
sqlite3_uint64 d = iMin - pCur->ss.iBase;
67316731
pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
67326732
}
67336733
if( pCur->ss.iTerm>iMax ){
6734
- sqlite3_uint64 d = pCur->ss.iTerm - iMax;
6735
- pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep;
6734
+ pCur->ss.iTerm = iMax;
67366735
}
67376736
}else{
67386737
sqlite3_int64 szStep = -pCur->ss.iStep;
67396738
assert( szStep>0 );
67406739
if( pCur->ss.iBase>iMax ){
67416740
sqlite3_uint64 d = pCur->ss.iBase - iMax;
67426741
pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
67436742
}
67446743
if( pCur->ss.iTerm<iMin ){
6745
- sqlite3_uint64 d = iMin - pCur->ss.iTerm;
6746
- pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep;
6744
+ pCur->ss.iTerm = iMin;
67476745
}
67486746
}
67496747
}
67506748
67516749
/* Apply LIMIT and OFFSET constraints, if any */
@@ -18710,10 +18708,20 @@
1871018708
1871118709
/* typedef unsigned int u32; */
1871218710
/* typedef unsigned char u8; */
1871318711
/* typedef sqlite3_int64 i64; */
1871418712
18713
+/*
18714
+** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
18715
+** to avoid complaints from -fsanitize=strict-bounds.
18716
+*/
18717
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
18718
+# define FLEXARRAY
18719
+#else
18720
+# define FLEXARRAY 1
18721
+#endif
18722
+
1871518723
typedef struct RecoverTable RecoverTable;
1871618724
typedef struct RecoverColumn RecoverColumn;
1871718725
1871818726
/*
1871918727
** When recovering rows of data that can be associated with table
@@ -18817,12 +18825,15 @@
1881718825
** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
1881818826
*/
1881918827
typedef struct RecoverBitmap RecoverBitmap;
1882018828
struct RecoverBitmap {
1882118829
i64 nPg; /* Size of bitmap */
18822
- u32 aElem[1]; /* Array of 32-bit bitmasks */
18830
+ u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */
1882318831
};
18832
+
18833
+/* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */
18834
+#define SZ_RECOVERBITMAP_32 (16)
1882418835
1882518836
/*
1882618837
** State variables (part of the sqlite3_recover structure) used while
1882718838
** recovering data for tables identified in the recovered schema (state
1882818839
** RECOVER_STATE_WRITING).
@@ -19059,11 +19070,11 @@
1905919070
** large enough to store a bit for all page numbers between 1 and nPg,
1906019071
** inclusive. The bitmap is initially zeroed.
1906119072
*/
1906219073
static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
1906319074
int nElem = (nPg+1+31) / 32;
19064
- int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32);
19075
+ int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32);
1906519076
RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);
1906619077
1906719078
if( pRet ){
1906819079
pRet->nPg = nPg;
1906919080
}
@@ -32463,11 +32474,11 @@
3246332474
/*
3246432475
** The CLI needs a working sqlite3_complete() to work properly. So error
3246532476
** out of the build if compiling with SQLITE_OMIT_COMPLETE.
3246632477
*/
3246732478
#ifdef SQLITE_OMIT_COMPLETE
32468
-# error the CLI application is incompatable with SQLITE_OMIT_COMPLETE.
32479
+# error the CLI application is incompatible with SQLITE_OMIT_COMPLETE.
3246932480
#endif
3247032481
3247132482
/*
3247232483
** Return true if zSql is a complete SQL statement. Return false if it
3247332484
** ends in the middle of a string literal or C-style comment.
@@ -33663,19 +33674,19 @@
3366333674
/* Run all arguments that do not begin with '-' as if they were separate
3366433675
** command-line inputs, except for the argToSkip argument which contains
3366533676
** the database filename.
3366633677
*/
3366733678
for(i=0; i<nCmd; i++){
33679
+ echo_group_input(&data, azCmd[i]);
3366833680
if( azCmd[i][0]=='.' ){
3366933681
rc = do_meta_command(azCmd[i], &data);
3367033682
if( rc ){
3367133683
if( rc==2 ) rc = 0;
3367233684
goto shell_main_exit;
3367333685
}
3367433686
}else{
3367533687
open_db(&data, 0);
33676
- echo_group_input(&data, azCmd[i]);
3367733688
rc = shell_exec(&data, azCmd[i], &zErrMsg);
3367833689
if( zErrMsg || rc ){
3367933690
if( zErrMsg!=0 ){
3368033691
shellEmitError(zErrMsg);
3368133692
}else{
3368233693
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -6729,23 +6729,21 @@
6729 if( pCur->ss.iBase<iMin ){
6730 sqlite3_uint64 d = iMin - pCur->ss.iBase;
6731 pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
6732 }
6733 if( pCur->ss.iTerm>iMax ){
6734 sqlite3_uint64 d = pCur->ss.iTerm - iMax;
6735 pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep;
6736 }
6737 }else{
6738 sqlite3_int64 szStep = -pCur->ss.iStep;
6739 assert( szStep>0 );
6740 if( pCur->ss.iBase>iMax ){
6741 sqlite3_uint64 d = pCur->ss.iBase - iMax;
6742 pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
6743 }
6744 if( pCur->ss.iTerm<iMin ){
6745 sqlite3_uint64 d = iMin - pCur->ss.iTerm;
6746 pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep;
6747 }
6748 }
6749 }
6750
6751 /* Apply LIMIT and OFFSET constraints, if any */
@@ -18710,10 +18708,20 @@
18710
18711 /* typedef unsigned int u32; */
18712 /* typedef unsigned char u8; */
18713 /* typedef sqlite3_int64 i64; */
18714
 
 
 
 
 
 
 
 
 
 
18715 typedef struct RecoverTable RecoverTable;
18716 typedef struct RecoverColumn RecoverColumn;
18717
18718 /*
18719 ** When recovering rows of data that can be associated with table
@@ -18817,12 +18825,15 @@
18817 ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
18818 */
18819 typedef struct RecoverBitmap RecoverBitmap;
18820 struct RecoverBitmap {
18821 i64 nPg; /* Size of bitmap */
18822 u32 aElem[1]; /* Array of 32-bit bitmasks */
18823 };
 
 
 
18824
18825 /*
18826 ** State variables (part of the sqlite3_recover structure) used while
18827 ** recovering data for tables identified in the recovered schema (state
18828 ** RECOVER_STATE_WRITING).
@@ -19059,11 +19070,11 @@
19059 ** large enough to store a bit for all page numbers between 1 and nPg,
19060 ** inclusive. The bitmap is initially zeroed.
19061 */
19062 static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
19063 int nElem = (nPg+1+31) / 32;
19064 int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32);
19065 RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);
19066
19067 if( pRet ){
19068 pRet->nPg = nPg;
19069 }
@@ -32463,11 +32474,11 @@
32463 /*
32464 ** The CLI needs a working sqlite3_complete() to work properly. So error
32465 ** out of the build if compiling with SQLITE_OMIT_COMPLETE.
32466 */
32467 #ifdef SQLITE_OMIT_COMPLETE
32468 # error the CLI application is incompatable with SQLITE_OMIT_COMPLETE.
32469 #endif
32470
32471 /*
32472 ** Return true if zSql is a complete SQL statement. Return false if it
32473 ** ends in the middle of a string literal or C-style comment.
@@ -33663,19 +33674,19 @@
33663 /* Run all arguments that do not begin with '-' as if they were separate
33664 ** command-line inputs, except for the argToSkip argument which contains
33665 ** the database filename.
33666 */
33667 for(i=0; i<nCmd; i++){
 
33668 if( azCmd[i][0]=='.' ){
33669 rc = do_meta_command(azCmd[i], &data);
33670 if( rc ){
33671 if( rc==2 ) rc = 0;
33672 goto shell_main_exit;
33673 }
33674 }else{
33675 open_db(&data, 0);
33676 echo_group_input(&data, azCmd[i]);
33677 rc = shell_exec(&data, azCmd[i], &zErrMsg);
33678 if( zErrMsg || rc ){
33679 if( zErrMsg!=0 ){
33680 shellEmitError(zErrMsg);
33681 }else{
33682
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -6729,23 +6729,21 @@
6729 if( pCur->ss.iBase<iMin ){
6730 sqlite3_uint64 d = iMin - pCur->ss.iBase;
6731 pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
6732 }
6733 if( pCur->ss.iTerm>iMax ){
6734 pCur->ss.iTerm = iMax;
 
6735 }
6736 }else{
6737 sqlite3_int64 szStep = -pCur->ss.iStep;
6738 assert( szStep>0 );
6739 if( pCur->ss.iBase>iMax ){
6740 sqlite3_uint64 d = pCur->ss.iBase - iMax;
6741 pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
6742 }
6743 if( pCur->ss.iTerm<iMin ){
6744 pCur->ss.iTerm = iMin;
 
6745 }
6746 }
6747 }
6748
6749 /* Apply LIMIT and OFFSET constraints, if any */
@@ -18710,10 +18708,20 @@
18708
18709 /* typedef unsigned int u32; */
18710 /* typedef unsigned char u8; */
18711 /* typedef sqlite3_int64 i64; */
18712
18713 /*
18714 ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
18715 ** to avoid complaints from -fsanitize=strict-bounds.
18716 */
18717 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
18718 # define FLEXARRAY
18719 #else
18720 # define FLEXARRAY 1
18721 #endif
18722
18723 typedef struct RecoverTable RecoverTable;
18724 typedef struct RecoverColumn RecoverColumn;
18725
18726 /*
18727 ** When recovering rows of data that can be associated with table
@@ -18817,12 +18825,15 @@
18825 ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
18826 */
18827 typedef struct RecoverBitmap RecoverBitmap;
18828 struct RecoverBitmap {
18829 i64 nPg; /* Size of bitmap */
18830 u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */
18831 };
18832
18833 /* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */
18834 #define SZ_RECOVERBITMAP_32 (16)
18835
18836 /*
18837 ** State variables (part of the sqlite3_recover structure) used while
18838 ** recovering data for tables identified in the recovered schema (state
18839 ** RECOVER_STATE_WRITING).
@@ -19059,11 +19070,11 @@
19070 ** large enough to store a bit for all page numbers between 1 and nPg,
19071 ** inclusive. The bitmap is initially zeroed.
19072 */
19073 static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
19074 int nElem = (nPg+1+31) / 32;
19075 int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32);
19076 RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);
19077
19078 if( pRet ){
19079 pRet->nPg = nPg;
19080 }
@@ -32463,11 +32474,11 @@
32474 /*
32475 ** The CLI needs a working sqlite3_complete() to work properly. So error
32476 ** out of the build if compiling with SQLITE_OMIT_COMPLETE.
32477 */
32478 #ifdef SQLITE_OMIT_COMPLETE
32479 # error the CLI application is incompatible with SQLITE_OMIT_COMPLETE.
32480 #endif
32481
32482 /*
32483 ** Return true if zSql is a complete SQL statement. Return false if it
32484 ** ends in the middle of a string literal or C-style comment.
@@ -33663,19 +33674,19 @@
33674 /* Run all arguments that do not begin with '-' as if they were separate
33675 ** command-line inputs, except for the argToSkip argument which contains
33676 ** the database filename.
33677 */
33678 for(i=0; i<nCmd; i++){
33679 echo_group_input(&data, azCmd[i]);
33680 if( azCmd[i][0]=='.' ){
33681 rc = do_meta_command(azCmd[i], &data);
33682 if( rc ){
33683 if( rc==2 ) rc = 0;
33684 goto shell_main_exit;
33685 }
33686 }else{
33687 open_db(&data, 0);
 
33688 rc = shell_exec(&data, azCmd[i], &zErrMsg);
33689 if( zErrMsg || rc ){
33690 if( zErrMsg!=0 ){
33691 shellEmitError(zErrMsg);
33692 }else{
33693
+486 -287
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** e6784af6d50f715338ae3218fc8ba1b89488 with changes in files:
21
+** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465465
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466466
** [sqlite_version()] and [sqlite_source_id()].
467467
*/
468468
#define SQLITE_VERSION "3.50.0"
469469
#define SQLITE_VERSION_NUMBER 3050000
470
-#define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7"
470
+#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
471471
472472
/*
473473
** CAPI3REF: Run-Time Library Version Numbers
474474
** KEYWORDS: sqlite3_version sqlite3_sourceid
475475
**
@@ -5492,11 +5492,11 @@
54925492
** For all versions of SQLite up to and including 3.6.23.1, a call to
54935493
** [sqlite3_reset()] was required after sqlite3_step() returned anything
54945494
** other than [SQLITE_ROW] before any subsequent invocation of
54955495
** sqlite3_step(). Failure to reset the prepared statement using
54965496
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
5497
-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
5497
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
54985498
** sqlite3_step() began
54995499
** calling [sqlite3_reset()] automatically in this circumstance rather
55005500
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
55015501
** break because any application that ever receives an SQLITE_MISUSE error
55025502
** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7388,10 +7388,12 @@
73887388
** ^Any callback set by a previous call to this function
73897389
** for the same database connection is overridden.
73907390
**
73917391
** ^The second argument is a pointer to the function to invoke when a
73927392
** row is updated, inserted or deleted in a rowid table.
7393
+** ^The update hook is disabled by invoking sqlite3_update_hook()
7394
+** with a NULL pointer as the second parameter.
73937395
** ^The first argument to the callback is a copy of the third argument
73947396
** to sqlite3_update_hook().
73957397
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
73967398
** or [SQLITE_UPDATE], depending on the operation that caused the callback
73977399
** to be invoked.
@@ -15170,11 +15172,21 @@
1517015172
/*
1517115173
** GCC does not define the offsetof() macro so we'll have to do it
1517215174
** ourselves.
1517315175
*/
1517415176
#ifndef offsetof
15175
-#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD))
15177
+#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
15178
+#endif
15179
+
15180
+/*
15181
+** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
15182
+** to avoid complaints from -fsanitize=strict-bounds.
15183
+*/
15184
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
15185
+# define FLEXARRAY
15186
+#else
15187
+# define FLEXARRAY 1
1517615188
#endif
1517715189
1517815190
/*
1517915191
** Macros to compute minimum and maximum of two numbers.
1518015192
*/
@@ -17405,12 +17417,12 @@
1740517417
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
1740617418
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
1740717419
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
1740817420
#endif
1740917421
17410
-/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
17411
-** each VDBE opcode.
17422
+/* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra
17423
+** comments on each VDBE opcode.
1741217424
**
1741317425
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
1741417426
** comments in VDBE programs that show key decision points in the code
1741517427
** generator.
1741617428
*/
@@ -18945,12 +18957,16 @@
1894518957
u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
1894618958
Trigger *apTrigger[2];/* Triggers for aAction[] actions */
1894718959
struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
1894818960
int iFrom; /* Index of column in pFrom */
1894918961
char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
18950
- } aCol[1]; /* One entry for each of nCol columns */
18962
+ } aCol[FLEXARRAY]; /* One entry for each of nCol columns */
1895118963
};
18964
+
18965
+/* The size (in bytes) of an FKey object holding N columns. The answer
18966
+** does NOT include space to hold the zTo name. */
18967
+#define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap))
1895218968
1895318969
/*
1895418970
** SQLite supports many different ways to resolve a constraint
1895518971
** error. ROLLBACK processing means that a constraint violation
1895618972
** causes the operation in process to fail and for the current transaction
@@ -19009,13 +19025,16 @@
1900919025
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
1901019026
u16 nKeyField; /* Number of key columns in the index */
1901119027
u16 nAllField; /* Total columns, including key plus others */
1901219028
sqlite3 *db; /* The database connection */
1901319029
u8 *aSortFlags; /* Sort order for each column. */
19014
- CollSeq *aColl[1]; /* Collating sequence for each term of the key */
19030
+ CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
1901519031
};
1901619032
19033
+/* The size (in bytes) of a KeyInfo object with up to N fields */
19034
+#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))
19035
+
1901719036
/*
1901819037
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
1901919038
*/
1902019039
#define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */
1902119040
#define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */
@@ -19584,12 +19603,17 @@
1958419603
u16 iAlias; /* Index into Parse.aAlias[] for zName */
1958519604
} x;
1958619605
int iConstExprReg; /* Register in which Expr value is cached. Used only
1958719606
** by Parse.pConstExpr */
1958819607
} u;
19589
- } a[1]; /* One slot for each expression in the list */
19608
+ } a[FLEXARRAY]; /* One slot for each expression in the list */
1959019609
};
19610
+
19611
+/* The size (in bytes) of an ExprList object that is big enough to hold
19612
+** as many as N expressions. */
19613
+#define SZ_EXPRLIST(N) \
19614
+ (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item))
1959119615
1959219616
/*
1959319617
** Allowed values for Expr.a.eEName
1959419618
*/
1959519619
#define ENAME_NAME 0 /* The AS clause of a result set */
@@ -19614,13 +19638,16 @@
1961419638
*/
1961519639
struct IdList {
1961619640
int nId; /* Number of identifiers on the list */
1961719641
struct IdList_item {
1961819642
char *zName; /* Name of the identifier */
19619
- } a[1];
19643
+ } a[FLEXARRAY];
1962019644
};
1962119645
19646
+/* The size (in bytes) of an IdList object that can hold up to N IDs. */
19647
+#define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item))
19648
+
1962219649
/*
1962319650
** Allowed values for IdList.eType, which determines which value of the a.u4
1962419651
** is valid.
1962519652
*/
1962619653
#define EU4_NONE 0 /* Does not use IdList.a.u4 */
@@ -19736,14 +19763,22 @@
1973619763
** is used to hold the FROM clause of a SELECT statement. SrcList also
1973719764
** represents the target tables for DELETE, INSERT, and UPDATE statements.
1973819765
**
1973919766
*/
1974019767
struct SrcList {
19741
- int nSrc; /* Number of tables or subqueries in the FROM clause */
19742
- u32 nAlloc; /* Number of entries allocated in a[] below */
19743
- SrcItem a[1]; /* One entry for each identifier on the list */
19768
+ int nSrc; /* Number of tables or subqueries in the FROM clause */
19769
+ u32 nAlloc; /* Number of entries allocated in a[] below */
19770
+ SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */
1974419771
};
19772
+
19773
+/* Size (in bytes) of a SrcList object that can hold as many as N
19774
+** SrcItem objects. */
19775
+#define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem))
19776
+
19777
+/* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a
19778
+** special case of SZ_SRCITEM(1) that comes up often. */
19779
+#define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem))
1974519780
1974619781
/*
1974719782
** Permitted values of the SrcList.a.jointype field
1974819783
*/
1974919784
#define JT_INNER 0x01 /* Any kind of inner or cross join */
@@ -20804,12 +20839,16 @@
2080420839
*/
2080520840
struct With {
2080620841
int nCte; /* Number of CTEs in the WITH clause */
2080720842
int bView; /* Belongs to the outermost Select of a view */
2080820843
With *pOuter; /* Containing WITH clause, or NULL */
20809
- Cte a[1]; /* For each CTE in the WITH clause.... */
20844
+ Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */
2081020845
};
20846
+
20847
+/* The size (in bytes) of a With object that can hold as many
20848
+** as N different CTEs. */
20849
+#define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte))
2081120850
2081220851
/*
2081320852
** The Cte object is not guaranteed to persist for the entire duration
2081420853
** of code generation. (The query flattener or other parser tree
2081520854
** edits might delete it.) The following object records information
@@ -20835,12 +20874,16 @@
2083520874
*/
2083620875
struct DbClientData {
2083720876
DbClientData *pNext; /* Next in a linked list */
2083820877
void *pData; /* The data */
2083920878
void (*xDestructor)(void*); /* Destructor. Might be NULL */
20840
- char zName[1]; /* Name of this client data. MUST BE LAST */
20879
+ char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */
2084120880
};
20881
+
20882
+/* The size (in bytes) of a DbClientData object that can has a name
20883
+** that is N bytes long, including the zero-terminator. */
20884
+#define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N))
2084220885
2084320886
#ifdef SQLITE_DEBUG
2084420887
/*
2084520888
** An instance of the TreeView object is used for printing the content of
2084620889
** data structures on sqlite3DebugPrintf() using a tree-like view.
@@ -22673,10 +22716,13 @@
2267322716
"EXTRA_IFNULLROW",
2267422717
#endif
2267522718
#ifdef SQLITE_EXTRA_INIT
2267622719
"EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
2267722720
#endif
22721
+#ifdef SQLITE_EXTRA_INIT_MUTEXED
22722
+ "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED),
22723
+#endif
2267822724
#ifdef SQLITE_EXTRA_SHUTDOWN
2267922725
"EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
2268022726
#endif
2268122727
#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
2268222728
"FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
@@ -23657,16 +23703,23 @@
2365723703
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
2365823704
u64 maskUsed; /* Mask of columns used by this cursor */
2365923705
#endif
2366023706
VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */
2366123707
23662
- /* 2*nField extra array elements allocated for aType[], beyond the one
23663
- ** static element declared in the structure. nField total array slots for
23664
- ** aType[] and nField+1 array slots for aOffset[] */
23665
- u32 aType[1]; /* Type values record decode. MUST BE LAST */
23708
+ /* Space is allocated for aType to hold at least 2*nField+1 entries:
23709
+ ** nField slots for aType[] and nField+1 array slots for aOffset[] */
23710
+ u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */
2366623711
};
2366723712
23713
+/*
23714
+** The size (in bytes) of a VdbeCursor object that has an nField value of N
23715
+** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple
23716
+** of 8.
23717
+*/
23718
+#define SZ_VDBECURSOR(N) \
23719
+ (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64))
23720
+
2366823721
/* Return true if P is a null-only cursor
2366923722
*/
2367023723
#define IsNullCursor(P) \
2367123724
((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)
2367223725
@@ -23919,13 +23972,20 @@
2391923972
int iOp; /* Instruction number of OP_Function */
2392023973
int isError; /* Error code returned by the function. */
2392123974
u8 enc; /* Encoding to use for results */
2392223975
u8 skipFlag; /* Skip accumulator loading if true */
2392323976
u16 argc; /* Number of arguments */
23924
- sqlite3_value *argv[1]; /* Argument set */
23977
+ sqlite3_value *argv[FLEXARRAY]; /* Argument set */
2392523978
};
2392623979
23980
+/*
23981
+** The size (in bytes) of an sqlite3_context object that holds N
23982
+** argv[] arguments.
23983
+*/
23984
+#define SZ_CONTEXT(N) \
23985
+ (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*))
23986
+
2392723987
2392823988
/* The ScanStatus object holds a single value for the
2392923989
** sqlite3_stmt_scanstatus() interface.
2393023990
**
2393123991
** aAddrRange[]:
@@ -24055,11 +24115,11 @@
2405524115
struct PreUpdate {
2405624116
Vdbe *v;
2405724117
VdbeCursor *pCsr; /* Cursor to read old values from */
2405824118
int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
2405924119
u8 *aRecord; /* old.* database record */
24060
- KeyInfo keyinfo;
24120
+ KeyInfo *pKeyinfo; /* Key information */
2406124121
UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
2406224122
UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
2406324123
int iNewReg; /* Register for new.* values */
2406424124
int iBlobWrite; /* Value returned by preupdate_blobwrite() */
2406524125
i64 iKey1; /* First key value passed to hook */
@@ -24067,10 +24127,11 @@
2406724127
Mem oldipk; /* Memory cell holding "old" IPK value */
2406824128
Mem *aNew; /* Array of new.* values */
2406924129
Table *pTab; /* Schema object being updated */
2407024130
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
2407124131
sqlite3_value **apDflt; /* Array of default values, if required */
24132
+ u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
2407224133
};
2407324134
2407424135
/*
2407524136
** An instance of this object is used to pass an vector of values into
2407624137
** OP_VFilter, the xFilter method of a virtual table. The vector is the
@@ -26000,11 +26061,11 @@
2600026061
** Return the number of days after the most recent Sunday.
2600126062
**
2600226063
** In other words, return the day of the week according
2600326064
** to this code:
2600426065
**
26005
-** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
26066
+** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday
2600626067
*/
2600726068
static int daysAfterSunday(DateTime *pDate){
2600826069
assert( pDate->validJD );
2600926070
return (int)((pDate->iJD+129600000)/86400000) % 7;
2601026071
}
@@ -32378,11 +32439,11 @@
3237832439
u32 nBack = 0;
3237932440
u32 nCtrl = 0;
3238032441
for(k=0; k<i; k++){
3238132442
if( escarg[k]=='\\' ){
3238232443
nBack++;
32383
- }else if( escarg[k]<=0x1f ){
32444
+ }else if( ((u8*)escarg)[k]<=0x1f ){
3238432445
nCtrl++;
3238532446
}
3238632447
}
3238732448
if( nCtrl || xtype==etESCAPE_q ){
3238832449
n += nBack + 5*nCtrl;
@@ -32416,11 +32477,11 @@
3241632477
bufpt[j++] = ch = escarg[i];
3241732478
if( ch==q ){
3241832479
bufpt[j++] = ch;
3241932480
}else if( ch=='\\' ){
3242032481
bufpt[j++] = '\\';
32421
- }else if( ch<=0x1f ){
32482
+ }else if( ((unsigned char)ch)<=0x1f ){
3242232483
bufpt[j-1] = '\\';
3242332484
bufpt[j++] = 'u';
3242432485
bufpt[j++] = '0';
3242532486
bufpt[j++] = '0';
3242632487
bufpt[j++] = ch>=0x10 ? '1' : '0';
@@ -37067,11 +37128,11 @@
3706737128
return 0;
3706837129
#endif
3706937130
}
3707037131
3707137132
/*
37072
-** Compute the absolute value of a 32-bit signed integer, of possible. Or
37133
+** Compute the absolute value of a 32-bit signed integer, if possible. Or
3707337134
** if the integer has a value of -2147483648, return +2147483647
3707437135
*/
3707537136
SQLITE_PRIVATE int sqlite3AbsInt32(int x){
3707637137
if( x>=0 ) return x;
3707737138
if( x==(int)0x80000000 ) return 0x7fffffff;
@@ -45614,11 +45675,11 @@
4561445675
sp.tv_sec = microseconds / 1000000;
4561545676
sp.tv_nsec = (microseconds % 1000000) * 1000;
4561645677
4561745678
/* Almost all modern unix systems support nanosleep(). But if you are
4561845679
** compiling for one of the rare exceptions, you can use
45619
- ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if
45680
+ ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if
4562045681
** usleep() is available) in order to bypass the use of nanosleep() */
4562145682
nanosleep(&sp, NULL);
4562245683
4562345684
UNUSED_PARAMETER(NotUsed);
4562445685
return microseconds;
@@ -56047,14 +56108,10 @@
5604756108
void *pStart, *pEnd; /* Bounds of global page cache memory */
5604856109
/* Above requires no mutex. Use mutex below for variable that follow. */
5604956110
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
5605056111
PgFreeslot *pFree; /* Free page blocks */
5605156112
int nFreeSlot; /* Number of unused pcache slots */
56052
- /* The following value requires a mutex to change. We skip the mutex on
56053
- ** reading because (1) most platforms read a 32-bit integer atomically and
56054
- ** (2) even if an incorrect value is read, no great harm is done since this
56055
- ** is really just an optimization. */
5605656113
int bUnderPressure; /* True if low on PAGECACHE memory */
5605756114
} pcache1_g;
5605856115
5605956116
/*
5606056117
** All code in this file should access the global structure above via the
@@ -56098,11 +56155,11 @@
5609856155
pcache1.szSlot = sz;
5609956156
pcache1.nSlot = pcache1.nFreeSlot = n;
5610056157
pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
5610156158
pcache1.pStart = pBuf;
5610256159
pcache1.pFree = 0;
56103
- pcache1.bUnderPressure = 0;
56160
+ AtomicStore(&pcache1.bUnderPressure,0);
5610456161
while( n-- ){
5610556162
p = (PgFreeslot*)pBuf;
5610656163
p->pNext = pcache1.pFree;
5610756164
pcache1.pFree = p;
5610856165
pBuf = (void*)&((char*)pBuf)[sz];
@@ -56166,11 +56223,11 @@
5616656223
sqlite3_mutex_enter(pcache1.mutex);
5616756224
p = (PgHdr1 *)pcache1.pFree;
5616856225
if( p ){
5616956226
pcache1.pFree = pcache1.pFree->pNext;
5617056227
pcache1.nFreeSlot--;
56171
- pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
56228
+ AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
5617256229
assert( pcache1.nFreeSlot>=0 );
5617356230
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
5617456231
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
5617556232
}
5617656233
sqlite3_mutex_leave(pcache1.mutex);
@@ -56205,11 +56262,11 @@
5620556262
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
5620656263
pSlot = (PgFreeslot*)p;
5620756264
pSlot->pNext = pcache1.pFree;
5620856265
pcache1.pFree = pSlot;
5620956266
pcache1.nFreeSlot++;
56210
- pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
56267
+ AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
5621156268
assert( pcache1.nFreeSlot<=pcache1.nSlot );
5621256269
sqlite3_mutex_leave(pcache1.mutex);
5621356270
}else{
5621456271
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
5621556272
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
@@ -56336,11 +56393,11 @@
5633656393
** allocating a new page cache entry in order to avoid stressing
5633756394
** the heap even further.
5633856395
*/
5633956396
static int pcache1UnderMemoryPressure(PCache1 *pCache){
5634056397
if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
56341
- return pcache1.bUnderPressure;
56398
+ return AtomicLoad(&pcache1.bUnderPressure);
5634256399
}else{
5634356400
return sqlite3HeapNearlyFull();
5634456401
}
5634556402
}
5634656403
@@ -66177,12 +66234,16 @@
6617766234
int iNext; /* Next slot in aIndex[] not yet returned */
6617866235
ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
6617966236
u32 *aPgno; /* Array of page numbers. */
6618066237
int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
6618166238
int iZero; /* Frame number associated with aPgno[0] */
66182
- } aSegment[1]; /* One for every 32KB page in the wal-index */
66239
+ } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */
6618366240
};
66241
+
66242
+/* Size (in bytes) of a WalIterator object suitable for N or fewer segments */
66243
+#define SZ_WALITERATOR(N) \
66244
+ (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment))
6618466245
6618566246
/*
6618666247
** Define the parameters of the hash tables in the wal-index file. There
6618766248
** is a hash-table following every HASHTABLE_NPAGE page numbers in the
6618866249
** wal-index.
@@ -67540,12 +67601,11 @@
6754067601
assert( pWal->ckptLock && pWal->hdr.mxFrame>0 );
6754167602
iLast = pWal->hdr.mxFrame;
6754267603
6754367604
/* Allocate space for the WalIterator object. */
6754467605
nSegment = walFramePage(iLast) + 1;
67545
- nByte = sizeof(WalIterator)
67546
- + (nSegment-1)*sizeof(struct WalSegment)
67606
+ nByte = SZ_WALITERATOR(nSegment)
6754767607
+ iLast*sizeof(ht_slot);
6754867608
p = (WalIterator *)sqlite3_malloc64(nByte
6754967609
+ sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
6755067610
);
6755167611
if( !p ){
@@ -86029,16 +86089,14 @@
8602986089
int nArg, /* Number of argument */
8603086090
const FuncDef *pFunc, /* The function to be invoked */
8603186091
int eCallCtx /* Calling context */
8603286092
){
8603386093
Vdbe *v = pParse->pVdbe;
86034
- int nByte;
8603586094
int addr;
8603686095
sqlite3_context *pCtx;
8603786096
assert( v );
86038
- nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*);
86039
- pCtx = sqlite3DbMallocRawNN(pParse->db, nByte);
86097
+ pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg));
8604086098
if( pCtx==0 ){
8604186099
assert( pParse->db->mallocFailed );
8604286100
freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
8604386101
return 0;
8604486102
}
@@ -91110,25 +91168,26 @@
9111091168
9111191169
preupdate.v = v;
9111291170
preupdate.pCsr = pCsr;
9111391171
preupdate.op = op;
9111491172
preupdate.iNewReg = iReg;
91115
- preupdate.keyinfo.db = db;
91116
- preupdate.keyinfo.enc = ENC(db);
91117
- preupdate.keyinfo.nKeyField = pTab->nCol;
91118
- preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
91173
+ preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
91174
+ preupdate.pKeyinfo->db = db;
91175
+ preupdate.pKeyinfo->enc = ENC(db);
91176
+ preupdate.pKeyinfo->nKeyField = pTab->nCol;
91177
+ preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder;
9111991178
preupdate.iKey1 = iKey1;
9112091179
preupdate.iKey2 = iKey2;
9112191180
preupdate.pTab = pTab;
9112291181
preupdate.iBlobWrite = iBlobWrite;
9112391182
9112491183
db->pPreUpdate = &preupdate;
9112591184
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
9112691185
db->pPreUpdate = 0;
9112791186
sqlite3DbFree(db, preupdate.aRecord);
91128
- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
91129
- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
91187
+ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked);
91188
+ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked);
9113091189
sqlite3VdbeMemRelease(&preupdate.oldipk);
9113191190
if( preupdate.aNew ){
9113291191
int i;
9113391192
for(i=0; i<pCsr->nField; i++){
9113491193
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
@@ -93363,11 +93422,11 @@
9336393422
nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
9336493423
aRec = sqlite3DbMallocRaw(db, nRec);
9336593424
if( !aRec ) goto preupdate_old_out;
9336693425
rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
9336793426
if( rc==SQLITE_OK ){
93368
- p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
93427
+ p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec);
9336993428
if( !p->pUnpacked ) rc = SQLITE_NOMEM;
9337093429
}
9337193430
if( rc!=SQLITE_OK ){
9337293431
sqlite3DbFree(db, aRec);
9337393432
goto preupdate_old_out;
@@ -93428,11 +93487,11 @@
9342893487
#ifdef SQLITE_ENABLE_API_ARMOR
9342993488
p = db!=0 ? db->pPreUpdate : 0;
9343093489
#else
9343193490
p = db->pPreUpdate;
9343293491
#endif
93433
- return (p ? p->keyinfo.nKeyField : 0);
93492
+ return (p ? p->pKeyinfo->nKeyField : 0);
9343493493
}
9343593494
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
9343693495
9343793496
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
9343893497
/*
@@ -93511,11 +93570,11 @@
9351193570
UnpackedRecord *pUnpack = p->pNewUnpacked;
9351293571
if( !pUnpack ){
9351393572
Mem *pData = &p->v->aMem[p->iNewReg];
9351493573
rc = ExpandBlob(pData);
9351593574
if( rc!=SQLITE_OK ) goto preupdate_new_out;
93516
- pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
93575
+ pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z);
9351793576
if( !pUnpack ){
9351893577
rc = SQLITE_NOMEM;
9351993578
goto preupdate_new_out;
9352093579
}
9352193580
p->pNewUnpacked = pUnpack;
@@ -94305,13 +94364,13 @@
9430594364
*/
9430694365
Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
9430794366
9430894367
i64 nByte;
9430994368
VdbeCursor *pCx = 0;
94310
- nByte =
94311
- ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
94312
- (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
94369
+ nByte = SZ_VDBECURSOR(nField);
94370
+ assert( ROUND8(nByte)==nByte );
94371
+ if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize();
9431394372
9431494373
assert( iCur>=0 && iCur<p->nCursor );
9431594374
if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
9431694375
sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]);
9431794376
p->apCsr[iCur] = 0;
@@ -94340,12 +94399,12 @@
9434094399
memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
9434194400
pCx->eCurType = eCurType;
9434294401
pCx->nField = nField;
9434394402
pCx->aOffset = &pCx->aType[nField];
9434494403
if( eCurType==CURTYPE_BTREE ){
94345
- pCx->uc.pCursor = (BtCursor*)
94346
- &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
94404
+ assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) );
94405
+ pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)];
9434794406
sqlite3BtreeCursorZero(pCx->uc.pCursor);
9434894407
}
9434994408
return pCx;
9435094409
}
9435194410
@@ -100083,11 +100142,11 @@
100083100142
pCrsr = pC->uc.pCursor;
100084100143
100085100144
/* The OP_RowData opcodes always follow OP_NotExists or
100086100145
** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
100087100146
** that might invalidate the cursor.
100088
- ** If this where not the case, on of the following assert()s
100147
+ ** If this were not the case, one of the following assert()s
100089100148
** would fail. Should this ever change (because of changes in the code
100090100149
** generator) then the fix would be to insert a call to
100091100150
** sqlite3VdbeCursorMoveto().
100092100151
*/
100093100152
assert( pC->deferredMoveto==0 );
@@ -101732,11 +101791,11 @@
101732101791
** cell in which to store the accumulation. Be careful that the memory
101733101792
** cell is 8-byte aligned, even on platforms where a pointer is 32-bits.
101734101793
**
101735101794
** Note: We could avoid this by using a regular memory cell from aMem[] for
101736101795
** the accumulator, instead of allocating one here. */
101737
- nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) );
101796
+ nAlloc = ROUND8P( SZ_CONTEXT(n) );
101738101797
pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem));
101739101798
if( pCtx==0 ) goto no_mem;
101740101799
pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc);
101741101800
assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) );
101742101801
@@ -103390,10 +103449,11 @@
103390103449
int iCol; /* Index of zColumn in row-record */
103391103450
int rc = SQLITE_OK;
103392103451
char *zErr = 0;
103393103452
Table *pTab;
103394103453
Incrblob *pBlob = 0;
103454
+ int iDb;
103395103455
Parse sParse;
103396103456
103397103457
#ifdef SQLITE_ENABLE_API_ARMOR
103398103458
if( ppBlob==0 ){
103399103459
return SQLITE_MISUSE_BKPT;
@@ -103435,11 +103495,14 @@
103435103495
if( pTab && IsView(pTab) ){
103436103496
pTab = 0;
103437103497
sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
103438103498
}
103439103499
#endif
103440
- if( !pTab ){
103500
+ if( pTab==0
103501
+ || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 &&
103502
+ sqlite3OpenTempDatabase(&sParse))
103503
+ ){
103441103504
if( sParse.zErrMsg ){
103442103505
sqlite3DbFree(db, zErr);
103443103506
zErr = sParse.zErrMsg;
103444103507
sParse.zErrMsg = 0;
103445103508
}
@@ -103446,11 +103509,11 @@
103446103509
rc = SQLITE_ERROR;
103447103510
sqlite3BtreeLeaveAll(db);
103448103511
goto blob_open_out;
103449103512
}
103450103513
pBlob->pTab = pTab;
103451
- pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
103514
+ pBlob->zDb = db->aDb[iDb].zDbSName;
103452103515
103453103516
/* Now search pTab for the exact column. */
103454103517
iCol = sqlite3ColumnIndex(pTab, zColumn);
103455103518
if( iCol<0 ){
103456103519
sqlite3DbFree(db, zErr);
@@ -103530,11 +103593,10 @@
103530103593
{OP_Column, 0, 0, 1}, /* 3 */
103531103594
{OP_ResultRow, 1, 0, 0}, /* 4 */
103532103595
{OP_Halt, 0, 0, 0}, /* 5 */
103533103596
};
103534103597
Vdbe *v = (Vdbe *)pBlob->pStmt;
103535
- int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
103536103598
VdbeOp *aOp;
103537103599
103538103600
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
103539103601
pTab->pSchema->schema_cookie,
103540103602
pTab->pSchema->iGeneration);
@@ -104108,12 +104170,15 @@
104108104170
u8 bUsePMA; /* True if one or more PMAs created */
104109104171
u8 bUseThreads; /* True to use background threads */
104110104172
u8 iPrev; /* Previous thread used to flush PMA */
104111104173
u8 nTask; /* Size of aTask[] array */
104112104174
u8 typeMask;
104113
- SortSubtask aTask[1]; /* One or more subtasks */
104175
+ SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */
104114104176
};
104177
+
104178
+/* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */
104179
+#define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask))
104115104180
104116104181
#define SORTER_TYPE_INTEGER 0x01
104117104182
#define SORTER_TYPE_TEXT 0x02
104118104183
104119104184
/*
@@ -104742,12 +104807,12 @@
104742104807
assert( pCsr->pKeyInfo );
104743104808
assert( !pCsr->isEphemeral );
104744104809
assert( pCsr->eCurType==CURTYPE_SORTER );
104745104810
assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
104746104811
< 0x7fffffff );
104747
- szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
104748
- sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
104812
+ szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1);
104813
+ sz = SZ_VDBESORTER(nWorker+1);
104749104814
104750104815
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
104751104816
pCsr->uc.pSorter = pSorter;
104752104817
if( pSorter==0 ){
104753104818
rc = SQLITE_NOMEM_BKPT;
@@ -105207,10 +105272,14 @@
105207105272
}
105208105273
105209105274
p->u.pNext = 0;
105210105275
for(i=0; aSlot[i]; i++){
105211105276
p = vdbeSorterMerge(pTask, p, aSlot[i]);
105277
+ /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use
105278
+ ** | up all 64 aSlots[] with only a 64-bit address space.
105279
+ ** v */
105280
+ assert( i<ArraySize(aSlot) );
105212105281
aSlot[i] = 0;
105213105282
}
105214105283
aSlot[i] = p;
105215105284
p = pNext;
105216105285
}
@@ -109981,32 +110050,34 @@
109981110050
Table *pTab, /* The table being referenced, or NULL */
109982110051
int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
109983110052
Expr *pExpr, /* Expression to resolve. May be NULL. */
109984110053
ExprList *pList /* Expression list to resolve. May be NULL. */
109985110054
){
109986
- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
110055
+ SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
109987110056
NameContext sNC; /* Name context for pParse->pNewTable */
109988110057
int rc;
110058
+ u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
109989110059
109990110060
assert( type==0 || pTab!=0 );
109991110061
assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
109992110062
|| type==NC_GenCol || pTab==0 );
109993110063
memset(&sNC, 0, sizeof(sNC));
109994
- memset(&sSrc, 0, sizeof(sSrc));
110064
+ pSrc = (SrcList*)srcSpace;
110065
+ memset(pSrc, 0, SZ_SRCLIST_1);
109995110066
if( pTab ){
109996
- sSrc.nSrc = 1;
109997
- sSrc.a[0].zName = pTab->zName;
109998
- sSrc.a[0].pSTab = pTab;
109999
- sSrc.a[0].iCursor = -1;
110067
+ pSrc->nSrc = 1;
110068
+ pSrc->a[0].zName = pTab->zName;
110069
+ pSrc->a[0].pSTab = pTab;
110070
+ pSrc->a[0].iCursor = -1;
110000110071
if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
110001110072
/* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
110002110073
** schema elements */
110003110074
type |= NC_FromDDL;
110004110075
}
110005110076
}
110006110077
sNC.pParse = pParse;
110007
- sNC.pSrcList = &sSrc;
110078
+ sNC.pSrcList = pSrc;
110008110079
sNC.ncFlags = type | NC_IsDDL;
110009110080
if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
110010110081
if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
110011110082
return rc;
110012110083
}
@@ -111751,11 +111822,11 @@
111751111822
*/
111752111823
#ifndef SQLITE_OMIT_CTE
111753111824
SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
111754111825
With *pRet = 0;
111755111826
if( p ){
111756
- sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
111827
+ sqlite3_int64 nByte = SZ_WITH(p->nCte);
111757111828
pRet = sqlite3DbMallocZero(db, nByte);
111758111829
if( pRet ){
111759111830
int i;
111760111831
pRet->nCte = p->nCte;
111761111832
for(i=0; i<p->nCte; i++){
@@ -111878,15 +111949,13 @@
111878111949
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
111879111950
|| !defined(SQLITE_OMIT_SUBQUERY)
111880111951
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
111881111952
SrcList *pNew;
111882111953
int i;
111883
- int nByte;
111884111954
assert( db!=0 );
111885111955
if( p==0 ) return 0;
111886
- nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
111887
- pNew = sqlite3DbMallocRawNN(db, nByte );
111956
+ pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) );
111888111957
if( pNew==0 ) return 0;
111889111958
pNew->nSrc = pNew->nAlloc = p->nSrc;
111890111959
for(i=0; i<p->nSrc; i++){
111891111960
SrcItem *pNewItem = &pNew->a[i];
111892111961
const SrcItem *pOldItem = &p->a[i];
@@ -111944,11 +112013,11 @@
111944112013
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
111945112014
IdList *pNew;
111946112015
int i;
111947112016
assert( db!=0 );
111948112017
if( p==0 ) return 0;
111949
- pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) );
112018
+ pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId));
111950112019
if( pNew==0 ) return 0;
111951112020
pNew->nId = p->nId;
111952112021
for(i=0; i<p->nId; i++){
111953112022
struct IdList_item *pNewItem = &pNew->a[i];
111954112023
const struct IdList_item *pOldItem = &p->a[i];
@@ -112028,11 +112097,11 @@
112028112097
Expr *pExpr /* Expression to be appended. Might be NULL */
112029112098
){
112030112099
struct ExprList_item *pItem;
112031112100
ExprList *pList;
112032112101
112033
- pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 );
112102
+ pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4));
112034112103
if( pList==0 ){
112035112104
sqlite3ExprDelete(db, pExpr);
112036112105
return 0;
112037112106
}
112038112107
pList->nAlloc = 4;
@@ -112048,12 +112117,11 @@
112048112117
Expr *pExpr /* Expression to be appended. Might be NULL */
112049112118
){
112050112119
struct ExprList_item *pItem;
112051112120
ExprList *pNew;
112052112121
pList->nAlloc *= 2;
112053
- pNew = sqlite3DbRealloc(db, pList,
112054
- sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0]));
112122
+ pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc));
112055112123
if( pNew==0 ){
112056112124
sqlite3ExprListDelete(db, pList);
112057112125
sqlite3ExprDelete(db, pExpr);
112058112126
return 0;
112059112127
}else{
@@ -114685,11 +114753,11 @@
114685114753
return -1; /* Not found */
114686114754
}
114687114755
114688114756
114689114757
/*
114690
-** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
114758
+** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This
114691114759
** function checks the Parse.pIdxPartExpr list to see if this column
114692114760
** can be replaced with a constant value. If so, it generates code to
114693114761
** put the constant value in a register (ideally, but not necessarily,
114694114762
** register iTarget) and returns the register number.
114695114763
**
@@ -118531,10 +118599,11 @@
118531118599
sqlite3 *db, /* Database handle */
118532118600
const char *zSql, /* SQL to parse */
118533118601
int bTemp /* True if SQL is from temp schema */
118534118602
){
118535118603
int rc;
118604
+ u64 flags;
118536118605
118537118606
sqlite3ParseObjectInit(p, db);
118538118607
if( zSql==0 ){
118539118608
return SQLITE_NOMEM;
118540118609
}
@@ -118549,11 +118618,15 @@
118549118618
db->init.iDb = (u8)iDb;
118550118619
}
118551118620
p->eParseMode = PARSE_MODE_RENAME;
118552118621
p->db = db;
118553118622
p->nQueryLoop = 1;
118623
+ flags = db->flags;
118624
+ testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 );
118625
+ db->flags |= SQLITE_Comments;
118554118626
rc = sqlite3RunParser(p, zSql);
118627
+ db->flags = flags;
118555118628
if( db->mallocFailed ) rc = SQLITE_NOMEM;
118556118629
if( rc==SQLITE_OK
118557118630
&& NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
118558118631
){
118559118632
rc = SQLITE_CORRUPT_BKPT;
@@ -119445,11 +119518,11 @@
119445119518
int rc;
119446119519
Parse sParse;
119447119520
u64 flags = db->flags;
119448119521
if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
119449119522
rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
119450
- db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
119523
+ db->flags = flags;
119451119524
if( rc==SQLITE_OK ){
119452119525
if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
119453119526
NameContext sNC;
119454119527
memset(&sNC, 0, sizeof(sNC));
119455119528
sNC.pParse = &sParse;
@@ -126268,11 +126341,11 @@
126268126341
"columns in the referenced table");
126269126342
goto fk_end;
126270126343
}else{
126271126344
nCol = pFromCol->nExpr;
126272126345
}
126273
- nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
126346
+ nByte = SZ_FKEY(nCol) + pTo->n + 1;
126274126347
if( pToCol ){
126275126348
for(i=0; i<pToCol->nExpr; i++){
126276126349
nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
126277126350
}
126278126351
}
@@ -127327,16 +127400,15 @@
127327127400
*/
127328127401
SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
127329127402
sqlite3 *db = pParse->db;
127330127403
int i;
127331127404
if( pList==0 ){
127332
- pList = sqlite3DbMallocZero(db, sizeof(IdList) );
127405
+ pList = sqlite3DbMallocZero(db, SZ_IDLIST(1));
127333127406
if( pList==0 ) return 0;
127334127407
}else{
127335127408
IdList *pNew;
127336
- pNew = sqlite3DbRealloc(db, pList,
127337
- sizeof(IdList) + pList->nId*sizeof(pList->a));
127409
+ pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1));
127338127410
if( pNew==0 ){
127339127411
sqlite3IdListDelete(db, pList);
127340127412
return 0;
127341127413
}
127342127414
pList = pNew;
@@ -127431,12 +127503,11 @@
127431127503
sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d",
127432127504
SQLITE_MAX_SRCLIST);
127433127505
return 0;
127434127506
}
127435127507
if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST;
127436
- pNew = sqlite3DbRealloc(db, pSrc,
127437
- sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) );
127508
+ pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc));
127438127509
if( pNew==0 ){
127439127510
assert( db->mallocFailed );
127440127511
return 0;
127441127512
}
127442127513
pSrc = pNew;
@@ -127507,11 +127578,11 @@
127507127578
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
127508127579
assert( pParse!=0 );
127509127580
assert( pParse->db!=0 );
127510127581
db = pParse->db;
127511127582
if( pList==0 ){
127512
- pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) );
127583
+ pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1));
127513127584
if( pList==0 ) return 0;
127514127585
pList->nAlloc = 1;
127515127586
pList->nSrc = 1;
127516127587
memset(&pList->a[0], 0, sizeof(pList->a[0]));
127517127588
pList->a[0].iCursor = -1;
@@ -128393,14 +128464,13 @@
128393128464
}
128394128465
}
128395128466
}
128396128467
128397128468
if( pWith ){
128398
- sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
128399
- pNew = sqlite3DbRealloc(db, pWith, nByte);
128469
+ pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1));
128400128470
}else{
128401
- pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
128471
+ pNew = sqlite3DbMallocZero(db, SZ_WITH(1));
128402128472
}
128403128473
assert( (pNew!=0 && zName!=0) || db->mallocFailed );
128404128474
128405128475
if( db->mallocFailed ){
128406128476
sqlite3CteDelete(db, pCte);
@@ -131933,11 +132003,11 @@
131933132003
**
131934132004
** The SUM() function follows the (broken) SQL standard which means
131935132005
** that it returns NULL if it sums over no inputs. TOTAL returns
131936132006
** 0.0 in that case. In addition, TOTAL always returns a float where
131937132007
** SUM might return an integer if it never encounters a floating point
131938
-** value. TOTAL never fails, but SUM might through an exception if
132008
+** value. TOTAL never fails, but SUM might throw an exception if
131939132009
** it overflows an integer.
131940132010
*/
131941132011
static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
131942132012
SumCtx *p;
131943132013
int type;
@@ -139748,52 +139818,52 @@
139748139818
/* 11 */ "notnull",
139749139819
/* 12 */ "dflt_value",
139750139820
/* 13 */ "pk",
139751139821
/* 14 */ "hidden",
139752139822
/* table_info reuses 8 */
139753
- /* 15 */ "schema", /* Used by: table_list */
139754
- /* 16 */ "name",
139823
+ /* 15 */ "name", /* Used by: function_list */
139824
+ /* 16 */ "builtin",
139755139825
/* 17 */ "type",
139756
- /* 18 */ "ncol",
139757
- /* 19 */ "wr",
139758
- /* 20 */ "strict",
139759
- /* 21 */ "seqno", /* Used by: index_xinfo */
139760
- /* 22 */ "cid",
139761
- /* 23 */ "name",
139762
- /* 24 */ "desc",
139763
- /* 25 */ "coll",
139764
- /* 26 */ "key",
139765
- /* 27 */ "name", /* Used by: function_list */
139766
- /* 28 */ "builtin",
139767
- /* 29 */ "type",
139768
- /* 30 */ "enc",
139769
- /* 31 */ "narg",
139770
- /* 32 */ "flags",
139771
- /* 33 */ "tbl", /* Used by: stats */
139772
- /* 34 */ "idx",
139773
- /* 35 */ "wdth",
139774
- /* 36 */ "hght",
139775
- /* 37 */ "flgs",
139776
- /* 38 */ "seq", /* Used by: index_list */
139777
- /* 39 */ "name",
139778
- /* 40 */ "unique",
139779
- /* 41 */ "origin",
139780
- /* 42 */ "partial",
139826
+ /* 18 */ "enc",
139827
+ /* 19 */ "narg",
139828
+ /* 20 */ "flags",
139829
+ /* 21 */ "schema", /* Used by: table_list */
139830
+ /* 22 */ "name",
139831
+ /* 23 */ "type",
139832
+ /* 24 */ "ncol",
139833
+ /* 25 */ "wr",
139834
+ /* 26 */ "strict",
139835
+ /* 27 */ "seqno", /* Used by: index_xinfo */
139836
+ /* 28 */ "cid",
139837
+ /* 29 */ "name",
139838
+ /* 30 */ "desc",
139839
+ /* 31 */ "coll",
139840
+ /* 32 */ "key",
139841
+ /* 33 */ "seq", /* Used by: index_list */
139842
+ /* 34 */ "name",
139843
+ /* 35 */ "unique",
139844
+ /* 36 */ "origin",
139845
+ /* 37 */ "partial",
139846
+ /* 38 */ "tbl", /* Used by: stats */
139847
+ /* 39 */ "idx",
139848
+ /* 40 */ "wdth",
139849
+ /* 41 */ "hght",
139850
+ /* 42 */ "flgs",
139781139851
/* 43 */ "table", /* Used by: foreign_key_check */
139782139852
/* 44 */ "rowid",
139783139853
/* 45 */ "parent",
139784139854
/* 46 */ "fkid",
139785
- /* index_info reuses 21 */
139786
- /* 47 */ "seq", /* Used by: database_list */
139787
- /* 48 */ "name",
139788
- /* 49 */ "file",
139789
- /* 50 */ "busy", /* Used by: wal_checkpoint */
139790
- /* 51 */ "log",
139791
- /* 52 */ "checkpointed",
139792
- /* collation_list reuses 38 */
139855
+ /* 47 */ "busy", /* Used by: wal_checkpoint */
139856
+ /* 48 */ "log",
139857
+ /* 49 */ "checkpointed",
139858
+ /* 50 */ "seq", /* Used by: database_list */
139859
+ /* 51 */ "name",
139860
+ /* 52 */ "file",
139861
+ /* index_info reuses 27 */
139793139862
/* 53 */ "database", /* Used by: lock_status */
139794139863
/* 54 */ "status",
139864
+ /* collation_list reuses 33 */
139795139865
/* 55 */ "cache_size", /* Used by: default_cache_size */
139796139866
/* module_list pragma_list reuses 9 */
139797139867
/* 56 */ "timeout", /* Used by: busy_timeout */
139798139868
};
139799139869
@@ -139882,11 +139952,11 @@
139882139952
#endif
139883139953
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139884139954
{/* zName: */ "collation_list",
139885139955
/* ePragTyp: */ PragTyp_COLLATION_LIST,
139886139956
/* ePragFlg: */ PragFlg_Result0,
139887
- /* ColNames: */ 38, 2,
139957
+ /* ColNames: */ 33, 2,
139888139958
/* iArg: */ 0 },
139889139959
#endif
139890139960
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
139891139961
{/* zName: */ "compile_options",
139892139962
/* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
@@ -139917,11 +139987,11 @@
139917139987
#endif
139918139988
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139919139989
{/* zName: */ "database_list",
139920139990
/* ePragTyp: */ PragTyp_DATABASE_LIST,
139921139991
/* ePragFlg: */ PragFlg_Result0,
139922
- /* ColNames: */ 47, 3,
139992
+ /* ColNames: */ 50, 3,
139923139993
/* iArg: */ 0 },
139924139994
#endif
139925139995
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
139926139996
{/* zName: */ "default_cache_size",
139927139997
/* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
@@ -139997,11 +140067,11 @@
139997140067
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139998140068
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
139999140069
{/* zName: */ "function_list",
140000140070
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
140001140071
/* ePragFlg: */ PragFlg_Result0,
140002
- /* ColNames: */ 27, 6,
140072
+ /* ColNames: */ 15, 6,
140003140073
/* iArg: */ 0 },
140004140074
#endif
140005140075
#endif
140006140076
{/* zName: */ "hard_heap_limit",
140007140077
/* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT,
@@ -140026,21 +140096,21 @@
140026140096
#endif
140027140097
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
140028140098
{/* zName: */ "index_info",
140029140099
/* ePragTyp: */ PragTyp_INDEX_INFO,
140030140100
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140031
- /* ColNames: */ 21, 3,
140101
+ /* ColNames: */ 27, 3,
140032140102
/* iArg: */ 0 },
140033140103
{/* zName: */ "index_list",
140034140104
/* ePragTyp: */ PragTyp_INDEX_LIST,
140035140105
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140036
- /* ColNames: */ 38, 5,
140106
+ /* ColNames: */ 33, 5,
140037140107
/* iArg: */ 0 },
140038140108
{/* zName: */ "index_xinfo",
140039140109
/* ePragTyp: */ PragTyp_INDEX_INFO,
140040140110
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140041
- /* ColNames: */ 21, 6,
140111
+ /* ColNames: */ 27, 6,
140042140112
/* iArg: */ 1 },
140043140113
#endif
140044140114
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
140045140115
{/* zName: */ "integrity_check",
140046140116
/* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
@@ -140215,11 +140285,11 @@
140215140285
#endif
140216140286
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
140217140287
{/* zName: */ "stats",
140218140288
/* ePragTyp: */ PragTyp_STATS,
140219140289
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
140220
- /* ColNames: */ 33, 5,
140290
+ /* ColNames: */ 38, 5,
140221140291
/* iArg: */ 0 },
140222140292
#endif
140223140293
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
140224140294
{/* zName: */ "synchronous",
140225140295
/* ePragTyp: */ PragTyp_SYNCHRONOUS,
@@ -140234,11 +140304,11 @@
140234140304
/* ColNames: */ 8, 6,
140235140305
/* iArg: */ 0 },
140236140306
{/* zName: */ "table_list",
140237140307
/* ePragTyp: */ PragTyp_TABLE_LIST,
140238140308
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1,
140239
- /* ColNames: */ 15, 6,
140309
+ /* ColNames: */ 21, 6,
140240140310
/* iArg: */ 0 },
140241140311
{/* zName: */ "table_xinfo",
140242140312
/* ePragTyp: */ PragTyp_TABLE_INFO,
140243140313
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140244140314
/* ColNames: */ 8, 7,
@@ -140311,11 +140381,11 @@
140311140381
/* ColNames: */ 0, 0,
140312140382
/* iArg: */ 0 },
140313140383
{/* zName: */ "wal_checkpoint",
140314140384
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
140315140385
/* ePragFlg: */ PragFlg_NeedSchema,
140316
- /* ColNames: */ 50, 3,
140386
+ /* ColNames: */ 47, 3,
140317140387
/* iArg: */ 0 },
140318140388
#endif
140319140389
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
140320140390
{/* zName: */ "writable_schema",
140321140391
/* ePragTyp: */ PragTyp_FLAG,
@@ -140333,11 +140403,11 @@
140333140403
** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
140334140404
** will be run with an analysis_limit set to the lessor of the value of
140335140405
** the following macro or to the actual analysis_limit if it is non-zero,
140336140406
** in order to prevent PRAGMA optimize from running for too long.
140337140407
**
140338
-** The value of 2000 is chosen emperically so that the worst-case run-time
140408
+** The value of 2000 is chosen empirically so that the worst-case run-time
140339140409
** for PRAGMA optimize does not exceed 100 milliseconds against a variety
140340140410
** of test databases on a RaspberryPI-4 compiled using -Os and without
140341140411
** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
140342140412
** this paragraph, "worst-case" means that ANALYZE ends up being
140343140413
** run on every table in the database. The worst case typically only
@@ -144622,11 +144692,11 @@
144622144692
pNew->iOffset = 0;
144623144693
pNew->selId = ++pParse->nSelect;
144624144694
pNew->addrOpenEphm[0] = -1;
144625144695
pNew->addrOpenEphm[1] = -1;
144626144696
pNew->nSelectRow = 0;
144627
- if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc));
144697
+ if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
144628144698
pNew->pSrc = pSrc;
144629144699
pNew->pWhere = pWhere;
144630144700
pNew->pGroupBy = pGroupBy;
144631144701
pNew->pHaving = pHaving;
144632144702
pNew->pOrderBy = pOrderBy;
@@ -146005,20 +146075,20 @@
146005146075
/*
146006146076
** Allocate a KeyInfo object sufficient for an index of N key columns and
146007146077
** X extra columns.
146008146078
*/
146009146079
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
146010
- int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
146011
- KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
146080
+ int nExtra = (N+X)*(sizeof(CollSeq*)+1);
146081
+ KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
146012146082
if( p ){
146013146083
p->aSortFlags = (u8*)&p->aColl[N+X];
146014146084
p->nKeyField = (u16)N;
146015146085
p->nAllField = (u16)(N+X);
146016146086
p->enc = ENC(db);
146017146087
p->db = db;
146018146088
p->nRef = 1;
146019
- memset(&p[1], 0, nExtra);
146089
+ memset(p->aColl, 0, nExtra);
146020146090
}else{
146021146091
return (KeyInfo*)sqlite3OomFault(db);
146022146092
}
146023146093
return p;
146024146094
}
@@ -150530,11 +150600,11 @@
150530150600
}
150531150601
pTabList = p->pSrc;
150532150602
pEList = p->pEList;
150533150603
if( pParse->pWith && (p->selFlags & SF_View) ){
150534150604
if( p->pWith==0 ){
150535
- p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With));
150605
+ p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) );
150536150606
if( p->pWith==0 ){
150537150607
return WRC_Abort;
150538150608
}
150539150609
}
150540150610
p->pWith->bView = 1;
@@ -151669,10 +151739,11 @@
151669151739
** * The subquery is a UNION ALL of two or more terms
151670151740
** * The subquery does not have a LIMIT clause
151671151741
** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
151672151742
** * The outer query is a simple count(*) with no WHERE clause or other
151673151743
** extraneous syntax.
151744
+** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10)
151674151745
**
151675151746
** Return TRUE if the optimization is undertaken.
151676151747
*/
151677151748
static int countOfViewOptimization(Parse *pParse, Select *p){
151678151749
Select *pSub, *pPrior;
@@ -151701,11 +151772,15 @@
151701151772
if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */
151702151773
do{
151703151774
if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
151704151775
if( pSub->pWhere ) return 0; /* No WHERE clause */
151705151776
if( pSub->pLimit ) return 0; /* No LIMIT clause */
151706
- if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
151777
+ if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){
151778
+ testcase( pSub->selFlags & SF_Aggregate );
151779
+ testcase( pSub->selFlags & SF_Distinct );
151780
+ return 0; /* Not an aggregate nor DISTINCT */
151781
+ }
151707151782
assert( pSub->pHaving==0 ); /* Due to the previous */
151708151783
pSub = pSub->pPrior; /* Repeat over compound */
151709151784
}while( pSub );
151710151785
151711151786
/* If we reach this point then it is OK to perform the transformation */
@@ -151713,11 +151788,11 @@
151713151788
db = pParse->db;
151714151789
pCount = pExpr;
151715151790
pExpr = 0;
151716151791
pSub = sqlite3SubqueryDetach(db, pFrom);
151717151792
sqlite3SrcListDelete(db, p->pSrc);
151718
- p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
151793
+ p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
151719151794
while( pSub ){
151720151795
Expr *pTerm;
151721151796
pPrior = pSub->pPrior;
151722151797
pSub->pPrior = 0;
151723151798
pSub->pNext = 0;
@@ -154504,11 +154579,12 @@
154504154579
Vdbe *v = pParse->pVdbe;
154505154580
sqlite3 *db = pParse->db;
154506154581
ExprList *pNew;
154507154582
Returning *pReturning;
154508154583
Select sSelect;
154509
- SrcList sFrom;
154584
+ SrcList *pFrom;
154585
+ u8 fromSpace[SZ_SRCLIST_1];
154510154586
154511154587
assert( v!=0 );
154512154588
if( !pParse->bReturning ){
154513154589
/* This RETURNING trigger must be for a different statement as
154514154590
** this statement lacks a RETURNING clause. */
@@ -154520,17 +154596,18 @@
154520154596
if( pTrigger != &(pReturning->retTrig) ){
154521154597
/* This RETURNING trigger is for a different statement */
154522154598
return;
154523154599
}
154524154600
memset(&sSelect, 0, sizeof(sSelect));
154525
- memset(&sFrom, 0, sizeof(sFrom));
154601
+ pFrom = (SrcList*)fromSpace;
154602
+ memset(pFrom, 0, SZ_SRCLIST_1);
154526154603
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
154527
- sSelect.pSrc = &sFrom;
154528
- sFrom.nSrc = 1;
154529
- sFrom.a[0].pSTab = pTab;
154530
- sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */
154531
- sFrom.a[0].iCursor = -1;
154604
+ sSelect.pSrc = pFrom;
154605
+ pFrom->nSrc = 1;
154606
+ pFrom->a[0].pSTab = pTab;
154607
+ pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
154608
+ pFrom->a[0].iCursor = -1;
154532154609
sqlite3SelectPrep(pParse, &sSelect, 0);
154533154610
if( pParse->nErr==0 ){
154534154611
assert( db->mallocFailed==0 );
154535154612
sqlite3GenerateColumnNames(pParse, &sSelect);
154536154613
}
@@ -156927,11 +157004,11 @@
156927157004
saved_flags = db->flags;
156928157005
saved_mDbFlags = db->mDbFlags;
156929157006
saved_nChange = db->nChange;
156930157007
saved_nTotalChange = db->nTotalChange;
156931157008
saved_mTrace = db->mTrace;
156932
- db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
157009
+ db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;
156933157010
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
156934157011
db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
156935157012
| SQLITE_Defensive | SQLITE_CountRows);
156936157013
db->mTrace = 0;
156937157014
@@ -159056,13 +159133,18 @@
159056159133
WhereLoop *pLoops; /* List of all WhereLoop objects */
159057159134
WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
159058159135
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
159059159136
WhereClause sWC; /* Decomposition of the WHERE clause */
159060159137
WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
159061
- WhereLevel a[1]; /* Information about each nest loop in WHERE */
159138
+ WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */
159062159139
};
159063159140
159141
+/*
159142
+** The size (in bytes) of a WhereInfo object that holds N WhereLevels.
159143
+*/
159144
+#define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel))
159145
+
159064159146
/*
159065159147
** Private interfaces - callable only by other where.c routines.
159066159148
**
159067159149
** where.c:
159068159150
*/
@@ -161509,12 +161591,11 @@
161509161591
*/
161510161592
if( pWInfo->nLevel>1 ){
161511161593
int nNotReady; /* The number of notReady tables */
161512161594
SrcItem *origSrc; /* Original list of tables */
161513161595
nNotReady = pWInfo->nLevel - iLevel - 1;
161514
- pOrTab = sqlite3DbMallocRawNN(db,
161515
- sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
161596
+ pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1));
161516161597
if( pOrTab==0 ) return notReady;
161517161598
pOrTab->nAlloc = (u8)(nNotReady + 1);
161518161599
pOrTab->nSrc = pOrTab->nAlloc;
161519161600
memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
161520161601
origSrc = pWInfo->pTabList->a;
@@ -162053,11 +162134,12 @@
162053162134
Expr *pSubWhere = 0;
162054162135
WhereClause *pWC = &pWInfo->sWC;
162055162136
WhereInfo *pSubWInfo;
162056162137
WhereLoop *pLoop = pLevel->pWLoop;
162057162138
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
162058
- SrcList sFrom;
162139
+ SrcList *pFrom;
162140
+ u8 fromSpace[SZ_SRCLIST_1];
162059162141
Bitmask mAll = 0;
162060162142
int k;
162061162143
162062162144
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
162063162145
sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
@@ -162097,17 +162179,18 @@
162097162179
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
162098162180
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
162099162181
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
162100162182
}
162101162183
}
162102
- sFrom.nSrc = 1;
162103
- sFrom.nAlloc = 1;
162104
- memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem));
162105
- sFrom.a[0].fg.jointype = 0;
162184
+ pFrom = (SrcList*)fromSpace;
162185
+ pFrom->nSrc = 1;
162186
+ pFrom->nAlloc = 1;
162187
+ memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
162188
+ pFrom->a[0].fg.jointype = 0;
162106162189
assert( pParse->withinRJSubrtn < 100 );
162107162190
pParse->withinRJSubrtn++;
162108
- pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
162191
+ pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0,
162109162192
WHERE_RIGHT_JOIN, 0);
162110162193
if( pSubWInfo ){
162111162194
int iCur = pLevel->iTabCur;
162112162195
int r = ++pParse->nMem;
162113162196
int nPk;
@@ -164091,15 +164174,20 @@
164091164174
WhereClause *pWC; /* The Where clause being analyzed */
164092164175
Parse *pParse; /* The parsing context */
164093164176
int eDistinct; /* Value to return from sqlite3_vtab_distinct() */
164094164177
u32 mIn; /* Mask of terms that are <col> IN (...) */
164095164178
u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */
164096
- sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST
164097
- ** because extra space is allocated to hold up
164098
- ** to nTerm such values */
164179
+ sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST
164180
+ ** Extra space is allocated to hold up
164181
+ ** to nTerm such values */
164099164182
};
164100164183
164184
+/* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as
164185
+** many as N constraints */
164186
+#define SZ_HIDDENINDEXINFO(N) \
164187
+ (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*))
164188
+
164101164189
/* Forward declaration of methods */
164102164190
static int whereLoopResize(sqlite3*, WhereLoop*, int);
164103164191
164104164192
/*
164105164193
** Return the estimated number of output rows from a WHERE clause
@@ -165573,12 +165661,12 @@
165573165661
165574165662
/* Allocate the sqlite3_index_info structure
165575165663
*/
165576165664
pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
165577165665
+ (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
165578
- + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)
165579
- + sizeof(sqlite3_value*)*nTerm );
165666
+ + sizeof(*pIdxOrderBy)*nOrderBy
165667
+ + SZ_HIDDENINDEXINFO(nTerm) );
165580165668
if( pIdxInfo==0 ){
165581165669
sqlite3ErrorMsg(pParse, "out of memory");
165582165670
return 0;
165583165671
}
165584165672
pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
@@ -170768,14 +170856,11 @@
170768170856
** struct, the contents of WhereInfo.a[], the WhereClause structure
170769170857
** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
170770170858
** field (type Bitmask) it must be aligned on an 8-byte boundary on
170771170859
** some architectures. Hence the ROUND8() below.
170772170860
*/
170773
- nByteWInfo = ROUND8P(sizeof(WhereInfo));
170774
- if( nTabList>1 ){
170775
- nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
170776
- }
170861
+ nByteWInfo = SZ_WHEREINFO(nTabList);
170777170862
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
170778170863
if( db->mallocFailed ){
170779170864
sqlite3DbFree(db, pWInfo);
170780170865
pWInfo = 0;
170781170866
goto whereBeginError;
@@ -181607,11 +181692,15 @@
181607181692
tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
181608181693
}else if( tokenType==TK_FILTER ){
181609181694
assert( n==6 );
181610181695
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
181611181696
#endif /* SQLITE_OMIT_WINDOWFUNC */
181612
- }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){
181697
+ }else if( tokenType==TK_COMMENT
181698
+ && (db->init.busy || (db->flags & SQLITE_Comments)!=0)
181699
+ ){
181700
+ /* Ignore SQL comments if either (1) we are reparsing the schema or
181701
+ ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */
181613181702
zSql += n;
181614181703
continue;
181615181704
}else if( tokenType!=TK_QNUMBER ){
181616181705
Token x;
181617181706
x.z = zSql;
@@ -182502,10 +182591,18 @@
182502182591
}
182503182592
#endif
182504182593
if( rc==SQLITE_OK ){
182505182594
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
182506182595
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
182596
+#ifdef SQLITE_EXTRA_INIT_MUTEXED
182597
+ {
182598
+ int SQLITE_EXTRA_INIT_MUTEXED(const char*);
182599
+ rc = SQLITE_EXTRA_INIT_MUTEXED(0);
182600
+ }
182601
+#endif
182602
+ }
182603
+ if( rc==SQLITE_OK ){
182507182604
sqlite3MemoryBarrier();
182508182605
sqlite3GlobalConfig.isInit = 1;
182509182606
#ifdef SQLITE_EXTRA_INIT
182510182607
bRunExtraInit = 1;
182511182608
#endif
@@ -184063,10 +184160,14 @@
184063184160
sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt));
184064184161
sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC);
184065184162
}
184066184163
}
184067184164
sqlite3BtreeLeaveAll(db);
184165
+#endif
184166
+#if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT)
184167
+ UNUSED_PARAMETER(db);
184168
+ UNUSED_PARAMETER(flags);
184068184169
#endif
184069184170
return SQLITE_OK;
184070184171
}
184071184172
184072184173
/*
@@ -186032,11 +186133,11 @@
186032186133
}else if( pData==0 ){
186033186134
sqlite3_mutex_leave(db->mutex);
186034186135
return SQLITE_OK;
186035186136
}else{
186036186137
size_t n = strlen(zName);
186037
- p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
186138
+ p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) );
186038186139
if( p==0 ){
186039186140
if( xDestructor ) xDestructor(pData);
186040186141
sqlite3_mutex_leave(db->mutex);
186041186142
return SQLITE_NOMEM;
186042186143
}
@@ -186398,12 +186499,12 @@
186398186499
#endif
186399186500
186400186501
/* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
186401186502
**
186402186503
** If b is true, then activate the SQLITE_FkNoAction setting. If b is
186403
- ** false then clearn that setting. If the SQLITE_FkNoAction setting is
186404
- ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
186504
+ ** false then clear that setting. If the SQLITE_FkNoAction setting is
186505
+ ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if
186405186506
** they were NO ACTION, regardless of how they are defined.
186406186507
**
186407186508
** NB: One must usually run "PRAGMA writable_schema=RESET" after
186408186509
** using this test-control, before it will take full effect. failing
186409186510
** to reset the schema can result in some unexpected behavior.
@@ -187746,11 +187847,11 @@
187746187847
** }
187747187848
**
187748187849
** Here, array { X } means zero or more occurrences of X, adjacent in
187749187850
** memory. A "position" is an index of a token in the token stream
187750187851
** generated by the tokenizer. Note that POS_END and POS_COLUMN occur
187751
-** in the same logical place as the position element, and act as sentinals
187852
+** in the same logical place as the position element, and act as sentinels
187752187853
** ending a position list array. POS_END is 0. POS_COLUMN is 1.
187753187854
** The positions numbers are not stored literally but rather as two more
187754187855
** than the difference from the prior position, or the just the position plus
187755187856
** 2 for the first position. Example:
187756187857
**
@@ -188433,10 +188534,23 @@
188433188534
188434188535
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
188435188536
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
188436188537
188437188538
#define deliberate_fall_through
188539
+
188540
+/*
188541
+** Macros needed to provide flexible arrays in a portable way
188542
+*/
188543
+#ifndef offsetof
188544
+# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
188545
+#endif
188546
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
188547
+# define FLEXARRAY
188548
+#else
188549
+# define FLEXARRAY 1
188550
+#endif
188551
+
188438188552
188439188553
#endif /* SQLITE_AMALGAMATION */
188440188554
188441188555
#ifdef SQLITE_DEBUG
188442188556
SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
@@ -188538,11 +188652,11 @@
188538188652
int inTransaction; /* True after xBegin but before xCommit/xRollback */
188539188653
int mxSavepoint; /* Largest valid xSavepoint integer */
188540188654
#endif
188541188655
188542188656
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
188543
- /* True to disable the incremental doclist optimization. This is controled
188657
+ /* True to disable the incremental doclist optimization. This is controlled
188544188658
** by special insert command 'test-no-incr-doclist'. */
188545188659
int bNoIncrDoclist;
188546188660
188547188661
/* Number of segments in a level */
188548188662
int nMergeCount;
@@ -188590,11 +188704,11 @@
188590188704
#define FTS3_EVAL_NEXT 1
188591188705
#define FTS3_EVAL_MATCHINFO 2
188592188706
188593188707
/*
188594188708
** The Fts3Cursor.eSearch member is always set to one of the following.
188595
-** Actualy, Fts3Cursor.eSearch can be greater than or equal to
188709
+** Actually, Fts3Cursor.eSearch can be greater than or equal to
188596188710
** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index
188597188711
** of the column to be searched. For example, in
188598188712
**
188599188713
** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
188600188714
** SELECT docid FROM ex1 WHERE b MATCH 'one two three';
@@ -188663,12 +188777,16 @@
188663188777
/* Variables below this point are populated by fts3_expr.c when parsing
188664188778
** a MATCH expression. Everything above is part of the evaluation phase.
188665188779
*/
188666188780
int nToken; /* Number of tokens in the phrase */
188667188781
int iColumn; /* Index of column this phrase must match */
188668
- Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
188782
+ Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */
188669188783
};
188784
+
188785
+/* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */
188786
+#define SZ_FTS3PHRASE(N) \
188787
+ (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken))
188670188788
188671188789
/*
188672188790
** A tree of these objects forms the RHS of a MATCH operator.
188673188791
**
188674188792
** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
@@ -191243,11 +191361,11 @@
191243191361
**
191244191362
** The space required to store the output is therefore the sum of the
191245191363
** sizes of the two inputs, plus enough space for exactly one of the input
191246191364
** docids to grow.
191247191365
**
191248
- ** A symetric argument may be made if the doclists are in descending
191366
+ ** A symmetric argument may be made if the doclists are in descending
191249191367
** order.
191250191368
*/
191251191369
aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
191252191370
if( !aOut ) return SQLITE_NOMEM;
191253191371
@@ -193341,11 +193459,11 @@
193341193459
**
193342193460
** * features at least one token that uses an incremental doclist, and
193343193461
**
193344193462
** * does not contain any deferred tokens.
193345193463
**
193346
-** Advance it to the next matching documnent in the database and populate
193464
+** Advance it to the next matching document in the database and populate
193347193465
** the Fts3Doclist.pList and nList fields.
193348193466
**
193349193467
** If there is no "next" entry and no error occurs, then *pbEof is set to
193350193468
** 1 before returning. Otherwise, if no error occurs and the iterator is
193351193469
** successfully advanced, *pbEof is set to 0.
@@ -194348,11 +194466,11 @@
194348194466
194349194467
return rc;
194350194468
}
194351194469
194352194470
/*
194353
-** Restart interation for expression pExpr so that the next call to
194471
+** Restart iteration for expression pExpr so that the next call to
194354194472
** fts3EvalNext() visits the first row. Do not allow incremental
194355194473
** loading or merging of phrase doclists for this iteration.
194356194474
**
194357194475
** If *pRc is other than SQLITE_OK when this function is called, it is
194358194476
** a no-op. If an error occurs within this function, *pRc is set to an
@@ -195539,10 +195657,27 @@
195539195657
/*
195540195658
** Function getNextNode(), which is called by fts3ExprParse(), may itself
195541195659
** call fts3ExprParse(). So this forward declaration is required.
195542195660
*/
195543195661
static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
195662
+
195663
+/*
195664
+** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis
195665
+** is defined, search for '(' and ')' as well. Return the index of the first
195666
+** such character in the buffer. If there is no such character, return -1.
195667
+*/
195668
+static int findBarredChar(const char *z, int n){
195669
+ int ii;
195670
+ for(ii=0; ii<n; ii++){
195671
+ if( (z[ii]=='"')
195672
+ || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')'))
195673
+ ){
195674
+ return ii;
195675
+ }
195676
+ }
195677
+ return -1;
195678
+}
195544195679
195545195680
/*
195546195681
** Extract the next token from buffer z (length n) using the tokenizer
195547195682
** and other information (column names etc.) in pParse. Create an Fts3Expr
195548195683
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
@@ -195564,38 +195699,42 @@
195564195699
sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
195565195700
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
195566195701
int rc;
195567195702
sqlite3_tokenizer_cursor *pCursor;
195568195703
Fts3Expr *pRet = 0;
195569
- int i = 0;
195570
-
195571
- /* Set variable i to the maximum number of bytes of input to tokenize. */
195572
- for(i=0; i<n; i++){
195573
- if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
195574
- if( z[i]=='"' ) break;
195575
- }
195576
-
195577
- *pnConsumed = i;
195578
- rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor);
195704
+
195705
+ *pnConsumed = n;
195706
+ rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
195579195707
if( rc==SQLITE_OK ){
195580195708
const char *zToken;
195581195709
int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
195582195710
sqlite3_int64 nByte; /* total space to allocate */
195583195711
195584195712
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
195585195713
if( rc==SQLITE_OK ){
195586
- nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
195714
+ /* Check that this tokenization did not gobble up any " characters. Or,
195715
+ ** if enable_parenthesis is true, that it did not gobble up any
195716
+ ** open or close parenthesis characters either. If it did, call
195717
+ ** getNextToken() again, but pass only that part of the input buffer
195718
+ ** up to the first such character. */
195719
+ int iBarred = findBarredChar(z, iEnd);
195720
+ if( iBarred>=0 ){
195721
+ pModule->xClose(pCursor);
195722
+ return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed);
195723
+ }
195724
+
195725
+ nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken;
195587195726
pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
195588195727
if( !pRet ){
195589195728
rc = SQLITE_NOMEM;
195590195729
}else{
195591195730
pRet->eType = FTSQUERY_PHRASE;
195592195731
pRet->pPhrase = (Fts3Phrase *)&pRet[1];
195593195732
pRet->pPhrase->nToken = 1;
195594195733
pRet->pPhrase->iColumn = iCol;
195595195734
pRet->pPhrase->aToken[0].n = nToken;
195596
- pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1];
195735
+ pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1];
195597195736
memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
195598195737
195599195738
if( iEnd<n && z[iEnd]=='*' ){
195600195739
pRet->pPhrase->aToken[0].isPrefix = 1;
195601195740
iEnd++;
@@ -195615,11 +195754,15 @@
195615195754
}
195616195755
}
195617195756
195618195757
}
195619195758
*pnConsumed = iEnd;
195620
- }else if( i && rc==SQLITE_DONE ){
195759
+ }else if( n && rc==SQLITE_DONE ){
195760
+ int iBarred = findBarredChar(z, n);
195761
+ if( iBarred>=0 ){
195762
+ *pnConsumed = iBarred;
195763
+ }
195621195764
rc = SQLITE_OK;
195622195765
}
195623195766
195624195767
pModule->xClose(pCursor);
195625195768
}
@@ -195664,11 +195807,11 @@
195664195807
Fts3Expr *p = 0;
195665195808
sqlite3_tokenizer_cursor *pCursor = 0;
195666195809
char *zTemp = 0;
195667195810
i64 nTemp = 0;
195668195811
195669
- const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
195812
+ const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1);
195670195813
int nToken = 0;
195671195814
195672195815
/* The final Fts3Expr data structure, including the Fts3Phrase,
195673195816
** Fts3PhraseToken structures token buffers are all stored as a single
195674195817
** allocation so that the expression can be freed with a single call to
@@ -196036,11 +196179,11 @@
196036196179
int eType = p->eType;
196037196180
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
196038196181
196039196182
/* The isRequirePhrase variable is set to true if a phrase or
196040196183
** an expression contained in parenthesis is required. If a
196041
- ** binary operator (AND, OR, NOT or NEAR) is encounted when
196184
+ ** binary operator (AND, OR, NOT or NEAR) is encountered when
196042196185
** isRequirePhrase is set, this is a syntax error.
196043196186
*/
196044196187
if( !isPhrase && isRequirePhrase ){
196045196188
sqlite3Fts3ExprFree(p);
196046196189
rc = SQLITE_ERROR;
@@ -196618,11 +196761,10 @@
196618196761
pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
196619196762
);
196620196763
}
196621196764
196622196765
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
196623
- sqlite3Fts3ExprFree(pExpr);
196624196766
sqlite3_result_error(context, "Error parsing expression", -1);
196625196767
}else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
196626196768
sqlite3_result_error_nomem(context);
196627196769
}else{
196628196770
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
@@ -196861,11 +197003,11 @@
196861197003
pEntry->count++;
196862197004
pEntry->chain = pNew;
196863197005
}
196864197006
196865197007
196866
-/* Resize the hash table so that it cantains "new_size" buckets.
197008
+/* Resize the hash table so that it contains "new_size" buckets.
196867197009
** "new_size" must be a power of 2. The hash table might fail
196868197010
** to resize if sqliteMalloc() fails.
196869197011
**
196870197012
** Return non-zero if a memory allocation error occurs.
196871197013
*/
@@ -197316,11 +197458,11 @@
197316197458
isConsonant(z+2);
197317197459
}
197318197460
197319197461
/*
197320197462
** If the word ends with zFrom and xCond() is true for the stem
197321
-** of the word that preceeds the zFrom ending, then change the
197463
+** of the word that precedes the zFrom ending, then change the
197322197464
** ending to zTo.
197323197465
**
197324197466
** The input word *pz and zFrom are both in reverse order. zTo
197325197467
** is in normal order.
197326197468
**
@@ -202899,11 +203041,11 @@
202899203041
** previous term. Before this function returns, it is updated to contain a
202900203042
** copy of zTerm/nTerm.
202901203043
**
202902203044
** It is assumed that the buffer associated with pNode is already large
202903203045
** enough to accommodate the new entry. The buffer associated with pPrev
202904
-** is extended by this function if requrired.
203046
+** is extended by this function if required.
202905203047
**
202906203048
** If an error (i.e. OOM condition) occurs, an SQLite error code is
202907203049
** returned. Otherwise, SQLITE_OK.
202908203050
*/
202909203051
static int fts3AppendToNode(
@@ -204562,11 +204704,11 @@
204562204704
#endif
204563204705
204564204706
/*
204565204707
** SQLite value pRowid contains the rowid of a row that may or may not be
204566204708
** present in the FTS3 table. If it is, delete it and adjust the contents
204567
-** of subsiduary data structures accordingly.
204709
+** of subsidiary data structures accordingly.
204568204710
*/
204569204711
static int fts3DeleteByRowid(
204570204712
Fts3Table *p,
204571204713
sqlite3_value *pRowid,
204572204714
int *pnChng, /* IN/OUT: Decrement if row is deleted */
@@ -204888,12 +205030,16 @@
204888205030
struct MatchinfoBuffer {
204889205031
u8 aRef[3];
204890205032
int nElem;
204891205033
int bGlobal; /* Set if global data is loaded */
204892205034
char *zMatchinfo;
204893
- u32 aMatchinfo[1];
205035
+ u32 aMI[FLEXARRAY];
204894205036
};
205037
+
205038
+/* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */
205039
+#define SZ_MATCHINFOBUFFER(N) \
205040
+ (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64))
204895205041
204896205042
204897205043
/*
204898205044
** The snippet() and offsets() functions both return text values. An instance
204899205045
** of the following structure is used to accumulate those values while the
@@ -204915,17 +205061,17 @@
204915205061
** Allocate a two-slot MatchinfoBuffer object.
204916205062
*/
204917205063
static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
204918205064
MatchinfoBuffer *pRet;
204919205065
sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
204920
- + sizeof(MatchinfoBuffer);
205066
+ + SZ_MATCHINFOBUFFER(1);
204921205067
sqlite3_int64 nStr = strlen(zMatchinfo);
204922205068
204923205069
pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
204924205070
if( pRet ){
204925
- pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
204926
- pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0]
205071
+ pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet;
205072
+ pRet->aMI[1+nElem] = pRet->aMI[0]
204927205073
+ sizeof(u32)*((int)nElem+1);
204928205074
pRet->nElem = (int)nElem;
204929205075
pRet->zMatchinfo = ((char*)pRet) + nByte;
204930205076
memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
204931205077
pRet->aRef[0] = 1;
@@ -204935,14 +205081,14 @@
204935205081
}
204936205082
204937205083
static void fts3MIBufferFree(void *p){
204938205084
MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
204939205085
204940
- assert( (u32*)p==&pBuf->aMatchinfo[1]
204941
- || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
205086
+ assert( (u32*)p==&pBuf->aMI[1]
205087
+ || (u32*)p==&pBuf->aMI[pBuf->nElem+2]
204942205088
);
204943
- if( (u32*)p==&pBuf->aMatchinfo[1] ){
205089
+ if( (u32*)p==&pBuf->aMI[1] ){
204944205090
pBuf->aRef[1] = 0;
204945205091
}else{
204946205092
pBuf->aRef[2] = 0;
204947205093
}
204948205094
@@ -204955,32 +205101,32 @@
204955205101
void (*xRet)(void*) = 0;
204956205102
u32 *aOut = 0;
204957205103
204958205104
if( p->aRef[1]==0 ){
204959205105
p->aRef[1] = 1;
204960
- aOut = &p->aMatchinfo[1];
205106
+ aOut = &p->aMI[1];
204961205107
xRet = fts3MIBufferFree;
204962205108
}
204963205109
else if( p->aRef[2]==0 ){
204964205110
p->aRef[2] = 1;
204965
- aOut = &p->aMatchinfo[p->nElem+2];
205111
+ aOut = &p->aMI[p->nElem+2];
204966205112
xRet = fts3MIBufferFree;
204967205113
}else{
204968205114
aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
204969205115
if( aOut ){
204970205116
xRet = sqlite3_free;
204971
- if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
205117
+ if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32));
204972205118
}
204973205119
}
204974205120
204975205121
*paOut = aOut;
204976205122
return xRet;
204977205123
}
204978205124
204979205125
static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
204980205126
p->bGlobal = 1;
204981
- memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32));
205127
+ memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32));
204982205128
}
204983205129
204984205130
/*
204985205131
** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
204986205132
*/
@@ -205391,11 +205537,11 @@
205391205537
if( nAppend<0 ){
205392205538
nAppend = (int)strlen(zAppend);
205393205539
}
205394205540
205395205541
/* If there is insufficient space allocated at StrBuffer.z, use realloc()
205396
- ** to grow the buffer until so that it is big enough to accomadate the
205542
+ ** to grow the buffer until so that it is big enough to accommodate the
205397205543
** appended data.
205398205544
*/
205399205545
if( pStr->n+nAppend+1>=pStr->nAlloc ){
205400205546
sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100;
205401205547
char *zNew = sqlite3_realloc64(pStr->z, nAlloc);
@@ -207766,11 +207912,11 @@
207766207912
**
207767207913
** When a match if found, the matching entry is moved to become the
207768207914
** most-recently used entry if it isn't so already.
207769207915
**
207770207916
** The JsonParse object returned still belongs to the Cache and might
207771
-** be deleted at any moment. If the caller whants the JsonParse to
207917
+** be deleted at any moment. If the caller wants the JsonParse to
207772207918
** linger, it needs to increment the nPJRef reference counter.
207773207919
*/
207774207920
static JsonParse *jsonCacheSearch(
207775207921
sqlite3_context *ctx, /* The SQL statement context holding the cache */
207776207922
sqlite3_value *pArg /* Function argument containing SQL text */
@@ -210811,11 +210957,11 @@
210811210957
**
210812210958
** This goes against all historical documentation about how the SQLite
210813210959
** JSON functions were suppose to work. From the beginning, blob was
210814210960
** reserved for expansion and a blob value should have raised an error.
210815210961
** But it did not, due to a bug. And many applications came to depend
210816
- ** upon this buggy behavior, espeically when using the CLI and reading
210962
+ ** upon this buggy behavior, especially when using the CLI and reading
210817210963
** JSON text using readfile(), which returns a blob. For this reason
210818210964
** we will continue to support the bug moving forward.
210819210965
** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
210820210966
*/
210821210967
}
@@ -212911,10 +213057,18 @@
212911213057
# define NEVER(X) ((X)?(assert(0),1):0)
212912213058
#else
212913213059
# define ALWAYS(X) (X)
212914213060
# define NEVER(X) (X)
212915213061
#endif
213062
+#ifndef offsetof
213063
+#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
213064
+#endif
213065
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
213066
+# define FLEXARRAY
213067
+#else
213068
+# define FLEXARRAY 1
213069
+#endif
212916213070
#endif /* !defined(SQLITE_AMALGAMATION) */
212917213071
212918213072
/* Macro to check for 4-byte alignment. Only used inside of assert() */
212919213073
#ifdef SQLITE_DEBUG
212920213074
# define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0)
@@ -213231,13 +213385,17 @@
213231213385
struct RtreeMatchArg {
213232213386
u32 iSize; /* Size of this object */
213233213387
RtreeGeomCallback cb; /* Info about the callback functions */
213234213388
int nParam; /* Number of parameters to the SQL function */
213235213389
sqlite3_value **apSqlParam; /* Original SQL parameter values */
213236
- RtreeDValue aParam[1]; /* Values for parameters to the SQL function */
213390
+ RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */
213237213391
};
213238213392
213393
+/* Size of an RtreeMatchArg object with N parameters */
213394
+#define SZ_RTREEMATCHARG(N) \
213395
+ (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue))
213396
+
213239213397
#ifndef MAX
213240213398
# define MAX(x,y) ((x) < (y) ? (y) : (x))
213241213399
#endif
213242213400
#ifndef MIN
213243213401
# define MIN(x,y) ((x) > (y) ? (y) : (x))
@@ -214922,11 +215080,11 @@
214922215080
214923215081
return rc;
214924215082
}
214925215083
214926215084
/*
214927
-** Return the N-dimensional volumn of the cell stored in *p.
215085
+** Return the N-dimensional volume of the cell stored in *p.
214928215086
*/
214929215087
static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
214930215088
RtreeDValue area = (RtreeDValue)1;
214931215089
assert( pRtree->nDim>=1 && pRtree->nDim<=5 );
214932215090
#ifndef SQLITE_RTREE_INT_ONLY
@@ -216688,11 +216846,11 @@
216688216846
}
216689216847
216690216848
/*
216691216849
** The second and subsequent arguments to this function are a printf()
216692216850
** style format string and arguments. This function formats the string and
216693
-** appends it to the report being accumuated in pCheck.
216851
+** appends it to the report being accumulated in pCheck.
216694216852
*/
216695216853
static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
216696216854
va_list ap;
216697216855
va_start(ap, zFmt);
216698216856
if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){
@@ -217876,11 +218034,11 @@
217876218034
217877218035
/*
217878218036
** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
217879218037
** Returns:
217880218038
**
217881
-** +2 x0,y0 is on the line segement
218039
+** +2 x0,y0 is on the line segment
217882218040
**
217883218041
** +1 x0,y0 is beneath line segment
217884218042
**
217885218043
** 0 x0,y0 is not on or beneath the line segment or the line segment
217886218044
** is vertical and x0,y0 is not on the line segment
@@ -217982,11 +218140,11 @@
217982218140
}
217983218141
sqlite3_free(p1);
217984218142
sqlite3_free(p2);
217985218143
}
217986218144
217987
-/* Objects used by the overlap algorihm. */
218145
+/* Objects used by the overlap algorithm. */
217988218146
typedef struct GeoEvent GeoEvent;
217989218147
typedef struct GeoSegment GeoSegment;
217990218148
typedef struct GeoOverlap GeoOverlap;
217991218149
struct GeoEvent {
217992218150
double x; /* X coordinate at which event occurs */
@@ -219029,12 +219187,11 @@
219029219187
RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
219030219188
RtreeMatchArg *pBlob;
219031219189
sqlite3_int64 nBlob;
219032219190
int memErr = 0;
219033219191
219034
- nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
219035
- + nArg*sizeof(sqlite3_value*);
219192
+ nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*);
219036219193
pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
219037219194
if( !pBlob ){
219038219195
sqlite3_result_error_nomem(ctx);
219039219196
}else{
219040219197
int i;
@@ -220125,11 +220282,11 @@
220125220282
** to read from the original database snapshot. In other words, partially
220126220283
** applied transactions are not visible to other clients.
220127220284
**
220128220285
** "RBU" stands for "Resumable Bulk Update". As in a large database update
220129220286
** transmitted via a wireless network to a mobile device. A transaction
220130
-** applied using this extension is hence refered to as an "RBU update".
220287
+** applied using this extension is hence referred to as an "RBU update".
220131220288
**
220132220289
**
220133220290
** LIMITATIONS
220134220291
**
220135220292
** An "RBU update" transaction is subject to the following limitations:
@@ -220422,11 +220579,11 @@
220422220579
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
220423220580
** of the state tables within the state database are zeroed. This way,
220424220581
** the next call to sqlite3rbu_vacuum() opens a handle that starts a
220425220582
** new RBU vacuum operation.
220426220583
**
220427
-** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
220584
+** As with sqlite3rbu_open(), Zipvfs users should refer to the comment
220428220585
** describing the sqlite3rbu_create_vfs() API function below for
220429220586
** a description of the complications associated with using RBU with
220430220587
** zipvfs databases.
220431220588
*/
220432220589
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
@@ -220518,11 +220675,11 @@
220518220675
/*
220519220676
** Close an RBU handle.
220520220677
**
220521220678
** If the RBU update has been completely applied, mark the RBU database
220522220679
** as fully applied. Otherwise, assuming no error has occurred, save the
220523
-** current state of the RBU update appliation to the RBU database.
220680
+** current state of the RBU update application to the RBU database.
220524220681
**
220525220682
** If an error has already occurred as part of an sqlite3rbu_step()
220526220683
** or sqlite3rbu_open() call, or if one occurs within this function, an
220527220684
** SQLite error code is returned. Additionally, if pzErrmsg is not NULL,
220528220685
** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted
@@ -225444,11 +225601,11 @@
225444225601
int rc;
225445225602
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
225446225603
225447225604
/* If this is an RBU vacuum operation and this is the target database,
225448225605
** pretend that it has at least one page. Otherwise, SQLite will not
225449
- ** check for the existance of a *-wal file. rbuVfsRead() contains
225606
+ ** check for the existence of a *-wal file. rbuVfsRead() contains
225450225607
** similar logic. */
225451225608
if( rc==SQLITE_OK && *pSize==0
225452225609
&& p->pRbu && rbuIsVacuum(p->pRbu)
225453225610
&& (p->openFlags & SQLITE_OPEN_MAIN_DB)
225454225611
){
@@ -228674,11 +228831,11 @@
228674228831
}
228675228832
228676228833
/*
228677228834
** This function is called to initialize the SessionTable.nCol, azCol[]
228678228835
** abPK[] and azDflt[] members of SessionTable object pTab. If these
228679
-** fields are already initilialized, this function is a no-op.
228836
+** fields are already initialized, this function is a no-op.
228680228837
**
228681228838
** If an error occurs, an error code is stored in sqlite3_session.rc and
228682228839
** non-zero returned. Or, if no error occurs but the table has no primary
228683228840
** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
228684228841
** indicate that updates on this table should be ignored. SessionTable.abPK
@@ -230497,11 +230654,11 @@
230497230654
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
230498230655
void **ppChangeset /* OUT: Buffer containing changeset */
230499230656
){
230500230657
sqlite3 *db = pSession->db; /* Source database handle */
230501230658
SessionTable *pTab; /* Used to iterate through attached tables */
230502
- SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
230659
+ SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */
230503230660
int rc; /* Return code */
230504230661
230505230662
assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
230506230663
assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );
230507230664
@@ -234931,10 +235088,22 @@
234931235088
# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
234932235089
#else
234933235090
# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
234934235091
#endif
234935235092
235093
+/*
235094
+** Macros needed to provide flexible arrays in a portable way
235095
+*/
235096
+#ifndef offsetof
235097
+# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
235098
+#endif
235099
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
235100
+# define FLEXARRAY
235101
+#else
235102
+# define FLEXARRAY 1
235103
+#endif
235104
+
234936235105
#endif
234937235106
234938235107
/* Truncate very long tokens to this many bytes. Hard limit is
234939235108
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
234940235109
** field that occurs at the start of each leaf page (see fts5_index.c). */
@@ -235003,14 +235172,15 @@
235003235172
**
235004235173
** This object is used by fts5_expr.c and fts5_index.c.
235005235174
*/
235006235175
struct Fts5Colset {
235007235176
int nCol;
235008
- int aiCol[1];
235177
+ int aiCol[FLEXARRAY];
235009235178
};
235010235179
235011
-
235180
+/* Size (int bytes) of a complete Fts5Colset object with N columns. */
235181
+#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2))
235012235182
235013235183
/**************************************************************************
235014235184
** Interface to code in fts5_config.c. fts5_config.c contains contains code
235015235185
** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
235016235186
*/
@@ -235835,11 +236005,11 @@
235835236005
*************************************************************************
235836236006
** Driver template for the LEMON parser generator.
235837236007
**
235838236008
** The "lemon" program processes an LALR(1) input grammar file, then uses
235839236009
** this template to construct a parser. The "lemon" program inserts text
235840
-** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
236010
+** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the
235841236011
** interstitial "-" characters) contained in this template is changed into
235842236012
** the value of the %name directive from the grammar. Otherwise, the content
235843236013
** of this template is copied straight through into the generate parser
235844236014
** source file.
235845236015
**
@@ -237989,11 +238159,11 @@
237989238159
** where "N" is the total number of documents in the set and nHit
237990238160
** is the number that contain at least one instance of the phrase
237991238161
** under consideration.
237992238162
**
237993238163
** The problem with this is that if (N < 2*nHit), the IDF is
237994
- ** negative. Which is undesirable. So the mimimum allowable IDF is
238164
+ ** negative. Which is undesirable. So the minimum allowable IDF is
237995238165
** (1e-6) - roughly the same as a term that appears in just over
237996238166
** half of set of 5,000,000 documents. */
237997238167
double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
237998238168
if( idf<=0.0 ) idf = 1e-6;
237999238169
p->aIDF[i] = idf;
@@ -238452,11 +238622,11 @@
238452238622
**
238453238623
** * All non-ASCII characters,
238454238624
** * The 52 upper and lower case ASCII characters, and
238455238625
** * The 10 integer ASCII characters.
238456238626
** * The underscore character "_" (0x5F).
238457
-** * The unicode "subsitute" character (0x1A).
238627
+** * The unicode "substitute" character (0x1A).
238458238628
*/
238459238629
static int sqlite3Fts5IsBareword(char t){
238460238630
u8 aBareword[128] = {
238461238631
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */
238462238632
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
@@ -239770,12 +239940,16 @@
239770239940
Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
239771239941
239772239942
/* Child nodes. For a NOT node, this array always contains 2 entries. For
239773239943
** AND or OR nodes, it contains 2 or more entries. */
239774239944
int nChild; /* Number of child nodes */
239775
- Fts5ExprNode *apChild[1]; /* Array of child nodes */
239945
+ Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */
239776239946
};
239947
+
239948
+/* Size (in bytes) of an Fts5ExprNode object that holds up to N children */
239949
+#define SZ_FTS5EXPRNODE(N) \
239950
+ (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*))
239777239951
239778239952
#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
239779239953
239780239954
/*
239781239955
** Invoke the xNext method of an Fts5ExprNode object. This macro should be
@@ -239803,24 +239977,31 @@
239803239977
*/
239804239978
struct Fts5ExprPhrase {
239805239979
Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
239806239980
Fts5Buffer poslist; /* Current position list */
239807239981
int nTerm; /* Number of entries in aTerm[] */
239808
- Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */
239982
+ Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */
239809239983
};
239984
+
239985
+/* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */
239986
+#define SZ_FTS5EXPRPHRASE(N) \
239987
+ (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm))
239810239988
239811239989
/*
239812239990
** One or more phrases that must appear within a certain token distance of
239813239991
** each other within each matching document.
239814239992
*/
239815239993
struct Fts5ExprNearset {
239816239994
int nNear; /* NEAR parameter */
239817239995
Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
239818239996
int nPhrase; /* Number of entries in aPhrase[] array */
239819
- Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */
239997
+ Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */
239820239998
};
239821239999
240000
+/* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */
240001
+#define SZ_FTS5EXPRNEARSET(N) \
240002
+ (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*))
239822240003
239823240004
/*
239824240005
** Parse context.
239825240006
*/
239826240007
struct Fts5Parse {
@@ -239976,11 +240157,11 @@
239976240157
assert_expr_depth_ok(sParse.rc, sParse.pExpr);
239977240158
239978240159
/* If the LHS of the MATCH expression was a user column, apply the
239979240160
** implicit column-filter. */
239980240161
if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
239981
- int n = sizeof(Fts5Colset);
240162
+ int n = SZ_FTS5COLSET(1);
239982240163
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
239983240164
if( pColset ){
239984240165
pColset->nCol = 1;
239985240166
pColset->aiCol[0] = iCol;
239986240167
sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset);
@@ -241334,11 +241515,11 @@
241334241515
Fts5ExprNearset *pRet = 0;
241335241516
241336241517
if( pParse->rc==SQLITE_OK ){
241337241518
if( pNear==0 ){
241338241519
sqlite3_int64 nByte;
241339
- nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
241520
+ nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1);
241340241521
pRet = sqlite3_malloc64(nByte);
241341241522
if( pRet==0 ){
241342241523
pParse->rc = SQLITE_NOMEM;
241343241524
}else{
241344241525
memset(pRet, 0, (size_t)nByte);
@@ -241345,11 +241526,11 @@
241345241526
}
241346241527
}else if( (pNear->nPhrase % SZALLOC)==0 ){
241347241528
int nNew = pNear->nPhrase + SZALLOC;
241348241529
sqlite3_int64 nByte;
241349241530
241350
- nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
241531
+ nByte = SZ_FTS5EXPRNEARSET(nNew+1);
241351241532
pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
241352241533
if( pRet==0 ){
241353241534
pParse->rc = SQLITE_NOMEM;
241354241535
}
241355241536
}else{
@@ -241436,16 +241617,16 @@
241436241617
if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
241437241618
Fts5ExprPhrase *pNew;
241438241619
int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
241439241620
241440241621
pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
241441
- sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
241622
+ SZ_FTS5EXPRPHRASE(nNew+1)
241442241623
);
241443241624
if( pNew==0 ){
241444241625
rc = SQLITE_NOMEM;
241445241626
}else{
241446
- if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
241627
+ if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1));
241447241628
pCtx->pPhrase = pPhrase = pNew;
241448241629
pNew->nTerm = nNew - SZALLOC;
241449241630
}
241450241631
}
241451241632
@@ -241549,11 +241730,11 @@
241549241730
}
241550241731
241551241732
if( sCtx.pPhrase==0 ){
241552241733
/* This happens when parsing a token or quoted phrase that contains
241553241734
** no token characters at all. (e.g ... MATCH '""'). */
241554
- sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase));
241735
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1));
241555241736
}else if( sCtx.pPhrase->nTerm ){
241556241737
sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
241557241738
}
241558241739
assert( pParse->apPhrase!=0 );
241559241740
pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
@@ -241584,23 +241765,22 @@
241584241765
if( rc==SQLITE_OK ){
241585241766
pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
241586241767
sizeof(Fts5ExprPhrase*));
241587241768
}
241588241769
if( rc==SQLITE_OK ){
241589
- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
241590
- sizeof(Fts5ExprNode));
241770
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1));
241591241771
}
241592241772
if( rc==SQLITE_OK ){
241593241773
pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
241594
- sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
241774
+ SZ_FTS5EXPRNEARSET(2));
241595241775
}
241596241776
if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
241597241777
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
241598241778
if( pColsetOrig ){
241599241779
sqlite3_int64 nByte;
241600241780
Fts5Colset *pColset;
241601
- nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
241781
+ nByte = SZ_FTS5COLSET(pColsetOrig->nCol);
241602241782
pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
241603241783
if( pColset ){
241604241784
memcpy(pColset, pColsetOrig, (size_t)nByte);
241605241785
}
241606241786
pNew->pRoot->pNear->pColset = pColset;
@@ -241624,11 +241804,11 @@
241624241804
}
241625241805
}
241626241806
}else{
241627241807
/* This happens when parsing a token or quoted phrase that contains
241628241808
** no token characters at all. (e.g ... MATCH '""'). */
241629
- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
241809
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1));
241630241810
}
241631241811
}
241632241812
241633241813
if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
241634241814
/* All the allocations succeeded. Put the expression object together. */
@@ -241718,11 +241898,11 @@
241718241898
Fts5Colset *pNew; /* New colset object to return */
241719241899
241720241900
assert( pParse->rc==SQLITE_OK );
241721241901
assert( iCol>=0 && iCol<pParse->pConfig->nCol );
241722241902
241723
- pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
241903
+ pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1));
241724241904
if( pNew==0 ){
241725241905
pParse->rc = SQLITE_NOMEM;
241726241906
}else{
241727241907
int *aiCol = pNew->aiCol;
241728241908
int i, j;
@@ -241753,11 +241933,11 @@
241753241933
static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
241754241934
Fts5Colset *pRet;
241755241935
int nCol = pParse->pConfig->nCol;
241756241936
241757241937
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
241758
- sizeof(Fts5Colset) + sizeof(int)*nCol
241938
+ SZ_FTS5COLSET(nCol+1)
241759241939
);
241760241940
if( pRet ){
241761241941
int i;
241762241942
int iOld = 0;
241763241943
for(i=0; i<nCol; i++){
@@ -241814,11 +241994,11 @@
241814241994
** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned.
241815241995
*/
241816241996
static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
241817241997
Fts5Colset *pRet;
241818241998
if( pOrig ){
241819
- sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
241999
+ sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol);
241820242000
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
241821242001
if( pRet ){
241822242002
memcpy(pRet, pOrig, (size_t)nByte);
241823242003
}
241824242004
}else{
@@ -241982,21 +242162,21 @@
241982242162
Fts5ExprNode *pRet;
241983242163
241984242164
assert( pNear->nPhrase==1 );
241985242165
assert( pParse->bPhraseToAnd );
241986242166
241987
- nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
242167
+ nByte = SZ_FTS5EXPRNODE(nTerm+1);
241988242168
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
241989242169
if( pRet ){
241990242170
pRet->eType = FTS5_AND;
241991242171
pRet->nChild = nTerm;
241992242172
pRet->iHeight = 1;
241993242173
fts5ExprAssignXNext(pRet);
241994242174
pParse->nPhrase--;
241995242175
for(ii=0; ii<nTerm; ii++){
241996242176
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
241997
- &pParse->rc, sizeof(Fts5ExprPhrase)
242177
+ &pParse->rc, SZ_FTS5EXPRPHRASE(1)
241998242178
);
241999242179
if( pPhrase ){
242000242180
if( parseGrowPhraseArray(pParse) ){
242001242181
fts5ExprPhraseFree(pPhrase);
242002242182
}else{
@@ -242061,11 +242241,11 @@
242061242241
nChild = 2;
242062242242
if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
242063242243
if( pRight->eType==eType ) nChild += pRight->nChild-1;
242064242244
}
242065242245
242066
- nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
242246
+ nByte = SZ_FTS5EXPRNODE(nChild);
242067242247
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
242068242248
242069242249
if( pRet ){
242070242250
pRet->eType = eType;
242071242251
pRet->pNear = pNear;
@@ -242936,11 +243116,11 @@
242936243116
}
242937243117
return rc;
242938243118
}
242939243119
242940243120
/*
242941
-** Clear the token mappings for all Fts5IndexIter objects mannaged by
243121
+** Clear the token mappings for all Fts5IndexIter objects managed by
242942243122
** the expression passed as the only argument.
242943243123
*/
242944243124
static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
242945243125
int ii;
242946243126
for(ii=0; ii<pExpr->nPhrase; ii++){
@@ -242971,11 +243151,11 @@
242971243151
242972243152
typedef struct Fts5HashEntry Fts5HashEntry;
242973243153
242974243154
/*
242975243155
** This file contains the implementation of an in-memory hash table used
242976
-** to accumuluate "term -> doclist" content before it is flused to a level-0
243156
+** to accumulate "term -> doclist" content before it is flushed to a level-0
242977243157
** segment.
242978243158
*/
242979243159
242980243160
242981243161
struct Fts5Hash {
@@ -243028,11 +243208,11 @@
243028243208
int iPos; /* Position of last value written */
243029243209
i64 iRowid; /* Rowid of last value written */
243030243210
};
243031243211
243032243212
/*
243033
-** Eqivalent to:
243213
+** Equivalent to:
243034243214
**
243035243215
** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
243036243216
*/
243037243217
#define fts5EntryKey(p) ( ((char *)(&(p)[1])) )
243038243218
@@ -243964,13 +244144,17 @@
243964244144
int nRef; /* Object reference count */
243965244145
u64 nWriteCounter; /* Total leaves written to level 0 */
243966244146
u64 nOriginCntr; /* Origin value for next top-level segment */
243967244147
int nSegment; /* Total segments in this structure */
243968244148
int nLevel; /* Number of levels in this index */
243969
- Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */
244149
+ Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */
243970244150
};
243971244151
244152
+/* Size (in bytes) of an Fts5Structure object holding up to N levels */
244153
+#define SZ_FTS5STRUCTURE(N) \
244154
+ (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel))
244155
+
243972244156
/*
243973244157
** An object of type Fts5SegWriter is used to write to segments.
243974244158
*/
243975244159
struct Fts5PageWriter {
243976244160
int pgno; /* Page number for this page */
@@ -244096,14 +244280,18 @@
244096244280
244097244281
/*
244098244282
** Array of tombstone pages. Reference counted.
244099244283
*/
244100244284
struct Fts5TombstoneArray {
244101
- int nRef; /* Number of pointers to this object */
244285
+ int nRef; /* Number of pointers to this object */
244102244286
int nTombstone;
244103
- Fts5Data *apTombstone[1]; /* Array of tombstone pages */
244287
+ Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */
244104244288
};
244289
+
244290
+/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */
244291
+#define SZ_FTS5TOMBSTONEARRAY(N) \
244292
+ (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*))
244105244293
244106244294
/*
244107244295
** Argument is a pointer to an Fts5Data structure that contains a
244108244296
** leaf page.
244109244297
*/
@@ -244169,12 +244357,15 @@
244169244357
int bRev; /* True to iterate in reverse order */
244170244358
u8 bSkipEmpty; /* True to skip deleted entries */
244171244359
244172244360
i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
244173244361
Fts5CResult *aFirst; /* Current merge state (see above) */
244174
- Fts5SegIter aSeg[1]; /* Array of segment iterators */
244362
+ Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */
244175244363
};
244364
+
244365
+/* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */
244366
+#define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter))
244176244367
244177244368
/*
244178244369
** An instance of the following type is used to iterate through the contents
244179244370
** of a doclist-index record.
244180244371
**
@@ -244198,12 +244389,16 @@
244198244389
i64 iRowid; /* First rowid on leaf iLeafPgno */
244199244390
};
244200244391
struct Fts5DlidxIter {
244201244392
int nLvl;
244202244393
int iSegid;
244203
- Fts5DlidxLvl aLvl[1];
244394
+ Fts5DlidxLvl aLvl[FLEXARRAY];
244204244395
};
244396
+
244397
+/* Size (in bytes) of an Fts5DlidxIter object with up to N levels */
244398
+#define SZ_FTS5DLIDXITER(N) \
244399
+ (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl))
244205244400
244206244401
static void fts5PutU16(u8 *aOut, u16 iVal){
244207244402
aOut[0] = (iVal>>8);
244208244403
aOut[1] = (iVal&0xFF);
244209244404
}
@@ -244568,11 +244763,11 @@
244568244763
** an error occurs, (*pRc) is set to an SQLite error code before returning.
244569244764
*/
244570244765
static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
244571244766
Fts5Structure *p = *pp;
244572244767
if( *pRc==SQLITE_OK && p->nRef>1 ){
244573
- i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel);
244768
+ i64 nByte = SZ_FTS5STRUCTURE(p->nLevel);
244574244769
Fts5Structure *pNew;
244575244770
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
244576244771
if( pNew ){
244577244772
int i;
244578244773
memcpy(pNew, p, nByte);
@@ -244642,14 +244837,11 @@
244642244837
if( nLevel>FTS5_MAX_SEGMENT || nLevel<0
244643244838
|| nSegment>FTS5_MAX_SEGMENT || nSegment<0
244644244839
){
244645244840
return FTS5_CORRUPT;
244646244841
}
244647
- nByte = (
244648
- sizeof(Fts5Structure) + /* Main structure */
244649
- sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */
244650
- );
244842
+ nByte = SZ_FTS5STRUCTURE(nLevel);
244651244843
pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);
244652244844
244653244845
if( pRet ){
244654244846
pRet->nRef = 1;
244655244847
pRet->nLevel = nLevel;
@@ -244725,14 +244917,11 @@
244725244917
fts5StructureMakeWritable(pRc, ppStruct);
244726244918
assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
244727244919
if( *pRc==SQLITE_OK ){
244728244920
Fts5Structure *pStruct = *ppStruct;
244729244921
int nLevel = pStruct->nLevel;
244730
- sqlite3_int64 nByte = (
244731
- sizeof(Fts5Structure) + /* Main structure */
244732
- sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
244733
- );
244922
+ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2);
244734244923
244735244924
pStruct = sqlite3_realloc64(pStruct, nByte);
244736244925
if( pStruct ){
244737244926
memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
244738244927
pStruct->nLevel++;
@@ -245267,11 +245456,11 @@
245267245456
Fts5DlidxIter *pIter = 0;
245268245457
int i;
245269245458
int bDone = 0;
245270245459
245271245460
for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
245272
- sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
245461
+ sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1);
245273245462
Fts5DlidxIter *pNew;
245274245463
245275245464
pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
245276245465
if( pNew==0 ){
245277245466
p->rc = SQLITE_NOMEM;
@@ -245485,11 +245674,11 @@
245485245674
** leave an error in the Fts5Index object.
245486245675
*/
245487245676
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
245488245677
const int nTomb = pIter->pSeg->nPgTombstone;
245489245678
if( nTomb>0 ){
245490
- int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
245679
+ int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
245491245680
Fts5TombstoneArray *pNew;
245492245681
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
245493245682
if( pNew ){
245494245683
pNew->nTombstone = nTomb;
245495245684
pNew->nRef = 1;
@@ -246946,12 +247135,11 @@
246946247135
Fts5Iter *pNew;
246947247136
i64 nSlot; /* Power of two >= nSeg */
246948247137
246949247138
for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
246950247139
pNew = fts5IdxMalloc(p,
246951
- sizeof(Fts5Iter) + /* pNew */
246952
- sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */
247140
+ SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */
246953247141
sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
246954247142
);
246955247143
if( pNew ){
246956247144
pNew->nSeg = nSlot;
246957247145
pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
@@ -249313,11 +249501,11 @@
249313249501
static Fts5Structure *fts5IndexOptimizeStruct(
249314249502
Fts5Index *p,
249315249503
Fts5Structure *pStruct
249316249504
){
249317249505
Fts5Structure *pNew = 0;
249318
- sqlite3_int64 nByte = sizeof(Fts5Structure);
249506
+ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1);
249319249507
int nSeg = pStruct->nSegment;
249320249508
int i;
249321249509
249322249510
/* Figure out if this structure requires optimization. A structure does
249323249511
** not require optimization if either:
@@ -249343,10 +249531,11 @@
249343249531
}
249344249532
assert( pStruct->aLevel[i].nMerge<=nThis );
249345249533
}
249346249534
249347249535
nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
249536
+ assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
249348249537
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
249349249538
249350249539
if( pNew ){
249351249540
Fts5StructureLevel *pLvl;
249352249541
nByte = nSeg * sizeof(Fts5StructureSegment);
@@ -249919,12 +250108,16 @@
249919250108
/* The following are used for other full-token tokendata queries only. */
249920250109
int nIter;
249921250110
int nIterAlloc;
249922250111
Fts5PoslistReader *aPoslistReader;
249923250112
int *aPoslistToIter;
249924
- Fts5Iter *apIter[1];
250113
+ Fts5Iter *apIter[FLEXARRAY];
249925250114
};
250115
+
250116
+/* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */
250117
+#define SZ_FTS5TOKENDATAITER(N) \
250118
+ (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter))
249926250119
249927250120
/*
249928250121
** The two input arrays - a1[] and a2[] - are in sorted order. This function
249929250122
** merges the two arrays together and writes the result to output array
249930250123
** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
@@ -249993,11 +250186,11 @@
249993250186
}
249994250187
249995250188
/*
249996250189
** Sort the contents of the pT->aMap[] array.
249997250190
**
249998
-** The sorting algorithm requries a malloc(). If this fails, an error code
250191
+** The sorting algorithm requires a malloc(). If this fails, an error code
249999250192
** is left in Fts5Index.rc before returning.
250000250193
*/
250001250194
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
250002250195
Fts5TokenDataMap *aTmp = 0;
250003250196
int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
@@ -250184,11 +250377,11 @@
250184250377
if( iIdx==0
250185250378
&& p->pConfig->eDetail==FTS5_DETAIL_FULL
250186250379
&& p->pConfig->bPrefixInsttoken
250187250380
){
250188250381
s.pTokendata = &s2;
250189
- s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
250382
+ s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1));
250190250383
}
250191250384
250192250385
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
250193250386
s.xMerge = fts5MergeRowidLists;
250194250387
s.xAppend = fts5AppendRowid;
@@ -250312,19 +250505,21 @@
250312250505
** The %_data table is completely empty when this function is called. This
250313250506
** function populates it with the initial structure objects for each index,
250314250507
** and the initial version of the "averages" record (a zero-byte blob).
250315250508
*/
250316250509
static int sqlite3Fts5IndexReinit(Fts5Index *p){
250317
- Fts5Structure s;
250510
+ Fts5Structure *pTmp;
250511
+ u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
250318250512
fts5StructureInvalidate(p);
250319250513
fts5IndexDiscardData(p);
250320
- memset(&s, 0, sizeof(Fts5Structure));
250514
+ pTmp = (Fts5Structure*)tmpSpace;
250515
+ memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
250321250516
if( p->pConfig->bContentlessDelete ){
250322
- s.nOriginCntr = 1;
250517
+ pTmp->nOriginCntr = 1;
250323250518
}
250324250519
fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
250325
- fts5StructureWrite(p, &s);
250520
+ fts5StructureWrite(p, pTmp);
250326250521
return fts5IndexReturn(p);
250327250522
}
250328250523
250329250524
/*
250330250525
** Open a new Fts5Index handle. If the bCreate argument is true, create
@@ -250528,11 +250723,11 @@
250528250723
Fts5TokenDataIter *pRet = pIn;
250529250724
250530250725
if( p->rc==SQLITE_OK ){
250531250726
if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
250532250727
int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
250533
- int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
250728
+ int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
250534250729
Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
250535250730
250536250731
if( pNew==0 ){
250537250732
p->rc = SQLITE_NOMEM;
250538250733
}else{
@@ -251044,11 +251239,12 @@
251044251239
251045251240
memset(&ctx, 0, sizeof(ctx));
251046251241
251047251242
fts5BufferGrow(&p->rc, &token, nToken+1);
251048251243
assert( token.p!=0 || p->rc!=SQLITE_OK );
251049
- ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT));
251244
+ ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
251245
+ SZ_FTS5TOKENDATAITER(1));
251050251246
251051251247
if( p->rc==SQLITE_OK ){
251052251248
251053251249
/* Fill in the token prefix to search for */
251054251250
token.p[0] = FTS5_MAIN_PREFIX;
@@ -251175,11 +251371,12 @@
251175251371
assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
251176251372
assert( pIter->pTokenDataIter || pIter->nSeg>0 );
251177251373
if( pIter->nSeg>0 ){
251178251374
/* This is a prefix term iterator. */
251179251375
if( pT==0 ){
251180
- pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));
251376
+ pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
251377
+ SZ_FTS5TOKENDATAITER(1));
251181251378
pIter->pTokenDataIter = pT;
251182251379
}
251183251380
if( pT ){
251184251381
fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
251185251382
fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
@@ -252209,11 +252406,11 @@
252209252406
}
252210252407
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
252211252408
252212252409
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
252213252410
static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
252214
- int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */
252411
+ int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */
252215252412
fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);
252216252413
252217252414
if( iSegid==0 ){
252218252415
if( iKey==FTS5_AVERAGES_ROWID ){
252219252416
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
@@ -253170,13 +253367,15 @@
253170253367
struct Fts5Sorter {
253171253368
sqlite3_stmt *pStmt;
253172253369
i64 iRowid; /* Current rowid */
253173253370
const u8 *aPoslist; /* Position lists for current row */
253174253371
int nIdx; /* Number of entries in aIdx[] */
253175
- int aIdx[1]; /* Offsets into aPoslist for current row */
253372
+ int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */
253176253373
};
253177253374
253375
+/* Size (int bytes) of an Fts5Sorter object with N indexes */
253376
+#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64))
253178253377
253179253378
/*
253180253379
** Virtual-table cursor object.
253181253380
**
253182253381
** iSpecial:
@@ -254050,11 +254249,11 @@
254050254249
int rc;
254051254250
const char *zRank = pCsr->zRank;
254052254251
const char *zRankArgs = pCsr->zRankArgs;
254053254252
254054254253
nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
254055
- nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
254254
+ nByte = SZ_FTS5SORTER(nPhrase);
254056254255
pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
254057254256
if( pSorter==0 ) return SQLITE_NOMEM;
254058254257
memset(pSorter, 0, (size_t)nByte);
254059254258
pSorter->nIdx = nPhrase;
254060254259
@@ -256576,11 +256775,11 @@
256576256775
int nArg, /* Number of args */
256577256776
sqlite3_value **apUnused /* Function arguments */
256578256777
){
256579256778
assert( nArg==0 );
256580256779
UNUSED_PARAM2(nArg, apUnused);
256581
- sqlite3_result_text(pCtx, "fts5: 2025-02-25 16:39:51 6f0b6d95db17e69ac7e46a39f52770291ac4cfe43eea09add224946a6e11f04e", -1, SQLITE_TRANSIENT);
256780
+ sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT);
256582256781
}
256583256782
256584256783
/*
256585256784
** Implementation of fts5_locale(LOCALE, TEXT) function.
256586256785
**
256587256786
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** e6784af6d50f715338ae3218fc8ba1b89488 with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.50.0"
469 #define SQLITE_VERSION_NUMBER 3050000
470 #define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -5492,11 +5492,11 @@
5492 ** For all versions of SQLite up to and including 3.6.23.1, a call to
5493 ** [sqlite3_reset()] was required after sqlite3_step() returned anything
5494 ** other than [SQLITE_ROW] before any subsequent invocation of
5495 ** sqlite3_step(). Failure to reset the prepared statement using
5496 ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
5497 ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
5498 ** sqlite3_step() began
5499 ** calling [sqlite3_reset()] automatically in this circumstance rather
5500 ** than returning [SQLITE_MISUSE]. This is not considered a compatibility
5501 ** break because any application that ever receives an SQLITE_MISUSE error
5502 ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7388,10 +7388,12 @@
7388 ** ^Any callback set by a previous call to this function
7389 ** for the same database connection is overridden.
7390 **
7391 ** ^The second argument is a pointer to the function to invoke when a
7392 ** row is updated, inserted or deleted in a rowid table.
 
 
7393 ** ^The first argument to the callback is a copy of the third argument
7394 ** to sqlite3_update_hook().
7395 ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
7396 ** or [SQLITE_UPDATE], depending on the operation that caused the callback
7397 ** to be invoked.
@@ -15170,11 +15172,21 @@
15170 /*
15171 ** GCC does not define the offsetof() macro so we'll have to do it
15172 ** ourselves.
15173 */
15174 #ifndef offsetof
15175 #define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD))
 
 
 
 
 
 
 
 
 
 
15176 #endif
15177
15178 /*
15179 ** Macros to compute minimum and maximum of two numbers.
15180 */
@@ -17405,12 +17417,12 @@
17405 SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
17406 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
17407 SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
17408 #endif
17409
17410 /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
17411 ** each VDBE opcode.
17412 **
17413 ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
17414 ** comments in VDBE programs that show key decision points in the code
17415 ** generator.
17416 */
@@ -18945,12 +18957,16 @@
18945 u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
18946 Trigger *apTrigger[2];/* Triggers for aAction[] actions */
18947 struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
18948 int iFrom; /* Index of column in pFrom */
18949 char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
18950 } aCol[1]; /* One entry for each of nCol columns */
18951 };
 
 
 
 
18952
18953 /*
18954 ** SQLite supports many different ways to resolve a constraint
18955 ** error. ROLLBACK processing means that a constraint violation
18956 ** causes the operation in process to fail and for the current transaction
@@ -19009,13 +19025,16 @@
19009 u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
19010 u16 nKeyField; /* Number of key columns in the index */
19011 u16 nAllField; /* Total columns, including key plus others */
19012 sqlite3 *db; /* The database connection */
19013 u8 *aSortFlags; /* Sort order for each column. */
19014 CollSeq *aColl[1]; /* Collating sequence for each term of the key */
19015 };
19016
 
 
 
19017 /*
19018 ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
19019 */
19020 #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */
19021 #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */
@@ -19584,12 +19603,17 @@
19584 u16 iAlias; /* Index into Parse.aAlias[] for zName */
19585 } x;
19586 int iConstExprReg; /* Register in which Expr value is cached. Used only
19587 ** by Parse.pConstExpr */
19588 } u;
19589 } a[1]; /* One slot for each expression in the list */
19590 };
 
 
 
 
 
19591
19592 /*
19593 ** Allowed values for Expr.a.eEName
19594 */
19595 #define ENAME_NAME 0 /* The AS clause of a result set */
@@ -19614,13 +19638,16 @@
19614 */
19615 struct IdList {
19616 int nId; /* Number of identifiers on the list */
19617 struct IdList_item {
19618 char *zName; /* Name of the identifier */
19619 } a[1];
19620 };
19621
 
 
 
19622 /*
19623 ** Allowed values for IdList.eType, which determines which value of the a.u4
19624 ** is valid.
19625 */
19626 #define EU4_NONE 0 /* Does not use IdList.a.u4 */
@@ -19736,14 +19763,22 @@
19736 ** is used to hold the FROM clause of a SELECT statement. SrcList also
19737 ** represents the target tables for DELETE, INSERT, and UPDATE statements.
19738 **
19739 */
19740 struct SrcList {
19741 int nSrc; /* Number of tables or subqueries in the FROM clause */
19742 u32 nAlloc; /* Number of entries allocated in a[] below */
19743 SrcItem a[1]; /* One entry for each identifier on the list */
19744 };
 
 
 
 
 
 
 
 
19745
19746 /*
19747 ** Permitted values of the SrcList.a.jointype field
19748 */
19749 #define JT_INNER 0x01 /* Any kind of inner or cross join */
@@ -20804,12 +20839,16 @@
20804 */
20805 struct With {
20806 int nCte; /* Number of CTEs in the WITH clause */
20807 int bView; /* Belongs to the outermost Select of a view */
20808 With *pOuter; /* Containing WITH clause, or NULL */
20809 Cte a[1]; /* For each CTE in the WITH clause.... */
20810 };
 
 
 
 
20811
20812 /*
20813 ** The Cte object is not guaranteed to persist for the entire duration
20814 ** of code generation. (The query flattener or other parser tree
20815 ** edits might delete it.) The following object records information
@@ -20835,12 +20874,16 @@
20835 */
20836 struct DbClientData {
20837 DbClientData *pNext; /* Next in a linked list */
20838 void *pData; /* The data */
20839 void (*xDestructor)(void*); /* Destructor. Might be NULL */
20840 char zName[1]; /* Name of this client data. MUST BE LAST */
20841 };
 
 
 
 
20842
20843 #ifdef SQLITE_DEBUG
20844 /*
20845 ** An instance of the TreeView object is used for printing the content of
20846 ** data structures on sqlite3DebugPrintf() using a tree-like view.
@@ -22673,10 +22716,13 @@
22673 "EXTRA_IFNULLROW",
22674 #endif
22675 #ifdef SQLITE_EXTRA_INIT
22676 "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
22677 #endif
 
 
 
22678 #ifdef SQLITE_EXTRA_SHUTDOWN
22679 "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
22680 #endif
22681 #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
22682 "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
@@ -23657,16 +23703,23 @@
23657 #ifdef SQLITE_ENABLE_COLUMN_USED_MASK
23658 u64 maskUsed; /* Mask of columns used by this cursor */
23659 #endif
23660 VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */
23661
23662 /* 2*nField extra array elements allocated for aType[], beyond the one
23663 ** static element declared in the structure. nField total array slots for
23664 ** aType[] and nField+1 array slots for aOffset[] */
23665 u32 aType[1]; /* Type values record decode. MUST BE LAST */
23666 };
23667
 
 
 
 
 
 
 
 
23668 /* Return true if P is a null-only cursor
23669 */
23670 #define IsNullCursor(P) \
23671 ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)
23672
@@ -23919,13 +23972,20 @@
23919 int iOp; /* Instruction number of OP_Function */
23920 int isError; /* Error code returned by the function. */
23921 u8 enc; /* Encoding to use for results */
23922 u8 skipFlag; /* Skip accumulator loading if true */
23923 u16 argc; /* Number of arguments */
23924 sqlite3_value *argv[1]; /* Argument set */
23925 };
23926
 
 
 
 
 
 
 
23927
23928 /* The ScanStatus object holds a single value for the
23929 ** sqlite3_stmt_scanstatus() interface.
23930 **
23931 ** aAddrRange[]:
@@ -24055,11 +24115,11 @@
24055 struct PreUpdate {
24056 Vdbe *v;
24057 VdbeCursor *pCsr; /* Cursor to read old values from */
24058 int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
24059 u8 *aRecord; /* old.* database record */
24060 KeyInfo keyinfo;
24061 UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
24062 UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
24063 int iNewReg; /* Register for new.* values */
24064 int iBlobWrite; /* Value returned by preupdate_blobwrite() */
24065 i64 iKey1; /* First key value passed to hook */
@@ -24067,10 +24127,11 @@
24067 Mem oldipk; /* Memory cell holding "old" IPK value */
24068 Mem *aNew; /* Array of new.* values */
24069 Table *pTab; /* Schema object being updated */
24070 Index *pPk; /* PK index if pTab is WITHOUT ROWID */
24071 sqlite3_value **apDflt; /* Array of default values, if required */
 
24072 };
24073
24074 /*
24075 ** An instance of this object is used to pass an vector of values into
24076 ** OP_VFilter, the xFilter method of a virtual table. The vector is the
@@ -26000,11 +26061,11 @@
26000 ** Return the number of days after the most recent Sunday.
26001 **
26002 ** In other words, return the day of the week according
26003 ** to this code:
26004 **
26005 ** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
26006 */
26007 static int daysAfterSunday(DateTime *pDate){
26008 assert( pDate->validJD );
26009 return (int)((pDate->iJD+129600000)/86400000) % 7;
26010 }
@@ -32378,11 +32439,11 @@
32378 u32 nBack = 0;
32379 u32 nCtrl = 0;
32380 for(k=0; k<i; k++){
32381 if( escarg[k]=='\\' ){
32382 nBack++;
32383 }else if( escarg[k]<=0x1f ){
32384 nCtrl++;
32385 }
32386 }
32387 if( nCtrl || xtype==etESCAPE_q ){
32388 n += nBack + 5*nCtrl;
@@ -32416,11 +32477,11 @@
32416 bufpt[j++] = ch = escarg[i];
32417 if( ch==q ){
32418 bufpt[j++] = ch;
32419 }else if( ch=='\\' ){
32420 bufpt[j++] = '\\';
32421 }else if( ch<=0x1f ){
32422 bufpt[j-1] = '\\';
32423 bufpt[j++] = 'u';
32424 bufpt[j++] = '0';
32425 bufpt[j++] = '0';
32426 bufpt[j++] = ch>=0x10 ? '1' : '0';
@@ -37067,11 +37128,11 @@
37067 return 0;
37068 #endif
37069 }
37070
37071 /*
37072 ** Compute the absolute value of a 32-bit signed integer, of possible. Or
37073 ** if the integer has a value of -2147483648, return +2147483647
37074 */
37075 SQLITE_PRIVATE int sqlite3AbsInt32(int x){
37076 if( x>=0 ) return x;
37077 if( x==(int)0x80000000 ) return 0x7fffffff;
@@ -45614,11 +45675,11 @@
45614 sp.tv_sec = microseconds / 1000000;
45615 sp.tv_nsec = (microseconds % 1000000) * 1000;
45616
45617 /* Almost all modern unix systems support nanosleep(). But if you are
45618 ** compiling for one of the rare exceptions, you can use
45619 ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if
45620 ** usleep() is available) in order to bypass the use of nanosleep() */
45621 nanosleep(&sp, NULL);
45622
45623 UNUSED_PARAMETER(NotUsed);
45624 return microseconds;
@@ -56047,14 +56108,10 @@
56047 void *pStart, *pEnd; /* Bounds of global page cache memory */
56048 /* Above requires no mutex. Use mutex below for variable that follow. */
56049 sqlite3_mutex *mutex; /* Mutex for accessing the following: */
56050 PgFreeslot *pFree; /* Free page blocks */
56051 int nFreeSlot; /* Number of unused pcache slots */
56052 /* The following value requires a mutex to change. We skip the mutex on
56053 ** reading because (1) most platforms read a 32-bit integer atomically and
56054 ** (2) even if an incorrect value is read, no great harm is done since this
56055 ** is really just an optimization. */
56056 int bUnderPressure; /* True if low on PAGECACHE memory */
56057 } pcache1_g;
56058
56059 /*
56060 ** All code in this file should access the global structure above via the
@@ -56098,11 +56155,11 @@
56098 pcache1.szSlot = sz;
56099 pcache1.nSlot = pcache1.nFreeSlot = n;
56100 pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
56101 pcache1.pStart = pBuf;
56102 pcache1.pFree = 0;
56103 pcache1.bUnderPressure = 0;
56104 while( n-- ){
56105 p = (PgFreeslot*)pBuf;
56106 p->pNext = pcache1.pFree;
56107 pcache1.pFree = p;
56108 pBuf = (void*)&((char*)pBuf)[sz];
@@ -56166,11 +56223,11 @@
56166 sqlite3_mutex_enter(pcache1.mutex);
56167 p = (PgHdr1 *)pcache1.pFree;
56168 if( p ){
56169 pcache1.pFree = pcache1.pFree->pNext;
56170 pcache1.nFreeSlot--;
56171 pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
56172 assert( pcache1.nFreeSlot>=0 );
56173 sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
56174 sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
56175 }
56176 sqlite3_mutex_leave(pcache1.mutex);
@@ -56205,11 +56262,11 @@
56205 sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
56206 pSlot = (PgFreeslot*)p;
56207 pSlot->pNext = pcache1.pFree;
56208 pcache1.pFree = pSlot;
56209 pcache1.nFreeSlot++;
56210 pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
56211 assert( pcache1.nFreeSlot<=pcache1.nSlot );
56212 sqlite3_mutex_leave(pcache1.mutex);
56213 }else{
56214 assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
56215 sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
@@ -56336,11 +56393,11 @@
56336 ** allocating a new page cache entry in order to avoid stressing
56337 ** the heap even further.
56338 */
56339 static int pcache1UnderMemoryPressure(PCache1 *pCache){
56340 if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
56341 return pcache1.bUnderPressure;
56342 }else{
56343 return sqlite3HeapNearlyFull();
56344 }
56345 }
56346
@@ -66177,12 +66234,16 @@
66177 int iNext; /* Next slot in aIndex[] not yet returned */
66178 ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
66179 u32 *aPgno; /* Array of page numbers. */
66180 int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
66181 int iZero; /* Frame number associated with aPgno[0] */
66182 } aSegment[1]; /* One for every 32KB page in the wal-index */
66183 };
 
 
 
 
66184
66185 /*
66186 ** Define the parameters of the hash tables in the wal-index file. There
66187 ** is a hash-table following every HASHTABLE_NPAGE page numbers in the
66188 ** wal-index.
@@ -67540,12 +67601,11 @@
67540 assert( pWal->ckptLock && pWal->hdr.mxFrame>0 );
67541 iLast = pWal->hdr.mxFrame;
67542
67543 /* Allocate space for the WalIterator object. */
67544 nSegment = walFramePage(iLast) + 1;
67545 nByte = sizeof(WalIterator)
67546 + (nSegment-1)*sizeof(struct WalSegment)
67547 + iLast*sizeof(ht_slot);
67548 p = (WalIterator *)sqlite3_malloc64(nByte
67549 + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
67550 );
67551 if( !p ){
@@ -86029,16 +86089,14 @@
86029 int nArg, /* Number of argument */
86030 const FuncDef *pFunc, /* The function to be invoked */
86031 int eCallCtx /* Calling context */
86032 ){
86033 Vdbe *v = pParse->pVdbe;
86034 int nByte;
86035 int addr;
86036 sqlite3_context *pCtx;
86037 assert( v );
86038 nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*);
86039 pCtx = sqlite3DbMallocRawNN(pParse->db, nByte);
86040 if( pCtx==0 ){
86041 assert( pParse->db->mallocFailed );
86042 freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
86043 return 0;
86044 }
@@ -91110,25 +91168,26 @@
91110
91111 preupdate.v = v;
91112 preupdate.pCsr = pCsr;
91113 preupdate.op = op;
91114 preupdate.iNewReg = iReg;
91115 preupdate.keyinfo.db = db;
91116 preupdate.keyinfo.enc = ENC(db);
91117 preupdate.keyinfo.nKeyField = pTab->nCol;
91118 preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
 
91119 preupdate.iKey1 = iKey1;
91120 preupdate.iKey2 = iKey2;
91121 preupdate.pTab = pTab;
91122 preupdate.iBlobWrite = iBlobWrite;
91123
91124 db->pPreUpdate = &preupdate;
91125 db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
91126 db->pPreUpdate = 0;
91127 sqlite3DbFree(db, preupdate.aRecord);
91128 vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
91129 vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
91130 sqlite3VdbeMemRelease(&preupdate.oldipk);
91131 if( preupdate.aNew ){
91132 int i;
91133 for(i=0; i<pCsr->nField; i++){
91134 sqlite3VdbeMemRelease(&preupdate.aNew[i]);
@@ -93363,11 +93422,11 @@
93363 nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
93364 aRec = sqlite3DbMallocRaw(db, nRec);
93365 if( !aRec ) goto preupdate_old_out;
93366 rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
93367 if( rc==SQLITE_OK ){
93368 p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
93369 if( !p->pUnpacked ) rc = SQLITE_NOMEM;
93370 }
93371 if( rc!=SQLITE_OK ){
93372 sqlite3DbFree(db, aRec);
93373 goto preupdate_old_out;
@@ -93428,11 +93487,11 @@
93428 #ifdef SQLITE_ENABLE_API_ARMOR
93429 p = db!=0 ? db->pPreUpdate : 0;
93430 #else
93431 p = db->pPreUpdate;
93432 #endif
93433 return (p ? p->keyinfo.nKeyField : 0);
93434 }
93435 #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
93436
93437 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
93438 /*
@@ -93511,11 +93570,11 @@
93511 UnpackedRecord *pUnpack = p->pNewUnpacked;
93512 if( !pUnpack ){
93513 Mem *pData = &p->v->aMem[p->iNewReg];
93514 rc = ExpandBlob(pData);
93515 if( rc!=SQLITE_OK ) goto preupdate_new_out;
93516 pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
93517 if( !pUnpack ){
93518 rc = SQLITE_NOMEM;
93519 goto preupdate_new_out;
93520 }
93521 p->pNewUnpacked = pUnpack;
@@ -94305,13 +94364,13 @@
94305 */
94306 Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
94307
94308 i64 nByte;
94309 VdbeCursor *pCx = 0;
94310 nByte =
94311 ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
94312 (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
94313
94314 assert( iCur>=0 && iCur<p->nCursor );
94315 if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
94316 sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]);
94317 p->apCsr[iCur] = 0;
@@ -94340,12 +94399,12 @@
94340 memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
94341 pCx->eCurType = eCurType;
94342 pCx->nField = nField;
94343 pCx->aOffset = &pCx->aType[nField];
94344 if( eCurType==CURTYPE_BTREE ){
94345 pCx->uc.pCursor = (BtCursor*)
94346 &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
94347 sqlite3BtreeCursorZero(pCx->uc.pCursor);
94348 }
94349 return pCx;
94350 }
94351
@@ -100083,11 +100142,11 @@
100083 pCrsr = pC->uc.pCursor;
100084
100085 /* The OP_RowData opcodes always follow OP_NotExists or
100086 ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
100087 ** that might invalidate the cursor.
100088 ** If this where not the case, on of the following assert()s
100089 ** would fail. Should this ever change (because of changes in the code
100090 ** generator) then the fix would be to insert a call to
100091 ** sqlite3VdbeCursorMoveto().
100092 */
100093 assert( pC->deferredMoveto==0 );
@@ -101732,11 +101791,11 @@
101732 ** cell in which to store the accumulation. Be careful that the memory
101733 ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits.
101734 **
101735 ** Note: We could avoid this by using a regular memory cell from aMem[] for
101736 ** the accumulator, instead of allocating one here. */
101737 nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) );
101738 pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem));
101739 if( pCtx==0 ) goto no_mem;
101740 pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc);
101741 assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) );
101742
@@ -103390,10 +103449,11 @@
103390 int iCol; /* Index of zColumn in row-record */
103391 int rc = SQLITE_OK;
103392 char *zErr = 0;
103393 Table *pTab;
103394 Incrblob *pBlob = 0;
 
103395 Parse sParse;
103396
103397 #ifdef SQLITE_ENABLE_API_ARMOR
103398 if( ppBlob==0 ){
103399 return SQLITE_MISUSE_BKPT;
@@ -103435,11 +103495,14 @@
103435 if( pTab && IsView(pTab) ){
103436 pTab = 0;
103437 sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
103438 }
103439 #endif
103440 if( !pTab ){
 
 
 
103441 if( sParse.zErrMsg ){
103442 sqlite3DbFree(db, zErr);
103443 zErr = sParse.zErrMsg;
103444 sParse.zErrMsg = 0;
103445 }
@@ -103446,11 +103509,11 @@
103446 rc = SQLITE_ERROR;
103447 sqlite3BtreeLeaveAll(db);
103448 goto blob_open_out;
103449 }
103450 pBlob->pTab = pTab;
103451 pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
103452
103453 /* Now search pTab for the exact column. */
103454 iCol = sqlite3ColumnIndex(pTab, zColumn);
103455 if( iCol<0 ){
103456 sqlite3DbFree(db, zErr);
@@ -103530,11 +103593,10 @@
103530 {OP_Column, 0, 0, 1}, /* 3 */
103531 {OP_ResultRow, 1, 0, 0}, /* 4 */
103532 {OP_Halt, 0, 0, 0}, /* 5 */
103533 };
103534 Vdbe *v = (Vdbe *)pBlob->pStmt;
103535 int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
103536 VdbeOp *aOp;
103537
103538 sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
103539 pTab->pSchema->schema_cookie,
103540 pTab->pSchema->iGeneration);
@@ -104108,12 +104170,15 @@
104108 u8 bUsePMA; /* True if one or more PMAs created */
104109 u8 bUseThreads; /* True to use background threads */
104110 u8 iPrev; /* Previous thread used to flush PMA */
104111 u8 nTask; /* Size of aTask[] array */
104112 u8 typeMask;
104113 SortSubtask aTask[1]; /* One or more subtasks */
104114 };
 
 
 
104115
104116 #define SORTER_TYPE_INTEGER 0x01
104117 #define SORTER_TYPE_TEXT 0x02
104118
104119 /*
@@ -104742,12 +104807,12 @@
104742 assert( pCsr->pKeyInfo );
104743 assert( !pCsr->isEphemeral );
104744 assert( pCsr->eCurType==CURTYPE_SORTER );
104745 assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
104746 < 0x7fffffff );
104747 szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
104748 sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
104749
104750 pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
104751 pCsr->uc.pSorter = pSorter;
104752 if( pSorter==0 ){
104753 rc = SQLITE_NOMEM_BKPT;
@@ -105207,10 +105272,14 @@
105207 }
105208
105209 p->u.pNext = 0;
105210 for(i=0; aSlot[i]; i++){
105211 p = vdbeSorterMerge(pTask, p, aSlot[i]);
 
 
 
 
105212 aSlot[i] = 0;
105213 }
105214 aSlot[i] = p;
105215 p = pNext;
105216 }
@@ -109981,32 +110050,34 @@
109981 Table *pTab, /* The table being referenced, or NULL */
109982 int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
109983 Expr *pExpr, /* Expression to resolve. May be NULL. */
109984 ExprList *pList /* Expression list to resolve. May be NULL. */
109985 ){
109986 SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
109987 NameContext sNC; /* Name context for pParse->pNewTable */
109988 int rc;
 
109989
109990 assert( type==0 || pTab!=0 );
109991 assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
109992 || type==NC_GenCol || pTab==0 );
109993 memset(&sNC, 0, sizeof(sNC));
109994 memset(&sSrc, 0, sizeof(sSrc));
 
109995 if( pTab ){
109996 sSrc.nSrc = 1;
109997 sSrc.a[0].zName = pTab->zName;
109998 sSrc.a[0].pSTab = pTab;
109999 sSrc.a[0].iCursor = -1;
110000 if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
110001 /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
110002 ** schema elements */
110003 type |= NC_FromDDL;
110004 }
110005 }
110006 sNC.pParse = pParse;
110007 sNC.pSrcList = &sSrc;
110008 sNC.ncFlags = type | NC_IsDDL;
110009 if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
110010 if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
110011 return rc;
110012 }
@@ -111751,11 +111822,11 @@
111751 */
111752 #ifndef SQLITE_OMIT_CTE
111753 SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
111754 With *pRet = 0;
111755 if( p ){
111756 sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
111757 pRet = sqlite3DbMallocZero(db, nByte);
111758 if( pRet ){
111759 int i;
111760 pRet->nCte = p->nCte;
111761 for(i=0; i<p->nCte; i++){
@@ -111878,15 +111949,13 @@
111878 #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
111879 || !defined(SQLITE_OMIT_SUBQUERY)
111880 SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
111881 SrcList *pNew;
111882 int i;
111883 int nByte;
111884 assert( db!=0 );
111885 if( p==0 ) return 0;
111886 nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
111887 pNew = sqlite3DbMallocRawNN(db, nByte );
111888 if( pNew==0 ) return 0;
111889 pNew->nSrc = pNew->nAlloc = p->nSrc;
111890 for(i=0; i<p->nSrc; i++){
111891 SrcItem *pNewItem = &pNew->a[i];
111892 const SrcItem *pOldItem = &p->a[i];
@@ -111944,11 +112013,11 @@
111944 SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
111945 IdList *pNew;
111946 int i;
111947 assert( db!=0 );
111948 if( p==0 ) return 0;
111949 pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) );
111950 if( pNew==0 ) return 0;
111951 pNew->nId = p->nId;
111952 for(i=0; i<p->nId; i++){
111953 struct IdList_item *pNewItem = &pNew->a[i];
111954 const struct IdList_item *pOldItem = &p->a[i];
@@ -112028,11 +112097,11 @@
112028 Expr *pExpr /* Expression to be appended. Might be NULL */
112029 ){
112030 struct ExprList_item *pItem;
112031 ExprList *pList;
112032
112033 pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 );
112034 if( pList==0 ){
112035 sqlite3ExprDelete(db, pExpr);
112036 return 0;
112037 }
112038 pList->nAlloc = 4;
@@ -112048,12 +112117,11 @@
112048 Expr *pExpr /* Expression to be appended. Might be NULL */
112049 ){
112050 struct ExprList_item *pItem;
112051 ExprList *pNew;
112052 pList->nAlloc *= 2;
112053 pNew = sqlite3DbRealloc(db, pList,
112054 sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0]));
112055 if( pNew==0 ){
112056 sqlite3ExprListDelete(db, pList);
112057 sqlite3ExprDelete(db, pExpr);
112058 return 0;
112059 }else{
@@ -114685,11 +114753,11 @@
114685 return -1; /* Not found */
114686 }
114687
114688
114689 /*
114690 ** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
114691 ** function checks the Parse.pIdxPartExpr list to see if this column
114692 ** can be replaced with a constant value. If so, it generates code to
114693 ** put the constant value in a register (ideally, but not necessarily,
114694 ** register iTarget) and returns the register number.
114695 **
@@ -118531,10 +118599,11 @@
118531 sqlite3 *db, /* Database handle */
118532 const char *zSql, /* SQL to parse */
118533 int bTemp /* True if SQL is from temp schema */
118534 ){
118535 int rc;
 
118536
118537 sqlite3ParseObjectInit(p, db);
118538 if( zSql==0 ){
118539 return SQLITE_NOMEM;
118540 }
@@ -118549,11 +118618,15 @@
118549 db->init.iDb = (u8)iDb;
118550 }
118551 p->eParseMode = PARSE_MODE_RENAME;
118552 p->db = db;
118553 p->nQueryLoop = 1;
 
 
 
118554 rc = sqlite3RunParser(p, zSql);
 
118555 if( db->mallocFailed ) rc = SQLITE_NOMEM;
118556 if( rc==SQLITE_OK
118557 && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
118558 ){
118559 rc = SQLITE_CORRUPT_BKPT;
@@ -119445,11 +119518,11 @@
119445 int rc;
119446 Parse sParse;
119447 u64 flags = db->flags;
119448 if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
119449 rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
119450 db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
119451 if( rc==SQLITE_OK ){
119452 if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
119453 NameContext sNC;
119454 memset(&sNC, 0, sizeof(sNC));
119455 sNC.pParse = &sParse;
@@ -126268,11 +126341,11 @@
126268 "columns in the referenced table");
126269 goto fk_end;
126270 }else{
126271 nCol = pFromCol->nExpr;
126272 }
126273 nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
126274 if( pToCol ){
126275 for(i=0; i<pToCol->nExpr; i++){
126276 nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
126277 }
126278 }
@@ -127327,16 +127400,15 @@
127327 */
127328 SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
127329 sqlite3 *db = pParse->db;
127330 int i;
127331 if( pList==0 ){
127332 pList = sqlite3DbMallocZero(db, sizeof(IdList) );
127333 if( pList==0 ) return 0;
127334 }else{
127335 IdList *pNew;
127336 pNew = sqlite3DbRealloc(db, pList,
127337 sizeof(IdList) + pList->nId*sizeof(pList->a));
127338 if( pNew==0 ){
127339 sqlite3IdListDelete(db, pList);
127340 return 0;
127341 }
127342 pList = pNew;
@@ -127431,12 +127503,11 @@
127431 sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d",
127432 SQLITE_MAX_SRCLIST);
127433 return 0;
127434 }
127435 if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST;
127436 pNew = sqlite3DbRealloc(db, pSrc,
127437 sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) );
127438 if( pNew==0 ){
127439 assert( db->mallocFailed );
127440 return 0;
127441 }
127442 pSrc = pNew;
@@ -127507,11 +127578,11 @@
127507 assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
127508 assert( pParse!=0 );
127509 assert( pParse->db!=0 );
127510 db = pParse->db;
127511 if( pList==0 ){
127512 pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) );
127513 if( pList==0 ) return 0;
127514 pList->nAlloc = 1;
127515 pList->nSrc = 1;
127516 memset(&pList->a[0], 0, sizeof(pList->a[0]));
127517 pList->a[0].iCursor = -1;
@@ -128393,14 +128464,13 @@
128393 }
128394 }
128395 }
128396
128397 if( pWith ){
128398 sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
128399 pNew = sqlite3DbRealloc(db, pWith, nByte);
128400 }else{
128401 pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
128402 }
128403 assert( (pNew!=0 && zName!=0) || db->mallocFailed );
128404
128405 if( db->mallocFailed ){
128406 sqlite3CteDelete(db, pCte);
@@ -131933,11 +132003,11 @@
131933 **
131934 ** The SUM() function follows the (broken) SQL standard which means
131935 ** that it returns NULL if it sums over no inputs. TOTAL returns
131936 ** 0.0 in that case. In addition, TOTAL always returns a float where
131937 ** SUM might return an integer if it never encounters a floating point
131938 ** value. TOTAL never fails, but SUM might through an exception if
131939 ** it overflows an integer.
131940 */
131941 static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
131942 SumCtx *p;
131943 int type;
@@ -139748,52 +139818,52 @@
139748 /* 11 */ "notnull",
139749 /* 12 */ "dflt_value",
139750 /* 13 */ "pk",
139751 /* 14 */ "hidden",
139752 /* table_info reuses 8 */
139753 /* 15 */ "schema", /* Used by: table_list */
139754 /* 16 */ "name",
139755 /* 17 */ "type",
139756 /* 18 */ "ncol",
139757 /* 19 */ "wr",
139758 /* 20 */ "strict",
139759 /* 21 */ "seqno", /* Used by: index_xinfo */
139760 /* 22 */ "cid",
139761 /* 23 */ "name",
139762 /* 24 */ "desc",
139763 /* 25 */ "coll",
139764 /* 26 */ "key",
139765 /* 27 */ "name", /* Used by: function_list */
139766 /* 28 */ "builtin",
139767 /* 29 */ "type",
139768 /* 30 */ "enc",
139769 /* 31 */ "narg",
139770 /* 32 */ "flags",
139771 /* 33 */ "tbl", /* Used by: stats */
139772 /* 34 */ "idx",
139773 /* 35 */ "wdth",
139774 /* 36 */ "hght",
139775 /* 37 */ "flgs",
139776 /* 38 */ "seq", /* Used by: index_list */
139777 /* 39 */ "name",
139778 /* 40 */ "unique",
139779 /* 41 */ "origin",
139780 /* 42 */ "partial",
139781 /* 43 */ "table", /* Used by: foreign_key_check */
139782 /* 44 */ "rowid",
139783 /* 45 */ "parent",
139784 /* 46 */ "fkid",
139785 /* index_info reuses 21 */
139786 /* 47 */ "seq", /* Used by: database_list */
139787 /* 48 */ "name",
139788 /* 49 */ "file",
139789 /* 50 */ "busy", /* Used by: wal_checkpoint */
139790 /* 51 */ "log",
139791 /* 52 */ "checkpointed",
139792 /* collation_list reuses 38 */
139793 /* 53 */ "database", /* Used by: lock_status */
139794 /* 54 */ "status",
 
139795 /* 55 */ "cache_size", /* Used by: default_cache_size */
139796 /* module_list pragma_list reuses 9 */
139797 /* 56 */ "timeout", /* Used by: busy_timeout */
139798 };
139799
@@ -139882,11 +139952,11 @@
139882 #endif
139883 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139884 {/* zName: */ "collation_list",
139885 /* ePragTyp: */ PragTyp_COLLATION_LIST,
139886 /* ePragFlg: */ PragFlg_Result0,
139887 /* ColNames: */ 38, 2,
139888 /* iArg: */ 0 },
139889 #endif
139890 #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
139891 {/* zName: */ "compile_options",
139892 /* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
@@ -139917,11 +139987,11 @@
139917 #endif
139918 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139919 {/* zName: */ "database_list",
139920 /* ePragTyp: */ PragTyp_DATABASE_LIST,
139921 /* ePragFlg: */ PragFlg_Result0,
139922 /* ColNames: */ 47, 3,
139923 /* iArg: */ 0 },
139924 #endif
139925 #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
139926 {/* zName: */ "default_cache_size",
139927 /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
@@ -139997,11 +140067,11 @@
139997 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139998 #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
139999 {/* zName: */ "function_list",
140000 /* ePragTyp: */ PragTyp_FUNCTION_LIST,
140001 /* ePragFlg: */ PragFlg_Result0,
140002 /* ColNames: */ 27, 6,
140003 /* iArg: */ 0 },
140004 #endif
140005 #endif
140006 {/* zName: */ "hard_heap_limit",
140007 /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT,
@@ -140026,21 +140096,21 @@
140026 #endif
140027 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
140028 {/* zName: */ "index_info",
140029 /* ePragTyp: */ PragTyp_INDEX_INFO,
140030 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140031 /* ColNames: */ 21, 3,
140032 /* iArg: */ 0 },
140033 {/* zName: */ "index_list",
140034 /* ePragTyp: */ PragTyp_INDEX_LIST,
140035 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140036 /* ColNames: */ 38, 5,
140037 /* iArg: */ 0 },
140038 {/* zName: */ "index_xinfo",
140039 /* ePragTyp: */ PragTyp_INDEX_INFO,
140040 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140041 /* ColNames: */ 21, 6,
140042 /* iArg: */ 1 },
140043 #endif
140044 #if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
140045 {/* zName: */ "integrity_check",
140046 /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
@@ -140215,11 +140285,11 @@
140215 #endif
140216 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
140217 {/* zName: */ "stats",
140218 /* ePragTyp: */ PragTyp_STATS,
140219 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
140220 /* ColNames: */ 33, 5,
140221 /* iArg: */ 0 },
140222 #endif
140223 #if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
140224 {/* zName: */ "synchronous",
140225 /* ePragTyp: */ PragTyp_SYNCHRONOUS,
@@ -140234,11 +140304,11 @@
140234 /* ColNames: */ 8, 6,
140235 /* iArg: */ 0 },
140236 {/* zName: */ "table_list",
140237 /* ePragTyp: */ PragTyp_TABLE_LIST,
140238 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1,
140239 /* ColNames: */ 15, 6,
140240 /* iArg: */ 0 },
140241 {/* zName: */ "table_xinfo",
140242 /* ePragTyp: */ PragTyp_TABLE_INFO,
140243 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140244 /* ColNames: */ 8, 7,
@@ -140311,11 +140381,11 @@
140311 /* ColNames: */ 0, 0,
140312 /* iArg: */ 0 },
140313 {/* zName: */ "wal_checkpoint",
140314 /* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
140315 /* ePragFlg: */ PragFlg_NeedSchema,
140316 /* ColNames: */ 50, 3,
140317 /* iArg: */ 0 },
140318 #endif
140319 #if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
140320 {/* zName: */ "writable_schema",
140321 /* ePragTyp: */ PragTyp_FLAG,
@@ -140333,11 +140403,11 @@
140333 ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
140334 ** will be run with an analysis_limit set to the lessor of the value of
140335 ** the following macro or to the actual analysis_limit if it is non-zero,
140336 ** in order to prevent PRAGMA optimize from running for too long.
140337 **
140338 ** The value of 2000 is chosen emperically so that the worst-case run-time
140339 ** for PRAGMA optimize does not exceed 100 milliseconds against a variety
140340 ** of test databases on a RaspberryPI-4 compiled using -Os and without
140341 ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
140342 ** this paragraph, "worst-case" means that ANALYZE ends up being
140343 ** run on every table in the database. The worst case typically only
@@ -144622,11 +144692,11 @@
144622 pNew->iOffset = 0;
144623 pNew->selId = ++pParse->nSelect;
144624 pNew->addrOpenEphm[0] = -1;
144625 pNew->addrOpenEphm[1] = -1;
144626 pNew->nSelectRow = 0;
144627 if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc));
144628 pNew->pSrc = pSrc;
144629 pNew->pWhere = pWhere;
144630 pNew->pGroupBy = pGroupBy;
144631 pNew->pHaving = pHaving;
144632 pNew->pOrderBy = pOrderBy;
@@ -146005,20 +146075,20 @@
146005 /*
146006 ** Allocate a KeyInfo object sufficient for an index of N key columns and
146007 ** X extra columns.
146008 */
146009 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
146010 int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
146011 KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
146012 if( p ){
146013 p->aSortFlags = (u8*)&p->aColl[N+X];
146014 p->nKeyField = (u16)N;
146015 p->nAllField = (u16)(N+X);
146016 p->enc = ENC(db);
146017 p->db = db;
146018 p->nRef = 1;
146019 memset(&p[1], 0, nExtra);
146020 }else{
146021 return (KeyInfo*)sqlite3OomFault(db);
146022 }
146023 return p;
146024 }
@@ -150530,11 +150600,11 @@
150530 }
150531 pTabList = p->pSrc;
150532 pEList = p->pEList;
150533 if( pParse->pWith && (p->selFlags & SF_View) ){
150534 if( p->pWith==0 ){
150535 p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With));
150536 if( p->pWith==0 ){
150537 return WRC_Abort;
150538 }
150539 }
150540 p->pWith->bView = 1;
@@ -151669,10 +151739,11 @@
151669 ** * The subquery is a UNION ALL of two or more terms
151670 ** * The subquery does not have a LIMIT clause
151671 ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
151672 ** * The outer query is a simple count(*) with no WHERE clause or other
151673 ** extraneous syntax.
 
151674 **
151675 ** Return TRUE if the optimization is undertaken.
151676 */
151677 static int countOfViewOptimization(Parse *pParse, Select *p){
151678 Select *pSub, *pPrior;
@@ -151701,11 +151772,15 @@
151701 if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */
151702 do{
151703 if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
151704 if( pSub->pWhere ) return 0; /* No WHERE clause */
151705 if( pSub->pLimit ) return 0; /* No LIMIT clause */
151706 if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
 
 
 
 
151707 assert( pSub->pHaving==0 ); /* Due to the previous */
151708 pSub = pSub->pPrior; /* Repeat over compound */
151709 }while( pSub );
151710
151711 /* If we reach this point then it is OK to perform the transformation */
@@ -151713,11 +151788,11 @@
151713 db = pParse->db;
151714 pCount = pExpr;
151715 pExpr = 0;
151716 pSub = sqlite3SubqueryDetach(db, pFrom);
151717 sqlite3SrcListDelete(db, p->pSrc);
151718 p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
151719 while( pSub ){
151720 Expr *pTerm;
151721 pPrior = pSub->pPrior;
151722 pSub->pPrior = 0;
151723 pSub->pNext = 0;
@@ -154504,11 +154579,12 @@
154504 Vdbe *v = pParse->pVdbe;
154505 sqlite3 *db = pParse->db;
154506 ExprList *pNew;
154507 Returning *pReturning;
154508 Select sSelect;
154509 SrcList sFrom;
 
154510
154511 assert( v!=0 );
154512 if( !pParse->bReturning ){
154513 /* This RETURNING trigger must be for a different statement as
154514 ** this statement lacks a RETURNING clause. */
@@ -154520,17 +154596,18 @@
154520 if( pTrigger != &(pReturning->retTrig) ){
154521 /* This RETURNING trigger is for a different statement */
154522 return;
154523 }
154524 memset(&sSelect, 0, sizeof(sSelect));
154525 memset(&sFrom, 0, sizeof(sFrom));
 
154526 sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
154527 sSelect.pSrc = &sFrom;
154528 sFrom.nSrc = 1;
154529 sFrom.a[0].pSTab = pTab;
154530 sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */
154531 sFrom.a[0].iCursor = -1;
154532 sqlite3SelectPrep(pParse, &sSelect, 0);
154533 if( pParse->nErr==0 ){
154534 assert( db->mallocFailed==0 );
154535 sqlite3GenerateColumnNames(pParse, &sSelect);
154536 }
@@ -156927,11 +157004,11 @@
156927 saved_flags = db->flags;
156928 saved_mDbFlags = db->mDbFlags;
156929 saved_nChange = db->nChange;
156930 saved_nTotalChange = db->nTotalChange;
156931 saved_mTrace = db->mTrace;
156932 db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
156933 db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
156934 db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
156935 | SQLITE_Defensive | SQLITE_CountRows);
156936 db->mTrace = 0;
156937
@@ -159056,13 +159133,18 @@
159056 WhereLoop *pLoops; /* List of all WhereLoop objects */
159057 WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
159058 Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
159059 WhereClause sWC; /* Decomposition of the WHERE clause */
159060 WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
159061 WhereLevel a[1]; /* Information about each nest loop in WHERE */
159062 };
159063
 
 
 
 
 
159064 /*
159065 ** Private interfaces - callable only by other where.c routines.
159066 **
159067 ** where.c:
159068 */
@@ -161509,12 +161591,11 @@
161509 */
161510 if( pWInfo->nLevel>1 ){
161511 int nNotReady; /* The number of notReady tables */
161512 SrcItem *origSrc; /* Original list of tables */
161513 nNotReady = pWInfo->nLevel - iLevel - 1;
161514 pOrTab = sqlite3DbMallocRawNN(db,
161515 sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
161516 if( pOrTab==0 ) return notReady;
161517 pOrTab->nAlloc = (u8)(nNotReady + 1);
161518 pOrTab->nSrc = pOrTab->nAlloc;
161519 memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
161520 origSrc = pWInfo->pTabList->a;
@@ -162053,11 +162134,12 @@
162053 Expr *pSubWhere = 0;
162054 WhereClause *pWC = &pWInfo->sWC;
162055 WhereInfo *pSubWInfo;
162056 WhereLoop *pLoop = pLevel->pWLoop;
162057 SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
162058 SrcList sFrom;
 
162059 Bitmask mAll = 0;
162060 int k;
162061
162062 ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
162063 sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
@@ -162097,17 +162179,18 @@
162097 if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
162098 pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
162099 sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
162100 }
162101 }
162102 sFrom.nSrc = 1;
162103 sFrom.nAlloc = 1;
162104 memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem));
162105 sFrom.a[0].fg.jointype = 0;
 
162106 assert( pParse->withinRJSubrtn < 100 );
162107 pParse->withinRJSubrtn++;
162108 pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
162109 WHERE_RIGHT_JOIN, 0);
162110 if( pSubWInfo ){
162111 int iCur = pLevel->iTabCur;
162112 int r = ++pParse->nMem;
162113 int nPk;
@@ -164091,15 +164174,20 @@
164091 WhereClause *pWC; /* The Where clause being analyzed */
164092 Parse *pParse; /* The parsing context */
164093 int eDistinct; /* Value to return from sqlite3_vtab_distinct() */
164094 u32 mIn; /* Mask of terms that are <col> IN (...) */
164095 u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */
164096 sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST
164097 ** because extra space is allocated to hold up
164098 ** to nTerm such values */
164099 };
164100
 
 
 
 
 
164101 /* Forward declaration of methods */
164102 static int whereLoopResize(sqlite3*, WhereLoop*, int);
164103
164104 /*
164105 ** Return the estimated number of output rows from a WHERE clause
@@ -165573,12 +165661,12 @@
165573
165574 /* Allocate the sqlite3_index_info structure
165575 */
165576 pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
165577 + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
165578 + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)
165579 + sizeof(sqlite3_value*)*nTerm );
165580 if( pIdxInfo==0 ){
165581 sqlite3ErrorMsg(pParse, "out of memory");
165582 return 0;
165583 }
165584 pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
@@ -170768,14 +170856,11 @@
170768 ** struct, the contents of WhereInfo.a[], the WhereClause structure
170769 ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
170770 ** field (type Bitmask) it must be aligned on an 8-byte boundary on
170771 ** some architectures. Hence the ROUND8() below.
170772 */
170773 nByteWInfo = ROUND8P(sizeof(WhereInfo));
170774 if( nTabList>1 ){
170775 nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
170776 }
170777 pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
170778 if( db->mallocFailed ){
170779 sqlite3DbFree(db, pWInfo);
170780 pWInfo = 0;
170781 goto whereBeginError;
@@ -181607,11 +181692,15 @@
181607 tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
181608 }else if( tokenType==TK_FILTER ){
181609 assert( n==6 );
181610 tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
181611 #endif /* SQLITE_OMIT_WINDOWFUNC */
181612 }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){
 
 
 
 
181613 zSql += n;
181614 continue;
181615 }else if( tokenType!=TK_QNUMBER ){
181616 Token x;
181617 x.z = zSql;
@@ -182502,10 +182591,18 @@
182502 }
182503 #endif
182504 if( rc==SQLITE_OK ){
182505 sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
182506 sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
 
 
 
 
 
 
 
 
182507 sqlite3MemoryBarrier();
182508 sqlite3GlobalConfig.isInit = 1;
182509 #ifdef SQLITE_EXTRA_INIT
182510 bRunExtraInit = 1;
182511 #endif
@@ -184063,10 +184160,14 @@
184063 sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt));
184064 sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC);
184065 }
184066 }
184067 sqlite3BtreeLeaveAll(db);
 
 
 
 
184068 #endif
184069 return SQLITE_OK;
184070 }
184071
184072 /*
@@ -186032,11 +186133,11 @@
186032 }else if( pData==0 ){
186033 sqlite3_mutex_leave(db->mutex);
186034 return SQLITE_OK;
186035 }else{
186036 size_t n = strlen(zName);
186037 p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
186038 if( p==0 ){
186039 if( xDestructor ) xDestructor(pData);
186040 sqlite3_mutex_leave(db->mutex);
186041 return SQLITE_NOMEM;
186042 }
@@ -186398,12 +186499,12 @@
186398 #endif
186399
186400 /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
186401 **
186402 ** If b is true, then activate the SQLITE_FkNoAction setting. If b is
186403 ** false then clearn that setting. If the SQLITE_FkNoAction setting is
186404 ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
186405 ** they were NO ACTION, regardless of how they are defined.
186406 **
186407 ** NB: One must usually run "PRAGMA writable_schema=RESET" after
186408 ** using this test-control, before it will take full effect. failing
186409 ** to reset the schema can result in some unexpected behavior.
@@ -187746,11 +187847,11 @@
187746 ** }
187747 **
187748 ** Here, array { X } means zero or more occurrences of X, adjacent in
187749 ** memory. A "position" is an index of a token in the token stream
187750 ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur
187751 ** in the same logical place as the position element, and act as sentinals
187752 ** ending a position list array. POS_END is 0. POS_COLUMN is 1.
187753 ** The positions numbers are not stored literally but rather as two more
187754 ** than the difference from the prior position, or the just the position plus
187755 ** 2 for the first position. Example:
187756 **
@@ -188433,10 +188534,23 @@
188433
188434 #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
188435 #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
188436
188437 #define deliberate_fall_through
 
 
 
 
 
 
 
 
 
 
 
 
 
188438
188439 #endif /* SQLITE_AMALGAMATION */
188440
188441 #ifdef SQLITE_DEBUG
188442 SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
@@ -188538,11 +188652,11 @@
188538 int inTransaction; /* True after xBegin but before xCommit/xRollback */
188539 int mxSavepoint; /* Largest valid xSavepoint integer */
188540 #endif
188541
188542 #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
188543 /* True to disable the incremental doclist optimization. This is controled
188544 ** by special insert command 'test-no-incr-doclist'. */
188545 int bNoIncrDoclist;
188546
188547 /* Number of segments in a level */
188548 int nMergeCount;
@@ -188590,11 +188704,11 @@
188590 #define FTS3_EVAL_NEXT 1
188591 #define FTS3_EVAL_MATCHINFO 2
188592
188593 /*
188594 ** The Fts3Cursor.eSearch member is always set to one of the following.
188595 ** Actualy, Fts3Cursor.eSearch can be greater than or equal to
188596 ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index
188597 ** of the column to be searched. For example, in
188598 **
188599 ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
188600 ** SELECT docid FROM ex1 WHERE b MATCH 'one two three';
@@ -188663,12 +188777,16 @@
188663 /* Variables below this point are populated by fts3_expr.c when parsing
188664 ** a MATCH expression. Everything above is part of the evaluation phase.
188665 */
188666 int nToken; /* Number of tokens in the phrase */
188667 int iColumn; /* Index of column this phrase must match */
188668 Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
188669 };
 
 
 
 
188670
188671 /*
188672 ** A tree of these objects forms the RHS of a MATCH operator.
188673 **
188674 ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
@@ -191243,11 +191361,11 @@
191243 **
191244 ** The space required to store the output is therefore the sum of the
191245 ** sizes of the two inputs, plus enough space for exactly one of the input
191246 ** docids to grow.
191247 **
191248 ** A symetric argument may be made if the doclists are in descending
191249 ** order.
191250 */
191251 aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
191252 if( !aOut ) return SQLITE_NOMEM;
191253
@@ -193341,11 +193459,11 @@
193341 **
193342 ** * features at least one token that uses an incremental doclist, and
193343 **
193344 ** * does not contain any deferred tokens.
193345 **
193346 ** Advance it to the next matching documnent in the database and populate
193347 ** the Fts3Doclist.pList and nList fields.
193348 **
193349 ** If there is no "next" entry and no error occurs, then *pbEof is set to
193350 ** 1 before returning. Otherwise, if no error occurs and the iterator is
193351 ** successfully advanced, *pbEof is set to 0.
@@ -194348,11 +194466,11 @@
194348
194349 return rc;
194350 }
194351
194352 /*
194353 ** Restart interation for expression pExpr so that the next call to
194354 ** fts3EvalNext() visits the first row. Do not allow incremental
194355 ** loading or merging of phrase doclists for this iteration.
194356 **
194357 ** If *pRc is other than SQLITE_OK when this function is called, it is
194358 ** a no-op. If an error occurs within this function, *pRc is set to an
@@ -195539,10 +195657,27 @@
195539 /*
195540 ** Function getNextNode(), which is called by fts3ExprParse(), may itself
195541 ** call fts3ExprParse(). So this forward declaration is required.
195542 */
195543 static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195544
195545 /*
195546 ** Extract the next token from buffer z (length n) using the tokenizer
195547 ** and other information (column names etc.) in pParse. Create an Fts3Expr
195548 ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
@@ -195564,38 +195699,42 @@
195564 sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
195565 sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
195566 int rc;
195567 sqlite3_tokenizer_cursor *pCursor;
195568 Fts3Expr *pRet = 0;
195569 int i = 0;
195570
195571 /* Set variable i to the maximum number of bytes of input to tokenize. */
195572 for(i=0; i<n; i++){
195573 if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
195574 if( z[i]=='"' ) break;
195575 }
195576
195577 *pnConsumed = i;
195578 rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor);
195579 if( rc==SQLITE_OK ){
195580 const char *zToken;
195581 int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
195582 sqlite3_int64 nByte; /* total space to allocate */
195583
195584 rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
195585 if( rc==SQLITE_OK ){
195586 nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
 
 
 
 
 
 
 
 
 
 
 
195587 pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
195588 if( !pRet ){
195589 rc = SQLITE_NOMEM;
195590 }else{
195591 pRet->eType = FTSQUERY_PHRASE;
195592 pRet->pPhrase = (Fts3Phrase *)&pRet[1];
195593 pRet->pPhrase->nToken = 1;
195594 pRet->pPhrase->iColumn = iCol;
195595 pRet->pPhrase->aToken[0].n = nToken;
195596 pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1];
195597 memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
195598
195599 if( iEnd<n && z[iEnd]=='*' ){
195600 pRet->pPhrase->aToken[0].isPrefix = 1;
195601 iEnd++;
@@ -195615,11 +195754,15 @@
195615 }
195616 }
195617
195618 }
195619 *pnConsumed = iEnd;
195620 }else if( i && rc==SQLITE_DONE ){
 
 
 
 
195621 rc = SQLITE_OK;
195622 }
195623
195624 pModule->xClose(pCursor);
195625 }
@@ -195664,11 +195807,11 @@
195664 Fts3Expr *p = 0;
195665 sqlite3_tokenizer_cursor *pCursor = 0;
195666 char *zTemp = 0;
195667 i64 nTemp = 0;
195668
195669 const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
195670 int nToken = 0;
195671
195672 /* The final Fts3Expr data structure, including the Fts3Phrase,
195673 ** Fts3PhraseToken structures token buffers are all stored as a single
195674 ** allocation so that the expression can be freed with a single call to
@@ -196036,11 +196179,11 @@
196036 int eType = p->eType;
196037 isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
196038
196039 /* The isRequirePhrase variable is set to true if a phrase or
196040 ** an expression contained in parenthesis is required. If a
196041 ** binary operator (AND, OR, NOT or NEAR) is encounted when
196042 ** isRequirePhrase is set, this is a syntax error.
196043 */
196044 if( !isPhrase && isRequirePhrase ){
196045 sqlite3Fts3ExprFree(p);
196046 rc = SQLITE_ERROR;
@@ -196618,11 +196761,10 @@
196618 pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
196619 );
196620 }
196621
196622 if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
196623 sqlite3Fts3ExprFree(pExpr);
196624 sqlite3_result_error(context, "Error parsing expression", -1);
196625 }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
196626 sqlite3_result_error_nomem(context);
196627 }else{
196628 sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
@@ -196861,11 +197003,11 @@
196861 pEntry->count++;
196862 pEntry->chain = pNew;
196863 }
196864
196865
196866 /* Resize the hash table so that it cantains "new_size" buckets.
196867 ** "new_size" must be a power of 2. The hash table might fail
196868 ** to resize if sqliteMalloc() fails.
196869 **
196870 ** Return non-zero if a memory allocation error occurs.
196871 */
@@ -197316,11 +197458,11 @@
197316 isConsonant(z+2);
197317 }
197318
197319 /*
197320 ** If the word ends with zFrom and xCond() is true for the stem
197321 ** of the word that preceeds the zFrom ending, then change the
197322 ** ending to zTo.
197323 **
197324 ** The input word *pz and zFrom are both in reverse order. zTo
197325 ** is in normal order.
197326 **
@@ -202899,11 +203041,11 @@
202899 ** previous term. Before this function returns, it is updated to contain a
202900 ** copy of zTerm/nTerm.
202901 **
202902 ** It is assumed that the buffer associated with pNode is already large
202903 ** enough to accommodate the new entry. The buffer associated with pPrev
202904 ** is extended by this function if requrired.
202905 **
202906 ** If an error (i.e. OOM condition) occurs, an SQLite error code is
202907 ** returned. Otherwise, SQLITE_OK.
202908 */
202909 static int fts3AppendToNode(
@@ -204562,11 +204704,11 @@
204562 #endif
204563
204564 /*
204565 ** SQLite value pRowid contains the rowid of a row that may or may not be
204566 ** present in the FTS3 table. If it is, delete it and adjust the contents
204567 ** of subsiduary data structures accordingly.
204568 */
204569 static int fts3DeleteByRowid(
204570 Fts3Table *p,
204571 sqlite3_value *pRowid,
204572 int *pnChng, /* IN/OUT: Decrement if row is deleted */
@@ -204888,12 +205030,16 @@
204888 struct MatchinfoBuffer {
204889 u8 aRef[3];
204890 int nElem;
204891 int bGlobal; /* Set if global data is loaded */
204892 char *zMatchinfo;
204893 u32 aMatchinfo[1];
204894 };
 
 
 
 
204895
204896
204897 /*
204898 ** The snippet() and offsets() functions both return text values. An instance
204899 ** of the following structure is used to accumulate those values while the
@@ -204915,17 +205061,17 @@
204915 ** Allocate a two-slot MatchinfoBuffer object.
204916 */
204917 static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
204918 MatchinfoBuffer *pRet;
204919 sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
204920 + sizeof(MatchinfoBuffer);
204921 sqlite3_int64 nStr = strlen(zMatchinfo);
204922
204923 pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
204924 if( pRet ){
204925 pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
204926 pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0]
204927 + sizeof(u32)*((int)nElem+1);
204928 pRet->nElem = (int)nElem;
204929 pRet->zMatchinfo = ((char*)pRet) + nByte;
204930 memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
204931 pRet->aRef[0] = 1;
@@ -204935,14 +205081,14 @@
204935 }
204936
204937 static void fts3MIBufferFree(void *p){
204938 MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
204939
204940 assert( (u32*)p==&pBuf->aMatchinfo[1]
204941 || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
204942 );
204943 if( (u32*)p==&pBuf->aMatchinfo[1] ){
204944 pBuf->aRef[1] = 0;
204945 }else{
204946 pBuf->aRef[2] = 0;
204947 }
204948
@@ -204955,32 +205101,32 @@
204955 void (*xRet)(void*) = 0;
204956 u32 *aOut = 0;
204957
204958 if( p->aRef[1]==0 ){
204959 p->aRef[1] = 1;
204960 aOut = &p->aMatchinfo[1];
204961 xRet = fts3MIBufferFree;
204962 }
204963 else if( p->aRef[2]==0 ){
204964 p->aRef[2] = 1;
204965 aOut = &p->aMatchinfo[p->nElem+2];
204966 xRet = fts3MIBufferFree;
204967 }else{
204968 aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
204969 if( aOut ){
204970 xRet = sqlite3_free;
204971 if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
204972 }
204973 }
204974
204975 *paOut = aOut;
204976 return xRet;
204977 }
204978
204979 static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
204980 p->bGlobal = 1;
204981 memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32));
204982 }
204983
204984 /*
204985 ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
204986 */
@@ -205391,11 +205537,11 @@
205391 if( nAppend<0 ){
205392 nAppend = (int)strlen(zAppend);
205393 }
205394
205395 /* If there is insufficient space allocated at StrBuffer.z, use realloc()
205396 ** to grow the buffer until so that it is big enough to accomadate the
205397 ** appended data.
205398 */
205399 if( pStr->n+nAppend+1>=pStr->nAlloc ){
205400 sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100;
205401 char *zNew = sqlite3_realloc64(pStr->z, nAlloc);
@@ -207766,11 +207912,11 @@
207766 **
207767 ** When a match if found, the matching entry is moved to become the
207768 ** most-recently used entry if it isn't so already.
207769 **
207770 ** The JsonParse object returned still belongs to the Cache and might
207771 ** be deleted at any moment. If the caller whants the JsonParse to
207772 ** linger, it needs to increment the nPJRef reference counter.
207773 */
207774 static JsonParse *jsonCacheSearch(
207775 sqlite3_context *ctx, /* The SQL statement context holding the cache */
207776 sqlite3_value *pArg /* Function argument containing SQL text */
@@ -210811,11 +210957,11 @@
210811 **
210812 ** This goes against all historical documentation about how the SQLite
210813 ** JSON functions were suppose to work. From the beginning, blob was
210814 ** reserved for expansion and a blob value should have raised an error.
210815 ** But it did not, due to a bug. And many applications came to depend
210816 ** upon this buggy behavior, espeically when using the CLI and reading
210817 ** JSON text using readfile(), which returns a blob. For this reason
210818 ** we will continue to support the bug moving forward.
210819 ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
210820 */
210821 }
@@ -212911,10 +213057,18 @@
212911 # define NEVER(X) ((X)?(assert(0),1):0)
212912 #else
212913 # define ALWAYS(X) (X)
212914 # define NEVER(X) (X)
212915 #endif
 
 
 
 
 
 
 
 
212916 #endif /* !defined(SQLITE_AMALGAMATION) */
212917
212918 /* Macro to check for 4-byte alignment. Only used inside of assert() */
212919 #ifdef SQLITE_DEBUG
212920 # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0)
@@ -213231,13 +213385,17 @@
213231 struct RtreeMatchArg {
213232 u32 iSize; /* Size of this object */
213233 RtreeGeomCallback cb; /* Info about the callback functions */
213234 int nParam; /* Number of parameters to the SQL function */
213235 sqlite3_value **apSqlParam; /* Original SQL parameter values */
213236 RtreeDValue aParam[1]; /* Values for parameters to the SQL function */
213237 };
213238
 
 
 
 
213239 #ifndef MAX
213240 # define MAX(x,y) ((x) < (y) ? (y) : (x))
213241 #endif
213242 #ifndef MIN
213243 # define MIN(x,y) ((x) > (y) ? (y) : (x))
@@ -214922,11 +215080,11 @@
214922
214923 return rc;
214924 }
214925
214926 /*
214927 ** Return the N-dimensional volumn of the cell stored in *p.
214928 */
214929 static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
214930 RtreeDValue area = (RtreeDValue)1;
214931 assert( pRtree->nDim>=1 && pRtree->nDim<=5 );
214932 #ifndef SQLITE_RTREE_INT_ONLY
@@ -216688,11 +216846,11 @@
216688 }
216689
216690 /*
216691 ** The second and subsequent arguments to this function are a printf()
216692 ** style format string and arguments. This function formats the string and
216693 ** appends it to the report being accumuated in pCheck.
216694 */
216695 static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
216696 va_list ap;
216697 va_start(ap, zFmt);
216698 if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){
@@ -217876,11 +218034,11 @@
217876
217877 /*
217878 ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
217879 ** Returns:
217880 **
217881 ** +2 x0,y0 is on the line segement
217882 **
217883 ** +1 x0,y0 is beneath line segment
217884 **
217885 ** 0 x0,y0 is not on or beneath the line segment or the line segment
217886 ** is vertical and x0,y0 is not on the line segment
@@ -217982,11 +218140,11 @@
217982 }
217983 sqlite3_free(p1);
217984 sqlite3_free(p2);
217985 }
217986
217987 /* Objects used by the overlap algorihm. */
217988 typedef struct GeoEvent GeoEvent;
217989 typedef struct GeoSegment GeoSegment;
217990 typedef struct GeoOverlap GeoOverlap;
217991 struct GeoEvent {
217992 double x; /* X coordinate at which event occurs */
@@ -219029,12 +219187,11 @@
219029 RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
219030 RtreeMatchArg *pBlob;
219031 sqlite3_int64 nBlob;
219032 int memErr = 0;
219033
219034 nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
219035 + nArg*sizeof(sqlite3_value*);
219036 pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
219037 if( !pBlob ){
219038 sqlite3_result_error_nomem(ctx);
219039 }else{
219040 int i;
@@ -220125,11 +220282,11 @@
220125 ** to read from the original database snapshot. In other words, partially
220126 ** applied transactions are not visible to other clients.
220127 **
220128 ** "RBU" stands for "Resumable Bulk Update". As in a large database update
220129 ** transmitted via a wireless network to a mobile device. A transaction
220130 ** applied using this extension is hence refered to as an "RBU update".
220131 **
220132 **
220133 ** LIMITATIONS
220134 **
220135 ** An "RBU update" transaction is subject to the following limitations:
@@ -220422,11 +220579,11 @@
220422 ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
220423 ** of the state tables within the state database are zeroed. This way,
220424 ** the next call to sqlite3rbu_vacuum() opens a handle that starts a
220425 ** new RBU vacuum operation.
220426 **
220427 ** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
220428 ** describing the sqlite3rbu_create_vfs() API function below for
220429 ** a description of the complications associated with using RBU with
220430 ** zipvfs databases.
220431 */
220432 SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
@@ -220518,11 +220675,11 @@
220518 /*
220519 ** Close an RBU handle.
220520 **
220521 ** If the RBU update has been completely applied, mark the RBU database
220522 ** as fully applied. Otherwise, assuming no error has occurred, save the
220523 ** current state of the RBU update appliation to the RBU database.
220524 **
220525 ** If an error has already occurred as part of an sqlite3rbu_step()
220526 ** or sqlite3rbu_open() call, or if one occurs within this function, an
220527 ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL,
220528 ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted
@@ -225444,11 +225601,11 @@
225444 int rc;
225445 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
225446
225447 /* If this is an RBU vacuum operation and this is the target database,
225448 ** pretend that it has at least one page. Otherwise, SQLite will not
225449 ** check for the existance of a *-wal file. rbuVfsRead() contains
225450 ** similar logic. */
225451 if( rc==SQLITE_OK && *pSize==0
225452 && p->pRbu && rbuIsVacuum(p->pRbu)
225453 && (p->openFlags & SQLITE_OPEN_MAIN_DB)
225454 ){
@@ -228674,11 +228831,11 @@
228674 }
228675
228676 /*
228677 ** This function is called to initialize the SessionTable.nCol, azCol[]
228678 ** abPK[] and azDflt[] members of SessionTable object pTab. If these
228679 ** fields are already initilialized, this function is a no-op.
228680 **
228681 ** If an error occurs, an error code is stored in sqlite3_session.rc and
228682 ** non-zero returned. Or, if no error occurs but the table has no primary
228683 ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
228684 ** indicate that updates on this table should be ignored. SessionTable.abPK
@@ -230497,11 +230654,11 @@
230497 int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
230498 void **ppChangeset /* OUT: Buffer containing changeset */
230499 ){
230500 sqlite3 *db = pSession->db; /* Source database handle */
230501 SessionTable *pTab; /* Used to iterate through attached tables */
230502 SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
230503 int rc; /* Return code */
230504
230505 assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
230506 assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );
230507
@@ -234931,10 +235088,22 @@
234931 # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
234932 #else
234933 # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
234934 #endif
234935
 
 
 
 
 
 
 
 
 
 
 
 
234936 #endif
234937
234938 /* Truncate very long tokens to this many bytes. Hard limit is
234939 ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
234940 ** field that occurs at the start of each leaf page (see fts5_index.c). */
@@ -235003,14 +235172,15 @@
235003 **
235004 ** This object is used by fts5_expr.c and fts5_index.c.
235005 */
235006 struct Fts5Colset {
235007 int nCol;
235008 int aiCol[1];
235009 };
235010
235011
 
235012
235013 /**************************************************************************
235014 ** Interface to code in fts5_config.c. fts5_config.c contains contains code
235015 ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
235016 */
@@ -235835,11 +236005,11 @@
235835 *************************************************************************
235836 ** Driver template for the LEMON parser generator.
235837 **
235838 ** The "lemon" program processes an LALR(1) input grammar file, then uses
235839 ** this template to construct a parser. The "lemon" program inserts text
235840 ** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
235841 ** interstitial "-" characters) contained in this template is changed into
235842 ** the value of the %name directive from the grammar. Otherwise, the content
235843 ** of this template is copied straight through into the generate parser
235844 ** source file.
235845 **
@@ -237989,11 +238159,11 @@
237989 ** where "N" is the total number of documents in the set and nHit
237990 ** is the number that contain at least one instance of the phrase
237991 ** under consideration.
237992 **
237993 ** The problem with this is that if (N < 2*nHit), the IDF is
237994 ** negative. Which is undesirable. So the mimimum allowable IDF is
237995 ** (1e-6) - roughly the same as a term that appears in just over
237996 ** half of set of 5,000,000 documents. */
237997 double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
237998 if( idf<=0.0 ) idf = 1e-6;
237999 p->aIDF[i] = idf;
@@ -238452,11 +238622,11 @@
238452 **
238453 ** * All non-ASCII characters,
238454 ** * The 52 upper and lower case ASCII characters, and
238455 ** * The 10 integer ASCII characters.
238456 ** * The underscore character "_" (0x5F).
238457 ** * The unicode "subsitute" character (0x1A).
238458 */
238459 static int sqlite3Fts5IsBareword(char t){
238460 u8 aBareword[128] = {
238461 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */
238462 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
@@ -239770,12 +239940,16 @@
239770 Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
239771
239772 /* Child nodes. For a NOT node, this array always contains 2 entries. For
239773 ** AND or OR nodes, it contains 2 or more entries. */
239774 int nChild; /* Number of child nodes */
239775 Fts5ExprNode *apChild[1]; /* Array of child nodes */
239776 };
 
 
 
 
239777
239778 #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
239779
239780 /*
239781 ** Invoke the xNext method of an Fts5ExprNode object. This macro should be
@@ -239803,24 +239977,31 @@
239803 */
239804 struct Fts5ExprPhrase {
239805 Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
239806 Fts5Buffer poslist; /* Current position list */
239807 int nTerm; /* Number of entries in aTerm[] */
239808 Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */
239809 };
 
 
 
 
239810
239811 /*
239812 ** One or more phrases that must appear within a certain token distance of
239813 ** each other within each matching document.
239814 */
239815 struct Fts5ExprNearset {
239816 int nNear; /* NEAR parameter */
239817 Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
239818 int nPhrase; /* Number of entries in aPhrase[] array */
239819 Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */
239820 };
239821
 
 
 
239822
239823 /*
239824 ** Parse context.
239825 */
239826 struct Fts5Parse {
@@ -239976,11 +240157,11 @@
239976 assert_expr_depth_ok(sParse.rc, sParse.pExpr);
239977
239978 /* If the LHS of the MATCH expression was a user column, apply the
239979 ** implicit column-filter. */
239980 if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
239981 int n = sizeof(Fts5Colset);
239982 Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
239983 if( pColset ){
239984 pColset->nCol = 1;
239985 pColset->aiCol[0] = iCol;
239986 sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset);
@@ -241334,11 +241515,11 @@
241334 Fts5ExprNearset *pRet = 0;
241335
241336 if( pParse->rc==SQLITE_OK ){
241337 if( pNear==0 ){
241338 sqlite3_int64 nByte;
241339 nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
241340 pRet = sqlite3_malloc64(nByte);
241341 if( pRet==0 ){
241342 pParse->rc = SQLITE_NOMEM;
241343 }else{
241344 memset(pRet, 0, (size_t)nByte);
@@ -241345,11 +241526,11 @@
241345 }
241346 }else if( (pNear->nPhrase % SZALLOC)==0 ){
241347 int nNew = pNear->nPhrase + SZALLOC;
241348 sqlite3_int64 nByte;
241349
241350 nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
241351 pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
241352 if( pRet==0 ){
241353 pParse->rc = SQLITE_NOMEM;
241354 }
241355 }else{
@@ -241436,16 +241617,16 @@
241436 if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
241437 Fts5ExprPhrase *pNew;
241438 int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
241439
241440 pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
241441 sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
241442 );
241443 if( pNew==0 ){
241444 rc = SQLITE_NOMEM;
241445 }else{
241446 if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
241447 pCtx->pPhrase = pPhrase = pNew;
241448 pNew->nTerm = nNew - SZALLOC;
241449 }
241450 }
241451
@@ -241549,11 +241730,11 @@
241549 }
241550
241551 if( sCtx.pPhrase==0 ){
241552 /* This happens when parsing a token or quoted phrase that contains
241553 ** no token characters at all. (e.g ... MATCH '""'). */
241554 sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase));
241555 }else if( sCtx.pPhrase->nTerm ){
241556 sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
241557 }
241558 assert( pParse->apPhrase!=0 );
241559 pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
@@ -241584,23 +241765,22 @@
241584 if( rc==SQLITE_OK ){
241585 pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
241586 sizeof(Fts5ExprPhrase*));
241587 }
241588 if( rc==SQLITE_OK ){
241589 pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
241590 sizeof(Fts5ExprNode));
241591 }
241592 if( rc==SQLITE_OK ){
241593 pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
241594 sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
241595 }
241596 if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
241597 Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
241598 if( pColsetOrig ){
241599 sqlite3_int64 nByte;
241600 Fts5Colset *pColset;
241601 nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
241602 pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
241603 if( pColset ){
241604 memcpy(pColset, pColsetOrig, (size_t)nByte);
241605 }
241606 pNew->pRoot->pNear->pColset = pColset;
@@ -241624,11 +241804,11 @@
241624 }
241625 }
241626 }else{
241627 /* This happens when parsing a token or quoted phrase that contains
241628 ** no token characters at all. (e.g ... MATCH '""'). */
241629 sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
241630 }
241631 }
241632
241633 if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
241634 /* All the allocations succeeded. Put the expression object together. */
@@ -241718,11 +241898,11 @@
241718 Fts5Colset *pNew; /* New colset object to return */
241719
241720 assert( pParse->rc==SQLITE_OK );
241721 assert( iCol>=0 && iCol<pParse->pConfig->nCol );
241722
241723 pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
241724 if( pNew==0 ){
241725 pParse->rc = SQLITE_NOMEM;
241726 }else{
241727 int *aiCol = pNew->aiCol;
241728 int i, j;
@@ -241753,11 +241933,11 @@
241753 static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
241754 Fts5Colset *pRet;
241755 int nCol = pParse->pConfig->nCol;
241756
241757 pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
241758 sizeof(Fts5Colset) + sizeof(int)*nCol
241759 );
241760 if( pRet ){
241761 int i;
241762 int iOld = 0;
241763 for(i=0; i<nCol; i++){
@@ -241814,11 +241994,11 @@
241814 ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned.
241815 */
241816 static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
241817 Fts5Colset *pRet;
241818 if( pOrig ){
241819 sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
241820 pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
241821 if( pRet ){
241822 memcpy(pRet, pOrig, (size_t)nByte);
241823 }
241824 }else{
@@ -241982,21 +242162,21 @@
241982 Fts5ExprNode *pRet;
241983
241984 assert( pNear->nPhrase==1 );
241985 assert( pParse->bPhraseToAnd );
241986
241987 nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
241988 pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
241989 if( pRet ){
241990 pRet->eType = FTS5_AND;
241991 pRet->nChild = nTerm;
241992 pRet->iHeight = 1;
241993 fts5ExprAssignXNext(pRet);
241994 pParse->nPhrase--;
241995 for(ii=0; ii<nTerm; ii++){
241996 Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
241997 &pParse->rc, sizeof(Fts5ExprPhrase)
241998 );
241999 if( pPhrase ){
242000 if( parseGrowPhraseArray(pParse) ){
242001 fts5ExprPhraseFree(pPhrase);
242002 }else{
@@ -242061,11 +242241,11 @@
242061 nChild = 2;
242062 if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
242063 if( pRight->eType==eType ) nChild += pRight->nChild-1;
242064 }
242065
242066 nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
242067 pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
242068
242069 if( pRet ){
242070 pRet->eType = eType;
242071 pRet->pNear = pNear;
@@ -242936,11 +243116,11 @@
242936 }
242937 return rc;
242938 }
242939
242940 /*
242941 ** Clear the token mappings for all Fts5IndexIter objects mannaged by
242942 ** the expression passed as the only argument.
242943 */
242944 static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
242945 int ii;
242946 for(ii=0; ii<pExpr->nPhrase; ii++){
@@ -242971,11 +243151,11 @@
242971
242972 typedef struct Fts5HashEntry Fts5HashEntry;
242973
242974 /*
242975 ** This file contains the implementation of an in-memory hash table used
242976 ** to accumuluate "term -> doclist" content before it is flused to a level-0
242977 ** segment.
242978 */
242979
242980
242981 struct Fts5Hash {
@@ -243028,11 +243208,11 @@
243028 int iPos; /* Position of last value written */
243029 i64 iRowid; /* Rowid of last value written */
243030 };
243031
243032 /*
243033 ** Eqivalent to:
243034 **
243035 ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
243036 */
243037 #define fts5EntryKey(p) ( ((char *)(&(p)[1])) )
243038
@@ -243964,13 +244144,17 @@
243964 int nRef; /* Object reference count */
243965 u64 nWriteCounter; /* Total leaves written to level 0 */
243966 u64 nOriginCntr; /* Origin value for next top-level segment */
243967 int nSegment; /* Total segments in this structure */
243968 int nLevel; /* Number of levels in this index */
243969 Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */
243970 };
243971
 
 
 
 
243972 /*
243973 ** An object of type Fts5SegWriter is used to write to segments.
243974 */
243975 struct Fts5PageWriter {
243976 int pgno; /* Page number for this page */
@@ -244096,14 +244280,18 @@
244096
244097 /*
244098 ** Array of tombstone pages. Reference counted.
244099 */
244100 struct Fts5TombstoneArray {
244101 int nRef; /* Number of pointers to this object */
244102 int nTombstone;
244103 Fts5Data *apTombstone[1]; /* Array of tombstone pages */
244104 };
 
 
 
 
244105
244106 /*
244107 ** Argument is a pointer to an Fts5Data structure that contains a
244108 ** leaf page.
244109 */
@@ -244169,12 +244357,15 @@
244169 int bRev; /* True to iterate in reverse order */
244170 u8 bSkipEmpty; /* True to skip deleted entries */
244171
244172 i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
244173 Fts5CResult *aFirst; /* Current merge state (see above) */
244174 Fts5SegIter aSeg[1]; /* Array of segment iterators */
244175 };
 
 
 
244176
244177 /*
244178 ** An instance of the following type is used to iterate through the contents
244179 ** of a doclist-index record.
244180 **
@@ -244198,12 +244389,16 @@
244198 i64 iRowid; /* First rowid on leaf iLeafPgno */
244199 };
244200 struct Fts5DlidxIter {
244201 int nLvl;
244202 int iSegid;
244203 Fts5DlidxLvl aLvl[1];
244204 };
 
 
 
 
244205
244206 static void fts5PutU16(u8 *aOut, u16 iVal){
244207 aOut[0] = (iVal>>8);
244208 aOut[1] = (iVal&0xFF);
244209 }
@@ -244568,11 +244763,11 @@
244568 ** an error occurs, (*pRc) is set to an SQLite error code before returning.
244569 */
244570 static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
244571 Fts5Structure *p = *pp;
244572 if( *pRc==SQLITE_OK && p->nRef>1 ){
244573 i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel);
244574 Fts5Structure *pNew;
244575 pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
244576 if( pNew ){
244577 int i;
244578 memcpy(pNew, p, nByte);
@@ -244642,14 +244837,11 @@
244642 if( nLevel>FTS5_MAX_SEGMENT || nLevel<0
244643 || nSegment>FTS5_MAX_SEGMENT || nSegment<0
244644 ){
244645 return FTS5_CORRUPT;
244646 }
244647 nByte = (
244648 sizeof(Fts5Structure) + /* Main structure */
244649 sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */
244650 );
244651 pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);
244652
244653 if( pRet ){
244654 pRet->nRef = 1;
244655 pRet->nLevel = nLevel;
@@ -244725,14 +244917,11 @@
244725 fts5StructureMakeWritable(pRc, ppStruct);
244726 assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
244727 if( *pRc==SQLITE_OK ){
244728 Fts5Structure *pStruct = *ppStruct;
244729 int nLevel = pStruct->nLevel;
244730 sqlite3_int64 nByte = (
244731 sizeof(Fts5Structure) + /* Main structure */
244732 sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
244733 );
244734
244735 pStruct = sqlite3_realloc64(pStruct, nByte);
244736 if( pStruct ){
244737 memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
244738 pStruct->nLevel++;
@@ -245267,11 +245456,11 @@
245267 Fts5DlidxIter *pIter = 0;
245268 int i;
245269 int bDone = 0;
245270
245271 for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
245272 sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
245273 Fts5DlidxIter *pNew;
245274
245275 pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
245276 if( pNew==0 ){
245277 p->rc = SQLITE_NOMEM;
@@ -245485,11 +245674,11 @@
245485 ** leave an error in the Fts5Index object.
245486 */
245487 static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
245488 const int nTomb = pIter->pSeg->nPgTombstone;
245489 if( nTomb>0 ){
245490 int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
245491 Fts5TombstoneArray *pNew;
245492 pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
245493 if( pNew ){
245494 pNew->nTombstone = nTomb;
245495 pNew->nRef = 1;
@@ -246946,12 +247135,11 @@
246946 Fts5Iter *pNew;
246947 i64 nSlot; /* Power of two >= nSeg */
246948
246949 for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
246950 pNew = fts5IdxMalloc(p,
246951 sizeof(Fts5Iter) + /* pNew */
246952 sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */
246953 sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
246954 );
246955 if( pNew ){
246956 pNew->nSeg = nSlot;
246957 pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
@@ -249313,11 +249501,11 @@
249313 static Fts5Structure *fts5IndexOptimizeStruct(
249314 Fts5Index *p,
249315 Fts5Structure *pStruct
249316 ){
249317 Fts5Structure *pNew = 0;
249318 sqlite3_int64 nByte = sizeof(Fts5Structure);
249319 int nSeg = pStruct->nSegment;
249320 int i;
249321
249322 /* Figure out if this structure requires optimization. A structure does
249323 ** not require optimization if either:
@@ -249343,10 +249531,11 @@
249343 }
249344 assert( pStruct->aLevel[i].nMerge<=nThis );
249345 }
249346
249347 nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
 
249348 pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
249349
249350 if( pNew ){
249351 Fts5StructureLevel *pLvl;
249352 nByte = nSeg * sizeof(Fts5StructureSegment);
@@ -249919,12 +250108,16 @@
249919 /* The following are used for other full-token tokendata queries only. */
249920 int nIter;
249921 int nIterAlloc;
249922 Fts5PoslistReader *aPoslistReader;
249923 int *aPoslistToIter;
249924 Fts5Iter *apIter[1];
249925 };
 
 
 
 
249926
249927 /*
249928 ** The two input arrays - a1[] and a2[] - are in sorted order. This function
249929 ** merges the two arrays together and writes the result to output array
249930 ** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
@@ -249993,11 +250186,11 @@
249993 }
249994
249995 /*
249996 ** Sort the contents of the pT->aMap[] array.
249997 **
249998 ** The sorting algorithm requries a malloc(). If this fails, an error code
249999 ** is left in Fts5Index.rc before returning.
250000 */
250001 static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
250002 Fts5TokenDataMap *aTmp = 0;
250003 int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
@@ -250184,11 +250377,11 @@
250184 if( iIdx==0
250185 && p->pConfig->eDetail==FTS5_DETAIL_FULL
250186 && p->pConfig->bPrefixInsttoken
250187 ){
250188 s.pTokendata = &s2;
250189 s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
250190 }
250191
250192 if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
250193 s.xMerge = fts5MergeRowidLists;
250194 s.xAppend = fts5AppendRowid;
@@ -250312,19 +250505,21 @@
250312 ** The %_data table is completely empty when this function is called. This
250313 ** function populates it with the initial structure objects for each index,
250314 ** and the initial version of the "averages" record (a zero-byte blob).
250315 */
250316 static int sqlite3Fts5IndexReinit(Fts5Index *p){
250317 Fts5Structure s;
 
250318 fts5StructureInvalidate(p);
250319 fts5IndexDiscardData(p);
250320 memset(&s, 0, sizeof(Fts5Structure));
 
250321 if( p->pConfig->bContentlessDelete ){
250322 s.nOriginCntr = 1;
250323 }
250324 fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
250325 fts5StructureWrite(p, &s);
250326 return fts5IndexReturn(p);
250327 }
250328
250329 /*
250330 ** Open a new Fts5Index handle. If the bCreate argument is true, create
@@ -250528,11 +250723,11 @@
250528 Fts5TokenDataIter *pRet = pIn;
250529
250530 if( p->rc==SQLITE_OK ){
250531 if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
250532 int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
250533 int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
250534 Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
250535
250536 if( pNew==0 ){
250537 p->rc = SQLITE_NOMEM;
250538 }else{
@@ -251044,11 +251239,12 @@
251044
251045 memset(&ctx, 0, sizeof(ctx));
251046
251047 fts5BufferGrow(&p->rc, &token, nToken+1);
251048 assert( token.p!=0 || p->rc!=SQLITE_OK );
251049 ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT));
 
251050
251051 if( p->rc==SQLITE_OK ){
251052
251053 /* Fill in the token prefix to search for */
251054 token.p[0] = FTS5_MAIN_PREFIX;
@@ -251175,11 +251371,12 @@
251175 assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
251176 assert( pIter->pTokenDataIter || pIter->nSeg>0 );
251177 if( pIter->nSeg>0 ){
251178 /* This is a prefix term iterator. */
251179 if( pT==0 ){
251180 pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));
 
251181 pIter->pTokenDataIter = pT;
251182 }
251183 if( pT ){
251184 fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
251185 fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
@@ -252209,11 +252406,11 @@
252209 }
252210 #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
252211
252212 #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
252213 static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
252214 int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */
252215 fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);
252216
252217 if( iSegid==0 ){
252218 if( iKey==FTS5_AVERAGES_ROWID ){
252219 sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
@@ -253170,13 +253367,15 @@
253170 struct Fts5Sorter {
253171 sqlite3_stmt *pStmt;
253172 i64 iRowid; /* Current rowid */
253173 const u8 *aPoslist; /* Position lists for current row */
253174 int nIdx; /* Number of entries in aIdx[] */
253175 int aIdx[1]; /* Offsets into aPoslist for current row */
253176 };
253177
 
 
253178
253179 /*
253180 ** Virtual-table cursor object.
253181 **
253182 ** iSpecial:
@@ -254050,11 +254249,11 @@
254050 int rc;
254051 const char *zRank = pCsr->zRank;
254052 const char *zRankArgs = pCsr->zRankArgs;
254053
254054 nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
254055 nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
254056 pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
254057 if( pSorter==0 ) return SQLITE_NOMEM;
254058 memset(pSorter, 0, (size_t)nByte);
254059 pSorter->nIdx = nPhrase;
254060
@@ -256576,11 +256775,11 @@
256576 int nArg, /* Number of args */
256577 sqlite3_value **apUnused /* Function arguments */
256578 ){
256579 assert( nArg==0 );
256580 UNUSED_PARAM2(nArg, apUnused);
256581 sqlite3_result_text(pCtx, "fts5: 2025-02-25 16:39:51 6f0b6d95db17e69ac7e46a39f52770291ac4cfe43eea09add224946a6e11f04e", -1, SQLITE_TRANSIENT);
256582 }
256583
256584 /*
256585 ** Implementation of fts5_locale(LOCALE, TEXT) function.
256586 **
256587
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.50.0"
469 #define SQLITE_VERSION_NUMBER 3050000
470 #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -5492,11 +5492,11 @@
5492 ** For all versions of SQLite up to and including 3.6.23.1, a call to
5493 ** [sqlite3_reset()] was required after sqlite3_step() returned anything
5494 ** other than [SQLITE_ROW] before any subsequent invocation of
5495 ** sqlite3_step(). Failure to reset the prepared statement using
5496 ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
5497 ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
5498 ** sqlite3_step() began
5499 ** calling [sqlite3_reset()] automatically in this circumstance rather
5500 ** than returning [SQLITE_MISUSE]. This is not considered a compatibility
5501 ** break because any application that ever receives an SQLITE_MISUSE error
5502 ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7388,10 +7388,12 @@
7388 ** ^Any callback set by a previous call to this function
7389 ** for the same database connection is overridden.
7390 **
7391 ** ^The second argument is a pointer to the function to invoke when a
7392 ** row is updated, inserted or deleted in a rowid table.
7393 ** ^The update hook is disabled by invoking sqlite3_update_hook()
7394 ** with a NULL pointer as the second parameter.
7395 ** ^The first argument to the callback is a copy of the third argument
7396 ** to sqlite3_update_hook().
7397 ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
7398 ** or [SQLITE_UPDATE], depending on the operation that caused the callback
7399 ** to be invoked.
@@ -15170,11 +15172,21 @@
15172 /*
15173 ** GCC does not define the offsetof() macro so we'll have to do it
15174 ** ourselves.
15175 */
15176 #ifndef offsetof
15177 #define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
15178 #endif
15179
15180 /*
15181 ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
15182 ** to avoid complaints from -fsanitize=strict-bounds.
15183 */
15184 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
15185 # define FLEXARRAY
15186 #else
15187 # define FLEXARRAY 1
15188 #endif
15189
15190 /*
15191 ** Macros to compute minimum and maximum of two numbers.
15192 */
@@ -17405,12 +17417,12 @@
17417 SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
17418 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
17419 SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
17420 #endif
17421
17422 /* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra
17423 ** comments on each VDBE opcode.
17424 **
17425 ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
17426 ** comments in VDBE programs that show key decision points in the code
17427 ** generator.
17428 */
@@ -18945,12 +18957,16 @@
18957 u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
18958 Trigger *apTrigger[2];/* Triggers for aAction[] actions */
18959 struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
18960 int iFrom; /* Index of column in pFrom */
18961 char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
18962 } aCol[FLEXARRAY]; /* One entry for each of nCol columns */
18963 };
18964
18965 /* The size (in bytes) of an FKey object holding N columns. The answer
18966 ** does NOT include space to hold the zTo name. */
18967 #define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap))
18968
18969 /*
18970 ** SQLite supports many different ways to resolve a constraint
18971 ** error. ROLLBACK processing means that a constraint violation
18972 ** causes the operation in process to fail and for the current transaction
@@ -19009,13 +19025,16 @@
19025 u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
19026 u16 nKeyField; /* Number of key columns in the index */
19027 u16 nAllField; /* Total columns, including key plus others */
19028 sqlite3 *db; /* The database connection */
19029 u8 *aSortFlags; /* Sort order for each column. */
19030 CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
19031 };
19032
19033 /* The size (in bytes) of a KeyInfo object with up to N fields */
19034 #define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))
19035
19036 /*
19037 ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
19038 */
19039 #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */
19040 #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */
@@ -19584,12 +19603,17 @@
19603 u16 iAlias; /* Index into Parse.aAlias[] for zName */
19604 } x;
19605 int iConstExprReg; /* Register in which Expr value is cached. Used only
19606 ** by Parse.pConstExpr */
19607 } u;
19608 } a[FLEXARRAY]; /* One slot for each expression in the list */
19609 };
19610
19611 /* The size (in bytes) of an ExprList object that is big enough to hold
19612 ** as many as N expressions. */
19613 #define SZ_EXPRLIST(N) \
19614 (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item))
19615
19616 /*
19617 ** Allowed values for Expr.a.eEName
19618 */
19619 #define ENAME_NAME 0 /* The AS clause of a result set */
@@ -19614,13 +19638,16 @@
19638 */
19639 struct IdList {
19640 int nId; /* Number of identifiers on the list */
19641 struct IdList_item {
19642 char *zName; /* Name of the identifier */
19643 } a[FLEXARRAY];
19644 };
19645
19646 /* The size (in bytes) of an IdList object that can hold up to N IDs. */
19647 #define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item))
19648
19649 /*
19650 ** Allowed values for IdList.eType, which determines which value of the a.u4
19651 ** is valid.
19652 */
19653 #define EU4_NONE 0 /* Does not use IdList.a.u4 */
@@ -19736,14 +19763,22 @@
19763 ** is used to hold the FROM clause of a SELECT statement. SrcList also
19764 ** represents the target tables for DELETE, INSERT, and UPDATE statements.
19765 **
19766 */
19767 struct SrcList {
19768 int nSrc; /* Number of tables or subqueries in the FROM clause */
19769 u32 nAlloc; /* Number of entries allocated in a[] below */
19770 SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */
19771 };
19772
19773 /* Size (in bytes) of a SrcList object that can hold as many as N
19774 ** SrcItem objects. */
19775 #define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem))
19776
19777 /* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a
19778 ** special case of SZ_SRCITEM(1) that comes up often. */
19779 #define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem))
19780
19781 /*
19782 ** Permitted values of the SrcList.a.jointype field
19783 */
19784 #define JT_INNER 0x01 /* Any kind of inner or cross join */
@@ -20804,12 +20839,16 @@
20839 */
20840 struct With {
20841 int nCte; /* Number of CTEs in the WITH clause */
20842 int bView; /* Belongs to the outermost Select of a view */
20843 With *pOuter; /* Containing WITH clause, or NULL */
20844 Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */
20845 };
20846
20847 /* The size (in bytes) of a With object that can hold as many
20848 ** as N different CTEs. */
20849 #define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte))
20850
20851 /*
20852 ** The Cte object is not guaranteed to persist for the entire duration
20853 ** of code generation. (The query flattener or other parser tree
20854 ** edits might delete it.) The following object records information
@@ -20835,12 +20874,16 @@
20874 */
20875 struct DbClientData {
20876 DbClientData *pNext; /* Next in a linked list */
20877 void *pData; /* The data */
20878 void (*xDestructor)(void*); /* Destructor. Might be NULL */
20879 char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */
20880 };
20881
20882 /* The size (in bytes) of a DbClientData object that can has a name
20883 ** that is N bytes long, including the zero-terminator. */
20884 #define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N))
20885
20886 #ifdef SQLITE_DEBUG
20887 /*
20888 ** An instance of the TreeView object is used for printing the content of
20889 ** data structures on sqlite3DebugPrintf() using a tree-like view.
@@ -22673,10 +22716,13 @@
22716 "EXTRA_IFNULLROW",
22717 #endif
22718 #ifdef SQLITE_EXTRA_INIT
22719 "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
22720 #endif
22721 #ifdef SQLITE_EXTRA_INIT_MUTEXED
22722 "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED),
22723 #endif
22724 #ifdef SQLITE_EXTRA_SHUTDOWN
22725 "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
22726 #endif
22727 #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
22728 "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
@@ -23657,16 +23703,23 @@
23703 #ifdef SQLITE_ENABLE_COLUMN_USED_MASK
23704 u64 maskUsed; /* Mask of columns used by this cursor */
23705 #endif
23706 VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */
23707
23708 /* Space is allocated for aType to hold at least 2*nField+1 entries:
23709 ** nField slots for aType[] and nField+1 array slots for aOffset[] */
23710 u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */
 
23711 };
23712
23713 /*
23714 ** The size (in bytes) of a VdbeCursor object that has an nField value of N
23715 ** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple
23716 ** of 8.
23717 */
23718 #define SZ_VDBECURSOR(N) \
23719 (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64))
23720
23721 /* Return true if P is a null-only cursor
23722 */
23723 #define IsNullCursor(P) \
23724 ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)
23725
@@ -23919,13 +23972,20 @@
23972 int iOp; /* Instruction number of OP_Function */
23973 int isError; /* Error code returned by the function. */
23974 u8 enc; /* Encoding to use for results */
23975 u8 skipFlag; /* Skip accumulator loading if true */
23976 u16 argc; /* Number of arguments */
23977 sqlite3_value *argv[FLEXARRAY]; /* Argument set */
23978 };
23979
23980 /*
23981 ** The size (in bytes) of an sqlite3_context object that holds N
23982 ** argv[] arguments.
23983 */
23984 #define SZ_CONTEXT(N) \
23985 (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*))
23986
23987
23988 /* The ScanStatus object holds a single value for the
23989 ** sqlite3_stmt_scanstatus() interface.
23990 **
23991 ** aAddrRange[]:
@@ -24055,11 +24115,11 @@
24115 struct PreUpdate {
24116 Vdbe *v;
24117 VdbeCursor *pCsr; /* Cursor to read old values from */
24118 int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
24119 u8 *aRecord; /* old.* database record */
24120 KeyInfo *pKeyinfo; /* Key information */
24121 UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
24122 UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
24123 int iNewReg; /* Register for new.* values */
24124 int iBlobWrite; /* Value returned by preupdate_blobwrite() */
24125 i64 iKey1; /* First key value passed to hook */
@@ -24067,10 +24127,11 @@
24127 Mem oldipk; /* Memory cell holding "old" IPK value */
24128 Mem *aNew; /* Array of new.* values */
24129 Table *pTab; /* Schema object being updated */
24130 Index *pPk; /* PK index if pTab is WITHOUT ROWID */
24131 sqlite3_value **apDflt; /* Array of default values, if required */
24132 u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
24133 };
24134
24135 /*
24136 ** An instance of this object is used to pass an vector of values into
24137 ** OP_VFilter, the xFilter method of a virtual table. The vector is the
@@ -26000,11 +26061,11 @@
26061 ** Return the number of days after the most recent Sunday.
26062 **
26063 ** In other words, return the day of the week according
26064 ** to this code:
26065 **
26066 ** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday
26067 */
26068 static int daysAfterSunday(DateTime *pDate){
26069 assert( pDate->validJD );
26070 return (int)((pDate->iJD+129600000)/86400000) % 7;
26071 }
@@ -32378,11 +32439,11 @@
32439 u32 nBack = 0;
32440 u32 nCtrl = 0;
32441 for(k=0; k<i; k++){
32442 if( escarg[k]=='\\' ){
32443 nBack++;
32444 }else if( ((u8*)escarg)[k]<=0x1f ){
32445 nCtrl++;
32446 }
32447 }
32448 if( nCtrl || xtype==etESCAPE_q ){
32449 n += nBack + 5*nCtrl;
@@ -32416,11 +32477,11 @@
32477 bufpt[j++] = ch = escarg[i];
32478 if( ch==q ){
32479 bufpt[j++] = ch;
32480 }else if( ch=='\\' ){
32481 bufpt[j++] = '\\';
32482 }else if( ((unsigned char)ch)<=0x1f ){
32483 bufpt[j-1] = '\\';
32484 bufpt[j++] = 'u';
32485 bufpt[j++] = '0';
32486 bufpt[j++] = '0';
32487 bufpt[j++] = ch>=0x10 ? '1' : '0';
@@ -37067,11 +37128,11 @@
37128 return 0;
37129 #endif
37130 }
37131
37132 /*
37133 ** Compute the absolute value of a 32-bit signed integer, if possible. Or
37134 ** if the integer has a value of -2147483648, return +2147483647
37135 */
37136 SQLITE_PRIVATE int sqlite3AbsInt32(int x){
37137 if( x>=0 ) return x;
37138 if( x==(int)0x80000000 ) return 0x7fffffff;
@@ -45614,11 +45675,11 @@
45675 sp.tv_sec = microseconds / 1000000;
45676 sp.tv_nsec = (microseconds % 1000000) * 1000;
45677
45678 /* Almost all modern unix systems support nanosleep(). But if you are
45679 ** compiling for one of the rare exceptions, you can use
45680 ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if
45681 ** usleep() is available) in order to bypass the use of nanosleep() */
45682 nanosleep(&sp, NULL);
45683
45684 UNUSED_PARAMETER(NotUsed);
45685 return microseconds;
@@ -56047,14 +56108,10 @@
56108 void *pStart, *pEnd; /* Bounds of global page cache memory */
56109 /* Above requires no mutex. Use mutex below for variable that follow. */
56110 sqlite3_mutex *mutex; /* Mutex for accessing the following: */
56111 PgFreeslot *pFree; /* Free page blocks */
56112 int nFreeSlot; /* Number of unused pcache slots */
 
 
 
 
56113 int bUnderPressure; /* True if low on PAGECACHE memory */
56114 } pcache1_g;
56115
56116 /*
56117 ** All code in this file should access the global structure above via the
@@ -56098,11 +56155,11 @@
56155 pcache1.szSlot = sz;
56156 pcache1.nSlot = pcache1.nFreeSlot = n;
56157 pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
56158 pcache1.pStart = pBuf;
56159 pcache1.pFree = 0;
56160 AtomicStore(&pcache1.bUnderPressure,0);
56161 while( n-- ){
56162 p = (PgFreeslot*)pBuf;
56163 p->pNext = pcache1.pFree;
56164 pcache1.pFree = p;
56165 pBuf = (void*)&((char*)pBuf)[sz];
@@ -56166,11 +56223,11 @@
56223 sqlite3_mutex_enter(pcache1.mutex);
56224 p = (PgHdr1 *)pcache1.pFree;
56225 if( p ){
56226 pcache1.pFree = pcache1.pFree->pNext;
56227 pcache1.nFreeSlot--;
56228 AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
56229 assert( pcache1.nFreeSlot>=0 );
56230 sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
56231 sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
56232 }
56233 sqlite3_mutex_leave(pcache1.mutex);
@@ -56205,11 +56262,11 @@
56262 sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
56263 pSlot = (PgFreeslot*)p;
56264 pSlot->pNext = pcache1.pFree;
56265 pcache1.pFree = pSlot;
56266 pcache1.nFreeSlot++;
56267 AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
56268 assert( pcache1.nFreeSlot<=pcache1.nSlot );
56269 sqlite3_mutex_leave(pcache1.mutex);
56270 }else{
56271 assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
56272 sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
@@ -56336,11 +56393,11 @@
56393 ** allocating a new page cache entry in order to avoid stressing
56394 ** the heap even further.
56395 */
56396 static int pcache1UnderMemoryPressure(PCache1 *pCache){
56397 if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
56398 return AtomicLoad(&pcache1.bUnderPressure);
56399 }else{
56400 return sqlite3HeapNearlyFull();
56401 }
56402 }
56403
@@ -66177,12 +66234,16 @@
66234 int iNext; /* Next slot in aIndex[] not yet returned */
66235 ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
66236 u32 *aPgno; /* Array of page numbers. */
66237 int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
66238 int iZero; /* Frame number associated with aPgno[0] */
66239 } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */
66240 };
66241
66242 /* Size (in bytes) of a WalIterator object suitable for N or fewer segments */
66243 #define SZ_WALITERATOR(N) \
66244 (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment))
66245
66246 /*
66247 ** Define the parameters of the hash tables in the wal-index file. There
66248 ** is a hash-table following every HASHTABLE_NPAGE page numbers in the
66249 ** wal-index.
@@ -67540,12 +67601,11 @@
67601 assert( pWal->ckptLock && pWal->hdr.mxFrame>0 );
67602 iLast = pWal->hdr.mxFrame;
67603
67604 /* Allocate space for the WalIterator object. */
67605 nSegment = walFramePage(iLast) + 1;
67606 nByte = SZ_WALITERATOR(nSegment)
 
67607 + iLast*sizeof(ht_slot);
67608 p = (WalIterator *)sqlite3_malloc64(nByte
67609 + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
67610 );
67611 if( !p ){
@@ -86029,16 +86089,14 @@
86089 int nArg, /* Number of argument */
86090 const FuncDef *pFunc, /* The function to be invoked */
86091 int eCallCtx /* Calling context */
86092 ){
86093 Vdbe *v = pParse->pVdbe;
 
86094 int addr;
86095 sqlite3_context *pCtx;
86096 assert( v );
86097 pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg));
 
86098 if( pCtx==0 ){
86099 assert( pParse->db->mallocFailed );
86100 freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
86101 return 0;
86102 }
@@ -91110,25 +91168,26 @@
91168
91169 preupdate.v = v;
91170 preupdate.pCsr = pCsr;
91171 preupdate.op = op;
91172 preupdate.iNewReg = iReg;
91173 preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
91174 preupdate.pKeyinfo->db = db;
91175 preupdate.pKeyinfo->enc = ENC(db);
91176 preupdate.pKeyinfo->nKeyField = pTab->nCol;
91177 preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder;
91178 preupdate.iKey1 = iKey1;
91179 preupdate.iKey2 = iKey2;
91180 preupdate.pTab = pTab;
91181 preupdate.iBlobWrite = iBlobWrite;
91182
91183 db->pPreUpdate = &preupdate;
91184 db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
91185 db->pPreUpdate = 0;
91186 sqlite3DbFree(db, preupdate.aRecord);
91187 vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked);
91188 vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked);
91189 sqlite3VdbeMemRelease(&preupdate.oldipk);
91190 if( preupdate.aNew ){
91191 int i;
91192 for(i=0; i<pCsr->nField; i++){
91193 sqlite3VdbeMemRelease(&preupdate.aNew[i]);
@@ -93363,11 +93422,11 @@
93422 nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
93423 aRec = sqlite3DbMallocRaw(db, nRec);
93424 if( !aRec ) goto preupdate_old_out;
93425 rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
93426 if( rc==SQLITE_OK ){
93427 p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec);
93428 if( !p->pUnpacked ) rc = SQLITE_NOMEM;
93429 }
93430 if( rc!=SQLITE_OK ){
93431 sqlite3DbFree(db, aRec);
93432 goto preupdate_old_out;
@@ -93428,11 +93487,11 @@
93487 #ifdef SQLITE_ENABLE_API_ARMOR
93488 p = db!=0 ? db->pPreUpdate : 0;
93489 #else
93490 p = db->pPreUpdate;
93491 #endif
93492 return (p ? p->pKeyinfo->nKeyField : 0);
93493 }
93494 #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
93495
93496 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
93497 /*
@@ -93511,11 +93570,11 @@
93570 UnpackedRecord *pUnpack = p->pNewUnpacked;
93571 if( !pUnpack ){
93572 Mem *pData = &p->v->aMem[p->iNewReg];
93573 rc = ExpandBlob(pData);
93574 if( rc!=SQLITE_OK ) goto preupdate_new_out;
93575 pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z);
93576 if( !pUnpack ){
93577 rc = SQLITE_NOMEM;
93578 goto preupdate_new_out;
93579 }
93580 p->pNewUnpacked = pUnpack;
@@ -94305,13 +94364,13 @@
94364 */
94365 Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
94366
94367 i64 nByte;
94368 VdbeCursor *pCx = 0;
94369 nByte = SZ_VDBECURSOR(nField);
94370 assert( ROUND8(nByte)==nByte );
94371 if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize();
94372
94373 assert( iCur>=0 && iCur<p->nCursor );
94374 if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
94375 sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]);
94376 p->apCsr[iCur] = 0;
@@ -94340,12 +94399,12 @@
94399 memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
94400 pCx->eCurType = eCurType;
94401 pCx->nField = nField;
94402 pCx->aOffset = &pCx->aType[nField];
94403 if( eCurType==CURTYPE_BTREE ){
94404 assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) );
94405 pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)];
94406 sqlite3BtreeCursorZero(pCx->uc.pCursor);
94407 }
94408 return pCx;
94409 }
94410
@@ -100083,11 +100142,11 @@
100142 pCrsr = pC->uc.pCursor;
100143
100144 /* The OP_RowData opcodes always follow OP_NotExists or
100145 ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
100146 ** that might invalidate the cursor.
100147 ** If this were not the case, one of the following assert()s
100148 ** would fail. Should this ever change (because of changes in the code
100149 ** generator) then the fix would be to insert a call to
100150 ** sqlite3VdbeCursorMoveto().
100151 */
100152 assert( pC->deferredMoveto==0 );
@@ -101732,11 +101791,11 @@
101791 ** cell in which to store the accumulation. Be careful that the memory
101792 ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits.
101793 **
101794 ** Note: We could avoid this by using a regular memory cell from aMem[] for
101795 ** the accumulator, instead of allocating one here. */
101796 nAlloc = ROUND8P( SZ_CONTEXT(n) );
101797 pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem));
101798 if( pCtx==0 ) goto no_mem;
101799 pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc);
101800 assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) );
101801
@@ -103390,10 +103449,11 @@
103449 int iCol; /* Index of zColumn in row-record */
103450 int rc = SQLITE_OK;
103451 char *zErr = 0;
103452 Table *pTab;
103453 Incrblob *pBlob = 0;
103454 int iDb;
103455 Parse sParse;
103456
103457 #ifdef SQLITE_ENABLE_API_ARMOR
103458 if( ppBlob==0 ){
103459 return SQLITE_MISUSE_BKPT;
@@ -103435,11 +103495,14 @@
103495 if( pTab && IsView(pTab) ){
103496 pTab = 0;
103497 sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
103498 }
103499 #endif
103500 if( pTab==0
103501 || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 &&
103502 sqlite3OpenTempDatabase(&sParse))
103503 ){
103504 if( sParse.zErrMsg ){
103505 sqlite3DbFree(db, zErr);
103506 zErr = sParse.zErrMsg;
103507 sParse.zErrMsg = 0;
103508 }
@@ -103446,11 +103509,11 @@
103509 rc = SQLITE_ERROR;
103510 sqlite3BtreeLeaveAll(db);
103511 goto blob_open_out;
103512 }
103513 pBlob->pTab = pTab;
103514 pBlob->zDb = db->aDb[iDb].zDbSName;
103515
103516 /* Now search pTab for the exact column. */
103517 iCol = sqlite3ColumnIndex(pTab, zColumn);
103518 if( iCol<0 ){
103519 sqlite3DbFree(db, zErr);
@@ -103530,11 +103593,10 @@
103593 {OP_Column, 0, 0, 1}, /* 3 */
103594 {OP_ResultRow, 1, 0, 0}, /* 4 */
103595 {OP_Halt, 0, 0, 0}, /* 5 */
103596 };
103597 Vdbe *v = (Vdbe *)pBlob->pStmt;
 
103598 VdbeOp *aOp;
103599
103600 sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
103601 pTab->pSchema->schema_cookie,
103602 pTab->pSchema->iGeneration);
@@ -104108,12 +104170,15 @@
104170 u8 bUsePMA; /* True if one or more PMAs created */
104171 u8 bUseThreads; /* True to use background threads */
104172 u8 iPrev; /* Previous thread used to flush PMA */
104173 u8 nTask; /* Size of aTask[] array */
104174 u8 typeMask;
104175 SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */
104176 };
104177
104178 /* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */
104179 #define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask))
104180
104181 #define SORTER_TYPE_INTEGER 0x01
104182 #define SORTER_TYPE_TEXT 0x02
104183
104184 /*
@@ -104742,12 +104807,12 @@
104807 assert( pCsr->pKeyInfo );
104808 assert( !pCsr->isEphemeral );
104809 assert( pCsr->eCurType==CURTYPE_SORTER );
104810 assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
104811 < 0x7fffffff );
104812 szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1);
104813 sz = SZ_VDBESORTER(nWorker+1);
104814
104815 pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
104816 pCsr->uc.pSorter = pSorter;
104817 if( pSorter==0 ){
104818 rc = SQLITE_NOMEM_BKPT;
@@ -105207,10 +105272,14 @@
105272 }
105273
105274 p->u.pNext = 0;
105275 for(i=0; aSlot[i]; i++){
105276 p = vdbeSorterMerge(pTask, p, aSlot[i]);
105277 /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use
105278 ** | up all 64 aSlots[] with only a 64-bit address space.
105279 ** v */
105280 assert( i<ArraySize(aSlot) );
105281 aSlot[i] = 0;
105282 }
105283 aSlot[i] = p;
105284 p = pNext;
105285 }
@@ -109981,32 +110050,34 @@
110050 Table *pTab, /* The table being referenced, or NULL */
110051 int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
110052 Expr *pExpr, /* Expression to resolve. May be NULL. */
110053 ExprList *pList /* Expression list to resolve. May be NULL. */
110054 ){
110055 SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
110056 NameContext sNC; /* Name context for pParse->pNewTable */
110057 int rc;
110058 u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
110059
110060 assert( type==0 || pTab!=0 );
110061 assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
110062 || type==NC_GenCol || pTab==0 );
110063 memset(&sNC, 0, sizeof(sNC));
110064 pSrc = (SrcList*)srcSpace;
110065 memset(pSrc, 0, SZ_SRCLIST_1);
110066 if( pTab ){
110067 pSrc->nSrc = 1;
110068 pSrc->a[0].zName = pTab->zName;
110069 pSrc->a[0].pSTab = pTab;
110070 pSrc->a[0].iCursor = -1;
110071 if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
110072 /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
110073 ** schema elements */
110074 type |= NC_FromDDL;
110075 }
110076 }
110077 sNC.pParse = pParse;
110078 sNC.pSrcList = pSrc;
110079 sNC.ncFlags = type | NC_IsDDL;
110080 if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
110081 if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
110082 return rc;
110083 }
@@ -111751,11 +111822,11 @@
111822 */
111823 #ifndef SQLITE_OMIT_CTE
111824 SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
111825 With *pRet = 0;
111826 if( p ){
111827 sqlite3_int64 nByte = SZ_WITH(p->nCte);
111828 pRet = sqlite3DbMallocZero(db, nByte);
111829 if( pRet ){
111830 int i;
111831 pRet->nCte = p->nCte;
111832 for(i=0; i<p->nCte; i++){
@@ -111878,15 +111949,13 @@
111949 #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
111950 || !defined(SQLITE_OMIT_SUBQUERY)
111951 SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
111952 SrcList *pNew;
111953 int i;
 
111954 assert( db!=0 );
111955 if( p==0 ) return 0;
111956 pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) );
 
111957 if( pNew==0 ) return 0;
111958 pNew->nSrc = pNew->nAlloc = p->nSrc;
111959 for(i=0; i<p->nSrc; i++){
111960 SrcItem *pNewItem = &pNew->a[i];
111961 const SrcItem *pOldItem = &p->a[i];
@@ -111944,11 +112013,11 @@
112013 SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
112014 IdList *pNew;
112015 int i;
112016 assert( db!=0 );
112017 if( p==0 ) return 0;
112018 pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId));
112019 if( pNew==0 ) return 0;
112020 pNew->nId = p->nId;
112021 for(i=0; i<p->nId; i++){
112022 struct IdList_item *pNewItem = &pNew->a[i];
112023 const struct IdList_item *pOldItem = &p->a[i];
@@ -112028,11 +112097,11 @@
112097 Expr *pExpr /* Expression to be appended. Might be NULL */
112098 ){
112099 struct ExprList_item *pItem;
112100 ExprList *pList;
112101
112102 pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4));
112103 if( pList==0 ){
112104 sqlite3ExprDelete(db, pExpr);
112105 return 0;
112106 }
112107 pList->nAlloc = 4;
@@ -112048,12 +112117,11 @@
112117 Expr *pExpr /* Expression to be appended. Might be NULL */
112118 ){
112119 struct ExprList_item *pItem;
112120 ExprList *pNew;
112121 pList->nAlloc *= 2;
112122 pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc));
 
112123 if( pNew==0 ){
112124 sqlite3ExprListDelete(db, pList);
112125 sqlite3ExprDelete(db, pExpr);
112126 return 0;
112127 }else{
@@ -114685,11 +114753,11 @@
114753 return -1; /* Not found */
114754 }
114755
114756
114757 /*
114758 ** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This
114759 ** function checks the Parse.pIdxPartExpr list to see if this column
114760 ** can be replaced with a constant value. If so, it generates code to
114761 ** put the constant value in a register (ideally, but not necessarily,
114762 ** register iTarget) and returns the register number.
114763 **
@@ -118531,10 +118599,11 @@
118599 sqlite3 *db, /* Database handle */
118600 const char *zSql, /* SQL to parse */
118601 int bTemp /* True if SQL is from temp schema */
118602 ){
118603 int rc;
118604 u64 flags;
118605
118606 sqlite3ParseObjectInit(p, db);
118607 if( zSql==0 ){
118608 return SQLITE_NOMEM;
118609 }
@@ -118549,11 +118618,15 @@
118618 db->init.iDb = (u8)iDb;
118619 }
118620 p->eParseMode = PARSE_MODE_RENAME;
118621 p->db = db;
118622 p->nQueryLoop = 1;
118623 flags = db->flags;
118624 testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 );
118625 db->flags |= SQLITE_Comments;
118626 rc = sqlite3RunParser(p, zSql);
118627 db->flags = flags;
118628 if( db->mallocFailed ) rc = SQLITE_NOMEM;
118629 if( rc==SQLITE_OK
118630 && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
118631 ){
118632 rc = SQLITE_CORRUPT_BKPT;
@@ -119445,11 +119518,11 @@
119518 int rc;
119519 Parse sParse;
119520 u64 flags = db->flags;
119521 if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
119522 rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
119523 db->flags = flags;
119524 if( rc==SQLITE_OK ){
119525 if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
119526 NameContext sNC;
119527 memset(&sNC, 0, sizeof(sNC));
119528 sNC.pParse = &sParse;
@@ -126268,11 +126341,11 @@
126341 "columns in the referenced table");
126342 goto fk_end;
126343 }else{
126344 nCol = pFromCol->nExpr;
126345 }
126346 nByte = SZ_FKEY(nCol) + pTo->n + 1;
126347 if( pToCol ){
126348 for(i=0; i<pToCol->nExpr; i++){
126349 nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
126350 }
126351 }
@@ -127327,16 +127400,15 @@
127400 */
127401 SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
127402 sqlite3 *db = pParse->db;
127403 int i;
127404 if( pList==0 ){
127405 pList = sqlite3DbMallocZero(db, SZ_IDLIST(1));
127406 if( pList==0 ) return 0;
127407 }else{
127408 IdList *pNew;
127409 pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1));
 
127410 if( pNew==0 ){
127411 sqlite3IdListDelete(db, pList);
127412 return 0;
127413 }
127414 pList = pNew;
@@ -127431,12 +127503,11 @@
127503 sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d",
127504 SQLITE_MAX_SRCLIST);
127505 return 0;
127506 }
127507 if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST;
127508 pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc));
 
127509 if( pNew==0 ){
127510 assert( db->mallocFailed );
127511 return 0;
127512 }
127513 pSrc = pNew;
@@ -127507,11 +127578,11 @@
127578 assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
127579 assert( pParse!=0 );
127580 assert( pParse->db!=0 );
127581 db = pParse->db;
127582 if( pList==0 ){
127583 pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1));
127584 if( pList==0 ) return 0;
127585 pList->nAlloc = 1;
127586 pList->nSrc = 1;
127587 memset(&pList->a[0], 0, sizeof(pList->a[0]));
127588 pList->a[0].iCursor = -1;
@@ -128393,14 +128464,13 @@
128464 }
128465 }
128466 }
128467
128468 if( pWith ){
128469 pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1));
 
128470 }else{
128471 pNew = sqlite3DbMallocZero(db, SZ_WITH(1));
128472 }
128473 assert( (pNew!=0 && zName!=0) || db->mallocFailed );
128474
128475 if( db->mallocFailed ){
128476 sqlite3CteDelete(db, pCte);
@@ -131933,11 +132003,11 @@
132003 **
132004 ** The SUM() function follows the (broken) SQL standard which means
132005 ** that it returns NULL if it sums over no inputs. TOTAL returns
132006 ** 0.0 in that case. In addition, TOTAL always returns a float where
132007 ** SUM might return an integer if it never encounters a floating point
132008 ** value. TOTAL never fails, but SUM might throw an exception if
132009 ** it overflows an integer.
132010 */
132011 static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
132012 SumCtx *p;
132013 int type;
@@ -139748,52 +139818,52 @@
139818 /* 11 */ "notnull",
139819 /* 12 */ "dflt_value",
139820 /* 13 */ "pk",
139821 /* 14 */ "hidden",
139822 /* table_info reuses 8 */
139823 /* 15 */ "name", /* Used by: function_list */
139824 /* 16 */ "builtin",
139825 /* 17 */ "type",
139826 /* 18 */ "enc",
139827 /* 19 */ "narg",
139828 /* 20 */ "flags",
139829 /* 21 */ "schema", /* Used by: table_list */
139830 /* 22 */ "name",
139831 /* 23 */ "type",
139832 /* 24 */ "ncol",
139833 /* 25 */ "wr",
139834 /* 26 */ "strict",
139835 /* 27 */ "seqno", /* Used by: index_xinfo */
139836 /* 28 */ "cid",
139837 /* 29 */ "name",
139838 /* 30 */ "desc",
139839 /* 31 */ "coll",
139840 /* 32 */ "key",
139841 /* 33 */ "seq", /* Used by: index_list */
139842 /* 34 */ "name",
139843 /* 35 */ "unique",
139844 /* 36 */ "origin",
139845 /* 37 */ "partial",
139846 /* 38 */ "tbl", /* Used by: stats */
139847 /* 39 */ "idx",
139848 /* 40 */ "wdth",
139849 /* 41 */ "hght",
139850 /* 42 */ "flgs",
139851 /* 43 */ "table", /* Used by: foreign_key_check */
139852 /* 44 */ "rowid",
139853 /* 45 */ "parent",
139854 /* 46 */ "fkid",
139855 /* 47 */ "busy", /* Used by: wal_checkpoint */
139856 /* 48 */ "log",
139857 /* 49 */ "checkpointed",
139858 /* 50 */ "seq", /* Used by: database_list */
139859 /* 51 */ "name",
139860 /* 52 */ "file",
139861 /* index_info reuses 27 */
 
139862 /* 53 */ "database", /* Used by: lock_status */
139863 /* 54 */ "status",
139864 /* collation_list reuses 33 */
139865 /* 55 */ "cache_size", /* Used by: default_cache_size */
139866 /* module_list pragma_list reuses 9 */
139867 /* 56 */ "timeout", /* Used by: busy_timeout */
139868 };
139869
@@ -139882,11 +139952,11 @@
139952 #endif
139953 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139954 {/* zName: */ "collation_list",
139955 /* ePragTyp: */ PragTyp_COLLATION_LIST,
139956 /* ePragFlg: */ PragFlg_Result0,
139957 /* ColNames: */ 33, 2,
139958 /* iArg: */ 0 },
139959 #endif
139960 #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
139961 {/* zName: */ "compile_options",
139962 /* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
@@ -139917,11 +139987,11 @@
139987 #endif
139988 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139989 {/* zName: */ "database_list",
139990 /* ePragTyp: */ PragTyp_DATABASE_LIST,
139991 /* ePragFlg: */ PragFlg_Result0,
139992 /* ColNames: */ 50, 3,
139993 /* iArg: */ 0 },
139994 #endif
139995 #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
139996 {/* zName: */ "default_cache_size",
139997 /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
@@ -139997,11 +140067,11 @@
140067 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
140068 #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
140069 {/* zName: */ "function_list",
140070 /* ePragTyp: */ PragTyp_FUNCTION_LIST,
140071 /* ePragFlg: */ PragFlg_Result0,
140072 /* ColNames: */ 15, 6,
140073 /* iArg: */ 0 },
140074 #endif
140075 #endif
140076 {/* zName: */ "hard_heap_limit",
140077 /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT,
@@ -140026,21 +140096,21 @@
140096 #endif
140097 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
140098 {/* zName: */ "index_info",
140099 /* ePragTyp: */ PragTyp_INDEX_INFO,
140100 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140101 /* ColNames: */ 27, 3,
140102 /* iArg: */ 0 },
140103 {/* zName: */ "index_list",
140104 /* ePragTyp: */ PragTyp_INDEX_LIST,
140105 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140106 /* ColNames: */ 33, 5,
140107 /* iArg: */ 0 },
140108 {/* zName: */ "index_xinfo",
140109 /* ePragTyp: */ PragTyp_INDEX_INFO,
140110 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140111 /* ColNames: */ 27, 6,
140112 /* iArg: */ 1 },
140113 #endif
140114 #if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
140115 {/* zName: */ "integrity_check",
140116 /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
@@ -140215,11 +140285,11 @@
140285 #endif
140286 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
140287 {/* zName: */ "stats",
140288 /* ePragTyp: */ PragTyp_STATS,
140289 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
140290 /* ColNames: */ 38, 5,
140291 /* iArg: */ 0 },
140292 #endif
140293 #if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
140294 {/* zName: */ "synchronous",
140295 /* ePragTyp: */ PragTyp_SYNCHRONOUS,
@@ -140234,11 +140304,11 @@
140304 /* ColNames: */ 8, 6,
140305 /* iArg: */ 0 },
140306 {/* zName: */ "table_list",
140307 /* ePragTyp: */ PragTyp_TABLE_LIST,
140308 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1,
140309 /* ColNames: */ 21, 6,
140310 /* iArg: */ 0 },
140311 {/* zName: */ "table_xinfo",
140312 /* ePragTyp: */ PragTyp_TABLE_INFO,
140313 /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
140314 /* ColNames: */ 8, 7,
@@ -140311,11 +140381,11 @@
140381 /* ColNames: */ 0, 0,
140382 /* iArg: */ 0 },
140383 {/* zName: */ "wal_checkpoint",
140384 /* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
140385 /* ePragFlg: */ PragFlg_NeedSchema,
140386 /* ColNames: */ 47, 3,
140387 /* iArg: */ 0 },
140388 #endif
140389 #if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
140390 {/* zName: */ "writable_schema",
140391 /* ePragTyp: */ PragTyp_FLAG,
@@ -140333,11 +140403,11 @@
140403 ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
140404 ** will be run with an analysis_limit set to the lessor of the value of
140405 ** the following macro or to the actual analysis_limit if it is non-zero,
140406 ** in order to prevent PRAGMA optimize from running for too long.
140407 **
140408 ** The value of 2000 is chosen empirically so that the worst-case run-time
140409 ** for PRAGMA optimize does not exceed 100 milliseconds against a variety
140410 ** of test databases on a RaspberryPI-4 compiled using -Os and without
140411 ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
140412 ** this paragraph, "worst-case" means that ANALYZE ends up being
140413 ** run on every table in the database. The worst case typically only
@@ -144622,11 +144692,11 @@
144692 pNew->iOffset = 0;
144693 pNew->selId = ++pParse->nSelect;
144694 pNew->addrOpenEphm[0] = -1;
144695 pNew->addrOpenEphm[1] = -1;
144696 pNew->nSelectRow = 0;
144697 if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
144698 pNew->pSrc = pSrc;
144699 pNew->pWhere = pWhere;
144700 pNew->pGroupBy = pGroupBy;
144701 pNew->pHaving = pHaving;
144702 pNew->pOrderBy = pOrderBy;
@@ -146005,20 +146075,20 @@
146075 /*
146076 ** Allocate a KeyInfo object sufficient for an index of N key columns and
146077 ** X extra columns.
146078 */
146079 SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
146080 int nExtra = (N+X)*(sizeof(CollSeq*)+1);
146081 KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
146082 if( p ){
146083 p->aSortFlags = (u8*)&p->aColl[N+X];
146084 p->nKeyField = (u16)N;
146085 p->nAllField = (u16)(N+X);
146086 p->enc = ENC(db);
146087 p->db = db;
146088 p->nRef = 1;
146089 memset(p->aColl, 0, nExtra);
146090 }else{
146091 return (KeyInfo*)sqlite3OomFault(db);
146092 }
146093 return p;
146094 }
@@ -150530,11 +150600,11 @@
150600 }
150601 pTabList = p->pSrc;
150602 pEList = p->pEList;
150603 if( pParse->pWith && (p->selFlags & SF_View) ){
150604 if( p->pWith==0 ){
150605 p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) );
150606 if( p->pWith==0 ){
150607 return WRC_Abort;
150608 }
150609 }
150610 p->pWith->bView = 1;
@@ -151669,10 +151739,11 @@
151739 ** * The subquery is a UNION ALL of two or more terms
151740 ** * The subquery does not have a LIMIT clause
151741 ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
151742 ** * The outer query is a simple count(*) with no WHERE clause or other
151743 ** extraneous syntax.
151744 ** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10)
151745 **
151746 ** Return TRUE if the optimization is undertaken.
151747 */
151748 static int countOfViewOptimization(Parse *pParse, Select *p){
151749 Select *pSub, *pPrior;
@@ -151701,11 +151772,15 @@
151772 if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */
151773 do{
151774 if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
151775 if( pSub->pWhere ) return 0; /* No WHERE clause */
151776 if( pSub->pLimit ) return 0; /* No LIMIT clause */
151777 if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){
151778 testcase( pSub->selFlags & SF_Aggregate );
151779 testcase( pSub->selFlags & SF_Distinct );
151780 return 0; /* Not an aggregate nor DISTINCT */
151781 }
151782 assert( pSub->pHaving==0 ); /* Due to the previous */
151783 pSub = pSub->pPrior; /* Repeat over compound */
151784 }while( pSub );
151785
151786 /* If we reach this point then it is OK to perform the transformation */
@@ -151713,11 +151788,11 @@
151788 db = pParse->db;
151789 pCount = pExpr;
151790 pExpr = 0;
151791 pSub = sqlite3SubqueryDetach(db, pFrom);
151792 sqlite3SrcListDelete(db, p->pSrc);
151793 p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
151794 while( pSub ){
151795 Expr *pTerm;
151796 pPrior = pSub->pPrior;
151797 pSub->pPrior = 0;
151798 pSub->pNext = 0;
@@ -154504,11 +154579,12 @@
154579 Vdbe *v = pParse->pVdbe;
154580 sqlite3 *db = pParse->db;
154581 ExprList *pNew;
154582 Returning *pReturning;
154583 Select sSelect;
154584 SrcList *pFrom;
154585 u8 fromSpace[SZ_SRCLIST_1];
154586
154587 assert( v!=0 );
154588 if( !pParse->bReturning ){
154589 /* This RETURNING trigger must be for a different statement as
154590 ** this statement lacks a RETURNING clause. */
@@ -154520,17 +154596,18 @@
154596 if( pTrigger != &(pReturning->retTrig) ){
154597 /* This RETURNING trigger is for a different statement */
154598 return;
154599 }
154600 memset(&sSelect, 0, sizeof(sSelect));
154601 pFrom = (SrcList*)fromSpace;
154602 memset(pFrom, 0, SZ_SRCLIST_1);
154603 sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
154604 sSelect.pSrc = pFrom;
154605 pFrom->nSrc = 1;
154606 pFrom->a[0].pSTab = pTab;
154607 pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
154608 pFrom->a[0].iCursor = -1;
154609 sqlite3SelectPrep(pParse, &sSelect, 0);
154610 if( pParse->nErr==0 ){
154611 assert( db->mallocFailed==0 );
154612 sqlite3GenerateColumnNames(pParse, &sSelect);
154613 }
@@ -156927,11 +157004,11 @@
157004 saved_flags = db->flags;
157005 saved_mDbFlags = db->mDbFlags;
157006 saved_nChange = db->nChange;
157007 saved_nTotalChange = db->nTotalChange;
157008 saved_mTrace = db->mTrace;
157009 db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;
157010 db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
157011 db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
157012 | SQLITE_Defensive | SQLITE_CountRows);
157013 db->mTrace = 0;
157014
@@ -159056,13 +159133,18 @@
159133 WhereLoop *pLoops; /* List of all WhereLoop objects */
159134 WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
159135 Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
159136 WhereClause sWC; /* Decomposition of the WHERE clause */
159137 WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
159138 WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */
159139 };
159140
159141 /*
159142 ** The size (in bytes) of a WhereInfo object that holds N WhereLevels.
159143 */
159144 #define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel))
159145
159146 /*
159147 ** Private interfaces - callable only by other where.c routines.
159148 **
159149 ** where.c:
159150 */
@@ -161509,12 +161591,11 @@
161591 */
161592 if( pWInfo->nLevel>1 ){
161593 int nNotReady; /* The number of notReady tables */
161594 SrcItem *origSrc; /* Original list of tables */
161595 nNotReady = pWInfo->nLevel - iLevel - 1;
161596 pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1));
 
161597 if( pOrTab==0 ) return notReady;
161598 pOrTab->nAlloc = (u8)(nNotReady + 1);
161599 pOrTab->nSrc = pOrTab->nAlloc;
161600 memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
161601 origSrc = pWInfo->pTabList->a;
@@ -162053,11 +162134,12 @@
162134 Expr *pSubWhere = 0;
162135 WhereClause *pWC = &pWInfo->sWC;
162136 WhereInfo *pSubWInfo;
162137 WhereLoop *pLoop = pLevel->pWLoop;
162138 SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
162139 SrcList *pFrom;
162140 u8 fromSpace[SZ_SRCLIST_1];
162141 Bitmask mAll = 0;
162142 int k;
162143
162144 ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
162145 sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
@@ -162097,17 +162179,18 @@
162179 if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
162180 pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
162181 sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
162182 }
162183 }
162184 pFrom = (SrcList*)fromSpace;
162185 pFrom->nSrc = 1;
162186 pFrom->nAlloc = 1;
162187 memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
162188 pFrom->a[0].fg.jointype = 0;
162189 assert( pParse->withinRJSubrtn < 100 );
162190 pParse->withinRJSubrtn++;
162191 pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0,
162192 WHERE_RIGHT_JOIN, 0);
162193 if( pSubWInfo ){
162194 int iCur = pLevel->iTabCur;
162195 int r = ++pParse->nMem;
162196 int nPk;
@@ -164091,15 +164174,20 @@
164174 WhereClause *pWC; /* The Where clause being analyzed */
164175 Parse *pParse; /* The parsing context */
164176 int eDistinct; /* Value to return from sqlite3_vtab_distinct() */
164177 u32 mIn; /* Mask of terms that are <col> IN (...) */
164178 u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */
164179 sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST
164180 ** Extra space is allocated to hold up
164181 ** to nTerm such values */
164182 };
164183
164184 /* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as
164185 ** many as N constraints */
164186 #define SZ_HIDDENINDEXINFO(N) \
164187 (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*))
164188
164189 /* Forward declaration of methods */
164190 static int whereLoopResize(sqlite3*, WhereLoop*, int);
164191
164192 /*
164193 ** Return the estimated number of output rows from a WHERE clause
@@ -165573,12 +165661,12 @@
165661
165662 /* Allocate the sqlite3_index_info structure
165663 */
165664 pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
165665 + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
165666 + sizeof(*pIdxOrderBy)*nOrderBy
165667 + SZ_HIDDENINDEXINFO(nTerm) );
165668 if( pIdxInfo==0 ){
165669 sqlite3ErrorMsg(pParse, "out of memory");
165670 return 0;
165671 }
165672 pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
@@ -170768,14 +170856,11 @@
170856 ** struct, the contents of WhereInfo.a[], the WhereClause structure
170857 ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
170858 ** field (type Bitmask) it must be aligned on an 8-byte boundary on
170859 ** some architectures. Hence the ROUND8() below.
170860 */
170861 nByteWInfo = SZ_WHEREINFO(nTabList);
 
 
 
170862 pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
170863 if( db->mallocFailed ){
170864 sqlite3DbFree(db, pWInfo);
170865 pWInfo = 0;
170866 goto whereBeginError;
@@ -181607,11 +181692,15 @@
181692 tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
181693 }else if( tokenType==TK_FILTER ){
181694 assert( n==6 );
181695 tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
181696 #endif /* SQLITE_OMIT_WINDOWFUNC */
181697 }else if( tokenType==TK_COMMENT
181698 && (db->init.busy || (db->flags & SQLITE_Comments)!=0)
181699 ){
181700 /* Ignore SQL comments if either (1) we are reparsing the schema or
181701 ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */
181702 zSql += n;
181703 continue;
181704 }else if( tokenType!=TK_QNUMBER ){
181705 Token x;
181706 x.z = zSql;
@@ -182502,10 +182591,18 @@
182591 }
182592 #endif
182593 if( rc==SQLITE_OK ){
182594 sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
182595 sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
182596 #ifdef SQLITE_EXTRA_INIT_MUTEXED
182597 {
182598 int SQLITE_EXTRA_INIT_MUTEXED(const char*);
182599 rc = SQLITE_EXTRA_INIT_MUTEXED(0);
182600 }
182601 #endif
182602 }
182603 if( rc==SQLITE_OK ){
182604 sqlite3MemoryBarrier();
182605 sqlite3GlobalConfig.isInit = 1;
182606 #ifdef SQLITE_EXTRA_INIT
182607 bRunExtraInit = 1;
182608 #endif
@@ -184063,10 +184160,14 @@
184160 sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt));
184161 sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC);
184162 }
184163 }
184164 sqlite3BtreeLeaveAll(db);
184165 #endif
184166 #if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT)
184167 UNUSED_PARAMETER(db);
184168 UNUSED_PARAMETER(flags);
184169 #endif
184170 return SQLITE_OK;
184171 }
184172
184173 /*
@@ -186032,11 +186133,11 @@
186133 }else if( pData==0 ){
186134 sqlite3_mutex_leave(db->mutex);
186135 return SQLITE_OK;
186136 }else{
186137 size_t n = strlen(zName);
186138 p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) );
186139 if( p==0 ){
186140 if( xDestructor ) xDestructor(pData);
186141 sqlite3_mutex_leave(db->mutex);
186142 return SQLITE_NOMEM;
186143 }
@@ -186398,12 +186499,12 @@
186499 #endif
186500
186501 /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
186502 **
186503 ** If b is true, then activate the SQLITE_FkNoAction setting. If b is
186504 ** false then clear that setting. If the SQLITE_FkNoAction setting is
186505 ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if
186506 ** they were NO ACTION, regardless of how they are defined.
186507 **
186508 ** NB: One must usually run "PRAGMA writable_schema=RESET" after
186509 ** using this test-control, before it will take full effect. failing
186510 ** to reset the schema can result in some unexpected behavior.
@@ -187746,11 +187847,11 @@
187847 ** }
187848 **
187849 ** Here, array { X } means zero or more occurrences of X, adjacent in
187850 ** memory. A "position" is an index of a token in the token stream
187851 ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur
187852 ** in the same logical place as the position element, and act as sentinels
187853 ** ending a position list array. POS_END is 0. POS_COLUMN is 1.
187854 ** The positions numbers are not stored literally but rather as two more
187855 ** than the difference from the prior position, or the just the position plus
187856 ** 2 for the first position. Example:
187857 **
@@ -188433,10 +188534,23 @@
188534
188535 #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
188536 #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
188537
188538 #define deliberate_fall_through
188539
188540 /*
188541 ** Macros needed to provide flexible arrays in a portable way
188542 */
188543 #ifndef offsetof
188544 # define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
188545 #endif
188546 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
188547 # define FLEXARRAY
188548 #else
188549 # define FLEXARRAY 1
188550 #endif
188551
188552
188553 #endif /* SQLITE_AMALGAMATION */
188554
188555 #ifdef SQLITE_DEBUG
188556 SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
@@ -188538,11 +188652,11 @@
188652 int inTransaction; /* True after xBegin but before xCommit/xRollback */
188653 int mxSavepoint; /* Largest valid xSavepoint integer */
188654 #endif
188655
188656 #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
188657 /* True to disable the incremental doclist optimization. This is controlled
188658 ** by special insert command 'test-no-incr-doclist'. */
188659 int bNoIncrDoclist;
188660
188661 /* Number of segments in a level */
188662 int nMergeCount;
@@ -188590,11 +188704,11 @@
188704 #define FTS3_EVAL_NEXT 1
188705 #define FTS3_EVAL_MATCHINFO 2
188706
188707 /*
188708 ** The Fts3Cursor.eSearch member is always set to one of the following.
188709 ** Actually, Fts3Cursor.eSearch can be greater than or equal to
188710 ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index
188711 ** of the column to be searched. For example, in
188712 **
188713 ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
188714 ** SELECT docid FROM ex1 WHERE b MATCH 'one two three';
@@ -188663,12 +188777,16 @@
188777 /* Variables below this point are populated by fts3_expr.c when parsing
188778 ** a MATCH expression. Everything above is part of the evaluation phase.
188779 */
188780 int nToken; /* Number of tokens in the phrase */
188781 int iColumn; /* Index of column this phrase must match */
188782 Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */
188783 };
188784
188785 /* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */
188786 #define SZ_FTS3PHRASE(N) \
188787 (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken))
188788
188789 /*
188790 ** A tree of these objects forms the RHS of a MATCH operator.
188791 **
188792 ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
@@ -191243,11 +191361,11 @@
191361 **
191362 ** The space required to store the output is therefore the sum of the
191363 ** sizes of the two inputs, plus enough space for exactly one of the input
191364 ** docids to grow.
191365 **
191366 ** A symmetric argument may be made if the doclists are in descending
191367 ** order.
191368 */
191369 aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
191370 if( !aOut ) return SQLITE_NOMEM;
191371
@@ -193341,11 +193459,11 @@
193459 **
193460 ** * features at least one token that uses an incremental doclist, and
193461 **
193462 ** * does not contain any deferred tokens.
193463 **
193464 ** Advance it to the next matching document in the database and populate
193465 ** the Fts3Doclist.pList and nList fields.
193466 **
193467 ** If there is no "next" entry and no error occurs, then *pbEof is set to
193468 ** 1 before returning. Otherwise, if no error occurs and the iterator is
193469 ** successfully advanced, *pbEof is set to 0.
@@ -194348,11 +194466,11 @@
194466
194467 return rc;
194468 }
194469
194470 /*
194471 ** Restart iteration for expression pExpr so that the next call to
194472 ** fts3EvalNext() visits the first row. Do not allow incremental
194473 ** loading or merging of phrase doclists for this iteration.
194474 **
194475 ** If *pRc is other than SQLITE_OK when this function is called, it is
194476 ** a no-op. If an error occurs within this function, *pRc is set to an
@@ -195539,10 +195657,27 @@
195657 /*
195658 ** Function getNextNode(), which is called by fts3ExprParse(), may itself
195659 ** call fts3ExprParse(). So this forward declaration is required.
195660 */
195661 static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
195662
195663 /*
195664 ** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis
195665 ** is defined, search for '(' and ')' as well. Return the index of the first
195666 ** such character in the buffer. If there is no such character, return -1.
195667 */
195668 static int findBarredChar(const char *z, int n){
195669 int ii;
195670 for(ii=0; ii<n; ii++){
195671 if( (z[ii]=='"')
195672 || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')'))
195673 ){
195674 return ii;
195675 }
195676 }
195677 return -1;
195678 }
195679
195680 /*
195681 ** Extract the next token from buffer z (length n) using the tokenizer
195682 ** and other information (column names etc.) in pParse. Create an Fts3Expr
195683 ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
@@ -195564,38 +195699,42 @@
195699 sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
195700 sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
195701 int rc;
195702 sqlite3_tokenizer_cursor *pCursor;
195703 Fts3Expr *pRet = 0;
195704
195705 *pnConsumed = n;
195706 rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
 
 
 
 
 
 
 
195707 if( rc==SQLITE_OK ){
195708 const char *zToken;
195709 int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
195710 sqlite3_int64 nByte; /* total space to allocate */
195711
195712 rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
195713 if( rc==SQLITE_OK ){
195714 /* Check that this tokenization did not gobble up any " characters. Or,
195715 ** if enable_parenthesis is true, that it did not gobble up any
195716 ** open or close parenthesis characters either. If it did, call
195717 ** getNextToken() again, but pass only that part of the input buffer
195718 ** up to the first such character. */
195719 int iBarred = findBarredChar(z, iEnd);
195720 if( iBarred>=0 ){
195721 pModule->xClose(pCursor);
195722 return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed);
195723 }
195724
195725 nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken;
195726 pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
195727 if( !pRet ){
195728 rc = SQLITE_NOMEM;
195729 }else{
195730 pRet->eType = FTSQUERY_PHRASE;
195731 pRet->pPhrase = (Fts3Phrase *)&pRet[1];
195732 pRet->pPhrase->nToken = 1;
195733 pRet->pPhrase->iColumn = iCol;
195734 pRet->pPhrase->aToken[0].n = nToken;
195735 pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1];
195736 memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
195737
195738 if( iEnd<n && z[iEnd]=='*' ){
195739 pRet->pPhrase->aToken[0].isPrefix = 1;
195740 iEnd++;
@@ -195615,11 +195754,15 @@
195754 }
195755 }
195756
195757 }
195758 *pnConsumed = iEnd;
195759 }else if( n && rc==SQLITE_DONE ){
195760 int iBarred = findBarredChar(z, n);
195761 if( iBarred>=0 ){
195762 *pnConsumed = iBarred;
195763 }
195764 rc = SQLITE_OK;
195765 }
195766
195767 pModule->xClose(pCursor);
195768 }
@@ -195664,11 +195807,11 @@
195807 Fts3Expr *p = 0;
195808 sqlite3_tokenizer_cursor *pCursor = 0;
195809 char *zTemp = 0;
195810 i64 nTemp = 0;
195811
195812 const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1);
195813 int nToken = 0;
195814
195815 /* The final Fts3Expr data structure, including the Fts3Phrase,
195816 ** Fts3PhraseToken structures token buffers are all stored as a single
195817 ** allocation so that the expression can be freed with a single call to
@@ -196036,11 +196179,11 @@
196179 int eType = p->eType;
196180 isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
196181
196182 /* The isRequirePhrase variable is set to true if a phrase or
196183 ** an expression contained in parenthesis is required. If a
196184 ** binary operator (AND, OR, NOT or NEAR) is encountered when
196185 ** isRequirePhrase is set, this is a syntax error.
196186 */
196187 if( !isPhrase && isRequirePhrase ){
196188 sqlite3Fts3ExprFree(p);
196189 rc = SQLITE_ERROR;
@@ -196618,11 +196761,10 @@
196761 pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
196762 );
196763 }
196764
196765 if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
 
196766 sqlite3_result_error(context, "Error parsing expression", -1);
196767 }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
196768 sqlite3_result_error_nomem(context);
196769 }else{
196770 sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
@@ -196861,11 +197003,11 @@
197003 pEntry->count++;
197004 pEntry->chain = pNew;
197005 }
197006
197007
197008 /* Resize the hash table so that it contains "new_size" buckets.
197009 ** "new_size" must be a power of 2. The hash table might fail
197010 ** to resize if sqliteMalloc() fails.
197011 **
197012 ** Return non-zero if a memory allocation error occurs.
197013 */
@@ -197316,11 +197458,11 @@
197458 isConsonant(z+2);
197459 }
197460
197461 /*
197462 ** If the word ends with zFrom and xCond() is true for the stem
197463 ** of the word that precedes the zFrom ending, then change the
197464 ** ending to zTo.
197465 **
197466 ** The input word *pz and zFrom are both in reverse order. zTo
197467 ** is in normal order.
197468 **
@@ -202899,11 +203041,11 @@
203041 ** previous term. Before this function returns, it is updated to contain a
203042 ** copy of zTerm/nTerm.
203043 **
203044 ** It is assumed that the buffer associated with pNode is already large
203045 ** enough to accommodate the new entry. The buffer associated with pPrev
203046 ** is extended by this function if required.
203047 **
203048 ** If an error (i.e. OOM condition) occurs, an SQLite error code is
203049 ** returned. Otherwise, SQLITE_OK.
203050 */
203051 static int fts3AppendToNode(
@@ -204562,11 +204704,11 @@
204704 #endif
204705
204706 /*
204707 ** SQLite value pRowid contains the rowid of a row that may or may not be
204708 ** present in the FTS3 table. If it is, delete it and adjust the contents
204709 ** of subsidiary data structures accordingly.
204710 */
204711 static int fts3DeleteByRowid(
204712 Fts3Table *p,
204713 sqlite3_value *pRowid,
204714 int *pnChng, /* IN/OUT: Decrement if row is deleted */
@@ -204888,12 +205030,16 @@
205030 struct MatchinfoBuffer {
205031 u8 aRef[3];
205032 int nElem;
205033 int bGlobal; /* Set if global data is loaded */
205034 char *zMatchinfo;
205035 u32 aMI[FLEXARRAY];
205036 };
205037
205038 /* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */
205039 #define SZ_MATCHINFOBUFFER(N) \
205040 (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64))
205041
205042
205043 /*
205044 ** The snippet() and offsets() functions both return text values. An instance
205045 ** of the following structure is used to accumulate those values while the
@@ -204915,17 +205061,17 @@
205061 ** Allocate a two-slot MatchinfoBuffer object.
205062 */
205063 static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
205064 MatchinfoBuffer *pRet;
205065 sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
205066 + SZ_MATCHINFOBUFFER(1);
205067 sqlite3_int64 nStr = strlen(zMatchinfo);
205068
205069 pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
205070 if( pRet ){
205071 pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet;
205072 pRet->aMI[1+nElem] = pRet->aMI[0]
205073 + sizeof(u32)*((int)nElem+1);
205074 pRet->nElem = (int)nElem;
205075 pRet->zMatchinfo = ((char*)pRet) + nByte;
205076 memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
205077 pRet->aRef[0] = 1;
@@ -204935,14 +205081,14 @@
205081 }
205082
205083 static void fts3MIBufferFree(void *p){
205084 MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
205085
205086 assert( (u32*)p==&pBuf->aMI[1]
205087 || (u32*)p==&pBuf->aMI[pBuf->nElem+2]
205088 );
205089 if( (u32*)p==&pBuf->aMI[1] ){
205090 pBuf->aRef[1] = 0;
205091 }else{
205092 pBuf->aRef[2] = 0;
205093 }
205094
@@ -204955,32 +205101,32 @@
205101 void (*xRet)(void*) = 0;
205102 u32 *aOut = 0;
205103
205104 if( p->aRef[1]==0 ){
205105 p->aRef[1] = 1;
205106 aOut = &p->aMI[1];
205107 xRet = fts3MIBufferFree;
205108 }
205109 else if( p->aRef[2]==0 ){
205110 p->aRef[2] = 1;
205111 aOut = &p->aMI[p->nElem+2];
205112 xRet = fts3MIBufferFree;
205113 }else{
205114 aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
205115 if( aOut ){
205116 xRet = sqlite3_free;
205117 if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32));
205118 }
205119 }
205120
205121 *paOut = aOut;
205122 return xRet;
205123 }
205124
205125 static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
205126 p->bGlobal = 1;
205127 memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32));
205128 }
205129
205130 /*
205131 ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
205132 */
@@ -205391,11 +205537,11 @@
205537 if( nAppend<0 ){
205538 nAppend = (int)strlen(zAppend);
205539 }
205540
205541 /* If there is insufficient space allocated at StrBuffer.z, use realloc()
205542 ** to grow the buffer until so that it is big enough to accommodate the
205543 ** appended data.
205544 */
205545 if( pStr->n+nAppend+1>=pStr->nAlloc ){
205546 sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100;
205547 char *zNew = sqlite3_realloc64(pStr->z, nAlloc);
@@ -207766,11 +207912,11 @@
207912 **
207913 ** When a match if found, the matching entry is moved to become the
207914 ** most-recently used entry if it isn't so already.
207915 **
207916 ** The JsonParse object returned still belongs to the Cache and might
207917 ** be deleted at any moment. If the caller wants the JsonParse to
207918 ** linger, it needs to increment the nPJRef reference counter.
207919 */
207920 static JsonParse *jsonCacheSearch(
207921 sqlite3_context *ctx, /* The SQL statement context holding the cache */
207922 sqlite3_value *pArg /* Function argument containing SQL text */
@@ -210811,11 +210957,11 @@
210957 **
210958 ** This goes against all historical documentation about how the SQLite
210959 ** JSON functions were suppose to work. From the beginning, blob was
210960 ** reserved for expansion and a blob value should have raised an error.
210961 ** But it did not, due to a bug. And many applications came to depend
210962 ** upon this buggy behavior, especially when using the CLI and reading
210963 ** JSON text using readfile(), which returns a blob. For this reason
210964 ** we will continue to support the bug moving forward.
210965 ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
210966 */
210967 }
@@ -212911,10 +213057,18 @@
213057 # define NEVER(X) ((X)?(assert(0),1):0)
213058 #else
213059 # define ALWAYS(X) (X)
213060 # define NEVER(X) (X)
213061 #endif
213062 #ifndef offsetof
213063 #define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
213064 #endif
213065 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
213066 # define FLEXARRAY
213067 #else
213068 # define FLEXARRAY 1
213069 #endif
213070 #endif /* !defined(SQLITE_AMALGAMATION) */
213071
213072 /* Macro to check for 4-byte alignment. Only used inside of assert() */
213073 #ifdef SQLITE_DEBUG
213074 # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0)
@@ -213231,13 +213385,17 @@
213385 struct RtreeMatchArg {
213386 u32 iSize; /* Size of this object */
213387 RtreeGeomCallback cb; /* Info about the callback functions */
213388 int nParam; /* Number of parameters to the SQL function */
213389 sqlite3_value **apSqlParam; /* Original SQL parameter values */
213390 RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */
213391 };
213392
213393 /* Size of an RtreeMatchArg object with N parameters */
213394 #define SZ_RTREEMATCHARG(N) \
213395 (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue))
213396
213397 #ifndef MAX
213398 # define MAX(x,y) ((x) < (y) ? (y) : (x))
213399 #endif
213400 #ifndef MIN
213401 # define MIN(x,y) ((x) > (y) ? (y) : (x))
@@ -214922,11 +215080,11 @@
215080
215081 return rc;
215082 }
215083
215084 /*
215085 ** Return the N-dimensional volume of the cell stored in *p.
215086 */
215087 static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
215088 RtreeDValue area = (RtreeDValue)1;
215089 assert( pRtree->nDim>=1 && pRtree->nDim<=5 );
215090 #ifndef SQLITE_RTREE_INT_ONLY
@@ -216688,11 +216846,11 @@
216846 }
216847
216848 /*
216849 ** The second and subsequent arguments to this function are a printf()
216850 ** style format string and arguments. This function formats the string and
216851 ** appends it to the report being accumulated in pCheck.
216852 */
216853 static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
216854 va_list ap;
216855 va_start(ap, zFmt);
216856 if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){
@@ -217876,11 +218034,11 @@
218034
218035 /*
218036 ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
218037 ** Returns:
218038 **
218039 ** +2 x0,y0 is on the line segment
218040 **
218041 ** +1 x0,y0 is beneath line segment
218042 **
218043 ** 0 x0,y0 is not on or beneath the line segment or the line segment
218044 ** is vertical and x0,y0 is not on the line segment
@@ -217982,11 +218140,11 @@
218140 }
218141 sqlite3_free(p1);
218142 sqlite3_free(p2);
218143 }
218144
218145 /* Objects used by the overlap algorithm. */
218146 typedef struct GeoEvent GeoEvent;
218147 typedef struct GeoSegment GeoSegment;
218148 typedef struct GeoOverlap GeoOverlap;
218149 struct GeoEvent {
218150 double x; /* X coordinate at which event occurs */
@@ -219029,12 +219187,11 @@
219187 RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
219188 RtreeMatchArg *pBlob;
219189 sqlite3_int64 nBlob;
219190 int memErr = 0;
219191
219192 nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*);
 
219193 pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
219194 if( !pBlob ){
219195 sqlite3_result_error_nomem(ctx);
219196 }else{
219197 int i;
@@ -220125,11 +220282,11 @@
220282 ** to read from the original database snapshot. In other words, partially
220283 ** applied transactions are not visible to other clients.
220284 **
220285 ** "RBU" stands for "Resumable Bulk Update". As in a large database update
220286 ** transmitted via a wireless network to a mobile device. A transaction
220287 ** applied using this extension is hence referred to as an "RBU update".
220288 **
220289 **
220290 ** LIMITATIONS
220291 **
220292 ** An "RBU update" transaction is subject to the following limitations:
@@ -220422,11 +220579,11 @@
220579 ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
220580 ** of the state tables within the state database are zeroed. This way,
220581 ** the next call to sqlite3rbu_vacuum() opens a handle that starts a
220582 ** new RBU vacuum operation.
220583 **
220584 ** As with sqlite3rbu_open(), Zipvfs users should refer to the comment
220585 ** describing the sqlite3rbu_create_vfs() API function below for
220586 ** a description of the complications associated with using RBU with
220587 ** zipvfs databases.
220588 */
220589 SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
@@ -220518,11 +220675,11 @@
220675 /*
220676 ** Close an RBU handle.
220677 **
220678 ** If the RBU update has been completely applied, mark the RBU database
220679 ** as fully applied. Otherwise, assuming no error has occurred, save the
220680 ** current state of the RBU update application to the RBU database.
220681 **
220682 ** If an error has already occurred as part of an sqlite3rbu_step()
220683 ** or sqlite3rbu_open() call, or if one occurs within this function, an
220684 ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL,
220685 ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted
@@ -225444,11 +225601,11 @@
225601 int rc;
225602 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
225603
225604 /* If this is an RBU vacuum operation and this is the target database,
225605 ** pretend that it has at least one page. Otherwise, SQLite will not
225606 ** check for the existence of a *-wal file. rbuVfsRead() contains
225607 ** similar logic. */
225608 if( rc==SQLITE_OK && *pSize==0
225609 && p->pRbu && rbuIsVacuum(p->pRbu)
225610 && (p->openFlags & SQLITE_OPEN_MAIN_DB)
225611 ){
@@ -228674,11 +228831,11 @@
228831 }
228832
228833 /*
228834 ** This function is called to initialize the SessionTable.nCol, azCol[]
228835 ** abPK[] and azDflt[] members of SessionTable object pTab. If these
228836 ** fields are already initialized, this function is a no-op.
228837 **
228838 ** If an error occurs, an error code is stored in sqlite3_session.rc and
228839 ** non-zero returned. Or, if no error occurs but the table has no primary
228840 ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
228841 ** indicate that updates on this table should be ignored. SessionTable.abPK
@@ -230497,11 +230654,11 @@
230654 int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
230655 void **ppChangeset /* OUT: Buffer containing changeset */
230656 ){
230657 sqlite3 *db = pSession->db; /* Source database handle */
230658 SessionTable *pTab; /* Used to iterate through attached tables */
230659 SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */
230660 int rc; /* Return code */
230661
230662 assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
230663 assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );
230664
@@ -234931,10 +235088,22 @@
235088 # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
235089 #else
235090 # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
235091 #endif
235092
235093 /*
235094 ** Macros needed to provide flexible arrays in a portable way
235095 */
235096 #ifndef offsetof
235097 # define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
235098 #endif
235099 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
235100 # define FLEXARRAY
235101 #else
235102 # define FLEXARRAY 1
235103 #endif
235104
235105 #endif
235106
235107 /* Truncate very long tokens to this many bytes. Hard limit is
235108 ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
235109 ** field that occurs at the start of each leaf page (see fts5_index.c). */
@@ -235003,14 +235172,15 @@
235172 **
235173 ** This object is used by fts5_expr.c and fts5_index.c.
235174 */
235175 struct Fts5Colset {
235176 int nCol;
235177 int aiCol[FLEXARRAY];
235178 };
235179
235180 /* Size (int bytes) of a complete Fts5Colset object with N columns. */
235181 #define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2))
235182
235183 /**************************************************************************
235184 ** Interface to code in fts5_config.c. fts5_config.c contains contains code
235185 ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
235186 */
@@ -235835,11 +236005,11 @@
236005 *************************************************************************
236006 ** Driver template for the LEMON parser generator.
236007 **
236008 ** The "lemon" program processes an LALR(1) input grammar file, then uses
236009 ** this template to construct a parser. The "lemon" program inserts text
236010 ** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the
236011 ** interstitial "-" characters) contained in this template is changed into
236012 ** the value of the %name directive from the grammar. Otherwise, the content
236013 ** of this template is copied straight through into the generate parser
236014 ** source file.
236015 **
@@ -237989,11 +238159,11 @@
238159 ** where "N" is the total number of documents in the set and nHit
238160 ** is the number that contain at least one instance of the phrase
238161 ** under consideration.
238162 **
238163 ** The problem with this is that if (N < 2*nHit), the IDF is
238164 ** negative. Which is undesirable. So the minimum allowable IDF is
238165 ** (1e-6) - roughly the same as a term that appears in just over
238166 ** half of set of 5,000,000 documents. */
238167 double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
238168 if( idf<=0.0 ) idf = 1e-6;
238169 p->aIDF[i] = idf;
@@ -238452,11 +238622,11 @@
238622 **
238623 ** * All non-ASCII characters,
238624 ** * The 52 upper and lower case ASCII characters, and
238625 ** * The 10 integer ASCII characters.
238626 ** * The underscore character "_" (0x5F).
238627 ** * The unicode "substitute" character (0x1A).
238628 */
238629 static int sqlite3Fts5IsBareword(char t){
238630 u8 aBareword[128] = {
238631 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */
238632 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
@@ -239770,12 +239940,16 @@
239940 Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
239941
239942 /* Child nodes. For a NOT node, this array always contains 2 entries. For
239943 ** AND or OR nodes, it contains 2 or more entries. */
239944 int nChild; /* Number of child nodes */
239945 Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */
239946 };
239947
239948 /* Size (in bytes) of an Fts5ExprNode object that holds up to N children */
239949 #define SZ_FTS5EXPRNODE(N) \
239950 (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*))
239951
239952 #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
239953
239954 /*
239955 ** Invoke the xNext method of an Fts5ExprNode object. This macro should be
@@ -239803,24 +239977,31 @@
239977 */
239978 struct Fts5ExprPhrase {
239979 Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
239980 Fts5Buffer poslist; /* Current position list */
239981 int nTerm; /* Number of entries in aTerm[] */
239982 Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */
239983 };
239984
239985 /* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */
239986 #define SZ_FTS5EXPRPHRASE(N) \
239987 (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm))
239988
239989 /*
239990 ** One or more phrases that must appear within a certain token distance of
239991 ** each other within each matching document.
239992 */
239993 struct Fts5ExprNearset {
239994 int nNear; /* NEAR parameter */
239995 Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
239996 int nPhrase; /* Number of entries in aPhrase[] array */
239997 Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */
239998 };
239999
240000 /* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */
240001 #define SZ_FTS5EXPRNEARSET(N) \
240002 (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*))
240003
240004 /*
240005 ** Parse context.
240006 */
240007 struct Fts5Parse {
@@ -239976,11 +240157,11 @@
240157 assert_expr_depth_ok(sParse.rc, sParse.pExpr);
240158
240159 /* If the LHS of the MATCH expression was a user column, apply the
240160 ** implicit column-filter. */
240161 if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
240162 int n = SZ_FTS5COLSET(1);
240163 Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
240164 if( pColset ){
240165 pColset->nCol = 1;
240166 pColset->aiCol[0] = iCol;
240167 sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset);
@@ -241334,11 +241515,11 @@
241515 Fts5ExprNearset *pRet = 0;
241516
241517 if( pParse->rc==SQLITE_OK ){
241518 if( pNear==0 ){
241519 sqlite3_int64 nByte;
241520 nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1);
241521 pRet = sqlite3_malloc64(nByte);
241522 if( pRet==0 ){
241523 pParse->rc = SQLITE_NOMEM;
241524 }else{
241525 memset(pRet, 0, (size_t)nByte);
@@ -241345,11 +241526,11 @@
241526 }
241527 }else if( (pNear->nPhrase % SZALLOC)==0 ){
241528 int nNew = pNear->nPhrase + SZALLOC;
241529 sqlite3_int64 nByte;
241530
241531 nByte = SZ_FTS5EXPRNEARSET(nNew+1);
241532 pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
241533 if( pRet==0 ){
241534 pParse->rc = SQLITE_NOMEM;
241535 }
241536 }else{
@@ -241436,16 +241617,16 @@
241617 if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
241618 Fts5ExprPhrase *pNew;
241619 int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
241620
241621 pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
241622 SZ_FTS5EXPRPHRASE(nNew+1)
241623 );
241624 if( pNew==0 ){
241625 rc = SQLITE_NOMEM;
241626 }else{
241627 if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1));
241628 pCtx->pPhrase = pPhrase = pNew;
241629 pNew->nTerm = nNew - SZALLOC;
241630 }
241631 }
241632
@@ -241549,11 +241730,11 @@
241730 }
241731
241732 if( sCtx.pPhrase==0 ){
241733 /* This happens when parsing a token or quoted phrase that contains
241734 ** no token characters at all. (e.g ... MATCH '""'). */
241735 sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1));
241736 }else if( sCtx.pPhrase->nTerm ){
241737 sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
241738 }
241739 assert( pParse->apPhrase!=0 );
241740 pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
@@ -241584,23 +241765,22 @@
241765 if( rc==SQLITE_OK ){
241766 pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
241767 sizeof(Fts5ExprPhrase*));
241768 }
241769 if( rc==SQLITE_OK ){
241770 pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1));
 
241771 }
241772 if( rc==SQLITE_OK ){
241773 pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
241774 SZ_FTS5EXPRNEARSET(2));
241775 }
241776 if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
241777 Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
241778 if( pColsetOrig ){
241779 sqlite3_int64 nByte;
241780 Fts5Colset *pColset;
241781 nByte = SZ_FTS5COLSET(pColsetOrig->nCol);
241782 pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
241783 if( pColset ){
241784 memcpy(pColset, pColsetOrig, (size_t)nByte);
241785 }
241786 pNew->pRoot->pNear->pColset = pColset;
@@ -241624,11 +241804,11 @@
241804 }
241805 }
241806 }else{
241807 /* This happens when parsing a token or quoted phrase that contains
241808 ** no token characters at all. (e.g ... MATCH '""'). */
241809 sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1));
241810 }
241811 }
241812
241813 if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
241814 /* All the allocations succeeded. Put the expression object together. */
@@ -241718,11 +241898,11 @@
241898 Fts5Colset *pNew; /* New colset object to return */
241899
241900 assert( pParse->rc==SQLITE_OK );
241901 assert( iCol>=0 && iCol<pParse->pConfig->nCol );
241902
241903 pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1));
241904 if( pNew==0 ){
241905 pParse->rc = SQLITE_NOMEM;
241906 }else{
241907 int *aiCol = pNew->aiCol;
241908 int i, j;
@@ -241753,11 +241933,11 @@
241933 static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
241934 Fts5Colset *pRet;
241935 int nCol = pParse->pConfig->nCol;
241936
241937 pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
241938 SZ_FTS5COLSET(nCol+1)
241939 );
241940 if( pRet ){
241941 int i;
241942 int iOld = 0;
241943 for(i=0; i<nCol; i++){
@@ -241814,11 +241994,11 @@
241994 ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned.
241995 */
241996 static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
241997 Fts5Colset *pRet;
241998 if( pOrig ){
241999 sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol);
242000 pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
242001 if( pRet ){
242002 memcpy(pRet, pOrig, (size_t)nByte);
242003 }
242004 }else{
@@ -241982,21 +242162,21 @@
242162 Fts5ExprNode *pRet;
242163
242164 assert( pNear->nPhrase==1 );
242165 assert( pParse->bPhraseToAnd );
242166
242167 nByte = SZ_FTS5EXPRNODE(nTerm+1);
242168 pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
242169 if( pRet ){
242170 pRet->eType = FTS5_AND;
242171 pRet->nChild = nTerm;
242172 pRet->iHeight = 1;
242173 fts5ExprAssignXNext(pRet);
242174 pParse->nPhrase--;
242175 for(ii=0; ii<nTerm; ii++){
242176 Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
242177 &pParse->rc, SZ_FTS5EXPRPHRASE(1)
242178 );
242179 if( pPhrase ){
242180 if( parseGrowPhraseArray(pParse) ){
242181 fts5ExprPhraseFree(pPhrase);
242182 }else{
@@ -242061,11 +242241,11 @@
242241 nChild = 2;
242242 if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
242243 if( pRight->eType==eType ) nChild += pRight->nChild-1;
242244 }
242245
242246 nByte = SZ_FTS5EXPRNODE(nChild);
242247 pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
242248
242249 if( pRet ){
242250 pRet->eType = eType;
242251 pRet->pNear = pNear;
@@ -242936,11 +243116,11 @@
243116 }
243117 return rc;
243118 }
243119
243120 /*
243121 ** Clear the token mappings for all Fts5IndexIter objects managed by
243122 ** the expression passed as the only argument.
243123 */
243124 static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
243125 int ii;
243126 for(ii=0; ii<pExpr->nPhrase; ii++){
@@ -242971,11 +243151,11 @@
243151
243152 typedef struct Fts5HashEntry Fts5HashEntry;
243153
243154 /*
243155 ** This file contains the implementation of an in-memory hash table used
243156 ** to accumulate "term -> doclist" content before it is flushed to a level-0
243157 ** segment.
243158 */
243159
243160
243161 struct Fts5Hash {
@@ -243028,11 +243208,11 @@
243208 int iPos; /* Position of last value written */
243209 i64 iRowid; /* Rowid of last value written */
243210 };
243211
243212 /*
243213 ** Equivalent to:
243214 **
243215 ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
243216 */
243217 #define fts5EntryKey(p) ( ((char *)(&(p)[1])) )
243218
@@ -243964,13 +244144,17 @@
244144 int nRef; /* Object reference count */
244145 u64 nWriteCounter; /* Total leaves written to level 0 */
244146 u64 nOriginCntr; /* Origin value for next top-level segment */
244147 int nSegment; /* Total segments in this structure */
244148 int nLevel; /* Number of levels in this index */
244149 Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */
244150 };
244151
244152 /* Size (in bytes) of an Fts5Structure object holding up to N levels */
244153 #define SZ_FTS5STRUCTURE(N) \
244154 (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel))
244155
244156 /*
244157 ** An object of type Fts5SegWriter is used to write to segments.
244158 */
244159 struct Fts5PageWriter {
244160 int pgno; /* Page number for this page */
@@ -244096,14 +244280,18 @@
244280
244281 /*
244282 ** Array of tombstone pages. Reference counted.
244283 */
244284 struct Fts5TombstoneArray {
244285 int nRef; /* Number of pointers to this object */
244286 int nTombstone;
244287 Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */
244288 };
244289
244290 /* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */
244291 #define SZ_FTS5TOMBSTONEARRAY(N) \
244292 (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*))
244293
244294 /*
244295 ** Argument is a pointer to an Fts5Data structure that contains a
244296 ** leaf page.
244297 */
@@ -244169,12 +244357,15 @@
244357 int bRev; /* True to iterate in reverse order */
244358 u8 bSkipEmpty; /* True to skip deleted entries */
244359
244360 i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
244361 Fts5CResult *aFirst; /* Current merge state (see above) */
244362 Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */
244363 };
244364
244365 /* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */
244366 #define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter))
244367
244368 /*
244369 ** An instance of the following type is used to iterate through the contents
244370 ** of a doclist-index record.
244371 **
@@ -244198,12 +244389,16 @@
244389 i64 iRowid; /* First rowid on leaf iLeafPgno */
244390 };
244391 struct Fts5DlidxIter {
244392 int nLvl;
244393 int iSegid;
244394 Fts5DlidxLvl aLvl[FLEXARRAY];
244395 };
244396
244397 /* Size (in bytes) of an Fts5DlidxIter object with up to N levels */
244398 #define SZ_FTS5DLIDXITER(N) \
244399 (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl))
244400
244401 static void fts5PutU16(u8 *aOut, u16 iVal){
244402 aOut[0] = (iVal>>8);
244403 aOut[1] = (iVal&0xFF);
244404 }
@@ -244568,11 +244763,11 @@
244763 ** an error occurs, (*pRc) is set to an SQLite error code before returning.
244764 */
244765 static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
244766 Fts5Structure *p = *pp;
244767 if( *pRc==SQLITE_OK && p->nRef>1 ){
244768 i64 nByte = SZ_FTS5STRUCTURE(p->nLevel);
244769 Fts5Structure *pNew;
244770 pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
244771 if( pNew ){
244772 int i;
244773 memcpy(pNew, p, nByte);
@@ -244642,14 +244837,11 @@
244837 if( nLevel>FTS5_MAX_SEGMENT || nLevel<0
244838 || nSegment>FTS5_MAX_SEGMENT || nSegment<0
244839 ){
244840 return FTS5_CORRUPT;
244841 }
244842 nByte = SZ_FTS5STRUCTURE(nLevel);
 
 
 
244843 pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);
244844
244845 if( pRet ){
244846 pRet->nRef = 1;
244847 pRet->nLevel = nLevel;
@@ -244725,14 +244917,11 @@
244917 fts5StructureMakeWritable(pRc, ppStruct);
244918 assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
244919 if( *pRc==SQLITE_OK ){
244920 Fts5Structure *pStruct = *ppStruct;
244921 int nLevel = pStruct->nLevel;
244922 sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2);
 
 
 
244923
244924 pStruct = sqlite3_realloc64(pStruct, nByte);
244925 if( pStruct ){
244926 memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
244927 pStruct->nLevel++;
@@ -245267,11 +245456,11 @@
245456 Fts5DlidxIter *pIter = 0;
245457 int i;
245458 int bDone = 0;
245459
245460 for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
245461 sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1);
245462 Fts5DlidxIter *pNew;
245463
245464 pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
245465 if( pNew==0 ){
245466 p->rc = SQLITE_NOMEM;
@@ -245485,11 +245674,11 @@
245674 ** leave an error in the Fts5Index object.
245675 */
245676 static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
245677 const int nTomb = pIter->pSeg->nPgTombstone;
245678 if( nTomb>0 ){
245679 int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
245680 Fts5TombstoneArray *pNew;
245681 pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
245682 if( pNew ){
245683 pNew->nTombstone = nTomb;
245684 pNew->nRef = 1;
@@ -246946,12 +247135,11 @@
247135 Fts5Iter *pNew;
247136 i64 nSlot; /* Power of two >= nSeg */
247137
247138 for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
247139 pNew = fts5IdxMalloc(p,
247140 SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */
 
247141 sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
247142 );
247143 if( pNew ){
247144 pNew->nSeg = nSlot;
247145 pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
@@ -249313,11 +249501,11 @@
249501 static Fts5Structure *fts5IndexOptimizeStruct(
249502 Fts5Index *p,
249503 Fts5Structure *pStruct
249504 ){
249505 Fts5Structure *pNew = 0;
249506 sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1);
249507 int nSeg = pStruct->nSegment;
249508 int i;
249509
249510 /* Figure out if this structure requires optimization. A structure does
249511 ** not require optimization if either:
@@ -249343,10 +249531,11 @@
249531 }
249532 assert( pStruct->aLevel[i].nMerge<=nThis );
249533 }
249534
249535 nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
249536 assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
249537 pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
249538
249539 if( pNew ){
249540 Fts5StructureLevel *pLvl;
249541 nByte = nSeg * sizeof(Fts5StructureSegment);
@@ -249919,12 +250108,16 @@
250108 /* The following are used for other full-token tokendata queries only. */
250109 int nIter;
250110 int nIterAlloc;
250111 Fts5PoslistReader *aPoslistReader;
250112 int *aPoslistToIter;
250113 Fts5Iter *apIter[FLEXARRAY];
250114 };
250115
250116 /* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */
250117 #define SZ_FTS5TOKENDATAITER(N) \
250118 (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter))
250119
250120 /*
250121 ** The two input arrays - a1[] and a2[] - are in sorted order. This function
250122 ** merges the two arrays together and writes the result to output array
250123 ** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
@@ -249993,11 +250186,11 @@
250186 }
250187
250188 /*
250189 ** Sort the contents of the pT->aMap[] array.
250190 **
250191 ** The sorting algorithm requires a malloc(). If this fails, an error code
250192 ** is left in Fts5Index.rc before returning.
250193 */
250194 static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
250195 Fts5TokenDataMap *aTmp = 0;
250196 int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
@@ -250184,11 +250377,11 @@
250377 if( iIdx==0
250378 && p->pConfig->eDetail==FTS5_DETAIL_FULL
250379 && p->pConfig->bPrefixInsttoken
250380 ){
250381 s.pTokendata = &s2;
250382 s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1));
250383 }
250384
250385 if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
250386 s.xMerge = fts5MergeRowidLists;
250387 s.xAppend = fts5AppendRowid;
@@ -250312,19 +250505,21 @@
250505 ** The %_data table is completely empty when this function is called. This
250506 ** function populates it with the initial structure objects for each index,
250507 ** and the initial version of the "averages" record (a zero-byte blob).
250508 */
250509 static int sqlite3Fts5IndexReinit(Fts5Index *p){
250510 Fts5Structure *pTmp;
250511 u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
250512 fts5StructureInvalidate(p);
250513 fts5IndexDiscardData(p);
250514 pTmp = (Fts5Structure*)tmpSpace;
250515 memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
250516 if( p->pConfig->bContentlessDelete ){
250517 pTmp->nOriginCntr = 1;
250518 }
250519 fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
250520 fts5StructureWrite(p, pTmp);
250521 return fts5IndexReturn(p);
250522 }
250523
250524 /*
250525 ** Open a new Fts5Index handle. If the bCreate argument is true, create
@@ -250528,11 +250723,11 @@
250723 Fts5TokenDataIter *pRet = pIn;
250724
250725 if( p->rc==SQLITE_OK ){
250726 if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
250727 int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
250728 int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
250729 Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
250730
250731 if( pNew==0 ){
250732 p->rc = SQLITE_NOMEM;
250733 }else{
@@ -251044,11 +251239,12 @@
251239
251240 memset(&ctx, 0, sizeof(ctx));
251241
251242 fts5BufferGrow(&p->rc, &token, nToken+1);
251243 assert( token.p!=0 || p->rc!=SQLITE_OK );
251244 ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
251245 SZ_FTS5TOKENDATAITER(1));
251246
251247 if( p->rc==SQLITE_OK ){
251248
251249 /* Fill in the token prefix to search for */
251250 token.p[0] = FTS5_MAIN_PREFIX;
@@ -251175,11 +251371,12 @@
251371 assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
251372 assert( pIter->pTokenDataIter || pIter->nSeg>0 );
251373 if( pIter->nSeg>0 ){
251374 /* This is a prefix term iterator. */
251375 if( pT==0 ){
251376 pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
251377 SZ_FTS5TOKENDATAITER(1));
251378 pIter->pTokenDataIter = pT;
251379 }
251380 if( pT ){
251381 fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
251382 fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
@@ -252209,11 +252406,11 @@
252406 }
252407 #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
252408
252409 #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
252410 static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
252411 int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */
252412 fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);
252413
252414 if( iSegid==0 ){
252415 if( iKey==FTS5_AVERAGES_ROWID ){
252416 sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
@@ -253170,13 +253367,15 @@
253367 struct Fts5Sorter {
253368 sqlite3_stmt *pStmt;
253369 i64 iRowid; /* Current rowid */
253370 const u8 *aPoslist; /* Position lists for current row */
253371 int nIdx; /* Number of entries in aIdx[] */
253372 int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */
253373 };
253374
253375 /* Size (int bytes) of an Fts5Sorter object with N indexes */
253376 #define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64))
253377
253378 /*
253379 ** Virtual-table cursor object.
253380 **
253381 ** iSpecial:
@@ -254050,11 +254249,11 @@
254249 int rc;
254250 const char *zRank = pCsr->zRank;
254251 const char *zRankArgs = pCsr->zRankArgs;
254252
254253 nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
254254 nByte = SZ_FTS5SORTER(nPhrase);
254255 pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
254256 if( pSorter==0 ) return SQLITE_NOMEM;
254257 memset(pSorter, 0, (size_t)nByte);
254258 pSorter->nIdx = nPhrase;
254259
@@ -256576,11 +256775,11 @@
256775 int nArg, /* Number of args */
256776 sqlite3_value **apUnused /* Function arguments */
256777 ){
256778 assert( nArg==0 );
256779 UNUSED_PARAM2(nArg, apUnused);
256780 sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT);
256781 }
256782
256783 /*
256784 ** Implementation of fts5_locale(LOCALE, TEXT) function.
256785 **
256786
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.50.0"
150150
#define SQLITE_VERSION_NUMBER 3050000
151
-#define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7"
151
+#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
@@ -5173,11 +5173,11 @@
51735173
** For all versions of SQLite up to and including 3.6.23.1, a call to
51745174
** [sqlite3_reset()] was required after sqlite3_step() returned anything
51755175
** other than [SQLITE_ROW] before any subsequent invocation of
51765176
** sqlite3_step(). Failure to reset the prepared statement using
51775177
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
5178
-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
5178
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
51795179
** sqlite3_step() began
51805180
** calling [sqlite3_reset()] automatically in this circumstance rather
51815181
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
51825182
** break because any application that ever receives an SQLITE_MISUSE error
51835183
** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7069,10 +7069,12 @@
70697069
** ^Any callback set by a previous call to this function
70707070
** for the same database connection is overridden.
70717071
**
70727072
** ^The second argument is a pointer to the function to invoke when a
70737073
** row is updated, inserted or deleted in a rowid table.
7074
+** ^The update hook is disabled by invoking sqlite3_update_hook()
7075
+** with a NULL pointer as the second parameter.
70747076
** ^The first argument to the callback is a copy of the third argument
70757077
** to sqlite3_update_hook().
70767078
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
70777079
** or [SQLITE_UPDATE], depending on the operation that caused the callback
70787080
** to be invoked.
70797081
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.50.0"
150 #define SQLITE_VERSION_NUMBER 3050000
151 #define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -5173,11 +5173,11 @@
5173 ** For all versions of SQLite up to and including 3.6.23.1, a call to
5174 ** [sqlite3_reset()] was required after sqlite3_step() returned anything
5175 ** other than [SQLITE_ROW] before any subsequent invocation of
5176 ** sqlite3_step(). Failure to reset the prepared statement using
5177 ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
5178 ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
5179 ** sqlite3_step() began
5180 ** calling [sqlite3_reset()] automatically in this circumstance rather
5181 ** than returning [SQLITE_MISUSE]. This is not considered a compatibility
5182 ** break because any application that ever receives an SQLITE_MISUSE error
5183 ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7069,10 +7069,12 @@
7069 ** ^Any callback set by a previous call to this function
7070 ** for the same database connection is overridden.
7071 **
7072 ** ^The second argument is a pointer to the function to invoke when a
7073 ** row is updated, inserted or deleted in a rowid table.
 
 
7074 ** ^The first argument to the callback is a copy of the third argument
7075 ** to sqlite3_update_hook().
7076 ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
7077 ** or [SQLITE_UPDATE], depending on the operation that caused the callback
7078 ** to be invoked.
7079
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.50.0"
150 #define SQLITE_VERSION_NUMBER 3050000
151 #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -5173,11 +5173,11 @@
5173 ** For all versions of SQLite up to and including 3.6.23.1, a call to
5174 ** [sqlite3_reset()] was required after sqlite3_step() returned anything
5175 ** other than [SQLITE_ROW] before any subsequent invocation of
5176 ** sqlite3_step(). Failure to reset the prepared statement using
5177 ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
5178 ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
5179 ** sqlite3_step() began
5180 ** calling [sqlite3_reset()] automatically in this circumstance rather
5181 ** than returning [SQLITE_MISUSE]. This is not considered a compatibility
5182 ** break because any application that ever receives an SQLITE_MISUSE error
5183 ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7069,10 +7069,12 @@
7069 ** ^Any callback set by a previous call to this function
7070 ** for the same database connection is overridden.
7071 **
7072 ** ^The second argument is a pointer to the function to invoke when a
7073 ** row is updated, inserted or deleted in a rowid table.
7074 ** ^The update hook is disabled by invoking sqlite3_update_hook()
7075 ** with a NULL pointer as the second parameter.
7076 ** ^The first argument to the callback is a copy of the third argument
7077 ** to sqlite3_update_hook().
7078 ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
7079 ** or [SQLITE_UPDATE], depending on the operation that caused the callback
7080 ** to be invoked.
7081
+1 -1
--- src/add.c
+++ src/add.c
@@ -78,11 +78,11 @@
7878
static int numManifests;
7979
8080
if( cachedManifest == -1 ){
8181
int i;
8282
Blob repo;
83
- cachedManifest = db_get_manifest_setting();
83
+ cachedManifest = db_get_manifest_setting(0);
8484
numManifests = 0;
8585
for(i=0; i<count(aManifestflags); i++){
8686
if( cachedManifest&aManifestflags[i].flg ) {
8787
azManifests[numManifests++] = aManifestflags[i].fname;
8888
}
8989
--- src/add.c
+++ src/add.c
@@ -78,11 +78,11 @@
78 static int numManifests;
79
80 if( cachedManifest == -1 ){
81 int i;
82 Blob repo;
83 cachedManifest = db_get_manifest_setting();
84 numManifests = 0;
85 for(i=0; i<count(aManifestflags); i++){
86 if( cachedManifest&aManifestflags[i].flg ) {
87 azManifests[numManifests++] = aManifestflags[i].fname;
88 }
89
--- src/add.c
+++ src/add.c
@@ -78,11 +78,11 @@
78 static int numManifests;
79
80 if( cachedManifest == -1 ){
81 int i;
82 Blob repo;
83 cachedManifest = db_get_manifest_setting(0);
84 numManifests = 0;
85 for(i=0; i<count(aManifestflags); i++){
86 if( cachedManifest&aManifestflags[i].flg ) {
87 azManifests[numManifests++] = aManifestflags[i].fname;
88 }
89
+27 -6
--- src/alerts.c
+++ src/alerts.c
@@ -51,10 +51,11 @@
5151
@ -- f - Forum posts
5252
@ -- k - ** Special: Unsubscribed using /oneclickunsub
5353
@ -- n - New forum threads
5454
@ -- r - Replies to my own forum posts
5555
@ -- t - Ticket changes
56
+@ -- u - Elevation of users' permissions (admins only)
5657
@ -- w - Wiki changes
5758
@ -- x - Edits to forum posts
5859
@ -- Probably different codes will be added in the future. In the future
5960
@ -- we might also add a separate table that allows subscribing to email
6061
@ -- notifications for specific branches or tags or tickets.
@@ -1133,11 +1134,11 @@
11331134
** designated host and port and all times.
11341135
*/
11351136
11361137
11371138
/*
1138
-** COMMAND: alerts* abbreviated-subcommands
1139
+** COMMAND: alerts* abbrv-subcom
11391140
**
11401141
** Usage: %fossil alerts SUBCOMMAND ARGS...
11411142
**
11421143
** Subcommands:
11431144
**
@@ -1258,11 +1259,11 @@
12581259
g.argc = 3;
12591260
}
12601261
pSetting = setting_info(&nSetting);
12611262
for(; nSetting>0; nSetting--, pSetting++ ){
12621263
if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1263
- print_setting(pSetting, 0);
1264
+ print_setting(pSetting, 0, 0);
12641265
}
12651266
}else
12661267
if( strncmp(zCmd, "status", nCmd)==0 ){
12671268
Stmt q;
12681269
int iCutoff;
@@ -1273,11 +1274,11 @@
12731274
verify_all_options();
12741275
if( g.argc!=3 ) usage("status");
12751276
pSetting = setting_info(&nSetting);
12761277
for(; nSetting>0; nSetting--, pSetting++ ){
12771278
if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1278
- print_setting(pSetting, 0);
1279
+ print_setting(pSetting, 0, 0);
12791280
}
12801281
n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep");
12811282
fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n);
12821283
n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest");
12831284
fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n);
@@ -1563,10 +1564,11 @@
15631564
if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
15641565
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
15651566
if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
15661567
if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
15671568
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
1569
+ if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u';
15681570
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
15691571
if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
15701572
ssub[nsub] = 0;
15711573
zCode = db_text(0,
15721574
"INSERT INTO subscriber(semail,suname,"
@@ -1627,10 +1629,11 @@
16271629
if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
16281630
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
16291631
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
16301632
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
16311633
if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
1634
+ if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1);
16321635
if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
16331636
}
16341637
@ <p>To receive email notifications for changes to this
16351638
@ repository, fill out the form below and press the "Submit" button.</p>
16361639
form_begin(0, "%R/subscribe");
@@ -1699,10 +1702,14 @@
16991702
}
17001703
if( g.perm.RdWiki ){
17011704
@ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
17021705
@ Wiki</label><br>
17031706
}
1707
+ if( g.perm.Admin ){
1708
+ @ <label><input type="checkbox" name="su" %s(PCK("su"))> \
1709
+ @ User permission elevation</label>
1710
+ }
17041711
di = PB("di");
17051712
@ </td></tr>
17061713
@ <tr>
17071714
@ <td class="form_label">Delivery:</td>
17081715
@ <td><select size="1" name="di">
@@ -1820,11 +1827,11 @@
18201827
** verifying the email address.
18211828
*/
18221829
void alert_page(void){
18231830
const char *zName = 0; /* Value of the name= query parameter */
18241831
Stmt q; /* For querying the database */
1825
- int sa, sc, sf, st, sw, sx; /* Types of notifications requested */
1832
+ int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */
18261833
int sn, sr;
18271834
int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
18281835
int isLogin; /* True if logged in as an individual */
18291836
const char *ssub = 0; /* Subscription flags */
18301837
const char *semail = 0; /* Email address */
@@ -1881,10 +1888,11 @@
18811888
if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
18821889
if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
18831890
if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
18841891
if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
18851892
if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
1893
+ if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u';
18861894
if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
18871895
if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
18881896
newSsub[nsub] = 0;
18891897
ssub = newSsub;
18901898
blob_init(&update, "UPDATE subscriber SET", -1);
@@ -1979,10 +1987,11 @@
19791987
sc = strchr(ssub,'c')!=0;
19801988
sf = strchr(ssub,'f')!=0;
19811989
sn = strchr(ssub,'n')!=0;
19821990
sr = strchr(ssub,'r')!=0;
19831991
st = strchr(ssub,'t')!=0;
1992
+ su = strchr(ssub,'u')!=0;
19841993
sw = strchr(ssub,'w')!=0;
19851994
sx = strchr(ssub,'x')!=0;
19861995
smip = db_column_text(&q, 5);
19871996
mtime = db_column_text(&q, 7);
19881997
sctime = db_column_text(&q, 8);
@@ -2097,11 +2106,19 @@
20972106
@ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
20982107
@ Ticket changes</label><br>
20992108
}
21002109
if( g.perm.RdWiki ){
21012110
@ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
2102
- @ Wiki</label>
2111
+ @ Wiki</label><br>
2112
+ }
2113
+ if( g.perm.Admin ){
2114
+ /* Corner-case bug: if an admin assigns 'u' to a non-admin, that
2115
+ ** subscription will get removed if the user later edits their
2116
+ ** subscriptions, as non-admins are not permitted to add that
2117
+ ** subscription. */
2118
+ @ <label><input type="checkbox" name="su" %s(su?"checked":"")>\
2119
+ @ User permission elevation</label>
21032120
}
21042121
@ </td></tr>
21052122
if( strchr(ssub,'k')!=0 ){
21062123
@ <tr><td></td><td>&nbsp;&uarr;&nbsp;
21072124
@ Note: User did a one-click unsubscribe</td></tr>
@@ -2529,15 +2546,16 @@
25292546
** f An original forum post
25302547
** n New forum threads
25312548
** r Replies to my forum posts
25322549
** x An edit to a prior forum post
25332550
** t A new ticket or a change to an existing ticket
2551
+** u A user was added or received new permissions
25342552
** w A change to a wiki page
25352553
** x Edits to forum posts
25362554
*/
25372555
struct EmailEvent {
2538
- int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */
2556
+ int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */
25392557
int needMod; /* Pending moderator approval */
25402558
Blob hdr; /* Header content, for forum entries */
25412559
Blob txt; /* Text description to appear in an alert */
25422560
char *zFromName; /* Human name of the sender */
25432561
char *zPriors; /* Upthread sender IDs for forum posts */
@@ -2932,10 +2950,11 @@
29322950
);
29332951
if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n");
29342952
if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n");
29352953
if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n");
29362954
if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n");
2955
+ if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n");
29372956
if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n");
29382957
blob_appendf(pBody, "\n"
29392958
"If you take no action, your subscription will expire and you will be\n"
29402959
"unsubscribed in about %d days. To make other changes or to unsubscribe\n"
29412960
"immediately, visit the following webpage:\n\n"
@@ -3144,10 +3163,11 @@
31443163
switch( p->type ){
31453164
case 'x': case 'f':
31463165
case 'n': case 'r': xType = '5'; break;
31473166
case 't': xType = 'q'; break;
31483167
case 'w': xType = 'l'; break;
3168
+ /* Note: case 'u' is not handled here */
31493169
}
31503170
if( strchr(zCap,xType)==0 ) continue;
31513171
}
31523172
}else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
31533173
/* Setup and admin users can get any notification that does not
@@ -3160,10 +3180,11 @@
31603180
case 'c': xType = 'o'; break;
31613181
case 'x': case 'f':
31623182
case 'n': case 'r': xType = '2'; break;
31633183
case 't': xType = 'r'; break;
31643184
case 'w': xType = 'j'; break;
3185
+ /* Note: case 'u' is not handled here */
31653186
}
31663187
if( strchr(zCap,xType)==0 ) continue;
31673188
}
31683189
if( blob_size(&p->hdr)>0 ){
31693190
/* This alert should be sent as a separate email */
31703191
--- src/alerts.c
+++ src/alerts.c
@@ -51,10 +51,11 @@
51 @ -- f - Forum posts
52 @ -- k - ** Special: Unsubscribed using /oneclickunsub
53 @ -- n - New forum threads
54 @ -- r - Replies to my own forum posts
55 @ -- t - Ticket changes
 
56 @ -- w - Wiki changes
57 @ -- x - Edits to forum posts
58 @ -- Probably different codes will be added in the future. In the future
59 @ -- we might also add a separate table that allows subscribing to email
60 @ -- notifications for specific branches or tags or tickets.
@@ -1133,11 +1134,11 @@
1133 ** designated host and port and all times.
1134 */
1135
1136
1137 /*
1138 ** COMMAND: alerts* abbreviated-subcommands
1139 **
1140 ** Usage: %fossil alerts SUBCOMMAND ARGS...
1141 **
1142 ** Subcommands:
1143 **
@@ -1258,11 +1259,11 @@
1258 g.argc = 3;
1259 }
1260 pSetting = setting_info(&nSetting);
1261 for(; nSetting>0; nSetting--, pSetting++ ){
1262 if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1263 print_setting(pSetting, 0);
1264 }
1265 }else
1266 if( strncmp(zCmd, "status", nCmd)==0 ){
1267 Stmt q;
1268 int iCutoff;
@@ -1273,11 +1274,11 @@
1273 verify_all_options();
1274 if( g.argc!=3 ) usage("status");
1275 pSetting = setting_info(&nSetting);
1276 for(; nSetting>0; nSetting--, pSetting++ ){
1277 if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1278 print_setting(pSetting, 0);
1279 }
1280 n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep");
1281 fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n);
1282 n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest");
1283 fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n);
@@ -1563,10 +1564,11 @@
1563 if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
1564 if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
1565 if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
1566 if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
1567 if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
 
1568 if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
1569 if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
1570 ssub[nsub] = 0;
1571 zCode = db_text(0,
1572 "INSERT INTO subscriber(semail,suname,"
@@ -1627,10 +1629,11 @@
1627 if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
1628 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
1629 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
1630 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
1631 if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
 
1632 if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
1633 }
1634 @ <p>To receive email notifications for changes to this
1635 @ repository, fill out the form below and press the "Submit" button.</p>
1636 form_begin(0, "%R/subscribe");
@@ -1699,10 +1702,14 @@
1699 }
1700 if( g.perm.RdWiki ){
1701 @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
1702 @ Wiki</label><br>
1703 }
 
 
 
 
1704 di = PB("di");
1705 @ </td></tr>
1706 @ <tr>
1707 @ <td class="form_label">Delivery:</td>
1708 @ <td><select size="1" name="di">
@@ -1820,11 +1827,11 @@
1820 ** verifying the email address.
1821 */
1822 void alert_page(void){
1823 const char *zName = 0; /* Value of the name= query parameter */
1824 Stmt q; /* For querying the database */
1825 int sa, sc, sf, st, sw, sx; /* Types of notifications requested */
1826 int sn, sr;
1827 int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
1828 int isLogin; /* True if logged in as an individual */
1829 const char *ssub = 0; /* Subscription flags */
1830 const char *semail = 0; /* Email address */
@@ -1881,10 +1888,11 @@
1881 if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
1882 if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
1883 if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
1884 if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
1885 if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
 
1886 if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
1887 if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
1888 newSsub[nsub] = 0;
1889 ssub = newSsub;
1890 blob_init(&update, "UPDATE subscriber SET", -1);
@@ -1979,10 +1987,11 @@
1979 sc = strchr(ssub,'c')!=0;
1980 sf = strchr(ssub,'f')!=0;
1981 sn = strchr(ssub,'n')!=0;
1982 sr = strchr(ssub,'r')!=0;
1983 st = strchr(ssub,'t')!=0;
 
1984 sw = strchr(ssub,'w')!=0;
1985 sx = strchr(ssub,'x')!=0;
1986 smip = db_column_text(&q, 5);
1987 mtime = db_column_text(&q, 7);
1988 sctime = db_column_text(&q, 8);
@@ -2097,11 +2106,19 @@
2097 @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
2098 @ Ticket changes</label><br>
2099 }
2100 if( g.perm.RdWiki ){
2101 @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
2102 @ Wiki</label>
 
 
 
 
 
 
 
 
2103 }
2104 @ </td></tr>
2105 if( strchr(ssub,'k')!=0 ){
2106 @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
2107 @ Note: User did a one-click unsubscribe</td></tr>
@@ -2529,15 +2546,16 @@
2529 ** f An original forum post
2530 ** n New forum threads
2531 ** r Replies to my forum posts
2532 ** x An edit to a prior forum post
2533 ** t A new ticket or a change to an existing ticket
 
2534 ** w A change to a wiki page
2535 ** x Edits to forum posts
2536 */
2537 struct EmailEvent {
2538 int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */
2539 int needMod; /* Pending moderator approval */
2540 Blob hdr; /* Header content, for forum entries */
2541 Blob txt; /* Text description to appear in an alert */
2542 char *zFromName; /* Human name of the sender */
2543 char *zPriors; /* Upthread sender IDs for forum posts */
@@ -2932,10 +2950,11 @@
2932 );
2933 if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n");
2934 if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n");
2935 if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n");
2936 if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n");
 
2937 if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n");
2938 blob_appendf(pBody, "\n"
2939 "If you take no action, your subscription will expire and you will be\n"
2940 "unsubscribed in about %d days. To make other changes or to unsubscribe\n"
2941 "immediately, visit the following webpage:\n\n"
@@ -3144,10 +3163,11 @@
3144 switch( p->type ){
3145 case 'x': case 'f':
3146 case 'n': case 'r': xType = '5'; break;
3147 case 't': xType = 'q'; break;
3148 case 'w': xType = 'l'; break;
 
3149 }
3150 if( strchr(zCap,xType)==0 ) continue;
3151 }
3152 }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
3153 /* Setup and admin users can get any notification that does not
@@ -3160,10 +3180,11 @@
3160 case 'c': xType = 'o'; break;
3161 case 'x': case 'f':
3162 case 'n': case 'r': xType = '2'; break;
3163 case 't': xType = 'r'; break;
3164 case 'w': xType = 'j'; break;
 
3165 }
3166 if( strchr(zCap,xType)==0 ) continue;
3167 }
3168 if( blob_size(&p->hdr)>0 ){
3169 /* This alert should be sent as a separate email */
3170
--- src/alerts.c
+++ src/alerts.c
@@ -51,10 +51,11 @@
51 @ -- f - Forum posts
52 @ -- k - ** Special: Unsubscribed using /oneclickunsub
53 @ -- n - New forum threads
54 @ -- r - Replies to my own forum posts
55 @ -- t - Ticket changes
56 @ -- u - Elevation of users' permissions (admins only)
57 @ -- w - Wiki changes
58 @ -- x - Edits to forum posts
59 @ -- Probably different codes will be added in the future. In the future
60 @ -- we might also add a separate table that allows subscribing to email
61 @ -- notifications for specific branches or tags or tickets.
@@ -1133,11 +1134,11 @@
1134 ** designated host and port and all times.
1135 */
1136
1137
1138 /*
1139 ** COMMAND: alerts* abbrv-subcom
1140 **
1141 ** Usage: %fossil alerts SUBCOMMAND ARGS...
1142 **
1143 ** Subcommands:
1144 **
@@ -1258,11 +1259,11 @@
1259 g.argc = 3;
1260 }
1261 pSetting = setting_info(&nSetting);
1262 for(; nSetting>0; nSetting--, pSetting++ ){
1263 if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1264 print_setting(pSetting, 0, 0);
1265 }
1266 }else
1267 if( strncmp(zCmd, "status", nCmd)==0 ){
1268 Stmt q;
1269 int iCutoff;
@@ -1273,11 +1274,11 @@
1274 verify_all_options();
1275 if( g.argc!=3 ) usage("status");
1276 pSetting = setting_info(&nSetting);
1277 for(; nSetting>0; nSetting--, pSetting++ ){
1278 if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1279 print_setting(pSetting, 0, 0);
1280 }
1281 n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep");
1282 fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n);
1283 n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest");
1284 fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n);
@@ -1563,10 +1564,11 @@
1564 if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
1565 if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
1566 if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
1567 if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
1568 if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
1569 if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u';
1570 if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
1571 if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
1572 ssub[nsub] = 0;
1573 zCode = db_text(0,
1574 "INSERT INTO subscriber(semail,suname,"
@@ -1627,10 +1629,11 @@
1629 if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
1630 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
1631 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
1632 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
1633 if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
1634 if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1);
1635 if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
1636 }
1637 @ <p>To receive email notifications for changes to this
1638 @ repository, fill out the form below and press the "Submit" button.</p>
1639 form_begin(0, "%R/subscribe");
@@ -1699,10 +1702,14 @@
1702 }
1703 if( g.perm.RdWiki ){
1704 @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
1705 @ Wiki</label><br>
1706 }
1707 if( g.perm.Admin ){
1708 @ <label><input type="checkbox" name="su" %s(PCK("su"))> \
1709 @ User permission elevation</label>
1710 }
1711 di = PB("di");
1712 @ </td></tr>
1713 @ <tr>
1714 @ <td class="form_label">Delivery:</td>
1715 @ <td><select size="1" name="di">
@@ -1820,11 +1827,11 @@
1827 ** verifying the email address.
1828 */
1829 void alert_page(void){
1830 const char *zName = 0; /* Value of the name= query parameter */
1831 Stmt q; /* For querying the database */
1832 int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */
1833 int sn, sr;
1834 int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
1835 int isLogin; /* True if logged in as an individual */
1836 const char *ssub = 0; /* Subscription flags */
1837 const char *semail = 0; /* Email address */
@@ -1881,10 +1888,11 @@
1888 if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
1889 if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
1890 if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
1891 if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
1892 if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
1893 if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u';
1894 if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
1895 if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
1896 newSsub[nsub] = 0;
1897 ssub = newSsub;
1898 blob_init(&update, "UPDATE subscriber SET", -1);
@@ -1979,10 +1987,11 @@
1987 sc = strchr(ssub,'c')!=0;
1988 sf = strchr(ssub,'f')!=0;
1989 sn = strchr(ssub,'n')!=0;
1990 sr = strchr(ssub,'r')!=0;
1991 st = strchr(ssub,'t')!=0;
1992 su = strchr(ssub,'u')!=0;
1993 sw = strchr(ssub,'w')!=0;
1994 sx = strchr(ssub,'x')!=0;
1995 smip = db_column_text(&q, 5);
1996 mtime = db_column_text(&q, 7);
1997 sctime = db_column_text(&q, 8);
@@ -2097,11 +2106,19 @@
2106 @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
2107 @ Ticket changes</label><br>
2108 }
2109 if( g.perm.RdWiki ){
2110 @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
2111 @ Wiki</label><br>
2112 }
2113 if( g.perm.Admin ){
2114 /* Corner-case bug: if an admin assigns 'u' to a non-admin, that
2115 ** subscription will get removed if the user later edits their
2116 ** subscriptions, as non-admins are not permitted to add that
2117 ** subscription. */
2118 @ <label><input type="checkbox" name="su" %s(su?"checked":"")>\
2119 @ User permission elevation</label>
2120 }
2121 @ </td></tr>
2122 if( strchr(ssub,'k')!=0 ){
2123 @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
2124 @ Note: User did a one-click unsubscribe</td></tr>
@@ -2529,15 +2546,16 @@
2546 ** f An original forum post
2547 ** n New forum threads
2548 ** r Replies to my forum posts
2549 ** x An edit to a prior forum post
2550 ** t A new ticket or a change to an existing ticket
2551 ** u A user was added or received new permissions
2552 ** w A change to a wiki page
2553 ** x Edits to forum posts
2554 */
2555 struct EmailEvent {
2556 int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */
2557 int needMod; /* Pending moderator approval */
2558 Blob hdr; /* Header content, for forum entries */
2559 Blob txt; /* Text description to appear in an alert */
2560 char *zFromName; /* Human name of the sender */
2561 char *zPriors; /* Upthread sender IDs for forum posts */
@@ -2932,10 +2950,11 @@
2950 );
2951 if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n");
2952 if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n");
2953 if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n");
2954 if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n");
2955 if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n");
2956 if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n");
2957 blob_appendf(pBody, "\n"
2958 "If you take no action, your subscription will expire and you will be\n"
2959 "unsubscribed in about %d days. To make other changes or to unsubscribe\n"
2960 "immediately, visit the following webpage:\n\n"
@@ -3144,10 +3163,11 @@
3163 switch( p->type ){
3164 case 'x': case 'f':
3165 case 'n': case 'r': xType = '5'; break;
3166 case 't': xType = 'q'; break;
3167 case 'w': xType = 'l'; break;
3168 /* Note: case 'u' is not handled here */
3169 }
3170 if( strchr(zCap,xType)==0 ) continue;
3171 }
3172 }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
3173 /* Setup and admin users can get any notification that does not
@@ -3160,10 +3180,11 @@
3180 case 'c': xType = 'o'; break;
3181 case 'x': case 'f':
3182 case 'n': case 'r': xType = '2'; break;
3183 case 't': xType = 'r'; break;
3184 case 'w': xType = 'j'; break;
3185 /* Note: case 'u' is not handled here */
3186 }
3187 if( strchr(zCap,xType)==0 ) continue;
3188 }
3189 if( blob_size(&p->hdr)>0 ){
3190 /* This alert should be sent as a separate email */
3191
+2 -1
--- src/allrepo.c
+++ src/allrepo.c
@@ -51,11 +51,11 @@
5151
blob_appendf(pExtra, " %s", g.argv[i]);
5252
}
5353
}
5454
5555
/*
56
-** COMMAND: all abbreviated-subcommands
56
+** COMMAND: all abbrv-subcom
5757
**
5858
** Usage: %fossil all SUBCOMMAND ...
5959
**
6060
** The ~/.fossil file records the location of all repositories for a
6161
** user. This command performs certain operations on all repositories
@@ -316,10 +316,11 @@
316316
zCmd = "repack";
317317
}else if( fossil_strcmp(zCmd, "set")==0
318318
|| fossil_strcmp(zCmd, "setting")==0
319319
|| fossil_strcmp(zCmd, "settings")==0 ){
320320
zCmd = "settings -R";
321
+ collect_argument(&extra, "changed", 0);
321322
collect_argv(&extra, 3);
322323
}else if( fossil_strcmp(zCmd, "unset")==0 ){
323324
zCmd = "unset -R";
324325
collect_argv(&extra, 3);
325326
}else if( fossil_strcmp(zCmd, "fts-config")==0 ){
326327
--- src/allrepo.c
+++ src/allrepo.c
@@ -51,11 +51,11 @@
51 blob_appendf(pExtra, " %s", g.argv[i]);
52 }
53 }
54
55 /*
56 ** COMMAND: all abbreviated-subcommands
57 **
58 ** Usage: %fossil all SUBCOMMAND ...
59 **
60 ** The ~/.fossil file records the location of all repositories for a
61 ** user. This command performs certain operations on all repositories
@@ -316,10 +316,11 @@
316 zCmd = "repack";
317 }else if( fossil_strcmp(zCmd, "set")==0
318 || fossil_strcmp(zCmd, "setting")==0
319 || fossil_strcmp(zCmd, "settings")==0 ){
320 zCmd = "settings -R";
 
321 collect_argv(&extra, 3);
322 }else if( fossil_strcmp(zCmd, "unset")==0 ){
323 zCmd = "unset -R";
324 collect_argv(&extra, 3);
325 }else if( fossil_strcmp(zCmd, "fts-config")==0 ){
326
--- src/allrepo.c
+++ src/allrepo.c
@@ -51,11 +51,11 @@
51 blob_appendf(pExtra, " %s", g.argv[i]);
52 }
53 }
54
55 /*
56 ** COMMAND: all abbrv-subcom
57 **
58 ** Usage: %fossil all SUBCOMMAND ...
59 **
60 ** The ~/.fossil file records the location of all repositories for a
61 ** user. This command performs certain operations on all repositories
@@ -316,10 +316,11 @@
316 zCmd = "repack";
317 }else if( fossil_strcmp(zCmd, "set")==0
318 || fossil_strcmp(zCmd, "setting")==0
319 || fossil_strcmp(zCmd, "settings")==0 ){
320 zCmd = "settings -R";
321 collect_argument(&extra, "changed", 0);
322 collect_argv(&extra, 3);
323 }else if( fossil_strcmp(zCmd, "unset")==0 ){
324 zCmd = "unset -R";
325 collect_argv(&extra, 3);
326 }else if( fossil_strcmp(zCmd, "fts-config")==0 ){
327
+1 -1
--- src/cache.c
+++ src/cache.c
@@ -255,11 +255,11 @@
255255
void cache_initialize(void){
256256
sqlite3_close(cacheOpen(1));
257257
}
258258
259259
/*
260
-** COMMAND: cache* abbreviated-subcommands
260
+** COMMAND: cache* abbrv-subcom
261261
**
262262
** Usage: %fossil cache SUBCOMMAND
263263
**
264264
** Manage the cache used for potentially expensive web pages such as
265265
** /zip and /tarball. SUBCOMMAND can be:
266266
--- src/cache.c
+++ src/cache.c
@@ -255,11 +255,11 @@
255 void cache_initialize(void){
256 sqlite3_close(cacheOpen(1));
257 }
258
259 /*
260 ** COMMAND: cache* abbreviated-subcommands
261 **
262 ** Usage: %fossil cache SUBCOMMAND
263 **
264 ** Manage the cache used for potentially expensive web pages such as
265 ** /zip and /tarball. SUBCOMMAND can be:
266
--- src/cache.c
+++ src/cache.c
@@ -255,11 +255,11 @@
255 void cache_initialize(void){
256 sqlite3_close(cacheOpen(1));
257 }
258
259 /*
260 ** COMMAND: cache* abbrv-subcom
261 **
262 ** Usage: %fossil cache SUBCOMMAND
263 **
264 ** Manage the cache used for potentially expensive web pages such as
265 ** /zip and /tarball. SUBCOMMAND can be:
266
+25 -5
--- src/cgi.c
+++ src/cgi.c
@@ -688,21 +688,41 @@
688688
}
689689
690690
691691
/*
692692
** Return true if the current request is coming from the same origin.
693
+**
694
+** If the request comes from a different origin and bErrorLog is true, then
695
+** put a warning message on the error log as this was a possible hack
696
+** attempt.
693697
*/
694
-int cgi_same_origin(void){
698
+int cgi_same_origin(int bErrorLog){
695699
const char *zRef;
700
+ char *zToFree = 0;
696701
int nBase;
702
+ int rc;
697703
if( g.zBaseURL==0 ) return 0;
698704
zRef = P("HTTP_REFERER");
699705
if( zRef==0 ) return 0;
706
+ if( strchr(zRef,'%')!=0 ){
707
+ zToFree = strdup(zRef);
708
+ dehttpize(zToFree);
709
+ zRef = zToFree;
710
+ }
700711
nBase = (int)strlen(g.zBaseURL);
701
- if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
702
- if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
703
- return 1;
712
+ if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){
713
+ rc = 0;
714
+ }else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){
715
+ rc = 0;
716
+ }else{
717
+ rc = 1;
718
+ }
719
+ if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){
720
+ fossil_errorlog("warning: POST from different origin");
721
+ }
722
+ fossil_free(zToFree);
723
+ return rc;
704724
}
705725
706726
/*
707727
** Return true if the current CGI request is a POST request
708728
*/
@@ -733,11 +753,11 @@
733753
** 3: (2) plus there is a valid "csrf" token in the request
734754
*/
735755
int cgi_csrf_safe(int securityLevel){
736756
if( g.okCsrf<0 ) return 0;
737757
if( g.okCsrf==0 ){
738
- if( !cgi_same_origin() ){
758
+ if( !cgi_same_origin(1) ){
739759
g.okCsrf = -1;
740760
}else{
741761
g.okCsrf = 1;
742762
if( cgi_is_post_request() ){
743763
g.okCsrf = 2;
744764
--- src/cgi.c
+++ src/cgi.c
@@ -688,21 +688,41 @@
688 }
689
690
691 /*
692 ** Return true if the current request is coming from the same origin.
 
 
 
 
693 */
694 int cgi_same_origin(void){
695 const char *zRef;
 
696 int nBase;
 
697 if( g.zBaseURL==0 ) return 0;
698 zRef = P("HTTP_REFERER");
699 if( zRef==0 ) return 0;
 
 
 
 
 
700 nBase = (int)strlen(g.zBaseURL);
701 if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
702 if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
703 return 1;
 
 
 
 
 
 
 
 
 
704 }
705
706 /*
707 ** Return true if the current CGI request is a POST request
708 */
@@ -733,11 +753,11 @@
733 ** 3: (2) plus there is a valid "csrf" token in the request
734 */
735 int cgi_csrf_safe(int securityLevel){
736 if( g.okCsrf<0 ) return 0;
737 if( g.okCsrf==0 ){
738 if( !cgi_same_origin() ){
739 g.okCsrf = -1;
740 }else{
741 g.okCsrf = 1;
742 if( cgi_is_post_request() ){
743 g.okCsrf = 2;
744
--- src/cgi.c
+++ src/cgi.c
@@ -688,21 +688,41 @@
688 }
689
690
691 /*
692 ** Return true if the current request is coming from the same origin.
693 **
694 ** If the request comes from a different origin and bErrorLog is true, then
695 ** put a warning message on the error log as this was a possible hack
696 ** attempt.
697 */
698 int cgi_same_origin(int bErrorLog){
699 const char *zRef;
700 char *zToFree = 0;
701 int nBase;
702 int rc;
703 if( g.zBaseURL==0 ) return 0;
704 zRef = P("HTTP_REFERER");
705 if( zRef==0 ) return 0;
706 if( strchr(zRef,'%')!=0 ){
707 zToFree = strdup(zRef);
708 dehttpize(zToFree);
709 zRef = zToFree;
710 }
711 nBase = (int)strlen(g.zBaseURL);
712 if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){
713 rc = 0;
714 }else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){
715 rc = 0;
716 }else{
717 rc = 1;
718 }
719 if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){
720 fossil_errorlog("warning: POST from different origin");
721 }
722 fossil_free(zToFree);
723 return rc;
724 }
725
726 /*
727 ** Return true if the current CGI request is a POST request
728 */
@@ -733,11 +753,11 @@
753 ** 3: (2) plus there is a valid "csrf" token in the request
754 */
755 int cgi_csrf_safe(int securityLevel){
756 if( g.okCsrf<0 ) return 0;
757 if( g.okCsrf==0 ){
758 if( !cgi_same_origin(1) ){
759 g.okCsrf = -1;
760 }else{
761 g.okCsrf = 1;
762 if( cgi_is_post_request() ){
763 g.okCsrf = 2;
764
+252 -70
--- src/checkin.c
+++ src/checkin.c
@@ -2281,42 +2281,165 @@
22812281
static int tagCmp(const void *a, const void *b){
22822282
char **pA = (char**)a;
22832283
char **pB = (char**)b;
22842284
return fossil_strcmp(pA[0], pB[0]);
22852285
}
2286
+
2287
+/*
2288
+** SETTING: verify-comments width=8 default=on
2289
+**
2290
+** This setting determines how much sanity checking, if any, the
2291
+** "fossil commit" and "fossil amend" commands do against check-in
2292
+** comments. Recognized values:
2293
+**
2294
+** on (Default) Check for bad syntax in check-in comments
2295
+** and offer the user a chance to continue editing for
2296
+** interactive sessions, or simply abort the commit if
2297
+** commit was entered using -m or -M
2298
+**
2299
+** off Do not do syntax checking of any kind
2300
+**
2301
+** links Similar to "on", except only check for bad hyperlinks
2302
+**
2303
+** preview Do all the same checks as "on" but also preview the
2304
+** check-in comment to the user during interactive sessions
2305
+** and provide an opportunity to accept or re-edit
2306
+*/
2307
+
2308
+#if INTERFACE
2309
+#define COMCK_LINKS 0x01 /* Check for back hyperlinks */
2310
+#define COMCK_MARKUP 0x02 /* Check markup */
2311
+#define COMCK_PREVIEW 0x04 /* Always preview, even if no issues found */
2312
+#define COMCK_NOPREVIEW 0x08 /* Never preview, even for other errors */
2313
+#endif /* INTERFACE */
2314
+
2315
+/*
2316
+** Check for possible formatting errors in the comment string pComment.
2317
+**
2318
+** If concerns are found, write a description of the problem(s) to
2319
+** stdout and return non-zero. The return value is some combination
2320
+** of the COMCK_* flags, depending on what went wrong.
2321
+**
2322
+** If no issues are seen, do not output anything and return zero.
2323
+*/
2324
+int suspicious_comment(Blob *pComment, int mFlags){
2325
+ char *zStart = blob_str(pComment);
2326
+ char *z;
2327
+ char *zEnd, *zEnd2;
2328
+ char *zSep;
2329
+ char cSave1;
2330
+ int nIssue = 0;
2331
+ int rc = mFlags & COMCK_PREVIEW;
2332
+ Blob out;
2333
+ static const char zSpecial[] = "\\&<*_`[";
2334
+
2335
+ if( mFlags==0 ) return 0;
2336
+ z = zStart;
2337
+ blob_init(&out, 0, 0);
2338
+ if( mFlags & COMCK_LINKS ){
2339
+ while( (z = strchr(z,'['))!=0 ){
2340
+ zEnd = strchr(z,']');
2341
+ if( zEnd==0 ){
2342
+ blob_appendf(&out,"\n (%d) ", ++nIssue);
2343
+ blob_appendf(&out, "Unterminated hyperlink \"%.12s...\"", z);
2344
+ break;
2345
+ }
2346
+ if( zEnd[1]=='(' && (zEnd2 = strchr(zEnd,')'))!=0 ){
2347
+ blob_appendf(&out,"\n (%d) ", ++nIssue);
2348
+ blob_appendf(&out, "Markdown hyperlink syntax: %.*s",
2349
+ (int)(zEnd2+1-z), z);
2350
+ z = zEnd2;
2351
+ continue;
2352
+ }
2353
+ zSep = strchr(z+1,'|');
2354
+ if( zSep==0 || zSep>zEnd ) zSep = zEnd;
2355
+ while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--;
2356
+ cSave1 = zSep[0];
2357
+ zSep[0] = 0;
2358
+ if( !wiki_valid_link_target(z+1) ){
2359
+ blob_appendf(&out,"\n (%d) ", ++nIssue);
2360
+ blob_appendf(&out, "Broken hyperlink: [%s]", z+1);
2361
+ }
2362
+ zSep[0] = cSave1;
2363
+ z = zEnd;
2364
+ }
2365
+ }
2366
+
2367
+ if( nIssue>0
2368
+ || (mFlags & COMCK_PREVIEW)!=0
2369
+ || ((mFlags & COMCK_MARKUP)!=0 && strcspn(zStart,zSpecial)<strlen(zStart))
2370
+ ){
2371
+ char zGot[16];
2372
+ int nGot = 0;
2373
+ int i;
2374
+ if( (mFlags & COMCK_MARKUP)!=0 ){
2375
+ for(i=0; zSpecial[i]; i++){
2376
+ if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i];
2377
+ }
2378
+ }
2379
+ zGot[nGot] = 0;
2380
+ if( nGot>0 ) rc |= COMCK_MARKUP;
2381
+ if( nGot>0 && nIssue>0 ){
2382
+ blob_appendf(&out,"\n (%d) Comment uses special character%s \"%s\"",
2383
+ ++nIssue, (nGot>1 ? "s" : ""), zGot);
2384
+ nGot = 0;
2385
+ }
2386
+ if( nIssue ){
2387
+ rc |= COMCK_LINKS;
2388
+ fossil_print(
2389
+ "Possible comment formatting error%s:%b\n",
2390
+ nIssue>1 ? "s" : "", &out
2391
+ );
2392
+ }
2393
+ if( (mFlags & COMCK_NOPREVIEW)==0 ){
2394
+ Blob in, html, txt;
2395
+ blob_init(&in, blob_str(pComment), -1);
2396
+ blob_init(&html, 0, 0);
2397
+ blob_init(&txt, 0, 0);
2398
+ wiki_convert(&in, &html, WIKI_INLINE);
2399
+ html_to_plaintext(blob_str(&html), &txt);
2400
+ if( nGot>0 ){
2401
+ fossil_print(
2402
+ "The comment uses special character%s \"%s\". "
2403
+ "Does it render as you expect?\n\n ",
2404
+ (nGot>1 ? "s" : ""), zGot
2405
+ );
2406
+ }else{
2407
+ fossil_print("Preview of the check-in comment:\n\n ");
2408
+ }
2409
+ comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
2410
+ blob_reset(&in);
2411
+ blob_reset(&html);
2412
+ blob_reset(&txt);
2413
+ }
2414
+ }
2415
+ blob_reset(&out);
2416
+ return rc;
2417
+}
22862418
22872419
/*
22882420
** COMMAND: ci#
22892421
** COMMAND: commit
22902422
**
22912423
** Usage: %fossil commit ?OPTIONS? ?FILE...?
22922424
** or: %fossil ci ?OPTIONS? ?FILE...?
22932425
**
2294
-** Create a new version containing all of the changes in the current
2295
-** check-out. You will be prompted to enter a check-in comment unless
2296
-** the comment has been specified on the command-line using "-m" or a
2297
-** file containing the comment using -M. The editor defined in the
2298
-** "editor" fossil option (see %fossil help set) will be used, or from
2299
-** the "VISUAL" or "EDITOR" environment variables (in that order) if
2300
-** no editor is set.
2426
+** Create a new check-in containing all of the changes in the current
2427
+** check-out. All changes are committed unless some subset of files
2428
+** is specified on the command line, in which case only the named files
2429
+** become part of the new check-in.
23012430
**
2302
-** All files that have changed will be committed unless some subset of
2303
-** files is specified on the command line.
2431
+** You will be prompted to enter a check-in comment unless the comment
2432
+** has been specified on the command-line using "-m" or "-M". The
2433
+** text editor used is determined by the "editor" setting, or by the
2434
+** "VISUAL" or "EDITOR" environment variables. Commit message text is
2435
+** interpreted as fossil-wiki format. Potentially misformatted check-in
2436
+** comment text is detected and reported unless the --no-verify-comment
2437
+** option is used.
23042438
**
23052439
** The --branch option followed by a branch name causes the new
2306
-** check-in to be placed in a newly-created branch with the name
2307
-** passed to the --branch option.
2308
-**
2309
-** Use the --branchcolor option followed by a color name (ex:
2310
-** '#ffc0c0') to specify the background color of entries in the new
2311
-** branch when shown in the web timeline interface. The use of
2312
-** the --branchcolor option is not recommended. Instead, let Fossil
2313
-** choose the branch color automatically.
2314
-**
2315
-** The --bgcolor option works like --branchcolor but only sets the
2316
-** background color for a single check-in. Subsequent check-ins revert
2317
-** to the default color.
2440
+** check-in to be placed in a newly-created branch with name specified.
23182441
**
23192442
** A check-in is not permitted to fork unless the --allow-fork option
23202443
** appears. An empty check-in (i.e. with nothing changed) is not
23212444
** allowed unless the --allow-empty option appears. A check-in may not
23222445
** be older than its ancestor unless the --allow-older option appears.
@@ -2328,68 +2451,60 @@
23282451
** unless the interactive user chooses to proceed. If there is no
23292452
** interactive user or these warnings should be skipped for some other
23302453
** reason, the --no-warnings option may be used. A check-in is not
23312454
** allowed against a closed leaf.
23322455
**
2333
-** If a commit message is blank, you will be prompted:
2334
-** ("continue (y/N)?") to confirm you really want to commit with a
2335
-** blank commit message. The default value is "N", do not commit.
2336
-**
23372456
** The --private option creates a private check-in that is never synced.
23382457
** Children of private check-ins are automatically private.
23392458
**
23402459
** The --tag option applies the symbolic tag name to the check-in.
2341
-**
2342
-** The --hash option detects edited files by computing each file's
2343
-** artifact hash rather than just checking for changes to its size or mtime.
2460
+** The --tag option can be repeated to assign multiple tags to a check-in.
2461
+** For example: "... --tag release --tag version-1.2.3 ..."
23442462
**
23452463
** Options:
23462464
** --allow-conflict Allow unresolved merge conflicts
23472465
** --allow-empty Allow a commit with no changes
23482466
** --allow-fork Allow the commit to fork
23492467
** --allow-older Allow a commit older than its ancestor
23502468
** --baseline Use a baseline manifest in the commit process
2351
-** --bgcolor COLOR Apply COLOR to this one check-in only
23522469
** --branch NEW-BRANCH-NAME Check in to this new branch
2353
-** --branchcolor COLOR Apply given COLOR to the branch
23542470
** --close Close the branch being committed
2355
-** --date-override DATETIME DATE to use instead of 'now'
2471
+** --date-override DATETIME Make DATETIME the time of the check-in.
2472
+** Useful when importing historical check-ins
2473
+** from another version control system.
23562474
** --delta Use a delta manifest in the commit process
23572475
** --hash Verify file status using hashing rather
2358
-** than relying on file mtimes
2476
+** than relying on filesystem mtimes
23592477
** --if-changes Make this command a silent no-op if there
23602478
** are no changes
23612479
** --ignore-clock-skew If a clock skew is detected, ignore it and
23622480
** behave as if the user had entered 'yes' to
23632481
** the question of whether to proceed despite
23642482
** the skew.
23652483
** --ignore-oversize Do not warn the user about oversized files
23662484
** --integrate Close all merged-in branches
2367
-** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment
2368
-** -M|--message-file FILE Read the commit comment from given file
2369
-** --mimetype MIMETYPE Mimetype of check-in comment
2370
-** -n|--dry-run If given, display instead of run actions
2485
+** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment
2486
+** -M|--message-file FILE Read the check-in comment from FILE
2487
+** -n|--dry-run Do not actually create a new check-in. Just
2488
+** show what would have happened. For debugging.
23712489
** -v|--verbose Show a diff in the commit message prompt
23722490
** --no-prompt This option disables prompting the user for
23732491
** input and assumes an answer of 'No' for every
23742492
** question.
23752493
** --no-warnings Omit all warnings about file contents
23762494
** --no-verify Do not run before-commit hooks
2495
+** --no-verify-comment Do not validate the check-in comment
23772496
** --nosign Do not attempt to sign this commit with gpg
23782497
** --nosync Do not auto-sync prior to committing
23792498
** --override-lock Allow a check-in even though parent is locked
2380
-** --private Do not sync changes and their descendants
2499
+** --private Never sync the resulting check-in and make
2500
+** all descendants private too.
23812501
** --proxy PROXY Use PROXY as http proxy during sync operation
2382
-** --tag TAG-NAME Assign given tag TAG-NAME to the check-in
2502
+** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated.
23832503
** --trace Debug tracing
2384
-** --user-override USER USER to use instead of the current default
2385
-**
2386
-** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
2387
-** year-month-day form, it may be truncated, the "T" may be replaced by
2388
-** a space, and it may also name a timezone offset from UTC as "-HH:MM"
2389
-** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
2390
-** means UTC.
2504
+** --user-override USER Record USER as the login that created the
2505
+** new check-in, rather that the current user.
23912506
**
23922507
** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]]
23932508
*/
23942509
void commit_cmd(void){
23952510
int hasChanges; /* True if unsaved changes exist */
@@ -2415,10 +2530,11 @@
24152530
int allowConflict = 0; /* Allow unresolve merge conflicts */
24162531
int allowEmpty = 0; /* Allow a commit with no changes */
24172532
int onlyIfChanges = 0; /* No-op if there are no changes */
24182533
int allowFork = 0; /* Allow the commit to fork */
24192534
int allowOlder = 0; /* Allow a commit older than its ancestor */
2535
+ int noVerifyCom = 0; /* Allow suspicious check-in comments */
24202536
char *zManifestFile; /* Name of the manifest file */
24212537
int useCksum; /* True if checksums should be computed and verified */
24222538
int outputManifest; /* True to output "manifest" and "manifest.uuid" */
24232539
int dryRunFlag; /* True for a test run. Debugging only */
24242540
CheckinInfo sCiInfo; /* Information about this check-in */
@@ -2440,10 +2556,11 @@
24402556
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
24412557
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
24422558
int mxSize;
24432559
char *zCurBranch = 0; /* The current branch name of checkout */
24442560
char *zNewBranch = 0; /* The branch name after update */
2561
+ int ckComFlgs; /* Flags passed to suspicious_comment() */
24452562
24462563
memset(&sCiInfo, 0, sizeof(sCiInfo));
24472564
url_proxy_options();
24482565
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
24492566
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
@@ -2470,24 +2587,30 @@
24702587
}
24712588
zComment = find_option("comment","m",1);
24722589
forceFlag = find_option("force", "f", 0)!=0;
24732590
allowConflict = find_option("allow-conflict",0,0)!=0;
24742591
allowEmpty = find_option("allow-empty",0,0)!=0;
2592
+ noVerifyCom = find_option("no-verify-comment",0,0)!=0;
24752593
onlyIfChanges = find_option("if-changes",0,0)!=0;
24762594
allowFork = find_option("allow-fork",0,0)!=0;
24772595
if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
24782596
allowOlder = find_option("allow-older",0,0)!=0;
24792597
noPrompt = find_option("no-prompt", 0, 0)!=0;
24802598
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
24812599
noVerify = find_option("no-verify",0,0)!=0;
24822600
bTrace = find_option("trace",0,0)!=0;
24832601
sCiInfo.zBranch = find_option("branch","b",1);
2484
- sCiInfo.zColor = find_option("bgcolor",0,1);
2485
- sCiInfo.zBrClr = find_option("branchcolor",0,1);
2602
+
2603
+ /* NB: the --bgcolor and --branchcolor flags still work, but are
2604
+ ** now undocumented, to discourage their use. --mimetype has never
2605
+ ** been used for anything, so also leave it undocumented */
2606
+ sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/
2607
+ sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/
2608
+ sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/
2609
+
24862610
sCiInfo.closeFlag = find_option("close",0,0)!=0;
24872611
sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
2488
- sCiInfo.zMimetype = find_option("mimetype",0,1);
24892612
sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
24902613
while( (zTag = find_option("tag",0,1))!=0 ){
24912614
if( zTag[0]==0 ) continue;
24922615
sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
24932616
sizeof(char*)*(nTag+2));
@@ -2499,14 +2622,20 @@
24992622
sCiInfo.zUserOvrd = find_option("user-override",0,1);
25002623
noSign = db_get_boolean("omitsign", 0)|noSign;
25012624
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
25022625
useCksum = db_get_boolean("repo-cksum", 1);
25032626
bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
2504
- outputManifest = db_get_manifest_setting();
2627
+ outputManifest = db_get_manifest_setting(0);
25052628
mxSize = db_large_file_size();
25062629
if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
25072630
verify_all_options();
2631
+
2632
+ /* The --no-warnings flag and the --force flag each imply
2633
+ ** the --no-verify-comment flag */
2634
+ if( noWarningFlag || forceFlag ){
2635
+ noVerifyCom = 1;
2636
+ }
25082637
25092638
/* Get the ID of the parent manifest artifact */
25102639
vid = db_lget_int("checkout", 0);
25112640
if( vid==0 ){
25122641
useCksum = 1;
@@ -2607,33 +2736,35 @@
26072736
fossil_exit(1);
26082737
}
26092738
}
26102739
26112740
/* So that older versions of Fossil (that do not understand delta-
2612
- ** manifest) can continue to use this repository, do not create a new
2741
+ ** manifest) can continue to use this repository, and because
2742
+ ** delta manifests are usually a bad idea unless the repository
2743
+ ** has a really large number of files, do not create a new
26132744
** delta-manifest unless this repository already contains one or more
26142745
** delta-manifests, or unless the delta-manifest is explicitly requested
26152746
** by the --delta option.
26162747
**
2617
- ** The forbid-delta-manifests setting prevents new delta manifests.
2748
+ ** The forbid-delta-manifests setting prevents new delta manifests,
2749
+ ** even if the --delta option is used.
26182750
**
26192751
** If the remote repository sent an avoid-delta-manifests pragma on
2620
- ** the autosync above, then also try to avoid deltas, unless the
2752
+ ** the autosync above, then also forbid delta manifests, even if the
26212753
** --delta option is specified. The remote repo will send the
2622
- ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests"
2623
- ** setting enabled.
2754
+ ** avoid-delta-manifests pragma if its "forbid-delta-manifests"
2755
+ ** setting is enabled.
26242756
*/
2625
- if( !db_get_boolean("seen-delta-manifest",0)
2757
+ if( !(forceDelta || db_get_boolean("seen-delta-manifest",0))
26262758
|| db_get_boolean("forbid-delta-manifests",0)
26272759
|| g.bAvoidDeltaManifests
26282760
){
2629
- if( !forceDelta ) forceBaseline = 1;
2761
+ forceBaseline = 1;
26302762
}
2631
-
26322763
26332764
/* Require confirmation to continue with the check-in if there is
2634
- ** clock skew
2765
+ ** clock skew. This helps to prevent timewarps.
26352766
*/
26362767
if( g.clockSkewSeen ){
26372768
if( bIgnoreSkew!=0 ){
26382769
cReply = 'y';
26392770
fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
@@ -2788,35 +2919,86 @@
27882919
fossil_free(zNewBranch);
27892920
27902921
/* Always exit the loop on the second pass */
27912922
if( bRecheck ) break;
27922923
2924
+
2925
+ /* Figure out how much comment verification is requested */
2926
+ if( noVerifyCom ){
2927
+ ckComFlgs = 0;
2928
+ }else{
2929
+ const char *zVerComs = db_get("verify-comments","on");
2930
+ if( is_false(zVerComs) ){
2931
+ ckComFlgs = 0;
2932
+ }else if( strcmp(zVerComs,"preview")==0 ){
2933
+ ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
2934
+ }else if( strcmp(zVerComs,"links")==0 ){
2935
+ ckComFlgs = COMCK_LINKS;
2936
+ }else{
2937
+ ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
2938
+ }
2939
+ }
27932940
27942941
/* Get the check-in comment. This might involve prompting the
27952942
** user for the check-in comment, in which case we should resync
27962943
** to renew the check-in lock and repeat the checks for conflicts.
27972944
*/
27982945
if( zComment ){
27992946
blob_zero(&comment);
28002947
blob_append(&comment, zComment, -1);
2948
+ ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2949
+ ckComFlgs |= COMCK_NOPREVIEW;
2950
+ if( suspicious_comment(&comment, ckComFlgs) ){
2951
+ fossil_fatal("Commit aborted; "
2952
+ "use --no-verify-comment to override");
2953
+ }
28012954
}else if( zComFile ){
28022955
blob_zero(&comment);
28032956
blob_read_from_file(&comment, zComFile, ExtFILE);
28042957
blob_to_utf8_no_bom(&comment, 1);
2958
+ ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2959
+ ckComFlgs |= COMCK_NOPREVIEW;
2960
+ if( suspicious_comment(&comment, ckComFlgs) ){
2961
+ fossil_fatal("Commit aborted; "
2962
+ "use --no-verify-comment to override");
2963
+ }
28052964
}else if( !noPrompt ){
2806
- char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
2807
- prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
2808
- if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
2809
- prompt_user("unchanged check-in comment. continue (y/N)? ", &ans);
2810
- cReply = blob_str(&ans)[0];
2811
- blob_reset(&ans);
2812
- if( cReply!='y' && cReply!='Y' ){
2813
- fossil_fatal("Commit aborted.");
2814
- }
2815
- }
2816
- free(zInit);
2817
- db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
2965
+ while( 1/*exit-by-break*/ ){
2966
+ int rc;
2967
+ char *zInit;
2968
+ zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
2969
+ prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
2970
+ db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
2971
+ if( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
2972
+ if( rc==COMCK_PREVIEW ){
2973
+ prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
2974
+ }else{
2975
+ prompt_user("\nContinue (y/n/E=edit)? ", &ans);
2976
+ }
2977
+ cReply = blob_str(&ans)[0];
2978
+ cReply = fossil_tolower(cReply);
2979
+ blob_reset(&ans);
2980
+ if( cReply=='n' ){
2981
+ fossil_fatal("Commit aborted.");
2982
+ }
2983
+ if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
2984
+ fossil_free(zInit);
2985
+ continue;
2986
+ }
2987
+ }
2988
+ if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
2989
+ prompt_user("unchanged check-in comment. continue (y/N)? ", &ans);
2990
+ cReply = blob_str(&ans)[0];
2991
+ blob_reset(&ans);
2992
+ if( cReply!='y' && cReply!='Y' ){
2993
+ fossil_fatal("Commit aborted.");
2994
+ }
2995
+ }
2996
+ fossil_free(zInit);
2997
+ break;
2998
+ }
2999
+
28183000
db_end_transaction(0);
28193001
db_begin_transaction();
28203002
if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
28213003
/* Do another auto-pull, renewing the check-in lock. Then set
28223004
** bRecheck so that we loop back above to verify that the check-in
28233005
--- src/checkin.c
+++ src/checkin.c
@@ -2281,42 +2281,165 @@
2281 static int tagCmp(const void *a, const void *b){
2282 char **pA = (char**)a;
2283 char **pB = (char**)b;
2284 return fossil_strcmp(pA[0], pB[0]);
2285 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2286
2287 /*
2288 ** COMMAND: ci#
2289 ** COMMAND: commit
2290 **
2291 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
2292 ** or: %fossil ci ?OPTIONS? ?FILE...?
2293 **
2294 ** Create a new version containing all of the changes in the current
2295 ** check-out. You will be prompted to enter a check-in comment unless
2296 ** the comment has been specified on the command-line using "-m" or a
2297 ** file containing the comment using -M. The editor defined in the
2298 ** "editor" fossil option (see %fossil help set) will be used, or from
2299 ** the "VISUAL" or "EDITOR" environment variables (in that order) if
2300 ** no editor is set.
2301 **
2302 ** All files that have changed will be committed unless some subset of
2303 ** files is specified on the command line.
 
 
 
 
 
2304 **
2305 ** The --branch option followed by a branch name causes the new
2306 ** check-in to be placed in a newly-created branch with the name
2307 ** passed to the --branch option.
2308 **
2309 ** Use the --branchcolor option followed by a color name (ex:
2310 ** '#ffc0c0') to specify the background color of entries in the new
2311 ** branch when shown in the web timeline interface. The use of
2312 ** the --branchcolor option is not recommended. Instead, let Fossil
2313 ** choose the branch color automatically.
2314 **
2315 ** The --bgcolor option works like --branchcolor but only sets the
2316 ** background color for a single check-in. Subsequent check-ins revert
2317 ** to the default color.
2318 **
2319 ** A check-in is not permitted to fork unless the --allow-fork option
2320 ** appears. An empty check-in (i.e. with nothing changed) is not
2321 ** allowed unless the --allow-empty option appears. A check-in may not
2322 ** be older than its ancestor unless the --allow-older option appears.
@@ -2328,68 +2451,60 @@
2328 ** unless the interactive user chooses to proceed. If there is no
2329 ** interactive user or these warnings should be skipped for some other
2330 ** reason, the --no-warnings option may be used. A check-in is not
2331 ** allowed against a closed leaf.
2332 **
2333 ** If a commit message is blank, you will be prompted:
2334 ** ("continue (y/N)?") to confirm you really want to commit with a
2335 ** blank commit message. The default value is "N", do not commit.
2336 **
2337 ** The --private option creates a private check-in that is never synced.
2338 ** Children of private check-ins are automatically private.
2339 **
2340 ** The --tag option applies the symbolic tag name to the check-in.
2341 **
2342 ** The --hash option detects edited files by computing each file's
2343 ** artifact hash rather than just checking for changes to its size or mtime.
2344 **
2345 ** Options:
2346 ** --allow-conflict Allow unresolved merge conflicts
2347 ** --allow-empty Allow a commit with no changes
2348 ** --allow-fork Allow the commit to fork
2349 ** --allow-older Allow a commit older than its ancestor
2350 ** --baseline Use a baseline manifest in the commit process
2351 ** --bgcolor COLOR Apply COLOR to this one check-in only
2352 ** --branch NEW-BRANCH-NAME Check in to this new branch
2353 ** --branchcolor COLOR Apply given COLOR to the branch
2354 ** --close Close the branch being committed
2355 ** --date-override DATETIME DATE to use instead of 'now'
 
 
2356 ** --delta Use a delta manifest in the commit process
2357 ** --hash Verify file status using hashing rather
2358 ** than relying on file mtimes
2359 ** --if-changes Make this command a silent no-op if there
2360 ** are no changes
2361 ** --ignore-clock-skew If a clock skew is detected, ignore it and
2362 ** behave as if the user had entered 'yes' to
2363 ** the question of whether to proceed despite
2364 ** the skew.
2365 ** --ignore-oversize Do not warn the user about oversized files
2366 ** --integrate Close all merged-in branches
2367 ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment
2368 ** -M|--message-file FILE Read the commit comment from given file
2369 ** --mimetype MIMETYPE Mimetype of check-in comment
2370 ** -n|--dry-run If given, display instead of run actions
2371 ** -v|--verbose Show a diff in the commit message prompt
2372 ** --no-prompt This option disables prompting the user for
2373 ** input and assumes an answer of 'No' for every
2374 ** question.
2375 ** --no-warnings Omit all warnings about file contents
2376 ** --no-verify Do not run before-commit hooks
 
2377 ** --nosign Do not attempt to sign this commit with gpg
2378 ** --nosync Do not auto-sync prior to committing
2379 ** --override-lock Allow a check-in even though parent is locked
2380 ** --private Do not sync changes and their descendants
 
2381 ** --proxy PROXY Use PROXY as http proxy during sync operation
2382 ** --tag TAG-NAME Assign given tag TAG-NAME to the check-in
2383 ** --trace Debug tracing
2384 ** --user-override USER USER to use instead of the current default
2385 **
2386 ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
2387 ** year-month-day form, it may be truncated, the "T" may be replaced by
2388 ** a space, and it may also name a timezone offset from UTC as "-HH:MM"
2389 ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
2390 ** means UTC.
2391 **
2392 ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]]
2393 */
2394 void commit_cmd(void){
2395 int hasChanges; /* True if unsaved changes exist */
@@ -2415,10 +2530,11 @@
2415 int allowConflict = 0; /* Allow unresolve merge conflicts */
2416 int allowEmpty = 0; /* Allow a commit with no changes */
2417 int onlyIfChanges = 0; /* No-op if there are no changes */
2418 int allowFork = 0; /* Allow the commit to fork */
2419 int allowOlder = 0; /* Allow a commit older than its ancestor */
 
2420 char *zManifestFile; /* Name of the manifest file */
2421 int useCksum; /* True if checksums should be computed and verified */
2422 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
2423 int dryRunFlag; /* True for a test run. Debugging only */
2424 CheckinInfo sCiInfo; /* Information about this check-in */
@@ -2440,10 +2556,11 @@
2440 int bRecheck = 0; /* Repeat fork and closed-branch checks*/
2441 int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
2442 int mxSize;
2443 char *zCurBranch = 0; /* The current branch name of checkout */
2444 char *zNewBranch = 0; /* The branch name after update */
 
2445
2446 memset(&sCiInfo, 0, sizeof(sCiInfo));
2447 url_proxy_options();
2448 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2449 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
@@ -2470,24 +2587,30 @@
2470 }
2471 zComment = find_option("comment","m",1);
2472 forceFlag = find_option("force", "f", 0)!=0;
2473 allowConflict = find_option("allow-conflict",0,0)!=0;
2474 allowEmpty = find_option("allow-empty",0,0)!=0;
 
2475 onlyIfChanges = find_option("if-changes",0,0)!=0;
2476 allowFork = find_option("allow-fork",0,0)!=0;
2477 if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
2478 allowOlder = find_option("allow-older",0,0)!=0;
2479 noPrompt = find_option("no-prompt", 0, 0)!=0;
2480 noWarningFlag = find_option("no-warnings", 0, 0)!=0;
2481 noVerify = find_option("no-verify",0,0)!=0;
2482 bTrace = find_option("trace",0,0)!=0;
2483 sCiInfo.zBranch = find_option("branch","b",1);
2484 sCiInfo.zColor = find_option("bgcolor",0,1);
2485 sCiInfo.zBrClr = find_option("branchcolor",0,1);
 
 
 
 
 
 
2486 sCiInfo.closeFlag = find_option("close",0,0)!=0;
2487 sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
2488 sCiInfo.zMimetype = find_option("mimetype",0,1);
2489 sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
2490 while( (zTag = find_option("tag",0,1))!=0 ){
2491 if( zTag[0]==0 ) continue;
2492 sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
2493 sizeof(char*)*(nTag+2));
@@ -2499,14 +2622,20 @@
2499 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2500 noSign = db_get_boolean("omitsign", 0)|noSign;
2501 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
2502 useCksum = db_get_boolean("repo-cksum", 1);
2503 bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
2504 outputManifest = db_get_manifest_setting();
2505 mxSize = db_large_file_size();
2506 if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
2507 verify_all_options();
 
 
 
 
 
 
2508
2509 /* Get the ID of the parent manifest artifact */
2510 vid = db_lget_int("checkout", 0);
2511 if( vid==0 ){
2512 useCksum = 1;
@@ -2607,33 +2736,35 @@
2607 fossil_exit(1);
2608 }
2609 }
2610
2611 /* So that older versions of Fossil (that do not understand delta-
2612 ** manifest) can continue to use this repository, do not create a new
 
 
2613 ** delta-manifest unless this repository already contains one or more
2614 ** delta-manifests, or unless the delta-manifest is explicitly requested
2615 ** by the --delta option.
2616 **
2617 ** The forbid-delta-manifests setting prevents new delta manifests.
 
2618 **
2619 ** If the remote repository sent an avoid-delta-manifests pragma on
2620 ** the autosync above, then also try to avoid deltas, unless the
2621 ** --delta option is specified. The remote repo will send the
2622 ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests"
2623 ** setting enabled.
2624 */
2625 if( !db_get_boolean("seen-delta-manifest",0)
2626 || db_get_boolean("forbid-delta-manifests",0)
2627 || g.bAvoidDeltaManifests
2628 ){
2629 if( !forceDelta ) forceBaseline = 1;
2630 }
2631
2632
2633 /* Require confirmation to continue with the check-in if there is
2634 ** clock skew
2635 */
2636 if( g.clockSkewSeen ){
2637 if( bIgnoreSkew!=0 ){
2638 cReply = 'y';
2639 fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
@@ -2788,35 +2919,86 @@
2788 fossil_free(zNewBranch);
2789
2790 /* Always exit the loop on the second pass */
2791 if( bRecheck ) break;
2792
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2793
2794 /* Get the check-in comment. This might involve prompting the
2795 ** user for the check-in comment, in which case we should resync
2796 ** to renew the check-in lock and repeat the checks for conflicts.
2797 */
2798 if( zComment ){
2799 blob_zero(&comment);
2800 blob_append(&comment, zComment, -1);
 
 
 
 
 
 
2801 }else if( zComFile ){
2802 blob_zero(&comment);
2803 blob_read_from_file(&comment, zComFile, ExtFILE);
2804 blob_to_utf8_no_bom(&comment, 1);
 
 
 
 
 
 
2805 }else if( !noPrompt ){
2806 char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
2807 prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
2808 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
2809 prompt_user("unchanged check-in comment. continue (y/N)? ", &ans);
2810 cReply = blob_str(&ans)[0];
2811 blob_reset(&ans);
2812 if( cReply!='y' && cReply!='Y' ){
2813 fossil_fatal("Commit aborted.");
2814 }
2815 }
2816 free(zInit);
2817 db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2818 db_end_transaction(0);
2819 db_begin_transaction();
2820 if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
2821 /* Do another auto-pull, renewing the check-in lock. Then set
2822 ** bRecheck so that we loop back above to verify that the check-in
2823
--- src/checkin.c
+++ src/checkin.c
@@ -2281,42 +2281,165 @@
2281 static int tagCmp(const void *a, const void *b){
2282 char **pA = (char**)a;
2283 char **pB = (char**)b;
2284 return fossil_strcmp(pA[0], pB[0]);
2285 }
2286
2287 /*
2288 ** SETTING: verify-comments width=8 default=on
2289 **
2290 ** This setting determines how much sanity checking, if any, the
2291 ** "fossil commit" and "fossil amend" commands do against check-in
2292 ** comments. Recognized values:
2293 **
2294 ** on (Default) Check for bad syntax in check-in comments
2295 ** and offer the user a chance to continue editing for
2296 ** interactive sessions, or simply abort the commit if
2297 ** commit was entered using -m or -M
2298 **
2299 ** off Do not do syntax checking of any kind
2300 **
2301 ** links Similar to "on", except only check for bad hyperlinks
2302 **
2303 ** preview Do all the same checks as "on" but also preview the
2304 ** check-in comment to the user during interactive sessions
2305 ** and provide an opportunity to accept or re-edit
2306 */
2307
2308 #if INTERFACE
2309 #define COMCK_LINKS 0x01 /* Check for back hyperlinks */
2310 #define COMCK_MARKUP 0x02 /* Check markup */
2311 #define COMCK_PREVIEW 0x04 /* Always preview, even if no issues found */
2312 #define COMCK_NOPREVIEW 0x08 /* Never preview, even for other errors */
2313 #endif /* INTERFACE */
2314
2315 /*
2316 ** Check for possible formatting errors in the comment string pComment.
2317 **
2318 ** If concerns are found, write a description of the problem(s) to
2319 ** stdout and return non-zero. The return value is some combination
2320 ** of the COMCK_* flags, depending on what went wrong.
2321 **
2322 ** If no issues are seen, do not output anything and return zero.
2323 */
2324 int suspicious_comment(Blob *pComment, int mFlags){
2325 char *zStart = blob_str(pComment);
2326 char *z;
2327 char *zEnd, *zEnd2;
2328 char *zSep;
2329 char cSave1;
2330 int nIssue = 0;
2331 int rc = mFlags & COMCK_PREVIEW;
2332 Blob out;
2333 static const char zSpecial[] = "\\&<*_`[";
2334
2335 if( mFlags==0 ) return 0;
2336 z = zStart;
2337 blob_init(&out, 0, 0);
2338 if( mFlags & COMCK_LINKS ){
2339 while( (z = strchr(z,'['))!=0 ){
2340 zEnd = strchr(z,']');
2341 if( zEnd==0 ){
2342 blob_appendf(&out,"\n (%d) ", ++nIssue);
2343 blob_appendf(&out, "Unterminated hyperlink \"%.12s...\"", z);
2344 break;
2345 }
2346 if( zEnd[1]=='(' && (zEnd2 = strchr(zEnd,')'))!=0 ){
2347 blob_appendf(&out,"\n (%d) ", ++nIssue);
2348 blob_appendf(&out, "Markdown hyperlink syntax: %.*s",
2349 (int)(zEnd2+1-z), z);
2350 z = zEnd2;
2351 continue;
2352 }
2353 zSep = strchr(z+1,'|');
2354 if( zSep==0 || zSep>zEnd ) zSep = zEnd;
2355 while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--;
2356 cSave1 = zSep[0];
2357 zSep[0] = 0;
2358 if( !wiki_valid_link_target(z+1) ){
2359 blob_appendf(&out,"\n (%d) ", ++nIssue);
2360 blob_appendf(&out, "Broken hyperlink: [%s]", z+1);
2361 }
2362 zSep[0] = cSave1;
2363 z = zEnd;
2364 }
2365 }
2366
2367 if( nIssue>0
2368 || (mFlags & COMCK_PREVIEW)!=0
2369 || ((mFlags & COMCK_MARKUP)!=0 && strcspn(zStart,zSpecial)<strlen(zStart))
2370 ){
2371 char zGot[16];
2372 int nGot = 0;
2373 int i;
2374 if( (mFlags & COMCK_MARKUP)!=0 ){
2375 for(i=0; zSpecial[i]; i++){
2376 if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i];
2377 }
2378 }
2379 zGot[nGot] = 0;
2380 if( nGot>0 ) rc |= COMCK_MARKUP;
2381 if( nGot>0 && nIssue>0 ){
2382 blob_appendf(&out,"\n (%d) Comment uses special character%s \"%s\"",
2383 ++nIssue, (nGot>1 ? "s" : ""), zGot);
2384 nGot = 0;
2385 }
2386 if( nIssue ){
2387 rc |= COMCK_LINKS;
2388 fossil_print(
2389 "Possible comment formatting error%s:%b\n",
2390 nIssue>1 ? "s" : "", &out
2391 );
2392 }
2393 if( (mFlags & COMCK_NOPREVIEW)==0 ){
2394 Blob in, html, txt;
2395 blob_init(&in, blob_str(pComment), -1);
2396 blob_init(&html, 0, 0);
2397 blob_init(&txt, 0, 0);
2398 wiki_convert(&in, &html, WIKI_INLINE);
2399 html_to_plaintext(blob_str(&html), &txt);
2400 if( nGot>0 ){
2401 fossil_print(
2402 "The comment uses special character%s \"%s\". "
2403 "Does it render as you expect?\n\n ",
2404 (nGot>1 ? "s" : ""), zGot
2405 );
2406 }else{
2407 fossil_print("Preview of the check-in comment:\n\n ");
2408 }
2409 comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
2410 blob_reset(&in);
2411 blob_reset(&html);
2412 blob_reset(&txt);
2413 }
2414 }
2415 blob_reset(&out);
2416 return rc;
2417 }
2418
2419 /*
2420 ** COMMAND: ci#
2421 ** COMMAND: commit
2422 **
2423 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
2424 ** or: %fossil ci ?OPTIONS? ?FILE...?
2425 **
2426 ** Create a new check-in containing all of the changes in the current
2427 ** check-out. All changes are committed unless some subset of files
2428 ** is specified on the command line, in which case only the named files
2429 ** become part of the new check-in.
 
 
 
2430 **
2431 ** You will be prompted to enter a check-in comment unless the comment
2432 ** has been specified on the command-line using "-m" or "-M". The
2433 ** text editor used is determined by the "editor" setting, or by the
2434 ** "VISUAL" or "EDITOR" environment variables. Commit message text is
2435 ** interpreted as fossil-wiki format. Potentially misformatted check-in
2436 ** comment text is detected and reported unless the --no-verify-comment
2437 ** option is used.
2438 **
2439 ** The --branch option followed by a branch name causes the new
2440 ** check-in to be placed in a newly-created branch with name specified.
 
 
 
 
 
 
 
 
 
 
 
2441 **
2442 ** A check-in is not permitted to fork unless the --allow-fork option
2443 ** appears. An empty check-in (i.e. with nothing changed) is not
2444 ** allowed unless the --allow-empty option appears. A check-in may not
2445 ** be older than its ancestor unless the --allow-older option appears.
@@ -2328,68 +2451,60 @@
2451 ** unless the interactive user chooses to proceed. If there is no
2452 ** interactive user or these warnings should be skipped for some other
2453 ** reason, the --no-warnings option may be used. A check-in is not
2454 ** allowed against a closed leaf.
2455 **
 
 
 
 
2456 ** The --private option creates a private check-in that is never synced.
2457 ** Children of private check-ins are automatically private.
2458 **
2459 ** The --tag option applies the symbolic tag name to the check-in.
2460 ** The --tag option can be repeated to assign multiple tags to a check-in.
2461 ** For example: "... --tag release --tag version-1.2.3 ..."
 
2462 **
2463 ** Options:
2464 ** --allow-conflict Allow unresolved merge conflicts
2465 ** --allow-empty Allow a commit with no changes
2466 ** --allow-fork Allow the commit to fork
2467 ** --allow-older Allow a commit older than its ancestor
2468 ** --baseline Use a baseline manifest in the commit process
 
2469 ** --branch NEW-BRANCH-NAME Check in to this new branch
 
2470 ** --close Close the branch being committed
2471 ** --date-override DATETIME Make DATETIME the time of the check-in.
2472 ** Useful when importing historical check-ins
2473 ** from another version control system.
2474 ** --delta Use a delta manifest in the commit process
2475 ** --hash Verify file status using hashing rather
2476 ** than relying on filesystem mtimes
2477 ** --if-changes Make this command a silent no-op if there
2478 ** are no changes
2479 ** --ignore-clock-skew If a clock skew is detected, ignore it and
2480 ** behave as if the user had entered 'yes' to
2481 ** the question of whether to proceed despite
2482 ** the skew.
2483 ** --ignore-oversize Do not warn the user about oversized files
2484 ** --integrate Close all merged-in branches
2485 ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment
2486 ** -M|--message-file FILE Read the check-in comment from FILE
2487 ** -n|--dry-run Do not actually create a new check-in. Just
2488 ** show what would have happened. For debugging.
2489 ** -v|--verbose Show a diff in the commit message prompt
2490 ** --no-prompt This option disables prompting the user for
2491 ** input and assumes an answer of 'No' for every
2492 ** question.
2493 ** --no-warnings Omit all warnings about file contents
2494 ** --no-verify Do not run before-commit hooks
2495 ** --no-verify-comment Do not validate the check-in comment
2496 ** --nosign Do not attempt to sign this commit with gpg
2497 ** --nosync Do not auto-sync prior to committing
2498 ** --override-lock Allow a check-in even though parent is locked
2499 ** --private Never sync the resulting check-in and make
2500 ** all descendants private too.
2501 ** --proxy PROXY Use PROXY as http proxy during sync operation
2502 ** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated.
2503 ** --trace Debug tracing
2504 ** --user-override USER Record USER as the login that created the
2505 ** new check-in, rather that the current user.
 
 
 
 
 
2506 **
2507 ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]]
2508 */
2509 void commit_cmd(void){
2510 int hasChanges; /* True if unsaved changes exist */
@@ -2415,10 +2530,11 @@
2530 int allowConflict = 0; /* Allow unresolve merge conflicts */
2531 int allowEmpty = 0; /* Allow a commit with no changes */
2532 int onlyIfChanges = 0; /* No-op if there are no changes */
2533 int allowFork = 0; /* Allow the commit to fork */
2534 int allowOlder = 0; /* Allow a commit older than its ancestor */
2535 int noVerifyCom = 0; /* Allow suspicious check-in comments */
2536 char *zManifestFile; /* Name of the manifest file */
2537 int useCksum; /* True if checksums should be computed and verified */
2538 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
2539 int dryRunFlag; /* True for a test run. Debugging only */
2540 CheckinInfo sCiInfo; /* Information about this check-in */
@@ -2440,10 +2556,11 @@
2556 int bRecheck = 0; /* Repeat fork and closed-branch checks*/
2557 int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
2558 int mxSize;
2559 char *zCurBranch = 0; /* The current branch name of checkout */
2560 char *zNewBranch = 0; /* The branch name after update */
2561 int ckComFlgs; /* Flags passed to suspicious_comment() */
2562
2563 memset(&sCiInfo, 0, sizeof(sCiInfo));
2564 url_proxy_options();
2565 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2566 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
@@ -2470,24 +2587,30 @@
2587 }
2588 zComment = find_option("comment","m",1);
2589 forceFlag = find_option("force", "f", 0)!=0;
2590 allowConflict = find_option("allow-conflict",0,0)!=0;
2591 allowEmpty = find_option("allow-empty",0,0)!=0;
2592 noVerifyCom = find_option("no-verify-comment",0,0)!=0;
2593 onlyIfChanges = find_option("if-changes",0,0)!=0;
2594 allowFork = find_option("allow-fork",0,0)!=0;
2595 if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
2596 allowOlder = find_option("allow-older",0,0)!=0;
2597 noPrompt = find_option("no-prompt", 0, 0)!=0;
2598 noWarningFlag = find_option("no-warnings", 0, 0)!=0;
2599 noVerify = find_option("no-verify",0,0)!=0;
2600 bTrace = find_option("trace",0,0)!=0;
2601 sCiInfo.zBranch = find_option("branch","b",1);
2602
2603 /* NB: the --bgcolor and --branchcolor flags still work, but are
2604 ** now undocumented, to discourage their use. --mimetype has never
2605 ** been used for anything, so also leave it undocumented */
2606 sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/
2607 sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/
2608 sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/
2609
2610 sCiInfo.closeFlag = find_option("close",0,0)!=0;
2611 sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
 
2612 sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
2613 while( (zTag = find_option("tag",0,1))!=0 ){
2614 if( zTag[0]==0 ) continue;
2615 sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
2616 sizeof(char*)*(nTag+2));
@@ -2499,14 +2622,20 @@
2622 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2623 noSign = db_get_boolean("omitsign", 0)|noSign;
2624 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
2625 useCksum = db_get_boolean("repo-cksum", 1);
2626 bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
2627 outputManifest = db_get_manifest_setting(0);
2628 mxSize = db_large_file_size();
2629 if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
2630 verify_all_options();
2631
2632 /* The --no-warnings flag and the --force flag each imply
2633 ** the --no-verify-comment flag */
2634 if( noWarningFlag || forceFlag ){
2635 noVerifyCom = 1;
2636 }
2637
2638 /* Get the ID of the parent manifest artifact */
2639 vid = db_lget_int("checkout", 0);
2640 if( vid==0 ){
2641 useCksum = 1;
@@ -2607,33 +2736,35 @@
2736 fossil_exit(1);
2737 }
2738 }
2739
2740 /* So that older versions of Fossil (that do not understand delta-
2741 ** manifest) can continue to use this repository, and because
2742 ** delta manifests are usually a bad idea unless the repository
2743 ** has a really large number of files, do not create a new
2744 ** delta-manifest unless this repository already contains one or more
2745 ** delta-manifests, or unless the delta-manifest is explicitly requested
2746 ** by the --delta option.
2747 **
2748 ** The forbid-delta-manifests setting prevents new delta manifests,
2749 ** even if the --delta option is used.
2750 **
2751 ** If the remote repository sent an avoid-delta-manifests pragma on
2752 ** the autosync above, then also forbid delta manifests, even if the
2753 ** --delta option is specified. The remote repo will send the
2754 ** avoid-delta-manifests pragma if its "forbid-delta-manifests"
2755 ** setting is enabled.
2756 */
2757 if( !(forceDelta || db_get_boolean("seen-delta-manifest",0))
2758 || db_get_boolean("forbid-delta-manifests",0)
2759 || g.bAvoidDeltaManifests
2760 ){
2761 forceBaseline = 1;
2762 }
 
2763
2764 /* Require confirmation to continue with the check-in if there is
2765 ** clock skew. This helps to prevent timewarps.
2766 */
2767 if( g.clockSkewSeen ){
2768 if( bIgnoreSkew!=0 ){
2769 cReply = 'y';
2770 fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
@@ -2788,35 +2919,86 @@
2919 fossil_free(zNewBranch);
2920
2921 /* Always exit the loop on the second pass */
2922 if( bRecheck ) break;
2923
2924
2925 /* Figure out how much comment verification is requested */
2926 if( noVerifyCom ){
2927 ckComFlgs = 0;
2928 }else{
2929 const char *zVerComs = db_get("verify-comments","on");
2930 if( is_false(zVerComs) ){
2931 ckComFlgs = 0;
2932 }else if( strcmp(zVerComs,"preview")==0 ){
2933 ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
2934 }else if( strcmp(zVerComs,"links")==0 ){
2935 ckComFlgs = COMCK_LINKS;
2936 }else{
2937 ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
2938 }
2939 }
2940
2941 /* Get the check-in comment. This might involve prompting the
2942 ** user for the check-in comment, in which case we should resync
2943 ** to renew the check-in lock and repeat the checks for conflicts.
2944 */
2945 if( zComment ){
2946 blob_zero(&comment);
2947 blob_append(&comment, zComment, -1);
2948 ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2949 ckComFlgs |= COMCK_NOPREVIEW;
2950 if( suspicious_comment(&comment, ckComFlgs) ){
2951 fossil_fatal("Commit aborted; "
2952 "use --no-verify-comment to override");
2953 }
2954 }else if( zComFile ){
2955 blob_zero(&comment);
2956 blob_read_from_file(&comment, zComFile, ExtFILE);
2957 blob_to_utf8_no_bom(&comment, 1);
2958 ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2959 ckComFlgs |= COMCK_NOPREVIEW;
2960 if( suspicious_comment(&comment, ckComFlgs) ){
2961 fossil_fatal("Commit aborted; "
2962 "use --no-verify-comment to override");
2963 }
2964 }else if( !noPrompt ){
2965 while( 1/*exit-by-break*/ ){
2966 int rc;
2967 char *zInit;
2968 zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
2969 prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
2970 db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
2971 if( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
2972 if( rc==COMCK_PREVIEW ){
2973 prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
2974 }else{
2975 prompt_user("\nContinue (y/n/E=edit)? ", &ans);
2976 }
2977 cReply = blob_str(&ans)[0];
2978 cReply = fossil_tolower(cReply);
2979 blob_reset(&ans);
2980 if( cReply=='n' ){
2981 fossil_fatal("Commit aborted.");
2982 }
2983 if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
2984 fossil_free(zInit);
2985 continue;
2986 }
2987 }
2988 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
2989 prompt_user("unchanged check-in comment. continue (y/N)? ", &ans);
2990 cReply = blob_str(&ans)[0];
2991 blob_reset(&ans);
2992 if( cReply!='y' && cReply!='Y' ){
2993 fossil_fatal("Commit aborted.");
2994 }
2995 }
2996 fossil_free(zInit);
2997 break;
2998 }
2999
3000 db_end_transaction(0);
3001 db_begin_transaction();
3002 if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
3003 /* Do another auto-pull, renewing the check-in lock. Then set
3004 ** bRecheck so that we loop back above to verify that the check-in
3005
+1 -1
--- src/checkout.c
+++ src/checkout.c
@@ -173,11 +173,11 @@
173173
*/
174174
void manifest_to_disk(int vid){
175175
char *zManFile;
176176
int flg;
177177
178
- flg = db_get_manifest_setting();
178
+ flg = db_get_manifest_setting(0);
179179
180180
if( flg & MFESTFLG_RAW ){
181181
Blob manifest = BLOB_INITIALIZER;
182182
content_get(vid, &manifest);
183183
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
184184
--- src/checkout.c
+++ src/checkout.c
@@ -173,11 +173,11 @@
173 */
174 void manifest_to_disk(int vid){
175 char *zManFile;
176 int flg;
177
178 flg = db_get_manifest_setting();
179
180 if( flg & MFESTFLG_RAW ){
181 Blob manifest = BLOB_INITIALIZER;
182 content_get(vid, &manifest);
183 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
184
--- src/checkout.c
+++ src/checkout.c
@@ -173,11 +173,11 @@
173 */
174 void manifest_to_disk(int vid){
175 char *zManFile;
176 int flg;
177
178 flg = db_get_manifest_setting(0);
179
180 if( flg & MFESTFLG_RAW ){
181 Blob manifest = BLOB_INITIALIZER;
182 content_get(vid, &manifest);
183 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
184
--- src/configure.c
+++ src/configure.c
@@ -891,10 +891,11 @@
891891
}
892892
url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
893893
if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
894894
user_select();
895895
url_enable_proxy("via proxy: ");
896
+ g.zHttpAuth = get_httpauth();
896897
if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
897898
if( strncmp(zMethod, "push", n)==0 ){
898899
client_sync(0,0,(unsigned)mask,0,0);
899900
}else if( strncmp(zMethod, "pull", n)==0 ){
900901
if( overwriteFlag ) db_unprotect(PROTECT_USER);
901902
--- src/configure.c
+++ src/configure.c
@@ -891,10 +891,11 @@
891 }
892 url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
893 if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
894 user_select();
895 url_enable_proxy("via proxy: ");
 
896 if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
897 if( strncmp(zMethod, "push", n)==0 ){
898 client_sync(0,0,(unsigned)mask,0,0);
899 }else if( strncmp(zMethod, "pull", n)==0 ){
900 if( overwriteFlag ) db_unprotect(PROTECT_USER);
901
--- src/configure.c
+++ src/configure.c
@@ -891,10 +891,11 @@
891 }
892 url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
893 if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
894 user_select();
895 url_enable_proxy("via proxy: ");
896 g.zHttpAuth = get_httpauth();
897 if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
898 if( strncmp(zMethod, "push", n)==0 ){
899 client_sync(0,0,(unsigned)mask,0,0);
900 }else if( strncmp(zMethod, "pull", n)==0 ){
901 if( overwriteFlag ) db_unprotect(PROTECT_USER);
902
+194 -73
--- src/db.c
+++ src/db.c
@@ -3204,23 +3204,16 @@
32043204
32053205
db_unprotect(PROTECT_ALL);
32063206
db_set("content-schema", CONTENT_SCHEMA, 0);
32073207
db_set("aux-schema", AUX_SCHEMA_MAX, 0);
32083208
db_set("rebuilt", get_version(), 0);
3209
- db_set("admin-log", "1", 0);
3210
- db_set("access-log", "1", 0);
32113209
db_multi_exec(
32123210
"INSERT INTO config(name,value,mtime)"
32133211
" VALUES('server-code', lower(hex(randomblob(20))),now());"
32143212
"INSERT INTO config(name,value,mtime)"
32153213
" VALUES('project-code', lower(hex(randomblob(20))),now());"
32163214
);
3217
- if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
3218
- if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
3219
- if( !db_is_global("timeline-plaintext") ){
3220
- db_set_int("timeline-plaintext", 1, 0);
3221
- }
32223215
db_create_default_users(0, zDefaultUser);
32233216
if( zDefaultUser ) g.zLogin = zDefaultUser;
32243217
user_select();
32253218
32263219
if( zTemplate ){
@@ -3647,66 +3640,79 @@
36473640
**
36483641
** If the zNonVersionedSetting parameter is not NULL then it holds the
36493642
** non-versioned value for this setting. If both a versioned and a
36503643
** non-versioned value exist and are not equal, then a warning message
36513644
** might be generated.
3645
+**
3646
+** zCkin is normally NULL. In that case, the versioned setting is
3647
+** take from the local check-out, if a local checkout exists, or from
3648
+** checkin named by the g.zOpenRevision global variable. If zCkin is
3649
+** not NULL, then zCkin is the name of the specific checkin from which
3650
+** versioned setting value is taken. When zCkin is not NULL, the cache
3651
+** is bypassed.
36523652
*/
3653
-char *db_get_versioned(const char *zName, char *zNonVersionedSetting){
3653
+char *db_get_versioned(
3654
+ const char *zName,
3655
+ char *zNonVersionedSetting,
3656
+ const char *zCkin
3657
+){
36543658
char *zVersionedSetting = 0;
36553659
int noWarn = 0;
36563660
int found = 0;
36573661
struct _cacheEntry {
36583662
struct _cacheEntry *next;
36593663
const char *zName, *zValue;
36603664
} *cacheEntry = 0;
36613665
static struct _cacheEntry *cache = 0;
36623666
3663
- if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting;
3667
+ if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){
3668
+ return zNonVersionedSetting;
3669
+ }
3670
+
36643671
/* Look up name in cache */
3665
- cacheEntry = cache;
3666
- while( cacheEntry!=0 ){
3667
- if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
3668
- zVersionedSetting = fossil_strdup(cacheEntry->zValue);
3669
- break;
3670
- }
3671
- cacheEntry = cacheEntry->next;
3672
- }
3672
+ if( zCkin==0 ){
3673
+ cacheEntry = cache;
3674
+ while( cacheEntry!=0 ){
3675
+ if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
3676
+ zVersionedSetting = fossil_strdup(cacheEntry->zValue);
3677
+ break;
3678
+ }
3679
+ cacheEntry = cacheEntry->next;
3680
+ }
3681
+ }
3682
+
36733683
/* Attempt to read value from file in check-out if there wasn't a cache hit.*/
36743684
if( cacheEntry==0 ){
36753685
Blob versionedPathname;
36763686
Blob setting;
3677
- blob_zero(&versionedPathname);
3678
- blob_zero(&setting);
3679
- blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
3680
- g.zLocalRoot, zName);
3681
- if( !g.localOpen ){
3682
- /* Repository is in the process of being opened, but files have not been
3683
- * written to disk. Load from the database. */
3684
- Blob noWarnFile;
3685
- if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
3686
- &setting, 0) ){
3687
- found = 1;
3688
- }
3689
- /* See if there's a no-warn flag */
3690
- blob_append(&versionedPathname, ".no-warn", -1);
3691
- blob_zero(&noWarnFile);
3692
- if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
3693
- &noWarnFile, 0) ){
3694
- noWarn = 1;
3695
- }
3696
- blob_reset(&noWarnFile);
3697
- }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3698
- /* File exists, and contains the value for this setting. Load from
3699
- ** the file. */
3700
- const char *zFile = blob_str(&versionedPathname);
3701
- if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
3702
- found = 1;
3703
- }
3704
- /* See if there's a no-warn flag */
3705
- blob_append(&versionedPathname, ".no-warn", -1);
3706
- if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3707
- noWarn = 1;
3687
+ blob_init(&versionedPathname, 0, 0);
3688
+ blob_init(&setting, 0, 0);
3689
+ if( !g.localOpen || zCkin!=0 ){
3690
+ /* Repository is in the process of being opened, but files have not been
3691
+ * written to disk. Load from the database. */
3692
+ blob_appendf(&versionedPathname, ".fossil-settings/%s", zName);
3693
+ if( historical_blob(zCkin ? zCkin : g.zOpenRevision,
3694
+ blob_str(&versionedPathname),
3695
+ &setting, 0)
3696
+ ){
3697
+ found = 1;
3698
+ }
3699
+ }else{
3700
+ blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
3701
+ g.zLocalRoot, zName);
3702
+ if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3703
+ /* File exists, and contains the value for this setting. Load from
3704
+ ** the file. */
3705
+ const char *zFile = blob_str(&versionedPathname);
3706
+ if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
3707
+ found = 1;
3708
+ }
3709
+ /* See if there's a no-warn flag */
3710
+ blob_append(&versionedPathname, ".no-warn", -1);
3711
+ if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3712
+ noWarn = 1;
3713
+ }
37083714
}
37093715
}
37103716
blob_reset(&versionedPathname);
37113717
if( found ){
37123718
blob_strip_comment_lines(&setting, &setting);
@@ -3713,20 +3719,27 @@
37133719
blob_trim(&setting); /* Avoid non-obvious problems with line endings
37143720
** on boolean properties */
37153721
zVersionedSetting = fossil_strdup(blob_str(&setting));
37163722
}
37173723
blob_reset(&setting);
3724
+
37183725
/* Store result in cache, which can be the value or 0 if not found */
3719
- cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry));
3720
- cacheEntry->next = cache;
3721
- cacheEntry->zName = zName;
3722
- cacheEntry->zValue = fossil_strdup(zVersionedSetting);
3723
- cache = cacheEntry;
3726
+ if( zCkin==0 ){
3727
+ cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry));
3728
+ cacheEntry->next = cache;
3729
+ cacheEntry->zName = zName;
3730
+ cacheEntry->zValue = fossil_strdup(zVersionedSetting);
3731
+ cache = cacheEntry;
3732
+ }
37243733
}
3734
+
37253735
/* Display a warning? */
3726
- if( zVersionedSetting!=0 && zNonVersionedSetting!=0
3727
- && zNonVersionedSetting[0]!='\0' && !noWarn
3736
+ if( zVersionedSetting!=0
3737
+ && zNonVersionedSetting!=0
3738
+ && zNonVersionedSetting[0]!='\0'
3739
+ && zCkin==0
3740
+ && !noWarn
37283741
){
37293742
/* There's a versioned setting, and a non-versioned setting. Tell
37303743
** the user about the conflict */
37313744
fossil_warning(
37323745
"setting %s has both versioned and non-versioned values: using "
@@ -3735,10 +3748,11 @@
37353748
"\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
37363749
"the non-versioned setting with \"fossil unset %s\")", zName,
37373750
g.zLocalRoot, zName, g.zLocalRoot, zName, zName
37383751
);
37393752
}
3753
+
37403754
/* Prefer the versioned setting */
37413755
return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
37423756
}
37433757
37443758
@@ -3778,11 +3792,11 @@
37783792
}
37793793
if( pSetting!=0 && pSetting->versionable ){
37803794
/* This is a versionable setting, try and get the info from a
37813795
** checked-out file */
37823796
char * zZ = z;
3783
- z = db_get_versioned(zName, z);
3797
+ z = db_get_versioned(zName, z, 0);
37843798
if(zZ != z){
37853799
fossil_free(zZ);
37863800
}
37873801
}
37883802
if( z==0 ){
@@ -3919,11 +3933,11 @@
39193933
}
39203934
fossil_free(zVal);
39213935
return dflt;
39223936
}
39233937
int db_get_versioned_boolean(const char *zName, int dflt){
3924
- char *zVal = db_get_versioned(zName, 0);
3938
+ char *zVal = db_get_versioned(zName, 0, 0);
39253939
if( zVal==0 ) return dflt;
39263940
if( is_truth(zVal) ) return 1;
39273941
if( is_false(zVal) ) return 0;
39283942
return dflt;
39293943
}
@@ -4048,14 +4062,30 @@
40484062
** Get the manifest setting. For backwards compatibility first check if the
40494063
** value is a boolean. If it's not a boolean, treat each character as a flag
40504064
** to enable a manifest type. This system puts certain boundary conditions on
40514065
** which letters can be used to represent flags (any permutation of flags must
40524066
** not be able to fully form one of the boolean values).
4067
+**
4068
+** "manifest" is a versionable setting. But we do not issue a warning
4069
+** if there is a conflict. Instead, the value returned is the value for
4070
+** the versioned setting if the versioned setting exists, or the ordinary
4071
+** setting otherwise.
4072
+**
4073
+** The argument zCkin is the specific check-in for which we want the
4074
+** manifest setting.
40534075
*/
4054
-int db_get_manifest_setting(void){
4076
+int db_get_manifest_setting(const char *zCkin){
40554077
int flg;
4056
- char *zVal = db_get("manifest", 0);
4078
+ char *zVal;
4079
+
4080
+ /* Look for the versioned setting first */
4081
+ zVal = db_get_versioned("manifest", 0, zCkin);
4082
+
4083
+ if( zVal==0 && g.repositoryOpen ){
4084
+ /* No versioned setting, look for the repository setting second */
4085
+ zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'");
4086
+ }
40574087
if( zVal==0 || is_false(zVal) ){
40584088
return 0;
40594089
}else if( is_truth(zVal) ){
40604090
return MFESTFLG_RAW|MFESTFLG_UUID;
40614091
}
@@ -4068,10 +4098,37 @@
40684098
}
40694099
zVal++;
40704100
}
40714101
return flg;
40724102
}
4103
+
4104
+/*
4105
+** COMMAND: test-manifest-setting
4106
+**
4107
+** Usage: %fossil test-manifest-setting VERSION VERSION ...
4108
+**
4109
+** Display the value for the "manifest" setting for various versions
4110
+** of the repository.
4111
+*/
4112
+void test_manfest_setting_cmd(void){
4113
+ int i;
4114
+ db_find_and_open_repository(0, 0);
4115
+ for(i=2; i<g.argc; i++){
4116
+ int m = db_get_manifest_setting(g.argv[i]);
4117
+ fossil_print("%s:\n", g.argv[i]);
4118
+ fossil_print(" flags = 0x%02x\n", m);
4119
+ if( m & MFESTFLG_RAW ){
4120
+ fossil_print(" manifest\n");
4121
+ }
4122
+ if( m & MFESTFLG_UUID ){
4123
+ fossil_print(" manifest.uuid\n");
4124
+ }
4125
+ if( m & MFESTFLG_TAGS ){
4126
+ fossil_print(" manifest.tags\n");
4127
+ }
4128
+ }
4129
+}
40734130
40744131
40754132
/*
40764133
** Record the name of a local repository in the global_config() database.
40774134
** The repository filename %s is recorded as an entry with a "name" field
@@ -4371,16 +4428,37 @@
43714428
}
43724429
}
43734430
g.argc = 2;
43744431
info_cmd();
43754432
}
4433
+
4434
+/*
4435
+** Return true if pSetting has its default value assuming its
4436
+** current value is zVal.
4437
+*/
4438
+int setting_has_default_value(const Setting *pSetting, const char *zVal){
4439
+ if( zVal==0 ) return 1;
4440
+ if( pSetting->def==0 ) return 0;
4441
+ if( pSetting->width==0 ){
4442
+ return is_false(pSetting->def)==is_false(zVal);
4443
+ }
4444
+ if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1;
4445
+ if( is_false(zVal) && is_false(pSetting->def) ) return 1;
4446
+ if( is_truth(zVal) && is_truth(pSetting->def) ) return 1;
4447
+ return 0;
4448
+}
43764449
43774450
/*
43784451
** Print the current value of a setting identified by the pSetting
43794452
** pointer.
4453
+**
4454
+** Only show the value, not the setting name, if valueOnly is true.
4455
+**
4456
+** Show nothing if bIfChng is true and the setting is not currently set
4457
+** or is set to its default value.
43804458
*/
4381
-void print_setting(const Setting *pSetting, int valueOnly){
4459
+void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){
43824460
Stmt q;
43834461
int versioned = 0;
43844462
if( pSetting->versionable && g.localOpen ){
43854463
/* Check to see if this is overridden by a versionable settings file */
43864464
Blob versionedPathname;
@@ -4391,11 +4469,16 @@
43914469
versioned = 1;
43924470
}
43934471
blob_reset(&versionedPathname);
43944472
}
43954473
if( valueOnly && versioned ){
4396
- fossil_print("%s\n", db_get_versioned(pSetting->name, NULL));
4474
+ const char *zVal = db_get_versioned(pSetting->name, NULL, NULL);
4475
+ if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){
4476
+ fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL));
4477
+ }else{
4478
+ versioned = 0;
4479
+ }
43974480
return;
43984481
}
43994482
if( g.repositoryOpen ){
44004483
db_prepare(&q,
44014484
"SELECT '(local)', value FROM config WHERE name=%Q"
@@ -4408,20 +4491,47 @@
44084491
"SELECT '(global)', value FROM global_config WHERE name=%Q",
44094492
pSetting->name
44104493
);
44114494
}
44124495
if( db_step(&q)==SQLITE_ROW ){
4413
- if( valueOnly ){
4496
+ const char *zVal = db_column_text(&q,1);
4497
+ if( bIfChng && setting_has_default_value(pSetting,zVal) ){
4498
+ if( versioned ){
4499
+ fossil_print("%-24s (versioned)\n", pSetting->name);
4500
+ versioned = 0;
4501
+ }
4502
+ }else if( valueOnly ){
44144503
fossil_print("%s\n", db_column_text(&q, 1));
44154504
}else{
4416
- fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0),
4417
- db_column_text(&q, 1));
4505
+ const char *zVal = (const char*)db_column_text(&q,1);
4506
+ const char *zName = (const char*)db_column_text(&q,0);
4507
+ if( zVal==0 ) zVal = "NULL";
4508
+ if( strchr(zVal,'\n')==0 ){
4509
+ fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal);
4510
+ }else{
4511
+ fossil_print("%-24s %-11s\n", pSetting->name, zName);
4512
+ while( zVal[0] ){
4513
+ char *zNL = strchr(zVal, '\n');
4514
+ if( zNL==0 ){
4515
+ fossil_print(" %s\n", zVal);
4516
+ break;
4517
+ }else{
4518
+ int n = (int)(zNL - zVal);
4519
+ while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; }
4520
+ fossil_print(" %.*s\n", n, zVal);
4521
+ zVal = zNL+1;
4522
+ }
4523
+ }
4524
+ }
44184525
}
4526
+ }else if( bIfChng ){
4527
+ /* Display nothing */
4528
+ versioned = 0;
44194529
}else if( valueOnly ){
44204530
fossil_print("\n");
44214531
}else{
4422
- fossil_print("%-20s\n", pSetting->name);
4532
+ fossil_print("%-24s\n", pSetting->name);
44234533
}
44244534
if( versioned ){
44254535
fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
44264536
pSetting->name);
44274537
}
@@ -4454,21 +4564,22 @@
44544564
char versionable; /* Is this setting versionable? */
44554565
char forceTextArea; /* Force using a text area for display? */
44564566
char sensitive; /* True if this a security-sensitive setting */
44574567
const char *def; /* Default value */
44584568
};
4569
+
44594570
#endif /* INTERFACE */
44604571
44614572
/*
4462
-** SETTING: access-log boolean default=off
4573
+** SETTING: access-log boolean default=on
44634574
**
44644575
** When the access-log setting is enabled, all login attempts (successful
44654576
** and unsuccessful) on the web interface are recorded in the "access" table
44664577
** of the repository.
44674578
*/
44684579
/*
4469
-** SETTING: admin-log boolean default=off
4580
+** SETTING: admin-log boolean default=on
44704581
**
44714582
** When the admin-log setting is enabled, configuration changes are recorded
44724583
** in the "admin_log" table of the repository.
44734584
*/
44744585
/*
@@ -4707,10 +4818,18 @@
47074818
*/
47084819
/*
47094820
** SETTING: editor width=32 sensitive
47104821
** The value is an external command that will launch the
47114822
** text editor command used for check-in comments.
4823
+**
4824
+** If this value is not set, then environment variables VISUAL and
4825
+** EDITOR are consulted, in that order. If neither of those are set,
4826
+** then a search is made for common text editors, including
4827
+** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed".
4828
+**
4829
+** If this setting is false ("off", "no", "false", or "0") then no
4830
+** text editor is used.
47124831
*/
47134832
/*
47144833
** SETTING: empty-dirs width=40 versionable block-text
47154834
** The value is a list of pathnames parsed according to the same rules as
47164835
** the *-glob settings. On update and checkout commands, if no directory
@@ -4757,13 +4876,14 @@
47574876
** send the "pragma avoid-delta-manifests" statement in its reply,
47584877
** which will cause the client to avoid generating a delta
47594878
** manifest.
47604879
*/
47614880
/*
4762
-** SETTING: gdiff-command width=40 default=gdiff sensitive
4881
+** SETTING: gdiff-command width=40 sensitive
47634882
** The value is an external command to run when performing a graphical
4764
-** diff. If undefined, text diff will be used.
4883
+** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish"
4884
+** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable.
47654885
*/
47664886
/*
47674887
** SETTING: gmerge-command width=40 sensitive
47684888
** The value is a graphical merge conflict resolver command operating
47694889
** on four files. Examples:
@@ -5152,20 +5272,22 @@
51525272
** configuration database. If both a local and a global value exists for a
51535273
** setting, the local value takes precedence. This command normally operates
51545274
** on the local settings. Use the --global option to change global settings.
51555275
**
51565276
** Options:
5277
+** --changed Only show settings if the value differs from the default
5278
+** --exact Only consider exact name matches
51575279
** --global Set or unset the given property globally instead of
51585280
** setting or unsetting it for the open repository only
5159
-** --exact Only consider exact name matches
51605281
** --value Only show the value of a given property (implies --exact)
51615282
**
51625283
** See also: [[configuration]]
51635284
*/
51645285
void setting_cmd(void){
51655286
int i;
51665287
int globalFlag = find_option("global","g",0)!=0;
5288
+ int bIfChng = find_option("changed",0,0)!=0;
51675289
int exactFlag = find_option("exact",0,0)!=0;
51685290
int valueFlag = find_option("value",0,0)!=0;
51695291
/* Undocumented "--test-for-subsystem SUBSYS" option used to test
51705292
** the db_get_for_subsystem() interface: */
51715293
const char *zSubsys = find_option("test-for-subsystem",0,1);
@@ -5186,16 +5308,15 @@
51865308
}
51875309
if( valueFlag ){
51885310
if( g.argc!=3 ){
51895311
fossil_fatal("--value is only supported when qurying a given property");
51905312
}
5191
- exactFlag = 1;
51925313
}
51935314
51945315
if( g.argc==2 ){
51955316
for(i=0; i<nSetting; i++){
5196
- print_setting(&aSetting[i], 0);
5317
+ print_setting(&aSetting[i], 0, bIfChng);
51975318
}
51985319
}else if( g.argc==3 || g.argc==4 ){
51995320
const char *zName = g.argv[2];
52005321
int n = (int)strlen(zName);
52015322
const Setting *pSetting = db_find_setting(zName, !exactFlag);
@@ -5246,11 +5367,11 @@
52465367
fossil_print(" [%s]", zValue);
52475368
fossil_free(zValue);
52485369
}
52495370
fossil_print("\n");
52505371
}else{
5251
- print_setting(pSetting, valueFlag);
5372
+ print_setting(pSetting, valueFlag, bIfChng);
52525373
}
52535374
pSetting++;
52545375
}
52555376
}
52565377
}else{
52575378
--- src/db.c
+++ src/db.c
@@ -3204,23 +3204,16 @@
3204
3205 db_unprotect(PROTECT_ALL);
3206 db_set("content-schema", CONTENT_SCHEMA, 0);
3207 db_set("aux-schema", AUX_SCHEMA_MAX, 0);
3208 db_set("rebuilt", get_version(), 0);
3209 db_set("admin-log", "1", 0);
3210 db_set("access-log", "1", 0);
3211 db_multi_exec(
3212 "INSERT INTO config(name,value,mtime)"
3213 " VALUES('server-code', lower(hex(randomblob(20))),now());"
3214 "INSERT INTO config(name,value,mtime)"
3215 " VALUES('project-code', lower(hex(randomblob(20))),now());"
3216 );
3217 if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
3218 if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
3219 if( !db_is_global("timeline-plaintext") ){
3220 db_set_int("timeline-plaintext", 1, 0);
3221 }
3222 db_create_default_users(0, zDefaultUser);
3223 if( zDefaultUser ) g.zLogin = zDefaultUser;
3224 user_select();
3225
3226 if( zTemplate ){
@@ -3647,66 +3640,79 @@
3647 **
3648 ** If the zNonVersionedSetting parameter is not NULL then it holds the
3649 ** non-versioned value for this setting. If both a versioned and a
3650 ** non-versioned value exist and are not equal, then a warning message
3651 ** might be generated.
 
 
 
 
 
 
 
3652 */
3653 char *db_get_versioned(const char *zName, char *zNonVersionedSetting){
 
 
 
 
3654 char *zVersionedSetting = 0;
3655 int noWarn = 0;
3656 int found = 0;
3657 struct _cacheEntry {
3658 struct _cacheEntry *next;
3659 const char *zName, *zValue;
3660 } *cacheEntry = 0;
3661 static struct _cacheEntry *cache = 0;
3662
3663 if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting;
 
 
 
3664 /* Look up name in cache */
3665 cacheEntry = cache;
3666 while( cacheEntry!=0 ){
3667 if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
3668 zVersionedSetting = fossil_strdup(cacheEntry->zValue);
3669 break;
3670 }
3671 cacheEntry = cacheEntry->next;
3672 }
 
 
 
3673 /* Attempt to read value from file in check-out if there wasn't a cache hit.*/
3674 if( cacheEntry==0 ){
3675 Blob versionedPathname;
3676 Blob setting;
3677 blob_zero(&versionedPathname);
3678 blob_zero(&setting);
3679 blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
3680 g.zLocalRoot, zName);
3681 if( !g.localOpen ){
3682 /* Repository is in the process of being opened, but files have not been
3683 * written to disk. Load from the database. */
3684 Blob noWarnFile;
3685 if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
3686 &setting, 0) ){
3687 found = 1;
3688 }
3689 /* See if there's a no-warn flag */
3690 blob_append(&versionedPathname, ".no-warn", -1);
3691 blob_zero(&noWarnFile);
3692 if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
3693 &noWarnFile, 0) ){
3694 noWarn = 1;
3695 }
3696 blob_reset(&noWarnFile);
3697 }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3698 /* File exists, and contains the value for this setting. Load from
3699 ** the file. */
3700 const char *zFile = blob_str(&versionedPathname);
3701 if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
3702 found = 1;
3703 }
3704 /* See if there's a no-warn flag */
3705 blob_append(&versionedPathname, ".no-warn", -1);
3706 if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3707 noWarn = 1;
3708 }
3709 }
3710 blob_reset(&versionedPathname);
3711 if( found ){
3712 blob_strip_comment_lines(&setting, &setting);
@@ -3713,20 +3719,27 @@
3713 blob_trim(&setting); /* Avoid non-obvious problems with line endings
3714 ** on boolean properties */
3715 zVersionedSetting = fossil_strdup(blob_str(&setting));
3716 }
3717 blob_reset(&setting);
 
3718 /* Store result in cache, which can be the value or 0 if not found */
3719 cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry));
3720 cacheEntry->next = cache;
3721 cacheEntry->zName = zName;
3722 cacheEntry->zValue = fossil_strdup(zVersionedSetting);
3723 cache = cacheEntry;
 
 
3724 }
 
3725 /* Display a warning? */
3726 if( zVersionedSetting!=0 && zNonVersionedSetting!=0
3727 && zNonVersionedSetting[0]!='\0' && !noWarn
 
 
 
3728 ){
3729 /* There's a versioned setting, and a non-versioned setting. Tell
3730 ** the user about the conflict */
3731 fossil_warning(
3732 "setting %s has both versioned and non-versioned values: using "
@@ -3735,10 +3748,11 @@
3735 "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
3736 "the non-versioned setting with \"fossil unset %s\")", zName,
3737 g.zLocalRoot, zName, g.zLocalRoot, zName, zName
3738 );
3739 }
 
3740 /* Prefer the versioned setting */
3741 return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
3742 }
3743
3744
@@ -3778,11 +3792,11 @@
3778 }
3779 if( pSetting!=0 && pSetting->versionable ){
3780 /* This is a versionable setting, try and get the info from a
3781 ** checked-out file */
3782 char * zZ = z;
3783 z = db_get_versioned(zName, z);
3784 if(zZ != z){
3785 fossil_free(zZ);
3786 }
3787 }
3788 if( z==0 ){
@@ -3919,11 +3933,11 @@
3919 }
3920 fossil_free(zVal);
3921 return dflt;
3922 }
3923 int db_get_versioned_boolean(const char *zName, int dflt){
3924 char *zVal = db_get_versioned(zName, 0);
3925 if( zVal==0 ) return dflt;
3926 if( is_truth(zVal) ) return 1;
3927 if( is_false(zVal) ) return 0;
3928 return dflt;
3929 }
@@ -4048,14 +4062,30 @@
4048 ** Get the manifest setting. For backwards compatibility first check if the
4049 ** value is a boolean. If it's not a boolean, treat each character as a flag
4050 ** to enable a manifest type. This system puts certain boundary conditions on
4051 ** which letters can be used to represent flags (any permutation of flags must
4052 ** not be able to fully form one of the boolean values).
 
 
 
 
 
 
 
 
4053 */
4054 int db_get_manifest_setting(void){
4055 int flg;
4056 char *zVal = db_get("manifest", 0);
 
 
 
 
 
 
 
 
4057 if( zVal==0 || is_false(zVal) ){
4058 return 0;
4059 }else if( is_truth(zVal) ){
4060 return MFESTFLG_RAW|MFESTFLG_UUID;
4061 }
@@ -4068,10 +4098,37 @@
4068 }
4069 zVal++;
4070 }
4071 return flg;
4072 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4073
4074
4075 /*
4076 ** Record the name of a local repository in the global_config() database.
4077 ** The repository filename %s is recorded as an entry with a "name" field
@@ -4371,16 +4428,37 @@
4371 }
4372 }
4373 g.argc = 2;
4374 info_cmd();
4375 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4376
4377 /*
4378 ** Print the current value of a setting identified by the pSetting
4379 ** pointer.
 
 
 
 
 
4380 */
4381 void print_setting(const Setting *pSetting, int valueOnly){
4382 Stmt q;
4383 int versioned = 0;
4384 if( pSetting->versionable && g.localOpen ){
4385 /* Check to see if this is overridden by a versionable settings file */
4386 Blob versionedPathname;
@@ -4391,11 +4469,16 @@
4391 versioned = 1;
4392 }
4393 blob_reset(&versionedPathname);
4394 }
4395 if( valueOnly && versioned ){
4396 fossil_print("%s\n", db_get_versioned(pSetting->name, NULL));
 
 
 
 
 
4397 return;
4398 }
4399 if( g.repositoryOpen ){
4400 db_prepare(&q,
4401 "SELECT '(local)', value FROM config WHERE name=%Q"
@@ -4408,20 +4491,47 @@
4408 "SELECT '(global)', value FROM global_config WHERE name=%Q",
4409 pSetting->name
4410 );
4411 }
4412 if( db_step(&q)==SQLITE_ROW ){
4413 if( valueOnly ){
 
 
 
 
 
 
4414 fossil_print("%s\n", db_column_text(&q, 1));
4415 }else{
4416 fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0),
4417 db_column_text(&q, 1));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4418 }
 
 
 
4419 }else if( valueOnly ){
4420 fossil_print("\n");
4421 }else{
4422 fossil_print("%-20s\n", pSetting->name);
4423 }
4424 if( versioned ){
4425 fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
4426 pSetting->name);
4427 }
@@ -4454,21 +4564,22 @@
4454 char versionable; /* Is this setting versionable? */
4455 char forceTextArea; /* Force using a text area for display? */
4456 char sensitive; /* True if this a security-sensitive setting */
4457 const char *def; /* Default value */
4458 };
 
4459 #endif /* INTERFACE */
4460
4461 /*
4462 ** SETTING: access-log boolean default=off
4463 **
4464 ** When the access-log setting is enabled, all login attempts (successful
4465 ** and unsuccessful) on the web interface are recorded in the "access" table
4466 ** of the repository.
4467 */
4468 /*
4469 ** SETTING: admin-log boolean default=off
4470 **
4471 ** When the admin-log setting is enabled, configuration changes are recorded
4472 ** in the "admin_log" table of the repository.
4473 */
4474 /*
@@ -4707,10 +4818,18 @@
4707 */
4708 /*
4709 ** SETTING: editor width=32 sensitive
4710 ** The value is an external command that will launch the
4711 ** text editor command used for check-in comments.
 
 
 
 
 
 
 
 
4712 */
4713 /*
4714 ** SETTING: empty-dirs width=40 versionable block-text
4715 ** The value is a list of pathnames parsed according to the same rules as
4716 ** the *-glob settings. On update and checkout commands, if no directory
@@ -4757,13 +4876,14 @@
4757 ** send the "pragma avoid-delta-manifests" statement in its reply,
4758 ** which will cause the client to avoid generating a delta
4759 ** manifest.
4760 */
4761 /*
4762 ** SETTING: gdiff-command width=40 default=gdiff sensitive
4763 ** The value is an external command to run when performing a graphical
4764 ** diff. If undefined, text diff will be used.
 
4765 */
4766 /*
4767 ** SETTING: gmerge-command width=40 sensitive
4768 ** The value is a graphical merge conflict resolver command operating
4769 ** on four files. Examples:
@@ -5152,20 +5272,22 @@
5152 ** configuration database. If both a local and a global value exists for a
5153 ** setting, the local value takes precedence. This command normally operates
5154 ** on the local settings. Use the --global option to change global settings.
5155 **
5156 ** Options:
 
 
5157 ** --global Set or unset the given property globally instead of
5158 ** setting or unsetting it for the open repository only
5159 ** --exact Only consider exact name matches
5160 ** --value Only show the value of a given property (implies --exact)
5161 **
5162 ** See also: [[configuration]]
5163 */
5164 void setting_cmd(void){
5165 int i;
5166 int globalFlag = find_option("global","g",0)!=0;
 
5167 int exactFlag = find_option("exact",0,0)!=0;
5168 int valueFlag = find_option("value",0,0)!=0;
5169 /* Undocumented "--test-for-subsystem SUBSYS" option used to test
5170 ** the db_get_for_subsystem() interface: */
5171 const char *zSubsys = find_option("test-for-subsystem",0,1);
@@ -5186,16 +5308,15 @@
5186 }
5187 if( valueFlag ){
5188 if( g.argc!=3 ){
5189 fossil_fatal("--value is only supported when qurying a given property");
5190 }
5191 exactFlag = 1;
5192 }
5193
5194 if( g.argc==2 ){
5195 for(i=0; i<nSetting; i++){
5196 print_setting(&aSetting[i], 0);
5197 }
5198 }else if( g.argc==3 || g.argc==4 ){
5199 const char *zName = g.argv[2];
5200 int n = (int)strlen(zName);
5201 const Setting *pSetting = db_find_setting(zName, !exactFlag);
@@ -5246,11 +5367,11 @@
5246 fossil_print(" [%s]", zValue);
5247 fossil_free(zValue);
5248 }
5249 fossil_print("\n");
5250 }else{
5251 print_setting(pSetting, valueFlag);
5252 }
5253 pSetting++;
5254 }
5255 }
5256 }else{
5257
--- src/db.c
+++ src/db.c
@@ -3204,23 +3204,16 @@
3204
3205 db_unprotect(PROTECT_ALL);
3206 db_set("content-schema", CONTENT_SCHEMA, 0);
3207 db_set("aux-schema", AUX_SCHEMA_MAX, 0);
3208 db_set("rebuilt", get_version(), 0);
 
 
3209 db_multi_exec(
3210 "INSERT INTO config(name,value,mtime)"
3211 " VALUES('server-code', lower(hex(randomblob(20))),now());"
3212 "INSERT INTO config(name,value,mtime)"
3213 " VALUES('project-code', lower(hex(randomblob(20))),now());"
3214 );
 
 
 
 
 
3215 db_create_default_users(0, zDefaultUser);
3216 if( zDefaultUser ) g.zLogin = zDefaultUser;
3217 user_select();
3218
3219 if( zTemplate ){
@@ -3647,66 +3640,79 @@
3640 **
3641 ** If the zNonVersionedSetting parameter is not NULL then it holds the
3642 ** non-versioned value for this setting. If both a versioned and a
3643 ** non-versioned value exist and are not equal, then a warning message
3644 ** might be generated.
3645 **
3646 ** zCkin is normally NULL. In that case, the versioned setting is
3647 ** take from the local check-out, if a local checkout exists, or from
3648 ** checkin named by the g.zOpenRevision global variable. If zCkin is
3649 ** not NULL, then zCkin is the name of the specific checkin from which
3650 ** versioned setting value is taken. When zCkin is not NULL, the cache
3651 ** is bypassed.
3652 */
3653 char *db_get_versioned(
3654 const char *zName,
3655 char *zNonVersionedSetting,
3656 const char *zCkin
3657 ){
3658 char *zVersionedSetting = 0;
3659 int noWarn = 0;
3660 int found = 0;
3661 struct _cacheEntry {
3662 struct _cacheEntry *next;
3663 const char *zName, *zValue;
3664 } *cacheEntry = 0;
3665 static struct _cacheEntry *cache = 0;
3666
3667 if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){
3668 return zNonVersionedSetting;
3669 }
3670
3671 /* Look up name in cache */
3672 if( zCkin==0 ){
3673 cacheEntry = cache;
3674 while( cacheEntry!=0 ){
3675 if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
3676 zVersionedSetting = fossil_strdup(cacheEntry->zValue);
3677 break;
3678 }
3679 cacheEntry = cacheEntry->next;
3680 }
3681 }
3682
3683 /* Attempt to read value from file in check-out if there wasn't a cache hit.*/
3684 if( cacheEntry==0 ){
3685 Blob versionedPathname;
3686 Blob setting;
3687 blob_init(&versionedPathname, 0, 0);
3688 blob_init(&setting, 0, 0);
3689 if( !g.localOpen || zCkin!=0 ){
3690 /* Repository is in the process of being opened, but files have not been
3691 * written to disk. Load from the database. */
3692 blob_appendf(&versionedPathname, ".fossil-settings/%s", zName);
3693 if( historical_blob(zCkin ? zCkin : g.zOpenRevision,
3694 blob_str(&versionedPathname),
3695 &setting, 0)
3696 ){
3697 found = 1;
3698 }
3699 }else{
3700 blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
3701 g.zLocalRoot, zName);
3702 if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3703 /* File exists, and contains the value for this setting. Load from
3704 ** the file. */
3705 const char *zFile = blob_str(&versionedPathname);
3706 if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
3707 found = 1;
3708 }
3709 /* See if there's a no-warn flag */
3710 blob_append(&versionedPathname, ".no-warn", -1);
3711 if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
3712 noWarn = 1;
3713 }
 
 
 
 
3714 }
3715 }
3716 blob_reset(&versionedPathname);
3717 if( found ){
3718 blob_strip_comment_lines(&setting, &setting);
@@ -3713,20 +3719,27 @@
3719 blob_trim(&setting); /* Avoid non-obvious problems with line endings
3720 ** on boolean properties */
3721 zVersionedSetting = fossil_strdup(blob_str(&setting));
3722 }
3723 blob_reset(&setting);
3724
3725 /* Store result in cache, which can be the value or 0 if not found */
3726 if( zCkin==0 ){
3727 cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry));
3728 cacheEntry->next = cache;
3729 cacheEntry->zName = zName;
3730 cacheEntry->zValue = fossil_strdup(zVersionedSetting);
3731 cache = cacheEntry;
3732 }
3733 }
3734
3735 /* Display a warning? */
3736 if( zVersionedSetting!=0
3737 && zNonVersionedSetting!=0
3738 && zNonVersionedSetting[0]!='\0'
3739 && zCkin==0
3740 && !noWarn
3741 ){
3742 /* There's a versioned setting, and a non-versioned setting. Tell
3743 ** the user about the conflict */
3744 fossil_warning(
3745 "setting %s has both versioned and non-versioned values: using "
@@ -3735,10 +3748,11 @@
3748 "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
3749 "the non-versioned setting with \"fossil unset %s\")", zName,
3750 g.zLocalRoot, zName, g.zLocalRoot, zName, zName
3751 );
3752 }
3753
3754 /* Prefer the versioned setting */
3755 return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
3756 }
3757
3758
@@ -3778,11 +3792,11 @@
3792 }
3793 if( pSetting!=0 && pSetting->versionable ){
3794 /* This is a versionable setting, try and get the info from a
3795 ** checked-out file */
3796 char * zZ = z;
3797 z = db_get_versioned(zName, z, 0);
3798 if(zZ != z){
3799 fossil_free(zZ);
3800 }
3801 }
3802 if( z==0 ){
@@ -3919,11 +3933,11 @@
3933 }
3934 fossil_free(zVal);
3935 return dflt;
3936 }
3937 int db_get_versioned_boolean(const char *zName, int dflt){
3938 char *zVal = db_get_versioned(zName, 0, 0);
3939 if( zVal==0 ) return dflt;
3940 if( is_truth(zVal) ) return 1;
3941 if( is_false(zVal) ) return 0;
3942 return dflt;
3943 }
@@ -4048,14 +4062,30 @@
4062 ** Get the manifest setting. For backwards compatibility first check if the
4063 ** value is a boolean. If it's not a boolean, treat each character as a flag
4064 ** to enable a manifest type. This system puts certain boundary conditions on
4065 ** which letters can be used to represent flags (any permutation of flags must
4066 ** not be able to fully form one of the boolean values).
4067 **
4068 ** "manifest" is a versionable setting. But we do not issue a warning
4069 ** if there is a conflict. Instead, the value returned is the value for
4070 ** the versioned setting if the versioned setting exists, or the ordinary
4071 ** setting otherwise.
4072 **
4073 ** The argument zCkin is the specific check-in for which we want the
4074 ** manifest setting.
4075 */
4076 int db_get_manifest_setting(const char *zCkin){
4077 int flg;
4078 char *zVal;
4079
4080 /* Look for the versioned setting first */
4081 zVal = db_get_versioned("manifest", 0, zCkin);
4082
4083 if( zVal==0 && g.repositoryOpen ){
4084 /* No versioned setting, look for the repository setting second */
4085 zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'");
4086 }
4087 if( zVal==0 || is_false(zVal) ){
4088 return 0;
4089 }else if( is_truth(zVal) ){
4090 return MFESTFLG_RAW|MFESTFLG_UUID;
4091 }
@@ -4068,10 +4098,37 @@
4098 }
4099 zVal++;
4100 }
4101 return flg;
4102 }
4103
4104 /*
4105 ** COMMAND: test-manifest-setting
4106 **
4107 ** Usage: %fossil test-manifest-setting VERSION VERSION ...
4108 **
4109 ** Display the value for the "manifest" setting for various versions
4110 ** of the repository.
4111 */
4112 void test_manfest_setting_cmd(void){
4113 int i;
4114 db_find_and_open_repository(0, 0);
4115 for(i=2; i<g.argc; i++){
4116 int m = db_get_manifest_setting(g.argv[i]);
4117 fossil_print("%s:\n", g.argv[i]);
4118 fossil_print(" flags = 0x%02x\n", m);
4119 if( m & MFESTFLG_RAW ){
4120 fossil_print(" manifest\n");
4121 }
4122 if( m & MFESTFLG_UUID ){
4123 fossil_print(" manifest.uuid\n");
4124 }
4125 if( m & MFESTFLG_TAGS ){
4126 fossil_print(" manifest.tags\n");
4127 }
4128 }
4129 }
4130
4131
4132 /*
4133 ** Record the name of a local repository in the global_config() database.
4134 ** The repository filename %s is recorded as an entry with a "name" field
@@ -4371,16 +4428,37 @@
4428 }
4429 }
4430 g.argc = 2;
4431 info_cmd();
4432 }
4433
4434 /*
4435 ** Return true if pSetting has its default value assuming its
4436 ** current value is zVal.
4437 */
4438 int setting_has_default_value(const Setting *pSetting, const char *zVal){
4439 if( zVal==0 ) return 1;
4440 if( pSetting->def==0 ) return 0;
4441 if( pSetting->width==0 ){
4442 return is_false(pSetting->def)==is_false(zVal);
4443 }
4444 if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1;
4445 if( is_false(zVal) && is_false(pSetting->def) ) return 1;
4446 if( is_truth(zVal) && is_truth(pSetting->def) ) return 1;
4447 return 0;
4448 }
4449
4450 /*
4451 ** Print the current value of a setting identified by the pSetting
4452 ** pointer.
4453 **
4454 ** Only show the value, not the setting name, if valueOnly is true.
4455 **
4456 ** Show nothing if bIfChng is true and the setting is not currently set
4457 ** or is set to its default value.
4458 */
4459 void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){
4460 Stmt q;
4461 int versioned = 0;
4462 if( pSetting->versionable && g.localOpen ){
4463 /* Check to see if this is overridden by a versionable settings file */
4464 Blob versionedPathname;
@@ -4391,11 +4469,16 @@
4469 versioned = 1;
4470 }
4471 blob_reset(&versionedPathname);
4472 }
4473 if( valueOnly && versioned ){
4474 const char *zVal = db_get_versioned(pSetting->name, NULL, NULL);
4475 if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){
4476 fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL));
4477 }else{
4478 versioned = 0;
4479 }
4480 return;
4481 }
4482 if( g.repositoryOpen ){
4483 db_prepare(&q,
4484 "SELECT '(local)', value FROM config WHERE name=%Q"
@@ -4408,20 +4491,47 @@
4491 "SELECT '(global)', value FROM global_config WHERE name=%Q",
4492 pSetting->name
4493 );
4494 }
4495 if( db_step(&q)==SQLITE_ROW ){
4496 const char *zVal = db_column_text(&q,1);
4497 if( bIfChng && setting_has_default_value(pSetting,zVal) ){
4498 if( versioned ){
4499 fossil_print("%-24s (versioned)\n", pSetting->name);
4500 versioned = 0;
4501 }
4502 }else if( valueOnly ){
4503 fossil_print("%s\n", db_column_text(&q, 1));
4504 }else{
4505 const char *zVal = (const char*)db_column_text(&q,1);
4506 const char *zName = (const char*)db_column_text(&q,0);
4507 if( zVal==0 ) zVal = "NULL";
4508 if( strchr(zVal,'\n')==0 ){
4509 fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal);
4510 }else{
4511 fossil_print("%-24s %-11s\n", pSetting->name, zName);
4512 while( zVal[0] ){
4513 char *zNL = strchr(zVal, '\n');
4514 if( zNL==0 ){
4515 fossil_print(" %s\n", zVal);
4516 break;
4517 }else{
4518 int n = (int)(zNL - zVal);
4519 while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; }
4520 fossil_print(" %.*s\n", n, zVal);
4521 zVal = zNL+1;
4522 }
4523 }
4524 }
4525 }
4526 }else if( bIfChng ){
4527 /* Display nothing */
4528 versioned = 0;
4529 }else if( valueOnly ){
4530 fossil_print("\n");
4531 }else{
4532 fossil_print("%-24s\n", pSetting->name);
4533 }
4534 if( versioned ){
4535 fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
4536 pSetting->name);
4537 }
@@ -4454,21 +4564,22 @@
4564 char versionable; /* Is this setting versionable? */
4565 char forceTextArea; /* Force using a text area for display? */
4566 char sensitive; /* True if this a security-sensitive setting */
4567 const char *def; /* Default value */
4568 };
4569
4570 #endif /* INTERFACE */
4571
4572 /*
4573 ** SETTING: access-log boolean default=on
4574 **
4575 ** When the access-log setting is enabled, all login attempts (successful
4576 ** and unsuccessful) on the web interface are recorded in the "access" table
4577 ** of the repository.
4578 */
4579 /*
4580 ** SETTING: admin-log boolean default=on
4581 **
4582 ** When the admin-log setting is enabled, configuration changes are recorded
4583 ** in the "admin_log" table of the repository.
4584 */
4585 /*
@@ -4707,10 +4818,18 @@
4818 */
4819 /*
4820 ** SETTING: editor width=32 sensitive
4821 ** The value is an external command that will launch the
4822 ** text editor command used for check-in comments.
4823 **
4824 ** If this value is not set, then environment variables VISUAL and
4825 ** EDITOR are consulted, in that order. If neither of those are set,
4826 ** then a search is made for common text editors, including
4827 ** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed".
4828 **
4829 ** If this setting is false ("off", "no", "false", or "0") then no
4830 ** text editor is used.
4831 */
4832 /*
4833 ** SETTING: empty-dirs width=40 versionable block-text
4834 ** The value is a list of pathnames parsed according to the same rules as
4835 ** the *-glob settings. On update and checkout commands, if no directory
@@ -4757,13 +4876,14 @@
4876 ** send the "pragma avoid-delta-manifests" statement in its reply,
4877 ** which will cause the client to avoid generating a delta
4878 ** manifest.
4879 */
4880 /*
4881 ** SETTING: gdiff-command width=40 sensitive
4882 ** The value is an external command to run when performing a graphical
4883 ** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish"
4884 ** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable.
4885 */
4886 /*
4887 ** SETTING: gmerge-command width=40 sensitive
4888 ** The value is a graphical merge conflict resolver command operating
4889 ** on four files. Examples:
@@ -5152,20 +5272,22 @@
5272 ** configuration database. If both a local and a global value exists for a
5273 ** setting, the local value takes precedence. This command normally operates
5274 ** on the local settings. Use the --global option to change global settings.
5275 **
5276 ** Options:
5277 ** --changed Only show settings if the value differs from the default
5278 ** --exact Only consider exact name matches
5279 ** --global Set or unset the given property globally instead of
5280 ** setting or unsetting it for the open repository only
 
5281 ** --value Only show the value of a given property (implies --exact)
5282 **
5283 ** See also: [[configuration]]
5284 */
5285 void setting_cmd(void){
5286 int i;
5287 int globalFlag = find_option("global","g",0)!=0;
5288 int bIfChng = find_option("changed",0,0)!=0;
5289 int exactFlag = find_option("exact",0,0)!=0;
5290 int valueFlag = find_option("value",0,0)!=0;
5291 /* Undocumented "--test-for-subsystem SUBSYS" option used to test
5292 ** the db_get_for_subsystem() interface: */
5293 const char *zSubsys = find_option("test-for-subsystem",0,1);
@@ -5186,16 +5308,15 @@
5308 }
5309 if( valueFlag ){
5310 if( g.argc!=3 ){
5311 fossil_fatal("--value is only supported when qurying a given property");
5312 }
 
5313 }
5314
5315 if( g.argc==2 ){
5316 for(i=0; i<nSetting; i++){
5317 print_setting(&aSetting[i], 0, bIfChng);
5318 }
5319 }else if( g.argc==3 || g.argc==4 ){
5320 const char *zName = g.argv[2];
5321 int n = (int)strlen(zName);
5322 const Setting *pSetting = db_find_setting(zName, !exactFlag);
@@ -5246,11 +5367,11 @@
5367 fossil_print(" [%s]", zValue);
5368 fossil_free(zValue);
5369 }
5370 fossil_print("\n");
5371 }else{
5372 print_setting(pSetting, valueFlag, bIfChng);
5373 }
5374 pSetting++;
5375 }
5376 }
5377 }else{
5378
--- src/default.css
+++ src/default.css
@@ -750,10 +750,11 @@
750750
body.tkt div.content ol.tkt-changes > li:target > ol {
751751
border-left: 1px solid gold;
752752
}
753753
body.cpage-ckout .file-change-line,
754754
body.cpage-info .file-change-line,
755
+body.cpage-vinfo .file-change-line,
755756
body.cpage-vdiff .file-change-line {
756757
margin-top: 16px;
757758
margin-bottom: 16px;
758759
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
759760
display: flex;
760761
--- src/default.css
+++ src/default.css
@@ -750,10 +750,11 @@
750 body.tkt div.content ol.tkt-changes > li:target > ol {
751 border-left: 1px solid gold;
752 }
753 body.cpage-ckout .file-change-line,
754 body.cpage-info .file-change-line,
 
755 body.cpage-vdiff .file-change-line {
756 margin-top: 16px;
757 margin-bottom: 16px;
758 margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
759 display: flex;
760
--- src/default.css
+++ src/default.css
@@ -750,10 +750,11 @@
750 body.tkt div.content ol.tkt-changes > li:target > ol {
751 border-left: 1px solid gold;
752 }
753 body.cpage-ckout .file-change-line,
754 body.cpage-info .file-change-line,
755 body.cpage-vinfo .file-change-line,
756 body.cpage-vdiff .file-change-line {
757 margin-top: 16px;
758 margin-bottom: 16px;
759 margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
760 display: flex;
761
+7 -1
--- src/diff.c
+++ src/diff.c
@@ -3175,11 +3175,13 @@
31753175
}
31763176
g.diffCnt[1] += nIns;
31773177
g.diffCnt[2] += nDel;
31783178
if( nIns+nDel ){
31793179
g.diffCnt[0]++;
3180
- blob_appendf(pOut, "%10d %10d", nIns, nDel);
3180
+ if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3181
+ blob_appendf(pOut, "%10d %10d", nIns, nDel);
3182
+ }
31813183
}
31823184
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
31833185
const int *R = c.aEdit;
31843186
unsigned int r;
31853187
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
@@ -3335,10 +3337,14 @@
33353337
if( zDiffBinary ){
33363338
if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
33373339
}else if( db_get_boolean("diff-binary", 1) ){
33383340
diffFlags |= DIFF_INCBINARY;
33393341
}
3342
+ }else if( isGDiff) {
3343
+ /* No external gdiff command found, using --by */
3344
+ diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER
3345
+ |DIFF_SIDEBYSIDE;
33403346
}
33413347
}
33423348
if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
33433349
/* Deprecated, but retained for script compatibility. */
33443350
else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;
33453351
--- src/diff.c
+++ src/diff.c
@@ -3175,11 +3175,13 @@
3175 }
3176 g.diffCnt[1] += nIns;
3177 g.diffCnt[2] += nDel;
3178 if( nIns+nDel ){
3179 g.diffCnt[0]++;
3180 blob_appendf(pOut, "%10d %10d", nIns, nDel);
 
 
3181 }
3182 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
3183 const int *R = c.aEdit;
3184 unsigned int r;
3185 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
@@ -3335,10 +3337,14 @@
3335 if( zDiffBinary ){
3336 if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
3337 }else if( db_get_boolean("diff-binary", 1) ){
3338 diffFlags |= DIFF_INCBINARY;
3339 }
 
 
 
 
3340 }
3341 }
3342 if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
3343 /* Deprecated, but retained for script compatibility. */
3344 else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;
3345
--- src/diff.c
+++ src/diff.c
@@ -3175,11 +3175,13 @@
3175 }
3176 g.diffCnt[1] += nIns;
3177 g.diffCnt[2] += nDel;
3178 if( nIns+nDel ){
3179 g.diffCnt[0]++;
3180 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3181 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3182 }
3183 }
3184 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
3185 const int *R = c.aEdit;
3186 unsigned int r;
3187 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
@@ -3335,10 +3337,14 @@
3337 if( zDiffBinary ){
3338 if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
3339 }else if( db_get_boolean("diff-binary", 1) ){
3340 diffFlags |= DIFF_INCBINARY;
3341 }
3342 }else if( isGDiff) {
3343 /* No external gdiff command found, using --by */
3344 diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER
3345 |DIFF_SIDEBYSIDE;
3346 }
3347 }
3348 if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
3349 /* Deprecated, but retained for script compatibility. */
3350 else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;
3351
+36 -7
--- src/diff.tcl
+++ src/diff.tcl
@@ -90,28 +90,56 @@
9090
set x [lindex $difftxt $ii]
9191
incr ii
9292
return $x
9393
}
9494
95
-proc readDiffs {fossilcmd} {
95
+proc reloadDiff {} {
96
+ global fossilcmd difftxt
97
+ unset -nocomplain difftxt
98
+ set idx [.txtA index @0,0]
99
+ readDiffs $fossilcmd 1
100
+ update
101
+ viewDiff $idx
102
+}
103
+
104
+proc readDiffs {fossilcmd redo} {
96105
global difftxt debug
97106
if {![info exists difftxt]} {
98107
if {$debug} {
99108
puts "# [list open $fossilcmd r]"
100109
flush stdout
101110
}
102
- set in [open $fossilcmd r]
103
- fconfigure $in -encoding utf-8
104
- set difftxt [split [read $in] \n]
105
- close $in
111
+ if {[catch {
112
+ set in [open $fossilcmd r]
113
+ fconfigure $in -encoding utf-8
114
+ set difftxt [split [read $in] \n]
115
+ close $in
116
+ } msg]} {
117
+ if {$redo} {
118
+ tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg"
119
+ return 0
120
+ } else {
121
+ puts $msg
122
+ exit 1
123
+ }
124
+ }
106125
}
107126
set N [llength $difftxt]
108127
set ii 0
109128
set nDiffs 0
110129
set n1 0
111130
set n2 0
112131
array set widths {txt 3 ln 3 mkr 1}
132
+ if {$redo} {
133
+ foreach c [cols] {$c config -state normal}
134
+ .lnA delete 1.0 end
135
+ .txtA delete 1.0 end
136
+ .lnB delete 1.0 end
137
+ .txtB delete 1.0 end
138
+ .mkr delete 1.0 end
139
+ .wfiles.lb delete 0 end
140
+ }
113141
114142
115143
set fromIndex [lsearch -glob $fossilcmd *-from]
116144
set toIndex [lsearch -glob $fossilcmd *-to]
117145
set branchIndex [lsearch -glob $fossilcmd *-branch]
@@ -459,11 +487,11 @@
459487
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
460488
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
461489
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
462490
frame .spacer
463491
464
-if {[readDiffs $fossilcmd] == 0} {
492
+if {[readDiffs $fossilcmd 0] == 0} {
465493
tk_messageBox -type ok -title $CFG(TITLE) -message "No changes"
466494
exit
467495
}
468496
update idletasks
469497
@@ -574,14 +602,15 @@
574602
$w tag config search -background {#fcc000}
575603
}
576604
set ::search $w
577605
}
578606
::ttk::button .bb.quit -text {Quit} -command exit
607
+::ttk::button .bb.reload -text {Reload} -command reloadDiff
579608
::ttk::button .bb.invert -text {Invert} -command invertDiff
580609
::ttk::button .bb.save -text {Save As...} -command saveDiff
581610
::ttk::button .bb.search -text {Search} -command searchOnOff
582
-pack .bb.quit .bb.invert -side left
611
+pack .bb.quit .bb.reload .bb.invert -side left
583612
if {$fossilcmd!=""} {pack .bb.save -side left}
584613
pack .bb.files .bb.search -side left
585614
grid rowconfigure . 1 -weight 1
586615
grid columnconfigure . 1 -weight 1
587616
grid columnconfigure . 4 -weight 1
588617
--- src/diff.tcl
+++ src/diff.tcl
@@ -90,28 +90,56 @@
90 set x [lindex $difftxt $ii]
91 incr ii
92 return $x
93 }
94
95 proc readDiffs {fossilcmd} {
 
 
 
 
 
 
 
 
 
96 global difftxt debug
97 if {![info exists difftxt]} {
98 if {$debug} {
99 puts "# [list open $fossilcmd r]"
100 flush stdout
101 }
102 set in [open $fossilcmd r]
103 fconfigure $in -encoding utf-8
104 set difftxt [split [read $in] \n]
105 close $in
 
 
 
 
 
 
 
 
 
 
106 }
107 set N [llength $difftxt]
108 set ii 0
109 set nDiffs 0
110 set n1 0
111 set n2 0
112 array set widths {txt 3 ln 3 mkr 1}
 
 
 
 
 
 
 
 
 
113
114
115 set fromIndex [lsearch -glob $fossilcmd *-from]
116 set toIndex [lsearch -glob $fossilcmd *-to]
117 set branchIndex [lsearch -glob $fossilcmd *-branch]
@@ -459,11 +487,11 @@
459 ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
460 ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
461 ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
462 frame .spacer
463
464 if {[readDiffs $fossilcmd] == 0} {
465 tk_messageBox -type ok -title $CFG(TITLE) -message "No changes"
466 exit
467 }
468 update idletasks
469
@@ -574,14 +602,15 @@
574 $w tag config search -background {#fcc000}
575 }
576 set ::search $w
577 }
578 ::ttk::button .bb.quit -text {Quit} -command exit
 
579 ::ttk::button .bb.invert -text {Invert} -command invertDiff
580 ::ttk::button .bb.save -text {Save As...} -command saveDiff
581 ::ttk::button .bb.search -text {Search} -command searchOnOff
582 pack .bb.quit .bb.invert -side left
583 if {$fossilcmd!=""} {pack .bb.save -side left}
584 pack .bb.files .bb.search -side left
585 grid rowconfigure . 1 -weight 1
586 grid columnconfigure . 1 -weight 1
587 grid columnconfigure . 4 -weight 1
588
--- src/diff.tcl
+++ src/diff.tcl
@@ -90,28 +90,56 @@
90 set x [lindex $difftxt $ii]
91 incr ii
92 return $x
93 }
94
95 proc reloadDiff {} {
96 global fossilcmd difftxt
97 unset -nocomplain difftxt
98 set idx [.txtA index @0,0]
99 readDiffs $fossilcmd 1
100 update
101 viewDiff $idx
102 }
103
104 proc readDiffs {fossilcmd redo} {
105 global difftxt debug
106 if {![info exists difftxt]} {
107 if {$debug} {
108 puts "# [list open $fossilcmd r]"
109 flush stdout
110 }
111 if {[catch {
112 set in [open $fossilcmd r]
113 fconfigure $in -encoding utf-8
114 set difftxt [split [read $in] \n]
115 close $in
116 } msg]} {
117 if {$redo} {
118 tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg"
119 return 0
120 } else {
121 puts $msg
122 exit 1
123 }
124 }
125 }
126 set N [llength $difftxt]
127 set ii 0
128 set nDiffs 0
129 set n1 0
130 set n2 0
131 array set widths {txt 3 ln 3 mkr 1}
132 if {$redo} {
133 foreach c [cols] {$c config -state normal}
134 .lnA delete 1.0 end
135 .txtA delete 1.0 end
136 .lnB delete 1.0 end
137 .txtB delete 1.0 end
138 .mkr delete 1.0 end
139 .wfiles.lb delete 0 end
140 }
141
142
143 set fromIndex [lsearch -glob $fossilcmd *-from]
144 set toIndex [lsearch -glob $fossilcmd *-to]
145 set branchIndex [lsearch -glob $fossilcmd *-branch]
@@ -459,11 +487,11 @@
487 ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
488 ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
489 ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
490 frame .spacer
491
492 if {[readDiffs $fossilcmd 0] == 0} {
493 tk_messageBox -type ok -title $CFG(TITLE) -message "No changes"
494 exit
495 }
496 update idletasks
497
@@ -574,14 +602,15 @@
602 $w tag config search -background {#fcc000}
603 }
604 set ::search $w
605 }
606 ::ttk::button .bb.quit -text {Quit} -command exit
607 ::ttk::button .bb.reload -text {Reload} -command reloadDiff
608 ::ttk::button .bb.invert -text {Invert} -command invertDiff
609 ::ttk::button .bb.save -text {Save As...} -command saveDiff
610 ::ttk::button .bb.search -text {Search} -command searchOnOff
611 pack .bb.quit .bb.reload .bb.invert -side left
612 if {$fossilcmd!=""} {pack .bb.save -side left}
613 pack .bb.files .bb.search -side left
614 grid rowconfigure . 1 -weight 1
615 grid columnconfigure . 1 -weight 1
616 grid columnconfigure . 4 -weight 1
617
+69 -30
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -127,16 +127,16 @@
127127
DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
128128
fossil_print("Fossil-Diff-From: %s\n",
129129
zFrom[0]=='(' ? zFrom : mprintf("%S %s",
130130
rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
131131
db_text("","SELECT datetime(%f)||' UTC'",
132
- symbolic_name_to_mtime(zFrom, 0))));
132
+ symbolic_name_to_mtime(zFrom, 0, 0))));
133133
fossil_print("Fossil-Diff-To: %s\n",
134134
zTo[0]=='(' ? zTo : mprintf("%S %s",
135135
rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
136136
db_text("","SELECT datetime(%f)||' UTC'",
137
- symbolic_name_to_mtime(zTo, 0))));
137
+ symbolic_name_to_mtime(zTo, 0, 1))));
138138
fossil_print("%.66c\n", '-');
139139
}
140140
}
141141
142142
/*
@@ -587,20 +587,22 @@
587587
blob_read_from_file(&file2, zFile2, ExtFILE);
588588
zName2 = zName;
589589
}
590590
591591
/* Compute and output the differences */
592
- if( pCfg->diffFlags & DIFF_BRIEF ){
592
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
593593
if( blob_compare(pFile1, &file2) ){
594594
fossil_print("CHANGED %s\n", zName);
595595
}
596596
}else{
597597
blob_zero(&out);
598598
text_diff(pFile1, &file2, &out, pCfg);
599599
if( blob_size(&out) ){
600600
if( pCfg->diffFlags & DIFF_NUMSTAT ){
601
- blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
601
+ if( !(pCfg->diffFlags & DIFF_BRIEF) ){
602
+ blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
603
+ }
602604
}else{
603605
diff_print_filenames(zName, zName2, pCfg, pOut);
604606
blob_appendf(pOut, "%s\n", blob_str(&out));
605607
}
606608
}
@@ -665,11 +667,16 @@
665667
blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
666668
blob_append_escaped_arg(&cmd, zFile2, 1);
667669
}
668670
669671
/* Run the external diff command */
670
- fossil_system(blob_str(&cmd));
672
+ if( fossil_system(blob_str(&cmd)) ){
673
+#if !defined(_WIN32)
674
+ /* On Windows, exit codes are unreliable. */
675
+ fossil_warning("External diff command failed: %b\n", &cmd);
676
+#endif
677
+ }
671678
672679
/* Delete the temporary file and clean up memory used */
673680
if( useTempfile ) file_delete(blob_str(&nameFile1));
674681
blob_reset(&nameFile1);
675682
blob_reset(&cmd);
@@ -693,18 +700,22 @@
693700
Blob *pFile1, /* In memory content to compare from */
694701
Blob *pFile2, /* In memory content to compare to */
695702
const char *zName, /* Display name of the file */
696703
DiffConfig *pCfg /* Diff flags */
697704
){
698
- if( pCfg->diffFlags & DIFF_BRIEF ) return;
705
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
706
+ return;
707
+ }
699708
if( pCfg->zDiffCmd==0 ){
700709
Blob out; /* Diff output text */
701710
702711
blob_zero(&out);
703712
text_diff(pFile1, pFile2, &out, pCfg);
704713
if( pCfg->diffFlags & DIFF_NUMSTAT ){
705
- fossil_print("%s %s\n", blob_str(&out), zName);
714
+ if( !(pCfg->diffFlags & DIFF_BRIEF) ){
715
+ fossil_print("%s %s\n", blob_str(&out), zName);
716
+ }
706717
}else{
707718
diff_print_filenames(zName, zName, pCfg, 0);
708719
fossil_print("%s\n", blob_str(&out));
709720
}
710721
@@ -990,11 +1001,13 @@
9901001
}else if( pTo ){
9911002
zName = pTo->zName;
9921003
}else{
9931004
zName = DIFF_NO_NAME;
9941005
}
995
- if( pCfg->diffFlags & DIFF_BRIEF ) return;
1006
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
1007
+ return;
1008
+ }
9961009
diff_print_index(zName, pCfg, 0);
9971010
if( pFrom ){
9981011
rid = uuid_to_rid(pFrom->zUuid, 0);
9991012
content_get(rid, &f1);
10001013
}else{
@@ -1078,11 +1091,11 @@
10781091
(void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
10791092
pFromFile = manifest_file_next(pFrom,0);
10801093
pToFile = manifest_file_next(pTo,0);
10811094
}else{
10821095
if( file_dir_match(pFileDir, pToFile->zName) ){
1083
- if( pCfg->diffFlags & DIFF_BRIEF ){
1096
+ if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){
10841097
fossil_print("CHANGED %s\n", pFromFile->zName);
10851098
}else{
10861099
diff_manifest_entry(pFromFile, pToFile, pCfg);
10871100
}
10881101
}
@@ -1154,25 +1167,34 @@
11541167
/*
11551168
** Return the name of the external diff command, or return NULL if
11561169
** no external diff command is defined.
11571170
*/
11581171
const char *diff_command_external(int guiDiff){
1159
- const char *zDefault;
11601172
const char *zName;
1161
-
1162
- if( guiDiff ){
1163
-#if defined(_WIN32)
1164
- zDefault = "WinDiff.exe";
1165
-#else
1166
- zDefault = 0;
1167
-#endif
1168
- zName = "gdiff-command";
1169
- }else{
1170
- zDefault = 0;
1171
- zName = "diff-command";
1172
- }
1173
- return db_get(zName, zDefault);
1173
+ zName = guiDiff ? "gdiff-command" : "diff-command";
1174
+ return db_get(zName, 0);
1175
+}
1176
+
1177
+/*
1178
+** Return true if it reasonable to run "diff -tk" for "gdiff".
1179
+**
1180
+** Details: Return true if all of the following are true:
1181
+**
1182
+** (1) The isGDiff flags is true
1183
+** (2) The "gdiff-command" setting is undefined
1184
+** (3) There is a "tclsh" on PATH
1185
+** (4) There is a "wish" on PATH
1186
+*/
1187
+int gdiff_using_tk(int isGdiff){
1188
+ if( isGdiff
1189
+ && db_get("gdiff-command","")[0]==0
1190
+ && fossil_app_on_path("tclsh",0)
1191
+ && fossil_app_on_path("wish",0)
1192
+ ){
1193
+ return 1;
1194
+ }
1195
+ return 0;
11741196
}
11751197
11761198
/*
11771199
** Show diff output in a Tcl/Tk window, in response to the --tk option
11781200
** to the diff command.
@@ -1190,10 +1212,11 @@
11901212
const char *zTempFile = 0;
11911213
char *zCmd;
11921214
const char *zTclsh;
11931215
int bDebug = find_option("tkdebug",0,0)!=0;
11941216
int bDarkMode = find_option("dark",0,0)!=0;
1217
+ (void)find_option("debug",0,0);
11951218
blob_zero(&script);
11961219
/* Caution: When this routine is called from the merge-info command,
11971220
** the --tcl argument requires an argument. But merge-info does not
11981221
** use -i, so we can take -i as that argument. This routine needs to
11991222
** always have -i after --tcl.
@@ -1271,11 +1294,12 @@
12711294
** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
12721295
**
12731296
** Show the difference between the current version of each of the FILEs
12741297
** specified (as they exist on disk) and that same file as it was checked-
12751298
** out. Or if the FILE arguments are omitted, show all unsaved changes
1276
-** currently in the working check-out.
1299
+** currently in the working check-out. The "gdiff" variant means to
1300
+** to use a GUI diff.
12771301
**
12781302
** The default output format is a "unified patch" (the same as the
12791303
** output of "diff -u" on most unix systems). Many alternative formats
12801304
** are available. A few of the more useful alternatives:
12811305
**
@@ -1337,11 +1361,13 @@
13371361
** -i|--internal Use internal diff logic
13381362
** --invert Invert the diff
13391363
** --json Output formatted as JSON
13401364
** -n|--linenum Show line numbers
13411365
** -N|--new-file Alias for --verbose
1342
-** --numstat Show only the number of added and deleted lines
1366
+** --numstat Show the number of added and deleted lines per
1367
+** file, omitting the diff. When combined with
1368
+** --brief, show only the total row.
13431369
** -y|--side-by-side Side-by-side diff
13441370
** --strip-trailing-cr Strip trailing CR
13451371
** --tcl Tcl-formatted output used internally by --tk
13461372
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
13471373
** --tk Launch a Tcl/Tk GUI for display
@@ -1363,15 +1389,15 @@
13631389
int againstUndo = 0; /* Diff against files in the undo buffer */
13641390
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
13651391
DiffConfig DCfg; /* Diff configuration object */
13661392
int bFromIsDir = 0; /* True if zFrom is a directory name */
13671393
1368
- if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1394
+ isGDiff = g.argv[1][0]=='g';
1395
+ if( find_option("tk",0,0)!=0|| has_option("tclsh") ){
13691396
diff_tk("diff", 2);
13701397
return;
13711398
}
1372
- isGDiff = g.argv[1][0]=='g';
13731399
zFrom = find_option("from", "r", 1);
13741400
zTo = find_option("to", 0, 1);
13751401
zCheckin = find_option("checkin", "ci", 1);
13761402
zBranch = find_option("branch", 0, 1);
13771403
againstUndo = find_option("undo",0,0)!=0;
@@ -1383,10 +1409,11 @@
13831409
if( zTo || zFrom || zCheckin ){
13841410
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
13851411
}
13861412
zTo = zBranch;
13871413
zFrom = mprintf("root:%s", zBranch);
1414
+ zBranch = 0;
13881415
}
13891416
if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
13901417
fossil_fatal("cannot use --checkin together with --from or --to");
13911418
}
13921419
if( 0==zCheckin ){
@@ -1397,10 +1424,19 @@
13971424
}else{
13981425
db_find_and_open_repository(0, 0);
13991426
}
14001427
}else{
14011428
db_find_and_open_repository(0, 0);
1429
+ }
1430
+ if( gdiff_using_tk(isGDiff) ){
1431
+ restore_option("--from", zFrom, 1);
1432
+ restore_option("--to", zTo, 1);
1433
+ restore_option("--checkin", zCheckin, 1);
1434
+ restore_option("--branch", zBranch, 1);
1435
+ if( againstUndo ) restore_option("--undo", 0, 0);
1436
+ diff_tk("diff", 2);
1437
+ return;
14021438
}
14031439
determine_exec_relative_option(1);
14041440
if( zFrom!=file_tail(zFrom)
14051441
&& file_isdir(zFrom, ExtFILE)==1
14061442
&& !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
@@ -1429,11 +1465,14 @@
14291465
pFileDir[i-2].nName = blob_size(&fname);
14301466
pFileDir[i-2].nUsed = 0;
14311467
blob_reset(&fname);
14321468
}
14331469
}
1434
- if ( zCheckin!=0 ){
1470
+ if( DCfg.diffFlags & DIFF_NUMSTAT ){
1471
+ fossil_print("%10s %10s\n", "INSERTED", "DELETED");
1472
+ }
1473
+ if( zCheckin!=0 ){
14351474
int ridTo = name_to_typed_rid(zCheckin, "ci");
14361475
zTo = zCheckin;
14371476
zFrom = db_text(0,
14381477
"SELECT uuid FROM blob, plink"
14391478
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
@@ -1469,12 +1508,12 @@
14691508
}
14701509
fossil_free(pFileDir);
14711510
}
14721511
diff_end(&DCfg, 0);
14731512
if ( DCfg.diffFlags & DIFF_NUMSTAT ){
1474
- fossil_print("%10d %10d TOTAL over %d changed files\n",
1475
- g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
1513
+ fossil_print("%10d %10d TOTAL over %d changed file%s\n",
1514
+ g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": "");
14761515
}
14771516
}
14781517
14791518
/*
14801519
** WEBPAGE: vpatch
14811520
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -127,16 +127,16 @@
127 DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
128 fossil_print("Fossil-Diff-From: %s\n",
129 zFrom[0]=='(' ? zFrom : mprintf("%S %s",
130 rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
131 db_text("","SELECT datetime(%f)||' UTC'",
132 symbolic_name_to_mtime(zFrom, 0))));
133 fossil_print("Fossil-Diff-To: %s\n",
134 zTo[0]=='(' ? zTo : mprintf("%S %s",
135 rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
136 db_text("","SELECT datetime(%f)||' UTC'",
137 symbolic_name_to_mtime(zTo, 0))));
138 fossil_print("%.66c\n", '-');
139 }
140 }
141
142 /*
@@ -587,20 +587,22 @@
587 blob_read_from_file(&file2, zFile2, ExtFILE);
588 zName2 = zName;
589 }
590
591 /* Compute and output the differences */
592 if( pCfg->diffFlags & DIFF_BRIEF ){
593 if( blob_compare(pFile1, &file2) ){
594 fossil_print("CHANGED %s\n", zName);
595 }
596 }else{
597 blob_zero(&out);
598 text_diff(pFile1, &file2, &out, pCfg);
599 if( blob_size(&out) ){
600 if( pCfg->diffFlags & DIFF_NUMSTAT ){
601 blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
 
 
602 }else{
603 diff_print_filenames(zName, zName2, pCfg, pOut);
604 blob_appendf(pOut, "%s\n", blob_str(&out));
605 }
606 }
@@ -665,11 +667,16 @@
665 blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
666 blob_append_escaped_arg(&cmd, zFile2, 1);
667 }
668
669 /* Run the external diff command */
670 fossil_system(blob_str(&cmd));
 
 
 
 
 
671
672 /* Delete the temporary file and clean up memory used */
673 if( useTempfile ) file_delete(blob_str(&nameFile1));
674 blob_reset(&nameFile1);
675 blob_reset(&cmd);
@@ -693,18 +700,22 @@
693 Blob *pFile1, /* In memory content to compare from */
694 Blob *pFile2, /* In memory content to compare to */
695 const char *zName, /* Display name of the file */
696 DiffConfig *pCfg /* Diff flags */
697 ){
698 if( pCfg->diffFlags & DIFF_BRIEF ) return;
 
 
699 if( pCfg->zDiffCmd==0 ){
700 Blob out; /* Diff output text */
701
702 blob_zero(&out);
703 text_diff(pFile1, pFile2, &out, pCfg);
704 if( pCfg->diffFlags & DIFF_NUMSTAT ){
705 fossil_print("%s %s\n", blob_str(&out), zName);
 
 
706 }else{
707 diff_print_filenames(zName, zName, pCfg, 0);
708 fossil_print("%s\n", blob_str(&out));
709 }
710
@@ -990,11 +1001,13 @@
990 }else if( pTo ){
991 zName = pTo->zName;
992 }else{
993 zName = DIFF_NO_NAME;
994 }
995 if( pCfg->diffFlags & DIFF_BRIEF ) return;
 
 
996 diff_print_index(zName, pCfg, 0);
997 if( pFrom ){
998 rid = uuid_to_rid(pFrom->zUuid, 0);
999 content_get(rid, &f1);
1000 }else{
@@ -1078,11 +1091,11 @@
1078 (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
1079 pFromFile = manifest_file_next(pFrom,0);
1080 pToFile = manifest_file_next(pTo,0);
1081 }else{
1082 if( file_dir_match(pFileDir, pToFile->zName) ){
1083 if( pCfg->diffFlags & DIFF_BRIEF ){
1084 fossil_print("CHANGED %s\n", pFromFile->zName);
1085 }else{
1086 diff_manifest_entry(pFromFile, pToFile, pCfg);
1087 }
1088 }
@@ -1154,25 +1167,34 @@
1154 /*
1155 ** Return the name of the external diff command, or return NULL if
1156 ** no external diff command is defined.
1157 */
1158 const char *diff_command_external(int guiDiff){
1159 const char *zDefault;
1160 const char *zName;
1161
1162 if( guiDiff ){
1163 #if defined(_WIN32)
1164 zDefault = "WinDiff.exe";
1165 #else
1166 zDefault = 0;
1167 #endif
1168 zName = "gdiff-command";
1169 }else{
1170 zDefault = 0;
1171 zName = "diff-command";
1172 }
1173 return db_get(zName, zDefault);
 
 
 
 
 
 
 
 
 
 
1174 }
1175
1176 /*
1177 ** Show diff output in a Tcl/Tk window, in response to the --tk option
1178 ** to the diff command.
@@ -1190,10 +1212,11 @@
1190 const char *zTempFile = 0;
1191 char *zCmd;
1192 const char *zTclsh;
1193 int bDebug = find_option("tkdebug",0,0)!=0;
1194 int bDarkMode = find_option("dark",0,0)!=0;
 
1195 blob_zero(&script);
1196 /* Caution: When this routine is called from the merge-info command,
1197 ** the --tcl argument requires an argument. But merge-info does not
1198 ** use -i, so we can take -i as that argument. This routine needs to
1199 ** always have -i after --tcl.
@@ -1271,11 +1294,12 @@
1271 ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
1272 **
1273 ** Show the difference between the current version of each of the FILEs
1274 ** specified (as they exist on disk) and that same file as it was checked-
1275 ** out. Or if the FILE arguments are omitted, show all unsaved changes
1276 ** currently in the working check-out.
 
1277 **
1278 ** The default output format is a "unified patch" (the same as the
1279 ** output of "diff -u" on most unix systems). Many alternative formats
1280 ** are available. A few of the more useful alternatives:
1281 **
@@ -1337,11 +1361,13 @@
1337 ** -i|--internal Use internal diff logic
1338 ** --invert Invert the diff
1339 ** --json Output formatted as JSON
1340 ** -n|--linenum Show line numbers
1341 ** -N|--new-file Alias for --verbose
1342 ** --numstat Show only the number of added and deleted lines
 
 
1343 ** -y|--side-by-side Side-by-side diff
1344 ** --strip-trailing-cr Strip trailing CR
1345 ** --tcl Tcl-formatted output used internally by --tk
1346 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1347 ** --tk Launch a Tcl/Tk GUI for display
@@ -1363,15 +1389,15 @@
1363 int againstUndo = 0; /* Diff against files in the undo buffer */
1364 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1365 DiffConfig DCfg; /* Diff configuration object */
1366 int bFromIsDir = 0; /* True if zFrom is a directory name */
1367
1368 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
 
1369 diff_tk("diff", 2);
1370 return;
1371 }
1372 isGDiff = g.argv[1][0]=='g';
1373 zFrom = find_option("from", "r", 1);
1374 zTo = find_option("to", 0, 1);
1375 zCheckin = find_option("checkin", "ci", 1);
1376 zBranch = find_option("branch", 0, 1);
1377 againstUndo = find_option("undo",0,0)!=0;
@@ -1383,10 +1409,11 @@
1383 if( zTo || zFrom || zCheckin ){
1384 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1385 }
1386 zTo = zBranch;
1387 zFrom = mprintf("root:%s", zBranch);
 
1388 }
1389 if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
1390 fossil_fatal("cannot use --checkin together with --from or --to");
1391 }
1392 if( 0==zCheckin ){
@@ -1397,10 +1424,19 @@
1397 }else{
1398 db_find_and_open_repository(0, 0);
1399 }
1400 }else{
1401 db_find_and_open_repository(0, 0);
 
 
 
 
 
 
 
 
 
1402 }
1403 determine_exec_relative_option(1);
1404 if( zFrom!=file_tail(zFrom)
1405 && file_isdir(zFrom, ExtFILE)==1
1406 && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
@@ -1429,11 +1465,14 @@
1429 pFileDir[i-2].nName = blob_size(&fname);
1430 pFileDir[i-2].nUsed = 0;
1431 blob_reset(&fname);
1432 }
1433 }
1434 if ( zCheckin!=0 ){
 
 
 
1435 int ridTo = name_to_typed_rid(zCheckin, "ci");
1436 zTo = zCheckin;
1437 zFrom = db_text(0,
1438 "SELECT uuid FROM blob, plink"
1439 " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
@@ -1469,12 +1508,12 @@
1469 }
1470 fossil_free(pFileDir);
1471 }
1472 diff_end(&DCfg, 0);
1473 if ( DCfg.diffFlags & DIFF_NUMSTAT ){
1474 fossil_print("%10d %10d TOTAL over %d changed files\n",
1475 g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
1476 }
1477 }
1478
1479 /*
1480 ** WEBPAGE: vpatch
1481
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -127,16 +127,16 @@
127 DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
128 fossil_print("Fossil-Diff-From: %s\n",
129 zFrom[0]=='(' ? zFrom : mprintf("%S %s",
130 rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
131 db_text("","SELECT datetime(%f)||' UTC'",
132 symbolic_name_to_mtime(zFrom, 0, 0))));
133 fossil_print("Fossil-Diff-To: %s\n",
134 zTo[0]=='(' ? zTo : mprintf("%S %s",
135 rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
136 db_text("","SELECT datetime(%f)||' UTC'",
137 symbolic_name_to_mtime(zTo, 0, 1))));
138 fossil_print("%.66c\n", '-');
139 }
140 }
141
142 /*
@@ -587,20 +587,22 @@
587 blob_read_from_file(&file2, zFile2, ExtFILE);
588 zName2 = zName;
589 }
590
591 /* Compute and output the differences */
592 if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
593 if( blob_compare(pFile1, &file2) ){
594 fossil_print("CHANGED %s\n", zName);
595 }
596 }else{
597 blob_zero(&out);
598 text_diff(pFile1, &file2, &out, pCfg);
599 if( blob_size(&out) ){
600 if( pCfg->diffFlags & DIFF_NUMSTAT ){
601 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
602 blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
603 }
604 }else{
605 diff_print_filenames(zName, zName2, pCfg, pOut);
606 blob_appendf(pOut, "%s\n", blob_str(&out));
607 }
608 }
@@ -665,11 +667,16 @@
667 blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
668 blob_append_escaped_arg(&cmd, zFile2, 1);
669 }
670
671 /* Run the external diff command */
672 if( fossil_system(blob_str(&cmd)) ){
673 #if !defined(_WIN32)
674 /* On Windows, exit codes are unreliable. */
675 fossil_warning("External diff command failed: %b\n", &cmd);
676 #endif
677 }
678
679 /* Delete the temporary file and clean up memory used */
680 if( useTempfile ) file_delete(blob_str(&nameFile1));
681 blob_reset(&nameFile1);
682 blob_reset(&cmd);
@@ -693,18 +700,22 @@
700 Blob *pFile1, /* In memory content to compare from */
701 Blob *pFile2, /* In memory content to compare to */
702 const char *zName, /* Display name of the file */
703 DiffConfig *pCfg /* Diff flags */
704 ){
705 if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
706 return;
707 }
708 if( pCfg->zDiffCmd==0 ){
709 Blob out; /* Diff output text */
710
711 blob_zero(&out);
712 text_diff(pFile1, pFile2, &out, pCfg);
713 if( pCfg->diffFlags & DIFF_NUMSTAT ){
714 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
715 fossil_print("%s %s\n", blob_str(&out), zName);
716 }
717 }else{
718 diff_print_filenames(zName, zName, pCfg, 0);
719 fossil_print("%s\n", blob_str(&out));
720 }
721
@@ -990,11 +1001,13 @@
1001 }else if( pTo ){
1002 zName = pTo->zName;
1003 }else{
1004 zName = DIFF_NO_NAME;
1005 }
1006 if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
1007 return;
1008 }
1009 diff_print_index(zName, pCfg, 0);
1010 if( pFrom ){
1011 rid = uuid_to_rid(pFrom->zUuid, 0);
1012 content_get(rid, &f1);
1013 }else{
@@ -1078,11 +1091,11 @@
1091 (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
1092 pFromFile = manifest_file_next(pFrom,0);
1093 pToFile = manifest_file_next(pTo,0);
1094 }else{
1095 if( file_dir_match(pFileDir, pToFile->zName) ){
1096 if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){
1097 fossil_print("CHANGED %s\n", pFromFile->zName);
1098 }else{
1099 diff_manifest_entry(pFromFile, pToFile, pCfg);
1100 }
1101 }
@@ -1154,25 +1167,34 @@
1167 /*
1168 ** Return the name of the external diff command, or return NULL if
1169 ** no external diff command is defined.
1170 */
1171 const char *diff_command_external(int guiDiff){
 
1172 const char *zName;
1173 zName = guiDiff ? "gdiff-command" : "diff-command";
1174 return db_get(zName, 0);
1175 }
1176
1177 /*
1178 ** Return true if it reasonable to run "diff -tk" for "gdiff".
1179 **
1180 ** Details: Return true if all of the following are true:
1181 **
1182 ** (1) The isGDiff flags is true
1183 ** (2) The "gdiff-command" setting is undefined
1184 ** (3) There is a "tclsh" on PATH
1185 ** (4) There is a "wish" on PATH
1186 */
1187 int gdiff_using_tk(int isGdiff){
1188 if( isGdiff
1189 && db_get("gdiff-command","")[0]==0
1190 && fossil_app_on_path("tclsh",0)
1191 && fossil_app_on_path("wish",0)
1192 ){
1193 return 1;
1194 }
1195 return 0;
1196 }
1197
1198 /*
1199 ** Show diff output in a Tcl/Tk window, in response to the --tk option
1200 ** to the diff command.
@@ -1190,10 +1212,11 @@
1212 const char *zTempFile = 0;
1213 char *zCmd;
1214 const char *zTclsh;
1215 int bDebug = find_option("tkdebug",0,0)!=0;
1216 int bDarkMode = find_option("dark",0,0)!=0;
1217 (void)find_option("debug",0,0);
1218 blob_zero(&script);
1219 /* Caution: When this routine is called from the merge-info command,
1220 ** the --tcl argument requires an argument. But merge-info does not
1221 ** use -i, so we can take -i as that argument. This routine needs to
1222 ** always have -i after --tcl.
@@ -1271,11 +1294,12 @@
1294 ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
1295 **
1296 ** Show the difference between the current version of each of the FILEs
1297 ** specified (as they exist on disk) and that same file as it was checked-
1298 ** out. Or if the FILE arguments are omitted, show all unsaved changes
1299 ** currently in the working check-out. The "gdiff" variant means to
1300 ** to use a GUI diff.
1301 **
1302 ** The default output format is a "unified patch" (the same as the
1303 ** output of "diff -u" on most unix systems). Many alternative formats
1304 ** are available. A few of the more useful alternatives:
1305 **
@@ -1337,11 +1361,13 @@
1361 ** -i|--internal Use internal diff logic
1362 ** --invert Invert the diff
1363 ** --json Output formatted as JSON
1364 ** -n|--linenum Show line numbers
1365 ** -N|--new-file Alias for --verbose
1366 ** --numstat Show the number of added and deleted lines per
1367 ** file, omitting the diff. When combined with
1368 ** --brief, show only the total row.
1369 ** -y|--side-by-side Side-by-side diff
1370 ** --strip-trailing-cr Strip trailing CR
1371 ** --tcl Tcl-formatted output used internally by --tk
1372 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1373 ** --tk Launch a Tcl/Tk GUI for display
@@ -1363,15 +1389,15 @@
1389 int againstUndo = 0; /* Diff against files in the undo buffer */
1390 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1391 DiffConfig DCfg; /* Diff configuration object */
1392 int bFromIsDir = 0; /* True if zFrom is a directory name */
1393
1394 isGDiff = g.argv[1][0]=='g';
1395 if( find_option("tk",0,0)!=0|| has_option("tclsh") ){
1396 diff_tk("diff", 2);
1397 return;
1398 }
 
1399 zFrom = find_option("from", "r", 1);
1400 zTo = find_option("to", 0, 1);
1401 zCheckin = find_option("checkin", "ci", 1);
1402 zBranch = find_option("branch", 0, 1);
1403 againstUndo = find_option("undo",0,0)!=0;
@@ -1383,10 +1409,11 @@
1409 if( zTo || zFrom || zCheckin ){
1410 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1411 }
1412 zTo = zBranch;
1413 zFrom = mprintf("root:%s", zBranch);
1414 zBranch = 0;
1415 }
1416 if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
1417 fossil_fatal("cannot use --checkin together with --from or --to");
1418 }
1419 if( 0==zCheckin ){
@@ -1397,10 +1424,19 @@
1424 }else{
1425 db_find_and_open_repository(0, 0);
1426 }
1427 }else{
1428 db_find_and_open_repository(0, 0);
1429 }
1430 if( gdiff_using_tk(isGDiff) ){
1431 restore_option("--from", zFrom, 1);
1432 restore_option("--to", zTo, 1);
1433 restore_option("--checkin", zCheckin, 1);
1434 restore_option("--branch", zBranch, 1);
1435 if( againstUndo ) restore_option("--undo", 0, 0);
1436 diff_tk("diff", 2);
1437 return;
1438 }
1439 determine_exec_relative_option(1);
1440 if( zFrom!=file_tail(zFrom)
1441 && file_isdir(zFrom, ExtFILE)==1
1442 && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
@@ -1429,11 +1465,14 @@
1465 pFileDir[i-2].nName = blob_size(&fname);
1466 pFileDir[i-2].nUsed = 0;
1467 blob_reset(&fname);
1468 }
1469 }
1470 if( DCfg.diffFlags & DIFF_NUMSTAT ){
1471 fossil_print("%10s %10s\n", "INSERTED", "DELETED");
1472 }
1473 if( zCheckin!=0 ){
1474 int ridTo = name_to_typed_rid(zCheckin, "ci");
1475 zTo = zCheckin;
1476 zFrom = db_text(0,
1477 "SELECT uuid FROM blob, plink"
1478 " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
@@ -1469,12 +1508,12 @@
1508 }
1509 fossil_free(pFileDir);
1510 }
1511 diff_end(&DCfg, 0);
1512 if ( DCfg.diffFlags & DIFF_NUMSTAT ){
1513 fossil_print("%10d %10d TOTAL over %d changed file%s\n",
1514 g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": "");
1515 }
1516 }
1517
1518 /*
1519 ** WEBPAGE: vpatch
1520
+31 -1
--- src/dispatch.c
+++ src/dispatch.c
@@ -811,10 +811,32 @@
811811
fossil_print(" %s\n", az[j]);
812812
}
813813
}
814814
}
815815
816
+
817
+/*
818
+** Returns 1 if the command or page name zName is known to be a
819
+** command/page which is only available in certain builds/platforms,
820
+** else returns 0.
821
+*/
822
+static int help_is_platform_command(const char *zName){
823
+ const char *aList[] = {
824
+ /* List of commands/pages which are known to only be available in
825
+ ** certain builds/platforms. */
826
+ "winsrv",
827
+ "json", "/json",
828
+ NULL /* end-of-list sentinel */
829
+ };
830
+ int i = 0;
831
+ const char *z;
832
+ for( z = aList[0]; z ; z = aList[++i] ){
833
+ if( 0==fossil_strcmp(zName, z) ) return 1;
834
+ }
835
+ return 0;
836
+}
837
+
816838
/*
817839
** WEBPAGE: help
818840
** URL: /help?name=CMD
819841
**
820842
** Show the built-in help text for CMD. CMD can be a command-line interface
@@ -860,11 +882,15 @@
860882
@ <h1>The "%h(pCmd->zName)" setting:</h1>
861883
}else{
862884
@ <h1>The "%h(pCmd->zName)" command:</h1>
863885
}
864886
if( rc==1 || (rc==2 && zCmd[0]=='/') ){
865
- @ Unknown topic: "%h(zCmd)"
887
+ if( zCmd && help_is_platform_command(zCmd) ){
888
+ @ Not available in this build: "%h(zCmd)"
889
+ }else{
890
+ @ Unknown topic: "%h(zCmd)"
891
+ }
866892
}else if( rc==2 ){
867893
@ Ambiguous prefix: "%h(zCmd)"
868894
}else{
869895
if( pCmd->zHelp[0]==0 ){
870896
@ No help available for "%h(pCmd->zName)"
@@ -1518,10 +1544,14 @@
15181544
rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
15191545
if( rc ){
15201546
int i, n;
15211547
const char *az[5];
15221548
if( rc==1 ){
1549
+ if( help_is_platform_command(g.argv[2]) ){
1550
+ fossil_print("Not available in this build: %s\n", g.argv[2]);
1551
+ return;
1552
+ }
15231553
fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
15241554
}else{
15251555
fossil_print("ambiguous %s prefix: %s\n",
15261556
zCmdOrPage, g.argv[2]);
15271557
}
15281558
--- src/dispatch.c
+++ src/dispatch.c
@@ -811,10 +811,32 @@
811 fossil_print(" %s\n", az[j]);
812 }
813 }
814 }
815
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
816 /*
817 ** WEBPAGE: help
818 ** URL: /help?name=CMD
819 **
820 ** Show the built-in help text for CMD. CMD can be a command-line interface
@@ -860,11 +882,15 @@
860 @ <h1>The "%h(pCmd->zName)" setting:</h1>
861 }else{
862 @ <h1>The "%h(pCmd->zName)" command:</h1>
863 }
864 if( rc==1 || (rc==2 && zCmd[0]=='/') ){
865 @ Unknown topic: "%h(zCmd)"
 
 
 
 
866 }else if( rc==2 ){
867 @ Ambiguous prefix: "%h(zCmd)"
868 }else{
869 if( pCmd->zHelp[0]==0 ){
870 @ No help available for "%h(pCmd->zName)"
@@ -1518,10 +1544,14 @@
1518 rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
1519 if( rc ){
1520 int i, n;
1521 const char *az[5];
1522 if( rc==1 ){
 
 
 
 
1523 fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
1524 }else{
1525 fossil_print("ambiguous %s prefix: %s\n",
1526 zCmdOrPage, g.argv[2]);
1527 }
1528
--- src/dispatch.c
+++ src/dispatch.c
@@ -811,10 +811,32 @@
811 fossil_print(" %s\n", az[j]);
812 }
813 }
814 }
815
816
817 /*
818 ** Returns 1 if the command or page name zName is known to be a
819 ** command/page which is only available in certain builds/platforms,
820 ** else returns 0.
821 */
822 static int help_is_platform_command(const char *zName){
823 const char *aList[] = {
824 /* List of commands/pages which are known to only be available in
825 ** certain builds/platforms. */
826 "winsrv",
827 "json", "/json",
828 NULL /* end-of-list sentinel */
829 };
830 int i = 0;
831 const char *z;
832 for( z = aList[0]; z ; z = aList[++i] ){
833 if( 0==fossil_strcmp(zName, z) ) return 1;
834 }
835 return 0;
836 }
837
838 /*
839 ** WEBPAGE: help
840 ** URL: /help?name=CMD
841 **
842 ** Show the built-in help text for CMD. CMD can be a command-line interface
@@ -860,11 +882,15 @@
882 @ <h1>The "%h(pCmd->zName)" setting:</h1>
883 }else{
884 @ <h1>The "%h(pCmd->zName)" command:</h1>
885 }
886 if( rc==1 || (rc==2 && zCmd[0]=='/') ){
887 if( zCmd && help_is_platform_command(zCmd) ){
888 @ Not available in this build: "%h(zCmd)"
889 }else{
890 @ Unknown topic: "%h(zCmd)"
891 }
892 }else if( rc==2 ){
893 @ Ambiguous prefix: "%h(zCmd)"
894 }else{
895 if( pCmd->zHelp[0]==0 ){
896 @ No help available for "%h(pCmd->zName)"
@@ -1518,10 +1544,14 @@
1544 rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
1545 if( rc ){
1546 int i, n;
1547 const char *az[5];
1548 if( rc==1 ){
1549 if( help_is_platform_command(g.argv[2]) ){
1550 fossil_print("Not available in this build: %s\n", g.argv[2]);
1551 return;
1552 }
1553 fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
1554 }else{
1555 fossil_print("ambiguous %s prefix: %s\n",
1556 zCmdOrPage, g.argv[2]);
1557 }
1558
+13 -8
--- src/export.c
+++ src/export.c
@@ -1074,12 +1074,11 @@
10741074
*/
10751075
static int gitmirror_send_checkin(
10761076
FILE *xCmd, /* Write fast-import text on this pipe */
10771077
int rid, /* BLOB.RID for the check-in to export */
10781078
const char *zUuid, /* BLOB.UUID for the check-in to export */
1079
- int *pnLimit, /* Stop when the counter reaches zero */
1080
- int fManifest /* MFESTFLG_* values */
1079
+ int *pnLimit /* Stop when the counter reaches zero */
10811080
){
10821081
Manifest *pMan; /* The check-in to be output */
10831082
int i; /* Loop counter */
10841083
int iParent; /* Which immediate ancestor is primary. -1 for none */
10851084
Stmt q; /* An SQL query */
@@ -1089,10 +1088,12 @@
10891088
Blob comment; /* The comment text for the check-in */
10901089
int nErr = 0; /* Number of errors */
10911090
int bPhantomOk; /* True if phantom files should be ignored */
10921091
char buf[24];
10931092
char *zEmail; /* Contact info for Git committer field */
1093
+ int fManifest; /* Should the manifest files be included? */
1094
+ int fPManifest = 0; /* OR of the manifest files for all parents */
10941095
10951096
pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
10961097
if( pMan==0 ){
10971098
/* Must be a phantom. Return without doing anything, and in particular
10981099
** without creating a mark for this check-in. */
@@ -1106,11 +1107,11 @@
11061107
char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
11071108
if( zPMark==0 ){
11081109
int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
11091110
pMan->azParent[i]);
11101111
int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
1111
- pnLimit, fManifest);
1112
+ pnLimit);
11121113
if( rc || *pnLimit<=0 ){
11131114
manifest_destroy(pMan);
11141115
return 1;
11151116
}
11161117
}
@@ -1215,10 +1216,11 @@
12151216
blob_reset(&comment);
12161217
iParent = -1; /* Which ancestor is the primary parent */
12171218
for(i=0; i<pMan->nParent; i++){
12181219
char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
12191220
if( zOther==0 ) continue;
1221
+ fPManifest |= db_get_manifest_setting(pMan->azParent[i]);
12201222
if( iParent<0 ){
12211223
iParent = i;
12221224
fprintf(xCmd, "from %s\n", zOther);
12231225
}else{
12241226
fprintf(xCmd, "merge %s\n", zOther);
@@ -1271,29 +1273,36 @@
12711273
db_finalize(&q);
12721274
manifest_destroy(pMan);
12731275
pMan = 0;
12741276
12751277
/* Include Fossil-generated auxiliary files in the check-in */
1278
+ fManifest = db_get_manifest_setting(zUuid);
12761279
if( fManifest & MFESTFLG_RAW ){
12771280
Blob manifest;
12781281
content_get(rid, &manifest);
12791282
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
12801283
fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
12811284
blob_strlen(&manifest), blob_str(&manifest));
12821285
blob_reset(&manifest);
1286
+ }else if( fPManifest & MFESTFLG_RAW ){
1287
+ fprintf(xCmd, "D manifest\n");
12831288
}
12841289
if( fManifest & MFESTFLG_UUID ){
12851290
int n = (int)strlen(zUuid);
12861291
fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid);
1292
+ }else if( fPManifest & MFESTFLG_UUID ){
1293
+ fprintf(xCmd, "D manifest.uuid\n");
12871294
}
12881295
if( fManifest & MFESTFLG_TAGS ){
12891296
Blob tagslist;
12901297
blob_init(&tagslist, 0, 0);
12911298
get_checkin_taglist(rid, &tagslist);
12921299
fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
12931300
blob_strlen(&tagslist), blob_str(&tagslist));
12941301
blob_reset(&tagslist);
1302
+ }else if( fPManifest & MFESTFLG_TAGS ){
1303
+ fprintf(xCmd, "D manifest.tags\n");
12951304
}
12961305
12971306
/* The check-in is finished, so decrement the counter */
12981307
(*pnLimit)--;
12991308
return 0;
@@ -1383,11 +1392,10 @@
13831392
char *zPushUrl; /* URL to sync the mirror to */
13841393
double rEnd; /* time of most recent export */
13851394
int rc; /* Result code */
13861395
int bForce; /* Do the export and sync even if no changes*/
13871396
int bNeedRepack = 0; /* True if we should run repack at the end */
1388
- int fManifest; /* Current "manifest" setting */
13891397
int bIfExists; /* The --if-mirrored flag */
13901398
FILE *xCmd; /* Pipe to the "git fast-import" command */
13911399
FILE *pMarks; /* Git mark files */
13921400
Stmt q; /* Queries */
13931401
char zLine[200]; /* One line of a mark file */
@@ -1521,13 +1529,10 @@
15211529
gitmirror_message(VERB_NORMAL, "no changes\n");
15221530
db_commit_transaction();
15231531
return;
15241532
}
15251533
1526
- /* Do we need to include manifest files in the clone? */
1527
- fManifest = db_get_manifest_setting();
1528
-
15291534
/* Change to the MIRROR directory so that the Git commands will work */
15301535
rc = file_chdir(zMirror, 0);
15311536
if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
15321537
zMirror);
15331538
@@ -1579,11 +1584,11 @@
15791584
while( nLimit && db_step(&q)==SQLITE_ROW ){
15801585
int rid = db_column_int(&q, 0);
15811586
double rMTime = db_column_double(&q, 1);
15821587
const char *zUuid = db_column_text(&q, 2);
15831588
if( rMTime>rEnd ) rEnd = rMTime;
1584
- rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest);
1589
+ rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit);
15851590
if( rc ) break;
15861591
gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal);
15871592
fflush(stdout);
15881593
}
15891594
db_finalize(&q);
15901595
--- src/export.c
+++ src/export.c
@@ -1074,12 +1074,11 @@
1074 */
1075 static int gitmirror_send_checkin(
1076 FILE *xCmd, /* Write fast-import text on this pipe */
1077 int rid, /* BLOB.RID for the check-in to export */
1078 const char *zUuid, /* BLOB.UUID for the check-in to export */
1079 int *pnLimit, /* Stop when the counter reaches zero */
1080 int fManifest /* MFESTFLG_* values */
1081 ){
1082 Manifest *pMan; /* The check-in to be output */
1083 int i; /* Loop counter */
1084 int iParent; /* Which immediate ancestor is primary. -1 for none */
1085 Stmt q; /* An SQL query */
@@ -1089,10 +1088,12 @@
1089 Blob comment; /* The comment text for the check-in */
1090 int nErr = 0; /* Number of errors */
1091 int bPhantomOk; /* True if phantom files should be ignored */
1092 char buf[24];
1093 char *zEmail; /* Contact info for Git committer field */
 
 
1094
1095 pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
1096 if( pMan==0 ){
1097 /* Must be a phantom. Return without doing anything, and in particular
1098 ** without creating a mark for this check-in. */
@@ -1106,11 +1107,11 @@
1106 char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
1107 if( zPMark==0 ){
1108 int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
1109 pMan->azParent[i]);
1110 int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
1111 pnLimit, fManifest);
1112 if( rc || *pnLimit<=0 ){
1113 manifest_destroy(pMan);
1114 return 1;
1115 }
1116 }
@@ -1215,10 +1216,11 @@
1215 blob_reset(&comment);
1216 iParent = -1; /* Which ancestor is the primary parent */
1217 for(i=0; i<pMan->nParent; i++){
1218 char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
1219 if( zOther==0 ) continue;
 
1220 if( iParent<0 ){
1221 iParent = i;
1222 fprintf(xCmd, "from %s\n", zOther);
1223 }else{
1224 fprintf(xCmd, "merge %s\n", zOther);
@@ -1271,29 +1273,36 @@
1271 db_finalize(&q);
1272 manifest_destroy(pMan);
1273 pMan = 0;
1274
1275 /* Include Fossil-generated auxiliary files in the check-in */
 
1276 if( fManifest & MFESTFLG_RAW ){
1277 Blob manifest;
1278 content_get(rid, &manifest);
1279 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
1280 fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
1281 blob_strlen(&manifest), blob_str(&manifest));
1282 blob_reset(&manifest);
 
 
1283 }
1284 if( fManifest & MFESTFLG_UUID ){
1285 int n = (int)strlen(zUuid);
1286 fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid);
 
 
1287 }
1288 if( fManifest & MFESTFLG_TAGS ){
1289 Blob tagslist;
1290 blob_init(&tagslist, 0, 0);
1291 get_checkin_taglist(rid, &tagslist);
1292 fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
1293 blob_strlen(&tagslist), blob_str(&tagslist));
1294 blob_reset(&tagslist);
 
 
1295 }
1296
1297 /* The check-in is finished, so decrement the counter */
1298 (*pnLimit)--;
1299 return 0;
@@ -1383,11 +1392,10 @@
1383 char *zPushUrl; /* URL to sync the mirror to */
1384 double rEnd; /* time of most recent export */
1385 int rc; /* Result code */
1386 int bForce; /* Do the export and sync even if no changes*/
1387 int bNeedRepack = 0; /* True if we should run repack at the end */
1388 int fManifest; /* Current "manifest" setting */
1389 int bIfExists; /* The --if-mirrored flag */
1390 FILE *xCmd; /* Pipe to the "git fast-import" command */
1391 FILE *pMarks; /* Git mark files */
1392 Stmt q; /* Queries */
1393 char zLine[200]; /* One line of a mark file */
@@ -1521,13 +1529,10 @@
1521 gitmirror_message(VERB_NORMAL, "no changes\n");
1522 db_commit_transaction();
1523 return;
1524 }
1525
1526 /* Do we need to include manifest files in the clone? */
1527 fManifest = db_get_manifest_setting();
1528
1529 /* Change to the MIRROR directory so that the Git commands will work */
1530 rc = file_chdir(zMirror, 0);
1531 if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
1532 zMirror);
1533
@@ -1579,11 +1584,11 @@
1579 while( nLimit && db_step(&q)==SQLITE_ROW ){
1580 int rid = db_column_int(&q, 0);
1581 double rMTime = db_column_double(&q, 1);
1582 const char *zUuid = db_column_text(&q, 2);
1583 if( rMTime>rEnd ) rEnd = rMTime;
1584 rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest);
1585 if( rc ) break;
1586 gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal);
1587 fflush(stdout);
1588 }
1589 db_finalize(&q);
1590
--- src/export.c
+++ src/export.c
@@ -1074,12 +1074,11 @@
1074 */
1075 static int gitmirror_send_checkin(
1076 FILE *xCmd, /* Write fast-import text on this pipe */
1077 int rid, /* BLOB.RID for the check-in to export */
1078 const char *zUuid, /* BLOB.UUID for the check-in to export */
1079 int *pnLimit /* Stop when the counter reaches zero */
 
1080 ){
1081 Manifest *pMan; /* The check-in to be output */
1082 int i; /* Loop counter */
1083 int iParent; /* Which immediate ancestor is primary. -1 for none */
1084 Stmt q; /* An SQL query */
@@ -1089,10 +1088,12 @@
1088 Blob comment; /* The comment text for the check-in */
1089 int nErr = 0; /* Number of errors */
1090 int bPhantomOk; /* True if phantom files should be ignored */
1091 char buf[24];
1092 char *zEmail; /* Contact info for Git committer field */
1093 int fManifest; /* Should the manifest files be included? */
1094 int fPManifest = 0; /* OR of the manifest files for all parents */
1095
1096 pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
1097 if( pMan==0 ){
1098 /* Must be a phantom. Return without doing anything, and in particular
1099 ** without creating a mark for this check-in. */
@@ -1106,11 +1107,11 @@
1107 char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
1108 if( zPMark==0 ){
1109 int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
1110 pMan->azParent[i]);
1111 int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
1112 pnLimit);
1113 if( rc || *pnLimit<=0 ){
1114 manifest_destroy(pMan);
1115 return 1;
1116 }
1117 }
@@ -1215,10 +1216,11 @@
1216 blob_reset(&comment);
1217 iParent = -1; /* Which ancestor is the primary parent */
1218 for(i=0; i<pMan->nParent; i++){
1219 char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
1220 if( zOther==0 ) continue;
1221 fPManifest |= db_get_manifest_setting(pMan->azParent[i]);
1222 if( iParent<0 ){
1223 iParent = i;
1224 fprintf(xCmd, "from %s\n", zOther);
1225 }else{
1226 fprintf(xCmd, "merge %s\n", zOther);
@@ -1271,29 +1273,36 @@
1273 db_finalize(&q);
1274 manifest_destroy(pMan);
1275 pMan = 0;
1276
1277 /* Include Fossil-generated auxiliary files in the check-in */
1278 fManifest = db_get_manifest_setting(zUuid);
1279 if( fManifest & MFESTFLG_RAW ){
1280 Blob manifest;
1281 content_get(rid, &manifest);
1282 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
1283 fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
1284 blob_strlen(&manifest), blob_str(&manifest));
1285 blob_reset(&manifest);
1286 }else if( fPManifest & MFESTFLG_RAW ){
1287 fprintf(xCmd, "D manifest\n");
1288 }
1289 if( fManifest & MFESTFLG_UUID ){
1290 int n = (int)strlen(zUuid);
1291 fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid);
1292 }else if( fPManifest & MFESTFLG_UUID ){
1293 fprintf(xCmd, "D manifest.uuid\n");
1294 }
1295 if( fManifest & MFESTFLG_TAGS ){
1296 Blob tagslist;
1297 blob_init(&tagslist, 0, 0);
1298 get_checkin_taglist(rid, &tagslist);
1299 fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
1300 blob_strlen(&tagslist), blob_str(&tagslist));
1301 blob_reset(&tagslist);
1302 }else if( fPManifest & MFESTFLG_TAGS ){
1303 fprintf(xCmd, "D manifest.tags\n");
1304 }
1305
1306 /* The check-in is finished, so decrement the counter */
1307 (*pnLimit)--;
1308 return 0;
@@ -1383,11 +1392,10 @@
1392 char *zPushUrl; /* URL to sync the mirror to */
1393 double rEnd; /* time of most recent export */
1394 int rc; /* Result code */
1395 int bForce; /* Do the export and sync even if no changes*/
1396 int bNeedRepack = 0; /* True if we should run repack at the end */
 
1397 int bIfExists; /* The --if-mirrored flag */
1398 FILE *xCmd; /* Pipe to the "git fast-import" command */
1399 FILE *pMarks; /* Git mark files */
1400 Stmt q; /* Queries */
1401 char zLine[200]; /* One line of a mark file */
@@ -1521,13 +1529,10 @@
1529 gitmirror_message(VERB_NORMAL, "no changes\n");
1530 db_commit_transaction();
1531 return;
1532 }
1533
 
 
 
1534 /* Change to the MIRROR directory so that the Git commands will work */
1535 rc = file_chdir(zMirror, 0);
1536 if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
1537 zMirror);
1538
@@ -1579,11 +1584,11 @@
1584 while( nLimit && db_step(&q)==SQLITE_ROW ){
1585 int rid = db_column_int(&q, 0);
1586 double rMTime = db_column_double(&q, 1);
1587 const char *zUuid = db_column_text(&q, 2);
1588 if( rMTime>rEnd ) rEnd = rMTime;
1589 rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit);
1590 if( rc ) break;
1591 gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal);
1592 fflush(stdout);
1593 }
1594 db_finalize(&q);
1595
+7
--- src/file.c
+++ src/file.c
@@ -1572,10 +1572,17 @@
15721572
**
15731573
** Usage: %fossil test-which ARGS...
15741574
**
15751575
** For each argument, search the PATH for the executable with the name
15761576
** and print its full pathname.
1577
+**
1578
+** See also the "which" command (without the "test-" prefix). The plain
1579
+** "which" command is more convenient to use since it provides the -a/-all
1580
+** option, and because it is shorter. The "fossil which" command without
1581
+** the "test-" prefix is recommended for day-to-day use. This command is
1582
+** retained because it tests the internal file_fullexename() function
1583
+** whereas plain "which" does not.
15771584
*/
15781585
void test_which_cmd(void){
15791586
int i;
15801587
for(i=2; i<g.argc; i++){
15811588
char *z = file_fullexename(g.argv[i]);
15821589
--- src/file.c
+++ src/file.c
@@ -1572,10 +1572,17 @@
1572 **
1573 ** Usage: %fossil test-which ARGS...
1574 **
1575 ** For each argument, search the PATH for the executable with the name
1576 ** and print its full pathname.
 
 
 
 
 
 
 
1577 */
1578 void test_which_cmd(void){
1579 int i;
1580 for(i=2; i<g.argc; i++){
1581 char *z = file_fullexename(g.argv[i]);
1582
--- src/file.c
+++ src/file.c
@@ -1572,10 +1572,17 @@
1572 **
1573 ** Usage: %fossil test-which ARGS...
1574 **
1575 ** For each argument, search the PATH for the executable with the name
1576 ** and print its full pathname.
1577 **
1578 ** See also the "which" command (without the "test-" prefix). The plain
1579 ** "which" command is more convenient to use since it provides the -a/-all
1580 ** option, and because it is shorter. The "fossil which" command without
1581 ** the "test-" prefix is recommended for day-to-day use. This command is
1582 ** retained because it tests the internal file_fullexename() function
1583 ** whereas plain "which" does not.
1584 */
1585 void test_which_cmd(void){
1586 int i;
1587 for(i=2; i<g.argc; i++){
1588 char *z = file_fullexename(g.argv[i]);
1589
+2 -2
--- src/finfo.c
+++ src/finfo.c
@@ -498,16 +498,16 @@
498498
" AND event.objid=mlink.mid\n",
499499
TAG_BRANCH
500500
);
501501
if( (zA = P("a"))!=0 ){
502502
blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
503
- symbolic_name_to_mtime(zA,0));
503
+ symbolic_name_to_mtime(zA,0,0));
504504
url_add_parameter(&url, "a", zA);
505505
}
506506
if( (zB = P("b"))!=0 ){
507507
blob_append_sql(&sql, " AND event.mtime<=%.16g\n",
508
- symbolic_name_to_mtime(zB,0));
508
+ symbolic_name_to_mtime(zB,0,1));
509509
url_add_parameter(&url, "b", zB);
510510
}
511511
if( ridFrom ){
512512
blob_append_sql(&sql,
513513
" AND mlink.mid IN (SELECT rid FROM ancestor)\n"
514514
--- src/finfo.c
+++ src/finfo.c
@@ -498,16 +498,16 @@
498 " AND event.objid=mlink.mid\n",
499 TAG_BRANCH
500 );
501 if( (zA = P("a"))!=0 ){
502 blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
503 symbolic_name_to_mtime(zA,0));
504 url_add_parameter(&url, "a", zA);
505 }
506 if( (zB = P("b"))!=0 ){
507 blob_append_sql(&sql, " AND event.mtime<=%.16g\n",
508 symbolic_name_to_mtime(zB,0));
509 url_add_parameter(&url, "b", zB);
510 }
511 if( ridFrom ){
512 blob_append_sql(&sql,
513 " AND mlink.mid IN (SELECT rid FROM ancestor)\n"
514
--- src/finfo.c
+++ src/finfo.c
@@ -498,16 +498,16 @@
498 " AND event.objid=mlink.mid\n",
499 TAG_BRANCH
500 );
501 if( (zA = P("a"))!=0 ){
502 blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
503 symbolic_name_to_mtime(zA,0,0));
504 url_add_parameter(&url, "a", zA);
505 }
506 if( (zB = P("b"))!=0 ){
507 blob_append_sql(&sql, " AND event.mtime<=%.16g\n",
508 symbolic_name_to_mtime(zB,0,1));
509 url_add_parameter(&url, "b", zB);
510 }
511 if( ridFrom ){
512 blob_append_sql(&sql,
513 " AND mlink.mid IN (SELECT rid FROM ancestor)\n"
514
+1 -1
--- src/graph.c
+++ src/graph.c
@@ -84,11 +84,11 @@
8484
8585
GraphRow *pNext; /* Next row down in the list of all rows */
8686
GraphRow *pPrev; /* Previous row */
8787
8888
int idx; /* Row index. Top row is smallest. */
89
- int idxTop; /* Direct descendent highest up on the graph */
89
+ int idxTop; /* Direct descendant highest up on the graph */
9090
GraphRow *pChild; /* Child immediately above this node */
9191
u8 isDup; /* True if this is duplicate of a prior entry */
9292
u8 isLeaf; /* True if this is a leaf node */
9393
u8 isStepParent; /* pChild is actually a step-child. The thick
9494
** arrow up to the child is dashed, not solid */
9595
--- src/graph.c
+++ src/graph.c
@@ -84,11 +84,11 @@
84
85 GraphRow *pNext; /* Next row down in the list of all rows */
86 GraphRow *pPrev; /* Previous row */
87
88 int idx; /* Row index. Top row is smallest. */
89 int idxTop; /* Direct descendent highest up on the graph */
90 GraphRow *pChild; /* Child immediately above this node */
91 u8 isDup; /* True if this is duplicate of a prior entry */
92 u8 isLeaf; /* True if this is a leaf node */
93 u8 isStepParent; /* pChild is actually a step-child. The thick
94 ** arrow up to the child is dashed, not solid */
95
--- src/graph.c
+++ src/graph.c
@@ -84,11 +84,11 @@
84
85 GraphRow *pNext; /* Next row down in the list of all rows */
86 GraphRow *pPrev; /* Previous row */
87
88 int idx; /* Row index. Top row is smallest. */
89 int idxTop; /* Direct descendant highest up on the graph */
90 GraphRow *pChild; /* Child immediately above this node */
91 u8 isDup; /* True if this is duplicate of a prior entry */
92 u8 isLeaf; /* True if this is a leaf node */
93 u8 isStepParent; /* pChild is actually a step-child. The thick
94 ** arrow up to the child is dashed, not solid */
95
+2 -2
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -910,12 +910,12 @@
910910
if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
911911
}
912912
#endif /* FOSSIL_ENABLE_SSL */
913913
914914
/*
915
-** COMMAND: tls-config* abbreviated-subcommands
916
-** COMMAND: ssl-config abbreviated-subcommands
915
+** COMMAND: tls-config* abbrv-subcom
916
+** COMMAND: ssl-config abbrv-subcom
917917
**
918918
** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
919919
**
920920
** This command is used to view or modify the TLS (Transport Layer
921921
** Security) configuration for Fossil. TLS (formerly SSL) is the
922922
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -910,12 +910,12 @@
910 if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
911 }
912 #endif /* FOSSIL_ENABLE_SSL */
913
914 /*
915 ** COMMAND: tls-config* abbreviated-subcommands
916 ** COMMAND: ssl-config abbreviated-subcommands
917 **
918 ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
919 **
920 ** This command is used to view or modify the TLS (Transport Layer
921 ** Security) configuration for Fossil. TLS (formerly SSL) is the
922
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -910,12 +910,12 @@
910 if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
911 }
912 #endif /* FOSSIL_ENABLE_SSL */
913
914 /*
915 ** COMMAND: tls-config* abbrv-subcom
916 ** COMMAND: ssl-config abbrv-subcom
917 **
918 ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
919 **
920 ** This command is used to view or modify the TLS (Transport Layer
921 ** Security) configuration for Fossil. TLS (formerly SSL) is the
922
+89 -91
--- src/info.c
+++ src/info.c
@@ -257,17 +257,19 @@
257257
}
258258
fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
259259
fossil_print("version: %s", z);
260260
blob_reset(&vx);
261261
}
262
- }else{
262
+ }else if( g.repositoryOpen ){
263263
int rid;
264264
rid = name_to_rid(g.argv[2]);
265265
if( rid==0 ){
266266
fossil_fatal("no such object: %s", g.argv[2]);
267267
}
268268
show_common_info(rid, "hash:", 1, 1);
269
+ }else{
270
+ fossil_fatal("Could not find or open a Fossil repository");
269271
}
270272
}
271273
272274
/*
273275
** Show the context graph (immediate parents and children) for
@@ -925,12 +927,12 @@
925927
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
926928
rid
927929
);
928930
isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
929931
db_prepare(&q1,
930
- "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
931
- " datetime(omtime,toLocal()), mtime"
932
+ "SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment,"
933
+ " datetime(omtime,toLocal(),'subsec'), mtime"
932934
" FROM blob, event"
933935
" WHERE blob.rid=%d"
934936
" AND event.objid=%d",
935937
rid, rid
936938
);
@@ -1366,72 +1368,10 @@
13661368
webpage_error("Artifact %s is not a check-in.", P(zParam));
13671369
return 0;
13681370
}
13691371
return manifest_get(rid, CFTYPE_MANIFEST, 0);
13701372
}
1371
-
1372
-#if 0 /* not used */
1373
-/*
1374
-** Output a description of a check-in
1375
-*/
1376
-static void checkin_description(int rid){
1377
- Stmt q;
1378
- db_prepare(&q,
1379
- "SELECT datetime(mtime), coalesce(euser,user),"
1380
- " coalesce(ecomment,comment), uuid,"
1381
- " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
1382
- " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
1383
- " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
1384
- " FROM event, blob"
1385
- " WHERE event.objid=%d AND type='ci'"
1386
- " AND blob.rid=%d",
1387
- rid, rid
1388
- );
1389
- while( db_step(&q)==SQLITE_ROW ){
1390
- const char *zDate = db_column_text(&q, 0);
1391
- const char *zUser = db_column_text(&q, 1);
1392
- const char *zUuid = db_column_text(&q, 3);
1393
- const char *zTagList = db_column_text(&q, 4);
1394
- Blob comment;
1395
- int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
1396
- if( db_get_boolean("timeline-block-markup", 0)==0 ){
1397
- wikiFlags |= WIKI_NOBLOCK;
1398
- }
1399
- hyperlink_to_version(zUuid);
1400
- blob_zero(&comment);
1401
- db_column_blob(&q, 2, &comment);
1402
- wiki_convert(&comment, 0, wikiFlags);
1403
- blob_reset(&comment);
1404
- @ (user:
1405
- hyperlink_to_user(zUser,zDate,",");
1406
- if( zTagList && zTagList[0] && g.perm.Hyperlink ){
1407
- int i;
1408
- const char *z = zTagList;
1409
- Blob links;
1410
- blob_zero(&links);
1411
- while( z && z[0] ){
1412
- for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
1413
- blob_appendf(&links,
1414
- "%z%#h</a>%.2s",
1415
- href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i]
1416
- );
1417
- if( z[i]==0 ) break;
1418
- z += i+2;
1419
- }
1420
- @ tags: %s(blob_str(&links)),
1421
- blob_reset(&links);
1422
- }else{
1423
- @ tags: %h(zTagList),
1424
- }
1425
- @ date:
1426
- hyperlink_to_date(zDate, ")");
1427
- tag_private_status(rid);
1428
- }
1429
- db_finalize(&q);
1430
-}
1431
-#endif /* not used */
1432
-
14331373
14341374
/*
14351375
** WEBPAGE: vdiff
14361376
** URL: /vdiff?from=TAG&to=TAG
14371377
**
@@ -3941,25 +3881,26 @@
39413881
** Usage: %fossil amend HASH OPTION ?OPTION ...?
39423882
**
39433883
** Amend the tags on check-in HASH to change how it displays in the timeline.
39443884
**
39453885
** Options:
3946
-** --author USER Make USER the author for check-in
3947
-** -m|--comment COMMENT Make COMMENT the check-in comment
3948
-** -M|--message-file FILE Read the amended comment from FILE
3949
-** -e|--edit-comment Launch editor to revise comment
3950
-** --date DATETIME Make DATETIME the check-in time
3951
-** --bgcolor COLOR Apply COLOR to this check-in
3952
-** --branchcolor COLOR Apply and propagate COLOR to the branch
3953
-** --tag TAG Add new TAG to this check-in
3954
-** --cancel TAG Cancel TAG from this check-in
3955
-** --branch NAME Rename branch of check-in to NAME
3956
-** --hide Hide branch starting from this check-in
3957
-** --close Mark this "leaf" as closed
3958
-** -n|--dry-run Print control artifact, but make no changes
3959
-** --date-override DATETIME Set the change time on the control artifact
3960
-** --user-override USER Set the user name on the control artifact
3886
+** --author USER Make USER the author for check-in
3887
+** --bgcolor COLOR Apply COLOR to this check-in
3888
+** --branch NAME Rename branch of check-in to NAME
3889
+** --branchcolor COLOR Apply and propagate COLOR to the branch
3890
+** --cancel TAG Cancel TAG from this check-in
3891
+** --close Mark this "leaf" as closed
3892
+** --date DATETIME Make DATETIME the check-in time
3893
+** --date-override DATETIME Set the change time on the control artifact
3894
+** -e|--edit-comment Launch editor to revise comment
3895
+** --hide Hide branch starting from this check-in
3896
+** -m|--comment COMMENT Make COMMENT the check-in comment
3897
+** -M|--message-file FILE Read the amended comment from FILE
3898
+** -n|--dry-run Print control artifact, but make no changes
3899
+** --no-verify-comment Do not validate the check-in comment
3900
+** --tag TAG Add new TAG to this check-in
3901
+** --user-override USER Set the user name on the control artifact
39613902
**
39623903
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
39633904
** year-month-day form, it may be truncated, the "T" may be replaced by
39643905
** a space, and it may also name a timezone offset from UTC as "-HH:MM"
39653906
** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
@@ -3986,19 +3927,22 @@
39863927
int fNewPropagateColor = 0; /* True if color propagates after amend */
39873928
int fHasHidden = 0; /* True if hidden tag already set */
39883929
int fHasClosed = 0; /* True if closed tag already set */
39893930
int fEditComment; /* True if editor to be used for comment */
39903931
int fDryRun; /* Print control artifact, make no changes */
3932
+ int noVerifyCom = 0; /* Allow suspicious check-in comments */
39913933
const char *zChngTime; /* The change time on the control artifact */
39923934
const char *zUserOvrd; /* The user name on the control artifact */
39933935
const char *zUuid;
39943936
Blob ctrl;
39953937
Blob comment;
39963938
char *zNow;
39973939
int nTags, nCancels;
39983940
int i;
39993941
Stmt q;
3942
+ int ckComFlgs; /* Flags passed to suspicious_comment() */
3943
+
40003944
40013945
fEditComment = find_option("edit-comment","e",0)!=0;
40023946
zNewComment = find_option("comment","m",1);
40033947
zComFile = find_option("message-file","M",1);
40043948
zNewBranch = find_option("branch",0,1);
@@ -4016,10 +3960,11 @@
40163960
fHide = find_option("hide",0,0)!=0;
40173961
fDryRun = find_option("dry-run","n",0)!=0;
40183962
zChngTime = find_option("date-override",0,1);
40193963
if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
40203964
zUserOvrd = find_option("user-override",0,1);
3965
+ noVerifyCom = find_option("no-verify-comment",0,0)!=0;
40213966
db_find_and_open_repository(0,0);
40223967
user_select();
40233968
verify_all_options();
40243969
if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
40253970
rid = name_to_typed_rid(g.argv[2], "ci");
@@ -4074,21 +4019,74 @@
40744019
);
40754020
}
40764021
if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
40774022
cancel_color();
40784023
}
4079
- if( fEditComment ){
4080
- prepare_amend_comment(&comment, zComment, zUuid);
4081
- zNewComment = blob_str(&comment);
4082
- }else if( zComFile ){
4083
- blob_zero(&comment);
4084
- blob_read_from_file(&comment, zComFile, ExtFILE);
4085
- blob_to_utf8_no_bom(&comment, 1);
4086
- zNewComment = blob_str(&comment);
4087
- }
4088
- if( zNewComment && zNewComment[0]
4089
- && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
4024
+ if( fEditComment || zNewComment || zComFile ){
4025
+ blob_init(&comment, 0, 0);
4026
+
4027
+ /* Figure out how much comment verification is requested */
4028
+ if( noVerifyCom ){
4029
+ ckComFlgs = 0;
4030
+ }else{
4031
+ const char *zVerComs = db_get("verify-comments","on");
4032
+ if( is_false(zVerComs) ){
4033
+ ckComFlgs = 0;
4034
+ }else if( strcmp(zVerComs,"preview")==0 ){
4035
+ ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
4036
+ }else if( strcmp(zVerComs,"links")==0 ){
4037
+ ckComFlgs = COMCK_LINKS;
4038
+ }else{
4039
+ ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
4040
+ }
4041
+ if( zNewComment || zComFile ){
4042
+ ckComFlgs = (ckComFlgs & COMCK_LINKS) | COMCK_NOPREVIEW;
4043
+ }
4044
+ }
4045
+ if( fEditComment ){
4046
+ prepare_amend_comment(&comment, zComment, zUuid);
4047
+ }else if( zComFile ){
4048
+ blob_read_from_file(&comment, zComFile, ExtFILE);
4049
+ blob_to_utf8_no_bom(&comment, 1);
4050
+ }else if( zNewComment ){
4051
+ blob_init(&comment, zNewComment, -1);
4052
+ }
4053
+ if( blob_size(&comment)>0
4054
+ && comment_compare(zComment, blob_str(&comment))==0
4055
+ ){
4056
+ int rc;
4057
+ while( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
4058
+ char cReply;
4059
+ Blob ans;
4060
+ if( !fEditComment ){
4061
+ fossil_fatal("Amend aborted; "
4062
+ "use --no-verify-comment to override");
4063
+ }
4064
+ if( rc==COMCK_PREVIEW ){
4065
+ prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
4066
+ }else{
4067
+ prompt_user("\nContinue (y/n/E=edit)? ", &ans);
4068
+ }
4069
+ cReply = blob_str(&ans)[0];
4070
+ cReply = fossil_tolower(cReply);
4071
+ blob_reset(&ans);
4072
+ if( cReply=='n' ){
4073
+ fossil_fatal("Amend aborted.");
4074
+ }
4075
+ if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
4076
+ char *zPrior = blob_materialize(&comment);
4077
+ blob_init(&comment, 0, 0);
4078
+ prepare_amend_comment(&comment, zPrior, zUuid);
4079
+ fossil_free(zPrior);
4080
+ continue;
4081
+ }else{
4082
+ break;
4083
+ }
4084
+ }
4085
+ }
4086
+ add_comment(blob_str(&comment));
4087
+ }
40904088
if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
40914089
if( is_datetime(zNewDate) ){
40924090
add_date(zNewDate);
40934091
}else{
40944092
fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
40954093
--- src/info.c
+++ src/info.c
@@ -257,17 +257,19 @@
257 }
258 fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
259 fossil_print("version: %s", z);
260 blob_reset(&vx);
261 }
262 }else{
263 int rid;
264 rid = name_to_rid(g.argv[2]);
265 if( rid==0 ){
266 fossil_fatal("no such object: %s", g.argv[2]);
267 }
268 show_common_info(rid, "hash:", 1, 1);
 
 
269 }
270 }
271
272 /*
273 ** Show the context graph (immediate parents and children) for
@@ -925,12 +927,12 @@
925 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
926 rid
927 );
928 isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
929 db_prepare(&q1,
930 "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
931 " datetime(omtime,toLocal()), mtime"
932 " FROM blob, event"
933 " WHERE blob.rid=%d"
934 " AND event.objid=%d",
935 rid, rid
936 );
@@ -1366,72 +1368,10 @@
1366 webpage_error("Artifact %s is not a check-in.", P(zParam));
1367 return 0;
1368 }
1369 return manifest_get(rid, CFTYPE_MANIFEST, 0);
1370 }
1371
1372 #if 0 /* not used */
1373 /*
1374 ** Output a description of a check-in
1375 */
1376 static void checkin_description(int rid){
1377 Stmt q;
1378 db_prepare(&q,
1379 "SELECT datetime(mtime), coalesce(euser,user),"
1380 " coalesce(ecomment,comment), uuid,"
1381 " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
1382 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
1383 " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
1384 " FROM event, blob"
1385 " WHERE event.objid=%d AND type='ci'"
1386 " AND blob.rid=%d",
1387 rid, rid
1388 );
1389 while( db_step(&q)==SQLITE_ROW ){
1390 const char *zDate = db_column_text(&q, 0);
1391 const char *zUser = db_column_text(&q, 1);
1392 const char *zUuid = db_column_text(&q, 3);
1393 const char *zTagList = db_column_text(&q, 4);
1394 Blob comment;
1395 int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
1396 if( db_get_boolean("timeline-block-markup", 0)==0 ){
1397 wikiFlags |= WIKI_NOBLOCK;
1398 }
1399 hyperlink_to_version(zUuid);
1400 blob_zero(&comment);
1401 db_column_blob(&q, 2, &comment);
1402 wiki_convert(&comment, 0, wikiFlags);
1403 blob_reset(&comment);
1404 @ (user:
1405 hyperlink_to_user(zUser,zDate,",");
1406 if( zTagList && zTagList[0] && g.perm.Hyperlink ){
1407 int i;
1408 const char *z = zTagList;
1409 Blob links;
1410 blob_zero(&links);
1411 while( z && z[0] ){
1412 for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
1413 blob_appendf(&links,
1414 "%z%#h</a>%.2s",
1415 href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i]
1416 );
1417 if( z[i]==0 ) break;
1418 z += i+2;
1419 }
1420 @ tags: %s(blob_str(&links)),
1421 blob_reset(&links);
1422 }else{
1423 @ tags: %h(zTagList),
1424 }
1425 @ date:
1426 hyperlink_to_date(zDate, ")");
1427 tag_private_status(rid);
1428 }
1429 db_finalize(&q);
1430 }
1431 #endif /* not used */
1432
1433
1434 /*
1435 ** WEBPAGE: vdiff
1436 ** URL: /vdiff?from=TAG&to=TAG
1437 **
@@ -3941,25 +3881,26 @@
3941 ** Usage: %fossil amend HASH OPTION ?OPTION ...?
3942 **
3943 ** Amend the tags on check-in HASH to change how it displays in the timeline.
3944 **
3945 ** Options:
3946 ** --author USER Make USER the author for check-in
3947 ** -m|--comment COMMENT Make COMMENT the check-in comment
3948 ** -M|--message-file FILE Read the amended comment from FILE
3949 ** -e|--edit-comment Launch editor to revise comment
3950 ** --date DATETIME Make DATETIME the check-in time
3951 ** --bgcolor COLOR Apply COLOR to this check-in
3952 ** --branchcolor COLOR Apply and propagate COLOR to the branch
3953 ** --tag TAG Add new TAG to this check-in
3954 ** --cancel TAG Cancel TAG from this check-in
3955 ** --branch NAME Rename branch of check-in to NAME
3956 ** --hide Hide branch starting from this check-in
3957 ** --close Mark this "leaf" as closed
3958 ** -n|--dry-run Print control artifact, but make no changes
3959 ** --date-override DATETIME Set the change time on the control artifact
3960 ** --user-override USER Set the user name on the control artifact
 
3961 **
3962 ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
3963 ** year-month-day form, it may be truncated, the "T" may be replaced by
3964 ** a space, and it may also name a timezone offset from UTC as "-HH:MM"
3965 ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
@@ -3986,19 +3927,22 @@
3986 int fNewPropagateColor = 0; /* True if color propagates after amend */
3987 int fHasHidden = 0; /* True if hidden tag already set */
3988 int fHasClosed = 0; /* True if closed tag already set */
3989 int fEditComment; /* True if editor to be used for comment */
3990 int fDryRun; /* Print control artifact, make no changes */
 
3991 const char *zChngTime; /* The change time on the control artifact */
3992 const char *zUserOvrd; /* The user name on the control artifact */
3993 const char *zUuid;
3994 Blob ctrl;
3995 Blob comment;
3996 char *zNow;
3997 int nTags, nCancels;
3998 int i;
3999 Stmt q;
 
 
4000
4001 fEditComment = find_option("edit-comment","e",0)!=0;
4002 zNewComment = find_option("comment","m",1);
4003 zComFile = find_option("message-file","M",1);
4004 zNewBranch = find_option("branch",0,1);
@@ -4016,10 +3960,11 @@
4016 fHide = find_option("hide",0,0)!=0;
4017 fDryRun = find_option("dry-run","n",0)!=0;
4018 zChngTime = find_option("date-override",0,1);
4019 if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
4020 zUserOvrd = find_option("user-override",0,1);
 
4021 db_find_and_open_repository(0,0);
4022 user_select();
4023 verify_all_options();
4024 if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
4025 rid = name_to_typed_rid(g.argv[2], "ci");
@@ -4074,21 +4019,74 @@
4074 );
4075 }
4076 if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
4077 cancel_color();
4078 }
4079 if( fEditComment ){
4080 prepare_amend_comment(&comment, zComment, zUuid);
4081 zNewComment = blob_str(&comment);
4082 }else if( zComFile ){
4083 blob_zero(&comment);
4084 blob_read_from_file(&comment, zComFile, ExtFILE);
4085 blob_to_utf8_no_bom(&comment, 1);
4086 zNewComment = blob_str(&comment);
4087 }
4088 if( zNewComment && zNewComment[0]
4089 && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4090 if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
4091 if( is_datetime(zNewDate) ){
4092 add_date(zNewDate);
4093 }else{
4094 fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
4095
--- src/info.c
+++ src/info.c
@@ -257,17 +257,19 @@
257 }
258 fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
259 fossil_print("version: %s", z);
260 blob_reset(&vx);
261 }
262 }else if( g.repositoryOpen ){
263 int rid;
264 rid = name_to_rid(g.argv[2]);
265 if( rid==0 ){
266 fossil_fatal("no such object: %s", g.argv[2]);
267 }
268 show_common_info(rid, "hash:", 1, 1);
269 }else{
270 fossil_fatal("Could not find or open a Fossil repository");
271 }
272 }
273
274 /*
275 ** Show the context graph (immediate parents and children) for
@@ -925,12 +927,12 @@
927 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
928 rid
929 );
930 isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
931 db_prepare(&q1,
932 "SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment,"
933 " datetime(omtime,toLocal(),'subsec'), mtime"
934 " FROM blob, event"
935 " WHERE blob.rid=%d"
936 " AND event.objid=%d",
937 rid, rid
938 );
@@ -1366,72 +1368,10 @@
1368 webpage_error("Artifact %s is not a check-in.", P(zParam));
1369 return 0;
1370 }
1371 return manifest_get(rid, CFTYPE_MANIFEST, 0);
1372 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1373
1374 /*
1375 ** WEBPAGE: vdiff
1376 ** URL: /vdiff?from=TAG&to=TAG
1377 **
@@ -3941,25 +3881,26 @@
3881 ** Usage: %fossil amend HASH OPTION ?OPTION ...?
3882 **
3883 ** Amend the tags on check-in HASH to change how it displays in the timeline.
3884 **
3885 ** Options:
3886 ** --author USER Make USER the author for check-in
3887 ** --bgcolor COLOR Apply COLOR to this check-in
3888 ** --branch NAME Rename branch of check-in to NAME
3889 ** --branchcolor COLOR Apply and propagate COLOR to the branch
3890 ** --cancel TAG Cancel TAG from this check-in
3891 ** --close Mark this "leaf" as closed
3892 ** --date DATETIME Make DATETIME the check-in time
3893 ** --date-override DATETIME Set the change time on the control artifact
3894 ** -e|--edit-comment Launch editor to revise comment
3895 ** --hide Hide branch starting from this check-in
3896 ** -m|--comment COMMENT Make COMMENT the check-in comment
3897 ** -M|--message-file FILE Read the amended comment from FILE
3898 ** -n|--dry-run Print control artifact, but make no changes
3899 ** --no-verify-comment Do not validate the check-in comment
3900 ** --tag TAG Add new TAG to this check-in
3901 ** --user-override USER Set the user name on the control artifact
3902 **
3903 ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
3904 ** year-month-day form, it may be truncated, the "T" may be replaced by
3905 ** a space, and it may also name a timezone offset from UTC as "-HH:MM"
3906 ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
@@ -3986,19 +3927,22 @@
3927 int fNewPropagateColor = 0; /* True if color propagates after amend */
3928 int fHasHidden = 0; /* True if hidden tag already set */
3929 int fHasClosed = 0; /* True if closed tag already set */
3930 int fEditComment; /* True if editor to be used for comment */
3931 int fDryRun; /* Print control artifact, make no changes */
3932 int noVerifyCom = 0; /* Allow suspicious check-in comments */
3933 const char *zChngTime; /* The change time on the control artifact */
3934 const char *zUserOvrd; /* The user name on the control artifact */
3935 const char *zUuid;
3936 Blob ctrl;
3937 Blob comment;
3938 char *zNow;
3939 int nTags, nCancels;
3940 int i;
3941 Stmt q;
3942 int ckComFlgs; /* Flags passed to suspicious_comment() */
3943
3944
3945 fEditComment = find_option("edit-comment","e",0)!=0;
3946 zNewComment = find_option("comment","m",1);
3947 zComFile = find_option("message-file","M",1);
3948 zNewBranch = find_option("branch",0,1);
@@ -4016,10 +3960,11 @@
3960 fHide = find_option("hide",0,0)!=0;
3961 fDryRun = find_option("dry-run","n",0)!=0;
3962 zChngTime = find_option("date-override",0,1);
3963 if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
3964 zUserOvrd = find_option("user-override",0,1);
3965 noVerifyCom = find_option("no-verify-comment",0,0)!=0;
3966 db_find_and_open_repository(0,0);
3967 user_select();
3968 verify_all_options();
3969 if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
3970 rid = name_to_typed_rid(g.argv[2], "ci");
@@ -4074,21 +4019,74 @@
4019 );
4020 }
4021 if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
4022 cancel_color();
4023 }
4024 if( fEditComment || zNewComment || zComFile ){
4025 blob_init(&comment, 0, 0);
4026
4027 /* Figure out how much comment verification is requested */
4028 if( noVerifyCom ){
4029 ckComFlgs = 0;
4030 }else{
4031 const char *zVerComs = db_get("verify-comments","on");
4032 if( is_false(zVerComs) ){
4033 ckComFlgs = 0;
4034 }else if( strcmp(zVerComs,"preview")==0 ){
4035 ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
4036 }else if( strcmp(zVerComs,"links")==0 ){
4037 ckComFlgs = COMCK_LINKS;
4038 }else{
4039 ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
4040 }
4041 if( zNewComment || zComFile ){
4042 ckComFlgs = (ckComFlgs & COMCK_LINKS) | COMCK_NOPREVIEW;
4043 }
4044 }
4045 if( fEditComment ){
4046 prepare_amend_comment(&comment, zComment, zUuid);
4047 }else if( zComFile ){
4048 blob_read_from_file(&comment, zComFile, ExtFILE);
4049 blob_to_utf8_no_bom(&comment, 1);
4050 }else if( zNewComment ){
4051 blob_init(&comment, zNewComment, -1);
4052 }
4053 if( blob_size(&comment)>0
4054 && comment_compare(zComment, blob_str(&comment))==0
4055 ){
4056 int rc;
4057 while( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
4058 char cReply;
4059 Blob ans;
4060 if( !fEditComment ){
4061 fossil_fatal("Amend aborted; "
4062 "use --no-verify-comment to override");
4063 }
4064 if( rc==COMCK_PREVIEW ){
4065 prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
4066 }else{
4067 prompt_user("\nContinue (y/n/E=edit)? ", &ans);
4068 }
4069 cReply = blob_str(&ans)[0];
4070 cReply = fossil_tolower(cReply);
4071 blob_reset(&ans);
4072 if( cReply=='n' ){
4073 fossil_fatal("Amend aborted.");
4074 }
4075 if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
4076 char *zPrior = blob_materialize(&comment);
4077 blob_init(&comment, 0, 0);
4078 prepare_amend_comment(&comment, zPrior, zUuid);
4079 fossil_free(zPrior);
4080 continue;
4081 }else{
4082 break;
4083 }
4084 }
4085 }
4086 add_comment(blob_str(&comment));
4087 }
4088 if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
4089 if( is_datetime(zNewDate) ){
4090 add_date(zNewDate);
4091 }else{
4092 fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
4093
+17 -6
--- src/main.c
+++ src/main.c
@@ -998,15 +998,12 @@
998998
999999
/*
10001000
** Remove n elements from g.argv beginning with the i-th element.
10011001
*/
10021002
static void remove_from_argv(int i, int n){
1003
- int j;
1004
- for(j=i+n; j<g.argc; i++, j++){
1005
- g.argv[i] = g.argv[j];
1006
- }
1007
- g.argc = i;
1003
+ memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n));
1004
+ g.argc -= n;
10081005
}
10091006
10101007
10111008
/*
10121009
** Look for a command-line option. If present, remove it from the
@@ -1065,10 +1062,19 @@
10651062
break;
10661063
}
10671064
}
10681065
return zReturn;
10691066
}
1067
+
1068
+/*
1069
+** Restore an option previously removed by find_option().
1070
+*/
1071
+void restore_option(const char *zName, const char *zValue, int hasOpt){
1072
+ if( zValue==0 && hasOpt ) return;
1073
+ g.argv[g.argc++] = (char*)zName;
1074
+ if( hasOpt ) g.argv[g.argc++] = (char*)zValue;
1075
+}
10701076
10711077
/* Return true if zOption exists in the command-line arguments,
10721078
** but do not remove it from the list or otherwise process it.
10731079
*/
10741080
int has_option(const char *zOption){
@@ -1849,10 +1855,15 @@
18491855
** not exist.
18501856
*/
18511857
zCleanRepo = file_cleanup_fullpath(zRepo);
18521858
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
18531859
szFile = file_size(zCleanRepo, ExtFILE);
1860
+ if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){
1861
+ /* Only let szFile be non-negative if zCleanRepo really is a file
1862
+ ** and not a directory or some other filesystem object. */
1863
+ szFile = -1;
1864
+ }
18541865
if( g.fHttpTrace ){
18551866
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
18561867
@ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
18571868
fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
18581869
}
@@ -2156,11 +2167,11 @@
21562167
}
21572168
}
21582169
#endif
21592170
if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
21602171
cgi_decode_post_parameters();
2161
- if( !cgi_same_origin() ){
2172
+ if( !cgi_same_origin(0) ){
21622173
isReadonly = 1;
21632174
db_protect(PROTECT_READONLY);
21642175
}
21652176
}
21662177
if( g.fCgiTrace ){
21672178
--- src/main.c
+++ src/main.c
@@ -998,15 +998,12 @@
998
999 /*
1000 ** Remove n elements from g.argv beginning with the i-th element.
1001 */
1002 static void remove_from_argv(int i, int n){
1003 int j;
1004 for(j=i+n; j<g.argc; i++, j++){
1005 g.argv[i] = g.argv[j];
1006 }
1007 g.argc = i;
1008 }
1009
1010
1011 /*
1012 ** Look for a command-line option. If present, remove it from the
@@ -1065,10 +1062,19 @@
1065 break;
1066 }
1067 }
1068 return zReturn;
1069 }
 
 
 
 
 
 
 
 
 
1070
1071 /* Return true if zOption exists in the command-line arguments,
1072 ** but do not remove it from the list or otherwise process it.
1073 */
1074 int has_option(const char *zOption){
@@ -1849,10 +1855,15 @@
1849 ** not exist.
1850 */
1851 zCleanRepo = file_cleanup_fullpath(zRepo);
1852 if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
1853 szFile = file_size(zCleanRepo, ExtFILE);
 
 
 
 
 
1854 if( g.fHttpTrace ){
1855 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
1856 @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
1857 fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
1858 }
@@ -2156,11 +2167,11 @@
2156 }
2157 }
2158 #endif
2159 if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
2160 cgi_decode_post_parameters();
2161 if( !cgi_same_origin() ){
2162 isReadonly = 1;
2163 db_protect(PROTECT_READONLY);
2164 }
2165 }
2166 if( g.fCgiTrace ){
2167
--- src/main.c
+++ src/main.c
@@ -998,15 +998,12 @@
998
999 /*
1000 ** Remove n elements from g.argv beginning with the i-th element.
1001 */
1002 static void remove_from_argv(int i, int n){
1003 memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n));
1004 g.argc -= n;
 
 
 
1005 }
1006
1007
1008 /*
1009 ** Look for a command-line option. If present, remove it from the
@@ -1065,10 +1062,19 @@
1062 break;
1063 }
1064 }
1065 return zReturn;
1066 }
1067
1068 /*
1069 ** Restore an option previously removed by find_option().
1070 */
1071 void restore_option(const char *zName, const char *zValue, int hasOpt){
1072 if( zValue==0 && hasOpt ) return;
1073 g.argv[g.argc++] = (char*)zName;
1074 if( hasOpt ) g.argv[g.argc++] = (char*)zValue;
1075 }
1076
1077 /* Return true if zOption exists in the command-line arguments,
1078 ** but do not remove it from the list or otherwise process it.
1079 */
1080 int has_option(const char *zOption){
@@ -1849,10 +1855,15 @@
1855 ** not exist.
1856 */
1857 zCleanRepo = file_cleanup_fullpath(zRepo);
1858 if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
1859 szFile = file_size(zCleanRepo, ExtFILE);
1860 if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){
1861 /* Only let szFile be non-negative if zCleanRepo really is a file
1862 ** and not a directory or some other filesystem object. */
1863 szFile = -1;
1864 }
1865 if( g.fHttpTrace ){
1866 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
1867 @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
1868 fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
1869 }
@@ -2156,11 +2167,11 @@
2167 }
2168 }
2169 #endif
2170 if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
2171 cgi_decode_post_parameters();
2172 if( !cgi_same_origin(0) ){
2173 isReadonly = 1;
2174 db_protect(PROTECT_READONLY);
2175 }
2176 }
2177 if( g.fCgiTrace ){
2178
+51 -17
--- src/name.c
+++ src/name.c
@@ -58,29 +58,42 @@
5858
** Check to see if the string might be a compact date/time that omits
5959
** the punctuation. Example: "20190327084549" instead of
6060
** "2019-03-27 08:45:49". If the string is of the appropriate form,
6161
** then return an alternative string (in static space) that is the same
6262
** string with punctuation inserted.
63
+**
64
+** If the bRoundUp parameter is true, then round the resulting date-time
65
+** up to the largest date/time that is consistent with the input value.
66
+** This is because the result will be used for an mtime<=julianday($DATE)
67
+** comparison. In other words:
68
+**
69
+** 20250317123421 -> 2025-03-17 12:34:21.999
70
+** ^^^^--- Added
71
+**
72
+** 202503171234 -> 2025-03-17 12:34:59.999
73
+** ^^^^^^^--- Added
74
+** 20250317 -> 2025-03-17 23:59:59.999
75
+** ^^^^^^^^^^^^--- Added
6376
**
6477
** If the bVerifyNotAHash flag is true, then a check is made to see if
65
-** the string is a hash prefix and NULL is returned if it is. If the
78
+** the input string is a hash prefix and NULL is returned if it is. If the
6679
** bVerifyNotAHash flag is false, then the result is determined by syntax
6780
** of the input string only, without reference to the artifact table.
6881
*/
69
-char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
82
+char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){
7083
static char zEDate[24];
7184
static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
7285
int n = (int)strlen(zIn);
7386
int i, j;
7487
int addZulu = 0;
7588
7689
/* These forms are allowed:
7790
**
78
- ** 123456789 1234 123456789 123456789
79
- ** (1) YYYYMMDD => YYYY-MM-DD
80
- ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM
81
- ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS
91
+ ** 123456789 1234 123456789 123456789 1234
92
+ ** (1) YYYYMMDD => YYYY-MM-DD 23:59:59.999
93
+ ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM:59.999
94
+ ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS.999
8295
**
8396
** An optional "Z" zulu timezone designator is allowed at the end.
8497
*/
8598
if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
8699
n--;
@@ -99,15 +112,23 @@
99112
if( i>=4 && (i%2)==0 ){
100113
zEDate[j++] = aPunct[i/2];
101114
}
102115
zEDate[j++] = zIn[i];
103116
}
104
- if( addZulu ){
117
+ if( bRoundUp ){
105118
if( j==10 ){
106
- memcpy(&zEDate[10]," 00:00", 6);
107
- j += 6;
119
+ memcpy(&zEDate[10], " 23:59:59.999", 13);
120
+ j += 13;
121
+ }else if( j==16 ){
122
+ memcpy(&zEDate[16], ":59.999",7);
123
+ j += 7;
124
+ }else if( j==19 ){
125
+ memcpy(&zEDate[19], ".999", 4);
126
+ j += 4;
108127
}
128
+ }
129
+ if( addZulu ){
109130
zEDate[j++] = 'Z';
110131
}
111132
zEDate[j] = 0;
112133
113134
/* Check for reasonable date values.
@@ -147,27 +168,40 @@
147168
** comparison. So add in missing factional seconds or seconds or time.
148169
**
149170
** The returned string is held in a static buffer that is overwritten
150171
** with each call, or else is just a copy of its input if there are
151172
** no changes.
173
+**
174
+** For reference:
175
+**
176
+** 0123456789 123456789 1234
177
+** YYYY-MM-DD HH:MM:SS.SSSz
152178
*/
153179
const char *fossil_roundup_date(const char *zDate){
154
- static char zUp[24];
180
+ static char zUp[28];
155181
int n = (int)strlen(zDate);
182
+ int addZ = 0;
183
+ if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){
184
+ n--;
185
+ addZ = 1;
186
+ }
156187
if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */
157188
memcpy(zUp, zDate, 19);
158
- memcpy(zUp+19, ".999", 5);
189
+ memcpy(zUp+19, ".999z", 6);
190
+ if( !addZ ) zUp[23] = 0;
159191
return zUp;
160192
}
161193
if( n==16 ){ /* YYYY-MM-DD HH:MM */
162194
memcpy(zUp, zDate, 16);
163
- memcpy(zUp+16, ":59.999", 8);
195
+ memcpy(zUp+16, ":59.999z", 8);
196
+ if( !addZ ) zUp[23] = 0;
164197
return zUp;
165198
}
166199
if( n==10 ){ /* YYYY-MM-DD */
167200
memcpy(zUp, zDate, 10);
168
- memcpy(zUp+10, " 23:59:59.999", 14);
201
+ memcpy(zUp+10, " 23:59:59.999z", 14);
202
+ if( !addZ ) zUp[23] = 0;
169203
return zUp;
170204
}
171205
return zDate;
172206
}
173207
@@ -479,11 +513,11 @@
479513
if( rid ) return rid;
480514
}
481515
482516
/* Date and times */
483517
if( memcmp(zTag, "date:", 5)==0 ){
484
- zDate = fossil_expand_datetime(&zTag[5],0);
518
+ zDate = fossil_expand_datetime(&zTag[5],0,1);
485519
if( zDate==0 ) zDate = &zTag[5];
486520
rid = db_int(0,
487521
"SELECT objid FROM event"
488522
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
489523
" ORDER BY mtime DESC LIMIT 1",
@@ -545,21 +579,21 @@
545579
546580
/* symbolic-name ":" date-time */
547581
nTag = strlen(zTag);
548582
for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
549583
if( zTag[i]==':'
550
- && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0)
584
+ && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0)
551585
){
552586
char *zDate = mprintf("%s", &zTag[i+1]);
553587
char *zTagBase = mprintf("%.*s", i, zTag);
554588
char *zXDate;
555589
int nDate = strlen(zDate);
556590
if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
557591
zDate[nDate-3] = 'z';
558592
zDate[nDate-2] = 0;
559593
}
560
- zXDate = fossil_expand_datetime(zDate,0);
594
+ zXDate = fossil_expand_datetime(zDate,0,1);
561595
if( zXDate==0 ) zXDate = zDate;
562596
rid = db_int(0,
563597
"SELECT event.objid, max(event.mtime)"
564598
" FROM tag, tagxref, event"
565599
" WHERE tag.tagname='sym-%q' "
@@ -631,11 +665,11 @@
631665
if( startOfBranch ) rid = start_of_branch(rid,1);
632666
return rid;
633667
}
634668
635669
/* Pure numeric date/time */
636
- zDate = fossil_expand_datetime(zTag, 0);
670
+ zDate = fossil_expand_datetime(zTag, 0,1);
637671
if( zDate ){
638672
rid = db_int(0,
639673
"SELECT objid FROM event"
640674
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
641675
" ORDER BY mtime DESC LIMIT 1",
642676
--- src/name.c
+++ src/name.c
@@ -58,29 +58,42 @@
58 ** Check to see if the string might be a compact date/time that omits
59 ** the punctuation. Example: "20190327084549" instead of
60 ** "2019-03-27 08:45:49". If the string is of the appropriate form,
61 ** then return an alternative string (in static space) that is the same
62 ** string with punctuation inserted.
 
 
 
 
 
 
 
 
 
 
 
 
 
63 **
64 ** If the bVerifyNotAHash flag is true, then a check is made to see if
65 ** the string is a hash prefix and NULL is returned if it is. If the
66 ** bVerifyNotAHash flag is false, then the result is determined by syntax
67 ** of the input string only, without reference to the artifact table.
68 */
69 char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
70 static char zEDate[24];
71 static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
72 int n = (int)strlen(zIn);
73 int i, j;
74 int addZulu = 0;
75
76 /* These forms are allowed:
77 **
78 ** 123456789 1234 123456789 123456789
79 ** (1) YYYYMMDD => YYYY-MM-DD
80 ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM
81 ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS
82 **
83 ** An optional "Z" zulu timezone designator is allowed at the end.
84 */
85 if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
86 n--;
@@ -99,15 +112,23 @@
99 if( i>=4 && (i%2)==0 ){
100 zEDate[j++] = aPunct[i/2];
101 }
102 zEDate[j++] = zIn[i];
103 }
104 if( addZulu ){
105 if( j==10 ){
106 memcpy(&zEDate[10]," 00:00", 6);
107 j += 6;
 
 
 
 
 
 
108 }
 
 
109 zEDate[j++] = 'Z';
110 }
111 zEDate[j] = 0;
112
113 /* Check for reasonable date values.
@@ -147,27 +168,40 @@
147 ** comparison. So add in missing factional seconds or seconds or time.
148 **
149 ** The returned string is held in a static buffer that is overwritten
150 ** with each call, or else is just a copy of its input if there are
151 ** no changes.
 
 
 
 
 
152 */
153 const char *fossil_roundup_date(const char *zDate){
154 static char zUp[24];
155 int n = (int)strlen(zDate);
 
 
 
 
 
156 if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */
157 memcpy(zUp, zDate, 19);
158 memcpy(zUp+19, ".999", 5);
 
159 return zUp;
160 }
161 if( n==16 ){ /* YYYY-MM-DD HH:MM */
162 memcpy(zUp, zDate, 16);
163 memcpy(zUp+16, ":59.999", 8);
 
164 return zUp;
165 }
166 if( n==10 ){ /* YYYY-MM-DD */
167 memcpy(zUp, zDate, 10);
168 memcpy(zUp+10, " 23:59:59.999", 14);
 
169 return zUp;
170 }
171 return zDate;
172 }
173
@@ -479,11 +513,11 @@
479 if( rid ) return rid;
480 }
481
482 /* Date and times */
483 if( memcmp(zTag, "date:", 5)==0 ){
484 zDate = fossil_expand_datetime(&zTag[5],0);
485 if( zDate==0 ) zDate = &zTag[5];
486 rid = db_int(0,
487 "SELECT objid FROM event"
488 " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
489 " ORDER BY mtime DESC LIMIT 1",
@@ -545,21 +579,21 @@
545
546 /* symbolic-name ":" date-time */
547 nTag = strlen(zTag);
548 for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
549 if( zTag[i]==':'
550 && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0)
551 ){
552 char *zDate = mprintf("%s", &zTag[i+1]);
553 char *zTagBase = mprintf("%.*s", i, zTag);
554 char *zXDate;
555 int nDate = strlen(zDate);
556 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
557 zDate[nDate-3] = 'z';
558 zDate[nDate-2] = 0;
559 }
560 zXDate = fossil_expand_datetime(zDate,0);
561 if( zXDate==0 ) zXDate = zDate;
562 rid = db_int(0,
563 "SELECT event.objid, max(event.mtime)"
564 " FROM tag, tagxref, event"
565 " WHERE tag.tagname='sym-%q' "
@@ -631,11 +665,11 @@
631 if( startOfBranch ) rid = start_of_branch(rid,1);
632 return rid;
633 }
634
635 /* Pure numeric date/time */
636 zDate = fossil_expand_datetime(zTag, 0);
637 if( zDate ){
638 rid = db_int(0,
639 "SELECT objid FROM event"
640 " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
641 " ORDER BY mtime DESC LIMIT 1",
642
--- src/name.c
+++ src/name.c
@@ -58,29 +58,42 @@
58 ** Check to see if the string might be a compact date/time that omits
59 ** the punctuation. Example: "20190327084549" instead of
60 ** "2019-03-27 08:45:49". If the string is of the appropriate form,
61 ** then return an alternative string (in static space) that is the same
62 ** string with punctuation inserted.
63 **
64 ** If the bRoundUp parameter is true, then round the resulting date-time
65 ** up to the largest date/time that is consistent with the input value.
66 ** This is because the result will be used for an mtime<=julianday($DATE)
67 ** comparison. In other words:
68 **
69 ** 20250317123421 -> 2025-03-17 12:34:21.999
70 ** ^^^^--- Added
71 **
72 ** 202503171234 -> 2025-03-17 12:34:59.999
73 ** ^^^^^^^--- Added
74 ** 20250317 -> 2025-03-17 23:59:59.999
75 ** ^^^^^^^^^^^^--- Added
76 **
77 ** If the bVerifyNotAHash flag is true, then a check is made to see if
78 ** the input string is a hash prefix and NULL is returned if it is. If the
79 ** bVerifyNotAHash flag is false, then the result is determined by syntax
80 ** of the input string only, without reference to the artifact table.
81 */
82 char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){
83 static char zEDate[24];
84 static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
85 int n = (int)strlen(zIn);
86 int i, j;
87 int addZulu = 0;
88
89 /* These forms are allowed:
90 **
91 ** 123456789 1234 123456789 123456789 1234
92 ** (1) YYYYMMDD => YYYY-MM-DD 23:59:59.999
93 ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM:59.999
94 ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS.999
95 **
96 ** An optional "Z" zulu timezone designator is allowed at the end.
97 */
98 if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
99 n--;
@@ -99,15 +112,23 @@
112 if( i>=4 && (i%2)==0 ){
113 zEDate[j++] = aPunct[i/2];
114 }
115 zEDate[j++] = zIn[i];
116 }
117 if( bRoundUp ){
118 if( j==10 ){
119 memcpy(&zEDate[10], " 23:59:59.999", 13);
120 j += 13;
121 }else if( j==16 ){
122 memcpy(&zEDate[16], ":59.999",7);
123 j += 7;
124 }else if( j==19 ){
125 memcpy(&zEDate[19], ".999", 4);
126 j += 4;
127 }
128 }
129 if( addZulu ){
130 zEDate[j++] = 'Z';
131 }
132 zEDate[j] = 0;
133
134 /* Check for reasonable date values.
@@ -147,27 +168,40 @@
168 ** comparison. So add in missing factional seconds or seconds or time.
169 **
170 ** The returned string is held in a static buffer that is overwritten
171 ** with each call, or else is just a copy of its input if there are
172 ** no changes.
173 **
174 ** For reference:
175 **
176 ** 0123456789 123456789 1234
177 ** YYYY-MM-DD HH:MM:SS.SSSz
178 */
179 const char *fossil_roundup_date(const char *zDate){
180 static char zUp[28];
181 int n = (int)strlen(zDate);
182 int addZ = 0;
183 if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){
184 n--;
185 addZ = 1;
186 }
187 if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */
188 memcpy(zUp, zDate, 19);
189 memcpy(zUp+19, ".999z", 6);
190 if( !addZ ) zUp[23] = 0;
191 return zUp;
192 }
193 if( n==16 ){ /* YYYY-MM-DD HH:MM */
194 memcpy(zUp, zDate, 16);
195 memcpy(zUp+16, ":59.999z", 8);
196 if( !addZ ) zUp[23] = 0;
197 return zUp;
198 }
199 if( n==10 ){ /* YYYY-MM-DD */
200 memcpy(zUp, zDate, 10);
201 memcpy(zUp+10, " 23:59:59.999z", 14);
202 if( !addZ ) zUp[23] = 0;
203 return zUp;
204 }
205 return zDate;
206 }
207
@@ -479,11 +513,11 @@
513 if( rid ) return rid;
514 }
515
516 /* Date and times */
517 if( memcmp(zTag, "date:", 5)==0 ){
518 zDate = fossil_expand_datetime(&zTag[5],0,1);
519 if( zDate==0 ) zDate = &zTag[5];
520 rid = db_int(0,
521 "SELECT objid FROM event"
522 " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
523 " ORDER BY mtime DESC LIMIT 1",
@@ -545,21 +579,21 @@
579
580 /* symbolic-name ":" date-time */
581 nTag = strlen(zTag);
582 for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
583 if( zTag[i]==':'
584 && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0)
585 ){
586 char *zDate = mprintf("%s", &zTag[i+1]);
587 char *zTagBase = mprintf("%.*s", i, zTag);
588 char *zXDate;
589 int nDate = strlen(zDate);
590 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
591 zDate[nDate-3] = 'z';
592 zDate[nDate-2] = 0;
593 }
594 zXDate = fossil_expand_datetime(zDate,0,1);
595 if( zXDate==0 ) zXDate = zDate;
596 rid = db_int(0,
597 "SELECT event.objid, max(event.mtime)"
598 " FROM tag, tagxref, event"
599 " WHERE tag.tagname='sym-%q' "
@@ -631,11 +665,11 @@
665 if( startOfBranch ) rid = start_of_branch(rid,1);
666 return rid;
667 }
668
669 /* Pure numeric date/time */
670 zDate = fossil_expand_datetime(zTag, 0,1);
671 if( zDate ){
672 rid = db_int(0,
673 "SELECT objid FROM event"
674 " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
675 " ORDER BY mtime DESC LIMIT 1",
676
+5 -1
--- src/patch.c
+++ src/patch.c
@@ -1122,14 +1122,18 @@
11221122
db_close(0);
11231123
diff_tk("patch diff", 3);
11241124
return;
11251125
}
11261126
db_find_and_open_repository(0, 0);
1127
+ if( gdiff_using_tk(zCmd[0]=='g') ){
1128
+ diff_tk("patch diff", 3);
1129
+ return;
1130
+ }
11271131
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
11281132
diff_options(&DCfg, zCmd[0]=='g', 0);
11291133
verify_all_options();
1130
- zIn = patch_find_patch_filename("apply");
1134
+ zIn = patch_find_patch_filename("diff");
11311135
patch_attach(zIn, stdin, 0);
11321136
patch_diff(flags, &DCfg);
11331137
fossil_free(zIn);
11341138
}else
11351139
if( strncmp(zCmd, "pull", n)==0 ){
11361140
--- src/patch.c
+++ src/patch.c
@@ -1122,14 +1122,18 @@
1122 db_close(0);
1123 diff_tk("patch diff", 3);
1124 return;
1125 }
1126 db_find_and_open_repository(0, 0);
 
 
 
 
1127 if( find_option("force","f",0) ) flags |= PATCH_FORCE;
1128 diff_options(&DCfg, zCmd[0]=='g', 0);
1129 verify_all_options();
1130 zIn = patch_find_patch_filename("apply");
1131 patch_attach(zIn, stdin, 0);
1132 patch_diff(flags, &DCfg);
1133 fossil_free(zIn);
1134 }else
1135 if( strncmp(zCmd, "pull", n)==0 ){
1136
--- src/patch.c
+++ src/patch.c
@@ -1122,14 +1122,18 @@
1122 db_close(0);
1123 diff_tk("patch diff", 3);
1124 return;
1125 }
1126 db_find_and_open_repository(0, 0);
1127 if( gdiff_using_tk(zCmd[0]=='g') ){
1128 diff_tk("patch diff", 3);
1129 return;
1130 }
1131 if( find_option("force","f",0) ) flags |= PATCH_FORCE;
1132 diff_options(&DCfg, zCmd[0]=='g', 0);
1133 verify_all_options();
1134 zIn = patch_find_patch_filename("diff");
1135 patch_attach(zIn, stdin, 0);
1136 patch_diff(flags, &DCfg);
1137 fossil_free(zIn);
1138 }else
1139 if( strncmp(zCmd, "pull", n)==0 ){
1140
+25
--- src/printf.c
+++ src/printf.c
@@ -239,10 +239,35 @@
239239
int n = 0;
240240
while( (N-- != 0) && *(z++)!=0 ){ n++; }
241241
return n;
242242
}
243243
#endif
244
+
245
+/*
246
+** SETTING: timeline-plaintext boolean default=off
247
+**
248
+** If enabled, no wiki-formatting is done for timeline comment messages.
249
+** Hyperlinks are activated, but they show up on screen using the
250
+** complete input text, not just the display text. No other formatting
251
+** is done.
252
+*/
253
+/*
254
+** SETTING: timeline-block-markup boolean default=off
255
+**
256
+** If enabled, block markup (paragraph brakes, tables, lists, headings, etc)
257
+** is enabled while rendering check-in comment message on the timeline.
258
+** This is disabled by default, because the timeline works best if the
259
+** check-in comments are short and do not take up too much vertical space.
260
+*/
261
+/*
262
+** SETTING: timeline-hard-newlines boolean default=off
263
+**
264
+** If enabled, the timeline honors newline characters in check-in comments.
265
+** In other words, newlines are coverted into <br> for HTML display.
266
+** The default behavior, when this setting is off, is that newlines are
267
+** treated like any other whitespace character.
268
+*/
244269
245270
/*
246271
** Return an appropriate set of flags for wiki_convert() for displaying
247272
** comments on a timeline. These flag settings are determined by
248273
** configuration parameters.
249274
--- src/printf.c
+++ src/printf.c
@@ -239,10 +239,35 @@
239 int n = 0;
240 while( (N-- != 0) && *(z++)!=0 ){ n++; }
241 return n;
242 }
243 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
245 /*
246 ** Return an appropriate set of flags for wiki_convert() for displaying
247 ** comments on a timeline. These flag settings are determined by
248 ** configuration parameters.
249
--- src/printf.c
+++ src/printf.c
@@ -239,10 +239,35 @@
239 int n = 0;
240 while( (N-- != 0) && *(z++)!=0 ){ n++; }
241 return n;
242 }
243 #endif
244
245 /*
246 ** SETTING: timeline-plaintext boolean default=off
247 **
248 ** If enabled, no wiki-formatting is done for timeline comment messages.
249 ** Hyperlinks are activated, but they show up on screen using the
250 ** complete input text, not just the display text. No other formatting
251 ** is done.
252 */
253 /*
254 ** SETTING: timeline-block-markup boolean default=off
255 **
256 ** If enabled, block markup (paragraph brakes, tables, lists, headings, etc)
257 ** is enabled while rendering check-in comment message on the timeline.
258 ** This is disabled by default, because the timeline works best if the
259 ** check-in comments are short and do not take up too much vertical space.
260 */
261 /*
262 ** SETTING: timeline-hard-newlines boolean default=off
263 **
264 ** If enabled, the timeline honors newline characters in check-in comments.
265 ** In other words, newlines are coverted into <br> for HTML display.
266 ** The default behavior, when this setting is off, is that newlines are
267 ** treated like any other whitespace character.
268 */
269
270 /*
271 ** Return an appropriate set of flags for wiki_convert() for displaying
272 ** comments on a timeline. These flag settings are determined by
273 ** configuration parameters.
274
+9 -9
--- src/search.c
+++ src/search.c
@@ -1497,18 +1497,18 @@
14971497
** Search for check-in comments, documents, tickets, or wiki that
14981498
** match a user-supplied pattern.
14991499
**
15001500
** s=PATTERN Specify the full-text pattern to search for
15011501
** y=TYPE What to search.
1502
-** c -> check-ins
1503
-** d -> documentation
1504
-** t -> tickets
1505
-** w -> wiki
1506
-** e -> tech notes
1507
-** f -> forum
1508
-** h -> built-in help
1509
-** all -> everything
1502
+** c -> check-ins,
1503
+** d -> documentation,
1504
+** t -> tickets,
1505
+** w -> wiki,
1506
+** e -> tech notes,
1507
+** f -> forum,
1508
+** h -> built-in help,
1509
+** all -> everything.
15101510
*/
15111511
void search_page(void){
15121512
const int isSearch = P("s")!=0;
15131513
login_check_credentials();
15141514
style_header("Search%s", isSearch ? " Results" : "");
@@ -2291,11 +2291,11 @@
22912291
}
22922292
fossil_print(" done\n");
22932293
}
22942294
22952295
/*
2296
-** COMMAND: fts-config* abbreviated-subcommands
2296
+** COMMAND: fts-config* abbrv-subcom
22972297
**
22982298
** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
22992299
**
23002300
** The "fossil fts-config" command configures the full-text search capabilities
23012301
** of the repository. Subcommands:
23022302
--- src/search.c
+++ src/search.c
@@ -1497,18 +1497,18 @@
1497 ** Search for check-in comments, documents, tickets, or wiki that
1498 ** match a user-supplied pattern.
1499 **
1500 ** s=PATTERN Specify the full-text pattern to search for
1501 ** y=TYPE What to search.
1502 ** c -> check-ins
1503 ** d -> documentation
1504 ** t -> tickets
1505 ** w -> wiki
1506 ** e -> tech notes
1507 ** f -> forum
1508 ** h -> built-in help
1509 ** all -> everything
1510 */
1511 void search_page(void){
1512 const int isSearch = P("s")!=0;
1513 login_check_credentials();
1514 style_header("Search%s", isSearch ? " Results" : "");
@@ -2291,11 +2291,11 @@
2291 }
2292 fossil_print(" done\n");
2293 }
2294
2295 /*
2296 ** COMMAND: fts-config* abbreviated-subcommands
2297 **
2298 ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
2299 **
2300 ** The "fossil fts-config" command configures the full-text search capabilities
2301 ** of the repository. Subcommands:
2302
--- src/search.c
+++ src/search.c
@@ -1497,18 +1497,18 @@
1497 ** Search for check-in comments, documents, tickets, or wiki that
1498 ** match a user-supplied pattern.
1499 **
1500 ** s=PATTERN Specify the full-text pattern to search for
1501 ** y=TYPE What to search.
1502 ** c -> check-ins,
1503 ** d -> documentation,
1504 ** t -> tickets,
1505 ** w -> wiki,
1506 ** e -> tech notes,
1507 ** f -> forum,
1508 ** h -> built-in help,
1509 ** all -> everything.
1510 */
1511 void search_page(void){
1512 const int isSearch = P("s")!=0;
1513 login_check_credentials();
1514 style_header("Search%s", isSearch ? " Results" : "");
@@ -2291,11 +2291,11 @@
2291 }
2292 fossil_print(" done\n");
2293 }
2294
2295 /*
2296 ** COMMAND: fts-config* abbrv-subcom
2297 **
2298 ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
2299 **
2300 ** The "fossil fts-config" command configures the full-text search capabilities
2301 ** of the repository. Subcommands:
2302
+126 -237
--- src/security_audit.c
+++ src/security_audit.c
@@ -553,17 +553,17 @@
553553
@ checkbox on the <a href="setup_access">Access Control</a> page.
554554
}
555555
556556
/* Logging should be turned on
557557
*/
558
- if( db_get_boolean("access-log",0)==0 ){
558
+ if( db_get_boolean("access-log",1)==0 ){
559559
@ <li><p>
560560
@ The <a href="access_log">User Log</a> is disabled. The user log
561561
@ keeps a record of successful and unsuccessful login attempts and is
562562
@ useful for security monitoring.
563563
}
564
- if( db_get_boolean("admin-log",0)==0 ){
564
+ if( db_get_boolean("admin-log",1)==0 ){
565565
@ <li><p>
566566
@ The <a href="admin_log">Administrative Log</a> is disabled.
567567
@ The administrative log provides a record of configuration changes
568568
@ and is useful for security monitoring.
569569
}
@@ -804,37 +804,59 @@
804804
@ </pre></blockquote>
805805
blob_reset(&fullname);
806806
}
807807
}
808808
809
-/*
810
-** The maximum number of bytes of the error log to show by default.
811
-*/
812
-#define MXSHOWLOG 500000
813
-
814809
/*
815810
** WEBPAGE: errorlog
816811
**
817812
** Show the content of the error log. Only the administrator can view
818813
** this page.
814
+**
815
+** y=0x01 Show only hack attempts
816
+** y=0x02 Show only panics and assertion faults
817
+** y=0x04 Show hung backoffice processes
818
+** y=0x08 Show POST requests from a different origin
819
+** y=0x40 Show other uncategorized messages
820
+**
821
+** If y is omitted or is zero, a count of the various message types is
822
+** shown.
819823
*/
820824
void errorlog_page(void){
821825
i64 szFile;
822826
FILE *in;
823827
char *zLog;
828
+ const char *zType = P("y");
829
+ static const int eAllTypes = 0x4f;
830
+ long eType = 0;
831
+ int bOutput = 0;
832
+ int prevWasTime = 0;
833
+ int nHack = 0;
834
+ int nPanic = 0;
835
+ int nOther = 0;
836
+ int nHang = 0;
837
+ int nXPost = 0;
824838
char z[10000];
839
+ char zTime[10000];
840
+
825841
login_check_credentials();
826842
if( !g.perm.Admin ){
827843
login_needed(0);
828844
return;
829845
}
846
+ if( zType ){
847
+ eType = strtol(zType,0,0) & eAllTypes;
848
+ }
830849
style_header("Server Error Log");
831850
style_submenu_element("Test", "%R/test-warning");
832851
style_submenu_element("Refresh", "%R/errorlog");
852
+ style_submenu_element("Download", "%R/errorlog?download");
853
+ style_submenu_element("Truncate", "%R/errorlog?truncate");
833854
style_submenu_element("Log-Menu", "%R/setup-logmenu");
834
- style_submenu_element("Panics", "%R/paniclog");
835
- style_submenu_element("Non-Hacks", "%R/hacklog?not");
855
+ if( eType ){
856
+ style_submenu_element("Summary", "%R/errorlog");
857
+ }
836858
837859
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
838860
no_error_log_available();
839861
style_finish_page();
840862
return;
@@ -861,250 +883,117 @@
861883
return;
862884
}
863885
zLog = file_canonical_name_dup(g.zErrlog);
864886
@ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size.
865887
fossil_free(zLog);
866
- style_submenu_element("Download", "%R/errorlog?download");
867
- style_submenu_element("Truncate", "%R/errorlog?truncate");
868888
in = fossil_fopen(g.zErrlog, "rb");
869889
if( in==0 ){
870890
@ <p class='generalError'>Unable to open that file for reading!</p>
871891
style_finish_page();
872892
return;
873893
}
874
- if( szFile>MXSHOWLOG && P("all")==0 ){
875
- @ <form action="%R/errorlog" method="POST">
876
- @ <p>Only the last %,d(MXSHOWLOG) bytes are shown.
877
- @ <input type="submit" name="all" value="Show All">
878
- @ </form>
879
- fseek(in, -MXSHOWLOG, SEEK_END);
880
- }
881
- @ <hr>
882
- @ <pre>
883
- while( fgets(z, sizeof(z), in) ){
884
- @ %h(z)\
885
- }
886
- fclose(in);
887
- @ </pre>
888
- style_finish_page();
889
-}
890
-
891
-/*
892
-** WEBPAGE: paniclog
893
-**
894
-** Scan the error log for panics. Show all panic messages, ignoring all
895
-** other error log entries.
896
-*/
897
-void paniclog_page(void){
898
- i64 szFile;
899
- char *zLog;
900
- FILE *in;
901
- int bOutput = 0;
902
- int prevWasTime = 0;
903
- char z[10000];
904
- char zTime[10000];
905
-
906
- login_check_credentials();
907
- if( !g.perm.Admin ){
908
- login_needed(0);
909
- return;
910
- }
911
- style_header("Server Panic Log");
912
- style_submenu_element("Log-Menu", "%R/setup-logmenu");
913
-
914
- if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
915
- no_error_log_available();
916
- style_finish_page();
917
- return;
918
- }
919
- in = fossil_fopen(g.zErrlog, "rb");
920
- if( in==0 ){
921
- @ <p class='generalError'>Unable to open that file for reading!</p>
922
- style_finish_page();
923
- return;
924
- }
925
- szFile = file_size(g.zErrlog, ExtFILE);
926
- zLog = file_canonical_name_dup(g.zErrlog);
927
- @ Panic messages contained within the %lld(szFile)-byte
928
- @ <a href="%R/errorlog?all">error log</a> found at
929
- @ "%h(zLog)".
930
- fossil_free(zLog);
931
- @ <hr>
932
- @ <pre>
933
- while( fgets(z, sizeof(z), in) ){
934
- if( prevWasTime
935
- && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0)
936
- ){
937
- @ %h(zTime)\
938
- bOutput = 1;
894
+ if( eType==0 ){
895
+ /* will do a summary */
896
+ }else if( (eType&eAllTypes)!=eAllTypes ){
897
+ @ Only the following types of messages displayed:
898
+ @ <ul>
899
+ if( eType & 0x01 ){
900
+ @ <li>Hack attempts
901
+ }
902
+ if( eType & 0x02 ){
903
+ @ <li>Panics and assertion faults
904
+ }
905
+ if( eType & 0x04 ){
906
+ @ <li>Hung backoffice processes
907
+ }
908
+ if( eType & 0x08 ){
909
+ @ <li>POST requests from different origin
910
+ }
911
+ if( eType & 0x40 ){
912
+ @ <li>Other uncategorized messages
913
+ }
914
+ @ </ul>
915
+ }
916
+ @ <hr>
917
+ if( eType ){
918
+ @ <pre>
919
+ }
920
+ while( fgets(z, sizeof(z), in) ){
921
+ if( prevWasTime ){
922
+ if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
923
+ bOutput = (eType & 0x01)!=0;
924
+ nHack++;
925
+ }else
926
+ if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){
927
+ bOutput = (eType & 0x02)!=0;
928
+ nPanic++;
929
+ }else
930
+ if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){
931
+ bOutput = (eType & 0x04)!=0;
932
+ nHang++;
933
+ }else
934
+ if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
935
+ bOutput = (eType & 0x08)!=0;
936
+ nXPost++;
937
+ }else
938
+ {
939
+ bOutput = (eType & 0x40)!=0;
940
+ nOther++;
941
+ }
942
+ if( bOutput ){
943
+ @ %h(zTime)\
944
+ }
939945
}
940946
if( strncmp(z, "--------", 8)==0 ){
941947
size_t n = strlen(z);
942948
memcpy(zTime, z, n+1);
943949
prevWasTime = 1;
944950
bOutput = 0;
945951
}else{
946952
prevWasTime = 0;
947953
}
948
- if( bOutput ){
949
- @ %h(z)\
950
- }
951
- }
952
- fclose(in);
953
- @ </pre>
954
- style_finish_page();
955
-}
956
-
957
-/*
958
-** WEBPAGE: hacklog
959
-**
960
-** Scan the error log for "possible hack attempt" entries Show hack
961
-** attempt messages only, omitting all others. Or if the "not" query
962
-** parameter is present, show only messages that are not hack attempts.
963
-*/
964
-void hacklog_page(void){
965
- i64 szFile;
966
- char *zLog;
967
- FILE *in;
968
- int bOutput = 0;
969
- int prevWasTime = 0;
970
- int isNot = P("not")!=0;
971
- char z[10000];
972
- char zTime[10000];
973
-
974
- login_check_credentials();
975
- if( !g.perm.Admin ){
976
- login_needed(0);
977
- return;
978
- }
979
- style_header("Server Hack Log");
980
- style_submenu_element("Log-Menu", "%R/setup-logmenu");
981
-
982
- if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
983
- no_error_log_available();
984
- style_finish_page();
985
- return;
986
- }
987
- in = fossil_fopen(g.zErrlog, "rb");
988
- if( in==0 ){
989
- @ <p class='generalError'>Unable to open that file for reading!</p>
990
- style_finish_page();
991
- return;
992
- }
993
- szFile = file_size(g.zErrlog, ExtFILE);
994
- zLog = file_canonical_name_dup(g.zErrlog);
995
- @ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte
996
- @ <a href="%R/errorlog?all">error log</a> found at
997
- @ "%h(zLog)".
998
- fossil_free(zLog);
999
- @ <hr>
1000
- @ <pre>
1001
- while( fgets(z, sizeof(z), in) ){
1002
- if( prevWasTime
1003
- && ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot)
1004
- ){
1005
- @ %h(zTime)\
1006
- bOutput = 1;
1007
- }
1008
- if( strncmp(z, "--------", 8)==0 ){
1009
- size_t n = strlen(z);
1010
- memcpy(zTime, z, n+1);
1011
- prevWasTime = 1;
1012
- bOutput = 0;
1013
- }else{
1014
- prevWasTime = 0;
1015
- }
1016
- if( bOutput ){
1017
- @ %h(z)\
1018
- }
1019
- }
1020
- fclose(in);
1021
- @ </pre>
1022
- style_finish_page();
1023
-}
1024
-
1025
-/*
1026
-** WEBPAGE: logsummary
1027
-**
1028
-** Scan the error log and count the various kinds of entries.
1029
-*/
1030
-void logsummary_page(void){
1031
- i64 szFile;
1032
- char *zLog;
1033
- FILE *in;
1034
- int prevWasTime = 0;
1035
- int nHack = 0;
1036
- int nPanic = 0;
1037
- int nOther = 0;
1038
- int nTotal = 0;
1039
- char z[10000];
1040
-
1041
- login_check_credentials();
1042
- if( !g.perm.Admin ){
1043
- login_needed(0);
1044
- return;
1045
- }
1046
- style_header("Server Hack Log");
1047
- style_submenu_element("Log-Menu", "%R/setup-logmenu");
1048
-
1049
- if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
1050
- no_error_log_available();
1051
- style_finish_page();
1052
- return;
1053
- }
1054
- in = fossil_fopen(g.zErrlog, "rb");
1055
- if( in==0 ){
1056
- @ <p class='generalError'>Unable to open that file for reading!</p>
1057
- style_finish_page();
1058
- return;
1059
- }
1060
- szFile = file_size(g.zErrlog, ExtFILE);
1061
- zLog = file_canonical_name_dup(g.zErrlog);
1062
- @ Summary of messages contained within the %lld(szFile)-byte
1063
- @ <a href="%R/errorlog?all">error log</a> found at
1064
- @ "%h(zLog)".
1065
- fossil_free(zLog);
1066
- @ <hr>
1067
- while( fgets(z, sizeof(z), in) ){
1068
- if( prevWasTime
1069
- && (strncmp(z,"possible hack attempt - 418 ", 27)==0)
1070
- ){
1071
- nHack++;
1072
- prevWasTime = 0;
1073
- continue;
1074
- }
1075
- if( prevWasTime
1076
- && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0)
1077
- ){
1078
- nPanic++;
1079
- prevWasTime = 0;
1080
- continue;
1081
- }
1082
- if( prevWasTime ) nOther++;
1083
- if( strncmp(z, "--------", 8)==0 ){
1084
- nTotal++;
1085
- prevWasTime = 1;
1086
- continue;
1087
- }
1088
- prevWasTime = 0;
1089
- }
1090
- fclose(in);
1091
- @ <p><table border="a" cellspacing="0" cellpadding="5">
1092
- @ <tr><td align="right">%d(nPanic)</td>
1093
- if( nPanic>0 ){
1094
- @ <td><a href="./paniclog">Panics</a></td>
1095
- } else {
1096
- @ <td>Panics</td>
1097
- }
1098
- @ <tr><td align="right">%d(nHack)</td>
1099
- if( nHack>0 ){
1100
- @ <td><a href="./hacklog">Hack Attempts</a></td>
1101
- }else{
1102
- @ <td>Hack Attempts</td>
1103
- }
1104
- @ <tr><td align="right">%d(nOther)</td>
1105
- @ <td>Other</td>
1106
- @ <tr><td align="right">%d(nTotal)</td>
1107
- @ <td>Total Messages</td>
1108
- @ </table>
954
+ if( bOutput && eType ){
955
+ @ %h(z)\
956
+ }
957
+ }
958
+ fclose(in);
959
+ if( eType ){
960
+ @ </pre>
961
+ }
962
+ if( eType==0 ){
963
+ int nNonHack = nPanic + nHang + nOther;
964
+ int nTotal = nNonHack + nHack + nXPost;
965
+ @ <p><table border="a" cellspacing="0" cellpadding="5">
966
+ if( nPanic>0 ){
967
+ @ <tr><td align="right">%d(nPanic)</td>
968
+ @ <td><a href="./errorlog?y=2">Panics</a></td>
969
+ }
970
+ if( nHack>0 ){
971
+ @ <tr><td align="right">%d(nHack)</td>
972
+ @ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
973
+ }
974
+ if( nHang>0 ){
975
+ @ <tr><td align="right">%d(nHang)</td>
976
+ @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td>
977
+ }
978
+ if( nXPost>0 ){
979
+ @ <tr><td align="right">%d(nXPost)</td>
980
+ @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td>
981
+ }
982
+ if( nOther>0 ){
983
+ @ <tr><td align="right">%d(nOther)</td>
984
+ @ <td><a href="./errorlog?y=64/">Other</a></td>
985
+ }
986
+ if( nHack+nXPost>0 && nNonHack>0 ){
987
+ @ <tr><td align="right">%d(nNonHack)</td>
988
+ @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td>
989
+ }
990
+ @ <tr><td align="right">%d(nTotal)</td>
991
+ if( nTotal>0 ){
992
+ @ <td><a href="./errorlog?y=255">All Messages</a></td>
993
+ }else{
994
+ @ <td>All Messages</td>
995
+ }
996
+ @ </table>
997
+ }
1109998
style_finish_page();
1110999
}
11111000
--- src/security_audit.c
+++ src/security_audit.c
@@ -553,17 +553,17 @@
553 @ checkbox on the <a href="setup_access">Access Control</a> page.
554 }
555
556 /* Logging should be turned on
557 */
558 if( db_get_boolean("access-log",0)==0 ){
559 @ <li><p>
560 @ The <a href="access_log">User Log</a> is disabled. The user log
561 @ keeps a record of successful and unsuccessful login attempts and is
562 @ useful for security monitoring.
563 }
564 if( db_get_boolean("admin-log",0)==0 ){
565 @ <li><p>
566 @ The <a href="admin_log">Administrative Log</a> is disabled.
567 @ The administrative log provides a record of configuration changes
568 @ and is useful for security monitoring.
569 }
@@ -804,37 +804,59 @@
804 @ </pre></blockquote>
805 blob_reset(&fullname);
806 }
807 }
808
809 /*
810 ** The maximum number of bytes of the error log to show by default.
811 */
812 #define MXSHOWLOG 500000
813
814 /*
815 ** WEBPAGE: errorlog
816 **
817 ** Show the content of the error log. Only the administrator can view
818 ** this page.
 
 
 
 
 
 
 
 
 
819 */
820 void errorlog_page(void){
821 i64 szFile;
822 FILE *in;
823 char *zLog;
 
 
 
 
 
 
 
 
 
 
824 char z[10000];
 
 
825 login_check_credentials();
826 if( !g.perm.Admin ){
827 login_needed(0);
828 return;
829 }
 
 
 
830 style_header("Server Error Log");
831 style_submenu_element("Test", "%R/test-warning");
832 style_submenu_element("Refresh", "%R/errorlog");
 
 
833 style_submenu_element("Log-Menu", "%R/setup-logmenu");
834 style_submenu_element("Panics", "%R/paniclog");
835 style_submenu_element("Non-Hacks", "%R/hacklog?not");
 
836
837 if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
838 no_error_log_available();
839 style_finish_page();
840 return;
@@ -861,250 +883,117 @@
861 return;
862 }
863 zLog = file_canonical_name_dup(g.zErrlog);
864 @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size.
865 fossil_free(zLog);
866 style_submenu_element("Download", "%R/errorlog?download");
867 style_submenu_element("Truncate", "%R/errorlog?truncate");
868 in = fossil_fopen(g.zErrlog, "rb");
869 if( in==0 ){
870 @ <p class='generalError'>Unable to open that file for reading!</p>
871 style_finish_page();
872 return;
873 }
874 if( szFile>MXSHOWLOG && P("all")==0 ){
875 @ <form action="%R/errorlog" method="POST">
876 @ <p>Only the last %,d(MXSHOWLOG) bytes are shown.
877 @ <input type="submit" name="all" value="Show All">
878 @ </form>
879 fseek(in, -MXSHOWLOG, SEEK_END);
880 }
881 @ <hr>
882 @ <pre>
883 while( fgets(z, sizeof(z), in) ){
884 @ %h(z)\
885 }
886 fclose(in);
887 @ </pre>
888 style_finish_page();
889 }
890
891 /*
892 ** WEBPAGE: paniclog
893 **
894 ** Scan the error log for panics. Show all panic messages, ignoring all
895 ** other error log entries.
896 */
897 void paniclog_page(void){
898 i64 szFile;
899 char *zLog;
900 FILE *in;
901 int bOutput = 0;
902 int prevWasTime = 0;
903 char z[10000];
904 char zTime[10000];
905
906 login_check_credentials();
907 if( !g.perm.Admin ){
908 login_needed(0);
909 return;
910 }
911 style_header("Server Panic Log");
912 style_submenu_element("Log-Menu", "%R/setup-logmenu");
913
914 if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
915 no_error_log_available();
916 style_finish_page();
917 return;
918 }
919 in = fossil_fopen(g.zErrlog, "rb");
920 if( in==0 ){
921 @ <p class='generalError'>Unable to open that file for reading!</p>
922 style_finish_page();
923 return;
924 }
925 szFile = file_size(g.zErrlog, ExtFILE);
926 zLog = file_canonical_name_dup(g.zErrlog);
927 @ Panic messages contained within the %lld(szFile)-byte
928 @ <a href="%R/errorlog?all">error log</a> found at
929 @ "%h(zLog)".
930 fossil_free(zLog);
931 @ <hr>
932 @ <pre>
933 while( fgets(z, sizeof(z), in) ){
934 if( prevWasTime
935 && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0)
936 ){
937 @ %h(zTime)\
938 bOutput = 1;
939 }
940 if( strncmp(z, "--------", 8)==0 ){
941 size_t n = strlen(z);
942 memcpy(zTime, z, n+1);
943 prevWasTime = 1;
944 bOutput = 0;
945 }else{
946 prevWasTime = 0;
947 }
948 if( bOutput ){
949 @ %h(z)\
950 }
951 }
952 fclose(in);
953 @ </pre>
954 style_finish_page();
955 }
956
957 /*
958 ** WEBPAGE: hacklog
959 **
960 ** Scan the error log for "possible hack attempt" entries Show hack
961 ** attempt messages only, omitting all others. Or if the "not" query
962 ** parameter is present, show only messages that are not hack attempts.
963 */
964 void hacklog_page(void){
965 i64 szFile;
966 char *zLog;
967 FILE *in;
968 int bOutput = 0;
969 int prevWasTime = 0;
970 int isNot = P("not")!=0;
971 char z[10000];
972 char zTime[10000];
973
974 login_check_credentials();
975 if( !g.perm.Admin ){
976 login_needed(0);
977 return;
978 }
979 style_header("Server Hack Log");
980 style_submenu_element("Log-Menu", "%R/setup-logmenu");
981
982 if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
983 no_error_log_available();
984 style_finish_page();
985 return;
986 }
987 in = fossil_fopen(g.zErrlog, "rb");
988 if( in==0 ){
989 @ <p class='generalError'>Unable to open that file for reading!</p>
990 style_finish_page();
991 return;
992 }
993 szFile = file_size(g.zErrlog, ExtFILE);
994 zLog = file_canonical_name_dup(g.zErrlog);
995 @ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte
996 @ <a href="%R/errorlog?all">error log</a> found at
997 @ "%h(zLog)".
998 fossil_free(zLog);
999 @ <hr>
1000 @ <pre>
1001 while( fgets(z, sizeof(z), in) ){
1002 if( prevWasTime
1003 && ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot)
1004 ){
1005 @ %h(zTime)\
1006 bOutput = 1;
1007 }
1008 if( strncmp(z, "--------", 8)==0 ){
1009 size_t n = strlen(z);
1010 memcpy(zTime, z, n+1);
1011 prevWasTime = 1;
1012 bOutput = 0;
1013 }else{
1014 prevWasTime = 0;
1015 }
1016 if( bOutput ){
1017 @ %h(z)\
1018 }
1019 }
1020 fclose(in);
1021 @ </pre>
1022 style_finish_page();
1023 }
1024
1025 /*
1026 ** WEBPAGE: logsummary
1027 **
1028 ** Scan the error log and count the various kinds of entries.
1029 */
1030 void logsummary_page(void){
1031 i64 szFile;
1032 char *zLog;
1033 FILE *in;
1034 int prevWasTime = 0;
1035 int nHack = 0;
1036 int nPanic = 0;
1037 int nOther = 0;
1038 int nTotal = 0;
1039 char z[10000];
1040
1041 login_check_credentials();
1042 if( !g.perm.Admin ){
1043 login_needed(0);
1044 return;
1045 }
1046 style_header("Server Hack Log");
1047 style_submenu_element("Log-Menu", "%R/setup-logmenu");
1048
1049 if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
1050 no_error_log_available();
1051 style_finish_page();
1052 return;
1053 }
1054 in = fossil_fopen(g.zErrlog, "rb");
1055 if( in==0 ){
1056 @ <p class='generalError'>Unable to open that file for reading!</p>
1057 style_finish_page();
1058 return;
1059 }
1060 szFile = file_size(g.zErrlog, ExtFILE);
1061 zLog = file_canonical_name_dup(g.zErrlog);
1062 @ Summary of messages contained within the %lld(szFile)-byte
1063 @ <a href="%R/errorlog?all">error log</a> found at
1064 @ "%h(zLog)".
1065 fossil_free(zLog);
1066 @ <hr>
1067 while( fgets(z, sizeof(z), in) ){
1068 if( prevWasTime
1069 && (strncmp(z,"possible hack attempt - 418 ", 27)==0)
1070 ){
1071 nHack++;
1072 prevWasTime = 0;
1073 continue;
1074 }
1075 if( prevWasTime
1076 && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0)
1077 ){
1078 nPanic++;
1079 prevWasTime = 0;
1080 continue;
1081 }
1082 if( prevWasTime ) nOther++;
1083 if( strncmp(z, "--------", 8)==0 ){
1084 nTotal++;
1085 prevWasTime = 1;
1086 continue;
1087 }
1088 prevWasTime = 0;
1089 }
1090 fclose(in);
1091 @ <p><table border="a" cellspacing="0" cellpadding="5">
1092 @ <tr><td align="right">%d(nPanic)</td>
1093 if( nPanic>0 ){
1094 @ <td><a href="./paniclog">Panics</a></td>
1095 } else {
1096 @ <td>Panics</td>
1097 }
1098 @ <tr><td align="right">%d(nHack)</td>
1099 if( nHack>0 ){
1100 @ <td><a href="./hacklog">Hack Attempts</a></td>
1101 }else{
1102 @ <td>Hack Attempts</td>
1103 }
1104 @ <tr><td align="right">%d(nOther)</td>
1105 @ <td>Other</td>
1106 @ <tr><td align="right">%d(nTotal)</td>
1107 @ <td>Total Messages</td>
1108 @ </table>
1109 style_finish_page();
1110 }
1111
--- src/security_audit.c
+++ src/security_audit.c
@@ -553,17 +553,17 @@
553 @ checkbox on the <a href="setup_access">Access Control</a> page.
554 }
555
556 /* Logging should be turned on
557 */
558 if( db_get_boolean("access-log",1)==0 ){
559 @ <li><p>
560 @ The <a href="access_log">User Log</a> is disabled. The user log
561 @ keeps a record of successful and unsuccessful login attempts and is
562 @ useful for security monitoring.
563 }
564 if( db_get_boolean("admin-log",1)==0 ){
565 @ <li><p>
566 @ The <a href="admin_log">Administrative Log</a> is disabled.
567 @ The administrative log provides a record of configuration changes
568 @ and is useful for security monitoring.
569 }
@@ -804,37 +804,59 @@
804 @ </pre></blockquote>
805 blob_reset(&fullname);
806 }
807 }
808
 
 
 
 
 
809 /*
810 ** WEBPAGE: errorlog
811 **
812 ** Show the content of the error log. Only the administrator can view
813 ** this page.
814 **
815 ** y=0x01 Show only hack attempts
816 ** y=0x02 Show only panics and assertion faults
817 ** y=0x04 Show hung backoffice processes
818 ** y=0x08 Show POST requests from a different origin
819 ** y=0x40 Show other uncategorized messages
820 **
821 ** If y is omitted or is zero, a count of the various message types is
822 ** shown.
823 */
824 void errorlog_page(void){
825 i64 szFile;
826 FILE *in;
827 char *zLog;
828 const char *zType = P("y");
829 static const int eAllTypes = 0x4f;
830 long eType = 0;
831 int bOutput = 0;
832 int prevWasTime = 0;
833 int nHack = 0;
834 int nPanic = 0;
835 int nOther = 0;
836 int nHang = 0;
837 int nXPost = 0;
838 char z[10000];
839 char zTime[10000];
840
841 login_check_credentials();
842 if( !g.perm.Admin ){
843 login_needed(0);
844 return;
845 }
846 if( zType ){
847 eType = strtol(zType,0,0) & eAllTypes;
848 }
849 style_header("Server Error Log");
850 style_submenu_element("Test", "%R/test-warning");
851 style_submenu_element("Refresh", "%R/errorlog");
852 style_submenu_element("Download", "%R/errorlog?download");
853 style_submenu_element("Truncate", "%R/errorlog?truncate");
854 style_submenu_element("Log-Menu", "%R/setup-logmenu");
855 if( eType ){
856 style_submenu_element("Summary", "%R/errorlog");
857 }
858
859 if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
860 no_error_log_available();
861 style_finish_page();
862 return;
@@ -861,250 +883,117 @@
883 return;
884 }
885 zLog = file_canonical_name_dup(g.zErrlog);
886 @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size.
887 fossil_free(zLog);
 
 
888 in = fossil_fopen(g.zErrlog, "rb");
889 if( in==0 ){
890 @ <p class='generalError'>Unable to open that file for reading!</p>
891 style_finish_page();
892 return;
893 }
894 if( eType==0 ){
895 /* will do a summary */
896 }else if( (eType&eAllTypes)!=eAllTypes ){
897 @ Only the following types of messages displayed:
898 @ <ul>
899 if( eType & 0x01 ){
900 @ <li>Hack attempts
901 }
902 if( eType & 0x02 ){
903 @ <li>Panics and assertion faults
904 }
905 if( eType & 0x04 ){
906 @ <li>Hung backoffice processes
907 }
908 if( eType & 0x08 ){
909 @ <li>POST requests from different origin
910 }
911 if( eType & 0x40 ){
912 @ <li>Other uncategorized messages
913 }
914 @ </ul>
915 }
916 @ <hr>
917 if( eType ){
918 @ <pre>
919 }
920 while( fgets(z, sizeof(z), in) ){
921 if( prevWasTime ){
922 if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
923 bOutput = (eType & 0x01)!=0;
924 nHack++;
925 }else
926 if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){
927 bOutput = (eType & 0x02)!=0;
928 nPanic++;
929 }else
930 if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){
931 bOutput = (eType & 0x04)!=0;
932 nHang++;
933 }else
934 if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
935 bOutput = (eType & 0x08)!=0;
936 nXPost++;
937 }else
938 {
939 bOutput = (eType & 0x40)!=0;
940 nOther++;
941 }
942 if( bOutput ){
943 @ %h(zTime)\
944 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
945 }
946 if( strncmp(z, "--------", 8)==0 ){
947 size_t n = strlen(z);
948 memcpy(zTime, z, n+1);
949 prevWasTime = 1;
950 bOutput = 0;
951 }else{
952 prevWasTime = 0;
953 }
954 if( bOutput && eType ){
955 @ %h(z)\
956 }
957 }
958 fclose(in);
959 if( eType ){
960 @ </pre>
961 }
962 if( eType==0 ){
963 int nNonHack = nPanic + nHang + nOther;
964 int nTotal = nNonHack + nHack + nXPost;
965 @ <p><table border="a" cellspacing="0" cellpadding="5">
966 if( nPanic>0 ){
967 @ <tr><td align="right">%d(nPanic)</td>
968 @ <td><a href="./errorlog?y=2">Panics</a></td>
969 }
970 if( nHack>0 ){
971 @ <tr><td align="right">%d(nHack)</td>
972 @ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
973 }
974 if( nHang>0 ){
975 @ <tr><td align="right">%d(nHang)</td>
976 @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td>
977 }
978 if( nXPost>0 ){
979 @ <tr><td align="right">%d(nXPost)</td>
980 @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td>
981 }
982 if( nOther>0 ){
983 @ <tr><td align="right">%d(nOther)</td>
984 @ <td><a href="./errorlog?y=64/">Other</a></td>
985 }
986 if( nHack+nXPost>0 && nNonHack>0 ){
987 @ <tr><td align="right">%d(nNonHack)</td>
988 @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td>
989 }
990 @ <tr><td align="right">%d(nTotal)</td>
991 if( nTotal>0 ){
992 @ <td><a href="./errorlog?y=255">All Messages</a></td>
993 }else{
994 @ <td>All Messages</td>
995 }
996 @ </table>
997 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998 style_finish_page();
999 }
1000
+26 -26
--- src/setup.c
+++ src/setup.c
@@ -202,11 +202,11 @@
202202
return;
203203
}
204204
style_header("Log Menu");
205205
@ <table border="0" cellspacing="3">
206206
207
- if( db_get_boolean("admin-log",0)==0 ){
207
+ if( db_get_boolean("admin-log",1)==0 ){
208208
blob_appendf(&desc,
209209
"The admin log records configuration changes to the repository.\n"
210210
"<b>Disabled</b>: Turn on the "
211211
" <a href='%R/setup_settings'>admin-log setting</a> to enable."
212212
);
@@ -220,11 +220,11 @@
220220
}
221221
setup_menu_entry("Artifact Log", "rcvfromlist",
222222
"The artifact log records when new content is added in the\n"
223223
"\"rcvfrom\" table.\n"
224224
);
225
- if( db_get_boolean("access-log",0) ){
225
+ if( db_get_boolean("access-log",1) ){
226226
setup_menu_entry("User Log", "user_log",
227227
"Login attempts recorded in the \"accesslog\" table."
228228
);
229229
}else{
230230
blob_appendf(&desc,
@@ -264,30 +264,10 @@
264264
bErrLog = 1;
265265
}
266266
setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
267267
blob_reset(&desc);
268268
269
- @ <tr><td><td><td>
270
- @ &mdash;&mdash;
271
- @ <i>The remaining links are subsets of the Error Log</i>
272
- @ &mdash;&mdash;
273
- @ </td>
274
-
275
- setup_menu_entry("Error Summary", bErrLog ? "logsummary" : 0,
276
- "Counts of the various message types seen in the Error Log.\n"
277
- );
278
- setup_menu_entry("Panic Log", bErrLog ? "paniclog" : 0,
279
- "Only the most important messages in the Error Log:\n"
280
- "assertion faults, segmentation faults, and similar malfunctions.\n"
281
- );
282
- setup_menu_entry("Hack Log", bErrLog ? "hacklog" : 0,
283
- "All code-418 hack attempts in the Error Log"
284
- );
285
- setup_menu_entry("Non-Hack Log", bErrLog ? "hacklog?not" : 0,
286
- "All log messages that are not code-418 hack attempts"
287
- );
288
-
289269
@ </table>
290270
style_finish_page();
291271
}
292272
293273
/*
@@ -1129,10 +1109,11 @@
11291109
*/
11301110
void setup_settings(void){
11311111
int nSetting;
11321112
int i;
11331113
Setting const *pSet;
1114
+ int bIfChng = P("all")==0;
11341115
const Setting *aSetting = setting_info(&nSetting);
11351116
11361117
login_check_credentials();
11371118
if( !g.perm.Setup ){
11381119
login_needed(0);
@@ -1145,23 +1126,36 @@
11451126
/* Provide read-only access to versioned settings,
11461127
but only if no repo file was explicitly provided. */
11471128
db_open_local(0);
11481129
}
11491130
db_begin_transaction();
1131
+ if( bIfChng ){
1132
+ @ <p>Only settings whose value is different from the default are shown.
1133
+ @ Click the "All" button above to set all settings.
1134
+ }
11501135
@ <p>Settings marked with (v) are "versionable" and will be overridden
11511136
@ by the contents of managed files named
11521137
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
11531138
@ If the file for a versionable setting exists, the value cannot be
11541139
@ changed on this screen.</p><hr><p>
11551140
@
11561141
@ <form action="%R/setup_settings" method="post"><div>
1142
+ if( bIfChng ){
1143
+ style_submenu_element("All", "%R/setup_settings?all");
1144
+ }else{
1145
+ @ <input type="hidden" name="all" value="1">
1146
+ style_submenu_element("Changes-Only", "%R/setup_settings");
1147
+ }
11571148
@ <table border="0"><tr><td valign="top">
11581149
login_insert_csrf_secret();
11591150
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
11601151
if( pSet->width==0 ){
11611152
int hasVersionableValue = pSet->versionable &&
1162
- (db_get_versioned(pSet->name, NULL)!=0);
1153
+ (db_get_versioned(pSet->name, NULL, NULL)!=0);
1154
+ if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1155
+ continue;
1156
+ }
11631157
onoff_attribute("", pSet->name,
11641158
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
11651159
is_truth(pSet->def), hasVersionableValue);
11661160
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
11671161
if( pSet->versionable ){
@@ -1175,11 +1169,14 @@
11751169
@ </td><td style="width:50px;"></td><td valign="top">
11761170
@ <table>
11771171
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
11781172
if( pSet->width>0 && !pSet->forceTextArea ){
11791173
int hasVersionableValue = pSet->versionable &&
1180
- (db_get_versioned(pSet->name, NULL)!=0);
1174
+ (db_get_versioned(pSet->name, NULL, NULL)!=0);
1175
+ if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1176
+ continue;
1177
+ }
11811178
@ <tr><td>
11821179
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
11831180
if( pSet->versionable ){
11841181
@ (v)
11851182
} else {
@@ -1194,11 +1191,14 @@
11941191
}
11951192
@</table>
11961193
@ </td><td style="width:50px;"></td><td valign="top">
11971194
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
11981195
if( pSet->width>0 && pSet->forceTextArea ){
1199
- int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
1196
+ int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
1197
+ if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1198
+ continue;
1199
+ }
12001200
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
12011201
if( pSet->versionable ){
12021202
@ (v)<br>
12031203
} else {
12041204
@ <br>
@@ -2141,11 +2141,11 @@
21412141
style_header("Admin Log");
21422142
style_submenu_element("Log-Menu", "setup-logmenu");
21432143
create_admin_log_table();
21442144
limit = atoi(PD("n","200"));
21452145
ofst = atoi(PD("x","0"));
2146
- fLogEnabled = db_get_boolean("admin-log", 0);
2146
+ fLogEnabled = db_get_boolean("admin-log", 1);
21472147
@ <div>Admin logging is %s(fLogEnabled?"on":"off").
21482148
@ (Change this on the <a href="setup_settings">settings</a> page.)</div>
21492149
21502150
if( ofst>0 ){
21512151
int prevx = ofst - limit;
21522152
--- src/setup.c
+++ src/setup.c
@@ -202,11 +202,11 @@
202 return;
203 }
204 style_header("Log Menu");
205 @ <table border="0" cellspacing="3">
206
207 if( db_get_boolean("admin-log",0)==0 ){
208 blob_appendf(&desc,
209 "The admin log records configuration changes to the repository.\n"
210 "<b>Disabled</b>: Turn on the "
211 " <a href='%R/setup_settings'>admin-log setting</a> to enable."
212 );
@@ -220,11 +220,11 @@
220 }
221 setup_menu_entry("Artifact Log", "rcvfromlist",
222 "The artifact log records when new content is added in the\n"
223 "\"rcvfrom\" table.\n"
224 );
225 if( db_get_boolean("access-log",0) ){
226 setup_menu_entry("User Log", "user_log",
227 "Login attempts recorded in the \"accesslog\" table."
228 );
229 }else{
230 blob_appendf(&desc,
@@ -264,30 +264,10 @@
264 bErrLog = 1;
265 }
266 setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
267 blob_reset(&desc);
268
269 @ <tr><td><td><td>
270 @ &mdash;&mdash;
271 @ <i>The remaining links are subsets of the Error Log</i>
272 @ &mdash;&mdash;
273 @ </td>
274
275 setup_menu_entry("Error Summary", bErrLog ? "logsummary" : 0,
276 "Counts of the various message types seen in the Error Log.\n"
277 );
278 setup_menu_entry("Panic Log", bErrLog ? "paniclog" : 0,
279 "Only the most important messages in the Error Log:\n"
280 "assertion faults, segmentation faults, and similar malfunctions.\n"
281 );
282 setup_menu_entry("Hack Log", bErrLog ? "hacklog" : 0,
283 "All code-418 hack attempts in the Error Log"
284 );
285 setup_menu_entry("Non-Hack Log", bErrLog ? "hacklog?not" : 0,
286 "All log messages that are not code-418 hack attempts"
287 );
288
289 @ </table>
290 style_finish_page();
291 }
292
293 /*
@@ -1129,10 +1109,11 @@
1129 */
1130 void setup_settings(void){
1131 int nSetting;
1132 int i;
1133 Setting const *pSet;
 
1134 const Setting *aSetting = setting_info(&nSetting);
1135
1136 login_check_credentials();
1137 if( !g.perm.Setup ){
1138 login_needed(0);
@@ -1145,23 +1126,36 @@
1145 /* Provide read-only access to versioned settings,
1146 but only if no repo file was explicitly provided. */
1147 db_open_local(0);
1148 }
1149 db_begin_transaction();
 
 
 
 
1150 @ <p>Settings marked with (v) are "versionable" and will be overridden
1151 @ by the contents of managed files named
1152 @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
1153 @ If the file for a versionable setting exists, the value cannot be
1154 @ changed on this screen.</p><hr><p>
1155 @
1156 @ <form action="%R/setup_settings" method="post"><div>
 
 
 
 
 
 
1157 @ <table border="0"><tr><td valign="top">
1158 login_insert_csrf_secret();
1159 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1160 if( pSet->width==0 ){
1161 int hasVersionableValue = pSet->versionable &&
1162 (db_get_versioned(pSet->name, NULL)!=0);
 
 
 
1163 onoff_attribute("", pSet->name,
1164 pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
1165 is_truth(pSet->def), hasVersionableValue);
1166 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1167 if( pSet->versionable ){
@@ -1175,11 +1169,14 @@
1175 @ </td><td style="width:50px;"></td><td valign="top">
1176 @ <table>
1177 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1178 if( pSet->width>0 && !pSet->forceTextArea ){
1179 int hasVersionableValue = pSet->versionable &&
1180 (db_get_versioned(pSet->name, NULL)!=0);
 
 
 
1181 @ <tr><td>
1182 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1183 if( pSet->versionable ){
1184 @ (v)
1185 } else {
@@ -1194,11 +1191,14 @@
1194 }
1195 @</table>
1196 @ </td><td style="width:50px;"></td><td valign="top">
1197 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1198 if( pSet->width>0 && pSet->forceTextArea ){
1199 int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
 
 
 
1200 @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
1201 if( pSet->versionable ){
1202 @ (v)<br>
1203 } else {
1204 @ <br>
@@ -2141,11 +2141,11 @@
2141 style_header("Admin Log");
2142 style_submenu_element("Log-Menu", "setup-logmenu");
2143 create_admin_log_table();
2144 limit = atoi(PD("n","200"));
2145 ofst = atoi(PD("x","0"));
2146 fLogEnabled = db_get_boolean("admin-log", 0);
2147 @ <div>Admin logging is %s(fLogEnabled?"on":"off").
2148 @ (Change this on the <a href="setup_settings">settings</a> page.)</div>
2149
2150 if( ofst>0 ){
2151 int prevx = ofst - limit;
2152
--- src/setup.c
+++ src/setup.c
@@ -202,11 +202,11 @@
202 return;
203 }
204 style_header("Log Menu");
205 @ <table border="0" cellspacing="3">
206
207 if( db_get_boolean("admin-log",1)==0 ){
208 blob_appendf(&desc,
209 "The admin log records configuration changes to the repository.\n"
210 "<b>Disabled</b>: Turn on the "
211 " <a href='%R/setup_settings'>admin-log setting</a> to enable."
212 );
@@ -220,11 +220,11 @@
220 }
221 setup_menu_entry("Artifact Log", "rcvfromlist",
222 "The artifact log records when new content is added in the\n"
223 "\"rcvfrom\" table.\n"
224 );
225 if( db_get_boolean("access-log",1) ){
226 setup_menu_entry("User Log", "user_log",
227 "Login attempts recorded in the \"accesslog\" table."
228 );
229 }else{
230 blob_appendf(&desc,
@@ -264,30 +264,10 @@
264 bErrLog = 1;
265 }
266 setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
267 blob_reset(&desc);
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269 @ </table>
270 style_finish_page();
271 }
272
273 /*
@@ -1129,10 +1109,11 @@
1109 */
1110 void setup_settings(void){
1111 int nSetting;
1112 int i;
1113 Setting const *pSet;
1114 int bIfChng = P("all")==0;
1115 const Setting *aSetting = setting_info(&nSetting);
1116
1117 login_check_credentials();
1118 if( !g.perm.Setup ){
1119 login_needed(0);
@@ -1145,23 +1126,36 @@
1126 /* Provide read-only access to versioned settings,
1127 but only if no repo file was explicitly provided. */
1128 db_open_local(0);
1129 }
1130 db_begin_transaction();
1131 if( bIfChng ){
1132 @ <p>Only settings whose value is different from the default are shown.
1133 @ Click the "All" button above to set all settings.
1134 }
1135 @ <p>Settings marked with (v) are "versionable" and will be overridden
1136 @ by the contents of managed files named
1137 @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
1138 @ If the file for a versionable setting exists, the value cannot be
1139 @ changed on this screen.</p><hr><p>
1140 @
1141 @ <form action="%R/setup_settings" method="post"><div>
1142 if( bIfChng ){
1143 style_submenu_element("All", "%R/setup_settings?all");
1144 }else{
1145 @ <input type="hidden" name="all" value="1">
1146 style_submenu_element("Changes-Only", "%R/setup_settings");
1147 }
1148 @ <table border="0"><tr><td valign="top">
1149 login_insert_csrf_secret();
1150 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1151 if( pSet->width==0 ){
1152 int hasVersionableValue = pSet->versionable &&
1153 (db_get_versioned(pSet->name, NULL, NULL)!=0);
1154 if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1155 continue;
1156 }
1157 onoff_attribute("", pSet->name,
1158 pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
1159 is_truth(pSet->def), hasVersionableValue);
1160 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1161 if( pSet->versionable ){
@@ -1175,11 +1169,14 @@
1169 @ </td><td style="width:50px;"></td><td valign="top">
1170 @ <table>
1171 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1172 if( pSet->width>0 && !pSet->forceTextArea ){
1173 int hasVersionableValue = pSet->versionable &&
1174 (db_get_versioned(pSet->name, NULL, NULL)!=0);
1175 if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1176 continue;
1177 }
1178 @ <tr><td>
1179 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
1180 if( pSet->versionable ){
1181 @ (v)
1182 } else {
@@ -1194,11 +1191,14 @@
1191 }
1192 @</table>
1193 @ </td><td style="width:50px;"></td><td valign="top">
1194 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1195 if( pSet->width>0 && pSet->forceTextArea ){
1196 int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
1197 if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1198 continue;
1199 }
1200 @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
1201 if( pSet->versionable ){
1202 @ (v)<br>
1203 } else {
1204 @ <br>
@@ -2141,11 +2141,11 @@
2141 style_header("Admin Log");
2142 style_submenu_element("Log-Menu", "setup-logmenu");
2143 create_admin_log_table();
2144 limit = atoi(PD("n","200"));
2145 ofst = atoi(PD("x","0"));
2146 fLogEnabled = db_get_boolean("admin-log", 1);
2147 @ <div>Admin logging is %s(fLogEnabled?"on":"off").
2148 @ (Change this on the <a href="setup_settings">settings</a> page.)</div>
2149
2150 if( ofst>0 ){
2151 int prevx = ofst - limit;
2152
+116 -12
--- src/setupuser.c
+++ src/setupuser.c
@@ -302,10 +302,101 @@
302302
if( zPw==0 ) return 0;
303303
if( zPw[0]==0 ) return 1;
304304
while( zPw[0]=='*' ){ zPw++; }
305305
return zPw[0]!=0;
306306
}
307
+
308
+/*
309
+** Return true if user capability string zNew contains any capability
310
+** letter which is not in user capability string zOrig, else 0. This
311
+** does not take inherited permissions into account. Either argument
312
+** may be NULL.
313
+*/
314
+static int userHasNewCaps(const char *zOrig, const char *zNew){
315
+ for( ; zNew && *zNew; ++zNew ){
316
+ if( !zOrig || strchr(zOrig,*zNew)==0 ){
317
+ return *zNew;
318
+ }
319
+ }
320
+ return 0;
321
+}
322
+
323
+/*
324
+** Sends notification of user permission elevation changes to all
325
+** subscribers with a "u" subscription. This is a no-op if alerts are
326
+** not enabled.
327
+**
328
+** These subscriptions differ from most, in that:
329
+**
330
+** - They currently lack an "unsubscribe" link.
331
+**
332
+** - Only an admin can assign this subscription, but if a non-admin
333
+** edits their subscriptions after an admin assigns them this one,
334
+** this particular one will be lost. "Feature or bug?" is unclear,
335
+** but it would be odd for a non-admin to be assigned this
336
+** capability.
337
+*/
338
+static void alert_user_elevation(const char *zLogin, /*Affected user*/
339
+ int uid, /*[user].uid*/
340
+ int bIsNew, /*true if new user*/
341
+ const char *zOrigCaps,/*Old caps*/
342
+ const char *zNewCaps /*New caps*/){
343
+ Blob hdr, body;
344
+ Stmt q;
345
+ int nBody;
346
+ AlertSender *pSender;
347
+ char *zSubname;
348
+ char *zURL;
349
+ char * zSubject;
350
+
351
+ if( !alert_enabled() ) return;
352
+ zSubject = bIsNew
353
+ ? mprintf("New user created: [%q]", zLogin)
354
+ : mprintf("User [%q] permissions elevated", zLogin);
355
+ zURL = db_get("email-url",0);
356
+ zSubname = db_get("email-subname", "[Fossil Repo]");
357
+ blob_init(&body, 0, 0);
358
+ blob_init(&hdr, 0, 0);
359
+ if( bIsNew ){
360
+ blob_appendf(&body, "User [%q] was created by with "
361
+ "permissions [%q] by user [%q].\n",
362
+ zLogin, zNewCaps, g.zLogin);
363
+ } else {
364
+ blob_appendf(&body, "Permissions for user [%q] where elevated "
365
+ "from [%q] to [%q] by user [%q].\n",
366
+ zLogin, zOrigCaps, zNewCaps, g.zLogin);
367
+ }
368
+ if( zURL ){
369
+ blob_appendf(&body, "\nUser editor: %s/setup_uedit?uid=%d\n", zURL, uid);
370
+ }
371
+ nBody = blob_size(&body);
372
+ pSender = alert_sender_new(0, 0);
373
+ db_prepare(&q,
374
+ "SELECT semail, hex(subscriberCode)"
375
+ " FROM subscriber, user "
376
+ " WHERE sverified AND NOT sdonotcall"
377
+ " AND suname=login"
378
+ " AND ssub GLOB '*u*'");
379
+ while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){
380
+ const char *zTo = db_column_text(&q, 0);
381
+ blob_truncate(&hdr, 0);
382
+ blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n",
383
+ zTo, zSubname, zSubject);
384
+ if( zURL ){
385
+ const char *zCode = db_column_text(&q, 1);
386
+ blob_truncate(&body, nBody);
387
+ blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
388
+ zURL, zCode);
389
+ }
390
+ alert_send(pSender, &hdr, &body, 0);
391
+ }
392
+ db_finalize(&q);
393
+ alert_sender_free(pSender);
394
+ fossil_free(zURL);
395
+ fossil_free(zSubname);
396
+ fossil_free(zSubject);
397
+}
307398
308399
/*
309400
** WEBPAGE: setup_uedit
310401
**
311402
** Edit information about a user or create a new user.
@@ -314,10 +405,11 @@
314405
void user_edit(void){
315406
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
316407
const char *zGroup;
317408
const char *zOldLogin;
318409
int uid, i;
410
+ char *zOldCaps = 0; /* Capabilities before edit */
319411
char *zDeleteVerify = 0; /* Delete user verification text */
320412
int higherUser = 0; /* True if user being edited is SETUP and the */
321413
/* user doing the editing is ADMIN. Disallow editing */
322414
const char *inherit[128];
323415
int a[128];
@@ -331,14 +423,15 @@
331423
/* Check to see if an ADMIN user is trying to edit a SETUP account.
332424
** Don't allow that.
333425
*/
334426
zId = PD("id", "0");
335427
uid = atoi(zId);
336
- if( zId && !g.perm.Setup && uid>0 ){
337
- char *zOldCaps;
338
- zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid);
339
- higherUser = zOldCaps && strchr(zOldCaps,'s');
428
+ if( uid>0 ){
429
+ zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid);
430
+ if( zId && !g.perm.Setup ){
431
+ higherUser = zOldCaps && strchr(zOldCaps,'s');
432
+ }
340433
}
341434
342435
if( P("can") ){
343436
/* User pressed the cancel button */
344437
cgi_redirect(cgi_referer("setup_ulist"));
@@ -393,10 +486,12 @@
393486
}else if( !cgi_csrf_safe(2) ){
394487
/* This might be a cross-site request forgery, so ignore it */
395488
}else{
396489
/* We have all the information we need to make the change to the user */
397490
char c;
491
+ int bHasNewCaps = 0 /* 1 if user's permissions are increased */;
492
+ const int bIsNew = uid<=0;
398493
char aCap[70], zNm[4];
399494
zNm[0] = 'a';
400495
zNm[2] = 0;
401496
for(i=0, c='a'; c<='z'; c++){
402497
zNm[1] = c;
@@ -413,10 +508,11 @@
413508
a[c&0x7f] = P(zNm)!=0;
414509
if( a[c&0x7f] ) aCap[i++] = c;
415510
}
416511
417512
aCap[i] = 0;
513
+ bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]);
418514
zPw = P("pw");
419515
zLogin = P("login");
420516
if( strlen(zLogin)==0 ){
421517
const char *zRef = cgi_referer("setup_ulist");
422518
style_header("User Creation Error");
@@ -444,15 +540,16 @@
444540
style_finish_page();
445541
return;
446542
}
447543
cgi_csrf_verify();
448544
db_unprotect(PROTECT_USER);
449
- db_multi_exec(
450
- "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
451
- "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",
452
- uid, zLogin, P("info"), zPw, &aCap[0]
453
- );
545
+ uid = db_int(0,
546
+ "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
547
+ "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) "
548
+ "RETURNING uid",
549
+ uid, zLogin, P("info"), zPw, &aCap[0]);
550
+ assert( uid>0 );
454551
if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){
455552
if( alert_tables_exist() ){
456553
/* Rename matching subscriber entry, else the user cannot
457554
re-subscribe with their same email address. */
458555
db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q",
@@ -460,11 +557,12 @@
460557
}
461558
admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin );
462559
}
463560
db_protect_pop();
464561
setup_incr_cfgcnt();
465
- admin_log( "Updated user [%q] with capabilities [%q].",
562
+ admin_log( "%s user [%q] with capabilities [%q].",
563
+ bIsNew ? "Added" : "Updated",
466564
zLogin, &aCap[0] );
467565
if( atoi(PD("all","0"))>0 ){
468566
Blob sql;
469567
char *zErr = 0;
470568
blob_zero(&sql);
@@ -515,30 +613,36 @@
515613
@ <span class="loginError">%h(zErr)</span>
516614
@
517615
@ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
518616
@ [Bummer]</a></p>
519617
style_finish_page();
618
+ if( bHasNewCaps ){
619
+ alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
620
+ }
520621
return;
521622
}
522623
}
624
+ if( bHasNewCaps ){
625
+ alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
626
+ }
523627
cgi_redirect(cgi_referer("setup_ulist"));
524628
return;
525629
}
526630
527631
/* Load the existing information about the user, if any
528632
*/
529633
zLogin = "";
530634
zInfo = "";
531
- zCap = "";
635
+ zCap = zOldCaps;
532636
zPw = "";
533637
for(i='a'; i<='z'; i++) oa[i] = "";
534638
for(i='0'; i<='9'; i++) oa[i] = "";
535639
for(i='A'; i<='Z'; i++) oa[i] = "";
536640
if( uid ){
641
+ assert( zCap );
537642
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
538643
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
539
- zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
540644
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
541645
for(i=0; zCap[i]; i++){
542646
char c = zCap[i];
543647
if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){
544648
oa[c&0x7f] = " checked=\"checked\"";
545649
--- src/setupuser.c
+++ src/setupuser.c
@@ -302,10 +302,101 @@
302 if( zPw==0 ) return 0;
303 if( zPw[0]==0 ) return 1;
304 while( zPw[0]=='*' ){ zPw++; }
305 return zPw[0]!=0;
306 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
308 /*
309 ** WEBPAGE: setup_uedit
310 **
311 ** Edit information about a user or create a new user.
@@ -314,10 +405,11 @@
314 void user_edit(void){
315 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
316 const char *zGroup;
317 const char *zOldLogin;
318 int uid, i;
 
319 char *zDeleteVerify = 0; /* Delete user verification text */
320 int higherUser = 0; /* True if user being edited is SETUP and the */
321 /* user doing the editing is ADMIN. Disallow editing */
322 const char *inherit[128];
323 int a[128];
@@ -331,14 +423,15 @@
331 /* Check to see if an ADMIN user is trying to edit a SETUP account.
332 ** Don't allow that.
333 */
334 zId = PD("id", "0");
335 uid = atoi(zId);
336 if( zId && !g.perm.Setup && uid>0 ){
337 char *zOldCaps;
338 zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid);
339 higherUser = zOldCaps && strchr(zOldCaps,'s');
 
340 }
341
342 if( P("can") ){
343 /* User pressed the cancel button */
344 cgi_redirect(cgi_referer("setup_ulist"));
@@ -393,10 +486,12 @@
393 }else if( !cgi_csrf_safe(2) ){
394 /* This might be a cross-site request forgery, so ignore it */
395 }else{
396 /* We have all the information we need to make the change to the user */
397 char c;
 
 
398 char aCap[70], zNm[4];
399 zNm[0] = 'a';
400 zNm[2] = 0;
401 for(i=0, c='a'; c<='z'; c++){
402 zNm[1] = c;
@@ -413,10 +508,11 @@
413 a[c&0x7f] = P(zNm)!=0;
414 if( a[c&0x7f] ) aCap[i++] = c;
415 }
416
417 aCap[i] = 0;
 
418 zPw = P("pw");
419 zLogin = P("login");
420 if( strlen(zLogin)==0 ){
421 const char *zRef = cgi_referer("setup_ulist");
422 style_header("User Creation Error");
@@ -444,15 +540,16 @@
444 style_finish_page();
445 return;
446 }
447 cgi_csrf_verify();
448 db_unprotect(PROTECT_USER);
449 db_multi_exec(
450 "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
451 "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",
452 uid, zLogin, P("info"), zPw, &aCap[0]
453 );
 
454 if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){
455 if( alert_tables_exist() ){
456 /* Rename matching subscriber entry, else the user cannot
457 re-subscribe with their same email address. */
458 db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q",
@@ -460,11 +557,12 @@
460 }
461 admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin );
462 }
463 db_protect_pop();
464 setup_incr_cfgcnt();
465 admin_log( "Updated user [%q] with capabilities [%q].",
 
466 zLogin, &aCap[0] );
467 if( atoi(PD("all","0"))>0 ){
468 Blob sql;
469 char *zErr = 0;
470 blob_zero(&sql);
@@ -515,30 +613,36 @@
515 @ <span class="loginError">%h(zErr)</span>
516 @
517 @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
518 @ [Bummer]</a></p>
519 style_finish_page();
 
 
 
520 return;
521 }
522 }
 
 
 
523 cgi_redirect(cgi_referer("setup_ulist"));
524 return;
525 }
526
527 /* Load the existing information about the user, if any
528 */
529 zLogin = "";
530 zInfo = "";
531 zCap = "";
532 zPw = "";
533 for(i='a'; i<='z'; i++) oa[i] = "";
534 for(i='0'; i<='9'; i++) oa[i] = "";
535 for(i='A'; i<='Z'; i++) oa[i] = "";
536 if( uid ){
 
537 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
538 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
539 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
540 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
541 for(i=0; zCap[i]; i++){
542 char c = zCap[i];
543 if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){
544 oa[c&0x7f] = " checked=\"checked\"";
545
--- src/setupuser.c
+++ src/setupuser.c
@@ -302,10 +302,101 @@
302 if( zPw==0 ) return 0;
303 if( zPw[0]==0 ) return 1;
304 while( zPw[0]=='*' ){ zPw++; }
305 return zPw[0]!=0;
306 }
307
308 /*
309 ** Return true if user capability string zNew contains any capability
310 ** letter which is not in user capability string zOrig, else 0. This
311 ** does not take inherited permissions into account. Either argument
312 ** may be NULL.
313 */
314 static int userHasNewCaps(const char *zOrig, const char *zNew){
315 for( ; zNew && *zNew; ++zNew ){
316 if( !zOrig || strchr(zOrig,*zNew)==0 ){
317 return *zNew;
318 }
319 }
320 return 0;
321 }
322
323 /*
324 ** Sends notification of user permission elevation changes to all
325 ** subscribers with a "u" subscription. This is a no-op if alerts are
326 ** not enabled.
327 **
328 ** These subscriptions differ from most, in that:
329 **
330 ** - They currently lack an "unsubscribe" link.
331 **
332 ** - Only an admin can assign this subscription, but if a non-admin
333 ** edits their subscriptions after an admin assigns them this one,
334 ** this particular one will be lost. "Feature or bug?" is unclear,
335 ** but it would be odd for a non-admin to be assigned this
336 ** capability.
337 */
338 static void alert_user_elevation(const char *zLogin, /*Affected user*/
339 int uid, /*[user].uid*/
340 int bIsNew, /*true if new user*/
341 const char *zOrigCaps,/*Old caps*/
342 const char *zNewCaps /*New caps*/){
343 Blob hdr, body;
344 Stmt q;
345 int nBody;
346 AlertSender *pSender;
347 char *zSubname;
348 char *zURL;
349 char * zSubject;
350
351 if( !alert_enabled() ) return;
352 zSubject = bIsNew
353 ? mprintf("New user created: [%q]", zLogin)
354 : mprintf("User [%q] permissions elevated", zLogin);
355 zURL = db_get("email-url",0);
356 zSubname = db_get("email-subname", "[Fossil Repo]");
357 blob_init(&body, 0, 0);
358 blob_init(&hdr, 0, 0);
359 if( bIsNew ){
360 blob_appendf(&body, "User [%q] was created by with "
361 "permissions [%q] by user [%q].\n",
362 zLogin, zNewCaps, g.zLogin);
363 } else {
364 blob_appendf(&body, "Permissions for user [%q] where elevated "
365 "from [%q] to [%q] by user [%q].\n",
366 zLogin, zOrigCaps, zNewCaps, g.zLogin);
367 }
368 if( zURL ){
369 blob_appendf(&body, "\nUser editor: %s/setup_uedit?uid=%d\n", zURL, uid);
370 }
371 nBody = blob_size(&body);
372 pSender = alert_sender_new(0, 0);
373 db_prepare(&q,
374 "SELECT semail, hex(subscriberCode)"
375 " FROM subscriber, user "
376 " WHERE sverified AND NOT sdonotcall"
377 " AND suname=login"
378 " AND ssub GLOB '*u*'");
379 while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){
380 const char *zTo = db_column_text(&q, 0);
381 blob_truncate(&hdr, 0);
382 blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n",
383 zTo, zSubname, zSubject);
384 if( zURL ){
385 const char *zCode = db_column_text(&q, 1);
386 blob_truncate(&body, nBody);
387 blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
388 zURL, zCode);
389 }
390 alert_send(pSender, &hdr, &body, 0);
391 }
392 db_finalize(&q);
393 alert_sender_free(pSender);
394 fossil_free(zURL);
395 fossil_free(zSubname);
396 fossil_free(zSubject);
397 }
398
399 /*
400 ** WEBPAGE: setup_uedit
401 **
402 ** Edit information about a user or create a new user.
@@ -314,10 +405,11 @@
405 void user_edit(void){
406 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
407 const char *zGroup;
408 const char *zOldLogin;
409 int uid, i;
410 char *zOldCaps = 0; /* Capabilities before edit */
411 char *zDeleteVerify = 0; /* Delete user verification text */
412 int higherUser = 0; /* True if user being edited is SETUP and the */
413 /* user doing the editing is ADMIN. Disallow editing */
414 const char *inherit[128];
415 int a[128];
@@ -331,14 +423,15 @@
423 /* Check to see if an ADMIN user is trying to edit a SETUP account.
424 ** Don't allow that.
425 */
426 zId = PD("id", "0");
427 uid = atoi(zId);
428 if( uid>0 ){
429 zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid);
430 if( zId && !g.perm.Setup ){
431 higherUser = zOldCaps && strchr(zOldCaps,'s');
432 }
433 }
434
435 if( P("can") ){
436 /* User pressed the cancel button */
437 cgi_redirect(cgi_referer("setup_ulist"));
@@ -393,10 +486,12 @@
486 }else if( !cgi_csrf_safe(2) ){
487 /* This might be a cross-site request forgery, so ignore it */
488 }else{
489 /* We have all the information we need to make the change to the user */
490 char c;
491 int bHasNewCaps = 0 /* 1 if user's permissions are increased */;
492 const int bIsNew = uid<=0;
493 char aCap[70], zNm[4];
494 zNm[0] = 'a';
495 zNm[2] = 0;
496 for(i=0, c='a'; c<='z'; c++){
497 zNm[1] = c;
@@ -413,10 +508,11 @@
508 a[c&0x7f] = P(zNm)!=0;
509 if( a[c&0x7f] ) aCap[i++] = c;
510 }
511
512 aCap[i] = 0;
513 bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]);
514 zPw = P("pw");
515 zLogin = P("login");
516 if( strlen(zLogin)==0 ){
517 const char *zRef = cgi_referer("setup_ulist");
518 style_header("User Creation Error");
@@ -444,15 +540,16 @@
540 style_finish_page();
541 return;
542 }
543 cgi_csrf_verify();
544 db_unprotect(PROTECT_USER);
545 uid = db_int(0,
546 "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
547 "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) "
548 "RETURNING uid",
549 uid, zLogin, P("info"), zPw, &aCap[0]);
550 assert( uid>0 );
551 if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){
552 if( alert_tables_exist() ){
553 /* Rename matching subscriber entry, else the user cannot
554 re-subscribe with their same email address. */
555 db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q",
@@ -460,11 +557,12 @@
557 }
558 admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin );
559 }
560 db_protect_pop();
561 setup_incr_cfgcnt();
562 admin_log( "%s user [%q] with capabilities [%q].",
563 bIsNew ? "Added" : "Updated",
564 zLogin, &aCap[0] );
565 if( atoi(PD("all","0"))>0 ){
566 Blob sql;
567 char *zErr = 0;
568 blob_zero(&sql);
@@ -515,30 +613,36 @@
613 @ <span class="loginError">%h(zErr)</span>
614 @
615 @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
616 @ [Bummer]</a></p>
617 style_finish_page();
618 if( bHasNewCaps ){
619 alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
620 }
621 return;
622 }
623 }
624 if( bHasNewCaps ){
625 alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
626 }
627 cgi_redirect(cgi_referer("setup_ulist"));
628 return;
629 }
630
631 /* Load the existing information about the user, if any
632 */
633 zLogin = "";
634 zInfo = "";
635 zCap = zOldCaps;
636 zPw = "";
637 for(i='a'; i<='z'; i++) oa[i] = "";
638 for(i='0'; i<='9'; i++) oa[i] = "";
639 for(i='A'; i<='Z'; i++) oa[i] = "";
640 if( uid ){
641 assert( zCap );
642 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
643 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
 
644 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
645 for(i=0; zCap[i]; i++){
646 char c = zCap[i];
647 if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){
648 oa[c&0x7f] = " checked=\"checked\"";
649
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -235,10 +235,11 @@
235235
char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
236236
g.zConfigDbName);
237237
sqlite3_exec(db, zSql, 0, 0, 0);
238238
sqlite3_free(zSql);
239239
}
240
+ (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */
240241
/* Arrange to trace close operations so that static prepared statements
241242
** will get cleaned up when the shell closes the database connection */
242243
if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
243244
sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
244245
db_protect_only(PROTECT_NONE);
245246
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -235,10 +235,11 @@
235 char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
236 g.zConfigDbName);
237 sqlite3_exec(db, zSql, 0, 0, 0);
238 sqlite3_free(zSql);
239 }
 
240 /* Arrange to trace close operations so that static prepared statements
241 ** will get cleaned up when the shell closes the database connection */
242 if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
243 sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
244 db_protect_only(PROTECT_NONE);
245
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -235,10 +235,11 @@
235 char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
236 g.zConfigDbName);
237 sqlite3_exec(db, zSql, 0, 0, 0);
238 sqlite3_free(zSql);
239 }
240 (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */
241 /* Arrange to trace close operations so that static prepared statements
242 ** will get cleaned up when the shell closes the database connection */
243 if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
244 sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
245 db_protect_only(PROTECT_NONE);
246
+1 -1
--- src/stash.c
+++ src/stash.c
@@ -756,11 +756,11 @@
756756
DiffConfig DCfg;
757757
758758
if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
759759
fBaseline = 1;
760760
}
761
- if( find_option("tk",0,0)!=0 ){
761
+ if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){
762762
db_close(0);
763763
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
764764
return;
765765
}
766766
diff_options(&DCfg, zCmd[0]=='g', 0);
767767
--- src/stash.c
+++ src/stash.c
@@ -756,11 +756,11 @@
756 DiffConfig DCfg;
757
758 if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
759 fBaseline = 1;
760 }
761 if( find_option("tk",0,0)!=0 ){
762 db_close(0);
763 diff_tk(fBaseline ? "stash show" : "stash diff", 3);
764 return;
765 }
766 diff_options(&DCfg, zCmd[0]=='g', 0);
767
--- src/stash.c
+++ src/stash.c
@@ -756,11 +756,11 @@
756 DiffConfig DCfg;
757
758 if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
759 fBaseline = 1;
760 }
761 if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){
762 db_close(0);
763 diff_tk(fBaseline ? "stash show" : "stash diff", 3);
764 return;
765 }
766 diff_options(&DCfg, zCmd[0]=='g', 0);
767
+1 -1
--- src/tar.c
+++ src/tar.c
@@ -499,11 +499,11 @@
499499
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
500500
if( pManifest ){
501501
int flg, eflg = 0;
502502
mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
503503
if( pTar ) tar_begin(mTime);
504
- flg = db_get_manifest_setting();
504
+ flg = db_get_manifest_setting(blob_str(&hash));
505505
if( flg ){
506506
/* eflg is the effective flags, taking include/exclude into account */
507507
if( (pInclude==0 || glob_match(pInclude, "manifest"))
508508
&& !glob_match(pExclude, "manifest")
509509
&& (flg & MFESTFLG_RAW) ){
510510
--- src/tar.c
+++ src/tar.c
@@ -499,11 +499,11 @@
499 pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
500 if( pManifest ){
501 int flg, eflg = 0;
502 mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
503 if( pTar ) tar_begin(mTime);
504 flg = db_get_manifest_setting();
505 if( flg ){
506 /* eflg is the effective flags, taking include/exclude into account */
507 if( (pInclude==0 || glob_match(pInclude, "manifest"))
508 && !glob_match(pExclude, "manifest")
509 && (flg & MFESTFLG_RAW) ){
510
--- src/tar.c
+++ src/tar.c
@@ -499,11 +499,11 @@
499 pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
500 if( pManifest ){
501 int flg, eflg = 0;
502 mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
503 if( pTar ) tar_begin(mTime);
504 flg = db_get_manifest_setting(blob_str(&hash));
505 if( flg ){
506 /* eflg is the effective flags, taking include/exclude into account */
507 if( (pInclude==0 || glob_match(pInclude, "manifest"))
508 && !glob_match(pExclude, "manifest")
509 && (flg & MFESTFLG_RAW) ){
510
+12 -3
--- src/terminal.c
+++ src/terminal.c
@@ -60,31 +60,40 @@
6060
memset(t, 0, sizeof(*t));
6161
6262
#if defined(TIOCGSIZE)
6363
{
6464
struct ttysize ts;
65
- if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){
65
+ if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
66
+ || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0
67
+ || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0
68
+ ){
6669
t->nColumns = ts.ts_cols;
6770
t->nLines = ts.ts_lines;
6871
return 1;
6972
}
7073
return 0;
7174
}
7275
#elif defined(TIOCGWINSZ)
7376
{
7477
struct winsize ws;
75
- if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){
78
+ if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0
79
+ || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0
80
+ || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0
81
+ ){
7682
t->nColumns = ws.ws_col;
7783
t->nLines = ws.ws_row;
7884
return 1;
7985
}
8086
return 0;
8187
}
8288
#elif defined(_WIN32)
8389
{
8490
CONSOLE_SCREEN_BUFFER_INFO csbi;
85
- if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){
91
+ if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
92
+ || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)
93
+ || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi)
94
+ ){
8695
t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
8796
t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
8897
return 1;
8998
}
9099
return 0;
91100
--- src/terminal.c
+++ src/terminal.c
@@ -60,31 +60,40 @@
60 memset(t, 0, sizeof(*t));
61
62 #if defined(TIOCGSIZE)
63 {
64 struct ttysize ts;
65 if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){
 
 
 
66 t->nColumns = ts.ts_cols;
67 t->nLines = ts.ts_lines;
68 return 1;
69 }
70 return 0;
71 }
72 #elif defined(TIOCGWINSZ)
73 {
74 struct winsize ws;
75 if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){
 
 
 
76 t->nColumns = ws.ws_col;
77 t->nLines = ws.ws_row;
78 return 1;
79 }
80 return 0;
81 }
82 #elif defined(_WIN32)
83 {
84 CONSOLE_SCREEN_BUFFER_INFO csbi;
85 if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){
 
 
 
86 t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
87 t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
88 return 1;
89 }
90 return 0;
91
--- src/terminal.c
+++ src/terminal.c
@@ -60,31 +60,40 @@
60 memset(t, 0, sizeof(*t));
61
62 #if defined(TIOCGSIZE)
63 {
64 struct ttysize ts;
65 if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
66 || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0
67 || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0
68 ){
69 t->nColumns = ts.ts_cols;
70 t->nLines = ts.ts_lines;
71 return 1;
72 }
73 return 0;
74 }
75 #elif defined(TIOCGWINSZ)
76 {
77 struct winsize ws;
78 if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0
79 || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0
80 || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0
81 ){
82 t->nColumns = ws.ws_col;
83 t->nLines = ws.ws_row;
84 return 1;
85 }
86 return 0;
87 }
88 #elif defined(_WIN32)
89 {
90 CONSOLE_SCREEN_BUFFER_INFO csbi;
91 if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
92 || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)
93 || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi)
94 ){
95 t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
96 t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
97 return 1;
98 }
99 return 0;
100
+183 -60
--- src/timeline.c
+++ src/timeline.c
@@ -221,11 +221,28 @@
221221
vid = db_lget_int("checkout", 0);
222222
}
223223
zPrevDate[0] = 0;
224224
mxWikiLen = db_get_int("timeline-max-comment", 0);
225225
dateFormat = db_get_int("timeline-date-format", 0);
226
+ /*
227
+ ** SETTING: timeline-truncate-at-blank boolean default=off
228
+ **
229
+ ** If enabled, check-in comments displayed on the timeline are truncated
230
+ ** at the first blank line of the comment text. The comment text after
231
+ ** the first blank line is only seen in the /info or similar pages that
232
+ ** show details about the check-in.
233
+ */
226234
bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
235
+ /*
236
+ ** SETTING: timeline-tslink-info boolean default=off
237
+ **
238
+ ** The hyperlink on the timestamp associated with each timeline entry,
239
+ ** on the far left-hand side of the screen, normally targets another
240
+ ** /timeline page that shows the entry in context. However, if this
241
+ ** option is turned on, that hyperlink targets the /info page showing
242
+ ** the details of the entry.
243
+ */
227244
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
228245
if( (tmFlags & TIMELINE_VIEWS)==0 ){
229246
tmFlags |= timeline_ss_cookie();
230247
}
231248
if( tmFlags & TIMELINE_COLUMNAR ){
@@ -1092,24 +1109,39 @@
10921109
}
10931110
10941111
/*
10951112
** Convert a symbolic name used as an argument to the a=, b=, or c=
10961113
** query parameters of timeline into a julianday mtime value.
1114
+**
1115
+** If pzDisplay is not null, then display text for the symbolic name might
1116
+** be written into *pzDisplay. But that is not guaranteed.
1117
+**
1118
+** If bRoundUp is true and the symbolic name is a timestamp with less
1119
+** than millisecond resolution, then the timestamp is rounding up to the
1120
+** largest millisecond consistent with that timestamp. If bRoundUp is
1121
+** false, then the resulting time is obtained by extending the timestamp
1122
+** with zeros (hence rounding down). Use bRoundUp==1 if the result
1123
+** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
1124
+** will be used in mtime>=$RESULT.
10971125
*/
1098
-double symbolic_name_to_mtime(const char *z, const char **pzDisplay){
1126
+double symbolic_name_to_mtime(
1127
+ const char *z, /* Input symbolic name */
1128
+ const char **pzDisplay, /* Perhaps write display text here, if not NULL */
1129
+ int bRoundUp /* Round up if true */
1130
+){
10991131
double mtime;
11001132
int rid;
11011133
const char *zDate;
11021134
if( z==0 ) return -1.0;
11031135
if( fossil_isdate(z) ){
11041136
mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
11051137
if( mtime>0.0 ) return mtime;
11061138
}
1107
- zDate = fossil_expand_datetime(z, 1);
1139
+ zDate = fossil_expand_datetime(z, 1, bRoundUp);
11081140
if( zDate!=0 ){
11091141
mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
1110
- fossil_roundup_date(zDate));
1142
+ bRoundUp ? fossil_roundup_date(zDate) : zDate);
11111143
if( mtime>0.0 ){
11121144
if( pzDisplay ) *pzDisplay = fossil_strdup(zDate);
11131145
return mtime;
11141146
}
11151147
}
@@ -1381,11 +1413,11 @@
13811413
** return 0.
13821414
*/
13831415
static int timeline_endpoint(
13841416
int iFrom, /* Starting point */
13851417
const char *zEnd, /* Tag we are searching for */
1386
- int bForward /* 1: forwards in time (descendents) 0: backwards */
1418
+ int bForward /* 1: forwards in time (descendants) 0: backwards */
13871419
){
13881420
int tagId;
13891421
int endId = 0;
13901422
Stmt q;
13911423
int ans = 0;
@@ -1513,17 +1545,17 @@
15131545
/*
15141546
** COMMAND: test-endpoint
15151547
**
15161548
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
15171549
**
1518
-** Show the first check-in with TAG that is a descendent or ancestor
1519
-** of BASE. The first descendent checkin is shown by default. Use
1550
+** Show the first check-in with TAG that is a descendant or ancestor
1551
+** of BASE. The first descendant checkin is shown by default. Use
15201552
** the --backto to see the first ancestor checkin.
15211553
**
15221554
** Options:
15231555
**
1524
-** --backto Show ancestor. Others defaults to descendents.
1556
+** --backto Show ancestor. Others defaults to descendants.
15251557
*/
15261558
void timeline_test_endpoint(void){
15271559
int bForward = find_option("backto",0,0)==0;
15281560
int from_rid;
15291561
int ans;
@@ -1703,11 +1735,11 @@
17031735
int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */
17041736
int bShort = P("shortest")!=0; /* shortest possible path */
17051737
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
17061738
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
17071739
int pd_rid;
1708
- const char *zDPName; /* Value of p=, d=, or dp= params */
1740
+ const char *zDPNameP, *zDPNameD; /* Value of p=, d=, or dp= params */
17091741
double rBefore, rAfter, rCirca; /* Boundary times */
17101742
const char *z;
17111743
char *zOlderButton = 0; /* URL for Older button at the bottom */
17121744
char *zOlderButtonLabel = 0; /* Label for the Older Button */
17131745
char *zNewerButton = 0; /* URL for Newer button at the top */
@@ -1770,20 +1802,20 @@
17701802
}else{
17711803
nEntry = 50;
17721804
}
17731805
17741806
/* Query parameters d=, p=, and f= and variants */
1775
- p_rid = name_choice("p","p2", &zDPName);
1776
- d_rid = name_choice("d","d2", &zDPName);
1807
+ p_rid = name_choice("p","p2", &zDPNameP);
1808
+ d_rid = name_choice("d","d2", &zDPNameD);
17771809
z = P("f");
17781810
f_rid = z ? name_to_typed_rid(z,"ci") : 0;
17791811
z = P("df");
17801812
if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
17811813
nEntry = 0;
17821814
useDividers = 0;
17831815
cgi_replace_query_parameter("d",fossil_strdup(z));
1784
- zDPName = z;
1816
+ zDPNameD = zDPNameP = z;
17851817
}
17861818
17871819
/* Undocumented query parameter to set JS mode */
17881820
builtin_set_js_delivery_mode(P("jsmode"),1);
17891821
@@ -1803,13 +1835,14 @@
18031835
showCherrypicks = 0;
18041836
}
18051837
18061838
/* To view the timeline, must have permission to read project data.
18071839
*/
1808
- pd_rid = name_choice("dp","dp2",&zDPName);
1840
+ pd_rid = name_choice("dp","dp2",&zDPNameP);
18091841
if( pd_rid ){
18101842
p_rid = d_rid = pd_rid;
1843
+ zDPNameD = zDPNameP;
18111844
}
18121845
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
18131846
|| (bisectLocal && !g.perm.Setup)
18141847
){
18151848
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
@@ -2226,37 +2259,47 @@
22262259
}
22272260
}
22282261
}
22292262
addFileGlobDescription(zChng, &desc);
22302263
}else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
2231
- /* If p= or d= is present, ignore all other parameters other than n= */
2232
- char *zUuid;
2233
- const char *zCiName;
2264
+ /* If either p= or d= or both are present, ignore all other parameters
2265
+ ** other than n=, ft=, and bt= */
2266
+ const char *zBaseName = 0;
22342267
int np = 0, nd;
22352268
const char *zBackTo = 0;
22362269
const char *zFwdTo = 0;
22372270
int ridBackTo = 0;
22382271
int ridFwdTo = 0;
2272
+ int bBackAdded = 0; /* True if the zBackTo node was added */
2273
+ int bFwdAdded = 0; /* True if the zBackTo node was added */
2274
+ int bSeparateDandP = 0; /* p_rid & d_rid both exist and are distinct */
22392275
22402276
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2241
- if( p_rid && d_rid ){
2242
- if( p_rid!=d_rid ) p_rid = d_rid;
2243
- if( !haveParameterN ) nEntry = 10;
2277
+ if( p_rid && d_rid && p_rid!=d_rid ){
2278
+ bSeparateDandP = 1;
2279
+ db_multi_exec(
2280
+ "CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)"
2281
+ );
2282
+ }else{
2283
+ zBaseName = p_rid ? zDPNameP : zDPNameD;
22442284
}
22452285
db_multi_exec(
2246
- "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
2286
+ "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
22472287
);
22482288
add_extra_rids("ok", P("x"));
2249
- zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
2250
- p_rid ? p_rid : d_rid);
2251
- zCiName = zDPName;
2252
- if( zCiName==0 ) zCiName = zUuid;
22532289
blob_append_sql(&sql, " AND event.objid IN ok");
22542290
nd = 0;
22552291
if( d_rid ){
22562292
double rStopTime = 9e99;
22572293
zFwdTo = P("ft");
2294
+ if( zFwdTo && bSeparateDandP ){
2295
+ if( zError==0 ){
2296
+ zError = "Cannot use the ft= query parameter when both p= and d= "
2297
+ "are used and have distinct values.";
2298
+ }
2299
+ zFwdTo = 0;
2300
+ }
22582301
if( zFwdTo ){
22592302
double rStartDate = mtime_of_rid(d_rid, 0.0);
22602303
ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
22612304
if( ridFwdTo==0 ){
22622305
ridFwdTo = name_to_typed_rid(zBackTo,"ci");
@@ -2263,10 +2306,13 @@
22632306
}
22642307
if( ridFwdTo ){
22652308
if( !haveParameterN ) nEntry = 0;
22662309
rStopTime = mtime_of_rid(ridFwdTo, 9e99);
22672310
}
2311
+ }else if( bSeparateDandP ){
2312
+ rStopTime = mtime_of_rid(p_rid, 9e99);
2313
+ nEntry = 0;
22682314
}
22692315
if( rStopTime<9e99 ){
22702316
rStopTime += 5.8e-6; /* Round up by 1/2 second */
22712317
}
22722318
db_multi_exec(
@@ -2279,71 +2325,111 @@
22792325
" ORDER BY 2\n"
22802326
")\n"
22812327
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
22822328
d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
22832329
);
2284
- nd = db_int(0, "SELECT count(*)-1 FROM ok");
2285
- if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2286
- if( nd>0 || p_rid==0 ){
2287
- blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2330
+ if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){
2331
+ db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo);
2332
+ bFwdAdded = 1;
22882333
}
2289
- if( useDividers && !selectedRid ) selectedRid = d_rid;
2290
- db_multi_exec("DELETE FROM ok");
2334
+ if( bSeparateDandP ){
2335
+ db_multi_exec(
2336
+ "INSERT INTO ok_d SELECT rid FROM ok;"
2337
+ "DELETE FROM ok;"
2338
+ );
2339
+ }else{
2340
+ nd = db_int(0, "SELECT count(*)-1 FROM ok");
2341
+ if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2342
+ if( nd>0 || p_rid==0 ){
2343
+ blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2344
+ }
2345
+ if( useDividers && !selectedRid ) selectedRid = d_rid;
2346
+ db_multi_exec("DELETE FROM ok");
2347
+ }
22912348
}
22922349
if( p_rid ){
22932350
zBackTo = P("bt");
2351
+ if( zBackTo && bSeparateDandP ){
2352
+ if( zError==0 ){
2353
+ zError = "Cannot use the bt= query parameter when both p= and d= "
2354
+ "are used and have distinct values.";
2355
+ }
2356
+ zBackTo = 0;
2357
+ }
22942358
if( zBackTo ){
22952359
double rDateLimit = mtime_of_rid(p_rid, 0.0);
22962360
ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
22972361
if( ridBackTo==0 ){
22982362
ridBackTo = name_to_typed_rid(zBackTo,"ci");
22992363
}
23002364
if( ridBackTo && !haveParameterN ) nEntry = 0;
2365
+ }else if( bSeparateDandP ){
2366
+ ridBackTo = d_rid;
2367
+ nEntry = 0;
23012368
}
23022369
compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
2303
- np = db_int(0, "SELECT count(*)-1 FROM ok");
2304
- if( np>0 || nd==0 ){
2305
- if( nd>0 ) blob_appendf(&desc, " and ");
2306
- blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
2370
+ if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){
2371
+ db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
2372
+ bBackAdded = 1;
2373
+ }
2374
+ if( bSeparateDandP ){
2375
+ db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;");
23072376
db_multi_exec("%s", blob_sql_text(&sql));
2377
+ }else{
2378
+ np = db_int(0, "SELECT count(*)-1 FROM ok");
2379
+ if( np>0 || nd==0 ){
2380
+ if( nd>0 ) blob_appendf(&desc, " and ");
2381
+ blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
2382
+ db_multi_exec("%s", blob_sql_text(&sql));
2383
+ }
2384
+ if( useDividers && !selectedRid ) selectedRid = p_rid;
23082385
}
2309
- if( useDividers && !selectedRid ) selectedRid = p_rid;
23102386
}
2311
-
2312
- blob_appendf(&desc, " of %z%h</a>",
2313
- href("%R/info?name=%h", zCiName), zCiName);
2387
+ if( bSeparateDandP ){
2388
+ int n = db_int(0, "SELECT count(*) FROM ok");
2389
+ blob_reset(&desc);
2390
+ blob_appendf(&desc,
2391
+ "%d check-ins that are both ancestors of %z%h</a>"
2392
+ " and descendants of %z%h</a>",
2393
+ n,
2394
+ href("%R/info?name=%h",zDPNameP),zDPNameP,
2395
+ href("%R/info?name=%h",zDPNameD),zDPNameD
2396
+ );
2397
+ ridBackTo = 0;
2398
+ ridFwdTo = 0;
2399
+ }else{
2400
+ blob_appendf(&desc, " of %z%h</a>",
2401
+ href("%R/info?name=%h", zBaseName), zBaseName);
2402
+ }
23142403
if( ridBackTo ){
23152404
if( np==0 ){
23162405
blob_reset(&desc);
23172406
blob_appendf(&desc,
2318
- "Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
2319
- href("%R/info?name=%h",zCiName), zCiName,
2407
+ "Check-in %z%h</a> only (%z%h</a> does not precede it)",
2408
+ href("%R/info?name=%h",zBaseName), zBaseName,
23202409
href("%R/info?name=%h",zBackTo), zBackTo);
23212410
}else{
2322
- blob_appendf(&desc, " back to %z%h</a>",
2323
- href("%R/info?name=%h",zBackTo), zBackTo);
2411
+ blob_appendf(&desc, " back to %z%h</a>%s",
2412
+ href("%R/info?name=%h",zBackTo), zBackTo,
2413
+ bBackAdded ? " (not a direct anscestor)" : "");
23242414
if( ridFwdTo && zFwdTo ){
2325
- blob_appendf(&desc, " and up to %z%h</a>",
2326
- href("%R/info?name=%h",zFwdTo), zFwdTo);
2415
+ blob_appendf(&desc, " and up to %z%h</a>%s",
2416
+ href("%R/info?name=%h",zFwdTo), zFwdTo,
2417
+ bFwdAdded ? " (not a direct descendant)" : "");
23272418
}
23282419
}
23292420
}else if( ridFwdTo ){
23302421
if( nd==0 ){
23312422
blob_reset(&desc);
23322423
blob_appendf(&desc,
2333
- "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
2334
- href("%R/info?name=%h",zCiName), zCiName,
2424
+ "Check-in %z%h</a> only (%z%h</a> does not follow it)",
2425
+ href("%R/info?name=%h",zBaseName), zBaseName,
23352426
href("%R/info?name=%h",zFwdTo), zFwdTo);
23362427
}else{
2337
- blob_appendf(&desc, " up to %z%h</a>",
2338
- href("%R/info?name=%h",zFwdTo), zFwdTo);
2339
- }
2340
- }
2341
- if( d_rid ){
2342
- if( p_rid ){
2343
- /* If both p= and d= are set, we don't have the uuid of d yet. */
2344
- zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
2428
+ blob_appendf(&desc, " up to %z%h</a>%s",
2429
+ href("%R/info?name=%h",zFwdTo), zFwdTo,
2430
+ bFwdAdded ? " (not a direct descendant)":"");
23452431
}
23462432
}
23472433
if( advancedMenu ){
23482434
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
23492435
}
@@ -2813,13 +2899,13 @@
28132899
blob_append_sql(&cond,
28142900
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
28152901
zSearch, zSearch);
28162902
}
28172903
}
2818
- rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
2819
- rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
2820
- rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
2904
+ rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1);
2905
+ rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0);
2906
+ rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0);
28212907
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
28222908
if( rAfter>0.0 ){
28232909
if( rBefore>0.0 ){
28242910
blob_append_sql(&sql,
28252911
" AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
@@ -2956,11 +3042,11 @@
29563042
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
29573043
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
29583044
zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
29593045
}
29603046
if( zDate ){
2961
- rDate = symbolic_name_to_mtime(zDate, 0);
3047
+ rDate = symbolic_name_to_mtime(zDate, 0, 0);
29623048
if( db_int(0,
29633049
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
29643050
" WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
29653051
rDate-ONE_SECOND, blob_sql_text(&cond))
29663052
){
@@ -2972,11 +3058,11 @@
29723058
zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
29733059
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
29743060
zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
29753061
}
29763062
if( zDate ){
2977
- rDate = symbolic_name_to_mtime(zDate, 0);
3063
+ rDate = symbolic_name_to_mtime(zDate, 0, 0);
29783064
if( db_int(0,
29793065
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
29803066
" WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
29813067
rDate+ONE_SECOND, blob_sql_text(&cond))
29823068
){
@@ -3015,11 +3101,11 @@
30153101
style_submenu_element("Advanced", "%s",
30163102
url_render(&url, "advm", "1", "udc", "1"));
30173103
}
30183104
if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
30193105
if( useDividers && zMark && zMark[0] ){
3020
- double r = symbolic_name_to_mtime(zMark, 0);
3106
+ double r = symbolic_name_to_mtime(zMark, 0, 0);
30213107
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
30223108
}
30233109
blob_zero(&sql);
30243110
if( PB("oldestfirst") ){
30253111
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
@@ -3381,22 +3467,54 @@
33813467
fossil_print("+++ no more data (%d) +++\n", nEntry);
33823468
}
33833469
}
33843470
if( fchngQueryInit ) db_finalize(&fchngQuery);
33853471
}
3472
+
3473
+/*
3474
+** wiki_to_text(TEXT)
3475
+**
3476
+** Return a plain-text rendering of Fossil-Wiki TEXT.
3477
+*/
3478
+static void wiki_to_text_sqlfunc(
3479
+ sqlite3_context *context,
3480
+ int argc,
3481
+ sqlite3_value **argv
3482
+){
3483
+ const char *zIn, *zOut;
3484
+ int nIn, nOut;
3485
+ Blob in, html, txt;
3486
+ zIn = (const char*)sqlite3_value_text(argv[0]);
3487
+ if( zIn==0 ) return;
3488
+ nIn = sqlite3_value_bytes(argv[0]);
3489
+ blob_init(&in, zIn, nIn);
3490
+ blob_init(&html, 0, 0);
3491
+ wiki_convert(&in, &html, WIKI_INLINE);
3492
+ blob_reset(&in);
3493
+ blob_init(&txt, 0, 0);
3494
+ html_to_plaintext(blob_str(&html), &txt);
3495
+ blob_reset(&html);
3496
+ nOut = blob_size(&txt);
3497
+ zOut = blob_str(&txt);
3498
+ while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; }
3499
+ while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; }
3500
+ sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT);
3501
+ blob_reset(&txt);
3502
+}
33863503
33873504
/*
33883505
** Return a pointer to a static string that forms the basis for
33893506
** a timeline query for display on a TTY.
33903507
*/
33913508
const char *timeline_query_for_tty(void){
3509
+ static int once = 0;
33923510
static const char zBaseSql[] =
33933511
@ SELECT
33943512
@ blob.rid AS rid,
33953513
@ uuid,
33963514
@ datetime(event.mtime,toLocal()) AS mDateTime,
3397
- @ coalesce(ecomment,comment)
3515
+ @ wiki_to_text(coalesce(ecomment,comment))
33983516
@ || ' (user: ' || coalesce(euser,user,'?')
33993517
@ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
34003518
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
34013519
@ FROM tag, tagxref
34023520
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
@@ -3420,10 +3538,15 @@
34203538
@ AND tagxref.tagtype>0
34213539
@ AND tagxref.rid=blob.rid
34223540
@ WHERE blob.rid=event.objid
34233541
@ AND tag.tagname='branch'
34243542
;
3543
+ if( !once && g.db ){
3544
+ once = 1;
3545
+ sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0,
3546
+ wiki_to_text_sqlfunc, 0, 0);
3547
+ }
34253548
return zBaseSql;
34263549
}
34273550
34283551
/*
34293552
** Return true if the input string is a date in the ISO 8601 format:
34303553
--- src/timeline.c
+++ src/timeline.c
@@ -221,11 +221,28 @@
221 vid = db_lget_int("checkout", 0);
222 }
223 zPrevDate[0] = 0;
224 mxWikiLen = db_get_int("timeline-max-comment", 0);
225 dateFormat = db_get_int("timeline-date-format", 0);
 
 
 
 
 
 
 
 
226 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
 
 
 
 
 
 
 
 
 
227 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
228 if( (tmFlags & TIMELINE_VIEWS)==0 ){
229 tmFlags |= timeline_ss_cookie();
230 }
231 if( tmFlags & TIMELINE_COLUMNAR ){
@@ -1092,24 +1109,39 @@
1092 }
1093
1094 /*
1095 ** Convert a symbolic name used as an argument to the a=, b=, or c=
1096 ** query parameters of timeline into a julianday mtime value.
 
 
 
 
 
 
 
 
 
 
 
1097 */
1098 double symbolic_name_to_mtime(const char *z, const char **pzDisplay){
 
 
 
 
1099 double mtime;
1100 int rid;
1101 const char *zDate;
1102 if( z==0 ) return -1.0;
1103 if( fossil_isdate(z) ){
1104 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
1105 if( mtime>0.0 ) return mtime;
1106 }
1107 zDate = fossil_expand_datetime(z, 1);
1108 if( zDate!=0 ){
1109 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
1110 fossil_roundup_date(zDate));
1111 if( mtime>0.0 ){
1112 if( pzDisplay ) *pzDisplay = fossil_strdup(zDate);
1113 return mtime;
1114 }
1115 }
@@ -1381,11 +1413,11 @@
1381 ** return 0.
1382 */
1383 static int timeline_endpoint(
1384 int iFrom, /* Starting point */
1385 const char *zEnd, /* Tag we are searching for */
1386 int bForward /* 1: forwards in time (descendents) 0: backwards */
1387 ){
1388 int tagId;
1389 int endId = 0;
1390 Stmt q;
1391 int ans = 0;
@@ -1513,17 +1545,17 @@
1513 /*
1514 ** COMMAND: test-endpoint
1515 **
1516 ** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
1517 **
1518 ** Show the first check-in with TAG that is a descendent or ancestor
1519 ** of BASE. The first descendent checkin is shown by default. Use
1520 ** the --backto to see the first ancestor checkin.
1521 **
1522 ** Options:
1523 **
1524 ** --backto Show ancestor. Others defaults to descendents.
1525 */
1526 void timeline_test_endpoint(void){
1527 int bForward = find_option("backto",0,0)==0;
1528 int from_rid;
1529 int ans;
@@ -1703,11 +1735,11 @@
1703 int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */
1704 int bShort = P("shortest")!=0; /* shortest possible path */
1705 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
1706 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
1707 int pd_rid;
1708 const char *zDPName; /* Value of p=, d=, or dp= params */
1709 double rBefore, rAfter, rCirca; /* Boundary times */
1710 const char *z;
1711 char *zOlderButton = 0; /* URL for Older button at the bottom */
1712 char *zOlderButtonLabel = 0; /* Label for the Older Button */
1713 char *zNewerButton = 0; /* URL for Newer button at the top */
@@ -1770,20 +1802,20 @@
1770 }else{
1771 nEntry = 50;
1772 }
1773
1774 /* Query parameters d=, p=, and f= and variants */
1775 p_rid = name_choice("p","p2", &zDPName);
1776 d_rid = name_choice("d","d2", &zDPName);
1777 z = P("f");
1778 f_rid = z ? name_to_typed_rid(z,"ci") : 0;
1779 z = P("df");
1780 if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
1781 nEntry = 0;
1782 useDividers = 0;
1783 cgi_replace_query_parameter("d",fossil_strdup(z));
1784 zDPName = z;
1785 }
1786
1787 /* Undocumented query parameter to set JS mode */
1788 builtin_set_js_delivery_mode(P("jsmode"),1);
1789
@@ -1803,13 +1835,14 @@
1803 showCherrypicks = 0;
1804 }
1805
1806 /* To view the timeline, must have permission to read project data.
1807 */
1808 pd_rid = name_choice("dp","dp2",&zDPName);
1809 if( pd_rid ){
1810 p_rid = d_rid = pd_rid;
 
1811 }
1812 if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
1813 || (bisectLocal && !g.perm.Setup)
1814 ){
1815 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
@@ -2226,37 +2259,47 @@
2226 }
2227 }
2228 }
2229 addFileGlobDescription(zChng, &desc);
2230 }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
2231 /* If p= or d= is present, ignore all other parameters other than n= */
2232 char *zUuid;
2233 const char *zCiName;
2234 int np = 0, nd;
2235 const char *zBackTo = 0;
2236 const char *zFwdTo = 0;
2237 int ridBackTo = 0;
2238 int ridFwdTo = 0;
 
 
 
2239
2240 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2241 if( p_rid && d_rid ){
2242 if( p_rid!=d_rid ) p_rid = d_rid;
2243 if( !haveParameterN ) nEntry = 10;
 
 
 
 
2244 }
2245 db_multi_exec(
2246 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
2247 );
2248 add_extra_rids("ok", P("x"));
2249 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
2250 p_rid ? p_rid : d_rid);
2251 zCiName = zDPName;
2252 if( zCiName==0 ) zCiName = zUuid;
2253 blob_append_sql(&sql, " AND event.objid IN ok");
2254 nd = 0;
2255 if( d_rid ){
2256 double rStopTime = 9e99;
2257 zFwdTo = P("ft");
 
 
 
 
 
 
 
2258 if( zFwdTo ){
2259 double rStartDate = mtime_of_rid(d_rid, 0.0);
2260 ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
2261 if( ridFwdTo==0 ){
2262 ridFwdTo = name_to_typed_rid(zBackTo,"ci");
@@ -2263,10 +2306,13 @@
2263 }
2264 if( ridFwdTo ){
2265 if( !haveParameterN ) nEntry = 0;
2266 rStopTime = mtime_of_rid(ridFwdTo, 9e99);
2267 }
 
 
 
2268 }
2269 if( rStopTime<9e99 ){
2270 rStopTime += 5.8e-6; /* Round up by 1/2 second */
2271 }
2272 db_multi_exec(
@@ -2279,71 +2325,111 @@
2279 " ORDER BY 2\n"
2280 ")\n"
2281 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
2282 d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
2283 );
2284 nd = db_int(0, "SELECT count(*)-1 FROM ok");
2285 if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2286 if( nd>0 || p_rid==0 ){
2287 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2288 }
2289 if( useDividers && !selectedRid ) selectedRid = d_rid;
2290 db_multi_exec("DELETE FROM ok");
 
 
 
 
 
 
 
 
 
 
 
 
2291 }
2292 if( p_rid ){
2293 zBackTo = P("bt");
 
 
 
 
 
 
 
2294 if( zBackTo ){
2295 double rDateLimit = mtime_of_rid(p_rid, 0.0);
2296 ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
2297 if( ridBackTo==0 ){
2298 ridBackTo = name_to_typed_rid(zBackTo,"ci");
2299 }
2300 if( ridBackTo && !haveParameterN ) nEntry = 0;
 
 
 
2301 }
2302 compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
2303 np = db_int(0, "SELECT count(*)-1 FROM ok");
2304 if( np>0 || nd==0 ){
2305 if( nd>0 ) blob_appendf(&desc, " and ");
2306 blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
 
 
2307 db_multi_exec("%s", blob_sql_text(&sql));
 
 
 
 
 
 
 
 
2308 }
2309 if( useDividers && !selectedRid ) selectedRid = p_rid;
2310 }
2311
2312 blob_appendf(&desc, " of %z%h</a>",
2313 href("%R/info?name=%h", zCiName), zCiName);
 
 
 
 
 
 
 
 
 
 
 
 
 
2314 if( ridBackTo ){
2315 if( np==0 ){
2316 blob_reset(&desc);
2317 blob_appendf(&desc,
2318 "Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
2319 href("%R/info?name=%h",zCiName), zCiName,
2320 href("%R/info?name=%h",zBackTo), zBackTo);
2321 }else{
2322 blob_appendf(&desc, " back to %z%h</a>",
2323 href("%R/info?name=%h",zBackTo), zBackTo);
 
2324 if( ridFwdTo && zFwdTo ){
2325 blob_appendf(&desc, " and up to %z%h</a>",
2326 href("%R/info?name=%h",zFwdTo), zFwdTo);
 
2327 }
2328 }
2329 }else if( ridFwdTo ){
2330 if( nd==0 ){
2331 blob_reset(&desc);
2332 blob_appendf(&desc,
2333 "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
2334 href("%R/info?name=%h",zCiName), zCiName,
2335 href("%R/info?name=%h",zFwdTo), zFwdTo);
2336 }else{
2337 blob_appendf(&desc, " up to %z%h</a>",
2338 href("%R/info?name=%h",zFwdTo), zFwdTo);
2339 }
2340 }
2341 if( d_rid ){
2342 if( p_rid ){
2343 /* If both p= and d= are set, we don't have the uuid of d yet. */
2344 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
2345 }
2346 }
2347 if( advancedMenu ){
2348 style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
2349 }
@@ -2813,13 +2899,13 @@
2813 blob_append_sql(&cond,
2814 " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
2815 zSearch, zSearch);
2816 }
2817 }
2818 rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
2819 rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
2820 rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
2821 blob_append_sql(&sql, "%s", blob_sql_text(&cond));
2822 if( rAfter>0.0 ){
2823 if( rBefore>0.0 ){
2824 blob_append_sql(&sql,
2825 " AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
@@ -2956,11 +3042,11 @@
2956 zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
2957 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
2958 zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
2959 }
2960 if( zDate ){
2961 rDate = symbolic_name_to_mtime(zDate, 0);
2962 if( db_int(0,
2963 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2964 " WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
2965 rDate-ONE_SECOND, blob_sql_text(&cond))
2966 ){
@@ -2972,11 +3058,11 @@
2972 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
2973 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
2974 zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
2975 }
2976 if( zDate ){
2977 rDate = symbolic_name_to_mtime(zDate, 0);
2978 if( db_int(0,
2979 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2980 " WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
2981 rDate+ONE_SECOND, blob_sql_text(&cond))
2982 ){
@@ -3015,11 +3101,11 @@
3015 style_submenu_element("Advanced", "%s",
3016 url_render(&url, "advm", "1", "udc", "1"));
3017 }
3018 if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
3019 if( useDividers && zMark && zMark[0] ){
3020 double r = symbolic_name_to_mtime(zMark, 0);
3021 if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
3022 }
3023 blob_zero(&sql);
3024 if( PB("oldestfirst") ){
3025 db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
@@ -3381,22 +3467,54 @@
3381 fossil_print("+++ no more data (%d) +++\n", nEntry);
3382 }
3383 }
3384 if( fchngQueryInit ) db_finalize(&fchngQuery);
3385 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3386
3387 /*
3388 ** Return a pointer to a static string that forms the basis for
3389 ** a timeline query for display on a TTY.
3390 */
3391 const char *timeline_query_for_tty(void){
 
3392 static const char zBaseSql[] =
3393 @ SELECT
3394 @ blob.rid AS rid,
3395 @ uuid,
3396 @ datetime(event.mtime,toLocal()) AS mDateTime,
3397 @ coalesce(ecomment,comment)
3398 @ || ' (user: ' || coalesce(euser,user,'?')
3399 @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
3400 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
3401 @ FROM tag, tagxref
3402 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
@@ -3420,10 +3538,15 @@
3420 @ AND tagxref.tagtype>0
3421 @ AND tagxref.rid=blob.rid
3422 @ WHERE blob.rid=event.objid
3423 @ AND tag.tagname='branch'
3424 ;
 
 
 
 
 
3425 return zBaseSql;
3426 }
3427
3428 /*
3429 ** Return true if the input string is a date in the ISO 8601 format:
3430
--- src/timeline.c
+++ src/timeline.c
@@ -221,11 +221,28 @@
221 vid = db_lget_int("checkout", 0);
222 }
223 zPrevDate[0] = 0;
224 mxWikiLen = db_get_int("timeline-max-comment", 0);
225 dateFormat = db_get_int("timeline-date-format", 0);
226 /*
227 ** SETTING: timeline-truncate-at-blank boolean default=off
228 **
229 ** If enabled, check-in comments displayed on the timeline are truncated
230 ** at the first blank line of the comment text. The comment text after
231 ** the first blank line is only seen in the /info or similar pages that
232 ** show details about the check-in.
233 */
234 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
235 /*
236 ** SETTING: timeline-tslink-info boolean default=off
237 **
238 ** The hyperlink on the timestamp associated with each timeline entry,
239 ** on the far left-hand side of the screen, normally targets another
240 ** /timeline page that shows the entry in context. However, if this
241 ** option is turned on, that hyperlink targets the /info page showing
242 ** the details of the entry.
243 */
244 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
245 if( (tmFlags & TIMELINE_VIEWS)==0 ){
246 tmFlags |= timeline_ss_cookie();
247 }
248 if( tmFlags & TIMELINE_COLUMNAR ){
@@ -1092,24 +1109,39 @@
1109 }
1110
1111 /*
1112 ** Convert a symbolic name used as an argument to the a=, b=, or c=
1113 ** query parameters of timeline into a julianday mtime value.
1114 **
1115 ** If pzDisplay is not null, then display text for the symbolic name might
1116 ** be written into *pzDisplay. But that is not guaranteed.
1117 **
1118 ** If bRoundUp is true and the symbolic name is a timestamp with less
1119 ** than millisecond resolution, then the timestamp is rounding up to the
1120 ** largest millisecond consistent with that timestamp. If bRoundUp is
1121 ** false, then the resulting time is obtained by extending the timestamp
1122 ** with zeros (hence rounding down). Use bRoundUp==1 if the result
1123 ** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
1124 ** will be used in mtime>=$RESULT.
1125 */
1126 double symbolic_name_to_mtime(
1127 const char *z, /* Input symbolic name */
1128 const char **pzDisplay, /* Perhaps write display text here, if not NULL */
1129 int bRoundUp /* Round up if true */
1130 ){
1131 double mtime;
1132 int rid;
1133 const char *zDate;
1134 if( z==0 ) return -1.0;
1135 if( fossil_isdate(z) ){
1136 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
1137 if( mtime>0.0 ) return mtime;
1138 }
1139 zDate = fossil_expand_datetime(z, 1, bRoundUp);
1140 if( zDate!=0 ){
1141 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
1142 bRoundUp ? fossil_roundup_date(zDate) : zDate);
1143 if( mtime>0.0 ){
1144 if( pzDisplay ) *pzDisplay = fossil_strdup(zDate);
1145 return mtime;
1146 }
1147 }
@@ -1381,11 +1413,11 @@
1413 ** return 0.
1414 */
1415 static int timeline_endpoint(
1416 int iFrom, /* Starting point */
1417 const char *zEnd, /* Tag we are searching for */
1418 int bForward /* 1: forwards in time (descendants) 0: backwards */
1419 ){
1420 int tagId;
1421 int endId = 0;
1422 Stmt q;
1423 int ans = 0;
@@ -1513,17 +1545,17 @@
1545 /*
1546 ** COMMAND: test-endpoint
1547 **
1548 ** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
1549 **
1550 ** Show the first check-in with TAG that is a descendant or ancestor
1551 ** of BASE. The first descendant checkin is shown by default. Use
1552 ** the --backto to see the first ancestor checkin.
1553 **
1554 ** Options:
1555 **
1556 ** --backto Show ancestor. Others defaults to descendants.
1557 */
1558 void timeline_test_endpoint(void){
1559 int bForward = find_option("backto",0,0)==0;
1560 int from_rid;
1561 int ans;
@@ -1703,11 +1735,11 @@
1735 int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */
1736 int bShort = P("shortest")!=0; /* shortest possible path */
1737 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
1738 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
1739 int pd_rid;
1740 const char *zDPNameP, *zDPNameD; /* Value of p=, d=, or dp= params */
1741 double rBefore, rAfter, rCirca; /* Boundary times */
1742 const char *z;
1743 char *zOlderButton = 0; /* URL for Older button at the bottom */
1744 char *zOlderButtonLabel = 0; /* Label for the Older Button */
1745 char *zNewerButton = 0; /* URL for Newer button at the top */
@@ -1770,20 +1802,20 @@
1802 }else{
1803 nEntry = 50;
1804 }
1805
1806 /* Query parameters d=, p=, and f= and variants */
1807 p_rid = name_choice("p","p2", &zDPNameP);
1808 d_rid = name_choice("d","d2", &zDPNameD);
1809 z = P("f");
1810 f_rid = z ? name_to_typed_rid(z,"ci") : 0;
1811 z = P("df");
1812 if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
1813 nEntry = 0;
1814 useDividers = 0;
1815 cgi_replace_query_parameter("d",fossil_strdup(z));
1816 zDPNameD = zDPNameP = z;
1817 }
1818
1819 /* Undocumented query parameter to set JS mode */
1820 builtin_set_js_delivery_mode(P("jsmode"),1);
1821
@@ -1803,13 +1835,14 @@
1835 showCherrypicks = 0;
1836 }
1837
1838 /* To view the timeline, must have permission to read project data.
1839 */
1840 pd_rid = name_choice("dp","dp2",&zDPNameP);
1841 if( pd_rid ){
1842 p_rid = d_rid = pd_rid;
1843 zDPNameD = zDPNameP;
1844 }
1845 if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
1846 || (bisectLocal && !g.perm.Setup)
1847 ){
1848 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
@@ -2226,37 +2259,47 @@
2259 }
2260 }
2261 }
2262 addFileGlobDescription(zChng, &desc);
2263 }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
2264 /* If either p= or d= or both are present, ignore all other parameters
2265 ** other than n=, ft=, and bt= */
2266 const char *zBaseName = 0;
2267 int np = 0, nd;
2268 const char *zBackTo = 0;
2269 const char *zFwdTo = 0;
2270 int ridBackTo = 0;
2271 int ridFwdTo = 0;
2272 int bBackAdded = 0; /* True if the zBackTo node was added */
2273 int bFwdAdded = 0; /* True if the zBackTo node was added */
2274 int bSeparateDandP = 0; /* p_rid & d_rid both exist and are distinct */
2275
2276 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2277 if( p_rid && d_rid && p_rid!=d_rid ){
2278 bSeparateDandP = 1;
2279 db_multi_exec(
2280 "CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)"
2281 );
2282 }else{
2283 zBaseName = p_rid ? zDPNameP : zDPNameD;
2284 }
2285 db_multi_exec(
2286 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
2287 );
2288 add_extra_rids("ok", P("x"));
 
 
 
 
2289 blob_append_sql(&sql, " AND event.objid IN ok");
2290 nd = 0;
2291 if( d_rid ){
2292 double rStopTime = 9e99;
2293 zFwdTo = P("ft");
2294 if( zFwdTo && bSeparateDandP ){
2295 if( zError==0 ){
2296 zError = "Cannot use the ft= query parameter when both p= and d= "
2297 "are used and have distinct values.";
2298 }
2299 zFwdTo = 0;
2300 }
2301 if( zFwdTo ){
2302 double rStartDate = mtime_of_rid(d_rid, 0.0);
2303 ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
2304 if( ridFwdTo==0 ){
2305 ridFwdTo = name_to_typed_rid(zBackTo,"ci");
@@ -2263,10 +2306,13 @@
2306 }
2307 if( ridFwdTo ){
2308 if( !haveParameterN ) nEntry = 0;
2309 rStopTime = mtime_of_rid(ridFwdTo, 9e99);
2310 }
2311 }else if( bSeparateDandP ){
2312 rStopTime = mtime_of_rid(p_rid, 9e99);
2313 nEntry = 0;
2314 }
2315 if( rStopTime<9e99 ){
2316 rStopTime += 5.8e-6; /* Round up by 1/2 second */
2317 }
2318 db_multi_exec(
@@ -2279,71 +2325,111 @@
2325 " ORDER BY 2\n"
2326 ")\n"
2327 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
2328 d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
2329 );
2330 if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){
2331 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo);
2332 bFwdAdded = 1;
 
2333 }
2334 if( bSeparateDandP ){
2335 db_multi_exec(
2336 "INSERT INTO ok_d SELECT rid FROM ok;"
2337 "DELETE FROM ok;"
2338 );
2339 }else{
2340 nd = db_int(0, "SELECT count(*)-1 FROM ok");
2341 if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2342 if( nd>0 || p_rid==0 ){
2343 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2344 }
2345 if( useDividers && !selectedRid ) selectedRid = d_rid;
2346 db_multi_exec("DELETE FROM ok");
2347 }
2348 }
2349 if( p_rid ){
2350 zBackTo = P("bt");
2351 if( zBackTo && bSeparateDandP ){
2352 if( zError==0 ){
2353 zError = "Cannot use the bt= query parameter when both p= and d= "
2354 "are used and have distinct values.";
2355 }
2356 zBackTo = 0;
2357 }
2358 if( zBackTo ){
2359 double rDateLimit = mtime_of_rid(p_rid, 0.0);
2360 ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
2361 if( ridBackTo==0 ){
2362 ridBackTo = name_to_typed_rid(zBackTo,"ci");
2363 }
2364 if( ridBackTo && !haveParameterN ) nEntry = 0;
2365 }else if( bSeparateDandP ){
2366 ridBackTo = d_rid;
2367 nEntry = 0;
2368 }
2369 compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
2370 if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){
2371 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
2372 bBackAdded = 1;
2373 }
2374 if( bSeparateDandP ){
2375 db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;");
2376 db_multi_exec("%s", blob_sql_text(&sql));
2377 }else{
2378 np = db_int(0, "SELECT count(*)-1 FROM ok");
2379 if( np>0 || nd==0 ){
2380 if( nd>0 ) blob_appendf(&desc, " and ");
2381 blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
2382 db_multi_exec("%s", blob_sql_text(&sql));
2383 }
2384 if( useDividers && !selectedRid ) selectedRid = p_rid;
2385 }
 
2386 }
2387 if( bSeparateDandP ){
2388 int n = db_int(0, "SELECT count(*) FROM ok");
2389 blob_reset(&desc);
2390 blob_appendf(&desc,
2391 "%d check-ins that are both ancestors of %z%h</a>"
2392 " and descendants of %z%h</a>",
2393 n,
2394 href("%R/info?name=%h",zDPNameP),zDPNameP,
2395 href("%R/info?name=%h",zDPNameD),zDPNameD
2396 );
2397 ridBackTo = 0;
2398 ridFwdTo = 0;
2399 }else{
2400 blob_appendf(&desc, " of %z%h</a>",
2401 href("%R/info?name=%h", zBaseName), zBaseName);
2402 }
2403 if( ridBackTo ){
2404 if( np==0 ){
2405 blob_reset(&desc);
2406 blob_appendf(&desc,
2407 "Check-in %z%h</a> only (%z%h</a> does not precede it)",
2408 href("%R/info?name=%h",zBaseName), zBaseName,
2409 href("%R/info?name=%h",zBackTo), zBackTo);
2410 }else{
2411 blob_appendf(&desc, " back to %z%h</a>%s",
2412 href("%R/info?name=%h",zBackTo), zBackTo,
2413 bBackAdded ? " (not a direct anscestor)" : "");
2414 if( ridFwdTo && zFwdTo ){
2415 blob_appendf(&desc, " and up to %z%h</a>%s",
2416 href("%R/info?name=%h",zFwdTo), zFwdTo,
2417 bFwdAdded ? " (not a direct descendant)" : "");
2418 }
2419 }
2420 }else if( ridFwdTo ){
2421 if( nd==0 ){
2422 blob_reset(&desc);
2423 blob_appendf(&desc,
2424 "Check-in %z%h</a> only (%z%h</a> does not follow it)",
2425 href("%R/info?name=%h",zBaseName), zBaseName,
2426 href("%R/info?name=%h",zFwdTo), zFwdTo);
2427 }else{
2428 blob_appendf(&desc, " up to %z%h</a>%s",
2429 href("%R/info?name=%h",zFwdTo), zFwdTo,
2430 bFwdAdded ? " (not a direct descendant)":"");
 
 
 
 
 
2431 }
2432 }
2433 if( advancedMenu ){
2434 style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
2435 }
@@ -2813,13 +2899,13 @@
2899 blob_append_sql(&cond,
2900 " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
2901 zSearch, zSearch);
2902 }
2903 }
2904 rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1);
2905 rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0);
2906 rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0);
2907 blob_append_sql(&sql, "%s", blob_sql_text(&cond));
2908 if( rAfter>0.0 ){
2909 if( rBefore>0.0 ){
2910 blob_append_sql(&sql,
2911 " AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
@@ -2956,11 +3042,11 @@
3042 zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
3043 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3044 zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
3045 }
3046 if( zDate ){
3047 rDate = symbolic_name_to_mtime(zDate, 0, 0);
3048 if( db_int(0,
3049 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
3050 " WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
3051 rDate-ONE_SECOND, blob_sql_text(&cond))
3052 ){
@@ -2972,11 +3058,11 @@
3058 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
3059 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3060 zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
3061 }
3062 if( zDate ){
3063 rDate = symbolic_name_to_mtime(zDate, 0, 0);
3064 if( db_int(0,
3065 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
3066 " WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
3067 rDate+ONE_SECOND, blob_sql_text(&cond))
3068 ){
@@ -3015,11 +3101,11 @@
3101 style_submenu_element("Advanced", "%s",
3102 url_render(&url, "advm", "1", "udc", "1"));
3103 }
3104 if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
3105 if( useDividers && zMark && zMark[0] ){
3106 double r = symbolic_name_to_mtime(zMark, 0, 0);
3107 if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
3108 }
3109 blob_zero(&sql);
3110 if( PB("oldestfirst") ){
3111 db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
@@ -3381,22 +3467,54 @@
3467 fossil_print("+++ no more data (%d) +++\n", nEntry);
3468 }
3469 }
3470 if( fchngQueryInit ) db_finalize(&fchngQuery);
3471 }
3472
3473 /*
3474 ** wiki_to_text(TEXT)
3475 **
3476 ** Return a plain-text rendering of Fossil-Wiki TEXT.
3477 */
3478 static void wiki_to_text_sqlfunc(
3479 sqlite3_context *context,
3480 int argc,
3481 sqlite3_value **argv
3482 ){
3483 const char *zIn, *zOut;
3484 int nIn, nOut;
3485 Blob in, html, txt;
3486 zIn = (const char*)sqlite3_value_text(argv[0]);
3487 if( zIn==0 ) return;
3488 nIn = sqlite3_value_bytes(argv[0]);
3489 blob_init(&in, zIn, nIn);
3490 blob_init(&html, 0, 0);
3491 wiki_convert(&in, &html, WIKI_INLINE);
3492 blob_reset(&in);
3493 blob_init(&txt, 0, 0);
3494 html_to_plaintext(blob_str(&html), &txt);
3495 blob_reset(&html);
3496 nOut = blob_size(&txt);
3497 zOut = blob_str(&txt);
3498 while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; }
3499 while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; }
3500 sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT);
3501 blob_reset(&txt);
3502 }
3503
3504 /*
3505 ** Return a pointer to a static string that forms the basis for
3506 ** a timeline query for display on a TTY.
3507 */
3508 const char *timeline_query_for_tty(void){
3509 static int once = 0;
3510 static const char zBaseSql[] =
3511 @ SELECT
3512 @ blob.rid AS rid,
3513 @ uuid,
3514 @ datetime(event.mtime,toLocal()) AS mDateTime,
3515 @ wiki_to_text(coalesce(ecomment,comment))
3516 @ || ' (user: ' || coalesce(euser,user,'?')
3517 @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
3518 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
3519 @ FROM tag, tagxref
3520 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
@@ -3420,10 +3538,15 @@
3538 @ AND tagxref.tagtype>0
3539 @ AND tagxref.rid=blob.rid
3540 @ WHERE blob.rid=event.objid
3541 @ AND tag.tagname='branch'
3542 ;
3543 if( !once && g.db ){
3544 once = 1;
3545 sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0,
3546 wiki_to_text_sqlfunc, 0, 0);
3547 }
3548 return zBaseSql;
3549 }
3550
3551 /*
3552 ** Return true if the input string is a date in the ISO 8601 format:
3553
--- src/unversioned.c
+++ src/unversioned.c
@@ -218,12 +218,12 @@
218218
}
219219
return 0;
220220
}
221221
222222
/*
223
-** COMMAND: uv# abbreviated-subcommands
224
-** COMMAND: unversioned abbreviated-subcommands
223
+** COMMAND: uv# abbrv-subcom
224
+** COMMAND: unversioned abbrv-subcom
225225
**
226226
** Usage: %fossil unversioned SUBCOMMAND ARGS...
227227
** or: %fossil uv SUBCOMMAND ARGS..
228228
**
229229
** Unversioned files (UV-files) are artifacts that are synced and are available
230230
--- src/unversioned.c
+++ src/unversioned.c
@@ -218,12 +218,12 @@
218 }
219 return 0;
220 }
221
222 /*
223 ** COMMAND: uv# abbreviated-subcommands
224 ** COMMAND: unversioned abbreviated-subcommands
225 **
226 ** Usage: %fossil unversioned SUBCOMMAND ARGS...
227 ** or: %fossil uv SUBCOMMAND ARGS..
228 **
229 ** Unversioned files (UV-files) are artifacts that are synced and are available
230
--- src/unversioned.c
+++ src/unversioned.c
@@ -218,12 +218,12 @@
218 }
219 return 0;
220 }
221
222 /*
223 ** COMMAND: uv# abbrv-subcom
224 ** COMMAND: unversioned abbrv-subcom
225 **
226 ** Usage: %fossil unversioned SUBCOMMAND ARGS...
227 ** or: %fossil uv SUBCOMMAND ARGS..
228 **
229 ** Unversioned files (UV-files) are artifacts that are synced and are available
230
+1 -1
--- src/update.c
+++ src/update.c
@@ -199,11 +199,11 @@
199199
}
200200
}
201201
}
202202
203203
/* If no VERSION is specified on the command-line, then look for a
204
- ** descendent of the current version. If there are multiple descendants,
204
+ ** descendant of the current version. If there are multiple descendants,
205205
** look for one from the same branch as the current version. If there
206206
** are still multiple descendants, show them all and refuse to update
207207
** until the user selects one.
208208
*/
209209
if( tid==0 ){
210210
--- src/update.c
+++ src/update.c
@@ -199,11 +199,11 @@
199 }
200 }
201 }
202
203 /* If no VERSION is specified on the command-line, then look for a
204 ** descendent of the current version. If there are multiple descendants,
205 ** look for one from the same branch as the current version. If there
206 ** are still multiple descendants, show them all and refuse to update
207 ** until the user selects one.
208 */
209 if( tid==0 ){
210
--- src/update.c
+++ src/update.c
@@ -199,11 +199,11 @@
199 }
200 }
201 }
202
203 /* If no VERSION is specified on the command-line, then look for a
204 ** descendant of the current version. If there are multiple descendants,
205 ** look for one from the same branch as the current version. If there
206 ** are still multiple descendants, show them all and refuse to update
207 ** until the user selects one.
208 */
209 if( tid==0 ){
210
+1 -1
--- src/user.c
+++ src/user.c
@@ -298,11 +298,11 @@
298298
** Prompt the user to enter a single line of text.
299299
*/
300300
void prompt_user(const char *zPrompt, Blob *pIn){
301301
char *z;
302302
char zLine[1000];
303
- blob_zero(pIn);
303
+ blob_init(pIn, 0, 0);
304304
fossil_force_newline();
305305
fossil_print("%s", zPrompt);
306306
fflush(stdout);
307307
z = fgets(zLine, sizeof(zLine), stdin);
308308
if( z ){
309309
--- src/user.c
+++ src/user.c
@@ -298,11 +298,11 @@
298 ** Prompt the user to enter a single line of text.
299 */
300 void prompt_user(const char *zPrompt, Blob *pIn){
301 char *z;
302 char zLine[1000];
303 blob_zero(pIn);
304 fossil_force_newline();
305 fossil_print("%s", zPrompt);
306 fflush(stdout);
307 z = fgets(zLine, sizeof(zLine), stdin);
308 if( z ){
309
--- src/user.c
+++ src/user.c
@@ -298,11 +298,11 @@
298 ** Prompt the user to enter a single line of text.
299 */
300 void prompt_user(const char *zPrompt, Blob *pIn){
301 char *z;
302 char zLine[1000];
303 blob_init(pIn, 0, 0);
304 fossil_force_newline();
305 fossil_print("%s", zPrompt);
306 fflush(stdout);
307 z = fgets(zLine, sizeof(zLine), stdin);
308 if( z ){
309
+63 -17
--- src/util.c
+++ src/util.c
@@ -670,28 +670,33 @@
670670
** Search algorithm:
671671
** (1) The local "editor" setting
672672
** (2) The global "editor" setting
673673
** (3) The VISUAL environment variable
674674
** (4) The EDITOR environment variable
675
-** (5) (Windows only:) "notepad.exe"
675
+** (5) Any of the following programs that are available:
676
+** notepad, nano, pico, jove, edit, vi, vim, ed,
676677
*/
677678
const char *fossil_text_editor(void){
678679
const char *zEditor = db_get("editor", 0);
680
+ const char *azStdEd[] = {
681
+ "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed"
682
+ };
683
+ int i = 0;
679684
if( zEditor==0 ){
680685
zEditor = fossil_getenv("VISUAL");
681686
}
682687
if( zEditor==0 ){
683688
zEditor = fossil_getenv("EDITOR");
684689
}
685
-#if defined(_WIN32) || defined(__CYGWIN__)
686
- if( zEditor==0 ){
687
- zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT"));
688
-#if defined(__CYGWIN__)
689
- zEditor = fossil_utf8_to_path(zEditor, 0);
690
-#endif
691
- }
692
-#endif
690
+ while( zEditor==0 && i<count(azStdEd) ){
691
+ if( fossil_app_on_path(azStdEd[i],0) ){
692
+ zEditor = azStdEd[i];
693
+ }else{
694
+ i++;
695
+ }
696
+ }
697
+ if( zEditor && is_false(zEditor) ) zEditor = 0;
693698
return zEditor;
694699
}
695700
696701
/*
697702
** Construct a temporary filename.
@@ -895,35 +900,76 @@
895900
return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
896901
: n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6
897902
: n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
898903
}
899904
900
-#if !defined(_WIN32)
901
-#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
902905
/*
903906
** Search for an executable on the PATH environment variable.
904907
** Return true (1) if found and false (0) if not found.
908
+**
909
+** Print the full pathname of the first location if ePrint==1. Print
910
+** all pathnames for the executable if ePrint==2 or more.
905911
*/
906
-static int binaryOnPath(const char *zBinary){
912
+int fossil_app_on_path(const char *zBinary, int ePrint){
907913
const char *zPath = fossil_getenv("PATH");
908914
char *zFull;
909915
int i;
910916
int bExists;
917
+ int bFound = 0;
911918
while( zPath && zPath[0] ){
919
+#ifdef _WIN32
920
+ while( zPath[0]==';' ) zPath++;
921
+ for(i=0; zPath[i] && zPath[i]!=';'; i++){}
922
+ zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary);
923
+ bExists = file_access(zFull, R_OK);
924
+ if( bExists!=0 ){
925
+ fossil_free(zFull);
926
+ zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary);
927
+ bExists = file_access(zFull, R_OK);
928
+ }
929
+#else
912930
while( zPath[0]==':' ) zPath++;
913931
for(i=0; zPath[i] && zPath[i]!=':'; i++){}
914932
zFull = mprintf("%.*s/%s", i, zPath, zBinary);
915933
bExists = file_access(zFull, X_OK);
934
+#endif
935
+ if( bExists==0 && ePrint ){
936
+ fossil_print("%s\n", zFull);
937
+ }
916938
fossil_free(zFull);
917
- if( bExists==0 ) return 1;
939
+ if( bExists==0 ){
940
+ if( ePrint<2 ) return 1;
941
+ bFound = 1;
942
+ }
918943
zPath += i;
919944
}
920
- return 0;
945
+ return bFound;
921946
}
922
-#endif
923
-#endif
924947
948
+/*
949
+** COMMAND: which*
950
+**
951
+** Usage: fossil which [-a] NAME ...
952
+**
953
+** For each NAME mentioned as an argument, print the first location on the
954
+** on PATH of the executable with that name. Or, show all locations on PATH
955
+** for each argument if the -a option is used.
956
+**
957
+** This command is a substitute for the unix "which" command, which is not
958
+** always available, especially on Windows.
959
+*/
960
+void test_app_on_path(void){
961
+ int i;
962
+ int ePrint = 1;
963
+ if( find_option("all","a",0)!=0 ) ePrint = 2;
964
+ verify_all_options();
965
+ for(i=2; i<g.argc; i++){
966
+ if( fossil_app_on_path(g.argv[i], ePrint)==0 ){
967
+ fossil_print("NOT FOUND: %s\n", g.argv[i]);
968
+ }
969
+ }
970
+}
925971
926972
/*
927973
** Return the name of a command that will launch a web-browser.
928974
*/
929975
const char *fossil_web_browser(void){
@@ -938,11 +984,11 @@
938984
static const char *const azBrowserProg[] =
939985
{ "xdg-open", "gnome-open", "firefox", "google-chrome" };
940986
int i;
941987
zBrowser = "echo";
942988
for(i=0; i<count(azBrowserProg); i++){
943
- if( binaryOnPath(azBrowserProg[i]) ){
989
+ if( fossil_app_on_path(azBrowserProg[i],0) ){
944990
zBrowser = azBrowserProg[i];
945991
break;
946992
}
947993
}
948994
zBrowser = mprintf("%s 2>/dev/null", zBrowser);
949995
--- src/util.c
+++ src/util.c
@@ -670,28 +670,33 @@
670 ** Search algorithm:
671 ** (1) The local "editor" setting
672 ** (2) The global "editor" setting
673 ** (3) The VISUAL environment variable
674 ** (4) The EDITOR environment variable
675 ** (5) (Windows only:) "notepad.exe"
 
676 */
677 const char *fossil_text_editor(void){
678 const char *zEditor = db_get("editor", 0);
 
 
 
 
679 if( zEditor==0 ){
680 zEditor = fossil_getenv("VISUAL");
681 }
682 if( zEditor==0 ){
683 zEditor = fossil_getenv("EDITOR");
684 }
685 #if defined(_WIN32) || defined(__CYGWIN__)
686 if( zEditor==0 ){
687 zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT"));
688 #if defined(__CYGWIN__)
689 zEditor = fossil_utf8_to_path(zEditor, 0);
690 #endif
691 }
692 #endif
693 return zEditor;
694 }
695
696 /*
697 ** Construct a temporary filename.
@@ -895,35 +900,76 @@
895 return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
896 : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6
897 : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
898 }
899
900 #if !defined(_WIN32)
901 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
902 /*
903 ** Search for an executable on the PATH environment variable.
904 ** Return true (1) if found and false (0) if not found.
 
 
 
905 */
906 static int binaryOnPath(const char *zBinary){
907 const char *zPath = fossil_getenv("PATH");
908 char *zFull;
909 int i;
910 int bExists;
 
911 while( zPath && zPath[0] ){
 
 
 
 
 
 
 
 
 
 
 
912 while( zPath[0]==':' ) zPath++;
913 for(i=0; zPath[i] && zPath[i]!=':'; i++){}
914 zFull = mprintf("%.*s/%s", i, zPath, zBinary);
915 bExists = file_access(zFull, X_OK);
 
 
 
 
916 fossil_free(zFull);
917 if( bExists==0 ) return 1;
 
 
 
918 zPath += i;
919 }
920 return 0;
921 }
922 #endif
923 #endif
924
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
925
926 /*
927 ** Return the name of a command that will launch a web-browser.
928 */
929 const char *fossil_web_browser(void){
@@ -938,11 +984,11 @@
938 static const char *const azBrowserProg[] =
939 { "xdg-open", "gnome-open", "firefox", "google-chrome" };
940 int i;
941 zBrowser = "echo";
942 for(i=0; i<count(azBrowserProg); i++){
943 if( binaryOnPath(azBrowserProg[i]) ){
944 zBrowser = azBrowserProg[i];
945 break;
946 }
947 }
948 zBrowser = mprintf("%s 2>/dev/null", zBrowser);
949
--- src/util.c
+++ src/util.c
@@ -670,28 +670,33 @@
670 ** Search algorithm:
671 ** (1) The local "editor" setting
672 ** (2) The global "editor" setting
673 ** (3) The VISUAL environment variable
674 ** (4) The EDITOR environment variable
675 ** (5) Any of the following programs that are available:
676 ** notepad, nano, pico, jove, edit, vi, vim, ed,
677 */
678 const char *fossil_text_editor(void){
679 const char *zEditor = db_get("editor", 0);
680 const char *azStdEd[] = {
681 "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed"
682 };
683 int i = 0;
684 if( zEditor==0 ){
685 zEditor = fossil_getenv("VISUAL");
686 }
687 if( zEditor==0 ){
688 zEditor = fossil_getenv("EDITOR");
689 }
690 while( zEditor==0 && i<count(azStdEd) ){
691 if( fossil_app_on_path(azStdEd[i],0) ){
692 zEditor = azStdEd[i];
693 }else{
694 i++;
695 }
696 }
697 if( zEditor && is_false(zEditor) ) zEditor = 0;
698 return zEditor;
699 }
700
701 /*
702 ** Construct a temporary filename.
@@ -895,35 +900,76 @@
900 return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
901 : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6
902 : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
903 }
904
 
 
905 /*
906 ** Search for an executable on the PATH environment variable.
907 ** Return true (1) if found and false (0) if not found.
908 **
909 ** Print the full pathname of the first location if ePrint==1. Print
910 ** all pathnames for the executable if ePrint==2 or more.
911 */
912 int fossil_app_on_path(const char *zBinary, int ePrint){
913 const char *zPath = fossil_getenv("PATH");
914 char *zFull;
915 int i;
916 int bExists;
917 int bFound = 0;
918 while( zPath && zPath[0] ){
919 #ifdef _WIN32
920 while( zPath[0]==';' ) zPath++;
921 for(i=0; zPath[i] && zPath[i]!=';'; i++){}
922 zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary);
923 bExists = file_access(zFull, R_OK);
924 if( bExists!=0 ){
925 fossil_free(zFull);
926 zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary);
927 bExists = file_access(zFull, R_OK);
928 }
929 #else
930 while( zPath[0]==':' ) zPath++;
931 for(i=0; zPath[i] && zPath[i]!=':'; i++){}
932 zFull = mprintf("%.*s/%s", i, zPath, zBinary);
933 bExists = file_access(zFull, X_OK);
934 #endif
935 if( bExists==0 && ePrint ){
936 fossil_print("%s\n", zFull);
937 }
938 fossil_free(zFull);
939 if( bExists==0 ){
940 if( ePrint<2 ) return 1;
941 bFound = 1;
942 }
943 zPath += i;
944 }
945 return bFound;
946 }
 
 
947
948 /*
949 ** COMMAND: which*
950 **
951 ** Usage: fossil which [-a] NAME ...
952 **
953 ** For each NAME mentioned as an argument, print the first location on the
954 ** on PATH of the executable with that name. Or, show all locations on PATH
955 ** for each argument if the -a option is used.
956 **
957 ** This command is a substitute for the unix "which" command, which is not
958 ** always available, especially on Windows.
959 */
960 void test_app_on_path(void){
961 int i;
962 int ePrint = 1;
963 if( find_option("all","a",0)!=0 ) ePrint = 2;
964 verify_all_options();
965 for(i=2; i<g.argc; i++){
966 if( fossil_app_on_path(g.argv[i], ePrint)==0 ){
967 fossil_print("NOT FOUND: %s\n", g.argv[i]);
968 }
969 }
970 }
971
972 /*
973 ** Return the name of a command that will launch a web-browser.
974 */
975 const char *fossil_web_browser(void){
@@ -938,11 +984,11 @@
984 static const char *const azBrowserProg[] =
985 { "xdg-open", "gnome-open", "firefox", "google-chrome" };
986 int i;
987 zBrowser = "echo";
988 for(i=0; i<count(azBrowserProg); i++){
989 if( fossil_app_on_path(azBrowserProg[i],0) ){
990 zBrowser = azBrowserProg[i];
991 break;
992 }
993 }
994 zBrowser = mprintf("%s 2>/dev/null", zBrowser);
995
+19 -13
--- src/wiki.c
+++ src/wiki.c
@@ -21,10 +21,13 @@
2121
#include "config.h"
2222
#include <assert.h>
2323
#include <ctype.h>
2424
#include "wiki.h"
2525
26
+#define has_prefix(literal_prfx, zStr) \
27
+ (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0)
28
+
2629
/*
2730
** Return true if the input string is a well-formed wiki page name.
2831
**
2932
** Well-formed wiki page names do not begin or end with whitespace,
3033
** and do not contain tabs or other control characters and do not
@@ -420,22 +423,22 @@
420423
*/
421424
int wiki_page_type(const char *zPageName){
422425
if( db_get_boolean("wiki-about",1)==0 ){
423426
return WIKITYPE_NORMAL;
424427
}else
425
- if( sqlite3_strglob("checkin/*", zPageName)==0
428
+ if( has_prefix("checkin/", zPageName)
426429
&& db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
427430
){
428431
return WIKITYPE_CHECKIN;
429432
}else
430
- if( sqlite3_strglob("branch/*", zPageName)==0 ){
433
+ if( has_prefix("branch/", zPageName) ){
431434
return WIKITYPE_BRANCH;
432435
}else
433
- if( sqlite3_strglob("tag/*", zPageName)==0 ){
436
+ if( has_prefix("tag/", zPageName) ){
434437
return WIKITYPE_TAG;
435438
}else
436
- if( sqlite3_strglob("ticket/*", zPageName)==0 ){
439
+ if( has_prefix("ticket/", zPageName) ){
437440
return WIKITYPE_TICKET;
438441
}
439442
return WIKITYPE_NORMAL;
440443
}
441444
@@ -1987,11 +1990,12 @@
19871990
style_submenu_element("All", "%R/wcontent?all=1");
19881991
}
19891992
cgi_check_for_malice();
19901993
showCkBr = db_exists(
19911994
"SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) "
1992
- "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) "
1995
+ "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR "
1996
+ " tn GLOB 'wiki-tag/*' OR tn GLOB 'wiki-ticket/*' ) "
19931997
" AND TYPEOF(tagxref.value+0)='integer'" );
19941998
if( showCkBr ){
19951999
showCkBr = P("showckbr")!=0;
19962000
style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0);
19972001
}
@@ -2017,20 +2021,20 @@
20172021
char *zAge;
20182022
int wcnt = db_column_int(&q, 4);
20192023
char *zWDisplayName;
20202024
20212025
if( !showCkBr &&
2022
- (sqlite3_strglob("checkin/*", zWName)==0 ||
2023
- sqlite3_strglob("branch/*", zWName)==0 ||
2024
- sqlite3_strglob("tag/*", zWName)==0 ||
2025
- sqlite3_strglob("ticket/*", zWName)==0) ){
2026
+ (has_prefix("checkin/", zWName) ||
2027
+ has_prefix("branch/", zWName) ||
2028
+ has_prefix("tag/", zWName) ||
2029
+ has_prefix("ticket/", zWName) )){
20262030
continue;
20272031
}
2028
- if( sqlite3_strglob("checkin/*", zWName)==0 ){
2032
+ if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){
20292033
zWDisplayName = mprintf("%.25s...", zWName);
20302034
}else{
2031
- zWDisplayName = mprintf("%s", zWName);
2035
+ zWDisplayName = fossil_strdup(zWName);
20322036
}
20332037
if( wrid==0 ){
20342038
if( !showAll ) continue;
20352039
@ <tr><td data-sortkey="%h(zSort)">\
20362040
@ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
@@ -2522,12 +2526,14 @@
25222526
const int wrid = db_column_int(&q, 2);
25232527
if(!showAll && !wrid){
25242528
continue;
25252529
}
25262530
if( !showCkBr &&
2527
- (sqlite3_strglob("checkin/*", zName)==0 ||
2528
- sqlite3_strglob("branch/*", zName)==0) ){
2531
+ (has_prefix("checkin/", zName) ||
2532
+ has_prefix("branch/", zName) ||
2533
+ has_prefix("tag/", zName) ||
2534
+ has_prefix("ticket/", zName) ) ){
25292535
continue;
25302536
}
25312537
if( showIds ){
25322538
const char *zUuid = db_column_text(&q, 1);
25332539
fossil_print("%s ",zUuid);
25342540
--- src/wiki.c
+++ src/wiki.c
@@ -21,10 +21,13 @@
21 #include "config.h"
22 #include <assert.h>
23 #include <ctype.h>
24 #include "wiki.h"
25
 
 
 
26 /*
27 ** Return true if the input string is a well-formed wiki page name.
28 **
29 ** Well-formed wiki page names do not begin or end with whitespace,
30 ** and do not contain tabs or other control characters and do not
@@ -420,22 +423,22 @@
420 */
421 int wiki_page_type(const char *zPageName){
422 if( db_get_boolean("wiki-about",1)==0 ){
423 return WIKITYPE_NORMAL;
424 }else
425 if( sqlite3_strglob("checkin/*", zPageName)==0
426 && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
427 ){
428 return WIKITYPE_CHECKIN;
429 }else
430 if( sqlite3_strglob("branch/*", zPageName)==0 ){
431 return WIKITYPE_BRANCH;
432 }else
433 if( sqlite3_strglob("tag/*", zPageName)==0 ){
434 return WIKITYPE_TAG;
435 }else
436 if( sqlite3_strglob("ticket/*", zPageName)==0 ){
437 return WIKITYPE_TICKET;
438 }
439 return WIKITYPE_NORMAL;
440 }
441
@@ -1987,11 +1990,12 @@
1987 style_submenu_element("All", "%R/wcontent?all=1");
1988 }
1989 cgi_check_for_malice();
1990 showCkBr = db_exists(
1991 "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) "
1992 "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) "
 
1993 " AND TYPEOF(tagxref.value+0)='integer'" );
1994 if( showCkBr ){
1995 showCkBr = P("showckbr")!=0;
1996 style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0);
1997 }
@@ -2017,20 +2021,20 @@
2017 char *zAge;
2018 int wcnt = db_column_int(&q, 4);
2019 char *zWDisplayName;
2020
2021 if( !showCkBr &&
2022 (sqlite3_strglob("checkin/*", zWName)==0 ||
2023 sqlite3_strglob("branch/*", zWName)==0 ||
2024 sqlite3_strglob("tag/*", zWName)==0 ||
2025 sqlite3_strglob("ticket/*", zWName)==0) ){
2026 continue;
2027 }
2028 if( sqlite3_strglob("checkin/*", zWName)==0 ){
2029 zWDisplayName = mprintf("%.25s...", zWName);
2030 }else{
2031 zWDisplayName = mprintf("%s", zWName);
2032 }
2033 if( wrid==0 ){
2034 if( !showAll ) continue;
2035 @ <tr><td data-sortkey="%h(zSort)">\
2036 @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
@@ -2522,12 +2526,14 @@
2522 const int wrid = db_column_int(&q, 2);
2523 if(!showAll && !wrid){
2524 continue;
2525 }
2526 if( !showCkBr &&
2527 (sqlite3_strglob("checkin/*", zName)==0 ||
2528 sqlite3_strglob("branch/*", zName)==0) ){
 
 
2529 continue;
2530 }
2531 if( showIds ){
2532 const char *zUuid = db_column_text(&q, 1);
2533 fossil_print("%s ",zUuid);
2534
--- src/wiki.c
+++ src/wiki.c
@@ -21,10 +21,13 @@
21 #include "config.h"
22 #include <assert.h>
23 #include <ctype.h>
24 #include "wiki.h"
25
26 #define has_prefix(literal_prfx, zStr) \
27 (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0)
28
29 /*
30 ** Return true if the input string is a well-formed wiki page name.
31 **
32 ** Well-formed wiki page names do not begin or end with whitespace,
33 ** and do not contain tabs or other control characters and do not
@@ -420,22 +423,22 @@
423 */
424 int wiki_page_type(const char *zPageName){
425 if( db_get_boolean("wiki-about",1)==0 ){
426 return WIKITYPE_NORMAL;
427 }else
428 if( has_prefix("checkin/", zPageName)
429 && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
430 ){
431 return WIKITYPE_CHECKIN;
432 }else
433 if( has_prefix("branch/", zPageName) ){
434 return WIKITYPE_BRANCH;
435 }else
436 if( has_prefix("tag/", zPageName) ){
437 return WIKITYPE_TAG;
438 }else
439 if( has_prefix("ticket/", zPageName) ){
440 return WIKITYPE_TICKET;
441 }
442 return WIKITYPE_NORMAL;
443 }
444
@@ -1987,11 +1990,12 @@
1990 style_submenu_element("All", "%R/wcontent?all=1");
1991 }
1992 cgi_check_for_malice();
1993 showCkBr = db_exists(
1994 "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) "
1995 "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR "
1996 " tn GLOB 'wiki-tag/*' OR tn GLOB 'wiki-ticket/*' ) "
1997 " AND TYPEOF(tagxref.value+0)='integer'" );
1998 if( showCkBr ){
1999 showCkBr = P("showckbr")!=0;
2000 style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0);
2001 }
@@ -2017,20 +2021,20 @@
2021 char *zAge;
2022 int wcnt = db_column_int(&q, 4);
2023 char *zWDisplayName;
2024
2025 if( !showCkBr &&
2026 (has_prefix("checkin/", zWName) ||
2027 has_prefix("branch/", zWName) ||
2028 has_prefix("tag/", zWName) ||
2029 has_prefix("ticket/", zWName) )){
2030 continue;
2031 }
2032 if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){
2033 zWDisplayName = mprintf("%.25s...", zWName);
2034 }else{
2035 zWDisplayName = fossil_strdup(zWName);
2036 }
2037 if( wrid==0 ){
2038 if( !showAll ) continue;
2039 @ <tr><td data-sortkey="%h(zSort)">\
2040 @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
@@ -2522,12 +2526,14 @@
2526 const int wrid = db_column_int(&q, 2);
2527 if(!showAll && !wrid){
2528 continue;
2529 }
2530 if( !showCkBr &&
2531 (has_prefix("checkin/", zName) ||
2532 has_prefix("branch/", zName) ||
2533 has_prefix("tag/", zName) ||
2534 has_prefix("ticket/", zName) ) ){
2535 continue;
2536 }
2537 if( showIds ){
2538 const char *zUuid = db_column_text(&q, 1);
2539 fossil_print("%s ",zUuid);
2540
+37 -7
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -34,10 +34,11 @@
3434
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
3535
#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
3636
#define WIKI_SAFE 0x100 /* Make the result safe for embedding */
3737
#define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */
3838
#define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */
39
+#define WIKI_ADMIN 0x800 /* Ignore g.perm.Hyperlink */
3940
#endif
4041
4142
4243
/*
4344
** These are the only markup attributes allowed.
@@ -1320,11 +1321,11 @@
13201321
zTerm = "";
13211322
}else{
13221323
blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
13231324
zTerm = "]</span>";
13241325
}
1325
- }else if( g.perm.Hyperlink ){
1326
+ }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){
13261327
blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
13271328
zTerm = "]</a>";
13281329
}else{
13291330
zTerm = "";
13301331
}
@@ -1364,10 +1365,24 @@
13641365
}
13651366
if( zExtra ) fossil_free(zExtra);
13661367
assert( (int)strlen(zTerm)<nClose );
13671368
sqlite3_snprintf(nClose, zClose, "%s", zTerm);
13681369
}
1370
+
1371
+/*
1372
+** Check zTarget to see if it looks like a valid hyperlink target.
1373
+** Return true if it does seem valid and false if not.
1374
+*/
1375
+int wiki_valid_link_target(char *zTarget){
1376
+ char zClose[30];
1377
+ Blob notUsed;
1378
+ blob_init(&notUsed, 0, 0);
1379
+ wiki_resolve_hyperlink(&notUsed, WIKI_NOBADLINKS|WIKI_ADMIN,
1380
+ zTarget, zClose, sizeof(zClose)-1, 0, 0);
1381
+ blob_reset(&notUsed);
1382
+ return zClose[0]!=0;
1383
+}
13691384
13701385
/*
13711386
** Check to see if the given parsed markup is the correct
13721387
** </verbatim> tag.
13731388
*/
@@ -1876,11 +1891,11 @@
18761891
** --htmlonly Set the WIKI_HTMLONLY flag
18771892
** --inline Set the WIKI_INLINE flag
18781893
** --linksonly Set the WIKI_LINKSONLY flag
18791894
** --nobadlinks Set the WIKI_NOBADLINKS flag
18801895
** --noblock Set the WIKI_NOBLOCK flag
1881
-** --text Run the output through html_to_plaintext().
1896
+** --text Run the output through html_to_plaintext().
18821897
*/
18831898
void test_wiki_render(void){
18841899
Blob in, out;
18851900
int flags = 0;
18861901
int bText;
@@ -2474,20 +2489,21 @@
24742489
blob_append_char(pOut, nNL ? '\n' : ' ');
24752490
nWS = 1;
24762491
}
24772492
}
24782493
}else if( zIn[0]=='&' ){
2479
- char c = '?';
2494
+ u32 c = '?';
24802495
if( zIn[1]=='#' ){
2481
- int x = atoi(&zIn[1]);
2482
- if( x>0 && x<=127 ) c = x;
2496
+ c = atoi(&zIn[2]);
2497
+ if( c==0 ) c = '?';
24832498
}else{
2484
- static const struct { int n; char c; char *z; } aEntity[] = {
2499
+ static const struct { int n; u32 c; char *z; } aEntity[] = {
24852500
{ 5, '&', "&amp;" },
24862501
{ 4, '<', "&lt;" },
24872502
{ 4, '>', "&gt;" },
24882503
{ 6, ' ', "&nbsp;" },
2504
+ { 6, '"', "&quot;" },
24892505
};
24902506
int jj;
24912507
for(jj=0; jj<count(aEntity); jj++){
24922508
if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
24932509
c = aEntity[jj].c;
@@ -2501,11 +2517,25 @@
25012517
nNL = c=='\n';
25022518
}else{
25032519
if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
25042520
seenText = 1;
25052521
nNL = nWS = 0;
2506
- blob_append_char(pOut, c);
2522
+ if( c<0x00080 ){
2523
+ blob_append_char(pOut, c & 0xff);
2524
+ }else if( c<0x00800 ){
2525
+ blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f));
2526
+ blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
2527
+ }else if( c<0x10000 ){
2528
+ blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f));
2529
+ blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
2530
+ blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
2531
+ }else{
2532
+ blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07));
2533
+ blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f));
2534
+ blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
2535
+ blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
2536
+ }
25072537
}
25082538
}else{
25092539
if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
25102540
seenText = 1;
25112541
nNL = nWS = 0;
25122542
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -34,10 +34,11 @@
34 #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
35 #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
36 #define WIKI_SAFE 0x100 /* Make the result safe for embedding */
37 #define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */
38 #define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */
 
39 #endif
40
41
42 /*
43 ** These are the only markup attributes allowed.
@@ -1320,11 +1321,11 @@
1320 zTerm = "";
1321 }else{
1322 blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
1323 zTerm = "]</span>";
1324 }
1325 }else if( g.perm.Hyperlink ){
1326 blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
1327 zTerm = "]</a>";
1328 }else{
1329 zTerm = "";
1330 }
@@ -1364,10 +1365,24 @@
1364 }
1365 if( zExtra ) fossil_free(zExtra);
1366 assert( (int)strlen(zTerm)<nClose );
1367 sqlite3_snprintf(nClose, zClose, "%s", zTerm);
1368 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1369
1370 /*
1371 ** Check to see if the given parsed markup is the correct
1372 ** </verbatim> tag.
1373 */
@@ -1876,11 +1891,11 @@
1876 ** --htmlonly Set the WIKI_HTMLONLY flag
1877 ** --inline Set the WIKI_INLINE flag
1878 ** --linksonly Set the WIKI_LINKSONLY flag
1879 ** --nobadlinks Set the WIKI_NOBADLINKS flag
1880 ** --noblock Set the WIKI_NOBLOCK flag
1881 ** --text Run the output through html_to_plaintext().
1882 */
1883 void test_wiki_render(void){
1884 Blob in, out;
1885 int flags = 0;
1886 int bText;
@@ -2474,20 +2489,21 @@
2474 blob_append_char(pOut, nNL ? '\n' : ' ');
2475 nWS = 1;
2476 }
2477 }
2478 }else if( zIn[0]=='&' ){
2479 char c = '?';
2480 if( zIn[1]=='#' ){
2481 int x = atoi(&zIn[1]);
2482 if( x>0 && x<=127 ) c = x;
2483 }else{
2484 static const struct { int n; char c; char *z; } aEntity[] = {
2485 { 5, '&', "&amp;" },
2486 { 4, '<', "&lt;" },
2487 { 4, '>', "&gt;" },
2488 { 6, ' ', "&nbsp;" },
 
2489 };
2490 int jj;
2491 for(jj=0; jj<count(aEntity); jj++){
2492 if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
2493 c = aEntity[jj].c;
@@ -2501,11 +2517,25 @@
2501 nNL = c=='\n';
2502 }else{
2503 if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
2504 seenText = 1;
2505 nNL = nWS = 0;
2506 blob_append_char(pOut, c);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2507 }
2508 }else{
2509 if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
2510 seenText = 1;
2511 nNL = nWS = 0;
2512
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -34,10 +34,11 @@
34 #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
35 #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
36 #define WIKI_SAFE 0x100 /* Make the result safe for embedding */
37 #define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */
38 #define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */
39 #define WIKI_ADMIN 0x800 /* Ignore g.perm.Hyperlink */
40 #endif
41
42
43 /*
44 ** These are the only markup attributes allowed.
@@ -1320,11 +1321,11 @@
1321 zTerm = "";
1322 }else{
1323 blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
1324 zTerm = "]</span>";
1325 }
1326 }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){
1327 blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
1328 zTerm = "]</a>";
1329 }else{
1330 zTerm = "";
1331 }
@@ -1364,10 +1365,24 @@
1365 }
1366 if( zExtra ) fossil_free(zExtra);
1367 assert( (int)strlen(zTerm)<nClose );
1368 sqlite3_snprintf(nClose, zClose, "%s", zTerm);
1369 }
1370
1371 /*
1372 ** Check zTarget to see if it looks like a valid hyperlink target.
1373 ** Return true if it does seem valid and false if not.
1374 */
1375 int wiki_valid_link_target(char *zTarget){
1376 char zClose[30];
1377 Blob notUsed;
1378 blob_init(&notUsed, 0, 0);
1379 wiki_resolve_hyperlink(&notUsed, WIKI_NOBADLINKS|WIKI_ADMIN,
1380 zTarget, zClose, sizeof(zClose)-1, 0, 0);
1381 blob_reset(&notUsed);
1382 return zClose[0]!=0;
1383 }
1384
1385 /*
1386 ** Check to see if the given parsed markup is the correct
1387 ** </verbatim> tag.
1388 */
@@ -1876,11 +1891,11 @@
1891 ** --htmlonly Set the WIKI_HTMLONLY flag
1892 ** --inline Set the WIKI_INLINE flag
1893 ** --linksonly Set the WIKI_LINKSONLY flag
1894 ** --nobadlinks Set the WIKI_NOBADLINKS flag
1895 ** --noblock Set the WIKI_NOBLOCK flag
1896 ** --text Run the output through html_to_plaintext().
1897 */
1898 void test_wiki_render(void){
1899 Blob in, out;
1900 int flags = 0;
1901 int bText;
@@ -2474,20 +2489,21 @@
2489 blob_append_char(pOut, nNL ? '\n' : ' ');
2490 nWS = 1;
2491 }
2492 }
2493 }else if( zIn[0]=='&' ){
2494 u32 c = '?';
2495 if( zIn[1]=='#' ){
2496 c = atoi(&zIn[2]);
2497 if( c==0 ) c = '?';
2498 }else{
2499 static const struct { int n; u32 c; char *z; } aEntity[] = {
2500 { 5, '&', "&amp;" },
2501 { 4, '<', "&lt;" },
2502 { 4, '>', "&gt;" },
2503 { 6, ' ', "&nbsp;" },
2504 { 6, '"', "&quot;" },
2505 };
2506 int jj;
2507 for(jj=0; jj<count(aEntity); jj++){
2508 if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
2509 c = aEntity[jj].c;
@@ -2501,11 +2517,25 @@
2517 nNL = c=='\n';
2518 }else{
2519 if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
2520 seenText = 1;
2521 nNL = nWS = 0;
2522 if( c<0x00080 ){
2523 blob_append_char(pOut, c & 0xff);
2524 }else if( c<0x00800 ){
2525 blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f));
2526 blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
2527 }else if( c<0x10000 ){
2528 blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f));
2529 blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
2530 blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
2531 }else{
2532 blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07));
2533 blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f));
2534 blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
2535 blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
2536 }
2537 }
2538 }else{
2539 if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
2540 seenText = 1;
2541 nNL = nWS = 0;
2542
+1 -1
--- src/zip.c
+++ src/zip.c
@@ -652,11 +652,11 @@
652652
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
653653
if( pManifest ){
654654
int flg, eflg = 0;
655655
char *zName = 0;
656656
zip_set_timedate(pManifest->rDate);
657
- flg = db_get_manifest_setting();
657
+ flg = db_get_manifest_setting(blob_str(&hash));
658658
if( flg ){
659659
/* eflg is the effective flags, taking include/exclude into account */
660660
if( (pInclude==0 || glob_match(pInclude, "manifest"))
661661
&& !glob_match(pExclude, "manifest")
662662
&& (flg & MFESTFLG_RAW) ){
663663
--- src/zip.c
+++ src/zip.c
@@ -652,11 +652,11 @@
652 pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
653 if( pManifest ){
654 int flg, eflg = 0;
655 char *zName = 0;
656 zip_set_timedate(pManifest->rDate);
657 flg = db_get_manifest_setting();
658 if( flg ){
659 /* eflg is the effective flags, taking include/exclude into account */
660 if( (pInclude==0 || glob_match(pInclude, "manifest"))
661 && !glob_match(pExclude, "manifest")
662 && (flg & MFESTFLG_RAW) ){
663
--- src/zip.c
+++ src/zip.c
@@ -652,11 +652,11 @@
652 pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
653 if( pManifest ){
654 int flg, eflg = 0;
655 char *zName = 0;
656 zip_set_timedate(pManifest->rDate);
657 flg = db_get_manifest_setting(blob_str(&hash));
658 if( flg ){
659 /* eflg is the effective flags, taking include/exclude into account */
660 if( (pInclude==0 || glob_match(pInclude, "manifest"))
661 && !glob_match(pExclude, "manifest")
662 && (flg & MFESTFLG_RAW) ){
663
+18 -17
--- tools/mkindex.c
+++ tools/mkindex.c
@@ -85,26 +85,26 @@
8585
8686
/***************************************************************************
8787
** These macros must match similar macros in dispatch.c.
8888
**
8989
** Allowed values for CmdOrPage.eCmdFlags. */
90
-#define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */
91
-#define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */
92
-#define CMDFLAG_TEST 0x0004 /* Commands for testing only */
93
-#define CMDFLAG_WEBPAGE 0x0008 /* Web pages */
94
-#define CMDFLAG_COMMAND 0x0010 /* A command */
95
-#define CMDFLAG_SETTING 0x0020 /* A setting */
96
-#define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */
97
-#define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */
98
-#define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */
99
-#define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */
100
-#define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */
101
-#define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */
102
-#define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */
103
-#define CMDFLAG_ALIAS 0x2000 /* Command aliases */
104
-#define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */
105
-#define CMDFLAG_ABBREVSUBCMD 0x8000 /* Abbreviated subcmd in help text */
90
+#define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */
91
+#define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */
92
+#define CMDFLAG_TEST 0x00004 /* Commands for testing only */
93
+#define CMDFLAG_WEBPAGE 0x00008 /* Web pages */
94
+#define CMDFLAG_COMMAND 0x00010 /* A command */
95
+#define CMDFLAG_SETTING 0x00020 /* A setting */
96
+#define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */
97
+#define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */
98
+#define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */
99
+#define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */
100
+#define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */
101
+#define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */
102
+#define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */
103
+#define CMDFLAG_ALIAS 0x02000 /* Command aliases */
104
+#define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */
105
+#define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */
106106
/**************************************************************************/
107107
108108
/*
109109
** Each entry looks like this:
110110
*/
@@ -281,11 +281,12 @@
281281
aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
282282
}else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
283283
aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
284284
}else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
285285
aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;
286
- }else if( j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0 ){
286
+ }else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0)
287
+ || (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){
287288
aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD;
288289
}else{
289290
fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
290291
zFile, nLine, j, &zLine[i]);
291292
nErr++;
292293
--- tools/mkindex.c
+++ tools/mkindex.c
@@ -85,26 +85,26 @@
85
86 /***************************************************************************
87 ** These macros must match similar macros in dispatch.c.
88 **
89 ** Allowed values for CmdOrPage.eCmdFlags. */
90 #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */
91 #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */
92 #define CMDFLAG_TEST 0x0004 /* Commands for testing only */
93 #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */
94 #define CMDFLAG_COMMAND 0x0010 /* A command */
95 #define CMDFLAG_SETTING 0x0020 /* A setting */
96 #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */
97 #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */
98 #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */
99 #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */
100 #define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */
101 #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */
102 #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */
103 #define CMDFLAG_ALIAS 0x2000 /* Command aliases */
104 #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */
105 #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Abbreviated subcmd in help text */
106 /**************************************************************************/
107
108 /*
109 ** Each entry looks like this:
110 */
@@ -281,11 +281,12 @@
281 aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
282 }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
283 aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
284 }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
285 aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;
286 }else if( j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0 ){
 
287 aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD;
288 }else{
289 fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
290 zFile, nLine, j, &zLine[i]);
291 nErr++;
292
--- tools/mkindex.c
+++ tools/mkindex.c
@@ -85,26 +85,26 @@
85
86 /***************************************************************************
87 ** These macros must match similar macros in dispatch.c.
88 **
89 ** Allowed values for CmdOrPage.eCmdFlags. */
90 #define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */
91 #define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */
92 #define CMDFLAG_TEST 0x00004 /* Commands for testing only */
93 #define CMDFLAG_WEBPAGE 0x00008 /* Web pages */
94 #define CMDFLAG_COMMAND 0x00010 /* A command */
95 #define CMDFLAG_SETTING 0x00020 /* A setting */
96 #define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */
97 #define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */
98 #define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */
99 #define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */
100 #define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */
101 #define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */
102 #define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */
103 #define CMDFLAG_ALIAS 0x02000 /* Command aliases */
104 #define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */
105 #define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */
106 /**************************************************************************/
107
108 /*
109 ** Each entry looks like this:
110 */
@@ -281,11 +281,12 @@
281 aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
282 }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
283 aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
284 }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
285 aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;
286 }else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0)
287 || (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){
288 aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD;
289 }else{
290 fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
291 zFile, nLine, j, &zLine[i]);
292 nErr++;
293
--- www/alerts.md
+++ www/alerts.md
@@ -7,10 +7,11 @@
77
88
* New [checkins](/help?cmd=ci)
99
* [Ticket](./tickets.wiki) changes
1010
* [Wiki](./wikitheory.wiki) page changes
1111
* New and edited [forum](./forum.wiki) posts
12
+ * Users receiving [new permissions](./caps/index.md) (admins only)
1213
* Announcements
1314
1415
Subscribers can elect to receive emails as soon as these events happen,
1516
or they can receive a daily digest of the events instead.
1617
@@ -515,10 +516,14 @@
515516
email addresses until the user clicks the link in the verification
516517
email. This checkbox lets the Fossil Admin user manually verify the
517518
user, such as in the case where the verification email message got
518519
lost. Unchecking this box does not cause another verification email
519520
to be sent.
521
+
522
+* Admin users (only) may activate the "user elevation" subscription,
523
+ which sends a notification when a user is created or is explicitly
524
+ assigned permission they did not formerly have.
520525
521526
This screen also allows a Fossil Admin user to perform other activities
522527
on behalf of a subscriber which they could do themselves, such as to
523528
[unsubscribe](#unsub) them.
524529
525530
--- www/alerts.md
+++ www/alerts.md
@@ -7,10 +7,11 @@
7
8 * New [checkins](/help?cmd=ci)
9 * [Ticket](./tickets.wiki) changes
10 * [Wiki](./wikitheory.wiki) page changes
11 * New and edited [forum](./forum.wiki) posts
 
12 * Announcements
13
14 Subscribers can elect to receive emails as soon as these events happen,
15 or they can receive a daily digest of the events instead.
16
@@ -515,10 +516,14 @@
515 email addresses until the user clicks the link in the verification
516 email. This checkbox lets the Fossil Admin user manually verify the
517 user, such as in the case where the verification email message got
518 lost. Unchecking this box does not cause another verification email
519 to be sent.
 
 
 
 
520
521 This screen also allows a Fossil Admin user to perform other activities
522 on behalf of a subscriber which they could do themselves, such as to
523 [unsubscribe](#unsub) them.
524
525
--- www/alerts.md
+++ www/alerts.md
@@ -7,10 +7,11 @@
7
8 * New [checkins](/help?cmd=ci)
9 * [Ticket](./tickets.wiki) changes
10 * [Wiki](./wikitheory.wiki) page changes
11 * New and edited [forum](./forum.wiki) posts
12 * Users receiving [new permissions](./caps/index.md) (admins only)
13 * Announcements
14
15 Subscribers can elect to receive emails as soon as these events happen,
16 or they can receive a daily digest of the events instead.
17
@@ -515,10 +516,14 @@
516 email addresses until the user clicks the link in the verification
517 email. This checkbox lets the Fossil Admin user manually verify the
518 user, such as in the case where the verification email message got
519 lost. Unchecking this box does not cause another verification email
520 to be sent.
521
522 * Admin users (only) may activate the "user elevation" subscription,
523 which sends a notification when a user is created or is explicitly
524 assigned permission they did not formerly have.
525
526 This screen also allows a Fossil Admin user to perform other activities
527 on behalf of a subscriber which they could do themselves, such as to
528 [unsubscribe](#unsub) them.
529
530
+48 -17
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,28 +1,46 @@
11
<title>Change Log</title>
22
33
<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
44
5
- * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that
6
- it optionally accepts a directory name as its argument, and uses files
7
- under that directory as the baseline for the diff.
5
+ * Enhancements to [/help?cmd=diff|fossil diff] and similar:
6
+ <ol type="a">
7
+ <li> The --from can optionally accepts a directory name as its argument,
8
+ and uses files under that directory as the baseline for the diff.
9
+ <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
10
+ is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
11
+ are available, or a --by diff if not.
12
+ <li> The "Reload" button is added to --tk diffs, to bring the displayed
13
+ diff up to date with the latest changes on disk.
14
+ <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
15
+ diffs of multiple files.
16
+ </ol>
817
* Added the [/help?cmd=/ckout|/ckout web page] to provide information
918
about pending changes in a working check-out
1019
* The [/help?cmd=ui|fossil ui] command defaults to using the
1120
[/help?cmd=/ckout|/ckout page] as its start page. Or, if the
1221
"--from PATH" option is present, the default start page becomes
1322
"/ckout?exbase=PATH".
14
- * Added the [/help?cmd=merge-info|fossil merge-info] command and especially
15
- the --tk option to that command, to provide analysis of the most recent
16
- merge or update operation.
17
- * Added the ability to sign check-ins with SSH keys.
18
- * Issue a warning if a user tries to commit on a check-in where the
19
- branch has been changed.
20
- * When a merge conflict occurs, a new section is added to the conflict
21
- text that shows Fossil's suggested resolution to the conflict.
22
- * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
23
- diffs of multiple files.
23
+ * Enhancements to [/help?cmd=merge|fossil merge]:
24
+ <ol type="a">
25
+ <li> Added the [/help?cmd=merge-info|fossil merge-info] command and especially
26
+ the --tk option to that command, to provide analysis of the most recent
27
+ merge or update operation.
28
+ <li> When a merge conflict occurs, a new section is added to the conflict
29
+ text that shows Fossil's suggested resolution to the conflict.
30
+ </ol>
31
+ * Enhancements to [/help?cmd=commit|fossil commit]:
32
+ <ol type="a">
33
+ <li> If Fossil sees potential formatting mistakes (bad hyperlinks)
34
+ in the check-in comment, it will alert the developer and give
35
+ him or her the opportunity to edit the comment before continuing.
36
+ <li> The new "--if-changes" option causes the commit to become
37
+ a quiet no-op if there are no pending changes.
38
+ <li> Added the ability to sign check-ins with SSH keys.
39
+ <li> Issue a warning if a user tries to commit on a check-in where the
40
+ branch has been changed.
41
+ </ol>
2442
* Deprecate the --comfmtflags and --comment-format global options and
2543
no longer list them in the built-in help, but keep them working for
2644
backwards compatibility.
2745
Alternative TTY comment formatting can still be specified using the
2846
[/help?cmd=comment-format|comment-format setting], if desired. The
@@ -49,30 +67,43 @@
4967
<li> Enhance the "ymd" query parameter so that when used like
5068
"ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of
5169
dates specified.
5270
<li> Accept the "Z" (Zulu-time) suffix on date arguments for the
5371
"ymd" and "yw" query parameters.
72
+ <li> The new "min" query parameter, when added to a from=,to= query,
73
+ collapses long runs of check-ins on the same branch into just
74
+ end-points.
75
+ <li> The p= and d= parameters an reference different check-ins, which
76
+ case the timeline shows those check-ins that are both ancestors
77
+ of p= and descendants of d=.
5478
</ol>
55
- * Add the "--if-changes" option to the [/help?cmd=commit|fossil commit]
56
- command that causes the command to become a quiet no-op if there are
57
- no pending changes.
5879
* Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
5980
and debugging
6081
* Fix a bug in [/help?cmd=patch|fossil patch create] that causes
6182
[/help?cmd=revert|fossil revert] operations that happened on individual
6283
files after a [/help?cmd=merge|fossil merge] to be omitted from the
6384
patch.
6485
* Added the [/help?cmd=patch|patch alias] command for managing aliases
6586
for remote checkout names.
66
- * Enhance the [/help?cmd=help|fossil help] command:
87
+ * Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
6788
<ol type="a">
89
+ <li> Add the ability to search the help text, either in the UI
90
+ (on the [/help?cmd=/search|/search page]) or from the command-line
91
+ (using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
6892
<li> Accepts an optional SUBCOMMAND argument following the
6993
COMMAND argument and only shows results for the specified
7094
subcommand, not the entire command.
7195
<li> The -u (--usage) option shows only the command-line syntax
7296
<li> The -o (--options) option shows only the command-line options
7397
</ol>
98
+ * Added the ability to attach wiki pages to a ticket for extended
99
+ descriptions.
100
+ * Add a "user elevation" [/doc/trunk/www/alerts.md|subscription]
101
+ which alerts subscribers when an admin creates a new user or
102
+ adds new permissions to one.
103
+ * Diverse minor fixes and additions.
104
+
74105
75106
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
76107
77108
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
78109
that have non-ASCII filenames
79110
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,28 +1,46 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
4
5 * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that
6 it optionally accepts a directory name as its argument, and uses files
7 under that directory as the baseline for the diff.
 
 
 
 
 
 
 
 
 
8 * Added the [/help?cmd=/ckout|/ckout web page] to provide information
9 about pending changes in a working check-out
10 * The [/help?cmd=ui|fossil ui] command defaults to using the
11 [/help?cmd=/ckout|/ckout page] as its start page. Or, if the
12 "--from PATH" option is present, the default start page becomes
13 "/ckout?exbase=PATH".
14 * Added the [/help?cmd=merge-info|fossil merge-info] command and especially
15 the --tk option to that command, to provide analysis of the most recent
16 merge or update operation.
17 * Added the ability to sign check-ins with SSH keys.
18 * Issue a warning if a user tries to commit on a check-in where the
19 branch has been changed.
20 * When a merge conflict occurs, a new section is added to the conflict
21 text that shows Fossil's suggested resolution to the conflict.
22 * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
23 diffs of multiple files.
 
 
 
 
 
 
 
 
 
24 * Deprecate the --comfmtflags and --comment-format global options and
25 no longer list them in the built-in help, but keep them working for
26 backwards compatibility.
27 Alternative TTY comment formatting can still be specified using the
28 [/help?cmd=comment-format|comment-format setting], if desired. The
@@ -49,30 +67,43 @@
49 <li> Enhance the "ymd" query parameter so that when used like
50 "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of
51 dates specified.
52 <li> Accept the "Z" (Zulu-time) suffix on date arguments for the
53 "ymd" and "yw" query parameters.
 
 
 
 
 
 
54 </ol>
55 * Add the "--if-changes" option to the [/help?cmd=commit|fossil commit]
56 command that causes the command to become a quiet no-op if there are
57 no pending changes.
58 * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
59 and debugging
60 * Fix a bug in [/help?cmd=patch|fossil patch create] that causes
61 [/help?cmd=revert|fossil revert] operations that happened on individual
62 files after a [/help?cmd=merge|fossil merge] to be omitted from the
63 patch.
64 * Added the [/help?cmd=patch|patch alias] command for managing aliases
65 for remote checkout names.
66 * Enhance the [/help?cmd=help|fossil help] command:
67 <ol type="a">
 
 
 
68 <li> Accepts an optional SUBCOMMAND argument following the
69 COMMAND argument and only shows results for the specified
70 subcommand, not the entire command.
71 <li> The -u (--usage) option shows only the command-line syntax
72 <li> The -o (--options) option shows only the command-line options
73 </ol>
 
 
 
 
 
 
 
74
75 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
76
77 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
78 that have non-ASCII filenames
79
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,28 +1,46 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
4
5 * Enhancements to [/help?cmd=diff|fossil diff] and similar:
6 <ol type="a">
7 <li> The --from can optionally accepts a directory name as its argument,
8 and uses files under that directory as the baseline for the diff.
9 <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
10 is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
11 are available, or a --by diff if not.
12 <li> The "Reload" button is added to --tk diffs, to bring the displayed
13 diff up to date with the latest changes on disk.
14 <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
15 diffs of multiple files.
16 </ol>
17 * Added the [/help?cmd=/ckout|/ckout web page] to provide information
18 about pending changes in a working check-out
19 * The [/help?cmd=ui|fossil ui] command defaults to using the
20 [/help?cmd=/ckout|/ckout page] as its start page. Or, if the
21 "--from PATH" option is present, the default start page becomes
22 "/ckout?exbase=PATH".
23 * Enhancements to [/help?cmd=merge|fossil merge]:
24 <ol type="a">
25 <li> Added the [/help?cmd=merge-info|fossil merge-info] command and especially
26 the --tk option to that command, to provide analysis of the most recent
27 merge or update operation.
28 <li> When a merge conflict occurs, a new section is added to the conflict
29 text that shows Fossil's suggested resolution to the conflict.
30 </ol>
31 * Enhancements to [/help?cmd=commit|fossil commit]:
32 <ol type="a">
33 <li> If Fossil sees potential formatting mistakes (bad hyperlinks)
34 in the check-in comment, it will alert the developer and give
35 him or her the opportunity to edit the comment before continuing.
36 <li> The new "--if-changes" option causes the commit to become
37 a quiet no-op if there are no pending changes.
38 <li> Added the ability to sign check-ins with SSH keys.
39 <li> Issue a warning if a user tries to commit on a check-in where the
40 branch has been changed.
41 </ol>
42 * Deprecate the --comfmtflags and --comment-format global options and
43 no longer list them in the built-in help, but keep them working for
44 backwards compatibility.
45 Alternative TTY comment formatting can still be specified using the
46 [/help?cmd=comment-format|comment-format setting], if desired. The
@@ -49,30 +67,43 @@
67 <li> Enhance the "ymd" query parameter so that when used like
68 "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of
69 dates specified.
70 <li> Accept the "Z" (Zulu-time) suffix on date arguments for the
71 "ymd" and "yw" query parameters.
72 <li> The new "min" query parameter, when added to a from=,to= query,
73 collapses long runs of check-ins on the same branch into just
74 end-points.
75 <li> The p= and d= parameters an reference different check-ins, which
76 case the timeline shows those check-ins that are both ancestors
77 of p= and descendants of d=.
78 </ol>
 
 
 
79 * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
80 and debugging
81 * Fix a bug in [/help?cmd=patch|fossil patch create] that causes
82 [/help?cmd=revert|fossil revert] operations that happened on individual
83 files after a [/help?cmd=merge|fossil merge] to be omitted from the
84 patch.
85 * Added the [/help?cmd=patch|patch alias] command for managing aliases
86 for remote checkout names.
87 * Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
88 <ol type="a">
89 <li> Add the ability to search the help text, either in the UI
90 (on the [/help?cmd=/search|/search page]) or from the command-line
91 (using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
92 <li> Accepts an optional SUBCOMMAND argument following the
93 COMMAND argument and only shows results for the specified
94 subcommand, not the entire command.
95 <li> The -u (--usage) option shows only the command-line syntax
96 <li> The -o (--options) option shows only the command-line options
97 </ol>
98 * Added the ability to attach wiki pages to a ticket for extended
99 descriptions.
100 * Add a "user elevation" [/doc/trunk/www/alerts.md|subscription]
101 which alerts subscribers when an admin creates a new user or
102 adds new permissions to one.
103 * Diverse minor fixes and additions.
104
105
106 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
107
108 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
109 that have non-ASCII filenames
110
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -173,11 +173,14 @@
173173
same file as it existed in the parent check-in. If the name of the
174174
file is unchanged from its parent, then the 4th argument is omitted.
175175
176176
A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the
177177
text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype
178
-is used.
178
+is used. Note that the <b>N</b> card has never actually been used by
179
+any Fossil implementation. The implementation has always interpreted
180
+check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules].
181
+There are no current plans to ever change that.
179182
180183
A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card.
181184
The <b>P</b> card has a varying number of arguments that
182185
define other manifests from which the current manifest
183186
is derived. Each argument is a lowercase
184187
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -173,11 +173,14 @@
173 same file as it existed in the parent check-in. If the name of the
174 file is unchanged from its parent, then the 4th argument is omitted.
175
176 A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the
177 text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype
178 is used.
 
 
 
179
180 A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card.
181 The <b>P</b> card has a varying number of arguments that
182 define other manifests from which the current manifest
183 is derived. Each argument is a lowercase
184
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -173,11 +173,14 @@
173 same file as it existed in the parent check-in. If the name of the
174 file is unchanged from its parent, then the 4th argument is omitted.
175
176 A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the
177 text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype
178 is used. Note that the <b>N</b> card has never actually been used by
179 any Fossil implementation. The implementation has always interpreted
180 check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules].
181 There are no current plans to ever change that.
182
183 A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card.
184 The <b>P</b> card has a varying number of arguments that
185 define other manifests from which the current manifest
186 is derived. Each argument is a lowercase
187
--- www/settings.wiki
+++ www/settings.wiki
@@ -1,18 +1,18 @@
11
<title>Fossil Settings</title>
22
3
-<h2>Using Fossil Settings</h2>
3
+<h1>Using Fossil Settings</h1>
44
55
Settings control the behaviour of fossil. They are set with the
66
<tt>fossil settings</tt> command, or through the web interface in
77
the Settings page in the Admin section.
88
99
For a list of all settings, view the Settings page, or type
1010
<tt>fossil help settings</tt> from the command line.
1111
1212
13
-<h3 id="repo">Repository settings</h3>
13
+<h2 id="repo">1.0 Repository settings</h2>
1414
1515
Settings are set on a per-repository basis. When you clone a repository,
1616
a subset of settings are copied to your local repository.
1717
1818
If you make a change to a setting on your local repository, it is not
@@ -24,11 +24,11 @@
2424
will be used for all repositories cloned to your machine, unless
2525
overridden explicitly in a particular repository. Global settings can be
2626
set by using the <tt>-global</tt> option on the <tt>fossil settings</tt>
2727
command.
2828
29
-<h3 id="versionable">"Versionable" settings</h3>
29
+<h2 id="versionable">2.0 "Versionable" settings</h2>
3030
3131
Most of the settings control the behaviour of fossil on your local
3232
machine, largely acting to reflect your preference on how you want to
3333
use Fossil, how you communicate with the server, or options for hosting
3434
a repository on the web.
3535
--- www/settings.wiki
+++ www/settings.wiki
@@ -1,18 +1,18 @@
1 <title>Fossil Settings</title>
2
3 <h2>Using Fossil Settings</h2>
4
5 Settings control the behaviour of fossil. They are set with the
6 <tt>fossil settings</tt> command, or through the web interface in
7 the Settings page in the Admin section.
8
9 For a list of all settings, view the Settings page, or type
10 <tt>fossil help settings</tt> from the command line.
11
12
13 <h3 id="repo">Repository settings</h3>
14
15 Settings are set on a per-repository basis. When you clone a repository,
16 a subset of settings are copied to your local repository.
17
18 If you make a change to a setting on your local repository, it is not
@@ -24,11 +24,11 @@
24 will be used for all repositories cloned to your machine, unless
25 overridden explicitly in a particular repository. Global settings can be
26 set by using the <tt>-global</tt> option on the <tt>fossil settings</tt>
27 command.
28
29 <h3 id="versionable">"Versionable" settings</h3>
30
31 Most of the settings control the behaviour of fossil on your local
32 machine, largely acting to reflect your preference on how you want to
33 use Fossil, how you communicate with the server, or options for hosting
34 a repository on the web.
35
--- www/settings.wiki
+++ www/settings.wiki
@@ -1,18 +1,18 @@
1 <title>Fossil Settings</title>
2
3 <h1>Using Fossil Settings</h1>
4
5 Settings control the behaviour of fossil. They are set with the
6 <tt>fossil settings</tt> command, or through the web interface in
7 the Settings page in the Admin section.
8
9 For a list of all settings, view the Settings page, or type
10 <tt>fossil help settings</tt> from the command line.
11
12
13 <h2 id="repo">1.0 Repository settings</h2>
14
15 Settings are set on a per-repository basis. When you clone a repository,
16 a subset of settings are copied to your local repository.
17
18 If you make a change to a setting on your local repository, it is not
@@ -24,11 +24,11 @@
24 will be used for all repositories cloned to your machine, unless
25 overridden explicitly in a particular repository. Global settings can be
26 set by using the <tt>-global</tt> option on the <tt>fossil settings</tt>
27 command.
28
29 <h2 id="versionable">2.0 "Versionable" settings</h2>
30
31 Most of the settings control the behaviour of fossil on your local
32 machine, largely acting to reflect your preference on how you want to
33 use Fossil, how you communicate with the server, or options for hosting
34 a repository on the web.
35

Keyboard Shortcuts

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